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 PortalHall v0.2.2
plugins/PortalHall.dll
Decompiled 6 days 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 System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using PortalHall.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("PortalHall")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.2.0")] [assembly: AssemblyInformationalVersion("0.2.2+6832af49c98836335819d0608bd429b70015f4f0")] [assembly: AssemblyProduct("PortalHall")] [assembly: AssemblyTitle("PortalHall")] [assembly: AssemblyVersion("0.2.2.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 PortalHall { [HarmonyPatch(typeof(ZoneSystem), "RPC_SetGlobalKey")] internal static class BossWatch { private const float VictorRadius = 60f; private static void Prefix(long sender, string name) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || name == null || !name.StartsWith("defeated_", StringComparison.Ordinal)) { return; } ZNetPeer peer = ZNet.instance.GetPeer(sender); ZDO val = ((peer == null || ((ZDOID)(ref peer.m_characterID)).IsNone()) ? null : ZDOMan.instance.GetZDO(peer.m_characterID)); if (val == null) { return; } Vector3 position = val.GetPosition(); List<ChroniclePlayer> list = new List<ChroniclePlayer>(); foreach (ZNetPeer peer2 in ZNet.instance.GetPeers()) { ZDO val2 = (((ZDOID)(ref peer2.m_characterID)).IsNone() ? null : ZDOMan.instance.GetZDO(peer2.m_characterID)); if (val2 != null && Vector3.Distance(val2.GetPosition(), position) <= 60f) { list.Add(new ChroniclePlayer(val2.GetLong(ZDOVars.s_playerID, 0L), peer2.m_playerName)); } } ChronicleEntry entry = new ChronicleEntry { Time = ZNet.instance.GetTimeSeconds(), Date = DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture), Kind = "boss", Id = name, Result = "victory", Players = list }; ChronicleFile.Append(ChronicleFile.PathFor(Paths.ConfigPath, ZNet.instance.GetWorldName()), entry); Plugin.Log.LogInfo((object)("Чертог славы: победа над боссом " + name + " — " + string.Join(", ", list.ConvertAll((ChroniclePlayer p) => p.Name)) + ".")); } catch (Exception ex) { Plugin.Log.LogError((object)("Чертог славы: ошибка записи победы над боссом: " + ex)); } } } internal static class Fame { private const string Version = "fame|2"; private static DateTime _rewardsStamp; private static List<Leader> _rewardLeaders = new List<Leader>(); public static void Tick(HallBounds hub, Style style, List<ZDO> hubOurs) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(Hall.HubAnchor.x, hub.FloorTop, FameLayout.OffsetZ); List<ZDO> list = (from z in Hall.Near(val) where z.GetString("sansara.PortalHall.sig", "") != "" select z).ToList(); if (!style.FamePortal) { if (list.Count > 0) { list.ForEach(Hall.Destroy); Plugin.Log.LogInfo((object)$"Чертог славы: выключен — убрано деталей {list.Count}."); } return; } List<DecorItem> list2 = (from i in FameLayout.Build(val.x, val.y, val.z, style) where (Object)(object)Hall.Prefab(i.Prefab) != (Object)null select i).ToList(); string sig = "fame|2|" + Hall.StyleKey(style); if (list.Count != list2.Count || list.Any((ZDO z) => z.GetString("sansara.PortalHall.sig", "") != sig)) { Rebuild(list2, list, sig); list = (from z in Hall.Near(val) where z.GetString("sansara.PortalHall.sig", "") != "" select z).ToList(); } Dictionary<string, ZDO> dictionary = (from z in list where z.GetString("sansara.PortalHall.role", "") != "" group z by z.GetString("sansara.PortalHall.role", "")).ToDictionary((IGrouping<string, ZDO> g) => g.Key, (IGrouping<string, ZDO> g) => g.First()); LinkPortals(((IEnumerable<ZDO>)hubOurs).FirstOrDefault((Func<ZDO, bool>)((ZDO z) => z.GetString("sansara.PortalHall.role", "") == "fame.portal.hub")), dictionary.TryGetValue("fame.portal.fame", out var value) ? value : null); UpdateBoard(dictionary); } private static void Rebuild(List<DecorItem> items, List<ZDO> ours, string sig) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) List<ZDO> list = ours.ToList(); int num = 0; int num2 = 0; foreach (DecorItem item in items) { int hash = StringExtensionMethods.GetStableHashCode(((Object)Hall.Prefab(item.Prefab)).name); Vector3 pos = new Vector3(item.X, item.Y, item.Z); ZDO val = ((IEnumerable<ZDO>)list).FirstOrDefault((Func<ZDO, bool>)((ZDO z) => z.GetPrefab() == hash && Vector3.Distance(z.GetPosition(), pos) < 0.05f && Quaternion.Angle(z.GetRotation(), Quaternion.Euler(item.RotX, item.RotY, 0f)) < 1f)); if (val != null) { list.Remove(val); val.SetOwner(ZDOMan.GetSessionID()); val.Set("sansara.PortalHall.sig", sig); val.Set("sansara.PortalHall.role", item.Role ?? ""); num++; } else { Hall.Place(item, sig); num2++; } } list.ForEach(Hall.Destroy); Plugin.Log.LogInfo((object)$"Чертог славы: оставлено деталей {num}, поставлено {num2}, убрано {list.Count}."); } private static void LinkPortals(ZDO hub, ZDO fame) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_004b: Unknown result type (might be due to invalid IL or missing references) if (hub == null || fame == null) { return; } ZDO[] array = (ZDO[])(object)new ZDO[2] { hub, fame }; foreach (ZDO val in array) { ZDO val2 = ((val == hub) ? fame : hub); if (val.GetString(ZDOVars.s_tag, "") != "Чертог славы" || val.GetConnectionZDOID((ConnectionType)1) != val2.m_uid) { val.SetOwner(ZDOMan.GetSessionID()); val.Set(ZDOVars.s_tag, "Чертог славы"); val.SetConnection((ConnectionType)1, val2.m_uid); ZDOMan.instance.ForceSendZDO(val.m_uid); } } } private static void UpdateBoard(Dictionary<string, ZDO> byRole) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) int bad; List<ChronicleEntry> chronicle = ChronicleFile.Read(ChronicleFile.PathFor(Paths.ConfigPath, ZNet.instance.GetWorldName()), out bad); HashSet<string> worldKeys = new HashSet<string>(ZoneSystem.instance.GetGlobalKeys()); foreach (KeyValuePair<string, string> item in FameBoard.Texts(chronicle, worldKeys, RewardLeaders())) { string text = "<color=white>" + item.Value + "</color>"; if (byRole.TryGetValue(item.Key, out var value) && value.GetString(ZDOVars.s_text, "") != text) { value.SetOwner(ZDOMan.GetSessionID()); value.Set(ZDOVars.s_text, text); ZDOMan.instance.ForceSendZDO(value.m_uid); } } foreach (KeyValuePair<string, string> item2 in FameBoard.Trophies(worldKeys)) { if (byRole.TryGetValue(item2.Key, out var value2)) { int num = ((!(item2.Value == "")) ? StringExtensionMethods.GetStableHashCode(item2.Value) : 0); if (value2.GetInt(ZDOVars.s_item, 0) != num) { value2.SetOwner(ZDOMan.GetSessionID()); value2.Set(ZDOVars.s_item, num, false); value2.Set(ZDOVars.s_variant, 0, false); value2.Set(ZDOVars.s_quality, 1, false); ZDOMan.instance.ForceSendZDO(value2.m_uid); ZRoutedRpc.instance.InvokeRoutedRPC(0L, value2.m_uid, "SetVisualItem", new object[4] { num, 0, 1, 0 }); } } } } private static List<Leader> RewardLeaders() { string path = Path.Combine(Paths.ConfigPath, "sansara.EventRewards.queue.json"); string path2 = Path.Combine(Paths.ConfigPath, "sansara.EventRewards.players.json"); if (!File.Exists(path)) { return _rewardLeaders; } DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(path); if (lastWriteTimeUtc == _rewardsStamp) { return _rewardLeaders; } _rewardsStamp = lastWriteTimeUtc; try { Dictionary<long, int> dictionary = new Dictionary<long, int>(); foreach (Match item in Regex.Matches(File.ReadAllText(path), "\\{[^{}]*\\}")) { if (Regex.IsMatch(item.Value, "\"state\"\\s*:\\s*\"delivered\"") && long.TryParse(Regex.Match(item.Value, "\"playerId\"\\s*:\\s*(-?\\d+)").Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { dictionary[result] = ((!dictionary.TryGetValue(result, out var value)) ? 1 : (value + 1)); } } Dictionary<long, string> names = new Dictionary<long, string>(); if (File.Exists(path2)) { foreach (Match item2 in Regex.Matches(File.ReadAllText(path2), "\\{[^{}]*\\}")) { if (long.TryParse(Regex.Match(item2.Value, "\"playerId\"\\s*:\\s*(-?\\d+)").Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { names[result2] = Regex.Match(item2.Value, "\"name\"\\s*:\\s*\"([^\"]*)\"").Groups[1].Value; } } } _rewardLeaders = (from kv in dictionary select new Leader { Name = ((names.TryGetValue(kv.Key, out var value2) && value2 != "") ? value2 : "?"), Count = kv.Value } into l orderby l.Count descending select l).ThenBy<Leader, string>((Leader l) => l.Name, StringComparer.Ordinal).ToList(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Чертог славы: не удалось прочитать данные Event Rewards: " + ex.Message)); } return _rewardLeaders; } } internal static class Hall { internal const string SigKey = "sansara.PortalHall.sig"; internal const string RoleKey = "sansara.PortalHall.role"; private const float MinHallY = 3000f; private const float SearchRadius = 200f; private static readonly int HubFloor = StringExtensionMethods.GetStableHashCode("Piece_grausten_floor_4x4"); private static readonly HashSet<string> Missing = new HashSet<string>(); private static readonly List<ZDO> Found = new List<ZDO>(); private static string _lastReport; internal static Vector3 HubAnchor => new Vector3(10820f, 0f, 0f); public static void Tick() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) List<ZDO> source = Near(HubAnchor); List<ZDO> list = source.Where((ZDO z) => z.GetString("sansara.PortalHall.sig", "") != "").ToList(); List<ZDO> list2 = source.Where((ZDO z) => z.GetPrefab() == HubFloor && z.GetPosition().y >= 3000f).ToList(); HallBounds hallBounds = HallBounds.FromFloorTiles(list2.Select((ZDO z) => new TilePos(z.GetPosition().x, z.GetPosition().y, z.GetPosition().z))); Style style = Plugin.CurrentStyle(); bool flag = Plugin.Enabled.Value && hallBounds != null && HallLayout.Raised(hallBounds, style); if (hallBounds != null) { KeepHubCeiling(hallBounds, list2, !flag); } if (hallBounds == null || !Plugin.Enabled.Value) { if (list.Count > 0) { list.ForEach(Destroy); Plugin.Log.LogInfo((object)string.Format("Чертог: {0} — убрано деталей {1}.", Plugin.Enabled.Value ? "зала порталов нет" : "мод выключен", list.Count)); } Report(Plugin.Enabled.Value ? "Чертог: зал порталов ServersideQoL не найден, жду." : null); return; } List<DecorItem> list3 = (from i in HallLayout.Build(hallBounds, style) where (Object)(object)Prefab(i.Prefab) != (Object)null select i).ToList(); string sig = hallBounds.Signature() + "|" + StyleKey(style); if (list.Count == list3.Count && list.All((ZDO z) => z.GetString("sansara.PortalHall.sig", "") == sig)) { Fame.Tick(hallBounds, style, list); return; } list.ForEach(Destroy); foreach (DecorItem item in list3) { Place(item, sig); } Plugin.Log.LogInfo((object)($"Чертог: зал {hallBounds.TilesX}×{hallBounds.TilesZ} плит, центр ({hallBounds.CenterX:0.#}; {hallBounds.FloorTop:0.##}; {hallBounds.CenterZ:0.#}), " + $"высота {HallLayout.InnerHeight(hallBounds, style):0.#} м — убрано деталей {list.Count}, поставлено {list3.Count}.")); _lastReport = null; Fame.Tick(hallBounds, style, (from z in Near(HubAnchor) where z.GetString("sansara.PortalHall.sig", "") != "" select z).ToList()); } private static void KeepHubCeiling(HallBounds b, List<ZDO> hubTiles, bool keep) { //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) float floorY = b.FloorTop - 0.25f; float ceilingY = floorY + 4.5f; List<ZDO> list = hubTiles.Where((ZDO z) => Mathf.Abs(z.GetPosition().y - ceilingY) < 0.3f).ToList(); if (!keep) { if (list.Count > 0) { list.ForEach(Destroy); Plugin.Log.LogInfo((object)$"Чертог: потолок зала ServersideQoL убран ({list.Count} плит) — зал выше."); } return; } List<ZDO> list2 = hubTiles.Where((ZDO z) => Mathf.Abs(z.GetPosition().y - floorY) < 0.3f).ToList(); HashSet<Vector2Int> present = new HashSet<Vector2Int>(list.Select((ZDO z) => Cell(z.GetPosition()))); List<ZDO> list3 = list2.Where((ZDO z) => !present.Contains(Cell(z.GetPosition()))).ToList(); if (list3.Count == 0) { return; } GameObject prefab = ZNetScene.instance.GetPrefab(HubFloor); long num = list2[0].GetLong(ZDOVars.s_creator, 0L); foreach (ZDO item in list3) { Vector3 position = item.GetPosition(); Create(prefab, new Vector3(position.x, ceilingY, position.z), item.GetRotation()).Set(ZDOVars.s_creator, num); } Plugin.Log.LogInfo((object)$"Чертог: потолок зала ServersideQoL восстановлен ({list3.Count} плит)."); } private static Vector2Int Cell(Vector3 p) { //IL_0000: 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) //IL_0016: Unknown result type (might be due to invalid IL or missing references) return new Vector2Int(Mathf.RoundToInt(p.x), Mathf.RoundToInt(p.z)); } private static void Report(string text) { if (text != null && text != _lastReport) { Plugin.Log.LogInfo((object)text); } _lastReport = text; } internal static List<ZDO> Near(Vector3 anchor) { //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_0028: 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_0035: Unknown result type (might be due to invalid IL or missing references) Found.Clear(); int num = Mathf.CeilToInt(3.125f); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(anchor), new SimulationDistance(num, 0, true), Found, (List<ZDO>)null); return Found.Where((ZDO z) => Mathf.Abs(z.GetPosition().x - anchor.x) <= 200f && Mathf.Abs(z.GetPosition().z - anchor.z) <= 200f).ToList(); } internal static string StyleKey(Style s) { return string.Join(";", s.FloorTiles, s.WallTiles, s.CeilingTiles, s.WallTileFlip, s.Throne, s.Retinue, s.Lights, s.FloorTile, s.WallTile, s.ThronePrefab, s.ThroneRotY, s.Wolf, s.Raven, s.Gold, s.Carpet, s.Light, s.Height, s.Lanterns, s.Lantern, s.LanternHeight, s.LanternEvery, s.FamePortal, s.Portal); } internal static GameObject Prefab(string name) { GameObject prefab = ZNetScene.instance.GetPrefab(name); if (((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<ZNetView>() == (Object)null) && Missing.Add(name)) { Plugin.Log.LogWarning((object)("Чертог: префаба " + name + " нет в игре — пропущен.")); return null; } if (!((Object)(object)prefab != (Object)null) || !((Object)(object)prefab.GetComponent<ZNetView>() != (Object)null)) { return null; } return prefab; } internal static void Place(DecorItem item, string sig) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) ZDO val = Create(Prefab(item.Prefab), new Vector3(item.X, item.Y, item.Z), Quaternion.Euler(item.RotX, item.RotY, 0f)); val.Set("sansara.PortalHall.sig", sig); if (item.Role != null) { val.Set("sansara.PortalHall.role", item.Role); } } private static ZDO Create(GameObject prefab, Vector3 pos, Quaternion rot) { //IL_0018: 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_004c: Unknown result type (might be due to invalid IL or missing references) ZNetView component = prefab.GetComponent<ZNetView>(); int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name); ZDO val = ZDOMan.instance.CreateNewZDO(pos, stableHashCode); val.Persistent = component.m_persistent; val.Type = component.m_type; val.Distant = component.m_distant; val.SetPrefab(stableHashCode); val.SetRotation(rot); val.Set("HasFields", true); if ((Object)(object)prefab.GetComponent<Piece>() != (Object)null) { val.Set("HasFieldsPiece", true); val.Set("Piece.m_canBeRemoved", false); } if ((Object)(object)prefab.GetComponent<WearNTear>() != (Object)null) { val.Set("HasFieldsWearNTear", true); val.Set("WearNTear.m_noRoofWear", false); val.Set("WearNTear.m_noSupportWear", false); val.Set("WearNTear.m_health", -1f); } if ((Object)(object)prefab.GetComponent<ItemStand>() != (Object)null) { val.Set("HasFieldsItemStand", true); val.Set("ItemStand.m_canBeRemoved", false); } if ((Object)(object)prefab.GetComponent<Fireplace>() != (Object)null) { val.Set("HasFieldsFireplace", true); val.Set("Fireplace.m_infiniteFuel", true); val.Set("Fireplace.m_disableCoverCheck", true); } return val; } internal static void Destroy(ZDO zdo) { zdo.SetOwner(ZDOMan.GetSessionID()); ZDOMan.instance.DestroyZDO(zdo); } } [BepInPlugin("sansara.PortalHall", "Portal Hall", "0.2.2")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "sansara.PortalHall"; public const string Name = "Portal Hall"; public const string Version = "0.2.2"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; private static ConfigEntry<bool> _floor; private static ConfigEntry<bool> _walls; private static ConfigEntry<bool> _ceiling; private static ConfigEntry<bool> _throne; private static ConfigEntry<bool> _retinue; private static ConfigEntry<bool> _lights; private static ConfigEntry<bool> _wallFlip; private static ConfigEntry<string> _floorTile; private static ConfigEntry<string> _wallTile; private static ConfigEntry<string> _thronePrefab; private static ConfigEntry<string> _wolf; private static ConfigEntry<string> _raven; private static ConfigEntry<string> _gold; private static ConfigEntry<string> _carpet; private static ConfigEntry<string> _light; private static ConfigEntry<float> _throneRot; private static ConfigEntry<float> _height; private static ConfigEntry<float> _lanternHeight; private static ConfigEntry<bool> _lanterns; private static ConfigEntry<string> _lantern; private static ConfigEntry<int> _lanternEvery; private static ConfigEntry<bool> _famePortal; private float _nextTick; private FileSystemWatcher _watcher; private volatile bool _reload; private void Awake() { //IL_036e: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Убранство зала порталов. false — мод убирает всё, что поставил."); _floor = ((BaseUnityPlugin)this).Config.Bind<bool>("Lining", "FloorTiles", true, "Пол — плитка поверх граустена."); _walls = ((BaseUnityPlugin)this).Config.Bind<bool>("Lining", "WallTiles", true, "Стены изнутри — настенная плитка."); _ceiling = ((BaseUnityPlugin)this).Config.Bind<bool>("Lining", "CeilingTiles", true, "Потолок — перевёрнутая напольная плитка."); _wallFlip = ((BaseUnityPlugin)this).Config.Bind<bool>("Lining", "WallTileFlip", false, "Развернуть настенную плитку лицом к залу, если она видна изнанкой."); _floorTile = ((BaseUnityPlugin)this).Config.Bind<string>("Lining", "FloorTilePrefab", "blackmarble_tile_floor_2x2", "Плитка пола и потолка (2×2 м)."); _wallTile = ((BaseUnityPlugin)this).Config.Bind<string>("Lining", "WallTilePrefab", "blackmarble_tile_wall_2x4", "Плитка стен (2 м шириной, 4 м высотой)."); _throne = ((BaseUnityPlugin)this).Config.Bind<bool>("Throne", "Throne", true, "Трон в центре зала и дорожка перед ним."); _thronePrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Throne", "ThronePrefab", "piece_blackmarble_throne", "Трон."); _throneRot = ((BaseUnityPlugin)this).Config.Bind<float>("Throne", "ThroneRotationY", 180f, "Поворот трона, градусы (подобрать, если трон смотрит не в зал)."); _carpet = ((BaseUnityPlugin)this).Config.Bind<string>("Throne", "CarpetPrefab", "jute_carpet", "Дорожка перед троном."); _retinue = ((BaseUnityPlugin)this).Config.Bind<bool>("Throne", "Retinue", true, "Волки и вороны у трона, золото за ним."); _wolf = ((BaseUnityPlugin)this).Config.Bind<string>("Throne", "WolfPrefab", "darkwood_wolf", "Волки (Гери и Фреки)."); _raven = ((BaseUnityPlugin)this).Config.Bind<string>("Throne", "RavenPrefab", "darkwood_raven", "Вороны (Хугин и Мунин)."); _gold = ((BaseUnityPlugin)this).Config.Bind<string>("Throne", "GoldPrefab", "bar_gold_stack", "Золото у трона."); _lights = ((BaseUnityPlugin)this).Config.Bind<bool>("Lights", "Lights", true, "Светильники вокруг трона."); _height = ((BaseUnityPlugin)this).Config.Bind<float>("Lining", "Height", 8f, "Высота зала изнутри, м. Больше 4 — потолок зала ServersideQoL убирается, мраморные стены и потолок поднимаются (зал ServersideQoL — 4 м); 4 — как построил ServersideQoL (его потолок возвращается)."); _lanterns = ((BaseUnityPlugin)this).Config.Bind<bool>("Lights", "Lanterns", true, "Настенные фонари двергов над порталами (если зал выше LanternHeight + 1 м)."); _lantern = ((BaseUnityPlugin)this).Config.Bind<string>("Lights", "LanternPrefab", "piece_dvergr_lantern", "Настенный фонарь."); _lanternHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Lights", "LanternHeight", 6f, "Высота фонарей над полом, м."); _famePortal = ((BaseUnityPlugin)this).Config.Bind<bool>("Fame", "HallOfFame", true, "Чертог славы: отдельный зал рядом с залом порталов, вход — портал за троном."); _lanternEvery = ((BaseUnityPlugin)this).Config.Bind<int>("Lights", "LanternEvery", 2, "Через сколько плит зала (по 4 м) вешать фонари вдоль стены."); _light = ((BaseUnityPlugin)this).Config.Bind<string>("Lights", "LightPrefab", "piece_groundtorch_mist", "Светильник (по умолчанию «Факел с магическим огоньком» Туманных земель: без огня и дыма, разгоняет туман)."); _watcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)); _watcher.Changed += delegate { _reload = true; }; _watcher.EnableRaisingEvents = true; new Harmony("sansara.PortalHall").PatchAll(typeof(BossWatch)); Log.LogInfo((object)"Portal Hall 0.2.2 загружен."); } internal static Style CurrentStyle() { return new Style { FloorTiles = _floor.Value, WallTiles = _walls.Value, CeilingTiles = _ceiling.Value, WallTileFlip = _wallFlip.Value, Throne = _throne.Value, Retinue = _retinue.Value, Lights = _lights.Value, FloorTile = _floorTile.Value, WallTile = _wallTile.Value, ThronePrefab = _thronePrefab.Value, ThroneRotY = _throneRot.Value, Wolf = _wolf.Value, Raven = _raven.Value, Gold = _gold.Value, Carpet = _carpet.Value, Light = _light.Value, Height = _height.Value, Lanterns = _lanterns.Value, Lantern = _lantern.Value, LanternHeight = _lanternHeight.Value, LanternEvery = _lanternEvery.Value, FamePortal = _famePortal.Value }; } private void Update() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || !ZoneSystem.instance.LocationsGenerated || Time.time < _nextTick) { return; } _nextTick = Time.time + 10f; try { if (_reload) { _reload = false; ((BaseUnityPlugin)this).Config.Reload(); Log.LogInfo((object)"Чертог: настройки перечитаны."); } Hall.Tick(); } catch (Exception ex) { Log.LogError((object)("Чертог: ошибка: " + ex)); } } } } namespace PortalHall.Core { public static class FameRoles { public const string HubPortal = "fame.portal.hub"; public const string FamePortal = "fame.portal.fame"; public const string TreasureSign = "treasure.sign"; public const string PortalTag = "Чертог славы"; public static string BossStand(int i) { return "fame.boss.stand." + i; } public static string BossSign(int i) { return "fame.boss.sign." + i; } public static string TrialSign(int i) { return "fame.trial.sign." + i; } public static string RaidSign(int i) { return "fame.raid.sign." + i; } public static string RecordSign(int i) { return "fame.record.sign." + i; } public static string Header(string what) { return "fame.header." + what; } public static string OrderChest(int i) { return "order.chest." + i; } public static string OrderSign(int i) { return "order.sign." + i; } } public sealed class Titled { public string Id { get; } public string Title { get; } public string Trophy { get; } public Titled(string id, string title, string trophy = null) { Id = id; Title = title; Trophy = trophy; } } public static class FameCatalog { public static readonly Titled[] Bosses = new Titled[9] { new Titled("defeated_eikthyr", "Эйктюр", "TrophyEikthyr"), new Titled("defeated_gdking", "Древний", "TrophyTheElder"), new Titled("defeated_bonemass", "Масса костей", "TrophyBonemass"), new Titled("defeated_dragon", "Модер", "TrophyDragonQueen"), new Titled("defeated_goblinking", "Яглут", "TrophyGoblinKing"), new Titled("defeated_queen", "Королева", "TrophySeekerQueen"), new Titled("defeated_fader", "Фадер", "TrophyFader"), new Titled("defeated_writhan", "Скручень", "TrophyWrithan"), new Titled("defeated_frozenking_p3", "Ледяной король") }; public static readonly Titled[] Trials = new Titled[14] { new Titled("eikthyr", "Поляна Эйктюра"), new Titled("eikthyr_plus", "Поляна Эйктюра+"), new Titled("elder", "Святилище Древнего"), new Titled("elder_plus", "Святилище Древнего+"), new Titled("bonemass", "Логово Массы"), new Titled("bonemass_plus", "Логово Массы+"), new Titled("moder", "Логово Модер"), new Titled("moder_plus", "Логово Модер+"), new Titled("yagluth", "Алтарь Яглута"), new Titled("yagluth_plus", "Алтарь Яглута+"), new Titled("queen", "Логово Королевы"), new Titled("queen_plus", "Логово Королевы+"), new Titled("fader", "Алтарь Фадера"), new Titled("fader_plus", "Алтарь Фадера+") }; public static readonly Titled[] Raids = new Titled[21] { new Titled("army_eikthyr", "Свита Эйктюра"), new Titled("army_theelder", "Грейдворфы"), new Titled("foresttrolls", "Тролли"), new Titled("skeletons", "Скелеты"), new Titled("ghosts", "Призраки"), new Titled("hildirboss1", "Бренна"), new Titled("army_bonemass", "Драугры"), new Titled("blobs", "Слизни"), new Titled("surtlings", "Суртлинги"), new Titled("army_moder", "Дракончики"), new Titled("wolves", "Волки"), new Titled("bats", "Летучие мыши"), new Titled("hildirboss2", "Гейрафа"), new Titled("army_goblin", "Фулинги"), new Titled("hildirboss3", "Зил и Тангр"), new Titled("army_gjall", "Гьяллы"), new Titled("army_seekers", "Искатели"), new Titled("army_charred", "Обугленные"), new Titled("army_charredspawners", "Камни обугленных"), new Titled("army_jotuns", "Ётуны"), new Titled("army_elakingar", "Элакинги") }; public static readonly Titled[] Records = new Titled[6] { new Titled("trial", "Испытания"), new Titled("raid", "Набеги"), new Titled("boss", "Боссы"), new Titled("rewards", "Награды"), new Titled("hunt", "Охота"), new Titled("treasure", "Клады") }; public const int RecordPlaces = 3; } public static class FameLayout { private enum Wall { South, North, West, East } public const float Half = 12f; public const float Height = 8f; public const float SignWidth = 1.2f; public const float SignRow = 0.7f; public const float WallFace = 11.8f; public static readonly float OffsetZ = 300f; public static List<DecorItem> Build(float ax, float ay, float az, Style s) { List<DecorItem> items = new List<DecorItem>(); for (float num = -10f; num < 12f; num += 4f) { for (float num2 = -10f; num2 < 12f; num2 += 4f) { Add("Piece_grausten_floor_4x4", num, -0.25f, num2, 0f); } } float num3 = 0.2f; for (float num4 = -11f; num4 < 12f; num4 += 2f) { for (float num5 = -11f; num5 < 12f; num5 += 2f) { Add(s.FloorTile, num4, 0f, num5, 0f); Add(s.FloorTile, num4, 8f, num5, 0f, 180f); } } for (int i = 0; i < 2; i++) { float y = (float)i * 4f; for (float num6 = -11f; num6 < 12f; num6 += 2f) { Add(s.WallTile, num6, y, -12f, 0f); Add(s.WallTile, num6, y, 12f, 180f); Add(s.WallTile, -12f, y, num6, 90f); Add(s.WallTile, 12f, y, num6, 270f); } } Add(s.Portal, 0f, num3, -10.5f, 0f, 0f, "fame.portal.fame"); Sign(Add, 0f, num3 + 3.9f, Wall.South, FameRoles.Header("gate")); Sign(Add, -7.5f, num3 + 2.9f, Wall.South, FameRoles.Header("orders")); for (int j = 0; j < 3; j++) { float num7 = -10f + 2.5f * (float)j; Add("piece_chest_blackmetal", num7, num3, -11.1f, 0f, 0f, FameRoles.OrderChest(j)); Sign(Add, num7, num3 + 1.9f, Wall.South, FameRoles.OrderSign(j)); } Sign(Add, 7.5f, num3 + 2.9f, Wall.South, FameRoles.Header("treasure")); Sign(Add, 7.5f, num3 + 2.2f, Wall.South, "treasure.sign"); int num8 = FameCatalog.Bosses.Length; for (int k = 0; k < num8; k++) { float x = ((float)k - (float)(num8 - 1) / 2f) * 2.2f; Add("blackmarble_base_1", x, num3 + 1f, 11f, 0f); Add("itemstandh", x, num3 + 2f, 11f, 0f, 0f, FameRoles.BossStand(k)); Add("sign", x, num3 + 1f, 9.93f, 180f, 0f, FameRoles.BossSign(k)); } Sign(Add, 0f, num3 + 6.8f, Wall.North, FameRoles.Header("records")); int num9 = FameCatalog.Records.Length * 3; for (int l = 0; l < num9; l++) { int num10 = l / 3; int num11 = l % 3; float along = ((float)num10 - (float)(FameCatalog.Records.Length - 1) / 2f) * 3.4f; Sign(Add, along, num3 + 4.6f + (float)(2 - num11) * 0.7f, Wall.North, FameRoles.RecordSign(l)); } Grid(Add, Wall.West, FameRoles.Header("trials"), FameCatalog.Trials.Length, FameRoles.TrialSign, num3); Grid(Add, Wall.East, FameRoles.Header("raids"), FameCatalog.Raids.Length, FameRoles.RaidSign, num3); for (float num12 = -9f; num12 <= 9f; num12 += 6f) { Add(s.Lantern, num12, s.LanternHeight, -11.8f, 90f); Add(s.Lantern, num12, s.LanternHeight, 11.8f, 270f); Add(s.Lantern, -11.8f, s.LanternHeight, num12, 180f); Add(s.Lantern, 11.8f, s.LanternHeight, num12, 0f); } float[] array = new float[2] { -10.5f, 10.5f }; foreach (float x2 in array) { float[] array2 = new float[2] { -8.5f, 8.5f }; foreach (float z in array2) { Add(s.Light, x2, num3, z, 0f); } } return items; void Add(string prefab, float num13, float num14, float num15, float rotY, float rotX = 0f, string role = null) { items.Add(new DecorItem(prefab, ax + num13, ay + num14, az + num15, rotY, rotX, role)); } } private static void Sign(Action<string, float, float, float, float, float, string> add, float along, float y, Wall wall, string role) { switch (wall) { case Wall.South: add("sign", along, y, -11.7300005f, 0f, 0f, role); break; case Wall.North: add("sign", along, y, 11.7300005f, 180f, 0f, role); break; case Wall.West: add("sign", -11.7300005f, y, along, 90f, 0f, role); break; default: add("sign", 11.7300005f, y, along, 270f, 0f, role); break; } } private static void Grid(Action<string, float, float, float, float, float, string> add, Wall wall, string header, int count, Func<int, string> role, float g) { Sign(add, 0f, g + 5.2f, wall, header); int num = (count + 6 - 1) / 6; for (int i = 0; i < count; i++) { int num2 = i / 6; int num3 = i % 6; float along = ((float)num2 - (float)(num - 1) / 2f) * 1.2f * 2f; Sign(add, along, g + 4.4f - (float)num3 * 0.7f, wall, role(i)); } } } public static class FameBoard { public const int SignLimit = 50; public static Dictionary<string, string> Texts(IList<ChronicleEntry> chronicle, ICollection<string> worldKeys, IList<Leader> rewardLeaders) { Dictionary<string, string> dictionary = new Dictionary<string, string> { [FameRoles.Header("gate")] = "Чертог славы", [FameRoles.Header("orders")] = "Заказы", [FameRoles.Header("treasure")] = "Клад", [FameRoles.Header("records")] = "Рекорды", [FameRoles.Header("trials")] = "Испытания", [FameRoles.Header("raids")] = "Набеги" }; Dictionary<string, FirstVictory> dictionary2 = ChronicleStats.Firsts(chronicle, "boss"); for (int i = 0; i < FameCatalog.Bosses.Length; i++) { Titled titled = FameCatalog.Bosses[i]; FirstVictory value; string s = ((!dictionary2.TryGetValue(titled.Id, out value)) ? (worldKeys.Contains(titled.Id) ? (titled.Title + " · побеждён до летописи") : (titled.Title + " · не побеждён")) : Line(titled.Title, value)); dictionary[FameRoles.BossSign(i)] = Fit(s); } Fill(dictionary, chronicle, "trial", FameCatalog.Trials, FameRoles.TrialSign); Fill(dictionary, chronicle, "raid", FameCatalog.Raids, FameRoles.RaidSign); for (int j = 0; j < FameCatalog.Records.Length; j++) { Titled titled2 = FameCatalog.Records[j]; IList<Leader> list2; if (!(titled2.Id == "rewards")) { IList<Leader> list = ChronicleStats.Leaders(chronicle, titled2.Id); list2 = list; } else { list2 = rewardLeaders; } IList<Leader> list3 = list2; for (int k = 0; k < 3; k++) { string text = ((k == 0) ? (titled2.Title + ": ") : (k + 1 + ". ")); text = ((k >= list3.Count) ? (text + "—") : (text + ((k == 0) ? "1. " : "") + list3[k].Name + " — " + list3[k].Count.ToString(CultureInfo.InvariantCulture))); dictionary[FameRoles.RecordSign(j * 3 + k)] = Fit(text); } } return dictionary; } public static Dictionary<string, string> Trophies(ICollection<string> worldKeys) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); for (int i = 0; i < FameCatalog.Bosses.Length; i++) { Titled titled = FameCatalog.Bosses[i]; dictionary[FameRoles.BossStand(i)] = ((titled.Trophy != null && worldKeys.Contains(titled.Id)) ? titled.Trophy : ""); } return dictionary; } private static void Fill(Dictionary<string, string> t, IList<ChronicleEntry> chronicle, string kind, Titled[] catalog, Func<int, string> role) { Dictionary<string, FirstVictory> dictionary = ChronicleStats.Firsts(chronicle, kind); for (int i = 0; i < catalog.Length; i++) { t[role(i)] = Fit(dictionary.TryGetValue(catalog[i].Id, out var value) ? Line(catalog[i].Title, value) : (catalog[i].Title + ": —")); } } internal static string Line(string title, FirstVictory f) { string text = ShortDate(f.Date); string text2 = ((text != "") ? (" · " + text) : "") + ((f.Count > 1) ? (" ×" + f.Count.ToString(CultureInfo.InvariantCulture)) : ""); string text3 = title + " · "; int num = 50 - text3.Length - text2.Length; string text4 = ((f.Names.Count > 0 && num > 3) ? ChronicleStats.Names(f.Names, num) : ((f.Names.Count == 0) ? "до летописи" : "")); return text3 + text4 + text2; } internal static string ShortDate(string iso) { if (!DateTime.TryParseExact(iso, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)) { return ""; } return result.ToString("dd.MM.yy", CultureInfo.InvariantCulture); } internal static string Fit(string s) { if (s.Length > 50) { return s.Substring(0, 49) + "…"; } return s; } } public struct TilePos { public float X; public float Y; public float Z; public TilePos(float x, float y, float z) { X = x; Y = y; Z = z; } } public sealed class HallBounds { public const float Tile = 4f; public const float FloorHalfThickness = 0.25f; public const float CeilingAbove = 4.5f; public const float WallHalfThickness = 0.2f; public float MinX; public float MaxX; public float MinZ; public float MaxZ; public float FloorTop; public float CeilingBottom; public int TilesX; public int TilesZ; public float CenterX => (MinX + MaxX) / 2f; public float CenterZ => (MinZ + MaxZ) / 2f; public float Height => CeilingBottom - FloorTop; public static HallBounds FromFloorTiles(IEnumerable<TilePos> tiles) { List<TilePos> list = tiles.ToList(); if (list.Count == 0) { return null; } float floorY = list.Min((TilePos t) => t.Y); List<TilePos> list2 = list.Where((TilePos t) => Math.Abs(t.Y - floorY) < 0.5f).ToList(); float num = list2.Min((TilePos t) => t.X); float num2 = list2.Max((TilePos t) => t.X); float num3 = list2.Min((TilePos t) => t.Z); float num4 = list2.Max((TilePos t) => t.Z); int num5 = (int)Math.Round((num2 - num) / 4f) + 1; int num6 = (int)Math.Round((num4 - num3) / 4f) + 1; if (num5 < 1 || num6 < 1 || list2.Count != num5 * num6) { return null; } return new HallBounds { MinX = num - 2f, MaxX = num2 + 2f, MinZ = num3 - 2f, MaxZ = num4 + 2f, FloorTop = floorY + 0.25f, CeilingBottom = floorY + 4.5f - 0.25f, TilesX = num5, TilesZ = num6 }; } public string Signature() { return string.Format(CultureInfo.InvariantCulture, "{0:F1},{1:F1},{2:F1},{3:F1},{4:F1}", MinX, MaxX, MinZ, MaxZ, FloorTop); } } public sealed class DecorItem { public string Role { get; } public string Prefab { get; } public float X { get; } public float Y { get; } public float Z { get; } public float RotY { get; } public float RotX { get; } public DecorItem(string prefab, float x, float y, float z, float rotY, float rotX = 0f, string role = null) { Prefab = prefab; X = x; Y = y; Z = z; RotY = rotY; RotX = rotX; Role = role; } } public sealed class Style { public bool FloorTiles = true; public bool WallTiles = true; public bool CeilingTiles = true; public bool Throne = true; public bool Retinue = true; public bool Lights = true; public string FloorTile = "blackmarble_tile_floor_2x2"; public string WallTile = "blackmarble_tile_wall_2x4"; public bool WallTileFlip; public string ThronePrefab = "piece_blackmarble_throne"; public float ThroneRotY = 180f; public string Wolf = "darkwood_wolf"; public string Raven = "darkwood_raven"; public string Gold = "bar_gold_stack"; public string Carpet = "jute_carpet"; public string Light = "piece_groundtorch_mist"; public float Height = 8f; public bool Lanterns = true; public string Lantern = "piece_dvergr_lantern"; public float LanternHeight = 6f; public int LanternEvery = 2; public bool FamePortal = true; public string Portal = "portal_wood"; } public static class HallLayout { public const float TileStep = 2f; public const float TileThickness = 0.2f; public const float WallTileHeight = 4f; public const float PortalMargin = 3.5f; public static List<DecorItem> Build(HallBounds b, Style s) { List<DecorItem> list = new List<DecorItem>(); float y = b.FloorTop + (s.FloorTiles ? 0.2f : 0f); float num = InnerHeight(b, s); bool flag = Raised(b, s); bool flag2 = s.CeilingTiles || flag; if (s.FloorTiles || flag2) { foreach (float item in Steps(b.MinX, b.MaxX)) { foreach (float item2 in Steps(b.MinZ, b.MaxZ)) { if (s.FloorTiles) { list.Add(new DecorItem(s.FloorTile, item, b.FloorTop, item2, 0f)); } if (flag2) { list.Add(new DecorItem(s.FloorTile, item, b.FloorTop + num, item2, 0f, 180f)); } } } } int num2 = (int)Math.Ceiling(num / 4f - 0.01f); for (int i = ((!s.WallTiles) ? 1 : 0); i < num2; i++) { AddWalls(list, b, s, b.FloorTop + (float)i * 4f); } if (s.Lanterns && num >= s.LanternHeight + 1f) { AddLanterns(list, b, s); } if (s.Throne || s.Retinue || s.Lights) { AddThroneGroup(list, b, s, y); } float num3 = 6.5f; if (s.FamePortal && (b.MaxZ - b.MinZ) / 2f - 3.5f >= num3) { list.Add(new DecorItem(s.Portal, b.CenterX, y, b.CenterZ + num3, 180f, 0f, "fame.portal.hub")); } return list; } public static float InnerHeight(HallBounds b, Style s) { return Math.Max(s.Height, b.Height); } public static bool Raised(HallBounds b, Style s) { return s.Height > b.Height + 0.01f; } internal static IEnumerable<float> Steps(float min, float max) { int n = (int)Math.Round((max - min) / 2f); for (int i = 0; i < n; i++) { yield return min + 1f + (float)i * 2f; } } private static void AddWalls(List<DecorItem> items, HallBounds b, Style s, float y) { float num = 0.019999996f; float num2 = (s.WallTileFlip ? 180f : 0f); foreach (float item in Steps(b.MinX, b.MaxX)) { items.Add(new DecorItem(s.WallTile, item, y, b.MinZ + num + (s.WallTileFlip ? 0.2f : 0f), 0f + num2)); items.Add(new DecorItem(s.WallTile, item, y, b.MaxZ - num - (s.WallTileFlip ? 0.2f : 0f), 180f + num2)); } foreach (float item2 in Steps(b.MinZ, b.MaxZ)) { items.Add(new DecorItem(s.WallTile, b.MinX + num + (s.WallTileFlip ? 0.2f : 0f), y, item2, 90f + num2)); items.Add(new DecorItem(s.WallTile, b.MaxX - num - (s.WallTileFlip ? 0.2f : 0f), y, item2, 270f + num2)); } } private static void AddLanterns(List<DecorItem> items, HallBounds b, Style s) { float num = 0.22f; float y = b.FloorTop + s.LanternHeight; int num2 = Math.Max(1, s.LanternEvery); for (int i = 0; i < b.TilesX; i += num2) { float x = b.MinX + 2f + (float)i * 4f; items.Add(new DecorItem(s.Lantern, x, y, b.MinZ + num, 90f)); items.Add(new DecorItem(s.Lantern, x, y, b.MaxZ - num, 270f)); } for (int j = 0; j < b.TilesZ; j += num2) { float z = b.MinZ + 2f + (float)j * 4f; items.Add(new DecorItem(s.Lantern, b.MinX + num, y, z, 180f)); items.Add(new DecorItem(s.Lantern, b.MaxX - num, y, z, 0f)); } } private static void AddThroneGroup(List<DecorItem> items, HallBounds b, Style s, float y) { float cx = b.CenterX; float cz = b.CenterZ; float halfX = (b.MaxX - b.MinX) / 2f - 3.5f; float halfZ = (b.MaxZ - b.MinZ) / 2f - 3.5f; if (s.Throne) { Add(s.ThronePrefab, 0f, 1.5f, s.ThroneRotY); for (int i = 0; i < 3; i++) { Add(s.Carpet, 0f, -0.5f - 3f * (float)i, 0f); } } if (s.Retinue) { Add(s.Wolf, -2.2f, 2.5f, 180f); Add(s.Wolf, 2.2f, 2.5f, 180f); Add(s.Raven, -1.4f, 3.6f, 180f); Add(s.Raven, 1.4f, 3.6f, 180f); Add(s.Gold, -3.4f, 2.8f, 0f); Add(s.Gold, 3.4f, 2.8f, 0f); } if (s.Lights) { Add(s.Light, -4.5f, 3f, 0f); Add(s.Light, 4.5f, 3f, 0f); Add(s.Light, -4.5f, -4f, 0f); Add(s.Light, 4.5f, -4f, 0f); } void Add(string prefab, float dx, float dz, float rot) { if (Fits(dx, dz)) { items.Add(new DecorItem(prefab, cx + dx, y, cz + dz, rot)); } } bool Fits(float dx, float dz) { if (Math.Abs(dx) <= halfX) { return Math.Abs(dz) <= halfZ; } return false; } } } }