Add cutscene talk, update EW parts

arr-p5
Liza 2024-06-01 18:46:57 +02:00
parent a5bb4f15cb
commit ab2c4f505c
Signed by: liza
GPG Key ID: 7199F8D727D55F67
31 changed files with 560 additions and 51 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "LLib"]
path = LLib
url = https://git.carvel.li/liza/LLib.git

1
LLib Submodule

@ -0,0 +1 @@
Subproject commit b5125d4b3f7cdc0c7514a01764e5b5d4d85f80a7

View File

@ -2,6 +2,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Questionable", "Questionable\Questionable.csproj", "{C91EEF13-A1AC-4A40-B695-DD4E378E5989}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Questionable", "Questionable\Questionable.csproj", "{C91EEF13-A1AC-4A40-B695-DD4E378E5989}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LLib", "LLib\LLib.csproj", "{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -12,5 +14,9 @@ Global
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|Any CPU.Build.0 = Debug|Any CPU {C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|Any CPU.ActiveCfg = Release|Any CPU {C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|Any CPU.Build.0 = Release|Any CPU {C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|Any CPU.Build.0 = Release|Any CPU
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@ -66,7 +66,7 @@ internal sealed class MovementController : IDisposable
} }
} }
else if (!Destination.IsFlying && !_condition[ConditionFlag.Mounted] && navPoints.Count > 0 && else if (!Destination.IsFlying && !_condition[ConditionFlag.Mounted] && navPoints.Count > 0 &&
!_gameFunctions.HasStatusPreventingSprintOrMount()) !_gameFunctions.HasStatusPreventingSprintOrMount() && Destination.CanSprint)
{ {
float actualDistance = 0; float actualDistance = 0;
foreach (Vector3 end in navPoints) foreach (Vector3 end in navPoints)
@ -118,10 +118,19 @@ internal sealed class MovementController : IDisposable
{ {
if (AetheryteConverter.IsLargeAetheryte((EAetheryteLocation)Destination.DataId)) if (AetheryteConverter.IsLargeAetheryte((EAetheryteLocation)Destination.DataId))
{ {
/*
if ((EAetheryteLocation) Destination.DataId is EAetheryteLocation.OldSharlayan
or EAetheryteLocation.UltimaThuleAbodeOfTheEa)
Stop();
// TODO verify the first part of this, is there any aetheryte like that? // TODO verify the first part of this, is there any aetheryte like that?
// TODO Unsure if this is per-aetheryte or what; because e.g. old sharlayan is at -1.53;
// but Elpis aetherytes fail at around -0.95
if (localPlayerPosition.Y - gameObject.Position.Y < 2.95f && if (localPlayerPosition.Y - gameObject.Position.Y < 2.95f &&
localPlayerPosition.Y - gameObject.Position.Y > -0.9f) localPlayerPosition.Y - gameObject.Position.Y > -0.9f)
Stop(); Stop();
*/
Stop();
} }
else else
{ {
@ -145,27 +154,27 @@ internal sealed class MovementController : IDisposable
return pointOnFloor != null && Math.Abs(pointOnFloor.Value.Y - p.Y) > 0.5f; return pointOnFloor != null && Math.Abs(pointOnFloor.Value.Y - p.Y) > 0.5f;
} }
private void PrepareNavigation(EMovementType type, uint? dataId, Vector3 to, bool fly, float? stopDistance) private void PrepareNavigation(EMovementType type, uint? dataId, Vector3 to, bool fly, bool sprint, float? stopDistance)
{ {
ResetPathfinding(); ResetPathfinding();
_gameFunctions.ExecuteCommand("/automove off"); _gameFunctions.ExecuteCommand("/automove off");
Destination = new DestinationData(dataId, to, stopDistance ?? (DefaultStopDistance - 0.2f), fly); Destination = new DestinationData(dataId, to, stopDistance ?? (DefaultStopDistance - 0.2f), fly, sprint);
} }
public void NavigateTo(EMovementType type, uint? dataId, Vector3 to, bool fly, float? stopDistance = null) public void NavigateTo(EMovementType type, uint? dataId, Vector3 to, bool fly, bool sprint, float? stopDistance = null)
{ {
PrepareNavigation(type, dataId, to, fly, stopDistance); PrepareNavigation(type, dataId, to, fly, sprint, stopDistance);
_cancellationTokenSource = new(); _cancellationTokenSource = new();
_cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10)); _cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
_pathfindTask = _pathfindTask =
_navmeshIpc.Pathfind(_clientState.LocalPlayer!.Position, to, fly, _cancellationTokenSource.Token); _navmeshIpc.Pathfind(_clientState.LocalPlayer!.Position, to, fly, _cancellationTokenSource.Token);
} }
public void NavigateTo(EMovementType type, uint? dataId, List<Vector3> to, bool fly, float? stopDistance) public void NavigateTo(EMovementType type, uint? dataId, List<Vector3> to, bool fly, bool sprint, float? stopDistance)
{ {
PrepareNavigation(type, dataId, to.Last(), fly, stopDistance); PrepareNavigation(type, dataId, to.Last(), fly, sprint, stopDistance);
_navmeshIpc.MoveTo(to); _navmeshIpc.MoveTo(to);
} }
@ -198,5 +207,5 @@ internal sealed class MovementController : IDisposable
Stop(); Stop();
} }
public sealed record DestinationData(uint? DataId, Vector3 Position, float StopDistance, bool IsFlying); public sealed record DestinationData(uint? DataId, Vector3 Position, float StopDistance, bool IsFlying, bool CanSprint);
} }

View File

@ -3,17 +3,23 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Numerics; using System.Numerics;
using System.Text.Json; using System.Text.Json;
using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI;
using LLib.GameUI;
using Lumina.Excel.CustomSheets;
using Questionable.Data; using Questionable.Data;
using Questionable.External; using Questionable.External;
using Questionable.Model.V1; using Questionable.Model.V1;
using Questionable.Model.V1.Converter; using Questionable.Model.V1.Converter;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Questionable.Controller; namespace Questionable.Controller;
@ -28,6 +34,7 @@ internal sealed class QuestController
private readonly ICondition _condition; private readonly ICondition _condition;
private readonly IChatGui _chatGui; private readonly IChatGui _chatGui;
private readonly IFramework _framework; private readonly IFramework _framework;
private readonly IGameGui _gameGui;
private readonly AetheryteData _aetheryteData; private readonly AetheryteData _aetheryteData;
private readonly LifestreamIpc _lifestreamIpc; private readonly LifestreamIpc _lifestreamIpc;
private readonly TerritoryData _territoryData; private readonly TerritoryData _territoryData;
@ -35,7 +42,8 @@ internal sealed class QuestController
public QuestController(DalamudPluginInterface pluginInterface, IDataManager dataManager, IClientState clientState, public QuestController(DalamudPluginInterface pluginInterface, IDataManager dataManager, IClientState clientState,
GameFunctions gameFunctions, MovementController movementController, IPluginLog pluginLog, ICondition condition, GameFunctions gameFunctions, MovementController movementController, IPluginLog pluginLog, ICondition condition,
IChatGui chatGui, IFramework framework, AetheryteData aetheryteData, LifestreamIpc lifestreamIpc) IChatGui chatGui, IFramework framework, IGameGui gameGui, AetheryteData aetheryteData,
LifestreamIpc lifestreamIpc)
{ {
_pluginInterface = pluginInterface; _pluginInterface = pluginInterface;
_dataManager = dataManager; _dataManager = dataManager;
@ -46,6 +54,7 @@ internal sealed class QuestController
_condition = condition; _condition = condition;
_chatGui = chatGui; _chatGui = chatGui;
_framework = framework; _framework = framework;
_gameGui = gameGui;
_aetheryteData = aetheryteData; _aetheryteData = aetheryteData;
_lifestreamIpc = lifestreamIpc; _lifestreamIpc = lifestreamIpc;
_territoryData = new TerritoryData(dataManager); _territoryData = new TerritoryData(dataManager);
@ -318,11 +327,45 @@ internal sealed class QuestController
} }
} }
if (step.SkipIf.Contains(ESkipCondition.FlyingUnlocked) && _gameFunctions.IsFlyingUnlocked(step.TerritoryId)) if (!step.SkipIf.Contains(ESkipCondition.Never))
{ {
_pluginLog.Information("Skipping step, as flying is unlocked"); _pluginLog.Information("Checking skip conditions");
IncreaseStepCount();
return; if (step.SkipIf.Contains(ESkipCondition.FlyingUnlocked) &&
_gameFunctions.IsFlyingUnlocked(step.TerritoryId))
{
_pluginLog.Information("Skipping step, as flying is unlocked");
IncreaseStepCount();
return;
}
if (step is
{
DataId: not null,
InteractionType: EInteractionType.AttuneAetheryte or EInteractionType.AttuneAethernetShard
} &&
_gameFunctions.IsAetheryteUnlocked((EAetheryteLocation)step.DataId.Value))
{
_pluginLog.Information("Skipping step, as aetheryte/aethernet shard is unlocked");
IncreaseStepCount();
return;
}
if (step is { DataId: not null, InteractionType: EInteractionType.AttuneAetherCurrent } &&
_gameFunctions.IsAetherCurrentUnlocked(step.DataId.Value))
{
_pluginLog.Information("Skipping step, as current is unlocked");
IncreaseStepCount();
return;
}
QuestWork? questWork = _gameFunctions.GetQuestEx(CurrentQuest.Quest.QuestId);
if (questWork != null && step.MatchesQuestVariables(questWork.Value))
{
_pluginLog.Information("Skipping step, as quest variables match");
IncreaseStepCount();
return;
}
} }
if (!CurrentQuest.StepProgress.AethernetShortcutUsed) if (!CurrentQuest.StepProgress.AethernetShortcutUsed)
@ -350,7 +393,7 @@ internal sealed class QuestController
} }
else else
_movementController.NavigateTo(EMovementType.Quest, (uint)from, _aetheryteData.Locations[from], _movementController.NavigateTo(EMovementType.Quest, (uint)from, _aetheryteData.Locations[from],
false, false, true,
AetheryteConverter.IsLargeAetheryte(from) ? 10.9f : 6.9f); AetheryteConverter.IsLargeAetheryte(from) ? 10.9f : 6.9f);
return; return;
@ -368,6 +411,11 @@ internal sealed class QuestController
{ {
_pluginLog.Information("We're at the jump destination, skipping movement"); _pluginLog.Information("We're at the jump destination, skipping movement");
} }
else if (step.InteractionType == EInteractionType.CutsceneSelectString &&
_condition[ConditionFlag.OccupiedInCutSceneEvent])
{
_pluginLog.Information("In cutscene selection, skipping movement");
}
else if (step.Position != null) else if (step.Position != null)
{ {
float distance; float distance;
@ -413,7 +461,9 @@ internal sealed class QuestController
if (actualDistance > distance) if (actualDistance > distance)
{ {
_movementController.NavigateTo(EMovementType.Quest, step.DataId, step.Position.Value, _movementController.NavigateTo(EMovementType.Quest, step.DataId, step.Position.Value,
step.Fly && _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), distance); fly: step.Fly == true && _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType),
sprint: step.Sprint != false,
stopDistance: distance);
return; return;
} }
} }
@ -428,7 +478,9 @@ internal sealed class QuestController
distance /= 2; distance /= 2;
_movementController.NavigateTo(EMovementType.Quest, step.DataId, [step.Position.Value], _movementController.NavigateTo(EMovementType.Quest, step.DataId, [step.Position.Value],
step.Fly && _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), distance); fly: step.Fly == true && _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType),
sprint: step.Sprint != false,
stopDistance: distance);
return; return;
} }
} }
@ -609,8 +661,8 @@ internal sealed class QuestController
else else
{ {
_movementController.NavigateTo(EMovementType.Quest, step.DataId, _movementController.NavigateTo(EMovementType.Quest, step.DataId,
[step.JumpDestination.Position], [step.JumpDestination.Position], false, false,
false, step.JumpDestination.StopDistance ?? stopDistance); step.JumpDestination.StopDistance ?? stopDistance);
_framework.RunOnTick(() => ActionManager.Instance()->UseAction(ActionType.GeneralAction, 2), _framework.RunOnTick(() => ActionManager.Instance()->UseAction(ActionType.GeneralAction, 2),
TimeSpan.FromSeconds(step.JumpDestination.DelaySeconds ?? 0.5f)); TimeSpan.FromSeconds(step.JumpDestination.DelaySeconds ?? 0.5f));
} }
@ -623,6 +675,54 @@ internal sealed class QuestController
// Need to manually forward // Need to manually forward
break; break;
case EInteractionType.CutsceneSelectString:
// to do this automatically, should likely be in Addon's post setup
if (_gameGui.TryGetAddonByName<AddonCutSceneSelectString>("CutSceneSelectString", out var addon) &&
LAddon.IsAddonReady(&addon->AtkUnitBase))
{
foreach (DialogueChoice dialogueChoice in step.DialogueChoices)
{
var excelSheet = _dataManager.Excel.GetSheet<QuestDialogueText>(dialogueChoice.ExcelSheet);
if (excelSheet == null)
{
_pluginLog.Error($"Unknown excel sheet '{dialogueChoice.ExcelSheet}'");
continue;
}
string? excelString = excelSheet
.FirstOrDefault(x => x.Key == dialogueChoice.Answer)
?.Value
?.ToString();
if (excelString == null)
{
_pluginLog.Error(
$"Could not extract '{dialogueChoice.Answer}' from sheet '{dialogueChoice.ExcelSheet}'");
return;
}
_pluginLog.Verbose($"Looking for option '{excelString}'");
for (int i = 5; i < addon->AtkUnitBase.AtkValuesCount; ++i)
{
var atkValue = addon->AtkUnitBase.AtkValues[i];
if (atkValue.Type != ValueType.String)
continue;
string? atkString = atkValue.ReadAtkString();
_pluginLog.Verbose($"Option {i}: {atkString}");
if (excelString == atkString)
{
_pluginLog.Information($"Selecting option {i - 5}: {atkString}");
addon->AtkUnitBase.FireCallbackInt(i - 5);
return;
}
}
}
}
else if (step.DataId != null && !_condition[ConditionFlag.OccupiedInCutSceneEvent])
_gameFunctions.InteractWith(step.DataId.Value);
break;
default: default:
_pluginLog.Warning($"Action '{step.InteractionType}' is not implemented"); _pluginLog.Warning($"Action '{step.InteractionType}' is not implemented");
break; break;

View File

@ -20,6 +20,7 @@ public sealed class InteractionTypeConverter() : EnumConverter<EInteractionType>
{ EInteractionType.Duty, "Duty" }, { EInteractionType.Duty, "Duty" },
{ EInteractionType.SinglePlayerDuty, "SinglePlayerDuty" }, { EInteractionType.SinglePlayerDuty, "SinglePlayerDuty" },
{ EInteractionType.Jump, "Jump" }, { EInteractionType.Jump, "Jump" },
{ EInteractionType.CutsceneSelectString, "CutsceneSelectString" },
{ EInteractionType.ShouldBeAJump, "ShouldBeAJump" }, { EInteractionType.ShouldBeAJump, "ShouldBeAJump" },
{ EInteractionType.Instruction, "Instruction" }, { EInteractionType.Instruction, "Instruction" },
}; };

View File

@ -6,6 +6,7 @@ public sealed class SkipConditionConverter() : EnumConverter<ESkipCondition>(Val
{ {
private static readonly Dictionary<ESkipCondition, string> Values = new() private static readonly Dictionary<ESkipCondition, string> Values = new()
{ {
{ ESkipCondition.Never, "Never" },
{ ESkipCondition.FlyingUnlocked, "FlyingUnlocked" }, { ESkipCondition.FlyingUnlocked, "FlyingUnlocked" },
}; };
} }

View File

@ -0,0 +1,7 @@
namespace Questionable.Model.V1;
public sealed class DialogueChoice
{
public string ExcelSheet { get; set; } = null!;
public string Answer { get; set; } = null!;
}

View File

@ -20,6 +20,7 @@ public enum EInteractionType
Duty, Duty,
SinglePlayerDuty, SinglePlayerDuty,
Jump, Jump,
CutsceneSelectString,
/// <summary> /// <summary>
/// Needs to be adjusted for coords etc. in the quest data. /// Needs to be adjusted for coords etc. in the quest data.

View File

@ -7,5 +7,6 @@ namespace Questionable.Model.V1;
public enum ESkipCondition public enum ESkipCondition
{ {
None, None,
Never,
FlyingUnlocked, FlyingUnlocked,
} }

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
using Questionable.Model.V1.Converter; using Questionable.Model.V1.Converter;
namespace Questionable.Model.V1; namespace Questionable.Model.V1;
@ -21,7 +22,8 @@ public class QuestStep
public bool Disabled { get; set; } public bool Disabled { get; set; }
public bool DisableNavmesh { get; set; } public bool DisableNavmesh { get; set; }
public bool? Mount { get; set; } public bool? Mount { get; set; }
public bool Fly { get; set; } public bool? Fly { get; set; }
public bool? Sprint { get; set; }
public string? Comment { get; set; } public string? Comment { get; set; }
public EAetheryteLocation? AetheryteShortcut { get; set; } public EAetheryteLocation? AetheryteShortcut { get; set; }
@ -42,4 +44,28 @@ public class QuestStep
public uint? ContentFinderConditionId { get; set; } public uint? ContentFinderConditionId { get; set; }
public IList<ESkipCondition> SkipIf { get; set; } = new List<ESkipCondition>(); public IList<ESkipCondition> SkipIf { get; set; } = new List<ESkipCondition>();
public IList<short?> CompletionQuestVariablesFlags { get; set; } = new List<short?>();
public IList<DialogueChoice> DialogueChoices { get; set; } = new List<DialogueChoice>();
public unsafe bool MatchesQuestVariables(QuestWork questWork)
{
if (CompletionQuestVariablesFlags.Count != 6)
return false;
for (int i = 0; i < 6; ++i)
{
short? check = CompletionQuestVariablesFlags[i];
if (check == null)
continue;
byte actualValue = questWork.Variables[i];
byte expectedValue = check > 0 ? (byte)check : (byte)0;
byte checkByte = check > 0 ? (byte)check : (byte)-check;
if ((actualValue & checkByte) != expectedValue)
return false;
}
return true;
}
} }

View File

@ -44,7 +44,9 @@
"Z": -239.7956 "Z": -239.7956
}, },
"TerritoryId": 957, "TerritoryId": 957,
"InteractionType": "Interact" "InteractionType": "Interact",
"Mount": false,
"Sprint": false
} }
] ]
}, },

View File

@ -44,6 +44,14 @@
}, },
"TerritoryId": 957, "TerritoryId": 957,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
],
"$": "QuestVariables after: 17 0 0 0 0 64" "$": "QuestVariables after: 17 0 0 0 0 64"
}, },
{ {
@ -60,6 +68,14 @@
"KillEnemyDataIds": [ "KillEnemyDataIds": [
14111 14111
], ],
"CompletionQuestVariablesFlags": [
null,
1,
null,
null,
null,
null
],
"$": "QuestVariables after killing enemy: 17 1 0 0 0 64" "$": "QuestVariables after killing enemy: 17 1 0 0 0 64"
}, },
{ {
@ -71,6 +87,14 @@
}, },
"TerritoryId": 957, "TerritoryId": 957,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
32
],
"$": "QuestVariables after: 33 2 0 0 0 96" "$": "QuestVariables after: 33 2 0 0 0 96"
}, },
{ {
@ -81,7 +105,16 @@
"Z": 372.54907 "Z": 372.54907
}, },
"TerritoryId": 957, "TerritoryId": 957,
"InteractionType": "Interact" "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
],
"$": "QuestVariables if done first: 16 16 16 0 0 128"
} }
] ]
}, },

View File

@ -43,8 +43,21 @@
"Z": 799.2217 "Z": 799.2217
}, },
"TerritoryId": 957, "TerritoryId": 957,
"InteractionType": "WaitForManualProgress", "InteractionType": "CutsceneSelectString",
"Comment": "Talk (2, 2, 1)" "DialogueChoices": [
{
"ExcelSheet": "quest/043/AktKma114_04370",
"Answer": "TEXT_AKTKMA114_04370_A2_000_088"
},
{
"ExcelSheet": "quest/043/AktKma114_04370",
"Answer": "TEXT_AKTKMA114_04370_A3_000_098"
},
{
"ExcelSheet": "quest/043/AktKma114_04370",
"Answer": "TEXT_AKTKMA114_04370_A5_000_107"
}
]
} }
] ]
}, },
@ -59,8 +72,21 @@
"Z": 681.7273 "Z": 681.7273
}, },
"TerritoryId": 957, "TerritoryId": 957,
"InteractionType": "WaitForManualProgress", "InteractionType": "CutsceneSelectString",
"Comment": "Talk (2, 1, 2)" "DialogueChoices": [
{
"ExcelSheet": "quest/043/AktKma114_04370",
"Answer": "TEXT_AKTKMA114_04370_A6_000_149"
},
{
"ExcelSheet": "quest/043/AktKma114_04370",
"Answer": "TEXT_AKTKMA114_04370_A7_000_158"
},
{
"ExcelSheet": "quest/043/AktKma114_04370",
"Answer": "TEXT_AKTKMA114_04370_A8_000_164"
}
]
} }
] ]
}, },
@ -75,8 +101,21 @@
"Z": 517.72327 "Z": 517.72327
}, },
"TerritoryId": 957, "TerritoryId": 957,
"InteractionType": "WaitForManualProgress", "InteractionType": "CutsceneSelectString",
"Comment": "Talk (2, 2, 2)" "DialogueChoices": [
{
"ExcelSheet": "quest/043/AktKma114_04370",
"Answer": "TEXT_AKTKMA114_04370_A9_000_200"
},
{
"ExcelSheet": "quest/043/AktKma114_04370",
"Answer": "TEXT_AKTKMA114_04370_A10_000_209"
},
{
"ExcelSheet": "quest/043/AktKma114_04370",
"Answer": "TEXT_AKTKMA114_04370_A11_000_218"
}
]
} }
] ]
}, },

View File

@ -77,9 +77,9 @@
"Y": 4.357494, "Y": 4.357494,
"Z": 0.7476196 "Z": 0.7476196
}, },
"StopDistance": 5,
"TerritoryId": 962, "TerritoryId": 962,
"InteractionType": "Interact", "InteractionType": "Interact",
"StopDistance": 5,
"AethernetShortcut": [ "AethernetShortcut": [
"[Old Sharlayan] The Rostra", "[Old Sharlayan] The Rostra",
"[Old Sharlayan] The Baldesion Annex" "[Old Sharlayan] The Baldesion Annex"

View File

@ -86,7 +86,8 @@
"AethernetShortcut": [ "AethernetShortcut": [
"[Old Sharlayan] Scholar's Harbor", "[Old Sharlayan] Scholar's Harbor",
"[Old Sharlayan] The Studium" "[Old Sharlayan] The Studium"
] ],
"$.1": "QuestVariables if done first: 16 16 128 0 0 128"
}, },
{ {
"DataId": 1039825, "DataId": 1039825,
@ -97,7 +98,6 @@
}, },
"TerritoryId": 962, "TerritoryId": 962,
"InteractionType": "Interact", "InteractionType": "Interact",
"Comment": "Unsure why this is on the same Sequence No",
"AethernetShortcut": [ "AethernetShortcut": [
"[Old Sharlayan] The Studium", "[Old Sharlayan] The Studium",
"[Old Sharlayan] The Leveilleur Estate" "[Old Sharlayan] The Leveilleur Estate"

View File

@ -58,7 +58,7 @@
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"$.0": "[2]", "$.0": "[2]",
"$.2": "QuestVariables if done after [1]: 33 1 0 0 0 192" "$.1": "QuestVariables if done after [1]: 33 1 0 0 0 192"
}, },
{ {
"DataId": 1040293, "DataId": 1040293,
@ -68,7 +68,9 @@
"Z": 665.5221 "Z": 665.5221
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact" "InteractionType": "Interact",
"$.0": "[3]",
"$.2": "QuestVariables if done first: 16 16 0 0 0 32"
} }
] ]
}, },

View File

@ -31,6 +31,14 @@
"StopDistance": 8, "StopDistance": 8,
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
],
"$.0": "[1]", "$.0": "[1]",
"$.1": "QuestVariables if done first: 1 0 0 0 0 64" "$.1": "QuestVariables if done first: 1 0 0 0 0 64"
}, },
@ -45,6 +53,14 @@
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"$.0": "[2]", "$.0": "[2]",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
32
],
"$.1": "QuestVariables if done after [1]: 2 0 0 0 0 96" "$.1": "QuestVariables if done after [1]: 2 0 0 0 0 96"
}, },
{ {
@ -67,7 +83,16 @@
}, },
"StopDistance": 8, "StopDistance": 8,
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact" "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
],
"$.1": "QuestVariables if done first: 1 0 0 0 0 128"
} }
] ]
}, },
@ -98,8 +123,16 @@
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
32
],
"$.0": "[1]", "$.0": "[1]",
"$.1": "QuestVariables if done first: 1 0 0 0 0 ??" "$.1": "QuestVariables if done first: 1 0 0 0 0 32"
}, },
{ {
"DataId": 2012282, "DataId": 2012282,
@ -110,6 +143,14 @@
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
],
"$.0": "[1]", "$.0": "[1]",
"$.1": "QuestVariables if done after [1]: 2 0 0 0 0 ??" "$.1": "QuestVariables if done after [1]: 2 0 0 0 0 ??"
}, },
@ -121,7 +162,16 @@
"Z": -20.676025 "Z": -20.676025
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact" "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
],
"Comment": "TODO Verify quest variables flags"
} }
] ]
}, },
@ -170,6 +220,7 @@
"Y": 64.78333, "Y": 64.78333,
"Z": -200.3357 "Z": -200.3357
}, },
"StopDistance": 8,
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact" "InteractionType": "Interact"
} }

View File

@ -82,6 +82,7 @@
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"Mount": true,
"DisableNavmesh": true "DisableNavmesh": true
} }
] ]
@ -114,6 +115,14 @@
"StopDistance": 4, "StopDistance": 4,
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
],
"$.0": "[1]", "$.0": "[1]",
"$.1": "QuestVariables if done first: 1 0 0 0 0 128" "$.1": "QuestVariables if done first: 1 0 0 0 0 128"
}, },
@ -127,6 +136,14 @@
"StopDistance": 4, "StopDistance": 4,
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
],
"$.0": "[2]", "$.0": "[2]",
"$.1": "QuestVariables if done after [1]: 2 0 0 0 0 192" "$.1": "QuestVariables if done after [1]: 2 0 0 0 0 192"
}, },
@ -152,7 +169,15 @@
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"$.0": "[3]", "$.0": "[3]",
"$.1": "QuestVariables if done after [2]: 3 0 0 0 0 224" "$.1": "QuestVariables if done after [2]: 3 0 0 0 0 224",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
32
]
}, },
{ {
"DataId": 2012357, "DataId": 2012357,
@ -164,8 +189,16 @@
"StopDistance": 4, "StopDistance": 4,
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
16
],
"$.0": "[4]", "$.0": "[4]",
"$.1": "QuestVariables if done first: TODO" "$.1": "QuestVariables if done first: 1 0 0 0 0 16"
} }
] ]
}, },

View File

@ -101,8 +101,7 @@
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "AttuneAetherCurrent", "InteractionType": "AttuneAetherCurrent",
"AetheryteShortcut": "Ultima Thule - Abode of the Ea", "AetheryteShortcut": "Ultima Thule - Abode of the Ea",
"AetherCurrentId": 2818390, "AetherCurrentId": 2818390
"Comment": "TODO Verify"
}, },
{ {
"DataId": 1039778, "DataId": 1039778,

View File

@ -12,7 +12,7 @@
"Y": 269.0203, "Y": 269.0203,
"Z": -633.5393 "Z": -633.5393
}, },
"StopDistance": 5, "StopDistance": 6,
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact" "InteractionType": "Interact"
} }

View File

@ -116,13 +116,24 @@
"AetherCurrentId": 2818396 "AetherCurrentId": 2818396
}, },
{ {
"DataId": 2012038,
"Position": { "Position": {
"X": 639.9123, "X": 645.6607,
"Y": 438.7276, "Y": 438.6276,
"Z": 293.33954 "Z": 291.0269
}, },
"StopDistance": 1,
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "WalkTo" "InteractionType": "Jump",
"JumpDestination": {
"DataId": 2012038,
"Position": {
"X": 637.1709,
"Y": 439.23096,
"Z": 289.66187
},
"StopDistance": 3
}
}, },
{ {
"DataId": 2012038, "DataId": 2012038,

View File

@ -68,6 +68,7 @@
"Y": 417.0675, "Y": 417.0675,
"Z": 414.66382 "Z": 414.66382
}, },
"StopDistance": 5,
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact" "InteractionType": "Interact"
} }

View File

@ -29,6 +29,14 @@
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
],
"$.0": "[1]", "$.0": "[1]",
"$.1": "QuestVariables if done first: 16 0 0 16 0 128" "$.1": "QuestVariables if done first: 16 0 0 16 0 128"
}, },
@ -41,6 +49,14 @@
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
16
],
"$.0": "[2]", "$.0": "[2]",
"$.1": "QuestVariables if done after [1]: 32 16 0 16 0 144" "$.1": "QuestVariables if done after [1]: 32 16 0 16 0 144"
}, },
@ -53,6 +69,14 @@
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
8
],
"$.0": "[3]", "$.0": "[3]",
"$.1": "QuestVariables if done after [1, 2]: 48 17 0 16 0 152" "$.1": "QuestVariables if done after [1, 2]: 48 17 0 16 0 152"
}, },
@ -65,6 +89,14 @@
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
32
],
"$.0": "[4]", "$.0": "[4]",
"$.1": "QuestVariables if done after [1, 2, 3]: 65 17 0 16 0 184" "$.1": "QuestVariables if done after [1, 2, 3]: 65 17 0 16 0 184"
}, },
@ -77,6 +109,14 @@
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
4
],
"$.0": "[5]", "$.0": "[5]",
"$.1": "QuestVariables if done after [1, 2, 3, 4]: 81 17 16 16 0 188" "$.1": "QuestVariables if done after [1, 2, 3, 4]: 81 17 16 16 0 188"
}, },
@ -88,7 +128,17 @@
"Z": 241.9928 "Z": 241.9928
}, },
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact" "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
],
"$.0": "[6]",
"$.1": "QuestVariables if done first: 16 0 1 0 0 64"
} }
] ]
}, },
@ -146,6 +196,7 @@
"Y": 637.10297, "Y": 637.10297,
"Z": 5.2338257 "Z": 5.2338257
}, },
"StopDistance": 5,
"TerritoryId": 960, "TerritoryId": 960,
"InteractionType": "Interact" "InteractionType": "Interact"
} }

View File

@ -69,6 +69,14 @@
"StopDistance": 5, "StopDistance": 5,
"TerritoryId": 351, "TerritoryId": 351,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
1
],
"$.0": "[1]", "$.0": "[1]",
"$.1": "QuestVariables if done first: 1 0 0 0 0 1" "$.1": "QuestVariables if done first: 1 0 0 0 0 1"
}, },
@ -82,6 +90,14 @@
"StopDistance": 5, "StopDistance": 5,
"TerritoryId": 351, "TerritoryId": 351,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
128
],
"$.0": "[2]", "$.0": "[2]",
"$.1": "QuestVariables if done after [1]: 2 0 0 0 0 129" "$.1": "QuestVariables if done after [1]: 2 0 0 0 0 129"
}, },
@ -94,6 +110,14 @@
}, },
"TerritoryId": 351, "TerritoryId": 351,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
64
],
"$.0": "[3]", "$.0": "[3]",
"$.1": "QuestVariables if done after [1, 2]: 3 0 0 0 0 193" "$.1": "QuestVariables if done after [1, 2]: 3 0 0 0 0 193"
}, },
@ -107,6 +131,14 @@
"StopDistance": 5, "StopDistance": 5,
"TerritoryId": 351, "TerritoryId": 351,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
16
],
"$.0": "[4]", "$.0": "[4]",
"$.1": "QuestVariables if done after [1, 2, 3]: 4 0 0 0 0 209" "$.1": "QuestVariables if done after [1, 2, 3]: 4 0 0 0 0 209"
}, },
@ -119,6 +151,14 @@
}, },
"TerritoryId": 351, "TerritoryId": 351,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
32
],
"$.0": "[5]", "$.0": "[5]",
"$.1": "QuestVariables if done after [1, 2, 3, 4]: 5 0 0 0 0 241" "$.1": "QuestVariables if done after [1, 2, 3, 4]: 5 0 0 0 0 241"
}, },
@ -131,6 +171,14 @@
}, },
"TerritoryId": 351, "TerritoryId": 351,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
2
],
"$.0": "[6]", "$.0": "[6]",
"$.1": "QuestVariables if done after [1, 2, 3, 4, 5]: 6 0 0 0 0 243" "$.1": "QuestVariables if done after [1, 2, 3, 4, 5]: 6 0 0 0 0 243"
}, },
@ -143,6 +191,14 @@
}, },
"TerritoryId": 351, "TerritoryId": 351,
"InteractionType": "Interact", "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
8
],
"$.0": "[7]", "$.0": "[7]",
"$.1": "QuestVariables if done after [1, 2, 3, 4, 5, 6]: 7 0 0 0 0 251" "$.1": "QuestVariables if done after [1, 2, 3, 4, 5, 6]: 7 0 0 0 0 251"
}, },
@ -154,7 +210,16 @@
"Z": 0.1373291 "Z": 0.1373291
}, },
"TerritoryId": 351, "TerritoryId": 351,
"InteractionType": "Interact" "InteractionType": "Interact",
"CompletionQuestVariablesFlags": [
null,
null,
null,
null,
null,
4
],
"$.2": "QuestVariables if done first: 1 0 0 0 0 0 4"
} }
] ]
}, },

View File

@ -13,7 +13,8 @@
"Z": -631.281 "Z": -631.281
}, },
"TerritoryId": 156, "TerritoryId": 156,
"InteractionType": "Interact" "InteractionType": "Interact",
"DisableNavmesh": true
}, },
{ {
"DataId": 1041232, "DataId": 1041232,

View File

@ -106,6 +106,7 @@
"Duty", "Duty",
"SinglePlayerDuty", "SinglePlayerDuty",
"Jump", "Jump",
"CutsceneSelectString",
"ShouldBeAJump", "ShouldBeAJump",
"Instruction" "Instruction"
] ]
@ -129,6 +130,12 @@
"type": "boolean", "type": "boolean",
"description": "If true and flying is unlocked in a zone, will use a flight path" "description": "If true and flying is unlocked in a zone, will use a flight path"
}, },
"Sprint": {
"type": [
"boolean",
"null"
]
},
"AetheryteShortcut": { "AetheryteShortcut": {
"type": "string", "type": "string",
"description": "The Aetheryte to teleport to (before moving)", "description": "The Aetheryte to teleport to (before moving)",
@ -419,7 +426,7 @@
] ]
}, },
"ContentFinderConditionId": { "ContentFinderConditionId": {
"type": "number", "type": "integer",
"exclusiveMinimum": 0 "exclusiveMinimum": 0
}, },
"SkipIf": { "SkipIf": {
@ -428,10 +435,60 @@
"items": { "items": {
"type": "string", "type": "string",
"enum": [ "enum": [
"Never",
"FlyingUnlocked" "FlyingUnlocked"
] ]
} }
}, },
"CompletionQuestVariablesFlags": {
"type": "array",
"description": "Quest Variables that dictate whether or not this step is skipped: null is don't check, positive values need to be set, negative values need to be unset",
"items": {
"type": [
"integer",
"null"
],
"enum": [
null,
1,
2,
4,
8,
16,
32,
64,
128,
-1,
-2,
-4,
-8,
-16,
-32,
-64,
-128
]
},
"minItems": 6,
"maxItems": 6
},
"DialogueChoices": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ExcelSheet": {
"type": "string"
},
"Answer": {
"type": "string"
}
},
"required": [
"ExcelSheet",
"Answer"
]
}
},
"Comment": { "Comment": {
"type": "string" "type": "string"
} }

View File

@ -54,6 +54,10 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LLib\LLib.csproj" />
</ItemGroup>
<!-- <!--
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="QuestPaths/**/*.json"/> <EmbeddedResource Include="QuestPaths/**/*.json"/>

View File

@ -53,7 +53,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
_movementController = _movementController =
new MovementController(navmeshIpc, clientState, _gameFunctions, condition, pluginLog); new MovementController(navmeshIpc, clientState, _gameFunctions, condition, pluginLog);
_questController = new QuestController(pluginInterface, dataManager, _clientState, _gameFunctions, _questController = new QuestController(pluginInterface, dataManager, _clientState, _gameFunctions,
_movementController, pluginLog, condition, chatGui, framework, aetheryteData, lifestreamIpc); _movementController, pluginLog, condition, chatGui, framework, gameGui, aetheryteData, lifestreamIpc);
_windowSystem.AddWindow(new DebugWindow(_movementController, _questController, _gameFunctions, clientState, _windowSystem.AddWindow(new DebugWindow(_movementController, _questController, _gameFunctions, clientState,
targetManager)); targetManager));
@ -88,7 +88,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
out Vector3 worldPos)) out Vector3 worldPos))
{ {
_movementController.NavigateTo(EMovementType.Shortcut, null, worldPos, _movementController.NavigateTo(EMovementType.Shortcut, null, worldPos,
_gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType)); _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), true);
} }
} }

View File

@ -147,7 +147,8 @@ internal sealed class DebugWindow : Window
if (ImGui.Button("Move to Target")) if (ImGui.Button("Move to Target"))
{ {
_movementController.NavigateTo(EMovementType.DebugWindow, _targetManager.Target.DataId, _movementController.NavigateTo(EMovementType.DebugWindow, _targetManager.Target.DataId,
_targetManager.Target.Position, _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType)); _targetManager.Target.Position, _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType),
true);
} }
} }
else else

View File

@ -21,6 +21,9 @@
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.0", "resolved": "8.0.0",
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
},
"llib": {
"type": "Project"
} }
} }
} }