Make 'TargetTerritoryId' auto-pick warps for SelectString/SelectIconString (except for lifts in Ul'dah/Limsa, since airship landings are in normal territories)

This commit is contained in:
Liza 2024-07-14 23:26:06 +02:00
parent c521860851
commit 320ce14aed
Signed by: liza
GPG Key ID: 7199F8D727D55F67
5 changed files with 126 additions and 51 deletions

2
LLib

@ -1 +1 @@
Subproject commit 93fac6efb01a1272192d929fd863328271512ea4 Subproject commit aec507a840b7f0a20635c6ddbc7862e9025cea4f

View File

@ -85,15 +85,7 @@
"StopDistance": 7, "StopDistance": 7,
"TerritoryId": 129, "TerritoryId": 129,
"InteractionType": "Interact", "InteractionType": "Interact",
"TargetTerritoryId": 138, "TargetTerritoryId": 138
"DialogueChoices": [
{
"Type": "List",
"ExcelSheet": "Warp",
"Prompt": null,
"Answer": 131109
}
]
}, },
{ {
"DataId": 14, "DataId": 14,

View File

@ -63,14 +63,6 @@
"AethernetShortcut": [ "AethernetShortcut": [
"[Gridania] Aetheryte Plaza", "[Gridania] Aetheryte Plaza",
"[Gridania] Lancers' Guild" "[Gridania] Lancers' Guild"
],
"DialogueChoices": [
{
"Type": "List",
"ExcelSheet": "Warp",
"Prompt": null,
"Answer": 131077
}
] ]
}, },
{ {

View File

@ -21,6 +21,21 @@ public class ExcelRef
Type = EType.RowId; Type = EType.RowId;
} }
/// <summary>
/// Only used internally (not serialized) with specific values that have been read from the sheets already.
/// </summary>
private ExcelRef(string value, bool v)
{
if (!v)
throw new ArgumentException(nameof(v));
_stringValue = value;
_rowIdValue = null;
Type = EType.RawString;
}
public static ExcelRef FromSheetValue(string value) => new(value, true);
public EType Type { get; } public EType Type { get; }
public string AsKey() public string AsKey()
@ -39,10 +54,19 @@ public class ExcelRef
return _rowIdValue!.Value; return _rowIdValue!.Value;
} }
public string AsRawString()
{
if (Type != EType.RawString)
throw new InvalidOperationException();
return _stringValue!;
}
public enum EType public enum EType
{ {
None, None,
Key, Key,
RowId, RowId,
RawString,
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Dalamud.Game.Addon.Lifecycle; using Dalamud.Game.Addon.Lifecycle;
@ -179,7 +180,8 @@ internal sealed class GameUiController : IDisposable
} }
} }
private unsafe bool CheckQuestSelection(AddonSelectIconString* addonSelectIconString, Quest quest, List<string?> answers) private unsafe bool CheckQuestSelection(AddonSelectIconString* addonSelectIconString, Quest quest,
List<string?> answers)
{ {
// it is possible for this to be a quest selection // it is possible for this to be a quest selection
string questName = quest.Info.Name; string questName = quest.Info.Name;
@ -205,7 +207,7 @@ internal sealed class GameUiController : IDisposable
private int? HandleListChoice(string? actualPrompt, List<string?> answers, bool checkAllSteps) private int? HandleListChoice(string? actualPrompt, List<string?> answers, bool checkAllSteps)
{ {
List<DialogueChoiceInfo> dialogueChoices = []; List<DialogueChoiceInfo> dialogueChoices = [];
var currentQuest = _questController.StartedQuest; var currentQuest = _questController.SimulatedQuest ?? _questController.StartedQuest;
if (currentQuest != null) if (currentQuest != null)
{ {
var quest = currentQuest.Quest; var quest = currentQuest.Quest;
@ -224,6 +226,29 @@ internal sealed class GameUiController : IDisposable
else else
dialogueChoices.AddRange(step.DialogueChoices.Select(x => new DialogueChoiceInfo(quest, x))); dialogueChoices.AddRange(step.DialogueChoices.Select(x => new DialogueChoiceInfo(quest, x)));
} }
// 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),
}));
}
}
}
} }
else else
_logger.LogDebug("Ignoring current quest dialogue choices, no active quest"); _logger.LogDebug("Ignoring current quest dialogue choices, no active quest");
@ -242,7 +267,8 @@ internal sealed class GameUiController : IDisposable
.ToList(); .ToList();
if (questChoices != null && questChoices.Count > 0) if (questChoices != null && questChoices.Count > 0)
{ {
_logger.LogInformation("Adding {Count} dialogue choices from not accepted quest {QuestName}", questChoices.Count, questInfo.Name); _logger.LogInformation("Adding {Count} dialogue choices from not accepted quest {QuestName}",
questChoices.Count, questInfo.Name);
dialogueChoices.AddRange(questChoices.Select(x => new DialogueChoiceInfo(knownQuest, x))); dialogueChoices.AddRange(questChoices.Select(x => new DialogueChoiceInfo(knownQuest, x)));
} }
} }
@ -334,9 +360,8 @@ internal sealed class GameUiController : IDisposable
_logger.LogTrace("Prompt: '{Prompt}'", actualPrompt); _logger.LogTrace("Prompt: '{Prompt}'", actualPrompt);
var currentQuest = _questController.StartedQuest; var currentQuest = _questController.StartedQuest;
if (currentQuest == null) if (currentQuest != null)
return; {
var quest = currentQuest.Quest; var quest = currentQuest.Quest;
if (checkAllSteps) if (checkAllSteps)
{ {
@ -352,7 +377,13 @@ internal sealed class GameUiController : IDisposable
return; return;
} }
HandleTravelYesNo(addonSelectYesno, currentQuest, actualPrompt); if (HandleTravelYesNo(addonSelectYesno, currentQuest, actualPrompt))
return;
}
var simulatedQuest = _questController.SimulatedQuest;
if (simulatedQuest != null)
HandleTravelYesNo(addonSelectYesno, simulatedQuest, actualPrompt);
} }
private unsafe bool HandleDefaultYesNo(AddonSelectYesno* addonSelectYesno, Quest quest, private unsafe bool HandleDefaultYesNo(AddonSelectYesno* addonSelectYesno, Quest quest,
@ -387,21 +418,35 @@ internal sealed class GameUiController : IDisposable
return false; return false;
} }
private unsafe void HandleTravelYesNo(AddonSelectYesno* addonSelectYesno, private unsafe bool HandleTravelYesNo(AddonSelectYesno* addonSelectYesno,
QuestController.QuestProgress currentQuest, string actualPrompt) QuestController.QuestProgress currentQuest, string actualPrompt)
{ {
if (_gameFunctions.ReturnRequestedAt >= DateTime.Now.AddSeconds(-2) && _returnRegex.IsMatch(actualPrompt)) if (_gameFunctions.ReturnRequestedAt >= DateTime.Now.AddSeconds(-2) && _returnRegex.IsMatch(actualPrompt))
{ {
_logger.LogInformation("Automatically confirming return..."); _logger.LogInformation("Automatically confirming return...");
addonSelectYesno->AtkUnitBase.FireCallbackInt(0); addonSelectYesno->AtkUnitBase.FireCallbackInt(0);
return; return true;
} }
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)
{
// this can be triggered either manually (in which case we should increase the step counter), or automatically // 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) // (in which case it is ~1 frame later, and the step counter has already been increased)
var sequence = currentQuest.Quest.FindSequence(currentQuest.Sequence); var sequence = currentQuest.Quest.FindSequence(currentQuest.Sequence);
if (sequence == null) if (sequence == null)
return; return null;
QuestStep? step = sequence.FindStep(currentQuest.Step); QuestStep? step = sequence.FindStep(currentQuest.Step);
if (step != null) if (step != null)
@ -421,24 +466,44 @@ internal sealed class GameUiController : IDisposable
if (step == null || step.TargetTerritoryId == null) if (step == null || step.TargetTerritoryId == null)
{ {
_logger.LogTrace("TravelYesNo: Not found"); _logger.LogTrace("TravelYesNo: Not found");
return; return null;
} }
_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)
{
var warps = _dataManager.GetExcelSheet<Warp>()! var warps = _dataManager.GetExcelSheet<Warp>()!
.Where(x => x.RowId > 0 && x.TerritoryType.Row == step.TargetTerritoryId); .Where(x => x.RowId > 0 && x.TerritoryType.Row == targetTerritoryId);
foreach (var entry in warps) foreach (var entry in warps)
{ {
string? excelPrompt = entry.Question?.ToString(); string? excelName = entry.Name?.ToString();
if (excelPrompt == null || !GameStringEquals(excelPrompt, actualPrompt)) string? excelQuestion = entry.Question?.ToString();
if (excelQuestion != null && GameStringEquals(excelQuestion, actualPrompt))
{ {
_logger.LogDebug("Ignoring prompt '{Prompt}'", excelPrompt); warpId = entry.RowId;
continue; warpText = excelQuestion;
return true;
}
else if (excelName != null && GameStringEquals(excelName, actualPrompt))
{
warpId = entry.RowId;
warpText = excelName;
return true;
}
else
{
_logger.LogDebug("Ignoring prompt '{Prompt}'", excelQuestion);
}
} }
_logger.LogInformation("Using warp {Id}, {Prompt}", entry.RowId, excelPrompt); warpId = null;
addonSelectYesno->AtkUnitBase.FireCallbackInt(0); warpText = null;
return; return false;
}
} }
private unsafe void PointMenuPostSetup(AddonEvent type, AddonArgs args) private unsafe void PointMenuPostSetup(AddonEvent type, AddonArgs args)
@ -551,6 +616,8 @@ internal sealed class GameUiController : IDisposable
return _gameFunctions.GetDialogueText(quest, excelSheet, excelRef.AsKey()); return _gameFunctions.GetDialogueText(quest, excelSheet, excelRef.AsKey());
else if (excelRef.Type == ExcelRef.EType.RowId) else if (excelRef.Type == ExcelRef.EType.RowId)
return _gameFunctions.GetDialogueTextByRowId(excelSheet, excelRef.AsRowId()); return _gameFunctions.GetDialogueTextByRowId(excelSheet, excelRef.AsRowId());
else if (excelRef.Type == ExcelRef.EType.RawString)
return excelRef.AsRawString();
return null; return null;
} }