using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using Dalamud.Game.Addon.Lifecycle; using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Component.GUI; using LLib.GameUI; using Microsoft.Extensions.Logging; using Questionable.Model.Questing; namespace Questionable.Controller.GameUi; internal sealed class ChocoboNameHandler : IDisposable { private static readonly HashSet ChocoboQuestIds = new HashSet { new QuestId(700), new QuestId(701), new QuestId(702) }; private readonly QuestController _questController; private readonly Configuration _configuration; private readonly IAddonLifecycle _addonLifecycle; private readonly ILogger _logger; private bool _awaitingConfirmation; public ChocoboNameHandler(QuestController questController, Configuration configuration, IAddonLifecycle addonLifecycle, ILogger logger) { _questController = questController; _configuration = configuration; _addonLifecycle = addonLifecycle; _logger = logger; _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "InputString", OnInputStringSetup); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectYesno", OnSelectYesNoSetup); } public void Dispose() { _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectYesno", OnSelectYesNoSetup); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "InputString", OnInputStringSetup); } private bool IsChocoboQuestActive() { QuestController.QuestProgress currentQuest = _questController.CurrentQuest; if (currentQuest != null) { return ChocoboQuestIds.Contains(currentQuest.Quest.Id); } return false; } private unsafe void OnInputStringSetup(AddonEvent type, AddonArgs args) { if (_questController.IsRunning && IsChocoboQuestActive()) { AtkUnitBase* address = (AtkUnitBase*)args.Addon.Address; if (LAddon.IsAddonReady(address)) { string validatedChocoboName = GetValidatedChocoboName(_configuration.Advanced.ChocoboName); _logger.LogInformation("Entering chocobo name: {Name}", validatedChocoboName); FireInputStringCallback(address, validatedChocoboName); _awaitingConfirmation = true; } } } private unsafe void OnSelectYesNoSetup(AddonEvent type, AddonArgs args) { if (_questController.IsRunning && _awaitingConfirmation) { AtkUnitBase* address = (AtkUnitBase*)args.Addon.Address; if (LAddon.IsAddonReady(address)) { _logger.LogInformation("Confirming chocobo name"); ClickYes(address); _awaitingConfirmation = false; } } } private static string GetValidatedChocoboName(string? configuredName) { if (string.IsNullOrWhiteSpace(configuredName)) { return "Kweh"; } string text = configuredName.Trim(); if (text.Length < 2 || text.Length > 20) { return "Kweh"; } string text2 = text.Substring(0, 1).ToUpperInvariant(); string text3; if (text.Length <= 1) { text3 = string.Empty; } else { string text4 = text; text3 = text4.Substring(1, text4.Length - 1).ToLowerInvariant(); } return text2 + text3; } private unsafe void FireInputStringCallback(AtkUnitBase* addon, string text) { AtkComponentNode* componentNodeById = addon->GetComponentNodeById(9u); if (componentNodeById == null) { _logger.LogWarning("Could not find text input node in InputString addon"); return; } AtkComponentTextInput* asAtkComponentTextInput = componentNodeById->GetAsAtkComponentTextInput(); if (asAtkComponentTextInput == null) { _logger.LogWarning("Node is not a text input component"); return; } asAtkComponentTextInput->SetText(text); AtkValue* ptr = stackalloc AtkValue[2]; ptr->Type = FFXIVClientStructs.FFXIV.Component.GUI.ValueType.Int; ptr->Int = 0; byte[] bytes = Encoding.UTF8.GetBytes(text); nint num = Marshal.AllocHGlobal(bytes.Length + 1); Marshal.Copy(bytes, 0, num, bytes.Length); Marshal.WriteByte(num + bytes.Length, 0); ptr[1].Type = FFXIVClientStructs.FFXIV.Component.GUI.ValueType.String; ptr[1].String = (byte*)num; addon->FireCallback(2u, ptr); Marshal.FreeHGlobal(num); } private unsafe static void ClickYes(AtkUnitBase* addon) { AtkValue* ptr = stackalloc AtkValue[1]; ptr->Type = FFXIVClientStructs.FFXIV.Component.GUI.ValueType.Int; ptr->Int = 0; addon->FireCallback(1u, ptr); } }