Compare commits

...

4 Commits

9 changed files with 384 additions and 11 deletions

View File

@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<Version>4.4</Version>
<Version>4.5</Version>
</PropertyGroup>
</Project>

View File

@ -1,7 +1,6 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"Disabled": true,
"QuestSequence": [
{
"Sequence": 0,
@ -28,6 +27,122 @@
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 2014504,
"Position": {
"X": -232.22711,
"Y": 120.25635,
"Z": 26.199707
},
"TerritoryId": 1188,
"InteractionType": "Combat",
"EnemySpawnType": "AfterItemUse",
"ItemId": 2003727,
"KillEnemyDataIds": [
18176
],
"AetheryteShortcut": "Kozama'uka - Earthenshire",
"Fly": true,
"RequiredQuestVariables": [
null,
null,
null,
[
{
"High": 1
}
],
null,
null
]
},
{
"DataId": 2014503,
"Position": {
"X": -134.29468,
"Y": 120.62256,
"Z": 41.000854
},
"TerritoryId": 1188,
"InteractionType": "Combat",
"EnemySpawnType": "AfterItemUse",
"ItemId": 2003727,
"KillEnemyDataIds": [
18176
],
"Fly": true,
"RequiredQuestVariables": [
null,
null,
null,
[
{
"High": 2
}
],
null,
null
]
},
{
"DataId": 2014505,
"Position": {
"X": -292.28656,
"Y": 119.46289,
"Z": 17.959839
},
"TerritoryId": 1188,
"InteractionType": "Combat",
"EnemySpawnType": "AfterItemUse",
"ItemId": 2003727,
"KillEnemyDataIds": [
18176
],
"Fly": true,
"RequiredQuestVariables": [
null,
null,
null,
[
{
"High": 3
}
],
null,
null
]
}
]
},
{
"Sequence": 2,
"Steps": [
{
"Position": {
"X": 746.76465,
"Y": 15.431515,
"Z": -201.92921
},
"TerritoryId": 1188,
"InteractionType": "WalkTo",
"AetheryteShortcut": "Kozama'uka - Dock Poga"
},
{
"DataId": 1051741,
"Position": {
"X": 744.19763,
"Y": 15.431515,
"Z": -199.17603
},
"StopDistance": 7,
"TerritoryId": 1188,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
@ -39,7 +154,20 @@
},
"TerritoryId": 1188,
"InteractionType": "WalkTo",
"AetheryteShortcut": "Kozama'uka - Dock Poga"
"AetheryteShortcut": "Kozama'uka - Dock Poga",
"SkipConditions": {
"AetheryteShortcutIf": {
"NearPosition": {
"Position": {
"X": 770.7179,
"Y": 12.84657,
"Z": -263.99634
},
"TerritoryId": 1188,
"MaximumDistance": 300
}
}
}
},
{
"DataId": 1051711,

View File

@ -1,7 +1,6 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"Disabled": true,
"QuestSequence": [
{
"Sequence": 0,
@ -28,6 +27,35 @@
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1052337,
"Position": {
"X": 479.14844,
"Y": 113.54922,
"Z": 175.37183
},
"TerritoryId": 1188,
"InteractionType": "Interact",
"AetheryteShortcut": "Kozama'uka - Many Fires",
"Fly": true,
"DialogueChoices": [
{
"Type": "List",
"Prompt": "TEXT_BANPEL123_05221_Q1_000_000",
"Answer": "TEXT_BANPEL123_05221_A1_000_003"
},
{
"Type": "List",
"Prompt": "TEXT_BANPEL123_05221_Q2_000_000",
"Answer": "TEXT_BANPEL123_05221_A2_000_002"
}
]
}
]
},
{
"Sequence": 255,
"Steps": [

View File

@ -1,7 +1,6 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"Disabled": true,
"QuestSequence": [
{
"Sequence": 0,
@ -28,6 +27,66 @@
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1052339,
"Position": {
"X": 910.3379,
"Y": 10.1397,
"Z": -381.36877
},
"TerritoryId": 1188,
"InteractionType": "Interact",
"Fly": true,
"AetheryteShortcut": "Kozama'uka - Dock Poga",
"SkipConditions": {
"AetheryteShortcutIf": {
"NearPosition": {
"Position": {
"X": 770.7179,
"Y": 12.84657,
"Z": -263.99634
},
"TerritoryId": 1188,
"MaximumDistance": 300
}
}
}
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 2014506,
"Position": {
"X": 910.3379,
"Y": 10.421875,
"Z": -379.01886
},
"TerritoryId": 1188,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 3,
"Steps": [
{
"DataId": 1052340,
"Position": {
"X": 789.9137,
"Y": 14.354868,
"Z": -215.77783
},
"TerritoryId": 1188,
"InteractionType": "Interact"
}
]
},
{
"Sequence": 255,
"Steps": [
@ -39,7 +98,20 @@
},
"TerritoryId": 1188,
"InteractionType": "WalkTo",
"AetheryteShortcut": "Kozama'uka - Dock Poga"
"AetheryteShortcut": "Kozama'uka - Dock Poga",
"SkipConditions": {
"AetheryteShortcutIf": {
"NearPosition": {
"Position": {
"X": 770.7179,
"Y": 12.84657,
"Z": -263.99634
},
"TerritoryId": 1188,
"MaximumDistance": 300
}
}
}
},
{
"DataId": 1051711,

View File

@ -1,7 +1,6 @@
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
"Disabled": true,
"QuestSequence": [
{
"Sequence": 0,
@ -28,6 +27,53 @@
}
]
},
{
"Sequence": 1,
"Steps": [
{
"DataId": 1051798,
"Position": {
"X": 897.734,
"Y": 6.8223433,
"Z": -285.1759
},
"TerritoryId": 1188,
"InteractionType": "Interact",
"Fly": true,
"AetheryteShortcut": "Kozama'uka - Dock Poga",
"SkipConditions": {
"AetheryteShortcutIf": {
"NearPosition": {
"Position": {
"X": 770.7179,
"Y": 12.84657,
"Z": -263.99634
},
"TerritoryId": 1188,
"MaximumDistance": 300
}
}
}
}
]
},
{
"Sequence": 2,
"Steps": [
{
"DataId": 1052345,
"Position": {
"X": 908.50684,
"Y": 5.7142797,
"Z": -337.85004
},
"TerritoryId": 1188,
"InteractionType": "Action",
"Action": "Bosom Brook",
"Fly": true
}
]
},
{
"Sequence": 255,
"Steps": [

View File

@ -4,8 +4,10 @@ using Dalamud.Game.Gui.ContextMenu;
using Dalamud.Game.Text;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using LLib.GameData;
using LLib.GameUI;
using Microsoft.Extensions.Logging;
using Questionable.Data;
using Questionable.Functions;
@ -59,10 +61,17 @@ internal sealed class ContextMenuController : IDisposable
private void MenuOpened(IMenuOpenedArgs args)
{
uint itemId = (uint)_gameGui.HoveredItem;
if (itemId == 0)
// no clue why this isn't the actual name, but here we are
if (args.AddonName != null)
return;
uint itemId = GetHoveredSatisfactionSupplyItemId();
if (itemId == 0)
{
_logger.LogTrace("Ignoring context menu, no item hovered");
return;
}
if (itemId > 1_000_000)
itemId -= 1_000_000;
@ -74,6 +83,25 @@ internal sealed class ContextMenuController : IDisposable
AddContextMenuEntry(args, itemId, npcId, EExtendedClassJob.Miner, "Mine");
AddContextMenuEntry(args, itemId, npcId, EExtendedClassJob.Botanist, "Harvest");
}
else
_logger.LogDebug("No custom delivery NPC found for item {ItemId}.", itemId);
}
private unsafe uint GetHoveredSatisfactionSupplyItemId()
{
AgentSatisfactionSupply* agent = AgentSatisfactionSupply.Instance();
if (agent == null || !agent->IsAgentActive())
return 0;
if (_gameGui.TryGetAddonByName("SatisfactionSupply", out AddonSatisfactionSupply* addon) &&
LAddon.IsAddonReady(&addon->AtkUnitBase) &&
addon->HoveredElementIndex is >= 0 and <= 2)
{
return agent->Items[addon->HoveredElementIndex].Id;
}
return 0;
}
private void AddContextMenuEntry(IMenuOpenedArgs args, uint itemId, uint npcId, EExtendedClassJob extendedClassJob,
@ -86,7 +114,7 @@ internal sealed class ContextMenuController : IDisposable
if (!_gatheringData.TryGetGatheringPointId(itemId, classJob, out _))
{
_logger.LogInformation("No gathering point found for current job.");
_logger.LogInformation("No gathering point found for {ClassJob}.", classJob);
return;
}

View File

@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Shared;
using Questionable.Data;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Common;
using Questionable.Model.Questing;
namespace Questionable.Controller.Steps;
internal static class QuestCleanUp
{
private static readonly Dictionary<ushort, MountConfiguration> AlliedSocietyMountConfiguration = new()
{
{ 369, new(1051798, EAetheryteLocation.KozamaukaDockPoga) }
};
internal sealed class CheckAlliedSocietyMount(GameFunctions gameFunctions, AetheryteData aetheryteData, ILogger<CheckAlliedSocietyMount> logger) : SimpleTaskFactory
{
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{
if (sequence.Sequence == 0)
return null;
// if you are on a allied society mount
if (gameFunctions.GetMountId() is { } mountId &&
AlliedSocietyMountConfiguration.TryGetValue(mountId, out var mountConfiguration))
{
logger.LogInformation("We are on a known allied society mount with id = {MountId}", mountId);
// it doesn't particularly matter if we teleport to the same aetheryte twice in the same quest step, as
// the second (normal) teleport instance should detect that we're within range and not do anything
var targetAetheryte = step.AetheryteShortcut ?? mountConfiguration.ClosestAetheryte;
var teleportTask = new AetheryteShortcut.Task(null, quest.Id, targetAetheryte, aetheryteData.TerritoryIds[targetAetheryte]);
// turn-in step can never be done while mounted on an allied society mount
if (sequence.Sequence == 255)
{
logger.LogInformation("Mount can't be used to finish quest, teleporting to {Aetheryte}", mountConfiguration.ClosestAetheryte);
return teleportTask;
}
// if the quest uses no mount actions, that's not a mount quest
if (!quest.AllSteps().Any(x => x.Step.Action is { } action && action.RequiresMount()))
{
logger.LogInformation("Quest doesn't use any mount actions, teleporting to {Aetheryte}", mountConfiguration.ClosestAetheryte);
return teleportTask;
}
// have any of the previous sequences interacted with the issuer?
var previousSequences =
quest.AllSequences()
.Where(x => x.Sequence > 0 // quest accept doesn't ever put us into a mount
&& x.Sequence < sequence.Sequence)
.ToList();
if (previousSequences.SelectMany(x => x.Steps).All(x => x.DataId != mountConfiguration.IssuerDataId))
{
// this quest hasn't given us a mount yet
logger.LogInformation("Haven't talked to mount NPC for this allied society quest; {Aetheryte}", mountConfiguration.ClosestAetheryte);
return teleportTask;
}
}
return null;
}
}
private sealed record MountConfiguration(uint IssuerDataId, EAetheryteLocation ClosestAetheryte);
}

View File

@ -243,7 +243,6 @@ internal sealed unsafe class QuestFunctions
{
return questId.Value switch
{
5215 => EAlliedSociety.None,
>= 5199 and <= 5226 => EAlliedSociety.Pelupelu,
_ => EAlliedSociety.None,
};

View File

@ -133,6 +133,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
private static void AddTaskFactories(ServiceCollection serviceCollection)
{
// individual tasks
serviceCollection.AddTaskFactory<QuestCleanUp.CheckAlliedSocietyMount>();
serviceCollection
.AddTaskExecutor<MoveToLandingLocation.Task, MoveToLandingLocation.MoveToLandingLocationExecutor>();
serviceCollection.AddTaskExecutor<DoGather.Task, DoGather.GatherExecutor>();