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 LostCamps v0.1.2
plugins/LostCamps/LostCamps.dll
Decompiled 8 hours ago
The result has been truncated due to the large size, download it to view full contents!
using 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.Serialization; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using LostCamps.Camps; using LostCamps.Logic; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: InternalsVisibleTo("LostCamps.Harness")] [assembly: AssemblyCompany("Laoch")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) Laoch")] [assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyInformationalVersion("0.1.2+c5584f3fa38eecb72d912b28193029689d7c67fb")] [assembly: AssemblyProduct("LostCamps")] [assembly: AssemblyTitle("LostCamps")] [assembly: AssemblyVersion("0.1.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 LostCamps { [BepInPlugin("com.laoch.lostcamps", "Lost Camps", "0.1.2")] public class LostCampsPlugin : BaseUnityPlugin { public const string Guid = "com.laoch.lostcamps"; public const string Version = "0.1.2"; internal static ManualLogSource Log; private Harmony _harmony; private bool _campsFault; private void Awake() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); SleepScreens.Bind(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, directoryName); CampSettings.Bind(((BaseUnityPlugin)this).Config); CampManager.Init(((BaseUnityPlugin)this).Logger, directoryName); _harmony = new Harmony("com.laoch.lostcamps"); _harmony.PatchAll(typeof(LostCampsPlugin).Assembly); } private void Update() { if (_campsFault) { return; } try { CampManager.Update(); } catch (Exception ex) { _campsFault = true; JournalWindow.Hide(); ((BaseUnityPlugin)this).Logger.LogError((object)("Camps stopped after an error: " + ex)); } } private void OnGUI() { if (!_campsFault) { JournalWindow.Draw(); } } private void OnDestroy() { JournalWindow.Hide(); Parchment.Release(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } public enum SleepTextMode { StoicQuotes, VanillaDream, Both, None } public enum SleepArtFit { Fit, Cover } internal static class SleepScreens { private sealed class Source { public SleepEntry Entry; public string Root; } private static ConfigEntry<bool> _enabled; private static ConfigEntry<SleepTextMode> _textMode; private static ConfigEntry<SleepArtFit> _fit; private static ConfigEntry<float> _fontScale; private static ConfigEntry<float> _quoteHeight; private static ManualLogSource _log; private static string _pluginDir; private static string _stateDir; private static readonly Dictionary<string, Source> Entries = new Dictionary<string, Source>(StringComparer.OrdinalIgnoreCase); private static readonly List<string> Pool = new List<string>(); private static readonly ShuffleBag Bag = new ShuffleBag(new Random()); private static readonly HashSet<string> Reported = new HashSet<string>(); private static bool _catalogLoaded; private static DateTime _overrideStamp; private static readonly MethodInfo LoadImageMethod = typeof(ImageConversion).GetMethod("LoadImage", new Type[3] { typeof(Texture2D), typeof(byte[]), typeof(bool) }); private static bool _stateRestored; internal static bool Active { get; private set; } internal static SleepTextMode TextMode { get { if (_textMode != null) { return _textMode.Value; } return SleepTextMode.Both; } } internal static bool Enabled { get { if (_enabled != null) { return _enabled.Value; } return false; } } private static string PackedRoot => Path.Combine(_pluginDir, "sleep"); private static string OverrideRoot => Path.Combine(_stateDir, "sleep"); private static string StatePath => Path.Combine(_stateDir, "sleep-state.txt"); internal static bool KeepVanillaDream { get { if (Enabled && TextMode != SleepTextMode.VanillaDream) { return TextMode == SleepTextMode.Both; } return true; } } internal static void Bind(ConfigFile c, ManualLogSource log, string pluginDir) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown _log = log; _pluginDir = pluginDir; _stateDir = Path.Combine(Paths.ConfigPath, "LostCamps"); _enabled = c.Bind<bool>("SleepScreens", "Enabled", true, "Show a painting and a short line over the sleep screen. Local to you; the server and other players are unaffected. Turn off to get the plain game sleep screen back at any time."); _textMode = c.Bind<SleepTextMode>("SleepScreens", "TextMode", SleepTextMode.StoicQuotes, "StoicQuotes: the line that goes with the painting. VanillaDream: only the game's own dream text (when the night has one). Both: the line and the game's dream text. None: no text."); _fit = c.Bind<SleepArtFit>("SleepScreens", "ImageFit", SleepArtFit.Fit, "Fit shows the whole painting (bars at the sides on ultrawide screens). Cover fills the screen and may crop the edges, including the line."); _fontScale = c.Bind<float>("SleepScreens", "QuoteFontScale", 1f, new ConfigDescription("Size of the line. 1 is the default.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>())); _quoteHeight = c.Bind<float>("SleepScreens", "QuoteHeight", 0.08f, new ConfigDescription("How far up from the bottom of the painting the line sits, as a fraction of the painting's height.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.4f), Array.Empty<object>())); } private static void Once(string key, string message) { if (Reported.Add(key)) { _log.LogWarning((object)("Sleep screens: " + message)); } } private static void LoadCatalog() { DateTime dateTime = SafeStamp(Path.Combine(OverrideRoot, "sleep_screens.json")); if (_catalogLoaded && dateTime == _overrideStamp) { return; } _catalogLoaded = true; _overrideStamp = dateTime; Entries.Clear(); Pool.Clear(); AddFrom(PackedRoot); AddFrom(OverrideRoot); foreach (KeyValuePair<string, Source> entry in Entries) { Pool.Add(entry.Key); } Pool.Sort(StringComparer.Ordinal); _log.LogInfo((object)("Sleep screens: " + Pool.Count + " usable painting(s).")); } private static DateTime SafeStamp(string p) { try { return File.Exists(p) ? File.GetLastWriteTimeUtc(p) : DateTime.MinValue; } catch (Exception) { return DateTime.MinValue; } } private static void AddFrom(string root) { string text = Path.Combine(root, "sleep_screens.json"); if (!File.Exists(text)) { return; } SleepCatalog sleepCatalog; try { sleepCatalog = SleepCatalog.Parse(File.ReadAllText(text)); } catch (Exception ex) { Once("read:" + root, "could not read " + text + " (" + ex.Message + ")"); return; } foreach (string problem in sleepCatalog.Problems) { Once("cat:" + root + problem, problem + " (" + text + ")"); } string text2 = Path.GetFullPath(root).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); char directorySeparatorChar = Path.DirectorySeparatorChar; string value = text2 + directorySeparatorChar; foreach (SleepEntry entry in sleepCatalog.Entries) { string fullPath; try { fullPath = Path.GetFullPath(Path.Combine(root, entry.Image)); } catch (Exception) { continue; } if (!fullPath.StartsWith(value, StringComparison.OrdinalIgnoreCase)) { Once("esc:" + entry.Id, "entry '" + entry.Id + "' points outside its folder; skipped"); } else if (!File.Exists(fullPath)) { Once("miss:" + entry.Id, "entry '" + entry.Id + "' has no image file (" + entry.Image + "); skipped"); } else { Entries[entry.Id] = new Source { Entry = entry, Root = root }; } } } internal static void Begin(SleepText st) { if (!Enabled || (Object)(object)st == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || !Player.m_localPlayer.IsSleeping()) { return; } try { if ((Object)(object)((Component)st).transform.parent == (Object)null) { return; } Transform val = ((Component)st).transform.parent.Find("DAL_SleepArt"); if ((Object)(object)val != (Object)null) { SleepArtView component = ((Component)val).GetComponent<SleepArtView>(); if ((Object)(object)component != (Object)null && component.IsFor(st)) { return; } Object.Destroy((Object)(object)((Component)val).gameObject); } LoadCatalog(); if (Pool.Count == 0) { Once("empty", "no usable paintings; using the vanilla sleep screen."); return; } RestoreState(); for (int i = 0; i < 4; i++) { string text = Bag.Next(Pool); if (text == null) { break; } SaveState(); Texture2D val2 = LoadTexture(Entries[text]); if ((Object)(object)val2 == (Object)null) { Pool.Remove(text); if (Pool.Count == 0) { break; } continue; } SleepArtView.Create(st, val2, Entries[text].Entry, _fit.Value, _fontScale.Value, _quoteHeight.Value, TextMode == SleepTextMode.StoicQuotes || TextMode == SleepTextMode.Both); Active = true; if (_log != null) { _log.LogDebug((object)("Sleep screens: showing " + text)); } break; } } catch (Exception ex) { Once("begin:" + ex.GetType().Name, "could not show a painting (" + ex.Message + "); using the vanilla sleep screen."); } } internal static void End() { Active = false; } private static Texture2D LoadTexture(Source s) { //IL_006e: 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) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown string fullPath = Path.GetFullPath(Path.Combine(s.Root, s.Entry.Image)); try { if (new FileInfo(fullPath).Length > 33554432) { Once("big:" + s.Entry.Id, "'" + s.Entry.Id + "' is larger than 32 MB; skipped"); return null; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { name = "DAL_Sleep_" + s.Entry.Id, wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; if (!Decode(val, File.ReadAllBytes(fullPath))) { Object.Destroy((Object)(object)val); Once("img:" + s.Entry.Id, "'" + s.Entry.Id + "' could not be decoded; skipped"); return null; } return val; } catch (Exception ex) { Once("load:" + s.Entry.Id, "'" + s.Entry.Id + "' could not be loaded (" + ex.Message + "); skipped"); return null; } } private static bool Decode(Texture2D tex, byte[] bytes) { if (LoadImageMethod != null) { return (bool)LoadImageMethod.Invoke(null, new object[3] { tex, bytes, true }); } return false; } private static void RestoreState() { if (_stateRestored) { return; } _stateRestored = true; try { Bag.Restore(File.Exists(StatePath) ? File.ReadAllText(StatePath) : ""); } catch (Exception) { Bag.Reset(); } } private static void SaveState() { try { Directory.CreateDirectory(_stateDir); File.WriteAllText(StatePath, Bag.Serialize()); } catch (Exception ex) { Once("state", "could not save the rotation (" + ex.Message + ")"); } } } internal sealed class SleepArtView : MonoBehaviour { internal const string ObjectName = "DAL_SleepArt"; private RectTransform _root; private RectTransform _art; private RectTransform _quote; private RectTransform _shadow; private TextMeshProUGUI _quoteText; private TextMeshProUGUI _shadowText; private Texture2D _tex; private SleepArtFit _fit; private float _scale; private float _height; private Vector2 _lastSize; private SleepText _owner; internal static void Create(SleepText st, Texture2D tex, SleepEntry entry, SleepArtFit fit, float fontScale, float quoteHeight, bool showQuote) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("DAL_SleepArt", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(((Component)st).transform.parent, false); val.transform.SetSiblingIndex(((Component)st).transform.GetSiblingIndex()); SleepArtView sleepArtView = val.AddComponent<SleepArtView>(); sleepArtView._owner = st; sleepArtView._tex = tex; sleepArtView._fit = fit; sleepArtView._scale = fontScale; sleepArtView._height = quoteHeight; sleepArtView._root = (RectTransform)val.transform; sleepArtView._root.anchorMin = Vector2.zero; sleepArtView._root.anchorMax = Vector2.one; RectTransform root = sleepArtView._root; Vector2 offsetMin = (sleepArtView._root.offsetMax = Vector2.zero); root.offsetMin = offsetMin; GameObject val2 = new GameObject("Art", new Type[2] { typeof(RectTransform), typeof(RawImage) }); val2.transform.SetParent(val.transform, false); sleepArtView._art = (RectTransform)val2.transform; RawImage component = val2.GetComponent<RawImage>(); component.texture = (Texture)(object)tex; ((Graphic)component).raycastTarget = false; if (showQuote && !string.IsNullOrEmpty(entry.Quote)) { TMP_FontAsset font = (((Object)(object)st.m_dreamField != (Object)null) ? st.m_dreamField.font : null); sleepArtView._shadowText = MakeText(val.transform, "QuoteShadow", entry.Quote, font, new Color(0f, 0f, 0f, 0.7f)); sleepArtView._quoteText = MakeText(val.transform, "Quote", entry.Quote, font, new Color(1f, 0.96f, 0.86f, 1f)); sleepArtView._shadow = ((TMP_Text)sleepArtView._shadowText).rectTransform; sleepArtView._quote = ((TMP_Text)sleepArtView._quoteText).rectTransform; } sleepArtView.Layout(); } private static TextMeshProUGUI MakeText(Transform parent, string name, string text, TMP_FontAsset font, Color color) { //IL_0021: 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_0051: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val.transform.SetParent(parent, false); TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>(); if ((Object)(object)font != (Object)null) { ((TMP_Text)component).font = font; } ((TMP_Text)component).text = text; ((Graphic)component).color = color; ((Graphic)component).raycastTarget = false; ((TMP_Text)component).alignment = (TextAlignmentOptions)514; ((TMP_Text)component).textWrappingMode = (TextWrappingModes)1; ((TMP_Text)component).enableAutoSizing = true; ((TMP_Text)component).overflowMode = (TextOverflowModes)1; return component; } internal bool IsFor(SleepText st) { if ((Object)(object)_owner == (Object)(object)st && (Object)(object)st != (Object)null) { return ((Component)st).gameObject.activeInHierarchy; } return false; } private void LateUpdate() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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) if ((Object)(object)_owner == (Object)null || !((Component)_owner).gameObject.activeInHierarchy) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Rect rect = _root.rect; if (((Rect)(ref rect)).size != _lastSize) { Layout(); } } private void Layout() { //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) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) Rect rect = _root.rect; Vector2 val = (_lastSize = ((Rect)(ref rect)).size); if (!(val.x < 1f) && !(val.y < 1f) && !((Object)(object)_tex == (Object)null)) { float num = (float)((Texture)_tex).width / (float)((Texture)_tex).height; float num2; float num3; if (_fit == SleepArtFit.Fit == val.x / val.y >= num) { num2 = val.y; num3 = num2 * num; } else { num3 = val.x; num2 = num3 / num; } if (_fit == SleepArtFit.Fit && num3 > val.x) { num3 = val.x; num2 = num3 / num; } RectTransform art = _art; RectTransform art2 = _art; RectTransform art3 = _art; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); art3.pivot = val2; Vector2 anchorMin = (art2.anchorMax = val2); art.anchorMin = anchorMin; _art.sizeDelta = new Vector2(num3, num2); _art.anchoredPosition = Vector2.zero; if (!((Object)(object)_quote == (Object)null)) { float h = num2 * 0.13f; float w = Mathf.Min(num3 * 0.74f, val.x * 0.92f); float num4 = (0f - num2) / 2f + num2 * _height; float num5 = (0f - val.y) / 2f + val.y * 0.03f; num4 = Mathf.Max(num4, num5); float num6 = Mathf.Clamp(num2 * 0.04f * _scale, 12f, 90f); Place(_quote, _quoteText, w, h, num4, num6, Vector2.zero); Place(_shadow, _shadowText, w, h, num4, num6, new Vector2(num6 * 0.06f, (0f - num6) * 0.06f)); } } } private static void Place(RectTransform rt, TextMeshProUGUI t, float w, float h, float bottom, float size, Vector2 offset) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_0037: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); rt.anchorMax = val; rt.anchorMin = val; rt.pivot = new Vector2(0.5f, 0f); rt.sizeDelta = new Vector2(w, h); rt.anchoredPosition = new Vector2(0f, bottom) + offset; ((TMP_Text)t).fontSizeMax = size; ((TMP_Text)t).fontSizeMin = size * 0.55f; ((TMP_Text)t).fontSize = size; } private void OnDisable() { Object.Destroy((Object)(object)((Component)this).gameObject); } private void OnDestroy() { SleepScreens.End(); if ((Object)(object)_tex != (Object)null) { Object.Destroy((Object)(object)_tex); _tex = null; } } } [HarmonyPatch(typeof(SleepText), "OnEnable")] internal static class SleepTextOnEnable { private static void Postfix(SleepText __instance) { SleepScreens.Begin(__instance); } } [HarmonyPatch(typeof(SleepText), "ShowDreamText")] internal static class SleepTextShowDream { private static void Postfix(SleepText __instance) { if (!SleepScreens.KeepVanillaDream && (Object)(object)__instance != (Object)null && (Object)(object)__instance.m_dreamField != (Object)null) { ((Behaviour)__instance.m_dreamField).enabled = false; } } } } namespace LostCamps.Logic { [DataContract] public sealed class LorePack { public const int MaxEntries = 1000; public const int MaxTextLength = 6000; public const int MaxDay = 1000000; [DataMember] public int schema = 1; [DataMember] public string id = ""; [DataMember] public string title = ""; [DataMember] public List<LoreEntry> entries = new List<LoreEntry>(); public void Validate() { if (schema != 1 || !Rules.ValidId(id)) { throw new InvalidDataException("Unsupported pack schema or ID."); } if (entries == null || entries.Count < 1 || entries.Count > 1000) { throw new InvalidDataException("A pack needs 1.." + 1000 + " entries."); } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal); foreach (LoreEntry entry in entries) { if (entry == null || !Rules.ValidId(entry.id) || !hashSet.Add(entry.id)) { throw new InvalidDataException("Missing or duplicate entry ID."); } if (entry.day < 0 || entry.day > 1000000 || string.IsNullOrWhiteSpace(entry.author) || entry.author.Length > 80 || string.IsNullOrWhiteSpace(entry.text) || entry.text.Length > 6000) { throw new InvalidDataException("Invalid day, author or text: " + entry.id); } if (entry.targets == null || entry.targets.Count == 0 || entry.targets.Any((string t) => !Rules.ValidId(t))) { throw new InvalidDataException("Entry needs explicit target location IDs: " + entry.id); } if (!hashSet2.Add(Regex.Replace(entry.text.Trim(), "\\s+", " "))) { throw new InvalidDataException("Repeated lore body: " + entry.id); } foreach (Match item in Regex.Matches(entry.text, "\\{([^{}]+)\\}")) { if (!Rules.Tokens.Contains(item.Groups[1].Value)) { throw new InvalidDataException("Unknown placeholder: " + item.Value); } } if (Regex.Replace(entry.text, "\\{[^{}]+\\}", "").IndexOfAny(new char[2] { '{', '}' }) >= 0) { throw new InvalidDataException("Unbalanced placeholders."); } } } } [DataContract] public sealed class LoreEntry { [DataMember] public string id = ""; [DataMember] public int day; [DataMember] public string author = ""; [DataMember] public string text = ""; [DataMember] public List<string> targets = new List<string>(); } [DataContract] public sealed class CampRecord { public const int MaxNearbyLength = 800; [DataMember] public string id = ""; [DataMember] public string pack = ""; [DataMember] public string entry = ""; [DataMember] public int day; [DataMember] public string author = ""; [DataMember] public string text = ""; [DataMember] public string target = ""; [DataMember] public string nearby = ""; [DataMember] public double x; [DataMember] public double z; [DataMember] public double targetX; [DataMember] public double targetZ; [DataMember] public string status = "reserved"; public bool IsPlausible() { if (Rules.ValidId(id) && Rules.ValidId(pack) && Rules.ValidId(entry) && day >= 0 && day <= 1000000 && author != null && author.Length <= 80 && text != null && text.Length <= 6000) { if (nearby != null) { return nearby.Length <= 800; } return true; } return false; } } public static class CampStatus { public const string Reserved = "reserved"; public const string Placed = "placed"; public const string Removed = "removed"; public const string Failed = "failed"; } [DataContract] public sealed class WorldState { [DataMember] public int schema = 1; [DataMember] public string world = ""; [DataMember] public List<CampRecord> camps = new List<CampRecord>(); public void Validate(string expectedWorld) { if (schema != 1 || world != expectedWorld || camps == null) { throw new InvalidDataException("World-state identity or schema mismatch."); } if (camps.Select((CampRecord c) => c.id).Distinct().Count() != camps.Count || camps.Select((CampRecord c) => c.pack + ":" + c.entry).Distinct().Count() != camps.Count) { throw new InvalidDataException("Duplicate camp or lore assignment."); } foreach (CampRecord camp in camps) { if (!camp.IsPlausible() || !Rules.Finite(camp.x) || !Rules.Finite(camp.z) || !Rules.Finite(camp.targetX) || !Rules.Finite(camp.targetZ) || (camp.status != "reserved" && camp.status != "placed" && camp.status != "removed" && camp.status != "failed")) { throw new InvalidDataException("Invalid camp record."); } } } } [DataContract] public sealed class Journal { public const int MaxEntries = 1000; [DataMember] public int schema = 1; [DataMember] public string world = ""; [DataMember] public string character = ""; [DataMember] public List<CampRecord> entries = new List<CampRecord>(); public bool Learn(CampRecord c) { if (entries.Any((CampRecord e) => e.pack == c.pack && e.entry == c.entry)) { return false; } entries.Add(c); return true; } public List<CampRecord> Ordered() { return entries.OrderBy((CampRecord c) => c.day).ThenBy<CampRecord, string>((CampRecord c) => c.pack, StringComparer.Ordinal).ThenBy<CampRecord, string>((CampRecord c) => c.entry, StringComparer.Ordinal) .ToList(); } public void Validate(string expectedWorld, string expectedCharacter) { if (schema != 1 || world != expectedWorld || character != expectedCharacter || entries == null || entries.Count > 1000) { throw new InvalidDataException("Journal identity or schema invalid."); } if (entries.Any((CampRecord e) => e == null || !e.IsPlausible())) { throw new InvalidDataException("Journal holds an invalid entry."); } } } public static class Rules { public static readonly HashSet<string> Tokens = new HashSet<string> { "direction", "distance_phrase", "distance_m", "location_name" }; public static bool ValidId(string s) { if (s != null) { return Regex.IsMatch(s, "\\A[a-zA-Z0-9_-]{1,80}\\z"); } return false; } public static bool Finite(double n) { if (!double.IsNaN(n)) { return !double.IsInfinity(n); } return false; } public static double Distance(double x, double z, double tx, double tz) { return Math.Sqrt((tx - x) * (tx - x) + (tz - z) * (tz - z)); } public static string Direction(double dx, double dz) { if (!Finite(dx) || !Finite(dz) || (dx == 0.0 && dz == 0.0)) { throw new ArgumentException("Invalid bearing."); } return (new string[8] { "north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest" })[(int)Math.Floor((Math.Atan2(dx, dz) * 180.0 / Math.PI + 360.0 + 22.5) / 45.0) % 8]; } public static string Resolve(string text, double x, double z, double tx, double tz, string locationName) { double num = Distance(x, z, tx, tz); if (!Finite(num) || num < 1.0) { throw new ArgumentException("Invalid target distance."); } return text.Replace("{direction}", Direction(tx - x, tz - z)).Replace("{distance_phrase}", (num < 200.0) ? "a short walk" : ((num < 500.0) ? "a longer walk" : "a considerable journey")).Replace("{distance_m}", (Math.Round(num / 10.0) * 10.0).ToString(CultureInfo.InvariantCulture)) .Replace("{location_name}", locationName); } public static LoreEntry Next(LorePack pack, WorldState state, string target, string stableKey) { HashSet<string> used = new HashSet<string>(from c in state.camps where c.pack == pack.id select c.entry); return (from e in pack.entries where !used.Contains(e.id) && e.targets.Contains(target) orderby Hash(stableKey + ":" + e.id) select e).ThenBy<LoreEntry, string>((LoreEntry e) => e.id, StringComparer.Ordinal).FirstOrDefault(); } public static uint Hash(string s) { uint num = 2166136261u; foreach (char c in s) { num ^= c; num *= 16777619; } return num; } } public static class JsonFiles { public static string Encode<T>(T value) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) using MemoryStream memoryStream = new MemoryStream(); ((XmlObjectSerializer)new DataContractJsonSerializer(typeof(T))).WriteObject((Stream)memoryStream, (object)value); return Encoding.UTF8.GetString(memoryStream.ToArray()); } public static T Decode<T>(string json) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) using MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)); return (T)((XmlObjectSerializer)new DataContractJsonSerializer(typeof(T))).ReadObject((Stream)memoryStream); } public static T Read<T>(string path) { return Decode<T>(File.ReadAllText(path)); } public static void Save<T>(string path, T value) { Directory.CreateDirectory(Path.GetDirectoryName(path)); string text = path + ".tmp"; using (FileStream fileStream = new FileStream(text, FileMode.Create, FileAccess.Write, FileShare.None)) { byte[] bytes = Encoding.UTF8.GetBytes(Encode(value)); fileStream.Write(bytes, 0, bytes.Length); fileStream.Flush(flushToDisk: true); } if (File.Exists(path)) { File.Replace(text, path, path + ".bak"); } else { File.Move(text, path); } } } public static class MiniJson { private const int MaxDepth = 32; public static object Parse(string text) { if (text == null) { throw new FormatException("no text"); } int i = 0; Skip(text, ref i); object result = Value(text, ref i, 0); Skip(text, ref i); if (i != text.Length) { throw new FormatException("unexpected text after the value at " + i); } return result; } private static void Skip(string s, ref int i) { while (i < s.Length && (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n' || s[i] == '\ufeff')) { i++; } } private static object Value(string s, ref int i, int depth) { if (depth > 32) { throw new FormatException("nested too deeply"); } if (i >= s.Length) { throw new FormatException("unexpected end"); } switch (s[i]) { case '{': return Obj(s, ref i, depth); case '[': return Arr(s, ref i, depth); case '"': return Str(s, ref i); default: if (Match(s, ref i, "true")) { return true; } if (Match(s, ref i, "false")) { return false; } if (Match(s, ref i, "null")) { return null; } return Num(s, ref i); } } private static bool Match(string s, ref int i, string word) { if (string.CompareOrdinal(s, i, word, 0, word.Length) != 0) { return false; } i += word.Length; return true; } private static Dictionary<string, object> Obj(string s, ref int i, int depth) { Dictionary<string, object> dictionary = new Dictionary<string, object>(); i++; Skip(s, ref i); if (i < s.Length && s[i] == '}') { i++; return dictionary; } while (true) { Skip(s, ref i); if (i >= s.Length || s[i] != '"') { throw new FormatException("expected a key at " + i); } string key = Str(s, ref i); Skip(s, ref i); if (i >= s.Length || s[i] != ':') { throw new FormatException("expected ':' at " + i); } i++; Skip(s, ref i); dictionary[key] = Value(s, ref i, depth + 1); Skip(s, ref i); if (i >= s.Length) { throw new FormatException("unterminated object"); } if (s[i] != ',') { break; } i++; } if (s[i] == '}') { i++; return dictionary; } throw new FormatException("expected ',' or '}' at " + i); } private static List<object> Arr(string s, ref int i, int depth) { List<object> list = new List<object>(); i++; Skip(s, ref i); if (i < s.Length && s[i] == ']') { i++; return list; } while (true) { Skip(s, ref i); list.Add(Value(s, ref i, depth + 1)); Skip(s, ref i); if (i >= s.Length) { throw new FormatException("unterminated array"); } if (s[i] != ',') { break; } i++; } if (s[i] == ']') { i++; return list; } throw new FormatException("expected ',' or ']' at " + i); } private static string Str(string s, ref int i) { StringBuilder stringBuilder = new StringBuilder(); i++; while (true) { if (i >= s.Length) { throw new FormatException("unterminated string"); } char c = s[i++]; if (c == '"') { return stringBuilder.ToString(); } if (c < ' ') { throw new FormatException("control character in string"); } if (c != '\\') { stringBuilder.Append(c); continue; } if (i >= s.Length) { break; } char c2 = s[i++]; switch (c2) { case '"': stringBuilder.Append('"'); break; case '\\': stringBuilder.Append('\\'); break; case '/': stringBuilder.Append('/'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; case 'u': { if (i + 4 > s.Length) { throw new FormatException("bad \\u escape"); } if (!int.TryParse(s.Substring(i, 4), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var result)) { throw new FormatException("bad \\u escape"); } stringBuilder.Append((char)result); i += 4; break; } default: throw new FormatException("bad escape \\" + c2); } } throw new FormatException("unterminated escape"); } private static double Num(string s, ref int i) { int num = i; while (i < s.Length && "+-0123456789.eE".IndexOf(s[i]) >= 0) { i++; } if (i == num || !double.TryParse(s.Substring(num, i - num), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || double.IsNaN(result) || double.IsInfinity(result)) { throw new FormatException("bad value at " + num); } return result; } } [DataContract] public sealed class PoiRule { [DataMember] public string prefix = ""; [DataMember] public string phrase = ""; } [DataContract] public sealed class PoiPack { public const int MaxRules = 500; [DataMember] public int schema = 1; [DataMember] public List<PoiRule> rules = new List<PoiRule>(); public void Validate() { if (schema != 1 || rules == null || rules.Count > 500) { throw new InvalidDataException("Unsupported place list."); } foreach (PoiRule rule in rules) { if (rule == null || !Rules.ValidId(rule.prefix) || string.IsNullOrWhiteSpace(rule.phrase) || rule.phrase.Length > 60 || rule.phrase.IndexOfAny(new char[3] { '{', '}', '\n' }) >= 0) { throw new InvalidDataException("A place rule is missing its prefix or phrase, or the phrase is not plain text."); } } } } public struct Place { public string Prefab; public double X; public double Z; public Place(string prefab, double x, double z) { Prefab = prefab; X = x; Z = z; } } public static class Pois { public static readonly PoiRule[] Defaults = new PoiRule[31] { new PoiRule { prefix = "SunkenCrypt", phrase = "a sunken crypt" }, new PoiRule { prefix = "Crypt", phrase = "a burial chamber" }, new PoiRule { prefix = "MountainCave", phrase = "a cave in the mountains" }, new PoiRule { prefix = "TrollCave", phrase = "a troll cave" }, new PoiRule { prefix = "StoneTowerRuins", phrase = "a ruined tower" }, new PoiRule { prefix = "Ruin", phrase = "old ruins" }, new PoiRule { prefix = "StoneHenge", phrase = "a ring of standing stones" }, new PoiRule { prefix = "WoodVillage", phrase = "an old village" }, new PoiRule { prefix = "WoodHouse", phrase = "a tumbledown house" }, new PoiRule { prefix = "SwampHut", phrase = "a hut in the swamp" }, new PoiRule { prefix = "AbandonedLogCabin", phrase = "an abandoned log cabin" }, new PoiRule { prefix = "GoblinCamp", phrase = "a camp of the Fuling" }, new PoiRule { prefix = "DrakeNest", phrase = "a drake's nest" }, new PoiRule { prefix = "FireHole", phrase = "a burning pit" }, new PoiRule { prefix = "TarPit", phrase = "a tar pit" }, new PoiRule { prefix = "Greydwarf_camp", phrase = "a Greydwarf camp" }, new PoiRule { prefix = "StoneHouse", phrase = "an old stone house" }, new PoiRule { prefix = "CharredRuins", phrase = "burnt ruins" }, new PoiRule { prefix = "Dolmen", phrase = "a dolmen" }, new PoiRule { prefix = "ShipWreck", phrase = "a shipwreck" }, new PoiRule { prefix = "Grave", phrase = "a lonely grave" }, new PoiRule { prefix = "StoneCircle", phrase = "a circle of old stones" }, new PoiRule { prefix = "WoodFarm", phrase = "an abandoned farm" }, new PoiRule { prefix = "BearCave", phrase = "a bear's den" }, new PoiRule { prefix = "MountainGrave", phrase = "a high grave" }, new PoiRule { prefix = "StoneTower", phrase = "an old stone tower" }, new PoiRule { prefix = "CharredTowerRuins", phrase = "a burnt tower" }, new PoiRule { prefix = "LumberCamp", phrase = "a lumber camp" }, new PoiRule { prefix = "FrozenShip", phrase = "a ship frozen in the ice" }, new PoiRule { prefix = "Excavation", phrase = "a dig site" }, new PoiRule { prefix = "Mistlands_Excavation", phrase = "a dig site" } }; public static string Classify(string prefab, IList<PoiRule> rules) { if (string.IsNullOrEmpty(prefab) || prefab.IndexOf("_sunk", StringComparison.Ordinal) >= 0) { return null; } foreach (PoiRule rule in rules) { if (prefab.StartsWith(rule.prefix, StringComparison.Ordinal)) { return rule.phrase; } } return null; } public static string Describe(double x, double z, double targetX, double targetZ, IEnumerable<Place> places, IList<PoiRule> rules, double radius, int maxKinds) { Dictionary<string, KeyValuePair<double, Place>> dictionary = new Dictionary<string, KeyValuePair<double, Place>>(StringComparer.Ordinal); foreach (Place place in places) { string text = Classify(place.Prefab, rules); if (text != null) { double num = Rules.Distance(x, z, place.X, place.Z); if (!(num < 25.0) && !(num > radius) && !(Rules.Distance(targetX, targetZ, place.X, place.Z) < 30.0) && (!dictionary.TryGetValue(text, out var value) || num < value.Key)) { dictionary[text] = new KeyValuePair<double, Place>(num, place); } } } List<KeyValuePair<string, KeyValuePair<double, Place>>> list = dictionary.OrderBy((KeyValuePair<string, KeyValuePair<double, Place>> kv) => kv.Value.Key).ThenBy<KeyValuePair<string, KeyValuePair<double, Place>>, string>((KeyValuePair<string, KeyValuePair<double, Place>> kv) => kv.Key, StringComparer.Ordinal).Take(Math.Max(0, maxKinds)) .ToList(); if (list.Count == 0) { return ""; } IEnumerable<string> source = list.Select((KeyValuePair<string, KeyValuePair<double, Place>> kv) => kv.Key + " to the " + Rules.Direction(kv.Value.Value.X - x, kv.Value.Value.Z - z) + ", " + Phrase(kv.Value.Key)); return "Also seen near this camp: " + string.Join("; ", source.ToArray()) + "."; } private static string Phrase(double distance) { if (!(distance < 200.0)) { if (!(distance < 500.0)) { return "a considerable journey"; } return "a longer walk"; } return "a short walk"; } } public sealed class ShuffleBag { private readonly Random _rng; private readonly List<string> _bag = new List<string>(); private string _last; public string Last => _last; public int Remaining => _bag.Count; public ShuffleBag(Random rng) { _rng = rng ?? new Random(); } public string Next(IList<string> pool) { if (pool == null || pool.Count == 0) { _bag.Clear(); return null; } HashSet<string> valid = new HashSet<string>(pool); _bag.RemoveAll((string id) => !valid.Contains(id)); if (_bag.Count == 0) { Refill(pool); } string text = Take(); if (text == _last && valid.Count > 1) { if (_bag.Count == 0) { Refill(pool); } else { _bag.Insert(0, text); } int index = _bag.FindLastIndex((string id) => id != _last); text = _bag[index]; _bag.RemoveAt(index); } _last = text; return text; } private string Take() { string result = _bag[_bag.Count - 1]; _bag.RemoveAt(_bag.Count - 1); return result; } private void Refill(IList<string> pool) { _bag.Clear(); HashSet<string> hashSet = new HashSet<string>(); foreach (string item in pool) { if (hashSet.Add(item)) { _bag.Add(item); } } for (int num = _bag.Count - 1; num > 0; num--) { int index = _rng.Next(num + 1); string value = _bag[num]; _bag[num] = _bag[index]; _bag[index] = value; } } public void Reset() { _bag.Clear(); _last = null; } public string Serialize() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("last=").Append(_last ?? "").Append('\n'); foreach (string item in _bag) { stringBuilder.Append("bag=").Append(item).Append('\n'); } return stringBuilder.ToString(); } public void Restore(string text) { _bag.Clear(); _last = null; if (string.IsNullOrEmpty(text)) { return; } string[] array = text.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.StartsWith("last=", StringComparison.Ordinal)) { string text3 = text2.Substring(5); _last = ((text3.Length == 0 || text3.Length > 64) ? null : text3); } else if (text2.StartsWith("bag=", StringComparison.Ordinal) && _bag.Count < 500) { string text4 = text2.Substring(4); if (text4.Length > 0 && text4.Length <= 64) { _bag.Add(text4); } } } } } public sealed class SleepEntry { public string Id; public string Image; public string Title; public string Quote; public string Attribution; public string Sha256; public int Width; public int Height; } public sealed class SleepCatalog { public const int MaxEntries = 500; public const int MaxQuoteLength = 240; public const int MaxTitleLength = 80; public const int MaxFileBytes = 1048576; private static readonly Regex IdRx = new Regex("^[A-Za-z0-9_\\-]{1,64}$", RegexOptions.Compiled); private static readonly Regex ShaRx = new Regex("^[0-9a-fA-F]{64}$", RegexOptions.Compiled); public readonly List<SleepEntry> Entries = new List<SleepEntry>(); public readonly List<string> Problems = new List<string>(); public static bool IsSafeRelativeImagePath(string p) { if (string.IsNullOrEmpty(p) || p.Length > 200) { return false; } if (p.IndexOf('\0') >= 0 || p.IndexOf(':') >= 0 || p[0] == '/' || p[0] == '\\') { return false; } string[] array = p.Split('/', '\\'); foreach (string text in array) { if (text.Length == 0 || text == "." || text == "..") { return false; } } string text2 = p.ToLowerInvariant(); if (!text2.EndsWith(".png") && !text2.EndsWith(".jpg")) { return text2.EndsWith(".jpeg"); } return true; } public static SleepCatalog Parse(string json) { SleepCatalog sleepCatalog = new SleepCatalog(); if (json == null) { sleepCatalog.Problems.Add("catalog is missing"); return sleepCatalog; } if (json.Length > 1048576) { sleepCatalog.Problems.Add("catalog is larger than " + 1048576 + " bytes; ignored"); return sleepCatalog; } object obj; try { obj = MiniJson.Parse(json); } catch (FormatException ex) { sleepCatalog.Problems.Add("catalog is not valid JSON: " + ex.Message); return sleepCatalog; } List<object> list = ((obj is Dictionary<string, object> dictionary && dictionary.ContainsKey("entries")) ? (dictionary["entries"] as List<object>) : null); if (list == null) { sleepCatalog.Problems.Add("catalog has no \"entries\" list"); return sleepCatalog; } if (list.Count > 500) { sleepCatalog.Problems.Add("catalog has more than " + 500 + " entries; only the first " + 500 + " are read"); } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < list.Count && i < 500; i++) { Dictionary<string, object> dictionary2 = list[i] as Dictionary<string, object>; string text = "entry " + (i + 1); if (dictionary2 == null) { sleepCatalog.Problems.Add(text + " is not an object; skipped"); continue; } string text2 = Text(dictionary2, "id"); if (text2 == null || !IdRx.IsMatch(text2)) { sleepCatalog.Problems.Add(text + " has a missing or invalid id; skipped"); continue; } text = "entry '" + text2 + "'"; if (!hashSet.Add(text2)) { sleepCatalog.Problems.Add(text + " repeats an earlier id; skipped"); continue; } string text3 = Text(dictionary2, "image"); if (!IsSafeRelativeImagePath(text3)) { sleepCatalog.Problems.Add(text + " has an unsafe or missing image path; skipped"); continue; } string text4 = Text(dictionary2, "quote") ?? ""; if (text4.Length > 240) { sleepCatalog.Problems.Add(text + " quote is longer than " + 240 + " characters; skipped"); continue; } string text5 = text4; foreach (char c in text5) { if (c < ' ' && c != '\n') { sleepCatalog.Problems.Add(text + " quote has control characters; skipped"); text4 = null; break; } } if (text4 == null) { continue; } string text6 = Text(dictionary2, "title") ?? ""; if (text6.Length > 80) { text6 = text6.Substring(0, 80); } if (!Dim(dictionary2, "width", out var value) || !Dim(dictionary2, "height", out var value2)) { sleepCatalog.Problems.Add(text + " has an invalid width or height; skipped"); continue; } string text7 = Text(dictionary2, "sha256"); if (text7 != null && !ShaRx.IsMatch(text7)) { sleepCatalog.Problems.Add(text + " has a malformed sha256; skipped"); continue; } string text8 = Text(dictionary2, "attribution"); if (text8 != null && text8.Length > 80) { text8 = text8.Substring(0, 80); } sleepCatalog.Entries.Add(new SleepEntry { Id = text2, Image = text3.Replace('\\', '/'), Title = text6, Quote = text4, Attribution = (string.IsNullOrWhiteSpace(text8) ? null : text8), Width = value, Height = value2, Sha256 = text7?.ToLowerInvariant() }); } return sleepCatalog; } private static string Text(Dictionary<string, object> d, string key) { if (!d.TryGetValue(key, out var value)) { return null; } return value as string; } private static bool Dim(Dictionary<string, object> d, string key, out int value) { value = 0; if (!d.TryGetValue(key, out var value2) || !(value2 is double num)) { return false; } if (num < 1.0 || num > 16384.0 || num != Math.Floor(num)) { return false; } value = (int)num; return true; } } } namespace LostCamps.Camps { internal static class CampCommands { internal const string Usage = "lostcamps status | journal | reload | cleanup | cleanup confirm TOKEN"; internal static void Run(ConsoleEventArgs args) { string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "status"); try { if (text == "journal") { JournalWindow.Toggle(); args.Context.AddString(JournalWindow.Ready ? "Journal toggled." : "No journal yet: load into a world first."); return; } if (text == "status") { args.Context.AddString(CampManager.Status + "; camps on record " + CampManager.AssignedCamps + "; notes in your journal " + JournalWindow.Count); return; } if (!CampWorld.IsServer) { throw new InvalidOperationException("Host or local game only. On a dedicated server use the Maintenance settings in its config file."); } if (text == "reload") { CampSettings.File.Reload(); CampManager.ReloadLore(); args.Context.AddString("Reloaded the config and the story pack."); return; } if (text == "cleanup" && args.Length == 2) { CampManager.PrepareCleanup(); args.Context.AddString(CampManager.Status); return; } if (text == "cleanup" && args.Length == 4 && args[2].Equals("confirm", StringComparison.OrdinalIgnoreCase)) { CampManager.RunCleanup(args[3]); args.Context.AddString(CampManager.Status); return; } throw new ArgumentException("Commands: lostcamps status | journal | reload | cleanup | cleanup confirm TOKEN"); } catch (Exception ex) { LostCampsPlugin.Log.LogWarning((object)ex.Message); args.Context.AddString(ex.Message); } } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] internal static class RegisterCampCommand { private static void Postfix() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) new ConsoleCommand("lostcamps", "lostcamps status | journal | reload | cleanup | cleanup confirm TOKEN", new ConsoleEvent(CampCommands.Run), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } internal static class CampManager { private const float CleanupRadius = 18f; private static ManualLogSource _log; private static string _root; private static string _bundledLore; private static LorePack _pack; private static IList<PoiRule> _poiRules = Pois.Defaults; private static List<Place> _places = new List<Place>(); private static WorldState _state; private static string _world = ""; private static string _statePath = ""; private static List<Target> _targets = new List<Target>(); private static bool _targetsBuilt; private static bool _placementFault; private static int _cursor; private static float _nextScan; private static DateTime _configStamp; private static string _cleanupToken = ""; private static float _cleanupExpires; private static readonly List<GameObject> CleanupObjects = new List<GameObject>(); private static readonly HashSet<string> Warned = new HashSet<string>(); internal static string Status = "Starting"; internal const float NearMeters = 80f; private static int _refusalsLogged; private static readonly HashSet<int> _failedSlots = new HashSet<int>(); internal const int Variants = 4; internal static int AssignedCamps { get { if (_state != null) { return _state.camps.Count; } return 0; } } internal static string Root => _root; internal static LorePack Pack => _pack; internal static IList<Target> CurrentTargets { get { EnsureTargets(); return _targets; } } internal static IList<CampRecord> Camps { get { if (_state != null) { return _state.camps; } return new List<CampRecord>(); } } internal static bool PlacementFaulted => _placementFault; internal static string CleanupTokenForTest => _cleanupToken; internal static IList<PoiRule> PoiRules => _poiRules; internal static IList<Place> AllPlacesForTest { get { EnsureTargets(); return _places; } } internal static void Init(ManualLogSource log, string pluginDir) { _log = log; _root = Path.Combine(Paths.ConfigPath, "LostCamps"); _bundledLore = Path.Combine(pluginDir, "lore.json"); Directory.CreateDirectory(_root); LoadPoiRules(); try { LoadLore(); Status = "Ready (placement is opt-in)"; } catch (Exception ex) { Status = "No story pack: " + ex.Message; _log.LogError((object)("Lost Camps could not load its story pack, so no camps will be placed: " + ex.Message)); } } internal static void LoadLore() { string text = Path.Combine(_root, "lore.json"); if (!File.Exists(text)) { if (!File.Exists(_bundledLore)) { throw new FileNotFoundException("lore.json is missing next to LostCamps.dll", _bundledLore); } File.Copy(_bundledLore, text); } LorePack lorePack = JsonFiles.Read<LorePack>(text); lorePack.Validate(); _pack = lorePack; _targetsBuilt = false; } private static void WarnOnce(string key, string message) { if (Warned.Add(key)) { _log.LogWarning((object)message); } } internal static void Update() { if (!CampSettings.Enabled.Value) { JournalWindow.Hide(); return; } string text = CampWorld.WorldId(); if (text != _world) { SwitchWorld(text); } if (_world.Length == 0) { JournalWindow.Hide(); return; } JournalWindow.Update(_root, _world, _log); if (CampWorld.IsServer && !(Time.realtimeSinceStartup < _nextScan)) { _nextScan = Time.realtimeSinceStartup + Mathf.Clamp(CampSettings.ScanIntervalSeconds.Value, 2f, 120f); ServerScan(); } } private static void SwitchWorld(string id) { _world = id; _state = null; _statePath = ""; _targets = new List<Target>(); _targetsBuilt = false; _cursor = 0; _failedSlots.Clear(); _placementFault = false; CleanupObjects.Clear(); _cleanupToken = ""; JournalWindow.Reset(); if (id.Length == 0 || !CampWorld.IsServer) { return; } _statePath = Path.Combine(_root, "worlds", "world-" + id + ".json"); try { _state = (File.Exists(_statePath) ? JsonFiles.Read<WorldState>(_statePath) : new WorldState { world = id }); _state.Validate(id); _log.LogInfo((object)("World " + id + ": " + _state.camps.Count + " camp assignment(s) on record.")); } catch (Exception ex) { _state = null; Status = "World record unreadable; placement stopped"; _log.LogError((object)("The camp record for this world is unreadable (" + ex.Message + "). Placement is stopped; the file is left untouched: " + _statePath)); } } private static void ServerScan() { try { MaybeReloadConfig(); if (CampSettings.CleanupPreview.Value) { CampSettings.CleanupPreview.Value = false; StampConfig(); PrepareCleanup(); } if (CampSettings.CleanupConfirm.Value) { CampSettings.CleanupConfirm.Value = false; StampConfig(); RunCleanup(_cleanupToken); } } catch (Exception ex) { _log.LogWarning((object)("Cleanup: " + ex.Message)); Status = "Cleanup: " + ex.Message; } if (!CampSettings.PlacementEnabled.Value || _state == null || _pack == null || _placementFault) { return; } try { EnsureTargets(); if (_targets.Count > 0 && _state.camps.Count < CampSettings.MaxCamps.Value) { PlaceTick(); } } catch (Exception ex2) { _placementFault = true; Status = "Placement stopped after an error; see the log"; _log.LogError((object)("Camp placement stopped for this session: " + ex2)); } } private static void EnsureTargets() { if (!_targetsBuilt && _pack != null && CampWorld.LocationCount != 0) { string world = _world; _targets = (from t in CampWorld.Targets(_pack) orderby Rules.Hash(world + ":" + t.Key) select t).ToList(); _targetsBuilt = true; _places = CampWorld.AllPlaces(); _log.LogInfo((object)(_targets.Count + " matching location(s) can host a camp.")); if (_targets.Count == 0) { _log.LogWarning((object)"No generated location matches the story pack's targets, so no camp will be placed. Check the target ids in lore.json."); } } } private static List<int> NearSlots() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) List<int> list = new List<int>(); List<Vector3> list2 = new List<Vector3>(); foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null) { list2.Add(((Component)allPlayer).transform.position); } } if (list2.Count == 0) { return list; } List<KeyValuePair<float, int>> list3 = new List<KeyValuePair<float, int>>(); for (int i = 0; i < _targets.Count; i++) { Target t = _targets[i]; bool flag = false; foreach (Vector3 item in list2) { if (Rules.Distance(item.x, item.z, t.Position.x, t.Position.z) < 320.0) { flag = true; break; } } if (!flag || _state.camps.Any((CampRecord c) => c.target == t.Key)) { continue; } for (int num = 0; num < 4; num++) { uint seed; Vector3 val = Propose(t, num, out seed); float num2 = float.MaxValue; foreach (Vector3 item2 in list2) { num2 = Math.Min(num2, (float)Rules.Distance(item2.x, item2.z, val.x, val.z)); } if (num2 <= 80f && !_failedSlots.Contains(i + num * _targets.Count)) { list3.Add(new KeyValuePair<float, int>(num2, i + num * _targets.Count)); } } } list3.Sort((KeyValuePair<float, int> a, KeyValuePair<float, int> b) => a.Key.CompareTo(b.Key)); for (int num3 = 0; num3 < list3.Count; num3++) { if (list.Count >= 8) { break; } list.Add(list3[num3].Value); } return list; } private static void PlaceTick() { //IL_00b5: 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_01de: 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_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0248: 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_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) CampWorld.PreflightPrefabs(); foreach (int item in NearSlots()) { Target t = _targets[item % _targets.Count]; int variant = item / _targets.Count % 4; if (_state.camps.Any((CampRecord c) => c.target == t.Key)) { continue; } LoreEntry loreEntry = Rules.Next(_pack, _state, t.Prefab, _world + ":" + t.Key); if (loreEntry == null) { continue; } uint seed; Vector3 proposed = Propose(t, variant, out seed); if (_state.camps.Any((CampRecord c) => Rules.Distance(c.x, c.z, proposed.x, proposed.z) < (double)CampSettings.CampSpacingMeters.Value)) { continue; } Vector3 result; string text = CampWorld.GroundReason(proposed, out result); if (text == null && CampWorld.PlayerConstruction(result, 18f)) { text = "player building nearby"; } if (text != null) { if (_refusalsLogged++ < 40) { _log.LogInfo((object)("Camp spot near " + t.Prefab + " skipped: " + text + ".")); } if (text != "terrain not loaded") { _failedSlots.Add(item); } continue; } CampRecord campRecord = new CampRecord { id = "camp_" + Guid.NewGuid().ToString("N"), pack = _pack.id, entry = loreEntry.id, day = loreEntry.day, author = loreEntry.author, x = result.x, z = result.z, targetX = t.Position.x, targetZ = t.Position.z, target = t.Key, text = Rules.Resolve(loreEntry.text, result.x, result.z, t.Position.x, t.Position.z, t.Label), nearby = DescribeNearby(result, t) }; _state.camps.Add(campRecord); SaveWorld(); List<GameObject> list = new List<GameObject>(); try { Quaternion yaw = Quaternion.Euler(0f, (float)(seed % 360), 0f); Part[] array = Layouts.For((int)(seed % 3)); foreach (Part part in array) { list.Add(CampWorld.SpawnPart(part, result, yaw, campRecord)); } campRecord.status = "placed"; SaveWorld(); Status = "Placed " + _state.camps.Count + " camp(s)"; _log.LogInfo((object)("Placed " + campRecord.id + " (" + campRecord.entry + ") near " + t.Prefab)); break; } catch (Exception) { campRecord.status = "failed"; SaveWorld(); foreach (GameObject item2 in list) { try { CampWorld.Destroy(item2); } catch (Exception ex2) { _log.LogError((object)("A partial camp piece remains: " + ex2.Message)); } } throw; } } } internal static Vector3 Propose(Target t, int variant, out uint seed) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) seed = Rules.Hash(_world + ":" + t.Key); float num = (float)((uint)((int)seed + variant * 91) % 360u) * ((float)Math.PI / 180f); float num2 = 100f + (float)((uint)((int)(seed / 360) + variant * 37) % 100u); return t.Position + new Vector3(Mathf.Sin(num) * num2, 0f, Mathf.Cos(num) * num2); } internal static int ScanFromHarness() { _nextScan = 0f; int count = Camps.Count; ServerScan(); return Camps.Count - count; } internal static void ClearRecordsForTest() { if (_state != null) { _state.camps.Clear(); _cursor = 0; _failedSlots.Clear(); _placementFault = false; SaveWorld(); } } private static void SaveWorld() { _state.Validate(_world); JsonFiles.Save(_statePath, _state); } private static void MaybeReloadConfig() { try { DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(CampSettings.File.ConfigFilePath); if (!(lastWriteTimeUtc == _configStamp)) { _configStamp = lastWriteTimeUtc; CampSettings.File.Reload(); } } catch (Exception ex) { WarnOnce("cfg", "Could not reload the config: " + ex.Message); } } private static void StampConfig() { try { _configStamp = File.GetLastWriteTimeUtc(CampSettings.File.ConfigFilePath); } catch (Exception) { } } internal static void PrepareCleanup() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) if (CampSettings.PlacementEnabled.Value) { throw new InvalidOperationException("Turn PlacementEnabled off before cleanup."); } CleanupObjects.Clear(); int num = 0; foreach (IGrouping<string, ZNetView> item in from v in Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0) where CampWorld.OwnerOf(v).StartsWith("camp_", StringComparison.Ordinal) group v by CampWorld.OwnerOf(v)) { if (CampWorld.PlayerConstruction(((Component)item.First()).transform.position, 18f)) { num++; continue; } CleanupObjects.AddRange(item.Select((ZNetView v) => ((Component)v).gameObject)); } _cleanupToken = Guid.NewGuid().ToString("N").Substring(0, 8); _cleanupExpires = Time.realtimeSinceStartup + 60f; Status = "Cleanup preview: " + CleanupObjects.Count + " loaded object(s); " + num + " camp(s) near player buildings skipped. Token " + _cleanupToken; _log.LogWarning((object)(Status + ". Camps in unloaded areas are not included. No terrain or player buildings are touched.")); } internal static void RunCleanup(string token) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) if (CampSettings.PlacementEnabled.Value || _cleanupToken.Length == 0 || token != _cleanupToken || Time.realtimeSinceStartup > _cleanupExpires) { _log.LogWarning((object)"Cleanup needs placement off and a fresh preview token."); Status = "Cleanup refused: needs placement off and a fresh preview"; return; } int num = 0; foreach (GameObject cleanupObject in CleanupObjects) { if (!((Object)(object)cleanupObject == (Object)null)) { ZNetView component = cleanupObject.GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && CampWorld.OwnerOf(component).StartsWith("camp_", StringComparison.Ordinal) && !CampWorld.PlayerConstruction(cleanupObject.transform.position, 18f)) { CampWorld.Destroy(cleanupObject); num++; } } } CleanupObjects.Clear(); _cleanupToken = ""; Status = "Removed " + num + " loaded camp object(s). Visit other areas and repeat before uninstalling."; _log.LogWarning((object)Status); } internal static void ReloadLore() { LoadLore(); LoadPoiRules(); Status = "Story pack reloaded"; } private static void LoadPoiRules() { _poiRules = Pois.Defaults; string path = Path.Combine(_root, "pois.json"); if (!File.Exists(path)) { return; } try { PoiPack poiPack = JsonFiles.Read<PoiPack>(path); poiPack.Validate(); _poiRules = poiPack.rules; _log.LogInfo((object)("Using " + poiPack.rules.Count + " place description(s) from pois.json.")); } catch (Exception ex) { WarnOnce("pois", "pois.json was ignored (" + ex.Message + "); using the built-in list."); } } internal static string DescribeNearby(Vector3 position, Target target) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!CampSettings.NearbyHints.Value) { return ""; } return Pois.Describe(position.x, position.z, target.Position.x, target.Position.z, _places, _poiRules, CampSettings.NearbyRadiusMeters.Value, CampSettings.NearbyMaxKinds.Value); } } internal static class CampSettings { internal static ConfigFile File; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> PlacementEnabled; internal static ConfigEntry<bool> CleanupPreview; internal static ConfigEntry<bool> CleanupConfirm; internal static ConfigEntry<bool> NearbyHints; internal static ConfigEntry<int> MaxCamps; internal static ConfigEntry<int> NearbyMaxKinds; internal static ConfigEntry<float> ScanIntervalSeconds; internal static ConfigEntry<float> CampSpacingMeters; internal static ConfigEntry<float> NearbyRadiusMeters; internal static ConfigEntry<KeyboardShortcut> JournalKey; internal static void Bind(ConfigFile c) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Expected O, but got Unknown //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Expected O, but got Unknown File = c; Enabled = c.Bind<bool>("Camps", "Enabled", true, "Turn the camp notes and the journal on or off for you. Does not remove camps that already exist."); JournalKey = c.Bind<KeyboardShortcut>("Camps", "JournalKey", new KeyboardShortcut((KeyCode)106, Array.Empty<KeyCode>()), "Opens the journal of camp notes you have read. (F7 to F10 are taken by Safety Net and DAL Compass.)"); PlacementEnabled = c.Bind<bool>("Server", "PlacementEnabled", false, "Server or host only. Lets this world gain new abandoned camps near real dungeon entrances. Off by default: try it in a disposable world first. No terrain is changed."); MaxCamps = c.Bind<int>("Server", "MaxCamps", 12, new ConfigDescription("Most camps this world will ever be given. Camps that failed or were removed still count, so a page is never handed out twice.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>())); ScanIntervalSeconds = c.Bind<float>("Server", "ScanIntervalSeconds", 10f, new ConfigDescription("How often the server tries to place one camp. Each try checks at most eight spots.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 120f), Array.Empty<object>())); CampSpacingMeters = c.Bind<float>("Server", "CampSpacingMeters", 500f, new ConfigDescription("Minimum distance between two camps.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 3000f), Array.Empty<object>())); NearbyHints = c.Bind<bool>("Server", "NearbyPlaceHints", true, "Each note also says, in general terms, what other places lie near its camp (for example a troll cave to the west). Worked out when the camp is placed, then saved with it."); NearbyRadiusMeters = c.Bind<float>("Server", "NearbyRadiusMeters", 700f, new ConfigDescription("How far from a camp a place can be and still be mentioned.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(200f, 2000f), Array.Empty<object>())); NearbyMaxKinds = c.Bind<int>("Server", "NearbyMaxKinds", 3, new ConfigDescription("Most different kinds of place a note will mention.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), Array.Empty<object>())); CleanupPreview = c.Bind<bool>("Maintenance", "CleanupPreview", false, "Set true to list the loaded camp objects that cleanup could remove (placement must be off). Nothing is removed. Resets itself."); CleanupConfirm = c.Bind<bool>("Maintenance", "CleanupConfirm", false, "Within 60 seconds of a preview, set true to remove exactly those loaded objects. Camps near player buildings are skipped. Resets itself."); } } internal static class CampSigns { private const int MaxPayload = 20000; internal static bool TryRead(Sign sign, bool hold, ref bool result) { if (!CampSettings.Enabled.Value || !JournalWindow.Ready) { return true; } ZNetView component = ((Component)sign).GetComponent<ZNetView>(); string text = CampWorld.OwnerOf(component); if (text.Length == 0) { return true; } if (hold) { result = false; return false; } try { string text2 = CampWorld.NoteOf(component); if (text2.Length == 0 || text2.Length > 20000) { throw new InvalidOperationException("Invalid note size."); } CampRecord campRecord = JsonFiles.Decode<CampRecord>(text2); if (campRecord == null || campRecord.id != text || !campRecord.IsPlausible()) { throw new InvalidOperationException("Invalid note data."); } JournalWindow.Learn(campRecord, LostCampsPlugin.Log); JournalWindow.Open(campRecord); } catch (Exception ex) { LostCampsPlugin.Log.LogWarning((object)("Cannot read this camp note: " + ex.Message)); } result = true; return false; } } [HarmonyPatch(typeof(Sign), "Interact")] internal static class SignInteractPatch { private static bool Prefix(Sign __instance, bool hold, ref bool __result) { return CampSigns.TryRead(__instance, hold, ref __result); } } internal sealed class Target { internal string Prefab = ""; internal string Label = ""; internal Vector3 Position; internal string Key => Prefab + "_" + Mathf.RoundToInt(Position.x) + "_" + Mathf.RoundToInt(Position.z); } internal struct Part { internal string Prefab; internal Vector3 Offset; internal float Yaw; internal Vector3 Lean; internal bool IsSign; internal Part(string prefab, float x, float y, float z, float yaw = 0f) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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) Prefab = prefab; Offset = new Vector3(x, y, z); Yaw = yaw; Lean = Vector3.zero; IsSign = prefab == "sign"; } internal Part Leaning(float dx, float dy, float dz) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Part result = this; result.Lean = new Vector3(dx, dy, dz); return result; } } internal static class Layouts { internal static string[] PrefabNames { get { HashSet<string> hashSet = new HashSet<string>(); for (int i = 0; i < 3; i++) { Part[] array = For(i); for (int j = 0; j < array.Length; j++) { Part part = array[j]; hashSet.Add(part.Prefab); } } string[] array2 = new string[hashSet.Count]; hashSet.CopyTo(array2); return array2; } } internal static Part[] For(int kind) { float num = ((kind == 1) ? (-1f) : 1f); List<Part> list = new List<Part>(); list.Add(new Part("wood_pole2", -1.7f, -0.5f, -1.2f).Leaning(0f, 1f, 0f)); list.Add(new Part("wood_pole2", 1.7f, -0.5f, -1.2f).Leaning(0f, 1f, 0f)); list.Add(new Part("wood_pole_log_4_worn", -2f, 1.7f, -1.2f).Leaning(1f, 0f, 0f)); for (int i = 0; i < 7; i++) { list.Add(new Part("wood_pole_log", -1.35f + 0.45f * (float)i, -0.1f, -2.5f).Leaning(0f, 1.55f, 1.3f)); } list.Add(new Part("Morkhalla_Bedroll2", -0.4f * num, 0.05f, -1.7f, 80f)); list.Add(new Part("fire_pit", 0f, 0f, 1.2f)); list.Add(new Part("Pinetree_01_Stub", -1.5f * num, 0f, 1.9f)); list.Add(new Part("FirTree_Stub", 1.5f * num, 0f, 1.7f)); list.Add(new Part("wood_stack", 2.3f * num, 0f, -0.2f, 15f)); list.Add(new Part("barrell", -2.2f * num, 0f, 0.3f)); if (kind != 2) { list.Add(new Part("barrell", -2.3f * num, 0f, -0.7f)); } list.Add(new Part("bucket", 0.9f * num, 0f, 0.2f)); if (kind == 2) { list.Add(new Part("stone_pile", -1.9f, 0f, 2.3f, 30f)); } list.Add(new Part("sign", 0.3f * num, 0f, 2.9f, 180f)); return list.ToArray(); } } internal static class CampWorld { internal const string OwnerKey = "LostCamps.camp"; internal const string NoteKey = "LostCamps.note"; private static readonly string[][] Labels = new string[26][] { new string[2] { "StoneCircle", "circle of standing stones" }, new string[2] { "StoneHenge", "ring of standing stones" }, new string[2] { "WoodVillage", "old village" }, new string[2] { "WoodFarm", "abandoned farm" }, new string[2] { "TrollCave", "troll cave" }, new string[2] { "BearCave", "bear's den" }, new string[2] { "Ruin", "old ruin" }, new string[2] { "Greydwarf_camp", "Greydwarf camp" }, new string[2] { "SwampHut", "swamp hut" }, new string[2] { "SwampRuin", "swamp ruin" }, new string[2] { "DrakeNest", "drake's nest" }, new string[2] { "AbandonedLogCabin", "abandoned log cabin" }, new string[2] { "MountainGrave", "mountain grave" }, new string[2] { "StoneTowerRuins", "ruined tower" }, new string[2] { "StoneTower", "old stone tower" }, new string[2] { "GoblinCamp", "Fuling camp" }, new string[2] { "GoblinHut", "Fuling hut" }, new string[2] { "TarPit", "tar pit" }, new string[2] { "CharredTowerRuins", "burnt tower" }, new string[2] { "CharredRuins", "burnt ruin" }, new string[2] { "FortressRuins", "ruined fortress" }, new string[2] { "SulfurArch", "sulfur arch" }, new string[2] { "LumberCamp", "lumber camp" }, new string[2] { "FrozenShip", "ship frozen in the ice" }, new string[2] { "DN_hut", "hut in the snow" }, new string[2] { "ShipWreck", "shipwreck" } }; private static readonly Dictionary<string, Vector3> FootCache = new Dictionary<string, Vector3>(); internal static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static int LocationCount { get { if (!((Object)(object)ZoneSystem.instance == (Object)null)) { return ZoneSystem.instance.m_locationInstances.Count; } return 0; } } internal static string WorldId() { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || ZNet.World == null) { return ""; } long worldUID = instance.GetWorldUID(); if (worldUID != 0L) { return worldUID.ToString(CultureInfo.InvariantCulture); } return ""; } internal static string CharacterId() { if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Game.instance == (Object)null) { return ""; } PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); if (playerProfile != null) { return playerProfile.GetPlayerID().ToString(CultureInfo.InvariantCulture); } return ""; } internal static bool InputBusy() { if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !TextInput.IsVisible() && !InventoryGui.IsVisible() && !Menu.IsVisible() && !Minimap.IsOpen()) { return StoreGui.IsVisible(); } return true; } internal static List<Target> Targets(LorePack pack) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) HashSet<string> hashSet = new HashSet<string>(); foreach (LoreEntry entry in pack.entries) { foreach (string target in entry.targets) { hashSet.Add(target); } } List<Target> list = new List<Target>(); if ((Object)(object)ZoneSystem.instance == (Object)null) { return list; } foreach (LocationInstance value in ZoneSystem.instance.m_locationInstances.Values) { string text = ((value.m_location != null) ? value.m_location.m_prefabName : ""); if (!string.IsNullOrEmpty(text) && hashSet.Contains(text)) { list.Add(new Target { Prefab = text, Position = value.m_position, Label = LabelFor(text) }); } } return list; } internal static List<Place> AllPlaces() { //IL_002e: 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) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) List<Place> list = new List<Place>(); if ((Object)(object)ZoneSystem.instance == (Object)null) { return list; } foreach (LocationInstance value in ZoneSystem.instance.m_locationInstances.Values) { string text = ((value.m_location != null) ? value.m_location.m_prefabName : ""); if (!string.IsNullOrEmpty(text)) { list.Add(new Place(text, value.m_position.x, value.m_position.z)); } } return list; } private static string LabelFor(string prefab) { if (prefab.StartsWith("Crypt", StringComparison.Ordinal)) { return "burial chamber"; } if (prefab.StartsWith("SunkenCrypt", StringComparison.Ordinal)) { return "sunken crypt"; } if (prefab.StartsWith("MountainCave", StringComparison.Ordinal)) { return "mountain cave"; } string[][] labels = Labels; foreach (string[] array in labels) { if (prefab.StartsWith(array[0], StringComparison.Ordinal)) { return array[1]; } } return "dungeon entrance"; } internal static bool Ground(Vector3 proposed, out Vector3 result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return GroundReason(proposed, out result) == null; } internal static string GroundReason(Vector3 proposed, out Vector3 result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0137: 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_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0206: 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_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) result = proposed; int mask = LayerMask.GetMask(new string[1] { "terrain" }); if (mask == 0) { throw new InvalidOperationException("The terrain layer is missing."); } RaycastHit val = default(RaycastHit); if (!Physics.Raycast(proposed + Vector3.up * 1000f, Vector3.down, ref val, 2000f, mask, (QueryTriggerInteraction)1)) { return "terrain not loaded"; } if (((RaycastHit)(ref val)).point.y < ZoneSystem.instance.m_waterLevel + 2f) { return "water or shore"; } if (Vector3.Angle(((RaycastHit)(ref val)).normal, Vector3.up) > 15f) { return "steep"; } result = ((RaycastHit)(ref val)).point + Vector3.up * 0.08f; Vector3[] array = (Vector3[])(object)new Vector3[4] { new Vector3(-2.7f, 0f, -2.9f), new Vector3(2.7f, 0f, -2.9f), new Vector3(-2.7f, 0f, 3.1f), new Vector3(2.7f, 0f, 3.1f) }; RaycastHit val3 = default(RaycastHit); foreach (Vector3 val2 in array) { if (!Physics.Raycast(result + val2 + Vector3.up * 10f, Vector3.down, ref val3, 20f, mask, (QueryTriggerInteraction)1) || Mathf.Abs(((RaycastHit)(ref val3)).point.y - ((RaycastHit)(ref val)).point.y) > 0.65f) { return "uneven"; } } int mask2 = LayerMask.GetMask(new string[4] { "piece", "static_solid", "Default", "Default_small" }); if (Physics.OverlapBox(result + new Vector3(0f, 1.7f, 0.1f), new Vector3(2.7f, 1.5f, 3.1f), Quaternion.identity, mask2, (QueryTriggerInteraction)1).Length != 0) { return "obstructed"; } return null; } internal static bool PlayerConstruction(Vector3 position, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(position, radius, LayerMask.GetMask(new string[2] { "piece", "piece_nonsolid" })); for (int i = 0; i < array.Length; i++) { Piece componentInParent = ((Component)array[i]).GetComponentInParent<Piece>(); if ((Object)(object)componentInParent != (Object)null && componentInParent.GetCreator() != 0L) { return true; } } return false; } internal static void PreflightPrefabs() { if ((Object)(object)ZNetScene.instance == (Object)null) { throw new InvalidOperationException("The world's object registry is not ready."); } string[] prefabNames = Layouts.PrefabNames; foreach (string text in prefabNames) { GameObject prefab = ZNetScene.instance.GetPrefab(text); if ((Object)(object)prefab == (Object)null) { throw new InvalidOperationException("Missing vanilla prefab: " + text); } if ((Object)(object)prefab.GetComponent<ZNetView>() == (Object)null) { throw new InvalidOperationException("Prefab lacks ZNetView: " + text); } } } private static Vector3 FootOffset(string name) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012c: 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_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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_00b5: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) if (FootCache.TryGetValue(name, out var value)) { return value; } GameObject prefab = ZNetScene.instance.GetPrefab(name); bool flag = false; Bounds val = default(Bounds); MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val2 in componentsInChildren) { if ((Object)(object)val2.sharedMesh == (Object)null) { continue; } Bounds bounds = val2.sharedMesh.bounds; for (int j = 0; j < 8; j++) { Vector3 val3 = ((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3(((j & 1) == 0) ? (-1f) : 1f, ((j & 2) == 0) ? (-1f) : 1f, ((j & 4) == 0) ? (-1f) : 1f)); Vector3 val4 = prefab.transform.InverseTransformPoint(((Component)val2).transform.TransformPoint(val3)); if (!flag) { ((Bounds)(ref val))..ctor(val4, Vector3.zero); flag = true; } else { ((Bounds)(ref val)).Encapsulate(val4); } } } Vector3 val5 = (Vector3)(flag ? new Vector3(((Bounds)(ref val)).center.x, ((Bounds)(ref val)).min.y, ((Bounds)(ref val)).center.z) : Vector3.zero); FootCache[name] = val5; return val5; } internal static float TerrainY(float x, float z, float fallback) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) RaycastHit val = default(RaycastHit); if (!Physics.Raycast(new Vector3(x, fallback + 500f, z), Vector3.down, ref val, 1000f, LayerMask.GetMask(new string[1] { "terrain" }), (QueryTriggerInteraction)1)) { return fallback; } return ((RaycastHit)(ref val)).point.y; } internal static GameObject SpawnPart(Part part, Vector3 center, Quaternion yaw, CampRecord camp) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //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) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) Vector3 val = yaw * new Vector3(part.Offset.x, 0f, part.Offset.z); float num = center.x + val.x; float num2 = center.z + val.z; Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(num, TerrainY(num, num2, center.y) + part.Offset.y, num2); Quaternion val4; Vector3 val5; if (part.Lean != Vector3.zero) { Vector3 up = Vector3.up; Vector3 val3 = yaw * part.Lean; val4 = Quaternion.FromToRotation(up, ((Vector3)(ref val3)).normalized) * Quaternion.Euler(0f, part.Yaw, 0f); val5 = val2 - val4 * FootOffset(part.Prefab); } else { val4 = yaw * Quaternion.Euler(0f, part.Yaw, 0f); val5 = val2 - val4 * FootOffset(part.Prefab); } GameObject val6 = Object.Instantiate<GameObject>(ZNetScene.instance.GetPrefab(part.Prefab), val5, val4); try { ZNetView component = val6.GetComponent<ZNetView>(); ZDO val7 = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); if (val7 == null) { throw new InvalidOperationException("No persistent network state for " + part.Prefab); } val7.Set("LostCamps.camp", camp.id); WearNTear component2 = val6.GetComponent<WearNTear>(); if ((Object)(object)component2 != (Object)null) { component2.m_noSupportWear = true; component2.m_noRoofWear = true; if ((Object)(object)component2.m_worn != (Object)null) { val7.Set(ZDOVars.s_health, component2.m_health * 0.6f); } } if ((Object)(object)val6.GetComponent<Fireplace>() != (O