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 OrderBoard v0.1.1
plugins/OrderBoard.dll
Decompiled a week 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 System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using OrderBoard.Core; using Sansara.Chronicle; 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("OrderBoard")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.1.0")] [assembly: AssemblyInformationalVersion("0.1.1+89d0e35d4030ff453ff6be1792b4258243bd15e0")] [assembly: AssemblyProduct("OrderBoard")] [assembly: AssemblyTitle("OrderBoard")] [assembly: AssemblyVersion("0.1.1.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.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 OrderBoard { internal static class Board { private const string RoleKey = "sansara.PortalHall.role"; private static readonly Random Rng = new Random(); private static readonly List<ZDO> Found = new List<ZDO>(); private static OrderState _state = new OrderState(); private static string _path; private static MethodInfo _enqueue; private static bool _apiChecked; private static bool _stallMissingReported; private static Vector3 FameAnchor => new Vector3(10820f, 0f, 300f); public static void Load() { string text = Path.Combine(Paths.ConfigPath, "sansara.OrderBoard"); Directory.CreateDirectory(text); string text2 = string.Concat(ZNet.instance.GetWorldName().Split(Path.GetInvalidFileNameChars())); _path = Path.Combine(text, text2 + ".orders"); _state = (File.Exists(_path) ? OrderState.Parse(File.ReadAllText(_path)) : new OrderState()); int num = OrderPlanner.Refresh(_state.Orders, Plugin.Templates); if (num > 0) { Save(); Plugin.Log.LogInfo((object)$"Доска заказов: количество в открытых заказах обновлено по каталогу: {num}."); } Plugin.Log.LogInfo((object)$"Доска заказов: цикл {_state.Cycle}, заказов {_state.Orders.Count}, выполнено {_state.Orders.Count((Order o) => o.Done)}."); } private static void Save() { File.WriteAllText(_path, _state.Serialize()); } public static void Tick() { //IL_01ee: Unknown result type (might be due to invalid IL or missing references) double timeSeconds = ZNet.instance.GetTimeSeconds(); double num = Math.Max(0.01, Plugin.CycleDays.Value) * 86400.0; if (_state.Cycle < 0 || OrderPlanner.CycleAt(timeSeconds, _state.CycleStart, num) >= 1 || _state.CycleStart > timeSeconds) { NewCycle(_state.Cycle + 1, timeSeconds); } Dictionary<string, ZDO> dictionary = Stall(); if (dictionary.Count == 0) { if (!_stallMissingReported) { _stallMissingReported = true; Plugin.Log.LogInfo((object)"Доска заказов: торгового ряда Чертога славы (Portal Hall 0.2.0) не найдено — жду."); } return; } _stallMissingReported = false; DateTime dateTime = DateTime.UtcNow.AddSeconds(_state.CycleStart + num - timeSeconds); for (int i = 0; i < 3; i++) { dictionary.TryGetValue("order.chest." + i, out var value); dictionary.TryGetValue("order.sign." + i, out var value2); Order order = ((i < _state.Orders.Count) ? _state.Orders[i] : null); if (order != null && !order.Done && value != null) { TryDeliver(i, order, value); } if (value2 != null) { string itemName = ((order != null) ? ItemNames.Of(order.Item) : ""); string text = ((order == null) ? "Заказов нет" : (order.Done ? OrderTexts.Done(order, itemName, order.DoneBy) : OrderTexts.Open(order, itemName, dateTime.ToString("dd.MM", CultureInfo.InvariantCulture)))); text = "<color=white>" + text + "</color>"; if (value2.GetString(ZDOVars.s_text, "") != text) { value2.SetOwner(ZDOMan.GetSessionID()); value2.Set(ZDOVars.s_text, text); ZDOMan.instance.ForceSendZDO(value2.m_uid); } } } } private static void NewCycle(int cycle, double now) { HashSet<string> keys = new HashSet<string>(ZoneSystem.instance.GetGlobalKeys()); int num = OrderPlanner.OpenTier(keys); List<Order> list = OrderPlanner.Plan(Plugin.Templates, num, (OrderTemplate t) => Deliverable(t, keys), Rng); int num2 = _state.Orders.Count((Order o) => !o.Done); _state = new OrderState { Cycle = cycle, CycleStart = now, Orders = list }; Save(); Plugin.Log.LogInfo((object)($"Доска заказов: цикл {cycle}, открытый уровень {num}, заказы: " + string.Join(", ", list.Select((Order o) => $"{o.Amount} {o.Item} (уровень {o.Tier})")) + ((num2 > 0) ? $"; сгорело невыполненных {num2}." : "."))); } private static bool Deliverable(OrderTemplate t, ICollection<string> keys) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(t.Item); if (((itemPrefab == null) ? ((bool?)null) : itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_teleportable) != true) { return keys.Contains(OrderPlanner.BiomeBossKeys[t.Tier - 1]); } return true; } private static Dictionary<string, ZDO> Stall() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Vector3 fameAnchor = FameAnchor; Found.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(fameAnchor), new SimulationDistance(1, 0, true), Found, (List<ZDO>)null); Dictionary<string, ZDO> dictionary = new Dictionary<string, ZDO>(); foreach (ZDO item in Found) { string text = item.GetString("sansara.PortalHall.role", ""); if (text.StartsWith("order.", StringComparison.Ordinal) && !dictionary.ContainsKey(text)) { dictionary[text] = item; } } return dictionary; } private static void TryDeliver(int slot, Order order, ZDO chest) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (chest.GetInt(ZDOVars.s_inUse, 0) == 1) { return; } byte[] byteArray = chest.GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray != null && byteArray.Length != 0) { string text = ItemNames.Token(order.Item); Inventory val = new Inventory("order", (Sprite)null, 8, 20); val.Load(new ZPackage(byteArray)); if (val.CountItems(text, -1, true) >= order.Amount && Deliverer(chest, out var playerId, out var name)) { val.RemoveItem(text, order.Amount, -1, true); ZPackage val2 = new ZPackage(); val.Save(val2); chest.SetOwner(ZDOMan.GetSessionID()); chest.Set(ZDOVars.s_items, val2.GetArray()); ZDOMan.instance.ForceSendZDO(chest.m_uid); order.Done = true; order.DoneBy = name; Save(); bool flag = Reward(slot, order, playerId); Message(playerId, $"Заказ выполнен: {order.Amount} {ItemNames.Of(order.Item)}." + (flag ? " Награда ждёт." : "")); Chronicle(order, playerId, name); Plugin.Log.LogInfo((object)string.Format("Доска заказов: заказ {0} ({1} {2}) выполнил {3} ({4}){5}.", slot, order.Amount, order.Item, name, playerId, flag ? ", награда поставлена" : "")); } } } private static bool Deliverer(ZDO chest, out long playerId, out string name) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) playerId = 0L; name = ""; float num = 10f; 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) { if (peer.m_uid == chest.GetOwner()) { playerId = val.GetLong(ZDOVars.s_playerID, 0L); name = peer.m_playerName; return playerId != 0; } float num2 = Vector3.Distance(val.GetPosition(), chest.GetPosition()); if (num2 <= num) { num = num2; playerId = val.GetLong(ZDOVars.s_playerID, 0L); name = peer.m_playerName; } } } return playerId != 0; } private static bool Reward(int slot, Order order, long playerId) { string[] array = Plugin.RewardPool[order.Tier].Value.Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { Plugin.Log.LogWarning((object)$"Доска заказов: награда не выдана — RewardPool уровня {order.Tier} пуст."); return false; } if (!_apiChecked) { _apiChecked = 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)); } if (_enqueue == null) { Plugin.Log.LogError((object)"Доска заказов: награда не выдана — нет Event Rewards (ExternalRewards.Enqueue)."); return false; } bool result = false; for (int i = 1; i <= Math.Max(1, Plugin.RewardCount[order.Tier].Value); i++) { string text = array[Rng.Next(array.Length)]; string text2 = $"order:{_state.Cycle}:{slot}" + ((i > 1) ? $"/{i}" : ""); try { string text3 = (string)_enqueue.Invoke(null, new object[3] { playerId, text, text2 }); if (text3 != null) { Plugin.Log.LogError((object)("Доска заказов: награда " + text + " не выдана: " + text3 + ".")); } else { result = true; } } catch (Exception arg) { Plugin.Log.LogError((object)$"Доска заказов: ошибка выдачи награды: {arg}"); } } return result; } private static void Message(long playerId, string text) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) 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 && val.GetLong(ZDOVars.s_playerID, 0L) == playerId) { ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "ShowMessage", new object[2] { 2, text }); } } } private static void Chronicle(Order order, long playerId, string name) { try { ChronicleFile.Append(ChronicleFile.PathFor(Paths.ConfigPath, ZNet.instance.GetWorldName()), new ChronicleEntry { Time = ZNet.instance.GetTimeSeconds(), Date = DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture), Kind = "order", Id = order.Item, Result = "victory", Players = new List<ChroniclePlayer> { new ChroniclePlayer(playerId, name) } }); } catch (Exception ex) { Plugin.Log.LogError((object)("Доска заказов: ошибка записи в летопись: " + ex)); } } } internal static class ItemNames { private static readonly Dictionary<string, string> Russian = new Dictionary<string, string>(); public static void Load() { try { foreach (TextAsset item in Assets()) { ReadColumn(item, "Russian", Russian); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Доска заказов: русская локализация не прочитана: " + ex.Message)); } Plugin.Log.LogInfo((object)$"Доска заказов: строк русской локализации {Russian.Count}."); } public static string Of(string prefab) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); string text = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_name) ?? prefab; string key = text.TrimStart(new char[1] { '$' }); if (Russian.TryGetValue(key, out var value) && value != "") { return value; } if (Localization.instance == null) { return prefab; } return Localization.instance.Localize(text); } public static string Token(string prefab) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); return ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_name) ?? ""; } private static IEnumerable<TextAsset> Assets() { _ = Localization.instance; object obj = AccessTools.Field(typeof(Localization), "m_localizationSettings")?.GetValue(null); if (obj == null) { yield break; } Traverse val = Traverse.Create(obj); if (!((val.Field("Localizations").GetValue() ?? val.Property("Localizations", (object[])null).GetValue()) is IEnumerable enumerable)) { yield break; } foreach (object item in enumerable) { TextAsset val2 = (TextAsset)((item is TextAsset) ? item : null); if (val2 != null && (Object)(object)val2 != (Object)null) { yield return val2; } } } private static void ReadColumn(TextAsset asset, string language, Dictionary<string, string> into) { List<List<string>> list = SplitCsv(asset.text); if (list.Count == 0) { return; } int num = list[0].FindIndex((string h) => StripQuotes(h).Trim() == language); if (num < 0) { return; } foreach (List<string> item in list.Skip(1)) { if (item.Count != 0 && item[0].Length != 0 && !item[0].StartsWith("//")) { string text = ((item.Count > num) ? item[num].Trim() : ""); if (text.Length == 0 && item.Count > 1) { text = item[1].Trim(); } into[item[0]] = text; } } } private static string StripQuotes(string s) { if (s.Length < 2 || s[0] != '"' || s[s.Length - 1] != '"') { return s; } return s.Substring(1, s.Length - 2); } private static List<List<string>> SplitCsv(string text) { List<List<string>> list = new List<List<string>>(); List<string> list2 = new List<string>(); StringBuilder stringBuilder = new StringBuilder(); bool flag = false; for (int i = 0; i < text.Length; i++) { char c = text[i]; switch (c) { case '"': if (flag && i + 1 < text.Length && text[i + 1] == '"') { stringBuilder.Append('"'); i++; } else { flag = !flag; } continue; case ',': if (!flag) { list2.Add(stringBuilder.ToString()); stringBuilder.Length = 0; continue; } break; } if (c == '\n' && !flag) { list2.Add(stringBuilder.ToString().TrimEnd(new char[1] { '\r' })); stringBuilder.Length = 0; list.Add(list2); list2 = new List<string>(); } else { stringBuilder.Append(c); } } if (stringBuilder.Length > 0 || list2.Count > 0) { list2.Add(stringBuilder.ToString()); list.Add(list2); } return list; } } [BepInPlugin("sansara.OrderBoard", "Order Board", "0.1.1")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "sansara.OrderBoard"; public const string Name = "Order Board"; public const string Version = "0.1.1"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> CycleDays; internal static ConfigEntry<string> Catalog; internal static readonly ConfigEntry<string>[] RewardPool = new ConfigEntry<string>[9]; internal static readonly ConfigEntry<int>[] RewardCount = new ConfigEntry<int>[9]; internal static List<OrderTemplate> Templates = new List<OrderTemplate>(); private float _nextTick; private bool _started; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Доска заказов в Чертоге славы."); CycleDays = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CycleDays", 3f, "Через сколько дней (реальных) доска обновляется; невыполненные заказы сгорают."); Catalog = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Catalog", "Wood:250:1, Flint:100:1, LeatherScraps:100:1, DeerHide:60:1, Copper:100:2, Tin:60:2, Bronze:60:2, RoundLog:200:2, TrollHide:30:2, Iron:100:3, Guck:50:3, ElderBark:200:3, Bloodbag:50:3, Silver:100:4, Obsidian:150:4, WolfPelt:60:4, FreezeGland:50:4, BlackMetal:100:5, LinenThread:80:5, Barley:200:5, Needle:80:5, Sap:80:6, Eitr:40:6, Carapace:80:6, RoyalJelly:40:6, FlametalNew:60:7, CharredBone:120:7, AskHide:50:7, Grausten:200:7", "Предмет:количество:уровень (1 Луга … 8 Дальний Север) через запятую — из чего выбираются заказы."); 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", 1, "Наград за один заказ."); } Log.LogInfo((object)"Order Board 0.1.1 загружен."); } private void Update() { if (!Enabled.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || !ZoneSystem.instance.LocationsGenerated || Time.time < _nextTick) { return; } _nextTick = Time.time + 5f; try { if (!_started) { _started = true; List<string> list = new List<string>(); Templates = OrderCatalog.Parse(Catalog.Value, list); list.ForEach(delegate(string e) { Log.LogWarning((object)("Доска заказов: Catalog — " + e)); }); Templates.RemoveAll(delegate(OrderTemplate t) { bool num = (Object)(object)ObjectDB.instance.GetItemPrefab(t.Item) == (Object)null; if (num) { Log.LogWarning((object)("Доска заказов: предмета " + t.Item + " нет в игре — строка пропущена.")); } return num; }); ItemNames.Load(); Board.Load(); Log.LogInfo((object)$"Доска заказов: в каталоге {Templates.Count} предметов."); } Board.Tick(); } catch (Exception ex) { Log.LogError((object)("Доска заказов: ошибка: " + ex)); } } } } namespace OrderBoard.Core { public sealed class OrderTemplate { public string Item { get; } public int Amount { get; } public int Tier { get; } public OrderTemplate(string item, int amount, int tier) { Item = item; Amount = amount; Tier = tier; } } public static class OrderCatalog { public const int MaxTier = 8; public const string Default = "Wood:250:1, Flint:100:1, LeatherScraps:100:1, DeerHide:60:1, Copper:100:2, Tin:60:2, Bronze:60:2, RoundLog:200:2, TrollHide:30:2, Iron:100:3, Guck:50:3, ElderBark:200:3, Bloodbag:50:3, Silver:100:4, Obsidian:150:4, WolfPelt:60:4, FreezeGland:50:4, BlackMetal:100:5, LinenThread:80:5, Barley:200:5, Needle:80:5, Sap:80:6, Eitr:40:6, Carapace:80:6, RoyalJelly:40:6, FlametalNew:60:7, CharredBone:120:7, AskHide:50:7, Grausten:200:7"; public static List<OrderTemplate> Parse(string text, List<string> errors) { List<OrderTemplate> list = new List<OrderTemplate>(); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); 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 = text2.Split(new char[1] { ':' }); if (array2.Length != 3 || array2[0].Trim() == "" || !int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 1 || !int.TryParse(array2[2].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2) || result2 < 1 || result2 > 8) { errors.Add($"строка «{text2}» пропущена: нужно Предмет:количество:уровень 1–{8}"); } else if (!hashSet.Add(array2[0].Trim())) { errors.Add("предмет " + array2[0].Trim() + " указан повторно, оставлена первая строка"); } else { list.Add(new OrderTemplate(array2[0].Trim(), result, result2)); } } } return list; } } public sealed class Order { public string Item = ""; public int Amount; public int Tier; public bool Done; public string DoneBy = ""; } public sealed class OrderState { public int Cycle = -1; public double CycleStart; public List<Order> Orders = new List<Order>(); public string Serialize() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("cycle\t").Append(Cycle.ToString(CultureInfo.InvariantCulture)).Append('\t') .Append(CycleStart.ToString("R", CultureInfo.InvariantCulture)) .Append('\n'); foreach (Order order in Orders) { stringBuilder.Append(string.Join("\t", order.Item, order.Amount.ToString(CultureInfo.InvariantCulture), order.Tier.ToString(CultureInfo.InvariantCulture), order.Done ? "1" : "0", (order.DoneBy ?? "").Replace("\t", " ").Replace("\n", " "))).Append('\n'); } return stringBuilder.ToString(); } public static OrderState Parse(string text) { OrderState orderState = new OrderState(); string[] array = (text ?? "").Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].TrimEnd(new char[1] { '\r' }).Split(new char[1] { '\t' }); int result; int result2; if (array2.Length >= 3 && array2[0] == "cycle") { int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out orderState.Cycle); double.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out orderState.CycleStart); } else if (array2.Length >= 4 && array2[0] != "" && int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out result) && int.TryParse(array2[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2)) { orderState.Orders.Add(new Order { Item = array2[0], Amount = result, Tier = result2, Done = (array2[3] == "1"), DoneBy = ((array2.Length > 4) ? array2[4] : "") }); } } return orderState; } } public static class OrderPlanner { 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 readonly string[] BiomeBossKeys = new string[8] { "defeated_eikthyr", "defeated_gdking", "defeated_bonemass", "defeated_dragon", "defeated_goblinking", "defeated_queen", "defeated_fader", "defeated_frozenking_p3" }; public static int Refresh(List<Order> orders, List<OrderTemplate> catalog) { int num = 0; foreach (Order o in orders) { OrderTemplate orderTemplate = catalog.FirstOrDefault((OrderTemplate c) => string.Equals(c.Item, o.Item, StringComparison.OrdinalIgnoreCase)); if (!o.Done && orderTemplate != null && orderTemplate.Amount != o.Amount) { o.Amount = orderTemplate.Amount; num++; } } return num; } public static int OpenTier(ICollection<string> worldKeys) { int result = 1; for (int i = 2; i <= 8 && worldKeys.Contains(OpeningKeys[i - 1]); i++) { result = i; } return result; } public static int CycleAt(double now, double firstStart, double period) { if (!(period <= 0.0)) { return (int)Math.Floor((now - firstStart) / period); } return 0; } public static List<Order> Plan(IList<OrderTemplate> catalog, int openTier, Func<OrderTemplate, bool> deliverable, Random rng) { List<Order> list = new List<Order>(); HashSet<string> used = new HashSet<string>(); int num = Math.Max(1, (int)Math.Round((double)openTier / 3.0)); int val = Math.Max(1, (int)Math.Round((double)(openTier * 2) / 3.0)); int num2 = Math.Min(num + 1, openTier); int[][] array = new int[3][] { new int[2] { 1, num }, new int[2] { num2, Math.Max(val, num2) }, new int[2] { openTier, openTier } }; foreach (int[] array2 in array) { OrderTemplate orderTemplate = null; for (int top = array2[1]; top >= 1; top--) { if (orderTemplate != null) { break; } int bottom = Math.Min(array2[0], top); List<OrderTemplate> list2 = catalog.Where((OrderTemplate t) => t.Tier >= bottom && t.Tier <= top && !used.Contains(t.Item) && deliverable(t)).ToList(); if (list2.Count > 0) { orderTemplate = list2[rng.Next(list2.Count)]; } } if (orderTemplate != null) { used.Add(orderTemplate.Item); list.Add(new Order { Item = orderTemplate.Item, Amount = orderTemplate.Amount, Tier = orderTemplate.Tier }); } } return list; } } public static class OrderTexts { public const int SignLimit = 50; public const string None = "Заказов нет"; public static string Open(Order o, string itemName, string until) { return Fit("Заказ: " + o.Amount.ToString(CultureInfo.InvariantCulture) + " " + itemName + " · до " + until); } public static string Done(Order o, string itemName, string by) { return Fit("Выполнено: " + by + " · " + itemName); } public static string Fit(string s) { if (s.Length > 50) { return s.Substring(0, 49) + "…"; } return s; } } }