From 44909e1df48e9a8a1a8ac09c7cb2d5ff535e1779 Mon Sep 17 00:00:00 2001 From: SillyStar-Github <90102598+SillyStar-Github@users.noreply.github.com> Date: Tue, 2 Jun 2026 13:09:42 -0400 Subject: [PATCH 1/2] just pushing these as the final 3.6.1 changes --- .../Patches/Modes/Odyssey/TravelStore_Patch.cs | 5 +++++ .../EventNodes/RuntimeEventNodeEditor_Patch.cs | 13 +++++++++++-- .../GameLevel/EventNodes/RuntimeNodeUI_Patch.cs | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/MelonLoader/Patches/Modes/Odyssey/TravelStore_Patch.cs b/MelonLoader/Patches/Modes/Odyssey/TravelStore_Patch.cs index 3bcb5ef..dc07b75 100644 --- a/MelonLoader/Patches/Modes/Odyssey/TravelStore_Patch.cs +++ b/MelonLoader/Patches/Modes/Odyssey/TravelStore_Patch.cs @@ -69,6 +69,11 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey { string res = investText; + if(!Utils.CheckForUntranslatedText(res)) + { + return res; + } + string[] investStats = investText.Split("\n\n"); List translatedInvestStats = new List(); foreach(string investStat in investStats) diff --git a/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeEventNodeEditor_Patch.cs b/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeEventNodeEditor_Patch.cs index 217e2c7..97278d9 100644 --- a/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeEventNodeEditor_Patch.cs +++ b/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeEventNodeEditor_Patch.cs @@ -45,7 +45,7 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor.GameLevel.EventNodes { RuntimeNodeUI nodeUI = node.value; - if(nodeUI.Node.nodeType == "PlantTypeValueNode") + if(nodeUI.Node.nodeType == "PlantTypeValueNode" || nodeUI.Node.nodeType == "SinglePlantTypeListNode") { Transform buttonTransform = nodeUI.transform.Find("Header/ValueEditContainer/Button(Clone)/Label"); TextMeshProUGUI txt = buttonTransform.GetComponent(); @@ -53,7 +53,16 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor.GameLevel.EventNodes if (!txt.gameObject.name.Contains("Label")) continue; if (!Utils.CheckForUntranslatedText(txt.text)) continue; - string plantName = Utils.GetPlantNameFromAlmanac(nodeUI.Node.GetPortValue(nodeUI.Node.GetOutputPorts()[0].portName, PlantType.Nothing)); + string plantName = ""; + if(nodeUI.Node.nodeType == "PlantTypeValueNode") + { + plantName = Utils.GetPlantNameFromAlmanac(nodeUI.Node.GetPortValue(nodeUI.Node.GetOutputPorts()[0].portName, PlantType.Nothing)); + } + else if(nodeUI.Node.nodeType == "SinglePlantTypeListNode") + { + SinglePlantTypeListNode tempNode = nodeUI.Node.TryCast(); + plantName = Utils.GetPlantNameFromAlmanac(tempNode.plantType); + } txt.text = (plantName != "") ? plantName : StringStore.TranslateText(txt.text); txt.font = fontAsset; } diff --git a/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeNodeUI_Patch.cs b/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeNodeUI_Patch.cs index bcf303d..fc31045 100644 --- a/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeNodeUI_Patch.cs +++ b/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeNodeUI_Patch.cs @@ -62,7 +62,7 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor.GameLevel.EventNodes TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); - if(__instance.Node.nodeType == "PlantTypeValueNode") + if(__instance.Node.nodeType == "PlantTypeValueNode" || __instance.Node.nodeType == "SinglePlantTypeListNode") { foreach(TextMeshProUGUI txt in __instance.GetComponentsInChildren()) { From 25cfb74d38e4da6505d1a0898c8e41b8e05db265 Mon Sep 17 00:00:00 2001 From: SillyStar-Github <90102598+SillyStar-Github@users.noreply.github.com> Date: Mon, 6 Jul 2026 05:16:02 -0400 Subject: [PATCH 2/2] =?UTF-8?q?3.7=20code=20i=20forgot=20to=20push=20?= =?UTF-8?q?=F0=9F=98=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.idea/.gitignore | 15 ++ .idea/.idea.PvZ Fusion Translator/.idea/.name | 1 + .../.idea/indexLayout.xml | 8 + .../.idea/misc.xml | 14 ++ .../.idea.PvZ Fusion Translator/.idea/vcs.xml | 6 + MelonLoader/AssetStore/FontStore.cs | 91 +++++---- MelonLoader/AssetStore/StringStore.cs | 95 +++++++--- MelonLoader/Core.cs | 14 +- MelonLoader/FileLoader.cs | 24 ++- .../BaseTextObjects/InGameText_Patch.cs | 50 +++-- .../BaseTextObjects/TextMeshProUGUI_Patch.cs | 4 + .../GameObjects/AlmanacBuffMenu_Patch.cs | 91 +++++++++ .../GameObjects/AlmanacCardUI_Patch.cs | 10 +- .../GameObjects/AlmanacPlantMenu_Patch.cs | 55 +++++- .../GameObjects/AlmanacPlantWindow_Patch.cs | 50 ++--- .../Patches/GameObjects/Almanac_Patch.cs | 4 +- .../Patches/GameObjects/BaseMenu_Patch.cs | 5 + .../ButtonObjects/LevelSaveEnter_Patch.cs | 24 +++ .../GameObjects/LevelResultMenu_Patch.cs | 3 +- .../Patches/GameObjects/MainMenu_Patch.cs | 5 +- .../MinorObjects/GameLose_Patch.cs | 1 + .../MinorObjects/HealthSlider_Patch.cs | 45 +++++ .../MinorObjects/LanternUmbrella_Patch.cs | 4 +- .../MinorObjects/PumpinerChestMenu_Patch.cs | 26 +++ .../MinorObjects/SpecialCard_Patch.cs | 32 ++++ .../MinorObjects/TrainingDummy_Patch.cs | 2 +- .../MinorObjects/UltimateChomper_Patch.cs | 4 +- .../GameObjects/MinorObjects/Zombie_Patch.cs | 6 +- .../GameObjects/PlantDamageMenu_Patch.cs | 13 +- .../GameObjects/PlantDataMenu_Patch.cs | 6 +- .../GameObjects/PlayerSaveMenu_Patch.cs | 6 +- .../Patches/GameObjects/ReNameMenu_Patch.cs | 30 +++ .../Patches/GameObjects/ShootingMenu_Patch.cs | 4 +- .../GameObjects/ShootingSelectWindow_Patch.cs | 4 +- .../Patches/Managers/SaveMenu_Patch.cs | 21 ++- .../Modes/Odyssey/MultipleChoiceMenu_Patch.cs | 53 ++++-- .../Odyssey/OppositeBuffManager_Patch.cs | 8 +- .../Modes/Odyssey/ShootingManager_Patch.cs | 19 ++ .../Modes/Odyssey/SynergyDisplay_Patch.cs | 3 +- .../Odyssey/TravelAdvantureMenu_Patch.cs | 9 +- .../Patches/Modes/Odyssey/TravelMgr_Patch.cs | 110 +++++++---- .../CustomMenuPage_event_Patch_B.cs | 29 +++ .../RuntimeEventNodeEditor_Patch.cs | 103 +++------- .../EventNodes/RuntimeNodeUI_Patch.cs | 169 ++++++++++------- ...PvZ Fusion Translator [MelonLoader].csproj | 20 +- MelonLoader/Resources/ModUpdateUtil.exe | Bin 129464 -> 0 bytes MelonLoader/Utils.cs | 177 ++++++++++++++---- PvZ Fusion Translator.sln | 4 +- 48 files changed, 1053 insertions(+), 424 deletions(-) create mode 100644 .idea/.idea.PvZ Fusion Translator/.idea/.gitignore create mode 100644 .idea/.idea.PvZ Fusion Translator/.idea/.name create mode 100644 .idea/.idea.PvZ Fusion Translator/.idea/indexLayout.xml create mode 100644 .idea/.idea.PvZ Fusion Translator/.idea/misc.xml create mode 100644 .idea/.idea.PvZ Fusion Translator/.idea/vcs.xml create mode 100644 MelonLoader/Patches/GameObjects/AlmanacBuffMenu_Patch.cs create mode 100644 MelonLoader/Patches/GameObjects/ButtonObjects/LevelSaveEnter_Patch.cs create mode 100644 MelonLoader/Patches/GameObjects/MinorObjects/HealthSlider_Patch.cs create mode 100644 MelonLoader/Patches/GameObjects/MinorObjects/PumpinerChestMenu_Patch.cs create mode 100644 MelonLoader/Patches/GameObjects/MinorObjects/SpecialCard_Patch.cs create mode 100644 MelonLoader/Patches/GameObjects/ReNameMenu_Patch.cs create mode 100644 MelonLoader/Patches/Modes/Odyssey/ShootingManager_Patch.cs create mode 100644 MelonLoader/Patches/Modes/Super Editor/CustomMenuPage_event_Patch_B.cs delete mode 100644 MelonLoader/Resources/ModUpdateUtil.exe diff --git a/.idea/.idea.PvZ Fusion Translator/.idea/.gitignore b/.idea/.idea.PvZ Fusion Translator/.idea/.gitignore new file mode 100644 index 0000000..115ad6a --- /dev/null +++ b/.idea/.idea.PvZ Fusion Translator/.idea/.gitignore @@ -0,0 +1,15 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/.idea.PvZ Fusion Translator.iml +/contentModel.xml +/projectSettingsUpdater.xml +/modules.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.PvZ Fusion Translator/.idea/.name b/.idea/.idea.PvZ Fusion Translator/.idea/.name new file mode 100644 index 0000000..82e789d --- /dev/null +++ b/.idea/.idea.PvZ Fusion Translator/.idea/.name @@ -0,0 +1 @@ +PvZ Fusion Translator \ No newline at end of file diff --git a/.idea/.idea.PvZ Fusion Translator/.idea/indexLayout.xml b/.idea/.idea.PvZ Fusion Translator/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.PvZ Fusion Translator/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.PvZ Fusion Translator/.idea/misc.xml b/.idea/.idea.PvZ Fusion Translator/.idea/misc.xml new file mode 100644 index 0000000..104c42f --- /dev/null +++ b/.idea/.idea.PvZ Fusion Translator/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.PvZ Fusion Translator/.idea/vcs.xml b/.idea/.idea.PvZ Fusion Translator/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/.idea.PvZ Fusion Translator/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/MelonLoader/AssetStore/FontStore.cs b/MelonLoader/AssetStore/FontStore.cs index 75d5b03..e497378 100644 --- a/MelonLoader/AssetStore/FontStore.cs +++ b/MelonLoader/AssetStore/FontStore.cs @@ -1,4 +1,8 @@ using Il2CppTMPro; +using MelonLoader; +using System.Reflection; +using UnityEngine; +using UnityEngine.TextCore.Text; namespace PvZ_Fusion_Translator.AssetStore { @@ -11,28 +15,24 @@ namespace PvZ_Fusion_Translator.AssetStore { string fontsDir = Path.Combine(Core.Instance.modsDirectory, "[Custom Fonts]"); + AssetBundle assetBundle = Utils.GetAssetBundle(Path.Combine(fontsDir, "translation")); + #if MULTI_LANGUAGE - foreach (string file in Directory.GetFiles(fontsDir)) + foreach (string file in assetBundle.GetAllAssetNames()) { string fileName = Path.GetFileNameWithoutExtension(file); - string fileExtension = Path.GetExtension(file); - if (fileExtension == ".ttf" || fileExtension == ".otf") + if (!fileName.EndsWith("_almanac") && !fileName.EndsWith("_fallback")) { - if (!fileName.EndsWith("_Almanac") && !fileName.EndsWith("_Fallback")) - { - string fontPath = Path.Combine("PvZ_Fusion_Translator", "[Custom Fonts]", fileName + fileExtension); + TMP_FontAsset fontAsset = assetBundle.LoadAsset(file); - TMP_FontAsset fontAsset = FontHandler.LoadTMPFont(fontPath, true); - fontAsset.name = fileName; - fontAssetDict.Add(fileName, fontAsset); + fontAssetDict.Add(fileName, fontAsset); - Log.LogInfo($"Font for language '{fileName}' loaded"); - } + Log.LogInfo($"Font for language '{fileName}' loaded"); } } - InitFallback(); + InitFallback(assetBundle); #else string defaultFontDir = Path.Combine("PvZ_Fusion_Translator", "[Custom Fonts]", "English.ttf"); @@ -41,43 +41,38 @@ namespace PvZ_Fusion_Translator.AssetStore #endif } - internal static void InitFallback() + internal static void InitFallback(AssetBundle assetBundle) { string fontsDir = Path.Combine(Core.Instance.modsDirectory, "[Custom Fonts]"); - foreach (string file in Directory.GetFiles(fontsDir)) - { + + foreach (string file in assetBundle.GetAllAssetNames()) + { string fileName = Path.GetFileNameWithoutExtension(file); - string fileExtension = Path.GetExtension(file); - if (fileExtension == ".ttf" || fileExtension == ".otf") + if (fileName.EndsWith("_almanac") || fileName.EndsWith("_fallback")) { - if (fileName.EndsWith("_Almanac") || fileName.EndsWith("_Fallback")) + TMP_FontAsset fallbackFont = assetBundle.LoadAsset(file); + + string fileNameLanguage = fileName.Replace("_fallback", "").Replace("_almanac", ""); + + if (fontAssetDictSecondary.ContainsKey(fileNameLanguage)) { - string fallbackPath = Path.Combine("PvZ_Fusion_Translator", "[Custom Fonts]", fileName + fileExtension); - TMP_FontAsset fallbackFont = FontHandler.LoadTMPFont(fallbackPath, true); - fallbackFont.name = fileName; - - string fileNameLanguage = fileName.Replace("_Fallback", "").Replace("_Almanac", ""); - - if (fontAssetDictSecondary.ContainsKey(fileNameLanguage)) + if (fileName.EndsWith("_Fallback")) { - if (fileName.EndsWith("_Fallback")) - { - fontAssetDictSecondary.Add(fileNameLanguage + "_Almanac", fontAssetDictSecondary[fileNameLanguage]); - fontAssetDictSecondary.Remove(fileNameLanguage); - } - else - { - fontAssetDictSecondary.Add(fileNameLanguage + "_Fallback", fontAssetDictSecondary[fileNameLanguage]); - fontAssetDictSecondary.Remove(fileNameLanguage); - } - fontAssetDictSecondary.Add(fileName, fallbackFont); + fontAssetDictSecondary.Add(fileNameLanguage + "_almanac", fontAssetDictSecondary[fileNameLanguage]); + fontAssetDictSecondary.Remove(fileNameLanguage); } else - fontAssetDictSecondary.Add(fileNameLanguage, fallbackFont); - - // Log.LogInfo($"Fallback font for language '{fileNameLanguage}' loaded"); + { + fontAssetDictSecondary.Add(fileNameLanguage + "_fallback", fontAssetDictSecondary[fileNameLanguage]); + fontAssetDictSecondary.Remove(fileNameLanguage); + } + fontAssetDictSecondary.Add(fileName, fallbackFont); } + else + fontAssetDictSecondary.Add(fileNameLanguage, fallbackFont); + + // Log.LogInfo($"Fallback font for language '{fileNameLanguage}' loaded"); } } AddFallback(); @@ -98,15 +93,15 @@ namespace PvZ_Fusion_Translator.AssetStore Log.LogInfo("Fallback font for language '" + lang + "' added"); continue; } - if (fontAssetDictSecondary.ContainsKey(lang + "_Almanac")) + if (fontAssetDictSecondary.ContainsKey(lang + "_almanac")) { - fontAssetDict[lang].fallbackFontAssetTable.Add(fontAssetDictSecondary[lang + "_Almanac"]); + fontAssetDict[lang].fallbackFontAssetTable.Add(fontAssetDictSecondary[lang + "_almanac"]); Log.LogInfo("Fallback font for language '" + lang + "' added"); continue; } - if (fontAssetDictSecondary.ContainsKey(lang + "_Fallback")) + if (fontAssetDictSecondary.ContainsKey(lang + "_fallback")) { - fontAssetDict[lang].fallbackFontAssetTable.Add(fontAssetDictSecondary[lang + "_Fallback"]); + fontAssetDict[lang].fallbackFontAssetTable.Add(fontAssetDictSecondary[lang + "_fallback"]); Log.LogInfo("Fallback font for language '" + lang + "' added"); continue; } @@ -116,7 +111,7 @@ namespace PvZ_Fusion_Translator.AssetStore #if MULTI_LANGUAGE public static TMP_FontAsset LoadTMPFont(string language) { - if (fontAssetDict.TryGetValue(language, out TMP_FontAsset font)) + if (fontAssetDict.TryGetValue(language.ToLower(), out TMP_FontAsset font)) { TMP_FontAsset fontAsset = font; if (fontAsset.fallbackFontAssetTable != null) @@ -125,7 +120,7 @@ namespace PvZ_Fusion_Translator.AssetStore } return fontAsset; } - return fontAssetDict.GetValueOrDefault("English"); + return fontAssetDict.GetValueOrDefault("english"); } #else public static TMP_FontAsset LoadTMPFont() @@ -144,15 +139,15 @@ namespace PvZ_Fusion_Translator.AssetStore return almanacAsset; } } - if (fontAssetDictSecondary.ContainsKey(language + "_Almanac")) + if (fontAssetDictSecondary.ContainsKey(language + "_almanac")) { - if (fontAssetDictSecondary.TryGetValue(language + "_Almanac", out TMP_FontAsset almanacAsset)) + if (fontAssetDictSecondary.TryGetValue(language + "_almanac", out TMP_FontAsset almanacAsset)) { return almanacAsset; } } - return fontAssetDict.GetValueOrDefault("English"); + return fontAssetDict.GetValueOrDefault("english"); } #if MULTI_LANGUAGE diff --git a/MelonLoader/AssetStore/StringStore.cs b/MelonLoader/AssetStore/StringStore.cs index 9c6f39c..b7c2995 100644 --- a/MelonLoader/AssetStore/StringStore.cs +++ b/MelonLoader/AssetStore/StringStore.cs @@ -1,6 +1,5 @@ -using Il2CppSystem.Runtime.Remoting.Messaging; -using Il2CppTMPro; -using System.Diagnostics; +using Il2CppTMPro; +using PvZ_Fusion_Translator.Patches.BaseTextObjects; using System.Text.RegularExpressions; using UnityEngine; @@ -90,17 +89,12 @@ namespace PvZ_Fusion_Translator.AssetStore izTipCollectionString.Clear(); fsLevelTipDictionary.Clear(); fsTipCollectionString.Clear(); + InGameText_Patch.currentText = ""; FileLoader.LoadStrings(); } public static TextMeshPro TranslateText(TextMeshPro originalTMP, bool isLog = false) { - /*var frame = new StackTrace().GetFrame(1); - var method = frame.GetMethod(); - var className = method.DeclaringType.Name; - - Console.WriteLine($"TextMeshPro: {className}.{method.Name}");*/ - #if MULTI_LANGUAGE string currentLanguage = Utils.Language.ToString(); TMP_FontAsset fontAsset = FontStore.LoadTMPFont(currentLanguage); @@ -139,12 +133,6 @@ namespace PvZ_Fusion_Translator.AssetStore public static string TranslateText(string originalText, bool isLog = false) { - /*var frame = new StackTrace().GetFrame(1); - var method = frame.GetMethod(); - var className = method.DeclaringType.Name; - - Console.WriteLine($"string: {className}.{method.Name}");*/ - string text = DoTranslateText(originalText, false); string checkText; #if DEBUG @@ -183,9 +171,7 @@ namespace PvZ_Fusion_Translator.AssetStore for (int i = 1; i < groupCount; i++) { string groupValue = match.Groups[i].Value; - string translatedValue = translationString.ContainsKey(groupValue) - ? translationString[groupValue] - : groupValue; + string translatedValue = DoTranslateString(groupValue); dynamicParts.Add(translatedValue); } @@ -232,7 +218,7 @@ namespace PvZ_Fusion_Translator.AssetStore } } - public static string DoTranslateText(string originalText, bool isLog = false) + public static string DoTranslateString(string originalText, bool isLog = false) { if (string.IsNullOrEmpty(originalText)) { @@ -275,6 +261,73 @@ namespace PvZ_Fusion_Translator.AssetStore } return customLevelString[originalText]; } + + if (Utils.plantIndiceString.ContainsKey(originalText)) + { + if(isLog) + { + Log.LogDebug($"Text '{originalText}' found in plantIndiceString"); + } + return Utils.plantIndiceString[originalText]; + } + + return originalText; + } + + public static string DoTranslateText(string originalText, bool isLog = false) + { + if (string.IsNullOrEmpty(originalText)) + { + if (isLog) + Log.LogError("Text Null or Empty"); + + return string.Empty; + } + + if (!Utils.CheckForUntranslatedText(originalText) && !(originalText).Contains("_")) return originalText; + + if (translationString.ContainsKey(originalText)) + { + if (isLog) + Log.LogDebug($"Text '{originalText} found in translationString"); + return translationString[originalText]; + } + + if(izTipCollectionString.ContainsKey(originalText)) + { + if(isLog) + { + Log.LogDebug($"Text '{originalText}' found in izTipCollectionString"); + } + return izTipCollectionString[originalText]; + } + + if(fsTipCollectionString.ContainsKey(originalText)) + { + if(isLog) + { + Log.LogDebug($"Text '{originalText}' found in fsTipCollectionString"); + } + return fsTipCollectionString[originalText]; + } + + if(customLevelString.ContainsKey(originalText)) + { + if(isLog) + { + Log.LogDebug($"Text '{originalText}' found in customLevelString"); + } + return customLevelString[originalText]; + } + + if (Utils.plantIndiceString.ContainsKey(originalText)) + { + if(isLog) + { + Log.LogDebug($"Text '{originalText}' found in plantIndiceString"); + } + return Utils.plantIndiceString[originalText]; + } // Regex-based dynamic translation foreach (var entry in translationStringRegex) @@ -296,9 +349,7 @@ namespace PvZ_Fusion_Translator.AssetStore for (int i = 1; i < groupCount; i++) { string groupValue = match.Groups[i].Value; - string translatedValue = translationString.ContainsKey(groupValue) - ? translationString[groupValue] - : groupValue; + string translatedValue = DoTranslateString(groupValue); dynamicParts.Add(translatedValue); } diff --git a/MelonLoader/Core.cs b/MelonLoader/Core.cs index 0cd8137..a93977c 100644 --- a/MelonLoader/Core.cs +++ b/MelonLoader/Core.cs @@ -1,13 +1,8 @@ -using Il2Cpp; -using Il2CppTMPro; -using MelonLoader; +using MelonLoader; using MelonLoader.Utils; using PvZ_Fusion_Translator.AssetStore; using PvZ_Fusion_Translator.Patches.Managers; -using PvZ_Fusion_Translator.Patches.Modes.Odyssey; using PvZ_Fusion_Translator.Patches.Modes.Super_Editor; -using System.Runtime.CompilerServices; -using System.Text.Json; using UnityEngine; using static PvZ_Fusion_Translator.FileLoader; @@ -21,6 +16,7 @@ namespace PvZ_Fusion_Translator private static DateTime dtStart; private static DateTime? dtStartToast; private static string toast_txt; + public static bool isInitialized = false; public static Core Instance { get; private set; } object replaceTextureRoutine = null; @@ -32,8 +28,6 @@ namespace PvZ_Fusion_Translator base.OnInitializeMelon(); Instance = this; - //DllStore.Init(MelonLoader.InternalUtils.UnityInformationHandler.GameVersion); - Config(); #if MULTI_LANGUAGE FileLoader.LoadLanguage(); @@ -42,9 +36,8 @@ namespace PvZ_Fusion_Translator StringStore.Init(); AudioStore.Init(); FontStore.Init(); - Utils.RegisterPlantIndices(); - //Utils.RegisterRecipeLinks(); + isInitialized = true; } public override void OnLateInitializeMelon() @@ -52,6 +45,7 @@ namespace PvZ_Fusion_Translator dtStart = DateTime.Now; replaceTextureRoutine = MelonCoroutines.Start(TextureStore.ReplaceTexturesCoroutine()); LoadTravelBuffs(); + Utils.RegisterPlantIndices(); } public override void OnDeinitializeMelon() diff --git a/MelonLoader/FileLoader.cs b/MelonLoader/FileLoader.cs index d838940..042d28b 100644 --- a/MelonLoader/FileLoader.cs +++ b/MelonLoader/FileLoader.cs @@ -159,14 +159,14 @@ namespace PvZ_Fusion_Translator if(plantAlmanacContent != null) { - AlmanacPlantMenu_Patch.almanacJson = plantAlmanacContent; + AlmanacPlantMenu_Patch.LoadPlantAlmanacData(plantAlmanacContent); } else { string path = Path.Combine(almanacDir, "LawnStringsTranslate.json"); if(File.Exists(path)) { - AlmanacPlantMenu_Patch.almanacJson = File.ReadAllText(path); + AlmanacPlantMenu_Patch.LoadPlantAlmanacData(File.ReadAllText(path)); } } @@ -199,6 +199,10 @@ namespace PvZ_Fusion_Translator } } + // load travel_buffs + + LoadTravelBuffs(); + // load abyss buffs Patches.Modes.Abyss.AbyssBuffMenu_Patch.LoadAbyssBuffData(); @@ -259,6 +263,10 @@ namespace PvZ_Fusion_Translator { Patches.Modes.Abyss.AbyssBuffMenu_Patch.LoadAbyssBuffData(); } + else if (fileName.EndsWith("travel_buffs")) + { + LoadTravelBuffs(); + } } string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language); @@ -272,7 +280,7 @@ namespace PvZ_Fusion_Translator if(File.Exists(plantAlmanacPath)) { - AlmanacPlantMenu_Patch.almanacJson = File.ReadAllText(plantAlmanacPath); + AlmanacPlantMenu_Patch.LoadPlantAlmanacData(File.ReadAllText(plantAlmanacPath)); } string zombieAlmanacPath = Path.Combine(almanacDir, "ZombieStringsTranslate.json"); @@ -429,6 +437,10 @@ namespace PvZ_Fusion_Translator changelogText = changelogContent; } } + else + { + changelogText = File.ReadAllText(changelogDir); + } NoticePauseMenu_Patch.changelogText = changelogText; } @@ -441,14 +453,14 @@ namespace PvZ_Fusion_Translator if (travelBuffsContent != null) { - TravelMgr_Patch.translatedTravelBuffs = TravelMgr_Patch.GenerateTranslatedTravelBuffs(travelBuffsContent); + TravelMgr_Patch.GenerateTranslatedTravelBuffs(travelBuffsContent); } else { string travelBuffsPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "travel_buffs.json"); if (File.Exists(travelBuffsPath)) { - TravelMgr_Patch.translatedTravelBuffs = TravelMgr_Patch.GenerateTranslatedTravelBuffs(File.ReadAllText(travelBuffsPath)); + TravelMgr_Patch.GenerateTranslatedTravelBuffs(File.ReadAllText(travelBuffsPath)); } } } @@ -462,7 +474,7 @@ namespace PvZ_Fusion_Translator string jsonString = File.ReadAllText(filepath); if (fileName.EndsWith("travel_buffs")) { - TravelMgr_Patch.translatedTravelBuffs = TravelMgr_Patch.GenerateTranslatedTravelBuffs(jsonString); + TravelMgr_Patch.GenerateTranslatedTravelBuffs(jsonString); } } } diff --git a/MelonLoader/Patches/BaseTextObjects/InGameText_Patch.cs b/MelonLoader/Patches/BaseTextObjects/InGameText_Patch.cs index 3b77165..a3c6c4c 100644 --- a/MelonLoader/Patches/BaseTextObjects/InGameText_Patch.cs +++ b/MelonLoader/Patches/BaseTextObjects/InGameText_Patch.cs @@ -1,5 +1,6 @@ using HarmonyLib; using Il2Cpp; +using Il2CppCore; using Il2CppAlmanacData; using Il2CppTMPro; using PvZ_Fusion_Translator.AssetStore; @@ -73,7 +74,8 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects int godsGachaCheck = CheckGodsGachaPopup(originalText); string godsGachaMatch = ""; string superEditorPlantMatch = CheckSuperEditorPopup(originalText); - string customLevelRegexMatch = CheckCustomLevelRegex(originalText); + string customLevelRegexMatch = CheckCustomLevelRegex(originalText); + string qualityCheckMatch = CheckQualityUpgrade(originalText); switch(godsGachaCheck) { @@ -112,6 +114,10 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects } } } + else if(qualityCheckMatch != "") + { + txt.text = qualityCheckMatch; + } else if(superEditorPlantMatch != "") { txt.text = superEditorPlantMatch; @@ -164,8 +170,10 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects public static string nameAlreadyPlantedPattern = "场上已经有一个([^\\s]+)了"; public static string upgradePathRemovedPattern = "已移除路线:([^\\s]+)"; - public static string lockedPlantPattern = "^([^\\s:]+)\\+([^\\s:]+)"; - public static string multiLockedPlantPattern = "^或 ([^\\s:]+)\\+([^\\s:]+)"; + public static string lockedPlantPattern = "^([\\s\\S]+)\\+([\\S]+)$"; + public static string multiLockedPlantPattern = "^或 ([\\s\\S]+)\\+([\\S]+)$"; + + public static string qualityChangePattern = "诸神注视着你,并选中了一个他们喜欢的词条\n植物【([\\S]+)】的词条【强化:([\\S]+)】获得品质升级\n当前品质:([\\S]+)"; public static Dictionary fallbackFStrs = new Dictionary() { @@ -175,7 +183,8 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects { upgradePathRemovedPattern, "Upgrade Path removed for {0}" }, { @"([^\s]+)\((\d+)\)", "{0}({1})" }, { lockedPlantPattern, "{0} + {1}" }, - { multiLockedPlantPattern, "or {0} + {1}" } + { multiLockedPlantPattern, "or {0} + {1}" }, + { qualityChangePattern, "The gods are watching you, and they have granted you an upgrade!\nThe {1} upgrade for {0} has been upgraded.\nCurrent Quality: {2}" } }; public static int CheckGodsGachaPopup(string originalText) @@ -205,13 +214,13 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects switch(type) { case 1: - res = TranslatePlantNameParts(originalText, namePlantedPattern); + res = StringStore.TranslateText(originalText, namePlantedPattern); break; case 2: - res = TranslatePlantNameParts(originalText, nameUpgradedPattern); + res = StringStore.TranslateText(originalText, nameUpgradedPattern); break; case 3: - res = TranslatePlantNameParts(originalText, nameAlreadyPlantedPattern); + res = StringStore.TranslateText(originalText, nameAlreadyPlantedPattern); break; default: break; @@ -269,7 +278,7 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects if(Regex.IsMatch(originalText, upgradePathRemovedPattern)) { - res = TranslatePlantNameParts(originalText, upgradePathRemovedPattern); + res = StringStore.TranslateText(originalText, upgradePathRemovedPattern); } else if(Regex.IsMatch(originalText, "当前全部基础植物:(.*)")) { @@ -316,17 +325,14 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects string firstLine = lines[0]; Match match = lockedPlantRegex.Match(firstLine); GroupCollection groups = match.Groups; - Log.LogDebug(Utils.plantIndiceStrings.ContainsKey(groups[1].Value)); - Log.LogDebug(Utils.plantIndiceStrings.ContainsKey(groups[2].Value)); - if (Utils.plantIndiceStrings.ContainsKey(groups[1].Value) && Utils.plantIndiceStrings.ContainsKey(groups[2].Value)) + if (Utils.plantIndiceString.ContainsKey(groups[1].Value) && Utils.plantIndiceString.ContainsKey(groups[2].Value)) { - firstLine = TranslatePlantNameParts(firstLine, lockedPlantPattern); + firstLine = StringStore.TranslateText(firstLine, lockedPlantPattern); List translatedLines = new List() { firstLine }; for(int i = 1; i < lines.Length; i++) { string nextLine = lines[i]; - Log.LogDebug(nextLine); - string translatedNextLine = TranslatePlantNameParts(nextLine, multiLockedPlantPattern); + string translatedNextLine = StringStore.TranslateText(nextLine, multiLockedPlantPattern); translatedLines.Add(translatedNextLine); } res = string.Join("\n", translatedLines); @@ -343,9 +349,9 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects { Match match = lockedPlantRegex.Match(originalText); GroupCollection groups = match.Groups; - if (Utils.plantIndiceStrings.ContainsKey(groups[1].Value) && Utils.plantIndiceStrings.ContainsKey(groups[2].Value)) + if (Utils.plantIndiceString.ContainsKey(groups[1].Value) && Utils.plantIndiceString.ContainsKey(groups[2].Value)) { - res = TranslatePlantNameParts(originalText, lockedPlantPattern); + res = StringStore.TranslateText(originalText, lockedPlantPattern); } else { @@ -371,5 +377,17 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects } return res; } + + public static string CheckQualityUpgrade(string originalText) + { + string res = ""; + + if(Regex.IsMatch(originalText, qualityChangePattern)) + { + res = StringStore.TranslateText(originalText, qualityChangePattern); + } + + return res; + } } } \ No newline at end of file diff --git a/MelonLoader/Patches/BaseTextObjects/TextMeshProUGUI_Patch.cs b/MelonLoader/Patches/BaseTextObjects/TextMeshProUGUI_Patch.cs index fbfd521..e7444ca 100644 --- a/MelonLoader/Patches/BaseTextObjects/TextMeshProUGUI_Patch.cs +++ b/MelonLoader/Patches/BaseTextObjects/TextMeshProUGUI_Patch.cs @@ -17,6 +17,10 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects { if (!string.IsNullOrEmpty(__instance.text)) { + __instance.font = FontStore.LoadTMPFont(Utils.Language.ToString()); + + if (!Utils.CheckForUntranslatedText(__instance.text) && !(__instance.text).Contains("_")) return; + if(__instance.transform.parent != null) { if(__instance.transform.parent.name.Contains("DrawCardsText")) diff --git a/MelonLoader/Patches/GameObjects/AlmanacBuffMenu_Patch.cs b/MelonLoader/Patches/GameObjects/AlmanacBuffMenu_Patch.cs new file mode 100644 index 0000000..b32868f --- /dev/null +++ b/MelonLoader/Patches/GameObjects/AlmanacBuffMenu_Patch.cs @@ -0,0 +1,91 @@ +using HarmonyLib; +using Il2Cpp; +using Il2CppAlmanacData; +using Il2CppTMPro; +using PvZ_Fusion_Translator.AssetStore; +using UnityEngine; +using System.Drawing; +using static Il2Cpp.AlmanacBuffMenu; +using static PvZ_Fusion_Translator.FileLoader; +using static PvZ_Fusion_Translator.Patches.Modes.Odyssey.TravelMgr_Patch; +using static PvZ_Fusion_Translator.Utils; + +namespace PvZ_Fusion_Translator.Patches.GameObjects +{ + [HarmonyPatch(typeof(AlmanacBuffMenu))] + public static class AlmanacBuffMenu_Patch + { + public static Dictionary modifierNameDict = new Dictionary() + { + { "advancedBuffs", "COMMON_MODIFIER_TAG" }, + { "ultimateBuffs", "EPIC_MODIFIER_TAG" }, + { "debuffs", "ZOMBIE_MODIFIER_TAG" }, + { "unlocks", "UNLOCK_MODIFIER_TAG" }, + { "investmentBuffs", "INVEST_MODIFIER_TAG" } + }; + + public static Dictionary fallbackNameDict = new Dictionary() + { + { "COMMON_MODIFIER_TAG", "Common Modifier" }, + { "EPIC_MODIFIER_TAG", "Epic Modifier" }, + { "ZOMBIE_MODIFIER_TAG", "Zombie Modifier" }, + { "UNLOCK_MODIFIER_TAG", "Unlock Modifier" }, + { "INVEST_MODIFIER_TAG", "Investment Modifier" } + }; + + public static string TranslateNameTag(string originalText) + { + string res = originalText; + + string nameTag = modifierNameDict[originalText]; + res = StringStore.translationString.ContainsKey(nameTag) ? StringStore.TranslateText(nameTag) : fallbackNameDict[nameTag]; + + return res; + } + + [HarmonyPatch(nameof(AlmanacBuffMenu.Awake))] + [HarmonyPostfix] + public static void Post_Awake(AlmanacBuffMenu __instance) + { + foreach(AlmanacCardUI cardUI in __instance.cards) + { + AlmanacBuffMenu.CardInfo cardInfo = __instance.cardInfos[cardUI]; + var buffData = FindBuffData(cardInfo.description); + + if (buffData.Item1 == null || buffData.Item3 == null) continue; + + string nameTag = TranslateNameTag(buffData.Item1); + cardUI.cost.text = $"{nameTag} #{buffData.Item2}"; + } + + Post_OnCardClick(__instance); + } + + [HarmonyPatch(nameof(AlmanacBuffMenu.OnCardClick))] + [HarmonyPostfix] + public static void Post_OnCardClick(AlmanacBuffMenu __instance) + { + var buffData = FindBuffData(__instance.buffDescription.text); + + if (buffData.Item1 == null || buffData.Item3 == null) return; + + string nameTag = TranslateNameTag(buffData.Item1); + __instance.buffTitle.text = $"{nameTag} #{buffData.Item2}"; + __instance.buffTitle.font = FontStore.LoadTMPFont(Utils.Language.ToString()); + __instance.buffDescription.text = buffData.Item3 + "\n\n"; + __instance.buffDescription.font = FontStore.LoadTMPFont(Utils.Language.ToString()); + string colorHex = StringStore.translationString.ContainsKey("MOD_ALMANAC_COL") ? StringStore.TranslateText("MOD_ALMANAC_COL") : "#000000"; + System.Drawing.Color descriptionColor = ColorTranslator.FromHtml(colorHex); + __instance.buffDescription.color = new UnityEngine.Color(descriptionColor.R / 255.0f, descriptionColor.G / 255.0f, descriptionColor.B / 255.0f); + + __instance.buffDescription.fontSizeMax = 18; + __instance.buffDescription.margin = new Vector4(8, 4, 25, 0); + + __instance.buffDescription.enableWordWrapping = true; + __instance.buffDescription.overflowMode = TextOverflowModes.ScrollRect; + + Canvas.ForceUpdateCanvases(); + __instance.buffDescription.ForceMeshUpdate(); + } + } +} diff --git a/MelonLoader/Patches/GameObjects/AlmanacCardUI_Patch.cs b/MelonLoader/Patches/GameObjects/AlmanacCardUI_Patch.cs index 8f6527c..9cdf33e 100644 --- a/MelonLoader/Patches/GameObjects/AlmanacCardUI_Patch.cs +++ b/MelonLoader/Patches/GameObjects/AlmanacCardUI_Patch.cs @@ -1,10 +1,6 @@ using HarmonyLib; using Il2Cpp; -using Il2CppTMPro; using PvZ_Fusion_Translator.AssetStore; -using UnityEngine; -using System.Text.RegularExpressions; -using Unity.VisualScripting; namespace PvZ_Fusion_Translator.Patches.GameObjects { @@ -19,14 +15,14 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects __instance.cost.text = StringStore.TranslateText(__instance.cost.text); } - [HarmonyPatch(nameof(AlmanacCardUI.OnPointerDown))] + /*[HarmonyPatch(nameof(AlmanacCardUI.OnPointerDown))] [HarmonyPostfix] public static void OnPointerDown(AlmanacCardUI __instance) { - foreach(AlmanacCardUI almanacCardUI in __instance.menu.GetComponentsInChildren()) + foreach (AlmanacCardUI almanacCardUI in __instance.menu.GetComponentsInChildren()) { AlmanacCardUI_Patch.Awake(almanacCardUI); } - } + }*/ } } diff --git a/MelonLoader/Patches/GameObjects/AlmanacPlantMenu_Patch.cs b/MelonLoader/Patches/GameObjects/AlmanacPlantMenu_Patch.cs index 837aa7e..39a454d 100644 --- a/MelonLoader/Patches/GameObjects/AlmanacPlantMenu_Patch.cs +++ b/MelonLoader/Patches/GameObjects/AlmanacPlantMenu_Patch.cs @@ -2,7 +2,9 @@ using Il2Cpp; using Il2CppAlmanacData; using Il2CppTMPro; +using MelonLoader.TinyJSON; using PvZ_Fusion_Translator.AssetStore; +using System.Text.Json; using System.Text.RegularExpressions; using Unity.Collections; using Unity.VisualScripting; @@ -18,6 +20,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects public static class AlmanacPlantMenu_Patch { public static string almanacJson = ""; + public static Dictionary plantInfoDict = new(); [HarmonyPatch(nameof(AlmanacPlantMenu.Awake))] [HarmonyPostfix] @@ -35,7 +38,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects } } - foreach(AlmanacCardUI almanacCardUI in __instance.GetComponentsInChildren()) + foreach (AlmanacCardUI almanacCardUI in __instance.GetComponentsInChildren()) { AlmanacCardUI_Patch.Awake(almanacCardUI); } @@ -44,10 +47,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects AlmanacPlantWindow window = __instance.window; GameObject skinButton = window.skinButton; - GameObject skinTextObj = skinButton.transform.GetChild(0).gameObject; - GameObject skinShadowTextObj = skinTextObj.transform.GetChild(0).gameObject; - TextMeshProUGUI skinShadowText = skinShadowTextObj.GetComponent(); - skinShadowText.text = StringStore.TranslateText("换肤_S"); + GameObject skinTextObj = skinButton.transform.Find("text_1").gameObject; skinButton.transform.localScale /= 1.75f; skinButton.transform.localPosition = new Vector3(0.0f, 111.345f, 0.0f); skinButton.transform.GetChild(1).Translate(new Vector3(-0.35f, 0)); @@ -79,6 +79,10 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects { searchedPlants.Add((PlantType)plantInfo.seedType); } + else if ($"{plantInfo.seedType}".ToLower().StartsWith(search.ToLower())) + { + searchedPlants.Add((PlantType)plantInfo.seedType); + } } __instance.ShowPlants(searchedPlants); @@ -88,5 +92,46 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects return true; } + + [HarmonyPatch(nameof(AlmanacPlantMenu.ShowPlants), argumentTypes: [typeof(Il2CppSystem.Func)])] + [HarmonyPostfix] + public static void ShowPlants(AlmanacPlantMenu __instance, ref Func condition) + { + foreach (AlmanacCardUI cardUI in __instance.cards) + { + AlmanacCardUI_Patch.Awake(cardUI); + } + } + + [HarmonyPatch(nameof(AlmanacPlantMenu.ClassicMix))] + [HarmonyPostfix] + public static void ClassicMix(AlmanacPlantMenu __instance) + { + foreach (AlmanacCardUI cardUI in __instance.cards) + { + AlmanacCardUI_Patch.Awake(cardUI); + } + } + + public static void LoadPlantAlmanacData(string json) + { + if (json == null) return; + + almanacJson = json; + + var plantData = JsonUtility.FromJson(almanacJson); + + foreach (Il2CppAlmanacData.PlantInfo plantInfo in plantData.plants) + { + if(plantInfoDict.ContainsKey(plantInfo.seedType)) + { + plantInfoDict[plantInfo.seedType] = plantInfo; + } + else + { + plantInfoDict.Add(plantInfo.seedType, plantInfo); + } + } + } } } diff --git a/MelonLoader/Patches/GameObjects/AlmanacPlantWindow_Patch.cs b/MelonLoader/Patches/GameObjects/AlmanacPlantWindow_Patch.cs index c0ae83a..239c747 100644 --- a/MelonLoader/Patches/GameObjects/AlmanacPlantWindow_Patch.cs +++ b/MelonLoader/Patches/GameObjects/AlmanacPlantWindow_Patch.cs @@ -1,5 +1,6 @@ using HarmonyLib; using Il2Cpp; +using Il2CppAlmanacData; using Il2CppTMPro; using PvZ_Fusion_Translator.AssetStore; using System.Text.RegularExpressions; @@ -34,35 +35,34 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects TMP_FontAsset fontAsset = (hasAlmanacFont) ? FontStore.LoadTMPFontAlmanac(Utils.Language.ToString()) : FontStore.LoadTMPFont(Utils.Language.ToString()); - Il2CppAlmanacData.AlmanacData plantData = JsonUtility.FromJson(json); + AlmanacData plantData = JsonUtility.FromJson(json); - foreach (Il2CppAlmanacData.PlantInfo plantInfo in plantData.plants) + if(AlmanacPlantMenu_Patch.plantInfoDict.ContainsKey((int)__instance.currentPlantType)) { - if (plantInfo.seedType == (int)__instance.currentPlantType) + PlantInfo plantInfo = AlmanacPlantMenu_Patch.plantInfoDict[(int)__instance.currentPlantType]; + + foreach (TextMeshProUGUI text in __instance.showedPlantName) { - foreach (TextMeshProUGUI text in __instance.showedPlantName) - { - text.autoSizeTextContainer = false; - text.text = $"{Utils.RemoveSizeTags(plantInfo.name)} ({plantInfo.seedType})"; - text.font = fontAsset; - text.fontSizeMax = 21; - } - - char[] toTrim = Environment.NewLine.ToCharArray(); - __instance.showedPlantIntroduce.text = Utils.RemoveSizeTags(plantInfo.info) + "\n\n" + Utils.RemoveSizeTags(plantInfo.introduce) + "\n\n" + Utils.RemoveSizeTags(plantInfo.cost).TrimStart(toTrim) + "\n\n"; - __instance.showedPlantIntroduce.font = fontAsset; - __instance.showedPlantIntroduce.fontSizeMax = 21; - __instance.showedPlantIntroduce.margin = new Vector4(6, 2, 12, 0); - - __instance.showedPlantIntroduce.enableWordWrapping = true; - __instance.showedPlantIntroduce.overflowMode = TextOverflowModes.ScrollRect; - - Canvas.ForceUpdateCanvases(); - __instance.showedPlantIntroduce.ForceMeshUpdate(); - - float textHeight = __instance.showedPlantIntroduce.preferredHeight; - __instance.plantTextContent.sizeDelta = new Vector2(__instance.plantTextContent.sizeDelta.x, textHeight); + text.autoSizeTextContainer = false; + text.text = $"{Utils.RemoveSizeTags(plantInfo.name)} ({plantInfo.seedType})"; + text.font = fontAsset; + text.fontSizeMax = 21; } + + char[] toTrim = Environment.NewLine.ToCharArray(); + __instance.showedPlantIntroduce.text = Utils.RemoveSizeTags(plantInfo.info) + "\n\n" + Utils.RemoveSizeTags(plantInfo.introduce) + "\n\n" + Utils.RemoveSizeTags(plantInfo.cost).TrimStart(toTrim) + "\n\n"; + __instance.showedPlantIntroduce.font = fontAsset; + __instance.showedPlantIntroduce.fontSizeMax = 21; + __instance.showedPlantIntroduce.margin = new Vector4(6, 2, 12, 0); + + __instance.showedPlantIntroduce.enableWordWrapping = true; + __instance.showedPlantIntroduce.overflowMode = TextOverflowModes.ScrollRect; + + Canvas.ForceUpdateCanvases(); + __instance.showedPlantIntroduce.ForceMeshUpdate(); + + float textHeight = __instance.showedPlantIntroduce.preferredHeight; + __instance.plantTextContent.sizeDelta = new Vector2(__instance.plantTextContent.sizeDelta.x, textHeight); } } } diff --git a/MelonLoader/Patches/GameObjects/Almanac_Patch.cs b/MelonLoader/Patches/GameObjects/Almanac_Patch.cs index be6b547..d1e2885 100644 --- a/MelonLoader/Patches/GameObjects/Almanac_Patch.cs +++ b/MelonLoader/Patches/GameObjects/Almanac_Patch.cs @@ -18,12 +18,12 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects TranslateAlmanacMenu(__instance); } - [HarmonyPatch(nameof(Almanac.OldAlmanac))] + /*[HarmonyPatch(nameof(Almanac.OldAlmanac))] [HarmonyPostfix] private static void OldAlmanac(Almanac __instance) { TranslateAlmanacMenu(__instance); - } + }*/ [HarmonyPatch(nameof(Almanac.AnimSwitch))] [HarmonyPostfix] diff --git a/MelonLoader/Patches/GameObjects/BaseMenu_Patch.cs b/MelonLoader/Patches/GameObjects/BaseMenu_Patch.cs index f528ec6..49c2fa3 100644 --- a/MelonLoader/Patches/GameObjects/BaseMenu_Patch.cs +++ b/MelonLoader/Patches/GameObjects/BaseMenu_Patch.cs @@ -35,6 +35,11 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects MainMenu_Patch.AnimOver(__instance); } + if(__instance.name.StartsWith("PlantDamageMenu")) + { + PlantDamageMenu_Patch.Awake(__instance); + } + TranslateBaseMenuTransform(__instance, "Level"); TranslateBaseMenuTransform(__instance, "Levels"); TranslateBaseMenuTransform(__instance, "CustomLevel"); diff --git a/MelonLoader/Patches/GameObjects/ButtonObjects/LevelSaveEnter_Patch.cs b/MelonLoader/Patches/GameObjects/ButtonObjects/LevelSaveEnter_Patch.cs new file mode 100644 index 0000000..86a42f0 --- /dev/null +++ b/MelonLoader/Patches/GameObjects/ButtonObjects/LevelSaveEnter_Patch.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using HarmonyLib; +using Il2Cpp; +using Il2CppTMPro; +using PvZ_Fusion_Translator.AssetStore; +using UnityEngine; + +namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects +{ + public static class LevelSaveEnter_Patch + { + public static void TranslateEnter(LevelSaveEnter __instance) + { + TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); + + __instance.text.text = StringStore.TranslateText(__instance.text.text); + __instance.text.font = fontAsset; + } + } +} diff --git a/MelonLoader/Patches/GameObjects/LevelResultMenu_Patch.cs b/MelonLoader/Patches/GameObjects/LevelResultMenu_Patch.cs index 8b2071e..3f52c2c 100644 --- a/MelonLoader/Patches/GameObjects/LevelResultMenu_Patch.cs +++ b/MelonLoader/Patches/GameObjects/LevelResultMenu_Patch.cs @@ -14,7 +14,6 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects [HarmonyPatch(typeof(LevelResultMenu))] public static class LevelResultMenu_Patch { - [HarmonyPatch(nameof(LevelResultMenu.Awake))] [HarmonyPatch(nameof(LevelResultMenu.Start))] [HarmonyPatch(nameof(LevelResultMenu.ShowResult))] [HarmonyPostfix] @@ -26,4 +25,4 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects } } } -} +} \ No newline at end of file diff --git a/MelonLoader/Patches/GameObjects/MainMenu_Patch.cs b/MelonLoader/Patches/GameObjects/MainMenu_Patch.cs index f6c1024..f1792c5 100644 --- a/MelonLoader/Patches/GameObjects/MainMenu_Patch.cs +++ b/MelonLoader/Patches/GameObjects/MainMenu_Patch.cs @@ -45,12 +45,13 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects } } - OptionBtn templateButton = newOptionsMenu.transform.Find("F").GetComponent(); + OptionBtn templateButton = newOptionsMenu.transform.Find("Buttons/F").GetComponent(); OptLanguageBtn_Patch.OptLangBtn_Patch.Awake(templateButton); UIButton goBackButton = newOptionsMenu.transform.Find("Goback").GetComponent(); - newOptionsMenu.transform.Find("F").gameObject.SetActive(false); + newOptionsMenu.transform.Find("Buttons/F").gameObject.SetActive(false); + newOptionsMenu.transform.Find("Buttons").gameObject.SetActive(true); foreach (TextMeshProUGUI text in newOptionsMenu.transform.Find("Background").GetComponentsInChildren()) { diff --git a/MelonLoader/Patches/GameObjects/MinorObjects/GameLose_Patch.cs b/MelonLoader/Patches/GameObjects/MinorObjects/GameLose_Patch.cs index 340fd95..e55957f 100644 --- a/MelonLoader/Patches/GameObjects/MinorObjects/GameLose_Patch.cs +++ b/MelonLoader/Patches/GameObjects/MinorObjects/GameLose_Patch.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using HarmonyLib; using UnityEngine; using Il2Cpp; +using Il2CppCore; using PvZ_Fusion_Translator.Patches.BaseTextObjects; namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects diff --git a/MelonLoader/Patches/GameObjects/MinorObjects/HealthSlider_Patch.cs b/MelonLoader/Patches/GameObjects/MinorObjects/HealthSlider_Patch.cs new file mode 100644 index 0000000..370d592 --- /dev/null +++ b/MelonLoader/Patches/GameObjects/MinorObjects/HealthSlider_Patch.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Il2Cpp; +using HarmonyLib; +using UnityEngine; +using Il2CppTMPro; +using PvZ_Fusion_Translator.AssetStore; +using Unity.VisualScripting; + +namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects +{ + [HarmonyPatch(typeof(HealthSlider))] + public static class HealthSlider_Patch + { + public static TMP_FontAsset fontAsset = null; + + [HarmonyPatch(nameof(HealthSlider.Start))] + [HarmonyPostfix] + public static void Post_Start(HealthSlider __instance) + { + if(!fontAsset) + { + fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); + } + } + + [HarmonyPatch(nameof(HealthSlider.UpdateText))] + [HarmonyPostfix] + public static void Post_UpdateText(HealthSlider __instance) + { + if(!fontAsset) fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); + + if (__instance.healthText.font != fontAsset) __instance.healthText.font = fontAsset; + + foreach(TextMeshProUGUI txt in __instance.healthTextContainer.GetComponentsInChildren(true)) + { + txt.text = StringStore.TranslateText(txt.text); + txt.font = fontAsset; + } + } + } +} diff --git a/MelonLoader/Patches/GameObjects/MinorObjects/LanternUmbrella_Patch.cs b/MelonLoader/Patches/GameObjects/MinorObjects/LanternUmbrella_Patch.cs index d751354..d4a1c82 100644 --- a/MelonLoader/Patches/GameObjects/MinorObjects/LanternUmbrella_Patch.cs +++ b/MelonLoader/Patches/GameObjects/MinorObjects/LanternUmbrella_Patch.cs @@ -4,7 +4,7 @@ using PvZ_Fusion_Translator.AssetStore; namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects { -[HarmonyPatch(typeof(LanternUmbrella))] + /*[HarmonyPatch(typeof(LanternUmbrella))] public class LanternUmbrella_Patch { [HarmonyPostfix] @@ -14,5 +14,5 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects __instance.energyText = StringStore.TranslateText(__instance.energyText); __instance.energyTextShadow = StringStore.TranslateText(__instance.energyTextShadow); } - } + }*/ } \ No newline at end of file diff --git a/MelonLoader/Patches/GameObjects/MinorObjects/PumpinerChestMenu_Patch.cs b/MelonLoader/Patches/GameObjects/MinorObjects/PumpinerChestMenu_Patch.cs new file mode 100644 index 0000000..4a54d10 --- /dev/null +++ b/MelonLoader/Patches/GameObjects/MinorObjects/PumpinerChestMenu_Patch.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Il2Cpp; +using UnityEngine; +using HarmonyLib; +using Il2CppTMPro; +using PvZ_Fusion_Translator.AssetStore; + +namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects +{ + [HarmonyPatch(typeof(PumpinerChestMenu))] + public static class PumpinerChestMenu_Patch + { + [HarmonyPatch(nameof(PumpinerChestMenu.InitPlants))] + [HarmonyPostfix] + public static void Post_InitPlants(PumpinerChestMenu __instance) + { + Transform closeBtnTransform = __instance.transform.Find("SampleButton"); + TextMeshProUGUI txt = closeBtnTransform.GetComponentInChildren(); + txt.text = StringStore.TranslateText("合上"); + } + } +} diff --git a/MelonLoader/Patches/GameObjects/MinorObjects/SpecialCard_Patch.cs b/MelonLoader/Patches/GameObjects/MinorObjects/SpecialCard_Patch.cs new file mode 100644 index 0000000..875809e --- /dev/null +++ b/MelonLoader/Patches/GameObjects/MinorObjects/SpecialCard_Patch.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using HarmonyLib; +using Il2Cpp; +using Il2CppTMPro; +using PvZ_Fusion_Translator.AssetStore; +using UnityEngine; + +namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects +{ + [HarmonyPatch(typeof(SpecialCard))] + public static class SpecialCard_Patch + { + [HarmonyPatch(nameof(SpecialCard.Awake))] + [HarmonyPatch(nameof(SpecialCard.Start))] + [HarmonyPatch(nameof(SpecialCard.ClickedEvent))] + [HarmonyPostfix] + public static void Post_Awake(SpecialCard __instance) + { + TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); + + foreach(TextMeshProUGUI txt in __instance.GetComponentsInChildren()) + { + txt.text = StringStore.TranslateText(txt.text); + txt.font = fontAsset; + } + } + } +} diff --git a/MelonLoader/Patches/GameObjects/MinorObjects/TrainingDummy_Patch.cs b/MelonLoader/Patches/GameObjects/MinorObjects/TrainingDummy_Patch.cs index f2bb0d5..8128ccc 100644 --- a/MelonLoader/Patches/GameObjects/MinorObjects/TrainingDummy_Patch.cs +++ b/MelonLoader/Patches/GameObjects/MinorObjects/TrainingDummy_Patch.cs @@ -10,7 +10,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects public static void UpdateHealthText(TrainingDummy __instance) { Zombie_Patch.TranslateHpUI_T(__instance.healthText, __instance); - Zombie_Patch.TranslateHpUI_T(__instance.healthTextShadow, __instance); + //Zombie_Patch.TranslateHpUI_T(__instance.healthTextShadow, __instance); } } } \ No newline at end of file diff --git a/MelonLoader/Patches/GameObjects/MinorObjects/UltimateChomper_Patch.cs b/MelonLoader/Patches/GameObjects/MinorObjects/UltimateChomper_Patch.cs index d04c947..9443605 100644 --- a/MelonLoader/Patches/GameObjects/MinorObjects/UltimateChomper_Patch.cs +++ b/MelonLoader/Patches/GameObjects/MinorObjects/UltimateChomper_Patch.cs @@ -4,7 +4,7 @@ using PvZ_Fusion_Translator.AssetStore; namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects { - [HarmonyPatch(typeof(UltimateChomper), nameof(UltimateChomper.UpdateText))] + /*[HarmonyPatch(typeof(UltimateChomper), nameof(UltimateChomper.UpdateText))] public static class UltimateChomper_Patch { [HarmonyPostfix] @@ -13,5 +13,5 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects StringStore.TranslateText(__instance.killingText); StringStore.TranslateText(__instance.killingTextShadow); } - } + }*/ } \ No newline at end of file diff --git a/MelonLoader/Patches/GameObjects/MinorObjects/Zombie_Patch.cs b/MelonLoader/Patches/GameObjects/MinorObjects/Zombie_Patch.cs index 5a67517..6dd850a 100644 --- a/MelonLoader/Patches/GameObjects/MinorObjects/Zombie_Patch.cs +++ b/MelonLoader/Patches/GameObjects/MinorObjects/Zombie_Patch.cs @@ -32,7 +32,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects public static void Start(Zombie __instance) { TranslateHpUI(__instance.healthText, __instance); - TranslateHpUI(__instance.healthTextShadow, __instance); + //TranslateHpUI(__instance.healthTextShadow, __instance); if (__instance.healthText.transform.rotation != Quaternion.Euler(0, 0, 0)) __instance.healthText.transform.rotation = Quaternion.Euler(0f, 0f, 0f); @@ -45,10 +45,10 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects if (__instance.theZombieType == ZombieType.TrainingDummy || !Board.Instance.showZombieHealth) return; - if (__instance.healthText == null || __instance.healthTextShadow == null) return; + if (__instance.healthText == null) return; TranslateHpUI(__instance.healthText, __instance); - TranslateHpUI(__instance.healthTextShadow, __instance); + //TranslateHpUI(__instance.healthTextShadow, __instance); if (__instance.healthText.transform.rotation != Quaternion.Euler(0f, 0f, 0f)) __instance.healthText.transform.rotation = Quaternion.Euler(0f, 0f, 0f); diff --git a/MelonLoader/Patches/GameObjects/PlantDamageMenu_Patch.cs b/MelonLoader/Patches/GameObjects/PlantDamageMenu_Patch.cs index c5e3bb1..f29c099 100644 --- a/MelonLoader/Patches/GameObjects/PlantDamageMenu_Patch.cs +++ b/MelonLoader/Patches/GameObjects/PlantDamageMenu_Patch.cs @@ -10,26 +10,27 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects [HarmonyPatch(typeof(PlantDamageMenu))] public static class PlantDamageMenu_Patch { - [HarmonyPatch(nameof(PlantDamageMenu.Awake))] - [HarmonyPostfix] - private static void Awake(PlantDamageMenu __instance) + public static void Awake(BaseMenu __instance) { TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); foreach(TextMeshProUGUI text in __instance.GetComponentsInChildren()) { - text.text = StringStore.TranslateText(text.text); + string originalKey = StringStore.translationString.FirstOrDefault(x => x.Value == text.text).Key; + text.text = StringStore.translationString.ContainsKey(originalKey + "_RM") ? StringStore.TranslateText(originalKey + "_RM") : StringStore.TranslateText(text.text); text.font = fontAsset; } } + [HarmonyPatch(nameof(PlantDamageMenu.Awake))] [HarmonyPatch(nameof(PlantDamageMenu.InitCards))] [HarmonyPostfix] - private static void InitCards(PlantDamageMenu __instance) + public static void InitCards(PlantDamageMenu __instance) { TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); foreach (TextMeshProUGUI text in __instance.GetComponentsInChildren()) { - text.text = StringStore.TranslateText(text.text); + string originalKey = StringStore.translationString.FirstOrDefault(x => x.Value == text.text).Key; + text.text = StringStore.translationString.ContainsKey(originalKey + "_RM") ? StringStore.TranslateText(originalKey + "_RM") : StringStore.TranslateText(text.text); text.font = fontAsset; } } diff --git a/MelonLoader/Patches/GameObjects/PlantDataMenu_Patch.cs b/MelonLoader/Patches/GameObjects/PlantDataMenu_Patch.cs index 52669ed..1643d0d 100644 --- a/MelonLoader/Patches/GameObjects/PlantDataMenu_Patch.cs +++ b/MelonLoader/Patches/GameObjects/PlantDataMenu_Patch.cs @@ -21,12 +21,12 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects TranslatePlantDataMenu(__instance); } - [HarmonyPatch(nameof(PlantDataMenu.Update))] + /*[HarmonyPatch(nameof(PlantDataMenu.Update))] [HarmonyPostfix] public static void Post_Update(PlantDataMenu __instance) { TranslatePlantDataMenu(__instance); - } + }*/ [HarmonyPatch(nameof(PlantDataMenu.InitDataFromPlant))] [HarmonyPostfix] @@ -37,6 +37,8 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects public static void TranslatePlantDataMenu(PlantDataMenu __instance) { + if (!__instance) return; + PlantType thePlantType = __instance.plant.thePlantType; string plantName = Utils.GetPlantNameFromAlmanac(thePlantType); diff --git a/MelonLoader/Patches/GameObjects/PlayerSaveMenu_Patch.cs b/MelonLoader/Patches/GameObjects/PlayerSaveMenu_Patch.cs index f8e702d..d550ff9 100644 --- a/MelonLoader/Patches/GameObjects/PlayerSaveMenu_Patch.cs +++ b/MelonLoader/Patches/GameObjects/PlayerSaveMenu_Patch.cs @@ -26,10 +26,10 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects public static void TranslateCreatePlayerSaveMenu() { - Transform createPlayerSaveMenuTransform = GameAPP.canvasUp.Find("CreatePlayerSaveMenu(Clone)"); - if(createPlayerSaveMenuTransform.TryGetComponent(out CreatePlayerSaveMenu createPlayerSaveMenu)) + Transform reNameMenuTransform = GameAPP.canvasUp.Find("ReNameMenu(Clone)"); + if(reNameMenuTransform.TryGetComponent(out ReNameMenu reNameMenu)) { - createPlayerSaveMenu.title.text = StringStore.TranslateText(createPlayerSaveMenu.title.text); + reNameMenu.title.text = StringStore.TranslateText(reNameMenu.title.text); } } } diff --git a/MelonLoader/Patches/GameObjects/ReNameMenu_Patch.cs b/MelonLoader/Patches/GameObjects/ReNameMenu_Patch.cs new file mode 100644 index 0000000..5a1ce8e --- /dev/null +++ b/MelonLoader/Patches/GameObjects/ReNameMenu_Patch.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using HarmonyLib; +using UnityEngine; +using Il2Cpp; +using PvZ_Fusion_Translator.AssetStore; + +namespace PvZ_Fusion_Translator.Patches.GameObjects +{ + [HarmonyPatch(typeof(ReNameMenu))] + public static class ReNameMenu_Patch + { + [HarmonyPatch(nameof(ReNameMenu.SetTitle))] + [HarmonyPostfix] + public static void SetTitle(ReNameMenu __instance) + { + __instance.title.text = StringStore.TranslateText(__instance.title.text); + } + + [HarmonyPatch(nameof(ReNameMenu.SetInputText))] + [HarmonyPostfix] + public static void SetInputText(ReNameMenu __instance) + { + __instance.input.text = StringStore.TranslateText(__instance.input.text); + } + } +} diff --git a/MelonLoader/Patches/GameObjects/ShootingMenu_Patch.cs b/MelonLoader/Patches/GameObjects/ShootingMenu_Patch.cs index 069ee0e..7735106 100644 --- a/MelonLoader/Patches/GameObjects/ShootingMenu_Patch.cs +++ b/MelonLoader/Patches/GameObjects/ShootingMenu_Patch.cs @@ -6,7 +6,7 @@ using UnityEngine; namespace PvZ_Fusion_Translator.Patches.GameObjects { - [HarmonyPatch(typeof(ShootingMenu))] + /*[HarmonyPatch(typeof(ShootingMenu))] public static class ShootingMenu_Patch { [HarmonyPatch(nameof(ShootingMenu.Update))] @@ -21,5 +21,5 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects txt.font = fontAsset; } } - } + }*/ } \ No newline at end of file diff --git a/MelonLoader/Patches/GameObjects/ShootingSelectWindow_Patch.cs b/MelonLoader/Patches/GameObjects/ShootingSelectWindow_Patch.cs index 2e49428..79580b5 100644 --- a/MelonLoader/Patches/GameObjects/ShootingSelectWindow_Patch.cs +++ b/MelonLoader/Patches/GameObjects/ShootingSelectWindow_Patch.cs @@ -7,7 +7,7 @@ using System.Text.RegularExpressions; namespace PvZ_Fusion_Translator.Patches.GameObjects { - [HarmonyPatch(typeof(ShootingSelectWindow))] + /*[HarmonyPatch(typeof(ShootingSelectWindow))] public static class ShootingSelectWindow_Patch { [HarmonyPatch(nameof(ShootingSelectWindow.InitPlantType))] @@ -69,5 +69,5 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects introduceText.text = StringStore.TranslateText(introduceText.text); introduceText.font = fontAsset; } - } + }*/ } diff --git a/MelonLoader/Patches/Managers/SaveMenu_Patch.cs b/MelonLoader/Patches/Managers/SaveMenu_Patch.cs index 2a36d90..0f5ae09 100644 --- a/MelonLoader/Patches/Managers/SaveMenu_Patch.cs +++ b/MelonLoader/Patches/Managers/SaveMenu_Patch.cs @@ -2,6 +2,8 @@ using Il2Cpp; using Il2CppTMPro; using PvZ_Fusion_Translator.AssetStore; +using PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects; +using UnityEngine; namespace PvZ_Fusion_Translator.Patches.Managers { @@ -9,20 +11,27 @@ namespace PvZ_Fusion_Translator.Patches.Managers public static class SaveMenu_Patch { [HarmonyPatch(nameof(SaveMenu.Start))] + [HarmonyPatch(nameof(SaveMenu.CreateLevelEnter))] + [HarmonyPatch(nameof(SaveMenu.InitLevelSaveEnters))] [HarmonyPostfix] private static void Start(SaveMenu __instance) { TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); - __instance.title.text = StringStore.TranslateText(__instance.title.text); - __instance.title.font = fontAsset; - __instance.title_shadow.text = StringStore.TranslateText(__instance.title_shadow.text); - __instance.title_shadow.font = fontAsset; + __instance.menuTitle.text = StringStore.TranslateText(__instance.menuTitle.text); + __instance.menuTitle.font = fontAsset; - foreach(SaveMenuBtn level in __instance.levels) + foreach(LevelSaveEnter enter in __instance.GetComponentsInChildren()) { - level.theName.text = StringStore.TranslateText(level.theName.text); + LevelSaveEnter_Patch.TranslateEnter(enter); } } + + [HarmonyPatch(nameof(SaveMenu.OnEditSelectedButtonClicked))] + [HarmonyPostfix] + private static void OnEditSelectedButtonClicked(SaveMenu __instance) + { + + } } } diff --git a/MelonLoader/Patches/Modes/Odyssey/MultipleChoiceMenu_Patch.cs b/MelonLoader/Patches/Modes/Odyssey/MultipleChoiceMenu_Patch.cs index acd06b3..9540207 100644 --- a/MelonLoader/Patches/Modes/Odyssey/MultipleChoiceMenu_Patch.cs +++ b/MelonLoader/Patches/Modes/Odyssey/MultipleChoiceMenu_Patch.cs @@ -1,42 +1,61 @@ -using System; +using HarmonyLib; +using Il2Cpp; +using Il2CppTMPro; +using PvZ_Fusion_Translator.AssetStore; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using Il2Cpp; -using HarmonyLib; using UnityEngine; -using Il2CppTMPro; -using PvZ_Fusion_Translator.AssetStore; +using UnityEngine.TextCore.Text; namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey { [HarmonyPatch(typeof(MultipleChoiceMenu))] public static class MultipleChoiceMenu_Patch { + public static TMP_FontAsset fontAsset => FontStore.LoadTMPFont(Utils.Language.ToString()); + [HarmonyPatch(nameof(MultipleChoiceMenu.Awake))] - [HarmonyPatch(nameof(MultipleChoiceMenu.UpdateWindow))] - [HarmonyPatch(nameof(MultipleChoiceMenu.Refresh))] [HarmonyPatch(nameof(MultipleChoiceMenu.Cancel))] + [HarmonyPatch(nameof(MultipleChoiceMenu.Refresh))] + [HarmonyPatch(nameof(MultipleChoiceMenu.UpdateWindow))] + [HarmonyPatch(nameof(MultipleChoiceMenu.UpdateWindows))] [HarmonyPostfix] public static void Awake(MultipleChoiceMenu __instance) { - TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); foreach(BaseWindow window in __instance.windows) { - foreach(TextMeshProUGUI txt in window.GetComponentsInChildren()) + TranslateWindowText(window.title); + TranslateWindowText(window.introduce); + } + + foreach(TheButton button in __instance.GetComponentsInChildren()) + { + foreach(TextMeshProUGUI txt in button.GetComponentsInChildren()) { + txt.text = StringStore.translationString.ContainsKey(txt.text + "_MC") ? StringStore.TranslateText(txt.text + "_MC") : StringStore.TranslateText(txt.text); txt.font = fontAsset; - if(StringStore.customLevelString.ContainsKey(txt.text)) - { - txt.text = StringStore.customLevelString[txt.text]; - } - else - { - txt.text = StringStore.translationString.ContainsKey(txt.text + "_MC") ? StringStore.TranslateText(txt.text + "_MC") : StringStore.TranslateText(txt.text); - } } } } + + public static void TranslateWindowText(TextMeshProUGUI txt) + { + txt.font = fontAsset; + if(StringStore.customLevelString.ContainsKey(txt.text)) + { + txt.text = StringStore.customLevelString[txt.text]; + } + else if(Utils.GetPlantNameFromAlmanac(txt.text) != "") + { + txt.text = Utils.GetPlantNameFromAlmanac(txt.text); + } + else + { + txt.text = StringStore.translationString.ContainsKey(txt.text + "_MC") ? StringStore.TranslateText(txt.text + "_MC") : StringStore.TranslateText(txt.text); + } + } } } diff --git a/MelonLoader/Patches/Modes/Odyssey/OppositeBuffManager_Patch.cs b/MelonLoader/Patches/Modes/Odyssey/OppositeBuffManager_Patch.cs index ef299fa..a29838f 100644 --- a/MelonLoader/Patches/Modes/Odyssey/OppositeBuffManager_Patch.cs +++ b/MelonLoader/Patches/Modes/Odyssey/OppositeBuffManager_Patch.cs @@ -7,7 +7,7 @@ using UnityEngine; namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey { - [HarmonyPatch(typeof(OppositeBuffManager))] + /*[HarmonyPatch(typeof(OppositeBuffManager))] public static class OppositeBuffManager_Patch { public static Dictionary buffLinks = TravelMgr_Patch.buffLinks; @@ -20,14 +20,14 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey [HarmonyPostfix] private static void SetText(OppositeBuffManager __instance) { - /*if (TravelMgr_Patch.travelBuffString.ContainsKey(__result)) + *//*if (TravelMgr_Patch.travelBuffString.ContainsKey(__result)) { __result = TravelMgr_Patch.travelBuffString[__result]; } else if(TravelMgr_Patch.travelBuffString.ContainsKey(TravelMgr_Patch.RemoveBuffName(__result))) { __result = TravelMgr_Patch.travelBuffString[TravelMgr_Patch.RemoveBuffName(__result)]; - }*/ + }*//* } public static void TranslateOppositeText(Il2CppSystem.Collections.Generic.List textList, Il2CppSystem.Object buff, bool isBad = false) @@ -48,5 +48,5 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey text.font = fontAsset; } } - } + }*/ } diff --git a/MelonLoader/Patches/Modes/Odyssey/ShootingManager_Patch.cs b/MelonLoader/Patches/Modes/Odyssey/ShootingManager_Patch.cs new file mode 100644 index 0000000..b20146b --- /dev/null +++ b/MelonLoader/Patches/Modes/Odyssey/ShootingManager_Patch.cs @@ -0,0 +1,19 @@ +using HarmonyLib; +using Il2CppGameLevel; +using Il2Cpp; +using UnityEngine; +using Il2CppGameLevel.RogueShooting; + +namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey +{ + [HarmonyPatch(typeof(ShootingManager))] + public static class ShootingManager_Patch + { + [HarmonyPatch(nameof(ShootingManager.UpgradeBuff))] + [HarmonyPostfix] + public static void UpgradeBuff(ShootingManager __instance, ref MultipleChoiceMenu menu) + { + MultipleChoiceMenu_Patch.Awake(menu); + } + } +} diff --git a/MelonLoader/Patches/Modes/Odyssey/SynergyDisplay_Patch.cs b/MelonLoader/Patches/Modes/Odyssey/SynergyDisplay_Patch.cs index cd944ff..2ec60a3 100644 --- a/MelonLoader/Patches/Modes/Odyssey/SynergyDisplay_Patch.cs +++ b/MelonLoader/Patches/Modes/Odyssey/SynergyDisplay_Patch.cs @@ -15,8 +15,7 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey [HarmonyPostfix] public static void Start(SynergyDisplay __instance) { - __instance.text1.text = TranslateInvestDisplay(__instance.text1.text); - __instance.text_shadow.text = Utils.RemoveColorTags(__instance.text1.text); + __instance.text.text = TranslateInvestDisplay(__instance.text.text); } public static string TranslateInvestDisplay(string originalText) diff --git a/MelonLoader/Patches/Modes/Odyssey/TravelAdvantureMenu_Patch.cs b/MelonLoader/Patches/Modes/Odyssey/TravelAdvantureMenu_Patch.cs index f2a38f4..4411728 100644 --- a/MelonLoader/Patches/Modes/Odyssey/TravelAdvantureMenu_Patch.cs +++ b/MelonLoader/Patches/Modes/Odyssey/TravelAdvantureMenu_Patch.cs @@ -20,7 +20,14 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey foreach(CustomButton_enterGame button in __instance.GetComponentsInChildren()) { string plantName = Utils.GetPlantNameFromAlmanac(button.levelName.text); - button.levelName.text = (plantName != "") ? plantName : StringStore.TranslateText(button.levelName.text); + if(Utils.Language == Utils.LanguageEnum.Arabic) + { + button.levelName.text = StringStore.TranslateText(button.levelName.text); + } + else + { + button.levelName.text = (plantName != "") ? plantName : StringStore.TranslateText(button.levelName.text); + } } } diff --git a/MelonLoader/Patches/Modes/Odyssey/TravelMgr_Patch.cs b/MelonLoader/Patches/Modes/Odyssey/TravelMgr_Patch.cs index 407880f..de356eb 100644 --- a/MelonLoader/Patches/Modes/Odyssey/TravelMgr_Patch.cs +++ b/MelonLoader/Patches/Modes/Odyssey/TravelMgr_Patch.cs @@ -17,6 +17,15 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey [HarmonyPatch(typeof(TravelMgr))] public static class TravelMgr_Patch { + public static readonly Dictionary> templateTravelBuffs = new() + { + { "advancedBuffs", new SortedDictionary() }, + { "ultimateBuffs", new SortedDictionary() }, + { "debuffs", new SortedDictionary() }, + { "unlocks", new SortedDictionary() }, + { "investmentBuffs", new SortedDictionary() } + }; + public static Dictionary> dumpedTravelBuffs = new() { { "advancedBuffs", new SortedDictionary() }, @@ -70,35 +79,37 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey public static void DumpTravelBuffs() { TravelMgr.Instance.GetPlantBuffUnlockCount(PlantType.DoomGatling); + dumpedTravelBuffs = new(templateTravelBuffs); + travelBuffString.Clear(); foreach (var pair in TravelDictionary.advancedBuffsText) { - dumpedTravelBuffs["advancedBuffs"].Add((int)pair.Key, pair.Value); - travelBuffString.Add(pair.Value, pair.Value); + Utils.TryAdd(dumpedTravelBuffs["advancedBuffs"], (int)pair.Key, pair.Value); + Utils.TryAdd(travelBuffString, pair.Value, pair.Value); } foreach (var pair in TravelDictionary.ultimateBuffsText) { - dumpedTravelBuffs["ultimateBuffs"].Add((int)pair.Key, pair.Value); - travelBuffString.Add(pair.Value, pair.Value); + Utils.TryAdd(dumpedTravelBuffs["ultimateBuffs"], (int)pair.Key, pair.Value); + Utils.TryAdd(travelBuffString, pair.Value, pair.Value); } foreach (var pair in TravelDictionary.debuffData) { - dumpedTravelBuffs["debuffs"].Add((int)pair.Key, pair.Value.Item1); - travelBuffString.Add(pair.Value.Item1, pair.Value.Item1); + Utils.TryAdd(dumpedTravelBuffs["debuffs"], (int)pair.Key, pair.Value.Item1); + Utils.TryAdd(travelBuffString, pair.Value.Item1, pair.Value.Item1); } foreach (var pair in TravelDictionary.unlocksText) { - dumpedTravelBuffs["unlocks"].Add((int)pair.Key, pair.Value); - travelBuffString.Add(pair.Value, pair.Value); + Utils.TryAdd(dumpedTravelBuffs["unlocks"], (int)pair.Key, pair.Value); + Utils.TryAdd(travelBuffString, pair.Value, pair.Value); } foreach (var pair in TravelMgr.InvestBuffsData) { - dumpedTravelBuffs["investmentBuffs"].Add((int)pair.Key, pair.Value.GetDescription()); - travelBuffString.Add(pair.Value.GetDescription(), pair.Value.GetDescription()); + Utils.TryAdd(dumpedTravelBuffs["investmentBuffs"], (int)pair.Key, pair.Value.GetDescription()); + Utils.TryAdd(travelBuffString, pair.Value.GetDescription(), pair.Value.GetDescription()); } File.WriteAllText(Path.Combine(GetAssetDir(AssetType.Dumps), "travel_buffs.json"), JsonSerializer.Serialize(dumpedTravelBuffs, new JsonSerializerOptions @@ -121,17 +132,15 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey } //System.Text.Json.JsonSerializer.Deserialize>>(travelBuffsContent); - public static Dictionary> GenerateTranslatedTravelBuffs(string travelBuffsContent) + public static void GenerateTranslatedTravelBuffs(string travelBuffsContent) { - TravelMgr_Patch.DumpTravelBuffs(); + if (!Core.isInitialized) return; + + DumpTravelBuffs(); - Dictionary> travelBuffs = System.Text.Json.JsonSerializer.Deserialize>>(travelBuffsContent); - - // save travel buffs - - string travelBuffsToSave = SerializeWithIndentation(travelBuffs); - string stringDir = GetAssetDir(AssetType.Strings, Utils.Language); - File.WriteAllText(Path.Combine(stringDir, "travel_buffs.json"), travelBuffsToSave); + Dictionary> travelBuffs = JsonSerializer.Deserialize>>(travelBuffsContent); + translatedTravelBuffs = travelBuffs; + travelBuffString.Clear(); foreach(var i in travelBuffs) { @@ -145,28 +154,14 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey { if (translatedBuffSet.ContainsKey(j.Key)) { - if(travelBuffString.ContainsKey(originalBuff)) - { - travelBuffString[originalBuff] = translatedBuffSet[j.Key]; - } - else - { - travelBuffString.Add(originalBuff, translatedBuffSet[j.Key]); - } + Utils.TryAdd(travelBuffString, originalBuff, translatedBuffSet[j.Key]); } } else if(j.Value == RemoveBuffName(originalBuff)) { if (translatedBuffSet.ContainsKey(j.Key)) { - if(travelBuffString.ContainsKey(originalBuff)) - { - travelBuffString[RemoveBuffName(originalBuff)] = translatedBuffSet[j.Key]; - } - else - { - travelBuffString.Add(RemoveBuffName(originalBuff), translatedBuffSet[j.Key]); - } + Utils.TryAdd(travelBuffString, originalBuff, translatedBuffSet[j.Key]); } } @@ -179,7 +174,9 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey } } - return travelBuffs; + string travelBuffsToSave = SerializeWithIndentation(TravelMgr_Patch.translatedTravelBuffs); + string stringDir = GetAssetDir(AssetType.Strings, Utils.Language); + File.WriteAllText(Path.Combine(stringDir, "travel_buffs.json"), travelBuffsToSave); } public static string MatchTravelBuff(string originalText) @@ -217,5 +214,46 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey } return res; } + + public static ValueTuple FindBuffData(string buffText) + { + ValueTuple res = new(); + foreach(var pair1 in translatedTravelBuffs) + { + foreach(var pair2 in pair1.Value) + { + if(buffText == pair2.Value) + { + res.Item1 = pair1.Key; + res.Item2 = pair2.Key; + } + } + } + + foreach(var pair1 in dumpedTravelBuffs) + { + foreach(var pair2 in pair1.Value) + { + if(buffText == pair2.Value) + { + res.Item1 = pair1.Key; + res.Item2 = pair2.Key; + } + } + } + + string finalBuffText = buffText; + if(travelBuffString.ContainsKey(buffText)) + { + finalBuffText = travelBuffString[buffText]; + } + else + { + finalBuffText = StringStore.TranslateText(buffText); + } + res.Item3 = finalBuffText; + + return res; + } } } diff --git a/MelonLoader/Patches/Modes/Super Editor/CustomMenuPage_event_Patch_B.cs b/MelonLoader/Patches/Modes/Super Editor/CustomMenuPage_event_Patch_B.cs new file mode 100644 index 0000000..b0cd093 --- /dev/null +++ b/MelonLoader/Patches/Modes/Super Editor/CustomMenuPage_event_Patch_B.cs @@ -0,0 +1,29 @@ +/*using MelonLoader; +using HarmonyLib; +using UnityEngine; +using Il2Cpp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Il2CppTMPro; +using PvZ_Fusion_Translator.AssetStore; +using Il2CppGameLevel; +using PvZ_Fusion_Translator.Patches.Modes.Super_Editor.GameLevel.EventNodes; + +namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor +{ + [HarmonyPatch(typeof(CustomMenuPage_event))] + public static class CustomMenuPage_event_Patch + { + [HarmonyPatch(nameof(CustomMenuPage_event.InitializeNodeEditor))] + [HarmonyPostfix] + public static void InitializeNodeEditor(CustomMenuPage_event __instance) + { + RuntimeEventNodeEditor_Patch.Update(__instance.nodeEditor); + NodeInfoPanel_Patch.UpdatePanelContent(__instance.nodeInfoPanel); + } + } +} +*/ \ No newline at end of file diff --git a/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeEventNodeEditor_Patch.cs b/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeEventNodeEditor_Patch.cs index 97278d9..91d43fc 100644 --- a/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeEventNodeEditor_Patch.cs +++ b/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeEventNodeEditor_Patch.cs @@ -1,96 +1,47 @@ -using Il2Cpp; -using Il2CppGameLevel; -using HarmonyLib; -using UnityEngine; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using HarmonyLib; using Il2CppGameLevel.EventNodes; using Il2CppTMPro; using PvZ_Fusion_Translator.AssetStore; +using PvZ_Fusion_Translator.Patches.BaseTextObjects; +using UnityEngine; namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor.GameLevel.EventNodes { [HarmonyPatch(typeof(RuntimeEventNodeEditor))] public static class RuntimeEventNodeEditor_Patch { - [HarmonyPatch(nameof(RuntimeEventNodeEditor.AddNode))] - [HarmonyPostfix] - public static void AddNode(RuntimeEventNodeEditor __instance) - { - TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); - - foreach(var nodeUI in __instance.nodeUIs) - { - string nodeName = nodeUI.key; - RuntimeNodeUI node = nodeUI.Value; - - foreach(TextMeshProUGUI txt in node.GetComponentsInChildren()) - { - txt.text = StringStore.translationString.ContainsKey(txt.text + "_code") ? StringStore.TranslateText(txt.text + "_code") : StringStore.TranslateText(txt.text); - txt.font = fontAsset; - } - } - } + public static TMP_FontAsset fontAsset => FontStore.LoadTMPFont(Utils.Language.ToString()); [HarmonyPatch(nameof(RuntimeEventNodeEditor.Update))] [HarmonyPostfix] public static void Update(RuntimeEventNodeEditor __instance) { - TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); - - foreach(var node in __instance.nodeUIs) + // translate the new screens (3.7) + TranslateFullTransform(__instance.nodeInfoPanel.transform); + TranslateFullTransform(__instance.attributePanel.transform); + TranslateFullTransform(__instance.canvas.transform); + if(__instance.TryGetComponent(out EventNodeContextMenu eventNodeContextMenu)) { - RuntimeNodeUI nodeUI = node.value; + TranslateFullTransform(eventNodeContextMenu.transform); + } + } + + public static void TranslateFullTransform(Transform transform) + { + if (transform == null) return; - if(nodeUI.Node.nodeType == "PlantTypeValueNode" || nodeUI.Node.nodeType == "SinglePlantTypeListNode") + foreach(TextMeshProUGUI txt in transform.GetComponentsInChildren()) + { + txt.text = RuntimeNodeUI_Patch.TranslateCodeText(txt.text); + txt.font = fontAsset; + } + + foreach(TMP_InputField inputField in transform.GetComponentsInChildren()) + { + TMP_InputField_Patch.OnEnable(inputField); + foreach(TextMeshProUGUI txt in inputField.GetComponentsInChildren(true)) { - Transform buttonTransform = nodeUI.transform.Find("Header/ValueEditContainer/Button(Clone)/Label"); - TextMeshProUGUI txt = buttonTransform.GetComponent(); - - if (!txt.gameObject.name.Contains("Label")) continue; - if (!Utils.CheckForUntranslatedText(txt.text)) continue; - - string plantName = ""; - if(nodeUI.Node.nodeType == "PlantTypeValueNode") - { - plantName = Utils.GetPlantNameFromAlmanac(nodeUI.Node.GetPortValue(nodeUI.Node.GetOutputPorts()[0].portName, PlantType.Nothing)); - } - else if(nodeUI.Node.nodeType == "SinglePlantTypeListNode") - { - SinglePlantTypeListNode tempNode = nodeUI.Node.TryCast(); - plantName = Utils.GetPlantNameFromAlmanac(tempNode.plantType); - } - txt.text = (plantName != "") ? plantName : StringStore.TranslateText(txt.text); - txt.font = fontAsset; - } - else if(nodeUI.Node.nodeType == "ZombieTypeValueNode") - { - Transform buttonTransform = nodeUI.transform.Find("Header/ValueEditContainer/Button(Clone)/Label"); - TextMeshProUGUI txt = buttonTransform.GetComponent(); - - if (!txt.gameObject.name.Contains("Label")) continue; - if (!Utils.CheckForUntranslatedText(txt.text)) continue; - - string zombieName = Utils.GetZombieNameFromAlmanac(nodeUI.Node.GetPortValue(nodeUI.Node.GetOutputPorts()[0].portName, ZombieType.Nothing)); - txt.text = (zombieName != "") ? zombieName : StringStore.TranslateText(txt.text); - txt.font = fontAsset; - } - else if(nodeUI.Node.nodeType == "PlantTypeListStorageNode") - { - Transform dropdownTransform = nodeUI.transform.Find("Header/ValueEditContainer/Dropdown(Clone)"); - - if (!dropdownTransform) continue; - - foreach(TextMeshProUGUI txt in dropdownTransform.GetComponentsInChildren()) - { - if (!Utils.CheckForUntranslatedText(txt.text)) continue; - - txt.text = StringStore.TranslateText(txt.text); - txt.font = fontAsset; - } + txt.text = RuntimeNodeUI_Patch.TranslateCodeText(txt.text); } } } diff --git a/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeNodeUI_Patch.cs b/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeNodeUI_Patch.cs index fc31045..540f243 100644 --- a/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeNodeUI_Patch.cs +++ b/MelonLoader/Patches/Modes/Super Editor/GameLevel/EventNodes/RuntimeNodeUI_Patch.cs @@ -1,12 +1,4 @@ -using Il2Cpp; -using Il2CppGameLevel; -using HarmonyLib; -using UnityEngine; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using HarmonyLib; using Il2CppGameLevel.EventNodes; using Il2CppTMPro; using PvZ_Fusion_Translator.AssetStore; @@ -18,99 +10,134 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor.GameLevel.EventNodes public static class RuntimeNodeUI_Patch { [HarmonyPatch(nameof(RuntimeNodeUI.Awake))] - [HarmonyPatch(nameof(RuntimeNodeUI.Initialize))] - [HarmonyPatch(nameof(RuntimeNodeUI.CreatePorts))] - [HarmonyPatch(nameof(RuntimeNodeUI.CreateTravelEntryEditUI), argumentTypes: [typeof(RectTransform), typeof(GetTravelEntryNode)])] - [HarmonyPatch(nameof(RuntimeNodeUI.CreateStringInputField))] [HarmonyPatch(nameof(RuntimeNodeUI.CreatePorts))] + [HarmonyPatch(nameof(RuntimeNodeUI.CreateValueEditUI))] + [HarmonyPatch(nameof(RuntimeNodeUI.GetPortUI))] + [HarmonyPatch(nameof(RuntimeNodeUI.UpdateDisplay))] + [HarmonyPatch(nameof(RuntimeNodeUI.UpdateSelectionVisual))] + [HarmonyPatch(nameof(RuntimeNodeUI.UpdateVisable))] [HarmonyPostfix] - public static void Awake(RuntimeNodeUI __instance) + public static void Post_UpdateDisplay(RuntimeNodeUI __instance) { if (!__instance.isActiveAndEnabled) return; + if (__instance.Node == null) return; + TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); - if(__instance.dropdownPrefab) - { - if(__instance.dropdownPrefab.TryGetComponent(out TMP_Dropdown dropdown)) - { - TMP_Dropdown_Patch.CreateDropdownList(dropdown); - } - } + __instance.titleText.text = TranslateCodeText(__instance.titleText.text); + __instance.titleText.font = fontAsset; - if(__instance.valueInputField) + foreach(TextMeshProUGUI txt in __instance.inputPortsContainer.GetComponentsInChildren()) { - TMP_InputField_Patch.OnEnable(__instance.valueInputField); - foreach(TextMeshProUGUI txt in __instance.valueInputField.GetComponentsInChildren(true)) - { - txt.text = StringStore.translationString.ContainsKey(txt.text + "_code") ? StringStore.TranslateText(txt.text + "_code") : StringStore.TranslateText(txt.text); - } - } - - foreach(TextMeshProUGUI txt in __instance.GetComponentsInChildren()) - { - txt.text = StringStore.translationString.ContainsKey(txt.text + "_code") ? StringStore.TranslateText(txt.text + "_code") : StringStore.TranslateText(txt.text); + txt.text = TranslateCodeText(txt.text); txt.font = fontAsset; } - } - [HarmonyPatch(nameof(RuntimeNodeUI.UpdateDisplay))] - [HarmonyPostfix] - public static void UpdateDisplay(RuntimeNodeUI __instance) - { - if (!__instance.isActiveAndEnabled) return; - - TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); - - if(__instance.Node.nodeType == "PlantTypeValueNode" || __instance.Node.nodeType == "SinglePlantTypeListNode") + foreach(TextMeshProUGUI txt in __instance.outputPortsContainer.GetComponentsInChildren()) { - foreach(TextMeshProUGUI txt in __instance.GetComponentsInChildren()) + txt.text = TranslateCodeText(txt.text); + txt.font = fontAsset; + } + + if ( + __instance.GetComponentsInChildren().Count == 0 + && __instance.GetComponentsInChildren().Count == 0 + && __instance.Node.nodeType != "PlantTypeValueNode" + && __instance.Node.nodeType != "SinglePlantTypeListNode" + && __instance.Node.nodeType != "ZombieTypeValueNode" + ) return; + + if (__instance.Node != null) + { + if(__instance.Node.nodeType == "PlantTypeValueNode") { - if(txt.gameObject.name.Contains("Label")) + foreach(TextMeshProUGUI txt in __instance.GetComponentsInChildren()) { - string plantName = Utils.GetPlantNameFromAlmanac(__instance.Node.GetPortValue(__instance.Node.nodeName, PlantType.Nothing)); - txt.text = (plantName != "") ? plantName : StringStore.TranslateText(txt.text); - txt.font = fontAsset; + if(txt.gameObject.name.Contains("Label")) + { + if (Utils.CheckForUntranslatedText(txt.text)) + { + PlantTypeValueNode castedNode = __instance.Node.TryCast(); + if (castedNode != null) + { + string portName = castedNode.value_PortName; + string plantName = Utils.GetPlantNameFromAlmanac(castedNode.value, removeTags: true); + txt.text = (plantName != "") ? plantName : StringStore.TranslateText(txt.text); + txt.font = fontAsset; + } + } + } + } + } + + if(__instance.Node.nodeType == "SinglePlantTypeListNode") + { + foreach(TextMeshProUGUI txt in __instance.GetComponentsInChildren()) + { + if (txt.gameObject.name.Contains("Label")) + { + if (Utils.CheckForUntranslatedText(txt.text)) + { + SinglePlantTypeListNode castedNode = __instance.Node.TryCast(); + if (castedNode != null) + { + string portName = castedNode.plantTypeList_PortName; + string plantName = Utils.GetPlantNameFromAlmanac(castedNode.plantType, removeTags: true); + txt.text = (plantName != "") ? plantName : StringStore.TranslateText(txt.text); + txt.font = fontAsset; + } + } + } + } + } + + if(__instance.Node.nodeType == "ZombieTypeValueNode") + { + foreach(TextMeshProUGUI txt in __instance.GetComponentsInChildren()) + { + if(txt.gameObject.name.Contains("Label")) + { + if(Utils.CheckForUntranslatedText(txt.text)) + { + ZombieTypeValueNode castedNode = __instance.Node.TryCast(); + if(castedNode != null) + { + string portName = castedNode.value_PortName; + string zombieName = Utils.GetZombieNameFromAlmanac(castedNode.value, removeTags: true); + txt.text = (zombieName != "") ? zombieName : StringStore.TranslateText(txt.text); + txt.font = fontAsset; + } + } + } } } } - if(__instance.Node.nodeType == "ZombieTypeValueNode") + if(__instance.GetComponentsInChildren().Count > 0) { - foreach(TextMeshProUGUI txt in __instance.GetComponentsInChildren()) - { - if(txt.gameObject.name.Contains("Label")) - { - string zombieName = Utils.GetZombieNameFromAlmanac(__instance.Node.GetPortValue(__instance.Node.nodeName, ZombieType.Nothing)); - txt.text = (zombieName != "") ? zombieName : StringStore.TranslateText(txt.text); - txt.font = fontAsset; - } - } - } - - if(__instance.dropdownPrefab) - { - if(__instance.dropdownPrefab.TryGetComponent(out TMP_Dropdown dropdown)) + foreach(TMP_Dropdown dropdown in __instance.GetComponentsInChildren()) { TMP_Dropdown_Patch.CreateDropdownList(dropdown); } } - if(__instance.valueInputField) + if(__instance.GetComponentsInChildren().Count > 0) { - TMP_InputField_Patch.OnEnable(__instance.valueInputField); - foreach(TextMeshProUGUI txt in __instance.valueInputField.GetComponentsInChildren(true)) + foreach(TMP_InputField inputField in __instance.GetComponentsInChildren()) { - txt.text = StringStore.translationString.ContainsKey(txt.text + "_code") ? StringStore.TranslateText(txt.text + "_code") : StringStore.TranslateText(txt.text); + TMP_InputField_Patch.OnEnable(inputField); + foreach(TextMeshProUGUI txt in inputField.GetComponentsInChildren(true)) + { + txt.text = TranslateCodeText(txt.text); + } } } } - - [HarmonyPatch(nameof(RuntimeNodeUI.UpdateSelectionVisual))] - [HarmonyPostfix] - public static void UpdateSelectionVisual(RuntimeNodeUI __instance) + + public static string TranslateCodeText(string originalText) { - UpdateDisplay(__instance); + return StringStore.translationString.ContainsKey(originalText + "_code") ? StringStore.TranslateText(originalText + "_code") : StringStore.TranslateText(originalText); } } } diff --git a/MelonLoader/PvZ Fusion Translator [MelonLoader].csproj b/MelonLoader/PvZ Fusion Translator [MelonLoader].csproj index 642359a..8eb8423 100644 --- a/MelonLoader/PvZ Fusion Translator [MelonLoader].csproj +++ b/MelonLoader/PvZ Fusion Translator [MelonLoader].csproj @@ -13,7 +13,7 @@ latest enable Debug;DebugEnglish;ReleaseMulti;ReleaseEnglish;DebugTesting;ReleaseOther;DebugObfuscate;ReleaseObfuscate - C:\Users\rainb\Desktop\PC_PVZ-Fusion_3.6.1 + C:\Users\rainb\Desktop\PC_PVZ-Fusion_3.7 AnyCPU;ARM64;x64 @@ -31,7 +31,6 @@ - @@ -305,6 +304,9 @@ $(GamePath)\MelonLoader\net6\Il2CppInterop.Common.dll + + $(GamePath)\MelonLoader\Il2CppAssemblies\Il2CppCore.dll + @@ -404,19 +406,26 @@ + + + + + + + @@ -515,13 +524,14 @@ + - + @@ -565,11 +575,11 @@ - + - + diff --git a/MelonLoader/Resources/ModUpdateUtil.exe b/MelonLoader/Resources/ModUpdateUtil.exe deleted file mode 100644 index fa4a7468eb005a9a5af1265cc2a7f573d39d10ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129464 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

eIUrHxlqKwKqKafw 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 + // Remove opening size tags like string withoutOpenTags = Regex.Replace(text, @"]+>", string.Empty, RegexOptions.IgnoreCase); - // Remove closing color tags like + // Remove closing size tags like string withoutCloseTags = Regex.Replace(withoutOpenTags, @"", string.Empty, RegexOptions.IgnoreCase); return withoutCloseTags; } + public static string RemoveFormatTags(string text) + { + if (string.IsNullOrEmpty(text)) + return text ?? string.Empty; + + string withoutUnderlineTags = Regex.Replace(text, @"", string.Empty, RegexOptions.IgnoreCase); + string withoutUnderlineCloseTags = Regex.Replace(withoutUnderlineTags, @"", string.Empty, RegexOptions.IgnoreCase); + string withoutItalicTags = Regex.Replace(withoutUnderlineCloseTags, @"", string.Empty, RegexOptions.IgnoreCase); + string withoutItalicCloseTags = Regex.Replace(withoutItalicTags, @"", string.Empty, RegexOptions.IgnoreCase); + + return withoutItalicCloseTags; + } + + public static string RemoveAllTags(string text) + { + if (string.IsNullOrEmpty(text)) + return text ?? string.Empty; + + return RemoveColorTags(RemoveSizeTags(RemoveFormatTags(text))); + } + public static GameObject ConvertToTextMeshProUGUI(GameObject originalText, Transform parent, string name) { GameObject newObj = new GameObject(name); @@ -152,7 +174,7 @@ namespace PvZ_Fusion_Translator newGoBackText.color = color; } - public static string GetPlantNameFromAlmanac(PlantType thePlantType, bool log = false) + public static string GetPlantNameFromAlmanac(PlantType thePlantType, bool removeTags = false, bool log = false) { bool foundPlantName = false; string thePlantName = ""; @@ -172,18 +194,20 @@ namespace PvZ_Fusion_Translator thePlantName = ""; } + thePlantName = (removeTags) ? RemoveAllTags(thePlantName) : thePlantName; + return thePlantName; } - public static string GetPlantNameFromAlmanac(string theOriginalPlantName, bool log = false) + public static string GetPlantNameFromAlmanac(string theOriginalPlantName, bool removeTags = false, bool log = false) { bool foundPlantName = false; string thePlantName = ""; - if(plantIndiceStrings.ContainsKey(theOriginalPlantName)) + if(plantIndiceString.ContainsKey(theOriginalPlantName)) { foundPlantName = true; - thePlantName = plantIndiceStrings[theOriginalPlantName]; + thePlantName = plantIndiceString[theOriginalPlantName]; } if (!foundPlantName) @@ -195,10 +219,12 @@ namespace PvZ_Fusion_Translator thePlantName = ""; } + thePlantName = (removeTags) ? RemoveAllTags(thePlantName) : thePlantName; + return thePlantName; } - public static string GetZombieNameFromAlmanac(ZombieType theZombieType) + public static string GetZombieNameFromAlmanac(ZombieType theZombieType, bool removeTags = false) { string json; string theZombieName = ""; @@ -216,16 +242,18 @@ namespace PvZ_Fusion_Translator } } + theZombieName = (removeTags) ? RemoveAllTags(theZombieName) : theZombieName; + return theZombieName; } public static Dictionary> plantIndices = new Dictionary>(); - public static Dictionary plantIndiceStrings = new Dictionary(); + public static Dictionary plantIndiceString = new Dictionary(); public static void RegisterPlantIndices() { plantIndices = new Dictionary>(); - plantIndiceStrings = new Dictionary(); + plantIndiceString = new Dictionary(); string originalJson; string translatedJson; @@ -241,37 +269,110 @@ namespace PvZ_Fusion_Translator } else { - originalJson = File.ReadAllText(originalPath); - translatedJson = AlmanacPlantMenu_Patch.almanacJson; - Il2CppAlmanacData.AlmanacData originalPlantData = JsonUtility.FromJson(originalJson); - Il2CppAlmanacData.AlmanacData translatedPlantData = JsonUtility.FromJson(translatedJson); - - for (int i = 0; i < originalPlantData.plants.Count; i++) + try { - Il2CppAlmanacData.PlantInfo originalPlantInfo = originalPlantData.plants[i]; - if(!plantIndiceStrings.ContainsKey(originalPlantInfo.name)) - { - plantIndiceStrings.Add(originalPlantInfo.name, originalPlantInfo.name); - } - Il2CppAlmanacData.PlantInfo translatedPlantInfo = null; + originalJson = File.ReadAllText(originalPath); + translatedJson = AlmanacPlantMenu_Patch.almanacJson; + Il2CppAlmanacData.AlmanacData originalPlantData = JsonUtility.FromJson(originalJson); + Il2CppAlmanacData.AlmanacData translatedPlantData = JsonUtility.FromJson(translatedJson); - foreach (Il2CppAlmanacData.PlantInfo info in translatedPlantData.plants) + foreach (Il2CppAlmanacData.PlantInfo originalPlantInfo in originalPlantData.plants) { - if (info.seedType == originalPlantInfo.seedType) + if(!plantIndiceString.ContainsKey(originalPlantInfo.name)) { - translatedPlantInfo = info; + plantIndiceString.Add(originalPlantInfo.name, originalPlantInfo.name); + } + Il2CppAlmanacData.PlantInfo translatedPlantInfo = null; + + foreach (Il2CppAlmanacData.PlantInfo info in translatedPlantData.plants) + { + if (info.seedType == originalPlantInfo.seedType) + { + translatedPlantInfo = info; + } + } + + if (translatedPlantInfo != null) + { + if(translatedPlantInfo.name != null) + { + KeyValuePair temp = new KeyValuePair(translatedPlantInfo.seedType, translatedPlantInfo.name); + plantIndices.Add(originalPlantInfo.seedType, temp); + plantIndiceString[originalPlantInfo.name] = translatedPlantInfo.name; + } } } - - if (translatedPlantInfo != null) - { - KeyValuePair temp = new KeyValuePair(translatedPlantInfo.seedType, translatedPlantInfo.name); - plantIndices.Add(originalPlantInfo.seedType, temp); - plantIndiceStrings[originalPlantInfo.name] = translatedPlantInfo.name; - } + } + catch (Exception e) + { + Log.LogError(e); } } } + + public static void TryAdd(Dictionary dict, A key, B val) + { + if (dict.ContainsKey(key)) + { + dict[key] = val; + } + else + { + dict.Add(key, val); + } + } + + public static void TryAdd(SortedDictionary dict, A key, B val) + { + if (dict.ContainsKey(key)) + { + dict[key] = val; + } + else + { + dict.Add(key, val); + } + } + + public static AssetBundle GetAssetBundle(Assembly assembly, string assetBundlePath, bool log = false) + { + try + { + if (assembly != null) + { + Stream manifestResourceStream = assembly.GetManifestResourceStream($"PvZ_Fusion_Translator.Resources.{assetBundlePath}"); + using (manifestResourceStream) + { + if (manifestResourceStream == null) + { + throw new Exception("Stream was null."); + } + + byte[] array = new byte[manifestResourceStream.Length]; + manifestResourceStream.Read(array, 0, array.Length); + AssetBundle assetBundle = AssetBundle.LoadFromMemory(array); + ArgumentNullException.ThrowIfNull(assetBundle, "assetBundle"); + if(log) + { + Log.LogDebug($"AssetBundle {assetBundlePath} has successfully been loaded."); + } + return assetBundle; + } + } + + throw new Exception($"AssetBundle {assetBundlePath} was null."); + } + catch (Exception value) + { + throw new ArgumentException($"Failed to load {assetBundlePath}. \n{value}"); + } + } + + public static AssetBundle GetAssetBundle(string path) + { + AssetBundle assetBundle = AssetBundle.LoadFromFile(path); + return assetBundle; + } /*public static Dictionary> recipeLinks = new Dictionary>(); @@ -316,12 +417,13 @@ namespace PvZ_Fusion_Translator } }*/ + public static Regex untranslatedRegex = new("\\p{IsCJKUnifiedIdeographs}+"); + public static bool CheckForUntranslatedText(string text) { - Regex regex = new("\\p{IsCJKUnifiedIdeographs}+"); - try + try { - Match match = regex.Match(text); + Match match = untranslatedRegex.Match(text); return match.Success; } catch(Exception e) @@ -346,6 +448,7 @@ namespace PvZ_Fusion_Translator confirmButton.clickEvent = new UnityEvent(); confirmButton.clickEvent.AddListener(new Action(() => SwapLocalData())); + confirmButton.clickEvent.AddListener(new Action(() => CursorChange.SetDefaultCursor())); confirmButton.clickEvent.AddListener(new Action(() => GameAPP.UIManager.Pop())); } @@ -356,6 +459,7 @@ namespace PvZ_Fusion_Translator ChangeLanguage(Utils.Language.ToString()); string sourceMsg = "" + (!(Utils.useLocal) ? "Translation Source:\nOnline" : "Translation Source:\nLocal"); OptLanguageBtn_Patch.FlashMessage(OptLanguageBtn_Patch.toggleSlots[2], sourceMsg, 0.1f, false); + CursorChange.SetDefaultCursor(); } public static async Task GetDataFromWeb(string url, bool isLog = true) @@ -560,8 +664,8 @@ namespace PvZ_Fusion_Translator // Slovak, //Polish, //Turkish, - //Arabic, - Romanian, + Arabic, + Romanian, LANG_END } @@ -578,6 +682,7 @@ namespace PvZ_Fusion_Translator { LanguageEnum.Japanese, "日本語"}, { LanguageEnum.Korean, "한국어"}, { LanguageEnum.Ukrainian, "українська"}, + { LanguageEnum.Arabic, "ﺔﻴﺑﺮﻌﻟا"}, { LanguageEnum.Romanian, "Română"} }; diff --git a/PvZ Fusion Translator.sln b/PvZ Fusion Translator.sln index b7a6813..7b38d36 100644 --- a/PvZ Fusion Translator.sln +++ b/PvZ Fusion Translator.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.10.35122.118 +# Visual Studio Version 18 +VisualStudioVersion = 18.7.11911.148 stable MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}" ProjectSection(SolutionItems) = preProject