Merge branch 'cassidy'

This commit is contained in:
SillyStar-Github 2026-03-30 13:49:05 -04:00
commit 5f5e7b6c91
16 changed files with 597 additions and 194 deletions

View file

@ -12,6 +12,10 @@ namespace PvZ_Fusion_Translator.AssetStore
public static Dictionary<string, string> translationString = new();
public static Dictionary<string, string> izTipCollectionString = new();
public static Dictionary<string, string> fsTipCollectionString = new();
public static Dictionary<string, Dictionary<string, string>> patchesStore = new()
{
{ "Difficulty", new Dictionary<string, string>
@ -70,6 +74,8 @@ namespace PvZ_Fusion_Translator.AssetStore
{
translationString.Clear();
translationStringRegex.Clear();
izTipCollectionString.Clear();
fsTipCollectionString.Clear();
FileLoader.LoadStrings();
}
@ -178,6 +184,24 @@ namespace PvZ_Fusion_Translator.AssetStore
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];
}
// Regex-based dynamic translation
foreach (var entry in translationStringRegex)
{

View file

@ -115,6 +115,7 @@ namespace PvZ_Fusion_Translator
public MelonPreferences_Entry<bool> configDefaultTextures;
public MelonPreferences_Entry<bool> configDefaultAudio;
public MelonPreferences_Entry<bool> configUseLocal;
public MelonPreferences_Entry<string> configLanguage;
private void Config()
@ -123,6 +124,7 @@ namespace PvZ_Fusion_Translator
configDefaultTextures = category.CreateEntry("DefaultTextures", false);
configDefaultAudio = category.CreateEntry("DefaultAudio", false);
configUseLocal = category.CreateEntry("UseLocal", false);
configLanguage = category.CreateEntry("Language", "English");
MelonPreferences.Save();

View file

@ -1,6 +1,10 @@
using Il2Cpp;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using JetBrains.Annotations;
using MelonLoader;
using MelonLoader.TinyJSON;
using PvZ_Fusion_Translator.AssetStore;
using PvZ_Fusion_Translator.Patches.GameObjects;
using PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects;
using PvZ_Fusion_Translator.Patches.Modes.Odyssey;
using PvZ_Fusion_Translator.Patches.OtherManagers;
@ -28,19 +32,133 @@ namespace PvZ_Fusion_Translator
}
// private static string GetAssetDir(AssetType assetType) => Path.Combine(Core.Instance.modsDirectory, assetType.ToString());
#if MULTI_LANGUAGE
internal static void LoadStrings() => LoadStrings(Utils.Language);
internal static void LoadStrings(Utils.LanguageEnum language)
#else
internal static void LoadStrings(bool isLog = false)
#endif
{
#if MULTI_LANGUAGE
string stringDir = GetAssetDir(AssetType.Strings, Utils.Language);
#else
string stringDir = GetAssetDir(AssetType.Strings);
#endif
if(!Utils.useLocal)
{
// load translationStrings
string translationStringsContent = Utils.GetDataFromWeb($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/translation_strings.json").Result;
if(translationStringsContent != null)
{
LoadTranslationStrings(translationStringsContent);
}
else
{
string translationStringsPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "translation_strings.json");
if(File.Exists(translationStringsPath))
{
LoadTranslationStrings(File.ReadAllText(translationStringsPath));
}
}
// load translationRegexs
string translationRegexsContent = Utils.GetDataFromWeb($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/translation_regexs.json").Result;
if(translationRegexsContent != null)
{
LoadTranslationRegexs(translationRegexsContent);
}
else
{
string translationRegexsPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "translation_regexs.json");
if(File.Exists(translationRegexsPath))
{
LoadTranslationRegexs(File.ReadAllText(translationRegexsPath));
}
}
// load travel_buffs
string travelBuffsContent = Utils.GetDataFromWeb($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/travel_buffs.json").Result;
if(travelBuffsContent != null)
{
TravelMgr_Patch.translatedTravelBuffs = JsonSerializer.Deserialize<Dictionary<string, SortedDictionary<int, string>>>(travelBuffsContent);
}
else
{
string travelBuffsPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "travel_buffs.json");
if(File.Exists(travelBuffsPath))
{
TravelMgr_Patch.translatedTravelBuffs = JsonSerializer.Deserialize<Dictionary<string, SortedDictionary<int, string>>>(File.ReadAllText(travelBuffsPath));
}
}
// load tips
string izTipsContent = Utils.GetDataFromWeb($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/tips_iz.json").Result;
if(izTipsContent != null)
{
LoadIZStrings(izTipsContent);
}
else
{
string izTipsPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "tips_iz.json");
if(File.Exists(izTipsPath))
{
LoadIZStrings(File.ReadAllText(izTipsPath));
}
}
string fsTipsContent = Utils.GetDataFromWeb($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/tips_fs.json").Result;
if(fsTipsContent != null)
{
LoadFSStrings(fsTipsContent);
}
else
{
string fsTipsPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "tips_fs.json");
if(File.Exists(fsTipsPath))
{
LoadFSStrings(File.ReadAllText(fsTipsPath));
}
}
// load almanacs
string plantAlmanacContent = Utils.GetDataFromWeb($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Almanac/LawnStringsTranslate.json").Result;
if(plantAlmanacContent != null)
{
AlmanacPlantMenu_Patch.almanacJson = plantAlmanacContent;
}
else
{
string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
string path = Path.Combine(almanacDir, "LawnStringsTranslate.json");
if(File.Exists(path))
{
AlmanacPlantMenu_Patch.almanacJson = File.ReadAllText(path);
}
}
string zombieAlmanacContent = Utils.GetDataFromWeb($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Almanac/ZombieStringsTranslate.json").Result;
if(zombieAlmanacContent != null)
{
AlmanacZombieMenu_Patch.almanacJson = zombieAlmanacContent;
}
else
{
string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
string path = Path.Combine(almanacDir, "ZombieStringsTranslate.json");
if(File.Exists(path))
{
AlmanacZombieMenu_Patch.almanacJson = File.ReadAllText(path);
}
}
Patches.Modes.Abyss.AbyssBuffMenu_Patch.LoadAbyssBuffData();
SaveStrings();
DumpJson();
}
else
{
if (!Directory.Exists(stringDir))
{
Directory.CreateDirectory(stringDir);
@ -66,26 +184,39 @@ namespace PvZ_Fusion_Translator
string jsonString = File.ReadAllText(filepath);
if (fileName.EndsWith("_strings"))
{
Dictionary<string, string> dictionary = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(jsonString);
foreach (var (key, value) in dictionary)
{
StringStore.translationString[key] = value;
}
LoadTranslationStrings(jsonString);
}
else if (fileName.EndsWith("_regexs"))
{
Dictionary<string, string> dictionary = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(jsonString);
foreach (var (key, value) in dictionary)
{
StringStore.translationStringRegex[key] = value;
}
Zombie_Patch.LoadHPStrings();
LoadTranslationRegexs(jsonString);
}
else if(fileName.EndsWith("travel_buffs"))
{
TravelMgr_Patch.translatedTravelBuffs = JsonSerializer.Deserialize<Dictionary<string, SortedDictionary<int, string>>>(jsonString);
}
else if(fileName.EndsWith("tips_iz"))
{
LoadIZStrings(jsonString);
}
else if(fileName.EndsWith("tips_fs"))
{
LoadFSStrings(jsonString);
}
}
string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
string plantAlmanacPath = Path.Combine(almanacDir, "LawnStringsTranslate.json");
if(File.Exists(plantAlmanacPath))
{
AlmanacPlantMenu_Patch.almanacJson = File.ReadAllText(plantAlmanacPath);
}
string zombieAlmanacPlant = Path.Combine(almanacDir, "ZombieStringsTranslate.json");
if(File.Exists(zombieAlmanacPlant))
{
AlmanacZombieMenu_Patch.almanacJson = File.ReadAllText(zombieAlmanacPlant);
}
SaveStrings();
DumpJson();
}
@ -94,9 +225,93 @@ namespace PvZ_Fusion_Translator
Log.LogError("Error loading string.");
Log.LogError(e.GetType() + " " + e.Message);
}
}
Log.LogInfo("Strings loaded successfully.");
}
internal static void LoadTranslationStrings(string content)
{
Dictionary<string, string> dictionary = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var (key, value) in dictionary)
{
StringStore.translationString[key] = value;
}
}
internal static void LoadTranslationRegexs(string content)
{
Dictionary<string, string> dictionary = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var (key, value) in dictionary)
{
StringStore.translationStringRegex[key] = value;
}
Zombie_Patch.LoadHPStrings();
}
internal static void LoadIZStrings(string content)
{
var izDump = DumpIZStrings();
var izLevelData = izDump.Item1;
var izLevelDataDump = izDump.Item2;
var izTranslatedTips = JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var level in izLevelData)
{
string data = level.text;
LevelData levelData = JsonUtility.FromJson<LevelData>(data);
if(levelData.tips != null)
{
if(izTranslatedTips.ContainsKey(level.name) && !StringStore.izTipCollectionString.ContainsKey(levelData.tips))
{
StringStore.izTipCollectionString.Add(levelData.tips, izTranslatedTips[level.name]);
}
}
}
}
internal static void LoadFSStrings(string content)
{
var fusionShowcaseDump = DumpFSStrings();
var fusionShowcaseData = fusionShowcaseDump.Item1;
var fusionShowcaseDataDump = fusionShowcaseDump.Item2;
var fsTranslatedTips = JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var level in fusionShowcaseData)
{
string data = level.text;
LevelData levelData = JsonUtility.FromJson<LevelData>(data);
if (levelData.tips != null)
{
if (fsTranslatedTips.ContainsKey(level.name) && !StringStore.fsTipCollectionString.ContainsKey(levelData.tips))
{
StringStore.fsTipCollectionString.Add(levelData.tips, fsTranslatedTips[level.name]);
}
}
}
}
internal static void LoadChangelogText()
{
string stringDir = FileLoader.GetAssetDir(FileLoader.AssetType.Strings, Utils.Language);
string changelogDir = Path.Combine(stringDir, "changelog.txt");
string changelogText = File.ReadAllText(changelogDir);
if(!Utils.useLocal)
{
string changelogContent = Utils.GetDataFromWeb($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/changelog.txt").Result;
if(changelogContent != null)
{
changelogText = changelogContent;
}
}
NoticePauseMenu_Patch.changelogText = changelogText;
}
public static bool loadDefaultTextures = MelonPreferences.GetEntryValue<bool>("PvZ_Fusion_Translator", "DefaultTextures");
internal static void LoadTextures()
{
@ -286,6 +501,8 @@ namespace PvZ_Fusion_Translator
Directory.CreateDirectory(stringDir);
}
// save regexs
string translationStringRegex = System.Text.Json.JsonSerializer.Serialize(StringStore.translationStringRegex, new JsonSerializerOptions
{
WriteIndented = true,
@ -294,6 +511,8 @@ namespace PvZ_Fusion_Translator
File.WriteAllText(Path.Combine(stringDir, "translation_regexs.json"), translationStringRegex);
// save strings
string translationString = System.Text.Json.JsonSerializer.Serialize(StringStore.translationString, new JsonSerializerOptions
{
WriteIndented = true,
@ -301,6 +520,81 @@ namespace PvZ_Fusion_Translator
});
File.WriteAllText(Path.Combine(stringDir, "translation_strings.json"), translationString);
// save iz tips
string izTipCollectionString = System.Text.Json.JsonSerializer.Serialize(StringStore.izTipCollectionString, new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
File.WriteAllText(Path.Combine(stringDir, "tips_iz.json"), izTipCollectionString);
// save fs tips
string fsTipCollectionString = System.Text.Json.JsonSerializer.Serialize(StringStore.fsTipCollectionString, new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
File.WriteAllText(Path.Combine(stringDir, "tips_fs.json"), fsTipCollectionString);
// save travel buffs
string travelBuffs = System.Text.Json.JsonSerializer.Serialize(TravelMgr_Patch.translatedTravelBuffs, new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
File.WriteAllText(Path.Combine(stringDir, "travel_buffs.json"), travelBuffs);
// save changelog
if(NoticePauseMenu_Patch.changelogText != "")
{
File.WriteAllText(Path.Combine(stringDir, "changelog.txt"), NoticePauseMenu_Patch.changelogText);
}
// save abyss buffs
if(Patches.Modes.Abyss.AbyssBuffMenu_Patch.abyssBuffData != new Dictionary<string, string>())
{
string abyssBuffData = System.Text.Json.JsonSerializer.Serialize(Patches.Modes.Abyss.AbyssBuffMenu_Patch.abyssBuffData, new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
File.WriteAllText(Path.Combine(stringDir, "abyss_buffs.json"), abyssBuffData);
}
// save almanacs
string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
if(AlmanacPlantMenu_Patch.almanacJson != "")
{
string plantAlmanac = System.Text.Json.JsonSerializer.Serialize(AlmanacPlantMenu_Patch.almanacJson, new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
File.WriteAllText(Path.Combine(almanacDir, "LawnStringsTranslate.json"), AlmanacPlantMenu_Patch.almanacJson);
}
if(AlmanacZombieMenu_Patch.almanacJson != "")
{
string zombieAlmanac = System.Text.Json.JsonSerializer.Serialize(AlmanacZombieMenu_Patch.almanacJson, new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
File.WriteAllText(Path.Combine(almanacDir, "ZombieStringsTranslate.json"), AlmanacZombieMenu_Patch.almanacJson);
}
}
public static void DumpJson()
@ -319,29 +613,9 @@ namespace PvZ_Fusion_Translator
File.WriteAllText(Path.Combine(dumpDir, "AbyssBuffData.json"), AbyssBuffData);
// dump iz tips
var izLevelData = Resources.LoadAll<TextAsset>("izleveldata");
Dictionary<string, string> izLevelDataDump = new Dictionary<string, string>();
string izTranslatedPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "tips_iz.json");
if (!File.Exists(izTranslatedPath))
{
File.WriteAllText(izTranslatedPath, JsonSerializer.Serialize(izLevelDataDump));
}
var izTranslatedTips = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(izTranslatedPath));
foreach (var level in izLevelData)
{
string data = level.text;
LevelData levelData = JsonUtility.FromJson<LevelData>(data);
if(levelData.tips != null)
{
izLevelDataDump.Add(level.name, levelData.tips);
if(izTranslatedTips.ContainsKey(level.name) && !StringStore.translationString.ContainsKey(levelData.tips))
{
StringStore.translationString.Add(levelData.tips, izTranslatedTips[level.name]);
}
}
}
var izDump = DumpIZStrings();
var izLevelData = izDump.Item1;
var izLevelDataDump = izDump.Item2;
File.WriteAllText(Path.Combine(dumpDir, "tips_iz.json"), JsonSerializer.Serialize(izLevelDataDump, new JsonSerializerOptions
{
@ -350,29 +624,9 @@ namespace PvZ_Fusion_Translator
}));
// dump fusion showcase tips
var fusionShowcaseData = Resources.LoadAll<TextAsset>("leveldata/explore");
Dictionary<string, string> fusionShowcaseDataDump = new Dictionary<string, string>();
string fsTranslatedPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "tips_fs.json");
if(!File.Exists(fsTranslatedPath))
{
File.WriteAllText(fsTranslatedPath, JsonSerializer.Serialize(fusionShowcaseDataDump));
}
var fsTranslatedTips = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(fsTranslatedPath));
foreach (var level in fusionShowcaseData)
{
string data = level.text;
LevelData levelData = JsonUtility.FromJson<LevelData>(data);
if (levelData.tips != null)
{
fusionShowcaseDataDump.Add(level.name, levelData.tips);
if (fsTranslatedTips.ContainsKey(level.name) && !StringStore.translationString.ContainsKey(levelData.tips))
{
StringStore.translationString.Add(levelData.tips, fsTranslatedTips[level.name]);
}
}
}
var fusionShowcaseDump = DumpIZStrings();
var fusionShowcaseData = izDump.Item1;
var fusionShowcaseDataDump = izDump.Item2;
File.WriteAllText(Path.Combine(dumpDir, "tips_fs.json"), JsonSerializer.Serialize(fusionShowcaseDataDump, new JsonSerializerOptions
{
@ -392,6 +646,38 @@ namespace PvZ_Fusion_Translator
}));
}
public static ValueTuple<Il2CppArrayBase<TextAsset>, Dictionary<string, string>> DumpIZStrings()
{
var izLevelData = Resources.LoadAll<TextAsset>("izleveldata");
Dictionary<string, string> izLevelDataDump = new Dictionary<string, string>();
foreach (var level in izLevelData)
{
string data = level.text;
LevelData levelData = JsonUtility.FromJson<LevelData>(data);
if(levelData.tips != null)
{
izLevelDataDump.Add(level.name, levelData.tips);
}
}
return new ValueTuple<Il2CppArrayBase<TextAsset>, Dictionary<string, string>>(izLevelData, izLevelDataDump);
}
public static ValueTuple<Il2CppArrayBase<TextAsset>, Dictionary<string, string>> DumpFSStrings()
{
var fusionShowcaseData = Resources.LoadAll<TextAsset>("leveldata/explore");
Dictionary<string, string> fusionShowcaseDataDump = new Dictionary<string, string>();
foreach (var level in fusionShowcaseData)
{
string data = level.text;
LevelData levelData = JsonUtility.FromJson<LevelData>(data);
if (levelData.tips != null)
{
fusionShowcaseDataDump.Add(level.name, levelData.tips);
}
}
return new ValueTuple<Il2CppArrayBase<TextAsset>, Dictionary<string, string>>(fusionShowcaseData, fusionShowcaseDataDump);
}
#if DEBUG
public static void DumpUntranslatedStrings(string text)
{

View file

@ -11,7 +11,7 @@ namespace PvZ_Fusion_Translator
{
ReloadStrings,
ReloadTextures,
SwapLevelData
SwapLocalData
}
private class ModFeature
@ -45,10 +45,10 @@ namespace PvZ_Fusion_Translator
return;
}
if(this.ModType == ModType.SwapLevelData)
if(this.ModType == ModType.SwapLocalData)
{
CustomLevelMenu_Patch.SwapLevelData();
string toast = (CustomLevelMenu_Patch.useLocal) ? "Using Local Data..." : "Using Online Data...";
Utils.SwapLocalData();
string toast = (Utils.useLocal) ? "Using Local Data..." : "Using Online Data...";
Core.ShowToast(toast);
return;
}
@ -59,7 +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)},
{ModType.SwapLocalData,new ModFeature("Swap Level Data Type",ModType.SwapLocalData,KeyCode.PageUp, true)},
};

View file

@ -16,6 +16,8 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
[HarmonyPatch(typeof(AlmanacPlantMenu))]
public static class AlmanacPlantMenu_Patch
{
public static string almanacJson = "";
[HarmonyPatch(nameof(AlmanacPlantMenu.Awake))]
[HarmonyPostfix]
private static void Awake(AlmanacPlantMenu __instance)
@ -56,10 +58,8 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
string currentLanguage = Utils.Language.ToString();
string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
string path = Path.Combine(almanacDir, "LawnStringsTranslate.json");
string moddedPath = Path.Combine(almanacDir, "ModdedPlantsTranslate.json");
string json;
json = File.ReadAllText(path);
string json = AlmanacPlantMenu_Patch.almanacJson;
TMP_FontAsset fontAsset = FontStore.LoadTMPFont(currentLanguage);

View file

@ -22,8 +22,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
string path = Path.Combine(almanacDir, "LawnStringsTranslate.json");
string moddedPath = Path.Combine(almanacDir, "ModdedPlantsTranslate.json");
string json;
json = File.ReadAllText(path);
string json = AlmanacPlantMenu_Patch.almanacJson;
bool hasAlmanacFont = false;
TMP_FontAsset almanacFontAsset = null;

View file

@ -14,6 +14,8 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
[HarmonyPatch(typeof(AlmanacZombieMenu))]
public static class AlmanacZombieMenu_Patch
{
public static string almanacJson = "";
[HarmonyPatch(nameof(AlmanacZombieMenu.Awake))]
[HarmonyPostfix]
private static void Awake(AlmanacZombieMenu __instance)

View file

@ -20,10 +20,8 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
string currentLanguage = Utils.Language.ToString();
string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
string path = Path.Combine(almanacDir, "ZombieStringsTranslate.json");
string moddedPath = Path.Combine(almanacDir, "ModdedZombiesTranslate.json");
string json;
json = File.ReadAllText(path);
string json = AlmanacZombieMenu_Patch.almanacJson;
bool hasAlmanacFont = false;
TMP_FontAsset almanacFontAsset = null;

View file

@ -36,14 +36,14 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
private const float ySpacing = 1.2505f;
private const float toggleStartX = 4.3241f;
private const float toggleStartY = -2.2251f;
private const float toggleStartY = -0.9746f;
private static List<Utils.LanguageEnum> AvailableLanguages;
private static List<Utils.ToggleEnum> AvailableToggles;
private const int LanguagesPerPage = 5;
private static int currentPage = 0;
private static OptionBtn[] buttonSlots = new OptionBtn[6]; // 5 language buttons + 1 next button
private static OptionBtn[] toggleSlots = new OptionBtn[2];
private static OptionBtn[] toggleSlots = new OptionBtn[3];
public static void CreateLanguageButtons(OptionBtn templateButton)
{
@ -93,7 +93,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
.Where(lang => lang != Utils.ToggleEnum.TOGGLE_END)
.ToList();
for (int i = 0; i < 2; i++)
for (int i = 0; i < 3; i++)
{
var newButton = Object.Instantiate(templateButton, templateButton.transform.parent);
newButton.tag = "LangOpt";
@ -149,7 +149,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
if (togglesCreated)
{
for (int i = 0; i < 2; i++)
for (int i = 0; i < 3; i++)
{
var btn = toggleSlots[i];
var data = ToggleBtnDict[btn.GetInstanceID()];
@ -160,7 +160,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
data.Toggle = toggle;
data.IsNextButton = false;
btn.gameObject.SetActive(true);
UpdateButtonText(btn, StringStore.TranslateText(toggle.ToString()), FontStore.LoadTMPFont(Utils.Language.ToString()));
UpdateButtonText(btn, StringStore.TranslateText(Utils.ToggleNames[toggle]), FontStore.LoadTMPFont(Utils.Language.ToString()));
}
else
{
@ -248,7 +248,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
private static void ToggleCustomAssets(string type)
{
if (type != "Textures" && type != "Audio") return;
if (type != "Textures" && type != "Audio" && type != "SwapLocal") return;
if(type == "Textures")
{
@ -263,6 +263,12 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
Utils.customAudio = (Utils.customAudio) ? false : true;
MelonPreferences.SetEntryValue<bool>("PvZ_Fusion_Translator", "DefaultAudio", !Utils.customAudio);
}
if (type == "SwapLocal")
{
Utils.SwapLocalData();
}
MelonPreferences.Save();
}
@ -321,6 +327,11 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects.ButtonObjects
else if (toggleType == "Audio")
{
FlashMessage(toggleData.Button, "<size=10>Toggled custom audio!", 0.1f);
}
else if (toggleType == "SwapLocal")
{
string message = (Utils.useLocal) ? "<size=10>Switched to local translations!" : "<size=10>Switched to online translations!";
FlashMessage(toggleData.Button, message, 0.1f);
}
}
}

View file

@ -4,11 +4,14 @@ using Il2CppTMPro;
using PvZ_Fusion_Translator.AssetStore;
using UnityEngine;
using UnityEngine.UI;
using static PvZ_Fusion_Translator.FileLoader;
namespace PvZ_Fusion_Translator.Patches.GameObjects
{
public class NoticePauseMenu_Patch
{
public static string changelogText = "";
public static void Pre_Awake(BaseMenu __instance)
{
GameObject contentObject = __instance.transform.FindChild("Scroll View/Viewport/Content").gameObject;
@ -28,10 +31,7 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
public static void Post_Awake(BaseMenu __instance)
{
TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString());
string stringDir = FileLoader.GetAssetDir(FileLoader.AssetType.Strings, Utils.Language);
string changelogDir = Path.Combine(stringDir, "changelog.txt");
string changelogText = File.ReadAllText(changelogDir);
FileLoader.LoadChangelogText();
GameObject contentObject = __instance.transform.FindChild("Scroll View/Viewport/Content").gameObject;

View file

@ -28,6 +28,13 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
TranslatePlantDataMenu(__instance);
}
[HarmonyPatch(nameof(PlantDataMenu.InitDataFromPlant))]
[HarmonyPostfix]
public static void Post_InitDataFromPlant(PlantDataMenu __instance)
{
TranslatePlantDataMenu(__instance);
}
public static void TranslatePlantDataMenu(PlantDataMenu __instance)
{
PlantType thePlantType = __instance.plant.thePlantType;
@ -75,6 +82,11 @@ namespace PvZ_Fusion_Translator.Patches.GameObjects
text.font = fontAsset;
}
foreach(TextMeshProUGUI text in __instance.toggles.transform.Find("Viewport/Content/Toggles").GetComponentsInChildren<TextMeshProUGUI>())
{
text.text = StringStore.TranslateText(text.text);
}
}
}
}

View file

@ -43,29 +43,67 @@ namespace PvZ_Fusion_Translator.Patches.Managers
}
}
[HarmonyPatch(nameof(InGameUI.Update))]
[HarmonyPatch(nameof(InGameUI.SetUniqueText))]
[HarmonyPostfix]
private static void Update(InGameUI __instance)
private static void SetUniqueText(InGameUI __instance)
{
foreach (TextMeshProUGUI txt in __instance.ShovelBank.GetComponentsInChildren<TextMeshProUGUI>())
foreach(TextMeshProUGUI text in __instance.T)
{
txt.text = StringStore.TranslateText(txt.text);
txt.font = FontStore.LoadTMPFont(Utils.Language.ToString());
if (text.text == null) continue;
string originalText = text.text;
text.text = text.text.Replace("\n", " ");
Regex checkFusionChallenge = new Regex("^超级([^\\s:]+)(:?挑战1?)");
if (text.text.Contains("配方:") && !checkFusionChallenge.IsMatch(text.text))
{
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]);
}
text.text = StringStore.TranslateText("配方:") + lines[1];
text.autoSizeTextContainer = true;
}
else
{
text.text = (StringStore.translationString.ContainsKey(text.text + "_MG")) ? StringStore.TranslateText(text.text + "_MG") : StringStore.TranslateText(text.text);
text.text = text.text.Replace("\n", " ");
}
}
}
[HarmonyPatch(typeof(InGameUI_IZ))]
public static class InGameUI_IZ_Patch
{
[HarmonyPatch(nameof(InGameUI_IZ.Update))]
[HarmonyPatch(nameof(InGameUI.SetLevelName))]
[HarmonyPostfix]
private static void Update(InGameUI_IZ __instance)
private static void SetLevelName(InGameUI __instance)
{
foreach (TextMeshProUGUI txt in __instance.shovel.GetComponentsInChildren<TextMeshProUGUI>())
foreach(TextMeshProUGUI text in __instance.T)
{
txt.text = StringStore.TranslateText(txt.text);
txt.font = FontStore.LoadTMPFont(Utils.Language.ToString());
if (text.text == null) continue;
string originalText = text.text;
text.text = text.text.Replace("\n", " ");
Regex checkFusionChallenge = new Regex("^超级([^\\s:]+)(:?挑战1?)");
if (text.text.Contains("配方:") && !checkFusionChallenge.IsMatch(text.text))
{
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]);
}
text.text = StringStore.TranslateText("配方:") + lines[1];
text.autoSizeTextContainer = true;
}
else
{
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

@ -21,12 +21,5 @@ namespace PvZ_Fusion_Translator.Patches.Managers
}
}
}
[HarmonyPatch(nameof(TowerManager.Update))]
[HarmonyPostfix]
public static void Update(TowerManager __instance)
{
}
}
}

View file

@ -11,6 +11,7 @@ using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using UnityEngine.TextCore.Text;
using static PvZ_Fusion_Translator.FileLoader;
namespace PvZ_Fusion_Translator.Patches.Modes.Abyss
{
@ -82,7 +83,30 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Abyss
{
abyssBuffData.Clear();
string abyssBuffText = File.ReadAllText(Path.Combine(FileLoader.GetAssetDir(FileLoader.AssetType.Strings, Utils.Language), "abyss_buffs.json"));
string abyssBuffText = "{}";
if(!Utils.useLocal)
{
string abyssBuffsContent = Utils.GetDataFromWeb($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/abyss_buffs.json").Result;
if(abyssBuffsContent != null)
{
abyssBuffText = abyssBuffsContent;
}
else
{
string abyssBuffsPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "abyss_buffs.json");
if(File.Exists(abyssBuffsPath))
{
abyssBuffText = File.ReadAllText(abyssBuffsPath);
}
}
}
else
{
abyssBuffText = File.ReadAllText(Path.Combine(FileLoader.GetAssetDir(FileLoader.AssetType.Strings, Utils.Language), "abyss_buffs.json"));
}
abyssBuffData = JsonSerializer.Deserialize<Dictionary<string, string>>(abyssBuffText);
}

View file

@ -21,15 +21,12 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor
{
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)
if (!Utils.useLocal)
{
LoadOnlineData();
}
@ -91,10 +88,9 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor
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 = Utils.GetDataFromWeb($"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/custom_level_data.json").Result;
if(levelDataString != null)
{
string levelDataString = await levelDataRequest.Content.ReadAsStringAsync();
Dictionary<string, TranslatedLevelData> levelDataJson = JsonConvert.DeserializeObject<Dictionary<string, TranslatedLevelData>>(levelDataString);
return levelDataJson;
}
@ -111,6 +107,8 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor
{
requestTimer = 5f; // 5 seconds in between request attempts to prevent any issues with github
translatedLevelData = new Dictionary<string, TranslatedLevelData>();
var translatedLevelDataRequest = System.Threading.Tasks.Task.Run(() => GetTranslatedLevelData());
if (translatedLevelDataRequest.Result != null)
@ -131,6 +129,7 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor
public static void LoadLocalData()
{
translatedLevelData = new Dictionary<string, TranslatedLevelData>();
string assetDir = Path.Combine(FileLoader.GetAssetDir(FileLoader.AssetType.Strings, Utils.Language), "custom_level_data.json");
if (!File.Exists(assetDir))
{
@ -141,11 +140,6 @@ namespace PvZ_Fusion_Translator.Patches.Modes.Super_Editor
Dictionary<string, TranslatedLevelData> levelDataJson = JsonConvert.DeserializeObject<Dictionary<string, TranslatedLevelData>>(levelDataString);
translatedLevelData = levelDataJson;
}
public static void SwapLevelData()
{
useLocal = !useLocal;
}
}
public class TranslatedLevelData

View file

@ -3,8 +3,11 @@ using Il2Cpp;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.TinyJSON;
using Newtonsoft.Json;
using PvZ_Fusion_Translator.AssetStore;
using PvZ_Fusion_Translator.Patches.GameObjects;
using PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects;
using PvZ_Fusion_Translator.Patches.Modes.Super_Editor;
using System.Diagnostics;
using System.Text.RegularExpressions;
using Unity.Burst.Intrinsics;
@ -140,19 +143,7 @@ namespace PvZ_Fusion_Translator
string json;
string thePlantName = "";
string currentLanguage = Utils.Language.ToString();
string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
string path = Path.Combine(almanacDir, "LawnStringsTranslate.json");
if (!File.Exists(path))
{
Log.LogError($"LawnStringsTranslate.json file not found at path: {path}");
Log.LogError("Plant name could not be found!");
thePlantName = "";
}
else
{
json = File.ReadAllText(path);
json = AlmanacPlantMenu_Patch.almanacJson;
AlmanacPlantBank.PlantData plantData = JsonUtility.FromJson<AlmanacPlantBank.PlantData>(json);
foreach (AlmanacPlantBank.PlantInfo plantInfo in plantData.plants)
@ -161,7 +152,6 @@ namespace PvZ_Fusion_Translator
{
thePlantName = plantInfo.name;
}
}
}
return thePlantName;
@ -179,7 +169,7 @@ namespace PvZ_Fusion_Translator
string originalPath = Path.Combine(dumpDir, "LawnStrings.json");
string path = Path.Combine(almanacDir, "LawnStringsTranslate.json");
if ((!File.Exists(path)) || (!File.Exists(originalPath)))
if ((!File.Exists(originalPath)))
{
if(log)
{
@ -193,7 +183,7 @@ namespace PvZ_Fusion_Translator
bool foundPlantName = false;
originalJson = File.ReadAllText(originalPath);
translatedJson = File.ReadAllText(path);
translatedJson = AlmanacPlantMenu_Patch.almanacJson;
AlmanacPlantBank.PlantData originalPlantData = JsonUtility.FromJson<AlmanacPlantBank.PlantData>(originalJson);
AlmanacPlantBank.PlantData translatedPlantData = JsonUtility.FromJson<AlmanacPlantBank.PlantData>(translatedJson);
@ -228,18 +218,8 @@ namespace PvZ_Fusion_Translator
string theZombieName = "";
string currentLanguage = Utils.Language.ToString();
string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
string path = Path.Combine(almanacDir, "ZombieStringsTranslate.json");
if (!File.Exists(path))
{
Log.LogError($"ZombieStringsTranslate.json file not found at path: {path}");
Log.LogError("Zombie name could not be found!");
theZombieName = "";
}
else
{
json = File.ReadAllText(path);
json = AlmanacZombieMenu_Patch.almanacJson;
ZombieAlmanacData zombieData = JsonUtility.FromJson<ZombieAlmanacData>(json);
foreach (ZombieInfo zombieInfo in zombieData.zombies)
@ -249,7 +229,6 @@ namespace PvZ_Fusion_Translator
theZombieName = zombieInfo.name;
}
}
}
return theZombieName;
}
@ -268,19 +247,14 @@ namespace PvZ_Fusion_Translator
string originalPath = Path.Combine(dumpDir, "LawnStrings.json");
string path = Path.Combine(almanacDir, "LawnStringsTranslate.json");
if ((!File.Exists(path)))
{
Log.LogError($"LawnStringsTranslate.json file not found at path: {path}");
}
else if ((!File.Exists(originalPath)))
if ((!File.Exists(originalPath)))
{
Log.LogError($"LawnStrings.json file not found at path: {originalPath}");
}
else
{
originalJson = File.ReadAllText(originalPath);
translatedJson = File.ReadAllText(path);
translatedJson = AlmanacPlantMenu_Patch.almanacJson;
AlmanacPlantBank.PlantData originalPlantData = JsonUtility.FromJson<AlmanacPlantBank.PlantData>(originalJson);
AlmanacPlantBank.PlantData translatedPlantData = JsonUtility.FromJson<AlmanacPlantBank.PlantData>(translatedJson);
@ -364,6 +338,44 @@ namespace PvZ_Fusion_Translator
return false;
}
public static bool useLocal = MelonPreferences.GetEntryValue<bool>("PvZ_Fusion_Translator", "UseLocal");
public static void SwapLocalData()
{
Utils.useLocal = (Utils.useLocal) ? false : true;
MelonPreferences.SetEntryValue<bool>("PvZ_Fusion_Translator", "UseLocal", Utils.useLocal);
StringStore.Reload();
}
public static async Task<string> GetDataFromWeb(string url, bool isLog = true)
{
if (isLog)
{
Log.LogInfo($"Attempting to read {url}");
}
var dataRequest = await new HttpClient().GetAsync(url);
if(dataRequest.StatusCode == System.Net.HttpStatusCode.OK)
{
if (isLog)
{
Log.LogInfo($"Successfully loaded data from {url}!");
}
string content = await dataRequest.Content.ReadAsStringAsync();
return content;
}
else
{
if (isLog)
{
Log.LogError($"Failed to load data from {url}! Falling back...");
Log.LogError($"Status code: {dataRequest.StatusCode}");
}
new CancellationTokenSource().Cancel();
return null;
}
}
#if MULTI_LANGUAGE
internal static void ChangeLanguage(string language)
{
@ -444,10 +456,18 @@ namespace PvZ_Fusion_Translator
{ LanguageEnum.Romanian, "Română"}
};
public static Dictionary<ToggleEnum, string> ToggleNames = new Dictionary<ToggleEnum, string>()
{
{ ToggleEnum.Textures, "Textures"},
{ ToggleEnum.Audio, "Audio"},
{ ToggleEnum.SwapLocal, "<size=85%>Swap Local\nTranslations"}
};
public enum ToggleEnum
{
Textures,
Audio,
SwapLocal,
TOGGLE_END
}
}