3.5 code changes + live update level list

This commit is contained in:
SillyStar-Github 2026-03-29 04:18:29 -04:00
parent 829f314392
commit d39d048259
23 changed files with 861 additions and 155 deletions

1
.gitignore vendored
View file

@ -252,3 +252,4 @@ MelonLoader/bin/Debug/net6.0/AsmResolver.PE.dll
MelonLoader/bin/Debug/net6.0/AsmResolver.DotNet.dll
MelonLoader/bin/Debug/net6.0/AsmResolver.dll
MelonLoader/bin/Debug/net6.0/0Harmony.dll
/MelonLoader/bin

View file

@ -5,6 +5,7 @@ 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;
@ -82,10 +83,16 @@ namespace PvZ_Fusion_Translator
Utils.OpenTrello();
}
if(TowerManager.Instance != null)
TowerManager_Patch.UpdateText();
if (CustomLevelMenu_Patch.requestTimer > 0)
{
TowerManager_Patch.UpdateText();
}
CustomLevelMenu_Patch.requestTimer -= Time.deltaTime;
if(CustomLevelMenu_Patch.requestTimer < 0)
{
CustomLevelMenu_Patch.requestTimer = 0;
}
}
}
public override void OnGUI()

View file

@ -116,10 +116,6 @@ namespace PvZ_Fusion_Translator
}
#if MULTI_LANGUAGE
if (Utils.Language != Utils.LanguageEnum.English)
{
LoadLocalizedTextures(Utils.LanguageEnum.English);
}
LoadLocalizedTextures(Utils.Language);
LoadLocalizedSprites(Utils.Language);
#else

View file

@ -1,5 +1,6 @@
//#if DEBUG
using PvZ_Fusion_Translator.AssetStore;
using PvZ_Fusion_Translator.Patches.Modes.Super_Editor;
using UnityEngine;
namespace PvZ_Fusion_Translator
@ -10,6 +11,7 @@ namespace PvZ_Fusion_Translator
{
ReloadStrings,
ReloadTextures,
SwapLevelData
}
private class ModFeature
@ -42,6 +44,14 @@ namespace PvZ_Fusion_Translator
Core.ShowToast("Textures Reloaded!");
return;
}
if(this.ModType == ModType.SwapLevelData)
{
CustomLevelMenu_Patch.SwapLevelData();
string toast = (CustomLevelMenu_Patch.useLocal) ? "Using Local Data..." : "Using Online Data...";
Core.ShowToast(toast);
return;
}
}
}
@ -49,6 +59,7 @@ namespace PvZ_Fusion_Translator
{
{ModType.ReloadStrings,new ModFeature("Reload Strings",ModType.ReloadStrings,KeyCode.Home, true)},
{ModType.ReloadTextures,new ModFeature("Reload Textures",ModType.ReloadTextures,KeyCode.End, true)},
{ModType.SwapLevelData,new ModFeature("Swap Level Data Type",ModType.SwapLevelData,KeyCode.PageUp, true)},
};

View file

@ -14,68 +14,224 @@ namespace PvZ_Fusion_Translator.Patches.BaseTextObjects
[HarmonyPatch(typeof(InGameText))]
public static class InGameText_Patch
{
public static string currentText = "";
[HarmonyPatch(nameof(InGameText.ShowText))]
[HarmonyPostfix]
public static void ShowText(InGameText __instance)
{
TranslateInGameText(__instance);
}
[HarmonyPatch(nameof(InGameText.DebugWarning))]
[HarmonyPostfix]
public static void DebugWarning(InGameText __instance)
{
TranslateInGameText(__instance);
}
[HarmonyPatch(nameof(InGameText.Update))]
[HarmonyPostfix]
public static void Update(InGameText __instance)
{
TranslateInGameText(__instance);
}
public static void TranslateInGameText(InGameText __instance)
{
if (__instance.textMesh == null) return;
TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString());
foreach (TextMeshProUGUI txt in __instance.GetComponentsInChildren<TextMeshProUGUI>())
currentText = __instance.textMesh.text;
TextMeshProUGUI txt = __instance.textMesh;
string originalText = txt.text;
string travelMatch = TravelMgr_Patch.MatchTravelBuff(originalText);
int godsGachaCheck = CheckGodsGachaPopup(originalText);
string godsGachaMatch = "";
string superEditorPlantMatch = CheckSuperEditorPopup(originalText);
switch(godsGachaCheck)
{
string originalText = txt.text;
string travelMatch = TravelMgr_Patch.MatchTravelBuff(originalText);
if (txt.gameObject.name.Contains("shadow"))
{
continue;
}
if (travelMatch != "")
{
txt.text = travelMatch;
if (txt.gameObject.name.Contains("main"))
{
originalText = txt.text;
Transform shadowText = txt.transform.parent.Find("Text_shadow");
if (shadowText != null)
{
shadowText.GetComponent<TextMeshProUGUI>().text = Utils.RemoveColorTags(originalText);
}
}
}
else if((Regex.Match(txt.text, @"(<color[^>]*>.*?</color>)", RegexOptions.Singleline).Success) && !StringStore.translationString.ContainsKey(txt.text))
{
txt.text = StringStore.TranslateColorText(txt.text, true);
if (txt.gameObject.name.Contains("main"))
{
originalText = txt.text;
Transform shadowText = txt.transform.parent.Find("Text_shadow");
if (shadowText != null)
{
shadowText.GetComponent<TextMeshProUGUI>().text = Utils.RemoveColorTags(originalText);
}
}
}
else
{
txt.text = StringStore.TranslateText(txt.text, true);
if (txt.gameObject.name.Contains("main"))
{
originalText = txt.text;
Transform shadowText = txt.transform.parent.Find("Text_shadow");
if (shadowText != null)
{
shadowText.GetComponent<TextMeshProUGUI>().text = Utils.RemoveColorTags(originalText);
}
}
}
txt.font = fontAsset;
case 1:
case 2:
case 3:
godsGachaMatch = TranslateGodsGachaPopup(originalText, godsGachaCheck);
break;
default:
break;
}
if(__instance.TryGetComponent<TextMeshProUGUI>(out TextMeshProUGUI text))
if(godsGachaMatch != "")
{
text.text = StringStore.TranslateText(text.text);
txt.text = godsGachaMatch;
if (txt.gameObject.name.Contains("main"))
{
originalText = txt.text;
Transform shadowText = txt.transform.parent.Find("Text_shadow");
if (shadowText != null)
{
shadowText.GetComponent<TextMeshProUGUI>().text = Utils.RemoveColorTags(originalText);
}
}
}
else if (travelMatch != "")
{
txt.text = travelMatch;
if (txt.gameObject.name.Contains("main"))
{
originalText = txt.text;
Transform shadowText = txt.transform.parent.Find("Text_shadow");
if (shadowText != null)
{
shadowText.GetComponent<TextMeshProUGUI>().text = Utils.RemoveColorTags(originalText);
}
}
}
else if(superEditorPlantMatch != "")
{
txt.text = superEditorPlantMatch;
}
else if((Regex.Match(txt.text, @"(<color[^>]*>.*?</color>)", RegexOptions.Singleline).Success) && !StringStore.translationString.ContainsKey(txt.text))
{
txt.text = StringStore.TranslateColorText(txt.text, true);
if (txt.gameObject.name.Contains("main"))
{
originalText = txt.text;
Transform shadowText = txt.transform.parent.Find("Text_shadow");
if (shadowText != null)
{
shadowText.GetComponent<TextMeshProUGUI>().text = Utils.RemoveColorTags(originalText);
}
}
}
else
{
txt.text = StringStore.TranslateText(txt.text, true);
if (txt.gameObject.name.Contains("main"))
{
originalText = txt.text;
Transform shadowText = txt.transform.parent.Find("Text_shadow");
if (shadowText != null)
{
shadowText.GetComponent<TextMeshProUGUI>().text = Utils.RemoveColorTags(originalText);
}
}
}
txt.font = fontAsset;
}
}
public static string namePlantedPattern = "种植了:([^\\s]+)";
public static string nameUpgradedPattern = "升级成功:([^\\s]+) -> ([^\\s]+)";
public static string nameAlreadyPlantedPattern = "场上已经有一个([^\\s]+)了";
public static Dictionary<string, string> fallbackFStrs = new Dictionary<string, string>()
{
{"种植了:([^\\s]+)", "{0} planted!"},
{"升级成功:([^\\s]+) -> ([^\\s]+)", "{0} successfully upgraded to {1}!"},
{"场上已经有一个([^\\s]+)了", "{0} already exists on the lawn!"},
{"已移除路线:([^\\s]+)", "Upgrade Path removed for {0}" },
{ @"([^\s]+)\((\d+)\)", "{0}({1})" }
};
public static int CheckGodsGachaPopup(string originalText)
{
int res = 0;
if (Regex.IsMatch(originalText, namePlantedPattern))
{
res = 1;
}
else if(Regex.IsMatch(originalText, nameUpgradedPattern))
{
res = 2;
}
else if(Regex.IsMatch(originalText, nameAlreadyPlantedPattern))
{
res = 3;
}
return res;
}
public static string TranslateGodsGachaPopup(string originalText, int type)
{
string res = originalText;
switch(type)
{
case 1:
res = TranslatePlantNameParts(originalText, namePlantedPattern);
break;
case 2:
res = TranslatePlantNameParts(originalText, nameUpgradedPattern);
break;
case 3:
res = TranslatePlantNameParts(originalText, nameAlreadyPlantedPattern);
break;
default:
break;
}
return res;
}
public static string TranslatePlantNameParts(string originalText, string pattern)
{
string res = "";
string fStr = "";
Match match = Regex.Match(originalText, pattern);
if(match.Success)
{
GroupCollection groups = match.Groups;
fStr = StringStore.translationStringRegex.ContainsKey(pattern) ? StringStore.translationStringRegex[pattern] : fallbackFStrs[pattern];
List<string> dynamicParts = [];
for(int i = 1; i < groups.Count; i++)
{
string group = groups[i].Value;
string plantName = Utils.GetPlantNameFromAlmanac(group);
dynamicParts.Add(plantName);
}
res = string.Format(fStr, [.. dynamicParts]);
}
return res;
}
public static string CheckSuperEditorPopup(string originalText)
{
string res = "";
if(Regex.IsMatch(originalText, "已移除路线:([^\\s]+)"))
{
res = TranslatePlantNameParts(originalText, "已移除路线:([^\\s]+)");
}
else if(Regex.IsMatch(originalText, "当前全部基础植物:(.*)"))
{
string pattern = "当前全部基础植物:(.*)";
Match match = Regex.Match(originalText, pattern);
if(match.Success)
{
GroupCollection groups = match.Groups;
string plantNameGroup = groups[1].Value;
string[] plantNames = plantNameGroup.Split(" ");
string plantRes = "";
string shortPattern = @"([^\s]+)\((\d+)\)";
foreach (string plantName in plantNames)
{
Match shortMatch = Regex.Match(plantName, shortPattern);
if(shortMatch.Success)
{
string fStr = StringStore.translationStringRegex.ContainsKey(shortPattern) ? StringStore.translationStringRegex[shortPattern] : fallbackFStrs[shortPattern];
string shortRes = string.Format(fStr, [Utils.GetPlantNameFromAlmanac(shortMatch.Groups[1].Value), shortMatch.Groups[2].Value]);
plantRes += shortRes + " ";
}
}
res = StringStore.TranslateText("当前全部基础植物:" + plantRes);
}
}
return res;
}
}
}

View file

@ -53,7 +53,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
ZombieDataManager.ZombieData zombieData = zombieDataDic[__instance.currentZombieType];
string spawnInfo = "";
string spawnInfoFormat = StringStore.translationStringRegex["出怪等级: (\\d+)\n出怪权重: (\\d+)"];
string spawnInfoFormat = StringStore.translationStringRegex.ContainsKey("出怪等级: (\\d+)\n出怪权重: (\\d+)") ? StringStore.translationStringRegex["出怪等级: (\\d+)\n出怪权重: (\\d+)"] : null;
if(spawnInfoFormat != null)
{
spawnInfo = string.Format(spawnInfoFormat, [zombieData.summonLevel, zombieData.summonWeight]) + "\n\n";

View file

@ -29,6 +29,11 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
NoticePauseMenu_Patch.Post_Awake(__instance);
}
if(__instance.name.StartsWith("MainMenu(Clone)"))
{
MainMenu_Patch.AnimOver(__instance);
}
TranslateBaseMenu(__instance, "Level");
TranslateBaseMenu(__instance, "Levels");
TranslateBaseMenu(__instance, "CustomLevel");

View file

@ -7,6 +7,7 @@ using PvZ_Fusion_Translator;
using PvZ_Fusion_Translator.AssetStore;
using UnityEngine;
using UnityEngine.UIElements;
using static MelonLoader.MelonLogger;
using Object = UnityEngine.Object;
public class OptionButtonData
@ -58,6 +59,11 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
for (int i = 0; i < 6; i++)
{
var newButton = Object.Instantiate(templateButton, templateButton.transform.parent);
newButton.tag = "LangOpt";
foreach(Transform child in newButton.GetComponentsInChildren<Transform>())
{
child.tag = "LangOpt";
}
newButton.optionType = 80 + i;
float yPos = startY - i * ySpacing;
@ -90,6 +96,11 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
for (int i = 0; i < 2; i++)
{
var newButton = Object.Instantiate(templateButton, templateButton.transform.parent);
newButton.tag = "LangOpt";
foreach(Transform child in newButton.GetComponentsInChildren<Transform>())
{
child.tag = "LangOpt";
}
newButton.optionType = 100 + i;
float yPos = toggleStartY - i * ySpacing;
@ -125,7 +136,8 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
data.Language = lang;
data.IsNextButton = false;
btn.gameObject.SetActive(true);
UpdateButtonText(btn, lang.ToString());
UpdateButtonText(btn, Utils.LanguageNames[lang], FontStore.LoadTMPFont(lang.ToString()));
}
else
{
@ -148,7 +160,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
data.Toggle = toggle;
data.IsNextButton = false;
btn.gameObject.SetActive(true);
UpdateButtonText(btn, toggle.ToString());
UpdateButtonText(btn, StringStore.TranslateText(toggle.ToString()), FontStore.LoadTMPFont(Utils.Language.ToString()));
}
else
{
@ -168,7 +180,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
UpdateButtonText(nextBtn, "Next");
}
public static void UpdateButtonText(OptionBtn button, string languageName)
public static void UpdateButtonText(OptionBtn button, string languageName, TMP_FontAsset fontAsset = null)
{
TMP_FontAsset defaultAsset = FontStore.LoadTMPFont("English");
@ -192,7 +204,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
text.text = languageName;
}
text.fontSize = 16;
text.font = defaultAsset;
text.font = (fontAsset != null) ? fontAsset : defaultAsset;
text.autoSizeTextContainer = false;
}
}
@ -257,17 +269,15 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
[HarmonyPatch(typeof(OptionBtn))]
public static class OptLangBtn_Patch
{
[HarmonyPatch(nameof(OptionBtn.Awake))]
[HarmonyPostfix]
private static void Awake(OptionBtn __instance)
public static void Awake(OptionBtn __instance)
{
if (!buttonsCreated || cachedTemplateButton == null)
{
buttonsCreated = false;
togglesCreated = false;
CreateLanguageButtons(__instance);
CreateLanguageButtons(__instance);
CreateToggleButtons(__instance);
}
}
}
[HarmonyPatch("OnMouseUpAsButton")]

View file

@ -0,0 +1,24 @@
using HarmonyLib;
using Il2Cpp;
using Il2CppTMPro;
using PvZ_Fusion_Translator.AssetStore;
using UnityEngine;
using System.Text.RegularExpressions;
namespace PvZ_Fusion_Translator.Patches.GameObjects
{
[HarmonyPatch(typeof(ConfirmMenu))]
public static class ConfirmMenu_Patch
{
[HarmonyPatch(nameof(ConfirmMenu.SetTitle))]
[HarmonyPostfix]
private static void SetTitle(ConfirmMenu __instance)
{
foreach(TextMeshProUGUI title in __instance.title)
{
title.text = StringStore.TranslateText(title.text);
title.font = FontStore.LoadTMPFont(Utils.Language.ToString());
}
}
}
}

View file

@ -0,0 +1,26 @@
using HarmonyLib;
using Il2Cpp;
using Il2CppTMPro;
using MelonLoader;
using PvZ_Fusion_Translator.AssetStore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace PvZ_Fusion_Translator.Patches.GameObjects
{
[HarmonyPatch(typeof(LoseMenu))]
public static class LoseMenu_Patch
{
public static void TranslateLoseMenu(BaseMenu __instance)
{
if(__instance.TryGetComponent<LoseMenu>(out LoseMenu loseMenu))
{
loseMenu.title.text = StringStore.TranslateText(loseMenu.title.text);
}
}
}
}

View file

@ -1,24 +1,106 @@
//using HarmonyLib;
//using Il2Cpp;
//using PvZ_Fusion_Translator.AssetStore;
//using UnityEngine;
using HarmonyLib;
using Il2Cpp;
using Il2CppTMPro;
using PvZ_Fusion_Translator.AssetStore;
using PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
//namespace PvZ_Fusion_Translator.Patches.GameObjects
//{
// [HarmonyPatch(typeof(MainMenu))]
// public class MainMenu_Patch
// {
// private static bool _warningShown = false;
namespace PvZ_Fusion_Translator.Patches.GameObjects
{
public class MainMenu_Patch
{
public static void AnimOver(BaseMenu __instance)
{
Transform changelogObj = __instance.transform.Find("Grave/UpdateInfo");
Transform langObj = UnityEngine.Object.Instantiate(changelogObj, changelogObj.position + new Vector3(0.0f, 0.855f), changelogObj.rotation, __instance.transform.Find("Grave"));
langObj.name = "Languages";
UIButton langBtn = langObj.GetComponent<UIButton>();
// [HarmonyPostfix]
// [HarmonyPatch(nameof(MainMenu.AnimOver))]
// private static void AnimOver(MainMenu __instance)
// {
// if (_warningShown || NoticeMenu.Instance == null)
// return;
foreach(TextMeshProUGUI text in langObj.GetComponentsInChildren<TextMeshProUGUI>())
{
text.text = StringStore.TranslateText("Languages");
}
// WarningStore.WarningReload(Utils.Language, NoticeMenu.Instance);
// _warningShown = true;
// }
// }
//}
langBtn.clickEvent = new UnityEvent();
langBtn.clickEvent.AddListener(new Action(() => OpenLanguageMenu(langBtn.gameObject)));
langBtn.clickEvent.AddListener(new Action(() => HideLangButton(langBtn.gameObject)));
}
public static void OpenLanguageMenu(GameObject langBtn)
{
UIMgr.EnterOptionMenu();
GameObject newOptionsMenu = GameAPP.canvasUp.Find("OptionMenu(Clone)").gameObject;
newOptionsMenu.name = "LanguageMenu";
List<string> toKeep = new List<string>() { "Background", "F", "Goback"};
foreach(Transform child in newOptionsMenu.GetComponentsInChildren<Transform>())
{
if(!toKeep.Contains(child.name) && !toKeep.Contains(child.parent.name) && child.name != "LanguageMenu")
{
child.gameObject.SetActive(false);
}
}
OptionBtn templateButton = newOptionsMenu.transform.Find("F").GetComponent<OptionBtn>();
OptLanguageBtn_Patch.OptLangBtn_Patch.Awake(templateButton);
UIButton goBackButton = newOptionsMenu.transform.Find("Goback").GetComponent<UIButton>();
goBackButton.clickEvent.AddListener(new Action(() => ShowLangButton(langBtn.gameObject)));
newOptionsMenu.transform.Find("F").gameObject.SetActive(false);
foreach (TextMeshProUGUI text in newOptionsMenu.transform.Find("Background").GetComponentsInChildren<TextMeshProUGUI>())
{
text.text = StringStore.TranslateText("Language Settings");
}
/*GameObject optionMenuAnchor = UnityEngine.Object.Instantiate(assetBundle.LoadAsset("OptionMenu").TryCast<GameObject>(), GameAPP.canvasUp);
optionMenuAnchor.transform.SetParent(newOptionsMenu.transform, false);
Transform messageAnchor = optionMenuAnchor.transform.Find("MessageAnchor");
Transform plantAnchor = optionMenuAnchor.transform.Find("PlantAnchor");
System.Random random = new System.Random();
var plantTypes = Enum.GetValues(typeof(PlantType));
PlantType randomPlantChoice = (PlantType)plantTypes.GetValue(random.Next(plantTypes.Length));
if(!GameAPP.resourcesManager.plantPrefabs.ContainsKey(randomPlantChoice) || GameAPP.resourcesManager.plantPrefabs[randomPlantChoice] == null)
{
randomPlantChoice = (PlantType)plantTypes.GetValue(random.Next(plantTypes.Length));
}
if (GameAPP.resourcesManager.plantPrefabs[randomPlantChoice] != null)
{
GameObject plant = CreatePlant.SetPlantInAlmamac(plantAnchor.position, (int)randomPlantChoice);
plant.transform.SetParent(newOptionsMenu.transform);
SortingGroup sortingGroup = plant.AddComponent<SortingGroup>();
sortingGroup.sortingLayerName = "UI";
sortingGroup.sortingOrder = 20001;
//plant.transform.position = new Vector3(-7.21f, -3.66f);
UnityEngine.Object.Destroy(plant.GetComponent<Plant>());
}*/
/*GameObject messageObj = new GameObject();
messageObj.name = "MultilangMessage";
messageObj.transform.SetParent(newOptionsMenu.transform);
TextMeshProUGUI messageText = messageObj.AddComponent<TextMeshProUGUI>();
messageObj.transform.localScale = Vector3.one;
messageText.autoSizeTextContainer = false;
messageText.font = FontStore.LoadTMPFont(Utils.LanguageEnum.English.ToString());
messageText.text = "Hello this is multilanguage choose language over there ------>";*/
}
public static void HideLangButton(GameObject btn)
{
btn.GetComponent<BoxCollider2D>().enabled = false;
}
public static void ShowLangButton(GameObject btn)
{
btn.GetComponent<BoxCollider2D>().enabled = true;
}
}
}

View file

@ -52,7 +52,6 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
{
startColorTag = match.Groups[1].Value;
toTranslate = match.Groups[2].Value;
Log.LogDebug(toTranslate);
}
string[] parts = toTranslate.Split("\n");

View file

@ -0,0 +1,36 @@
using HarmonyLib;
using Il2Cpp;
using Il2CppTMPro;
using PvZ_Fusion_Translator.AssetStore;
using UnityEngine;
using System.Text.RegularExpressions;
namespace PvZ_Fusion_Translator.Patches.GameObjects
{
[HarmonyPatch(typeof(PlayerSaveMenu))]
public static class PlayerSaveMenu_Patch
{
[HarmonyPatch(nameof(PlayerSaveMenu.CreatePlayerData))]
[HarmonyPostfix]
public static void CreatePlayerData(PlayerSaveMenu __instance)
{
TranslateCreatePlayerSaveMenu();
}
[HarmonyPatch(nameof(PlayerSaveMenu.ResetName))]
[HarmonyPostfix]
public static void ResetName(PlayerSaveMenu __instance)
{
TranslateCreatePlayerSaveMenu();
}
public static void TranslateCreatePlayerSaveMenu()
{
Transform createPlayerSaveMenuTransform = GameAPP.canvasUp.Find("CreatePlayerSaveMenu(Clone)");
if(createPlayerSaveMenuTransform.TryGetComponent<CreatePlayerSaveMenu>(out CreatePlayerSaveMenu createPlayerSaveMenu))
{
createPlayerSaveMenu.title.text = StringStore.TranslateText(createPlayerSaveMenu.title.text);
}
}
}
}

View file

@ -15,37 +15,30 @@ namespace PvZ_Fusion_Translator.Patches.Managers
[HarmonyPostfix]
private static void Start(InGameUI __instance)
{
TextMeshProUGUI[] array = new TextMeshProUGUI[]
foreach(TextMeshProUGUI text in __instance.GetComponentsInChildren<TextMeshProUGUI>())
{
__instance.transform.GetChild(4).GetComponent<TextMeshProUGUI>(),
__instance.transform.GetChild(4).GetChild(0).GetComponent<TextMeshProUGUI>(),
__instance.transform.GetChild(5).GetComponent<TextMeshProUGUI>(),
__instance.transform.GetChild(5).GetChild(0).GetComponent<TextMeshProUGUI>(),
__instance.transform.GetChild(6).GetComponent<TextMeshProUGUI>(),
__instance.transform.GetChild(6).GetChild(0).GetComponent<TextMeshProUGUI>()};
TextMeshProUGUI[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
string originalText = array2[i].text;
array2[i].text = array2[i].text.Replace("\n", " ");
if (text.text == null) continue;
string originalText = text.text;
text.text = text.text.Replace("\n", " ");
Regex checkFusionChallenge = new Regex("^超级([^\\s:]+)(:?挑战1?)");
if (array2[i].text.Contains("配方:") && !checkFusionChallenge.IsMatch(array2[i].text))
if (text.text.Contains("配方:") && !checkFusionChallenge.IsMatch(text.text))
{
string[] lines = array2[i].text.Split(":");
string[] lines = text.text.Split(":");
lines[1] = Utils.GetPlantNameFromAlmanac(lines[1]);
if (lines[1] == "")
{
Log.LogError("Couldn't find plant! (Fusion Showcase)");
lines[1] = StringStore.TranslateText(lines[1]);
}
array2[i].text = StringStore.TranslateText("配方:") + lines[1];
array2[i].autoSizeTextContainer = true;
text.text = StringStore.TranslateText("配方:") + lines[1];
text.autoSizeTextContainer = true;
}
else
{
array2[i].text = (StringStore.translationString.ContainsKey(array2[i].text + "_MG")) ? StringStore.TranslateText(array2[i].text + "_MG") : StringStore.TranslateText(array2[i].text);
array2[i].text = array2[i].text.Replace("\n", " ");
text.text = (StringStore.translationString.ContainsKey(text.text + "_MG")) ? StringStore.TranslateText(text.text + "_MG") : StringStore.TranslateText(text.text);
text.text = text.text.Replace("\n", " ");
}
}
}

View file

@ -1,22 +1,19 @@
using HarmonyLib;
using Il2Cpp;
using PvZ_Fusion_Translator.AssetStore;
using UnityEngine;
namespace PvZ_Fusion_Translator.Patches.Managers
{
[HarmonyPatch(typeof(UIMgr))]
public class UIMgr_Patch
{
//[HarmonyPatch(nameof(UIMgr.EnterMainMenu))]
//[HarmonyPostfix]
//private static void EnterMainMenu(UIMgr __instance)
//{
// // Log.LogInfo("Entered Main Menu");
// #if MULTI_LANGUAGE
// WarningStore.WarningReload(Utils.Language);
// #else
// WarningStore.WarningReload();
// #endif
//}
[HarmonyPatch(nameof(UIMgr.EnterLoseMenu))]
[HarmonyPostfix]
private static void EnterLoseMenu(UIMgr __instance)
{
LoseMenu loseMenu = GameObject.FindObjectOfType<LoseMenu>();
loseMenu.title.text = StringStore.TranslateText(loseMenu.title.text);
}
}
}

View file

@ -9,9 +9,9 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Odyssey
[HarmonyPatch(typeof(TravelRefresh))]
public static class TravelRefresh_Patch
{
[HarmonyPatch(nameof(TravelRefresh.Start))]
[HarmonyPatch(nameof(TravelRefresh.Awake))]
[HarmonyPostfix]
private static void Start(TravelRefresh __instance)
private static void Awake(TravelRefresh __instance)
{
__instance.text = StringStore.TranslateText(__instance.text);
__instance.text_shadow = StringStore.TranslateText(__instance.text_shadow);

View file

@ -0,0 +1,162 @@
using HarmonyLib;
using Il2Cpp;
using Il2CppTMPro;
using MelonLoader;
using Newtonsoft.Json;
using PvZ_Fusion_Translator.AssetStore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
using System.Threading.Tasks;
using UnityEngine;
namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor
{
[HarmonyPatch(typeof(CustomLevelMenu))]
public static class CustomLevelMenu_Patch
{
public static Dictionary<string, TranslatedLevelData> translatedLevelData = new Dictionary<string, TranslatedLevelData>();
public static float requestTimer = 0f;
public static bool useLocal = false;
[HarmonyPatch(nameof(CustomLevelMenu.LoadOnlineFiles))]
[HarmonyPostfix]
public static void LoadOnlineFiles(CustomLevelMenu __instance)
{
translatedLevelData = new Dictionary<string, TranslatedLevelData>();
if (!useLocal)
{
LoadOnlineData();
}
else
{
LoadLocalData();
}
}
[HarmonyPatch(nameof(CustomLevelMenu.InitWindows))]
[HarmonyPostfix]
public static void InitWindows(CustomLevelMenu __instance)
{
TranslateLevelButtons(__instance);
}
[HarmonyPatch(nameof(CustomLevelMenu.ReloadFiles))]
[HarmonyPostfix]
public static void ReloadFiles(CustomLevelMenu __instance)
{
TranslateLevelButtons(__instance);
}
public static void TranslateLevelButtons(CustomLevelMenu __instance)
{
Dictionary<string, TranslatedLevelData> dumpData = new Dictionary<string, TranslatedLevelData>();
foreach(CustomButton_enterGame level in __instance.levels)
{
string levelId = level.levelData.levelId;
if(levelId != null)
{
if(translatedLevelData.ContainsKey(levelId))
{
TranslatedLevelData levelData = translatedLevelData[levelId];
level.levelName.text = levelData.name;
level.levelData.Name = levelData.name;
level.serializedLevel.boardConfig.startTip = levelData.startTip;
}
else
{
TranslatedLevelData dumpedLevelData = new TranslatedLevelData(level.levelData.Name, level.serializedLevel.boardConfig.startTip);
dumpData.Add(levelId, dumpedLevelData);
}
}
else
{
level.levelName.text = StringStore.TranslateText(level.levelData.Name);
}
}
string dumpPath = Path.Combine(FileLoader.GetAssetDir(FileLoader.AssetType.Dumps), "custom_level_data.json");
File.WriteAllText(dumpPath, System.Text.Json.JsonSerializer.Serialize(dumpData, new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
}));
}
public static async System.Threading.Tasks.Task<Dictionary<string, TranslatedLevelData>> GetTranslatedLevelData()
{
var levelDataRequest = await new HttpClient().GetAsync($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/custom_level_data.json");
if(levelDataRequest.StatusCode == System.Net.HttpStatusCode.OK)
{
string levelDataString = await levelDataRequest.Content.ReadAsStringAsync();
Dictionary<string, TranslatedLevelData> levelDataJson = JsonConvert.DeserializeObject<Dictionary<string, TranslatedLevelData>>(levelDataString);
return levelDataJson;
}
else
{
new CancellationTokenSource().Cancel();
return null;
}
}
public static void LoadOnlineData()
{
if (requestTimer <= 0)
{
requestTimer = 5f; // 5 seconds in between request attempts to prevent any issues with github
var translatedLevelDataRequest = System.Threading.Tasks.Task.Run(() => GetTranslatedLevelData());
if (translatedLevelDataRequest.Result != null)
{
translatedLevelData = translatedLevelDataRequest.Result;
Log.LogInfo($"Successfully loaded level data for {Utils.Language.ToString()}!");
}
else
{
Log.LogError($"Couldn't find level data for {Utils.Language.ToString()}!");
}
}
else
{
Log.LogInfo($"Please wait before requesting levels again! Current cooldown: {Math.Round(requestTimer, 3)}s");
}
}
public static void LoadLocalData()
{
string assetDir = Path.Combine(FileLoader.GetAssetDir(FileLoader.AssetType.Strings, Utils.Language), "custom_level_data.json");
if (!File.Exists(assetDir))
{
File.CreateText(assetDir);
File.WriteAllText(assetDir, "{}");
}
string levelDataString = File.ReadAllText(assetDir);
Dictionary<string, TranslatedLevelData> levelDataJson = JsonConvert.DeserializeObject<Dictionary<string, TranslatedLevelData>>(levelDataString);
translatedLevelData = levelDataJson;
}
public static void SwapLevelData()
{
useLocal = !useLocal;
}
}
public class TranslatedLevelData
{
public string name { get; set; }
public string startTip { get; set; }
public TranslatedLevelData(string name, string startTip)
{
this.name = name;
this.startTip = startTip;
}
}
}

View file

@ -0,0 +1,32 @@
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;
namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor
{
[HarmonyPatch(typeof(CustomMenuPage_initZombies))]
public static class CustomMenuPage_initZombies_Patch
{
[HarmonyPatch(nameof(CustomMenuPage_initZombies.SubWave))]
[HarmonyPostfix]
public static void SubWave(CustomMenuPage_initZombies __instance)
{
__instance.currentWaveText.text = StringStore.TranslateText(__instance.currentWaveText.text);
}
[HarmonyPatch(nameof(CustomMenuPage_initZombies.AddWave))]
[HarmonyPostfix]
public static void AddWave(CustomMenuPage_initZombies __instance)
{
__instance.currentWaveText.text = StringStore.TranslateText(__instance.currentWaveText.text);
}
}
}

View file

@ -0,0 +1,34 @@
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;
namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor
{
[HarmonyPatch(typeof(CustomMenu_page3))]
public static class CustomMenuPage_page3_Patch
{
[HarmonyPatch(nameof(CustomMenu_page3.UpdateCardsText))]
[HarmonyPostfix]
public static void UpdateCardsText(CustomMenu_page3 __instance)
{
string plantName = Utils.GetPlantNameFromAlmanac(__instance.nameText.text);
__instance.nameText.text = (plantName != "") ? (plantName) : StringStore.TranslateText(__instance.nameText.text);
}
[HarmonyPatch(nameof(CustomMenu_page3.UpdateAdvancedInfo))]
[HarmonyPostfix]
public static void UpdateAdvancedInfo(CustomMenu_page3 __instance)
{
string plantName = Utils.GetPlantNameFromAlmanac(__instance.nameText.text);
__instance.nameText.text = (plantName != "") ? (plantName) : StringStore.TranslateText(__instance.nameText.text);
}
}
}

View file

@ -0,0 +1,65 @@
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;
namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor
{
[HarmonyPatch(typeof(CustomMenuPage_shooting))]
public static class CustomMenuPage_shooting_Patch
{
[HarmonyPatch(nameof(CustomMenuPage_shooting.CreateRoute))]
[HarmonyPostfix]
public static void CreateRoute(CustomMenuPage_shooting __instance)
{
TranslateRoutes(__instance);
}
[HarmonyPatch(nameof(CustomMenuPage_shooting.CreateStage))]
[HarmonyPostfix]
public static void CreateStage(CustomMenuPage_shooting __instance)
{
TranslateRoutes(__instance);
}
[HarmonyPatch(nameof(CustomMenuPage_shooting.Awake))]
[HarmonyPostfix]
public static void Awake(CustomMenuPage_shooting __instance)
{
TranslateRoutes(__instance);
}
[HarmonyPatch(nameof(CustomMenuPage_shooting.SearchRoute))]
[HarmonyPostfix]
public static void SearchRoute(CustomMenuPage_shooting __instance)
{
TranslateRoutes(__instance);
}
[HarmonyPatch(nameof(CustomMenuPage_shooting.SearchAllRoute))]
[HarmonyPostfix]
public static void SearchAllRoute(CustomMenuPage_shooting __instance)
{
TranslateRoutes(__instance);
}
public static void TranslateRoutes(CustomMenuPage_shooting __instance)
{
foreach(TextMeshProUGUI text in __instance.scollContent.GetComponentsInChildren<TextMeshProUGUI>())
{
if (text.name == "PlantText")
{
string plantName = Utils.GetPlantNameFromAlmanac(text.text);
text.text = (plantName != "") ? plantName : StringStore.TranslateText(text.text);
}
}
}
}
}

View file

@ -0,0 +1,39 @@
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;
namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor
{
[HarmonyPatch(typeof(CustomOnlineMenu))]
public static class CustomOnlineMenu_Patch
{
[HarmonyPatch(nameof(CustomOnlineMenu.EditLevel))]
[HarmonyPostfix]
public static void EditLevel(CustomOnlineMenu __instance)
{
TranslateLevelName(__instance);
}
[HarmonyPatch(nameof(CustomOnlineMenu.Start))]
[HarmonyPostfix]
public static void Start(CustomOnlineMenu __instance)
{
TranslateLevelName(__instance);
}
public static void TranslateLevelName(CustomOnlineMenu __instance)
{
string translatedText = StringStore.TranslateText(__instance.title.text);
__instance.title.text = translatedText;
__instance.title2.text = translatedText;
}
}
}

View file

@ -13,7 +13,7 @@
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Configurations>Debug;DebugEnglish;ReleaseMulti;ReleaseEnglish;DebugTesting;ReleaseOther;DebugObfuscate;ReleaseObfuscate</Configurations>
<GamePath>C:\Users\rainb\Desktop\PC_PVZ-Fusion_3.4.2</GamePath>
<GamePath>C:\Users\rainb\Desktop\PC_PVZ-Fusion_3.5</GamePath>
<Platforms>AnyCPU;ARM64;x64</Platforms>
</PropertyGroup>
@ -400,6 +400,9 @@
<Compile Include="Log.cs" />
<Compile Include="ModFeatures.cs" />
<Compile Include="AssetStore\AudioStore.cs" />
<Compile Include="Patches\GameObjects\ConfirmMenu_Patch.cs" />
<Compile Include="Patches\GameObjects\LoseMenu_Patch.cs" />
<Compile Include="Patches\GameObjects\PlayerSaveMenu_Patch.cs" />
<Compile Include="Patches\GameObjects\PVPRandomManager_Patch.cs" />
<Compile Include="Patches\GameObjects\GodShootingMenu_Patch.cs" />
<Compile Include="Patches\GameObjects\ButtonObjects\GodShootingLevelButton_Patch.cs" />
@ -502,6 +505,11 @@
<Compile Include="Patches\Managers\TowerManager_Patch.cs" />
<Compile Include="Patches\Managers\SaveMenu_Patch.cs" />
<Compile Include="Patches\Modes\Odyssey\TravelBuffOptionButton_Patch.cs" />
<Compile Include="Patches\Modes\Super Editor\CustomOnlineMenu_Patch.cs" />
<Compile Include="Patches\Modes\Super Editor\CustomMenuPage_shooting_Patch.cs" />
<Compile Include="Patches\Modes\Super Editor\CustomLevelMenu_Patch.cs" />
<Compile Include="Patches\Modes\Super Editor\CustomMenuPage_page3_Patch.cs" />
<Compile Include="Patches\Modes\Super Editor\CustomMenuPage_initZombies_Patch.cs" />
<Compile Include="Patches\OtherManagers\AchievementClip_Patch.cs" />
<Compile Include="Patches\GameObjects\ButtonObjects\Advanture_Btn_Patch.cs" />
<Compile Include="Patches\Managers\AlmanacMgrZombie_Patch.cs" />

View file

@ -167,7 +167,7 @@ namespace PvZ_Fusion_Translator
return thePlantName;
}
public static string GetPlantNameFromAlmanac(string theOriginalPlantName)
public static string GetPlantNameFromAlmanac(string theOriginalPlantName, bool log = false)
{
string originalJson;
string translatedJson;
@ -181,8 +181,11 @@ namespace PvZ_Fusion_Translator
if ((!File.Exists(path)) || (!File.Exists(originalPath)))
{
Log.LogError($"LawnStringsTranslate.json file not found at path: {path}");
Log.LogError("Plant name could not be found!");
if(log)
{
Log.LogError($"LawnStringsTranslate.json file not found at path: {path}");
Log.LogError("Plant name could not be found!");
}
thePlantName = "";
}
else
@ -208,7 +211,10 @@ namespace PvZ_Fusion_Translator
if (!foundPlantName)
{
Log.LogInfo("Couldn't find plant name!");
if(log)
{
Log.LogInfo("Couldn't find plant name!");
}
thePlantName = "";
}
}
@ -300,7 +306,7 @@ namespace PvZ_Fusion_Translator
}
}
public static Dictionary<int, List<int>> recipeLinks = new Dictionary<int, List<int>>();
/*public static Dictionary<int, List<int>> recipeLinks = new Dictionary<int, List<int>>();
public static void RegisterRecipeLinks()
{
@ -316,32 +322,32 @@ namespace PvZ_Fusion_Translator
recipeLinks.Add(seedType, new());
if(seedType > 0)
if (seedType > 0)
{
foreach (PlantType checkType in Enum.GetValues(typeof(PlantType)))
{
if(checkType > 0)
foreach (PlantType checkType in Enum.GetValues(typeof(PlantType)))
{
if (checkType > 0)
{
int[] checks = [mixData.GetValue(seedType, (int)checkType).Unbox<int>(), mixData.GetValue((int)checkType, seedType).Unbox<int>()];
int[] checks = [mixData.GetValue(seedType, (int)checkType).Unbox<int>(), mixData.GetValue((int)checkType, seedType).Unbox<int>()];
if (checks[0] != 0)
{
recipeLinks[seedType].Add(checks[0]);
}
if (checks[0] != 0)
{
recipeLinks[seedType].Add(checks[0]);
}
if (checks[1] != 0)
{
recipeLinks[seedType].Add(checks[1]);
}
}
}
}
if (checks[1] != 0)
{
recipeLinks[seedType].Add(checks[1]);
}
}
}
}
if (recipeLinks[seedType].Count > 0)
{
Log.LogDebug($"Found {recipeLinks[seedType].Count} fusions for {seedType}!");
}
}
}
}
}*/
public static bool CheckForUntranslatedText(string text)
{
@ -396,31 +402,48 @@ namespace PvZ_Fusion_Translator
{
// first column
English,
Chinese_cn,
French,
Italian,
German,
//Italian,
//German,
Spanish,
Portuguese,
// second column
Javanese, //Filipino,
//Portuguese,
//Javanese,
//Filipino,
Vietnamese,
Indonesian,
Russian, //NOTE: legacy language
//Indonesian,
//Russian, //NOTE: legacy language
Japanese,
Korean,
// third column
Ukrainian,
Korean,
Ukrainian,
// Slovak,
//Polish,
Turkish,
//Turkish,
//Arabic,
Romanian,
LANG_END
}
public static Dictionary<LanguageEnum, string> LanguageNames = new Dictionary<LanguageEnum, string>()
{
{ LanguageEnum.English, "English"},
{ LanguageEnum.Chinese_cn, "简体中文"},
{ LanguageEnum.French, "Français"},
{ LanguageEnum.Spanish, "Español"},
{ LanguageEnum.Vietnamese, "Tiếng Việt"},
{ LanguageEnum.Japanese, "日本語"},
{ LanguageEnum.Korean, "한국어"},
{ LanguageEnum.Ukrainian, "українська"},
{ LanguageEnum.Romanian, "Română"}
};
public enum ToggleEnum
{
Textures,