using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ObeliskRU")] [assembly: AssemblyDescription("Russian localization pack for supported Valheim mods")] [assembly: AssemblyCompany("DragonMotion")] [assembly: AssemblyProduct("ObeliskRU")] [assembly: ComVisible(false)] [assembly: Guid("b86875d4-c679-42d2-a813-23c2b953502c")] [assembly: AssemblyFileVersion("0.2.11.0")] [assembly: AssemblyInformationalVersion("0.2.11")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.2.11.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ObeliskRU { internal static class AmazingNatureTutorialLocalization { internal const string HeaderToken = "$obelisk_ban_header"; internal const string RecipeToken = "$obelisk_ban_recipes"; private const string TokenPrefix = "$obelisk_ban_"; private const string RawNamePrefix = "BAN conversions:"; private const string RawTitlePrefix = "BAN-"; private const string PatchedTitlePrefix = "Преобразования BAN — "; private static readonly IReadOnlyDictionary Titles = new Dictionary(StringComparer.Ordinal) { ["eitrrefinery"] = "Очиститель эйтра", ["composter_bal"] = "Компостер", ["smelter"] = "Плавильня", ["blastfurnace"] = "Доменная печь", ["piece_sawmill_bal"] = "Лесопилка", ["StoneboundKiln_bal"] = "Каменная углежоговая печь", ["fermenter"] = "Бродильная бочка", ["piece_oven"] = "Печь", ["piece_cookingstation"] = "Стойка для готовки", ["piece_cookingstation_iron"] = "Железная стойка для готовки", ["piece_singleIronRack_bal"] = "Одинарная железная стойка для готовки", ["piece_spinningwheel"] = "Прялка", ["charcoal_kiln"] = "Угольная печь", ["oilpress_bal"] = "Выжиматель" }; private static readonly IReadOnlyDictionary LegacyTitles = BuildLegacyTitles(); internal static void AddWords(IDictionary words) { words["$obelisk_ban_header".TrimStart(new char[1] { '$' })] = "УДИВИТЕЛЬНАЯ ПРИРОДА БАЛРОНДА:"; words["$obelisk_ban_recipes".TrimStart(new char[1] { '$' })] = "Рецепты удивительной природы Балронда"; foreach (KeyValuePair title in Titles) { words[GetToken(title.Key).TrimStart(new char[1] { '$' })] = title.Value; } } internal static bool TryGetToken(string source, out string token) { token = null; string text = ExtractPrefabName(source); if (text == null) { return false; } if (string.Equals(text, "Recipes", StringComparison.Ordinal)) { token = "$obelisk_ban_recipes"; return true; } if (!Titles.ContainsKey(text)) { return false; } token = GetToken(text); return true; } internal static bool IsCloneAlias(string source) { if (!string.IsNullOrEmpty(source)) { return source.IndexOf("(Clone)", StringComparison.Ordinal) >= 0; } return false; } internal static bool TryGetLegacyTitleToken(string source, string body, out string token) { token = null; string text = NormalizeBody(body); if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(text) || text.IndexOf("$obelisk_ban_header", StringComparison.Ordinal) < 0 || !LegacyTitles.TryGetValue(source, out var value)) { return false; } token = GetToken(value); return true; } internal static string NormalizeBody(string source) { return source?.Replace("BALROND AMAZING NATURE:", "$obelisk_ban_header").Replace("УДИВИТЕЛЬНАЯ ПРИРОДА БАЛРОНДА:", "$obelisk_ban_header").Replace("Удивительная природа Балронда:", "$obelisk_ban_header") .Replace("Stonebound Kiln accepts all items that Kiln does but requires fuel", "Каменная углежоговая печь принимает всё, что принимает обычная углежоговая печь, но требует топлива.") .Replace("Scrap does not go into Furnace/Smelter. Can be only converted in Smeltworks station", "Лом нельзя помещать в плавильню или доменную печь. Его можно переработать только на металлургической станции."); } private static string GetToken(string prefabName) { return "$obelisk_ban_" + prefabName; } private static IReadOnlyDictionary BuildLegacyTitles() { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (KeyValuePair title in Titles) { dictionary[title.Value] = title.Key; } dictionary["Переработчик эйтра"] = "eitrrefinery"; dictionary["Каменная угольная печь"] = "StoneboundKiln_bal"; dictionary["Костровая стойка"] = "piece_cookingstation"; dictionary["Железная стойка"] = "piece_cookingstation_iron"; dictionary["Малая железная стойка"] = "piece_singleIronRack_bal"; dictionary["Маслопресс"] = "oilpress_bal"; dictionary["Ферментер BAN"] = "fermenter"; return dictionary; } private static string ExtractPrefabName(string source) { if (string.IsNullOrEmpty(source)) { return null; } string text = source; if (text.StartsWith("$obelisk_ban_", StringComparison.Ordinal)) { text = text.Substring("$obelisk_ban_".Length); } else if (text.StartsWith("BAN conversions:", StringComparison.Ordinal)) { text = text.Substring("BAN conversions:".Length); } else if (text.StartsWith("Преобразования BAN — ", StringComparison.Ordinal)) { text = text.Substring("Преобразования BAN — ".Length); } else if (text.StartsWith("BAN-", StringComparison.Ordinal)) { text = text.Substring("BAN-".Length); } else { if (!string.Equals(text, "BAN Recipes", StringComparison.Ordinal) && !string.Equals(text, "Рецепты BAN", StringComparison.Ordinal)) { return null; } text = "Recipes"; } while (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - "(Clone)".Length); } return text; } } internal sealed class AmazingNatureTutorialPatcher { private const string PluginGuid = "balrond.astafaraios.BalrondAmazingNature"; private const string OptimizerHarmonyId = "dragonmotion.balrondnatureoptimizer"; private const string HarmonyId = "obelisk.ru.localization.amazing-nature-tutorial"; private readonly Harmony _harmony = new Harmony("obelisk.ru.localization.amazing-nature-tutorial"); private readonly ManualLogSource _log; internal AmazingNatureTutorialPatcher(ManualLogSource log) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _log = log; } internal int Apply() { Assembly pluginAssembly = ManagerAdapters.GetPluginAssembly("balrond.astafaraios.BalrondAmazingNature"); if (pluginAssembly == null) { return 0; } return 0 + PatchDuplicateGuard(pluginAssembly) + PatchTutorialFinalizer(); } private int PatchDuplicateGuard(Assembly assembly) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown MethodInfo methodInfo = assembly?.GetType("BalrondNature.ConversionChanges")?.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "buildConversionStationTutorialTag" && method.GetParameters().Length == 3); MethodInfo methodInfo2 = AccessTools.Method(typeof(AmazingNatureTutorialPatcher), "AllowTutorial", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { return 0; } try { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return 1; } catch (Exception ex) { _log.LogWarning((object)("Amazing Nature duplicate-tutorial guard skipped: " + ex.GetBaseException().Message)); return 0; } } private int PatchTutorialFinalizer() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(Tutorial), "Awake", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(AmazingNatureTutorialPatcher), "NormalizeTutorialTexts", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { _log.LogWarning((object)"Amazing Nature tutorial finalizer was not installed because Tutorial.Awake was not found."); return 0; } try { HarmonyMethod val = new HarmonyMethod(methodInfo2); val.priority = 0; val.after = new string[2] { "balrond.astafaraios.BalrondAmazingNature", "dragonmotion.balrondnatureoptimizer" }; HarmonyMethod val2 = val; _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return 1; } catch (Exception ex) { _log.LogWarning((object)("Amazing Nature tutorial finalizer skipped: " + ex.GetBaseException().Message)); return 0; } } private static bool AllowTutorial(GameObject __0) { if (!((Object)(object)__0 == (Object)null)) { return !((Object)__0).name.EndsWith("(Clone)", StringComparison.Ordinal); } return true; } private static void NormalizeTutorialTexts(Tutorial __instance) { if (__instance?.m_texts == null) { return; } for (int i = 0; i < __instance.m_texts.Count; i++) { TutorialText val = __instance.m_texts[i]; if (val != null && AmazingNatureTutorialLocalization.TryGetToken(val.m_name, out var token)) { val.m_topic = token; val.m_label = token; val.m_text = AmazingNatureTutorialLocalization.NormalizeBody(val.m_text); } } } } internal static class CulinaryCategoryAdapter { private static readonly IReadOnlyDictionary Categories = new Dictionary(StringComparer.Ordinal) { ["Distant Origins"] = "Древние истоки", ["Distant Origins Decor"] = "Декор древних истоков", ["Culinary Horizons"] = "Кулинарные горизонты", ["Enhanced Foods"] = "Улучшенные блюда", ["Forgotten Recipes"] = "Забытые рецепты", ["Banquets"] = "Пиры" }; private static readonly string[] PluginGuids = new string[4] { "Insanity.Culinary_Horizons", "Dreanegade.Eternal_Legends", "Dreanegade.Hunter_Legacy", "Dreanegade.Magic_Supremacy" }; internal static int Apply(ManualLogSource log) { int num = 0; string[] pluginGuids = PluginGuids; for (int i = 0; i < pluginGuids.Length; i++) { Assembly pluginAssembly = ManagerAdapters.GetPluginAssembly(pluginGuids[i]); if (pluginAssembly != null) { num += RewriteRegisteredCategories(pluginAssembly); } } return num + RewriteCulinaryLongCategoryHints(); } private static int RewriteRegisteredCategories(Assembly assembly) { Type? type = assembly.GetType("PieceManager.BuildPiece"); FieldInfo fieldInfo = type?.GetField("registeredPieces", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo fieldInfo2 = type?.GetField("Category", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(fieldInfo?.GetValue(null) is IEnumerable enumerable) || fieldInfo2 == null) { return 0; } int num = 0; foreach (object item in enumerable) { object value = fieldInfo2.GetValue(item); FieldInfo fieldInfo3 = value?.GetType().GetField("custom", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (fieldInfo3?.GetValue(value) is string key && Categories.TryGetValue(key, out var value2)) { fieldInfo3.SetValue(value, value2); num++; } } return num; } private static int RewriteCulinaryLongCategoryHints() { if (!(((ManagerAdapters.GetPluginAssembly("Insanity.Culinary_Horizons")?.GetType("Culinary_Horizons.Culinary_HorizonsPlugin+PieceCategoryTabTextFix"))?.GetField("longCategories", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null) is string[] array) || array.Length != 3) { return 0; } array[0] = "Забытые"; array[1] = "рецепты"; array[2] = "Улучшенные блюда"; return 3; } } internal static class DualMasteryAdapter { private const string PluginGuid = "balrond.astafaraios.BalrondDualMastery"; private const int DualWieldSkillId = 874298; internal static int Apply(TranslationCatalog catalog) { if (!catalog.Words.TryGetValue("skill_874298", out var value)) { return 0; } if (!((ManagerAdapters.GetPluginAssembly("balrond.astafaraios.BalrondDualMastery")?.GetType("BalrondDualWield.DualWieldSkills"))?.GetField("SkillsById", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) is IDictionary dictionary) || !dictionary.Contains(874298)) { return 0; } object instance = dictionary[874298]; if (ReflectionUtil.GetMember(instance, "SkillName") is string text && text == value) { return 0; } return ReflectionUtil.SetMember(instance, "SkillName", value) ? 1 : 0; } } internal sealed class HumanoidRandomizerPatcher { private const string PluginGuid = "balrond.astafaraios.BalrondHumanoidRandomizer"; private const string HarmonyId = "obelisk.ru.localization.humanoid-randomizer"; private static readonly MethodInfo TranslateVariantMethod = AccessTools.Method(typeof(HumanoidRandomizerPatcher), "TranslateVariant", (Type[])null, (Type[])null); private static readonly MethodInfo StringConcatThree = AccessTools.Method(typeof(string), "Concat", new Type[3] { typeof(string), typeof(string), typeof(string) }, (Type[])null); private static readonly IReadOnlyDictionary VariantNames = new Dictionary(StringComparer.Ordinal) { ["Guardian"] = "— страж", ["Protector"] = "— защитник", ["Vanguard"] = "— авангардист", ["Defender"] = "— оборонитель", ["Sentry"] = "— часовой", ["Warden"] = "— хранитель", ["Archer"] = "— лучник", ["Firestarter"] = "— поджигатель", ["Sharpshooter"] = "— меткий стрелок", ["Coldblood"] = "— хладнокровный", ["Soulpiercer"] = "— пронзатель душ", ["Hunter"] = "— охотник", ["Assassin"] = "— убийца", ["Cutthroat"] = "— головорез", ["Brute"] = "— громила", ["Duelist"] = "— дуэлянт", ["Berserker"] = "— берсерк", ["Bonebreaker"] = "— костолом", ["Swordmaster"] = "— мастер меча", ["Executioner"] = "— палач", ["Marauder"] = "— мародёр", ["Mercenary"] = "— наёмник", ["Fighter"] = "— боец", ["Warrior"] = "— воин", ["Bruiser"] = "— вышибала", ["Solider"] = "— солдат", ["Thug"] = "— бандит", ["Scout"] = "— разведчик", ["Savage"] = "— дикарь", ["Pugilist"] = "— кулачный боец", ["Corsair"] = "— корсар", ["Sailor"] = "— моряк", ["Stabber"] = "— ножевик", ["Thief"] = "— вор", ["Lightbringer"] = "— светоносец", ["Reaper"] = "— жнец", ["Frozenhammer"] = "— ледяной молот", ["Icecrusher"] = "— ледокрушитель", ["Soulshreder"] = "— терзатель душ", ["Blackblade"] = "— чёрный клинок", ["Soulcalibur"] = "— клинок душ", ["Splasher"] = "— брызгун", ["Pyromancer"] = "— пиромант", ["Cryomancer"] = "— криомант", ["Thundercaller"] = "— призыватель грома", ["Venomancer"] = "— ядомант", ["Cultist"] = "— культист", ["Mage"] = "— маг", ["Cleric"] = "— жрец", ["Reaver"] = "— разоритель", ["Arbalist"] = "— арбалетчик" }; private readonly ManualLogSource _log; private readonly Harmony _harmony = new Harmony("obelisk.ru.localization.humanoid-randomizer"); internal HumanoidRandomizerPatcher(ManualLogSource log) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _log = log; } internal int Apply() { Assembly pluginAssembly = ManagerAdapters.GetPluginAssembly("balrond.astafaraios.BalrondHumanoidRandomizer"); if (pluginAssembly == null) { return 0; } int num = 0; try { num += PatchVariantName(pluginAssembly); } catch (Exception ex) { _log.LogWarning((object)("HumanoidRandomizer contextual localization was only partially installed: " + ex.GetBaseException().Message)); } _log.LogInfo((object)$"HumanoidRandomizer: installed {num} constant-only localization patches."); return num; } private int PatchVariantName(Assembly assembly) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown MethodInfo methodInfo = assembly.GetType("BalrondHumanoidRandomizer.Monsters.ItemSetBuilder")?.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo candidate) => candidate.Name == "GeneratePrefabs" && candidate.GetParameters().Length == 3); if (methodInfo == null) { _log.LogWarning((object)"HumanoidRandomizer: the three-argument GeneratePrefabs method was not found."); return 0; } _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(HumanoidRandomizerPatcher), "TranslateVisibleVariantName", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null); return 1; } private static IEnumerable TranslateVisibleVariantName(IEnumerable instructions) { List source = instructions.ToList(); for (int index = 0; index < source.Count; index++) { CodeInstruction val = source[index]; if (val.opcode == OpCodes.Ldstr && object.Equals(val.operand, "Rancid")) { val.operand = "Гнилой"; } yield return val; if (IsVisibleSuffixOperand(source, index)) { yield return new CodeInstruction(OpCodes.Call, (object)TranslateVariantMethod); } } } private static bool IsVisibleSuffixOperand(IReadOnlyList instructions, int index) { if (!(instructions[index].operand is FieldInfo fieldInfo) || instructions[index].opcode != OpCodes.Ldfld || fieldInfo.Name != "m_name" || fieldInfo.DeclaringType?.FullName != "Humanoid+ItemSet") { return false; } int num = index + 1; if (num >= instructions.Count || !CodeInstructionExtensions.Calls(instructions[num], StringConcatThree)) { return false; } for (int num2 = index - 1; num2 >= Math.Max(0, index - 5); num2--) { if (!(instructions[num2].opcode != OpCodes.Ldstr)) { return object.Equals(instructions[num2].operand, " "); } } return false; } private static string TranslateVariant(string source) { if (source == null || !VariantNames.TryGetValue(source, out var value)) { return source; } return value; } } internal static class InventorySlotsAdapter { private sealed class SlotName { internal string Original { get; } internal string Russian { get; } internal SlotName(string original, string russian) { Original = original; Russian = russian; } } private static readonly IReadOnlyDictionary RussianNames = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["helmet"] = new SlotName("Helmet", "Шлем"), ["chest"] = new SlotName("Chest", "Нагрудник"), ["legs"] = new SlotName("Legs", "Поножи"), ["cape"] = new SlotName("Cape", "Плащ"), ["utility"] = new SlotName("Utility", "Аксессуар"), ["trinket"] = new SlotName("Trinket", "Талисман"), ["circlet"] = new SlotName("Circlet", "Венец"), ["wishbone"] = new SlotName("Wishbone", "Дужка"), ["demister"] = new SlotName("Demister", "Висп"), ["jewelcrafting.necklace"] = new SlotName("Necklace", "Ожерелье"), ["jewelcrafting.ring"] = new SlotName("Ring", "Кольцо"), ["smoothbrainbackpacks.backpack"] = new SlotName("Backpack", "Рюкзак"), ["adventurebackpacks.backpack"] = new SlotName("Backpack", "Рюкзак"), ["rustybags.bag"] = new SlotName("Bag", "Сумка"), ["rustybags.quiver"] = new SlotName("Quiver", "Колчан"), ["magicsupremacy.belt"] = new SlotName("MagicBelt", "Магический пояс") }; internal static int Apply(ManualLogSource log) { Assembly pluginAssembly = ManagerAdapters.GetPluginAssembly("sighsorry.InventorySlots"); Type type = pluginAssembly?.GetType("InventorySlots.InventorySlotsPlugin"); Type type2 = pluginAssembly?.GetType("InventorySlots.SlotDefinition"); if (!(type?.GetProperty("SlotDefinitions", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null) is IList list) || type2 == null) { return 0; } PropertyInfo property = type2.GetProperty("Id", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); PropertyInfo property2 = type2.GetProperty("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo field = type2.GetField("_accepts", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); PropertyInfo property3 = type2.GetProperty("Kind", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); PropertyInfo property4 = type2.GetProperty("QuickSlotIndex", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property == null || property2 == null || field == null || property3 == null || property4 == null) { return 0; } ConstructorInfo constructor = type2.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[5] { typeof(string), typeof(string), property3.PropertyType, field.FieldType, typeof(int) }, null); if (constructor == null) { return 0; } int num = 0; for (int i = 0; i < list.Count; i++) { object obj = list[i]; if (property.GetValue(obj, null) is string text && RussianNames.TryGetValue(text, out var value) && string.Equals(property2.GetValue(obj, null) as string, value.Original, StringComparison.Ordinal)) { object value2 = property3.GetValue(obj, null); object value3 = field.GetValue(obj); object value4 = property4.GetValue(obj, null); if (value2 != null && value3 != null && value4 != null) { list[i] = constructor.Invoke(new object[5] { text, value.Russian, value2, value3, value4 }); num++; } } } return num; } } internal sealed class KnownTextMigrationPatcher { private const string HarmonyId = "obelisk.ru.localization.known-text-migration"; private static readonly IReadOnlyDictionary TitleMigrations = BuildTitleMigrations(); private static readonly IReadOnlyDictionary TextMigrations = new Dictionary(StringComparer.Ordinal) { ["This is your new table for all things archery. A long the way as you progress more and more recipes for arrows will show up here"] = "Это новый стол для всего, что связано со стрельбой из лука. По мере развития здесь будет появляться всё больше рецептов стрел.", ["This is Scribe's table for creating Schematics. Those items provide information about ship upgrades"] = "Это чертёжный стол для создания схем с описанием корабельных улучшений.", ["This Shipyard will help you improve your ships. Put schematic in to the cargo and activate the table. If there is a ship in range that can be upgraded item put in to the cargo will be used."] = "Эта верфь поможет улучшать суда. Положите чертёж в грузовой отсек и активируйте стол. Если рядом есть подходящее судно, чертёж будет использован." }; private static FieldInfo _localPlayerField; private static FieldInfo _knownTextsField; private static ManualLogSource _log; private readonly Harmony _harmony = new Harmony("obelisk.ru.localization.known-text-migration"); private readonly ManualLogSource _instanceLog; internal KnownTextMigrationPatcher(ManualLogSource log) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _instanceLog = log; } internal int Apply() { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown Type type = AccessTools.TypeByName("Player"); Type type2 = AccessTools.TypeByName("ZPackage"); MethodInfo methodInfo = ((type == null || type2 == null) ? null : AccessTools.Method(type, "Load", new Type[1] { type2 }, (Type[])null)); _localPlayerField = type?.GetField("m_localPlayer", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _knownTextsField = type?.GetField("m_knownTexts", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo methodInfo2 = AccessTools.Method(typeof(KnownTextMigrationPatcher), "MigrateKnownTexts", (Type[])null, (Type[])null); if (methodInfo == null || _localPlayerField == null || _knownTextsField == null || methodInfo2 == null) { _instanceLog.LogWarning((object)"Known-text migration was not installed because the Valheim save fields were not found."); return 0; } try { _log = _instanceLog; _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return 1; } catch (Exception ex) { _instanceLog.LogWarning((object)("Known-text migration was not installed: " + ex.GetBaseException().Message)); return 0; } } private static void MigrateKnownTexts(object __instance) { try { MigrateKnownTextsSafely(__instance); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Saved Compendium text migration skipped: " + ex.GetBaseException().Message)); } } } private static void MigrateKnownTextsSafely(object instance) { if (_localPlayerField?.GetValue(null) != instance || !(_knownTextsField?.GetValue(instance) is IDictionary dictionary)) { return; } int num = 0; foreach (KeyValuePair titleMigration in TitleMigrations) { num += MigrateTitle(dictionary, titleMigration.Key, titleMigration.Value, clone: false); } List list = new List(); foreach (DictionaryEntry item in dictionary) { list.Add(item.Key); } foreach (object item2 in list) { if (item2 is string text) { string body = dictionary[item2] as string; if ((AmazingNatureTutorialLocalization.TryGetToken(text, out var token) || AmazingNatureTutorialLocalization.TryGetLegacyTitleToken(text, body, out token)) && !string.Equals(text, token, StringComparison.Ordinal)) { num += MigrateTitle(dictionary, text, token, AmazingNatureTutorialLocalization.IsCloneAlias(text)); } } } list.Clear(); foreach (DictionaryEntry item3 in dictionary) { list.Add(item3.Key); } foreach (object item4 in list) { if (dictionary[item4] is string text2) { string text3 = NormalizeMigratedText(text2); if (!string.Equals(text2, text3, StringComparison.Ordinal)) { dictionary[item4] = text3; num++; } } } if (num != 0) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"Migrated {num} saved Compendium text entries to Russian."); } } } private static string TranslateText(string source) { if (source == null || !TextMigrations.TryGetValue(source, out var value)) { return source; } return value; } private static string NormalizeMigratedText(string source) { return AmazingNatureTutorialLocalization.NormalizeBody(TranslateText(source)); } private static int MigrateTitle(IDictionary knownTexts, string source, string target, bool clone) { if (!knownTexts.Contains(source)) { return 0; } string text = NormalizeMigratedText(knownTexts[source] as string); knownTexts.Remove(source); if (!knownTexts.Contains(target)) { if (!clone || HasTutorialBody(text)) { knownTexts[target] = text; } } else if (knownTexts[target] is string source2) { string text2 = NormalizeMigratedText(source2); knownTexts[target] = ((!HasTutorialBody(text2) && HasTutorialBody(text)) ? text : text2); } return 1; } private static bool HasTutorialBody(string text) { if (string.IsNullOrWhiteSpace(text)) { return false; } int num = text.IndexOf("", StringComparison.OrdinalIgnoreCase); if (num >= 0) { return !string.IsNullOrWhiteSpace(text.Substring(num + "".Length)); } return true; } private static IReadOnlyDictionary BuildTitleMigrations() { return new Dictionary(StringComparer.Ordinal) { ["Fletcher's table"] = "Стол лучника", ["Scribe's table"] = "Чертёжный стол", ["Scribe's Table"] = "Чертёжный стол", ["Shipyard Station"] = "Верфь" }; } } internal sealed class LiteralPatchSpec { internal string PluginGuid { get; } internal string TypeName { get; } internal string MethodName { get; } internal IReadOnlyDictionary Replacements { get; } internal LiteralPatchSpec(string pluginGuid, string typeName, string methodName, IReadOnlyDictionary replacements) { PluginGuid = pluginGuid; TypeName = typeName; MethodName = methodName; Replacements = replacements; } } internal sealed class LiteralPatcher { private const string HarmonyId = "obelisk.ru.localization.constants"; private static readonly Dictionary> ReplacementsByMethod = new Dictionary>(); private readonly ManualLogSource _log; private readonly Harmony _harmony = new Harmony("obelisk.ru.localization.constants"); internal LiteralPatcher(ManualLogSource log) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _log = log; } internal int Apply(IEnumerable specs) { //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Expected O, but got Unknown int num = 0; int num2 = 0; MethodInfo methodInfo = AccessTools.Method(typeof(LiteralPatcher), "Transpile", (Type[])null, (Type[])null); foreach (IGrouping<(string, string, string), LiteralPatchSpec> item in from spec in specs group spec by (PluginGuid: spec.PluginGuid, TypeName: spec.TypeName, MethodName: spec.MethodName)) { Type type = ManagerAdapters.GetPluginAssembly(item.Key.Item1)?.GetType(item.Key.Item2); if (type == null) { continue; } Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (LiteralPatchSpec item2 in item) { foreach (KeyValuePair replacement in item2.Replacements) { dictionary[replacement.Key] = replacement.Value; } } HashSet matchedSources = new HashSet(StringComparer.Ordinal); foreach (MethodBase declaredTarget in GetDeclaredTargets(type, item.Key.Item3)) { int num3; try { num3 = 0; foreach (CodeInstruction originalInstruction in PatchProcessor.GetOriginalInstructions(declaredTarget, (ILGenerator)null)) { if (originalInstruction.opcode == OpCodes.Ldstr && originalInstruction.operand is string text && dictionary.ContainsKey(text)) { matchedSources.Add(text); num3++; } } } catch (Exception ex) { _log.LogWarning((object)("Literal audit skipped " + type.FullName + "." + declaredTarget.Name + ": " + ex.GetBaseException().Message)); continue; } if (num3 != 0) { try { ReplacementsByMethod[declaredTarget] = dictionary; _harmony.Patch(declaredTarget, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null); num++; num2 += num3; } catch (Exception ex2) { ReplacementsByMethod.Remove(declaredTarget); _log.LogWarning((object)("Literal patch skipped " + type.FullName + "." + declaredTarget.Name + ": " + ex2.GetBaseException().Message)); } } } foreach (string item3 in dictionary.Keys.Where((string source) => !matchedSources.Contains(source))) { _log.LogWarning((object)("Literal audit miss " + type.FullName + "." + item.Key.Item3 + ": '" + EscapeForLog(item3) + "'")); } } _log.LogInfo((object)$"Installed {num} startup constant-replacement patches for {num2} IL constants."); return num; } private static IEnumerable GetDeclaredTargets(Type type, string methodName) { BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; if (methodName == ".ctor") { return type.GetConstructors(bindingAttr).Cast(); } if (methodName == ".cctor") { if (!(type.TypeInitializer == null)) { return new MethodBase[1] { type.TypeInitializer }; } return Enumerable.Empty(); } return (from method in type.GetMethods(bindingAttr) where method.Name == methodName && !method.IsAbstract && !method.ContainsGenericParameters select method).Cast(); } private static string EscapeForLog(string text) { return text.Replace("\r", "\\r").Replace("\n", "\\n"); } private static IEnumerable Transpile(IEnumerable instructions, MethodBase __originalMethod) { ReplacementsByMethod.TryGetValue(__originalMethod, out var replacements); foreach (CodeInstruction instruction in instructions) { if (replacements != null && instruction.opcode == OpCodes.Ldstr && instruction.operand is string key && replacements.TryGetValue(key, out var value)) { instruction.operand = value; } yield return instruction; } } } internal static class LiteralRegistry { internal static IReadOnlyList Create() { return CreateCore(null); } internal static IReadOnlyList CreateForLoadedPlugins() { return CreateCore(delegate(string guid) { if (Chainloader.PluginInfos.TryGetValue(guid, out var value)) { object obj; if (value == null) { obj = null; } else { BepInPlugin metadata = value.Metadata; obj = ((metadata != null) ? metadata.Version : null); } if ((Version?)obj != null) { return value.Metadata.Version.ToString(); } } return (string)null; }); } private static IReadOnlyList CreateCore(Func pluginVersionResolver) { List result = new List(); AddPlantMods(result); AddAzuMods(result); AddUtilityMods(result); AddPerfectPlacement(result); AddLocationPlacementAccelerator(result); AddMoveBuildPiecesStaticText(result); AddConnectionErrors(result, pluginVersionResolver); AddBossAdd(result); AddMoreWorldLocations(result); AddVnei(result); AddHuginn(result); AddJotunn(result); AddEpicLoot(result); AddInventorySlots(result); AddCreatureLevelControl(result); AddPassivePowers(result); AddXPortal(result); AddBalrondShipyard(result); AddBalrondGaps(result); return result; } private static void AddPassivePowers(List result) { Add(result, "org.bepinex.plugins.passivepowers", "PassivePowers.BonusSpiritDefense", "get_Desc", "$powers_additional_spirit_damage", "$powers_additional_spirit_defense"); } private static void AddPlantMods(List result) { Add(result, "advize.PlantEasily", "Advize_PlantEasily.HoverTextPatches", "BuildAreaHint", " (area)", " (в области)"); Add(result, "advize.PlantEasily", "Advize_PlantEasily.HoverTextPatches", "Postfix", "\nReplant as: ", "\nПосадить снова: "); Add(result, "advize.PlantEasily", "Advize_PlantEasily.KeyHintPatches", "CreateKeyBoardHints", "Grid Size", "Размер сетки"); Add(result, "advize.PlantEasily", "Advize_PlantEasily.KeyHintPatches", "UpdateGamepadHints", "Resize Grid ", "Изменить сетку "); Add(result, "advize.PlantEasily", "Advize_PlantEasily.InputPatches", "Prefix", "PlantEasily.ModActive: {0}", "PlantEasily — мод включён: {0}", "PlantEasily.SnapActive: {0}", "PlantEasily — привязка включена: {0}", "PlantEasily.ReplantOnHarvest: {0}", "PlantEasily — пересадка включена: {0}"); Add(result, "advize.PlantEverything", "Advize_PlantEverything.HoverTextPatches", "FormatTime", "{0:D2}h {1:D2}m {2:D2}s", "{0:D2} ч {1:D2} мин {2:D2} с", "{0:D2}m {1:D2}s", "{0:D2} мин {1:D2} с", "{0:D2}s", "{0:D2} с"); } private static void AddAzuMods(List result) { Add(result, "Azumatt.AzuAreaRepair", "AzuAreaRepair.PlayerRepairPatch", "RepairArea", "{0} pieces repaired", "Отремонтировано построек: {0}"); Add(result, "Azumatt.AzuAutoStore", "AzuAutoStore.Util.Functions", "TryStore", "] is not in nearby containers", "] нет в ближайших контейнерах", "] cannot be stored based on configuration settings", "] нельзя сложить при текущих настройках"); Add(result, "Azumatt.AzuAutoStore", "AzuAutoStore.Util.Functions", "StoreSuccess", "Stored {0} items from your inventory into nearby containers", "Переложено предметов в ближайшие контейнеры: {0}"); Add(result, "Azumatt.AzuAutoStore", "AzuAutoStore.Interfaces.BackpackContainer", "TryStore", " is not in nearby containers", " нет в ближайших контейнерах"); Add(result, "Azumatt.AzuAutoStore", "AzuAutoStore.Interfaces.kgDrawer", "TryStoreToKG", " is not in nearby containers", " нет в ближайших контейнерах"); Add(result, "Azumatt.AzuAutoStore", "AzuAutoStore.Interfaces.mkzDrawer", "TryStoreThisItem", "] is not in nearby drawers", "] нет в ближайших ящиках"); Add(result, "Azumatt.AzuAutoStore", "AzuAutoStore.TerminalInitTerminalPatch", "Postfix", "[prefab name/query text] - search for items in chests near the player. It uses the prefab name", "[ID предмета/текст] — найти предметы в сундуках рядом с игроком; поиск учитывает ID предмета"); Add(result, "Azumatt.AzuAutoStore", "AzuAutoStore.TerminalInitTerminalPatch+<>c", "b__2_0", "Found {0} items matching '{1}' in nearby containers.", "В ближайших контейнерах найдено предметов по запросу «{1}»: {0}.", "No items matching '", "В ближайших контейнерах нет предметов по запросу «", "' found in nearby containers.", "»."); Add(result, "Azumatt.AzuAutoStore", "Backpacks.ItemContainer", "GetPressOpenText", "Press [", "Нажмите [", "] to open", "], чтобы открыть"); AddCraftyCountHover(result, "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes.Patches.FireplaceGetHoverTextPatch", "Postfix", includeItemName: false); AddCraftyCountHover(result, "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes.Patches.TurretGetHoverTextPatch", "Postfix", includeItemName: false); AddCraftyCountHover(result, "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes.Patches.OverrideHoverTextSg", "UpdateAddFuelSwitchHoverText", includeItemName: true); Add(result, "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes.Patches.OverrideHoverText", "UpdateAddWoodSwitchHoverText", "\n[{0} + $KEY_Use] $piece_smelter_add {1} {2} from Inventory & Nearby Containers", "\n[{0} + $KEY_Use] $piece_smelter_add {1} {2} из инвентаря и ближайших контейнеров"); Add(result, "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes.Patches.OverrideHoverText", "UpdateAddOreSwitchHoverText", "\n[{0} + $KEY_Use] {1} {2} from Inventory & Nearby Containers", "\n[{0} + $KEY_Use] {1} {2} из инвентаря и ближайших контейнеров"); Add(result, "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes.Patches.OverrideHoverTextFermenter", "UpdateAddSwitchHoverText", "\n[{0} + $KEY_Use] {1} {2} from Inventory & Nearby Containers", "\n[{0} + $KEY_Use] {1} {2} из инвентаря и ближайших контейнеров"); Add(result, "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes.Patches.SE_ContainerPull", "CreateEffect", "Preventing Pulling", "Блокировка автоподачи", "Prevents pulling from nearby containers & backpacks", "Запрещает брать ресурсы из ближайших контейнеров и рюкзаков"); Add(result, "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes.Patches.ShieldGeneratorOnAddFuelPatch", "Prefix", "$msg_added {0} items", "$msg_added {0} предметов"); Add(result, "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes.Patches.Turret_UseItem_Patch", "Prefix", "$msg_added {0} items", "$msg_added {0} предметов"); Add(result, "Azumatt.AzuCraftyBoxes", "Backpacks.ItemContainer", "GetPressOpenText", "Press [", "Нажмите [", "] to open", "], чтобы открыть"); } private static void AddCraftyCountHover(List result, string guid, string type, string method, bool includeItemName) { if (includeItemName) { Add(result, guid, type, method, "{0} {1} in inventory", "{0} {1} в инвентаре", "{0} {1} in nearby containers", "{0} {1} в ближайших контейнерах", "{0} needed to fill", "{0} не хватает для заполнения", " and ", " и ", "\n[{0} + $KEY_Use] Add {1}", "\n[{0} + $KEY_Use] Добавить {1}"); } else { Add(result, guid, type, method, "{0} in inventory", "{0} в инвентаре", "{0} in nearby containers", "{0} в ближайших контейнерах", "{0} needed to fill", "{0} не хватает для заполнения", " and ", " и "); } } private static void AddUtilityMods(List result) { Add(result, "Azumatt.BuildCameraCHE", "Valheim_Build_Camera.Utils", "EnableBuildMode", "Entering Build Mode.", "Включён режим строительства."); Add(result, "Azumatt.FirstPersonMode", "FirstPersonMode.Util.Functions", "HandleFirstPersonMode", "Changed fov to: {0}", "Поле зрения изменено: {0}"); Add(result, "vapok.mods.adventurebackpacks", "Vapok.Common.Managers.Skill.Skill", "Patch_Skills_CheatRaiseskill", "Skill increased ", "Навык повышен: ", "Skill ", "Навык "); Add(result, "vapok.mods.adventurebackpacks", "Vapok.Common.Managers.Skill.Skill", "Patch_Skills_CheatResetSkill", "Skill ", "Навык «", " reset", "» сброшен"); Add(result, "petri.valheim.takeallcooked", "TakeAllCooked.Patches.CookingStationHoverTextPatch", "GetShortcutDisplayText", "Unbound", "Не назначено"); } private static void AddPerfectPlacement(List result) { Add(result, "Azumatt_and_ValheimPlusDevs.PerfectPlacement", "PerfectPlacement.Patches.ABM", "changeModificationSpeed", "Modification Speed: ", "Скорость изменения: "); Add(result, "Azumatt_and_ValheimPlusDevs.PerfectPlacement", "PerfectPlacement.Patches.ABM", "startMode", "Starting ABM", "Включён расширенный режим строительства", "ABM is active", "Расширенный режим строительства активен"); Add(result, "Azumatt_and_ValheimPlusDevs.PerfectPlacement", "PerfectPlacement.Patches.ABM", "exitMode", "Exiting ABM", "Расширенный режим строительства выключен"); Add(result, "Azumatt_and_ValheimPlusDevs.PerfectPlacement", "PerfectPlacement.Patches.ABM", "resetObjectTransform", "Object has been reset to initial position & rotation.", "Исходные положение и поворот объекта восстановлены."); Add(result, "Azumatt_and_ValheimPlusDevs.PerfectPlacement", "PerfectPlacement.Patches.AEM", "changeModificationSpeed", "Modification Speed: ", "Скорость изменения: "); Add(result, "Azumatt_and_ValheimPlusDevs.PerfectPlacement", "PerfectPlacement.Patches.AEM", "startMode", "Entering AEM", "Включён расширенный режим редактирования", "AEM is active", "Расширенный режим редактирования активен"); Add(result, "Azumatt_and_ValheimPlusDevs.PerfectPlacement", "PerfectPlacement.Patches.AEM", "exitMode", "Exiting AEM", "Расширенный режим редактирования выключен"); Add(result, "Azumatt_and_ValheimPlusDevs.PerfectPlacement", "PerfectPlacement.Patches.AEM", "resetObjectTransform", "Object has been reset to initial position & rotation.", "Исходные положение и поворот объекта восстановлены."); Add(result, "Azumatt_and_ValheimPlusDevs.PerfectPlacement", "PerfectPlacement.Patches.GridAlignment", "Postfix", "Default grid alignment set to ", "Выравнивание по сетке по умолчанию: ", "Grid alignment by default ", "Выравнивание по сетке по умолчанию ", "enabled", "включено", "disabled", "выключено"); } private static void AddLocationPlacementAccelerator(List result) { Add(result, "nickpappas.locationplacementaccelerator", "LPA.GenerationProgress", "BuildModeName", "Survey", "Разведка", "Filter", "Фильтрация", "Force", "Принудительный", "Vanilla", "Обычный", "Replaced", "Заменённый движок", "Transpiled", "Транспилированный движок", " - Parallel", " — параллельно"); Add(result, "nickpappas.locationplacementaccelerator", "LPA.GenerationProgress", "UpdateText", "Placing locations using ", "Размещение локаций: ", "\nFAILED - ATTEMPTING RELAXATION:", "\nНЕ УДАЛОСЬ — ОСЛАБЛЕНИЕ ОГРАНИЧЕНИЙ:", "\nRELAXED CONSTRAINTS:", "\nОГРАНИЧЕНИЯ ОСЛАБЛЕНЫ:", "\nFAILED - COULD NOT PLACE:", "\nНЕ УДАЛОСЬ РАЗМЕСТИТЬ:", " {0} (exhausted {1} relaxation attempts)", " {0} (исчерпано попыток ослабления: {1})"); Add(result, "nickpappas.locationplacementaccelerator", "LPA.ConstraintRelaxer", "BuildAttemptDescription", "Altitude {0:F0}..{1:F0}-->{2:F0}..{3:F0}", "Высота {0:F0}..{1:F0} → {2:F0}..{3:F0}", "Distance {0:F0}..{1:F0}-->{2:F0}..{3:F0}", "Расстояние {0:F0}..{1:F0} → {2:F0}..{3:F0}", "TerrainDelta {0:F1}-->{1:F1}", "Перепад рельефа {0:F1} → {1:F1}", "ExteriorRadius {0:F0}-->{1:F0}", "Внешний радиус {0:F0} → {1:F0}", "Constraints loosened", "Ограничения ослаблены", "[attempt {0}]: {1}", "[попытка {0}]: {1}"); Add(result, "nickpappas.locationplacementaccelerator", "LPA.ConstraintRelaxer", "GetRelaxationSummary", "(Relaxed {0} times)", "(ослаблено раз: {0})", "MinAlt: {0:F0}->{1:F0}", "Мин. высота: {0:F0} → {1:F0}", "MaxDist: {0:F0}->{1:F0}", "Макс. расстояние: {0:F0} → {1:F0}", "MinDist: {0:F0}->{1:F0}", "Мин. расстояние: {0:F0} → {1:F0}", "MaxTerr: {0:F1}->{1:F1}", "Макс. рельеф: {0:F1} → {1:F1}", "ExtRadius: {0:F0}->{1:F0}", "Внешний радиус: {0:F0} → {1:F0}", "(Relaxed {0}x: {1})", "(ослаблено {0} раз: {1})"); Add(result, "nickpappas.locationplacementaccelerator", "LPA.PlacementEngine", "RunInlineRelaxation", "{0} (Relaxation attempt {1})", "{0} (попытка ослабления {1})"); } private static void AddMoveBuildPiecesStaticText(List result) { Add(result, "obelisk.valheim.movebuildpieces", "Obelisk.MoveBuildPieces.MoveController", "BeginRelocationPlacement", "Move", "Перемещение"); } private static void AddConnectionErrors(List result, Func pluginVersionResolver) { string[] array = new string[18] { "advize.PlantEverything", "Azumatt.AzuAreaRepair", "Azumatt.AzuAutoStore", "Azumatt.AzuCraftyBoxes", "Azumatt.AzuWorkbenchTweaks", "Azumatt.BuildCameraCHE", "org.bepinex.plugins.creaturelevelcontrol", "Insanity.Culinary_Horizons", "Dreanegade.Eternal_Legends", "Azumatt.FirstPersonMode", "blacks7ar.FoodDurationMultiplier", "Dreanegade.Hunter_Legacy", "sighsorry.InventorySlots", "Dreanegade.Magic_Supremacy", "warpalicious.More_World_Locations_AIO", "org.bepinex.plugins.odinssteelworks", "org.bepinex.plugins.passivepowers", "Azumatt_and_ValheimPlusDevs.PerfectPlacement" }; foreach (string guid in array) { AddServerSync(result, guid); } string pluginVersion = GetPluginVersion(pluginVersionResolver, "com.jotunn.jotunn", "2.29.2"); Add(result, "com.jotunn.jotunn", "Jotunn.Utils.ModCompatibility", "CreateModModuleLayoutErrorMessage", "Jotunn version on client is higher than server version: " + pluginVersion, "Версия Jotunn на клиенте выше версии сервера: " + pluginVersion, "Jotunn version on server is higher than client version: " + pluginVersion, "Версия Jotunn на сервере выше версии клиента: " + pluginVersion, "Jotunn versions on server and client are not compatible.", "Версии Jotunn на сервере и клиенте несовместимы."); AddCustomVersionErrors(result, pluginVersionResolver); } private static void AddServerSync(List result, string guid) { Add(result, guid, "ServerSync.VersionCheck", "ErrorServer", "Disconnect: The client (", "Отключение: на клиенте (", ") doesn't have the correct ", ") установлена неподходящая версия ", " version ", " — версия "); Add(result, guid, "ServerSync.VersionCheck", "ErrorClient", " is not installed on the server.", " не установлен на сервере.", " needs to be at least version ", ": требуется версия не ниже ", ". You have version ", ". У вас версия ", " may not be higher than version ", ": версия не должна быть выше "); Add(result, guid, "ServerSync.VersionCheck", "ShowConnectionError", "\nServer expects you to have ", "\nСервер требует установить ", " (Version: ", " (версия: ", ") installed.", ")."); } private static void AddCustomVersionErrors(List result, Func pluginVersionResolver) { Add(result, "Azumatt.AzuAreaRepair", "AzuAreaRepair.RpcHandlers", "RPC_AzuAutoStore_Version", "AzuAreaRepair Installed: 1.1.6 ", "Установлен AzuAreaRepair: 1.1.6 ", "\n Needed: ", "\n Требуется: "); Add(result, "Azumatt.AzuAutoStore", "AzuAutoStore.RpcHandlers", "RPC_AzuAutoStore_Version", "AzuAutoStore Installed: 3.0.14\n Needed: ", "Установлен AzuAutoStore: 3.0.14\n Требуется: "); Add(result, "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes.RpcHandlers", "RPC_AzuCraftyBoxes_Version", "AzuCraftyBoxes Installed: 1.8.14\n Needed: ", "Установлен AzuCraftyBoxes: 1.8.14\n Требуется: "); Add(result, "Azumatt.AzuWorkbenchTweaks", "AzuWorkbenchTweaks.RpcHandlers", "RPC_AzuWorkbenchTweaks_Version", "AzuWorkbenchTweaks Installed: 1.0.5\n Needed: ", "Установлен AzuWorkbenchTweaks: 1.0.5\n Требуется: "); Add(result, "Azumatt.BuildCameraCHE", "Valheim_Build_Camera.RpcHandlers", "RPC_AzuAutoStore_Version", "BuildCameraCHE Installed: 1.2.10\n Needed: ", "Установлен BuildCameraCHE: 1.2.10\n Требуется: "); string pluginVersion = GetPluginVersion(pluginVersionResolver, "Insanity.Culinary_Horizons", "1.0.2"); Add(result, "Insanity.Culinary_Horizons", "Culinary_Horizons.RpcHandlers", "RPC_Culinary_Horizons_Version", "Culinary_Horizons Installed: " + pluginVersion + "\n Needed: ", "Установлен Culinary_Horizons: " + pluginVersion + "\n Требуется: "); Add(result, "Dreanegade.Eternal_Legends", "Eternal_Legends.RpcHandlers", "RPC_Eternal_Legends_Version", "Eternal_Legends Installed: 1.0.4\n Needed: ", "Установлен Eternal_Legends: 1.0.4\n Требуется: "); Add(result, "Dreanegade.Hunter_Legacy", "Hunter_Legacy.RpcHandlers", "RPC_Hunter_Legacy_Version", "Hunter_Legacy Installed: 1.0.8\n Needed: ", "Установлен Hunter_Legacy: 1.0.8\n Требуется: "); string pluginVersion2 = GetPluginVersion(pluginVersionResolver, "Dreanegade.Magic_Supremacy", "3.0.7"); Add(result, "Dreanegade.Magic_Supremacy", "Magic_Supremacy.RpcHandlers", "RPC_Magic_Supremacy_Version", "Magic_Supremacy Installed: " + pluginVersion2 + "\n Needed: ", "Установлен Magic_Supremacy: " + pluginVersion2 + "\n Требуется: "); string pluginVersion3 = GetPluginVersion(pluginVersionResolver, "warpalicious.More_World_Locations_AIO", "5.0.8"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.RpcHandlers", "RPC_More_World_Locations_AIO_Version", "More_World_Locations_AIO Installed: " + pluginVersion3 + "\n Needed: ", "Установлен More_World_Locations_AIO: " + pluginVersion3 + "\n Требуется: "); Add(result, "Azumatt_and_ValheimPlusDevs.PerfectPlacement", "AllManagersModTemplate.RpcHandlers", "RPC_AllManagersModTemplate_Version", "PerfectPlacement Installed: 1.2.2 ", "Установлен PerfectPlacement: 1.2.2 ", "\n Needed: ", "\n Требуется: "); } private static void AddBossAdd(List result) { Add(result, "bossadd", "bossadd.Patches.BonemassKillConditionPatch", "Prefix", "분열된 본매스를 모두 처치하라!", "Уничтожьте всех разделившихся Масс Костей!"); Add(result, "bossadd", "bossadd.Patches.MiniBonemassOrderDeathPatch", "Prefix", "[", "Сначала уничтожьте Массу Костей цвета «", "]색 본매스를 먼저 처치하라!", "»!"); } private static void AddMoreWorldLocations(List result) { Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.PortInit+<>c", "b__13_0", "Wooden Shipment", "Грузовой деревянный сундук"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.PortInit+<>c", "b__13_1", "Barrel Shipment", "Грузовая бочка"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.PortInit+<>c", "b__13_2", "Fine Shipment", "Грузовой добротный сундук"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.PortInit+<>c", "b__13_3", "Fuling Shipment", "Грузовой сундук фулингов"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.PortInit+<>c", "b__13_4", "Dvergr Shipment", "Грузовой сундук двергов"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.Shrines.Shrine", "GetHoverText", "Unknown Shrine", "Неизвестное святилище", "Shrine", "Святилище", "\nShrine is misconfigured", "\nСвятилище настроено неправильно", "Duration: {0:0.#} min\n", "Длительность: {0:0.#} мин\n", "\nCan only be used once per day\n", "\nМожно использовать только раз в день\n", "[$KEY_Use] Sacrifice {0}x {1}\n", "[$KEY_Use] Принести в жертву {0}x {1}\n"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.Shrines.Shrine", "Interact", "Shrine can only be used once per day. Try again tomorrow!", "Святилище можно использовать лишь раз в день. Попробуйте завтра!"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.Shrines.ShrineDB", "BuildShrineConfigs", "Odin's Shrine of The Boar", "Святилище Одина: кабан", "Odin's Shrine of The Deer", "Святилище Одина: олень", "Odin's Shrine of The Neck", "Святилище Одина: никс", "Odin's Shrine of The Greydwarf", "Святилище Одина: грейдворф", "Odin's Shrine of The Skeleton", "Святилище Одина: скелет", "Odin's Shrine of The Greydwarf Shaman", "Святилище Одина: шаман грейдворфов", "Odin's Shrine of The Poison Skeleton", "Святилище Одина: ядовитый скелет", "Odin's Shrine of The Draugr", "Святилище Одина: драугр", "Odin's Shrine of The Leech", "Святилище Одина: пиявка", "Odin's Shrine of The Wraith", "Святилище Одина: призрак", "Odin's Shrine of The Abomination", "Святилище Одина: порождение", "Odin's Shrine of The Wolf", "Святилище Одина: волк", "Odin's Shrine of The Hatchling", "Святилище Одина: дракончик", "Odin's Shrine of The Ulv", "Святилище Одина: ульв", "Odin's Shrine of The Cultist", "Святилище Одина: культист", "Odin's Shrine of The Goblin", "Святилище Одина: фулинг", "Odin's Shrine of The Deathsquito", "Святилище Одина: комар смерти", "Odin's Shrine of The Goblin Shaman", "Святилище Одина: шаман фулингов", "Odin's Shrine of The Goblin Brute", "Святилище Одина: берсерк фулингов", "Odin's Shrine of The Seeker", "Святилище Одина: искатель", "Odin's Shrine of The Hare", "Святилище Одина: заяц", "Odin's Shrine of The Dvergr", "Святилище Одина: дверг", "Odin's Shrine of The Seeker Brute", "Святилище Одина: солдат-искатель"); AddMwlStatusEffects(result, "warpalicious.More_World_Locations_AIO"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.Waystones.Waystone", "GetHoverText", "Unknown Waystone", "Неизвестный путевой камень", "Waystone", "Путевой камень", "\n[$KEY_Use] Use Waystone\n", "\n[$KEY_Use] Использовать путевой камень\n"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.Waystones.Waystone", "Interact", "Map revealed in radius of {0} meters", "Карта открыта в радиусе {0} м"); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.Traders.BlacksmithStone_SE", "Setup", "Using Blacksmith Stone to enhance an item...", "Кузнечный камень улучшает предмет..."); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.Traders.BlacksmithStone_SE", "EnhanceItem", "Item: ", "Предмет: ", " was enhanced", " улучшен", "No suitable item found in top left corner of inventory.", "В левом верхнем углу инвентаря нет подходящего предмета."); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.Traders.BlacksmithStoneCanConsumeItemPatch", "Prefix", "No suitable item found in top left corner of inventory.", "В левом верхнем углу инвентаря нет подходящего предмета."); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.Traders.SkillBook_SE", "ApplySkillBook", " is already at max level", " уже достиг максимального уровня", " increased to ", " повышен до "); Add(result, "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO.PortUI+Tab", ".ctor", "MWL_Ports_Tooltip", "Порты"); Add(result, "warpalicious.More_World_Locations_AIO", "Forbidden_Catacombs.AltarDoor", ".ctor", "Sacrificial Altar", "Жертвенный алтарь"); Add(result, "warpalicious.More_World_Locations_AIO", "Forbidden_Catacombs.AltarDoor", "GetHoverText", "\n[$KEY_Use] Sacrifice", "\n[$KEY_Use] Принести жертву"); AddMoreWorldLocationsExtras(result, "warpalicious.More_World_Locations_AIO"); } private static void AddMoreWorldLocationsExtras(List result, string guid) { Add(result, guid, "Common.LootManager", "AddContainerToChild", "Chest", "$piece_chest"); Add(result, guid, "More_World_Locations_AIO.Commands", "Setup", "list of all shipments", "список всех грузов", "pins port locations on map", "отмечает порты на карте", "removes port pins from map", "удаляет метки портов с карты", "removes known ports from player save", "удаляет известные порты из сохранения игрока"); Add(result, guid, "More_World_Locations_AIO.Commands+<>c", "b__1_0", "No shipments found", "Грузы не найдены"); Add(result, guid, "More_World_Locations_AIO.Port+PortInfo", "GetTooltip", "\n\nShipments ({0}): ", "\n\nГрузы ({0}): "); Add(result, guid, "More_World_Locations_AIO.Shipment", "FormatTime", "{0}h", "{0} ч", "{0}m", "{0} мин", "{0}s", "{0} с", "~{0:0.#} days ({1})", "~{0:0.#} дн. ({1})"); AddMwlTerminalCommands(result, guid); AddMwlWaystones(result, guid); AddMwlShrineStats(result, guid); } private static void AddMwlTerminalCommands(List result, string guid) { Add(result, guid, "More_World_Locations_AIO.MWLCommands", "Postfix", "Teleport to a location. Usage: gotolocation [index]", "Телепортироваться к локации. Использование: gotolocation <название> [индекс]", "Pin multiple locations on map. Usage: findlocations ...", "Отметить несколько локаций на карте. Использование: findlocations <название1> <название2> ..."); Add(result, guid, "More_World_Locations_AIO.MWLCommands+<>c", "b__0_0", "ZoneSystem not available", "Система зон недоступна", "No local player", "Локальный игрок не найден", "Usage: gotolocation [index]", "Использование: gotolocation <название_локации> [индекс]", " index: which instance to go to (default: closest)", " индекс: к какому экземпляру перейти (по умолчанию — к ближайшему)", "Location '", "Локация «", "' exists but no instances have spawned yet", "» существует, но её экземпляры ещё не созданы", "No location found matching '", "Локация по запросу «", "'", "» не найдена", "Index {0} out of range. Only {1} instances exist.", "Индекс {0} вне диапазона. Доступно экземпляров: {1}.", "Teleporting to {0} ({1}/{2})", "Телепортация к {0} ({1}/{2})"); Add(result, guid, "More_World_Locations_AIO.MWLCommands+<>c", "b__0_2", "ZoneSystem not available", "Система зон недоступна", "Minimap not available", "Мини-карта недоступна", "Usage: findlocations ...", "Использование: findlocations <название_локации1> <название_локации2> ...", " Use 'findlocations clear' to remove all pins", " Команда 'findlocations clear' удаляет все метки", "Removed {0} location pins", "Удалено меток локаций: {0}", "No spawned instances found matching: ", "Не найдены созданные экземпляры по запросу: ", "Added {0} pins:", "Добавлено меток: {0}:", "Use 'findlocations clear' to remove pins", "Команда 'findlocations clear' удаляет метки"); Add(result, guid, "More_World_Locations_AIO.Utils.UpgradeWorldCommands", "AddUpgradeWorldCommands", "Adds MWL dungeon locations to corresponding biomes.", "Добавляет подземелья MWL в соответствующие биомы.", "Adds MWL locations to the Ashlands biome.", "Добавляет локации MWL в Пепельные земли.", "Adds MWL locations to the Black Forest biome.", "Добавляет локации MWL в Чёрный лес.", "Adds MWL locations to the Meadows biome.", "Добавляет локации MWL на Луга.", "Adds MWL locations to the Mistlands biome.", "Добавляет локации MWL в Туманные земли.", "Adds MWL locations to the Mountains biome.", "Добавляет локации MWL в Горы.", "Adds MWL locations to the Plains biome.", "Добавляет локации MWL на Равнины.", "Adds MWL locations to the Swamp biome.", "Добавляет локации MWL на Болота.", "Adds MWL shipping port locations to corresponding biomes.", "Добавляет грузовые порты MWL в соответствующие биомы.", "Adds MWL trader locations to corresponding biomes.", "Добавляет торговые локации MWL в соответствующие биомы.", "All MWL locations to the world.", "Добавляет в мир все локации MWL."); } private static void AddMwlWaystones(List result, string guid) { Add(result, guid, "More_World_Locations_AIO.Waystones.WaystoneDB", "BuildWaystoneConfigs", "Odin's Waystone of The Dragur", "Путевой камень Одина: драугр", "Sunken Crypt", "Затонувший склеп", "Odin's Waystone of The Ulv", "Путевой камень Одина: ульв", "Frost Cave", "Ледяная пещера", "Odin's Waystone of The Seeker", "Путевой камень Одина: искатель", "Infested Mine", "Заражённая шахта", "Odin's Waystone of Far Sight", "Путевой камень дальнего зрения Одина"); Add(result, guid, "More_World_Locations_AIO.Waystones.WaystoneDB", "GetDefaultWaystoneConfig", "Odin's Waystone", "Путевой камень Одина"); Add(result, guid, "More_World_Locations_AIO.Waystones.WaystoneUtils", "GetWaystoneDetails", "Marks a ", "Отмечает ", " on map", " на карте", "Reveal a radius of {0} on map", "Открывает карту в радиусе {0}"); Add(result, guid, "More_World_Locations_AIO.PortNames", "GetRandomName", "Port", "Порт"); } private static void AddMwlShrineStats(List result, string guid) { Add(result, guid, "More_World_Locations_AIO.Shrines.ShrineUtils", "GetEffectDescription", "HP per tick", "Здоровье за тик", "Health over time", "Здоровье со временем", "Stamina over time", "Выносливость со временем", "Eitr over time", "Эйтр со временем", "Health regen", "Регенерация здоровья", "Stamina regen", "Регенерация выносливости", "Eitr regen", "Регенерация эйтра", "Carry weight", "Переносимый вес", " kg", " кг", "Speed", "Скорость", "Stealth", "Скрытность", "Noise", "Шум", "Fall damage", "Урон от падения", "Max fall speed", "Макс. скорость падения", " m/s", " м/с", "Run stamina drain", "Расход выносливости при беге", "Jump stamina use", "Расход выносливости на прыжок", "Attack stamina use", "Расход выносливости на атаку", "Block stamina use", "Расход выносливости на блок", "Dodge stamina use", "Расход выносливости на уклонение", "Swim stamina use", "Расход выносливости при плавании", "Sneak stamina use", "Расход выносливости при скрытности", "Run stamina use", "Расход выносливости при беге", "Skill: {0} +{1:0.#}", "Навык: {0} +{1:0.#}", "Damage mods: ", "Модификаторы урона: "); } private static void AddMwlStatusEffects(List result, string guid) { Add(result, guid, "More_World_Locations_AIO.Shrines.StatusEffectDB", "BuildStatusEffects", "Blessing of the Boar", "Благословение кабана", "Blessing of the Greydwarf", "Благословение грейдворфа", "Blessing of the Draugr", "Благословение драугра", "Blessing of the Wolf", "Благословение волка", "Blessing of the Fuling", "Благословение фулинга", "Blessing of the Seeker", "Благословение искателя", "Blessing of the Deer", "Благословение оленя", "Blessing of the Skeleton", "Благословение скелета", "Blessing of the Leech", "Благословение пиявки", "Blessing of the Hatchling", "Благословение дракончика", "Blessing of the Deathsquito", "Благословение комара смерти", "Blessing of the Hare", "Благословение зайца", "Blessing of the Neck", "Благословение никса", "Blessing of the Greydwarf Shaman", "Благословение шамана грейдворфов", "Blessing of the Wraith", "Благословение призрака", "Blessing of the Ulv", "Благословение ульва", "Blessing of the Fuling Shaman", "Благословение шамана фулингов", "Blessing of the Dvergr", "Благословение дверга", "Blessing of the Poison Skeleton", "Благословение ядовитого скелета", "Blessing of the Abomination", "Благословение порождения", "Blessing of the Cultist", "Благословение культиста", "Blessing of the Fuling Berserker", "Благословение берсерка фулингов", "Blessing of the Seeker Soldier", "Благословение солдата-искателя"); } private static void AddVnei(List result) { Add(result, "com.maxsch.valheim.vnei", "VNEI.UI.HistoryElement", "Description", "Previous: ", "Назад: ", ", Next: ", ", вперёд: "); Add(result, "com.maxsch.valheim.vnei", "VNEI.UI.HistorySnapshotRecipe", "Description", "Recipe: ", "Рецепт: "); Add(result, "com.maxsch.valheim.vnei", "VNEI.UI.HistorySnapshotSearch", "Description", "Search", "Поиск"); Add(result, "com.maxsch.valheim.vnei", "VNEI.Logic.FileWriterController", "get_Help", "Writes all indexed items to a file", "Записывает все проиндексированные предметы в файл"); Add(result, "com.maxsch.valheim.vnei", "VNEI.Logic.IconExport", "get_Help", "Exports icons", "Экспортирует значки"); Add(result, "com.maxsch.valheim.vnei", "VNEI.UI.SelectUITest+ToggleUIConsoleCommand", "get_Help", "Shows the selection test", "Показывает тестовое окно выбора"); Add(result, "com.maxsch.valheim.vnei", "VNEI.Logic.Item", "PrintItem", " -- invalid item -- ", " -- недопустимый предмет -- ", "Internal Name: ", "Внутреннее имя: ", ", Localized Name: ", ", локализованное имя: ", ", Item Type: ", ", тип предмета: ", ", Description: ", ", описание: ", ", Source Mod: ", ", исходный мод: "); Add(result, "com.maxsch.valheim.vnei", "VNEI.Logic.Item", "PrintItemCSV", " -- invalid item -- ", " -- недопустимый предмет -- "); Add(result, "com.maxsch.valheim.vnei", "VNEI.Logic.Item", "PrintCSVHeader", "Internal Name", "Внутреннее имя", "Localized Name", "Локализованное имя", "Item Type", "Тип предмета", "Description", "Описание", "Source Mod", "Исходный мод"); } private static void AddHuginn(List result) { Add(result, "nightofgames.huginn", "Huginn.Names", "GroupLabel", "Meadows", "Луга", "Black Forest", "Чёрный лес", "Swamp", "Болота", "Mountain", "Горы", "Plains", "Равнины", "Mistlands", "Туманные земли", "Ashlands", "Пепельные земли", "Deep North", "Крайний север", "Ocean", "Океан", "Anywhere", "Везде", "Other", "Прочее"); Add(result, "nightofgames.huginn", "Huginn.Names", "Leaf", "Unknown", "Неизвестно", "Portal", "Портал", "Boat", "Судно", "Location", "Локация"); Add(result, "nightofgames.huginn", "Huginn.Names", "Pretty", "Unknown", "Неизвестно"); Add(result, "nightofgames.huginn", "Huginn.PinFilterUI", "BuildSkeleton", "Show all", "Показать всё", "Hide all", "Скрыть всё"); Add(result, "nightofgames.huginn", "Huginn.PinFilterUI", "BuildGroup", "Toggle all ", "Переключить всё: "); AddHuginnCommands(result, "nightofgames.huginn"); } private static void AddHuginnCommands(List result, string guid) { Add(result, guid, "Huginn.PinCleanup+PurgeCommand", "get_Help", "Remove pins copied onto your map from other players (cartography table). Your own pins and Huginn's pins are kept.", "Удаляет метки, скопированные на вашу карту у других игроков через картографический стол. Ваши метки и метки Huginn сохраняются."); Add(result, guid, "Huginn.PinCleanup+PurgeCommand", "Run", "Huginn: can't purge pins — no map loaded.", "Huginn: карта не загружена, удалить метки нельзя.", "Huginn: removed {0} imported foreign pin(s).", "Huginn: удалено чужих импортированных меток: {0}."); Add(result, guid, "Huginn.PinCleanup+ClearRadiusCommand", "get_Help", "huginn.clearpins — remove map pins within meters of you. Affects your own and other mods' pins; Huginn's managed pins are kept.", "huginn.clearpins <радиус> — удалить метки карты в указанном радиусе. Затрагивает ваши метки и метки других модов; метки Huginn сохраняются."); Add(result, guid, "Huginn.PinCleanup+ClearRadiusCommand", "Run", "Huginn: no map loaded.", "Huginn: карта не загружена.", "Usage: huginn.clearpins e.g. huginn.clearpins 50", "Использование: huginn.clearpins <радиус> пример: huginn.clearpins 50", "Huginn: no removable pins within {0:0}m.", "Huginn: в радиусе {0:0} м нет удаляемых меток.", "Huginn: removed {0} pin(s) within {1:0}m.", "Huginn: в радиусе {1:0} м удалено меток: {0}."); Add(result, guid, "Huginn.PinCleanup+LocationsCommand", "get_Help", "Log all world Location names grouped by biome (optionally filtered): huginn.locations [filter]", "Записать в журнал все локации мира по биомам (необязательный фильтр): huginn.locations [фильтр]"); Add(result, guid, "Huginn.PinCleanup+LocationsCommand", "Run", "Huginn: ZoneSystem not ready.", "Huginn: система зон ещё не готова.", "Huginn: logged {0} locations to LogOutput.log.", "Huginn: локаций записано в LogOutput.log: {0}."); Add(result, guid, "Huginn.PinCleanup+LookingAtCommand", "get_Help", "Print the prefab name of the object you're looking at.", "Показать ID объекта, на который вы смотрите."); Add(result, guid, "Huginn.PinCleanup+LookingAtCommand", "Run", "Huginn: not looking at anything (aim at it and try again).", "Huginn: цель не найдена — наведитесь на объект и повторите.", " [MineRock, removeWhenDestroyed={0}, allDestroyed={1}]", " [MineRock, удалить после разрушения={0}, всё разрушено={1}]", " [MineRock5, allDestroyed={0}]", " [MineRock5, всё разрушено={0}]", " [components: ", " [компоненты: ", "none", "нет", "Huginn: looking at '", "Huginn: выбран объект «", "'", "»"); Add(result, guid, "Huginn.PinCleanup+DumpCommand", "get_Help", "Log a summary of all map pins (type, owner, names) for diagnostics.", "Показать сводку всех меток карты: тип, владелец и названия."); Add(result, guid, "Huginn.PinCleanup+DumpCommand", "Run", "Huginn: no map loaded.", "Huginn: карта не загружена.", "Huginn: {0} pins across {1} type(s):", "Huginn: меток — {0}, типов — {1}:", "(unnamed)", "(без названия)", " PinType {0}: {1} pins (foreign={2}, saved={3}) e.g. [{4}]", " Тип метки {0}: {1} (чужих={2}, сохранённых={3}), например [{4}]"); } private static void AddJotunn(List result) { Add(result, "com.jotunn.jotunn", "Jotunn.Managers.SkillManager", "Skills_CheatRaiseSkill", "Skill increased {0}: {1}", "Навык повышен — {0}: {1}", "Skill {0} = {1}", "Навык {0} = {1}"); Add(result, "com.jotunn.jotunn", "Jotunn.Managers.SkillManager", "Skills_CheatResetSkill", "Skill ", "Навык «", " reset", "» сброшен"); Add(result, "com.jotunn.jotunn", "Jotunn.Utils.UndoActions+UndoCreate", "UndoMessage", "Undo: Removed ", "Отмена: удалено "); Add(result, "com.jotunn.jotunn", "Jotunn.Utils.UndoActions+UndoCreate", "RedoMessage", "Redo: Restored ", "Повтор: восстановлено "); Add(result, "com.jotunn.jotunn", "Jotunn.Utils.UndoActions+UndoRemove", "UndoMessage", "Undo: Restored ", "Отмена: восстановлено "); Add(result, "com.jotunn.jotunn", "Jotunn.Utils.UndoActions+UndoRemove", "RedoMessage", "Redo: Removed ", "Повтор: удалено "); Add(result, "com.jotunn.jotunn", "Jotunn.Utils.UndoActions+UndoTerrain", "UndoMessage", "Undoing terrain changes", "Отмена изменений рельефа"); Add(result, "com.jotunn.jotunn", "Jotunn.Utils.UndoActions+UndoTerrain", "RedoMessage", "Redoing terrain changes", "Повтор изменений рельефа"); Add(result, "com.jotunn.jotunn", "Jotunn.Managers.UndoManager+UndoQueue", "Undo", "Nothing to undo.", "Нечего отменять."); Add(result, "com.jotunn.jotunn", "Jotunn.Managers.UndoManager+UndoQueue", "Redo", "Nothing to redo.", "Нечего повторять."); } private static void AddEpicLoot(List result) { Add(result, "randyknapp.mods.epicloot", "EpicLoot.Compendium.MagicSearchField", ".ctor", "Search...", "Поиск..."); Add(result, "randyknapp.mods.epicloot", "EpicLoot.Adventure.Container_RPC_OpenRespons_Patch", "Postfix", "Treasure Found!", "Сокровище найдено!"); Add(result, "randyknapp.mods.epicloot", "EpicLoot.General.TerminalCommands+LuckTestCommand", "get_Help", "Rolls an example loot table with the specified luck eg: lucktest Greydwarf 1.0", "Выполняет пробный розыгрыш добычи с указанной удачей, пример: lucktest Greydwarf 1.0"); Add(result, "randyknapp.mods.epicloot", "EpicLoot.General.TerminalCommands+LuckTestCommand", "Run", "Using Lucktest Defaults: lucktest {0} {1}", "Используются значения lucktest по умолчанию: lucktest {0} {1}"); Add(result, "randyknapp.mods.epicloot", "EpicLoot.General.TerminalCommands+LuckTestCommand", "PrintError", "lucktest invalid arguments, was 'lucktest ", "Недопустимые аргументы lucktest: «lucktest ", "using the default: 'lucktest {0} {1}'\n", "используется команда по умолчанию: «lucktest {0} {1}»\n", "Supported formats are:\n lucktest\n lucktest 1.2\n lucktest Neck 1.2", "Допустимые форматы:\n lucktest\n lucktest 1.2\n lucktest Neck 1.2"); Add(result, "randyknapp.mods.epicloot", "EpicLoot.LootRoller", "PrintLuckTest", "Luck Test: {0}, {1}", "Проверка удачи: {0}, {1}", "Rarity Base % Luck % Diff Factor", "Редкость База % Удача % Разн. Множитель"); AddEpicLootTerminalPatch(result, "randyknapp.mods.epicloot"); } private static void AddEpicLootTerminalPatch(List result, string guid) { Add(result, guid, "EpicLoot.Terminal_Patch", "CheckStackQuality", "CheckStackQuality", "Проверка качества стопок", "> ObjectDB is null", "> База объектов не загружена", "> (none)", "> (нет)"); Add(result, guid, "EpicLoot.Terminal_Patch", "MagicItem", "magicitem - rarity:{0}, item:{1}, count:{2}", "magicitem — редкость:{0}, предмет:{1}, количество:{2}", "> Could not find item: ", "> Предмет не найден: ", "> {0} - rarity: [{1}], item: {2}", "> {0} — редкость: [{1}], предмет: {2}"); Add(result, guid, "EpicLoot.Terminal_Patch", "SpawnMagicItemWithEffect", "Specify effect and item name", "Укажите эффект и ID предмета", "magicitem - ", "magicitem — ", " with effect: ", " с эффектом: ", "> Could not find effect: ", "> Эффект не найден: ", "> Could not find item: ", "> Предмет не найден: ", "> Can't be magic item: ", "> Нельзя сделать магическим: "); Add(result, guid, "EpicLoot.Terminal_Patch", "SpawnLegendaryMagicItem", "> Specify legendaryID, itemID (optional)", "> Укажите legendaryID и необязательный itemID", "magicitemlegendary - legendaryID:", "magicitemlegendary — legendaryID:", "magicitemmythic - legendaryID:", "magicitemmythic — legendaryID:"); Add(result, guid, "EpicLoot.Terminal_Patch", "SpawnLegendaryItemHelper", "> Could not find legendary/mythic info for legendaryID: (", "> Данные легендарного/мифического предмета не найдены для legendaryID: (", "> Could not find suitable items with parameter (", "> Подходящие предметы не найдены для параметра (", ") for legendaryID: (", ") и legendaryID: (", "> Could not find suitable item for legendaryID: (", "> Подходящий предмет не найден для legendaryID: ("); Add(result, guid, "EpicLoot.Terminal_Patch", "SpawnMagicItemSet", "> Specify Set ID", "> Укажите ID комплекта", "magicitemset - setID:", "magicitemset — setID:", "> Could not find set info for setID: (", "> Данные комплекта не найдены для setID: ("); Add(result, guid, "EpicLoot.Terminal_Patch", "ToggleAlwaysDrop", "> Always Drop: {0}", "> Всегда выпадать: {0}"); Add(result, guid, "EpicLoot.Terminal_Patch", "OnTreasureChestSpawnComplete", "> Failed to spawn treasure map chest", "> Не удалось создать сундук карты сокровищ", "> Spawning Treasure Map Chest at <{0:0.#}, {1:0.#}> (height:{2:0.#})", "> Создание сундука карты сокровищ в <{0:0.#}, {1:0.#}> (высота:{2:0.#})"); Add(result, guid, "EpicLoot.Terminal_Patch+<>c", "b__0_13", "> Disable gating for magic item drops: {0}", "> Ограничения выпадения магических предметов отключены: {0}"); Add(result, guid, "EpicLoot.Terminal_Patch+<>c", "b__0_18", "> Treasure Map Debug Mode: {0}", "> Отладка карт сокровищ: {0}"); Add(result, guid, "EpicLoot.Terminal_Patch+<>c", "b__0_19", "> Treasure Map Debug Mode: {0}", "> Отладка карт сокровищ: {0}"); Add(result, guid, "EpicLoot.Terminal_Patch+<>c", "b__0_23", "Bounties for Interval {0}:", "Награды за период {0}:"); Add(result, guid, "EpicLoot.Terminal_Patch+<>c", "b__0_24", "Player Bounties:", "Награды игрока:"); Add(result, guid, "EpicLoot.Terminal_Patch+<>c", "b__0_27", "> Print Global Keys:", "> Глобальные ключи:"); } private static void AddInventorySlots(List result) { Add(result, "sighsorry.InventorySlots", "InventorySlots.InventorySlotsPlugin", "RebuildSlotDefinitions", "Helmet", "Шлем", "Chest", "Нагрудник", "Legs", "Поножи", "Cape", "Плащ", "Utility", "Аксессуар", "Trinket", "Безделушка"); Add(result, "sighsorry.InventorySlots", "InventorySlots.InventorySlotsPlugin", "AddJewelcraftingSlot", "Necklace", "Ожерелье", "Ring", "Кольцо"); Add(result, "sighsorry.InventorySlots", "InventorySlots.InventorySlotsPlugin", "TryAddAdventureBackpackSlot", "Backpack", "Рюкзак"); Add(result, "sighsorry.InventorySlots", "InventorySlots.InventorySlotsPlugin", "TryAddSmoothbrainBackpackSlot", "Backpack", "Рюкзак"); Add(result, "sighsorry.InventorySlots", "InventorySlots.InventorySlotsPlugin", "TryAddRustyBagSlot", "Bag", "Сумка"); Add(result, "sighsorry.InventorySlots", "InventorySlots.InventorySlotsPlugin", "TryAddRustyQuiverSlot", "Quiver", "Колчан"); Add(result, "sighsorry.InventorySlots", "InventorySlots.InventorySlotsPlugin", "TryAddMagicSupremacyCompatSlot", "MagicBelt", "Магический пояс"); } private static void AddCreatureLevelControl(List result) { Add(result, "org.bepinex.plugins.creaturelevelcontrol", "CreatureLevelControl.CreatureLevelControl+PatchTerminal", "Prefix", "Removed all nearby creatures with at least ", "Удалены все ближайшие существа как минимум с ", " stars", " звёздами", "Removed all nearby creatures", "Удалены все ближайшие существа", "Current world level: ", "Текущий уровень мира: ", "Kills in the current sector: ", "Убийств в текущем секторе: ", "Reset all nearby creature sectors", "Сброшены все ближайшие секторы существ", "Current star chances", "Текущие шансы звёзд", " for ", " для ", " star: ", " звёзд: ", " is not a creature", " не является существом", "Spawning object ", "Создание объекта ", "Unknown effect ", "Неизвестный эффект ", "CLLC console commands - use 'cllc' followed by one of the following options", "Команды CLLC — введите 'cllc', затем один из следующих параметров", "killhighstars [stars] - removes all nearby creatures with at least stars. Default stars is 6.", "killhighstars [звёзды] — удаляет всех ближайших существ с указанным минимумом звёзд. По умолчанию: 6.", "killall - removes all nearby creatures", "killall — удаляет всех ближайших существ", "worldlevel - displays the current world level", "worldlevel — показывает текущий уровень мира", "sectorkills - displays the creatures killed in the current sector", "sectorkills — показывает число существ, убитых в текущем секторе", "resetsector - resets all nearby creature sectors", "resetsector — сбрасывает все ближайшие секторы существ", "starchance [creature] - displays the current star chances. Optionally for a specific creature.", "starchance [существо] — показывает текущие шансы звёзд, при необходимости для конкретного существа.", "spawn [creature] [level] [affix] [extraeffect] [infusion] - spawns a creature with the provided properties.", "spawn [существо] [уровень] [аффикс] [допэффект] [инфузия] — создаёт существо с указанными свойствами.", "You are not allowed to use this command on this server.", "Вам запрещено использовать эту команду на этом сервере."); Add(result, "org.bepinex.plugins.creaturelevelcontrol", "CreatureLevelControl.CreatureSector+PatchMinimapAwake", "Postfix", "Sector Level: 0", "Уровень сектора: 0"); } private static void AddXPortal(List result) { Add(result, "yay.spikehimself.xportal", "XPortal.KnownPortal", "GetFriendlyTargetName", "{0} (invalid)", "{0} (недействителен)"); Add(result, "yay.spikehimself.xportal", "XPortal.UI.PortalConfigurationPanel", "PopulateDropdown", "{0:0.0} km", "{0:0.0} км", "{0} m", "{0} м"); Add(result, "yay.spikehimself.xportal", "XPortal.XPortal", "OnPrePortalHover", "Fetching portal info...", "Получение данных портала..."); Add(result, "yay.spikehimself.xportal", "XPortal.Patches.TeleportWorld_GetHoverText", "Prefix", "This portal doesn't actually appear to exist. Heimdallr sees you...", "Похоже, этого портала не существует. Хеймдалль видит тебя..."); } private static void AddBalrondShipyard(List result) { AddShipyardInteractionText(result, "balrond.astafaraios.BalrondShipyard"); AddShipyardFactoryText(result, "balrond.astafaraios.BalrondShipyard"); } private static void AddShipyardInteractionText(List result, string guid) { Add(result, guid, "BalrondShipyard.ShipyardStation", "GetLeverHoverText", "[$KEY_Use] Upgrade Ship", "[$KEY_Use] Улучшить судно"); Add(result, guid, "BalrondShipyard.ShipyardStation", "RPC_ShipyardRespons", "Upgrade Added", "Улучшение установлено", "Wrong resource amount - failed to upgrade", "Неверное количество ресурсов — улучшение не выполнено", "Cargo is empty", "Грузовой отсек пуст", "Shipyard is in use", "Верфь уже используется", "No Ship in range", "Поблизости нет судна", "There is no ship avaible for that upgrade", "Нет судна, подходящего для этого улучшения", "Something went wrong", "Что-то пошло не так"); Add(result, guid, "BalrondShipyard.ShipSwitch", "GetHoverText", "\n[$KEY_Use] Disable", "\n[$KEY_Use] Выключить", "\n[$KEY_Use] Enable", "\n[$KEY_Use] Включить"); Add(result, guid, "BalrondShipyard.ShipSwitch", "Awake", " Active Me", " Переключатель"); Add(result, guid, "BalrondShipyard.ShipSwitch", "Interact", " was ", " — ", "turned off", "выключено", "turned on", "включено"); Add(result, guid, "BalrondShipyard.AnchorSwitch", ".ctor", "Anchor", "Якорь"); Add(result, guid, "BalrondShipyard.AnchorSwitch", "GetHoverText", "\n[$KEY_Use] Drop Anchor", "\n[$KEY_Use] Бросить якорь", "\n[$KEY_Use] Pull Anchor", "\n[$KEY_Use] Поднять якорь"); Add(result, guid, "BalrondShipyard.AnchorSwitch", "Interact", " picked up", " поднят", " dropped", " брошен"); Add(result, guid, "BalrondShipyard.FishnetTrap", ".ctor", "FishnetTrap", "Рыболовная ловушка", "Add Fishbait", "Добавить наживку", "Empty the basket", "Опустошить корзину"); Add(result, guid, "BalrondShipyard.ShipFishnet", ".ctor", "Pick Fishes", "Забрать рыбу", "Out of Luck", "Не повезло", "Add Bait", "Добавить наживку", "Set sail to catch fishes", "Поднимите парус, чтобы ловить рыбу", "Can't pick up the net. Inventory is busy/full", "Нельзя забрать сеть: инвентарь занят или заполнен", "Fishnet is empty", "Сеть пуста"); Add(result, guid, "BalrondShipyard.ShieldPainter", ".ctor", "Paint Shields", "Раскрасить щиты"); Add(result, guid, "BalrondShipyard.ShipProcessor", "setupShipyard", "Upgrade Ship", "Улучшить судно"); Add(result, guid, "BalrondShipyard.ShipProcessor", "setupAnchor", "Anchor", "Якорь"); Add(result, guid, "BalrondShipyard.ShipProcessor", "setupAnchorDrakkar", "Anchor", "Якорь"); Add(result, guid, "BalrondShipyard.ShipProcessor", "setupLamp", "Lamp", "Фонарь"); Add(result, guid, "BalrondShipyard.ShipOwnerName", "GetHoverText", "Restricted\n", "Доступ запрещён\n", "\n[$KEY_Use] Change Name", "\n[$KEY_Use] Изменить имя", "\n[$KEY_Use] Set Ship Name", "\n[$KEY_Use] Задать имя судна", "\n[Owner] ", "\n[Владелец] ", "Unclaimed ship \n[$KEY_Use] $piece_bed_claim", "Судно без владельца \n[$KEY_Use] $piece_bed_claim"); Add(result, guid, "BalrondShipyard.ShipRamp", ".ctor", "ramp", "Трап"); Add(result, guid, "BalrondBiomes.SE_WetImmunity", "Awake", "Immune to wet effect", "Невосприимчивость к эффекту промокания"); } private static void AddShipyardFactoryText(List result, string guid) { Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createItemDescription", "\n Require: \n", "\n Требуется: \n", "Schematic x 1", "Чертёж x 1"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createAnchor", "Adds Anchor. You gain ability to stop your ship in place", "Добавляет якорь, позволяющий остановить судно на месте"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createArmor", "Add Carapace armor. Increase ships health and resistance against pierce damage", "Добавляет панцирную броню, повышающую прочность судна и сопротивление колющему урону"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createAshlandsDrakkarTent", "Adds Tent. Increase health by 200", "Добавляет шатёр и повышает прочность на 200"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createAshlandsRopes", "Adds side ropes. Allows you to get to upper deck from water level", "Добавляет боковые канаты, по которым можно подняться из воды на верхнюю палубу"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createAshlandsSupply", "Adds supplies. Increase ship blunt resistance", "Добавляет припасы и повышает сопротивление судна дробящему урону"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createDrakkarFishnet", "Adds fishnet. You can fuel it with fishing bait to catch fish while sailing. Require at leas t1 empty slot in ship's cargo!", "Добавляет рыболовную сеть. Заправьте её наживкой, чтобы ловить рыбу во время плавания. В грузовом отсеке судна нужна хотя бы одна свободная ячейка!"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createDrakkarRam", "Adds Iron Spike Ram. You gain ability to ram with front spikes. REQUIRE ARMOR UPGRADE", "Добавляет таран с железными шипами. Позволяет таранить носом судна. ТРЕБУЕТ УЛУЧШЕНИЯ БРОНИ"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createDrakkarSupply", "Adds Barrels. Increase blunt resistance and health", "Добавляет бочки, повышающие прочность и сопротивление дробящему урону"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createDrakkarSupply2", "Adds Crates. Increase slash resistance and health", "Добавляет ящики, повышающие прочность и сопротивление рубящему урону"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createDrakkarTent", "Adds Tent. Gain shelter status. Increase health by 300", "Добавляет шатёр, даёт эффект укрытия и повышает прочность на 300"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createHolkRamp", "Adds cage and ramp for transporting animals", "Добавляет клетку и трап для перевозки животных"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createHolkRopes", "Adds side ropes. Allows you to get to upper deck from water level", "Добавляет боковые канаты, по которым можно подняться из воды на верхнюю палубу"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createHolkSupply", "Adds supplies. Increase ship blunt resistance", "Добавляет припасы и повышает сопротивление судна дробящему урону"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createHolkTent", "Adds Tent. Increase health by 200", "Добавляет шатёр и повышает прочность на 200"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createKarveSupply", "Adds supplies. Increase ship blunt resistance", "Добавляет припасы и повышает сопротивление судна дробящему урону"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createKnarrSupply", "Adds supplies. Increase ship blunt resistance", "Добавляет припасы и повышает сопротивление судна дробящему урону"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createKnarrTent", "Adds Tent. Increase health by 200", "Добавляет шатёр и повышает прочность на 200"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createLamp", "Adds Lights. Adds Warm effect", "Добавляет фонари и эффект тепла"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createNamePlate", "Adds Nameplate. You can name your ship.", "Добавляет табличку, позволяющую дать судну имя."); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createOars", "Add Oars. Increase ships movement capability", "Добавляет вёсла и повышает ходовые качества судна"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createShield", "Adds Shields. Increase ships health by 200", "Добавляет щиты и повышает прочность судна на 200", "Adds Shields. Increase ships health by 300", "Добавляет щиты и повышает прочность судна на 300"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createSnekkeSupply", "Adds supplies. Increase ship blunt resistance", "Добавляет припасы и повышает сопротивление судна дробящему урону"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "createSnekkeTent", "Adds Tent. Increase health by 200", "Добавляет шатёр и повышает прочность на 200"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "drakarHeadCernyx", "Set Cernyx Head decoration", "Устанавливает носовое украшение «Керникс»"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "drakkarHeadDefault", "Set Default Head decoration", "Устанавливает обычное носовое украшение"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "drakkarHeadDragon", "Set Dragon Head decoration", "Устанавливает носовое украшение в виде дракона"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "drakkarHeadOsberg", "Set Osberg Head decoration", "Устанавливает носовое украшение «Осеберг»"); Add(result, guid, "BalrondShipyard.ShipExtensionFactory", "getSailDescription", "Set default(Red) sail color", "Устанавливает обычный красный парус", "Set blue sail color", "Устанавливает синий парус", "Set Triangle Red sail color", "Устанавливает красный треугольный узор паруса", "Set green sail color", "Устанавливает зелёный парус", "Set transparent sail color", "Устанавливает прозрачный парус", "Set white sail color", "Устанавливает белый парус", "Set Triangle Green sail pattern", "Устанавливает зелёный треугольный узор паруса", "Set Yellow sail color", "Устанавливает жёлтый парус", "Set Gray sail color", "Устанавливает серый парус", "Set Vegvisir sail pattern", "Устанавливает узор вегвизира на парус"); } private static void AddBalrondGaps(List result) { AddBalrondAmazingNature(result); AddBalrondConstructions(result); AddBalrondFurniture(result); AddBalrondDualMastery(result); AddBalrondLightkeeper(result); AddBalrondArsenal(result); Add(result, "balrond.astafaraios.BalrondHumanoidRandomizer", "BalrondHumanoidRandomizer.HumanoidExtend", "FindItemToSteal", " was stolen!", " — предмет похищен!"); } private static void AddBalrondAmazingNature(List result) { Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.BalrondConverter", ".ctor", "BalrondConverter", "Преобразователь"); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.BalrondConverter", "OnHoverAddFuel", "Fuel", "Топливо", "\n[$KEY_Use] Add ", "\n[$KEY_Use] Добавить ", "Fuel: {0}/{1} units (+{2} per {3})", "Топливо: {0}/{1} ед. (+{2} за {3})", "\nProcessing: ", "\nОбработка: ", "\nNext: {0} (cost {1} units)", "\nДалее: {0} (стоимость: {1} ед.)", "\nAdd a hide to see cost", "\nДобавьте шкуру, чтобы узнать стоимость", "\nReady to start", "\nГотово к запуску", "\nNeed {0} more units to start", "\nДля запуска не хватает {0} ед."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.BalrondConverter", "OnHoverAddOre", "{0} (Queue {1}/{2})", "{0} (очередь {1}/{2})", "\nFuel: {0}/{1} units", "\nТопливо: {0}/{1} ед.", "\nProcessing: {0} ({1}s)", "\nОбработка: {0} ({1} с)", "\nNext: {0} (cost {1} units, {2}s)", "\nДалее: {0} (стоимость: {1} ед., {2} с)", "\nStatus: ", "\nСостояние: ", "\nStatus: Ready", "\nСостояние: готово", "\nStatus: Need {0} more units", "\nСостояние: не хватает {0} ед."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.BalrondConverter", "OnHoverEmptyOre", "{0} ({1} Ready)\n", "{0} (готово: {1})\n"); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.DvergerBetrayal", "EnsureStatusEffect", "Mark of Betrayer", "Метка предателя", "The Dvergr remember your betrayal.", "Дверги помнят ваше предательство."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.DvergerBetrayal", "AddMark", "You betrayed our kind. We don't want anything to do with you.", "Вы предали наш народ. Мы не желаем иметь с вами ничего общего."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.Trader_Interact_BetrayerBlock", "Prefix", "You betrayed our kind. We don't want anything to do with you.", "Вы предали наш народ. Мы не желаем иметь с вами ничего общего."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.Trader_UseItem_BetrayerBlock", "Prefix", "You betrayed our kind. We don't want anything to do with you.", "Вы предали наш народ. Мы не желаем иметь с вами ничего общего."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.HerbPlanter", ".ctor", "HerbPlanter", "Грядка для трав"); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.HerbPlanter", "CheckCover", "Wont grow in this biome", "Не растёт в этом биоме", "Require Roof", "Требуется крыша", "Can't be covered", "Нельзя накрывать", "Require Cover", "Требуется укрытие", "Require Exposure", "Требуется открытое небо"); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.Launch+ZNetScene_Awake_Patch", "Prefix", "Challange his rule. Hunt his kin. Carve the skulls.", "Бросьте вызов его власти. Охотьтесь на его сородичей. Высекайте черепа."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.Launch", "Start", "Adds vegetations of mod BalrondAmazingNature.", "Добавляет растительность мода BalrondAmazingNature.", "Modify vanilla vegetations changed by mod BalrondAmazingNature.", "Обновляет обычную растительность, изменённую модом BalrondAmazingNature.", "Modify vanilla Locations changed by mod BalrondAmazingNature.", "Обновляет обычные локации, изменённые модом BalrondAmazingNature."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.Launch+RepairAllItems", "RepairAll", " item", " предмет", "s", "", " repaired!", " отремонтировано!"); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.ConversionChanges", "editSmelter", "\n Scrap does not go into Furnace/Smelter. Can be only converted in Smeltworks station", "\n Лом нельзя помещать в плавильню или доменную печь. Его можно переработать только на металлургической станции."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.ConversionChanges", "editFurnace", "\n Scrap does not go into Furnace/Smelter. Can be only converted in Smeltworks station", "\n Лом нельзя помещать в плавильню или доменную печь. Его можно переработать только на металлургической станции."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.ConversionChanges", "editStoneboundKiln", "\n Stonebound Kiln accepts all items that Kiln does but requires fuel", "\n Каменная углежоговая печь принимает всё, что принимает обычная углежоговая печь, но требует топлива."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.SE_FirstAidKit", "GetTooltipString", "\nTreatment duration: {0:0}s", "\nДлительность лечения: {0:0} с", "\nTick interval: {0:0.#}s", "\nИнтервал тиков: {0:0.#} с", "\nRemoves {0:0.#}s from injuries per tick", "\nСокращает длительность травм на {0:0.#} с за тик"); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.SE_ProgressiveDamageOverTime", "GetTooltipString", "\nCurrent damage: {0:0.##}", "\nТекущий урон: {0:0.##}", "\nStage: {0}", "\nСтадия: {0}"); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.SE_RepairKit", "ApplyRepair", "No damaged items to repair.", "Нет повреждённых предметов для ремонта.", "Repair kit restored durability on {0} item(s).", "Ремкомплект восстановил прочность предметов: {0}."); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.StatusEffectFactory", "CreateSitHealStatusEffect", "Rest", "Отдых"); Add(result, "balrond.astafaraios.BalrondAmazingNature", "BalrondNature.ItemEdits", "editPickaxeStone", "Flint Pickaxe", "Кремнёвая кирка"); Add(result, "balrond.astafaraios.BalrondAmazingNature", "MonsterDoorSensor", "NotifyNearbyPlayer", " forced the door open!", " — дверь взломана!"); } private static void AddBalrondConstructions(List result) { Add(result, "balrond.astafaraios.BalrondConstructions", "BalrondConstructions.BalrondConverter", ".ctor", "BalrondConverter", "Преобразователь", "Add Non-Base Wood", "Добавить особую древесину", "Pick up wood", "Забрать древесину"); Add(result, "balrond.astafaraios.BalrondConstructions", "BalrondConstructions.BalrondConverter", "OnHoverAddPowerSource", "{0} ({1} {2}/{3})\n[$KEY_Use] Add {4}", "{0} ({1} {2}/{3})\n[$KEY_Use] Добавить {4}"); Add(result, "balrond.astafaraios.BalrondConstructions", "BalrondConstructions.BalrondConverter", "OnHoverEmptyOre", "{0} ({1} Ready) \n[$KEY_Use] {2}", "{0} (готово: {1}) \n[$KEY_Use] {2}"); Add(result, "balrond.astafaraios.BalrondConstructions", "BalrondDrawBridge", ".ctor", "Drawbridge", "Подъёмный мост"); Add(result, "balrond.astafaraios.BalrondConstructions", "BalrondDrawBridge", "Interact", "Bridge is moving", "Мост движется"); Add(result, "balrond.astafaraios.BalrondConstructions", "BalrondDrawBridge", "GetHoverText", "Open", "Открыть", "Close", "Закрыть", "\nMoving", "\nДвижется"); Add(result, "balrond.astafaraios.BalrondConstructions", "OfferingStation", ".ctor", "Offer station", "Алтарь подношений", "Use", "Использовать"); Add(result, "balrond.astafaraios.BalrondConstructions", "OfferingStation", "Interact", "Smoke blocked", "Дымоход перекрыт"); Add(result, "balrond.astafaraios.BalrondConstructions", "OfferingStation", "UseItem", "Smoke blocked", "Дымоход перекрыт"); Add(result, "balrond.astafaraios.BalrondConstructions", "OfferingStation", "OnAddFuel", "No fuel required", "Топливо не требуется"); Add(result, "balrond.astafaraios.BalrondConstructions", "OfferingStation", "GetHoverText", " amount: ", " количество: "); } private static void AddBalrondFurniture(List result) { Add(result, "balrond.astafaraios.BalrondFurnitureReborn", "BalrondFurnitureReborn.BalrondToggle", ".ctor", "toggle shelfs", "переключить полки"); Add(result, "balrond.astafaraios.BalrondFurnitureReborn", "BalrondFurnitureReborn.BalrondToggle", "GetHoverText", "Shelfs\n[$KEY_Use] ", "Полки\n[$KEY_Use] "); Add(result, "balrond.astafaraios.BalrondFurnitureReborn", "YourModNamespace.RingingBell", ".ctor", "Bell", "Колокол"); } private static void AddBalrondDualMastery(List result) { Add(result, "balrond.astafaraios.BalrondDualMastery", "BalrondDualWield.DualWieldSkills+Patch_Skills_CheatRaiseSkill", "Prefix", "Skill increased ", "Навык повышен: ", "Skill ", "Навык "); Add(result, "balrond.astafaraios.BalrondDualMastery", "BalrondDualWield.DualWieldSkills+Patch_Skills_CheatResetSkill", "Prefix", "Skill ", "Навык «", " reset", "» сброшен"); } private static void AddBalrondLightkeeper(List result) { Add(result, "balrond.astafaraios.BalrondLightkeeper", "LightController.FireController", "ChangeRadius", "m", " м"); Add(result, "balrond.astafaraios.BalrondLightkeeper", "LightController.FireController", "GetHoverText", "m", " м"); } private static void AddBalrondArsenal(List result) { Add(result, "balrond.astafaraios.ZBalrondArsenalReborn", "BalrondArsenalReborn.ItemEdits", "editWolfFang", "Bone Spear", "Костяное копьё", "A clever usage of boar tusk", "Умелое применение кабаньего клыка"); } private static void Add(List result, string guid, string type, string method, params string[] pairs) { if ((pairs.Length & 1) != 0) { throw new ArgumentException("Literal replacements must be source/target pairs.", "pairs"); } Dictionary dictionary = new Dictionary(StringComparer.Ordinal); for (int i = 0; i < pairs.Length; i += 2) { dictionary[pairs[i]] = pairs[i + 1]; } result.Add(new LiteralPatchSpec(guid, type, method, dictionary)); } private static string GetPluginVersion(Func resolver, string guid, string fallback) { string text = resolver?.Invoke(guid); if (!string.IsNullOrWhiteSpace(text)) { return text; } return fallback; } } internal static class LocalizationBridge { internal static int Apply(IReadOnlyDictionary words, bool forceCreate, ManualLogSource log) { Type type = AccessTools.TypeByName("Localization"); if (type == null) { log.LogWarning((object)"Valheim Localization type was not found."); return 0; } object obj = type.GetField("m_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); if (obj == null && forceCreate) { obj = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null); } if (obj == null) { return 0; } int num = 0; if (type.GetField("m_translations", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) is IDictionary dictionary) { foreach (KeyValuePair word in words) { dictionary[word.Key.TrimStart(new char[1] { '$' })] = word.Value; num++; } } else { MethodInfo method = type.GetMethod("AddWord", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(string), typeof(string) }, null); if (method == null) { log.LogWarning((object)"Valheim localization dictionary and AddWord method were not found."); return 0; } foreach (KeyValuePair word2 in words) { method.Invoke(obj, new object[2] { word2.Key.TrimStart(new char[1] { '$' }), word2.Value }); num++; } } return num; } internal static void EvictCache(ManualLogSource log) { Type type = AccessTools.TypeByName("Localization"); object obj = type?.GetField("m_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); if (type != null && obj != null) { EvictCache(type, obj); } } private static void EvictCache(Type localizationType, object instance) { object obj = localizationType.GetField("m_cache", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance); obj?.GetType().GetMethod("EvictAll", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(obj, null); } } internal static class ManagerAdapters { private static readonly char[] JotunnForbiddenTokenCharacters = " (){}[]+-!?/\\&%,.:-=<>\n".ToCharArray(); internal static int ApplyAll(TranslationCatalog catalog, ManualLogSource log) { int num = 0; try { num += ApplyJotunnGlobal(catalog.Words, log); } catch (Exception ex) { log.LogWarning((object)("Jotunn global localization adapter skipped: " + ex.GetBaseException().Message)); } foreach (LoadedModule module in catalog.Modules) { try { switch (module.Definition.Adapter) { case AdapterKind.JotunnLocalizer: num += ApplyAssemblyJotunnLocalizer(module, log); break; case AdapterKind.BalrondTranslator: num += ApplyBalrondTranslator(module, log); break; case AdapterKind.DictionaryLocalizer: num += ApplyDictionaryLocalizer(module, log); break; case AdapterKind.CreatureLevelControl: num += ApplyCreatureLevelControl(module, log); break; case AdapterKind.NativeRussianDelta: break; } } catch (Exception ex2) { log.LogWarning((object)(module.Definition.Name + ": native localization adapter skipped: " + ex2.GetBaseException().Message)); } } return num; } private static int ApplyJotunnGlobal(IReadOnlyDictionary words, ManualLogSource log) { if (!Chainloader.PluginInfos.ContainsKey("com.jotunn.jotunn")) { return 0; } Type type = AccessTools.TypeByName("Jotunn.Managers.LocalizationManager"); object obj = type?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null); object obj2 = (type?.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "GetLocalization" && method.GetParameters().Length == 0))?.Invoke(obj, null); MethodInfo methodInfo = obj2?.GetType().GetMethod("AddJsonFile", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(string), typeof(string) }, null); if (methodInfo == null) { return 0; } Dictionary dictionary = FilterJotunnTokens(words); if (dictionary.Count == 0) { return 0; } methodInfo.Invoke(obj2, new object[2] { "Russian", ToFlatJson(dictionary) }); return dictionary.Count; } private static int ApplyAssemblyJotunnLocalizer(LoadedModule module, ManualLogSource log) { Assembly pluginAssembly = GetPluginAssembly(module.Definition.PluginGuid); if (pluginAssembly == null || module.Words.Count == 0) { return 0; } object obj = ReflectionUtil.GetTypesSafely(pluginAssembly).FirstOrDefault((Type type) => type.Name == "Localizer" && type.GetField("JvlLocalization", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) != null)?.GetField("JvlLocalization", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); MethodInfo methodInfo = obj?.GetType().GetMethod("AddJsonFile", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(string), typeof(string) }, null); if (methodInfo == null) { return 0; } Dictionary dictionary = FilterJotunnTokens(module.Words); if (dictionary.Count == 0) { return 0; } methodInfo.Invoke(obj, new object[2] { "Russian", ToFlatJson(dictionary) }); return dictionary.Count; } private static int ApplyBalrondTranslator(LoadedModule module, ManualLogSource log) { Assembly pluginAssembly = GetPluginAssembly(module.Definition.PluginGuid); if (!((((pluginAssembly == null) ? null : ReflectionUtil.GetTypesSafely(pluginAssembly).FirstOrDefault((Type type) => type.Name == "BalrondTranslator"))?.GetField("translations", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null) is IDictionary dictionary)) { return 0; } dictionary["Russian"] = new Dictionary(module.Words, StringComparer.Ordinal); return module.Words.Count; } private static int ApplyDictionaryLocalizer(LoadedModule module, ManualLogSource log) { if (!(((GetPluginAssembly(module.Definition.PluginGuid)?.GetType("LocalizationManager.Localizer"))?.GetField("loadedTexts", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null) is IDictionary dictionary)) { return 0; } dictionary["Russian"] = new Dictionary(module.Words, StringComparer.Ordinal); return module.Words.Count; } private static int ApplyCreatureLevelControl(LoadedModule module, ManualLogSource log) { Assembly pluginAssembly = GetPluginAssembly(module.Definition.PluginGuid); Type type = pluginAssembly?.GetType("CreatureLevelControl.LocalizationWrapper"); Type type2 = pluginAssembly?.GetType("YamlDotNet.Serialization.DeserializerBuilder"); if (type == null || type2 == null) { return 0; } object obj = Activator.CreateInstance(type2, nonPublic: true); object obj2 = type2.GetMethod("Build", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(obj, null); MethodInfo methodInfo = obj2?.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method) { if (method.Name != "Deserialize" || method.IsGenericMethod) { return false; } ParameterInfo[] parameters = method.GetParameters(); return parameters.Length == 2 && parameters[0].ParameterType == typeof(TextReader) && parameters[1].ParameterType == typeof(Type); }); if (methodInfo == null) { return 0; } object obj3; using (StreamReader streamReader = new StreamReader(module.Path, Encoding.UTF8)) { obj3 = methodInfo.Invoke(obj2, new object[2] { streamReader, type }); } if (obj3 == null) { return 0; } type.GetField("language", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(obj3, "Russian"); type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(null, obj3); return 1; } internal static Assembly GetPluginAssembly(string pluginGuid) { if (!Chainloader.PluginInfos.TryGetValue(pluginGuid, out var value) || !((Object)(object)value.Instance != (Object)null)) { return null; } return ((object)value.Instance).GetType().Assembly; } internal static string ToFlatJson(IReadOnlyDictionary words) { StringBuilder stringBuilder = new StringBuilder(words.Count * 48); stringBuilder.Append('{'); bool flag = true; foreach (KeyValuePair word in words) { if (!flag) { stringBuilder.Append(','); } flag = false; AppendJsonString(stringBuilder, word.Key.TrimStart(new char[1] { '$' })); stringBuilder.Append(':'); AppendJsonString(stringBuilder, word.Value ?? string.Empty); } stringBuilder.Append('}'); return stringBuilder.ToString(); } private static Dictionary FilterJotunnTokens(IReadOnlyDictionary words) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (KeyValuePair word in words) { string text = word.Key.TrimStart(new char[1] { '$' }); if (text.Length != 0 && text.IndexOfAny(JotunnForbiddenTokenCharacters) < 0) { dictionary[text] = word.Value; } } return dictionary; } private static void AppendJsonString(StringBuilder builder, string value) { builder.Append('"'); foreach (char c in value) { switch (c) { case '"': builder.Append("\\\""); continue; case '\\': builder.Append("\\\\"); continue; case '\b': builder.Append("\\b"); continue; case '\f': builder.Append("\\f"); continue; case '\n': builder.Append("\\n"); continue; case '\r': builder.Append("\\r"); continue; case '\t': builder.Append("\\t"); continue; } if (c < ' ') { StringBuilder stringBuilder = builder.Append("\\u"); int num = c; stringBuilder.Append(num.ToString("x4")); } else { builder.Append(c); } } builder.Append('"'); } } internal enum AdapterKind { None, JotunnLocalizer, BalrondTranslator, DictionaryLocalizer, NativeRussianDelta, CreatureLevelControl } internal sealed class ModuleDefinition { internal string Name { get; } internal string PluginGuid { get; } internal string RelativePath { get; } internal AdapterKind Adapter { get; } internal bool MergeIntoGame { get; } internal ModuleDefinition(string name, string pluginGuid, string relativePath, AdapterKind adapter = AdapterKind.None, bool mergeIntoGame = true) { Name = name; PluginGuid = pluginGuid; RelativePath = relativePath; Adapter = adapter; MergeIntoGame = mergeIntoGame; } } internal static class ModuleCatalog { internal static IReadOnlyList All { get; } = new ModuleDefinition[24] { new ModuleDefinition("Jotunn", "com.jotunn.jotunn", "Jotunn\\Russian.json"), new ModuleDefinition("Balrond Amazing Nature", "balrond.astafaraios.BalrondAmazingNature", "BalrondAmazingNature\\Russian.json", AdapterKind.BalrondTranslator), new ModuleDefinition("Balrond Arsenal Reborn", "balrond.astafaraios.ZBalrondArsenalReborn", "BalrondArsenalReborn\\Russian.json", AdapterKind.BalrondTranslator), new ModuleDefinition("Balrond Constructions", "balrond.astafaraios.BalrondConstructions", "BalrondConstructions\\Russian.json", AdapterKind.BalrondTranslator), new ModuleDefinition("Balrond Dual Mastery", "balrond.astafaraios.BalrondDualMastery", "BalrondDualMastery\\Russian.json", AdapterKind.BalrondTranslator), new ModuleDefinition("Balrond Furniture Reborn", "balrond.astafaraios.BalrondFurnitureReborn", "BalrondFurnitureReborn\\Russian.json", AdapterKind.BalrondTranslator), new ModuleDefinition("Balrond Lightkeeper", "balrond.astafaraios.BalrondLightkeeper", "BalrondLightkeeper\\Russian.json", AdapterKind.BalrondTranslator), new ModuleDefinition("Balrond Shipyard", "balrond.astafaraios.BalrondShipyard", "BalrondShipyard\\Russian.json", AdapterKind.None, mergeIntoGame: false), new ModuleDefinition("Creature Level and Loot Control", "org.bepinex.plugins.creaturelevelcontrol", "CreatureLevelAndLootControl\\Russian.yml", AdapterKind.CreatureLevelControl, mergeIntoGame: false), new ModuleDefinition("Culinary Horizons", "Insanity.Culinary_Horizons", "Culinary_Horizons\\ObeliskRU.Culinary_Horizons.Russian.yml", AdapterKind.NativeRussianDelta, mergeIntoGame: false), new ModuleDefinition("Epic Loot", "randyknapp.mods.epicloot", "EpicLoot\\Russian.json"), new ModuleDefinition("Eternal Legends", "Dreanegade.Eternal_Legends", "Eternal_Legends\\ObeliskRU.Eternal_Legends.Russian.yml", AdapterKind.NativeRussianDelta, mergeIntoGame: false), new ModuleDefinition("Hunter Legacy", "Dreanegade.Hunter_Legacy", "Hunter_Legacy\\ObeliskRU.Hunter_Legacy.Russian.yml", AdapterKind.NativeRussianDelta, mergeIntoGame: false), new ModuleDefinition("Impactful Skills", "MidnightsFX.ImpactfulSkills", "ImpactfulSkills\\Russian.json"), new ModuleDefinition("Inventory Slots", "sighsorry.InventorySlots", "InventorySlots\\InventorySlots.Russian.yml"), new ModuleDefinition("Magic Supremacy", "Dreanegade.Magic_Supremacy", "Magic_Supremacy\\ObeliskRU.Magic_Supremacy.Russian.yml", AdapterKind.NativeRussianDelta, mergeIntoGame: false), new ModuleDefinition("More World Locations AIO", "warpalicious.More_World_Locations_AIO", "More_World_Locations_AIO\\warpalicious.More_World_Locations_Localization.Russian.yml"), new ModuleDefinition("OdinsSteelworks", "org.bepinex.plugins.odinssteelworks", "OdinsSteelworks\\OdinsSteelworks.Russian.yml", AdapterKind.DictionaryLocalizer), new ModuleDefinition("PlantEverything", "advize.PlantEverything", "PlantEverything\\Russian.json"), new ModuleDefinition("Passive Powers", "org.bepinex.plugins.passivepowers", "PassivePowers\\Passive Powers.Russian.yml"), new ModuleDefinition("Adventure Backpacks", "vapok.mods.adventurebackpacks", "AdventureBackpacks\\AdventureBackpacks.Russian.json", AdapterKind.JotunnLocalizer), new ModuleDefinition("Valheim Armory", "MidnightsFX.ValheimArmory", "ValheimArmory\\Russian.json"), new ModuleDefinition("VNEI", "com.maxsch.valheim.vnei", "VNEI\\Russian.json"), new ModuleDefinition("XPortal", "yay.spikehimself.xportal", "XPortal\\Russian.json") }; } internal sealed class MoveBuildPiecesMessagePatcher { private const string PluginGuid = "obelisk.valheim.movebuildpieces"; private const string HarmonyId = "obelisk.ru.localization.move-build-pieces-messages"; private const string RequiredVersionPrefix = "Matching MoveBuildPieces "; private const string RequiredVersionSuffix = " is required on the server"; private static readonly IReadOnlyDictionary Exact = new Dictionary(StringComparer.Ordinal) { ["MoveBuildPieces is disabled by the server policy"] = "Мод MoveBuildPieces отключён настройками сервера", ["Move lock request timed out or the object owner has an incompatible mod version"] = "Не удалось получить блокировку перемещения: истекло время ожидания или у владельца объекта несовместимая версия мода", ["Move confirmation timed out; verify the object's position"] = "Истекло время ожидания подтверждения; проверьте положение объекта", ["Move cancelled because the selected build piece changed"] = "Перемещение отменено: выбрана другая постройка", ["The object has no network owner yet"] = "У объекта пока нет сетевого владельца", ["Requesting move lock"] = "Запрос права на перемещение", ["Move lock was lost"] = "Блокировка перемещения потеряна", ["MoveBuildPieces server protocol stopped responding"] = "Протокол MoveBuildPieces на сервере перестал отвечать", ["Move cancelled"] = "Перемещение отменено", ["Waiting for server confirmation"] = "Ожидание подтверждения сервера", ["Hover a build piece to move it"] = "Наведитесь на постройку, которую нужно переместить", ["Only player-built removable pieces can be moved"] = "Можно перемещать только съёмные постройки, созданные игроком", ["Pieces that modify terrain are blocked"] = "Постройки, изменяющие поверхность, перемещать нельзя", ["Dynamic pieces are blocked"] = "Динамические объекты перемещать нельзя", ["Pieces with nested network objects cannot be moved safely"] = "Постройки с вложенными сетевыми объектами нельзя безопасно перемещать", ["Only the player who claimed this bed can move it"] = "Только игрок, занявший эту кровать, может её перемещать", ["This piece contains world state that cannot be relocated safely"] = "Эта постройка содержит состояние мира, которое нельзя безопасно перенести", ["Already being moved by another player"] = "Эту постройку уже перемещает другой игрок" }; private readonly Harmony _harmony = new Harmony("obelisk.ru.localization.move-build-pieces-messages"); private readonly ManualLogSource _log; internal MoveBuildPiecesMessagePatcher(ManualLogSource log) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _log = log; } internal int Apply() { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown Assembly pluginAssembly = ManagerAdapters.GetPluginAssembly("obelisk.valheim.movebuildpieces"); if (pluginAssembly == null) { return 0; } MethodInfo methodInfo = pluginAssembly.GetType("Obelisk.MoveBuildPieces.MoveController")?.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "Message" && method.GetParameters().Length == 2 && method.GetParameters()[1].ParameterType == typeof(string)); MethodInfo methodInfo2 = AccessTools.Method(typeof(MoveBuildPiecesMessagePatcher), "TranslateMessage", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { _log.LogWarning((object)"Move Build Pieces message localization was not installed because its message sink was not found."); return 0; } try { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return 1; } catch (Exception ex) { _log.LogWarning((object)("Move Build Pieces message localization skipped: " + ex.GetBaseException().Message)); return 0; } } private static void TranslateMessage(ref string __1) { __1 = Translate(__1); } internal static string Translate(string text) { if (string.IsNullOrEmpty(text) || text[0] == '$') { return text; } if (Exact.TryGetValue(text, out var value)) { return value; } if (text.StartsWith("Matching MoveBuildPieces ", StringComparison.Ordinal) && text.EndsWith(" is required on the server", StringComparison.Ordinal)) { int num = text.Length - "Matching MoveBuildPieces ".Length - " is required on the server".Length; if (num > 0) { string text2 = text.Substring("Matching MoveBuildPieces ".Length, num); return "На сервере требуется MoveBuildPieces версии " + text2; } } return ReplacePrefix(text, "Already being moved by ", "Эту постройку уже перемещает: ") ?? ReplacePrefix(text, "Move mode: place the ghost to move ", "Режим перемещения: разместите призрак, чтобы перенести ") ?? ReplacePrefix(text, "Moving ", "Перемещение: ") ?? ReplacePrefix(text, "Moved ", "Перемещено: ") ?? text; } private static string ReplacePrefix(string text, string source, string target) { if (!text.StartsWith(source, StringComparison.Ordinal)) { return null; } return target + text.Substring(source.Length); } } internal static class MwlTutorialAdapter { private const string PluginGuid = "warpalicious.More_World_Locations_AIO"; private static readonly IReadOnlyDictionary Tutorials = new Dictionary(StringComparer.Ordinal) { ["Introduction"] = ("Введение", "introduction.md"), ["Port"] = ("Порт", "port.md"), ["Manifest"] = ("Накладные", "manifest.md"), ["Shipment"] = ("Отправки", "shipment.md"), ["Delivery"] = ("Доставки", "delivery.md"), ["Teleport"] = ("Телепорт", "teleport.md") }; internal static int Apply(ManualLogSource log, TranslationCatalog catalog) { Type? obj = ManagerAdapters.GetPluginAssembly("warpalicious.More_World_Locations_AIO")?.GetType("More_World_Locations_AIO.tutorials.PortTutorial"); FieldInfo fieldInfo = obj?.GetField("tutorials", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo fieldInfo2 = obj?.GetField("label", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo fieldInfo3 = obj?.GetField("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(fieldInfo?.GetValue(null) is IEnumerable enumerable) || fieldInfo2 == null || fieldInfo3 == null) { return 0; } LoadedModule loadedModule = catalog.Modules.Find((LoadedModule item) => item.Definition.PluginGuid == "warpalicious.More_World_Locations_AIO"); string text = ((loadedModule == null) ? null : Path.Combine(Path.GetDirectoryName(loadedModule.Path) ?? string.Empty, "Tutorials")); if (string.IsNullOrEmpty(text) || !Directory.Exists(text)) { return 0; } int num = 0; foreach (object item in enumerable) { try { string text2 = fieldInfo2.GetValue(item) as string; if (!Tutorials.TryGetValue(text2 ?? string.Empty, out (string, string) value)) { continue; } string path = Path.Combine(text, value.Item2); if (!File.Exists(path)) { log.LogWarning((object)("More World Locations tutorial file is missing: " + value.Item2)); continue; } string text3 = File.ReadAllText(path, Encoding.UTF8); if (!text3.EndsWith("\n", StringComparison.Ordinal)) { text3 += "\n"; } fieldInfo2.SetValue(item, value.Item1); fieldInfo3.SetValue(item, text3); num += 2; } catch (Exception ex) { log.LogWarning((object)("More World Locations tutorial could not be updated: " + ex.GetBaseException().Message)); } } return num; } } internal static class NativeRussianDeltaPatcher { private const string HarmonyId = "obelisk.ru.localization.native-russian-deltas"; private const string Russian = "Russian"; private static readonly Dictionary Words = new Dictionary(StringComparer.Ordinal); private static ManualLogSource _log; private static Type _localizationType; private static FieldInfo _translationsField; private static bool _failureReported; internal static int Install(TranslationCatalog catalog, ManualLogSource log) { //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown _log = log; Words.Clear(); foreach (LoadedModule item in catalog.Modules.Where((LoadedModule candidate) => candidate.Definition.Adapter == AdapterKind.NativeRussianDelta)) { foreach (KeyValuePair word in item.Words) { Words[word.Key.TrimStart(new char[1] { '$' })] = word.Value; } } if (Words.Count == 0) { return 0; } _localizationType = AccessTools.TypeByName("Localization"); _translationsField = _localizationType?.GetField("m_translations", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo methodInfo = _localizationType?.GetMethod("SetupLanguage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null); MethodInfo methodInfo2 = AccessTools.Method(typeof(NativeRussianDeltaPatcher), "AfterSetupLanguage", (Type[])null, (Type[])null); if (methodInfo == null || _translationsField == null || methodInfo2 == null) { log.LogWarning((object)"Native Russian delta hook was not installed because the exact localization target was not found."); Words.Clear(); return 0; } try { new Harmony("obelisk.ru.localization.native-russian-deltas").Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return Words.Count; } catch (Exception ex) { Words.Clear(); log.LogWarning((object)("Native Russian delta hook was not installed: " + ex.GetBaseException().Message)); return 0; } } internal static int ApplyCurrent(bool forceCreate) { if (Words.Count == 0 || _localizationType == null) { return 0; } try { object obj = _localizationType.GetField("m_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); if (obj == null && forceCreate) { obj = _localizationType.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null); } if (obj == null) { return 0; } return string.Equals(_localizationType.GetMethod("GetSelectedLanguage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)?.Invoke(obj, null) as string, "Russian", StringComparison.Ordinal) ? ApplyWords(obj) : 0; } catch (Exception exception) { ReportFailure(exception); return 0; } } private static void AfterSetupLanguage(object __instance, string language) { if (!string.Equals(language, "Russian", StringComparison.Ordinal)) { return; } try { ApplyWords(__instance); } catch (Exception exception) { ReportFailure(exception); } } private static int ApplyWords(object localization) { if (!(_translationsField?.GetValue(localization) is IDictionary translations)) { return 0; } return MergeMissing(translations, Words); } internal static int MergeMissing(IDictionary translations, IReadOnlyDictionary delta) { int num = 0; foreach (KeyValuePair deltum in delta) { if (!translations.Contains(deltum.Key) || string.IsNullOrWhiteSpace(translations[deltum.Key] as string)) { translations[deltum.Key] = deltum.Value; num++; } } return num; } private static void ReportFailure(Exception exception) { if (!_failureReported) { _failureReported = true; ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Native Russian delta application failed: " + exception.GetBaseException().Message)); } } } } internal static class PlantEverythingAdapter { internal static int Apply(ManualLogSource log, TranslationCatalog catalog) { if (!((ManagerAdapters.GetPluginAssembly("advize.PlantEverything")?.GetType("Advize_PlantEverything.StaticContent"))?.GetField("DefaultLocalizedStrings", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) is IDictionary dictionary)) { return 0; } LoadedModule loadedModule = catalog.Modules.FirstOrDefault((LoadedModule item) => item.Definition.PluginGuid == "advize.PlantEverything"); if (loadedModule == null) { return 0; } int num = 0; foreach (KeyValuePair word in loadedModule.Words) { string key = (word.Key.StartsWith("pe") ? word.Key.Substring(2) : word.Key); if (dictionary.Contains(key)) { dictionary[key] = word.Value; num++; } } return num; } } [BepInPlugin("obelisk.ru.localization", "ObeliskRU", "0.2.11")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [DefaultExecutionOrder(32000)] public sealed class Plugin : BaseUnityPlugin { internal const string PluginGuid = "obelisk.ru.localization"; internal const string PluginName = "ObeliskRU"; internal const string PluginVersion = "0.2.11"; private TranslationCatalog _catalog; private void Awake() { string pluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Paths.PluginPath; _catalog = new TranslationCatalog(((BaseUnityPlugin)this).Logger, pluginDirectory); _catalog.Load(); int num = NativeRussianDeltaPatcher.Install(_catalog, ((BaseUnityPlugin)this).Logger); new LiteralPatcher(((BaseUnityPlugin)this).Logger).Apply(LiteralRegistry.CreateForLoadedPlugins()); new MoveBuildPiecesMessagePatcher(((BaseUnityPlugin)this).Logger).Apply(); new HumanoidRandomizerPatcher(((BaseUnityPlugin)this).Logger).Apply(); new StatusDisplayPatcher(((BaseUnityPlugin)this).Logger).Apply(); new KnownTextMigrationPatcher(((BaseUnityPlugin)this).Logger).Apply(); new AmazingNatureTutorialPatcher(((BaseUnityPlugin)this).Logger).Apply(); int num2 = StaticTextAdapter.Apply(((BaseUnityPlugin)this).Logger); int num3 = ManagerAdapters.ApplyAll(_catalog, ((BaseUnityPlugin)this).Logger); int num4 = NativeRussianDeltaPatcher.ApplyCurrent(forceCreate: false); int num5 = LocalizationBridge.Apply(_catalog.Words, forceCreate: false, ((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Early Russian registration complete: vanilla={num5}, native={num3}, deltas={num4}/{num}, static={num2}."); } private void Start() { try { int num = ManagerAdapters.ApplyAll(_catalog, ((BaseUnityPlugin)this).Logger); int num2 = NativeRussianDeltaPatcher.ApplyCurrent(forceCreate: true); int num3 = StartupDataAdapters.Apply(((BaseUnityPlugin)this).Logger, _catalog); int num4 = LocalizationBridge.Apply(_catalog.Words, forceCreate: true, ((BaseUnityPlugin)this).Logger); LocalizationBridge.EvictCache(((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Russian localization finalized before the main menu: vanilla={num4}, native={num}, deltas={num2}, startup-data={num3}. No tick or polling translation hooks are installed."); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Final startup localization pass failed: {arg}"); } finally { _catalog = null; ((Behaviour)this).enabled = false; } } } internal static class PrefabTextAdapter { private static readonly string[] DirectDisplayFields = new string[13] { "m_name", "m_description", "m_tooltip", "m_hoverText", "m_topic", "m_label", "m_addOreTooltip", "m_emptyOreTooltip", "m_extractText", "m_useItemText", "m_notmoving", "m_inventoryInUseText", "m_emptyNet" }; private static readonly Regex ShipyardDisplayName = new Regex("^(?Plan|Sail|Head)\\s+(?[^:]+):\\s*(?.+)$", RegexOptions.Compiled | RegexOptions.CultureInvariant); internal static int Apply(ManualLogSource log, TranslationCatalog catalog) { return 0 + ApplyLoader("balrond.astafaraios.BalrondAmazingNature", BuildAmazingNatureWords(catalog), shipyard: false) + ApplyLoader("balrond.astafaraios.ZBalrondArsenalReborn", BuildArsenalWords(catalog), shipyard: false) + ApplyLoader("balrond.astafaraios.BalrondConstructions", BuildConstructionsWords(catalog), shipyard: false) + ApplyLoader("balrond.astafaraios.BalrondFurnitureReborn", GetModuleWords(catalog, "balrond.astafaraios.BalrondFurnitureReborn"), shipyard: false) + ApplyLoader("balrond.astafaraios.BalrondHumanoidRandomizer", BuildHumanoidRandomizerWords(), shipyard: false) + ApplyShipyard(catalog) + ApplyEmbeddedPrefabManagers("org.bepinex.plugins.odinssteelworks", BuildOdinsSteelworksWords(catalog)) + ApplyVnei(catalog) + ApplyAzuCraftyBoxesStatus(); } private static int ApplyAzuCraftyBoxesStatus() { object obj = (ManagerAdapters.GetPluginAssembly("Azumatt.AzuCraftyBoxes")?.GetType("AzuCraftyBoxes.Patches.SE_ContainerPull"))?.GetField("SE_ContainerPulling", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); if (obj == null) { return 0; } Dictionary words = new Dictionary(StringComparer.Ordinal) { ["Preventing Pulling"] = "Извлечение отключено", ["Prevents pulling from nearby containers & backpacks"] = "Запрещает извлекать предметы из ближайших контейнеров и рюкзаков." }; return NormalizeDisplayObject(obj, words, shipyard: false); } private static int ApplyEmbeddedPrefabManagers(string pluginGuid, Dictionary words) { Assembly pluginAssembly = ManagerAdapters.GetPluginAssembly(pluginGuid); if (pluginAssembly == null) { return 0; } HashSet hashSet = new HashSet(); (string, string)[] array = new(string, string)[2] { ("ItemManager.PrefabManager", "prefabs"), ("PieceManager.PiecePrefabManager", "piecePrefabs") }; for (int i = 0; i < array.Length; i++) { var (name, name2) = array[i]; CollectUnityObjects(pluginAssembly.GetType(name)?.GetField(name2, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null), hashSet); } return hashSet.Sum((Object root) => NormalizeUnityObject(root, words, shipyard: false)); } private static int ApplyVnei(TranslationCatalog catalog) { Type type = ManagerAdapters.GetPluginAssembly("com.maxsch.valheim.vnei")?.GetType("VNEI.Plugin"); if (type == null) { return 0; } Dictionary words = GetModuleWords(catalog, "com.maxsch.valheim.vnei"); Add(words, ("Search...", "Поиск..."), ("Search", "Поиск"), ("Show Undefined Items", "Показать неопределённые объекты"), ("Show Creatures", "Показать существ"), ("Show Pieces", "Показать постройки"), ("Show Generic Items", "Показать обычные предметы"), ("Show Food", "Показать еду"), ("Show Weapon/Shields", "Показать оружие и щиты"), ("Show Armor", "Показать броню"), ("Show only Favorites", "Показать только избранное")); HashSet hashSet = new HashSet(); FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < fields.Length; i++) { CollectUnityObjects(fields[i].GetValue(null), hashSet); } return hashSet.Sum((Object root) => NormalizeUnityObject(root, words, shipyard: false)); } private static int ApplyLoader(string pluginGuid, Dictionary words, bool shipyard) { Assembly pluginAssembly = ManagerAdapters.GetPluginAssembly(pluginGuid); if (pluginAssembly == null) { return 0; } HashSet hashSet = new HashSet(); foreach (Type item in ReflectionUtil.GetTypesSafely(pluginAssembly)) { FieldInfo field = item.GetField("modResourceLoader", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { continue; } object value = field.GetValue(null); if (value != null) { FieldInfo[] fields = value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < fields.Length; i++) { CollectUnityObjects(fields[i].GetValue(value), hashSet); } } } return hashSet.Sum((Object root) => NormalizeUnityObject(root, words, shipyard)); } private static int ApplyShipyard(TranslationCatalog catalog) { Type type = ManagerAdapters.GetPluginAssembly("balrond.astafaraios.BalrondShipyard")?.GetType("BalrondShipyard.Launch"); if (type == null) { return 0; } HashSet hashSet = new HashSet(); FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < fields.Length; i++) { CollectUnityObjects(fields[i].GetValue(null), hashSet); } Dictionary words = BuildShipyardWords(catalog); return hashSet.Sum((Object root) => NormalizeUnityObject(root, words, shipyard: true)); } private static void CollectUnityObjects(object value, ISet output) { Object val = (Object)((value is Object) ? value : null); if (val != null) { output.Add(val); } else { if (value is string || !(value is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { Object val2 = (Object)((item is Object) ? item : null); if (val2 != null) { output.Add(val2); } } } } private static int NormalizeUnityObject(Object root, IReadOnlyDictionary words, bool shipyard) { if (root == (Object)null) { return 0; } GameObject val = (GameObject)(object)((root is GameObject) ? root : null); if (val != null) { return NormalizeGameObject(val, words, shipyard); } return NormalizeDisplayObject(root, words, shipyard); } private static int NormalizeGameObject(GameObject gameObject, IReadOnlyDictionary words, bool shipyard) { int num = 0; Component[] componentsInChildren = gameObject.GetComponentsInChildren(true); foreach (Component val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } num += NormalizeDisplayObject(val, words, shipyard); if (((object)val).GetType().Name == "ItemDrop") { object member = ReflectionUtil.GetMember(ReflectionUtil.GetMember(val, "m_itemData"), "m_shared"); int num2 = NormalizeStringMember(member, "m_name", words, shipyard); num += num2; if (shipyard && num2 == 0) { num += NormalizeStringMemberByKey(member, "m_name", ((Object)val.gameObject).name, words); } num += NormalizeStringMember(member, "m_description", words, shipyard); } object member2 = ReflectionUtil.GetMember(val, "m_text"); if (member2 != null && !(member2 is string)) { num += NormalizeStringMember(member2, "m_label", words, shipyard); num += NormalizeStringMember(member2, "m_topic", words, shipyard); num += NormalizeStringMember(member2, "m_text", words, shipyard); } if (ReflectionUtil.GetMember(val, "m_texts") is IList list) { for (int j = 0; j < list.Count; j++) { if (list[j] is string current && TryTranslate(current, words, shipyard, out var translated)) { list[j] = translated; num++; } } } PropertyInfo property = ((object)val).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)property != null && property.CanRead && property.CanWrite && property.PropertyType == typeof(string) && TryTranslate(property.GetValue(val, null) as string, words, shipyard, out var translated2)) { property.SetValue(val, translated2, null); num++; } } if (!shipyard && ((Object)gameObject).name == "wood_roof_flat_bal") { Component componentByName = GetComponentByName(gameObject, "Piece"); if ((Object)(object)componentByName != (Object)null && !string.Equals(ReflectionUtil.GetMember(componentByName, "m_name") as string, "$obelisk_ban_wood_roof_flat", StringComparison.Ordinal)) { ReflectionUtil.SetMember(componentByName, "m_name", "$obelisk_ban_wood_roof_flat"); num++; } } return num; } private static int NormalizeDisplayObject(object instance, IReadOnlyDictionary words, bool shipyard) { int num = 0; string[] directDisplayFields = DirectDisplayFields; foreach (string memberName in directDisplayFields) { num += NormalizeStringMember(instance, memberName, words, shipyard); } return num; } private static int NormalizeStringMember(object instance, string memberName, IReadOnlyDictionary words, bool shipyard) { if (!TryTranslate(ReflectionUtil.GetMember(instance, memberName) as string, words, shipyard, out var translated)) { return 0; } return ReflectionUtil.SetMember(instance, memberName, translated) ? 1 : 0; } private static int NormalizeStringMemberByKey(object instance, string memberName, string key, IReadOnlyDictionary words) { string text = ReflectionUtil.GetMember(instance, memberName) as string; if (string.IsNullOrEmpty(text) || text[0] == '$' || string.IsNullOrEmpty(key) || !words.TryGetValue(key, out var value) || string.Equals(text, value, StringComparison.Ordinal)) { return 0; } return ReflectionUtil.SetMember(instance, memberName, value) ? 1 : 0; } private static bool TryTranslate(string current, IReadOnlyDictionary words, bool shipyard, out string translated) { translated = null; if (string.IsNullOrEmpty(current)) { return false; } if (words.TryGetValue(current, out translated) && !string.Equals(current, translated, StringComparison.Ordinal)) { return true; } if (current[0] == '$') { translated = null; return false; } if (shipyard && TryTranslateShipyardDisplayName(current, out translated)) { return true; } translated = null; return false; } private static Component GetComponentByName(GameObject gameObject, string typeName) { return ((IEnumerable)gameObject.GetComponents()).FirstOrDefault((Func)((Component component) => (Object)(object)component != (Object)null && ((object)component).GetType().Name == typeName)); } private static Dictionary BuildAmazingNatureWords(TranslationCatalog catalog) { Dictionary moduleWords = GetModuleWords(catalog, "balrond.astafaraios.BalrondAmazingNature"); Add(moduleWords, ("Add Non-base wood", "Добавить необычную древесину"), ("Add Patchwork leather", "Добавить лоскутную кожу"), ("Pick up refined leather", "Забрать выделанную кожу"), ("Pick up wood", "Забрать древесину"), ("Add", "Добавить"), ("Floating Water Debris", "Плавающие обломки"), ("Potato", "Картофель"), ("Can be eaten in many ways or grown into a plant.", "Его можно приготовить разными способами или посадить в землю."), ("Magma stone", "Магматический камень"), ("Igneous Rock. Solid and still warm", "Крепкая магматическая порода, всё ещё хранящая тепло."), ("Taunt Sword", "Меч вызова"), ("Hitting target with thsi sword will make him attack only you for next 5 seconds", "Попадание этим мечом заставляет цель атаковать только вас в течение 5 секунд."), ("Log", "Бревно"), ("Fletcher's table", "Стол лучника"), ("Rune Forge station", "Рунная кузница"), ("For your advanced work with iron and other metal. Her eyou be making your weapons and armors form now on.", "Для сложной работы с железом и другими металлами. Отныне здесь вы будете создавать оружие и доспехи."), ("This is Runeforge station. Most advanced forge blessed by gods. All your weapons and armors for near future will be made here.", "Это рунная кузница — вершина кузнечного мастерства, благословлённая богами. Здесь вы будете создавать своё лучшее оружие и доспехи."), ("This is your new table for all things archery. A long the way as you progress more and more recipes for arrows will show up here", "Это новый стол для всего, что связано со стрельбой из лука. По мере развития здесь будет появляться всё больше рецептов стрел."), ("We can see you!", "Мы тебя видим!"), ("Who are you ?", "Кто ты?"), ("You are not our Master!", "Ты не наш повелитель!"), ("Don't stare into the void...", "Не вглядывайся в пустоту..."), ("Let me out !", "Выпусти меня!"), ("I will carve your soul!", "Я изрежу твою душу!"), ("Raincoat", "Непромокаемость"), ("Waterproof", "Непромокаемость"), ("Protects against Wet but not Soaked", "Защищает от сырости, но не от полного промокания."), ("TauntAttack", "Провокация"), ("Taunts the Monster setting it target to caster", "Провоцирует чудовище и заставляет его выбрать заклинателя целью."), ("$piece_portal HD", "Малый природный портал"), ("$prop_birch_sapling Plains", "Саженец осенней берёзы")); return moduleWords; } private static Dictionary BuildArsenalWords(TranslationCatalog catalog) { Dictionary moduleWords = GetModuleWords(catalog, "balrond.astafaraios.ZBalrondArsenalReborn"); Add(moduleWords, ("Jormuntide", "Йормунтид"), ("Better stronger deadlier", "Острее, прочнее, смертоноснее."), ("Kromium", "Кромиум"), ("Blood boil", "Кровь вскипает."), ("Burn the heretics !", "Сжечь еретиков!"), ("Death to the scourge and death to the living!", "Смерть нечисти и смерть живым!"), ("Do nto defy the Lord", "Не противьтесь владыке."), ("Forgotten Power lies within you", "В вас дремлет забытая сила."), ("Geared up for first challange", "Вы готовы к первому испытанию."), ("Increase your skill in Blood Magic and doubles your health regeneration.", "Повышает навык магии крови и удваивает восстановление здоровья."), ("Increase your skill with axes and woodcutting", "Повышает навыки владения топорами и рубки леса."), ("Iron guard stands tall.", "Железный страж стоит непоколебимо."), ("LIght yet guarded", "Лёгкость под надёжной защитой."), ("Nature responds to your calls", "Природа отвечает на ваш зов."), ("One who sung in name of the gods.", "Тот, кто воспевал богов."), ("Proven as true lord of the Sea", "Вы доказали право зваться владыкой морей."), ("Reinforced to fight evil in name of the gods", "Укреплено для борьбы со злом во имя богов."), ("Ride with me....", "Скачи со мной..."), ("The best of Bronze and Troll armor", "Лучшее от бронзовых доспехов и брони тролля."), ("The gift from Allfather", "Дар Всеотца."), ("The spirits talk with you", "Духи говорят с вами."), ("Unbreakable", "Несокрушимость."), ("You fear not waters", "Вам не страшна вода."), ("You feel strange tingling eating this. Increase Reistance against lighting damage.", "После этого блюда по телу пробегает странное покалывание. Повышает сопротивление урону молнией."), ("You lead i follow", "Вы ведёте — я следую."), ("You move swiftly thru forest", "Вы стремительно движетесь сквозь лес.")); return moduleWords; } private static Dictionary BuildHumanoidRandomizerWords() { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); Add(dictionary, ("You been shielded", "На вас наложен щит."), ("Bleeding", "Кровотечение"), ("You been slashed open. Any more cuts will hurt more", "Вы истекаете кровью. Следующие рубящие удары причинят больше вреда."), ("Pierced", "Пробитая броня"), ("You been pierced. Another stab like that will be more fatal", "Ваша броня пробита. Следующий колющий удар будет опаснее."), ("Crushed", "Раздробленность"), ("Your bone was crushed. Another blunt hit will be fatal", "Ваши кости раздроблены. Следующий дробящий удар будет опаснее."), ("First Aid", "Первая помощь"), ("Applied first aid. This will help mend the pain", "Оказана первая помощь, постепенно облегчающая боль."), ("SkeletonHeal", "Исцеление скелета"), ("DraugrHeal", "Исцеление драугра"), ("GoblinHeal", "Исцеление фулинга"), ("A sailor's friend", "Верный спутник моряка."), ("A simple bow.", "Простой лук."), ("A slab of stone on a stick", "Каменная глыба на рукояти."), ("A well made two handed sword.", "Добротный двуручный меч."), ("base axe", "обычный топор"), ("base staff-left", "обычный посох в левой руке"), ("base staff-right", "обычный посох в правой руке"), ("base sword", "обычный меч"), ("base sword left", "обычный меч в левой руке"), ("Bow", "Лук"), ("claws", "когти"), ("claws kick", "удар когтями"), ("Club", "Дубина"), ("cluterbomb fire", "огненная кассетная бомба"), ("dual_knife", "парные ножи"), ("dual_knife_slash", "рубящий удар парными ножами"), ("dual_pierce", "парный колющий удар"), ("dual_slashBLUNT", "парный комбинированный удар"), ("fire spray", "струя огня"), ("fire_hand_left", "огонь в левой руке"), ("fire_hand_right", "огонь в правой руке"), ("fireball", "огненный шар"), ("Flint spear", "Кремнёвое копьё"), ("frostbolt", "ледяная стрела"), ("heal", "исцеление"), ("ice_hand_left", "лёд в левой руке"), ("ice_hand_right", "лёд в правой руке"), ("Iron Bastard Sword", "Железный полуторный меч"), ("kick", "пинок"), ("left special", "особая атака левой рукой"), ("left_blade", "клинок в левой руке"), ("left_blunt", "дробящее оружие в левой руке"), ("mage_hand_right", "магия в правой руке"), ("poison spray", "струя яда"), ("poison_hand_left", "яд в левой руке"), ("poison_hand_right", "яд в правой руке"), ("poisonbolt", "ядовитая стрела"), ("protect ", "защита"), ("protectslef", "самозащита"), ("right special", "особая атака правой рукой"), ("right_blade", "клинок в правой руке"), ("right_blunt", "дробящее оружие в правой руке"), ("Sailor's saber", "Матросская сабля"), ("shadowbolt_left", "теневая стрела"), ("spirit spray", "струя духа"), ("spirit_hand_left", "дух в левой руке"), ("spirit_hand_right", "дух в правой руке"), ("Stonecrusher", "Камнедробитель"), ("thunder_hand_left", "молния в левой руке"), ("thunder_hand_right", "молния в правой руке"), ("Torch left", "Факел в левой руке"), ("twohand_pierce", "двуручный колющий удар"), ("twohand_slam", "двуручный удар о землю"), ("twohand_slash", "двуручный рубящий удар"), ("twohand_spin", "двуручный круговой удар"), ("twohand_swing", "двуручный размашистый удар"), ("twohand_uppercutBlunt", "двуручный дробящий апперкот"), ("twohand_uppercutPierce", "двуручный колющий апперкот"), ("twohand_uppercutSlash", "двуручный рубящий апперкот"), ("Unarmed", "Без оружия")); return dictionary; } private static Dictionary BuildShipyardWords(TranslationCatalog catalog) { Dictionary moduleWords = GetModuleWords(catalog, "balrond.astafaraios.BalrondShipyard"); Add(moduleWords, ("Scribe's Table", "Чертёжный стол"), ("Scribe's table", "Чертёжный стол"), ("Shipyard Station", "Верфь"), ("For All Your ship upgrades", "Для всех улучшений вашего судна."), ("Recommended station . For creating all ship schematics", "Рекомендуемая мастерская для создания всех корабельных чертежей."), ("For creating all ship schematics", "Для создания всех корабельных чертежей."), ("Shipyard material storage", "Хранилище материалов верфи"), ("A set of easy to use dyes to color your sails.", "Набор удобных красок для окрашивания парусов."), ("Slower cargo vessel", "Медленное грузовое судно."), ("Slim fast vessel", "Лёгкое быстроходное судно."), ("Can't pick up the net. Inventory is busy/full", "Нельзя забрать сеть: инвентарь занят или заполнен."), ("This is Scribe's table for creating Schematics. Those items provide information about ship upgrades", "Это чертёжный стол для создания схем с описанием корабельных улучшений."), ("This Shipyard will help you improve your ships. Put schematic in to the cargo and activate the table. If there is a ship in range that can be upgraded item put in to the cargo will be used.", "Эта верфь поможет улучшать суда. Положите чертёж в грузовой отсек и активируйте стол. Если рядом есть подходящее судно, чертёж будет использован."), ("This Trap will le tyou catch fish native to the biome you build it . \nPut proper Fishing Bait for the biome to increase chance of catching valuabe fishes. Note: You cant catch Angler with it.", "Эта ловушка позволяет ловить рыбу, обитающую в выбранном биоме. Используйте подходящую наживку, чтобы повысить шанс ценного улова. Удильщика этой сетью поймать нельзя."), ("Fishing Net", "Рыболовная сеть"), ("My Ship", "Моё судно"), ("Adds Anchor. You gain ability to stop your ship in place", "Добавляет якорь, позволяющий остановить судно на месте."), ("Set default(Red) sail color", "Устанавливает красный цвет паруса."), ("Set blue sail color", "Устанавливает синий цвет паруса."), ("Set Triangle Red sail color", "Устанавливает красный треугольный узор паруса."), ("Set green sail color", "Устанавливает зелёный цвет паруса."), ("Set transparent sail color", "Устанавливает прозрачный парус."), ("Set white sail color", "Устанавливает белый цвет паруса."), ("Set Triangle Green sail pattern", "Устанавливает зелёный треугольный узор паруса."), ("Set Yellow sail color", "Устанавливает жёлтый цвет паруса."), ("Set Gray sail color", "Устанавливает серый цвет паруса."), ("Set Vegvisir sail pattern", "Наносит на парус узор вегвизира."), ("Adds Nameplate. You can name your ship.", "Добавляет табличку, позволяющую дать судну имя."), ("Adds Lights. Adds Warm effect", "Добавляет фонари и эффект тепла."), ("Adds Shields. Increase ships health by 200", "Добавляет щиты и повышает прочность судна на 200."), ("Adds Shields. Increase ships health by 300", "Добавляет щиты и повышает прочность судна на 300."), ("Adds cage and ramp for transporting animals", "Добавляет клетку и трап для перевозки животных."), ("Adds supplies. Increase ship blunt resistance", "Добавляет припасы и повышает сопротивление судна дробящему урону."), ("Adds Tent. Increase health by 200", "Добавляет тент и повышает прочность на 200."), ("Adds side ropes. Allows you to get to upper deck from water level", "Добавляет бортовые снасти, по которым можно подняться на верхнюю палубу из воды."), ("Add Carapace armor. Increase ships health and resistance against pierce damage", "Добавляет панцирную броню, повышающую прочность и сопротивление колющему урону."), ("Add Oars. Increase ships movement capability", "Добавляет вёсла и улучшает ходовые качества судна."), ("Adds Barrels. Increase blunt resistance and health", "Добавляет бочки, повышающие сопротивление дробящему урону и прочность."), ("Adds Crates. Increase slash resistance and health", "Добавляет ящики, повышающие сопротивление рубящему урону и прочность."), ("Adds Tent. Gain shelter status. Increase health by 300", "Добавляет тент, дающий укрытие и повышающий прочность на 300."), ("Adds fishnet. You can fuel it with fishing bait to catch fish while sailing. Require at leas t1 empty slot in ship's cargo!", "Добавляет рыболовную сеть. Загрузите наживку, чтобы ловить рыбу во время плавания. В грузовом отсеке нужен хотя бы один свободный слот."), ("Adds Iron Spike Ram. You gain ability to ram with front spikes. REQUIRE ARMOR UPGRADE", "Добавляет железный таран. Позволяет таранить носовыми шипами. Требуется улучшение брони.")); Add(moduleWords, ("$piece_craftingstation . For All ship work related stuff.", "Мастерская корабела. Для всех работ, связанных с кораблями."), ("$piece_craftingstation . For creating all ship schematics", "Чертёжный стол. Для создания всех корабельных чертежей."), ("$ship_raft floor", "Настил плота")); return moduleWords; } private static Dictionary BuildConstructionsWords(TranslationCatalog catalog) { Dictionary moduleWords = GetModuleWords(catalog, "balrond.astafaraios.BalrondConstructions"); Add(moduleWords, ("$piece_blackmarble_tile_wall_2x2 window", "Стена из чёрного мрамора с окном 2×2")); return moduleWords; } private static Dictionary BuildOdinsSteelworksWords(TranslationCatalog catalog) { Dictionary moduleWords = GetModuleWords(catalog, "org.bepinex.plugins.odinssteelworks"); Add(moduleWords, ("Add Ore", "Добавить руду"), ("Add oxidized steel", "Добавить окисленную сталь"), ("Empty", "Забрать"), ("[$KEY_Use] pour liquid steel.", "[$KEY_Use] залить жидкую сталь."), ("[$KEY_Use] remove oxidized steel.", "[$KEY_Use] забрать окисленную сталь."), ("Add SteelCore", "Добавить стальную сердцевину"), ("Add SurtlingCoal", "Добавить суртлингский уголь"), ("OopsHammer", "Ошибка переработки"), ("This hammer means you have recycled a Steelworks Material with another mod.", "Этот молот появился потому, что материал Сталелитейной мастерской был переработан другим модом.")); return moduleWords; } private static Dictionary GetModuleWords(TranslationCatalog catalog, string pluginGuid) { LoadedModule loadedModule = catalog.Modules.FirstOrDefault((LoadedModule item) => item.Definition.PluginGuid == pluginGuid); if (loadedModule != null) { return new Dictionary(loadedModule.Words, StringComparer.Ordinal); } return new Dictionary(StringComparer.Ordinal); } private static void Add(IDictionary words, params (string Source, string Target)[] entries) { for (int i = 0; i < entries.Length; i++) { (string Source, string Target) tuple = entries[i]; string item = tuple.Source; string item2 = tuple.Target; words[item] = item2; } } private static bool TryTranslateShipyardDisplayName(string source, out string translated) { translated = null; Match match = ShipyardDisplayName.Match(source); if (!match.Success) { return false; } string text = TranslateShip(match.Groups["ship"].Value.Trim()); string text2 = TranslateFeature(match.Groups["feature"].Value.Trim()); if (text == null || text2 == null) { return false; } string text3; switch (match.Groups["kind"].Value) { case "Plan": text3 = "План "; break; case "Sail": text3 = "Парус "; break; case "Head": text3 = "Носовое украшение "; break; default: return false; } translated = text3 + text + ": " + text2; return true; } private static string TranslateShip(string ship) { return ship switch { "Longship" => "драккара", "Drakkar" => "пепельного драккара", "Karve" => "карве", "Knarr" => "кнорра", "Snekke" => "снекки", "Holk" => "холька", _ => null, }; } private static string TranslateFeature(string feature) { switch (feature.Replace(" ", " ")) { case "Anchor": return "якорь"; case "Armor": return "броня"; case "Barrels": return "бочки"; case "Crates": return "ящики"; case "Lamps": return "фонари"; case "Wisp Lamps": return "светильни с огоньками"; case "Oar": case "Oars": return "вёсла"; case "Shields": return "щиты"; case "Roof": case "Tent": return "тент"; case "Supply Bag": case "Supply Crates": return "припасы"; case "Ropes": return "снасти"; case "Name Plate": return "табличка с названием"; case "Fishnet": return "рыболовная сеть"; case "Ram": return "таран"; case "Cage": return "клетка и трап"; case "Cernyx": return "керникс"; case "Dragon": return "дракон"; case "Osberg": return "Осеберг"; case "Skull": return "череп"; case "Default": return "красный"; case "Blue": return "синий"; case "Dark Gray": case "Dark Grey": return "серый"; case "Green": return "зелёный"; case "Transparent": return "прозрачный"; case "White": return "белый"; case "Triangle-Green": return "зелёный треугольный узор"; case "Triangle-Red": return "красный треугольный узор"; case "Vegvisir": return "вегвизир"; case "Yellow": return "жёлтый"; default: return null; } } } internal static class ReflectionUtil { internal const BindingFlags AllStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; internal const BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; internal static IEnumerable GetTypesSafely(Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type type) => type != null); } } internal static object GetMember(object instance, string name) { if (instance == null) { return null; } Type type = instance.GetType(); object obj = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance); if (obj == null) { PropertyInfo? property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)property == null) { return null; } obj = property.GetValue(instance, null); } return obj; } internal static bool SetMember(object instance, string name, object value) { if (instance == null) { return false; } Type type = instance.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(instance, value); return true; } PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)property != null && property.CanWrite) { property.SetValue(instance, value, null); return true; } return false; } } internal static class StartupDataAdapters { internal static int Apply(ManualLogSource log, TranslationCatalog catalog) { return 0 + ApplySafely("Plant Everything", () => PlantEverythingAdapter.Apply(log, catalog), log) + ApplySafely("prefab text", () => PrefabTextAdapter.Apply(log, catalog), log) + ApplySafely("Dual Mastery skill", () => DualMasteryAdapter.Apply(catalog), log) + ApplySafely("Inventory Slots", () => InventorySlotsAdapter.Apply(log), log) + ApplySafely("Culinary Horizons categories", () => CulinaryCategoryAdapter.Apply(log), log) + ApplySafely("More World Locations tutorials", () => MwlTutorialAdapter.Apply(log, catalog), log); } private static int ApplySafely(string name, Func apply, ManualLogSource log) { try { return apply(); } catch (Exception ex) { log.LogWarning((object)(name + " startup localization skipped: " + ex.GetBaseException().Message)); return 0; } } } internal static class StaticTextAdapter { internal static int Apply(ManualLogSource log) { int num = 0; try { num += ApplyBossAddColors(); } catch (Exception ex) { log.LogWarning((object)("BossAdd color labels could not be updated: " + ex.GetBaseException().Message)); } try { num += ApplyShipmentOwnerMessage(); } catch (Exception ex2) { log.LogWarning((object)("More World Locations static text could not be updated: " + ex2.GetBaseException().Message)); } return num; } private static int ApplyBossAddColors() { if (!(ManagerAdapters.GetPluginAssembly("bossadd")?.GetType("bossadd.MiniBonemassVariants")?.GetField("KoreanNames", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) is string[] array) || array.Length != 5) { return 0; } string[] array2 = new string[5] { "красный", "оранжевый", "жёлтый", "зелёный", "синий" }; int num = 0; for (int i = 0; i < array2.Length; i++) { if (!string.Equals(array[i], array2[i], StringComparison.Ordinal)) { array[i] = array2[i]; num++; } } return num; } private static int ApplyShipmentOwnerMessage() { FieldInfo fieldInfo = ManagerAdapters.GetPluginAssembly("warpalicious.More_World_Locations_AIO")?.GetType("More_World_Locations_AIO.LocalKeys")?.GetField("ShipmentNotOwned", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (fieldInfo == null) { return 0; } if (string.Equals(fieldInfo.GetValue(null) as string, "Этот груз принадлежит другому игроку", StringComparison.Ordinal)) { return 0; } fieldInfo.SetValue(null, "Этот груз принадлежит другому игроку"); return string.Equals(fieldInfo.GetValue(null) as string, "Этот груз принадлежит другому игроку", StringComparison.Ordinal) ? 1 : 0; } } internal sealed class StatusDisplayPatcher { private const string HarmonyId = "obelisk.ru.localization.status-display"; private static readonly IReadOnlyDictionary DisplayNames = new Dictionary(StringComparer.Ordinal) { ["Shield_MonsterRandomizer"] = "Щит", ["Waterproof"] = "Непромокаемость" }; private static readonly (string PluginGuid, string TypeName, string MethodName)[] Targets = new(string, string, string)[2] { ("balrond.astafaraios.BalrondHumanoidRandomizer", "BalrondHumanoidRandomizer.SE_LevelShield", "Setup"), ("balrond.astafaraios.BalrondShipyard", "BalrondBiomes.SE_WetImmunity", "Awake") }; private readonly ManualLogSource _log; private readonly Harmony _harmony = new Harmony("obelisk.ru.localization.status-display"); internal StatusDisplayPatcher(ManualLogSource log) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _log = log; } internal int Apply() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(StatusDisplayPatcher), "TranslateDisplayName", (Type[])null, (Type[])null); int num = 0; (string, string, string)[] targets = Targets; for (int i = 0; i < targets.Length; i++) { var (pluginGuid, text, name) = targets[i]; try { MethodInfo methodInfo2 = ManagerAdapters.GetPluginAssembly(pluginGuid)?.GetType(text)?.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(methodInfo2 == null) && HasDisplayNameAssignment(methodInfo2)) { _harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null); num++; } } catch (Exception ex) { _log.LogWarning((object)("Status display patch skipped for " + text + ": " + ex.GetBaseException().Message)); } } if (num != 0) { _log.LogInfo((object)$"Installed {num} identity-safe status display-name patches."); } return num; } private static bool HasDisplayNameAssignment(MethodInfo method) { List originalInstructions = PatchProcessor.GetOriginalInstructions((MethodBase)method, (ILGenerator)null); for (int i = 0; i + 1 < originalInstructions.Count; i++) { if (originalInstructions[i].opcode == OpCodes.Ldstr && originalInstructions[i].operand is string key && DisplayNames.ContainsKey(key) && originalInstructions[i + 1].opcode == OpCodes.Stfld && originalInstructions[i + 1].operand is FieldInfo { Name: "m_name" }) { return true; } } return false; } private static IEnumerable TranslateDisplayName(IEnumerable instructions) { List source = instructions.ToList(); for (int index = 0; index < source.Count; index++) { CodeInstruction val = source[index]; if (val.opcode == OpCodes.Ldstr && val.operand is string key && DisplayNames.TryGetValue(key, out var value) && index + 1 < source.Count && source[index + 1].opcode == OpCodes.Stfld && source[index + 1].operand is FieldInfo { Name: "m_name" }) { val.operand = value; } yield return val; } } } internal sealed class LoadedModule { internal ModuleDefinition Definition { get; } internal string Path { get; } internal Dictionary Words { get; } internal LoadedModule(ModuleDefinition definition, string path, Dictionary words) { Definition = definition; Path = path; Words = words; } } internal sealed class TranslationCatalog { private readonly ManualLogSource _log; private readonly string _pluginDirectory; internal Dictionary Words { get; } = new Dictionary(StringComparer.Ordinal); internal List Modules { get; } = new List(); internal TranslationCatalog(ManualLogSource log, string pluginDirectory) { _log = log; _pluginDirectory = pluginDirectory; } internal void Load() { Words.Clear(); Modules.Clear(); foreach (ModuleDefinition item in ModuleCatalog.All) { if (!Chainloader.PluginInfos.ContainsKey(item.PluginGuid)) { continue; } string path = Path.Combine(_pluginDirectory, item.RelativePath); if (!File.Exists(path)) { _log.LogWarning((object)(item.Name + ": localization file is missing: " + item.RelativePath)); continue; } Dictionary dictionary; try { dictionary = ((item.Adapter == AdapterKind.CreatureLevelControl) ? new Dictionary(StringComparer.Ordinal) : TranslationParser.ParseFile(path)); } catch (Exception ex) { _log.LogError((object)(item.Name + ": failed to parse localization: " + ex.Message)); continue; } Modules.Add(new LoadedModule(item, path, dictionary)); if (!item.MergeIntoGame) { continue; } foreach (KeyValuePair item2 in dictionary) { if (Words.TryGetValue(item2.Key, out var value) && !string.Equals(value, item2.Value, StringComparison.Ordinal) && !IsExpectedConflict(item2.Key)) { _log.LogWarning((object)("Localization key conflict '" + item2.Key + "': later module '" + item.Name + "' takes precedence.")); } Words[item2.Key] = item2.Value; } } AddBuiltInWords(); _log.LogInfo((object)$"Loaded {Words.Count} Russian words from {Modules.Count} active localization modules."); } private void AddBuiltInWords() { AmazingNatureTutorialLocalization.AddWords(Words); if (Chainloader.PluginInfos.ContainsKey("balrond.astafaraios.BalrondAmazingNature")) { Words["obelisk_ban_wood_roof_flat"] = "Плоская деревянная крыша"; } } private static bool IsExpectedConflict(string key) { switch (key) { default: return key == "config_update_line_5_DO"; case "tag_wood_roof_flat_bal": case "config_update_line_2_DO": case "config_update_line_4_DO": return true; } } } internal static class TranslationParser { private static readonly Regex JsonPair = new Regex("\\\"(?(?:\\\\.|[^\\\"\\\\])*)\\\"\\s*:\\s*\\\"(?(?:\\\\.|[^\\\"\\\\])*)\\\"", RegexOptions.Compiled | RegexOptions.CultureInvariant); internal static Dictionary ParseFile(string path) { string content = File.ReadAllText(path, Encoding.UTF8); if (!string.Equals(Path.GetExtension(path), ".json", StringComparison.OrdinalIgnoreCase)) { return ParseFlatYaml(content); } return ParseFlatJson(content); } internal static Dictionary ParseFlatJson(string content) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (Match item in JsonPair.Matches(content)) { string text = DecodeJsonString(item.Groups["key"].Value).TrimStart(new char[1] { '$' }); if (text.Length != 0) { dictionary[text] = DecodeJsonString(item.Groups["value"].Value); } } return dictionary; } internal static Dictionary ParseFlatYaml(string content) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); using StringReader stringReader = new StringReader(content); string text; while ((text = stringReader.ReadLine()) != null) { if (text.Length == 0 || char.IsWhiteSpace(text[0]) || text[0] == '#') { continue; } int num = text.IndexOf(':'); if (num > 0) { string text2 = text.Substring(0, num).Trim().TrimStart(new char[1] { '$' }); string text3 = text.Substring(num + 1).Trim(); if (text2.Length != 0 && !(text3 == "|") && !(text3 == ">")) { dictionary[text2] = DecodeYamlScalar(text3); } } } return dictionary; } private static string DecodeYamlScalar(string value) { if (value.Length >= 2 && value[0] == '\'' && value[value.Length - 1] == '\'') { return value.Substring(1, value.Length - 2).Replace("''", "'"); } if (value.Length >= 2 && value[0] == '"' && value[value.Length - 1] == '"') { return DecodeJsonString(value.Substring(1, value.Length - 2)); } return value; } private static string DecodeJsonString(string value) { StringBuilder stringBuilder = new StringBuilder(value.Length); for (int i = 0; i < value.Length; i++) { char c = value[i]; if (c != '\\' || i + 1 >= value.Length) { stringBuilder.Append(c); continue; } char c2 = value[++i]; switch (c2) { case '"': stringBuilder.Append('"'); continue; case '\\': stringBuilder.Append('\\'); continue; case '/': stringBuilder.Append('/'); continue; case 'b': stringBuilder.Append('\b'); continue; case 'f': stringBuilder.Append('\f'); continue; case 'n': stringBuilder.Append('\n'); continue; case 'r': stringBuilder.Append('\r'); continue; case 't': stringBuilder.Append('\t'); continue; case 'u': if (i + 4 < value.Length) { if (ushort.TryParse(value.Substring(i + 1, 4), NumberStyles.HexNumber, null, out var result)) { stringBuilder.Append((char)result); i += 4; } else { stringBuilder.Append("\\u"); } continue; } break; } stringBuilder.Append(c2); } return stringBuilder.ToString(); } } }