2024-06-03 21:17:35 +00:00
|
|
|
|
using System;
|
2024-06-06 16:49:49 +00:00
|
|
|
|
using System.Collections.Generic;
|
2024-07-14 21:26:06 +00:00
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
using System.Linq;
|
2024-06-13 23:10:05 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
using Dalamud.Game.Addon.Lifecycle;
|
|
|
|
|
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
2024-06-12 16:03:48 +00:00
|
|
|
|
using Dalamud.Game.ClientState.Objects;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
using Dalamud.Plugin.Services;
|
2024-08-07 23:49:14 +00:00
|
|
|
|
using FFXIVClientStructs.FFXIV.Client.Game.Event;
|
|
|
|
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
2024-08-07 23:49:14 +00:00
|
|
|
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
2024-06-13 23:10:05 +00:00
|
|
|
|
using LLib;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
using LLib.GameUI;
|
|
|
|
|
using Lumina.Excel.GeneratedSheets;
|
2024-06-08 19:16:57 +00:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2024-08-07 23:49:14 +00:00
|
|
|
|
using Questionable.Controller.Steps.Interactions;
|
2024-07-11 00:56:42 +00:00
|
|
|
|
using Questionable.Data;
|
2024-08-05 15:09:49 +00:00
|
|
|
|
using Questionable.Functions;
|
|
|
|
|
using Questionable.Model;
|
2024-08-02 16:30:21 +00:00
|
|
|
|
using Questionable.Model.Questing;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
using Quest = Questionable.Model.Quest;
|
2024-06-09 14:37:26 +00:00
|
|
|
|
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
|
|
|
|
namespace Questionable.Controller;
|
|
|
|
|
|
|
|
|
|
internal sealed class GameUiController : IDisposable
|
|
|
|
|
{
|
|
|
|
|
private readonly IAddonLifecycle _addonLifecycle;
|
|
|
|
|
private readonly IDataManager _dataManager;
|
2024-08-05 15:09:49 +00:00
|
|
|
|
private readonly QuestFunctions _questFunctions;
|
2024-08-11 00:29:34 +00:00
|
|
|
|
private readonly AetheryteFunctions _aetheryteFunctions;
|
2024-08-05 15:09:49 +00:00
|
|
|
|
private readonly ExcelFunctions _excelFunctions;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
private readonly QuestController _questController;
|
2024-07-11 00:56:42 +00:00
|
|
|
|
private readonly QuestRegistry _questRegistry;
|
|
|
|
|
private readonly QuestData _questData;
|
2024-06-06 16:49:49 +00:00
|
|
|
|
private readonly IGameGui _gameGui;
|
2024-06-12 16:03:48 +00:00
|
|
|
|
private readonly ITargetManager _targetManager;
|
2024-08-07 23:49:14 +00:00
|
|
|
|
private readonly IFramework _framework;
|
2024-06-08 19:16:57 +00:00
|
|
|
|
private readonly ILogger<GameUiController> _logger;
|
2024-06-13 23:10:05 +00:00
|
|
|
|
private readonly Regex _returnRegex;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
2024-08-05 15:09:49 +00:00
|
|
|
|
public GameUiController(
|
|
|
|
|
IAddonLifecycle addonLifecycle,
|
|
|
|
|
IDataManager dataManager,
|
|
|
|
|
QuestFunctions questFunctions,
|
2024-08-11 00:29:34 +00:00
|
|
|
|
AetheryteFunctions aetheryteFunctions,
|
2024-08-05 15:09:49 +00:00
|
|
|
|
ExcelFunctions excelFunctions,
|
|
|
|
|
QuestController questController,
|
|
|
|
|
QuestRegistry questRegistry,
|
|
|
|
|
QuestData questData,
|
|
|
|
|
IGameGui gameGui,
|
|
|
|
|
ITargetManager targetManager,
|
2024-08-07 23:49:14 +00:00
|
|
|
|
IFramework framework,
|
|
|
|
|
IPluginLog pluginLog,
|
|
|
|
|
ILogger<GameUiController> logger)
|
2024-06-03 21:17:35 +00:00
|
|
|
|
{
|
|
|
|
|
_addonLifecycle = addonLifecycle;
|
|
|
|
|
_dataManager = dataManager;
|
2024-08-05 15:09:49 +00:00
|
|
|
|
_questFunctions = questFunctions;
|
2024-08-11 00:29:34 +00:00
|
|
|
|
_aetheryteFunctions = aetheryteFunctions;
|
2024-08-05 15:09:49 +00:00
|
|
|
|
_excelFunctions = excelFunctions;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
_questController = questController;
|
2024-07-11 00:56:42 +00:00
|
|
|
|
_questRegistry = questRegistry;
|
|
|
|
|
_questData = questData;
|
2024-06-06 16:49:49 +00:00
|
|
|
|
_gameGui = gameGui;
|
2024-06-12 16:03:48 +00:00
|
|
|
|
_targetManager = targetManager;
|
2024-08-07 23:49:14 +00:00
|
|
|
|
_framework = framework;
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger = logger;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
2024-06-13 23:10:05 +00:00
|
|
|
|
_returnRegex = _dataManager.GetExcelSheet<Addon>()!.GetRow(196)!.GetRegex(addon => addon.Text, pluginLog)!;
|
|
|
|
|
|
2024-06-03 21:17:35 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectString", SelectStringPostSetup);
|
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "CutSceneSelectString", CutsceneSelectStringPostSetup);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectIconString", SelectIconStringPostSetup);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectYesno", SelectYesnoPostSetup);
|
2024-07-09 14:58:26 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup);
|
2024-07-22 22:39:12 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "CreditScroll", CreditScrollPostSetup);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "Credit", CreditPostSetup);
|
2024-08-10 19:37:33 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "CreditPlayer", CreditPlayerPostSetup);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "AkatsukiNote", UnendingCodexPostSetup);
|
2024-06-13 23:10:05 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "ContentsTutorial", ContentsTutorialPostSetup);
|
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "MultipleHelpWindow", MultipleHelpWindowPostSetup);
|
2024-07-29 19:19:22 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup);
|
2024-08-07 23:49:14 +00:00
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "JournalResult", JournalResultPostSetup);
|
|
|
|
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "GuildLeve", GuildLevePostSetup);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-06 16:49:49 +00:00
|
|
|
|
internal unsafe void HandleCurrentDialogueChoices()
|
|
|
|
|
{
|
|
|
|
|
if (_gameGui.TryGetAddonByName("SelectString", out AddonSelectString* addonSelectString))
|
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogInformation("SelectString window is open");
|
2024-06-08 09:30:26 +00:00
|
|
|
|
SelectStringPostSetup(addonSelectString, true);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_gameGui.TryGetAddonByName("CutSceneSelectString",
|
|
|
|
|
out AddonCutSceneSelectString* addonCutSceneSelectString))
|
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogInformation("CutSceneSelectString window is open");
|
2024-06-08 09:30:26 +00:00
|
|
|
|
CutsceneSelectStringPostSetup(addonCutSceneSelectString, true);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_gameGui.TryGetAddonByName("SelectIconString", out AddonSelectIconString* addonSelectIconString))
|
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogInformation("SelectIconString window is open");
|
2024-06-08 09:30:26 +00:00
|
|
|
|
SelectIconStringPostSetup(addonSelectIconString, true);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_gameGui.TryGetAddonByName("SelectYesno", out AddonSelectYesno* addonSelectYesno))
|
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogInformation("SelectYesno window is open");
|
2024-06-08 09:30:26 +00:00
|
|
|
|
SelectYesnoPostSetup(addonSelectYesno, true);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
2024-07-09 14:58:26 +00:00
|
|
|
|
|
|
|
|
|
if (_gameGui.TryGetAddonByName("PointMenu", out AtkUnitBase* addonPointMenu))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("PointMenu is open");
|
|
|
|
|
PointMenuPostSetup(addonPointMenu);
|
|
|
|
|
}
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-03 21:17:35 +00:00
|
|
|
|
private unsafe void SelectStringPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
|
|
|
|
AddonSelectString* addonSelectString = (AddonSelectString*)args.Addon;
|
2024-06-08 09:30:26 +00:00
|
|
|
|
SelectStringPostSetup(addonSelectString, false);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 09:30:26 +00:00
|
|
|
|
private unsafe void SelectStringPostSetup(AddonSelectString* addonSelectString, bool checkAllSteps)
|
2024-06-06 16:49:49 +00:00
|
|
|
|
{
|
2024-06-03 21:17:35 +00:00
|
|
|
|
string? actualPrompt = addonSelectString->AtkUnitBase.AtkValues[2].ReadAtkString();
|
|
|
|
|
if (actualPrompt == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2024-06-06 16:49:49 +00:00
|
|
|
|
List<string?> answers = new();
|
|
|
|
|
for (ushort i = 7; i < addonSelectString->AtkUnitBase.AtkValuesCount; ++i)
|
2024-06-09 14:37:26 +00:00
|
|
|
|
{
|
|
|
|
|
if (addonSelectString->AtkUnitBase.AtkValues[i].Type == ValueType.String)
|
|
|
|
|
answers.Add(addonSelectString->AtkUnitBase.AtkValues[i].ReadAtkString());
|
|
|
|
|
}
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
2024-07-07 16:36:53 +00:00
|
|
|
|
int? answer = HandleListChoice(actualPrompt, answers, checkAllSteps) ?? HandleInstanceListChoice(actualPrompt);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
if (answer != null)
|
|
|
|
|
addonSelectString->AtkUnitBase.FireCallbackInt(answer.Value);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private unsafe void CutsceneSelectStringPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
|
|
|
|
AddonCutSceneSelectString* addonCutSceneSelectString = (AddonCutSceneSelectString*)args.Addon;
|
2024-06-08 09:30:26 +00:00
|
|
|
|
CutsceneSelectStringPostSetup(addonCutSceneSelectString, false);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 09:30:26 +00:00
|
|
|
|
private unsafe void CutsceneSelectStringPostSetup(AddonCutSceneSelectString* addonCutSceneSelectString,
|
|
|
|
|
bool checkAllSteps)
|
2024-06-06 16:49:49 +00:00
|
|
|
|
{
|
2024-06-03 21:17:35 +00:00
|
|
|
|
string? actualPrompt = addonCutSceneSelectString->AtkUnitBase.AtkValues[2].ReadAtkString();
|
|
|
|
|
if (actualPrompt == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2024-06-06 16:49:49 +00:00
|
|
|
|
List<string?> answers = new();
|
|
|
|
|
for (int i = 5; i < addonCutSceneSelectString->AtkUnitBase.AtkValuesCount; ++i)
|
|
|
|
|
answers.Add(addonCutSceneSelectString->AtkUnitBase.AtkValues[i].ReadAtkString());
|
|
|
|
|
|
2024-06-08 09:30:26 +00:00
|
|
|
|
int? answer = HandleListChoice(actualPrompt, answers, checkAllSteps);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
if (answer != null)
|
|
|
|
|
addonCutSceneSelectString->AtkUnitBase.FireCallbackInt(answer.Value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private unsafe void SelectIconStringPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
|
|
|
|
AddonSelectIconString* addonSelectIconString = (AddonSelectIconString*)args.Addon;
|
2024-06-08 09:30:26 +00:00
|
|
|
|
SelectIconStringPostSetup(addonSelectIconString, false);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-16 08:54:47 +00:00
|
|
|
|
[SuppressMessage("ReSharper", "RedundantJumpStatement")]
|
2024-06-08 09:30:26 +00:00
|
|
|
|
private unsafe void SelectIconStringPostSetup(AddonSelectIconString* addonSelectIconString, bool checkAllSteps)
|
2024-06-06 16:49:49 +00:00
|
|
|
|
{
|
|
|
|
|
string? actualPrompt = addonSelectIconString->AtkUnitBase.AtkValues[3].ReadAtkString();
|
|
|
|
|
if (string.IsNullOrEmpty(actualPrompt))
|
|
|
|
|
actualPrompt = null;
|
|
|
|
|
|
2024-07-14 01:42:30 +00:00
|
|
|
|
var answers = GetChoices(addonSelectIconString);
|
2024-06-08 09:30:26 +00:00
|
|
|
|
int? answer = HandleListChoice(actualPrompt, answers, checkAllSteps);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
if (answer != null)
|
|
|
|
|
{
|
|
|
|
|
addonSelectIconString->AtkUnitBase.FireCallbackInt(answer.Value);
|
2024-06-13 23:10:05 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-14 19:31:07 +00:00
|
|
|
|
// this is 'Daily Quests' for tribal quests, but not set for normal selections
|
|
|
|
|
string? title = addonSelectIconString->AtkValues[0].ReadAtkString();
|
|
|
|
|
|
2024-07-12 00:42:37 +00:00
|
|
|
|
var currentQuest = _questController.StartedQuest;
|
2024-07-14 19:31:07 +00:00
|
|
|
|
if (currentQuest != null && (actualPrompt == null || title != null))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Checking if current quest {Name} is on the list", currentQuest.Quest.Info.Name);
|
|
|
|
|
if (CheckQuestSelection(addonSelectIconString, currentQuest.Quest, answers))
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var nextQuest = _questController.NextQuest;
|
|
|
|
|
if (nextQuest != null && (actualPrompt == null || title != null))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Checking if next quest {Name} is on the list", nextQuest.Quest.Info.Name);
|
|
|
|
|
if (CheckQuestSelection(addonSelectIconString, nextQuest.Quest, answers))
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-14 21:26:06 +00:00
|
|
|
|
private unsafe bool CheckQuestSelection(AddonSelectIconString* addonSelectIconString, Quest quest,
|
|
|
|
|
List<string?> answers)
|
2024-07-14 19:31:07 +00:00
|
|
|
|
{
|
|
|
|
|
// it is possible for this to be a quest selection
|
|
|
|
|
string questName = quest.Info.Name;
|
2024-08-05 15:09:49 +00:00
|
|
|
|
int questSelection = answers.FindIndex(x => GameFunctions.GameStringEquals(questName, x));
|
2024-07-14 19:31:07 +00:00
|
|
|
|
if (questSelection >= 0)
|
2024-06-13 23:10:05 +00:00
|
|
|
|
{
|
2024-07-14 19:31:07 +00:00
|
|
|
|
addonSelectIconString->AtkUnitBase.FireCallbackInt(questSelection);
|
|
|
|
|
return true;
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
2024-07-14 19:31:07 +00:00
|
|
|
|
|
|
|
|
|
return false;
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-14 01:42:30 +00:00
|
|
|
|
public static unsafe List<string?> GetChoices(AddonSelectIconString* addonSelectIconString)
|
|
|
|
|
{
|
|
|
|
|
List<string?> answers = new();
|
|
|
|
|
for (ushort i = 0; i < addonSelectIconString->AtkUnitBase.AtkValues[5].Int; i++)
|
2024-07-14 21:26:06 +00:00
|
|
|
|
answers.Add(addonSelectIconString->AtkValues[i * 3 + 7].ReadAtkString());
|
2024-07-14 01:42:30 +00:00
|
|
|
|
|
|
|
|
|
return answers;
|
|
|
|
|
}
|
2024-06-06 16:49:49 +00:00
|
|
|
|
|
2024-06-08 09:30:26 +00:00
|
|
|
|
private int? HandleListChoice(string? actualPrompt, List<string?> answers, bool checkAllSteps)
|
2024-06-06 16:49:49 +00:00
|
|
|
|
{
|
2024-07-11 00:56:42 +00:00
|
|
|
|
List<DialogueChoiceInfo> dialogueChoices = [];
|
2024-08-07 23:49:14 +00:00
|
|
|
|
|
|
|
|
|
// levequest choices have some vague sort of priority
|
|
|
|
|
if (_questController.HasCurrentTaskMatching<Interact.DoInteract>(out var interact) &&
|
|
|
|
|
interact.Quest != null &&
|
|
|
|
|
interact.InteractionType is EInteractionType.AcceptLeve or EInteractionType.CompleteLeve)
|
|
|
|
|
{
|
|
|
|
|
if (interact.InteractionType == EInteractionType.AcceptLeve)
|
|
|
|
|
{
|
|
|
|
|
dialogueChoices.Add(new DialogueChoiceInfo(interact.Quest,
|
|
|
|
|
new DialogueChoice
|
|
|
|
|
{
|
|
|
|
|
Type = EDialogChoiceType.List,
|
|
|
|
|
ExcelSheet = "leve/GuildleveAssignment",
|
|
|
|
|
Prompt = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_TITLE"),
|
|
|
|
|
Answer = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_01"),
|
|
|
|
|
}));
|
|
|
|
|
interact.InteractionType = EInteractionType.None;
|
|
|
|
|
}
|
|
|
|
|
else if (interact.InteractionType == EInteractionType.CompleteLeve)
|
|
|
|
|
{
|
|
|
|
|
dialogueChoices.Add(new DialogueChoiceInfo(interact.Quest,
|
|
|
|
|
new DialogueChoice
|
|
|
|
|
{
|
|
|
|
|
Type = EDialogChoiceType.List,
|
|
|
|
|
ExcelSheet = "leve/GuildleveAssignment",
|
|
|
|
|
Prompt = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_TITLE"),
|
|
|
|
|
Answer = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_REWARD"),
|
|
|
|
|
}));
|
|
|
|
|
interact.InteractionType = EInteractionType.None;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var currentQuest = _questController.SimulatedQuest ??
|
|
|
|
|
_questController.GatheringQuest ??
|
|
|
|
|
_questController.StartedQuest;
|
2024-07-11 00:56:42 +00:00
|
|
|
|
if (currentQuest != null)
|
2024-06-06 16:49:49 +00:00
|
|
|
|
{
|
2024-07-11 00:56:42 +00:00
|
|
|
|
var quest = currentQuest.Quest;
|
|
|
|
|
if (checkAllSteps)
|
|
|
|
|
{
|
|
|
|
|
var sequence = quest.FindSequence(currentQuest.Sequence);
|
|
|
|
|
var choices = sequence?.Steps.SelectMany(x => x.DialogueChoices);
|
|
|
|
|
if (choices != null)
|
|
|
|
|
dialogueChoices.AddRange(choices.Select(x => new DialogueChoiceInfo(quest, x)));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var step = quest.FindSequence(currentQuest.Sequence)?.FindStep(currentQuest.Step);
|
|
|
|
|
if (step == null)
|
2024-07-12 12:37:54 +00:00
|
|
|
|
_logger.LogDebug("Ignoring current quest dialogue choices, no active step");
|
2024-07-11 00:56:42 +00:00
|
|
|
|
else
|
|
|
|
|
dialogueChoices.AddRange(step.DialogueChoices.Select(x => new DialogueChoiceInfo(quest, x)));
|
|
|
|
|
}
|
2024-07-14 21:26:06 +00:00
|
|
|
|
|
|
|
|
|
// add all travel dialogue choices
|
|
|
|
|
var targetTerritoryId = FindTargetTerritoryFromQuestStep(currentQuest);
|
|
|
|
|
if (targetTerritoryId != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (string? answer in answers)
|
|
|
|
|
{
|
|
|
|
|
if (answer == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (TryFindWarp(targetTerritoryId.Value, answer, out uint? warpId, out string? warpText))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Adding warp {Id}, {Prompt}", warpId, warpText);
|
|
|
|
|
dialogueChoices.Add(new DialogueChoiceInfo(quest, new DialogueChoice
|
|
|
|
|
{
|
|
|
|
|
Type = EDialogChoiceType.List,
|
|
|
|
|
ExcelSheet = null,
|
|
|
|
|
Prompt = null,
|
|
|
|
|
Answer = ExcelRef.FromSheetValue(warpText),
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-08 09:30:26 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2024-07-12 12:37:54 +00:00
|
|
|
|
_logger.LogDebug("Ignoring current quest dialogue choices, no active quest");
|
2024-07-11 00:56:42 +00:00
|
|
|
|
|
|
|
|
|
// add all quests that start with the targeted npc
|
|
|
|
|
var target = _targetManager.Target;
|
|
|
|
|
if (target != null)
|
2024-06-08 09:30:26 +00:00
|
|
|
|
{
|
2024-08-05 15:09:49 +00:00
|
|
|
|
foreach (var questInfo in _questData.GetAllByIssuerDataId(target.DataId).Where(x => x.QuestId is QuestId))
|
2024-06-08 09:30:26 +00:00
|
|
|
|
{
|
2024-08-05 15:09:49 +00:00
|
|
|
|
if (_questFunctions.IsReadyToAcceptQuest(questInfo.QuestId) &&
|
2024-07-11 00:56:42 +00:00
|
|
|
|
_questRegistry.TryGetQuest(questInfo.QuestId, out Quest? knownQuest))
|
|
|
|
|
{
|
|
|
|
|
var questChoices = knownQuest.FindSequence(0)?.Steps
|
|
|
|
|
.SelectMany(x => x.DialogueChoices)
|
|
|
|
|
.ToList();
|
|
|
|
|
if (questChoices != null && questChoices.Count > 0)
|
|
|
|
|
{
|
2024-07-14 21:26:06 +00:00
|
|
|
|
_logger.LogInformation("Adding {Count} dialogue choices from not accepted quest {QuestName}",
|
|
|
|
|
questChoices.Count, questInfo.Name);
|
2024-07-11 00:56:42 +00:00
|
|
|
|
dialogueChoices.AddRange(questChoices.Select(x => new DialogueChoiceInfo(knownQuest, x)));
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-08 09:30:26 +00:00
|
|
|
|
}
|
2024-08-07 23:49:14 +00:00
|
|
|
|
|
2024-08-08 13:31:08 +00:00
|
|
|
|
if ((_questController.IsRunning || _questController.WasLastTaskUpdateWithin(TimeSpan.FromSeconds(5)))
|
|
|
|
|
&& _questController.NextQuest == null)
|
2024-08-07 23:49:14 +00:00
|
|
|
|
{
|
|
|
|
|
// make sure to always close the leve dialogue
|
|
|
|
|
if (_questData.GetAllByIssuerDataId(target.DataId).Any(x => x.QuestId is LeveId))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Adding close leve dialogue as option");
|
|
|
|
|
dialogueChoices.Add(new DialogueChoiceInfo(null,
|
|
|
|
|
new DialogueChoice
|
|
|
|
|
{
|
|
|
|
|
Type = EDialogChoiceType.List,
|
|
|
|
|
ExcelSheet = "leve/GuildleveAssignment",
|
|
|
|
|
Prompt = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_TITLE"),
|
|
|
|
|
Answer = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_07"),
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
2024-07-11 00:56:42 +00:00
|
|
|
|
if (dialogueChoices.Count == 0)
|
2024-08-07 23:49:14 +00:00
|
|
|
|
{
|
|
|
|
|
_logger.LogDebug("No dialogue choices to check");
|
2024-07-11 00:56:42 +00:00
|
|
|
|
return null;
|
2024-08-07 23:49:14 +00:00
|
|
|
|
}
|
2024-07-11 00:56:42 +00:00
|
|
|
|
|
|
|
|
|
foreach (var (quest, dialogueChoice) in dialogueChoices)
|
2024-06-03 21:17:35 +00:00
|
|
|
|
{
|
2024-06-12 16:03:48 +00:00
|
|
|
|
if (dialogueChoice.Type != EDialogChoiceType.List)
|
|
|
|
|
continue;
|
|
|
|
|
|
2024-06-03 21:17:35 +00:00
|
|
|
|
if (dialogueChoice.Answer == null)
|
2024-06-06 16:49:49 +00:00
|
|
|
|
{
|
2024-06-12 16:03:48 +00:00
|
|
|
|
_logger.LogDebug("Ignoring entry in DialogueChoices, no answer");
|
2024-06-03 21:17:35 +00:00
|
|
|
|
continue;
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
2024-06-12 16:03:48 +00:00
|
|
|
|
if (dialogueChoice.DataId != null && dialogueChoice.DataId != _targetManager.Target?.DataId)
|
2024-06-06 16:49:49 +00:00
|
|
|
|
{
|
2024-06-12 16:03:48 +00:00
|
|
|
|
_logger.LogDebug(
|
|
|
|
|
"Skipping entry in DialogueChoice expecting target dataId {ExpectedDataId}, actual target is {ActualTargetId}",
|
|
|
|
|
dialogueChoice.DataId, _targetManager.Target?.DataId);
|
|
|
|
|
continue;
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-08-05 15:09:49 +00:00
|
|
|
|
string? excelPrompt = ResolveReference(quest, dialogueChoice.ExcelSheet, dialogueChoice.Prompt, false)
|
|
|
|
|
?.GetString();
|
|
|
|
|
StringOrRegex? excelAnswer = ResolveReference(quest, dialogueChoice.ExcelSheet, dialogueChoice.Answer,
|
|
|
|
|
dialogueChoice.AnswerIsRegularExpression);
|
2024-06-12 16:03:48 +00:00
|
|
|
|
|
2024-06-06 16:49:49 +00:00
|
|
|
|
if (actualPrompt == null && !string.IsNullOrEmpty(excelPrompt))
|
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogInformation("Unexpected excelPrompt: {ExcelPrompt}", excelPrompt);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
continue;
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
2024-08-05 15:09:49 +00:00
|
|
|
|
if (actualPrompt != null &&
|
|
|
|
|
(excelPrompt == null || !GameFunctions.GameStringEquals(actualPrompt, excelPrompt)))
|
2024-06-03 21:17:35 +00:00
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogInformation("Unexpected excelPrompt: {ExcelPrompt}, actualPrompt: {ActualPrompt}",
|
|
|
|
|
excelPrompt, actualPrompt);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
2024-06-06 16:49:49 +00:00
|
|
|
|
for (int i = 0; i < answers.Count; ++i)
|
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogTrace("Checking if {ActualAnswer} == {ExpectedAnswer}",
|
|
|
|
|
answers[i], excelAnswer);
|
2024-08-05 15:09:49 +00:00
|
|
|
|
if (IsMatch(answers[i], excelAnswer))
|
2024-06-06 16:49:49 +00:00
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogInformation("Returning {Index}: '{Answer}' for '{Prompt}'",
|
|
|
|
|
i, answers[i], actualPrompt);
|
2024-08-05 15:09:49 +00:00
|
|
|
|
|
|
|
|
|
// ensure we only open the dialog once
|
2024-08-07 23:49:14 +00:00
|
|
|
|
if (quest?.Id is SatisfactionSupplyNpcId)
|
2024-08-05 15:09:49 +00:00
|
|
|
|
{
|
|
|
|
|
if (_questController.GatheringQuest == null ||
|
|
|
|
|
_questController.GatheringQuest.Sequence == 255)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
_questController.GatheringQuest.SetSequence(1);
|
2024-08-10 18:36:43 +00:00
|
|
|
|
_questController.StartSingleQuest("SatisfactionSupply turn in");
|
2024-08-05 15:09:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-06 16:49:49 +00:00
|
|
|
|
return i;
|
|
|
|
|
}
|
2024-06-03 21:17:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-06 16:49:49 +00:00
|
|
|
|
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogInformation("No matching answer found for {Prompt}.", actualPrompt);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
return null;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-08-05 15:09:49 +00:00
|
|
|
|
private static bool IsMatch(string? actualAnswer, StringOrRegex? expectedAnswer)
|
|
|
|
|
{
|
|
|
|
|
if (actualAnswer == null && expectedAnswer == null)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (actualAnswer == null || expectedAnswer == null)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return expectedAnswer.IsMatch(actualAnswer);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-07 16:36:53 +00:00
|
|
|
|
private int? HandleInstanceListChoice(string? actualPrompt)
|
|
|
|
|
{
|
|
|
|
|
if (!_questController.IsRunning)
|
|
|
|
|
return null;
|
|
|
|
|
|
2024-08-05 15:09:49 +00:00
|
|
|
|
string? expectedPrompt = _excelFunctions.GetDialogueTextByRowId("Addon", 2090, false).GetString();
|
|
|
|
|
if (GameFunctions.GameStringEquals(actualPrompt, expectedPrompt))
|
2024-07-07 16:36:53 +00:00
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Selecting no prefered instance as answer for '{Prompt}'", actualPrompt);
|
|
|
|
|
return 0; // any instance
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-03 21:17:35 +00:00
|
|
|
|
private unsafe void SelectYesnoPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
|
|
|
|
AddonSelectYesno* addonSelectYesno = (AddonSelectYesno*)args.Addon;
|
2024-06-08 09:30:26 +00:00
|
|
|
|
SelectYesnoPostSetup(addonSelectYesno, false);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-29 19:19:22 +00:00
|
|
|
|
[SuppressMessage("ReSharper", "RedundantJumpStatement")]
|
2024-06-08 09:30:26 +00:00
|
|
|
|
private unsafe void SelectYesnoPostSetup(AddonSelectYesno* addonSelectYesno, bool checkAllSteps)
|
2024-06-06 16:49:49 +00:00
|
|
|
|
{
|
2024-06-03 21:17:35 +00:00
|
|
|
|
string? actualPrompt = addonSelectYesno->AtkUnitBase.AtkValues[0].ReadAtkString();
|
|
|
|
|
if (actualPrompt == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogTrace("Prompt: '{Prompt}'", actualPrompt);
|
2024-08-07 23:49:14 +00:00
|
|
|
|
var director = UIState.Instance()->DirectorTodo.Director;
|
|
|
|
|
if (director != null && director->EventHandlerInfo != null &&
|
|
|
|
|
director->EventHandlerInfo->EventId.ContentId == EventHandlerType.GatheringLeveDirector &&
|
|
|
|
|
director->Sequence == 254)
|
|
|
|
|
{
|
|
|
|
|
// just close the dialogue for 'do you want to return to next settlement', should prolly be different for
|
|
|
|
|
// ARR territories
|
|
|
|
|
addonSelectYesno->AtkUnitBase.FireCallbackInt(1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
2024-07-12 00:42:37 +00:00
|
|
|
|
var currentQuest = _questController.StartedQuest;
|
2024-07-29 19:19:22 +00:00
|
|
|
|
if (currentQuest != null && CheckQuestYesNo(addonSelectYesno, currentQuest, actualPrompt, checkAllSteps))
|
|
|
|
|
return;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
2024-07-14 21:26:06 +00:00
|
|
|
|
var simulatedQuest = _questController.SimulatedQuest;
|
2024-07-21 14:22:01 +00:00
|
|
|
|
if (simulatedQuest != null && HandleTravelYesNo(addonSelectYesno, simulatedQuest, actualPrompt))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var nextQuest = _questController.NextQuest;
|
2024-07-29 19:19:22 +00:00
|
|
|
|
if (nextQuest != null && CheckQuestYesNo(addonSelectYesno, nextQuest, actualPrompt, checkAllSteps))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private unsafe bool CheckQuestYesNo(AddonSelectYesno* addonSelectYesno, QuestController.QuestProgress currentQuest,
|
|
|
|
|
string actualPrompt, bool checkAllSteps)
|
|
|
|
|
{
|
|
|
|
|
var quest = currentQuest.Quest;
|
|
|
|
|
if (checkAllSteps)
|
|
|
|
|
{
|
|
|
|
|
var sequence = quest.FindSequence(currentQuest.Sequence);
|
|
|
|
|
if (sequence != null && HandleDefaultYesNo(addonSelectYesno, quest,
|
|
|
|
|
sequence.Steps.SelectMany(x => x.DialogueChoices).ToList(), actualPrompt))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var step = quest.FindSequence(currentQuest.Sequence)?.FindStep(currentQuest.Step);
|
|
|
|
|
if (step != null && HandleDefaultYesNo(addonSelectYesno, quest, step.DialogueChoices, actualPrompt))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 23:49:14 +00:00
|
|
|
|
if (currentQuest.Quest.Id is LeveId)
|
|
|
|
|
{
|
|
|
|
|
var dialogueChoice = new DialogueChoice
|
|
|
|
|
{
|
|
|
|
|
Type = EDialogChoiceType.YesNo,
|
|
|
|
|
ExcelSheet = "Addon",
|
|
|
|
|
Prompt = new ExcelRef(608),
|
|
|
|
|
Yes = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (HandleDefaultYesNo(addonSelectYesno, quest, [dialogueChoice], actualPrompt))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-29 19:19:22 +00:00
|
|
|
|
if (HandleTravelYesNo(addonSelectYesno, currentQuest, actualPrompt))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 09:30:26 +00:00
|
|
|
|
private unsafe bool HandleDefaultYesNo(AddonSelectYesno* addonSelectYesno, Quest quest,
|
2024-06-13 23:27:40 +00:00
|
|
|
|
IList<DialogueChoice> dialogueChoices, string actualPrompt)
|
2024-06-03 21:17:35 +00:00
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogTrace("DefaultYesNo: Choice count: {Count}", dialogueChoices.Count);
|
2024-06-08 09:30:26 +00:00
|
|
|
|
foreach (var dialogueChoice in dialogueChoices)
|
2024-06-03 21:17:35 +00:00
|
|
|
|
{
|
2024-06-12 16:03:48 +00:00
|
|
|
|
if (dialogueChoice.Type != EDialogChoiceType.YesNo)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (dialogueChoice.DataId != null && dialogueChoice.DataId != _targetManager.Target?.DataId)
|
2024-06-06 16:49:49 +00:00
|
|
|
|
{
|
2024-06-12 16:03:48 +00:00
|
|
|
|
_logger.LogDebug(
|
|
|
|
|
"Skipping entry in DialogueChoice expecting target dataId {ExpectedDataId}, actual target is {ActualTargetId}",
|
|
|
|
|
dialogueChoice.DataId, _targetManager.Target?.DataId);
|
|
|
|
|
continue;
|
2024-06-06 16:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-08-05 15:09:49 +00:00
|
|
|
|
string? excelPrompt = ResolveReference(quest, dialogueChoice.ExcelSheet, dialogueChoice.Prompt, false)
|
|
|
|
|
?.GetString();
|
|
|
|
|
if (excelPrompt == null || !GameFunctions.GameStringEquals(actualPrompt, excelPrompt))
|
2024-06-12 16:03:48 +00:00
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Unexpected excelPrompt: {ExcelPrompt}, actualPrompt: {ActualPrompt}",
|
|
|
|
|
excelPrompt, actualPrompt);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
continue;
|
2024-06-12 16:03:48 +00:00
|
|
|
|
}
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
|
|
|
|
addonSelectYesno->AtkUnitBase.FireCallbackInt(dialogueChoice.Yes ? 0 : 1);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-14 21:26:06 +00:00
|
|
|
|
private unsafe bool HandleTravelYesNo(AddonSelectYesno* addonSelectYesno,
|
2024-06-03 21:17:35 +00:00
|
|
|
|
QuestController.QuestProgress currentQuest, string actualPrompt)
|
|
|
|
|
{
|
2024-08-11 00:29:34 +00:00
|
|
|
|
if (_aetheryteFunctions.ReturnRequestedAt >= DateTime.Now.AddSeconds(-2) && _returnRegex.IsMatch(actualPrompt))
|
2024-06-13 23:10:05 +00:00
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Automatically confirming return...");
|
|
|
|
|
addonSelectYesno->AtkUnitBase.FireCallbackInt(0);
|
2024-07-14 21:26:06 +00:00
|
|
|
|
return true;
|
2024-06-13 23:10:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-29 19:19:22 +00:00
|
|
|
|
if (_questController.IsRunning && _gameGui.TryGetAddonByName("HousingSelectBlock", out AtkUnitBase* _))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Automatically confirming ward selection");
|
|
|
|
|
addonSelectYesno->AtkUnitBase.FireCallbackInt(0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-14 21:26:06 +00:00
|
|
|
|
var targetTerritoryId = FindTargetTerritoryFromQuestStep(currentQuest);
|
|
|
|
|
if (targetTerritoryId != null &&
|
|
|
|
|
TryFindWarp(targetTerritoryId.Value, actualPrompt, out uint? warpId, out string? warpText))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Using warp {Id}, {Prompt}", warpId, warpText);
|
|
|
|
|
addonSelectYesno->AtkUnitBase.FireCallbackInt(0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ushort? FindTargetTerritoryFromQuestStep(QuestController.QuestProgress currentQuest)
|
|
|
|
|
{
|
2024-06-03 21:17:35 +00:00
|
|
|
|
// this can be triggered either manually (in which case we should increase the step counter), or automatically
|
|
|
|
|
// (in which case it is ~1 frame later, and the step counter has already been increased)
|
|
|
|
|
var sequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
|
|
|
|
|
if (sequence == null)
|
2024-07-14 21:26:06 +00:00
|
|
|
|
return null;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
|
|
|
|
QuestStep? step = sequence.FindStep(currentQuest.Step);
|
|
|
|
|
if (step != null)
|
2024-08-07 23:49:14 +00:00
|
|
|
|
_logger.LogTrace("FindTargetTerritoryFromQuestStep (current): {CurrentTerritory}, {TargetTerritory}",
|
|
|
|
|
step.TerritoryId,
|
2024-06-08 19:16:57 +00:00
|
|
|
|
step.TargetTerritoryId);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
|
2024-06-06 16:49:49 +00:00
|
|
|
|
if (step == null || step.TargetTerritoryId == null)
|
2024-06-03 21:17:35 +00:00
|
|
|
|
{
|
2024-08-07 23:49:14 +00:00
|
|
|
|
_logger.LogTrace("FindTargetTerritoryFromQuestStep: Checking previous step...");
|
2024-06-03 21:17:35 +00:00
|
|
|
|
step = sequence.FindStep(currentQuest.Step == 255 ? (sequence.Steps.Count - 1) : (currentQuest.Step - 1));
|
|
|
|
|
|
|
|
|
|
if (step != null)
|
2024-08-07 23:49:14 +00:00
|
|
|
|
_logger.LogTrace("FindTargetTerritoryFromQuestStep (previous): {CurrentTerritory}, {TargetTerritory}",
|
|
|
|
|
step.TerritoryId,
|
2024-06-12 16:03:48 +00:00
|
|
|
|
step.TargetTerritoryId);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-06 16:49:49 +00:00
|
|
|
|
if (step == null || step.TargetTerritoryId == null)
|
2024-06-03 21:17:35 +00:00
|
|
|
|
{
|
2024-08-07 23:49:14 +00:00
|
|
|
|
_logger.LogTrace("FindTargetTerritoryFromQuestStep: Not found");
|
2024-07-14 21:26:06 +00:00
|
|
|
|
return null;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-14 21:26:06 +00:00
|
|
|
|
_logger.LogDebug("Target territory for quest step: {TargetTerritory}", step.TargetTerritoryId);
|
|
|
|
|
return step.TargetTerritoryId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool TryFindWarp(ushort targetTerritoryId, string actualPrompt, [NotNullWhen(true)] out uint? warpId,
|
|
|
|
|
[NotNullWhen(true)] out string? warpText)
|
|
|
|
|
{
|
2024-06-03 21:17:35 +00:00
|
|
|
|
var warps = _dataManager.GetExcelSheet<Warp>()!
|
2024-07-14 21:26:06 +00:00
|
|
|
|
.Where(x => x.RowId > 0 && x.TerritoryType.Row == targetTerritoryId);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
foreach (var entry in warps)
|
|
|
|
|
{
|
2024-07-14 21:26:06 +00:00
|
|
|
|
string? excelName = entry.Name?.ToString();
|
|
|
|
|
string? excelQuestion = entry.Question?.ToString();
|
|
|
|
|
|
2024-08-05 15:09:49 +00:00
|
|
|
|
if (excelQuestion != null && GameFunctions.GameStringEquals(excelQuestion, actualPrompt))
|
2024-06-03 21:17:35 +00:00
|
|
|
|
{
|
2024-07-14 21:26:06 +00:00
|
|
|
|
warpId = entry.RowId;
|
|
|
|
|
warpText = excelQuestion;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2024-08-05 15:09:49 +00:00
|
|
|
|
else if (excelName != null && GameFunctions.GameStringEquals(excelName, actualPrompt))
|
2024-07-14 21:26:06 +00:00
|
|
|
|
{
|
|
|
|
|
warpId = entry.RowId;
|
|
|
|
|
warpText = excelName;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_logger.LogDebug("Ignoring prompt '{Prompt}'", excelQuestion);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-14 21:26:06 +00:00
|
|
|
|
|
|
|
|
|
warpId = null;
|
|
|
|
|
warpText = null;
|
|
|
|
|
return false;
|
2024-06-03 21:17:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-09 14:58:26 +00:00
|
|
|
|
private unsafe void PointMenuPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
|
|
|
|
AtkUnitBase* addonPointMenu = (AtkUnitBase*)args.Addon;
|
|
|
|
|
PointMenuPostSetup(addonPointMenu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private unsafe void PointMenuPostSetup(AtkUnitBase* addonPointMenu)
|
|
|
|
|
{
|
2024-07-12 00:42:37 +00:00
|
|
|
|
var currentQuest = _questController.StartedQuest;
|
2024-07-09 14:58:26 +00:00
|
|
|
|
if (currentQuest == null)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Ignoring point menu, no active quest");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
|
|
|
|
|
if (sequence == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QuestStep? step = sequence.FindStep(currentQuest.Step);
|
|
|
|
|
if (step == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (step.PointMenuChoices.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("No point menu choices");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int counter = currentQuest.StepProgress.PointMenuCounter;
|
|
|
|
|
if (counter >= step.PointMenuChoices.Count)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("No remaining point menu choices");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint choice = step.PointMenuChoices[counter];
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("Handling point menu, picking choice {Choice} (index = {Index})", choice, counter);
|
|
|
|
|
var selectChoice = stackalloc AtkValue[]
|
|
|
|
|
{
|
|
|
|
|
new() { Type = ValueType.Int, Int = 13 },
|
|
|
|
|
new() { Type = ValueType.UInt, UInt = choice }
|
|
|
|
|
};
|
|
|
|
|
addonPointMenu->FireCallback(2, selectChoice);
|
|
|
|
|
|
2024-07-11 00:56:42 +00:00
|
|
|
|
currentQuest.IncreasePointMenuCounter();
|
2024-07-09 14:58:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-22 22:39:12 +00:00
|
|
|
|
/// <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>
|
2024-06-03 21:17:35 +00:00
|
|
|
|
private unsafe void CreditPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogInformation("Closing Credits sequence");
|
2024-06-03 21:17:35 +00:00
|
|
|
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
|
|
|
|
addon->FireCallbackInt(-2);
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-10 19:37:33 +00:00
|
|
|
|
private unsafe void CreditPlayerPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Closing CreditPlayer");
|
|
|
|
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
|
|
|
|
addon->Close(true);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-03 21:17:35 +00:00
|
|
|
|
private unsafe void UnendingCodexPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
2024-08-04 14:03:23 +00:00
|
|
|
|
if (_questController.StartedQuest?.Quest.Id.Value == 4526)
|
2024-06-03 21:17:35 +00:00
|
|
|
|
{
|
2024-06-08 19:16:57 +00:00
|
|
|
|
_logger.LogInformation("Closing Unending Codex");
|
2024-06-03 21:17:35 +00:00
|
|
|
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
|
|
|
|
addon->FireCallbackInt(-2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-13 23:10:05 +00:00
|
|
|
|
private unsafe void ContentsTutorialPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
2024-08-04 14:03:23 +00:00
|
|
|
|
if (_questController.StartedQuest?.Quest.Id.Value == 245)
|
2024-06-13 23:10:05 +00:00
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Closing ContentsTutorial");
|
|
|
|
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
|
|
|
|
addon->FireCallbackInt(13);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-29 19:19:22 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Opened e.g. the first time you open the duty finder window during Sastasha.
|
|
|
|
|
/// </summary>
|
2024-06-13 23:10:05 +00:00
|
|
|
|
private unsafe void MultipleHelpWindowPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
2024-08-04 14:03:23 +00:00
|
|
|
|
if (_questController.StartedQuest?.Quest.Id.Value == 245)
|
2024-06-13 23:10:05 +00:00
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Closing MultipleHelpWindow");
|
|
|
|
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
|
|
|
|
addon->FireCallbackInt(-2);
|
|
|
|
|
addon->FireCallbackInt(-1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-29 19:19:22 +00:00
|
|
|
|
private unsafe void HousingSelectBlockPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
|
|
|
|
if (_questController.IsRunning)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Confirming selected housing ward");
|
|
|
|
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
|
|
|
|
addon->FireCallbackInt(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 23:49:14 +00:00
|
|
|
|
private unsafe void JournalResultPostSetup(AddonEvent type, AddonArgs args)
|
|
|
|
|
{
|
|
|
|
|
if (_questController.IsRunning)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("Checking for quest name of journal result");
|
|
|
|
|
AddonJournalResult* addon = (AddonJournalResult*)args.Addon;
|
|
|
|
|
|
|
|
|
|
string questName = addon->AtkTextNode250->NodeText.ToString();
|
2024-08-08 21:58:29 +00:00
|
|
|
|
if (_questController.CurrentQuest is { Quest.Id: LeveId } &&
|
2024-08-07 23:49:14 +00:00
|
|
|
|
GameFunctions.GameStringEquals(_questController.CurrentQuest.Quest.Info.Name, questName))
|
2024-08-08 22:53:05 +00:00
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("JournalResult has the current leve, auto-accepting it");
|
2024-08-07 23:49:14 +00:00
|
|
|
|
addon->FireCallbackInt(0);
|
2024-08-08 22:53:05 +00:00
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-07 23:49:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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(() =>
|
|
|
|
|
{
|
|
|
|
|
_questController.SetPendingQuest(nextQuest);
|
|
|
|
|
_questController.SetNextQuest(null);
|
|
|
|
|
|
|
|
|
|
var agent = UIModule.Instance()->GetAgentModule()->GetAgentByInternalId(AgentId.LeveQuest);
|
|
|
|
|
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);
|
|
|
|
|
}, TimeSpan.FromMilliseconds(100));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private StringOrRegex? ResolveReference(Quest? quest, string? excelSheet, ExcelRef? excelRef, bool isRegExp)
|
2024-06-12 16:03:48 +00:00
|
|
|
|
{
|
|
|
|
|
if (excelRef == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
if (excelRef.Type == ExcelRef.EType.Key)
|
2024-08-05 15:09:49 +00:00
|
|
|
|
return _excelFunctions.GetDialogueText(quest, excelSheet, excelRef.AsKey(), isRegExp);
|
2024-06-12 16:03:48 +00:00
|
|
|
|
else if (excelRef.Type == ExcelRef.EType.RowId)
|
2024-08-05 15:09:49 +00:00
|
|
|
|
return _excelFunctions.GetDialogueTextByRowId(excelSheet, excelRef.AsRowId(), isRegExp);
|
2024-07-14 21:26:06 +00:00
|
|
|
|
else if (excelRef.Type == ExcelRef.EType.RawString)
|
2024-08-05 15:09:49 +00:00
|
|
|
|
return new StringOrRegex(excelRef.AsRawString());
|
2024-06-12 16:03:48 +00:00
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-03 21:17:35 +00:00
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2024-08-07 23:49:14 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "GuildLeve", GuildLevePostSetup);
|
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "JournalResult", JournalResultPostSetup);
|
2024-07-29 19:19:22 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "HousingSelectBlock", HousingSelectBlockPostSetup);
|
2024-06-13 23:10:05 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "MultipleHelpWindow", MultipleHelpWindowPostSetup);
|
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "ContentsTutorial", ContentsTutorialPostSetup);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "AkatsukiNote", UnendingCodexPostSetup);
|
2024-08-10 19:37:33 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "CreditPlayer", CreditPlayerPostSetup);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "Credit", CreditPostSetup);
|
2024-07-22 22:39:12 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "CreditScroll", CreditScrollPostSetup);
|
2024-07-09 14:58:26 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "PointMenu", PointMenuPostSetup);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectYesno", SelectYesnoPostSetup);
|
2024-06-06 16:49:49 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectIconString", SelectIconStringPostSetup);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "CutSceneSelectString", CutsceneSelectStringPostSetup);
|
|
|
|
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectString", SelectStringPostSetup);
|
|
|
|
|
}
|
2024-07-11 00:56:42 +00:00
|
|
|
|
|
2024-08-07 23:49:14 +00:00
|
|
|
|
private sealed record DialogueChoiceInfo(Quest? Quest, DialogueChoice DialogueChoice);
|
2024-06-03 21:17:35 +00:00
|
|
|
|
}
|