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 Treasure v0.1.6
plugins/Treasure.dll
Decompiled 4 hours agousing System; 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 BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Sansara.Admin; using Sansara.Chronicle; using Sansara.Clock; using Splatform; using Treasure.Core; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Treasure")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.6.0")] [assembly: AssemblyInformationalVersion("0.1.6+ae0a16433674e458d86a7b9f14512aae571a955f")] [assembly: AssemblyProduct("Treasure")] [assembly: AssemblyTitle("Treasure")] [assembly: AssemblyVersion("0.1.6.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 Sansara.Admin { internal static class AdminChannel { private const float ScanSeconds = 1f; private const float DenialLogSeconds = 30f; private static ManualLogSource _log; private static bool _owner; private static bool _announced; private static float _nextScan; private static readonly HashSet<ZRpc> Registered = new HashSet<ZRpc>(); private static readonly Dictionary<ZRpc, float> LastDenial = new Dictionary<ZRpc, float>(); public static void Register(string module, string[] verbs, Func<string, string, string, string> handler, ManualLogSource log) { _log = log; if (AdminRegistry.Shared.Register(module, verbs, handler)) { log.LogWarning((object)("Админ-канал: модуль " + module + " зарегистрирован повторно — обработчик заменён.")); } if (AdminRegistry.Shared.ClaimOwner(typeof(AdminChannel).Assembly.GetName().Name)) { _owner = true; } } public static void Tick() { if (!_owner || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || Time.realtimeSinceStartup < _nextScan) { return; } _nextScan = Time.realtimeSinceStartup + 1f; try { Scan(); } catch (Exception ex) { _log.LogError((object)("Админ-канал: ошибка: " + ex)); } } private static void Scan() { if (!_announced) { _announced = true; _log.LogInfo((object)("Админ-канал: владелец " + AdminRegistry.Shared.Owner + ", модули: " + string.Join(", ", AdminRegistry.Shared.Modules().ToArray()) + ".")); } HashSet<ZRpc> live = new HashSet<ZRpc>(); foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.m_rpc != null) { live.Add(peer.m_rpc); if (Registered.Add(peer.m_rpc)) { peer.m_rpc.Register<ZPackage>("Sansara_Admin", (Action<ZRpc, ZPackage>)OnRequest); } } } Registered.RemoveWhere((ZRpc r) => !live.Contains(r)); foreach (ZRpc item in new List<ZRpc>(LastDenial.Keys)) { if (!live.Contains(item)) { LastDenial.Remove(item); } } } private static void OnRequest(ZRpc rpc, ZPackage pkg) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown int num = 0; AdminReply adminReply; try { int protocol = pkg.ReadInt(); num = pkg.ReadInt(); string module = pkg.ReadString(); string line = pkg.ReadString(); adminReply = Handle(rpc, protocol, module, line); } catch (Exception ex) { ZNetPeer val = ZNet.instance.GetPeers().Find((ZNetPeer p) => p.m_rpc == rpc); if (val == null || !val.IsReady()) { return; } LogThrottled(rpc, "Админ-канал: неверный запрос от " + AdminProtocol.ForLog(val.m_playerName) + ": " + AdminProtocol.ForLog(ex.Message)); adminReply = new AdminReply(4, "неверный запрос"); } if (adminReply != null) { ZPackage val2 = new ZPackage(); val2.Write(1); val2.Write(num); val2.Write(adminReply.Code); val2.Write(AdminProtocol.Truncate(adminReply.Text)); rpc.Invoke("Sansara_AdminReply", new object[1] { val2 }); } } private static void LogThrottled(ZRpc rpc, string message) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!LastDenial.TryGetValue(rpc, out var value) || realtimeSinceStartup - value >= 30f) { LastDenial[rpc] = realtimeSinceStartup; _log.LogWarning((object)message); } } private static AdminReply Handle(ZRpc rpc, int protocol, string module, string line) { ZNetPeer val = ZNet.instance.GetPeers().Find((ZNetPeer p) => p.m_rpc == rpc); if (val == null || !val.IsReady()) { return null; } AdminReply adminReply = AdminProtocol.Check(protocol, module, line); if (adminReply != null) { return adminReply; } string text = AdminProtocol.ForLog(val.m_playerName); string text2 = AdminProtocol.ForLog(val.m_socket.GetHostName()); string text3 = AdminProtocol.ForLog(AdminProtocol.Shown(module, line)); bool flag = ((ZDOID)(ref val.m_characterID)).IsNone(); if (flag || !ZNet.instance.IsAdmin(val.m_socket.GetHostName())) { LogThrottled(rpc, "Админ-канал: отказано " + text + " (" + text2 + "): " + text3); return new AdminReply(1, flag ? "отказано: персонаж не в мире (ещё не вошёл или погиб)" : "отказано: нужны права администратора"); } Exception error; AdminReply adminReply2 = AdminRegistry.Shared.Dispatch(module, line, val.m_playerName, val.m_socket.GetHostName(), out error); if (error != null) { _log.LogError((object)("Админ-канал: " + text + " (" + text2 + ") → " + text3 + ": " + AdminProtocol.ForLog(error.Message) + Environment.NewLine + error.StackTrace)); } else { _log.LogInfo((object)("Админ-канал: " + text + " (" + text2 + ") → " + text3 + ": " + AdminProtocol.ForLog(AdminProtocol.FirstLine(adminReply2.Text)))); } return adminReply2; } } public sealed class AdminCommand { public string Verb = ""; public readonly List<string> Args = new List<string>(); public string Argument => string.Join(" ", Args); public static AdminCommand Parse(string text) { AdminCommand adminCommand = new AdminCommand(); List<string> list = Tokens(text); if (list.Count == 0) { return adminCommand; } adminCommand.Verb = list[0].ToLowerInvariant(); adminCommand.Args.AddRange(list.GetRange(1, list.Count - 1)); return adminCommand; } public static List<string> Tokens(string text) { List<string> list = new List<string>(); StringBuilder stringBuilder = new StringBuilder(); bool flag = false; bool flag2 = false; string text2 = text ?? ""; foreach (char c in text2) { if (c == '"') { flag = !flag; flag2 = true; } else if (!flag && char.IsWhiteSpace(c)) { if (flag2) { list.Add(stringBuilder.ToString()); stringBuilder.Clear(); flag2 = false; } } else { stringBuilder.Append(c); flag2 = true; } } if (flag2) { list.Add(stringBuilder.ToString()); } return list; } public static bool TryId(string argument, out string id) { string text = (argument ?? "").Trim(); id = ((text.Length > 1 && text[0] == '@') ? text.Substring(1).Trim() : null); if (id == "") { id = null; } return id != null; } } public sealed class AdminReply { public readonly int Code; public readonly string Text; public AdminReply(int code, string text) { Code = code; Text = text ?? ""; } } public static class AdminProtocol { public const int Version = 1; public const string RequestRpc = "Sansara_Admin"; public const string ReplyRpc = "Sansara_AdminReply"; public const int MaxLine = 256; public const int MaxReply = 4000; public const int Done = 0; public const int Denied = 1; public const int NoModule = 2; public const int Failed = 3; public const int Invalid = 4; public const int MaxLogText = 300; public static string Truncate(string text) { string text2 = text ?? ""; if (text2.Length > 4000) { return text2.Substring(0, 3999) + "…"; } return text2; } public static AdminReply Check(int protocol, string module, string line) { if (protocol != 1) { return new AdminReply(4, $"неверный запрос: обновите Sansara Admin (протокол сервера {1})"); } if (module == null || line == null) { return new AdminReply(4, "неверный запрос"); } if (Shown(module, line).Length > 256) { return new AdminReply(4, $"строка длиннее {256} символов"); } return null; } public static string Shown(string module, string line) { string text = (module ?? "").Trim(); if (!(text == "")) { return (text + " " + (line ?? "").Trim()).Trim(); } return "help"; } public static string ForLog(string s) { char[] array = (s ?? "").ToCharArray(); for (int i = 0; i < array.Length; i++) { if (char.IsControl(array[i])) { array[i] = ' '; } } if (array.Length > 300) { return new string(array, 0, 300); } return new string(array); } public static string FirstLine(string text) { string text2 = (text ?? "").Replace("\r", ""); int num = text2.IndexOf('\n'); if (num >= 0) { return text2.Substring(0, num); } return text2; } public static string HelpLine(string module, IEnumerable<string> verbs) { return module + ": " + string.Join(", ", verbs); } public static Dictionary<string, string[]> ParseHelp(string text) { Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.Ordinal); string[] array = (text ?? "").Replace("\r", "").Split(new char[1] { '\n' }); foreach (string text2 in array) { int num = text2.IndexOf(':'); if (num <= 0) { continue; } string text3 = text2.Substring(0, num).Trim(); if (IsName(text3)) { dictionary[text3] = (from v in text2.Substring(num + 1).Split(new char[1] { ',' }) select v.Trim()).Where(IsName).ToArray(); } } return dictionary; } public static bool IsName(string s) { if (!string.IsNullOrEmpty(s)) { return s.All((char c) => c >= 'a' && c <= 'z'); } return false; } public static string Unknown(string verb, IEnumerable<string> verbs) { return (string.IsNullOrEmpty(verb) ? "укажите действие" : ("неизвестное действие «" + verb + "»")) + "; есть: " + string.Join(", ", verbs); } } public sealed class AdminRegistry { public const string SharedKey = "sansara.Admin.v1"; public static readonly AdminRegistry Shared = new AdminRegistry("sansara.Admin.v1"); private readonly string _key; private Dictionary<string, Tuple<string[], Func<string, string, string, string>>> Table { get { object data = AppDomain.CurrentDomain.GetData(_key); if (data == null) { Dictionary<string, Tuple<string[], Func<string, string, string, string>>> dictionary = new Dictionary<string, Tuple<string[], Func<string, string, string, string>>>(StringComparer.Ordinal); AppDomain.CurrentDomain.SetData(_key, dictionary); return dictionary; } return (data as Dictionary<string, Tuple<string[], Func<string, string, string, string>>>) ?? throw new InvalidOperationException("данные " + _key + " другого формата"); } } public string Owner => (AppDomain.CurrentDomain.GetData(_key + ".owner") as string) ?? ""; public AdminRegistry(string key) { _key = key; } public bool Register(string module, string[] verbs, Func<string, string, string, string> handler) { string text = (module ?? "").Trim().ToLowerInvariant(); if (!AdminProtocol.IsName(text)) { throw new ArgumentException("имя модуля — латинские буквы: " + module); } string[] array = verbs ?? new string[0]; foreach (string text2 in array) { if (!AdminProtocol.IsName(text2)) { throw new ArgumentException("глагол — латинские буквы: " + text2); } } if (handler == null) { throw new ArgumentNullException("handler"); } Dictionary<string, Tuple<string[], Func<string, string, string, string>>> table = Table; bool result = table.ContainsKey(text); table[text] = Tuple.Create(verbs ?? new string[0], handler); return result; } public bool ClaimOwner(string assembly) { if (string.IsNullOrEmpty(assembly)) { throw new ArgumentException("пустое имя сборки", "assembly"); } string name = _key + ".owner"; if (!(AppDomain.CurrentDomain.GetData(name) is string text)) { AppDomain.CurrentDomain.SetData(name, assembly); return true; } return text == assembly; } public List<string> Modules() { return Table.Keys.OrderBy<string, string>((string k) => k, StringComparer.Ordinal).ToList(); } public string HelpText() { Dictionary<string, Tuple<string[], Func<string, string, string, string>>> table = Table; if (table.Count != 0) { return string.Join("\n", from k in Modules() select AdminProtocol.HelpLine(k, table[k].Item1)); } return "на сервере нет модулей Sansara Admin"; } public AdminReply Dispatch(string module, string line, string adminName, string adminId, out Exception error) { error = null; try { string text = (module ?? "").Trim().ToLowerInvariant(); if (text == "") { return new AdminReply(0, AdminProtocol.Truncate(HelpText())); } Dictionary<string, Tuple<string[], Func<string, string, string, string>>> table = Table; if (!table.TryGetValue(text, out var value)) { return new AdminReply(2, "модуля " + text + " нет на сервере; есть: " + ((table.Count == 0) ? "ничего" : string.Join(", ", Modules()))); } string text2 = value.Item2((line ?? "").Trim(), adminName ?? "", adminId ?? ""); return new AdminReply(0, AdminProtocol.Truncate(string.IsNullOrEmpty(text2) ? "готово" : text2)); } catch (Exception ex) { return new AdminReply(3, AdminProtocol.Truncate("ошибка: " + (error = ex).Message)); } } } } namespace Sansara.Clock { public static class RealClock { private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public const double WorldClockLimit = 1000000000.0; public static double Now => (DateTime.UtcNow - Epoch).TotalSeconds; public static bool IsWorldTime(double t) { if (t > 0.0) { return t < 1000000000.0; } return false; } public static double FromWorld(double t, double worldNow, double realNow) { if (!IsWorldTime(t)) { return t; } return realNow - (worldNow - t); } } } namespace Sansara.Chronicle { public sealed class ChroniclePlayer { public long Id { get; } public string Name { get; } public ChroniclePlayer(long id, string name) { Id = id; Name = name ?? ""; } } public sealed class ChronicleEntry { public double Time; public string Date = ""; public string Kind = ""; public string Id = ""; public string Result = ""; public List<ChroniclePlayer> Players = new List<ChroniclePlayer>(); public const string Victory = "victory"; public bool IsVictory { get { if (!(Result == "victory")) { return Result == "found"; } return true; } } public static string Clean(string s) { return new string((s ?? "").Where((char c) => c != '\t' && c != '\n' && c != '\r' && c != '|' && c != '=').ToArray()).Trim(); } public string ToLine() { string text = string.Join("|", Players.Select((ChroniclePlayer p) => p.Id.ToString(CultureInfo.InvariantCulture) + "=" + Clean(p.Name))); return string.Join("\t", Time.ToString("R", CultureInfo.InvariantCulture), Clean(Date), Clean(Kind), Clean(Id), Clean(Result), text); } public static ChronicleEntry Parse(string line) { if (string.IsNullOrWhiteSpace(line) || line.TrimStart(Array.Empty<char>()).StartsWith("#", StringComparison.Ordinal)) { return null; } string[] array = line.TrimEnd(new char[1] { '\r' }).Split(new char[1] { '\t' }); if (array.Length < 5 || !double.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || array[2].Trim() == "" || array[3].Trim() == "") { return null; } ChronicleEntry chronicleEntry = new ChronicleEntry { Time = result, Date = array[1].Trim(), Kind = array[2].Trim(), Id = array[3].Trim(), Result = array[4].Trim() }; if (array.Length > 5) { string[] array2 = array[5].Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array2) { int num = text.IndexOf('='); long.TryParse((num > 0) ? text.Substring(0, num) : "0", NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2); chronicleEntry.Players.Add(new ChroniclePlayer(result2, (num >= 0) ? text.Substring(num + 1) : text)); } } return chronicleEntry; } public static List<ChronicleEntry> ParseAll(string text, out int bad) { List<ChronicleEntry> list = new List<ChronicleEntry>(); bad = 0; string[] array = (text ?? "").Split(new char[1] { '\n' }); foreach (string text2 in array) { ChronicleEntry chronicleEntry = Parse(text2); if (chronicleEntry != null) { list.Add(chronicleEntry); } else if (!string.IsNullOrWhiteSpace(text2) && !text2.TrimStart(Array.Empty<char>()).StartsWith("#", StringComparison.Ordinal)) { bad++; } } return list; } } public static class ChronicleFile { public static string PathFor(string configDir, string world) { string text = string.Concat((world ?? "world").Split(Path.GetInvalidFileNameChars())); return Path.Combine(configDir, "sansara.Chronicle", text + ".tsv"); } public static void Append(string path, ChronicleEntry entry) { Directory.CreateDirectory(Path.GetDirectoryName(path)); File.AppendAllText(path, entry.ToLine() + "\n", new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); } public static List<ChronicleEntry> Read(string path, out int bad) { bad = 0; if (!File.Exists(path)) { return new List<ChronicleEntry>(); } return ChronicleEntry.ParseAll(File.ReadAllText(path), out bad); } } public sealed class FirstVictory { public string Id; public string Date; public List<string> Names = new List<string>(); public int Count; } public sealed class Leader { public string Name; public int Count; } public static class ChronicleStats { public static Dictionary<string, FirstVictory> Firsts(IEnumerable<ChronicleEntry> all, string kind) { Dictionary<string, FirstVictory> dictionary = new Dictionary<string, FirstVictory>(); foreach (ChronicleEntry item in from e in all where e.Kind == kind && e.IsVictory orderby e.Time select e) { if (!dictionary.TryGetValue(item.Id, out var value)) { value = new FirstVictory { Id = item.Id, Date = item.Date, Names = (from p in item.Players select p.Name into n where n != "" select n).Distinct().ToList() }; dictionary[item.Id] = value; } value.Count++; } return dictionary; } public static List<Leader> Leaders(IEnumerable<ChronicleEntry> all, string kind) { Dictionary<long, int> dictionary = new Dictionary<long, int>(); Dictionary<long, string> names = new Dictionary<long, string>(); foreach (ChronicleEntry item in from e in all where e.Kind == kind && e.IsVictory orderby e.Time select e) { foreach (ChroniclePlayer item2 in from p in item.Players group p by (p.Id == 0L) ? p.Name : p.Id.ToString() into g select g.First()) { long key = ((item2.Id != 0L) ? item2.Id : item2.Name.GetHashCode()); dictionary[key] = ((!dictionary.TryGetValue(key, out var value)) ? 1 : (value + 1)); if (item2.Name != "") { names[key] = item2.Name; } } } string value2; return (from kv in dictionary select new Leader { Name = (names.TryGetValue(kv.Key, out value2) ? value2 : "?"), Count = kv.Value } into l orderby l.Count descending select l).ThenBy<Leader, string>((Leader l) => l.Name, StringComparer.Ordinal).ToList(); } public static string Names(IList<string> names, int max) { if (names.Count == 0) { return ""; } for (int num = names.Count; num >= 1; num--) { string text = string.Join(", ", names.Take(num)) + ((num < names.Count) ? $" +{names.Count - num}" : ""); if (text.Length <= max) { return text; } } string text2 = names[0]; if (text2.Length <= max) { return text2; } return text2.Substring(0, Math.Max(1, max - 1)) + "…"; } } } namespace Treasure { internal static class Hoard { private const float WorldRadius = 9800f; private static readonly Random Rng = new Random(); private static readonly Dictionary<long, int> LastLevel = new Dictionary<long, int>(); private static readonly Dictionary<long, double> LastTold = new Dictionary<long, double>(); private static string _path; private static TreasureState _state; private static float _nextSign; private static bool _signMissingReported; private static string _lastWait = ""; internal static readonly string[] Verbs = new string[5] { "status", "help", "hide", "rehide", "remove" }; private const string HelpText = "status — спрятан ли клад, где сундук, загадка, когда следующий\nhide — спрятать клад сейчас, если его нет\nrehide — убрать текущий клад и сразу спрятать новый\nremove — тихо убрать клад; следующий через IntervalDays"; public static void Load() { //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) string text = Path.Combine(Paths.ConfigPath, "sansara.Treasure"); Directory.CreateDirectory(text); string text2 = string.Concat(ZNet.instance.GetWorldName().Split(Path.GetInvalidFileNameChars())); _path = Path.Combine(text, text2 + ".treasure"); _state = (File.Exists(_path) ? TreasureState.Parse(File.ReadAllText(_path)) : new TreasureState()); if (RealClock.IsWorldTime(_state.NextAt) || RealClock.IsWorldTime(_state.ExpiresAt)) { double timeSeconds = ZNet.instance.GetTimeSeconds(); double now = RealClock.Now; _state.NextAt = RealClock.FromWorld(_state.NextAt, timeSeconds, now); _state.ExpiresAt = RealClock.FromWorld(_state.ExpiresAt, timeSeconds, now); Save(); Plugin.Log.LogInfo((object)"Клад: сроки переведены на реальное время."); } double now2 = World.Now; if (_state.NextAt <= 0.0) { _state.NextAt = now2 + Math.Max(0.01, Plugin.FirstDelayHours.Value) * 3600.0; Save(); } if (_state.Active && Riddle.IsOld(_state.Riddle)) { Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor((float)_state.X, (float)_state.Y, (float)_state.Z); string riddle = _state.Riddle; _state.Riddle = (World.Landmark(val, Plugin.LandmarkRadius.Value, Plugin.LandmarkTable, out var name, out var at) ? Riddle.Text(_state.Tier, name, at.x, at.z, val.x, val.z) : Riddle.Text(_state.Tier, null, 0.0, 0.0, val.x, val.z)); Save(); Plugin.Log.LogInfo((object)("Клад: загадка дополнена — «" + riddle + "» → «" + _state.Riddle + "».")); } List<string> list = World.UnnamedLocations(Plugin.LandmarkTable); Plugin.Log.LogInfo((object)($"Клад: ориентиров в таблице {Plugin.LandmarkTable.Count}, строк добычи {Plugin.LootLines.Count}; " + string.Format("локации мира без названия ({0}): {1}.", list.Count, string.Join(", ", list.Take(60))))); Plugin.Log.LogInfo((object)(_state.Active ? $"Клад: спрятан ({Biomes.Names[_state.Tier]}), «{_state.Riddle}», осталось {(_state.ExpiresAt - now2) / 86400.0:0.#} дн." : $"Клад: следующий через {(_state.NextAt - now2) / 3600.0:0.#} ч.")); } private static void Save() { File.WriteAllText(_path, _state.Serialize()); } public static void Tick() { //IL_011e: Unknown result type (might be due to invalid IL or missing references) double now = World.Now; if (Time.time >= _nextSign) { _nextSign = Time.time + 10f; UpdateSign(now); } if (!_state.Active) { if (now >= _state.NextAt) { TryHide(now); } return; } ZDO val = Chest(); long playerId; string name; long peerUid; if (val == null) { Plugin.Log.LogWarning((object)"Клад: сундука клада больше нет в мире (убран не модом) — клад пропал."); End(now, "Клад пропал. Новый — через неделю.", "Клад пропал · " + Today()); } else if ((val.GetInt(ZDOVars.s_inUse, 0) == 1 || Signature(World.Items(val)) != _state.Sig) && World.Finder(val, out playerId, out name, out peerUid)) { Found(now, playerId, name, peerUid); } else if (now >= _state.ExpiresAt) { World.Destroy(val); Plugin.Log.LogInfo((object)"Клад: срок вышел, клад не найден — сундук убран."); End(now, "Клад так и не нашли — он пропал.", "Не найден · " + Today()); } else { Heat(now, new Vector3((float)_state.X, (float)_state.Y, (float)_state.Z)); } } private static string Tag(double hiddenAt) { return ((long)hiddenAt).ToString(CultureInfo.InvariantCulture); } private static ZDO Chest() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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) ZDO zDO = ZDOMan.instance.GetZDO(new ZDOID(_state.ChestUser, _state.ChestId)); if (zDO != null && zDO.GetString("sansara.Treasure.tag", "") == Tag(_state.HiddenAt)) { return zDO; } Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor((float)_state.X, (float)_state.Y, (float)_state.Z); zDO = World.Tagged(val, 1, "sansara.Treasure.tag", Tag(_state.HiddenAt)).FirstOrDefault() ?? World.ChestAt(val, Plugin.ChestPrefab.Value); if (zDO != null) { _state.ChestUser = ((ZDOID)(ref zDO.m_uid)).UserID; _state.ChestId = ((ZDOID)(ref zDO.m_uid)).ID; if (zDO.GetString("sansara.Treasure.tag", "") == "") { zDO.Set("sansara.Treasure.tag", Tag(_state.HiddenAt)); } Save(); Plugin.Log.LogInfo((object)$"Клад: сундук найден на месте после перезапуска ({zDO.m_uid})."); } return zDO; } public static void CheckCommand() { string path = Path.Combine(Paths.ConfigPath, "sansara.Treasure.command"); if (File.Exists(path)) { string text; try { text = File.ReadAllText(path).Trim(); File.Delete(path); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Клад: файл команды не прочитан: " + ex.Message)); return; } Plugin.Log.LogInfo((object)("Клад: команда «" + text + "».")); Plugin.Log.LogInfo((object)("Клад: " + Handle(text, "файл команд", "").Replace("\n", "; "))); } } public static string Handle(string line, string admin, string adminId) { AdminCommand adminCommand = AdminCommand.Parse(line); switch (adminCommand.Verb) { case "help": return "status — спрятан ли клад, где сундук, загадка, когда следующий\nhide — спрятать клад сейчас, если его нет\nrehide — убрать текущий клад и сразу спрятать новый\nremove — тихо убрать клад; следующий через IntervalDays"; case "status": return Status(); case "hide": case "rehide": case "remove": if (!Plugin.Enabled.Value) { return "модуль выключен в настройках (Enabled = false)"; } if (_state != null) { return Command(adminCommand.Verb); } return "клад ещё не запущен: мир загружается"; default: return AdminProtocol.Unknown(adminCommand.Verb, Verbs); } } private static string Status() { //IL_0099: Unknown result type (might be due to invalid IL or missing references) string text = (Plugin.Enabled.Value ? "" : "выключен в настройках; "); if (_state == null) { if (Plugin.Enabled.Value) { return "клад ещё не запущен: мир загружается"; } return "модуль выключен в настройках (Enabled = false)"; } double now = World.Now; if (_state.Active) { Vector3 p = default(Vector3); ((Vector3)(ref p))..ctor((float)_state.X, (float)_state.Y, (float)_state.Z); return $"{text}спрятан ({Biomes.Names[_state.Tier]}), сундук в {Round(p)}, пропадёт через {Math.Max(0.0, (_state.ExpiresAt - now) / 86400.0):0.#} дн.\n" + "загадка: " + _state.Riddle; } string text2 = $"{text}не спрятан; следующий через {Math.Max(0.0, (_state.NextAt - now) / 3600.0):0.#} ч"; if (!(_state.Last != "")) { return text2; } return text2 + "\nпрошлый: " + _state.Last; } private static string Command(string verb) { //IL_01ce: Unknown result type (might be due to invalid IL or missing references) double now = World.Now; if (verb == "hide" && _state.Active) { return "клад уже спрятан — для нового используйте rehide"; } string text = ""; if (verb == "rehide" || verb == "remove") { if (_state.Active) { ZDO val = Chest(); if (val != null) { World.Destroy(val); } text = ((val != null) ? "сундук удалён" : "сундука не было"); Plugin.Log.LogInfo((object)("Клад: прежний клад убран по команде (" + text + ").")); } else { text = "клада не было"; } _state = new TreasureState { NextAt = now + Math.Max(0.01, Plugin.IntervalDays.Value) * 86400.0, Last = _state.Last }; Save(); _nextSign = 0f; if (verb == "remove") { return $"клад убран ({text}); следующий через {Plugin.IntervalDays.Value:0.##} дн."; } } double nextAt = _state.NextAt; TryHide(now); if (!_state.Active) { _state.NextAt = nextAt; Save(); return "клад не спрятан: " + _lastWait + "; следующий — по расписанию" + ((text != "") ? (" (прежний: " + text + ")") : ""); } Vector3 p = default(Vector3); ((Vector3)(ref p))..ctor((float)_state.X, (float)_state.Y, (float)_state.Z); return "спрятан (" + Biomes.Names[_state.Tier] + "), сундук в " + Round(p) + "\nзагадка: " + _state.Riddle; } private static void Heat(double now, Vector3 pos) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) foreach (World.PlayerInfo item in from p in World.Players() where p.PlayerId != 0 select p) { int num = Treasure.Core.Heat.Level(World.DistanceXZ(item.Pos, pos), Plugin.WarmRadius.Value, Plugin.HotRadius.Value, Plugin.CloseRadius.Value); int value; int lastLevel = (LastLevel.TryGetValue(item.PlayerId, out value) ? value : 0); double value2; double lastAt = (LastTold.TryGetValue(item.PlayerId, out value2) ? value2 : 0.0); if (Treasure.Core.Heat.ShouldTell(num, lastLevel, now, lastAt, Plugin.HeatCooldownSeconds.Value)) { World.Message(item.Peer.m_uid, Treasure.Core.Heat.Texts[num]); LastTold[item.PlayerId] = now; } LastLevel[item.PlayerId] = num; } } private static void Found(double now, long playerId, string name, long peerUid) { int num = Biomes.OpenTier(new HashSet<string>(ZoneSystem.instance.GetGlobalKeys())); bool flag = Rewards.Give(num, _state.HiddenAt, playerId); Plugin.Log.LogInfo((object)string.Format("Клад: найден — {0} ({1}), уровень наград {2}{3}.", name, playerId, num, flag ? ", награды поставлены" : ", наград нет")); if (!flag) { World.Message(peerUid, "Клад ваш! Награду выдать не удалось — сообщите администратору."); } WriteChronicle(now, playerId, name); End(now, "Клад найден: " + name + "!", "Найден: " + name + " · " + Today()); } private static void End(double now, string message, string last) { World.MessageAll(message); World.Chat(message); _state = new TreasureState { NextAt = now + Math.Max(0.01, Plugin.IntervalDays.Value) * 86400.0, Last = last }; LastLevel.Clear(); LastTold.Clear(); Save(); _nextSign = 0f; Plugin.Log.LogInfo((object)$"Клад: следующий через {Plugin.IntervalDays.Value:0.##} дн."); } private static void UpdateSign(double now) { ZDO val = World.HintSign(); if (val == null) { if (!_signMissingReported) { _signMissingReported = true; Plugin.Log.LogInfo((object)"Клад: табличка Чертога славы (Portal Hall 0.2.0+, роль treasure.sign) не найдена — загадка только в чате."); } return; } string text; if (_state.Active) { text = Riddle.ForSign(_state.Riddle); } else { double num = Math.Max(0.0, (_state.NextAt - now) / 86400.0); string text2 = ((num < 1.0) ? "Новый клад — скоро" : $"Новый клад — через {Math.Ceiling(num):0} дн."); text = ((_state.Last != "") ? (_state.Last + "\n" + text2) : text2); } World.SetSign(val, text); } private static void Wait(double now, double seconds, string why) { _state.NextAt = now + seconds; if (why != _lastWait) { _lastWait = why; Plugin.Log.LogInfo((object)("Клад: откладывается — " + why + ".")); } } private static void TryHide(double now) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_027c: 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_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_035e: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_049e: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Unknown result type (might be due to invalid IL or missing references) //IL_04a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) int num = Biomes.OpenTier(new HashSet<string>(ZoneSystem.instance.GetGlobalKeys())); int num2 = Biomes.PickTier(num, Plugin.MaxBiomeTier.Value, Rng); List<World.PlayerInfo> list = World.Players(); Vector3 val = Vector3.zero; string name = null; Vector3 at = Vector3.zero; bool flag = false; Vector3 val2 = Vector3.zero; float num3 = 9800f; if (Plugin.SearchRadius.Value > 0f) { if (list.Count == 0) { Wait(now, 60.0, "нет игроков (SearchRadius > 0)"); return; } val2 = list[Rng.Next(list.Count)].Pos; num3 = Plugin.SearchRadius.Value; } for (int i = 0; i < 4000 && !flag; i++) { double num4 = (double)num3 * Math.Sqrt(Rng.NextDouble()); double num5 = Rng.NextDouble() * 2.0 * Math.PI; Vector3 p = new Vector3(val2.x + (float)(num4 * Math.Cos(num5)), 0f, val2.z + (float)(num4 * Math.Sin(num5))); int num6 = Biomes.TierOf(World.BiomeAt(p)); bool num7; if (!(Plugin.SearchRadius.Value > 0f)) { num7 = num6 != num2; } else { if (num6 < 1) { continue; } num7 = num6 > Math.Min(num, Plugin.MaxBiomeTier.Value); } if (!num7 && World.DryLand(p.x, p.z, out var height)) { p.y = height; if (!list.Any((World.PlayerInfo o) => World.DistanceXZ(o.Pos, p) < Plugin.PlayerClearance.Value) && !World.NearPlayerThings(p) && !World.NearTrial(p) && (World.Landmark(p, Plugin.LandmarkRadius.Value, Plugin.LandmarkTable, out name, out at) || i >= 3000)) { val = p; num2 = num6; flag = true; } } } if (!flag) { Wait(now, 600.0, "не нашлось места в биоме " + Biomes.Names[num2]); return; } List<KeyValuePair<string, int>> list2 = Loot.Roll(Plugin.LootLines, num2, Rng); Vector3 val3 = val + Vector3.down * Mathf.Max(0f, Plugin.ChestDepth.Value); int added; ZDO val4 = World.CreateChest(Plugin.ChestPrefab.Value, val3, list2, Tag(now), out added); if (val4 == null) { Wait(now, 3600.0, "префаб " + Plugin.ChestPrefab.Value + " не сундук"); return; } string text = ((name != null) ? Riddle.Text(num2, name, at.x, at.z, val.x, val.z) : Riddle.Text(num2, null, 0.0, 0.0, val.x, val.z)); _state = new TreasureState { NextAt = _state.NextAt, Active = true, ChestUser = ((ZDOID)(ref val4.m_uid)).UserID, ChestId = ((ZDOID)(ref val4.m_uid)).ID, X = val3.x, Y = val3.y, Z = val3.z, Tier = num2, HiddenAt = now, ExpiresAt = now + Math.Max(0.01, Plugin.LifetimeDays.Value) * 86400.0, Riddle = text, Sig = Signature(World.Items(val4)), Last = _state.Last }; _lastWait = ""; Save(); _nextSign = 0f; Plugin.Log.LogInfo((object)(string.Format("Клад: спрятан в {0} ({1}), предметов {2} ({3}); ", Round(val3), Biomes.Names[num2], added, string.Join(", ", list2.Select((KeyValuePair<string, int> l) => l.Key + "×" + l.Value))) + "загадка «" + text + "»" + ((name != null) ? $", ориентир в {Round(at)} ({World.DistanceXZ(at, val):0} м)." : "."))); World.MessageAll("Спрятан новый клад! Загадка — в чате и в Чертоге славы."); World.Chat("Загадка клада: " + Riddle.ForChat(text) + "."); } private static void WriteChronicle(double now, long playerId, string name) { try { ChronicleEntry entry = new ChronicleEntry { Time = ZNet.instance.GetTimeSeconds(), Date = DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture), Kind = "treasure", Id = Biomes.Names[_state.Tier], Result = "victory", Players = new List<ChroniclePlayer> { new ChroniclePlayer(playerId, name) } }; ChronicleFile.Append(ChronicleFile.PathFor(Paths.ConfigPath, ZNet.instance.GetWorldName()), entry); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Клад: летопись не записана: " + ex.Message)); } } private static string Signature(byte[] bytes) { if (bytes == null) { return "0:0"; } uint num = 2166136261u; foreach (byte b in bytes) { num = (num ^ b) * 16777619; } return bytes.Length.ToString(CultureInfo.InvariantCulture) + ":" + num.ToString("x8", CultureInfo.InvariantCulture); } private static string Today() { return DateTime.Now.ToString("dd.MM", CultureInfo.InvariantCulture); } private static string Round(Vector3 p) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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) return $"({p.x:0}, {p.y:0}, {p.z:0})"; } } [BepInPlugin("sansara.Treasure", "Treasure", "0.1.6")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "sansara.Treasure"; public const string Name = "Treasure"; public const string Version = "0.1.6"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> IntervalDays; internal static ConfigEntry<float> LifetimeDays; internal static ConfigEntry<float> FirstDelayHours; internal static ConfigEntry<string> ChestPrefab; internal static ConfigEntry<float> ChestDepth; internal static ConfigEntry<int> MaxBiomeTier; internal static ConfigEntry<float> LandmarkRadius; internal static ConfigEntry<float> PlayerClearance; internal static ConfigEntry<float> SearchRadius; internal static ConfigEntry<float> WarmRadius; internal static ConfigEntry<float> HotRadius; internal static ConfigEntry<float> CloseRadius; internal static ConfigEntry<float> HeatCooldownSeconds; internal static ConfigEntry<string> LootText; internal static ConfigEntry<string> LandmarksText; internal static readonly ConfigEntry<string>[] RewardPool = new ConfigEntry<string>[9]; internal static readonly ConfigEntry<int>[] RewardCount = new ConfigEntry<int>[9]; internal static List<LootLine> LootLines = new List<LootLine>(); internal static List<KeyValuePair<string, string>> LandmarkTable = new List<KeyValuePair<string, string>>(); private float _nextTick; private bool _started; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Клады."); IntervalDays = ((BaseUnityPlugin)this).Config.Bind<float>("General", "IntervalDays", 7f, "Через сколько дней (работы сервера) после находки или пропажи прячется новый клад."); LifetimeDays = ((BaseUnityPlugin)this).Config.Bind<float>("General", "LifetimeDays", 7f, "Сколько дней клад ждёт; не найден — пропадает."); FirstDelayHours = ((BaseUnityPlugin)this).Config.Bind<float>("General", "FirstDelayHours", 1f, "Через сколько часов после первого запуска мода прячется первый клад."); ChestPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ChestPrefab", "TreasureChest_meadows_buried", "Сундук клада (префаб с Container; закопанный клад игры виден Вишбону)."); ChestDepth = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ChestDepth", 0.3f, "Насколько сундук ниже поверхности земли, м."); MaxBiomeTier = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxBiomeTier", 7, "Самый поздний биом для клада (1 Луга … 7 Пепельные земли); выбирается из открытых, поздние чаще."); LandmarkRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "LandmarkRadius", 600f, "Ориентир для загадки ищется в этом радиусе от клада, м; нет — «от центра мира»."); PlayerClearance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PlayerClearance", 300f, "Клад не прячется ближе этого к игрокам, м."); SearchRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SearchRadius", 0f, "0 — клад прячется по всему миру; больше 0 — не дальше этого от случайного игрока в игре, м (для проверок)."); WarmRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Heat", "WarmRadius", 300f, "«Тепло», м."); HotRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Heat", "HotRadius", 100f, "«Горячо», м."); CloseRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Heat", "CloseRadius", 30f, "«Совсем рядом», м."); HeatCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Heat", "CooldownSeconds", 30f, "Не чаще раза в столько секунд на игрока (если не стало ближе)."); LootText = ((BaseUnityPlugin)this).Config.Bind<string>("Loot", "Items", "0:Coins:200:400, 0:Ruby:2:4, 0:AmberPearl:2:5, 0:Amber:3:6, 1:DeerHide:10:20, 2:Copper:20:40, 2:Tin:20:30, 3:Iron:20:40, 3:SilverNecklace:1:2, 4:Silver:20:40, 4:SilverNecklace:2:3, 5:BlackMetal:20:40, 5:SilverNecklace:2:4, 6:Eitr:10:20, 6:SilverNecklace:3:5, 7:FlametalNew:10:20, 7:SilverNecklace:3:5", "уровень:Предмет:от:до через запятую; уровень 0 — в любом кладе, N — в кладе биома уровня N."); LandmarksText = ((BaseUnityPlugin)this).Config.Bind<string>("Riddle", "Landmarks", "StartTemple=древнего святилища, Eikthyrnir=алтаря Эйктюра, GDKing=алтаря Древнего, Bonemass=алтаря Массы костей, Dragonqueen=алтаря Модер, GoblinKing=алтаря Яглута, Mistlands_DvergrBossEntrance=логова Королевы, FaderLocation=алтаря Фадера, Vendor_BlackForest=лавки Халдора, Hildir_camp=лагеря Хильдир, BogWitch=хижины ведьмы, SunkenCrypt=затопленного склепа, Crypt=склепа, TrollCave=пещеры тролля, StoneTowerRuins=каменной башни, StoneHenge=каменного круга, ShipWreck=обломков корабля, WoodHouse=заброшенного дома, WoodFarm=заброшенной фермы, WoodVillage=заброшенной деревни, MountainCave=горной пещеры, DrakeNest=гнезда дрейков, AbandonedLogCabin=бревенчатой хижины, GoblinCamp=лагеря фулингов, TarPit=смоляной ямы, Mistlands_DvergrTownEntrance=поселения двергов, Mistlands_Giant=останков великана, Mistlands_Harbour=гавани двергов, Runestone=рунного камня, Dolmen=дольмена, CharredFortress=крепости обугленных, MorgenHole=норы моргена, Ruin=руин, BearCave=берлоги, AshlandRuins=пепельных руин, CharredRuins=обугленных руин, CharredTowerRuins=обугленной башни, FortressRuins=руин крепости, GoblinHut=хижины фулингов, Greydwarf_camp=стоянки карликов, Grave=могилы, MountainGrave=горной могилы, MountainWell=горного колодца, Hildir_cave=пещеры Хильдир, Hildir_crypt=склепа Хильдир, Hildir_plainsfortress=крепости Хильдир, InfestedTree=заражённого дерева, LumberCamp=лесопилки, Mistlands_Excavation=раскопок двергов, Mistlands_GuardTower=башни двергов, Mistlands_Lighthouse=маяка двергов, Mistlands_Statue=статуи двергов, Mistlands_Swords=мечей великана, Mistlands_Viaduct=виадука двергов, Mistlands_RockSpire=скального шпиля, IcePond=ледяного пруда, StoneCircle=каменного круга, StoneHouse=каменного дома, StoneTower=каменной башни, SwampHut=болотной хижины, SulfurArch=серной арки, PlaceofMystery=таинственного места, Mistlands_RoadPost=дорожного столба, DrakeLorestone=камня дрейков", "ПрефиксЛокации=название в родительном падеже («от склепа») через запятую."); for (int i = 1; i <= 8; i++) { RewardPool[i] = ((BaseUnityPlugin)this).Config.Bind<string>($"Tier{i}", "RewardPool", "", "Награды Event Rewards нашедшему клад этого уровня (уровень — самый поздний открытый биом) через запятую."); RewardCount[i] = ((BaseUnityPlugin)this).Config.Bind<int>($"Tier{i}", "RewardCount", 2, "Наград нашедшему."); } AdminChannel.Register("treasure", Hoard.Verbs, Hoard.Handle, Log); Log.LogInfo((object)"Treasure 0.1.6 загружен."); } private void Update() { AdminChannel.Tick(); if (!Enabled.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null || WorldGenerator.instance == null || Time.time < _nextTick) { return; } _nextTick = Time.time + 1f; try { if (!_started) { _started = true; List<string> list = new List<string>(); LootLines = Loot.Parse(LootText.Value, list); LandmarkTable = Landmarks.Parse(LandmarksText.Value, list); list.ForEach(delegate(string e) { Log.LogWarning((object)("Клад: настройки — " + e)); }); Hoard.Load(); } Hoard.CheckCommand(); Hoard.Tick(); } catch (Exception ex) { Log.LogError((object)("Клад: ошибка: " + ex)); } } } internal static class Rewards { private static readonly Random Rng = new Random(); private static MethodInfo _enqueue; private static bool _checked; public static bool Give(int tier, double startedAt, long playerId) { if (tier < 1 || tier > 8) { return false; } string[] array = Plugin.RewardPool[tier].Value.Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { Plugin.Log.LogWarning((object)$"Клад: награда игроку {playerId} не выдана — RewardPool уровня {tier} пуст."); return false; } MethodInfo methodInfo = Api(); if (methodInfo == null) { Plugin.Log.LogError((object)$"Клад: награда игроку {playerId} не выдана — нет Event Rewards (ExternalRewards.Enqueue)."); return false; } bool result = false; for (int i = 1; i <= Math.Max(1, Plugin.RewardCount[tier].Value); i++) { string text = array[Rng.Next(array.Length)]; string text2 = $"treasure:{(long)startedAt}" + ((i > 1) ? $"/{i}" : ""); string text3 = (string)methodInfo.Invoke(null, new object[3] { playerId, text, text2 }); if (text3 != null) { Plugin.Log.LogError((object)$"Клад: награда {text} игроку {playerId} не выдана: {text3}."); continue; } result = true; Plugin.Log.LogInfo((object)$"Клад: игроку {playerId} поставлена награда {text} ({text2})."); } return result; } private static MethodInfo Api() { if (!_checked) { _checked = true; Type type = AccessTools.TypeByName("EventRewards.ExternalRewards"); _enqueue = ((type == null) ? null : AccessTools.Method(type, "Enqueue", new Type[3] { typeof(long), typeof(string), typeof(string) }, (Type[])null)); } return _enqueue; } } internal static class World { private enum Kind { Other, Player, Piece, Tombstone, ItemStand, Character, Terrain } internal struct PlayerInfo { public ZNetPeer Peer; public long PlayerId; public Vector3 Pos; } private const string RoleKey = "sansara.PortalHall.role"; public const string TagKey = "sansara.Treasure.tag"; private static readonly Dictionary<int, Kind> Kinds = new Dictionary<int, Kind>(); private static readonly List<ZDO> Found = new List<ZDO>(); private static List<ZDO>[] _bySector; private static FieldInfo _ewpSkip; private static bool _ewpChecked; private static readonly PlatformUserID ServerUser = new PlatformUserID("Steam_0"); public static double Now => RealClock.Now; private static FieldInfo EwpSkip { get { if (!_ewpChecked) { _ewpChecked = true; Type type = AccessTools.TypeByName("ExpandWorld.Prefab.HandleCreated"); _ewpSkip = ((type == null) ? null : AccessTools.Field(type, "Skip")); } return _ewpSkip; } } public static List<PlayerInfo> Players() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) List<PlayerInfo> list = new List<PlayerInfo>(); foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { ZDO val = (((ZDOID)(ref peer.m_characterID)).IsNone() ? null : ZDOMan.instance.GetZDO(peer.m_characterID)); if (val != null) { list.Add(new PlayerInfo { Peer = peer, PlayerId = val.GetLong(ZDOVars.s_playerID, 0L), Pos = val.GetPosition() }); } } return list; } public static float DistanceXZ(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Utils.DistanceXZ(a, b); } public static string BiomeAt(Vector3 pos) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) return ((object)WorldGenerator.instance.GetBiome(pos)/*cast due to .constrained prefix*/).ToString(); } public static bool DryLand(float x, float z, out float height) { height = WorldGenerator.instance.GetHeight(x, z); float num = ZoneSystem.instance.m_waterLevel + 2f; if (height < num) { return false; } for (int i = 0; i < 8; i++) { float num2 = (float)i * (float)Math.PI / 4f; if (WorldGenerator.instance.GetHeight(x + 15f * Mathf.Cos(num2), z + 15f * Mathf.Sin(num2)) < num) { return false; } } return true; } public static bool NearPlayerThings(Vector3 pos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) Vector2s zone = ZoneSystem.GetZone(pos); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { foreach (ZDO item in ZoneObjects(new Vector2s(zone.x + i, zone.y + j))) { switch (KindOf(item.GetPrefab())) { case Kind.Piece: if (item.GetLong(ZDOVars.s_creator, 0L) == 0L) { break; } goto case Kind.Player; case Kind.Player: case Kind.Tombstone: case Kind.Terrain: return true; case Kind.ItemStand: if (item.GetInt(ZDOVars.s_item, 0) != 0) { return true; } break; case Kind.Character: if (item.GetBool(ZDOVars.s_tamed, false)) { return true; } break; } } } } return false; } public static bool NearTrial(Vector3 pos) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) string text = string.Concat(ZNet.instance.GetWorldName().Split(Path.GetInvalidFileNameChars())); string path = Path.Combine(Paths.ConfigPath, "sansara.MinePortal", text + ".slots.txt"); if (!File.Exists(path)) { return false; } Vector2s zone = ZoneSystem.GetZone(pos); string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array2.Length == 3 && int.TryParse(array2[1], out var result) && int.TryParse(array2[2], out var result2) && Math.Abs(zone.x - result) <= 6 && Math.Abs(zone.y - result2) <= 6) { return true; } } return false; } public static bool Landmark(Vector3 pos, float radius, List<KeyValuePair<string, string>> table, out string name, out Vector3 at) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) name = null; at = Vector3.zero; float num = radius; foreach (KeyValuePair<Vector2s, LocationInstance> locationInstance in ZoneSystem.instance.m_locationInstances) { string text = locationInstance.Value.m_location?.m_prefab.Name; if (text == null) { continue; } float num2 = DistanceXZ(locationInstance.Value.m_position, pos); if (!(num2 > num) && !(num2 < 20f)) { string text2 = Landmarks.NameOf(table, text); if (text2 != null) { num = num2; name = text2; at = locationInstance.Value.m_position; } } } return name != null; } public static List<string> UnnamedLocations(List<KeyValuePair<string, string>> table) { return (from n in (from l in ZoneSystem.instance.m_locationInstances.Values select l.m_location?.m_prefab.Name into n where n != null select n).Distinct() where Landmarks.NameOf(table, n) == null orderby n select n).ToList(); } private static List<ZDO> ZoneObjects(Vector2s zone) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (_bySector == null) { _bySector = (List<ZDO>[])AccessTools.Field(typeof(ZDOMan), "m_objectsBySector").GetValue(ZDOMan.instance); } uint sector = ZoneSystem.SectorToIndex(zone).Sector; List<ZDO> list = ((sector < _bySector.Length) ? _bySector[sector] : null); if (list == null) { return new List<ZDO>(); } return new List<ZDO>(list); } private static Kind KindOf(int hash) { if (!Kinds.TryGetValue(hash, out var value)) { GameObject prefab = ZNetScene.instance.GetPrefab(hash); value = ((!((Object)(object)prefab == (Object)null)) ? (((Object)(object)prefab.GetComponent<Player>() != (Object)null) ? Kind.Player : (((Object)(object)prefab.GetComponent<TombStone>() != (Object)null) ? Kind.Tombstone : (((Object)(object)prefab.GetComponent<TerrainComp>() != (Object)null) ? Kind.Terrain : (((Object)(object)prefab.GetComponent<ItemStand>() != (Object)null && (Object)(object)prefab.GetComponent<Piece>() == (Object)null) ? Kind.ItemStand : (((Object)(object)prefab.GetComponent<Piece>() != (Object)null) ? Kind.Piece : (((Object)(object)prefab.GetComponent<Character>() != (Object)null) ? Kind.Character : Kind.Other)))))) : Kind.Other); Kinds[hash] = value; } return value; } public static ZDO CreateChest(string prefabName, Vector3 pos, List<KeyValuePair<string, int>> items, string tag, out int added) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Expected O, but got Unknown added = 0; GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); Container val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Container>() : null); if ((Object)(object)val == (Object)null) { return null; } Inventory val2 = new Inventory("treasure", (Sprite)null, val.m_width, val.m_height); foreach (KeyValuePair<string, int> item in items) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.Key); if ((Object)(object)itemPrefab == (Object)null || (Object)(object)itemPrefab.GetComponent<ItemDrop>() == (Object)null) { Plugin.Log.LogWarning((object)("Клад: предмета " + item.Key + " нет в игре — пропущен.")); continue; } int num = item.Value; while (num > 0) { int num2 = Mathf.Min(num, itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_maxStackSize); if (!val2.AddItem(itemPrefab, num2)) { Plugin.Log.LogWarning((object)$"Клад: сундук полон — {item.Key} ×{num} не поместились."); break; } num -= num2; added += num2; } } ZNetView component = prefab.GetComponent<ZNetView>(); int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name); object obj = EwpSkip?.GetValue(null); ZDO val3; try { EwpSkip?.SetValue(null, true); val3 = ZDOMan.instance.CreateNewZDO(pos, stableHashCode); } finally { if (obj != null) { EwpSkip.SetValue(null, obj); } } val3.Persistent = component.m_persistent; val3.Type = component.m_type; val3.Distant = component.m_distant; val3.SetPrefab(stableHashCode); val3.SetRotation(Quaternion.Euler(0f, Random.Range(0f, 360f), 0f)); ZPackage val4 = new ZPackage(); val2.Save(val4); val3.Set(ZDOVars.s_items, val4.GetArray()); val3.Set(ZDOVars.s_addedDefaultItems, true); val3.Set("sansara.Treasure.tag", tag); return val3; } public static byte[] Items(ZDO chest) { return chest.GetByteArray(ZDOVars.s_items, (byte[])null); } public static List<ZDO> Tagged(Vector3 around, int zones, string key, string value) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Found.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(around), new SimulationDistance(zones, 0, true), Found, (List<ZDO>)null); return Found.Where((ZDO z) => z.GetString(key, "") == value).ToList(); } public static ZDO ChestAt(Vector3 pos, string prefabName) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) int hash = StringExtensionMethods.GetStableHashCode(prefabName); Found.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(pos), new SimulationDistance(1, 0, true), Found, (List<ZDO>)null); return (from z in Found where z.GetPrefab() == hash && Vector3.Distance(z.GetPosition(), pos) < 2f orderby Vector3.Distance(z.GetPosition(), pos) select z).FirstOrDefault(); } public static void Destroy(ZDO zdo) { zdo.SetOwner(ZDOMan.GetSessionID()); ZDOMan.instance.DestroyZDO(zdo); } public static bool Finder(ZDO chest, out long playerId, out string name, out long peerUid) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) playerId = 0L; name = ""; peerUid = 0L; float num = 10f; foreach (PlayerInfo item in Players()) { if (item.Peer.m_uid == chest.GetOwner()) { playerId = item.PlayerId; name = item.Peer.m_playerName; peerUid = item.Peer.m_uid; return playerId != 0; } float num2 = Vector3.Distance(item.Pos, chest.GetPosition()); if (num2 <= num) { num = num2; playerId = item.PlayerId; name = item.Peer.m_playerName; peerUid = item.Peer.m_uid; } } return playerId != 0; } public static ZDO HintSign() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(10820f, 0f, 300f); Found.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(val), new SimulationDistance(1, 0, true), Found, (List<ZDO>)null); return ((IEnumerable<ZDO>)Found).FirstOrDefault((Func<ZDO, bool>)((ZDO z) => z.GetString("sansara.PortalHall.role", "") == "treasure.sign")); } public static void SetSign(ZDO sign, string text) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) text = "<color=white>" + text + "</color>"; if (!(sign.GetString(ZDOVars.s_text, "") == text)) { sign.SetOwner(ZDOMan.GetSessionID()); sign.Set(ZDOVars.s_text, text); ZDOMan.instance.ForceSendZDO(sign.m_uid); } } public static void MessageAll(string text) { ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ShowMessage", new object[2] { 2, text }); } public static void Message(long peerUid, string text) { ZRoutedRpc.instance.InvokeRoutedRPC(peerUid, "ShowMessage", new object[2] { 2, text }); } public static void Chat(string text) { //IL_0014: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ChatMessage", new object[4] { Vector3.zero, 1, (object)new UserInfo { Name = "Клад", UserId = ServerUser }, text }); } } } namespace Treasure.Core { public static class Biomes { public const int MaxTier = 8; public static readonly string[] Names = new string[9] { "", "Meadows", "BlackForest", "Swamp", "Mountain", "Plains", "Mistlands", "AshLands", "DeepNorth" }; public static readonly string[] Russian = new string[9] { "", "Луга", "Чёрный лес", "Болото", "Горы", "Равнины", "Туманные земли", "Пепельные земли", "Дальний Север" }; public static readonly string[] OpeningKeys = new string[8] { null, "defeated_eikthyr", "defeated_gdking", "defeated_bonemass", "defeated_dragon", "defeated_goblinking", "defeated_queen", "defeated_fader" }; public static int TierOf(string biome) { for (int i = 1; i <= 8; i++) { if (string.Equals(Names[i], biome, StringComparison.OrdinalIgnoreCase)) { return i; } } return 0; } public static int OpenTier(ICollection<string> worldKeys) { int result = 1; for (int i = 2; i <= 8; i++) { if (worldKeys.Contains(OpeningKeys[i - 1])) { result = i; } } return result; } public static int PickTier(int open, int maxTier, Random rng) { int num = Math.Max(1, Math.Min(open, maxTier)); int maxValue = num * (num + 1) / 2; int num2 = rng.Next(maxValue); for (int i = 1; i <= num; i++) { num2 -= i; if (num2 < 0) { return i; } } return num; } } public sealed class LootLine { public int Tier; public string Item = ""; public int Min; public int Max; } public static class Loot { public const string Default = "0:Coins:200:400, 0:Ruby:2:4, 0:AmberPearl:2:5, 0:Amber:3:6, 1:DeerHide:10:20, 2:Copper:20:40, 2:Tin:20:30, 3:Iron:20:40, 3:SilverNecklace:1:2, 4:Silver:20:40, 4:SilverNecklace:2:3, 5:BlackMetal:20:40, 5:SilverNecklace:2:4, 6:Eitr:10:20, 6:SilverNecklace:3:5, 7:FlametalNew:10:20, 7:SilverNecklace:3:5"; public static List<LootLine> Parse(string text, List<string> errors) { List<LootLine> list = new List<LootLine>(); string[] array = (text ?? "").Split(new char[3] { ',', ';', '\n' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0) { string[] array2 = (from s in text2.Split(new char[1] { ':' }) select s.Trim()).ToArray(); int result = 0; int result2 = 0; int result3 = 0; if (array2.Length != 4 || !int.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out result) || result < 0 || result > 8 || array2[1] == "" || !int.TryParse(array2[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2) || !int.TryParse(array2[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out result3) || result2 < 1 || result3 < result2) { errors.Add("строка «" + text2 + "» не разобрана (нужно уровень:Предмет:от:до)"); continue; } list.Add(new LootLine { Tier = result, Item = array2[1], Min = result2, Max = result3 }); } } return list; } public static List<KeyValuePair<string, int>> Roll(List<LootLine> lines, int tier, Random rng) { return (from l in lines where l.Tier == 0 || l.Tier == tier select new KeyValuePair<string, int>(l.Item, rng.Next(l.Min, l.Max + 1))).ToList(); } } public static class Landmarks { public const string Default = "StartTemple=древнего святилища, Eikthyrnir=алтаря Эйктюра, GDKing=алтаря Древнего, Bonemass=алтаря Массы костей, Dragonqueen=алтаря Модер, GoblinKing=алтаря Яглута, Mistlands_DvergrBossEntrance=логова Королевы, FaderLocation=алтаря Фадера, Vendor_BlackForest=лавки Халдора, Hildir_camp=лагеря Хильдир, BogWitch=хижины ведьмы, SunkenCrypt=затопленного склепа, Crypt=склепа, TrollCave=пещеры тролля, StoneTowerRuins=каменной башни, StoneHenge=каменного круга, ShipWreck=обломков корабля, WoodHouse=заброшенного дома, WoodFarm=заброшенной фермы, WoodVillage=заброшенной деревни, MountainCave=горной пещеры, DrakeNest=гнезда дрейков, AbandonedLogCabin=бревенчатой хижины, GoblinCamp=лагеря фулингов, TarPit=смоляной ямы, Mistlands_DvergrTownEntrance=поселения двергов, Mistlands_Giant=останков великана, Mistlands_Harbour=гавани двергов, Runestone=рунного камня, Dolmen=дольмена, CharredFortress=крепости обугленных, MorgenHole=норы моргена, Ruin=руин, BearCave=берлоги, AshlandRuins=пепельных руин, CharredRuins=обугленных руин, CharredTowerRuins=обугленной башни, FortressRuins=руин крепости, GoblinHut=хижины фулингов, Greydwarf_camp=стоянки карликов, Grave=могилы, MountainGrave=горной могилы, MountainWell=горного колодца, Hildir_cave=пещеры Хильдир, Hildir_crypt=склепа Хильдир, Hildir_plainsfortress=крепости Хильдир, InfestedTree=заражённого дерева, LumberCamp=лесопилки, Mistlands_Excavation=раскопок двергов, Mistlands_GuardTower=башни двергов, Mistlands_Lighthouse=маяка двергов, Mistlands_Statue=статуи двергов, Mistlands_Swords=мечей великана, Mistlands_Viaduct=виадука двергов, Mistlands_RockSpire=скального шпиля, IcePond=ледяного пруда, StoneCircle=каменного круга, StoneHouse=каменного дома, StoneTower=каменной башни, SwampHut=болотной хижины, SulfurArch=серной арки, PlaceofMystery=таинственного места, Mistlands_RoadPost=дорожного столба, DrakeLorestone=камня дрейков"; public static List<KeyValuePair<string, string>> Parse(string text, List<string> errors) { List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>(); string[] array = (text ?? "").Split(new char[3] { ',', ';', '\n' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); int num = text2.IndexOf('='); if (text2.Length != 0) { if (num <= 0 || num == text2.Length - 1) { errors.Add("строка «" + text2 + "» не разобрана (нужно Префикс=название)"); } else { list.Add(new KeyValuePair<string, string>(text2.Substring(0, num).Trim(), text2.Substring(num + 1).Trim())); } } } return list; } public static string NameOf(List<KeyValuePair<string, string>> table, string prefab) { string result = null; int num = 0; foreach (KeyValuePair<string, string> item in table) { if (prefab.StartsWith(item.Key, StringComparison.OrdinalIgnoreCase) && item.Key.Length > num) { result = item.Value; num = item.Key.Length; } } return result; } } public static class Riddle { private static readonly string[] Directions = new string[8] { "к востоку", "к северо-востоку", "к северу", "к северо-западу", "к западу", "к юго-западу", "к югу", "к юго-востоку" }; public const string Separator = " | "; public static string Direction(double fromX, double fromZ, double toX, double toZ) { int num = (int)Math.Round((Math.Atan2(toZ - fromZ, toX - fromX) * 180.0 / Math.PI % 360.0 + 360.0) % 360.0 / 45.0) % 8; return Directions[num]; } public static int RoundDistance(double meters) { if (!(meters < 100.0)) { return (int)(Math.Round(meters / 50.0) * 50.0); } return (int)Math.Max(10.0, Math.Round(meters / 10.0) * 10.0); } public static string Text(int tier, string landmark, double markX, double markZ, double toX, double toZ) { double num = Math.Sqrt(toX * toX + toZ * toZ) / 1000.0; string text = Biomes.Russian[tier] + ": " + num.ToString("0.0", CultureInfo.InvariantCulture).Replace('.', ',') + " км " + Direction(0.0, 0.0, toX, toZ) + " от центра"; if (landmark != null) { double meters = Math.Sqrt((toX - markX) * (toX - markX) + (toZ - markZ) * (toZ - markZ)); text += string.Format("{0}{1} м {2} от {3}", " | ", RoundDistance(meters), Direction(markX, markZ, toX, toZ), landmark); } return text; } public static string ForSign(string riddle) { return riddle.Replace(" | ", "\n"); } public static string ForChat(string riddle) { return riddle.Replace(" | ", "; "); } public static bool IsOld(string riddle) { return !riddle.Contains(" от центра"); } } public static class Heat { public static readonly string[] Texts = new string[4] { "", "Клад: тепло…", "Клад: горячо!", "Клад: совсем рядом!" }; public static int Level(double distance, double warm, double hot, double close) { if (!(distance <= close)) { if (!(distance <= hot)) { return (distance <= warm) ? 1 : 0; } return 2; } return 3; } public static bool ShouldTell(int level, int lastLevel, double now, double lastAt, double cooldown) { if (level > 0) { if (level <= lastLevel) { return now - lastAt >= cooldown; } return true; } return false; } } public sealed class TreasureState { public double NextAt; public bool Active; public long ChestUser; public uint ChestId; public double X; public double Y; public double Z; public int Tier; public double HiddenAt; public double ExpiresAt; public string Riddle = ""; public string Sig = ""; public string Last = ""; public string Serialize() { StringBuilder sb = new StringBuilder(); Line("next", D(NextAt)); if (Last != "") { Line("last", Last); } if (Active) { Line("chest", ChestUser.ToString(CultureInfo.InvariantCulture) + ":" + ChestId.ToString(CultureInfo.InvariantCulture)); Line("pos", D(X) + " " + D(Y) + " " + D(Z)); Line("tier", Tier.ToString(CultureInfo.InvariantCulture)); Line("hidden", D(HiddenAt)); Line("expires", D(ExpiresAt)); Line("riddle", Riddle); Line("sig", Sig); } return sb.ToString(); static string D(double v) { return v.ToString("R", CultureInfo.InvariantCulture); } void Line(string key, string value) { sb.Append(key).Append('\t').Append(value.Replace("\t", " ").Replace("\n", " ")) .Append('\n'); } } public static TreasureState Parse(string text) { TreasureState treasureState = new TreasureState(); string[] array = (text ?? "").Split(new char[1] { '\n' }); foreach (string text2 in array) { int num = text2.IndexOf('\t'); if (num <= 0) { continue; } string text3 = text2.Substring(0, num); string text4 = text2.Substring(num + 1).TrimEnd(new char[1] { '\r' }); switch (text3) { case "next": treasureState.NextAt = Num(text4); break; case "last": treasureState.Last = text4; break; case "chest": { string[] array3 = text4.Split(new char[1] { ':' }); if (array3.Length == 2 && long.TryParse(array3[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && uint.TryParse(array3[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { treasureState.ChestUser = result; treasureState.ChestId = result2; treasureState.Active = true; } break; } case "pos": { string[] array2 = text4.Split(new char[1] { ' ' }); if (array2.Length == 3) { treasureState.X = Num(array2[0]); treasureState.Y = Num(array2[1]); treasureState.Z = Num(array2[2]); } break; } case "tier": int.TryParse(text4, NumberStyles.Integer, CultureInfo.InvariantCulture, out treasureState.Tier); break; case "hidden": treasureState.HiddenAt = Num(text4); break; case "expires": treasureState.ExpiresAt = Num(text4); break; case "riddle": treasureState.Riddle = text4; break; case "sig": treasureState.Sig = text4; break; } } return treasureState; } private static double Num(string text) { if (!double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return 0.0; } return result; } } }