muffin v7.38.8

This commit is contained in:
alydev 2025-11-30 10:36:46 +10:00
parent 5e2d8f648b
commit 3e10cbbbf2
51 changed files with 2585 additions and 1972 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,20 @@
using Questionable.Functions;
namespace Questionable.Controller.DebugCommands;
internal sealed class AbandonDutyCommandHandler : IDebugCommandHandler
{
private readonly GameFunctions _gameFunctions;
public string CommandName => "abandon-duty";
public AbandonDutyCommandHandler(GameFunctions gameFunctions)
{
_gameFunctions = gameFunctions;
}
public void Execute(string[] arguments)
{
_gameFunctions.AbandonDuty();
}
}

View file

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Plugin.Services;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model.Common;
using Questionable.Model.Questing.Converter;
namespace Questionable.Controller.DebugCommands;
internal sealed class AethernetCommandHandler : IDebugCommandHandler
{
private readonly IDataManager _dataManager;
private readonly IClientState _clientState;
private readonly AetheryteFunctions _aetheryteFunctions;
private readonly IChatGui _chatGui;
public string CommandName => "aethernet";
public AethernetCommandHandler(IDataManager dataManager, IClientState clientState, AetheryteFunctions aetheryteFunctions, IChatGui chatGui)
{
_dataManager = dataManager;
_clientState = clientState;
_aetheryteFunctions = aetheryteFunctions;
_chatGui = chatGui;
}
public void Execute(string[] arguments)
{
ushort territoryType = _clientState.TerritoryType;
Dictionary<EAetheryteLocation, string> values = AethernetShardConverter.Values;
AetheryteData aetheryteData = new AetheryteData(_dataManager);
HashSet<string> hashSet = new HashSet<string>();
Dictionary<string, List<(EAetheryteLocation, string, bool)>> dictionary = new Dictionary<string, List<(EAetheryteLocation, string, bool)>>();
EAetheryteLocation key;
string value;
foreach (KeyValuePair<EAetheryteLocation, string> item3 in values)
{
item3.Deconstruct(out key, out value);
EAetheryteLocation key2 = key;
string text = value;
if (aetheryteData.TerritoryIds.TryGetValue(key2, out var value2) && value2 == territoryType)
{
int num = text.IndexOf(']', StringComparison.Ordinal);
if (num > 0)
{
string item = text.Substring(1, num - 1);
hashSet.Add(item);
}
}
}
if (hashSet.Count == 0)
{
_chatGui.Print("No aethernet shards found in current zone.", "Questionable", 576);
return;
}
foreach (KeyValuePair<EAetheryteLocation, string> item4 in values)
{
item4.Deconstruct(out key, out value);
EAetheryteLocation eAetheryteLocation = key;
string text2 = value;
int num2 = text2.IndexOf(']', StringComparison.Ordinal);
if (num2 <= 0)
{
continue;
}
string text3 = text2.Substring(1, num2 - 1);
if (hashSet.Contains(text3))
{
if (!dictionary.ContainsKey(text3))
{
dictionary[text3] = new List<(EAetheryteLocation, string, bool)>();
}
bool item2 = _aetheryteFunctions.IsAetheryteUnlocked(eAetheryteLocation);
dictionary[text3].Add((eAetheryteLocation, text2, item2));
}
}
foreach (KeyValuePair<string, List<(EAetheryteLocation, string, bool)>> item5 in dictionary.OrderBy<KeyValuePair<string, List<(EAetheryteLocation, string, bool)>>, string>((KeyValuePair<string, List<(EAetheryteLocation Location, string Name, bool Unlocked)>> x) => x.Key))
{
item5.Deconstruct(out value, out var value3);
string value4 = value;
List<(EAetheryteLocation, string, bool)> list = value3;
List<(EAetheryteLocation, string, bool)> list2 = list.Where<(EAetheryteLocation, string, bool)>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => x.Unlocked).ToList();
List<(EAetheryteLocation, string, bool)> list3 = list.Where<(EAetheryteLocation, string, bool)>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => !x.Unlocked).ToList();
_chatGui.Print($"Aethernet Shards in {value4} ({list.Count} total):", "Questionable", 576);
_chatGui.Print($" Unlocked: {list2.Count}", "Questionable", 576);
_chatGui.Print($" Missing: {list3.Count}", "Questionable", 576);
_chatGui.Print("", "Questionable", 576);
if (list3.Count > 0)
{
_chatGui.Print("Missing/Unattuned Aethernet Shards:", "Questionable", 576);
foreach (var item6 in list3.OrderBy<(EAetheryteLocation, string, bool), string>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => x.Name))
{
_chatGui.Print(" " + item6.Item2, "Questionable", 576);
}
_chatGui.Print("", "Questionable", 576);
}
if (list2.Count > 0)
{
_chatGui.Print("Unlocked Aethernet Shards:", "Questionable", 576);
foreach (var item7 in list2.OrderBy<(EAetheryteLocation, string, bool), string>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => x.Name))
{
_chatGui.Print(" " + item7.Item2, "Questionable", 576);
}
}
if (dictionary.Count > 1)
{
_chatGui.Print("", "Questionable", 576);
}
}
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Questionable.Controller.DebugCommands;
internal sealed class DebugCommandExecutor
{
private readonly Dictionary<string, IDebugCommandHandler> _handlers;
public DebugCommandExecutor(IEnumerable<IDebugCommandHandler> handlers)
{
_handlers = handlers.ToDictionary<IDebugCommandHandler, string>((IDebugCommandHandler h) => h.CommandName, StringComparer.OrdinalIgnoreCase);
}
public bool TryExecute(string command, string[] arguments)
{
if (_handlers.TryGetValue(command, out IDebugCommandHandler value))
{
value.Execute(arguments);
return true;
}
return false;
}
}

View file

@ -0,0 +1,50 @@
using Dalamud.Plugin.Services;
using Questionable.Model;
using Questionable.Model.Questing;
using Questionable.Windows;
namespace Questionable.Controller.DebugCommands;
internal sealed class DebugOverlayCommandHandler : IDebugCommandHandler
{
private readonly DebugOverlay _debugOverlay;
private readonly QuestRegistry _questRegistry;
private readonly IChatGui _chatGui;
public string CommandName => "do";
public DebugOverlayCommandHandler(DebugOverlay debugOverlay, QuestRegistry questRegistry, IChatGui chatGui)
{
_debugOverlay = debugOverlay;
_questRegistry = questRegistry;
_chatGui = chatGui;
}
public void Execute(string[] arguments)
{
ElementId elementId;
if (!_debugOverlay.DrawConditions())
{
_chatGui.PrintError("You don't have the debug overlay enabled.", "Questionable", 576);
}
else if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out elementId) && elementId != null)
{
if (_questRegistry.TryGetQuest(elementId, out Quest quest))
{
_debugOverlay.HighlightedQuest = quest.Id;
_chatGui.Print($"Set highlighted quest to {elementId} ({quest.Info.Name}).", "Questionable", 576);
}
else
{
_chatGui.PrintError($"Unknown quest {elementId}.", "Questionable", 576);
}
}
else
{
_debugOverlay.HighlightedQuest = null;
_chatGui.Print("Cleared highlighted quest.", "Questionable", 576);
}
}
}

View file

@ -0,0 +1,39 @@
using System.Collections.Generic;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
namespace Questionable.Controller.DebugCommands;
internal sealed class FestivalsCommandHandler : IDebugCommandHandler
{
private readonly IChatGui _chatGui;
public string CommandName => "festivals";
public FestivalsCommandHandler(IChatGui chatGui)
{
_chatGui = chatGui;
}
public unsafe void Execute(string[] arguments)
{
List<string> list = new List<string>();
for (byte b = 0; b < 4; b++)
{
GameMain.Festival festival = GameMain.Instance()->ActiveFestivals[b];
if (festival.Id == 0)
{
list.Add($"Slot {b}: None");
}
else
{
list.Add($"Slot {b}: {festival.Id}({festival.Phase})");
}
}
_chatGui.Print("Festival slots:", "Questionable", 576);
foreach (string item in list)
{
_chatGui.Print(" " + item, "Questionable", 576);
}
}
}

View file

@ -0,0 +1,8 @@
namespace Questionable.Controller.DebugCommands;
internal interface IDebugCommandHandler
{
string CommandName { get; }
void Execute(string[] arguments);
}

View file

@ -0,0 +1,37 @@
using Dalamud.Plugin.Services;
using Lumina.Excel.Sheets;
using Questionable.Functions;
namespace Questionable.Controller.DebugCommands;
internal sealed class MountIdCommandHandler : IDebugCommandHandler
{
private readonly GameFunctions _gameFunctions;
private readonly IDataManager _dataManager;
private readonly IChatGui _chatGui;
public string CommandName => "mountid";
public MountIdCommandHandler(GameFunctions gameFunctions, IDataManager dataManager, IChatGui chatGui)
{
_gameFunctions = gameFunctions;
_dataManager = dataManager;
_chatGui = chatGui;
}
public void Execute(string[] arguments)
{
ushort? mountId = _gameFunctions.GetMountId();
if (mountId.HasValue)
{
Mount? rowOrDefault = _dataManager.GetExcelSheet<Mount>().GetRowOrDefault(mountId.Value);
_chatGui.Print($"Mount ID: {mountId}, Name: {rowOrDefault?.Singular}, Obtainable: {((rowOrDefault?.Order == -1) ? "No" : "Yes")}", "Questionable", 576);
}
else
{
_chatGui.Print("You are not mounted.", "Questionable", 576);
}
}
}

View file

@ -0,0 +1,53 @@
using Dalamud.Plugin.Services;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
namespace Questionable.Controller.DebugCommands;
internal sealed class NextQuestCommandHandler : IDebugCommandHandler
{
private readonly QuestController _questController;
private readonly QuestRegistry _questRegistry;
private readonly QuestFunctions _questFunctions;
private readonly IChatGui _chatGui;
public string CommandName => "next";
public NextQuestCommandHandler(QuestController questController, QuestRegistry questRegistry, QuestFunctions questFunctions, IChatGui chatGui)
{
_questController = questController;
_questRegistry = questRegistry;
_questFunctions = questFunctions;
_chatGui = chatGui;
}
public void Execute(string[] arguments)
{
if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out ElementId elementId) && elementId != null)
{
Quest quest;
if (_questFunctions.IsQuestLocked(elementId))
{
_chatGui.PrintError($"Quest {elementId} is locked.", "Questionable", 576);
}
else if (_questRegistry.TryGetQuest(elementId, out quest))
{
_questController.SetNextQuest(quest);
_chatGui.Print($"Set next quest to {elementId} ({quest.Info.Name}).", "Questionable", 576);
}
else
{
_chatGui.PrintError($"Unknown quest {elementId}.", "Questionable", 576);
}
}
else
{
_questController.SetNextQuest(null);
_chatGui.Print("Cleared next quest.", "Questionable", 576);
}
}
}

View file

@ -0,0 +1,220 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using Lumina.Excel;
using Lumina.Excel.Sheets;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
namespace Questionable.Controller.DebugCommands;
internal sealed class QuestKillsCommandHandler : IDebugCommandHandler
{
private readonly QuestController _questController;
private readonly QuestFunctions _questFunctions;
private readonly IDataManager _dataManager;
private readonly IObjectTable _objectTable;
private readonly IChatGui _chatGui;
public string CommandName => "quest-kills";
public QuestKillsCommandHandler(QuestController questController, QuestFunctions questFunctions, IDataManager dataManager, IObjectTable objectTable, IChatGui chatGui)
{
_questController = questController;
_questFunctions = questFunctions;
_dataManager = dataManager;
_objectTable = objectTable;
_chatGui = chatGui;
}
public void Execute(string[] arguments)
{
(QuestController.QuestProgress, QuestController.ECurrentQuestType)? currentQuestDetails = _questController.CurrentQuestDetails;
if (!currentQuestDetails.HasValue)
{
_chatGui.PrintError("No active quest.", "Questionable", 576);
return;
}
QuestController.QuestProgress item = currentQuestDetails.Value.Item1;
Questionable.Model.Quest quest = item.Quest;
QuestProgressInfo questProgressInfo = null;
if (quest.Id is QuestId elementId)
{
questProgressInfo = _questFunctions.GetQuestProgressInfo(elementId);
}
if (questProgressInfo == null)
{
_chatGui.PrintError("Unable to retrieve quest progress information.", "Questionable", 576);
return;
}
QuestSequence questSequence = quest.FindSequence(item.Sequence);
if (questSequence == null)
{
_chatGui.PrintError($"Sequence {item.Sequence} not found for quest {quest.Id}.", "Questionable", 576);
return;
}
QuestStep questStep = ((item.Step < questSequence.Steps.Count) ? questSequence.Steps[item.Step] : null);
if (questStep == null)
{
_chatGui.PrintError($"Step {item.Step} not found in sequence {item.Sequence}.", "Questionable", 576);
return;
}
_chatGui.Print($"Quest: {quest.Info.Name} ({quest.Id})", "Questionable", 576);
_chatGui.Print($"Sequence: {item.Sequence}, Step: {item.Step}", "Questionable", 576);
_chatGui.Print("", "Questionable", 576);
_chatGui.Print("Quest Variables: " + string.Join(", ", questProgressInfo.Variables.Select((byte v, int i) => $"[{i}]={v}")), "Questionable", 576);
_chatGui.Print("", "Questionable", 576);
ExcelSheet<BNpcName> bnpcNameSheet = _dataManager.GetExcelSheet<BNpcName>();
HashSet<uint> hashSet = new HashSet<uint>(questStep.KillEnemyDataIds);
foreach (ComplexCombatData complexCombatDatum in questStep.ComplexCombatData)
{
hashSet.Add(complexCombatDatum.DataId);
}
if (hashSet.Count > 0)
{
_chatGui.Print($"All Enemy DataIds Found: {hashSet.Count}", "Questionable", 576);
foreach (uint item3 in hashSet.OrderBy((uint x) => x))
{
(string Name, bool Found) tuple = GetEnemyName(item3);
var (value, _) = tuple;
if (tuple.Found)
{
_chatGui.Print($" - {value} (DataId: {item3})", "Questionable", 576);
}
else
{
_chatGui.Print($" - DataId: {item3}", "Questionable", 576);
}
}
_chatGui.Print("", "Questionable", 576);
}
if (questStep.ComplexCombatData.Count > 0)
{
_chatGui.Print($"Complex Combat Data Entries: {questStep.ComplexCombatData.Count}", "Questionable", 576);
_chatGui.Print("Kill Progress:", "Questionable", 576);
if (questStep.ComplexCombatData.Count == 1 && hashSet.Count > 1)
{
ComplexCombatData complexCombatData = questStep.ComplexCombatData[0];
int num = -1;
byte? b = null;
for (int num2 = 0; num2 < complexCombatData.CompletionQuestVariablesFlags.Count; num2++)
{
QuestWorkValue questWorkValue = complexCombatData.CompletionQuestVariablesFlags[num2];
if (questWorkValue != null && questWorkValue.Low.HasValue)
{
num = num2;
b = questWorkValue.Low;
break;
}
}
byte b2 = (byte)(((num >= 0 && num < questProgressInfo.Variables.Count) ? questProgressInfo.Variables[num] : 0) & 0xF);
string value2 = (b.HasValue ? $" {b2}/{b}" : "");
string value3 = ((b.HasValue && b2 >= b) ? "✓" : "○");
foreach (uint item4 in hashSet.OrderBy((uint x) => x))
{
(string Name, bool Found) tuple3 = GetEnemyName(item4);
var (value4, _) = tuple3;
if (tuple3.Found)
{
_chatGui.Print($" {value3} Slay {value4}.{value2} (DataId: {item4})", "Questionable", 576);
}
else
{
_chatGui.Print($" {value3} Slay enemy.{value2} (DataId: {item4})", "Questionable", 576);
}
}
}
else
{
for (int num3 = 0; num3 < questStep.ComplexCombatData.Count; num3++)
{
ComplexCombatData complexCombatData2 = questStep.ComplexCombatData[num3];
int num4 = -1;
byte? b3 = null;
bool flag = false;
for (int num5 = 0; num5 < complexCombatData2.CompletionQuestVariablesFlags.Count; num5++)
{
QuestWorkValue questWorkValue2 = complexCombatData2.CompletionQuestVariablesFlags[num5];
if (questWorkValue2 != null)
{
if (questWorkValue2.Low.HasValue)
{
num4 = num5;
b3 = questWorkValue2.Low;
flag = false;
break;
}
if (questWorkValue2.High.HasValue)
{
num4 = num5;
b3 = questWorkValue2.High;
flag = true;
break;
}
}
}
byte b4 = (byte)((num4 >= 0 && num4 < questProgressInfo.Variables.Count) ? questProgressInfo.Variables[num4] : 0);
byte b5 = (flag ? ((byte)(b4 >> 4)) : ((byte)(b4 & 0xF)));
string value5;
if (complexCombatData2.NameId.HasValue)
{
BNpcName? bNpcName = bnpcNameSheet?.GetRowOrDefault(complexCombatData2.NameId.Value);
value5 = ((!bNpcName.HasValue || string.IsNullOrEmpty(bNpcName.Value.Singular.ToString())) ? "enemy" : bNpcName.Value.Singular.ToString());
}
else
{
(string Name, bool Found) tuple5 = GetEnemyName(complexCombatData2.DataId);
string item2 = tuple5.Name;
value5 = (tuple5.Found ? item2 : "enemy");
}
string value6 = (b3.HasValue ? $" {b5}/{b3}" : "");
string value7 = ((b3.HasValue && b5 >= b3) ? "✓" : "○");
string value8 = (complexCombatData2.NameId.HasValue ? $" (DataId: {complexCombatData2.DataId}, NameId: {complexCombatData2.NameId})" : $" (DataId: {complexCombatData2.DataId})");
_chatGui.Print($" {value7} Slay {value5}.{value6}{value8}", "Questionable", 576);
}
}
_chatGui.Print("", "Questionable", 576);
}
else if (questStep.KillEnemyDataIds.Count == 0)
{
_chatGui.Print("No kill enemy data for this step.", "Questionable", 576);
_chatGui.Print("", "Questionable", 576);
}
if (questStep.CompletionQuestVariablesFlags.Count <= 0 || !questStep.CompletionQuestVariablesFlags.Any((QuestWorkValue x) => x != null))
{
return;
}
_chatGui.Print("Completion Flags (Debug):", "Questionable", 576);
for (int num6 = 0; num6 < questStep.CompletionQuestVariablesFlags.Count; num6++)
{
QuestWorkValue questWorkValue3 = questStep.CompletionQuestVariablesFlags[num6];
if (questWorkValue3 != null)
{
int num7 = ((num6 < questProgressInfo.Variables.Count) ? questProgressInfo.Variables[num6] : 0);
byte b6 = (byte)(num7 >> 4);
byte b7 = (byte)(num7 & 0xF);
string value9 = (((!questWorkValue3.High.HasValue || questWorkValue3.High == b6) && (!questWorkValue3.Low.HasValue || questWorkValue3.Low == b7)) ? " ✓" : " ✗");
_chatGui.Print($" [{num6}] Expected: H={questWorkValue3.High?.ToString(CultureInfo.InvariantCulture) ?? "any"} L={questWorkValue3.Low?.ToString(CultureInfo.InvariantCulture) ?? "any"} | Actual: H={b6.ToString(CultureInfo.InvariantCulture)} L={b7.ToString(CultureInfo.InvariantCulture)}{value9}", "Questionable", 576);
}
}
(string Name, bool Found) GetEnemyName(uint dataId)
{
if (_objectTable.FirstOrDefault((IGameObject x) => x is IBattleNpc battleNpc2 && battleNpc2.BaseId == dataId) is IBattleNpc { NameId: not 0u } battleNpc)
{
BNpcName? bNpcName2 = bnpcNameSheet?.GetRowOrDefault(battleNpc.NameId);
if (bNpcName2.HasValue && !string.IsNullOrEmpty(bNpcName2.Value.Singular.ToString()))
{
return (Name: bNpcName2.Value.Singular.ToString(), Found: true);
}
}
return (Name: string.Empty, Found: false);
}
}
}

View file

@ -0,0 +1,20 @@
using Questionable.Windows;
namespace Questionable.Controller.DebugCommands;
internal sealed class SequencesCommandHandler : IDebugCommandHandler
{
private readonly QuestSequenceWindow _questSequenceWindow;
public string CommandName => "seq";
public SequencesCommandHandler(QuestSequenceWindow questSequenceWindow)
{
_questSequenceWindow = questSequenceWindow;
}
public void Execute(string[] arguments)
{
_questSequenceWindow.ToggleOrUncollapse();
}
}

View file

@ -0,0 +1,20 @@
using Questionable.Windows;
namespace Questionable.Controller.DebugCommands;
internal sealed class SetupCommandHandler : IDebugCommandHandler
{
private readonly OneTimeSetupWindow _oneTimeSetupWindow;
public string CommandName => "setup";
public SetupCommandHandler(OneTimeSetupWindow oneTimeSetupWindow)
{
_oneTimeSetupWindow = oneTimeSetupWindow;
}
public void Execute(string[] arguments)
{
_oneTimeSetupWindow.IsOpenAndUncollapsed = true;
}
}

View file

@ -0,0 +1,58 @@
using Dalamud.Plugin.Services;
using Questionable.Model;
using Questionable.Model.Questing;
namespace Questionable.Controller.DebugCommands;
internal sealed class SimulateQuestCommandHandler : IDebugCommandHandler
{
private readonly QuestController _questController;
private readonly QuestRegistry _questRegistry;
private readonly IChatGui _chatGui;
public string CommandName => "sim";
public SimulateQuestCommandHandler(QuestController questController, QuestRegistry questRegistry, IChatGui chatGui)
{
_questController = questController;
_questRegistry = questRegistry;
_chatGui = chatGui;
}
public void Execute(string[] arguments)
{
if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out ElementId elementId) && elementId != null)
{
if (_questRegistry.TryGetQuest(elementId, out Quest quest))
{
byte sequence = 0;
int step = 0;
if (arguments.Length >= 2 && byte.TryParse(arguments[1], out var result))
{
QuestSequence questSequence = quest.FindSequence(result);
if (questSequence != null)
{
sequence = questSequence.Sequence;
if (arguments.Length >= 3 && int.TryParse(arguments[2], out var result2) && questSequence.FindStep(result2) != null)
{
step = result2;
}
}
}
_questController.SimulateQuest(quest, sequence, step);
_chatGui.Print($"Simulating quest {elementId} ({quest.Info.Name}).", "Questionable", 576);
}
else
{
_chatGui.PrintError($"Unknown quest {elementId}.", "Questionable", 576);
}
}
else
{
_questController.SimulateQuest(null, 0, 0);
_chatGui.Print("Cleared simulated quest.", "Questionable", 576);
}
}
}

View file

@ -0,0 +1,66 @@
using System.Collections.Generic;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using Lumina.Excel;
using Lumina.Excel.Sheets;
namespace Questionable.Controller.DebugCommands;
internal sealed class TaxiCommandHandler : IDebugCommandHandler
{
private readonly IDataManager _dataManager;
private readonly IClientState _clientState;
private readonly IChatGui _chatGui;
public string CommandName => "taxi";
public TaxiCommandHandler(IDataManager dataManager, IClientState clientState, IChatGui chatGui)
{
_dataManager = dataManager;
_clientState = clientState;
_chatGui = chatGui;
}
public unsafe void Execute(string[] arguments)
{
List<string> list = new List<string>();
ExcelSheet<ChocoboTaxiStand> excelSheet = _dataManager.GetExcelSheet<ChocoboTaxiStand>();
UIState* ptr = UIState.Instance();
if (ptr == null)
{
_chatGui.PrintError("UIState is null", "Questionable", 576);
return;
}
for (int i = 0; i < 192; i++)
{
uint num = (uint)(i + 1179648);
try
{
if (excelSheet.HasRow(num) && ptr->IsChocoboTaxiStandUnlocked(num))
{
string value = excelSheet.GetRow(num).PlaceName.ToString();
if (string.IsNullOrEmpty(value))
{
value = "Unknown";
}
list.Add($"{value} (ID: {i}, Row: 0x{num:X})");
}
}
catch
{
}
}
_chatGui.Print($"Unlocked taxi stands ({list.Count}):", "Questionable", 576);
if (list.Count == 0)
{
_chatGui.Print(" (No unlocked taxi stands found)", "Questionable", 576);
return;
}
foreach (string item in list)
{
_chatGui.Print(" - " + item, "Questionable", 576);
}
}
}

View file

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Plugin.Services;
using Questionable.Functions;
namespace Questionable.Controller.DebugCommands;
internal sealed class UnlockLinksCommandHandler : IDebugCommandHandler
{
private readonly GameFunctions _gameFunctions;
private readonly IChatGui _chatGui;
private IReadOnlyList<uint> _previouslyUnlockedUnlockLinks = Array.Empty<uint>();
public string CommandName => "unlock-links";
public UnlockLinksCommandHandler(GameFunctions gameFunctions, IChatGui chatGui)
{
_gameFunctions = gameFunctions;
_chatGui = chatGui;
}
public void Execute(string[] arguments)
{
IReadOnlyList<uint> unlockLinks = _gameFunctions.GetUnlockLinks();
if (unlockLinks.Count >= 0)
{
_chatGui.Print($"Saved {unlockLinks.Count} unlock links to log.", "Questionable", 576);
List<uint> list = unlockLinks.Except(_previouslyUnlockedUnlockLinks).ToList();
if (_previouslyUnlockedUnlockLinks.Count > 0 && list.Count > 0)
{
_chatGui.Print("New unlock links: " + string.Join(", ", list), "Questionable", 576);
}
}
else
{
_chatGui.PrintError("Could not query unlock links.", "Questionable", 576);
}
_previouslyUnlockedUnlockLinks = unlockLinks;
}
public void Reset()
{
_previouslyUnlockedUnlockLinks = Array.Empty<uint>();
}
}

View file

@ -58,6 +58,8 @@ internal sealed class InteractionUiController : IDisposable
private readonly IClientState _clientState;
private readonly IObjectTable _objectTable;
private readonly ShopController _shopController;
private readonly BossModIpc _bossModIpc;
@ -84,7 +86,7 @@ internal sealed class InteractionUiController : IDisposable
}
}
public unsafe InteractionUiController(IAddonLifecycle addonLifecycle, IDataManager dataManager, QuestFunctions questFunctions, AetheryteFunctions aetheryteFunctions, ExcelFunctions excelFunctions, QuestController questController, GatheringPointRegistry gatheringPointRegistry, QuestRegistry questRegistry, QuestData questData, TerritoryData territoryData, IGameGui gameGui, ITargetManager targetManager, IPluginLog pluginLog, IClientState clientState, ShopController shopController, BossModIpc bossModIpc, Configuration configuration, ILogger<InteractionUiController> logger)
public unsafe InteractionUiController(IAddonLifecycle addonLifecycle, IDataManager dataManager, QuestFunctions questFunctions, AetheryteFunctions aetheryteFunctions, ExcelFunctions excelFunctions, QuestController questController, GatheringPointRegistry gatheringPointRegistry, QuestRegistry questRegistry, QuestData questData, TerritoryData territoryData, IGameGui gameGui, ITargetManager targetManager, IPluginLog pluginLog, IClientState clientState, IObjectTable objectTable, ShopController shopController, BossModIpc bossModIpc, Configuration configuration, ILogger<InteractionUiController> logger)
{
_addonLifecycle = addonLifecycle;
_dataManager = dataManager;
@ -99,6 +101,7 @@ internal sealed class InteractionUiController : IDisposable
_gameGui = gameGui;
_targetManager = targetManager;
_clientState = clientState;
_objectTable = objectTable;
_shopController = shopController;
_bossModIpc = bossModIpc;
_configuration = configuration;
@ -857,7 +860,10 @@ internal sealed class InteractionUiController : IDisposable
{
_logger.LogTrace("FindTargetTerritoryFromQuestStep (current): {CurrentTerritory}, {TargetTerritory}", questStep.TerritoryId, questStep.TargetTerritoryId);
}
if (questStep != null && (questStep.TerritoryId != _clientState.TerritoryType || !questStep.TargetTerritoryId.HasValue) && questStep.InteractionType == EInteractionType.Gather && _gatheringPointRegistry.TryGetGatheringPointId(questStep.ItemsToGather[0].ItemId, ((EClassJob?)_clientState.LocalPlayer?.ClassJob.RowId).GetValueOrDefault(), out GatheringPointId gatheringPointId) && _gatheringPointRegistry.TryGetGatheringPoint(gatheringPointId, out GatheringRoot gatheringRoot))
if (questStep != null && (questStep.TerritoryId != _clientState.TerritoryType || !questStep.TargetTerritoryId.HasValue) && questStep.InteractionType == EInteractionType.Gather)
{
IGameObject gameObject = _objectTable[0];
if (_gatheringPointRegistry.TryGetGatheringPointId(questStep.ItemsToGather[0].ItemId, ((EClassJob?)(gameObject as ICharacter)?.ClassJob.RowId).GetValueOrDefault(), out GatheringPointId gatheringPointId) && _gatheringPointRegistry.TryGetGatheringPoint(gatheringPointId, out GatheringRoot gatheringRoot))
{
foreach (QuestStep step in gatheringRoot.Steps)
{
@ -868,6 +874,7 @@ internal sealed class InteractionUiController : IDisposable
}
}
}
}
if (questStep == null || !questStep.TargetTerritoryId.HasValue)
{
_logger.LogTrace("FindTargetTerritoryFromQuestStep: Checking previous step...");

View file

@ -50,7 +50,7 @@ internal static class Mount
}
}
internal sealed class MountEvaluator(GameFunctions gameFunctions, ICondition condition, TerritoryData territoryData, IClientState clientState, ILogger<MountEvaluator> logger)
internal sealed class MountEvaluator(GameFunctions gameFunctions, ICondition condition, TerritoryData territoryData, IClientState clientState, IObjectTable objectTable, ILogger<MountEvaluator> logger)
{
public unsafe MountResult EvaluateMountState(MountTask task, bool dryRun, ref DateTime retryAt)
{
@ -71,7 +71,7 @@ internal static class Mount
}
if (task.MountIf == EMountIf.AwayFromPosition)
{
float num = System.Numerics.Vector3.Distance((FFXIVClientStructs.FFXIV.Common.Math.Vector3)(clientState.LocalPlayer?.Position ?? ((System.Numerics.Vector3)FFXIVClientStructs.FFXIV.Common.Math.Vector3.Zero)), task.Position.GetValueOrDefault());
float num = System.Numerics.Vector3.Distance((FFXIVClientStructs.FFXIV.Common.Math.Vector3)(objectTable[0]?.Position ?? ((System.Numerics.Vector3)FFXIVClientStructs.FFXIV.Common.Math.Vector3.Zero)), task.Position.GetValueOrDefault());
if (task.TerritoryId == clientState.TerritoryType && num < 30f && !Conditions.Instance()->Diving)
{
logger.Log(logLevel, "Not using mount, as we're close to the target");
@ -159,7 +159,7 @@ internal static class Mount
}
}
internal sealed class UnmountExecutor(ICondition condition, ILogger<UnmountTask> logger, GameFunctions gameFunctions, IClientState clientState) : TaskExecutor<UnmountTask>()
internal sealed class UnmountExecutor(ICondition condition, ILogger<UnmountTask> logger, GameFunctions gameFunctions, IObjectTable objectTable) : TaskExecutor<UnmountTask>()
{
private bool _unmountTriggered;
@ -221,10 +221,9 @@ internal static class Mount
private unsafe bool IsUnmounting()
{
IPlayerCharacter localPlayer = clientState.LocalPlayer;
if (localPlayer != null)
if (objectTable[0] is IPlayerCharacter playerCharacter)
{
BattleChara* address = (BattleChara*)localPlayer.Address;
BattleChara* address = (BattleChara*)playerCharacter.Address;
return (address->Mount.Flags & 1) == 1;
}
return false;

View file

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI;
@ -30,7 +31,7 @@ internal static class DoGather
}
}
internal sealed class GatherExecutor(GatheringController gatheringController, GameFunctions gameFunctions, IGameGui gameGui, IClientState clientState, ICondition condition, ILogger<GatherExecutor> logger) : TaskExecutor<Task>()
internal sealed class GatherExecutor(GatheringController gatheringController, GameFunctions gameFunctions, IGameGui gameGui, IObjectTable objectTable, ICondition condition, ILogger<GatherExecutor> logger) : TaskExecutor<Task>()
{
private bool _wasGathering;
@ -268,7 +269,7 @@ internal static class DoGather
private EAction PickAction(EAction minerAction, EAction botanistAction)
{
if (clientState.LocalPlayer?.ClassJob.RowId == 16)
if ((objectTable[0] as ICharacter)?.ClassJob.RowId == 16)
{
return minerAction;
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Text;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
@ -27,7 +28,7 @@ internal static class DoGatherCollectable
}
}
internal sealed class GatherCollectableExecutor(GatheringController gatheringController, GameFunctions gameFunctions, IClientState clientState, IGameGui gameGui, ILogger<GatherCollectableExecutor> logger) : TaskExecutor<Task>()
internal sealed class GatherCollectableExecutor(GatheringController gatheringController, GameFunctions gameFunctions, IObjectTable objectTable, IGameGui gameGui, ILogger<GatherCollectableExecutor> logger) : TaskExecutor<Task>()
{
private Queue<EAction>? _actionQueue;
@ -137,42 +138,42 @@ internal static class DoGatherCollectable
private Queue<EAction> GetNextActions(NodeCondition nodeCondition)
{
uint currentGp = clientState.LocalPlayer.CurrentGp;
logger.LogTrace("Getting next actions (with {GP} GP, {MeticulousCollectability}~ meticulous, {ScourCollectability}~ scour)", currentGp, nodeCondition.CollectabilityFromMeticulous, nodeCondition.CollectabilityFromScour);
uint num = (objectTable[0] as ICharacter)?.CurrentGp ?? 0;
logger.LogTrace("Getting next actions (with {GP} GP, {MeticulousCollectability}~ meticulous, {ScourCollectability}~ scour)", num, nodeCondition.CollectabilityFromMeticulous, nodeCondition.CollectabilityFromScour);
Queue<EAction> queue = new Queue<EAction>();
uint num = nodeCondition.CollectabilityToGoal(base.Task.Request.Collectability);
if (num <= nodeCondition.CollectabilityFromMeticulous)
uint num2 = nodeCondition.CollectabilityToGoal(base.Task.Request.Collectability);
if (num2 <= nodeCondition.CollectabilityFromMeticulous)
{
logger.LogTrace("Can get all needed {NeededCollectability} from {Collectability}~ meticulous", num, nodeCondition.CollectabilityFromMeticulous);
logger.LogTrace("Can get all needed {NeededCollectability} from {Collectability}~ meticulous", num2, nodeCondition.CollectabilityFromMeticulous);
queue.Enqueue(PickAction(EAction.MeticulousMiner, EAction.MeticulousBotanist));
return queue;
}
if (num <= nodeCondition.CollectabilityFromScour)
if (num2 <= nodeCondition.CollectabilityFromScour)
{
logger.LogTrace("Can get all needed {NeededCollectability} from {Collectability}~ scour", num, nodeCondition.CollectabilityFromScour);
logger.LogTrace("Can get all needed {NeededCollectability} from {Collectability}~ scour", num2, nodeCondition.CollectabilityFromScour);
queue.Enqueue(PickAction(EAction.ScourMiner, EAction.ScourBotanist));
return queue;
}
if (!nodeCondition.ScrutinyActive && currentGp >= 200)
if (!nodeCondition.ScrutinyActive && num >= 200)
{
logger.LogTrace("Still missing {NeededCollectability} collectability, scrutiny inactive", num);
logger.LogTrace("Still missing {NeededCollectability} collectability, scrutiny inactive", num2);
queue.Enqueue(PickAction(EAction.ScrutinyMiner, EAction.ScrutinyBotanist));
return queue;
}
if (nodeCondition.ScrutinyActive)
{
logger.LogTrace("Scrutiny active, need {NeededCollectability} and we expect {Collectability}~ meticulous", num, nodeCondition.CollectabilityFromMeticulous);
logger.LogTrace("Scrutiny active, need {NeededCollectability} and we expect {Collectability}~ meticulous", num2, nodeCondition.CollectabilityFromMeticulous);
queue.Enqueue(PickAction(EAction.MeticulousMiner, EAction.MeticulousBotanist));
return queue;
}
logger.LogTrace("Scrutiny active, need {NeededCollectability} and we expect {Collectability}~ scour", num, nodeCondition.CollectabilityFromScour);
logger.LogTrace("Scrutiny active, need {NeededCollectability} and we expect {Collectability}~ scour", num2, nodeCondition.CollectabilityFromScour);
queue.Enqueue(PickAction(EAction.ScourMiner, EAction.ScourBotanist));
return queue;
}
private EAction PickAction(EAction minerAction, EAction botanistAction)
{
if (clientState.LocalPlayer?.ClassJob.RowId == 16)
if ((objectTable[0] as ICharacter)?.ClassJob.RowId == 16)
{
return minerAction;
}

View file

@ -1,5 +1,6 @@
using System;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
@ -43,7 +44,7 @@ internal static class EquipRecommended
}
}
internal sealed class DoEquipRecommended(IClientState clientState, IChatGui chatGui, ICondition condition) : TaskExecutor<EquipTask>()
internal sealed class DoEquipRecommended(IObjectTable objectTable, IChatGui chatGui, ICondition condition) : TaskExecutor<EquipTask>()
{
private bool _checkedOrTriggeredEquipmentUpdate;
@ -55,7 +56,11 @@ internal static class EquipRecommended
{
return false;
}
RecommendEquipModule.Instance()->SetupForClassJob((byte)clientState.LocalPlayer.ClassJob.RowId);
if (!(objectTable[0] is ICharacter character))
{
return false;
}
RecommendEquipModule.Instance()->SetupForClassJob((byte)character.ClassJob.RowId);
return true;
}

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using Microsoft.Extensions.Logging;
@ -47,12 +48,17 @@ internal static class Jump
}
}
internal abstract class JumpBase<T>(MovementController movementController, IClientState clientState, IFramework framework) : TaskExecutor<T>() where T : class, IJumpTask
internal abstract class JumpBase<T>(MovementController movementController, IObjectTable objectTable, IFramework framework) : TaskExecutor<T>() where T : class, IJumpTask
{
protected unsafe override bool Start()
{
IGameObject gameObject = _003CobjectTable_003EP[0];
if (gameObject == null)
{
return false;
}
float num = base.Task.JumpDestination.CalculateStopDistance();
if ((_003CclientState_003EP.LocalPlayer.Position - base.Task.JumpDestination.Position).Length() <= num)
if ((gameObject.Position - base.Task.JumpDestination.Position).Length() <= num)
{
return false;
}
@ -94,8 +100,8 @@ internal static class Jump
internal sealed class DoSingleJump : JumpBase<SingleJumpTask>
{
public DoSingleJump(MovementController movementController, IClientState clientState, IFramework framework)
: base(movementController, clientState, framework)
public DoSingleJump(MovementController movementController, IObjectTable objectTable, IFramework framework)
: base(movementController, objectTable, framework)
{
}
}
@ -110,19 +116,19 @@ internal static class Jump
internal sealed class DoRepeatedJumps : JumpBase<RepeatedJumpTask>
{
private readonly IClientState _clientState;
private readonly IObjectTable _objectTable;
private DateTime _continueAt;
private int _attempts;
public DoRepeatedJumps(MovementController movementController, IClientState clientState, IFramework framework, ICondition condition, ILogger<DoRepeatedJumps> logger)
public DoRepeatedJumps(MovementController movementController, IObjectTable objectTable, IFramework framework, ICondition condition, ILogger<DoRepeatedJumps> logger)
{
_003Ccondition_003EP = condition;
_003Clogger_003EP = logger;
_clientState = clientState;
_objectTable = objectTable;
_continueAt = DateTime.MinValue;
base._002Ector(movementController, clientState, framework);
base._002Ector(movementController, objectTable, framework);
}
protected override bool Start()
@ -137,12 +143,17 @@ internal static class Jump
{
return ETaskResult.StillRunning;
}
float num = base.Task.JumpDestination.CalculateStopDistance();
if ((_clientState.LocalPlayer.Position - base.Task.JumpDestination.Position).Length() <= num || _clientState.LocalPlayer.Position.Y >= base.Task.JumpDestination.Position.Y - 0.5f)
IGameObject gameObject = _objectTable[0];
if (gameObject == null)
{
return ETaskResult.TaskComplete;
}
_003Clogger_003EP.LogTrace("Y-Heights for jumps: player={A}, target={B}", _clientState.LocalPlayer.Position.Y, base.Task.JumpDestination.Position.Y - 0.5f);
float num = base.Task.JumpDestination.CalculateStopDistance();
if ((gameObject.Position - base.Task.JumpDestination.Position).Length() <= num || gameObject.Position.Y >= base.Task.JumpDestination.Position.Y - 0.5f)
{
return ETaskResult.TaskComplete;
}
_003Clogger_003EP.LogTrace("Y-Heights for jumps: player={A}, target={B}", gameObject.Position.Y, base.Task.JumpDestination.Position.Y - 0.5f);
if (ActionManager.Instance()->UseAction(ActionType.GeneralAction, 2u, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null))
{
_attempts++;

View file

@ -29,7 +29,7 @@ internal static class SinglePlayerDuty
public const ushort Naadam = 688;
}
internal sealed class Factory(BossModIpc bossModIpc, TerritoryData territoryData, ICondition condition, IClientState clientState) : ITaskFactory
internal sealed class Factory(BossModIpc bossModIpc, TerritoryData territoryData, ICondition condition, IClientState clientState, IObjectTable objectTable) : ITaskFactory
{
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{
@ -70,7 +70,7 @@ internal static class SinglePlayerDuty
{
return true;
}
Vector3 vector = clientState.LocalPlayer?.Position ?? default(Vector3);
Vector3 vector = objectTable[0]?.Position ?? default(Vector3);
return (new Vector3(352.01f, -1.45f, 288.59f) - vector).Length() < 10f;
}, "Wait(moving to Ovoo)");
yield return new Mount.UnmountTask();

View file

@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging;
namespace Questionable.Controller.Steps.Movement;
internal sealed class LandExecutor(IClientState clientState, ICondition condition, ILogger<LandExecutor> logger) : TaskExecutor<LandTask>()
internal sealed class LandExecutor(IObjectTable objectTable, ICondition condition, ILogger<LandExecutor> logger) : TaskExecutor<LandTask>()
{
private bool _landing;
@ -45,7 +45,7 @@ internal sealed class LandExecutor(IClientState clientState, ICondition conditio
private unsafe bool AttemptLanding()
{
Character* ptr = (Character*)(clientState.LocalPlayer?.Address ?? 0);
Character* ptr = (Character*)(objectTable[0]?.Address ?? 0);
if (ptr != null && ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 23u, 3758096384uL, checkRecastActive: true, checkCastingActive: true, null) == 0)
{
logger.LogInformation("Attempting to land");

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using LLib;
@ -28,6 +29,8 @@ internal sealed class MoveExecutor : TaskExecutor<MoveTask>, IToastAware, ITaskE
private readonly IClientState _clientState;
private readonly IObjectTable _objectTable;
private readonly ICondition _condition;
private readonly Questionable.Controller.Steps.Common.Mount.MountEvaluator _mountEvaluator;
@ -46,12 +49,13 @@ internal sealed class MoveExecutor : TaskExecutor<MoveTask>, IToastAware, ITaskE
private (Questionable.Controller.Steps.Common.Mount.MountExecutor Executor, Questionable.Controller.Steps.Common.Mount.MountTask Task)? _mountDuringMovement;
public MoveExecutor(MovementController movementController, GameFunctions gameFunctions, ILogger<MoveExecutor> logger, IClientState clientState, ICondition condition, IDataManager dataManager, Questionable.Controller.Steps.Common.Mount.MountEvaluator mountEvaluator, IServiceProvider serviceProvider)
public MoveExecutor(MovementController movementController, GameFunctions gameFunctions, ILogger<MoveExecutor> logger, IClientState clientState, IObjectTable objectTable, ICondition condition, IDataManager dataManager, Questionable.Controller.Steps.Common.Mount.MountEvaluator mountEvaluator, IServiceProvider serviceProvider)
{
_movementController = movementController;
_gameFunctions = gameFunctions;
_logger = logger;
_clientState = clientState;
_objectTable = objectTable;
_condition = condition;
_serviceProvider = serviceProvider;
_mountEvaluator = mountEvaluator;
@ -95,7 +99,7 @@ internal sealed class MoveExecutor : TaskExecutor<MoveTask>, IToastAware, ITaskE
_canRestart = base.Task.RestartNavigation;
_destination = base.Task.Destination;
float num = base.Task.StopDistance ?? 3f;
Vector3? vector = _clientState.LocalPlayer?.Position;
Vector3? vector = _objectTable[0]?.Position;
float num2 = ((!vector.HasValue) ? float.MaxValue : Vector3.Distance(vector.Value, _destination));
if (num2 > num)
{
@ -170,7 +174,8 @@ internal sealed class MoveExecutor : TaskExecutor<MoveTask>, IToastAware, ITaskE
{
return ETaskResult.StillRunning;
}
if (_canRestart && Vector3.Distance(_clientState.LocalPlayer.Position, _destination) > (base.Task.StopDistance ?? 3f) + 5f)
IGameObject gameObject = _objectTable[0];
if (_canRestart && gameObject != null && Vector3.Distance(gameObject.Position, _destination) > (base.Task.StopDistance ?? 3f) + 5f)
{
_canRestart = false;
if (_clientState.TerritoryType == base.Task.TerritoryId)

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
@ -13,7 +14,7 @@ namespace Questionable.Controller.Steps.Movement;
internal static class MoveTo
{
internal sealed class Factory(IClientState clientState, AetheryteData aetheryteData, TerritoryData territoryData, ILogger<Factory> logger) : ITaskFactory
internal sealed class Factory(IClientState clientState, IObjectTable objectTable, AetheryteData aetheryteData, TerritoryData territoryData, ILogger<Factory> logger) : ITaskFactory
{
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{
@ -62,7 +63,8 @@ internal static class MoveTo
private IEnumerable<ITask> CreateMoveTasks(QuestStep step, Vector3 destination)
{
if (step.InteractionType == EInteractionType.Jump && step.JumpDestination != null && (clientState.LocalPlayer.Position - step.JumpDestination.Position).Length() <= (step.JumpDestination.StopDistance ?? 1f))
IGameObject gameObject = objectTable[0];
if (step.InteractionType == EInteractionType.Jump && step.JumpDestination != null && gameObject != null && (gameObject.Position - step.JumpDestination.Position).Length() <= (step.JumpDestination.StopDistance ?? 1f))
{
logger.LogInformation("We're at the jump destination, skipping movement");
yield break;

View file

@ -4,7 +4,7 @@ using Questionable.Functions;
namespace Questionable.Controller.Steps.Movement;
internal sealed class WaitForNearDataIdExecutor(GameFunctions gameFunctions, IClientState clientState) : TaskExecutor<WaitForNearDataId>()
internal sealed class WaitForNearDataIdExecutor(GameFunctions gameFunctions, IObjectTable objectTable) : TaskExecutor<WaitForNearDataId>()
{
protected override bool Start()
{
@ -14,7 +14,8 @@ internal sealed class WaitForNearDataIdExecutor(GameFunctions gameFunctions, ICl
public override ETaskResult Update()
{
IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.DataId);
if (gameObject == null || (gameObject.Position - clientState.LocalPlayer.Position).Length() > base.Task.StopDistance)
IGameObject gameObject2 = objectTable[0];
if (gameObject == null || gameObject2 == null || (gameObject.Position - gameObject2.Position).Length() > base.Task.StopDistance)
{
throw new TaskException("Object not found or too far away, no position so we can't move");
}

View file

@ -4,6 +4,7 @@ using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
@ -50,7 +51,7 @@ internal static class AethernetShortcut
}
}
internal sealed class UseAethernetShortcut(ILogger<UseAethernetShortcut> logger, AetheryteFunctions aetheryteFunctions, GameFunctions gameFunctions, QuestFunctions questFunctions, IClientState clientState, AetheryteData aetheryteData, TerritoryData territoryData, LifestreamIpc lifestreamIpc, MovementController movementController, ICondition condition) : TaskExecutor<Task>()
internal sealed class UseAethernetShortcut(ILogger<UseAethernetShortcut> logger, AetheryteFunctions aetheryteFunctions, GameFunctions gameFunctions, QuestFunctions questFunctions, IClientState clientState, IObjectTable objectTable, AetheryteData aetheryteData, TerritoryData territoryData, LifestreamIpc lifestreamIpc, MovementController movementController, ICondition condition) : TaskExecutor<Task>()
{
private bool _moving;
@ -98,7 +99,12 @@ internal static class AethernetShortcut
if (aetheryteFunctions.IsAetheryteUnlocked(base.Task.From) && aetheryteFunctions.IsAetheryteUnlocked(base.Task.To))
{
ushort territoryType = clientState.TerritoryType;
Vector3 playerPosition = clientState.LocalPlayer.Position;
IGameObject gameObject = objectTable[0];
if (gameObject == null)
{
return false;
}
Vector3 playerPosition = gameObject.Position;
if (aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.From) < aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.To))
{
if (aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.From) < (base.Task.From.IsFirmamentAetheryte() ? 11f : 4f))
@ -202,7 +208,7 @@ internal static class AethernetShortcut
DoTeleport();
return ETaskResult.StillRunning;
}
Vector3? vector = clientState.LocalPlayer?.Position;
Vector3? vector = objectTable[0]?.Position;
if (!vector.HasValue)
{
return ETaskResult.StillRunning;

View file

@ -4,6 +4,7 @@ using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Common;
@ -45,7 +46,7 @@ internal static class AetheryteShortcut
}
}
internal sealed class UseAetheryteShortcut(ILogger<UseAetheryteShortcut> logger, AetheryteFunctions aetheryteFunctions, QuestFunctions questFunctions, IClientState clientState, IChatGui chatGui, ICondition condition, AetheryteData aetheryteData, ExtraConditionUtils extraConditionUtils) : TaskExecutor<Task>()
internal sealed class UseAetheryteShortcut(ILogger<UseAetheryteShortcut> logger, AetheryteFunctions aetheryteFunctions, QuestFunctions questFunctions, IClientState clientState, IObjectTable objectTable, IChatGui chatGui, ICondition condition, AetheryteData aetheryteData, ExtraConditionUtils extraConditionUtils) : TaskExecutor<Task>()
{
private bool _teleported;
@ -116,14 +117,15 @@ internal static class AetheryteShortcut
return true;
}
}
IGameObject gameObject = objectTable[0];
NearPositionCondition nearPosition = skipAetheryteCondition.NearPosition;
if (nearPosition != null && clientState.TerritoryType == nearPosition.TerritoryId && Vector3.Distance(nearPosition.Position, clientState.LocalPlayer.Position) <= nearPosition.MaximumDistance)
if (nearPosition != null && clientState.TerritoryType == nearPosition.TerritoryId && gameObject != null && Vector3.Distance(nearPosition.Position, gameObject.Position) <= nearPosition.MaximumDistance)
{
logger.LogInformation("Skipping aetheryte shortcut, as we're near the position");
return true;
}
NearPositionCondition notNearPosition = skipAetheryteCondition.NotNearPosition;
if (notNearPosition != null && clientState.TerritoryType == notNearPosition.TerritoryId && notNearPosition.MaximumDistance <= Vector3.Distance(notNearPosition.Position, clientState.LocalPlayer.Position))
if (notNearPosition != null && clientState.TerritoryType == notNearPosition.TerritoryId && gameObject != null && notNearPosition.MaximumDistance <= Vector3.Distance(notNearPosition.Position, gameObject.Position))
{
logger.LogInformation("Skipping aetheryte shortcut, as we're not near the position");
return true;
@ -134,14 +136,17 @@ internal static class AetheryteShortcut
return true;
}
}
if (base.Task.ExpectedTerritoryId == territoryType && !skipAetheryteCondition.Never)
if (base.Task.ExpectedTerritoryId == territoryType)
{
IGameObject gameObject2 = objectTable[0];
if (gameObject2 != null && !skipAetheryteCondition.Never)
{
if (skipAetheryteCondition != null && skipAetheryteCondition.InSameTerritory)
{
logger.LogInformation("Skipping aetheryte teleport due to SkipCondition (InSameTerritory)");
return true;
}
Vector3 position = clientState.LocalPlayer.Position;
Vector3 position = gameObject2.Position;
if (base.Task.Step.Position.HasValue && (position - base.Task.Step.Position.Value).Length() < base.Task.Step.CalculateActualStopDistance())
{
logger.LogInformation("Skipping aetheryte teleport, we're near the target");
@ -154,6 +159,7 @@ internal static class AetheryteShortcut
}
}
}
}
return false;
}
@ -208,7 +214,7 @@ internal static class AetheryteShortcut
}
}
internal sealed class MoveAwayFromAetheryteExecutor(MoveExecutor moveExecutor, AetheryteData aetheryteData, IClientState clientState) : TaskExecutor<MoveAwayFromAetheryte>()
internal sealed class MoveAwayFromAetheryteExecutor(MoveExecutor moveExecutor, AetheryteData aetheryteData, IClientState clientState, IObjectTable objectTable) : TaskExecutor<MoveAwayFromAetheryte>()
{
private static readonly Dictionary<EAetheryteLocation, List<Vector3>> AetherytesToMoveFrom;
@ -219,7 +225,12 @@ internal static class AetheryteShortcut
protected override bool Start()
{
Vector3 playerPosition = clientState.LocalPlayer.Position;
IGameObject gameObject = objectTable[0];
if (gameObject == null)
{
return false;
}
Vector3 playerPosition = gameObject.Position;
if (aetheryteData.CalculateDistance(playerPosition, clientState.TerritoryType, base.Task.TargetAetheryte) >= 20f)
{
return false;

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
@ -43,7 +44,7 @@ internal static class Craft
}
}
internal sealed class DoCraft(IDataManager dataManager, IClientState clientState, ArtisanIpc artisanIpc, ILogger<DoCraft> logger) : TaskExecutor<CraftTask>()
internal sealed class DoCraft(IDataManager dataManager, IObjectTable objectTable, ArtisanIpc artisanIpc, ILogger<DoCraft> logger) : TaskExecutor<CraftTask>()
{
protected override bool Start()
{
@ -57,7 +58,7 @@ internal static class Craft
{
throw new TaskException($"Item {base.Task.ItemId} is not craftable");
}
uint num = (EClassJob)clientState.LocalPlayer.ClassJob.RowId switch
uint num = (EClassJob?)(objectTable[0] as ICharacter)?.ClassJob.RowId switch
{
EClassJob.Carpenter => rowOrDefault.Value.CRP.RowId,
EClassJob.Blacksmith => rowOrDefault.Value.BSM.RowId,

View file

@ -9,14 +9,17 @@ internal sealed class ExtraConditionUtils
{
private readonly IClientState _clientState;
public ExtraConditionUtils(IClientState clientState)
private readonly IObjectTable _objectTable;
public ExtraConditionUtils(IClientState clientState, IObjectTable objectTable)
{
_clientState = clientState;
_objectTable = objectTable;
}
public bool MatchesExtraCondition(EExtraSkipCondition skipCondition)
{
Vector3? vector = _clientState.LocalPlayer?.Position;
Vector3? vector = _objectTable[0]?.Position;
if (vector.HasValue && _clientState.TerritoryType != 0)
{
return MatchesExtraCondition(skipCondition, vector.Value, _clientState.TerritoryType);

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
@ -42,7 +43,7 @@ internal static class Gather
}
}
internal sealed class DelayedGatheringExecutor(GatheringPointRegistry gatheringPointRegistry, TerritoryData territoryData, IClientState clientState, IServiceProvider serviceProvider, ILogger<DelayedGatheringExecutor> logger) : TaskExecutor<DelayedGatheringTask>(), IExtraTaskCreator, ITaskExecutor
internal sealed class DelayedGatheringExecutor(GatheringPointRegistry gatheringPointRegistry, TerritoryData territoryData, IClientState clientState, IObjectTable objectTable, IServiceProvider serviceProvider, ILogger<DelayedGatheringExecutor> logger) : TaskExecutor<DelayedGatheringTask>(), IExtraTaskCreator, ITaskExecutor
{
protected override bool Start()
{
@ -56,8 +57,8 @@ internal static class Gather
public IEnumerable<ITask> CreateExtraTasks()
{
EClassJob rowId = (EClassJob)clientState.LocalPlayer.ClassJob.RowId;
if (!gatheringPointRegistry.TryGetGatheringPointId(base.Task.GatheredItem.ItemId, rowId, out GatheringPointId gatheringPointId))
EClassJob valueOrDefault = ((EClassJob?)(objectTable[0] as ICharacter)?.ClassJob.RowId).GetValueOrDefault();
if (!gatheringPointRegistry.TryGetGatheringPointId(base.Task.GatheredItem.ItemId, valueOrDefault, out GatheringPointId gatheringPointId))
{
throw new TaskException($"No gathering point found for item {base.Task.GatheredItem.ItemId}");
}
@ -69,7 +70,7 @@ internal static class Gather
{
yield break;
}
switch (rowId)
switch (valueOrDefault)
{
case EClassJob.Miner:
yield return new Questionable.Controller.Steps.Interactions.Action.TriggerStatusIfMissing(EStatus.Prospect, EAction.Prospect);

View file

@ -45,7 +45,7 @@ internal static class SkipCondition
}
}
internal sealed class CheckSkip(ILogger<CheckSkip> logger, Configuration configuration, AetheryteFunctions aetheryteFunctions, GameFunctions gameFunctions, QuestFunctions questFunctions, IClientState clientState, ICondition condition, ExtraConditionUtils extraConditionUtils, ClassJobUtils classJobUtils) : TaskExecutor<SkipTask>()
internal sealed class CheckSkip(ILogger<CheckSkip> logger, Configuration configuration, AetheryteFunctions aetheryteFunctions, GameFunctions gameFunctions, QuestFunctions questFunctions, IClientState clientState, IObjectTable objectTable, ICondition condition, ExtraConditionUtils extraConditionUtils, ClassJobUtils classJobUtils) : TaskExecutor<SkipTask>()
{
protected override bool Start()
{
@ -200,9 +200,10 @@ internal static class SkipCondition
if (skipConditions.NotTargetable && step != null && step.DataId.HasValue)
{
IGameObject gameObject = gameFunctions.FindObjectByDataId(step.DataId.Value);
IGameObject gameObject2 = objectTable[0];
if (gameObject == null)
{
if ((step.Position.GetValueOrDefault() - clientState.LocalPlayer.Position).Length() < 100f)
if (gameObject2 != null && (step.Position.GetValueOrDefault() - gameObject2.Position).Length() < 100f)
{
logger.LogInformation("Skipping step, object is not nearby (but we are)");
return true;
@ -340,9 +341,10 @@ internal static class SkipCondition
private bool CheckLevelCondition(SkipStepConditions skipConditions)
{
if (skipConditions.MinimumLevel.HasValue && clientState.LocalPlayer != null && clientState.LocalPlayer.Level >= skipConditions.MinimumLevel.Value)
ICharacter character = objectTable[0] as ICharacter;
if (skipConditions.MinimumLevel.HasValue && character != null && character.Level >= skipConditions.MinimumLevel.Value)
{
logger.LogInformation("Skipping step, as player level {CurrentLevel} >= minimum level {MinLevel}", clientState.LocalPlayer.Level, skipConditions.MinimumLevel.Value);
logger.LogInformation("Skipping step, as player level {CurrentLevel} >= minimum level {MinLevel}", character.Level, skipConditions.MinimumLevel.Value);
return true;
}
return false;
@ -407,9 +409,9 @@ internal static class SkipCondition
if (requiredCurrentJob != null && requiredCurrentJob.Count > 0)
{
List<EClassJob> list = step.RequiredCurrentJob.SelectMany((EExtendedClassJob x) => classJobUtils.AsIndividualJobs(x, elementId)).ToList();
EClassJob rowId = (EClassJob)clientState.LocalPlayer.ClassJob.RowId;
logger.LogInformation("Checking current job {CurrentJob} against {ExpectedJobs}", rowId, string.Join(",", list));
if (!list.Contains(rowId))
EClassJob valueOrDefault = ((EClassJob?)(objectTable[0] as ICharacter)?.ClassJob.RowId).GetValueOrDefault();
logger.LogInformation("Checking current job {CurrentJob} against {ExpectedJobs}", valueOrDefault, string.Join(",", list));
if (!list.Contains(valueOrDefault))
{
logger.LogInformation("Skipping step, as step requires a different job");
return true;
@ -421,14 +423,15 @@ internal static class SkipCondition
private bool CheckPositionCondition(SkipStepConditions skipConditions)
{
IGameObject gameObject = objectTable[0];
NearPositionCondition nearPosition = skipConditions.NearPosition;
if (nearPosition != null && clientState.TerritoryType == nearPosition.TerritoryId && Vector3.Distance(nearPosition.Position, clientState.LocalPlayer.Position) <= nearPosition.MaximumDistance)
if (nearPosition != null && clientState.TerritoryType == nearPosition.TerritoryId && gameObject != null && Vector3.Distance(nearPosition.Position, gameObject.Position) <= nearPosition.MaximumDistance)
{
logger.LogInformation("Skipping step, as we're near the position");
return true;
}
NearPositionCondition notNearPosition = skipConditions.NotNearPosition;
if (notNearPosition != null && clientState.TerritoryType == notNearPosition.TerritoryId && notNearPosition.MaximumDistance <= Vector3.Distance(notNearPosition.Position, clientState.LocalPlayer.Position))
if (notNearPosition != null && clientState.TerritoryType == notNearPosition.TerritoryId && gameObject != null && notNearPosition.MaximumDistance <= Vector3.Distance(notNearPosition.Position, gameObject.Position))
{
logger.LogInformation("Skipping step, as we're not near the position");
return true;
@ -479,7 +482,7 @@ internal static class SkipCondition
private unsafe bool IsBetterOrEqualItemEquipped(uint itemId)
{
if (clientState.LocalPlayer == null)
if (objectTable[0] == null)
{
return false;
}

View file

@ -1,4 +1,5 @@
using System.Linq;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using LLib.GameData;
@ -31,11 +32,11 @@ internal static class SwitchClassJob
}
}
internal sealed class SwitchClassJobExecutor(IClientState clientState) : AbstractDelayedTaskExecutor<Task>()
internal sealed class SwitchClassJobExecutor(IObjectTable objectTable) : AbstractDelayedTaskExecutor<Task>()
{
protected unsafe override bool StartInternal()
{
if (clientState.LocalPlayer.ClassJob.RowId == (uint)base.Task.ClassJob)
if ((objectTable[0] as ICharacter)?.ClassJob.RowId == (uint?)base.Task.ClassJob)
{
return false;
}

View file

@ -17,7 +17,7 @@ namespace Questionable.Controller.Steps.Shared;
internal static class WaitAtEnd
{
internal sealed class Factory(IClientState clientState, ICondition condition, TerritoryData territoryData, AutoDutyIpc autoDutyIpc, BossModIpc bossModIpc) : ITaskFactory
internal sealed class Factory(IClientState clientState, IObjectTable objectTable, ICondition condition, TerritoryData territoryData, AutoDutyIpc autoDutyIpc, BossModIpc bossModIpc) : ITaskFactory
{
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{
@ -129,10 +129,10 @@ internal static class WaitAtEnd
}
else
{
Vector3 lastPosition = step.Position ?? clientState.LocalPlayer?.Position ?? Vector3.Zero;
Vector3 lastPosition = step.Position ?? objectTable[0]?.Position ?? Vector3.Zero;
task = new WaitCondition.Task(delegate
{
Vector3? vector = clientState.LocalPlayer?.Position;
Vector3? vector = objectTable[0]?.Position;
return vector.HasValue && (lastPosition - vector.Value).Length() > 2f;
}, "Wait(tp away from " + lastPosition.ToString("G", CultureInfo.InvariantCulture) + ")");
}

View file

@ -6,7 +6,6 @@ using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
@ -323,12 +322,13 @@ internal sealed class CombatController : IDisposable
}
}
}
IGameObject localPlayer = _objectTable[0];
IGameObject gameObject = (from x in _objectTable
select new
{
GameObject = x,
Priority = GetKillPriority(x).Priority,
Distance = Vector3.Distance(x.Position, _clientState.LocalPlayer.Position)
Distance = Vector3.Distance(x.Position, localPlayer?.Position ?? Vector3.Zero)
} into x
where x.Priority > 0
orderby x.Priority descending, x.Distance
@ -355,7 +355,8 @@ internal sealed class CombatController : IDisposable
}
if (gameObject is IBattleNpc battleNpc && battleNpc.StatusFlags.HasFlag(StatusFlags.InCombat))
{
if (gameObject.TargetObjectId == _clientState.LocalPlayer?.GameObjectId)
IGameObject? gameObject2 = _objectTable[0];
if (gameObject.TargetObjectId == gameObject2?.GameObjectId)
{
return (Priority: num.Value + 150, Reason: text + "/Targeted");
}
@ -393,11 +394,12 @@ internal sealed class CombatController : IDisposable
}
List<ComplexCombatData> complexCombatDatas = _currentFight.Data.ComplexCombatDatas;
GameObject* address = (GameObject*)gameObject.Address;
if (address->FateId != 0 && _currentFight.Data.SpawnType != EEnemySpawnType.FateEnemies && gameObject.TargetObjectId != _clientState.LocalPlayer?.GameObjectId)
IGameObject gameObject2 = _objectTable[0];
if (address->FateId != 0 && _currentFight.Data.SpawnType != EEnemySpawnType.FateEnemies && gameObject.TargetObjectId != gameObject2?.GameObjectId)
{
return (Priority: null, Reason: "FATE mob");
}
Vector3 value = _clientState.LocalPlayer?.Position ?? Vector3.Zero;
Vector3 value = gameObject2?.Position ?? Vector3.Zero;
bool flag = _currentFight.Data.SpawnType != EEnemySpawnType.FinishCombatIfAny && (_currentFight.Data.SpawnType != EEnemySpawnType.OverworldEnemies || !(Vector3.Distance(value, battleNpc.Position) >= 50f)) && _currentFight.Data.SpawnType != EEnemySpawnType.FateEnemies;
if (complexCombatDatas.Count > 0)
{
@ -430,6 +432,7 @@ internal sealed class CombatController : IDisposable
private void SetTarget(IGameObject? target)
{
IGameObject gameObject = _objectTable[0];
if (target == null)
{
if (_targetManager.Target != null)
@ -438,9 +441,9 @@ internal sealed class CombatController : IDisposable
_targetManager.Target = null;
}
}
else if (Vector3.Distance(_clientState.LocalPlayer.Position, target.Position) > 55f)
else if (gameObject != null && Vector3.Distance(gameObject.Position, target.Position) > 55f)
{
_logger.LogInformation("Moving to target, distance: {Distance:N2}", Vector3.Distance(_clientState.LocalPlayer.Position, target.Position));
_logger.LogInformation("Moving to target, distance: {Distance:N2}", Vector3.Distance(gameObject.Position, target.Position));
MoveToTarget(target);
}
else
@ -468,39 +471,45 @@ internal sealed class CombatController : IDisposable
private void MoveToTarget(IGameObject gameObject)
{
IPlayerCharacter localPlayer = _clientState.LocalPlayer;
if (localPlayer == null)
IGameObject gameObject2 = _objectTable[0];
if (gameObject2 == null)
{
return;
}
float num = localPlayer.HitboxRadius + gameObject.HitboxRadius;
float num2 = Vector3.Distance(localPlayer.Position, gameObject.Position);
byte? b = localPlayer.ClassJob.ValueNullable?.Role;
bool flag;
float num = gameObject2.HitboxRadius + gameObject.HitboxRadius;
float num2 = Vector3.Distance(gameObject2.Position, gameObject.Position);
ICharacter character = gameObject2 as ICharacter;
bool flag = character != null;
bool flag2;
if (flag)
{
byte? b = character.ClassJob.ValueNullable?.Role;
if (b.HasValue)
{
byte valueOrDefault = b.GetValueOrDefault();
if ((uint)(valueOrDefault - 3) <= 1u)
{
flag = true;
goto IL_008e;
flag2 = true;
goto IL_00a3;
}
}
flag = false;
goto IL_008e;
IL_008e:
flag2 = false;
goto IL_00a3;
}
goto IL_00a7;
IL_00a7:
float num3 = (flag ? 20f : 2.9f);
bool flag2 = num2 - num >= num3;
bool flag3 = IsInLineOfSight(gameObject);
if (flag2 || !flag3)
bool flag3 = num2 - num >= num3;
bool flag4 = IsInLineOfSight(gameObject);
if (flag3 || !flag4)
{
bool flag4 = num2 - num > 5f;
if (!flag2 && !flag3)
bool flag5 = num2 - num > 5f;
if (!flag3 && !flag4)
{
num3 = Math.Min(num3, num2) / 2f;
flag4 = true;
flag5 = true;
}
if (!flag4)
if (!flag5)
{
_logger.LogInformation("Moving to {TargetName} ({BaseId}) to attack", gameObject.Name, gameObject.BaseId);
MovementController movementController = _movementController;
@ -518,11 +527,20 @@ internal sealed class CombatController : IDisposable
_movementController.NavigateTo(EMovementType.Combat, null, gameObject.Position, fly: false, sprint: false, num3 + num - 0.25f, float.MaxValue);
}
}
return;
IL_00a3:
flag = flag2;
goto IL_00a7;
}
internal unsafe bool IsInLineOfSight(IGameObject target)
{
Vector3 position = _clientState.LocalPlayer.Position;
IGameObject gameObject = _objectTable[0];
if (gameObject == null)
{
return false;
}
Vector3 position = gameObject.Position;
position.Y += 2f;
Vector3 position2 = target.Position;
position2.Y += 2f;

View file

@ -1,20 +1,8 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.Command;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using Lumina.Excel;
using Lumina.Excel.Sheets;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Common;
using Questionable.Model.Questing;
using Questionable.Model.Questing.Converter;
using Questionable.Windows;
namespace Questionable.Controller;
@ -33,12 +21,8 @@ internal sealed class CommandHandler : IDisposable
private readonly MovementController _movementController;
private readonly QuestRegistry _questRegistry;
private readonly ConfigWindow _configWindow;
private readonly DebugOverlay _debugOverlay;
private readonly OneTimeSetupWindow _oneTimeSetupWindow;
private readonly QuestWindow _questWindow;
@ -53,33 +37,19 @@ internal sealed class CommandHandler : IDisposable
private readonly ITargetManager _targetManager;
private readonly QuestFunctions _questFunctions;
private readonly GameFunctions _gameFunctions;
private readonly AetheryteFunctions _aetheryteFunctions;
private readonly IDataManager _dataManager;
private readonly IClientState _clientState;
private readonly IObjectTable _objectTable;
private readonly Configuration _configuration;
private readonly ChangelogWindow _changelogWindow;
private IReadOnlyList<uint> _previouslyUnlockedUnlockLinks = Array.Empty<uint>();
public CommandHandler(ICommandManager commandManager, IChatGui chatGui, QuestController questController, MovementController movementController, QuestRegistry questRegistry, ConfigWindow configWindow, DebugOverlay debugOverlay, OneTimeSetupWindow oneTimeSetupWindow, QuestWindow questWindow, QuestSelectionWindow questSelectionWindow, QuestSequenceWindow questSequenceWindow, JournalProgressWindow journalProgressWindow, PriorityWindow priorityWindow, ITargetManager targetManager, QuestFunctions questFunctions, GameFunctions gameFunctions, AetheryteFunctions aetheryteFunctions, IDataManager dataManager, IClientState clientState, IObjectTable objectTable, Configuration configuration, ChangelogWindow changelogWindow)
{
_commandManager = commandManager;
_chatGui = chatGui;
_questController = questController;
_movementController = movementController;
_questRegistry = questRegistry;
_configWindow = configWindow;
_debugOverlay = debugOverlay;
_oneTimeSetupWindow = oneTimeSetupWindow;
_questWindow = questWindow;
_questSelectionWindow = questSelectionWindow;
@ -87,12 +57,7 @@ internal sealed class CommandHandler : IDisposable
_journalProgressWindow = journalProgressWindow;
_priorityWindow = priorityWindow;
_targetManager = targetManager;
_questFunctions = questFunctions;
_gameFunctions = gameFunctions;
_aetheryteFunctions = aetheryteFunctions;
_dataManager = dataManager;
_clientState = clientState;
_objectTable = objectTable;
_configuration = configuration;
_changelogWindow = changelogWindow;
_clientState.Logout += OnLogout;
@ -185,465 +150,8 @@ internal sealed class CommandHandler : IDisposable
}
}
private unsafe void ProcessDebugCommand(string command, string arguments)
private void ProcessDebugCommand(string command, string arguments)
{
if (OpenSetupIfNeeded(arguments))
{
return;
}
string[] array = arguments.Split(' ');
string text = array[0];
if (text == null)
{
return;
}
switch (text.Length)
{
case 4:
switch (text[0])
{
case 'n':
if (text == "next")
{
SetNextQuest(array.Skip(1).ToArray());
}
break;
case 't':
{
if (!(text == "taxi"))
{
break;
}
List<string> list5 = new List<string>();
ExcelSheet<ChocoboTaxiStand> excelSheet = _dataManager.GetExcelSheet<ChocoboTaxiStand>();
UIState* ptr = UIState.Instance();
if (ptr == null)
{
_chatGui.PrintError("UIState is null", "Questionable", 576);
break;
}
for (int num10 = 0; num10 < 192; num10++)
{
uint num11 = (uint)(num10 + 1179648);
try
{
if (excelSheet.HasRow(num11) && ptr->IsChocoboTaxiStandUnlocked(num11))
{
string value14 = excelSheet.GetRow(num11).PlaceName.ToString();
if (string.IsNullOrEmpty(value14))
{
value14 = "Unknown";
}
list5.Add($"{value14} (ID: {num10}, Row: 0x{num11:X})");
}
}
catch
{
}
}
_chatGui.Print($"Unlocked taxi stands ({list5.Count}):", "Questionable", 576);
if (list5.Count == 0)
{
_chatGui.Print(" (No unlocked taxi stands found)", "Questionable", 576);
break;
}
{
foreach (string item5 in list5)
{
_chatGui.Print(" - " + item5, "Questionable", 576);
}
break;
}
}
}
break;
case 3:
switch (text[1])
{
default:
return;
case 'i':
if (text == "sim")
{
SetSimulatedQuest(array.Skip(1).ToArray());
}
return;
case 'e':
break;
}
if (!(text == "seq"))
{
break;
}
goto IL_0209;
case 12:
switch (text[0])
{
case 'a':
if (text == "abandon-duty")
{
_gameFunctions.AbandonDuty();
}
break;
case 'u':
{
if (!(text == "unlock-links"))
{
break;
}
IReadOnlyList<uint> unlockLinks = _gameFunctions.GetUnlockLinks();
if (unlockLinks.Count >= 0)
{
_chatGui.Print($"Saved {unlockLinks.Count} unlock links to log.", "Questionable", 576);
List<uint> list6 = unlockLinks.Except(_previouslyUnlockedUnlockLinks).ToList();
if (_previouslyUnlockedUnlockLinks.Count > 0 && list6.Count > 0)
{
_chatGui.Print("New unlock links: " + string.Join(", ", list6), "Questionable", 576);
}
}
else
{
_chatGui.PrintError("Could not query unlock links.", "Questionable", 576);
}
_previouslyUnlockedUnlockLinks = unlockLinks;
break;
}
}
break;
case 9:
switch (text[0])
{
default:
return;
case 's':
break;
case 'f':
{
if (!(text == "festivals"))
{
return;
}
List<string> list4 = new List<string>();
for (byte b8 = 0; b8 < 4; b8++)
{
GameMain.Festival festival = GameMain.Instance()->ActiveFestivals[b8];
if (festival.Id == 0)
{
list4.Add($"Slot {b8}: None");
}
else
{
list4.Add($"Slot {b8}: {festival.Id}({festival.Phase})");
}
}
_chatGui.Print("Festival slots:", "Questionable", 576);
{
foreach (string item6 in list4)
{
_chatGui.Print(" " + item6, "Questionable", 576);
}
return;
}
}
case 'a':
{
if (!(text == "aethernet"))
{
return;
}
ushort territoryType = _clientState.TerritoryType;
Dictionary<EAetheryteLocation, string> values = AethernetShardConverter.Values;
AetheryteData aetheryteData = new AetheryteData(_dataManager);
HashSet<string> hashSet2 = new HashSet<string>();
Dictionary<string, List<(EAetheryteLocation, string, bool)>> dictionary = new Dictionary<string, List<(EAetheryteLocation, string, bool)>>();
EAetheryteLocation key;
string value10;
foreach (KeyValuePair<EAetheryteLocation, string> item7 in values)
{
item7.Deconstruct(out key, out value10);
EAetheryteLocation key2 = key;
string text2 = value10;
if (aetheryteData.TerritoryIds.TryGetValue(key2, out var value11) && value11 == territoryType)
{
int num8 = text2.IndexOf(']', StringComparison.Ordinal);
if (num8 > 0)
{
string item3 = text2.Substring(1, num8 - 1);
hashSet2.Add(item3);
}
}
}
if (hashSet2.Count == 0)
{
_chatGui.Print("No aethernet shards found in current zone.", "Questionable", 576);
return;
}
foreach (KeyValuePair<EAetheryteLocation, string> item8 in values)
{
item8.Deconstruct(out key, out value10);
EAetheryteLocation eAetheryteLocation = key;
string text3 = value10;
int num9 = text3.IndexOf(']', StringComparison.Ordinal);
if (num9 <= 0)
{
continue;
}
string text4 = text3.Substring(1, num9 - 1);
if (hashSet2.Contains(text4))
{
if (!dictionary.ContainsKey(text4))
{
dictionary[text4] = new List<(EAetheryteLocation, string, bool)>();
}
bool item4 = _aetheryteFunctions.IsAetheryteUnlocked(eAetheryteLocation);
dictionary[text4].Add((eAetheryteLocation, text3, item4));
}
}
{
foreach (KeyValuePair<string, List<(EAetheryteLocation, string, bool)>> item9 in dictionary.OrderBy<KeyValuePair<string, List<(EAetheryteLocation, string, bool)>>, string>((KeyValuePair<string, List<(EAetheryteLocation Location, string Name, bool Unlocked)>> x) => x.Key))
{
item9.Deconstruct(out value10, out var value12);
string value13 = value10;
List<(EAetheryteLocation, string, bool)> list = value12;
List<(EAetheryteLocation, string, bool)> list2 = list.Where<(EAetheryteLocation, string, bool)>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => x.Unlocked).ToList();
List<(EAetheryteLocation, string, bool)> list3 = list.Where<(EAetheryteLocation, string, bool)>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => !x.Unlocked).ToList();
_chatGui.Print($"Aethernet Shards in {value13} ({list.Count} total):", "Questionable", 576);
_chatGui.Print($" Unlocked: {list2.Count}", "Questionable", 576);
_chatGui.Print($" Missing: {list3.Count}", "Questionable", 576);
_chatGui.Print("", "Questionable", 576);
if (list3.Count > 0)
{
_chatGui.Print("Missing/Unattuned Aethernet Shards:", "Questionable", 576);
foreach (var item10 in list3.OrderBy<(EAetheryteLocation, string, bool), string>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => x.Name))
{
_chatGui.Print(" " + item10.Item2, "Questionable", 576);
}
_chatGui.Print("", "Questionable", 576);
}
if (list2.Count > 0)
{
_chatGui.Print("Unlocked Aethernet Shards:", "Questionable", 576);
foreach (var item11 in list2.OrderBy<(EAetheryteLocation, string, bool), string>(((EAetheryteLocation Location, string Name, bool Unlocked) x) => x.Name))
{
_chatGui.Print(" " + item11.Item2, "Questionable", 576);
}
}
if (dictionary.Count > 1)
{
_chatGui.Print("", "Questionable", 576);
}
}
return;
}
}
}
if (!(text == "sequences"))
{
break;
}
goto IL_0209;
case 5:
if (text == "setup")
{
_oneTimeSetupWindow.IsOpenAndUncollapsed = true;
}
break;
case 2:
if (text == "do")
{
ConfigureDebugOverlay(array.Skip(1).ToArray());
}
break;
case 7:
if (text == "mountid")
{
PrintMountId();
}
break;
case 11:
{
if (!(text == "quest-kills"))
{
break;
}
(QuestController.QuestProgress, QuestController.ECurrentQuestType)? currentQuestDetails = _questController.CurrentQuestDetails;
if (!currentQuestDetails.HasValue)
{
_chatGui.PrintError("No active quest.", "Questionable", 576);
break;
}
QuestController.QuestProgress item = currentQuestDetails.Value.Item1;
Questionable.Model.Quest quest = item.Quest;
QuestProgressInfo questProgressInfo = null;
if (quest.Id is QuestId elementId)
{
questProgressInfo = _questFunctions.GetQuestProgressInfo(elementId);
}
if (questProgressInfo == null)
{
_chatGui.PrintError("Unable to retrieve quest progress information.", "Questionable", 576);
break;
}
QuestSequence questSequence = quest.FindSequence(item.Sequence);
if (questSequence == null)
{
_chatGui.PrintError($"Sequence {item.Sequence} not found for quest {quest.Id}.", "Questionable", 576);
break;
}
QuestStep questStep = ((item.Step < questSequence.Steps.Count) ? questSequence.Steps[item.Step] : null);
if (questStep == null)
{
_chatGui.PrintError($"Step {item.Step} not found in sequence {item.Sequence}.", "Questionable", 576);
break;
}
_chatGui.Print($"Quest: {quest.Info.Name} ({quest.Id})", "Questionable", 576);
_chatGui.Print($"Sequence: {item.Sequence}, Step: {item.Step}", "Questionable", 576);
_chatGui.Print("", "Questionable", 576);
_chatGui.Print("Quest Variables: " + string.Join(", ", questProgressInfo.Variables.Select((byte v, int i) => $"[{i}]={v}")), "Questionable", 576);
_chatGui.Print("", "Questionable", 576);
ExcelSheet<BNpcName> bnpcNameSheet = _dataManager.GetExcelSheet<BNpcName>();
HashSet<uint> hashSet = new HashSet<uint>(questStep.KillEnemyDataIds);
foreach (ComplexCombatData complexCombatDatum in questStep.ComplexCombatData)
{
hashSet.Add(complexCombatDatum.DataId);
}
if (hashSet.Count > 0)
{
_chatGui.Print($"All Enemy DataIds Found: {hashSet.Count}", "Questionable", 576);
foreach (uint item12 in hashSet.OrderBy((uint x) => x))
{
(string Name, bool Found) tuple = GetEnemyName(item12);
var (value, _) = tuple;
if (tuple.Found)
{
_chatGui.Print($" - {value} (DataId: {item12})", "Questionable", 576);
}
else
{
_chatGui.Print($" - DataId: {item12}", "Questionable", 576);
}
}
_chatGui.Print("", "Questionable", 576);
}
if (questStep.ComplexCombatData.Count > 0)
{
_chatGui.Print($"Complex Combat Data Entries: {questStep.ComplexCombatData.Count}", "Questionable", 576);
_chatGui.Print("Kill Progress:", "Questionable", 576);
if (questStep.ComplexCombatData.Count == 1 && hashSet.Count > 1)
{
ComplexCombatData complexCombatData = questStep.ComplexCombatData[0];
int num = -1;
byte? b = null;
for (int num2 = 0; num2 < complexCombatData.CompletionQuestVariablesFlags.Count; num2++)
{
QuestWorkValue questWorkValue = complexCombatData.CompletionQuestVariablesFlags[num2];
if (questWorkValue != null && questWorkValue.Low.HasValue)
{
num = num2;
b = questWorkValue.Low;
break;
}
}
byte b2 = (byte)(((num >= 0 && num < questProgressInfo.Variables.Count) ? questProgressInfo.Variables[num] : 0) & 0xF);
string value2 = (b.HasValue ? $" {b2}/{b}" : "");
string value3 = ((b.HasValue && b2 >= b) ? "✓" : "○");
foreach (uint item13 in hashSet.OrderBy((uint x) => x))
{
(string Name, bool Found) tuple3 = GetEnemyName(item13);
var (value4, _) = tuple3;
if (tuple3.Found)
{
_chatGui.Print($" {value3} Slay {value4}.{value2} (DataId: {item13})", "Questionable", 576);
}
else
{
_chatGui.Print($" {value3} Slay enemy.{value2} (DataId: {item13})", "Questionable", 576);
}
}
}
else
{
for (int num3 = 0; num3 < questStep.ComplexCombatData.Count; num3++)
{
ComplexCombatData complexCombatData2 = questStep.ComplexCombatData[num3];
int num4 = -1;
byte? b3 = null;
bool flag = false;
for (int num5 = 0; num5 < complexCombatData2.CompletionQuestVariablesFlags.Count; num5++)
{
QuestWorkValue questWorkValue2 = complexCombatData2.CompletionQuestVariablesFlags[num5];
if (questWorkValue2 != null)
{
if (questWorkValue2.Low.HasValue)
{
num4 = num5;
b3 = questWorkValue2.Low;
flag = false;
break;
}
if (questWorkValue2.High.HasValue)
{
num4 = num5;
b3 = questWorkValue2.High;
flag = true;
break;
}
}
}
byte b4 = (byte)((num4 >= 0 && num4 < questProgressInfo.Variables.Count) ? questProgressInfo.Variables[num4] : 0);
byte b5 = (flag ? ((byte)(b4 >> 4)) : ((byte)(b4 & 0xF)));
string value5;
if (complexCombatData2.NameId.HasValue)
{
BNpcName? bNpcName = bnpcNameSheet?.GetRowOrDefault(complexCombatData2.NameId.Value);
value5 = ((!bNpcName.HasValue || string.IsNullOrEmpty(bNpcName.Value.Singular.ToString())) ? "enemy" : bNpcName.Value.Singular.ToString());
}
else
{
(string Name, bool Found) tuple5 = GetEnemyName(complexCombatData2.DataId);
string item2 = tuple5.Name;
value5 = (tuple5.Found ? item2 : "enemy");
}
string value6 = (b3.HasValue ? $" {b5}/{b3}" : "");
string value7 = ((b3.HasValue && b5 >= b3) ? "✓" : "○");
string value8 = (complexCombatData2.NameId.HasValue ? $" (DataId: {complexCombatData2.DataId}, NameId: {complexCombatData2.NameId})" : $" (DataId: {complexCombatData2.DataId})");
_chatGui.Print($" {value7} Slay {value5}.{value6}{value8}", "Questionable", 576);
}
}
_chatGui.Print("", "Questionable", 576);
}
else if (questStep.KillEnemyDataIds.Count == 0)
{
_chatGui.Print("No kill enemy data for this step.", "Questionable", 576);
_chatGui.Print("", "Questionable", 576);
}
if (questStep.CompletionQuestVariablesFlags.Count <= 0 || !questStep.CompletionQuestVariablesFlags.Any((QuestWorkValue x) => x != null))
{
break;
}
_chatGui.Print("Completion Flags (Debug):", "Questionable", 576);
for (int num6 = 0; num6 < questStep.CompletionQuestVariablesFlags.Count; num6++)
{
QuestWorkValue questWorkValue3 = questStep.CompletionQuestVariablesFlags[num6];
if (questWorkValue3 != null)
{
int num7 = ((num6 < questProgressInfo.Variables.Count) ? questProgressInfo.Variables[num6] : 0);
byte b6 = (byte)(num7 >> 4);
byte b7 = (byte)(num7 & 0xF);
string value9 = (((!questWorkValue3.High.HasValue || questWorkValue3.High == b6) && (!questWorkValue3.Low.HasValue || questWorkValue3.Low == b7)) ? " ✓" : " ✗");
_chatGui.Print($" [{num6}] Expected: H={questWorkValue3.High?.ToString(CultureInfo.InvariantCulture) ?? "any"} L={questWorkValue3.Low?.ToString(CultureInfo.InvariantCulture) ?? "any"} | Actual: H={b6.ToString(CultureInfo.InvariantCulture)} L={b7.ToString(CultureInfo.InvariantCulture)}{value9}", "Questionable", 576);
}
}
break;
}
case 6:
case 8:
case 10:
break;
IL_0209:
_questSequenceWindow.ToggleOrUncollapse();
break;
}
}
private bool OpenSetupIfNeeded(string arguments)
@ -663,110 +171,8 @@ internal sealed class CommandHandler : IDisposable
return false;
}
private void ConfigureDebugOverlay(string[] arguments)
{
ElementId elementId;
if (!_debugOverlay.DrawConditions())
{
_chatGui.PrintError("You don't have the debug overlay enabled.", "Questionable", 576);
}
else if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out elementId) && elementId != null)
{
if (_questRegistry.TryGetQuest(elementId, out Questionable.Model.Quest quest))
{
_debugOverlay.HighlightedQuest = quest.Id;
_chatGui.Print($"Set highlighted quest to {elementId} ({quest.Info.Name}).", "Questionable", 576);
}
else
{
_chatGui.PrintError($"Unknown quest {elementId}.", "Questionable", 576);
}
}
else
{
_debugOverlay.HighlightedQuest = null;
_chatGui.Print("Cleared highlighted quest.", "Questionable", 576);
}
}
private void SetNextQuest(string[] arguments)
{
if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out ElementId elementId) && elementId != null)
{
Questionable.Model.Quest quest;
if (_questFunctions.IsQuestLocked(elementId))
{
_chatGui.PrintError($"Quest {elementId} is locked.", "Questionable", 576);
}
else if (_questRegistry.TryGetQuest(elementId, out quest))
{
_questController.SetNextQuest(quest);
_chatGui.Print($"Set next quest to {elementId} ({quest.Info.Name}).", "Questionable", 576);
}
else
{
_chatGui.PrintError($"Unknown quest {elementId}.", "Questionable", 576);
}
}
else
{
_questController.SetNextQuest(null);
_chatGui.Print("Cleared next quest.", "Questionable", 576);
}
}
private void SetSimulatedQuest(string[] arguments)
{
if (arguments.Length >= 1 && ElementId.TryFromString(arguments[0], out ElementId elementId) && elementId != null)
{
if (_questRegistry.TryGetQuest(elementId, out Questionable.Model.Quest quest))
{
byte sequence = 0;
int step = 0;
if (arguments.Length >= 2 && byte.TryParse(arguments[1], out var result))
{
QuestSequence questSequence = quest.FindSequence(result);
if (questSequence != null)
{
sequence = questSequence.Sequence;
if (arguments.Length >= 3 && int.TryParse(arguments[2], out var result2) && questSequence.FindStep(result2) != null)
{
step = result2;
}
}
}
_questController.SimulateQuest(quest, sequence, step);
_chatGui.Print($"Simulating quest {elementId} ({quest.Info.Name}).", "Questionable", 576);
}
else
{
_chatGui.PrintError($"Unknown quest {elementId}.", "Questionable", 576);
}
}
else
{
_questController.SimulateQuest(null, 0, 0);
_chatGui.Print("Cleared simulated quest.", "Questionable", 576);
}
}
private void PrintMountId()
{
ushort? mountId = _gameFunctions.GetMountId();
if (mountId.HasValue)
{
Mount? rowOrDefault = _dataManager.GetExcelSheet<Mount>().GetRowOrDefault(mountId.Value);
_chatGui.Print($"Mount ID: {mountId}, Name: {rowOrDefault?.Singular}, Obtainable: {((rowOrDefault?.Order == -1) ? "No" : "Yes")}", "Questionable", 576);
}
else
{
_chatGui.Print("You are not mounted.", "Questionable", 576);
}
}
private void OnLogout(int type, int code)
{
_previouslyUnlockedUnlockLinks = Array.Empty<uint>();
}
public void Dispose()

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Gui.ContextMenu;
using Dalamud.Game.Text;
using Dalamud.Plugin.Services;
@ -43,9 +44,11 @@ internal sealed class ContextMenuController : IDisposable
private readonly IClientState _clientState;
private readonly IObjectTable _objectTable;
private readonly ILogger<ContextMenuController> _logger;
public ContextMenuController(IContextMenu contextMenu, QuestController questController, GatheringPointRegistry gatheringPointRegistry, GatheringData gatheringData, QuestRegistry questRegistry, QuestData questData, GameFunctions gameFunctions, QuestFunctions questFunctions, IGameGui gameGui, IChatGui chatGui, IClientState clientState, ILogger<ContextMenuController> logger)
public ContextMenuController(IContextMenu contextMenu, QuestController questController, GatheringPointRegistry gatheringPointRegistry, GatheringData gatheringData, QuestRegistry questRegistry, QuestData questData, GameFunctions gameFunctions, QuestFunctions questFunctions, IGameGui gameGui, IChatGui chatGui, IClientState clientState, IObjectTable objectTable, ILogger<ContextMenuController> logger)
{
_contextMenu = contextMenu;
_questController = questController;
@ -58,6 +61,7 @@ internal sealed class ContextMenuController : IDisposable
_gameGui = gameGui;
_chatGui = chatGui;
_clientState = clientState;
_objectTable = objectTable;
_logger = logger;
_contextMenu.OnMenuOpened += MenuOpened;
}
@ -113,7 +117,11 @@ internal sealed class ContextMenuController : IDisposable
private unsafe void AddContextMenuEntry(IMenuOpenedArgs args, uint itemId, uint npcId, EClassJob classJob, string verb)
{
EClassJob rowId = (EClassJob)_clientState.LocalPlayer.ClassJob.RowId;
if (!(_objectTable[0] is ICharacter { ClassJob: var classJob2 }))
{
return;
}
EClassJob rowId = (EClassJob)classJob2.RowId;
bool flag = classJob != rowId;
if (flag)
{

View file

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Hooking;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
@ -135,15 +136,18 @@ internal sealed class InterruptHandler : IDisposable
private readonly IClientState _clientState;
private readonly IObjectTable _objectTable;
private readonly TerritoryData _territoryData;
private readonly ILogger<InterruptHandler> _logger;
public event EventHandler? Interrupted;
public unsafe InterruptHandler(IGameInteropProvider gameInteropProvider, IClientState clientState, TerritoryData territoryData, ILogger<InterruptHandler> logger)
public unsafe InterruptHandler(IGameInteropProvider gameInteropProvider, IClientState clientState, IObjectTable objectTable, TerritoryData territoryData, ILogger<InterruptHandler> logger)
{
_clientState = clientState;
_objectTable = objectTable;
_territoryData = territoryData;
_logger = logger;
_processActionEffectHook = gameInteropProvider.HookFromSignature<ProcessActionEffect>("40 ?? 56 57 41 ?? 41 ?? 41 ?? 48 ?? ?? ?? ?? ?? ?? ?? 48", HandleProcessActionEffect);
@ -158,11 +162,12 @@ internal sealed class InterruptHandler : IDisposable
{
return;
}
IGameObject gameObject = _objectTable[0];
for (int i = 0; i < effectHeader->TargetCount; i++)
{
int num = (int)(effectTail[i] & 0xFFFFFFFFu);
EffectEntry* ptr = effectArray + 8 * i;
bool flag = (uint)num == _clientState.LocalPlayer?.GameObjectId;
bool flag = (uint)num == gameObject?.GameObjectId;
if (flag)
{
EActionEffectType type = ptr->Type;

View file

@ -73,6 +73,8 @@ internal sealed class MovementController : IDisposable
private readonly IClientState _clientState;
private readonly IObjectTable _objectTable;
private readonly GameFunctions _gameFunctions;
private readonly ChatFunctions _chatFunctions;
@ -89,6 +91,8 @@ internal sealed class MovementController : IDisposable
private Task<List<Vector3>>? _pathfindTask;
private long _pathfindStartTime;
public bool IsNavmeshReady
{
get
@ -138,10 +142,15 @@ internal sealed class MovementController : IDisposable
public int BuiltNavmeshPercent => _navmeshIpc.GetBuildProgress();
public MovementController(NavmeshIpc navmeshIpc, IClientState clientState, GameFunctions gameFunctions, ChatFunctions chatFunctions, ICondition condition, MovementOverrideController movementOverrideController, AetheryteData aetheryteData, ILogger<MovementController> logger)
public bool IsNavmeshPathfindInProgress => _navmeshIpc.IsPathfindInProgress;
public int NumQueuedPathfindRequests => _navmeshIpc.NumQueuedPathfindRequests;
public MovementController(NavmeshIpc navmeshIpc, IClientState clientState, IObjectTable objectTable, GameFunctions gameFunctions, ChatFunctions chatFunctions, ICondition condition, MovementOverrideController movementOverrideController, AetheryteData aetheryteData, ILogger<MovementController> logger)
{
_navmeshIpc = navmeshIpc;
_clientState = clientState;
_objectTable = objectTable;
_gameFunctions = gameFunctions;
_chatFunctions = chatFunctions;
_condition = condition;
@ -154,16 +163,51 @@ internal sealed class MovementController : IDisposable
{
if (_pathfindTask != null && Destination != null)
{
if (!_pathfindTask.IsCompleted && Environment.TickCount64 - _pathfindStartTime > 30000 && _navmeshIpc.NumQueuedPathfindRequests > 5)
{
_logger.LogWarning("Pathfinding appears stuck: {QueuedRequests} queued requests, task running for {Duration}ms", _navmeshIpc.NumQueuedPathfindRequests, Environment.TickCount64 - _pathfindStartTime);
}
if (_pathfindTask.IsCompletedSuccessfully)
{
_logger.LogInformation("Pathfinding complete, got {Count} points", _pathfindTask.Result.Count);
if (_pathfindTask.Result.Count == 0)
{
if (Destination.NavmeshCalculations == 1)
{
_logger.LogWarning("Initial pathfinding returned 0 points, attempting to find accessible destination");
Vector3? vector = TryFindAccessibleDestination(Destination.Position, Destination.IsFlying, Destination.Land);
if (vector.HasValue && Vector3.Distance(Destination.Position, vector.Value) < 30f)
{
_logger.LogInformation("Retrying pathfinding with adjusted destination: {AdjustedDestination}", vector.Value.ToString("G", CultureInfo.InvariantCulture));
Restart(Destination with
{
Position = vector.Value
});
return;
}
if (Destination.IsFlying && Destination.Land)
{
_logger.LogWarning("Adjusted destination failed, trying tolerance-based pathfinding");
_cancellationTokenSource = new CancellationTokenSource();
_cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(30L));
Vector3 vector2 = _objectTable[0]?.Position ?? Vector3.Zero;
if (Destination.IsFlying)
{
Vector3 vector3 = vector2;
vector3.Y = vector2.Y + 0.2f;
vector2 = vector3;
}
_pathfindStartTime = Environment.TickCount64;
_pathfindTask = _navmeshIpc.PathfindWithTolerance(vector2, Destination.Position, Destination.IsFlying, 10f, _cancellationTokenSource.Token);
Destination.NavmeshCalculations++;
return;
}
}
ResetPathfinding();
throw new PathfindingFailedException();
}
List<Vector3> list = _pathfindTask.Result.Skip(1).ToList();
Vector3 p = _clientState.LocalPlayer?.Position ?? list[0];
Vector3 p = _objectTable[0]?.Position ?? list[0];
if (Destination.IsFlying && !_condition[ConditionFlag.InFlight] && _condition[ConditionFlag.Mounted] && (IsOnFlightPath(p) || list.Any(IsOnFlightPath)))
{
ActionManager.Instance()->UseAction(ActionType.GeneralAction, 2u, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null);
@ -179,6 +223,7 @@ internal sealed class MovementController : IDisposable
_logger.LogInformation("Running navmesh recalculation with fudged point ({From} to {To})", list.Last(), Destination.Position);
_cancellationTokenSource = new CancellationTokenSource();
_cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(30L));
_pathfindStartTime = Environment.TickCount64;
_pathfindTask = _navmeshIpc.Pathfind(list.Last(), Destination.Position, Destination.IsFlying, _cancellationTokenSource.Token);
return;
}
@ -220,7 +265,7 @@ internal sealed class MovementController : IDisposable
Restart(Destination);
return;
}
Vector3 vector = _clientState.LocalPlayer?.Position ?? Vector3.Zero;
Vector3 vector4 = _objectTable[0]?.Position ?? Vector3.Zero;
if (Destination.MovementType == EMovementType.Landing)
{
if (!_condition[ConditionFlag.InFlight])
@ -228,9 +273,9 @@ internal sealed class MovementController : IDisposable
Stop();
}
}
else if ((vector - Destination.Position).Length() < Destination.StopDistance)
else if ((vector4 - Destination.Position).Length() < Destination.StopDistance)
{
if (vector.Y - Destination.Position.Y <= Destination.VerticalStopDistance)
if (vector4.Y - Destination.Position.Y <= Destination.VerticalStopDistance)
{
Stop();
}
@ -239,7 +284,7 @@ internal sealed class MovementController : IDisposable
IGameObject gameObject = _gameFunctions.FindObjectByDataId(Destination.DataId.Value);
if ((gameObject is ICharacter || gameObject is IEventObj) ? true : false)
{
if (Math.Abs(vector.Y - gameObject.Position.Y) < 1.95f)
if (Math.Abs(vector4.Y - gameObject.Position.Y) < 1.95f)
{
Stop();
}
@ -250,7 +295,7 @@ internal sealed class MovementController : IDisposable
{
Stop();
}
else if (Math.Abs(vector.Y - gameObject.Position.Y) < 1.95f)
else if (Math.Abs(vector4.Y - gameObject.Position.Y) < 1.95f)
{
Stop();
}
@ -268,10 +313,10 @@ internal sealed class MovementController : IDisposable
else
{
List<Vector3> waypoints = _navmeshIpc.GetWaypoints();
Vector3? vector2 = _clientState.LocalPlayer?.Position;
if (vector2.HasValue && (!Destination.ShouldRecalculateNavmesh() || !RecalculateNavmesh(waypoints, vector2.Value)) && !Destination.IsFlying && !_condition[ConditionFlag.Mounted] && !_gameFunctions.HasStatusPreventingSprint() && Destination.CanSprint)
Vector3? vector5 = _objectTable[0]?.Position;
if (vector5.HasValue && (!Destination.ShouldRecalculateNavmesh() || !RecalculateNavmesh(waypoints, vector5.Value)) && !Destination.IsFlying && !_condition[ConditionFlag.Mounted] && !_gameFunctions.HasStatusPreventingSprint() && Destination.CanSprint)
{
TriggerSprintIfNeeded(waypoints, vector2.Value);
TriggerSprintIfNeeded(waypoints, vector5.Value);
}
}
}
@ -331,7 +376,7 @@ internal sealed class MovementController : IDisposable
Destination.NavmeshCalculations++;
_cancellationTokenSource = new CancellationTokenSource();
_cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(30L));
Vector3 vector2 = _clientState.LocalPlayer.Position;
Vector3 vector2 = _objectTable[0]?.Position ?? Vector3.Zero;
if (fly && _aetheryteData.CalculateDistance(vector2, _clientState.TerritoryType, EAetheryteLocation.CoerthasCentralHighlandsCampDragonhead) < 11f)
{
Vector3 vector = vector2;
@ -345,8 +390,17 @@ internal sealed class MovementController : IDisposable
vector.Y = vector2.Y + 0.2f;
vector2 = vector;
}
_pathfindStartTime = Environment.TickCount64;
if (fly && land)
{
_logger.LogInformation("Using tolerance-based pathfinding for landing (tolerance: 5.0)");
_pathfindTask = _navmeshIpc.PathfindWithTolerance(vector2, to, fly, 5f, _cancellationTokenSource.Token);
}
else
{
_pathfindTask = _navmeshIpc.Pathfind(vector2, to, fly, _cancellationTokenSource.Token);
}
}
public void NavigateTo(EMovementType type, uint? dataId, List<Vector3> to, bool fly, bool sprint, float? stopDistance, float? verticalStopDistance = null, bool land = false)
{
@ -380,6 +434,46 @@ internal sealed class MovementController : IDisposable
_pathfindTask = null;
}
private Vector3? TryFindAccessibleDestination(Vector3 target, bool flying, bool landing)
{
float[] array = ((!(flying && landing)) ? ((!flying) ? new float[3] { 1f, 3f, 5f } : new float[3] { 2f, 5f, 10f }) : new float[3] { 5f, 10f, 15f });
float[] array2 = ((!flying) ? new float[3] { 1f, 2f, 3f } : new float[3] { 3f, 5f, 10f });
for (int i = 0; i < array.Length; i++)
{
float num = array[i];
float num2 = array2[Math.Min(i, array2.Length - 1)];
Vector3? vector = _navmeshIpc.FindNearestMeshPoint(target, num, num2);
if (vector.HasValue)
{
float num3 = Vector3.Distance(target, vector.Value);
if (num3 <= num * 1.5f)
{
if (i > 0)
{
_logger.LogInformation("Adjusted destination from {Original} to {Adjusted} (distance: {Distance:F2}, extent: {ExtentXZ}/{ExtentY})", target.ToString("G", CultureInfo.InvariantCulture), vector.Value.ToString("G", CultureInfo.InvariantCulture), num3, num, num2);
}
return vector.Value;
}
}
Vector3? pointOnFloor = _navmeshIpc.GetPointOnFloor(target, flying, num);
if (!pointOnFloor.HasValue)
{
continue;
}
float num4 = Vector3.Distance(target, pointOnFloor.Value);
if (num4 <= num * 1.5f)
{
if (i > 0)
{
_logger.LogInformation("Adjusted destination via floor point from {Original} to {Adjusted} (distance: {Distance:F2}, extent: {ExtentXZ})", target.ToString("G", CultureInfo.InvariantCulture), pointOnFloor.Value.ToString("G", CultureInfo.InvariantCulture), num4, num);
}
return pointOnFloor.Value;
}
}
_logger.LogWarning("Could not find accessible mesh point near {Target} within max extent {MaxExtent}", target.ToString("G", CultureInfo.InvariantCulture), array[^1]);
return null;
}
private unsafe bool RecalculateNavmesh(List<Vector3> navPoints, Vector3 start)
{
if (Destination == null)

View file

@ -6,6 +6,7 @@ using System.Numerics;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Gui.Toast;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
@ -84,6 +85,8 @@ internal sealed class QuestController : MiniTaskController<QuestController>
private readonly IClientState _clientState;
private readonly IObjectTable _objectTable;
private readonly GameFunctions _gameFunctions;
private readonly QuestFunctions _questFunctions;
@ -231,10 +234,11 @@ internal sealed class QuestController : MiniTaskController<QuestController>
public event AutomationTypeChangedEventHandler? AutomationTypeChanged;
public QuestController(IClientState clientState, GameFunctions gameFunctions, QuestFunctions questFunctions, MovementController movementController, CombatController combatController, GatheringController gatheringController, ILogger<QuestController> logger, QuestRegistry questRegistry, IKeyState keyState, IChatGui chatGui, ICondition condition, IToastGui toastGui, Configuration configuration, TaskCreator taskCreator, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent)
public QuestController(IClientState clientState, IObjectTable objectTable, GameFunctions gameFunctions, QuestFunctions questFunctions, MovementController movementController, CombatController combatController, GatheringController gatheringController, ILogger<QuestController> logger, QuestRegistry questRegistry, IKeyState keyState, IChatGui chatGui, ICondition condition, IToastGui toastGui, Configuration configuration, TaskCreator taskCreator, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent)
: base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger)
{
_clientState = clientState;
_objectTable = objectTable;
_gameFunctions = gameFunctions;
_questFunctions = questFunctions;
_movementController = movementController;
@ -425,12 +429,12 @@ internal sealed class QuestController : MiniTaskController<QuestController>
_lastProgressUpdate = DateTime.Now;
return;
}
IPlayerCharacter localPlayer = _clientState.LocalPlayer;
if (localPlayer == null)
IGameObject gameObject = _objectTable[0];
if (gameObject == null)
{
return;
}
Vector3 position = localPlayer.Position;
Vector3 position = gameObject.Position;
if (CurrentQuest == null)
{
return;
@ -463,7 +467,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
{
if (_configuration.General.AutoStepRefreshEnabled && AutomationType == EAutomationType.Automatic && IsRunning && CurrentQuest != null && _clientState.IsLoggedIn)
{
return _clientState.LocalPlayer != null;
return _objectTable[0] != null;
}
return false;
}
@ -651,9 +655,9 @@ internal sealed class QuestController : MiniTaskController<QuestController>
{
_logger.LogInformation("New quest: {QuestName}", quest.Info.Name);
_startedQuest = new QuestProgress(quest, b);
if (_clientState.LocalPlayer != null && _clientState.LocalPlayer.Level < quest.Info.Level)
if (_objectTable[0] is IPlayerCharacter playerCharacter && playerCharacter.Level < quest.Info.Level)
{
_logger.LogInformation("Stopping automation, player level ({PlayerLevel}) < quest level ({QuestLevel}", _clientState.LocalPlayer.Level, quest.Info.Level);
_logger.LogInformation("Stopping automation, player level ({PlayerLevel}) < quest level ({QuestLevel}", playerCharacter.Level, quest.Info.Level);
Stop("Quest level too high");
return;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
using System.Collections.Generic;
using Dalamud.Plugin.Services;
using Lumina.Excel;
using Lumina.Excel.Sheets;
namespace Questionable.Data;
internal sealed class HuntMobData
{
private readonly HashSet<uint> _aRankDataIds = new HashSet<uint>();
private readonly HashSet<uint> _sRankDataIds = new HashSet<uint>();
private readonly HashSet<uint> _allHuntMobDataIds = new HashSet<uint>();
public const float SafeDistance = 15f;
public IReadOnlySet<uint> ARankDataIds => _aRankDataIds;
public IReadOnlySet<uint> SRankDataIds => _sRankDataIds;
public IReadOnlySet<uint> AllHuntMobDataIds => _allHuntMobDataIds;
public HuntMobData(IDataManager dataManager)
{
ExcelSheet<NotoriousMonster> excelSheet = dataManager.GetExcelSheet<NotoriousMonster>();
if (excelSheet == null)
{
return;
}
foreach (NotoriousMonster item in excelSheet)
{
byte rank = item.Rank;
if ((uint)(rank - 2) > 1u)
{
continue;
}
uint rowId = item.BNpcBase.RowId;
if (rowId != 0)
{
_allHuntMobDataIds.Add(rowId);
if (item.Rank == 2)
{
_aRankDataIds.Add(rowId);
}
else if (item.Rank == 3)
{
_sRankDataIds.Add(rowId);
}
}
}
}
public bool IsHuntMob(uint dataId)
{
return _allHuntMobDataIds.Contains(dataId);
}
public bool IsARank(uint dataId)
{
return _aRankDataIds.Contains(dataId);
}
public bool IsSRank(uint dataId)
{
return _sRankDataIds.Contains(dataId);
}
}

View file

@ -11,12 +11,18 @@ namespace Questionable.External;
internal sealed class NavmeshIpc
{
private readonly IDalamudPluginInterface _pluginInterface;
private readonly ILogger<NavmeshIpc> _logger;
private readonly ICallGateSubscriber<bool> _isNavReady;
private readonly ICallGateSubscriber<Vector3, Vector3, bool, CancellationToken, Task<List<Vector3>>> _navPathfind;
private readonly ICallGateSubscriber<bool> _navPathfindInProgress;
private readonly ICallGateSubscriber<int> _navNumQueuedRequests;
private readonly ICallGateSubscriber<List<Vector3>, bool, object> _pathMoveTo;
private readonly ICallGateSubscriber<object> _pathStop;
@ -29,6 +35,8 @@ internal sealed class NavmeshIpc
private readonly ICallGateSubscriber<Vector3, bool, float, Vector3?> _queryPointOnFloor;
private readonly ICallGateSubscriber<Vector3, float, float, Vector3?> _queryNearestPoint;
private readonly ICallGateSubscriber<float> _buildProgress;
public bool IsReady
@ -61,17 +69,51 @@ internal sealed class NavmeshIpc
}
}
public bool IsPathfindInProgress
{
get
{
try
{
return _navPathfindInProgress.InvokeFunc();
}
catch (IpcError)
{
return false;
}
}
}
public int NumQueuedPathfindRequests
{
get
{
try
{
return _navNumQueuedRequests.InvokeFunc();
}
catch (IpcError)
{
return 0;
}
}
}
public NavmeshIpc(IDalamudPluginInterface pluginInterface, ILogger<NavmeshIpc> logger)
{
_pluginInterface = pluginInterface;
_logger = logger;
_isNavReady = pluginInterface.GetIpcSubscriber<bool>("vnavmesh.Nav.IsReady");
_navPathfind = pluginInterface.GetIpcSubscriber<Vector3, Vector3, bool, CancellationToken, Task<List<Vector3>>>("vnavmesh.Nav.PathfindCancelable");
_navPathfindInProgress = pluginInterface.GetIpcSubscriber<bool>("vnavmesh.Nav.PathfindInProgress");
_navNumQueuedRequests = pluginInterface.GetIpcSubscriber<int>("vnavmesh.Nav.PathfindNumQueued");
_pathMoveTo = pluginInterface.GetIpcSubscriber<List<Vector3>, bool, object>("vnavmesh.Path.MoveTo");
_pathStop = pluginInterface.GetIpcSubscriber<object>("vnavmesh.Path.Stop");
_pathIsRunning = pluginInterface.GetIpcSubscriber<bool>("vnavmesh.Path.IsRunning");
_pathListWaypoints = pluginInterface.GetIpcSubscriber<List<Vector3>>("vnavmesh.Path.ListWaypoints");
_pathSetTolerance = pluginInterface.GetIpcSubscriber<float, object>("vnavmesh.Path.SetTolerance");
_queryPointOnFloor = pluginInterface.GetIpcSubscriber<Vector3, bool, float, Vector3?>("vnavmesh.Query.Mesh.PointOnFloor");
_queryNearestPoint = pluginInterface.GetIpcSubscriber<Vector3, float, float, Vector3?>("vnavmesh.Query.Mesh.NearestPoint");
_buildProgress = pluginInterface.GetIpcSubscriber<float>("vnavmesh.Nav.BuildProgress");
}
@ -101,6 +143,20 @@ internal sealed class NavmeshIpc
}
}
public Task<List<Vector3>> PathfindWithTolerance(Vector3 localPlayerPosition, Vector3 targetPosition, bool fly, float tolerance, CancellationToken cancellationToken)
{
try
{
_pathSetTolerance.InvokeAction(0.25f);
return _pluginInterface.GetIpcSubscriber<Vector3, Vector3, bool, float, CancellationToken, Task<List<Vector3>>>("vnavmesh.Nav.PathfindWithTolerance").InvokeFunc(localPlayerPosition, targetPosition, fly, tolerance, cancellationToken);
}
catch (IpcError exception)
{
_logger.LogWarning(exception, "Could not pathfind with tolerance via navmesh");
return Task.FromException<List<Vector3>>(exception);
}
}
public void MoveTo(List<Vector3> position, bool fly)
{
Stop();
@ -126,6 +182,30 @@ internal sealed class NavmeshIpc
}
}
public Vector3? GetPointOnFloor(Vector3 position, bool unlandable, float halfExtentXZ)
{
try
{
return _queryPointOnFloor.InvokeFunc(position, unlandable, halfExtentXZ);
}
catch (IpcError)
{
return null;
}
}
public Vector3? FindNearestMeshPoint(Vector3 position, float halfExtentXZ, float halfExtentY)
{
try
{
return _queryNearestPoint.InvokeFunc(position, halfExtentXZ, halfExtentY);
}
catch (IpcError)
{
return null;
}
}
public List<Vector3> GetWaypoints()
{
if (IsPathRunning)

View file

@ -22,16 +22,16 @@ internal sealed class AetheryteFunctions
private readonly IDataManager _dataManager;
private readonly IClientState _clientState;
private readonly IObjectTable _objectTable;
public DateTime ReturnRequestedAt { get; set; } = DateTime.MinValue;
public AetheryteFunctions(IServiceProvider serviceProvider, ILogger<AetheryteFunctions> logger, IDataManager dataManager, IClientState clientState)
public AetheryteFunctions(IServiceProvider serviceProvider, ILogger<AetheryteFunctions> logger, IDataManager dataManager, IObjectTable objectTable)
{
_serviceProvider = serviceProvider;
_logger = logger;
_dataManager = dataManager;
_clientState = clientState;
_objectTable = objectTable;
}
public unsafe bool IsAetheryteUnlocked(uint aetheryteId, out byte subIndex)
@ -114,7 +114,7 @@ internal sealed class AetheryteFunctions
public unsafe AetheryteRegistrationResult CanRegisterFreeOrFavoriteAetheryte(EAetheryteLocation aetheryteLocation)
{
if (_clientState.LocalPlayer == null)
if (_objectTable[0] == null)
{
return AetheryteRegistrationResult.NotPossible;
}

View file

@ -104,7 +104,7 @@ internal sealed class GameFunctions
public unsafe ushort? GetMountId()
{
BattleChara* ptr = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0);
BattleChara* ptr = (BattleChara*)(_objectTable.LocalPlayer?.Address ?? 0);
if (ptr != null && ptr->Mount.MountId != 0)
{
return ptr->Mount.MountId;
@ -203,14 +203,14 @@ internal sealed class GameFunctions
if (gameObject != null)
{
Vector3 position = gameObject.Position;
return ActionManager.Instance()->UseActionLocation(ActionType.KeyItem, itemId, 3758096384uL, &position, 0u, 0);
return ActionManager.Instance()->UseActionLocation(ActionType.EventItem, itemId, 3758096384uL, &position, 0u, 0);
}
return false;
}
public unsafe bool UseItemOnPosition(Vector3 position, uint itemId)
{
return ActionManager.Instance()->UseActionLocation(ActionType.KeyItem, itemId, 3758096384uL, &position, 0u, 0);
return ActionManager.Instance()->UseActionLocation(ActionType.EventItem, itemId, 3758096384uL, &position, 0u, 0);
}
public unsafe bool UseAction(EAction action)
@ -287,7 +287,7 @@ internal sealed class GameFunctions
{
return true;
}
IPlayerCharacter localPlayer = _clientState.LocalPlayer;
IPlayerCharacter localPlayer = _objectTable.LocalPlayer;
if (localPlayer == null)
{
return false;
@ -308,7 +308,7 @@ internal sealed class GameFunctions
private unsafe bool HasCharacterStatusPreventingMountOrSprint()
{
IPlayerCharacter localPlayer = _clientState.LocalPlayer;
IPlayerCharacter localPlayer = _objectTable.LocalPlayer;
if (localPlayer == null)
{
return false;
@ -324,7 +324,7 @@ internal sealed class GameFunctions
public unsafe bool HasStatus(EStatus statusId)
{
IPlayerCharacter localPlayer = _clientState.LocalPlayer;
IPlayerCharacter localPlayer = _objectTable.LocalPlayer;
if (localPlayer == null)
{
return false;
@ -423,7 +423,7 @@ internal sealed class GameFunctions
public unsafe bool IsOccupied()
{
if (!_clientState.IsLoggedIn || _clientState.LocalPlayer == null)
if (!_clientState.IsLoggedIn || _objectTable.LocalPlayer == null)
{
return true;
}
@ -547,7 +547,7 @@ internal sealed class GameFunctions
public unsafe bool SyncToFate(uint fateId)
{
IPlayerCharacter localPlayer = _clientState.LocalPlayer;
IPlayerCharacter localPlayer = _objectTable.LocalPlayer;
if (localPlayer == null)
{
_logger.LogWarning("Cannot sync to FATE: LocalPlayer is null");

View file

@ -44,6 +44,8 @@ internal sealed class QuestFunctions
private readonly IDataManager _dataManager;
private readonly IObjectTable _objectTable;
private readonly IClientState _clientState;
private readonly IGameGui _gameGui;
@ -68,7 +70,7 @@ internal sealed class QuestFunctions
private ElementId? _lastLoggedAcceptedHiddenMsq;
public QuestFunctions(QuestRegistry questRegistry, QuestData questData, AetheryteFunctions aetheryteFunctions, AlliedSocietyQuestFunctions alliedSocietyQuestFunctions, AlliedSocietyData alliedSocietyData, AetheryteData aetheryteData, Configuration configuration, IDataManager dataManager, IClientState clientState, IGameGui gameGui, IAetheryteList aetheryteList, ILogger<QuestFunctions> logger)
public QuestFunctions(QuestRegistry questRegistry, QuestData questData, AetheryteFunctions aetheryteFunctions, AlliedSocietyQuestFunctions alliedSocietyQuestFunctions, AlliedSocietyData alliedSocietyData, AetheryteData aetheryteData, Configuration configuration, IDataManager dataManager, IObjectTable objectTable, IClientState clientState, IGameGui gameGui, IAetheryteList aetheryteList, ILogger<QuestFunctions> logger)
{
_questRegistry = questRegistry;
_questData = questData;
@ -78,6 +80,7 @@ internal sealed class QuestFunctions
_aetheryteData = aetheryteData;
_configuration = configuration;
_dataManager = dataManager;
_objectTable = objectTable;
_clientState = clientState;
_gameGui = gameGui;
_aetheryteList = aetheryteList;
@ -163,14 +166,14 @@ internal sealed class QuestFunctions
}
questReference = QuestReference.NoQuest(questReference.State);
}
byte currentLevel = _clientState.LocalPlayer?.Level ?? 0;
byte currentLevel = _objectTable.LocalPlayer?.Level ?? 0;
if (questReference.CurrentQuest != null)
{
Questionable.Model.Quest quest;
bool flag = _questRegistry.TryGetQuest(questReference.CurrentQuest, out quest);
bool flag2 = IsQuestAccepted(questReference.CurrentQuest);
_logger.LogTrace("MSQ check: QuestId={QuestId}, InRegistry={InRegistry}, Level={QuestLevel}, CurrentLevel={CurrentLevel}, IsAccepted={IsAccepted}", questReference.CurrentQuest, flag, ((int?)quest?.Info.Level) ?? (-1), currentLevel, flag2);
EClassJob valueOrDefault = ((EClassJob?)_clientState.LocalPlayer?.ClassJob.RowId).GetValueOrDefault();
EClassJob valueOrDefault = ((EClassJob?)_objectTable.LocalPlayer?.ClassJob.RowId).GetValueOrDefault();
if (valueOrDefault != EClassJob.Adventurer)
{
QuestInfo questInfo = (from x in _questData.GetClassJobQuests(valueOrDefault)
@ -467,7 +470,7 @@ internal sealed class QuestFunctions
return (QuestReference.NoQuest(MainScenarioQuestState.Unavailable), $"Not readdy to accept quest {questId.Value}");
}
_lastLoggedNotReadyQuest = null;
if (!(_clientState.LocalPlayer?.Level).HasValue)
if (!(_objectTable.LocalPlayer?.Level).HasValue)
{
_logger.LogTrace("GetMainScenarioQuest: In loading screen");
return (QuestReference.NoQuest(MainScenarioQuestState.LoadingScreen), "In loading screen");
@ -492,7 +495,7 @@ internal sealed class QuestFunctions
private unsafe bool IsOnAlliedSocietyMount()
{
BattleChara* ptr = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0);
BattleChara* ptr = (BattleChara*)(_objectTable.LocalPlayer?.Address ?? 0);
if (ptr != null && ptr->Mount.MountId != 0)
{
return _alliedSocietyData.Mounts.ContainsKey(ptr->Mount.MountId);
@ -639,7 +642,7 @@ internal sealed class QuestFunctions
}
if (!_configuration.Advanced.SkipClassJobQuests)
{
EClassJob valueOrDefault = ((EClassJob?)_clientState.LocalPlayer?.ClassJob.RowId).GetValueOrDefault();
EClassJob valueOrDefault = ((EClassJob?)_objectTable.LocalPlayer?.ClassJob.RowId).GetValueOrDefault();
uint[] shadowbringersRoleQuestChapters = QuestData.AllRoleQuestChapters.Select((IReadOnlyList<uint> x) => x[0]).ToArray();
if (valueOrDefault != EClassJob.Adventurer)
{
@ -712,7 +715,7 @@ internal sealed class QuestFunctions
}
if (!ignoreLevel)
{
byte b = _clientState.LocalPlayer?.Level ?? 0;
byte b = _objectTable.LocalPlayer?.Level ?? 0;
if (b == 0)
{
return false;

View file

@ -31,11 +31,13 @@ internal sealed class StopConditionComponent : ConfigComponent
private readonly UiUtils _uiUtils;
private readonly IObjectTable _objectTable;
private readonly IClientState _clientState;
private readonly QuestController _questController;
public StopConditionComponent(IDalamudPluginInterface pluginInterface, QuestSelector questSelector, QuestFunctions questFunctions, QuestRegistry questRegistry, QuestTooltipComponent questTooltipComponent, UiUtils uiUtils, IClientState clientState, QuestController questController, Configuration configuration)
public StopConditionComponent(IDalamudPluginInterface pluginInterface, QuestSelector questSelector, QuestFunctions questFunctions, QuestRegistry questRegistry, QuestTooltipComponent questTooltipComponent, UiUtils uiUtils, IObjectTable objectTable, IClientState clientState, QuestController questController, Configuration configuration)
: base(pluginInterface, configuration)
{
StopConditionComponent stopConditionComponent = this;
@ -45,6 +47,7 @@ internal sealed class StopConditionComponent : ConfigComponent
_questFunctions = questFunctions;
_questTooltipComponent = questTooltipComponent;
_uiUtils = uiUtils;
_objectTable = objectTable;
_clientState = clientState;
_questController = questController;
_questSelector.SuggestionPredicate = (Quest quest) => configuration.Stop.QuestsToStopAfter.All((ElementId x) => x != quest.Id);
@ -88,7 +91,7 @@ internal sealed class StopConditionComponent : ConfigComponent
base.Configuration.Stop.TargetLevel = Math.Max(1, Math.Min(100, data));
Save();
}
int num = _clientState.LocalPlayer?.Level ?? 0;
int num = _objectTable.LocalPlayer?.Level ?? 0;
if (num > 0)
{
ImGui.SameLine();

View file

@ -43,6 +43,8 @@ internal sealed class ActiveQuestComponent
private readonly UiUtils _uiUtils;
private readonly IObjectTable _objectTable;
private readonly IClientState _clientState;
private readonly IChatGui _chatGui;
@ -58,7 +60,7 @@ internal sealed class ActiveQuestComponent
return _003CRegexGenerator_g_003EFBB8301322196CF81C64F1652C2FA6E1D6BF3907141F781E9D97ABED51BF056C4__MultipleWhitespaceRegex_0.Instance;
}
public ActiveQuestComponent(QuestController questController, MovementController movementController, CombatController combatController, GatheringController gatheringController, QuestFunctions questFunctions, ICommandManager commandManager, Configuration configuration, QuestRegistry questRegistry, PriorityWindow priorityWindow, UiUtils uiUtils, IClientState clientState, IChatGui chatGui, ILogger<ActiveQuestComponent> logger)
public ActiveQuestComponent(QuestController questController, MovementController movementController, CombatController combatController, GatheringController gatheringController, QuestFunctions questFunctions, ICommandManager commandManager, Configuration configuration, QuestRegistry questRegistry, PriorityWindow priorityWindow, UiUtils uiUtils, IObjectTable objectTable, IClientState clientState, IChatGui chatGui, ILogger<ActiveQuestComponent> logger)
{
_questController = questController;
_movementController = movementController;
@ -70,6 +72,7 @@ internal sealed class ActiveQuestComponent
_questRegistry = questRegistry;
_priorityWindow = priorityWindow;
_uiUtils = uiUtils;
_objectTable = objectTable;
_clientState = clientState;
_chatGui = chatGui;
_logger = logger;
@ -243,7 +246,7 @@ internal sealed class ActiveQuestComponent
Vector4 col = ImGuiColors.ParsedPurple;
if (flag)
{
int num = _clientState.LocalPlayer?.Level ?? 0;
int num = _objectTable.LocalPlayer?.Level ?? 0;
if (num > 0 && num >= _configuration.Stop.TargetLevel)
{
col = ImGuiColors.ParsedGreen;
@ -267,7 +270,7 @@ internal sealed class ActiveQuestComponent
ImGui.Separator();
if (flag)
{
int num2 = _clientState.LocalPlayer?.Level ?? 0;
int num2 = _objectTable.LocalPlayer?.Level ?? 0;
text = new ImU8String(14, 1);
text.AppendLiteral("Stop at level ");
text.AppendFormatted(_configuration.Stop.TargetLevel);

View file

@ -37,6 +37,8 @@ internal sealed class CreationUtilsComponent
private readonly QuestSelectionWindow _questSelectionWindow;
private readonly IObjectTable _objectTable;
private readonly IClientState _clientState;
private readonly ITargetManager _targetManager;
@ -49,7 +51,7 @@ internal sealed class CreationUtilsComponent
private readonly ILogger<CreationUtilsComponent> _logger;
public CreationUtilsComponent(MovementController movementController, GameFunctions gameFunctions, QuestFunctions questFunctions, TerritoryData territoryData, QuestData questData, QuestSelectionWindow questSelectionWindow, IClientState clientState, ITargetManager targetManager, ICondition condition, IGameGui gameGui, Configuration configuration, ILogger<CreationUtilsComponent> logger)
public CreationUtilsComponent(MovementController movementController, GameFunctions gameFunctions, QuestFunctions questFunctions, TerritoryData territoryData, QuestData questData, QuestSelectionWindow questSelectionWindow, IObjectTable objectTable, IClientState clientState, ITargetManager targetManager, ICondition condition, IGameGui gameGui, Configuration configuration, ILogger<CreationUtilsComponent> logger)
{
_movementController = movementController;
_gameFunctions = gameFunctions;
@ -57,6 +59,7 @@ internal sealed class CreationUtilsComponent
_territoryData = territoryData;
_questData = questData;
_questSelectionWindow = questSelectionWindow;
_objectTable = objectTable;
_clientState = clientState;
_targetManager = targetManager;
_condition = condition;
@ -129,16 +132,16 @@ internal sealed class CreationUtilsComponent
handler.AppendFormatted(value);
handler.AppendLiteral(")");
ImGui.Text(string.Create(provider, ref handler));
if (_clientState.LocalPlayer != null)
if (_objectTable.LocalPlayer != null)
{
invariantCulture = CultureInfo.InvariantCulture;
IFormatProvider provider2 = invariantCulture;
handler = new DefaultInterpolatedStringHandler(10, 1, invariantCulture);
handler.AppendLiteral("Distance: ");
handler.AppendFormatted((target.Position - _clientState.LocalPlayer.Position).Length(), "F2");
handler.AppendFormatted((target.Position - _objectTable.LocalPlayer.Position).Length(), "F2");
ImGui.Text(string.Create(provider2, ref handler));
ImGui.SameLine();
float value2 = target.Position.Y - _clientState.LocalPlayer.Position.Y;
float value2 = target.Position.Y - _objectTable.LocalPlayer.Position.Y;
invariantCulture = CultureInfo.InvariantCulture;
IFormatProvider provider3 = invariantCulture;
handler = new DefaultInterpolatedStringHandler(3, 1, invariantCulture);
@ -317,7 +320,7 @@ internal sealed class CreationUtilsComponent
private void DrawCopyButton()
{
if (_clientState.LocalPlayer != null)
if (_objectTable.LocalPlayer != null)
{
bool num = ImGuiComponents.IconButton(FontAwesomeIcon.Copy);
if (ImGui.IsItemHovered())
@ -328,11 +331,11 @@ internal sealed class CreationUtilsComponent
{
ImU8String clipboardText = new ImU8String(87, 4);
clipboardText.AppendLiteral("\"Position\": {\n \"X\": ");
clipboardText.AppendFormatted(_clientState.LocalPlayer.Position.X.ToString(CultureInfo.InvariantCulture));
clipboardText.AppendFormatted(_objectTable.LocalPlayer.Position.X.ToString(CultureInfo.InvariantCulture));
clipboardText.AppendLiteral(",\n \"Y\": ");
clipboardText.AppendFormatted(_clientState.LocalPlayer.Position.Y.ToString(CultureInfo.InvariantCulture));
clipboardText.AppendFormatted(_objectTable.LocalPlayer.Position.Y.ToString(CultureInfo.InvariantCulture));
clipboardText.AppendLiteral(",\n \"Z\": ");
clipboardText.AppendFormatted(_clientState.LocalPlayer.Position.Z.ToString(CultureInfo.InvariantCulture));
clipboardText.AppendFormatted(_objectTable.LocalPlayer.Position.Z.ToString(CultureInfo.InvariantCulture));
clipboardText.AppendLiteral("\n},\n\"TerritoryId\": ");
clipboardText.AppendFormatted(_clientState.TerritoryType);
clipboardText.AppendLiteral(",\n\"InteractionType\": \"\"");
@ -340,7 +343,7 @@ internal sealed class CreationUtilsComponent
}
else if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
Vector3 position = _clientState.LocalPlayer.Position;
Vector3 position = _objectTable.LocalPlayer.Position;
IFormatProvider invariantCulture = CultureInfo.InvariantCulture;
DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(12, 3, invariantCulture);
handler.AppendLiteral("new(");

View file

@ -75,7 +75,7 @@ internal sealed class DebugOverlay : Window
return;
}
IClientState clientState = _clientState;
if (clientState != null && clientState.IsLoggedIn && clientState.LocalPlayer != null && !clientState.IsPvPExcludingDen && _questController.IsQuestWindowOpen)
if (clientState != null && clientState.IsLoggedIn && !clientState.IsPvPExcludingDen && _objectTable.LocalPlayer != null && _questController.IsQuestWindowOpen)
{
DrawCurrentQuest();
DrawHighlightedQuest();
@ -103,7 +103,7 @@ internal sealed class DebugOverlay : Window
QuestStep questStep = questSequence.FindStep(i);
if (questStep != null && TryGetPosition(questStep, out var position))
{
DrawStep(i.ToString(CultureInfo.InvariantCulture), questStep, position.Value, (Vector3.Distance(_clientState.LocalPlayer.Position, position.Value) < questStep.CalculateActualStopDistance()) ? 4278255360u : 4278190335u);
DrawStep(i.ToString(CultureInfo.InvariantCulture), questStep, position.Value, (Vector3.Distance(_objectTable.LocalPlayer.Position, position.Value) < questStep.CalculateActualStopDistance()) ? 4278255360u : 4278190335u);
}
}
}
@ -141,7 +141,7 @@ internal sealed class DebugOverlay : Window
text.AppendLiteral("\n");
text.AppendFormatted(position.ToString("G", CultureInfo.InvariantCulture));
text.AppendLiteral(" [");
text.AppendFormatted((position - _clientState.LocalPlayer.Position).Length(), "N2");
text.AppendFormatted((position - _objectTable.LocalPlayer.Position).Length(), "N2");
text.AppendLiteral("]\n");
text.AppendFormatted(step.Comment);
windowDrawList.AddText(pos, color, text);
@ -175,7 +175,7 @@ internal sealed class DebugOverlay : Window
text.AppendLiteral(" - ");
text.AppendFormatted(item2);
text.AppendLiteral(", ");
text.AppendFormatted(Vector3.Distance(item3.Position, _clientState.LocalPlayer.Position), "N2");
text.AppendFormatted(Vector3.Distance(item3.Position, _objectTable.LocalPlayer.Position), "N2");
text.AppendLiteral(", ");
text.AppendFormatted(item3.IsTargetable);
windowDrawList.AddText(pos, (uint)col, text);

View file

@ -22,6 +22,8 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
private readonly QuestController _questController;
private readonly IObjectTable _objectTable;
private readonly IClientState _clientState;
private readonly Configuration _configuration;
@ -50,12 +52,13 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
public bool IsMinimized { get; set; }
public QuestWindow(IDalamudPluginInterface pluginInterface, QuestController questController, IClientState clientState, Configuration configuration, TerritoryData territoryData, ActiveQuestComponent activeQuestComponent, ARealmRebornComponent aRealmRebornComponent, EventInfoComponent eventInfoComponent, CreationUtilsComponent creationUtilsComponent, QuickAccessButtonsComponent quickAccessButtonsComponent, RemainingTasksComponent remainingTasksComponent, IFramework framework, InteractionUiController interactionUiController, ConfigWindow configWindow)
public QuestWindow(IDalamudPluginInterface pluginInterface, QuestController questController, IObjectTable objectTable, IClientState clientState, Configuration configuration, TerritoryData territoryData, ActiveQuestComponent activeQuestComponent, ARealmRebornComponent aRealmRebornComponent, EventInfoComponent eventInfoComponent, CreationUtilsComponent creationUtilsComponent, QuickAccessButtonsComponent quickAccessButtonsComponent, RemainingTasksComponent remainingTasksComponent, IFramework framework, InteractionUiController interactionUiController, ConfigWindow configWindow)
: base("Questionable v" + PluginVersion.ToString(3) + "###Questionable", ImGuiWindowFlags.AlwaysAutoResize)
{
QuestWindow questWindow = this;
_pluginInterface = pluginInterface;
_questController = questController;
_objectTable = objectTable;
_clientState = clientState;
_configuration = configuration;
_territoryData = territoryData;
@ -134,7 +137,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
{
return false;
}
if (!_clientState.IsLoggedIn || _clientState.LocalPlayer == null || _clientState.IsPvPExcludingDen)
if (!_clientState.IsLoggedIn || _objectTable.LocalPlayer == null || _clientState.IsPvPExcludingDen)
{
return false;
}