diff --git a/DbOperations.cs b/DbOperations.cs index 3005630..fac262a 100644 --- a/DbOperations.cs +++ b/DbOperations.cs @@ -221,6 +221,43 @@ public bool FindErrorToastMessage(ToastMessage toastMessage) } } + public BattleTalkMessage FindAndReturnBattleTalkMessage(BattleTalkMessage battleTalkMessage) + { + using EchoglossianDbContext context = new EchoglossianDbContext(PluginInterface.GetPluginConfigDirectory() + Path.DirectorySeparatorChar); + + var pluginConfig = PluginInterface.GetPluginConfig() as Config; + + try + { + IQueryable existingBattleTalkMessage = + context.BattleTalkMessage.Where(t => + t.SenderName == battleTalkMessage.SenderName && + t.OriginalBattleTalkMessage == battleTalkMessage.OriginalBattleTalkMessage && + t.TranslationLang == battleTalkMessage.TranslationLang); + + if (pluginConfig.TranslateAlreadyTranslatedTexts) + { + existingBattleTalkMessage = existingBattleTalkMessage.Where(t => t.TranslationEngine == battleTalkMessage.TranslationEngine); + } + + BattleTalkMessage localFoundBattleTalkMessage = existingBattleTalkMessage.FirstOrDefault(); + if (existingBattleTalkMessage.FirstOrDefault() == null || + localFoundBattleTalkMessage?.OriginalBattleTalkMessage != battleTalkMessage.OriginalBattleTalkMessage) + { + return null; + } + + FoundBattleTalkMessage = localFoundBattleTalkMessage; + + return localFoundBattleTalkMessage; + } + catch (Exception e) + { + PluginLog.Debug($"FindAndReturnBattleTalkMessage exception {e}"); + return null; + } + } + public static bool FindBattleTalkMessage(BattleTalkMessage battleTalkMessage) { using EchoglossianDbContext context = new EchoglossianDbContext(PluginInterface.GetPluginConfigDirectory() + Path.DirectorySeparatorChar); diff --git a/Echoglossian.cs b/Echoglossian.cs index dd967be..9c04449 100644 --- a/Echoglossian.cs +++ b/Echoglossian.cs @@ -450,7 +450,8 @@ private void EgloAddonHandler() #endif AddonLifecycle.RegisterListener(AddonEvent.PreRefresh, "Talk", this.UiTalkAsyncHandler); AddonLifecycle.RegisterListener(AddonEvent.PreDraw, "Talk", this.UiTalkAsyncHandler); - + AddonLifecycle.RegisterListener(AddonEvent.PreDraw, "_BattleTalk", this.UiBattleTalkAsyncHandler); + AddonLifecycle.RegisterListener(AddonEvent.PreRefresh, "_BattleTalk", this.UiBattleTalkAsyncHandler); AddonLifecycle.RegisterListener(AddonEvent.PreSetup, "JournalResult", this.UiJournalResultHandler); AddonLifecycle.RegisterListener(AddonEvent.PostReceiveEvent, "RecommendList", this.UiRecommendListHandler); AddonLifecycle.RegisterListener(AddonEvent.PostRequestedUpdate, "RecommendList", this.UiRecommendListHandlerAsync); @@ -463,7 +464,7 @@ private void EgloAddonHandler() AddonLifecycle.RegisterListener(AddonEvent.PostRequestedUpdate, "_ToDoList", this.UiToDoListHandler); // this.EgloNeutralAddonHandler("Talk", new string[] { /* "PreUpdate", "PostUpdate",*/ "PreDraw",/* "PostDraw", "PreReceiveEvent", "PostReceiveEvent", "PreRequestedUpdate", "PostRequestedUpdate" ,*/ "PreRefresh",/* "PostRefresh"*/ }); - this.EgloNeutralAddonHandler("_BattleTalk", new string[] { /* "PreUpdate", "PostUpdate",*/ "PreDraw",/* "PostDraw", "PreReceiveEvent", "PostReceiveEvent", "PreRequestedUpdate", "PostRequestedUpdate" ,*/ "PreRefresh",/* "PostRefresh"*/}); + // this.EgloNeutralAddonHandler("_BattleTalk", new string[] { /* "PreUpdate", "PostUpdate",*/ "PreDraw",/* "PostDraw", "PreReceiveEvent", "PostReceiveEvent", "PreRequestedUpdate", "PostRequestedUpdate" ,*/ "PreRefresh",/* "PostRefresh"*/}); this.EgloNeutralAddonHandler("TalkSubtitle", new string[] {/* "PreUpdate", "PostUpdate",*/ "PreDraw",/* "PostDraw", "PreReceiveEvent", "PostReceiveEvent", "PreRequestedUpdate", "PostRequestedUpdate" ,*/ "PreRefresh",/* "PostRefresh"*/}); /*"PreSetup","PostSetup", "PreUpdate", "PostUpdate", "PreDraw", "PostDraw", "PreFinalize", "PreReceiveEvent", "PostReceiveEvent", "PreRequestedUpdate", "PostRequestedUpdate", "PreRefresh", "PostRefresh" */ diff --git a/UiBattleTalkAsyncHandler.cs b/UiBattleTalkAsyncHandler.cs new file mode 100644 index 0000000..d509799 --- /dev/null +++ b/UiBattleTalkAsyncHandler.cs @@ -0,0 +1,165 @@ +// +// Copyright (c) lokinmodar. All rights reserved. +// Licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License license. +// + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +using Dalamud.Game.Addon.Lifecycle; +using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; +using Dalamud.Memory; +using Dalamud.Utility; +using Echoglossian.EFCoreSqlite.Models; +using FFXIVClientStructs.FFXIV.Component.GUI; +using Humanizer; + +namespace Echoglossian +{ + public partial class Echoglossian + { + private readonly int delayBetweenTriesToTranslateBattleTalk = 50; + + private BattleTalkMessage lastBattleTalkMessage = null; + + private HashSet translatedBattleTalkTexts = new HashSet(); + + private unsafe void ManageBattleTalk() + { + Task.Run(() => + { + for (int i = 0; i < 5; i++) + { + var addon = GameGui.GetAddonByName("_BattleTalk"); + var battleTalkAddon = (AtkUnitBase*)addon; + if (battleTalkAddon == null || !battleTalkAddon->IsVisible) + { + Thread.Sleep(this.delayBetweenTriesToTranslateBattleTalk); + continue; + } + + var nameToTranslate = string.Empty; + + var nameNode = battleTalkAddon->GetTextNodeById(4); + if (nameNode != null && !nameNode->NodeText.IsEmpty) + { + nameToTranslate = MemoryHelper.ReadSeStringAsString(out _, (nint)nameNode->NodeText.StringPtr); + } + + var textNode = battleTalkAddon->GetTextNodeById(6); + if (textNode == null || textNode->NodeText.IsEmpty) + { + Thread.Sleep(this.delayBetweenTriesToTranslateBattleTalk); + continue; + } + + var textToTranslate = MemoryHelper.ReadSeStringAsString(out _, (nint)textNode->NodeText.StringPtr); + if (this.translatedBattleTalkTexts.Contains(textToTranslate)) + { + Thread.Sleep(this.delayBetweenTriesToTranslateBattleTalk); + continue; + } + + PluginLog.Debug($"ManageBattleTalk text to translate {nameToTranslate}: {textToTranslate}"); + + this.lastBattleTalkMessage = new BattleTalkMessage( + senderName: nameToTranslate, + originalBattleTalkMessage: textToTranslate, + originalSenderNameLang: ClientState.ClientLanguage.Humanize(), + translatedBattleTalkMessage: string.Empty, + originalBattleTalkMessageLang: ClientState.ClientLanguage.Humanize(), + translationLang: langDict[languageInt].Code, + translationEngine: this.configuration.ChosenTransEngine, + translatedSenderName: string.Empty, + createdDate: DateTime.Now, + updatedDate: DateTime.Now); + + var foundBattleTalk = this.FindAndReturnBattleTalkMessage(this.lastBattleTalkMessage); + if (foundBattleTalk == null) + { + string textTranslation = this.Translate(textToTranslate); + string nameTranslation = nameToTranslate.IsNullOrEmpty() ? string.Empty : this.Translate(nameToTranslate); + this.lastBattleTalkMessage.TranslatedSenderName = nameTranslation; + this.lastBattleTalkMessage.TranslatedBattleTalkMessage = textTranslation; + translatedBattleTalkTexts.Add(textTranslation); + InsertBattleTalkData(this.lastBattleTalkMessage); + + return; + } + + this.lastBattleTalkMessage.TranslatedSenderName = foundBattleTalk.TranslatedSenderName; + this.lastBattleTalkMessage.TranslatedBattleTalkMessage = foundBattleTalk.TranslatedBattleTalkMessage; + this.translatedBattleTalkTexts.Add(foundBattleTalk.TranslatedBattleTalkMessage); + + return; + } + }); + } + + private unsafe void TranslateBattleTalkReplacing() + { + PluginLog.Debug($"TranslateBattleTalkReplacing"); + + if (this.lastBattleTalkMessage == null) + { + return; + } + + try + { + var addon = GameGui.GetAddonByName("_BattleTalk"); + var battleTalkAddon = (AtkUnitBase*)addon; + if (battleTalkAddon == null || !battleTalkAddon->IsVisible) + { + return; + } + + var nameNode = battleTalkAddon->GetTextNodeById(4); + var textNode = battleTalkAddon->GetTextNodeById(6); + if (textNode == null || textNode->NodeText.IsEmpty) + { + return; + } + + if (this.configuration.TranslateNpcNames && nameNode != null && !nameNode->NodeText.IsEmpty) + { + nameNode->SetText(this.lastBattleTalkMessage.TranslatedSenderName); + } + + textNode->SetText(this.lastBattleTalkMessage.TranslatedBattleTalkMessage); + } + catch (Exception e) + { + PluginLog.Warning("TranslateBattleTalkReplacing Exception: " + e); + } + } + + private unsafe void UiBattleTalkAsyncHandler(AddonEvent type, AddonArgs args) + { + PluginLog.Debug($"UiBattleTalkAsyncHandler: {type} {args.AddonName}"); + + if (!this.configuration.TranslateBattleTalk) + { + return; + } + + if (args is not AddonRefreshArgs) + { + this.TranslateBattleTalkReplacing(); + return; + } + + try + { + this.lastBattleTalkMessage = null; + this.ManageBattleTalk(); + } + catch (Exception e) + { + PluginLog.Warning("UiTalkAsyncHandler Exception: " + e); + } + } + } +} \ No newline at end of file