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 SeedMap v1.0.0
SeedMap.dll
Decompiled 2 weeks agousing 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 BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SoftReferenceableAssets; using Splatform; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("SeedMap")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SeedMap")] [assembly: AssemblyTitle("SeedMap")] [assembly: AssemblyVersion("1.0.0.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 SeedMap { internal sealed class Category { public string Id; public string English; public bool DefaultOn; public string IconItem; public PinType FallbackIcon = (PinType)3; public bool ShowNames; public string Sample = ""; public Func<string, bool> Matches; public string Label => Strings.Get(Id); } internal static class Catalog { internal static readonly List<Category> Categories = new List<Category> { new Category { Id = "Bosses", English = "Bosses", DefaultOn = true, ShowNames = true, FallbackIcon = (PinType)9, Matches = Exact("Eikthyrnir", "GDKing", "Bonemass", "Dragonqueen", "GoblinKing", "Mistlands_DvergrBossEntrance1", "FaderLocation", "DN_Bossroom") }, new Category { Id = "Traders", English = "Traders", Sample = "Vendor_BlackForest", DefaultOn = true, ShowNames = true, FallbackIcon = (PinType)2, Matches = Exact("Vendor_BlackForest", "Hildir_camp", "BogWitch_Camp") }, new Category { Id = "HildirQuests", English = "Hildir's quests", ShowNames = true, IconItem = "chest_hildir1", Matches = Prefix("Hildir_") }, new Category { Id = "StartTemple", English = "Sacrificial stones", Sample = "StartTemple", DefaultOn = true, ShowNames = true, Matches = Exact("StartTemple") }, new Category { Id = "Crypts", English = "Burial chambers", IconItem = "TrophySkeleton", Matches = Prefix("Crypt") }, new Category { Id = "TrollCaves", English = "Troll caves", IconItem = "TrophyForestTroll", Matches = Prefix("TrollCave") }, new Category { Id = "SunkenCrypts", English = "Sunken crypts", IconItem = "TrophyDraugr", Matches = Prefix("SunkenCrypt") }, new Category { Id = "FrostCaves", English = "Frost caves", IconItem = "TrophyCultist", Matches = Prefix("MountainCave") }, new Category { Id = "InfestedMines", English = "Infested mines", IconItem = "TrophySeeker", Matches = Prefix("Mistlands_DvergrTownEntrance") }, new Category { Id = "Fortresses", English = "Charred fortresses", IconItem = "TrophyCharredMelee", Matches = Exact("CharredFortress") }, new Category { Id = "MorgenHoles", English = "Morgen holes", IconItem = "TrophyMorgen", Matches = Prefix("MorgenHole") }, new Category { Id = "Runestones", English = "Runestones", FallbackIcon = (PinType)6, Matches = (string n) => n.StartsWith("Runestone_", StringComparison.Ordinal) || n == "DrakeLorestone" }, new Category { Id = "DragonEggs", English = "Dragon egg nests", IconItem = "DragonEgg", Matches = Prefix("DrakeNest") }, new Category { Id = "FulingVillages", English = "Fuling villages", IconItem = "TrophyGoblin", Matches = Prefix("GoblinCamp") }, new Category { Id = "TarPits", English = "Tar pits", IconItem = "Tar", Matches = Prefix("TarPit") }, new Category { Id = "DvergrOutposts", English = "Dvergr outposts", IconItem = "TrophyDvergr", Matches = (string n) => n.StartsWith("Mistlands_GuardTower", StringComparison.Ordinal) || n == "Mistlands_Lighthouse1_new" || n == "Mistlands_Harbour1" }, new Category { Id = "Giants", English = "Giant remains", IconItem = "BlackMarble", Matches = (string n) => n.StartsWith("Mistlands_Giant", StringComparison.Ordinal) || n.StartsWith("Mistlands_Swords", StringComparison.Ordinal) }, new Category { Id = "Villages", English = "Abandoned villages", FallbackIcon = (PinType)1, Matches = (string n) => n.StartsWith("WoodVillage", StringComparison.Ordinal) || n == "WoodFarm1" || n == "NorthVillage" }, new Category { Id = "Unique", English = "Unique places", ShowNames = true, IconItem = "Thunderstone", Matches = (string n) => n.StartsWith("PlaceofMystery", StringComparison.Ordinal) || n == "AncientUpgradeStation" || n == "BigRockClearing" }, new Category { Id = "Other", English = "Everything else", Matches = (string _) => true } }; private static readonly Dictionary<string, Category> s_byLocation = new Dictionary<string, Category>(); private static readonly Dictionary<string, Sprite> s_icons = new Dictionary<string, Sprite>(); private static Func<string, bool> Exact(params string[] names) { return (string n) => names.Contains(n); } private static Func<string, bool> Prefix(params string[] prefixes) { return (string n) => prefixes.Any((string p) => n.StartsWith(p, StringComparison.Ordinal)); } internal static Category For(string locationName) { if (!s_byLocation.TryGetValue(locationName, out var value)) { value = Categories.First((Category c) => c.Matches(locationName)); s_byLocation[locationName] = value; } return value; } internal static Sprite PanelIcon(Category category) { return IconFor(category, category.Sample); } internal static Sprite IconFor(Category category, string locationName) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) string key = category.Id + "|" + locationName; if (s_icons.TryGetValue(key, out var value) && (Object)(object)value != (Object)null) { return value; } value = ItemIcon(category.IconItem) ?? GameLocationIcon(locationName) ?? PinIcon(category.FallbackIcon); s_icons[key] = value; return value; } private static Sprite ItemIcon(string itemName) { if (string.IsNullOrEmpty(itemName) || (Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemName); ItemDrop val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val != (Object)null) { return val.m_itemData.GetIcon(); } ZNetScene instance = ZNetScene.instance; object obj; if (instance == null) { obj = null; } else { GameObject prefab = instance.GetPrefab(itemName); obj = ((prefab != null) ? prefab.GetComponent<Piece>() : null); } Piece val2 = (Piece)obj; if (!((Object)(object)val2 != (Object)null)) { return null; } return val2.m_icon; } private static Sprite GameLocationIcon(string locationName) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Minimap.instance == (Object)null) { return null; } foreach (LocationSpriteData locationIcon in Minimap.instance.m_locationIcons) { if (locationIcon.m_name == locationName) { return locationIcon.m_icon; } } return null; } private static Sprite PinIcon(PinType type) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Minimap.instance == (Object)null) { return null; } foreach (SpriteData icon in Minimap.instance.m_icons) { if (icon.m_name == type) { return icon.m_icon; } } return null; } internal static string DisplayName(Category category, string locationName) { string text = Strings.Game("$npc_hildir", "Hildir"); return locationName switch { "Eikthyrnir" => Strings.Game("$enemy_eikthyr", "Eikthyr"), "GDKing" => Strings.Game("$enemy_gdking", "The Elder"), "Bonemass" => Strings.Game("$enemy_bonemass", "Bonemass"), "Dragonqueen" => Strings.Game("$enemy_dragon", "Moder"), "GoblinKing" => Strings.Game("$enemy_goblinking", "Yagluth"), "Mistlands_DvergrBossEntrance1" => Strings.Game("$enemy_seekerqueen", "The Queen"), "FaderLocation" => Strings.Game("$enemy_fader", "Fader"), "DN_Bossroom" => Strings.Game("$enemy_frozenking", Strings.Get("DeepNorthBoss")), "Vendor_BlackForest" => Strings.Game("$npc_haldor", "Haldor"), "Hildir_camp" => text, "BogWitch_Camp" => Strings.Game("$npc_bogwitch", "Bog Witch"), "Hildir_crypt" => text + ": " + Strings.Game("$biome_blackforest", "Black Forest"), "Hildir_cave" => text + ": " + Strings.Game("$biome_mountain", "Mountains"), "Hildir_plainsfortress" => text + ": " + Strings.Game("$biome_plains", "Plains"), "StartTemple" => category.Label, "AncientUpgradeStation" => Strings.Get("AncientStation"), _ => category.Label, }; } } internal sealed class LocationGenerator { private static readonly MethodInfo s_placeLocation = AccessTools.Method(typeof(ZoneSystem), "GenerateLocationsTimeSliced", new Type[3] { typeof(ZoneLocation), typeof(Stopwatch), typeof(ZPackage) }, (Type[])null); private static readonly FieldInfo s_generatedZones = AccessTools.Field(typeof(ZoneSystem), "m_generatedZones"); private readonly ZoneSystem _zoneSystem; private readonly List<ZoneLocation> _ordered; private readonly Dictionary<Vector2s, LocationInstance> _instances = new Dictionary<Vector2s, LocationInstance>(); private readonly Dictionary<AssetID, List<LocationInstance>> _idCache = new Dictionary<AssetID, List<LocationInstance>>(); private readonly Dictionary<string, List<LocationInstance>> _groupCache = new Dictionary<string, List<LocationInstance>>(); private readonly Dictionary<string, List<LocationInstance>> _maxGroupCache = new Dictionary<string, List<LocationInstance>>(); private readonly HashSet<Vector2s> _noGeneratedZones = new HashSet<Vector2s>(); private readonly Stopwatch _sliceWatch = new Stopwatch(); private IEnumerator _current; private int _index; internal static bool Supported { get { if (s_placeLocation != null) { return s_generatedZones != null; } return false; } } internal bool Done { get; private set; } internal float Progress { get { if (_ordered.Count != 0) { return (float)_index / (float)_ordered.Count; } return 1f; } } internal IEnumerable<LocationInstance> Results => _instances.Values; internal LocationGenerator(ZoneSystem zoneSystem) { _zoneSystem = zoneSystem; _ordered = (from l in zoneSystem.m_locations orderby l.m_prioritized descending where l.m_enable && l.m_quantity != 0 select l).ToList(); } internal void Step(double budgetSeconds) { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown if (Done) { return; } ZoneSystem zoneSystem = _zoneSystem; Dictionary<Vector2s, LocationInstance> locationInstances = zoneSystem.m_locationInstances; Dictionary<AssetID, List<LocationInstance>> locationIDCache = zoneSystem.m_locationIDCache; Dictionary<string, List<LocationInstance>> locationGroupCache = zoneSystem.m_locationGroupCache; Dictionary<string, List<LocationInstance>> locationMaxGroupCache = zoneSystem.m_locationMaxGroupCache; object value = s_generatedZones.GetValue(zoneSystem); zoneSystem.m_locationInstances = _instances; zoneSystem.m_locationIDCache = _idCache; zoneSystem.m_locationGroupCache = _groupCache; zoneSystem.m_locationMaxGroupCache = _maxGroupCache; s_generatedZones.SetValue(zoneSystem, _noGeneratedZones); try { Stopwatch stopwatch = Stopwatch.StartNew(); while (stopwatch.Elapsed.TotalSeconds < budgetSeconds) { if (_current == null) { if (_index >= _ordered.Count) { Done = true; break; } _current = (IEnumerator)s_placeLocation.Invoke(zoneSystem, new object[3] { _ordered[_index], _sliceWatch, (object)new ZPackage() }); } _sliceWatch.Restart(); if (!_current.MoveNext()) { _current = null; _index++; } } } finally { zoneSystem.m_locationInstances = locationInstances; zoneSystem.m_locationIDCache = locationIDCache; zoneSystem.m_locationGroupCache = locationGroupCache; zoneSystem.m_locationMaxGroupCache = locationMaxGroupCache; s_generatedZones.SetValue(zoneSystem, value); } } } internal struct WorldLocation { public string Name; public Vector3 Position; } internal static class LocationStore { internal enum State { Idle, Generating, Ready, Unsupported } private static readonly List<WorldLocation> s_locations = new List<WorldLocation>(); private static LocationGenerator s_generator; private static string s_worldKey; private static HashSet<string> s_uniqueNames; internal static State Status { get; private set; } = State.Idle; internal static bool Exact { get; private set; } internal static float Progress { get { LocationGenerator locationGenerator = s_generator; if (locationGenerator == null) { if (Status != State.Ready) { return 0f; } return 1f; } return locationGenerator.Progress; } } internal static IReadOnlyList<WorldLocation> Locations => s_locations; internal static int Version { get; private set; } private static string CachePath => Path.Combine(Paths.CachePath, "SeedMap", s_worldKey + ".csv"); internal static bool IsUnique(string locationName) { if (s_uniqueNames == null && (Object)(object)ZoneSystem.instance != (Object)null) { s_uniqueNames = new HashSet<string>(from l in ZoneSystem.instance.m_locations where l.m_unique select l.m_prefab.Name); } if (s_uniqueNames != null) { return s_uniqueNames.Contains(locationName); } return false; } internal static void Reset() { s_locations.Clear(); s_generator = null; s_worldKey = null; s_uniqueNames = null; Status = State.Idle; Exact = false; Version++; } internal static void Tick() { ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null || (Object)(object)ZNet.instance == (Object)null || ZNet.World == null || WorldGenerator.instance == null) { return; } if (Status == State.Idle) { Start(instance); } if (Status == State.Generating && s_generator != null) { s_generator.Step((double)Plugin.GenerationBudgetMs.Value / 1000.0); if (s_generator.Done) { s_locations.Clear(); s_locations.AddRange(s_generator.Results.Select(ToWorldLocation)); s_generator = null; Status = State.Ready; Version++; SaveCache(); Plugin.Log.LogInfo((object)$"Calculated {s_locations.Count} locations for this world."); } } } private static void Start(ZoneSystem zs) { if (ZNet.instance.IsServer()) { if (zs.LocationsGenerated) { s_locations.Clear(); s_locations.AddRange(zs.m_locationInstances.Values.Select(ToWorldLocation)); Exact = true; Status = State.Ready; Version++; } } else if (!LocationGenerator.Supported) { Plugin.Log.LogError((object)"This game version is not supported: the location placement routine was not found."); Status = State.Unsupported; } else { s_worldKey = WorldKey(zs); if (LoadCache()) { Status = State.Ready; Version++; } else { s_generator = new LocationGenerator(zs); Status = State.Generating; Plugin.Log.LogInfo((object)"Calculating the locations of this world from its seed..."); } } } internal static WorldLocation ToWorldLocation(LocationInstance instance) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) return new WorldLocation { Name = instance.m_location.m_prefab.Name, Position = instance.m_position }; } private static string WorldKey(ZoneSystem zs) { World world = ZNet.World; string versionString = Version.GetVersionString(false); string text = $"{world.m_seedName}_{world.m_seed}_{world.m_worldGenVersion}_{zs.m_locationVersion}_{versionString}_{zs.m_locations.Count}"; char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { text = text.Replace(oldChar, '-'); } return text; } private static bool LoadCache() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) try { if (!File.Exists(CachePath)) { return false; } List<WorldLocation> list = new List<WorldLocation>(); string[] array = File.ReadAllLines(CachePath); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ';' }); if (array2.Length == 4) { list.Add(new WorldLocation { Name = array2[0], Position = new Vector3(float.Parse(array2[1], CultureInfo.InvariantCulture), float.Parse(array2[2], CultureInfo.InvariantCulture), float.Parse(array2[3], CultureInfo.InvariantCulture)) }); } } s_locations.Clear(); s_locations.AddRange(list); Plugin.Log.LogInfo((object)$"Loaded {list.Count} locations from cache."); return list.Count > 0; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not read the location cache: " + ex.Message)); return false; } } private static void SaveCache() { if (s_worldKey == null) { return; } try { Directory.CreateDirectory(Path.GetDirectoryName(CachePath)); File.WriteAllLines(CachePath, s_locations.Select((WorldLocation l) => string.Join(";", l.Name, l.Position.x.ToString("R", CultureInfo.InvariantCulture), l.Position.y.ToString("R", CultureInfo.InvariantCulture), l.Position.z.ToString("R", CultureInfo.InvariantCulture)))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not write the location cache: " + ex.Message)); } } } internal static class MapOverlay { private static readonly FieldRef<Minimap, Material> s_largeMaterial = AccessTools.FieldRefAccess<Minimap, Material>("m_mapLargeShader"); private static readonly FieldRef<Minimap, Material> s_smallMaterial = AccessTools.FieldRefAccess<Minimap, Material>("m_mapSmallShader"); private static readonly FieldRef<Minimap, Texture2D> s_fogTexture = AccessTools.FieldRefAccess<Minimap, Texture2D>("m_fogTexture"); private static readonly Dictionary<string, ConfigEntry<bool>> s_enabled = new Dictionary<string, ConfigEntry<bool>>(); private static readonly List<PinData> s_pins = new List<PinData>(); private static readonly Dictionary<string, int> s_counts = new Dictionary<string, int>(); private static Texture2D s_clearFog; private static bool s_fogCleared; private static bool s_dirty = true; private static int s_pinsForVersion = -1; private static bool s_pinsForLargeMap; private static readonly Dictionary<Vector3, string> s_gameIcons = new Dictionary<Vector3, string>(); private static float s_iconCheckTimer; private static int s_iconSignature; internal static IReadOnlyDictionary<string, ConfigEntry<bool>> Enabled => s_enabled; internal static IReadOnlyDictionary<string, int> Counts => s_counts; internal static void BindConfig(ConfigFile config) { foreach (Category category in Catalog.Categories) { ConfigEntry<bool> val = config.Bind<bool>("1 - Categories", category.Id, category.DefaultOn, "Show " + category.English.ToLowerInvariant() + " on the map."); val.SettingChanged += delegate { s_dirty = true; }; s_enabled[category.Id] = val; } Plugin.RevealBiomes.SettingChanged += delegate { s_dirty = true; }; Plugin.ShowOnMinimap.SettingChanged += delegate { s_dirty = true; }; } internal static void Reset() { s_pins.Clear(); s_counts.Clear(); s_fogCleared = false; s_pinsForVersion = -1; s_dirty = true; } internal static void Tick() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 Minimap instance = Minimap.instance; if (!((Object)(object)instance == (Object)null)) { bool flag = (int)instance.m_mode == 2; bool flag2 = flag || Plugin.ShowOnMinimap.Value; if (flag2) { CheckDiscoveries(Time.deltaTime); } if (s_dirty || s_pinsForVersion != LocationStore.Version || s_pinsForLargeMap != flag2) { RebuildPins(instance, flag2); s_pinsForVersion = LocationStore.Version; s_pinsForLargeMap = flag2; } ApplyFog(instance, Plugin.RevealBiomes.Value && (flag || Plugin.ShowOnMinimap.Value)); s_dirty = false; MapPanel.Tick(instance, s_enabled, s_counts); } } private static void RebuildPins(Minimap minimap, bool show) { //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) foreach (PinData s_pin in s_pins) { minimap.RemovePin(s_pin); } s_pins.Clear(); s_counts.Clear(); foreach (WorldLocation location in LocationStore.Locations) { Category category = Catalog.For(location.Name); s_counts[category.Id] = ((!s_counts.TryGetValue(category.Id, out var value)) ? 1 : (value + 1)); } if (!show) { return; } HashSet<string> hashSet = DiscoveredLocationNames(); string text = " (" + Strings.Get("Possible") + ")"; foreach (WorldLocation location2 in LocationStore.Locations) { Category category2 = Catalog.For(location2.Name); if (!s_enabled[category2.Id].Value) { continue; } bool flag = LocationStore.IsUnique(location2.Name); if (!flag || !hashSet.Contains(location2.Name)) { string text2 = (category2.ShowNames ? Catalog.DisplayName(category2, location2.Name) : ""); if (flag && text2.Length > 0) { text2 += text; } PinData val = minimap.AddPin(location2.Position, (PinType)8, text2, false, false, 0L, default(PlatformUserID)); val.m_icon = Catalog.IconFor(category2, location2.Name); val.m_doubleSize = category2.ShowNames; s_pins.Add(val); } } } private static HashSet<string> DiscoveredLocationNames() { s_gameIcons.Clear(); ZoneSystem instance = ZoneSystem.instance; if (instance != null) { instance.GetLocationIcons(s_gameIcons); } return new HashSet<string>(s_gameIcons.Values); } private static void CheckDiscoveries(float dt) { s_iconCheckTimer -= dt; if (s_iconCheckTimer > 0f) { return; } s_iconCheckTimer = 5f; int num = 17; foreach (string item in from n in DiscoveredLocationNames() orderby n select n) { num = num * 31 + StringExtensionMethods.GetStableHashCode(item); } if (num != s_iconSignature) { s_iconSignature = num; s_dirty = true; } } private static void ApplyFog(Minimap minimap, bool clear) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (clear == s_fogCleared) { return; } Material val = s_largeMaterial.Invoke(minimap); Material val2 = s_smallMaterial.Invoke(minimap); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return; } if (clear) { if ((Object)(object)s_clearFog == (Object)null) { s_clearFog = new Texture2D(1, 1, (TextureFormat)4, false) { name = "SeedMap clear fog" }; s_clearFog.SetPixel(0, 0, new Color(0f, 0f, 0f, 0f)); s_clearFog.Apply(); } val.SetTexture("_FogTex", (Texture)(object)s_clearFog); if (Plugin.ShowOnMinimap.Value) { val2.SetTexture("_FogTex", (Texture)(object)s_clearFog); } } else { Texture2D val3 = s_fogTexture.Invoke(minimap); val.SetTexture("_FogTex", (Texture)(object)val3); val2.SetTexture("_FogTex", (Texture)(object)val3); } s_fogCleared = clear; } } internal static class MapPanel { private sealed class Row { public Toggle Toggle; public TMP_Text Label; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__13_0; public static UnityAction<bool> <>9__13_1; internal void <Build>b__13_0() { s_collapsed = !s_collapsed; ((Component)s_list).gameObject.SetActive(!s_collapsed); ((Component)s_fogRow.Toggle).gameObject.SetActive(!s_collapsed); } internal void <Build>b__13_1(bool on) { Plugin.RevealBiomes.Value = on; } } private const float Width = 300f; private const float RowHeight = 30f; private static RectTransform s_root; private static RectTransform s_list; private static TMP_Text s_title; private static TMP_Text s_status; private static Row s_fogRow; private static readonly Dictionary<string, Row> s_rows = new Dictionary<string, Row>(); private static string s_language; private static bool s_collapsed; internal static void Tick(Minimap minimap, IReadOnlyDictionary<string, ConfigEntry<bool>> categories, IReadOnlyDictionary<string, int> counts) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Invalid comparison between Unknown and I4 if ((Object)(object)s_root == (Object)null && ((Object)(object)minimap.m_largeRoot == (Object)null || !Build(minimap, categories))) { return; } ((Component)s_root).gameObject.SetActive(Plugin.ShowPanel.Value); if ((int)minimap.m_mode != 2 || !Plugin.ShowPanel.Value) { return; } bool num = s_language != Strings.Language; s_language = Strings.Language; s_title.text = (s_collapsed ? "SeedMap [+]" : "SeedMap [-]"); s_status.text = StatusText(); if (num) { s_fogRow.Label.text = Strings.Get("Fog"); } s_fogRow.Toggle.SetIsOnWithoutNotify(Plugin.RevealBiomes.Value); foreach (Category category in Catalog.Categories) { Row row = s_rows[category.Id]; counts.TryGetValue(category.Id, out var value); row.Label.text = $"{category.Label} <color=#B8A27A>{value:N0}</color>"; row.Toggle.SetIsOnWithoutNotify(categories[category.Id].Value); } } private static string StatusText() { return LocationStore.Status switch { LocationStore.State.Generating => Strings.Format("Calculating", (LocationStore.Progress * 100f).ToString("0")), LocationStore.State.Ready => Strings.Format("Count", LocationStore.Locations.Count.ToString("N0")) + " (" + Strings.Get(LocationStore.Exact ? "Exact" : "FromSeed") + ")", LocationStore.State.Unsupported => Strings.Get("Unsupported"), _ => Strings.Get("Waiting"), }; } private static bool Build(Minimap minimap, IReadOnlyDictionary<string, ConfigEntry<bool>> categories) { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Expected O, but got Unknown //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Expected O, but got Unknown //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Expected O, but got Unknown //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Expected O, but got Unknown Transform transform = minimap.m_largeRoot.transform; Transform obj = transform.Find("SharedPanel/PublicPosition"); Toggle val = ((obj != null) ? ((Component)obj).GetComponent<Toggle>() : null) ?? minimap.m_publicPosition; object obj2; if (!((Object)(object)val != (Object)null)) { obj2 = null; } else { Transform obj3 = ((Component)val).transform.Find("Label"); obj2 = ((obj3 != null) ? ((Component)obj3).GetComponent<TMP_Text>() : null); } TMP_Text val2 = (TMP_Text)obj2; Transform obj4 = transform.Find("PublicPanel"); Image val3 = ((obj4 != null) ? ((Component)obj4).GetComponent<Image>() : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null) { Plugin.Log.LogWarning((object)"Could not find the map's own UI pieces; the SeedMap panel is not available."); return false; } GameObject val4 = new GameObject("SeedMapPanel", new Type[4] { typeof(RectTransform), typeof(Image), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter) }); s_root = (RectTransform)val4.transform; ((Transform)s_root).SetParent(transform, false); RectTransform obj5 = s_root; RectTransform obj6 = s_root; RectTransform obj7 = s_root; Vector2 val5 = default(Vector2); ((Vector2)(ref val5))..ctor(0f, 1f); obj7.pivot = val5; Vector2 anchorMin = (obj6.anchorMax = val5); obj5.anchorMin = anchorMin; s_root.anchoredPosition = new Vector2(16f, -16f); s_root.sizeDelta = new Vector2(300f, 0f); Image component = val4.GetComponent<Image>(); component.sprite = val3.sprite; component.type = val3.type; ((Graphic)component).color = ((Graphic)val3).color; ((Graphic)component).material = ((Graphic)val3).material; VerticalLayoutGroup component2 = val4.GetComponent<VerticalLayoutGroup>(); ((LayoutGroup)component2).padding = new RectOffset(12, 12, 8, 10); ((HorizontalOrVerticalLayoutGroup)component2).spacing = 2f; bool childControlWidth = (((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = true); ((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = childControlWidth; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; val4.GetComponent<ContentSizeFitter>().verticalFit = (FitMode)2; s_title = MakeText((Transform)(object)s_root, val2, 22f, (FontStyles)1, 30f); Button obj8 = ((Component)s_title).gameObject.AddComponent<Button>(); ((Selectable)obj8).transition = (Transition)0; ButtonClickedEvent onClick = obj8.onClick; object obj9 = <>c.<>9__13_0; if (obj9 == null) { UnityAction val7 = delegate { s_collapsed = !s_collapsed; ((Component)s_list).gameObject.SetActive(!s_collapsed); ((Component)s_fogRow.Toggle).gameObject.SetActive(!s_collapsed); }; <>c.<>9__13_0 = val7; obj9 = (object)val7; } ((UnityEvent)onClick).AddListener((UnityAction)obj9); s_status = MakeText((Transform)(object)s_root, val2, 15f, (FontStyles)2, 22f); ((Graphic)s_status).color = new Color(0.85f, 0.85f, 0.85f, 0.9f); s_fogRow = MakeRow((Transform)(object)s_root, val, null); ((UnityEvent<bool>)(object)s_fogRow.Toggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on) { Plugin.RevealBiomes.Value = on; }); GameObject val8 = new GameObject("Categories", new Type[2] { typeof(RectTransform), typeof(VerticalLayoutGroup) }); s_list = (RectTransform)val8.transform; ((Transform)s_list).SetParent((Transform)(object)s_root, false); VerticalLayoutGroup component3 = val8.GetComponent<VerticalLayoutGroup>(); ((LayoutGroup)component3).padding = new RectOffset(0, 0, 6, 0); ((HorizontalOrVerticalLayoutGroup)component3).spacing = 2f; childControlWidth = (((HorizontalOrVerticalLayoutGroup)component3).childControlHeight = true); ((HorizontalOrVerticalLayoutGroup)component3).childControlWidth = childControlWidth; ((HorizontalOrVerticalLayoutGroup)component3).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)component3).childForceExpandHeight = false; s_rows.Clear(); foreach (Category category in Catalog.Categories) { Row row = MakeRow((Transform)(object)s_list, val, Catalog.PanelIcon(category)); ConfigEntry<bool> entry = categories[category.Id]; ((UnityEvent<bool>)(object)row.Toggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on) { entry.Value = on; }); s_rows[category.Id] = row; } s_language = null; return true; } private static TMP_Text MakeText(Transform parent, TMP_Text template, float size, FontStyles style, float height) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) TMP_Text obj = Object.Instantiate<TMP_Text>(template, parent, false); ((Object)obj).name = "SeedMapText"; obj.fontSize = size; obj.fontStyle = style; obj.alignment = (TextAlignmentOptions)4097; obj.textWrappingMode = (TextWrappingModes)0; obj.overflowMode = (TextOverflowModes)1; LayoutElement obj2 = ((Component)obj).gameObject.AddComponent<LayoutElement>(); float minHeight = (obj2.preferredHeight = height); obj2.minHeight = minHeight; return obj; } private static Row MakeRow(Transform parent, Toggle template, Sprite icon) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_010f: 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_0128: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, parent, false); ((Object)val).name = "SeedMapToggle"; val.SetActive(true); MonoBehaviour[] components = val.GetComponents<MonoBehaviour>(); foreach (MonoBehaviour val2 in components) { if (val2 is UIGamePad) { Object.DestroyImmediate((Object)(object)val2); } } string[] array = new string[2] { "Trigger", "gamepad_hint" }; foreach (string text in array) { Transform val3 = val.transform.Find(text); if ((Object)(object)val3 != (Object)null) { Object.Destroy((Object)(object)((Component)val3).gameObject); } } Toggle component = val.GetComponent<Toggle>(); component.onValueChanged = new ToggleEvent(); component.group = null; LayoutElement obj = val.AddComponent<LayoutElement>(); float minHeight = (obj.preferredHeight = 30f); obj.minHeight = minHeight; Transform obj2 = val.transform.Find("Background"); RectTransform val4 = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null); Vector2 val5 = default(Vector2); if ((Object)(object)val4 != (Object)null) { ((Vector2)(ref val5))..ctor(1f, 0.5f); val4.anchorMax = val5; val4.anchorMin = val5; val4.anchoredPosition = new Vector2(-15f, 0f); val4.sizeDelta = new Vector2(30f, 30f); Transform obj3 = ((Transform)val4).Find("Checkmark"); RectTransform val6 = (RectTransform)(object)((obj3 is RectTransform) ? obj3 : null); if ((Object)(object)val6 != (Object)null) { val6.sizeDelta = new Vector2(26f, 26f); } } float num2 = 4f; if ((Object)(object)icon != (Object)null) { GameObject val7 = new GameObject("Icon", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val8 = (RectTransform)val7.transform; ((Transform)val8).SetParent(val.transform, false); ((Vector2)(ref val5))..ctor(0f, 0.5f); val8.anchorMax = val5; val8.anchorMin = val5; val8.anchoredPosition = new Vector2(14f, 0f); val8.sizeDelta = new Vector2(26f, 26f); Image component2 = val7.GetComponent<Image>(); component2.sprite = icon; component2.preserveAspect = true; ((Graphic)component2).raycastTarget = false; num2 = 34f; } Transform obj4 = val.transform.Find("Label"); TMP_Text val9 = ((obj4 != null) ? ((Component)obj4).GetComponent<TMP_Text>() : null); if ((Object)(object)val9 != (Object)null) { RectTransform rectTransform = val9.rectTransform; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.offsetMin = new Vector2(num2, 0f); rectTransform.offsetMax = new Vector2(-36f, 0f); val9.alignment = (TextAlignmentOptions)4097; val9.textWrappingMode = (TextWrappingModes)0; val9.overflowMode = (TextOverflowModes)1; val9.fontSize = 17f; val9.richText = true; } return new Row { Toggle = component, Label = val9 }; } internal static bool MouseOver() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)s_root == (Object)null || !((Component)s_root).gameObject.activeInHierarchy) { return false; } Canvas componentInParent = ((Component)s_root).GetComponentInParent<Canvas>(); Camera val = (((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null); return RectTransformUtility.RectangleContainsScreenPoint(s_root, Vector2.op_Implicit(Input.mousePosition), val); } } [BepInPlugin("thelm.SeedMap", "SeedMap", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string ModGuid = "thelm.SeedMap"; public const string ModName = "SeedMap"; public const string ModVersion = "1.0.0"; internal static Plugin Instance; internal static ManualLogSource Log; internal static ConfigEntry<bool> RevealBiomes; internal static ConfigEntry<bool> ShowOnMinimap; internal static ConfigEntry<bool> ShowPanel; internal static ConfigEntry<int> GenerationBudgetMs; internal static ConfigEntry<bool> ValidateOnServer; private void Awake() { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_00d3: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; RevealBiomes = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - General", "RevealBiomes", false, "Show the whole world's biomes without the fog of war. Only changes what you see: nothing is marked as explored."); ShowOnMinimap = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - General", "ShowOnMinimap", false, "Also show the pins (and the fogless biomes) on the small corner minimap, not only on the large map."); ShowPanel = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - General", "ShowPanel", true, "Show the SeedMap panel with the category switches when the large map is open."); GenerationBudgetMs = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Advanced", "GenerationBudgetMs", 6, new ConfigDescription("Milliseconds per frame spent calculating the world's locations the first time you join a world. Higher finishes sooner but can drop frames while it runs. The result is cached, so it only happens once per world.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>())); ValidateOnServer = ((BaseUnityPlugin)this).Config.Bind<bool>("9 - Development", "ValidateOnServer", false, "Dedicated servers only: calculate the locations the way clients do and compare them with the real ones in the log."); MapOverlay.BindConfig(((BaseUnityPlugin)this).Config); new Harmony("thelm.SeedMap").PatchAll(typeof(Plugin).Assembly); } private void Update() { if (!((Object)(object)ZNet.instance == (Object)null)) { if (ZNet.instance.IsDedicated()) { ServerValidation.Tick(); return; } LocationStore.Tick(); MapOverlay.Tick(); } } } [HarmonyPatch] internal static class Patches { [HarmonyPatch(typeof(ZNet), "Awake")] [HarmonyPostfix] private static void ZNet_Awake() { LocationStore.Reset(); MapOverlay.Reset(); } [HarmonyPatch(typeof(Minimap), "UpdateMap")] [HarmonyPrefix] private static void Minimap_UpdateMap(ref bool takeInput) { if (MapPanel.MouseOver()) { takeInput = false; } } [HarmonyPatch(typeof(Minimap), "OnMapLeftDown")] [HarmonyPatch(typeof(Minimap), "OnMapLeftUp")] [HarmonyPatch(typeof(Minimap), "OnMapDblClick")] [HarmonyPatch(typeof(Minimap), "OnMapLeftClick")] [HarmonyPatch(typeof(Minimap), "OnMapMiddleClick")] [HarmonyPatch(typeof(Minimap), "RemovePinUnderPointer")] [HarmonyPrefix] private static bool Minimap_MouseOnMap() { return !MapPanel.MouseOver(); } } internal static class ServerValidation { private static LocationGenerator s_generator; private static bool s_done; private static readonly Stopwatch s_watch = new Stopwatch(); private static void TryBuildPanel() { try { Minimap val = Minimap.instance ?? Resources.FindObjectsOfTypeAll<Minimap>().FirstOrDefault(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogInfo((object)"Validation: no minimap on this server, panel not tested."); return; } MapPanel.Tick(val, MapOverlay.Enabled, MapOverlay.Counts); Transform val2 = val.m_largeRoot.transform.Find("SeedMapPanel"); int num = ((!((Object)(object)val2 == (Object)null)) ? ((Component)val2).GetComponentsInChildren<Toggle>(true).Length : 0); Plugin.Log.LogInfo((object)$"Validation: panel built={(Object)(object)val2 != (Object)null}, toggles={num}"); } catch (Exception ex) { Plugin.Log.LogError((object)("Validation: panel failed: " + ex)); } } private static void CheckIcons() { foreach (Category category in Catalog.Categories) { if (!string.IsNullOrEmpty(category.IconItem)) { ObjectDB instance = ObjectDB.instance; int num; if (!((Object)(object)((instance != null) ? instance.GetItemPrefab(category.IconItem) : null) != (Object)null)) { ZNetScene instance2 = ZNetScene.instance; num = (((Object)(object)((instance2 != null) ? instance2.GetPrefab(category.IconItem) : null) != (Object)null) ? 1 : 0); } else { num = 1; } bool flag = (byte)num != 0; Plugin.Log.LogInfo((object)("Validation: icon " + category.IconItem + " for " + category.Id + ": " + (flag ? "ok" : "MISSING"))); } } IOrderedEnumerable<string> values = from l in ZoneSystem.instance.m_locations where l.m_enable && l.m_quantity != 0 select l.m_prefab.Name into n where Catalog.For(n).Id == "Other" orderby n select n; Plugin.Log.LogInfo((object)("Validation: in 'Other': " + string.Join(", ", values))); } internal static void Tick() { if (s_done || !Plugin.ValidateOnServer.Value) { return; } ZoneSystem instance = ZoneSystem.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsDedicated() && instance.LocationsGenerated) { if (s_generator == null) { TryBuildPanel(); DumpLocationTypes(instance); CheckIcons(); s_generator = new LocationGenerator(instance); Plugin.Log.LogInfo((object)"Validation: calculating locations the client way..."); } s_watch.Start(); s_generator.Step(0.25); s_watch.Stop(); if (s_generator.Done) { s_done = true; Plugin.Log.LogInfo((object)($"Validation: calculation took {s_watch.Elapsed.TotalSeconds:0.0} s of CPU time " + $"(about {s_watch.Elapsed.TotalSeconds / ((double)Plugin.GenerationBudgetMs.Value / 1000.0) / 60.0 / 60.0:0.0} minutes on a client at 60 fps with the configured budget)")); Compare(instance.m_locationInstances.Values.Select(LocationStore.ToWorldLocation).ToList(), s_generator.Results.Select(LocationStore.ToWorldLocation).ToList()); } } } private static void DumpLocationTypes(ZoneSystem zs) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) foreach (ZoneLocation item in from l in zs.m_locations where l.m_enable && l.m_quantity != 0 orderby l.m_prefab.Name select l) { Plugin.Log.LogInfo((object)($"LOCTYPE {item.m_prefab.Name} | biome={item.m_biome} qty={item.m_quantity} unique={item.m_unique} " + $"iconAlways={item.m_iconAlways} iconPlaced={item.m_iconPlaced} group={item.m_group} prio={item.m_prioritized}")); } } private static void Compare(List<WorldLocation> real, List<WorldLocation> calculated) { HashSet<string> realByKey = new HashSet<string>(real.Select(Key)); int num = calculated.Count((WorldLocation l) => realByKey.Contains(Key(l))); Plugin.Log.LogInfo((object)($"Validation: real={real.Count} calculated={calculated.Count} identical={num} " + $"({((real.Count == 0) ? 0f : (100f * (float)num / (float)real.Count)):0.0}%)")); foreach (IGrouping<string, WorldLocation> item in from l in calculated where !realByKey.Contains(Key(l)) group l by l.Name into g orderby g.Key select g) { Plugin.Log.LogInfo((object)$"Validation: extra {item.Key}: {item.Count()}"); } HashSet<string> calculatedByKey = new HashSet<string>(calculated.Select(Key)); foreach (IGrouping<string, WorldLocation> item2 in from l in real group l by l.Name into g orderby g.Key select g) { int num2 = item2.Count((WorldLocation l) => calculatedByKey.Contains(Key(l))); if (num2 != item2.Count()) { Plugin.Log.LogInfo((object)$"Validation: {item2.Key}: {num2}/{item2.Count()} identical"); } } } private static string Key(WorldLocation l) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) return $"{l.Name}@{Mathf.RoundToInt(l.Position.x)},{Mathf.RoundToInt(l.Position.z)}"; } } internal static class Strings { private static readonly string[] s_languages = new string[8] { "English", "Spanish", "German", "French", "Portuguese_Brazilian", "Russian", "Polish", "Italian" }; private static readonly Dictionary<string, string[]> s_texts = new Dictionary<string, string[]> { ["Bosses"] = new string[8] { "Bosses", "Jefes", "Bosse", "Boss", "Chefes", "Боссы", "Bossowie", "Boss" }, ["Traders"] = new string[8] { "Traders", "Comerciantes", "Händler", "Marchands", "Comerciantes", "Торговцы", "Handlarze", "Mercanti" }, ["HildirQuests"] = new string[8] { "Hildir's quests", "Misiones de Hildir", "Hildirs Aufträge", "Quêtes de Hildir", "Missões de Hildir", "Задания Хильдир", "Zadania Hildir", "Missioni di Hildir" }, ["StartTemple"] = new string[8] { "Sacrificial stones", "Piedras de sacrificio", "Opfersteine", "Pierres sacrificielles", "Pedras sacrificiais", "Жертвенные камни", "Kamienie ofiarne", "Pietre sacrificali" }, ["Crypts"] = new string[8] { "Burial chambers", "Cámaras funerarias", "Grabkammern", "Chambres funéraires", "Câmaras funerárias", "Погребальные камеры", "Komory grobowe", "Camere sepolcrali" }, ["TrollCaves"] = new string[8] { "Troll caves", "Cuevas de trol", "Trollhöhlen", "Grottes de trolls", "Cavernas de troll", "Пещеры троллей", "Jaskinie trolli", "Caverne dei troll" }, ["SunkenCrypts"] = new string[8] { "Sunken crypts", "Criptas hundidas", "Versunkene Krypten", "Cryptes englouties", "Criptas submersas", "Затонувшие склепы", "Zatopione krypty", "Cripte sommerse" }, ["FrostCaves"] = new string[8] { "Frost caves", "Cuevas de hielo", "Frosthöhlen", "Grottes de givre", "Cavernas congeladas", "Ледяные пещеры", "Lodowe jaskinie", "Caverne di ghiaccio" }, ["InfestedMines"] = new string[8] { "Infested mines", "Minas infestadas", "Befallene Minen", "Mines infestées", "Minas infestadas", "Заражённые шахты", "Zainfekowane kopalnie", "Miniere infestate" }, ["Fortresses"] = new string[8] { "Charred fortresses", "Fortalezas carbonizadas", "Verkohlte Festungen", "Forteresses calcinées", "Fortalezas carbonizadas", "Обугленные крепости", "Zwęglone fortece", "Fortezze carbonizzate" }, ["MorgenHoles"] = new string[8] { "Morgen holes", "Guaridas de morgen", "Morgen-Höhlen", "Repaires de morgens", "Covis de morgen", "Логова моргенов", "Nory morgenów", "Tane dei morgen" }, ["Runestones"] = new string[8] { "Runestones", "Piedras rúnicas", "Runensteine", "Pierres runiques", "Pedras rúnicas", "Рунные камни", "Kamienie runiczne", "Pietre runiche" }, ["DragonEggs"] = new string[8] { "Dragon egg nests", "Nidos de dragón", "Drachennester", "Nids de dragon", "Ninhos de dragão", "Драконьи гнёзда", "Smocze gniazda", "Nidi di drago" }, ["FulingVillages"] = new string[8] { "Fuling villages", "Aldeas de fulings", "Fuling-Dörfer", "Villages fulings", "Vilas de fulings", "Деревни фулингов", "Wioski fulingów", "Villaggi dei fuling" }, ["TarPits"] = new string[8] { "Tar pits", "Pozos de brea", "Teergruben", "Fosses de goudron", "Poços de piche", "Смоляные ямы", "Doły smołowe", "Pozze di catrame" }, ["DvergrOutposts"] = new string[8] { "Dvergr outposts", "Puestos dvergr", "Dvergr-Außenposten", "Avant-postes dvergrs", "Postos dvergr", "Аванпосты двергров", "Posterunki dvergrów", "Avamposti dvergr" }, ["Giants"] = new string[8] { "Giant remains", "Restos de gigantes", "Überreste von Riesen", "Restes de géants", "Restos de gigantes", "Останки великанов", "Szczątki olbrzymów", "Resti di giganti" }, ["Villages"] = new string[8] { "Abandoned villages", "Aldeas abandonadas", "Verlassene Dörfer", "Villages abandonnés", "Vilas abandonadas", "Заброшенные деревни", "Opuszczone wioski", "Villaggi abbandonati" }, ["Unique"] = new string[8] { "Unique places", "Lugares únicos", "Einzigartige Orte", "Lieux uniques", "Lugares únicos", "Уникальные места", "Unikalne miejsca", "Luoghi unici" }, ["Other"] = new string[8] { "Everything else", "Todo lo demás", "Alles andere", "Tout le reste", "Todo o resto", "Всё остальное", "Wszystko inne", "Tutto il resto" }, ["Fog"] = new string[8] { "Biomes without fog", "Biomas sin niebla", "Biome ohne Nebel", "Biomes sans brouillard", "Biomas sem névoa", "Биомы без тумана", "Biomy bez mgły", "Biomi senza nebbia" }, ["Calculating"] = new string[8] { "Calculating locations... {0}%", "Calculando ubicaciones... {0}%", "Orte werden berechnet... {0}%", "Calcul des lieux... {0}%", "Calculando locais... {0}%", "Вычисление мест... {0}%", "Obliczanie miejsc... {0}%", "Calcolo dei luoghi... {0}%" }, ["Count"] = new string[8] { "{0} locations", "{0} ubicaciones", "{0} Orte", "{0} lieux", "{0} locais", "Мест: {0}", "Miejsc: {0}", "{0} luoghi" }, ["Exact"] = new string[8] { "exact", "exactas", "exakt", "exacts", "exatos", "точно", "dokładne", "esatti" }, ["FromSeed"] = new string[8] { "from the seed", "desde la semilla", "aus dem Seed", "depuis la graine", "pela semente", "по сиду", "z ziarna", "dal seed" }, ["Waiting"] = new string[8] { "Waiting for the world...", "Esperando al mundo...", "Warte auf die Welt...", "En attente du monde...", "Aguardando o mundo...", "Ожидание мира...", "Oczekiwanie na świat...", "In attesa del mondo..." }, ["Unsupported"] = new string[8] { "Game version not supported", "Versión del juego no soportada", "Spielversion nicht unterstützt", "Version du jeu non prise en charge", "Versão do jogo não suportada", "Версия игры не поддерживается", "Nieobsługiwana wersja gry", "Versione del gioco non supportata" }, ["Possible"] = new string[8] { "possible", "posible", "möglich", "possible", "possível", "возможно", "możliwe", "possibile" }, ["DeepNorthBoss"] = new string[8] { "Deep North boss", "Jefe del Norte Profundo", "Boss des Hohen Nordens", "Boss du Grand Nord", "Chefe do Norte Profundo", "Босс Глубокого Севера", "Boss Dalekiej Północy", "Boss del Profondo Nord" }, ["AncientStation"] = new string[8] { "Ancient table", "Mesa antigua", "Uralter Tisch", "Table ancienne", "Mesa antiga", "Древний стол", "Starożytny stół", "Tavolo antico" } }; internal static string Language { get { if (Localization.instance == null) { return "English"; } return Localization.instance.GetSelectedLanguage(); } } internal static string Get(string id) { if (!s_texts.TryGetValue(id, out var value)) { return id; } int num = Array.IndexOf(s_languages, Language); return value[(num >= 0) ? num : 0]; } internal static string Format(string id, object arg) { return string.Format(Get(id), arg); } internal static string Game(string token, string fallback) { if (Localization.instance == null) { return fallback; } string text = Localization.instance.Localize(token); if (!string.IsNullOrEmpty(text) && !text.StartsWith("[")) { return text; } return fallback; } } }