1
0
forked from liza/Questionable

Add Wachumeqimeqi deliveries

This commit is contained in:
Liza 2024-08-18 01:55:38 +02:00
parent 8a011bb1f4
commit 4aee22510b
Signed by: liza
GPG Key ID: 7199F8D727D55F67
21 changed files with 1373 additions and 209 deletions

View File

@ -0,0 +1,178 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
"Author": "liza",
"Steps": [
{
"Position": {
"X": 417.1447,
"Y": -0.6,
"Z": -647.60004
},
"TerritoryId": 1189,
"InteractionType": "Dive",
"AetheryteShortcut": "Yak T'el - Iq Br'aax",
"SkipConditions": {
"StepIf": {
"Flying": "Unlocked"
},
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
},
{
"Position": {
"X": 417.1447,
"Y": 3,
"Z": -647.60004
},
"TerritoryId": 1189,
"InteractionType": "WalkTo",
"Fly": true,
"SkipConditions": {
"StepIf": {
"Flying": "Locked"
}
}
},
{
"Position": {
"X": 419.8578,
"Y": -32.6974,
"Z": -653.75275
},
"TerritoryId": 1189,
"InteractionType": "WalkTo",
"DisableNavmesh": true,
"Fly": true
}
],
"Groups": [
{
"Nodes": [
{
"DataId": 34912,
"Locations": [
{
"Position": {
"X": 458.8916,
"Y": -51.02777,
"Z": -689.8627
}
},
{
"Position": {
"X": 430.228,
"Y": -56.21914,
"Z": -693.9346
}
},
{
"Position": {
"X": 462.8787,
"Y": -58.29268,
"Z": -704.244
}
}
]
},
{
"DataId": 34911,
"Locations": [
{
"Position": {
"X": 448.169,
"Y": -53.1458,
"Z": -696.1208
}
}
]
}
]
},
{
"Nodes": [
{
"DataId": 34914,
"Locations": [
{
"Position": {
"X": 453.7438,
"Y": -59.20442,
"Z": -884.0787
}
},
{
"Position": {
"X": 399.0516,
"Y": -48.41589,
"Z": -900.1575
}
},
{
"Position": {
"X": 470.4918,
"Y": -54.81378,
"Z": -912.1257
}
}
]
},
{
"DataId": 34913,
"Locations": [
{
"Position": {
"X": 433.2036,
"Y": -56.63199,
"Z": -898.0532
}
}
]
}
]
},
{
"Nodes": [
{
"DataId": 34915,
"Locations": [
{
"Position": {
"X": 263.8979,
"Y": -44.71192,
"Z": -873.9875
}
}
]
},
{
"DataId": 34916,
"Locations": [
{
"Position": {
"X": 287.7073,
"Y": -43.04572,
"Z": -886.5245
}
},
{
"Position": {
"X": 266.3744,
"Y": -47.55014,
"Z": -846.1501
}
},
{
"Position": {
"X": 259.2106,
"Y": -44.82758,
"Z": -817.9664
}
}
]
}
]
}
]
}

View File

@ -0,0 +1,200 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
"Author": "liza",
"Steps": [
{
"Position": {
"X": 417.1447,
"Y": -0.6,
"Z": -647.60004
},
"TerritoryId": 1189,
"InteractionType": "Dive",
"AetheryteShortcut": "Yak T'el - Iq Br'aax",
"SkipConditions": {
"StepIf": {
"Flying": "Unlocked"
},
"AetheryteShortcutIf": {
"InSameTerritory": true
}
}
},
{
"Position": {
"X": 417.1447,
"Y": 3,
"Z": -647.60004
},
"TerritoryId": 1189,
"InteractionType": "WalkTo",
"Fly": true,
"SkipConditions": {
"StepIf": {
"Flying": "Locked"
}
}
},
{
"Position": {
"X": 419.8578,
"Y": -32.6974,
"Z": -653.75275
},
"TerritoryId": 1189,
"InteractionType": "WalkTo",
"DisableNavmesh": true,
"Fly": true
}
],
"Groups": [
{
"Nodes": [
{
"DataId": 34787,
"Locations": [
{
"Position": {
"X": 482.7197,
"Y": -38.14573,
"Z": -612.8046
},
"MinimumAngle": 100,
"MaximumAngle": 275
}
]
},
{
"DataId": 34788,
"Locations": [
{
"Position": {
"X": 503.5652,
"Y": -41.40348,
"Z": -600.9512
},
"MinimumAngle": 185,
"MaximumAngle": 275
},
{
"Position": {
"X": 441.1733,
"Y": -36.58192,
"Z": -610.3331
},
"MinimumAngle": 120,
"MaximumAngle": 265
},
{
"Position": {
"X": 457.5484,
"Y": -40.0437,
"Z": -608.3312
},
"MinimumAngle": 115,
"MaximumAngle": 240
}
]
}
]
},
{
"Nodes": [
{
"DataId": 34790,
"Locations": [
{
"Position": {
"X": 584.035,
"Y": -49.84215,
"Z": -759.925
},
"MinimumAngle": 115,
"MaximumAngle": 240
},
{
"Position": {
"X": 624.3585,
"Y": -61.07853,
"Z": -748.2542
}
},
{
"Position": {
"X": 605.4849,
"Y": -59.0002,
"Z": -772.6049
},
"MinimumAngle": 175,
"MaximumAngle": 275
}
]
},
{
"DataId": 34789,
"Locations": [
{
"Position": {
"X": 601.6854,
"Y": -53.68699,
"Z": -741.3439
},
"MinimumAngle": 185,
"MaximumAngle": 355
}
]
}
]
},
{
"Nodes": [
{
"DataId": 34785,
"Locations": [
{
"Position": {
"X": 754.1298,
"Y": -57.09224,
"Z": -571.5818
},
"MinimumAngle": 100,
"MaximumAngle": 250
}
]
},
{
"DataId": 34786,
"Locations": [
{
"Position": {
"X": 734.2795,
"Y": -55.15427,
"Z": -573.6763
},
"MinimumAngle": 90,
"MaximumAngle": 260
},
{
"Position": {
"X": 714.931,
"Y": -53.3118,
"Z": -569.4072
},
"MinimumAngle": 115,
"MaximumAngle": 250
},
{
"Position": {
"X": 773.049,
"Y": -55.97124,
"Z": -569.7167
},
"MinimumAngle": 105,
"MaximumAngle": 240
}
]
}
]
}
]
}

View File

@ -28,7 +28,8 @@
"Z": -8.316223 "Z": -8.316223
}, },
"TerritoryId": 1185, "TerritoryId": 1185,
"InteractionType": "CompleteQuest" "InteractionType": "CompleteQuest",
"NextQuestId": 4990
} }
] ]
} }

View File

@ -20,6 +20,22 @@
{ {
"Sequence": 255, "Sequence": 255,
"Steps": [ "Steps": [
{
"TerritoryId": 1185,
"InteractionType": "None",
"RequiredGatheredItems": [
{
"ItemId": 43899,
"ItemCount": 6,
"Collectability": 600
}
],
"AetheryteShortcut": "Tuliyollal",
"AethernetShortcut": [
"[Tuliyollal] Aetheryte Plaza",
"[Tuliyollal] Wachumeqimeqi"
]
},
{ {
"DataId": 1047132, "DataId": 1047132,
"Position": { "Position": {
@ -28,7 +44,8 @@
"Z": -5.6916504 "Z": -5.6916504
}, },
"TerritoryId": 1185, "TerritoryId": 1185,
"InteractionType": "CompleteQuest" "InteractionType": "CompleteQuest",
"NextQuestId": 4991
} }
] ]
} }

View File

@ -0,0 +1,53 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"TerritoryId": 1185,
"InteractionType": "None",
"RequiredGatheredItems": [
{
"ItemId": 43900,
"ItemCount": 6,
"Collectability": 600
}
],
"AetheryteShortcut": "Tuliyollal",
"AethernetShortcut": [
"[Tuliyollal] Aetheryte Plaza",
"[Tuliyollal] Wachumeqimeqi"
]
},
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "CompleteQuest",
"NextQuestId": 4992
}
]
}
]
}

View File

@ -0,0 +1,53 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"TerritoryId": 1185,
"InteractionType": "None",
"RequiredGatheredItems": [
{
"ItemId": 43901,
"ItemCount": 6,
"Collectability": 600
}
],
"AetheryteShortcut": "Tuliyollal",
"AethernetShortcut": [
"[Tuliyollal] Aetheryte Plaza",
"[Tuliyollal] Wachumeqimeqi"
]
},
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "CompleteQuest",
"NextQuestId": 4993
}
]
}
]
}

View File

@ -0,0 +1,71 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1047153,
"Position": {
"X": -270.4662,
"Y": 40.0732,
"Z": -12.253052
},
"TerritoryId": 1185,
"InteractionType": "Interact",
"AethernetShortcut": [
"[Tuliyollal] Wachumeqimeqi",
"[Tuliyollal] The Resplendent Quarter"
]
}
]
},
{
"Sequence": 255,
"Steps": [
{
"TerritoryId": 1185,
"InteractionType": "None",
"RequiredGatheredItems": [
{
"ItemId": 43913,
"ItemCount": 1
}
],
"AetheryteShortcut": "Tuliyollal",
"AethernetShortcut": [
"[Tuliyollal] Aetheryte Plaza",
"[Tuliyollal] Wachumeqimeqi"
]
},
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "CompleteQuest",
"NextQuestId": 4994
}
]
}
]
}

View File

@ -0,0 +1,53 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 255,
"Steps": [
{
"TerritoryId": 1185,
"InteractionType": "None",
"RequiredGatheredItems": [
{
"ItemId": 43902,
"ItemCount": 6,
"Collectability": 600
}
],
"AetheryteShortcut": "Tuliyollal",
"AethernetShortcut": [
"[Tuliyollal] Aetheryte Plaza",
"[Tuliyollal] Wachumeqimeqi"
]
},
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "CompleteQuest",
"NextQuestId": 4995
}
]
}
]
}

View File

@ -0,0 +1,284 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"QuestSequence": [
{
"Sequence": 0,
"Steps": [
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "AcceptQuest"
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1047155,
"Position": {
"X": 746.24243,
"Y": -133.18861,
"Z": 507.5608
},
"TerritoryId": 1189,
"InteractionType": "Interact",
"AetheryteShortcut": "Yak T'el - Mamook"
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1048981,
"Position": {
"X": 686.51855,
"Y": -137.174,
"Z": 534.8745
},
"TerritoryId": 1189,
"InteractionType": "Interact",
"Fly": true,
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
16
]
},
{
"DataId": 1048973,
"Position": {
"X": 661.86,
"Y": -135.17876,
"Z": 582.11633
},
"StopDistance": 4,
"TerritoryId": 1189,
"InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
]
},
{
"DataId": 1047156,
"Position": {
"X": 632.5017,
"Y": -137.17401,
"Z": 590.8445
},
"TerritoryId": 1189,
"InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
32
]
},
{
"DataId": 1048974,
"Position": {
"X": 621.51514,
"Y": -135.12726,
"Z": 531.1207
},
"TerritoryId": 1189,
"InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
]
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1047158,
"Position": {
"X": 539.69617,
"Y": -142.49185,
"Z": 481.65088
},
"TerritoryId": 1189,
"InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
],
"Fly": true
},
{
"DataId": 1047157,
"Position": {
"X": 586.26685,
"Y": -142.4984,
"Z": 462.97388
},
"TerritoryId": 1189,
"InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
],
"Fly": true
}
]
},
{
"Sequence": 4,
"Steps": [
{
"DataId": 1047159,
"Position": {
"X": 191.45496,
"Y": -160.64616,
"Z": 414.0536
},
"TerritoryId": 1189,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 5,
"Steps": [
{
"DataId": 1047160,
"Position": {
"X": 664.6067,
"Y": 1.554378,
"Z": -477.22595
},
"TerritoryId": 1189,
"InteractionType": "Interact",
"AetheryteShortcut": "Yak T'el - Mamook",
"Fly": true
}
]
},
{
"Sequence": 6,
"Steps": [
{
"Position": {
"X": 436.87848,
"Y": 4.0999737,
"Z": -551.09174
},
"TerritoryId": 1189,
"InteractionType": "WalkTo",
"SkipConditions": {
"StepIf": {
"Flying": "Unlocked"
}
}
},
{
"Position": {
"X": 674.17834,
"Y": -33.187485,
"Z": -598.0982
},
"TerritoryId": 1189,
"InteractionType": "WalkTo",
"Fly": true,
"RequiredGatheredItems": [
{
"ItemId": 43914,
"ItemCount": 1
}
]
},
{
"Position": {
"X": 674.17834,
"Y": -0.6,
"Z": -598.0982
},
"TerritoryId": 1189,
"InteractionType": "WalkTo",
"Fly": true,
"DisableNavmesh": true
},
{
"DataId": 1047160,
"Position": {
"X": 664.6067,
"Y": 1.554378,
"Z": -477.22595
},
"TerritoryId": 1189,
"InteractionType": "Interact",
"Fly": true
}
]
},
{
"Sequence": 7,
"Steps": [
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "Interact",
"AetheryteShortcut": "Tuliyollal",
"AethernetShortcut": [
"[Tuliyollal] Aetheryte Plaza",
"[Tuliyollal] Wachumeqimeqi"
]
}
]
},
{
"Sequence": 255,
"Steps": [
{
"DataId": 1047132,
"Position": {
"X": 217.36475,
"Y": -14.000001,
"Z": -5.6916504
},
"TerritoryId": 1185,
"InteractionType": "CompleteQuest"
}
]
}
]
}

View File

@ -0,0 +1,123 @@
using System;
using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameUI;
using Microsoft.Extensions.Logging;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Questionable.Controller.GameUi;
internal sealed class CraftworksSupplyController : IDisposable
{
private readonly QuestController _questController;
private readonly IAddonLifecycle _addonLifecycle;
private readonly IGameGui _gameGui;
private readonly IFramework _framework;
private readonly ILogger<CraftworksSupplyController> _logger;
public CraftworksSupplyController(QuestController questController, IAddonLifecycle addonLifecycle,
IGameGui gameGui, IFramework framework, ILogger<CraftworksSupplyController> logger)
{
_questController = questController;
_addonLifecycle = addonLifecycle;
_gameGui = gameGui;
_framework = framework;
_logger = logger;
_addonLifecycle.RegisterListener(AddonEvent.PostReceiveEvent, "ContextIconMenu", ContextIconMenuPostReceiveEvent);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "BankaCraftworksSupply",
BankaCraftworksSupplyPostUpdate);
}
private bool ShouldHandleUiInteractions => _questController.IsRunning;
private unsafe void BankaCraftworksSupplyPostUpdate(AddonEvent type, AddonArgs args)
{
if (!ShouldHandleUiInteractions)
return;
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
InteractWithBankaCraftworksSupply(addon);
}
private unsafe void InteractWithBankaCraftworksSupply()
{
if (_gameGui.TryGetAddonByName("BankaCraftworksSupply", out AtkUnitBase* addon))
InteractWithBankaCraftworksSupply(addon);
}
private unsafe void InteractWithBankaCraftworksSupply(AtkUnitBase* addon)
{
AtkValue* atkValues = addon->AtkValues;
uint completedCount = atkValues[7].UInt;
uint missingCount = 6 - completedCount;
for (int slot = 0; slot < missingCount; ++slot)
{
if (atkValues[31 + slot].UInt != 0)
continue;
_logger.LogInformation("Selecting an item for slot {Slot}", slot);
var selectSlot = stackalloc AtkValue[]
{
new() { Type = ValueType.Int, Int = 2 },
new() { Type = ValueType.Int, Int = slot /* slot */ },
};
addon->FireCallback(2, selectSlot);
return;
}
// do turn-in if any item is provided
if (atkValues[31].UInt != 0)
{
_logger.LogInformation("Confirming turn-in");
addon->FireCallbackInt(0);
}
}
// FIXME: This seems to not work if the mouse isn't over the FFXIV window?
private unsafe void ContextIconMenuPostReceiveEvent(AddonEvent type, AddonArgs args)
{
if (!ShouldHandleUiInteractions)
return;
AddonContextIconMenu* addonContextIconMenu = (AddonContextIconMenu*)args.Addon;
if (!addonContextIconMenu->IsVisible)
return;
ushort parentId = addonContextIconMenu->ContextMenuParentId;
if (parentId == 0)
return;
AtkUnitBase* parentAddon = AtkStage.Instance()->RaptureAtkUnitManager->GetAddonById(parentId);
if (parentAddon->NameString is "BankaCraftworksSupply")
{
_logger.LogInformation("Picking item for {AddonName}", parentAddon->NameString);
var selectSlot = stackalloc AtkValue[]
{
new() { Type = ValueType.Int, Int = 0 },
new() { Type = ValueType.Int, Int = 0 /* slot */ },
new() { Type = ValueType.UInt, UInt = 20802 /* probably the item's icon */ },
new() { Type = ValueType.UInt, UInt = 0 },
new() { Type = 0, Int = 0 },
};
addonContextIconMenu->FireCallback(5, selectSlot);
addonContextIconMenu->Close(true);
if (parentAddon->NameString == "BankaCraftworksSupply")
_framework.RunOnTick(InteractWithBankaCraftworksSupply, TimeSpan.FromMilliseconds(50));
}
else
_logger.LogTrace("Ignoring contextmenu event for {AddonName}", parentAddon->NameString);
}
public void Dispose()
{
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "BankaCraftworksSupply",
BankaCraftworksSupplyPostUpdate);
_addonLifecycle.UnregisterListener(AddonEvent.PostReceiveEvent, "ContextIconMenu", ContextIconMenuPostReceiveEvent);
}
}

View File

@ -0,0 +1,59 @@
using System;
using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Microsoft.Extensions.Logging;
namespace Questionable.Controller.GameUi;
internal sealed class CreditsController : IDisposable
{
private readonly IAddonLifecycle _addonLifecycle;
private readonly ILogger<CreditsController> _logger;
public CreditsController(IAddonLifecycle addonLifecycle, ILogger<CreditsController> logger)
{
_addonLifecycle = addonLifecycle;
_logger = logger;
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "CreditScroll", CreditScrollPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "Credit", CreditPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "CreditPlayer", CreditPlayerPostSetup);
}
/// <summary>
/// ARR Credits.
/// </summary>
private unsafe void CreditScrollPostSetup(AddonEvent type, AddonArgs args)
{
_logger.LogInformation("Closing Credits sequence");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->FireCallbackInt(-2);
}
/// <summary>
/// Credits for (possibly all?) expansions, not used for ARR.
/// </summary>
private unsafe void CreditPostSetup(AddonEvent type, AddonArgs args)
{
_logger.LogInformation("Closing Credits sequence");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->FireCallbackInt(-2);
}
private unsafe void CreditPlayerPostSetup(AddonEvent type, AddonArgs args)
{
_logger.LogInformation("Closing CreditPlayer");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->Close(true);
}
public void Dispose()
{
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "CreditPlayer", CreditPlayerPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "Credit", CreditPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "CreditScroll", CreditScrollPostSetup);
}
}

View File

@ -0,0 +1,78 @@
using System;
using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Microsoft.Extensions.Logging;
namespace Questionable.Controller.GameUi;
internal sealed class HelpUiController : IDisposable
{
private readonly QuestController _questController;
private readonly IAddonLifecycle _addonLifecycle;
private readonly ILogger<HelpUiController> _logger;
public HelpUiController(QuestController questController, IAddonLifecycle addonLifecycle, ILogger<HelpUiController> logger)
{
_questController = questController;
_addonLifecycle = addonLifecycle;
_logger = logger;
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "AkatsukiNote", UnendingCodexPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "ContentsTutorial", ContentsTutorialPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "MultipleHelpWindow", MultipleHelpWindowPostSetup);
}
private bool ShouldHandleUiInteractions => _questController.IsRunning;
private unsafe void UnendingCodexPostSetup(AddonEvent type, AddonArgs args)
{
if (!ShouldHandleUiInteractions)
return;
if (_questController.StartedQuest?.Quest.Id.Value == 4526)
{
_logger.LogInformation("Closing Unending Codex");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->FireCallbackInt(-2);
}
}
private unsafe void ContentsTutorialPostSetup(AddonEvent type, AddonArgs args)
{
if (!ShouldHandleUiInteractions)
return;
if (_questController.StartedQuest?.Quest.Id.Value == 245)
{
_logger.LogInformation("Closing ContentsTutorial");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->FireCallbackInt(13);
}
}
/// <summary>
/// Opened e.g. the first time you open the duty finder window during Sastasha.
/// </summary>
private unsafe void MultipleHelpWindowPostSetup(AddonEvent type, AddonArgs args)
{
if (!ShouldHandleUiInteractions)
return;
if (_questController.StartedQuest?.Quest.Id.Value == 245)
{
_logger.LogInformation("Closing MultipleHelpWindow");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->FireCallbackInt(-2);
addon->FireCallbackInt(-1);
}
}
public void Dispose()
{
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "MultipleHelpWindow", MultipleHelpWindowPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "ContentsTutorial", ContentsTutorialPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "AkatsukiNote", UnendingCodexPostSetup);
}
}

View File

@ -10,7 +10,6 @@ using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Event; using FFXIVClientStructs.FFXIV.Client.Game.Event;
using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib; using LLib;
using LLib.GameData; using LLib.GameData;
@ -25,12 +24,13 @@ using Questionable.Model.Common;
using Questionable.Model.Gathering; using Questionable.Model.Gathering;
using Questionable.Model.Questing; using Questionable.Model.Questing;
using AethernetShortcut = Questionable.Controller.Steps.Shared.AethernetShortcut; using AethernetShortcut = Questionable.Controller.Steps.Shared.AethernetShortcut;
using EAetheryteLocationExtensions = Questionable.Model.Common.EAetheryteLocationExtensions;
using Quest = Questionable.Model.Quest; using Quest = Questionable.Model.Quest;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Questionable.Controller; namespace Questionable.Controller.GameUi;
internal sealed class GameUiController : IDisposable internal sealed class InteractionUiController : IDisposable
{ {
private readonly IAddonLifecycle _addonLifecycle; private readonly IAddonLifecycle _addonLifecycle;
private readonly IDataManager _dataManager; private readonly IDataManager _dataManager;
@ -44,14 +44,13 @@ internal sealed class GameUiController : IDisposable
private readonly QuestData _questData; private readonly QuestData _questData;
private readonly IGameGui _gameGui; private readonly IGameGui _gameGui;
private readonly ITargetManager _targetManager; private readonly ITargetManager _targetManager;
private readonly IFramework _framework;
private readonly IClientState _clientState; private readonly IClientState _clientState;
private readonly ILogger<GameUiController> _logger; private readonly ILogger<InteractionUiController> _logger;
private readonly Regex _returnRegex; private readonly Regex _returnRegex;
private bool _isInitialCheck; private bool _isInitialCheck;
public GameUiController( public InteractionUiController(
IAddonLifecycle addonLifecycle, IAddonLifecycle addonLifecycle,
IDataManager dataManager, IDataManager dataManager,
QuestFunctions questFunctions, QuestFunctions questFunctions,
@ -67,7 +66,7 @@ internal sealed class GameUiController : IDisposable
IFramework framework, IFramework framework,
IPluginLog pluginLog, IPluginLog pluginLog,
IClientState clientState, IClientState clientState,
ILogger<GameUiController> logger) ILogger<InteractionUiController> logger)
{ {
_addonLifecycle = addonLifecycle; _addonLifecycle = addonLifecycle;
_dataManager = dataManager; _dataManager = dataManager;
@ -81,7 +80,6 @@ internal sealed class GameUiController : IDisposable
_questData = questData; _questData = questData;
_gameGui = gameGui; _gameGui = gameGui;
_targetManager = targetManager; _targetManager = targetManager;
_framework = framework;
_clientState = clientState; _clientState = clientState;
_logger = logger; _logger = logger;
@ -92,15 +90,7 @@ internal sealed class GameUiController : IDisposable
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectIconString", SelectIconStringPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectIconString", SelectIconStringPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectYesno", SelectYesnoPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectYesno", SelectYesnoPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "CreditScroll", CreditScrollPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "Credit", CreditPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "CreditPlayer", CreditPlayerPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "AkatsukiNote", UnendingCodexPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "ContentsTutorial", ContentsTutorialPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "MultipleHelpWindow", MultipleHelpWindowPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "JournalResult", JournalResultPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "GuildLeve", GuildLevePostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "TelepotTown", TeleportTownPostSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "TelepotTown", TeleportTownPostSetup);
} }
@ -774,76 +764,6 @@ internal sealed class GameUiController : IDisposable
currentQuest.IncreasePointMenuCounter(); currentQuest.IncreasePointMenuCounter();
} }
/// <summary>
/// ARR Credits.
/// </summary>
private unsafe void CreditScrollPostSetup(AddonEvent type, AddonArgs args)
{
_logger.LogInformation("Closing Credits sequence");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->FireCallbackInt(-2);
}
/// <summary>
/// Credits for (possibly all?) expansions, not used for ARR.
/// </summary>
private unsafe void CreditPostSetup(AddonEvent type, AddonArgs args)
{
_logger.LogInformation("Closing Credits sequence");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->FireCallbackInt(-2);
}
private unsafe void CreditPlayerPostSetup(AddonEvent type, AddonArgs args)
{
_logger.LogInformation("Closing CreditPlayer");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->Close(true);
}
private unsafe void UnendingCodexPostSetup(AddonEvent type, AddonArgs args)
{
if (!ShouldHandleUiInteractions)
return;
if (_questController.StartedQuest?.Quest.Id.Value == 4526)
{
_logger.LogInformation("Closing Unending Codex");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->FireCallbackInt(-2);
}
}
private unsafe void ContentsTutorialPostSetup(AddonEvent type, AddonArgs args)
{
if (!ShouldHandleUiInteractions)
return;
if (_questController.StartedQuest?.Quest.Id.Value == 245)
{
_logger.LogInformation("Closing ContentsTutorial");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->FireCallbackInt(13);
}
}
/// <summary>
/// Opened e.g. the first time you open the duty finder window during Sastasha.
/// </summary>
private unsafe void MultipleHelpWindowPostSetup(AddonEvent type, AddonArgs args)
{
if (!ShouldHandleUiInteractions)
return;
if (_questController.StartedQuest?.Quest.Id.Value == 245)
{
_logger.LogInformation("Closing MultipleHelpWindow");
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
addon->FireCallbackInt(-2);
addon->FireCallbackInt(-1);
}
}
private unsafe void HousingSelectBlockPostSetup(AddonEvent type, AddonArgs args) private unsafe void HousingSelectBlockPostSetup(AddonEvent type, AddonArgs args)
{ {
if (!ShouldHandleUiInteractions) if (!ShouldHandleUiInteractions)
@ -854,111 +774,11 @@ internal sealed class GameUiController : IDisposable
addon->FireCallbackInt(0); addon->FireCallbackInt(0);
} }
private unsafe void JournalResultPostSetup(AddonEvent type, AddonArgs args)
{
if (!ShouldHandleUiInteractions)
return;
_logger.LogInformation("Checking for quest name of journal result");
AddonJournalResult* addon = (AddonJournalResult*)args.Addon;
string questName = addon->AtkTextNode250->NodeText.ToString();
if (_questController.CurrentQuest is { Quest.Id: LeveId } &&
GameFunctions.GameStringEquals(_questController.CurrentQuest.Quest.Info.Name, questName))
{
_logger.LogInformation("JournalResult has the current leve, auto-accepting it");
addon->FireCallbackInt(0);
}
else if (_targetManager.Target is { } target)
{
var issuedLeves = _questData.GetAllByIssuerDataId(target.DataId)
.Where(x => x.QuestId is LeveId)
.ToList();
if (issuedLeves.Any(x => GameFunctions.GameStringEquals(x.Name, questName)))
{
_logger.LogInformation(
"JournalResult has a leve but not the one we're currently on, auto-declining it");
addon->FireCallbackInt(1);
}
}
}
private unsafe void GuildLevePostSetup(AddonEvent type, AddonArgs args)
{
var target = _targetManager.Target;
if (target == null)
return;
if (_questController is { IsRunning: true, NextQuest: { Quest.Id: LeveId } nextQuest } &&
_questFunctions.IsReadyToAcceptQuest(nextQuest.Quest.Id))
{
var addon = (AddonGuildLeve*)args.Addon;
/*
var atkValues = addon->AtkValues;
var availableLeves = _questData.GetAllByIssuerDataId(target.DataId);
List<(int, IQuestInfo)> offeredLeves = [];
for (int i = 0; i <= 20; ++i) // 3 leves per group, 1 label for group
{
string? leveName = atkValues[626 + i * 2].ReadAtkString();
if (leveName == null)
continue;
var questInfo = availableLeves.FirstOrDefault(x => GameFunctions.GameStringEquals(x.Name, leveName));
if (questInfo == null)
continue;
offeredLeves.Add((i, questInfo));
}
foreach (var (i, questInfo) in offeredLeves)
_logger.LogInformation("Leve {Index} = {Id}, {Name}", i, questInfo.QuestId, questInfo.Name);
*/
_framework.RunOnTick(() => AcceptLeveOrWait(nextQuest), TimeSpan.FromMilliseconds(100));
}
}
private unsafe void AcceptLeveOrWait(QuestController.QuestProgress nextQuest, int counter = 0)
{
var agent = UIModule.Instance()->GetAgentModule()->GetAgentByInternalId(AgentId.LeveQuest);
if (agent->IsAgentActive() &&
_gameGui.TryGetAddonByName("GuildLeve", out AddonGuildLeve* addonGuildLeve) &&
LAddon.IsAddonReady(&addonGuildLeve->AtkUnitBase) &&
_gameGui.TryGetAddonByName("JournalDetail", out AtkUnitBase* addonJournalDetail) &&
LAddon.IsAddonReady(addonJournalDetail))
{
AcceptLeve(agent, addonGuildLeve, nextQuest);
}
else if (counter >= 10)
_logger.LogWarning("Unable to accept leve?");
else
_framework.RunOnTick(() => AcceptLeveOrWait(nextQuest, counter + 1), TimeSpan.FromMilliseconds(100));
}
private unsafe void AcceptLeve(AgentInterface* agent, AddonGuildLeve* addon,
QuestController.QuestProgress nextQuest)
{
_questController.SetPendingQuest(nextQuest);
_questController.SetNextQuest(null);
var returnValue = stackalloc AtkValue[1];
var selectQuest = stackalloc AtkValue[]
{
new() { Type = ValueType.Int, Int = 3 },
new() { Type = ValueType.UInt, UInt = nextQuest.Quest.Id.Value }
};
agent->ReceiveEvent(returnValue, selectQuest, 2, 0);
addon->Close(true);
}
private void TeleportTownPostSetup(AddonEvent type, AddonArgs args) private void TeleportTownPostSetup(AddonEvent type, AddonArgs args)
{ {
if (ShouldHandleUiInteractions && if (ShouldHandleUiInteractions &&
_questController.HasCurrentTaskMatching(out AethernetShortcut.UseAethernetShortcut? aethernetShortcut) && _questController.HasCurrentTaskMatching(out AethernetShortcut.UseAethernetShortcut? aethernetShortcut) &&
aethernetShortcut.From.IsFirmamentAetheryte()) EAetheryteLocationExtensions.IsFirmamentAetheryte(aethernetShortcut.From))
{ {
// this might be better via atkvalues; but this works for now // this might be better via atkvalues; but this works for now
uint toIndex = aethernetShortcut.To switch uint toIndex = aethernetShortcut.To switch
@ -1012,15 +832,7 @@ internal sealed class GameUiController : IDisposable
public void Dispose() public void Dispose()
{ {
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "TelepotTown", TeleportTownPostSetup); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "TelepotTown", TeleportTownPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "GuildLeve", GuildLevePostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "JournalResult", JournalResultPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "MultipleHelpWindow", MultipleHelpWindowPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "ContentsTutorial", ContentsTutorialPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "AkatsukiNote", UnendingCodexPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "CreditPlayer", CreditPlayerPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "Credit", CreditPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "CreditScroll", CreditScrollPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectYesno", SelectYesnoPostSetup); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectYesno", SelectYesnoPostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectIconString", SelectIconStringPostSetup); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectIconString", SelectIconStringPostSetup);

View File

@ -0,0 +1,154 @@
using System;
using System.Linq;
using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameUI;
using Microsoft.Extensions.Logging;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model.Questing;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Questionable.Controller.GameUi;
internal sealed class LeveUiController : IDisposable
{
private readonly QuestController _questController;
private readonly QuestData _questData;
private readonly QuestFunctions _questFunctions;
private readonly IAddonLifecycle _addonLifecycle;
private readonly IGameGui _gameGui;
private readonly ITargetManager _targetManager;
private readonly IFramework _framework;
private readonly ILogger<LeveUiController> _logger;
public LeveUiController(QuestController questController, QuestData questData, QuestFunctions questFunctions,
IAddonLifecycle addonLifecycle, IGameGui gameGui, ITargetManager targetManager, IFramework framework,
ILogger<LeveUiController> logger)
{
_questController = questController;
_questData = questData;
_questFunctions = questFunctions;
_addonLifecycle = addonLifecycle;
_gameGui = gameGui;
_targetManager = targetManager;
_framework = framework;
_logger = logger;
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "JournalResult", JournalResultPostSetup);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "GuildLeve", GuildLevePostSetup);
}
private bool ShouldHandleUiInteractions => _questController.IsRunning;
private unsafe void JournalResultPostSetup(AddonEvent type, AddonArgs args)
{
if (!ShouldHandleUiInteractions)
return;
_logger.LogInformation("Checking for quest name of journal result");
AddonJournalResult* addon = (AddonJournalResult*)args.Addon;
string questName = addon->AtkTextNode250->NodeText.ToString();
if (_questController.CurrentQuest is { Quest.Id: LeveId } &&
GameFunctions.GameStringEquals(_questController.CurrentQuest.Quest.Info.Name, questName))
{
_logger.LogInformation("JournalResult has the current leve, auto-accepting it");
addon->FireCallbackInt(0);
}
else if (_targetManager.Target is { } target)
{
var issuedLeves = _questData.GetAllByIssuerDataId(target.DataId)
.Where(x => x.QuestId is LeveId)
.ToList();
if (issuedLeves.Any(x => GameFunctions.GameStringEquals(x.Name, questName)))
{
_logger.LogInformation(
"JournalResult has a leve but not the one we're currently on, auto-declining it");
addon->FireCallbackInt(1);
}
}
}
private unsafe void GuildLevePostSetup(AddonEvent type, AddonArgs args)
{
var target = _targetManager.Target;
if (target == null)
return;
if (_questController is { IsRunning: true, NextQuest: { Quest.Id: LeveId } nextQuest } &&
_questFunctions.IsReadyToAcceptQuest(nextQuest.Quest.Id))
{
var addon = (AddonGuildLeve*)args.Addon;
/*
var atkValues = addon->AtkValues;
var availableLeves = _questData.GetAllByIssuerDataId(target.DataId);
List<(int, IQuestInfo)> offeredLeves = [];
for (int i = 0; i <= 20; ++i) // 3 leves per group, 1 label for group
{
string? leveName = atkValues[626 + i * 2].ReadAtkString();
if (leveName == null)
continue;
var questInfo = availableLeves.FirstOrDefault(x => GameFunctions.GameStringEquals(x.Name, leveName));
if (questInfo == null)
continue;
offeredLeves.Add((i, questInfo));
}
foreach (var (i, questInfo) in offeredLeves)
_logger.LogInformation("Leve {Index} = {Id}, {Name}", i, questInfo.QuestId, questInfo.Name);
*/
_framework.RunOnTick(() => AcceptLeveOrWait(nextQuest), TimeSpan.FromMilliseconds(100));
}
}
private unsafe void AcceptLeveOrWait(QuestController.QuestProgress nextQuest, int counter = 0)
{
var agent = UIModule.Instance()->GetAgentModule()->GetAgentByInternalId(AgentId.LeveQuest);
if (agent->IsAgentActive() &&
_gameGui.TryGetAddonByName("GuildLeve", out AddonGuildLeve* addonGuildLeve) &&
LAddon.IsAddonReady(&addonGuildLeve->AtkUnitBase) &&
_gameGui.TryGetAddonByName("JournalDetail", out AtkUnitBase* addonJournalDetail) &&
LAddon.IsAddonReady(addonJournalDetail))
{
AcceptLeve(agent, addonGuildLeve, nextQuest);
}
else if (counter >= 10)
_logger.LogWarning("Unable to accept leve?");
else
_framework.RunOnTick(() => AcceptLeveOrWait(nextQuest, counter + 1), TimeSpan.FromMilliseconds(100));
}
private unsafe void AcceptLeve(AgentInterface* agent, AddonGuildLeve* addon,
QuestController.QuestProgress nextQuest)
{
_questController.SetPendingQuest(nextQuest);
_questController.SetNextQuest(null);
var returnValue = stackalloc AtkValue[1];
var selectQuest = stackalloc AtkValue[]
{
new() { Type = ValueType.Int, Int = 3 },
new() { Type = ValueType.UInt, UInt = nextQuest.Quest.Id.Value }
};
agent->ReceiveEvent(returnValue, selectQuest, 2, 0);
addon->Close(true);
}
public void Dispose()
{
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "GuildLeve", GuildLevePostSetup);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "JournalResult", JournalResultPostSetup);
}
}

View File

@ -81,7 +81,7 @@ internal abstract class MiniTaskController<T>
while (_taskQueue.TryDequeue(out ITask? nextTask)) while (_taskQueue.TryDequeue(out ITask? nextTask))
{ {
if (nextTask is ILastTask) if (nextTask is ILastTask or GatheringRequiredItems.SkipMarker)
{ {
_currentTask = nextTask; _currentTask = nextTask;
return; return;

View File

@ -68,7 +68,9 @@ internal static class GatheringRequiredItems
{ {
foreach (var task in serviceProvider.GetRequiredService<TaskCreator>() foreach (var task in serviceProvider.GetRequiredService<TaskCreator>()
.CreateTasks(quest, gatheringSequence, gatheringStep)) .CreateTasks(quest, gatheringSequence, gatheringStep))
if (task is not WaitAtEnd.NextStep) if (task is WaitAtEnd.NextStep)
yield return serviceProvider.GetRequiredService<SkipMarker>();
else
yield return task; yield return task;
} }
} }
@ -143,4 +145,13 @@ internal static class GatheringRequiredItems
$"Gather({_gatheredItem.ItemCount}x {_gatheredItem.ItemId} {SeIconChar.Collectible.ToIconString()} {_gatheredItem.Collectability})"; $"Gather({_gatheredItem.ItemCount}x {_gatheredItem.ItemId} {SeIconChar.Collectible.ToIconString()} {_gatheredItem.Collectability})";
} }
} }
/// <summary>
/// A task that does nothing, but if we're skipping a step, this will be the task next in queue to be executed (instead of progressing to the next step) if gathering.
/// </summary>
internal sealed class SkipMarker : ITask
{
public bool Start() => true;
public ETaskResult Update() => ETaskResult.TaskComplete;
}
} }

View File

@ -6,6 +6,7 @@ using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Questionable.Controller; using Questionable.Controller;
using Questionable.Controller.GameUi;
using Questionable.Windows; using Questionable.Windows;
namespace Questionable; namespace Questionable;
@ -27,7 +28,7 @@ internal sealed class DalamudInitializer : IDisposable
IFramework framework, IFramework framework,
QuestController questController, QuestController questController,
MovementController movementController, MovementController movementController,
GameUiController gameUiController, InteractionUiController interactionUiController,
WindowSystem windowSystem, WindowSystem windowSystem,
QuestWindow questWindow, QuestWindow questWindow,
DebugOverlay debugOverlay, DebugOverlay debugOverlay,
@ -59,7 +60,7 @@ internal sealed class DalamudInitializer : IDisposable
_pluginInterface.UiBuilder.OpenMainUi += _questWindow.Toggle; _pluginInterface.UiBuilder.OpenMainUi += _questWindow.Toggle;
_pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle; _pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle;
_framework.Update += FrameworkUpdate; _framework.Update += FrameworkUpdate;
_framework.RunOnTick(gameUiController.HandleCurrentDialogueChoices, TimeSpan.FromMilliseconds(200)); _framework.RunOnTick(interactionUiController.HandleCurrentDialogueChoices, TimeSpan.FromMilliseconds(200));
_toastGui.Toast += OnToast; _toastGui.Toast += OnToast;
_toastGui.ErrorToast += OnErrorToast; _toastGui.ErrorToast += OnErrorToast;
_toastGui.QuestToast += OnQuestToast; _toastGui.QuestToast += OnQuestToast;

View File

@ -10,6 +10,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Questionable.Controller; using Questionable.Controller;
using Questionable.Controller.CombatModules; using Questionable.Controller.CombatModules;
using Questionable.Controller.GameUi;
using Questionable.Controller.NavigationOverrides; using Questionable.Controller.NavigationOverrides;
using Questionable.Controller.Steps; using Questionable.Controller.Steps;
using Questionable.Controller.Steps.Shared; using Questionable.Controller.Steps.Shared;
@ -48,7 +49,8 @@ public sealed class QuestionablePlugin : IDalamudPlugin
IAddonLifecycle addonLifecycle, IAddonLifecycle addonLifecycle,
IKeyState keyState, IKeyState keyState,
IContextMenu contextMenu, IContextMenu contextMenu,
IToastGui toastGui) IToastGui toastGui,
IGameInteropProvider gameInteropProvider)
{ {
ArgumentNullException.ThrowIfNull(pluginInterface); ArgumentNullException.ThrowIfNull(pluginInterface);
ArgumentNullException.ThrowIfNull(chatGui); ArgumentNullException.ThrowIfNull(chatGui);
@ -75,6 +77,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddSingleton(keyState); serviceCollection.AddSingleton(keyState);
serviceCollection.AddSingleton(contextMenu); serviceCollection.AddSingleton(contextMenu);
serviceCollection.AddSingleton(toastGui); serviceCollection.AddSingleton(toastGui);
serviceCollection.AddSingleton(gameInteropProvider);
serviceCollection.AddSingleton(new WindowSystem(nameof(Questionable))); serviceCollection.AddSingleton(new WindowSystem(nameof(Questionable)));
serviceCollection.AddSingleton((Configuration?)pluginInterface.GetPluginConfig() ?? new Configuration()); serviceCollection.AddSingleton((Configuration?)pluginInterface.GetPluginConfig() ?? new Configuration());
@ -131,7 +134,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
// task factories // task factories
serviceCollection.AddTaskWithFactory<StepDisabled.Factory, StepDisabled.Task>(); serviceCollection.AddTaskWithFactory<StepDisabled.Factory, StepDisabled.Task>();
serviceCollection.AddSingleton<ITaskFactory, EquipRecommended.BeforeDutyOrInstance>(); serviceCollection.AddSingleton<ITaskFactory, EquipRecommended.BeforeDutyOrInstance>();
serviceCollection.AddTaskWithFactory<GatheringRequiredItems.Factory, GatheringRequiredItems.StartGathering>(); serviceCollection.AddTaskWithFactory<GatheringRequiredItems.Factory, GatheringRequiredItems.StartGathering, GatheringRequiredItems.SkipMarker>();
serviceCollection.AddTaskWithFactory<AetheryteShortcut.Factory, AetheryteShortcut.UseAetheryteShortcut>(); serviceCollection.AddTaskWithFactory<AetheryteShortcut.Factory, AetheryteShortcut.UseAetheryteShortcut>();
serviceCollection.AddTaskWithFactory<SkipCondition.Factory, SkipCondition.CheckSkip>(); serviceCollection.AddTaskWithFactory<SkipCondition.Factory, SkipCondition.CheckSkip>();
serviceCollection.AddTaskWithFactory<AethernetShortcut.Factory, AethernetShortcut.UseAethernetShortcut>(); serviceCollection.AddTaskWithFactory<AethernetShortcut.Factory, AethernetShortcut.UseAethernetShortcut>();
@ -184,11 +187,16 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddSingleton<GatheringPointRegistry>(); serviceCollection.AddSingleton<GatheringPointRegistry>();
serviceCollection.AddSingleton<QuestRegistry>(); serviceCollection.AddSingleton<QuestRegistry>();
serviceCollection.AddSingleton<QuestController>(); serviceCollection.AddSingleton<QuestController>();
serviceCollection.AddSingleton<GameUiController>();
serviceCollection.AddSingleton<CombatController>(); serviceCollection.AddSingleton<CombatController>();
serviceCollection.AddSingleton<GatheringController>(); serviceCollection.AddSingleton<GatheringController>();
serviceCollection.AddSingleton<ContextMenuController>(); serviceCollection.AddSingleton<ContextMenuController>();
serviceCollection.AddSingleton<CraftworksSupplyController>();
serviceCollection.AddSingleton<CreditsController>();
serviceCollection.AddSingleton<HelpUiController>();
serviceCollection.AddSingleton<InteractionUiController>();
serviceCollection.AddSingleton<LeveUiController>();
serviceCollection.AddSingleton<ICombatModule, RotationSolverRebornModule>(); serviceCollection.AddSingleton<ICombatModule, RotationSolverRebornModule>();
} }
@ -231,6 +239,10 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceProvider.GetRequiredService<GatheringPointRegistry>().Reload(); serviceProvider.GetRequiredService<GatheringPointRegistry>().Reload();
serviceProvider.GetRequiredService<CommandHandler>(); serviceProvider.GetRequiredService<CommandHandler>();
serviceProvider.GetRequiredService<ContextMenuController>(); serviceProvider.GetRequiredService<ContextMenuController>();
serviceProvider.GetRequiredService<CraftworksSupplyController>();
serviceProvider.GetRequiredService<CreditsController>();
serviceProvider.GetRequiredService<HelpUiController>();
serviceProvider.GetRequiredService<LeveUiController>();
serviceProvider.GetRequiredService<DalamudInitializer>(); serviceProvider.GetRequiredService<DalamudInitializer>();
} }

View File

@ -51,12 +51,14 @@ internal sealed class QuestValidator
{ {
foreach (var issue in validator.Validate(quest)) foreach (var issue in validator.Validate(quest))
{ {
/*
var level = issue.Severity == EIssueSeverity.Error var level = issue.Severity == EIssueSeverity.Error
? LogLevel.Warning ? LogLevel.Warning
: LogLevel.Debug; : LogLevel.Debug;
_logger.Log(level, _logger.Log(level,
"Validation failed: {QuestId} ({QuestName}) / {QuestSequence} / {QuestStep} - {Description}", "Validation failed: {QuestId} ({QuestName}) / {QuestSequence} / {QuestStep} - {Description}",
issue.ElementId, quest.Info.Name, issue.Sequence, issue.Step, issue.Description); issue.ElementId, quest.Info.Name, issue.Sequence, issue.Step, issue.Description);
*/
if (issue.Type == EIssueType.QuestDisabled && quest.Info.AlliedSociety != EAlliedSociety.None) if (issue.Type == EIssueType.QuestDisabled && quest.Info.AlliedSociety != EAlliedSociety.None)
{ {
disabledTribeQuests.TryAdd(quest.Info.AlliedSociety, 0); disabledTribeQuests.TryAdd(quest.Info.AlliedSociety, 0);

View File

@ -16,6 +16,7 @@ using ImGuiNET;
using LLib.GameUI; using LLib.GameUI;
using LLib.ImGui; using LLib.ImGui;
using Questionable.Controller; using Questionable.Controller;
using Questionable.Controller.GameUi;
using Questionable.Data; using Questionable.Data;
using Questionable.Functions; using Questionable.Functions;
using Questionable.Model; using Questionable.Model;
@ -88,7 +89,7 @@ internal sealed class QuestSelectionWindow : LWindow
_quests = _questData.GetAllByIssuerDataId(targetId); _quests = _questData.GetAllByIssuerDataId(targetId);
if (_gameGui.TryGetAddonByName<AddonSelectIconString>("SelectIconString", out var addonSelectIconString)) if (_gameGui.TryGetAddonByName<AddonSelectIconString>("SelectIconString", out var addonSelectIconString))
{ {
var answers = GameUiController.GetChoices(addonSelectIconString); var answers = InteractionUiController.GetChoices(addonSelectIconString);
_offeredQuests = _quests _offeredQuests = _quests
.Where(x => answers.Any(y => GameFunctions.GameStringEquals(x.Name, y))) .Where(x => answers.Any(y => GameFunctions.GameStringEquals(x.Name, y)))
.ToList(); .ToList();

View File

@ -7,6 +7,7 @@ using Dalamud.Plugin.Services;
using ImGuiNET; using ImGuiNET;
using LLib.ImGui; using LLib.ImGui;
using Questionable.Controller; using Questionable.Controller;
using Questionable.Controller.GameUi;
using Questionable.Data; using Questionable.Data;
using Questionable.Windows.QuestComponents; using Questionable.Windows.QuestComponents;
@ -27,7 +28,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
private readonly QuickAccessButtonsComponent _quickAccessButtonsComponent; private readonly QuickAccessButtonsComponent _quickAccessButtonsComponent;
private readonly RemainingTasksComponent _remainingTasksComponent; private readonly RemainingTasksComponent _remainingTasksComponent;
private readonly IFramework _framework; private readonly IFramework _framework;
private readonly GameUiController _gameUiController; private readonly InteractionUiController _interactionUiController;
private readonly TitleBarButton _minimizeButton; private readonly TitleBarButton _minimizeButton;
public QuestWindow(IDalamudPluginInterface pluginInterface, public QuestWindow(IDalamudPluginInterface pluginInterface,
@ -41,7 +42,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
QuickAccessButtonsComponent quickAccessButtonsComponent, QuickAccessButtonsComponent quickAccessButtonsComponent,
RemainingTasksComponent remainingTasksComponent, RemainingTasksComponent remainingTasksComponent,
IFramework framework, IFramework framework,
GameUiController gameUiController) InteractionUiController interactionUiController)
: base($"Questionable v{PluginVersion.ToString(2)}###Questionable", : base($"Questionable v{PluginVersion.ToString(2)}###Questionable",
ImGuiWindowFlags.AlwaysAutoResize) ImGuiWindowFlags.AlwaysAutoResize)
{ {
@ -56,7 +57,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
_quickAccessButtonsComponent = quickAccessButtonsComponent; _quickAccessButtonsComponent = quickAccessButtonsComponent;
_remainingTasksComponent = remainingTasksComponent; _remainingTasksComponent = remainingTasksComponent;
_framework = framework; _framework = framework;
_gameUiController = gameUiController; _interactionUiController = interactionUiController;
#if DEBUG #if DEBUG
IsOpen = true; IsOpen = true;
@ -152,7 +153,7 @@ internal sealed class QuestWindow : LWindow, IPersistableWindowConfig
internal void Reload() internal void Reload()
{ {
_questController.Reload(); _questController.Reload();
_framework.RunOnTick(() => _gameUiController.HandleCurrentDialogueChoices(), _framework.RunOnTick(() => _interactionUiController.HandleCurrentDialogueChoices(),
TimeSpan.FromMilliseconds(200)); TimeSpan.FromMilliseconds(200));
} }
} }