Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Nexus Admin Tools v1.8.7
BepInEx/plugins/ValheimSpawner/ValheimSpawner.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
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.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ValheimSpawner")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.8.7.0")] [assembly: AssemblyInformationalVersion("1.8.7+3d7b5aa696e25836ea219ce323e5afe0779b2d82")] [assembly: AssemblyProduct("ValheimSpawner")] [assembly: AssemblyTitle("ValheimSpawner")] [assembly: AssemblyVersion("1.8.7.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 SuperNova.ValheimSpawner { internal static class AdminRules { internal const int Protocol = 185; internal const int MaxPacketBytes = 16384; internal const int AllModeBits = 1023; internal static bool Finite(double value) { if (!double.IsNaN(value)) { return !double.IsInfinity(value); } return false; } internal static bool Modes(int bits, float damage, float xp) { if (bits >= 0 && (bits & -1024) == 0 && Finite(damage) && damage >= 1f && damage <= 25f && Finite(xp) && xp >= 1f) { return xp <= 1000f; } return false; } internal static bool Spawn(string prefab, int count, int level, string name) { if (!string.IsNullOrWhiteSpace(prefab) && prefab.Length <= 128 && count >= 1 && count <= 50 && level >= 1 && level <= 3 && name != null) { return name.Length <= 48; } return false; } internal static bool Clock(int hour, int minute) { if (hour >= 0 && hour < 24 && minute >= 0) { return minute < 60; } return false; } internal static bool Skill(float level) { if (Finite(level) && level >= 0f) { return level <= 100f; } return false; } internal static int CycleIndex(int currentIndex, int delta, int count) { if (count <= 0) { return -1; } return (int)(((long)((currentIndex >= 0 && currentIndex < count) ? currentIndex : 0) + (long)(delta % count) + count) % count); } internal static string LocationName(string name, string prefab) { if (!string.IsNullOrWhiteSpace(name)) { return name; } return prefab ?? string.Empty; } internal static bool LocationNameInput(string value, bool allowEmpty) { if (value == null || value.Length > 48) { return false; } string text = value.Trim(); if (text.Length == 0) { return allowEmpty; } if (text.Length <= 48) { return !text.Any(char.IsControl); } return false; } internal static bool WorldPosition(float x, float y, float z, float maxCoordinate = 10500f) { if (Finite(x) && Finite(y) && Finite(z) && Math.Abs(x) <= maxCoordinate && Math.Abs(z) <= maxCoordinate && y >= -1000f) { return y <= 10000f; } return false; } internal static bool AdminDetail(string value) { if (value != null) { return value.Length <= 512; } return false; } internal static double SolarFraction(double raw) { if (!(raw < 0.15)) { if (!(raw <= 0.85)) { return 0.75 + (raw - 0.85) / 0.15 * 0.25; } return 0.25 + (raw - 0.15) / 0.7 * 0.5; } return raw / 0.15 * 0.25; } internal static double RawFraction(int hour, int minute) { if (!Clock(hour, minute)) { throw new ArgumentOutOfRangeException("hour"); } double num = (double)(hour * 60 + minute) / 1440.0; if (!(num < 0.25)) { if (!(num <= 0.75)) { return 0.85 + (num - 0.75) / 0.25 * 0.15; } return 0.15 + (num - 0.25) / 0.5 * 0.7; } return num / 0.25 * 0.15; } internal static double NextTime(double now, long dayLength, int hour, int minute) { if (!Finite(now) || now < 0.0 || dayLength < 1) { throw new ArgumentOutOfRangeException("now"); } double num = Math.Floor(now / (double)dayLength) * (double)dayLength + RawFraction(hour, minute) * (double)dayLength; if (!(num < now)) { return num; } return num + (double)dayLength; } } internal sealed class AdminRequestGate { private int _lastId; private double _nextPoll; private double _nextAction; internal bool TryAccept(int id, bool poll, double now) { if (!AdminRules.Finite(now) || now < 0.0 || id <= _lastId) { return false; } _lastId = id; if (poll) { if (now < _nextPoll) { return false; } _nextPoll = now + 0.5; } else { if (now < _nextAction) { return false; } _nextAction = now + 0.25; } return true; } } internal enum CreatureCategory { Regular, Boss } internal sealed class CreatureMetadata { public string English; public string Russian; public CreatureCategory Category; public string[] Biomes; public CreatureMetadata(string english, string russian, CreatureCategory category, string biomeList) { English = english; Russian = russian; Category = category; Biomes = (string.IsNullOrWhiteSpace(biomeList) ? new string[0] : biomeList.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries)); } } internal static class CreatureCatalog { private const string CatalogData = "\nEikthyr|Eikthyr|Эйктюр|Boss|Meadows\ngd_king|The Elder|Древний|Boss|Black Forest\nBonemass|Bonemass|Масса костей|Boss|Swamp\nDragon|Moder|Модер|Boss|Mountains\nGoblinKing|Yagluth|Яглут|Boss|Plains\nSeekerQueen|The Queen|Королева|Boss|Mistlands\nFader|Fader|Прародитель|Boss|Ashlands\nFrozenKing|Kall Fimbulbringer|Калль Фимбульбрингер|Boss|Deep North\nFrozenKing_p2|Kall Fimbulbringer (phase 2)|Калль Фимбульбрингер (фаза 2)|Boss|Deep North\nFrozenKing_p3|Kall Fimbulbringer (phase 3)|Калль Фимбульбрингер (фаза 3)|Boss|Deep North\nKallFimbulbringer|Kall Fimbulbringer|Калль Фимбульбрингер|Boss|Deep North\nBrenna|Brenna|Бренна|Boss|Black Forest\nGeirrhafa|Geirrhafa|Гейрафа|Boss|Mountains\nZil|Zil|Зил|Boss|Mistlands\nThungr|Thungr|Тангр|Boss|Mistlands\nCharred_Melee_Dyrnwyn|Lord Reto|Владыка Рето|Boss|Ashlands\nBoar|Boar|Кабан|Regular|Meadows;Black Forest\nBoar_piggy|Boar piglet|Поросёнок кабана|Regular|Meadows;Black Forest\nDeer|Deer|Олень|Regular|Meadows;Black Forest\nDeer_White|White deer|Белый олень|Regular|Deep North\nNeck|Neck|Никс|Regular|Meadows\nGreyling|Greyling|Грейлинг|Regular|Meadows\nGreydwarf|Greydwarf|Грейдворф|Regular|Black Forest\nGreydwarf_Elite|Greydwarf Brute|Грейдворф-берсерк|Regular|Black Forest\nGreydwarf_Shaman|Greydwarf Shaman|Грейдворф-шаман|Regular|Black Forest\nGreydwarf_Spawn|Greydwarf nest spawn|Порождение грейдворфов|Regular|Black Forest\nGreydwarf_Frozen|Frozen greydwarf|Ледяной грейдворф|Regular|Deep North\nGreydwarf_Shaman_Frozen|Frozen greydwarf shaman|Ледяной грейдворф-шаман|Regular|Deep North\nTroll|Troll|Тролль|Regular|Black Forest\nTrollFrost|Gammeltroll|Гаммельтролль|Regular|Deep North\nTroll_sleeping|Sleeping troll|Спящий тролль|Regular|Deep North\nSkeleton|Skeleton|Скелет|Regular|Black Forest;Swamp;Mountains\nSkeleton_NoArcher|Skeleton|Скелет|Regular|Black Forest;Swamp;Mountains\nSkeleton_Poison|Poison skeleton|Ядовитый скелет|Regular|Swamp\nSkeleton_Meadows|Meadows skeleton|Скелет лугов|Regular|Deep North\nSkeleton_Meadows_noarcher|Meadows skeleton|Скелет лугов|Regular|Deep North\nSkeleton_Mountains|Mountain skeleton|Горный скелет|Regular|Deep North\nSkeleton_Mountains_noarcher|Mountain skeleton|Горный скелет|Regular|Deep North\nSkeleton_Swamps|Swamp skeleton|Болотный скелет|Regular|Deep North\nSkeleton_Swamps_noarcher|Swamp skeleton|Болотный скелет|Regular|Deep North\nSkeleton_aspect|Skeleton aspect|Аспект скелета|Regular|Deep North\nDraugr|Draugr|Драугр|Regular|Swamp\nDraugr_Elite|Draugr Elite|Драугр-элита|Regular|Swamp\nDraugr_Ranged|Draugr Archer|Драугр-лучник|Regular|Swamp\nDraugr_Ranged_down|Draugr Archer (down)|Драугр-лучник (лежит)|Regular|Swamp\nDraugr_Elite_sleeping|Sleeping draugr elite|Спящий драугр-элита|Regular|Deep North\nDraugr_Ranged_sleeping|Sleeping draugr archer|Спящий драугр-лучник|Regular|Deep North\nDraugr_sleeping|Sleeping draugr|Спящий драугр|Regular|Deep North\nBlob|Blob|Сгустень|Regular|Swamp\nBlobElite|Blob elite|Большой сгустень|Regular|Swamp\nBlobTar|Tar blob|Дегтевой сгустень|Regular|Plains\nBlobMork|Shapeless Pulp|Бесформенная слизь|Regular|Deep North\nBlobMorkMini|Tiny Pulp|Крошечная слизь|Regular|Deep North\nBlobAspect|Blob aspect|Аспект сгустня|Regular|Deep North\nLeech|Leech|Пиявка|Regular|Swamp\nWraith|Wraith|Призрак|Regular|Swamp\nGhost|Ghost|Призрак|Regular|Black Forest\nGhost_old|Old ghost|Старый призрак|Regular|Deep North\nGhost_sleeping|Sleeping ghost|Спящий призрак|Regular|Deep North\nGhost_Void|Void ghost|Пустотный призрак|Regular|Deep North\nAbomination|Abomination|Мерзость|Regular|Swamp\nSurtling|Surtling|Суртлинг|Regular|Swamp;Ashlands\nWolf|Wolf|Волк|Regular|Mountains\nWolf_cub|Wolf cub|Волчонок|Regular|Mountains\nWolf_spiritcaller|Spirit-caller wolf|Волк призывателя духов|Regular|Deep North\nFenring|Fenring|Фенринг|Regular|Mountains\nFenring_Cultist|Fenring Cultist|Фенринг-культист|Regular|Mountains\nHatchling|Drake|Дракончик|Regular|Mountains\nStoneGolem|Stone Golem|Каменный голем|Regular|Mountains\nUlv|Ulv|Ульв|Regular|Mountains\nBat|Bat|Летучая мышь|Regular|Mountains\nBear|Bear|Медведь|Regular|Deep North\nCultist|Cultist|Культист|Regular|Mountains\nSerpent|Serpent|Морской змей|Regular|Ocean\nDeathsquito|Deathsquito|Смертожал|Regular|Plains\nLox|Lox|Локс|Regular|Plains\nGoblin|Fuling|Фулинг|Regular|Plains\nGoblinArcher|Fuling Archer|Фулинг-лучник|Regular|Plains\nGoblinShaman|Fuling Shaman|Фулинг-шаман|Regular|Plains\nGoblinBrute|Fuling Berserker|Фулинг-берсерк|Regular|Plains\nAsksvin|Asksvin|Асксвин|Regular|Ashlands\nAsksvin_cub|Asksvin calf|Детёныш асксвина|Regular|Ashlands\nMorgen|Morgen|Морген|Regular|Ashlands\nMorgen_Naked|Morgen (unarmored)|Морген без брони|Regular|Ashlands\nFallenValkyrie|Charred Valkyrie|Обугленная валькирия|Regular|Ashlands\nCharred_Twitcher|Charred Twitcher|Обугленный прыгун|Regular|Ashlands\nCharred_Melee|Charred Warrior|Обугленный воин|Regular|Ashlands\nCharred_Archer|Charred Marksman|Обугленный стрелок|Regular|Ashlands\nCharred_Mage|Charred Mage|Обугленный маг|Regular|Ashlands\nVolture|Volture|Вольтур|Regular|Ashlands\nTick|Tick|Клещ|Regular|Mistlands\nSeeker|Seeker|Искатель|Regular|Mistlands\nSeekerBrood|Seeker Brood|Выводок искателя|Regular|Mistlands\nSeekerBrute|Seeker Soldier|Искатель-солдат|Regular|Mistlands\nGjall|Gjall|Гьялль|Regular|Mistlands\nHare|Hare|Заяц|Regular|Mistlands\nDverger|Dvergr|Дверг|Regular|Mistlands\nDvergerMage|Dvergr Mage|Дверг-маг|Regular|Mistlands\nDvergerMageFire|Dvergr Fire Mage|Дверг-маг огня|Regular|Mistlands\nDvergerMageIce|Dvergr Frost Mage|Дверг-маг льда|Regular|Mistlands\nDvergerMageSupport|Dvergr Support Mage|Дверг-маг поддержки|Regular|Mistlands\nDvergerRogue|Dvergr Rogue|Дверг-разведчик|Regular|Mistlands\nChicken|Chicken|Курица|Regular|Any Biome\nHen|Hen|Курица-несушка|Regular|Any Biome\nFish1|Fish|Рыба|Regular|Rivers;Ocean\nFish2|Fish|Рыба|Regular|Rivers;Ocean\nFish3|Fish|Рыба|Regular|Ocean\nFish4_cave|Cave fish|Пещерная рыба|Regular|Mountain Caves\nCrow|Crow|Ворон|Regular|Any Biome\nAshCrow|Ash crow|Пепельный ворон|Regular|Ashlands\nGull|Gull|Чайка|Regular|Ocean;Meadows;Black Forest;Plains\nLeviathan|Leviathan|Левиафан|Regular|Ocean\nHugin|Hugin|Хугин|Regular|Any Biome\nMunin|Munin|Мунин|Regular|Mistlands;Ashlands\nOdin|Odin|Один|Regular|Any Biome\nValkyrie|Valkyrie|Валькирия|Regular|Any Biome\nHaldor|Haldor|Хальдор|Regular|Black Forest\nHildir|Hildir|Хильдир|Regular|Meadows\nBogWitch|The Bog Witch|Болотная ведьма|Regular|Swamp\nHalstein|Halstein|Хальштейн|Regular|Meadows\nBlub|Blåbær|Блубэр|Regular|Meadows\nHallon|Hallon|Халлон|Regular|Meadows\nBoar_spiritcaller|Spirit-caller boar|Кабан призывателя духов|Regular|Deep North\nMoose|Moose|Лось|Regular|Deep North\nMoose_calf|Moose calf|Лосёнок|Regular|Deep North\nMoose_spiritcaller|Spirit-caller moose|Лось призывателя духов|Regular|Deep North\nSeal|Seal|Тюлень|Regular|Deep North\nSeal_Pup|Seal pup|Детёныш тюленя|Regular|Deep North\nFrysling|Frysling|Фризлинг|Regular|Deep North\nFrostWisp|Frost Wisp|Ледяной дух|Regular|Deep North\nBat_Swamp|Swamp bat|Болотная летучая мышь|Regular|Swamp\nBarka|Barka|Барка|Regular|Deep North\nElaking|Elaking|Элакинг|Regular|Deep North\nElakingLantern|Elaking Lantern|Элакинг с фонарём|Regular|Deep North\nElakingMole|Eyeless One|Безглазый|Regular|Deep North\nFallenWarrior|Fallen Warrior|Павший воин|Regular|Deep North\nJotunWarrior|Krigen|Криген|Regular|Deep North\nJotunWarriorDualWield|Krigen (dual wield)|Криген с двумя оружиями|Regular|Deep North\nJotunWitch|Hexen|Хексен|Regular|Deep North\nShadowPerson|Shadow|Тень|Regular|Deep North\nTendril|Tendril|Усик|Regular|Deep North\nTendril_back|Back tendril|Задний усик|Regular|Deep North\nTentaRoot_wild|Wild tentaroot|Дикая тентакорень|Regular|Deep North\nWrithan|Writhan|Ритхан|Regular|Deep North\nGoblinDeepNorth|Deep North goblin|Гоблин Дальнего севера|Regular|Deep North\nDvergerDeepNorth|Imprisoned Dvergr|Пленённый дверг|Regular|Deep North\nBjorn_sleeping|Sleeping Bjorn|Спящий Бьёрн|Regular|Deep North\nBjorn_spiritcaller|Spirit-caller Bjorn|Бьёрн призывателя духов|Regular|Deep North\nAspect_Eikthyr|Aspect of the Lightning Stag|Аспект Эйктюра|Boss|Any Biome\nAspect_Elder|Aspect of the Living Forest|Аспект Древнего|Boss|Any Biome\nAspect_Bonemass|Aspect of the Twisted Soul|Аспект Массы костей|Boss|Any Biome\nAspect_Moder|Aspect of the Dragon Mother|Аспект Модер|Boss|Any Biome\nAspect_Yagluth|Aspect of the Writhing Dead|Аспект Яглута|Boss|Any Biome\nAspect_SeekerQueen|Aspect of the Crawling Matriarch|Аспект Королевы|Boss|Any Biome\nAspect_Fader|Aspect of the Emerald Flame|Аспект Прародителя|Boss|Any Biome\nAspect_TentaRoot|Aspect of the Tentaroot|Аспект тентакорня|Boss|Any Biome\n"; private static readonly Dictionary<string, CreatureMetadata> Metadata = Build(); private static Dictionary<string, CreatureMetadata> Build() { Dictionary<string, CreatureMetadata> dictionary = new Dictionary<string, CreatureMetadata>(StringComparer.OrdinalIgnoreCase); string[] array = "\nEikthyr|Eikthyr|Эйктюр|Boss|Meadows\ngd_king|The Elder|Древний|Boss|Black Forest\nBonemass|Bonemass|Масса костей|Boss|Swamp\nDragon|Moder|Модер|Boss|Mountains\nGoblinKing|Yagluth|Яглут|Boss|Plains\nSeekerQueen|The Queen|Королева|Boss|Mistlands\nFader|Fader|Прародитель|Boss|Ashlands\nFrozenKing|Kall Fimbulbringer|Калль Фимбульбрингер|Boss|Deep North\nFrozenKing_p2|Kall Fimbulbringer (phase 2)|Калль Фимбульбрингер (фаза 2)|Boss|Deep North\nFrozenKing_p3|Kall Fimbulbringer (phase 3)|Калль Фимбульбрингер (фаза 3)|Boss|Deep North\nKallFimbulbringer|Kall Fimbulbringer|Калль Фимбульбрингер|Boss|Deep North\nBrenna|Brenna|Бренна|Boss|Black Forest\nGeirrhafa|Geirrhafa|Гейрафа|Boss|Mountains\nZil|Zil|Зил|Boss|Mistlands\nThungr|Thungr|Тангр|Boss|Mistlands\nCharred_Melee_Dyrnwyn|Lord Reto|Владыка Рето|Boss|Ashlands\nBoar|Boar|Кабан|Regular|Meadows;Black Forest\nBoar_piggy|Boar piglet|Поросёнок кабана|Regular|Meadows;Black Forest\nDeer|Deer|Олень|Regular|Meadows;Black Forest\nDeer_White|White deer|Белый олень|Regular|Deep North\nNeck|Neck|Никс|Regular|Meadows\nGreyling|Greyling|Грейлинг|Regular|Meadows\nGreydwarf|Greydwarf|Грейдворф|Regular|Black Forest\nGreydwarf_Elite|Greydwarf Brute|Грейдворф-берсерк|Regular|Black Forest\nGreydwarf_Shaman|Greydwarf Shaman|Грейдворф-шаман|Regular|Black Forest\nGreydwarf_Spawn|Greydwarf nest spawn|Порождение грейдворфов|Regular|Black Forest\nGreydwarf_Frozen|Frozen greydwarf|Ледяной грейдворф|Regular|Deep North\nGreydwarf_Shaman_Frozen|Frozen greydwarf shaman|Ледяной грейдворф-шаман|Regular|Deep North\nTroll|Troll|Тролль|Regular|Black Forest\nTrollFrost|Gammeltroll|Гаммельтролль|Regular|Deep North\nTroll_sleeping|Sleeping troll|Спящий тролль|Regular|Deep North\nSkeleton|Skeleton|Скелет|Regular|Black Forest;Swamp;Mountains\nSkeleton_NoArcher|Skeleton|Скелет|Regular|Black Forest;Swamp;Mountains\nSkeleton_Poison|Poison skeleton|Ядовитый скелет|Regular|Swamp\nSkeleton_Meadows|Meadows skeleton|Скелет лугов|Regular|Deep North\nSkeleton_Meadows_noarcher|Meadows skeleton|Скелет лугов|Regular|Deep North\nSkeleton_Mountains|Mountain skeleton|Горный скелет|Regular|Deep North\nSkeleton_Mountains_noarcher|Mountain skeleton|Горный скелет|Regular|Deep North\nSkeleton_Swamps|Swamp skeleton|Болотный скелет|Regular|Deep North\nSkeleton_Swamps_noarcher|Swamp skeleton|Болотный скелет|Regular|Deep North\nSkeleton_aspect|Skeleton aspect|Аспект скелета|Regular|Deep North\nDraugr|Draugr|Драугр|Regular|Swamp\nDraugr_Elite|Draugr Elite|Драугр-элита|Regular|Swamp\nDraugr_Ranged|Draugr Archer|Драугр-лучник|Regular|Swamp\nDraugr_Ranged_down|Draugr Archer (down)|Драугр-лучник (лежит)|Regular|Swamp\nDraugr_Elite_sleeping|Sleeping draugr elite|Спящий драугр-элита|Regular|Deep North\nDraugr_Ranged_sleeping|Sleeping draugr archer|Спящий драугр-лучник|Regular|Deep North\nDraugr_sleeping|Sleeping draugr|Спящий драугр|Regular|Deep North\nBlob|Blob|Сгустень|Regular|Swamp\nBlobElite|Blob elite|Большой сгустень|Regular|Swamp\nBlobTar|Tar blob|Дегтевой сгустень|Regular|Plains\nBlobMork|Shapeless Pulp|Бесформенная слизь|Regular|Deep North\nBlobMorkMini|Tiny Pulp|Крошечная слизь|Regular|Deep North\nBlobAspect|Blob aspect|Аспект сгустня|Regular|Deep North\nLeech|Leech|Пиявка|Regular|Swamp\nWraith|Wraith|Призрак|Regular|Swamp\nGhost|Ghost|Призрак|Regular|Black Forest\nGhost_old|Old ghost|Старый призрак|Regular|Deep North\nGhost_sleeping|Sleeping ghost|Спящий призрак|Regular|Deep North\nGhost_Void|Void ghost|Пустотный призрак|Regular|Deep North\nAbomination|Abomination|Мерзость|Regular|Swamp\nSurtling|Surtling|Суртлинг|Regular|Swamp;Ashlands\nWolf|Wolf|Волк|Regular|Mountains\nWolf_cub|Wolf cub|Волчонок|Regular|Mountains\nWolf_spiritcaller|Spirit-caller wolf|Волк призывателя духов|Regular|Deep North\nFenring|Fenring|Фенринг|Regular|Mountains\nFenring_Cultist|Fenring Cultist|Фенринг-культист|Regular|Mountains\nHatchling|Drake|Дракончик|Regular|Mountains\nStoneGolem|Stone Golem|Каменный голем|Regular|Mountains\nUlv|Ulv|Ульв|Regular|Mountains\nBat|Bat|Летучая мышь|Regular|Mountains\nBear|Bear|Медведь|Regular|Deep North\nCultist|Cultist|Культист|Regular|Mountains\nSerpent|Serpent|Морской змей|Regular|Ocean\nDeathsquito|Deathsquito|Смертожал|Regular|Plains\nLox|Lox|Локс|Regular|Plains\nGoblin|Fuling|Фулинг|Regular|Plains\nGoblinArcher|Fuling Archer|Фулинг-лучник|Regular|Plains\nGoblinShaman|Fuling Shaman|Фулинг-шаман|Regular|Plains\nGoblinBrute|Fuling Berserker|Фулинг-берсерк|Regular|Plains\nAsksvin|Asksvin|Асксвин|Regular|Ashlands\nAsksvin_cub|Asksvin calf|Детёныш асксвина|Regular|Ashlands\nMorgen|Morgen|Морген|Regular|Ashlands\nMorgen_Naked|Morgen (unarmored)|Морген без брони|Regular|Ashlands\nFallenValkyrie|Charred Valkyrie|Обугленная валькирия|Regular|Ashlands\nCharred_Twitcher|Charred Twitcher|Обугленный прыгун|Regular|Ashlands\nCharred_Melee|Charred Warrior|Обугленный воин|Regular|Ashlands\nCharred_Archer|Charred Marksman|Обугленный стрелок|Regular|Ashlands\nCharred_Mage|Charred Mage|Обугленный маг|Regular|Ashlands\nVolture|Volture|Вольтур|Regular|Ashlands\nTick|Tick|Клещ|Regular|Mistlands\nSeeker|Seeker|Искатель|Regular|Mistlands\nSeekerBrood|Seeker Brood|Выводок искателя|Regular|Mistlands\nSeekerBrute|Seeker Soldier|Искатель-солдат|Regular|Mistlands\nGjall|Gjall|Гьялль|Regular|Mistlands\nHare|Hare|Заяц|Regular|Mistlands\nDverger|Dvergr|Дверг|Regular|Mistlands\nDvergerMage|Dvergr Mage|Дверг-маг|Regular|Mistlands\nDvergerMageFire|Dvergr Fire Mage|Дверг-маг огня|Regular|Mistlands\nDvergerMageIce|Dvergr Frost Mage|Дверг-маг льда|Regular|Mistlands\nDvergerMageSupport|Dvergr Support Mage|Дверг-маг поддержки|Regular|Mistlands\nDvergerRogue|Dvergr Rogue|Дверг-разведчик|Regular|Mistlands\nChicken|Chicken|Курица|Regular|Any Biome\nHen|Hen|Курица-несушка|Regular|Any Biome\nFish1|Fish|Рыба|Regular|Rivers;Ocean\nFish2|Fish|Рыба|Regular|Rivers;Ocean\nFish3|Fish|Рыба|Regular|Ocean\nFish4_cave|Cave fish|Пещерная рыба|Regular|Mountain Caves\nCrow|Crow|Ворон|Regular|Any Biome\nAshCrow|Ash crow|Пепельный ворон|Regular|Ashlands\nGull|Gull|Чайка|Regular|Ocean;Meadows;Black Forest;Plains\nLeviathan|Leviathan|Левиафан|Regular|Ocean\nHugin|Hugin|Хугин|Regular|Any Biome\nMunin|Munin|Мунин|Regular|Mistlands;Ashlands\nOdin|Odin|Один|Regular|Any Biome\nValkyrie|Valkyrie|Валькирия|Regular|Any Biome\nHaldor|Haldor|Хальдор|Regular|Black Forest\nHildir|Hildir|Хильдир|Regular|Meadows\nBogWitch|The Bog Witch|Болотная ведьма|Regular|Swamp\nHalstein|Halstein|Хальштейн|Regular|Meadows\nBlub|Blåbær|Блубэр|Regular|Meadows\nHallon|Hallon|Халлон|Regular|Meadows\nBoar_spiritcaller|Spirit-caller boar|Кабан призывателя духов|Regular|Deep North\nMoose|Moose|Лось|Regular|Deep North\nMoose_calf|Moose calf|Лосёнок|Regular|Deep North\nMoose_spiritcaller|Spirit-caller moose|Лось призывателя духов|Regular|Deep North\nSeal|Seal|Тюлень|Regular|Deep North\nSeal_Pup|Seal pup|Детёныш тюленя|Regular|Deep North\nFrysling|Frysling|Фризлинг|Regular|Deep North\nFrostWisp|Frost Wisp|Ледяной дух|Regular|Deep North\nBat_Swamp|Swamp bat|Болотная летучая мышь|Regular|Swamp\nBarka|Barka|Барка|Regular|Deep North\nElaking|Elaking|Элакинг|Regular|Deep North\nElakingLantern|Elaking Lantern|Элакинг с фонарём|Regular|Deep North\nElakingMole|Eyeless One|Безглазый|Regular|Deep North\nFallenWarrior|Fallen Warrior|Павший воин|Regular|Deep North\nJotunWarrior|Krigen|Криген|Regular|Deep North\nJotunWarriorDualWield|Krigen (dual wield)|Криген с двумя оружиями|Regular|Deep North\nJotunWitch|Hexen|Хексен|Regular|Deep North\nShadowPerson|Shadow|Тень|Regular|Deep North\nTendril|Tendril|Усик|Regular|Deep North\nTendril_back|Back tendril|Задний усик|Regular|Deep North\nTentaRoot_wild|Wild tentaroot|Дикая тентакорень|Regular|Deep North\nWrithan|Writhan|Ритхан|Regular|Deep North\nGoblinDeepNorth|Deep North goblin|Гоблин Дальнего севера|Regular|Deep North\nDvergerDeepNorth|Imprisoned Dvergr|Пленённый дверг|Regular|Deep North\nBjorn_sleeping|Sleeping Bjorn|Спящий Бьёрн|Regular|Deep North\nBjorn_spiritcaller|Spirit-caller Bjorn|Бьёрн призывателя духов|Regular|Deep North\nAspect_Eikthyr|Aspect of the Lightning Stag|Аспект Эйктюра|Boss|Any Biome\nAspect_Elder|Aspect of the Living Forest|Аспект Древнего|Boss|Any Biome\nAspect_Bonemass|Aspect of the Twisted Soul|Аспект Массы костей|Boss|Any Biome\nAspect_Moder|Aspect of the Dragon Mother|Аспект Модер|Boss|Any Biome\nAspect_Yagluth|Aspect of the Writhing Dead|Аспект Яглута|Boss|Any Biome\nAspect_SeekerQueen|Aspect of the Crawling Matriarch|Аспект Королевы|Boss|Any Biome\nAspect_Fader|Aspect of the Emerald Flame|Аспект Прародителя|Boss|Any Biome\nAspect_TentaRoot|Aspect of the Tentaroot|Аспект тентакорня|Boss|Any Biome\n".Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0 || text.StartsWith("#", StringComparison.Ordinal)) { continue; } string[] array2 = text.Split('|'); if (array2.Length >= 5) { CreatureCategory category = CreatureCategory.Regular; if (array2[3].Equals("Boss", StringComparison.OrdinalIgnoreCase)) { category = CreatureCategory.Boss; } dictionary[array2[0].Trim()] = new CreatureMetadata(array2[1].Trim(), array2[2].Trim(), category, array2[4].Trim()); } } return dictionary; } public static bool TryGet(string prefabName, out CreatureMetadata metadata) { return Metadata.TryGetValue(prefabName ?? string.Empty, out metadata); } } internal sealed class FavoriteIds { private const int MaxEntries = 1000; private const int MaxIdLength = 256; private readonly HashSet<string> _ids = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public int Count => _ids.Count; public bool Contains(string id) { if (!string.IsNullOrWhiteSpace(id)) { return _ids.Contains(id); } return false; } public bool Toggle(string id) { if (!IsValidId(id)) { return false; } if (_ids.Remove(id)) { return false; } if (_ids.Count >= 1000) { return false; } _ids.Add(id); return true; } public string ToEncodedString() { List<string> list = new List<string>(_ids); list.Sort(StringComparer.OrdinalIgnoreCase); List<string> list2 = new List<string>(list.Count); for (int i = 0; i < list.Count; i++) { list2.Add(Convert.ToBase64String(Encoding.UTF8.GetBytes(list[i]))); } return string.Join(",", list2.ToArray()); } public static FavoriteIds FromEncodedString(string data) { FavoriteIds favoriteIds = new FavoriteIds(); if (string.IsNullOrWhiteSpace(data)) { return favoriteIds; } string[] array = data.Split(','); for (int i = 0; i < array.Length; i++) { if (favoriteIds._ids.Count >= 1000) { break; } try { string text = Encoding.UTF8.GetString(Convert.FromBase64String(array[i])); if (IsValidId(text)) { favoriteIds._ids.Add(text); } } catch (FormatException) { } } return favoriteIds; } private static bool IsValidId(string id) { if (string.IsNullOrWhiteSpace(id) || id.Length > 256) { return false; } for (int i = 0; i < id.Length; i++) { if (char.IsControl(id[i])) { return false; } } return true; } } [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.supernova.valheimspawner", "Nexus Admin Tools", "1.8.7")] public sealed class ValheimSpawnerPlugin : BaseUnityPlugin { private sealed class OutgoingHitState { internal HitData Hit; internal DamageTypes Original; } private enum AdminAction { Poll, Local, Time, Spawn, Cleanup, Weather, Raids, Locations, Teleport, Return, Audit, AddLocation, RenameLocation, DeleteLocation, PlayerRoster, TeleportToPlayer, TeleportPlayerHere, KickPlayer, BanPlayer, UnbanPlayer, FreezePlayer, ClearDroppedItems, PlayerReturn, PlayerReturnDismiss } private sealed class PeerState { internal readonly AdminRequestGate Gate = new AdminRequestGate(); internal int Modes; internal float Damage; internal float Xp; internal float LastSeen; } private sealed class PendingAction { internal ZRpc Rpc; internal float Until; internal AdminAction Kind; internal Action<ZPackage> Callback; } private sealed class AdminOnlinePlayer { internal long Uid; internal string Name; internal bool Frozen; } private sealed class AdminBanEntry { internal string Identifier; internal long ExpiresAt; } private sealed class SavedTeleportPoint { internal string Id; internal string Name; internal Vector3 Position; } private sealed class NexusWorldLocation { internal string Key; internal string Name; internal string NameOverride; internal string Prefab; internal Vector3 Position; internal bool IsCustom; } private sealed class InventoryAddResult { public int Added; public string Reason = string.Empty; } private enum ItemCategory { Weapons, Armor, Food, Materials, Other } private sealed class ItemEntry { public readonly GameObject Prefab; public readonly string PrefabName; public readonly string DisplayName; public readonly string EnglishName; public readonly string LocalizedName; public readonly string ItemTypeLabel; public readonly ItemCategory Category; public readonly int MaxQuality; public readonly int MaxStack; public readonly bool UsesDurability; public readonly Texture2D IconTexture; public readonly Rect IconUv; public ItemEntry(GameObject prefab, string prefabName, string englishName, string localizedName, string itemTypeLabel, ItemCategory category, int maxQuality, int maxStack, bool usesDurability, Sprite icon) { //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) Prefab = prefab; PrefabName = prefabName ?? string.Empty; EnglishName = (string.IsNullOrWhiteSpace(englishName) ? HumanizePrefabName(PrefabName) : englishName); LocalizedName = (string.IsNullOrWhiteSpace(localizedName) ? EnglishName : localizedName); DisplayName = EnglishName; ItemTypeLabel = itemTypeLabel ?? string.Empty; Category = category; MaxQuality = Mathf.Clamp(maxQuality, 1, 10); MaxStack = Mathf.Clamp(maxStack, 1, 9999); UsesDurability = usesDurability; if (!((Object)(object)icon != (Object)null)) { return; } try { Texture2D texture = icon.texture; if ((Object)(object)texture != (Object)null && ((Texture)texture).width > 0 && ((Texture)texture).height > 0) { Rect textureRect = icon.textureRect; IconTexture = texture; IconUv = new Rect(((Rect)(ref textureRect)).x / (float)((Texture)texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)texture).height); } } catch (Exception) { } } } private sealed class CreatureEntry { public readonly GameObject Prefab; public readonly string PrefabName; public readonly string EnglishName; public readonly string RussianName; public readonly string LocalizedName; public readonly CreatureCategory Category; public readonly string[] Biomes; public string CategoryLabel { get { if (Category != CreatureCategory.Boss) { return "Regular"; } return "Boss"; } } public string BiomeLabel { get { if (Biomes != null && Biomes.Length != 0) { return string.Join(", ", Biomes); } return "Unknown"; } } public CreatureEntry(GameObject prefab, string prefabName, string englishName, string russianName, string localizedName, CreatureCategory category, string[] biomes) { Prefab = prefab; PrefabName = prefabName ?? string.Empty; EnglishName = (string.IsNullOrWhiteSpace(englishName) ? PrefabName : englishName); RussianName = (string.IsNullOrWhiteSpace(russianName) ? PrefabName : russianName); LocalizedName = localizedName ?? string.Empty; Category = category; Biomes = biomes ?? new string[1] { "Unknown" }; } } private bool _adminGhost; private bool _infiniteGuardianPower; private Player _preferencesPlayer; private Player _effectsPlayer; private string _preferencesScope; private ConfigEntry<int> _savedModes; private ConfigEntry<float> _savedDamage; private ConfigEntry<float> _savedXp; private ConfigEntry<float> _savedCarryWeight; private ConfigEntry<bool> _savedCarryWeightEnabled; private ConfigEntry<string> _savedSkill; private ConfigEntry<int> _savedSkillLevel; private ConfigEntry<int> _savedHour; private ConfigEntry<int> _savedMinute; private ConfigEntry<int> _savedTimedBanHours; private ConfigEntry<bool> _savedCleanupTamed; private ConfigEntry<bool> _savedCleanupBosses; private ConfigEntry<int> _savedAdminSection; private int _timeHour = 12; private int _timeMinute; private int _skillTarget = 100; private SkillType _skillChoice = (SkillType)1; private float _nextEffectsApply; private float _carryWeightTarget = 300f; private float _priorCarryWeight = 300f; private string _carryWeightInput = "300"; private bool _carryWeightEnabled; private bool _effectsActive; private bool _priorFly; private bool _priorCreative; private bool _priorGhost; private int _lastLocalAuditedModes = -1; private float _lastLocalAuditedDamage = float.NaN; private float _lastLocalAuditedXp = float.NaN; private const string CustomItemNameKey = "com.supernova.valheimnexus.itemname.v1"; private const string CustomItemDescriptionKey = "com.supernova.valheimnexus.itemdescription.v1"; private ConfigEntry<string> _advancedDataConfig; private NexusAdvancedData _advanced = new NexusAdvancedData(); private string _itemCustomName = string.Empty; private string _itemCustomDescription = string.Empty; private string _advancedDataPath; private bool _advancedSaveBlocked; private bool _advancedSaveError; private string _newKitName = string.Empty; private int _activeKitIndex; private int _doctorItemIndex; private string _doctorSearch = string.Empty; private string _doctorReport = string.Empty; private string _radarReport = string.Empty; private Vector2 _advancedScroll; private bool _showSpawnPreview; private const string CleanupOwnerRpc = "SN_Nexus_CleanupOwner185"; private const int MaxSpawnMarkerCount = 12; private readonly List<GameObject> _spawnMarkerGroups = new List<GameObject>(); private GameObject _spawnMarkerRoot; private Material _spawnMarkerMaterial; private float _nextMarkerRefresh; private Vector2 _adminScroll; private GUIStyle _hudSectionHeader; private NexusKit _workshopKit; private int _editingKitPart = -1; private bool _showKitRename; private string _kitRenameInput = string.Empty; private string _kitPartCountInput = "1"; private string _kitPartQualityInput = "1"; private string _kitPartNameInput = string.Empty; private string _kitPartDescriptionInput = string.Empty; private string _historyFilter = string.Empty; private int _historyKindFilter; private static readonly Vector2 HudTopLeft = new Vector2(0f, 1f); private static readonly Color HudGold = new Color(1f, 0.78f, 0.36f); private static AudioClip _nativeClickClip; private GameObject _nativeHud; private GameObject _nativeBody; private Canvas _nativeCanvas; private Text _nativeStatus; private Text _nativeClock; private RectTransform _nativeRect; private ScrollRect _nativeList; private readonly List<Action> _hudDynamic = new List<Action>(); private bool _hudDirty = true; private bool _listDirty = true; private bool _legacyToolsOpen; private bool _nativeInputBlocked; private int _nativeMode; private int _adminSection; private int _catalogPage; private int _catalogTotal; private int _catalogSelectionKind; private int _toolsSection = 1; private string _globalSearch = string.Empty; private bool _globalFavoritesOnly; private float _nextHudRefresh; private string _nativeListKey; private float _catalogScrollPosition = 1f; private int _skillPage; private int _locationPage; private int _locationTotal; private int _locationSelection; private int _loadedLocationPage = -1; private bool _playerReturnAvailable; private string _newLocationName = string.Empty; private string _locationRenameInput = string.Empty; private int _auditPage; private int _auditTotal; private int _auditSelection; private int _loadedAuditPage = -1; private List<NexusWorldLocation> _adminLocations = new List<NexusWorldLocation>(); private List<string> _adminAuditPage = new List<string>(); private bool _returnAvailable; private const float HudWidth = 1100f; private const float HudHeight = 860f; private const float BodyWidth = 1052f; private int _historyPage; private int _reportPage; private const string RequestRpc = "SN_Nexus_Request185"; private const string ReplyRpc = "SN_Nexus_Reply185"; private const string TargetCommandRpc = "SN_Nexus_Target185"; private readonly Dictionary<ZRpc, PeerState> _peerStates = new Dictionary<ZRpc, PeerState>(); private readonly Dictionary<int, PendingAction> _pendingActions = new Dictionary<int, PendingAction>(); private readonly HashSet<ZDOID> _networkGhosts = new HashSet<ZDOID>(); private readonly HashSet<ZDOID> _networkFrozenCharacters = new HashSet<ZDOID>(); private ZNet _networkSession; private ZRpc _serverConnection; private float _nextNetworkPoll; private float _accessUntil; private float _ghostSnapshotUntil; private int _nextRequestId; private bool _executingApprovedLocal; private const int AdminPlayerPageSize = 6; private const int MaxTimedBanHours = 8760; private const byte TargetCommandTeleport = 1; private const byte TargetCommandFreeze = 2; private const byte TargetCommandUnfreeze = 3; private readonly HashSet<long> _serverFrozenPlayers = new HashSet<long>(); private readonly Dictionary<string, long> _timedBans = new Dictionary<string, long>(StringComparer.Ordinal); private ConfigEntry<string> _savedTimedBanEntries; private string _moderationScope; private float _nextTimedBanCheck; private int _onlinePlayerPage; private int _bannedPlayerPage; private int _onlinePlayerTotal; private int _bannedPlayerTotal; private int _timedBanHours = 24; private int _loadedOnlinePlayerPage = -1; private int _loadedBannedPlayerPage = -1; private long _selectedAdminPlayerUid; private string _selectedAdminPlayerName = string.Empty; private string _selectedBannedIdentifier = string.Empty; private bool _targetLocalFrozen; private bool _selectedAdminPlayerFrozen; private List<AdminOnlinePlayer> _adminOnlinePlayers = new List<AdminOnlinePlayer>(); private List<AdminBanEntry> _adminBannedPlayers = new List<AdminBanEntry>(); private GameObject _playerReturnPrompt; private const string TrashSaveHeader = "NEXUS-ADMIN-TRASH-1"; private const int MaxTrashSaveBytes = 524288; private ItemData _trashedItem; private Vector2i _trashOriginalPosition; private string _trashScope; private string _trashLoadedScope; private Button _trashDropTarget; private Button _trashRestoreButton; private Image _trashItemIcon; private Text _trashItemLabel; private const int LocationPageSize = 9; private const int AuditPageSize = 8; private const int AuditMaxEntries = 1000; private const long AuditMaxBytes = 1048576L; private const int MaxSavedTeleportPoints = 100; private const int MaxLocationNameLength = 48; private readonly List<string> _serverAuditEntries = new List<string>(); private readonly Dictionary<long, Vector3> _adminReturnPositions = new Dictionary<long, Vector3>(); private ConfigEntry<bool> _savedRaidSuppression; private ConfigEntry<string> _savedForcedWeather; private ConfigEntry<string> _savedTeleportLocations; private string _serverStateScope; private string _auditFilePath; private string _lastAppliedWeather; private string _serverForcedWeather = string.Empty; private bool _serverRaidsDisabled; private bool _auditLoaded; private readonly List<SavedTeleportPoint> _customTeleportPoints = new List<SavedTeleportPoint>(); private readonly Dictionary<string, string> _locationNameOverrides = new Dictionary<string, string>(StringComparer.Ordinal); public const string PluginGuid = "com.supernova.valheimspawner"; public const string PluginName = "Nexus Admin Tools"; public const string PluginVersion = "1.8.7"; private const int ExtendedNonWeaponQualityCap = 10; private const int MaxUnstackableItemsPerAction = 100; private const int MaxStackBatchesPerAction = 200; private ConfigEntry<KeyCode> _toggleKey; private ConfigEntry<KeyCode> _spawnFrontKey; private ConfigEntry<KeyCode> _spawnAimKey; private ConfigEntry<int> _defaultCount; private ConfigEntry<int> _defaultLevel; private ConfigEntry<string> _languageSetting; private ConfigEntry<float> _killRadius; private ConfigEntry<bool> _spawnMarkerConfig; private ConfigEntry<bool> _spawnMarkerCrosshairConfig; private ConfigEntry<string> _favoriteItemsConfig; private ConfigEntry<string> _favoriteCreaturesConfig; private ConfigEntry<float> _hudPositionXConfig; private ConfigEntry<float> _hudPositionYConfig; private FavoriteIds _favoriteItems = new FavoriteIds(); private FavoriteIds _favoriteCreatures = new FavoriteIds(); private bool _itemFavoritesOnly; private bool _creatureFavoritesOnly; private bool _searchAllItems; private Harmony _harmony; private static ValheimSpawnerPlugin _activeInstance; private float _suppressInputUntil; private readonly List<CreatureEntry> _entries = new List<CreatureEntry>(); private readonly List<CreatureEntry> _filtered = new List<CreatureEntry>(); private readonly List<ItemEntry> _itemEntries = new List<ItemEntry>(); private readonly List<ItemEntry> _filteredItems = new List<ItemEntry>(); private readonly string[] _biomeOptions = new string[14] { "All Biomes", "Meadows", "Black Forest", "Swamp", "Mountains", "Plains", "Ocean", "Rivers", "Mistlands", "Ashlands", "Deep North", "Mountain Caves", "Any Biome", "Unknown" }; private static readonly Dictionary<string, string> RussianItemNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "AxeStone", "Каменный топор" }, { "AxeFlint", "Кремнёвый топор" }, { "AxeBronze", "Бронзовый топор" }, { "AxeIron", "Железный топор" }, { "AxeBlackMetal", "Чернометаллический топор" }, { "AxeJotunBane", "Топор йотунов" }, { "AxeBer", "Бер" }, { "AtgeirBronze", "Бронзовая алебарда" }, { "AtgeirIron", "Железная алебарда" }, { "AtgeirBlackmetal", "Чернометаллическая алебарда" }, { "AtgeirHimminAfl", "Химмин-Афль" }, { "Battleaxe", "Боевой топор" }, { "BattleaxeCrystal", "Кристальный боевой топор" }, { "BattleaxeBloodthirst", "Жажда крови" }, { "Bow", "Лук" }, { "BowFineWood", "Лук из ценной древесины" }, { "BowHuntsman", "Лук охотника" }, { "BowDraugrFang", "Клык драугра" }, { "BowSpineSnap", "Хребтолом" }, { "CrossbowArbalest", "Арбалет" }, { "KnifeFlint", "Кремнёвый нож" }, { "KnifeCopper", "Медный нож" }, { "KnifeSilver", "Серебряный нож" }, { "KnifeBlackMetal", "Чернометаллический нож" }, { "KnifeSkollAndHati", "Сколль и Хати" }, { "KnifeButcher", "Нож мясника" }, { "MaceBronze", "Бронзовая булава" }, { "MaceIron", "Железная булава" }, { "MaceSilver", "Серебряная булава" }, { "MaceNeedle", "Игла" }, { "MacePorcupine", "Дикобраз" }, { "MaceDemolisher", "Разрушитель" }, { "SledgeStagbreaker", "Оленебой" }, { "SledgeIron", "Железная кувалда" }, { "SpearFlint", "Кремнёвое копьё" }, { "SpearBronze", "Бронзовое копьё" }, { "SpearElderbark", "Древеснокорое копьё" }, { "SpearWolfFang", "Копьё с волчьим клыком" }, { "SpearCarapace", "Карапасное копьё" }, { "SpearChitin", "Гарпун бездны" }, { "SwordBronze", "Бронзовый меч" }, { "SwordIron", "Железный меч" }, { "SwordSilver", "Серебряный меч" }, { "SwordBlackmetal", "Чернометаллический меч" }, { "SwordMistwalker", "Туманоход" }, { "SwordNiedhogg", "Нидхёгг" }, { "SwordKrom", "Кром" }, { "SwordDyrnwyn", "Дюрнвин" }, { "StaffFireball", "Посох огненного шара" }, { "StaffIceShards", "Посох ледяных осколков" }, { "StaffShield", "Посох защиты" }, { "StaffSkeleton", "Посох скелетов" }, { "StaffGreenRoots", "Посох зелёных корней" }, { "StaffLightning", "Посох молнии" }, { "Torch", "Факел" }, { "ArrowWood", "Деревянная стрела" }, { "ArrowFire", "Огненная стрела" }, { "ArrowFlint", "Кремнёвая стрела" }, { "ArrowBronze", "Бронзовая стрела" }, { "ArrowIron", "Железная стрела" }, { "ArrowSilver", "Серебряная стрела" }, { "ArrowObsidian", "Обсидиановая стрела" }, { "ArrowNeedle", "Игла" }, { "ArrowCarapace", "Карапасная стрела" }, { "ArrowCharred", "Обугленная стрела" }, { "ArrowFrost", "Морозная стрела" }, { "ArrowPoison", "Отравленная стрела" }, { "BoltWood", "Деревянный болт" }, { "BoltBone", "Костяной болт" }, { "BoltIron", "Железный болт" }, { "BoltBlackmetal", "Чернометаллический болт" }, { "BoltCarapace", "Карапасный болт" }, { "BoltCharred", "Обугленный болт" }, { "ShieldWood", "Деревянный щит" }, { "ShieldWoodTower", "Деревянный башенный щит" }, { "ShieldBoneTower", "Костяной башенный щит" }, { "ShieldBronzeBuckler", "Бронзовый круглый щит" }, { "ShieldIronSquare", "Железный щит" }, { "ShieldBanded", "Полосатый щит" }, { "ShieldSerpentscale", "Щит из чешуи змея" }, { "ShieldBlackmetal", "Чернометаллический щит" }, { "ShieldBlackmetalTower", "Чернометаллический башенный щит" }, { "ShieldCarapace", "Карапасный щит" }, { "ShieldCarapaceBuckler", "Карапасный круглый щит" }, { "ShieldFlametal", "Пламеннометаллический щит" } }; private Rect _windowRect = new Rect(30f, 35f, 980f, 820f); private Vector2 _listScroll; private Vector2 _itemListScroll; private int _mainTab; private int _itemCount = 1; private string _itemCountInput = "1"; private int _itemQuality = 1; private bool _spawnTamed; private bool _infiniteHealth; private bool _infiniteStamina; private bool _infiniteEitr; private bool _adminFly; private bool _adminCreative; private bool _adminHotkeys; private bool _infiniteAdrenaline; private bool _superDamage; private float _superDamageMultiplier = 5f; private float _nextAdrenalineRefresh; private bool _showCreatureSafety; private bool _showItemTechnicalDetails; private bool _showItemCustomFields = true; private bool _outgoingDamageHookAvailable; private bool _adrenalineHookAvailable; private float _nextAdminRefresh; private float _skillXpMultiplier = 1f; private string _skillXpInput = "1"; private string _adminToast = string.Empty; private float _adminToastUntil; private string _itemSearch = string.Empty; private readonly Dictionary<ItemCategory, string> _itemTypeFilters = new Dictionary<ItemCategory, string>(); private bool _isRussian; private bool _itemCatalogReady; private object _objectDbInstance; private object _lastObjectDb; private bool _isOpen; private bool _inventoryVisibleLastFrame; private bool _searchHasFocus; private bool _nameHasFocus; private bool _killIncludeTamed; private bool _killIncludeBosses; private bool _cursorStateStored; private bool _oldCursorVisible; private CursorLockMode _oldCursorLockState; private bool _catalogReady; private float _nextRefresh; private string _search = string.Empty; private string _customName = string.Empty; private string _status = "Enter a world to load the catalogue."; private int _categoryFilter; private int _biomeFilter; private int _count = 1; private int _level = 1; private CreatureEntry _selected; private ItemEntry _selectedItem; private Type _objectDbType; private Type _itemDropType; private Type _characterType; private Type _zNetSceneType; private Type _zNetType; private Type _playerType; private Type _zNetViewType; private Component _playerComponent; private object _zNetSceneInstance; private object _lastCatalogScene; private object _zNetInstance; private bool AdminContextMatches { get { if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)ZNet.instance != (Object)null && Player.m_localPlayer == _preferencesPlayer) { return _preferencesScope == AdminScope(); } return false; } } private int DesiredModeBits => (int)((_infiniteHealth ? 1u : 0u) | (uint)(_infiniteStamina ? 2 : 0) | (uint)(_infiniteEitr ? 4 : 0) | (uint)(_infiniteAdrenaline ? 8 : 0) | (uint)(_superDamage ? 16 : 0) | (uint)(_adminFly ? 32 : 0) | (uint)(_adminCreative ? 64 : 0) | (uint)(_adminGhost ? 128 : 0) | (uint)(_adminHotkeys ? 256 : 0)) | (_infiniteGuardianPower ? 512 : 0); private string CatalogueViewKey => _mainTab + ":" + _catalogPage + ":" + _search + ":" + _itemSearch + ":" + _globalSearch + ":" + _categoryFilter + ":" + _biomeFilter + ":" + _searchAllItems + ":" + _globalFavoritesOnly + ":" + _creatureFavoritesOnly + ":" + _itemFavoritesOnly; private bool HasAdminAccess { get { if ((Object)(object)ZNet.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null) { if (!ZNet.instance.IsServer()) { if (_serverConnection != null && Time.unscaledTime < _accessUntil) { return ZNet.instance.GetServerPeer()?.m_rpc == _serverConnection; } return false; } return true; } return false; } } private bool TrashPageActive { get { if (_isOpen && !_legacyToolsOpen && _nativeMode == 1) { return _adminSection == 3; } return false; } } private string AdminScope() { return "Admin." + ZNet.instance.GetWorldUID().ToString("X16") + "." + Player.m_localPlayer.GetPlayerID().ToString("X16"); } private void ResetAdminContext() { _preferencesPlayer = null; _preferencesScope = null; _savedModes = null; _infiniteHealth = (_infiniteStamina = (_infiniteEitr = (_infiniteAdrenaline = (_superDamage = false)))); _adminFly = (_adminCreative = (_adminGhost = (_adminHotkeys = false))); _infiniteGuardianPower = false; _superDamageMultiplier = 5f; _skillXpMultiplier = 1f; _lastLocalAuditedModes = -1; } private void TickAdminPreferences() { //IL_0403: Unknown result type (might be due to invalid IL or missing references) //IL_040c: Unknown result type (might be due to invalid IL or missing references) //IL_040d: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!HasAdminAccess || (Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead()) { SuspendAdminEffects(); return; } string text = AdminScope(); if (text != _preferencesScope || localPlayer != _preferencesPlayer) { SuspendAdminEffects(); _preferencesPlayer = localPlayer; _preferencesScope = text; _savedModes = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Modes", 0, "Remembered admin choices for this world and character. Permissions are rechecked on every connection."); _savedDamage = ((BaseUnityPlugin)this).Config.Bind<float>(text, "DamageMultiplier", 5f, (ConfigDescription)null); _savedXp = ((BaseUnityPlugin)this).Config.Bind<float>(text, "SkillXpMultiplier", 1f, (ConfigDescription)null); _savedCarryWeight = ((BaseUnityPlugin)this).Config.Bind<float>(text, "CarryWeight", 300f, "Personal base carry weight configured by Nexus."); _savedCarryWeightEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>(text, "CarryWeightEnabled", false, "Apply the personal carry weight override while administrator access is available."); _savedSkill = ((BaseUnityPlugin)this).Config.Bind<string>(text, "SelectedSkill", "Swords", (ConfigDescription)null); _savedSkillLevel = ((BaseUnityPlugin)this).Config.Bind<int>(text, "SkillTarget", 100, (ConfigDescription)null); _savedHour = ((BaseUnityPlugin)this).Config.Bind<int>(text, "TimeHour", 12, (ConfigDescription)null); _savedMinute = ((BaseUnityPlugin)this).Config.Bind<int>(text, "TimeMinute", 0, (ConfigDescription)null); _savedCleanupTamed = ((BaseUnityPlugin)this).Config.Bind<bool>(text, "CleanupTamed", false, (ConfigDescription)null); _savedCleanupBosses = ((BaseUnityPlugin)this).Config.Bind<bool>(text, "CleanupBosses", false, (ConfigDescription)null); _savedTimedBanHours = ((BaseUnityPlugin)this).Config.Bind<int>(text, "TimedBanHours", 24, (ConfigDescription)null); _savedAdminSection = ((BaseUnityPlugin)this).Config.Bind<int>(text, "Section", 0, (ConfigDescription)null); _killIncludeTamed = _savedCleanupTamed.Value; _killIncludeBosses = _savedCleanupBosses.Value; _adminSection = Mathf.Clamp(_savedAdminSection.Value, 0, 6); int num = _savedModes.Value & 0x3FF; _infiniteHealth = (num & 1) != 0; _infiniteStamina = (num & 2) != 0; _infiniteEitr = (num & 4) != 0; _infiniteAdrenaline = (num & 8) != 0; _superDamage = (num & 0x10) != 0; _adminFly = (num & 0x20) != 0; _adminCreative = (num & 0x40) != 0; _adminGhost = (num & 0x80) != 0; _adminHotkeys = (num & 0x100) != 0; _infiniteGuardianPower = (num & 0x200) != 0; _superDamageMultiplier = (AdminRules.Finite(_savedDamage.Value) ? Mathf.Clamp(_savedDamage.Value, 1f, 25f) : 5f); _skillXpMultiplier = (AdminRules.Finite(_savedXp.Value) ? Mathf.Clamp(_savedXp.Value, 1f, 1000f) : 1f); _carryWeightTarget = (AdminRules.Finite(_savedCarryWeight.Value) ? Mathf.Clamp(_savedCarryWeight.Value, 1f, 10000f) : 300f); _carryWeightInput = _carryWeightTarget.ToString("0.##", CultureInfo.InvariantCulture); _carryWeightEnabled = _savedCarryWeightEnabled.Value; _skillXpInput = _skillXpMultiplier.ToString(CultureInfo.InvariantCulture); _skillTarget = Mathf.Clamp(_savedSkillLevel.Value, 0, 100); _timeHour = Mathf.Clamp(_savedHour.Value, 0, 23); _timeMinute = Mathf.Clamp(_savedMinute.Value, 0, 59); _timedBanHours = Mathf.Clamp(_savedTimedBanHours.Value, 1, 8760); if (Enum.TryParse<SkillType>(_savedSkill.Value, out SkillType result) && SkillChoices().Contains(result)) { _skillChoice = result; } _lastLocalAuditedModes = DesiredModeBits; _lastLocalAuditedDamage = _superDamageMultiplier; _lastLocalAuditedXp = _skillXpMultiplier; _hudDirty = true; } if (!_effectsActive || _effectsPlayer != localPlayer) { _effectsPlayer = localPlayer; object obj = ReadMember(localPlayer, "m_debugFly"); bool flag = default(bool); int num2; if (obj is bool) { flag = (bool)obj; num2 = 1; } else { num2 = 0; } _priorFly = (byte)((uint)num2 & (flag ? 1u : 0u)) != 0; obj = ReadMember(localPlayer, "m_noPlacementCost"); bool flag2 = default(bool); int num3; if (obj is bool) { flag2 = (bool)obj; num3 = 1; } else { num3 = 0; } _priorCreative = (byte)((uint)num3 & (flag2 ? 1u : 0u)) != 0; obj = ReadMember(localPlayer, "m_ghostMode"); bool flag3 = default(bool); int num4; if (obj is bool) { flag3 = (bool)obj; num4 = 1; } else { num4 = 0; } _priorGhost = (byte)((uint)num4 & (flag3 ? 1u : 0u)) != 0; _priorCarryWeight = ((ReadMember(localPlayer, "m_maxCarryWeight") is float num5 && AdminRules.Finite(num5)) ? num5 : 300f); _effectsActive = true; _nextEffectsApply = 0f; } if (Time.unscaledTime >= _nextEffectsApply) { _nextEffectsApply = Time.unscaledTime + 0.25f; ApplyFlightState(localPlayer, _adminFly || _priorFly); TrySetBoolMember(localPlayer, "m_noPlacementCost", _adminCreative || _priorCreative); localPlayer.SetGhostMode(_adminGhost || _priorGhost); TrySetFloatMember(localPlayer, "m_maxCarryWeight", _carryWeightEnabled ? _carryWeightTarget : _priorCarryWeight); if (_infiniteGuardianPower) { TrySetFloatMember(localPlayer, "m_guardianPowerCooldown", 0f); } } } private void SuspendAdminEffects() { if (_effectsActive && (Object)(object)_effectsPlayer != (Object)null) { ApplyFlightState(_effectsPlayer, _priorFly); TrySetBoolMember(_effectsPlayer, "m_noPlacementCost", _priorCreative); _effectsPlayer.SetGhostMode(_priorGhost); TrySetFloatMember(_effectsPlayer, "m_maxCarryWeight", _priorCarryWeight); } _effectsActive = false; _effectsPlayer = null; } private void SaveAdminPreferences() { if (HasAdminAccess && _savedModes != null && Player.m_localPlayer == _preferencesPlayer) { _savedModes.Value = DesiredModeBits; _savedDamage.Value = _superDamageMultiplier; _savedXp.Value = _skillXpMultiplier; _savedCarryWeight.Value = _carryWeightTarget; _savedCarryWeightEnabled.Value = _carryWeightEnabled; _savedSkill.Value = ((object)Unsafe.As<SkillType, SkillType>(ref _skillChoice)/*cast due to .constrained prefix*/).ToString(); _savedSkillLevel.Value = _skillTarget; _savedHour.Value = _timeHour; _savedMinute.Value = _timeMinute; _savedCleanupTamed.Value = _killIncludeTamed; _savedCleanupBosses.Value = _killIncludeBosses; _savedTimedBanHours.Value = _timedBanHours; _savedAdminSection.Value = _adminSection; if (ZNet.instance.IsServer()) { AuditLocalModeChanges(); } _nextEffectsApply = 0f; TrySavePreferences(); } } private void AuditLocalModeChanges() { int desiredModeBits = DesiredModeBits; if (_lastLocalAuditedModes >= 0) { for (int i = 0; i < 10; i++) { int num = 1 << i; if (((desiredModeBits ^ _lastLocalAuditedModes) & num) != 0) { AppendAdminAudit(Player.m_localPlayer.GetPlayerName(), Player.m_localPlayer.GetPlayerID(), AdminModeName(i) + (((desiredModeBits & num) != 0) ? " enabled" : " disabled"), "OK"); } } if (Math.Abs(_superDamageMultiplier - _lastLocalAuditedDamage) > 0.001f) { AppendAdminAudit(Player.m_localPlayer.GetPlayerName(), Player.m_localPlayer.GetPlayerID(), "damage multiplier ×" + _superDamageMultiplier.ToString("0.##", CultureInfo.InvariantCulture), "OK"); } if (Math.Abs(_skillXpMultiplier - _lastLocalAuditedXp) > 0.001f) { AppendAdminAudit(Player.m_localPlayer.GetPlayerName(), Player.m_localPlayer.GetPlayerID(), "skill XP multiplier ×" + _skillXpMultiplier.ToString("0.##", CultureInfo.InvariantCulture), "OK"); } } _lastLocalAuditedModes = desiredModeBits; _lastLocalAuditedDamage = _superDamageMultiplier; _lastLocalAuditedXp = _skillXpMultiplier; } private static void ApplyFlightState(Player player, bool enabled) { ZNetView val = (((Object)(object)player == (Object)null) ? null : ((Component)player).GetComponent<ZNetView>()); if (!((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner() && player.InDebugFlyMode() != enabled) { player.ToggleDebugFly(); } } private string WorldClock() { if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)EnvMan.instance == (Object)null || EnvMan.instance.m_dayLengthSec < 1) { return "—"; } double timeSeconds = ZNet.instance.GetTimeSeconds(); double num = EnvMan.instance.m_dayLengthSec; double raw = timeSeconds % num / num; int num2 = Math.Min(1439, (int)(AdminRules.SolarFraction(raw) * 1440.0)); return T("Day ", "День ") + EnvMan.instance.GetDay() + " · " + (num2 / 60).ToString("00") + ":" + (num2 % 60).ToString("00"); } private SkillType[] SkillChoices() { Player localPlayer = Player.m_localPlayer; return ((localPlayer == null) ? null : (from s in ((Character)localPlayer).GetSkills()?.m_skills?.Select((SkillDef s) => s.m_skill) where (int)s != 0 && (int)s != 999 select s).Distinct().ToArray()) ?? Array.Empty<SkillType>(); } private unsafe string SkillName(SkillType skill) { string text = "$skill_" + ((object)(*(SkillType*)(&skill))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); Localization instance = Localization.instance; string text2 = ((instance != null) ? instance.Localize(text) : null); if (!string.IsNullOrEmpty(text2) && !(text2 == text)) { return text2; } return ((object)(*(SkillType*)(&skill))/*cast due to .constrained prefix*/).ToString(); } private unsafe void RaiseSkills(bool all, SkillType choice, int target) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Expected I4, but got Unknown //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) if (!AdminRules.Skill(target) || !AuthorizeLocal(delegate { //IL_000d: Unknown result type (might be due to invalid IL or missing references) RaiseSkills(all, choice, target); }, (all ? "set all skills to " : ("set " + ((object)(*(SkillType*)(&choice))/*cast due to .constrained prefix*/).ToString() + " to ")) + target.ToString(CultureInfo.InvariantCulture))) { return; } Player localPlayer = Player.m_localPlayer; Skills val = ((localPlayer != null) ? ((Character)localPlayer).GetSkills() : null); if ((Object)(object)val == (Object)null) { return; } SkillType[] array = SkillChoices(); if (!all && !array.Contains(choice)) { _status = T("Skill unavailable.", "Навык недоступен."); return; } MethodInfo methodInfo = AccessTools.Method(typeof(Skills), "GetSkill", new Type[1] { typeof(SkillType) }, (Type[])null); int num = 0; SkillType[] array2 = (SkillType[])(all ? ((Array)array) : ((Array)new SkillType[1] { (SkillType)(int)choice })); foreach (SkillType val2 in array2) { if (!(Mathf.Abs(val.GetSkillLevel(val2) - (float)target) < 0.01f)) { object instance = methodInfo.Invoke(val, new object[1] { val2 }); if (TrySetFloatMember(instance, "m_level", target)) { TrySetFloatMember(instance, "m_accumulator", 0f); num++; } } } ShowAdminToast(T("Skill levels set: ", "Установлено навыков: ") + num + " → " + target); } private void HealSelf() { if (AuthorizeLocal(HealSelf, "restore player health")) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { ((Character)localPlayer).Heal(((Character)localPlayer).GetMaxHealth(), true); _status = T("Health restored.", "Здоровье восстановлено."); } } } private void SetMaxCarryWeight(float weight) { if (!AdminRules.Finite(weight) || weight < 1f || weight > 10000f) { _status = T("Carry weight must be between 1 and 10,000.", "Вес должен быть от 1 до 10 000."); } else if (AuthorizeLocal(delegate { SetMaxCarryWeight(weight); }, "set personal carry weight to " + weight.ToString("0.##", CultureInfo.InvariantCulture))) { _carryWeightTarget = weight; _carryWeightEnabled = true; _carryWeightInput = weight.ToString("0.##", CultureInfo.InvariantCulture); SaveAdminPreferences(); TickAdminPreferences(); Player localPlayer = Player.m_localPlayer; float num = (((Object)(object)localPlayer == (Object)null) ? weight : localPlayer.GetMaxCarryWeight()); ShowAdminToast(T("Base carry weight set to ", "Базовый переносимый вес установлен: ") + weight.ToString("0.##", CultureInfo.InvariantCulture) + T(" kg. Effective limit: ", " кг. Текущий лимит: ") + num.ToString("0.##", CultureInfo.InvariantCulture) + " kg."); } } private void ResetMaxCarryWeight() { if (AuthorizeLocal(ResetMaxCarryWeight, "restore vanilla personal carry weight")) { _carryWeightEnabled = false; SaveAdminPreferences(); TickAdminPreferences(); ShowAdminToast(T("Vanilla carry weight restored.", "Обычный переносимый вес восстановлен.")); } } private void InitializeNexusAdvanced() { _advancedDataConfig = ((BaseUnityPlugin)this).Config.Bind<string>("NexusAdvanced", "Data", string.Empty, "Legacy v1.4.0 state; retained read-only for migration to SuperNova.ValheimNexus.Advanced.json."); _advancedDataPath = Path.Combine(Paths.ConfigPath, "SuperNova.ValheimNexus.Advanced.json"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Nexus Advanced storage path: " + _advancedDataPath)); _advancedSaveBlocked = false; _advancedSaveError = false; _advanced = new NexusAdvancedData(); if (File.Exists(_advancedDataPath)) { if (TryLoadAdvancedFile(_advancedDataPath, out var value, out var reason)) { _advanced = value; string text = File.ReadAllText(_advancedDataPath, Encoding.UTF8); if (text.IndexOf("\"Format\":1", StringComparison.Ordinal) >= 0 && (text.IndexOf("\"Kits\"", StringComparison.Ordinal) < 0 || text.IndexOf("\"History\"", StringComparison.Ordinal) < 0 || text.IndexOf("\"Snapshot\"", StringComparison.Ordinal) < 0)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Legacy Nexus JSON contains missing collections (Unity JsonUtility data loss). Existing omitted entries cannot be restored from this file; original file kept until next save."); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Nexus Advanced state loaded: " + _advanced.Kits.Count + " kits, " + _advanced.History.Count + " history entries, " + _advanced.Snapshot.Count + " snapshot records.")); return; } ((BaseUnityPlugin)this).Logger.LogWarning((object)("Nexus Advanced primary state is unreadable: " + reason)); string text2 = _advancedDataPath + ".bak"; if (TryLoadAdvancedFile(text2, out value, out reason)) { _advanced = value; try { File.Copy(_advancedDataPath, _advancedDataPath + ".corrupt", overwrite: true); File.Copy(text2, _advancedDataPath, overwrite: true); ((BaseUnityPlugin)this).Logger.LogWarning((object)"Restored Nexus Advanced state from .bak; damaged main file preserved as .corrupt."); return; } catch (Exception ex) { _advancedSaveBlocked = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Nexus state recovered in memory but cannot restore primary file; writes disabled: " + ex.Message)); return; } } if (!string.IsNullOrWhiteSpace(_advancedDataConfig.Value) && NexusAdvancedData.TryLoad(_advancedDataConfig.Value, out var data, out var _)) { _advanced = data; _advancedSaveBlocked = true; ((BaseUnityPlugin)this).Logger.LogWarning((object)"Recovered legacy Nexus data in memory, but existing file is corrupt. Writes disabled until backup is made manually."); } else { _advancedSaveBlocked = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Nexus Advanced state cannot be read and no usable backup exists. Writes disabled to protect existing data. " + reason)); } return; } if (TryLoadAdvancedFile(_advancedDataPath + ".bak", out var value2, out var _)) { _advanced = value2; try { File.Copy(_advancedDataPath + ".bak", _advancedDataPath); ((BaseUnityPlugin)this).Logger.LogWarning((object)"Nexus Advanced primary JSON was missing; restored from .bak."); return; } catch (Exception ex2) { _advancedSaveBlocked = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Backup recovered in memory, but could not be copied to main state. Writes disabled: " + ex2.Message)); return; } } string value3 = _advancedDataConfig.Value; if (!string.IsNullOrWhiteSpace(value3)) { if (!NexusAdvancedData.TryLoad(value3, out var data2, out var reason4)) { _advancedSaveBlocked = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Legacy Nexus Advanced state could not be decoded; refusing to overwrite it. " + reason4)); } else { _advanced = data2; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Migrating legacy Nexus Advanced configuration: " + _advanced.Kits.Count + " kits.")); SaveNexusAdvanced(); } } } private static bool TryLoadAdvancedFile(string path, out NexusAdvancedData value, out string reason) { value = null; reason = "File does not exist."; if (!File.Exists(path)) { return false; } try { return NexusAdvancedData.TryLoad(File.ReadAllText(path, Encoding.UTF8), out value, out reason); } catch (Exception ex) { reason = ex.GetType().Name + ": " + ex.Message; return false; } } private bool SaveNexusAdvanced() { if (_advancedSaveBlocked) { _status = T("Nexus data could not be read; saving is disabled to protect your old data.", "Данные Nexus не прочитаны; сохранение отключено для защиты старых данных."); return false; } if (_advancedDataPath == null || _advanced == null) { return false; } string text = _advancedDataPath + ".tmp"; try { string text2 = _advanced.Save(); if (!NexusAdvancedData.TryLoad(text2, out var data, out var reason) || !_advanced.SamePersistedContent(data)) { throw new InvalidDataException("State JSON serialization failed verification: " + reason); } Directory.CreateDirectory(Path.GetDirectoryName(_advancedDataPath)); File.WriteAllText(text, text2, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); if (!TryLoadAdvancedFile(text, out data, out reason) || !_advanced.SamePersistedContent(data)) { throw new InvalidDataException("Staged state file failed verification: " + reason); } bool flag = false; if (File.Exists(_advancedDataPath)) { string text3 = File.ReadAllText(_advancedDataPath, Encoding.UTF8); if (!NexusAdvancedData.TryLoad(text3, out var _, out var reason2)) { throw new InvalidDataException("Existing state changed or became invalid before save: " + reason2); } flag = text3.IndexOf("\"Format\":1", StringComparison.Ordinal) >= 0; if (flag) { string text4 = _advancedDataPath + ".legacy-format1.json"; if (!File.Exists(text4)) { File.Copy(_advancedDataPath, text4); } } } if (!File.Exists(_advancedDataPath)) { File.Move(text, _advancedDataPath); File.Copy(_advancedDataPath, _advancedDataPath + ".bak", overwrite: false); } else { try { File.Replace(text, _advancedDataPath, _advancedDataPath + ".bak"); } catch (PlatformNotSupportedException) { File.Copy(_advancedDataPath, _advancedDataPath + ".bak", overwrite: true); File.Delete(_advancedDataPath); File.Move(text, _advancedDataPath); } } if (!TryLoadAdvancedFile(_advancedDataPath, out data, out reason) || !_advanced.SamePersistedContent(data)) { throw new InvalidDataException("Committed Nexus state differs from memory: " + reason); } if (flag) { File.Copy(_advancedDataPath, _advancedDataPath + ".bak", overwrite: true); } _advancedSaveError = false; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Nexus Advanced state saved: " + _advanced.Kits.Count + " kits, " + _advanced.History.Count + " history entries, " + _advanced.Snapshot.Count + " snapshot records to " + _advancedDataPath)); return true; } catch (Exception ex2) { _advancedSaveError = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Nexus Advanced state save failed: " + ex2)); _status = T("Could not save Nexus data. Check BepInEx log and folder permissions.", "Не удалось сохранить данные Nexus. Проверь журнал BepInEx и права на папку."); return false; } finally { try { if (File.Exists(text)) { File.Delete(text); } } catch { } } } private void RecordNexusHistory(string kind, string prefab, int count, int quality, int level, string name, string description = "") { if (count > 0 && _advanced != null) { _advanced.AddHistory(kind, prefab, count, quality, level, name, description); SaveNexusAdvanced(); } } private void DrawNexusAdvancedTabs() { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("NEXUS ADVANCED", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(142f) }); DrawMainTabButton(T("Doctor", "Диагностика"), 7); DrawMainTabButton(T("Kits", "Наборы"), 8); DrawMainTabButton(T("History", "История"), 9); DrawMainTabButton(T("Radar", "Радар"), 10); GUILayout.EndHorizontal(); if (_advancedSaveBlocked || _advancedSaveError) { GUILayout.Label(T("WARNING: Nexus data was not saved. Check BepInEx log; do not delete old configuration files.", "ВНИМАНИЕ: данные Nexus не сохранены. Проверь лог BepInEx; не удаляй старые конфиги."), Array.Empty<GUILayoutOption>()); } } private void DrawNexusAdvancedPanel() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) _advancedScroll = GUILayout.BeginScrollView(_advancedScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); switch (_mainTab) { case 7: DrawDoctorPanel(); break; case 8: DrawKitsPanel(); break; case 9: DrawHistoryPanel(); break; case 10: DrawRadarPanel(); break; } GUILayout.EndScrollView(); GUILayout.Label(_status, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); } private void DrawDoctorPanel() { GUILayout.Label(T("Nexus Doctor — inspect registered item prefabs. Results are advisory, not an in-game equipment test.", "Nexus Doctor — проверка зарегистрированных префабов. Результат не заменяет тест экипировки в игре."), Array.Empty<GUILayoutOption>()); if (_itemEntries.Count == 0) { GUILayout.Label(T("Enter a world and refresh catalogues first.", "Войди в мир и обнови каталоги."), Array.Empty<GUILayoutOption>()); return; } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(T("Filter ID:", "Поиск ID:"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(85f) }); GUI.SetNextControlName("ValheimSpawner.DoctorSearch"); _doctorSearch = GUILayout.TextField(_doctorSearch, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); _nameHasFocus = GUI.GetNameOfFocusedControl() == "ValheimSpawner.DoctorSearch"; GUILayout.EndHorizontal(); List<ItemEntry> list = new List<ItemEntry>(); for (int i = 0; i < _itemEntries.Count; i++) { if (list.Count >= 250) { break; } if (string.IsNullOrWhiteSpace(_doctorSearch) || _itemEntries[i].PrefabName.IndexOf(_doctorSearch, StringComparison.OrdinalIgnoreCase) >= 0) { list.Add(_itemEntries[i]); } } if (list.Count == 0) { GUILayout.Label(T("No matching items.", "Предметы не найдены."), Array.Empty<GUILayoutOption>()); return; } _doctorItemIndex = Mathf.Clamp(_doctorItemIndex, 0, list.Count - 1); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("◀", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(34f) })) { _doctorItemIndex = (_doctorItemIndex + list.Count - 1) % list.Count; } GUILayout.Label(list[_doctorItemIndex].PrefabName + " — " + GetItemDisplayName(list[_doctorItemIndex]), Array.Empty<GUILayoutOption>()); if (GUILayout.Button("▶", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(34f) })) { _doctorItemIndex = (_doctorItemIndex + 1) % list.Count; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button(T("Run Diagnostics", "Проверить"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) })) { _doctorReport = InspectItemPrefab(list[_doctorItemIndex]); } GUI.enabled = !string.IsNullOrEmpty(_doctorReport); if (GUILayout.Button(T("Copy Report", "Копировать отчёт"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) })) { GUIUtility.systemCopyBuffer = _doctorReport; _status = T("Diagnostic report copied.", "Диагностический отчёт скопирован."); } GUI.enabled = true; GUILayout.EndHorizontal(); if (!string.IsNullOrEmpty(_doctorReport)) { GUILayout.TextArea(_doctorReport, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(280f) }); } } private string InspectItemPrefab(ItemEntry entry) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("SuperNova Nexus Doctor v1.8.7"); stringBuilder.AppendLine("Prefab ID: " + entry.PrefabName); stringBuilder.AppendLine("Category: " + entry.Category.ToString() + " | Type: " + entry.ItemTypeLabel); stringBuilder.AppendLine("Max stack: " + entry.MaxStack + " | UI quality limit: " + entry.MaxQuality); stringBuilder.AppendLine("Durability: " + (entry.UsesDurability ? "yes" : "no")); stringBuilder.AppendLine("Icon: " + (((Object)(object)entry.IconTexture != (Object)null) ? "present" : "MISSING")); GameObject prefab = entry.Prefab; if ((Object)(object)prefab == (Object)null) { stringBuilder.AppendLine("[FAIL] Prefab reference is missing."); return stringBuilder.ToString(); } stringBuilder.AppendLine("[OK] Prefab: " + ((Object)prefab).name); Component val = ((_itemDropType == null) ? null : prefab.GetComponent(_itemDropType)); object obj = ReadMember(val, "m_itemData"); object obj2 = ReadMember(obj, "m_shared"); stringBuilder.AppendLine(((Object)(object)val == (Object)null) ? "[FAIL] ItemDrop missing" : "[OK] ItemDrop present"); stringBuilder.AppendLine((obj == null) ? "[FAIL] ItemData missing" : "[OK] ItemData present"); stringBuilder.AppendLine((obj2 == null) ? "[FAIL] SharedData missing" : "[OK] SharedData present"); Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true); int num = 0; int num2 = 0; for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] == (Object)null) { continue; } num++; Material[] sharedMaterials = componentsInChildren[i].sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { num2++; continue; } for (int j = 0; j < sharedMaterials.Length; j++) { if ((Object)(object)sharedMaterials[j] == (Object)null) { num2++; } } } stringBuilder.AppendLine("Renderer components on item prefab: " + num); stringBuilder.AppendLine("Missing renderer material slots: " + num2); int num3 = 0; Transform[] componentsInChildren2 = prefab.GetComponentsInChildren<Transform>(true); for (int k = 0; k < componentsInChildren2.Length; k++) { if ((Object)(object)componentsInChildren2[k] != (Object)null && ((Object)componentsInChildren2[k]).name.StartsWith("attach", StringComparison.OrdinalIgnoreCase)) { num3++; } } stringBuilder.AppendLine("Attachment-like transforms: " + num3); stringBuilder.AppendLine("Important: an equipped visual may use separate attach prefabs and skeleton bones."); stringBuilder.AppendLine("If the model is invisible, verify the result by equipping the item in-game."); return stringBuilder.ToString(); } private void AddSelectedItemToKit() { if (_selectedItem == null || _advanced.Kits.Count == 0) { _status = T("Select an item and create a kit first.", "Выбери предмет и сначала создай набор."); return; } _activeKitIndex = Mathf.Clamp(_activeKitIndex, 0, _advanced.Kits.Count - 1); NexusKit nexusKit = _advanced.Kits[_activeKitIndex]; if (nexusKit == null || nexusKit.Items == null || nexusKit.Items.Count >= 100) { _status = T("Kit limit: 100 item rows.", "Ограничение набора: 100 строк предметов."); return; } if (!TryParseItemAmount(_itemCountInput, out var amount)) { _status = T("Enter a valid count first.", "Сначала введи корректное количество."); return; } string text = NexusAdvancedData.CleanName(_itemCustomName); string text2 = NexusAdvancedData.CleanDescription(_itemCustomDescription); if ((text.Length > 0 || text2.Length > 0) && _selectedItem.MaxStack > 1) { _status = T("Custom names require max stack 1.", "Своё имя требует размера стака 1."); return; } NexusKitPart item = new NexusKitPart { PrefabId = _selectedItem.PrefabName, Count = Mathf.Clamp(amount, 1, 9999), Quality = Mathf.Clamp(_itemQuality, 1, _selectedItem.MaxQuality), CustomName = text, CustomDescription = text2 }; nexusKit.Items.Add(item); if (SaveNexusAdvanced()) { _status = T("Item added to kit: ", "Предмет добавлен в набор: ") + nexusKit.Name; } else { nexusKit.Items.Remove(item); } } private void DrawKitsPanel() { GUILayout.Label(T("Equipment Kits — save item IDs, counts, quality, custom names and descriptions.", "Наборы — хранение ID, количества, качества, собственных имён и описаний."), Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUI.SetNextControlName("ValheimSpawner.KitName"); _newKitName = GUILayout.TextField(_newKitName, 48, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.MinWidth(150f), GUILayout.ExpandWidth(true) }); _nameHasFocus = GUI.GetNameOfFocusedControl() == "ValheimSpawner.KitName"; if (GUILayout.Button(T("New Kit", "Создать набор"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) })) { string text = NexusAdvancedData.CleanName(_newKitName); if (text.Length == 0) { _status = T("Enter a kit name.", "Введи название набора."); } else if (_advanced.Kits.Count >= 25) { _status = T("Kit limit reached (25).", "Лимит наборов: 25."); } else { _advanced.Kits.Add(new NexusKit { Name = text }); _activeKitIndex = _advanced.Kits.Count - 1; _newKitName = string.Empty; SaveNexusAdvanced(); } } GUILayout.EndHorizontal(); if (_advanced.Kits.Count == 0) { GUILayout.Label(T("Create a kit, then select an item in its category and click Add to kit.", "Создай набор, затем выбери предмет в каталоге и нажми «В набор»."), Array.Empty<GUILayoutOption>()); return; } _activeKitIndex = Mathf.Clamp(_activeKitIndex, 0, _advanced.Kits.Count - 1); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("◀", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(34f) })) { _activeKitIndex = (_activeKitIndex + _advanced.Kits.Count - 1) % _advanced.Kits.Count; } NexusKit nexusKit = _advanced.Kits[_activeKitIndex]; GUILayout.Label(nexusKit.Name + " (" + nexusKit.Items.Count + "/" + 100 + ")", Array.Empty<GUILayoutOption>()); if (GUILayout.Button("▶", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(34f) })) { _activeKitIndex = (_activeKitIndex + 1) % _advanced.Kits.Count; } if (GUILayout.Button(T("Delete Kit", "Удалить набор"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { _advanced.Kits.RemoveAt(_activeKitIndex); _activeKitIndex = Mathf.Clamp(_activeKitIndex, 0, _advanced.Kits.Count - 1); SaveNexusAdvanced(); return; } GUILayout.EndHorizontal(); if (DrawKitWorkshopActions(nexusKit)) { return; } for (int num = nexusKit.Items.Count - 1; num >= 0; num--) { NexusKitPart nexusKitPart = nexusKit.Items[num]; if (nexusKitPart != null) { GUILayout.BeginHorizontal(GUIStyle.op_Implicit("box"), Array.Empty<GUILayoutOption>()); GUILayout.Label(nexusKitPart.PrefabId + " ×" + nexusKitPart.Count + " Q" + nexusKitPart.Quality + (string.IsNullOrEmpty(nexusKitPart.CustomName) ? "" : (" [" + nexusKitPart.CustomName + "]")) + (string.IsNullOrEmpty(nexusKitPart.CustomDescription) ? "" : " [custom description]"), Array.Empty<GUILayoutOption>()); if (GUILayout.Button(T("Edit", "Изменить"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { BeginEditKitPart(nexusKit, num); } if (GUILayout.Button(T("Remove", "Убрать"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { nexusKit.Items.RemoveAt(num); _editingKitPart = -1; SaveNexusAdvanced(); } GUILayout.EndHorizontal(); } } DrawKitPartEditor(nexusKit); if (GUILayout.Button(T("Apply Kit to Inventory", "Добавить набор в инвентарь"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) })) { ApplySelectedKit(nexusKit); } GUILayout.Label(T("Safety: at most 1,000 total units per application; incomplete insertion is reported. No auto-equip.", "Ограничение: максимум 1 000 предметов за применение; неполное добавление отображается. Без автоэкипировки."), Array.Empty<GUILayoutOption>()); } private void ApplySelectedKit(NexusKit kit) { if (!NexusKitWorkshop.Validate(kit, out var reason)) { _status = reason; return; } kit = NexusKitWorkshop.DeepCopy(kit); if (!AuthorizeLocal(delegate { ApplySelectedKit(kit); }, "apply kit " + kit.Name)) { return; } RefreshGameReferences(); if (!HasAdminAccess || (Object)(object)_playerComponent == (Object)null) { _status = T("Kit insertion requires a host player in a world.", "Для набора нужен игрок-хост в мире."); return; } object playerInventory = GetPlayerInventory(); if (playerInventory == null) { _status = T("Inventory unavailable.", "Инвентарь недоступен."); return; } int num = 0; List<ItemEntry> list = new List<ItemEntry>(); foreach (NexusKitPart item in kit.Items) { if (item == null || item.Count < 1 || item.Count > 9999) { _status = T("Invalid kit item amount.", "Недопустимое количество в наборе."); return; } num += item.Count; if (num > 1000) { _status = T("Kit exceeds 1,000 items per action.", "Набор превышает 1 000 предметов за одно действие."); return; } ItemEntry itemEntry = FindItemById(item.PrefabId); if (itemEntry == null) { _status = T("Missing prefab: ", "Нет префаба: ") + item.PrefabId; return; } if ((!string.IsNullOrEmpty(item.CustomName) || !string.IsNullOrEmpty(item.CustomDescription)) && itemEntry.MaxStack > 1) { _status = T("Named stack is unsupported: ", "Именованный стак не поддерживается: ") + item.PrefabId; return; } list.Add(itemEntry); } int num2 = 0; for (int num3 = 0; num3 < list.Count; num3++) { NexusKitPart nexusKitPart = kit.Items[num3]; InventoryAddResult inventoryAddResult = AddItemBatches(playerInventory, list[num3], nexusKitPart.Count, Mathf.Clamp(nexusKitPart.Quality, 1, list[num3].MaxQuality), NexusAdvancedData.CleanName(nexusKitPart.CustomName), NexusAdvancedData.CleanDescription(nexusKitPart.CustomDescription)); num2 += inventoryAddResult.Added; if (inventoryAddResult.Added != nexusKitPart.Count) { _status = T("Kit partially applied: ", "Набор добавлен частично: ") + num2 + "/" + num + ". " + inventoryAddResult.Reason; if (num2 > 0) { RecordNexusHistory("kit", kit.Name, num2, 1, 1, string.Empty); } return; } } if (num2 > 0) { RecordNexusHistory("kit", kit.Name, num2, 1, 1, string.Empty); } _status = T("Kit applied: ", "Набор добавлен: ") + kit.Name + " ×" + num2; } private ItemEntry FindItemById(string prefabId) { for (int i = 0; i < _itemEntries.Count; i++) { if (string.Equals(_itemEntries[i].PrefabName, prefabId, StringComparison.OrdinalIgnoreCase)) { return _itemEntries[i]; } } return null; } private void DrawHistoryPanel() { GUILayout.Label(T("Recent Nexus operations (last 100). Reuse fills controls; it never automatically repeats a spawn.", "Последние 100 операций Nexus. Повтор заполняет поля, но не создаёт объекты автоматически."), Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(T("Search:", "Поиск:"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(65f) }); GUI.SetNextControlName("ValheimNexus.HistorySearch"); _historyFilter = GUILayout.TextField(_historyFilter, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); _nameHasFocus = GUI.GetNameOfFocusedControl() == "ValheimNexus.HistorySearch"; if (GUILayout.Button(T("Clear History", "Очистить историю"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) })) { _advanced.History.Clear(); SaveNexusAdvanced(); } GUILayout.EndHorizontal(); _historyKindFilter = GUILayout.Toolbar(_historyKindFilter, new string[4] { T("All", "Все"), T("Items", "Предметы"), T("Creatures", "Существа"), T("Kits", "Наборы") }, Array.Empty<GUILayoutOption>()); int num = 0; for (int i = 0; i < _advanced.History.Count; i++) { NexusHistoryRecord nexusHistoryRecord = _advanced.History[i]; if (nexusHistoryRecord != null && (_historyKindFilter != 1 || !(nexusHistoryRecord.Kind != "item")) && (_historyKindFilter != 2 || !(nexusHistoryRecord.Kind != "creature")) && (_historyKindFilter != 3 || !(nexusHistoryRecord.Kind != "kit")) && (string.IsNullOrWhiteSpace(_historyFilter) || (nexusHistoryRecord.PrefabId ?? string.Empty).IndexOf(_historyFilter, StringComparison.OrdinalIgnoreCase) >= 0 || (nexusHistoryRecord.CustomName ?? string.Empty).IndexOf(_historyFilter, StringComparison.OrdinalIgnoreCase) >= 0 || (nexusHistoryRecord.CustomDescription ?? string.Empty).IndexOf(_historyFilter, StringComparison.OrdinalIgnoreCase) >= 0)) { num++; GUILayout.BeginHorizontal(GUIStyle.op_Implicit("box"), Array.Empty<GUILayoutOption>()); GUILayout.Label(nexusHistoryRecord.Utc + " | " + nexusHistoryRecord.Kind + " | " + nexusHistoryRecord.PrefabId + " ×" + nexusHistoryRecord.Count + (string.IsNullOrEmpty(nexusHistoryRecord.CustomName) ? "" : (" [" + nexusHistoryRecord.CustomName + "]")), Array.Empty<GUILayoutOption>()); if (nexusHistoryRecord.Kind != "kit" && GUILayout.Button(T("Reuse", "Повторить"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(95f) })) { LoadHistoryControls(nexusHistoryRecord); } GUILayout.EndHorizontal(); } } GUILayout.Label(T("Matching entries: ", "Найдено записей: ") + num, Array.Empty<GUILayoutOption>()); } private void LoadHistoryControls(NexusHistoryRecord record) { if (record.Kind == "item") { ItemEntry itemEntry = FindItemById(record.PrefabId); if (itemEntry == null) { _status = T("Item is no longer registered.", "Предмет больше не зарегистрирован."); return; } _selectedItem = itemEntry; _mainTab = (int)(1 + itemEntry.Category); _searchAllItems = true; _itemFavoritesOnly = false; _itemSearch = record.PrefabId; _itemCount = Mathf.Clamp(record.Count, 1, 9999); _itemCountInput = _itemCount.ToString(); _itemQuality = Mathf.Clamp(record.Quality, 1, itemEntry.MaxQuality); _itemCustomName = NexusAdvancedData.CleanName(record.CustomName); _itemCustomDescription = NexusAdvancedData.CleanDescription(record.CustomDescription); } else if (record.Kind == "creature") { CreatureEntry creatureEntry = null; for (int i = 0; i < _entries.Count; i++) { if (string.Equals(_entries[i].PrefabName, record.PrefabId, StringComparison.OrdinalIgnoreCase)) { creatureEntry = _entries[i]; break; } } if (creatureEntry == null) { _status = T("Creature is no longer registered.", "Существо больше не зарегистрировано."); return; } _selected = creatureEntry; _mainTab = 0; _creatureFavoritesOnly = false; _categoryFilter = 0; _biomeFilter = 0; _search = record.PrefabId; _count = Mathf.Clamp(record.Count, 1, 50); _level = Mathf.Clamp(record.Level, 1, 3); _customName = NexusAdvancedData.CleanName(record.CustomName); } _status = T("Settings restored. Review them before spawning.", "Параметры восстановлены. Проверь перед созданием."); } private void DrawRadarPanel() { GUILayout.Label(T("Compatibility Radar — compare runtime prefab catalogues with a saved snapshot.", "Радар совместимости — сравнение игровых каталогов с сохранённым снимком."), Array.Empty<GUILayoutOption>()); GUILayout.Label(T("Baseline: ", "Базовый снимок: ") + (string.IsNullOrEmpty(_advanced.SnapshotUtc) ? T("none", "отсутствует") : _advanced.SnapshotUtc), Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button(T("Compare Catalogues", "Сравнить каталоги"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) })) { _radarReport = CompareCatalogues(); } if (GUILayout.Button(T("Save Snapshot", "Сохранить снимок"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) })) { _advanced.Snapshot = BuildCatalogueSnapshot(); _advanced.SnapshotUtc = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss 'UTC'"); if (SaveNexusAdvanced()) { _radarReport = T("Snapshot saved. Compare again after a game or mod update.", "Снимок сохранён. Сравни после обновления игры или модов."); } else { _radarReport = T("Snapshot save failed! Check BepInEx log.", "Снимок не сохранён! Проверь журнал BepInEx."); } } GUILayout.EndHorizontal(); GUI.enabled = !string.IsNullOrEmpty(_radarReport); if (GUILayout.Button(T("Copy Comparison", "Копировать сравнение"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) })) { GUIUtility.systemCopyBuffer = _radarReport; } GUI.enabled = true; if (!string.IsNullOrEmpty(_radarReport)) { GUILayout.TextArea(_radarReport, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(150f) }); } } private List<NexusCatalogueRecord> BuildCatalogueSnapshot() { List<NexusCatalogueRecord> list = new List<NexusCatalogueRecord>(); for (int i = 0; i < _itemEntries.Count; i++) { if (list.Count >= 4500) { break; } ItemEntry itemEntry = _itemEntries[i]; list.Add(new NexusCatalogueRecord { Id = "item:" + itemEntry.PrefabName, Signature = itemEntry.ItemTypeLabel + "/" + itemEntry.Category.ToString() + "/" + itemEntry.MaxStack + "/" + itemEntry.MaxQuality + "/" + itemEntry.UsesDurability + "/" + ((Object)(object)itemEntry.IconTexture != (Object)null) }); } for (int j = 0; j < _entries.Count; j++) { if (list.Count >= 4500) { break; } list.Add(new NexusCatalogueRecord { Id = "creature:" + _entries[j].PrefabName, Signature = _entries[j].Category.ToString() }); } return list; } private string CompareCatalogues() { if (_advanced.Snapshot.Count == 0) { return T("No baseline. Save a snapshot first.", "Нет базового снимка. Сначала сохрани его."); } if (_itemEntries.Count == 0 || _entries.Count == 0) { return T("Load both catalogues in a world first.", "Сначала загрузи оба каталога в мире."); } Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);