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 ImmersiveTrader v1.0.10
ImmersiveTrader.dll
Decompiled 10 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using ImmersiveTrader.Commands; using ImmersiveTrader.Components; using ImmersiveTrader.Models; using ImmersiveTrader.Patches; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Splatform; using TMPro; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; using Valheim.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ImmersiveTrader")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ImmersiveTrader")] [assembly: AssemblyTitle("ImmersiveTrader")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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; } } internal static class IsExternalInit { } } namespace ImmersiveTrader { public sealed class TraderSanctuary : MonoBehaviour { private const float SafeRadius = 11f; private const float ExitRadius = 15f; private readonly Collider[] _nearby = (Collider[])(object)new Collider[128]; private readonly HashSet<Character> _seen = new HashSet<Character>(); private float _nextCheck; private void Update() { //IL_0056: 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_005c: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: 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_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: 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_017d: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || Time.time < _nextCheck) { return; } _nextCheck = Time.time + 0.5f; _seen.Clear(); Vector3 position = ((Component)this).transform.position; int num = Physics.OverlapSphereNonAlloc(position, 11f, _nearby); for (int i = 0; i < num; i++) { Character val = (((Object)(object)_nearby[i] == (Object)null) ? null : ((Component)_nearby[i]).GetComponentInParent<Character>()); if ((Object)(object)val == (Object)null || val.IsPlayer() || (Object)(object)((Component)val).GetComponent<BaseAI>() == (Object)null || ((Object)((Component)val).gameObject).name.StartsWith("ImmersiveTrader_", StringComparison.Ordinal) || !_seen.Add(val)) { continue; } Vector3 val2 = ((Component)val).transform.position - position; val2.y = 0f; if (!(((Vector3)(ref val2)).sqrMagnitude >= 121f)) { if (((Vector3)(ref val2)).sqrMagnitude < 0.01f) { val2 = Vector3.forward; } Vector3 position2 = position + ((Vector3)(ref val2)).normalized * 15f; position2.y = ((Component)val).transform.position.y; ((Component)val).transform.position = position2; } } } } [HarmonyPatch(typeof(Player), "Awake")] internal static class ItAintFunnyLaughPatch { private static void Postfix(Player __instance) { if (!MultiplayerNetwork.IsDedicatedServer && (Object)(object)((Component)__instance).GetComponent<ItAintFunnyAudio>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<ItAintFunnyAudio>(); } } } internal sealed class ItAintFunnyAudio : MonoBehaviour { private const string MaleResource = "ImmersiveTrader.Assets.ItAintFunny_Male.ogg"; private const string FemaleResource = "ImmersiveTrader.Assets.ItAintFunny_Female.ogg"; private const string LaughRpc = "ImmersiveTrader_NoKiddingLaugh"; private const int Stop = 0; private const int Male = 1; private const int Female = 2; private static AudioClip? _male; private static AudioClip? _female; private static bool _loading; private AudioSource? _source; private Player? _player; private ZNetView? _nview; private float _nextLaugh; private bool _wasActive; private bool _ownsLoad; private static readonly FieldInfo? ModelIndexField = AccessTools.Field(typeof(Player), "m_modelIndex"); private void Awake() { _player = ((Component)this).GetComponent<Player>(); _nview = ((Component)this).GetComponent<ZNetView>(); _source = ((Component)this).gameObject.AddComponent<AudioSource>(); _source.playOnAwake = false; _source.loop = false; _source.spatialBlend = 1f; _source.rolloffMode = (AudioRolloffMode)1; _source.minDistance = 2f; _source.maxDistance = 25f; _source.dopplerLevel = 0f; if ((Object)(object)_nview != (Object)null && _nview.GetZDO() != null) { _nview.Register<int>("ImmersiveTrader_NoKiddingLaugh", (Action<long, int>)RPC_Laugh); } if (!_loading) { _loading = true; _ownsLoad = true; ((MonoBehaviour)this).StartCoroutine(LoadClips()); } } private void OnDestroy() { if (_ownsLoad && ((Object)(object)_male == (Object)null || (Object)(object)_female == (Object)null)) { _loading = false; } } private static IEnumerator LoadClips() { yield return LoadEmbeddedOgg("ImmersiveTrader.Assets.ItAintFunny_Male.ogg", "ImmersiveTrader_ItAintFunny_Male.ogg", delegate(AudioClip c) { _male = c; }); yield return LoadEmbeddedOgg("ImmersiveTrader.Assets.ItAintFunny_Female.ogg", "ImmersiveTrader_ItAintFunny_Female.ogg", delegate(AudioClip c) { _female = c; }); } private static IEnumerator LoadEmbeddedOgg(string resource, string fileName, Action<AudioClip> assign) { Assembly asm = typeof(ItAintFunnyAudio).Assembly; using Stream stream = asm.GetManifestResourceStream(resource); if (stream == null) { Debug.LogError((object)("Missing embedded audio resource: " + resource)); yield break; } string dir = Path.Combine(Paths.CachePath, "ImmersiveTrader"); Directory.CreateDirectory(dir); string path = Path.Combine(dir, fileName); using (FileStream file = File.Create(path)) { stream.CopyTo(file); } UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip("file:///" + path.Replace('\\', '/'), (AudioType)14); try { yield return req.SendWebRequest(); if ((int)req.result != 1) { Debug.LogError((object)("Failed loading " + resource + ": " + req.error)); yield break; } AudioClip clip = DownloadHandlerAudioClip.GetContent(req); ((Object)clip).name = Path.GetFileNameWithoutExtension(fileName); assign(clip); } finally { ((IDisposable)req)?.Dispose(); } } private void Update() { if ((Object)(object)_player == (Object)null || (Object)(object)_source == (Object)null || (Object)(object)_player != (Object)(object)Player.m_localPlayer) { return; } if (!EnhancementStatusRegistry.Has((Character)(object)_player, "nokidding")) { if (_wasActive) { Broadcast(0); } _wasActive = false; _nextLaugh = 0f; } else if (!_wasActive) { _wasActive = true; _nextLaugh = Time.time + Random.Range(2f, 5f); } else if (!_source.isPlaying && !(Time.time < _nextLaugh)) { int num = ((!IsFemale()) ? 1 : 2); AudioClip val = ((num == 2) ? _female : _male); if (!((Object)(object)val == (Object)null)) { Broadcast(num); _nextLaugh = Time.time + val.length + Random.Range(2f, 5f); } } } private void Broadcast(int voice) { if ((Object)(object)_nview != (Object)null && _nview.IsValid()) { _nview.InvokeRPC(ZNetView.Everybody, "ImmersiveTrader_NoKiddingLaugh", new object[1] { voice }); } else { Play(voice); } } private void RPC_Laugh(long sender, int voice) { if (!((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.GetZDO().GetOwner() == sender) { Play(voice); } } private void Play(int voice) { if (!((Object)(object)_source == (Object)null)) { AudioClip? obj; switch (voice) { case 0: _source.Stop(); return; default: obj = _male; break; case 2: obj = _female; break; } AudioClip val = obj; if (!((Object)(object)val == (Object)null)) { _source.clip = val; _source.Play(); } } } private bool IsFemale() { if ((Object)(object)_player == (Object)null || ModelIndexField == null) { return false; } try { return ModelIndexField.GetValue(_player) is int num && num == 1; } catch { return false; } } } [BepInPlugin("p0kahontazz.immersivetrader", "ImmersiveTrader", "0.7.18")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { private Harmony? _harmony; private bool _rewardValidationPending; internal static ManualLogSource Log; public const string ModGuid = "p0kahontazz.immersivetrader"; public const string ModName = "ImmersiveTrader"; public const string ModVersion = "0.7.18"; internal static ConfigEntry<float> TreasureWeight; internal static ConfigEntry<int> MaxCarriedTreasures; internal static ConfigEntry<int> MidkaHealingMeadPrice; internal static ConfigEntry<bool> ProgressionLock; internal static ConfigEntry<int> TraderCooldownWorldDays; internal static ConfigEntry<int> MietegActiveWorldDays; internal static ConfigEntry<float> MietegRevealDistance; internal static ConfigEntry<EnhancementStockMode> EnhancementOffers; private void Awake() { //IL_0012: 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_0020: Expected O, but got Unknown //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Expected O, but got Unknown //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; PlayerLikeNpcVisual.InitializeOutfitFiles(); ConfigurationManagerAttributes val = new ConfigurationManagerAttributes { IsAdminOnly = true }; TreasureWeight = ((BaseUnityPlugin)this).Config.Bind<float>("Treasures", "Weight", 80f, new ConfigDescription("Weight of each quest treasure.", (AcceptableValueBase)null, new object[1] { val })); MaxCarriedTreasures = ((BaseUnityPlugin)this).Config.Bind<int>("Treasures", "MaxCarried", 2, new ConfigDescription("Maximum number of active quest treasures.", (AcceptableValueBase)null, new object[1] { val })); MidkaHealingMeadPrice = ((BaseUnityPlugin)this).Config.Bind<int>("Midka", "MinorHealingMeadPrice", 70, new ConfigDescription("Legacy configuration value. Midka's native shop always sells Minor Healing Mead for 70 coins.", (AcceptableValueBase)null, new object[1] { val })); ProgressionLock = ((BaseUnityPlugin)this).Config.Bind<bool>("Progression", "Enabled", true, new ConfigDescription("Prevent rewards from biomes not unlocked by boss progression.", (AcceptableValueBase)null, new object[1] { val })); TraderCooldownWorldDays = ((BaseUnityPlugin)this).Config.Bind<int>("Quests", "TraderCooldownWorldDays", 3, new ConfigDescription("World days before the same trader can issue another shipment to a player.", (AcceptableValueBase)null, new object[1] { val })); MietegActiveWorldDays = ((BaseUnityPlugin)this).Config.Bind<int>("LegendaryMieteg", "ActiveWorldDays", 2, new ConfigDescription("How many world days Mieteg remains active.", (AcceptableValueBase)null, new object[1] { val })); MietegRevealDistance = ((BaseUnityPlugin)this).Config.Bind<float>("LegendaryMieteg", "RevealDistance", 600f, new ConfigDescription("Distance in metres at which Mieteg becomes discoverable.", (AcceptableValueBase)null, new object[1] { val })); EnhancementOffers = ((BaseUnityPlugin)this).Config.Bind<EnhancementStockMode>("TraderStock", "EnhancementOffers", EnhancementStockMode.ScrollsAndWards, new ConfigDescription("Enhancement items offered by all NPCs, including Mieteg: ScrollsAndWards, ScrollsOnly, WardsOnly, None. The server controls multiplayer stock. Existing items and rewards are unchanged.", (AcceptableValueBase)null, new object[1] { val })); MultiplayerNetwork.Register(); _harmony = new Harmony("p0kahontazz.immersivetrader"); Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(Plugin).Assembly); foreach (Type type in typesFromAssembly) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0) { try { _harmony.CreateClassProcessor(type).Patch(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Patch " + type.Name + " failed and was skipped: " + ex.Message)); } } } CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ImmersiveTraderCommand()); PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabsAvailable; ZoneManager.OnVanillaLocationsAvailable += OnVanillaLocationsAvailable; ((BaseUnityPlugin)this).Logger.LogInfo((object)"ImmersiveTrader 0.7.18 loaded."); } private void OnDestroy() { PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable; ZoneManager.OnVanillaLocationsAvailable -= OnVanillaLocationsAvailable; Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void OnVanillaPrefabsAvailable() { TreasureRegistry.Register(); ContractRegistry.Register(); EnhancementStatusRegistry.Register(); EnhancementScrollItems.Register(); ShellItems.Register(); TraderRegistry.Initialize(); TraderActivityRegistry.Validate(); RewardRegistry.Initialize(); _rewardValidationPending = true; NpcPrefabRegistry.Register(); LegendaryMietegRegistry.RegisterPrefab(); PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable; } private void Update() { MultiplayerNetwork.TickStock(); LegendaryMietegSchedule.Tick(); if (_rewardValidationPending && !((Object)(object)ObjectDB.instance == (Object)null) && !((Object)(object)ObjectDB.instance.GetItemPrefab("Coins") == (Object)null)) { RewardRegistry.Validate(); TraderShopRegistry.Validate(); _rewardValidationPending = false; } } private void OnVanillaLocationsAvailable() { TraderLocationRegistry.Register(); LegendaryMietegRegistry.RegisterLocations(); ZoneManager.OnVanillaLocationsAvailable -= OnVanillaLocationsAvailable; } } internal static class AshlandsCampPlacement { private static readonly Dictionary<string, float> Footprints = new Dictionary<string, float>(StringComparer.Ordinal); private const float Border = 4f; private const float Grid = 2f; private const float LavaThreshold = 0.25f; internal static void Register(string group, float footprintRadius) { Footprints[group] = Math.Max(12f, footprintRadius) + 4f; } internal static bool Accept(string group, Vector3 centre) { //IL_0035: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) if (!Footprints.TryGetValue(group, out var value)) { return true; } if (WorldGenerator.instance == null) { return false; } if (ZoneSystem.IsLavaPreHeightmap(centre, 0.25f)) { return false; } float num = (float)Math.Floor((centre.x - value) / 2f) * 2f; float num2 = (float)Math.Floor((centre.z - value) / 2f) * 2f; float num3 = (float)Math.Ceiling((centre.x + value) / 2f) * 2f; float num4 = (float)Math.Ceiling((centre.z + value) / 2f) * 2f; float num5 = value + 2f; for (float num6 = num; num6 <= num3; num6 += 2f) { for (float num7 = num2; num7 <= num4; num7 += 2f) { float num8 = num6 - centre.x; float num9 = num7 - centre.z; if (!(num8 * num8 + num9 * num9 > num5 * num5) && ZoneSystem.IsLavaPreHeightmap(new Vector3(num6, centre.y, num7), 0.25f)) { return false; } } } return true; } } internal static class BlueprintLibrary { internal sealed record Piece(string Prefab, Quaternion Rotation, Vector3 Position, Vector3 Scale); internal sealed record Blueprint(string Name, IReadOnlyList<Piece> Pieces, Vector3 Origin, Vector3 Size); internal sealed record Options(float? BaseY = null, float Crop = 0f, float? Cut = null, IReadOnlyList<Vector4>? Exclude = null, bool Foundation = false, IReadOnlyList<Bounds>? Remove = null, bool Settle = false, bool NoNature = false) { internal string Key { get { string[] obj = new string[8] { BaseY.HasValue ? $"@base{BaseY.Value:0.##}" : string.Empty, (Crop > 0f) ? $"@crop{Crop:0.#}" : string.Empty, Cut.HasValue ? $"@cut{Cut.Value:0.##}" : string.Empty, null, null, null, null, null }; IReadOnlyList<Vector4> exclude = Exclude; obj[3] = ((exclude != null && exclude.Count > 0) ? ("@ex" + string.Join("_", Exclude.Select((Vector4 r) => $"{r.x:0}.{r.y:0}.{r.z:0}.{r.w:0}"))) : string.Empty); obj[4] = (Foundation ? "@found" : string.Empty); IReadOnlyList<Bounds> remove = Remove; obj[5] = ((remove != null && remove.Count > 0) ? ("@rm" + string.Join("_", Remove.Select((Bounds b) => $"{((Bounds)(ref b)).center.x:0.#}.{((Bounds)(ref b)).center.y:0.#}.{((Bounds)(ref b)).center.z:0.#}.{((Bounds)(ref b)).size.x:0.#}.{((Bounds)(ref b)).size.y:0.#}.{((Bounds)(ref b)).size.z:0.#}"))) : string.Empty); obj[6] = (Settle ? "@settle" : string.Empty); obj[7] = (NoNature ? "@nonature" : string.Empty); return string.Concat(obj); } } [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { RuntimeHelpers.EnsureSufficientExecutionStack(); builder.Append("BaseY = "); builder.Append(BaseY.ToString()); builder.Append(", Crop = "); builder.Append(Crop.ToString()); builder.Append(", Cut = "); builder.Append(Cut.ToString()); builder.Append(", Exclude = "); builder.Append(Exclude); builder.Append(", Foundation = "); builder.Append(Foundation.ToString()); builder.Append(", Remove = "); builder.Append(Remove); builder.Append(", Settle = "); builder.Append(Settle.ToString()); builder.Append(", NoNature = "); builder.Append(NoNature.ToString()); return true; } } private static readonly string[] Extensions = new string[2] { ".vbuild", ".blueprint" }; private static readonly Dictionary<string, string> StandIns = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["stone_1x1_enforced_bal"] = "stone_wall_1x1", ["hexwood_floor_4m_bal"] = "stone_floor_2x2", ["hexwood_floor_half_4m_bal"] = "stone_floor_2x2", ["rune_floor_bal"] = "stone_floor_2x2", ["darkwoodwall1m_bal"] = "stone_wall_1x1" }; private static readonly List<GameObject> Previews = new List<GameObject>(); private static readonly Dictionary<string, Blueprint> Parsed = new Dictionary<string, Blueprint>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, HashSet<(int, int)>> CellSets = new Dictionary<string, HashSet<(int, int)>>(); private static readonly Dictionary<string, (GameObject Root, int Built, int Total, List<string> Missing)> Templates = new Dictionary<string, (GameObject, int, int, List<string>)>(StringComparer.OrdinalIgnoreCase); private static GameObject? _templateHolder; internal static string Folder => Path.Combine(Paths.ConfigPath, "ImmersiveTrader", "blueprints"); internal static bool SkippedByDefault(string prefab) { return prefab.Equals("piece_xmastree", StringComparison.OrdinalIgnoreCase) || prefab.IndexOf("door", StringComparison.OrdinalIgnoreCase) >= 0 || prefab.IndexOf("gate", StringComparison.OrdinalIgnoreCase) >= 0 || prefab.IndexOf("portal", StringComparison.OrdinalIgnoreCase) >= 0 || prefab.Equals("iron_grate", StringComparison.OrdinalIgnoreCase); } internal static IEnumerable<string> List() { Directory.CreateDirectory(Folder); return from x in (from f in Directory.GetFiles(Folder) where Extensions.Contains<string>(Path.GetExtension(f), StringComparer.OrdinalIgnoreCase) select f).Select(Path.GetFileNameWithoutExtension).Distinct() orderby x select x; } private static string? FindFile(string name) { string text = Path.Combine(Folder, name); if (Extensions.Contains<string>(Path.GetExtension(name), StringComparer.OrdinalIgnoreCase) && File.Exists(text)) { return text; } string[] extensions = Extensions; foreach (string text2 in extensions) { string text3 = Path.Combine(Folder, name + text2); if (File.Exists(text3)) { return text3; } } return null; } internal static Blueprint? Load(string name, out string error, Options? options = null) { //IL_0471: Unknown result type (might be due to invalid IL or missing references) //IL_0478: Unknown result type (might be due to invalid IL or missing references) //IL_0488: Unknown result type (might be due to invalid IL or missing references) //IL_048f: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Unknown result type (might be due to invalid IL or missing references) //IL_04a7: Unknown result type (might be due to invalid IL or missing references) //IL_04a9: Unknown result type (might be due to invalid IL or missing references) //IL_04ab: Unknown result type (might be due to invalid IL or missing references) error = string.Empty; if ((object)options == null) { options = new Options(); } string text = name + options.Key; if (Parsed.TryGetValue(text, out Blueprint value)) { return value; } string text2 = FindFile(name); bool flag; string[] lines; if (text2 != null) { flag = text2.EndsWith(".blueprint", StringComparison.OrdinalIgnoreCase); lines = File.ReadAllLines(text2); } else { using Stream stream = typeof(BlueprintLibrary).Assembly.GetManifestResourceStream("ImmersiveTrader.Camps." + Path.GetFileNameWithoutExtension(name) + ".blueprint"); if (stream == null) { error = "Blueprint not found: " + name; return null; } using StreamReader streamReader = new StreamReader(stream); lines = streamReader.ReadToEnd().Split('\n'); flag = true; } List<Piece> list = (flag ? ParsePlanBuild(name, lines) : ParseVBuild(name, lines)); if (list.Count == 0) { error = "Blueprint " + name + " has no readable pieces."; return null; } if (name.Equals("home-on-stilts-dock", StringComparison.OrdinalIgnoreCase)) { list = list.Where((Piece p) => !p.Prefab.Equals("wood_floor", StringComparison.OrdinalIgnoreCase) || Mathf.Abs(p.Position.y) > 0.1f).ToList(); } float num = options.BaseY ?? (flag ? 0f : list.Min((Piece p) => p.Position.y)); IReadOnlyList<Vector4> exclude = options.Exclude; if (exclude != null && exclude.Count > 0) { list = list.Where((Piece p) => !options.Exclude.Any((Vector4 r) => p.Position.x >= Mathf.Min(r.x, r.z) && p.Position.x <= Mathf.Max(r.x, r.z) && p.Position.z >= Mathf.Min(r.y, r.w) && p.Position.z <= Mathf.Max(r.y, r.w))).ToList(); } if (options.NoNature) { list = list.Where((Piece p) => !IsNature(p.Prefab)).ToList(); } IReadOnlyList<Bounds> remove = options.Remove; if (remove != null && remove.Count > 0) { list = list.Where((Piece p) => !options.Remove.Any((Bounds b) => ((Bounds)(ref b)).Contains(p.Position))).ToList(); } if (options.Cut.HasValue) { list = list.Where((Piece p) => p.Position.y >= options.Cut.Value).ToList(); } if (options.Crop > 0f) { list = CropClusters(list, options.Crop); } if (options.Settle) { list = SettleBuildings(list, num); } if (options.Foundation) { list = WithFoundation(list, num); } if (list.Count == 0) { error = "Blueprint " + name + ": nothing left after crop/cut/exclude."; return null; } Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(list.Min((Piece p) => p.Position.x), list.Min((Piece p) => p.Position.y), list.Min((Piece p) => p.Position.z)); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(list.Max((Piece p) => p.Position.x), list.Max((Piece p) => p.Position.y), list.Max((Piece p) => p.Position.z)); Vector3 origin = default(Vector3); ((Vector3)(ref origin))..ctor((val.x + val2.x) * 0.5f, num, (val.z + val2.z) * 0.5f); Blueprint blueprint = new Blueprint(text, list, origin, val2 - val); Parsed[text] = blueprint; return blueprint; } internal static Blueprint WithPieces(Blueprint blueprint, string suffix, IEnumerable<Piece> extra) { string text = blueprint.Name + suffix; if (Parsed.TryGetValue(text, out Blueprint value)) { return value; } Blueprint blueprint2 = blueprint with { Name = text, Pieces = blueprint.Pieces.Concat(extra).ToList() }; Parsed[text] = blueprint2; return blueprint2; } internal static List<(Vector3 Centre, float Radius)> BuildingSpots(Blueprint blueprint) { return BuildingBounds(blueprint).Select(delegate((Vector3 Min, Vector3 Max) b) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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) Vector3 val = Flat3(b.Min - blueprint.Origin); Vector3 val2 = Flat3(b.Max - blueprint.Origin); Vector3 item = (val + val2) * 0.5f; Vector3 val3 = val2 - val; return (item, ((Vector3)(ref val3)).magnitude * 0.5f + 1f); }).ToList(); } private static Vector3 Flat3(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) return new Vector3(v.x, 0f, v.z); } internal static List<(Vector3 Min, Vector3 Max)> BuildingBounds(Blueprint blueprint) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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_028b: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) Dictionary<(int, int), List<Vector3>> dictionary = new Dictionary<(int, int), List<Vector3>>(); foreach (Piece piece in blueprint.Pieces) { if (!IsNature(piece.Prefab)) { Vector3 position = piece.Position; (int, int) key = (Mathf.FloorToInt(position.x / 3f), Mathf.FloorToInt(position.z / 3f)); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List<Vector3>()); } value.Add(new Vector3(position.x, 0f, position.z)); } } List<(Vector3, Vector3)> list2 = new List<(Vector3, Vector3)>(); HashSet<(int, int)> hashSet = new HashSet<(int, int)>(); Vector3 item = default(Vector3); Vector3 item2 = default(Vector3); foreach (var key3 in dictionary.Keys) { if (!hashSet.Add(key3)) { continue; } List<Vector3> list3 = new List<Vector3>(); Queue<(int, int)> queue = new Queue<(int, int)>(); queue.Enqueue(key3); while (queue.Count > 0) { (int, int) key2 = queue.Dequeue(); list3.AddRange(dictionary[key2]); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { (int, int) tuple = (key2.Item1 + i, key2.Item2 + j); if (dictionary.ContainsKey(tuple) && hashSet.Add(tuple)) { queue.Enqueue(tuple); } } } } if (list3.Count >= 8) { ((Vector3)(ref item))..ctor(list3.Min((Vector3 p) => p.x), 0f, list3.Min((Vector3 p) => p.z)); ((Vector3)(ref item2))..ctor(list3.Max((Vector3 p) => p.x), 0f, list3.Max((Vector3 p) => p.z)); list2.Add((item, item2)); } } return list2; } internal static List<(Vector3 A, Vector3 B)> VillagePaths(Blueprint blueprint, Vector3 squareFile) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) List<Vector3> nodes = (from b in BuildingBounds(blueprint) select (b.Min + b.Max) * 0.5f).Prepend(new Vector3(squareFile.x, 0f, squareFile.z)).ToList(); HashSet<(int, int)> edges = new HashSet<(int, int)>(); List<int> list = new List<int> { 0 }; List<int> list2 = Enumerable.Range(1, nodes.Count - 1).ToList(); Vector3 val; while (list2.Count > 0) { float num = float.MaxValue; int a = 0; int num2 = 0; foreach (int item in list) { foreach (int item2 in list2) { val = nodes[item] - nodes[item2]; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; a = item; num2 = item2; } } } Link(a, num2); list.Add(num2); list2.Remove(num2); } int i; for (i = 1; i < nodes.Count; i++) { foreach (int item3 in (from j in Enumerable.Range(1, nodes.Count - 1) where j != i select j).OrderBy(delegate(int j) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Vector3 val2 = nodes[i] - nodes[j]; return ((Vector3)(ref val2)).sqrMagnitude; }).Take(2)) { val = nodes[i] - nodes[item3]; if (((Vector3)(ref val)).magnitude < 45f) { Link(i, item3); } } } return edges.Select(((int, int) e) => (nodes[e.Item1], nodes[e.Item2])).ToList(); void Link(int num3, int b) { if (num3 != b) { edges.Add((num3 < b) ? (num3, b) : (b, num3)); } } } internal static bool OnBuilding(Blueprint blueprint, float fileX, float fileZ) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) HashSet<(int, int)> hashSet = GroundCellSet(blueprint); return hashSet.Contains((Mathf.FloorToInt((fileX - blueprint.Origin.x) / 3f), Mathf.FloorToInt((fileZ - blueprint.Origin.z) / 3f))); } private static HashSet<(int, int)> GroundCellSet(Blueprint blueprint) { if (CellSets.TryGetValue(blueprint.Name, out HashSet<(int, int)> value)) { return value; } value = new HashSet<(int, int)>(from c in GroundCells(blueprint) select (Mathf.FloorToInt(c.x / 3f), Mathf.FloorToInt(c.z / 3f))); CellSets[blueprint.Name] = value; return value; } internal static List<Vector3> GroundCells(Blueprint blueprint, float cell = 3f) { //IL_0052: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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) HashSet<(int, int)> hashSet = new HashSet<(int, int)>(); foreach (Piece piece in blueprint.Pieces) { if (!IsNature(piece.Prefab) && !SkippedByDefault(piece.Prefab)) { Vector3 val = piece.Position - blueprint.Origin; if (!(val.y > 3f)) { hashSet.Add((Mathf.FloorToInt(val.x / cell), Mathf.FloorToInt(val.z / cell))); } } } return ((IEnumerable<(int, int)>)hashSet).Select((Func<(int, int), Vector3>)(((int, int) c) => new Vector3(((float)c.Item1 + 0.5f) * cell, 0f, ((float)c.Item2 + 0.5f) * cell))).ToList(); } private static bool IsNature(string prefab) { return new string[11] { "Pickable", "Bush", "Beech", "Oak", "Fir", "Pine", "Birch", "shrub", "Rock", "vines", "Tree" }.Any((string w) => prefab.IndexOf(w, StringComparison.OrdinalIgnoreCase) >= 0); } private static List<Piece> SettleBuildings(List<Piece> pieces, float ground) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0294: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) List<Piece> list = new List<Piece>(); Dictionary<(int, int), List<Piece>> dictionary = new Dictionary<(int, int), List<Piece>>(); foreach (Piece piece in pieces) { if (IsNature(piece.Prefab)) { list.Add(piece with { Position = new Vector3(piece.Position.x, ground, piece.Position.z) }); continue; } (int, int) key = (Mathf.FloorToInt(piece.Position.x / 3f), Mathf.FloorToInt(piece.Position.z / 3f)); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List<Piece>()); } value.Add(piece); } HashSet<(int, int)> hashSet = new HashSet<(int, int)>(); foreach (var key3 in dictionary.Keys) { if (!hashSet.Add(key3)) { continue; } List<Piece> list3 = new List<Piece>(); Queue<(int, int)> queue = new Queue<(int, int)>(); queue.Enqueue(key3); while (queue.Count > 0) { (int, int) key2 = queue.Dequeue(); list3.AddRange(dictionary[key2]); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { (int, int) tuple = (key2.Item1 + i, key2.Item2 + j); if (dictionary.ContainsKey(tuple) && hashSet.Add(tuple)) { queue.Enqueue(tuple); } } } } List<float> list4 = (from p in list3 select p.Position.y into y orderby y select y).ToList(); float num = list4[Mathf.Min(list4.Count - 1, list4.Count / 20)]; float num2 = Mathf.Max(0f, num - ground); foreach (Piece item in list3) { list.Add((num2 > 0.01f) ? item with { Position = item.Position + Vector3.down * num2 } : item); } } return list; } private static List<Piece> CropClusters(List<Piece> pieces, float radius) { //IL_00d4: 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_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) float cx = (pieces.Min((Piece p) => p.Position.x) + pieces.Max((Piece p) => p.Position.x)) * 0.5f; float cz = (pieces.Min((Piece p) => p.Position.z) + pieces.Max((Piece p) => p.Position.z)) * 0.5f; Dictionary<(int, int), List<Piece>> dictionary = new Dictionary<(int, int), List<Piece>>(); foreach (Piece piece in pieces) { (int, int) key = (Mathf.FloorToInt(piece.Position.x / 4f), Mathf.FloorToInt(piece.Position.z / 4f)); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List<Piece>()); } value.Add(piece); } List<Piece> list2 = new List<Piece>(); HashSet<(int, int)> hashSet = new HashSet<(int, int)>(); foreach (var key3 in dictionary.Keys) { if (!hashSet.Add(key3)) { continue; } List<Piece> list3 = new List<Piece>(); Queue<(int, int)> queue = new Queue<(int, int)>(); queue.Enqueue(key3); while (queue.Count > 0) { (int, int) key2 = queue.Dequeue(); list3.AddRange(dictionary[key2]); for (int num = -1; num <= 1; num++) { for (int num2 = -1; num2 <= 1; num2++) { (int, int) tuple = (key2.Item1 + num, key2.Item2 + num2); if (dictionary.ContainsKey(tuple) && hashSet.Add(tuple)) { queue.Enqueue(tuple); } } } } float num3 = list3.Average((Piece p) => p.Position.x); float num4 = list3.Average((Piece p) => p.Position.z); Vector2 val = new Vector2(num3 - cx, num4 - cz); bool flag = ((Vector2)(ref val)).magnitude <= radius; bool flag2 = list3.Max(delegate(Piece p) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) Vector2 val2 = new Vector2(p.Position.x - cx, p.Position.z - cz); return ((Vector2)(ref val2)).magnitude; }) <= radius * 1.4f; if (flag && flag2) { list2.AddRange(list3); } } return list2; } private static List<Piece> WithFoundation(List<Piece> pieces, float ground) { //IL_0051: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00ee: Unknown result type (might be due to invalid IL or missing references) List<Piece> list = new List<Piece>(pieces); foreach (Piece piece in pieces) { if ((piece.Prefab.IndexOf("stone_wall", StringComparison.OrdinalIgnoreCase) >= 0 || piece.Prefab.IndexOf("stone_pillar", StringComparison.OrdinalIgnoreCase) >= 0) && !(piece.Position.y < ground - 0.3f) && !(piece.Position.y > ground + 1.2f)) { float num = ((piece.Prefab.IndexOf("x2", StringComparison.OrdinalIgnoreCase) >= 0 || piece.Prefab.IndexOf("pillar", StringComparison.OrdinalIgnoreCase) >= 0) ? 2f : 1f); for (float num2 = piece.Position.y - num; num2 > ground - 4.5f; num2 -= num) { list.Add(piece with { Position = new Vector3(piece.Position.x, num2, piece.Position.z) }); } } } return list; } internal static void ClearParsed() { Parsed.Clear(); CellSets.Clear(); } private static List<Piece> ParseVBuild(string name, string[] lines) { //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) List<Piece> list = new List<Piece>(); int num = 0; int num2 = 0; foreach (string text in lines) { num++; string text2 = text.TrimEnd('\r'); if (text2.Trim().Length == 0 || text2.TrimStart().StartsWith("#")) { continue; } string[] array = text2.Split(' '); if (array.Length < 8 || array[0].Length == 0 || !TryF(array[1], out var value) || !TryF(array[2], out var value2) || !TryF(array[3], out var value3) || !TryF(array[4], out var value4) || !TryF(array[5], out var value5) || !TryF(array[6], out var value6) || !TryF(array[7], out var value7)) { if (num2++ < 5) { Plugin.Log.LogWarning((object)$"Blueprint {name} line {num}: expected 'prefab qx qy qz qw px py pz'."); } } else { list.Add(new Piece(array[0], Rotation(value, value2, value3, value4), new Vector3(value5, value6, value7), Vector3.one)); } } return list; } private static List<Piece> ParsePlanBuild(string name, string[] lines) { //IL_014e: 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_01d1: 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_01e1: Unknown result type (might be due to invalid IL or missing references) List<Piece> list = new List<Piece>(); string text = string.Empty; int num = 0; int num2 = 0; foreach (string text2 in lines) { num++; string text3 = text2.Trim(); if (text3.Length == 0) { continue; } if (text3.StartsWith("#")) { if (text3.IndexOf(':') < 0) { text = text3.Substring(1).Trim().ToLowerInvariant(); } } else { if (text != "pieces") { continue; } string[] array = text3.Split(';'); if (array.Length < 9 || array[0].Length == 0 || !TryP(array[2], out var value) || !TryP(array[3], out var value2) || !TryP(array[4], out var value3) || !TryP(array[5], out var value4) || !TryP(array[6], out var value5) || !TryP(array[7], out var value6) || !TryP(array[8], out var value7)) { if (num2++ < 5) { Plugin.Log.LogWarning((object)$"Blueprint {name} line {num}: expected 'prefab;category;px;py;pz;qx;qy;qz;qw;...'."); } continue; } Vector3 one = Vector3.one; if (array.Length >= 13 && TryP(array[^3], out var value8) && TryP(array[^2], out var value9) && TryP(array[^1], out var value10) && value8 > 0f && value9 > 0f && value10 > 0f) { ((Vector3)(ref one))..ctor(value8, value9, value10); } list.Add(new Piece(array[0], Rotation(value4, value5, value6, value7), new Vector3(value, value2, value3), one)); } } return list; } private static Quaternion Rotation(float x, float y, float z, float w) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_004d: 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_0050: 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_0048: Unknown result type (might be due to invalid IL or missing references) Quaternion val = default(Quaternion); ((Quaternion)(ref val))..ctor(x, y, z, w); return (val.x == 0f && val.y == 0f && val.z == 0f && val.w == 0f) ? Quaternion.identity : Quaternion.Normalize(val); } private static bool TryP(string s, out float value) { return float.TryParse(s.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value); } internal static void ClearTemplates() { foreach (var value in Templates.Values) { if ((Object)(object)value.Root != (Object)null) { Object.Destroy((Object)(object)value.Root); } } Templates.Clear(); } internal static GameObject? Instantiate(string name, Transform parent, out string report, IReadOnlyCollection<string>? skip = null) { string error; Blueprint blueprint = Load(name, out error); if (blueprint == null) { report = error; return null; } return Instantiate(blueprint, parent, out report, skip); } internal static GameObject? Instantiate(Blueprint blueprint, Transform parent, out string report, IReadOnlyCollection<string>? skip = null) { //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown report = string.Empty; string name = blueprint.Name; if ((Object)(object)ZNetScene.instance == (Object)null) { report = "Game prefabs are not loaded yet."; return null; } string key = ((skip == null || skip.Count == 0) ? name : (name + "|" + string.Join(",", skip.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase)))); if (!Templates.TryGetValue(key, out (GameObject, int, int, List<string>) value) || (Object)(object)value.Item1 == (Object)null) { if ((Object)(object)_templateHolder == (Object)null) { _templateHolder = new GameObject("ImmersiveTrader_BlueprintTemplates"); _templateHolder.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_templateHolder); } Func<string, bool> skip2 = ((skip == null || skip.Count == 0) ? new Func<string, bool>(SkippedByDefault) : ((Func<string, bool>)((string prefab) => SkippedByDefault(prefab) || skip.Contains<string>(prefab, StringComparer.OrdinalIgnoreCase)))); List<string> missing; int item = Build(blueprint, _templateHolder.transform, skip2, out missing); GameObject gameObject = ((Component)_templateHolder.transform.GetChild(_templateHolder.transform.childCount - 1)).gameObject; value = (gameObject, item, blueprint.Pieces.Count, missing); Templates[key] = value; if (missing.Count > 0) { Plugin.Log.LogWarning((object)("Blueprint " + name + ": missing prefabs skipped: " + string.Join(", ", missing))); } } GameObject val = Object.Instantiate<GameObject>(value.Item1, parent, false); ((Object)val).name = "ImmersiveTrader_Blueprint_" + name; val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; report = $"{value.Item2}/{value.Item3} pieces" + ((value.Item4.Count > 0) ? ("; missing: " + string.Join(", ", value.Item4)) : string.Empty); return val; } internal static int Build(Blueprint blueprint, Transform parent, Func<string, bool>? skip, out List<string> missing) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_026a: 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_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) if (skip == null) { skip = SkippedByDefault; } missing = new List<string>(); GameObject val = new GameObject("ImmersiveTrader_Blueprint_" + blueprint.Name); val.transform.SetParent(parent, false); int num = 0; int num2 = 0; foreach (Piece piece in blueprint.Pieces) { if (skip(piece.Prefab)) { continue; } bool flag = blueprint.Name.StartsWith("home-on-stilts-dock", StringComparison.OrdinalIgnoreCase); if (flag && piece.Prefab.StartsWith("rug_", StringComparison.OrdinalIgnoreCase)) { continue; } int damageStage = 0; if (blueprint.Name.StartsWith("home-on-stilts-dock", StringComparison.OrdinalIgnoreCase) && !piece.Prefab.StartsWith("wood_pole_log", StringComparison.OrdinalIgnoreCase)) { damageStage = (num2++ & 1) + 1; } string text2; if (flag) { string prefab = piece.Prefab; if (1 == 0) { } string text = ((prefab == "piece_groundtorch") ? "piece_groundtorch_green" : ((!(prefab == "piece_throne01")) ? piece.Prefab : "piece_moose_throne")); if (1 == 0) { } text2 = text; } else { text2 = piece.Prefab; } string prefabName = text2; GameObject val2 = TraderNoticeBoard.SpawnVisual(prefabName, val.transform, damageStage); if ((Object)(object)val2 == (Object)null && StandIns.TryGetValue(piece.Prefab, out string value)) { val2 = TraderNoticeBoard.SpawnVisual(value, val.transform); } if ((Object)(object)val2 == (Object)null) { if (!missing.Contains(piece.Prefab)) { missing.Add(piece.Prefab); } continue; } val2.transform.localPosition = piece.Position - blueprint.Origin; val2.transform.localRotation = piece.Rotation; if (flag && piece.Prefab == "piece_throne01") { TraderSeatMarker[] componentsInChildren = val2.GetComponentsInChildren<TraderSeatMarker>(true); foreach (TraderSeatMarker traderSeatMarker in componentsInChildren) { traderSeatMarker.Prefab = piece.Prefab; } } if (piece.Scale != Vector3.one) { val2.transform.localScale = Vector3.Scale(val2.transform.localScale, piece.Scale); } num++; } return num; } internal static string Preview(string name, Player player, float distance) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_006b: 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_0075: 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_00b2: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) string error; Blueprint blueprint = Load(name, out error); if (blueprint == null) { return error; } float y = ((Component)player).transform.eulerAngles.y; Vector3 val = Quaternion.Euler(0f, y, 0f) * Vector3.forward; Vector3 val2 = ((Component)player).transform.position + val * (distance + blueprint.Size.z * 0.5f); if ((Object)(object)ZoneSystem.instance != (Object)null) { val2.y = ZoneSystem.instance.GetGroundHeight(val2); } GameObject val3 = new GameObject("ImmersiveTrader_BlueprintPreview_" + blueprint.Name); val3.transform.SetPositionAndRotation(val2, Quaternion.Euler(0f, y, 0f)); Instantiate(name, val3.transform, out string report); Previews.Add(val3); string text = "Blueprint " + blueprint.Name + ": " + report + ", " + $"{blueprint.Size.x:0.#} x {blueprint.Size.z:0.#} m, {blueprint.Size.y:0.#} m high."; Plugin.Log.LogInfo((object)text); return text; } internal static int ClearPreviews() { int num = 0; foreach (GameObject preview in Previews) { if ((Object)(object)preview != (Object)null) { Object.Destroy((Object)(object)preview); num++; } } Previews.Clear(); return num; } private static bool TryF(string s, out float value) { if (s.Length == 0) { value = 0f; return true; } return float.TryParse(s.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out value); } } internal static class CampBlueprintConfig { internal sealed record Building(string TraderId, string Blueprint, Vector2 Offset, float RotationY, float LevelMargin, bool AutoPosition = false, bool FaceWater = false) { internal bool Levels => LevelMargin >= 0f; internal float? BaseY { get; init; } internal float Crop { get; init; } internal Vector2? Entrance { get; init; } internal Vector2? EntranceOut { get; init; } internal float Step { get; init; } = 3f; internal float Side { get; init; } internal float FloorHint { get; init; } internal Vector2? BoardOffset { get; init; } internal float? Cut { get; init; } internal IReadOnlyList<Vector4> Exclude { get; init; } = Array.Empty<Vector4>(); internal bool Foundation { get; init; } internal IReadOnlyList<Bounds> Remove { get; init; } = Array.Empty<Bounds>(); internal bool Settle { get; init; } internal bool PavedPaths { get; init; } internal bool Greenery { get; init; } internal bool NoNature { get; init; } internal string? Fence { get; init; } internal BlueprintLibrary.Blueprint? Load(out string error) { //IL_0144: 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_015f: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0164: 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) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: 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_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) BlueprintLibrary.Blueprint blueprint = BlueprintLibrary.Load(Blueprint, out error, new BlueprintLibrary.Options(BaseY, Crop, Cut, Exclude, Foundation, Remove, Settle, NoNature)); if (blueprint != null && !string.IsNullOrEmpty(Fence)) { blueprint = CampLayout.WithFence(this, blueprint, Fence); } if (blueprint != null && Greenery) { blueprint = CampLayout.WithGreenery(this, blueprint); } List<Decor> list = ((blueprint == null) ? new List<Decor>() : (from d in DecorFor(TraderId) where !BlueprintLibrary.OnBuilding(blueprint, d.Position.x, d.Position.y) select d).ToList()); if (blueprint != null && PavedPaths) { TraderSpot traderSpot = TraderFor(TraderId); Vector3 squareFile = (Vector3)((traderSpot?.Mode == "file") ? new Vector3(traderSpot.Offset.x, 0f, traderSpot.Offset.y) : blueprint.Origin); List<BlueprintLibrary.Piece> list2 = new List<BlueprintLibrary.Piece>(); int num = 0; Vector3 val3 = default(Vector3); foreach (var (val, val2) in BlueprintLibrary.VillagePaths(blueprint, squareFile)) { ((Vector3)(ref val3))..ctor(val2.x - val.x, 0f, val2.z - val.z); float magnitude = ((Vector3)(ref val3)).magnitude; if (magnitude < 8f) { continue; } Vector3 val4 = val3 / magnitude; Vector3 val5 = Vector3.Cross(Vector3.up, val4); float num2 = 7f; while (num2 < magnitude - 4f) { Vector3 val6 = val + val4 * num2 + val5 * ((num % 2 == 0) ? 2.2f : (-2.2f)); if (!BlueprintLibrary.OnBuilding(blueprint, val6.x, val6.z)) { list2.Add(new BlueprintLibrary.Piece("piece_dvergr_lantern_pole", Quaternion.LookRotation(-val5 * ((num % 2 == 0) ? 1f : (-1f))), new Vector3(val6.x, blueprint.Origin.y, val6.z), Vector3.one)); } num2 += 14f; num++; } } if (list2.Count > 0) { blueprint = BlueprintLibrary.WithPieces(blueprint, "@lamps" + list2.Count, list2); } } if (blueprint != null && list.Count > 0) { blueprint = BlueprintLibrary.WithPieces(blueprint, "@decor" + StringExtensionMethods.GetStableHashCode(string.Join("_", list.Select((Decor d) => $"{d.Prefab}{d.Position.x:0.#}{d.Position.y:0.#}"))), list.Select((Decor d) => new BlueprintLibrary.Piece(d.Prefab, Quaternion.Euler(0f, d.RotationY, 0f), new Vector3(d.Position.x, blueprint.Origin.y, d.Position.y), Vector3.one * d.Scale))); } return blueprint; } [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) RuntimeHelpers.EnsureSufficientExecutionStack(); builder.Append("TraderId = "); builder.Append((object?)TraderId); builder.Append(", Blueprint = "); builder.Append((object?)Blueprint); builder.Append(", Offset = "); builder.Append(((object)Offset/*cast due to .constrained prefix*/).ToString()); builder.Append(", RotationY = "); builder.Append(RotationY.ToString()); builder.Append(", LevelMargin = "); builder.Append(LevelMargin.ToString()); builder.Append(", AutoPosition = "); builder.Append(AutoPosition.ToString()); builder.Append(", FaceWater = "); builder.Append(FaceWater.ToString()); return true; } } internal sealed record Altitude(string TraderId, float Min, float Max); internal sealed record TraderSpot(string TraderId, string Mode, Vector2 Offset, float? Height, float RotationY, string Base = "front", string? Prefab = null, float Pull = 0f); internal sealed record BoardSpot(string TraderId, string Mode, Vector2 Offset, float RotationY, float? Height = null); internal sealed record Decor(string TraderId, string Prefab, Vector2 Position, float RotationY, float Scale); private static List<Altitude> _altitudes = new List<Altitude>(); private static Dictionary<string, BiomeArea> _areas = new Dictionary<string, BiomeArea>(StringComparer.OrdinalIgnoreCase); private const string FormatMarker = "# format 15"; private static List<Building>? _buildings; private static List<TraderSpot> _traders = new List<TraderSpot>(); private static List<BoardSpot> _boards = new List<BoardSpot>(); private static List<BoardSpot> _caravans = new List<BoardSpot>(); private static List<BoardSpot> _wagons = new List<BoardSpot>(); private static List<Decor> _decor = new List<Decor>(); private static Dictionary<string, HashSet<string>> _skips = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase); internal const string DefaultTemplate = "# ImmersiveTrader - trader camp layout (template to paste into)\r\n# format 15\r\n#\r\n# Everything is relative to the camp centre, in metres: x = right, z = forward, rotY in degrees.\r\n#\r\n# building ; trader ; blueprint ; x ; z ; rotY ; margin\r\n# blueprint = file name without extension from BepInEx/config/ImmersiveTrader/blueprints\r\n# (.vbuild from valheimians.com or .blueprint from PlanBuild)\r\n# x, z = where the CENTRE of the building's footprint goes, or auto = moved so that the\r\n# trader spot ('inside' / 'front') is the camp centre\r\n# rotY = turn in degrees, or water = entrance turned towards the nearest water\r\n# margin = flat dirt ground around the building before the terrain slopes gently away,\r\n# or none = leave the terrain as it is\r\n# options (optional, any order):\r\n# base=11 ground level in the blueprint file (default y 0; foundations go below)\r\n# crop=60 keep only pieces within 60 m of the centre (very large builds)\r\n# entrance=x z the entrance in the blueprint file's coordinates (when it has no outer door);\r\n# entrance=x z dx dz ... and the direction out of it (x, z), e.g. -1 0 = out towards -x\r\n# step=5 side=2 trader spot this far from the entrance / off to the side\r\n# board=7.5 5 board this far out from the entrance / aside (default 7.5 5)\r\n# floor=5 an inside trader stands on a floor this high above the ground\r\n# cut=5 leave out everything below this height of the file (a cellar)\r\n# exclude=x1 z1 x2 z2 leave out everything inside this rectangle of the file\r\n# foundation extend the lowest stone walls 4 m down into the ground\r\n# fence=stake_wall a fence around the building with a gap at the entrance\r\n# fence=a@h+b@h several pieces per fence segment at these heights (Midka-like yard fence)\r\n# remove=x1 z1 x2 z2 y1 y2 leave out everything inside this box of the file (one roof, one bench)\r\n# settle every separate building goes down onto the ground (a village built on hills)\r\n# paths grass stays: dirt only under each building, paved paths between them\r\n# greenery vines on the outer walls of the building (a forest house)\r\n# nature=none leave out the blueprint's own trees, stones and bushes\r\n# (crop keeps whole buildings: those whose centre lies within the distance)\r\n# trader ; trader ; inside | front\r\n# inside = on a floor 5+ m in from the entrance, facing it; front = step m out from the entrance\r\n# (the entrance is the building's outer door, or its outermost stairs when it has no door)\r\n# trader ; trader ; seat ; prefab\r\n# seated on that seat piece of the building (a chair, a bench, piece_bathtub), with its animation\r\n# optional: ; x ; z (the seat of that prefab nearest to it) and ; pull=0.4 (the seat moved back first)\r\n# decor ; trader ; prefab ; x ; z ; rotY [; scale]\r\n# an extra piece (a rock, a bush, a tree) on the camp ground, in the blueprint file's coordinates\r\n# caravan ; trader ; file ; x ; y ; z ; rotY or caravan ; trader ; x ; z ; rotY\r\n# where the trader's Lox caravan stands: it camp caravan (stand there, look\n# where its head should point); file = blueprint coordinates, else the camp's own frame\r\n# board ; trader ; file ; x ; y ; z ; rotY\r\n# exact board spot in file coordinates (it camp board: stand there, look where readers will stand)\r\n# trader ; trader ; file ; x ; y ; z ; rotY\r\n# exact spot in the blueprint file's coordinates, the same in every world: stand there and\r\n# type it camp trader (it camp drop leaves out the building you stand next to)\r\n# trader ; trader ; x ; y ; z ; rotY\r\n# exact spot; y = height above the camp ground, or auto = on the floor found there\r\n# board ; trader ; door\r\n# beside the path in front of the entrance, facing out\r\n# board ; trader ; near\r\n# beside the door nearest to the trader (a trader standing in a village square)\r\n# altitude ; trader ; min ; max\r\n# height above the water where the camp may be generated (e.g. low / high mountain slopes)\r\n# area ; trader ; edge | median | everywhere\r\n# where in its biome the camp may be generated (edge = the border of the biome)\r\n# board ; trader ; x ; z ; rotY\r\n# exact spot (default without a board line: in front of the trader)\r\n# skip ; trader ; prefab, prefab, ...\r\n# blueprint pieces left out of this trader's buildings (e.g. a stone floor: dirt instead)\r\n#\r\n# Buildings are local visuals: nothing is saved into the world, they cannot be damaged, built on\r\n# or looted, and doors are left out so doorways stay open. Terrain levelling, the no-build area and\r\n# the camp size apply to camps generated after the change (a new world shows everything).\r\n# Preview anywhere: it blueprint build <name> Rebuild camps after editing: it camp reload\r\n# Lines starting with # are ignored. Decimal separator: dot or comma.\r\n\r\n\r\n# Current saved positions; moved together with their original houses.\r\nbuilding ; midka ; meadows-home ; auto ; auto ; 0 ; 4 ; step=5\r\ntrader ; midka ; inside\r\nboard ; midka ; door\r\ncaravan ; midka ; file ; 3.61 ; 0 ; -5.64 ; 286.55\r\nskip ; midka ; piece_chest_wood, piece_xmastree\r\nbuilding ; troldad ; arty-mtippi ; auto ; auto ; 0 ; 3 ; step=5.5 ; side=2.5\r\ntrader ; troldad ; front\r\nboard ; troldad ; file ; 9.13 ; 0 ; 15.34 ; 20.9\r\ncaravan ; troldad ; file ; 16.94 ; 0 ; 6.35 ; 23.19\r\nbuilding ; encek ; death-mountain ; auto ; auto ; water ; none ; base=4 ; entrance=-9.5 5 ; step=3 ; side=2.5 ; board=2 6\r\ntrader ; encek ; file ; -51.09 ; 21.67 ; 10.18 ; 296.19 ; base=front\nboard ; encek ; file ; -8.08 ; 2.74 ; -13.25 ; 124.84\r\ncaravan ; encek ; file ; -3.12 ; 3.75 ; 7.81 ; 160.39\r\naltitude ; encek ; 0.5 ; 3.5\r\narea ; encek ; edge\r\nbuilding ; hrothgar ; forteca ; auto ; auto ; 0 ; 3 ; base=6.5 ; cut=5 ; foundation ; entrance=28.1 26.1 -1 0 ; step=6 ; side=2.5 ; board=10.5 3\r\ntrader ; hrothgar ; file ; 30.95 ; 6.61 ; 26.13 ; 266.5\r\nboard ; hrothgar ; file ; 26.18 ; 6.5 ; 14.87 ; 269.5\r\ncaravan ; hrothgar ; file ; 23.06 ; 6.5 ; 38.3 ; 222.1\r\naltitude ; hrothgar ; 60 ; 100\r\nbuilding ; ylva_frost ; mountainfob ; auto ; auto ; 0 ; 3 ; step=5\r\ntrader ; ylva_frost ; inside\r\nboard ; ylva_frost ; door\r\nskip ; ylva_frost ; piece_chest_wood, piece_chest_barrel\r\naltitude ; ylva_frost ; 110 ; 165\r\nbuilding ; bjarki_goldtooth ; fantasyhouse ; 1.1 ; -1.35 ; 0 ; 4\r\ntrader ; bjarki_goldtooth ; seat ; piece_chair03 ; -16.95 ; 0.05 ; pull=0.4\r\nboard ; bjarki_goldtooth ; file ; -1.17 ; 0 ; 9.42 ; 134.45\r\ncaravan ; bjarki_goldtooth ; file ; -3.3 ; 0 ; -12.23 ; 68.75\r\nbuilding ; ragnar_turnipson ; plains-house ; auto ; auto ; 0 ; 4 ; step=5\r\ntrader ; ragnar_turnipson ; seat ; piece_bathtub\r\nboard ; ragnar_turnipson ; file ; 6.83 ; 0 ; -0.58 ; 315.55\r\ncaravan ; ragnar_turnipson ; file ; 13.48 ; 0 ; 6.83 ; 245.55\r\nwagon ; midka ; file ; -1.95 ; 0 ; 15.89 ; 188.35\r\nwagon ; troldad ; file ; 15.11 ; 0.11 ; -3.58 ; 2.85\r\nwagon ; bjarki_goldtooth ; file ; -9.91 ; 0 ; -18.12 ; 38.95\r\nwagon ; ragnar_turnipson ; file ; 27.62 ; 0 ; 6.96 ; 283.8\r\nwagon ; ylva_frost ; file ; -18.93 ; 0 ; -3.3 ; 131.1\r\nwagon ; hrothgar ; file ; 17.45 ; 6.5 ; 16.07 ; 312.15\r\nskip ; hrothgar ; rune_floor_bal, stone_floor_triangle_bal, stone_platform_bal, vigvisir_floor_bal\r\nbuilding ; rudy_warg ; gumdrop-v2 ; auto ; auto ; 0 ; 8 ; foundation ; step=6\r\ntrader ; rudy_warg ; file ; -0.78 ; 4.22 ; 14.28 ; 187.35 ; base=front\nboard ; rudy_warg ; file ; -6.42 ; 0 ; -1.99 ; 214.45\r\nskip ; rudy_warg ; piece_chest_wood, piece_chest_barrel\r\nbuilding ; grelka ; sorcerer-tower ; auto ; auto ; 0 ; 4 ; foundation ; step=5\r\ntrader ; grelka ; file ; 0 ; 0.89 ; -1.35 ; 31.79\r\nboard ; grelka ; file ; 8.24 ; 0 ; 0.52 ; 43.39\r\nwagon ; grelka ; file ; -7.64 ; 0 ; 5.82 ; 70.84\r\ncaravan ; grelka ; file ; -8.39 ; 0 ; 12.42 ; 103.69\r\nwagon ; rudy_warg ; file ; 10.43 ; 0 ; -1.06 ; 241.15\r\ncaravan ; rudy_warg ; file ; -11.81 ; 0 ; 4.72 ; 175.95\r\n# camps coastal revision 20260929\r\nbuilding ; grimvald ; small-lighthouse ; 0 ; 0 ; water ; none ; entrance=3.15 -1.28 0.924 -0.383 ; step=3\r\ntrader ; grimvald ; file ; 0.89 ; 0.46 ; 1.24 ; 150.11 ; base=front\nboard ; grimvald ; file ; 0.23 ; -0.49 ; -6.03 ; 140.56\ncaravan ; grimvald ; file ; 4.85 ; -0.32 ; 5.34 ; 153.91\naltitude ; grimvald ; 1 ; 3\r\nbuilding ; cmok ; gjall-hunter-keep ; 0 ; 0 ; 0 ; 4 ; base=-0.1 ; entrance=-7.9 -4.49 0.924 0.383 ; step=5\ntrader ; cmok ; file ; -14.4 ; 11.29 ; -5.84 ; 258.69 ; base=front\nboard ; cmok ; door\r\nbuilding ; spalony_zenek ; ashen-spire ; 0 ; 0 ; 0 ; 4 ; base=-1.5 ; entrance=-12.6 5.5 -0.924 0.383 ; step=5\ntrader ; spalony_zenek ; inside\r\nboard ; spalony_zenek ; file ; -8.18 ; -2.29 ; 9.03 ; 325.41\ncaravan ; spalony_zenek ; file ; -13.68 ; -2.5 ; -5.01 ; 341.31\nbuilding ; skjold_cinderborn ; barber-shop ; 0 ; 0 ; 0 ; 4 ; base=-0.5 ; entrance=-0.33 -0.47 0.707 0.707\ntrader ; skjold_cinderborn ; file ; -7.12 ; 10.93 ; -3.98 ; 183.1 ; base=front\nboard ; skjold_cinderborn ; file ; 3 ; -0.5 ; -2 ; 45\r\ncaravan ; skjold_cinderborn ; file ; -9.46 ; -0.5 ; 4.09 ; 109.6\nskip ; skjold_cinderborn ; piece_barber, wood_floor, fire_pit_iron\nbuilding ; mokra_dzika ; home-on-stilts-dock ; 0 ; 0 ; 0 ; none ; base=0.8 ; entrance=6 0 0 -1\ntrader ; mokra_dzika ; seat ; piece_throne01\r\nboard ; mokra_dzika ; file ; 6.28 ; 1.05 ; -2.48 ; 151.3\ncaravan ; mokra_dzika ; file ; 20.77 ; 1.05 ; 3.36 ; 231.6\n# camps land-and-cellar revision 20260930\n# camps mokra-water revision 20260930\n"; internal static string FilePath => Path.Combine(Paths.ConfigPath, "ImmersiveTrader-camps.cfg"); internal static IReadOnlyList<Building> Buildings { get { Load(); return _buildings; } } internal static BiomeArea? AreaFor(string traderId) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) Load(); BiomeArea value; return _areas.TryGetValue(traderId, out value) ? new BiomeArea?(value) : ((BiomeArea?)null); } internal static Altitude? AltitudeFor(string traderId) { Load(); return _altitudes.LastOrDefault((Altitude a) => a.TraderId.Equals(traderId, StringComparison.OrdinalIgnoreCase)); } internal static IEnumerable<Building> For(string traderId) { return Buildings.Where((Building b) => b.TraderId.Equals(traderId, StringComparison.OrdinalIgnoreCase)); } internal static TraderSpot? TraderFor(string traderId) { Load(); return _traders.LastOrDefault((TraderSpot t) => t.TraderId.Equals(traderId, StringComparison.OrdinalIgnoreCase)); } internal static IEnumerable<Decor> DecorFor(string traderId) { Load(); return _decor.Where((Decor d) => d.TraderId.Equals(traderId, StringComparison.OrdinalIgnoreCase)); } internal static BoardSpot? CaravanFor(string traderId) { Load(); return _caravans.LastOrDefault((BoardSpot b) => b.TraderId.Equals(traderId, StringComparison.OrdinalIgnoreCase)); } internal static BoardSpot? WagonFor(string traderId) { Load(); return _wagons.LastOrDefault((BoardSpot b) => b.TraderId.Equals(traderId, StringComparison.OrdinalIgnoreCase)); } internal static BoardSpot? BoardFor(string traderId) { Load(); return _boards.LastOrDefault((BoardSpot b) => b.TraderId.Equals(traderId, StringComparison.OrdinalIgnoreCase)); } internal static IReadOnlyCollection<string> SkipFor(string traderId) { Load(); IReadOnlyCollection<string> result; if (!_skips.TryGetValue(traderId, out HashSet<string> value)) { IReadOnlyCollection<string> readOnlyCollection = (IReadOnlyCollection<string>)(object)Array.Empty<string>(); result = readOnlyCollection; } else { IReadOnlyCollection<string> readOnlyCollection = value; result = readOnlyCollection; } return result; } internal static bool TryFootprint(Building building, out Vector3 centre, out Vector2 halfSize) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) centre = new Vector3(building.Offset.x, 0f, building.Offset.y); halfSize = Vector2.zero; string error; BlueprintLibrary.Blueprint blueprint = building.Load(out error); if (blueprint == null) { return false; } halfSize = new Vector2(blueprint.Size.x * 0.5f + 1.5f, blueprint.Size.z * 0.5f + 1.5f); return true; } internal static void Reload() { _buildings = null; } private static void Load() { //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_09df: Unknown result type (might be due to invalid IL or missing references) //IL_0ab7: Unknown result type (might be due to invalid IL or missing references) //IL_0bf2: Unknown result type (might be due to invalid IL or missing references) //IL_0ac7: Unknown result type (might be due to invalid IL or missing references) //IL_04ec: Unknown result type (might be due to invalid IL or missing references) //IL_0c8c: Unknown result type (might be due to invalid IL or missing references) //IL_0ac2: Unknown result type (might be due to invalid IL or missing references) //IL_05b1: Unknown result type (might be due to invalid IL or missing references) //IL_0532: Unknown result type (might be due to invalid IL or missing references) //IL_0d0d: Unknown result type (might be due to invalid IL or missing references) //IL_0b3c: Unknown result type (might be due to invalid IL or missing references) //IL_063a: Unknown result type (might be due to invalid IL or missing references) //IL_065d: Unknown result type (might be due to invalid IL or missing references) //IL_067d: Unknown result type (might be due to invalid IL or missing references) //IL_0697: Unknown result type (might be due to invalid IL or missing references) //IL_0daf: Unknown result type (might be due to invalid IL or missing references) //IL_0e30: Unknown result type (might be due to invalid IL or missing references) //IL_072b: Unknown result type (might be due to invalid IL or missing references) //IL_0ed2: Unknown result type (might be due to invalid IL or missing references) //IL_0fad: Unknown result type (might be due to invalid IL or missing references) //IL_1058: Unknown result type (might be due to invalid IL or missing references) //IL_1193: Unknown result type (might be due to invalid IL or missing references) //IL_121a: Unknown result type (might be due to invalid IL or missing references) if (_buildings != null) { return; } _buildings = new List<Building>(); _traders = new List<TraderSpot>(); _boards = new List<BoardSpot>(); _caravans = new List<BoardSpot>(); _wagons = new List<BoardSpot>(); _decor = new List<Decor>(); _skips = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase); _altitudes = new List<Altitude>(); _areas = new Dictionary<string, BiomeArea>(StringComparer.OrdinalIgnoreCase); string[] lines; try { Directory.CreateDirectory(Paths.ConfigPath); if (File.Exists(FilePath) && !File.ReadAllText(FilePath).Contains("# format 15")) { string text = FilePath + ".old.bak"; File.Copy(FilePath, text, overwrite: true); File.Delete(FilePath); Plugin.Log.LogInfo((object)("Camp layout upgraded; previous file kept as " + text + ".")); } if (!File.Exists(FilePath)) { File.WriteAllText(FilePath, "# ImmersiveTrader - trader camp layout (template to paste into)\r\n# format 15\r\n#\r\n# Everything is relative to the camp centre, in metres: x = right, z = forward, rotY in degrees.\r\n#\r\n# building ; trader ; blueprint ; x ; z ; rotY ; margin\r\n# blueprint = file name without extension from BepInEx/config/ImmersiveTrader/blueprints\r\n# (.vbuild from valheimians.com or .blueprint from PlanBuild)\r\n# x, z = where the CENTRE of the building's footprint goes, or auto = moved so that the\r\n# trader spot ('inside' / 'front') is the camp centre\r\n# rotY = turn in degrees, or water = entrance turned towards the nearest water\r\n# margin = flat dirt ground around the building before the terrain slopes gently away,\r\n# or none = leave the terrain as it is\r\n# options (optional, any order):\r\n# base=11 ground level in the blueprint file (default y 0; foundations go below)\r\n# crop=60 keep only pieces within 60 m of the centre (very large builds)\r\n# entrance=x z the entrance in the blueprint file's coordinates (when it has no outer door);\r\n# entrance=x z dx dz ... and the direction out of it (x, z), e.g. -1 0 = out towards -x\r\n# step=5 side=2 trader spot this far from the entrance / off to the side\r\n# board=7.5 5 board this far out from the entrance / aside (default 7.5 5)\r\n# floor=5 an inside trader stands on a floor this high above the ground\r\n# cut=5 leave out everything below this height of the file (a cellar)\r\n# exclude=x1 z1 x2 z2 leave out everything inside this rectangle of the file\r\n# foundation extend the lowest stone walls 4 m down into the ground\r\n# fence=stake_wall a fence around the building with a gap at the entrance\r\n# fence=a@h+b@h several pieces per fence segment at these heights (Midka-like yard fence)\r\n# remove=x1 z1 x2 z2 y1 y2 leave out everything inside this box of the file (one roof, one bench)\r\n# settle every separate building goes down onto the ground (a village built on hills)\r\n# paths grass stays: dirt only under each building, paved paths between them\r\n# greenery vines on the outer walls of the building (a forest house)\r\n# nature=none leave out the blueprint's own trees, stones and bushes\r\n# (crop keeps whole buildings: those whose centre lies within the distance)\r\n# trader ; trader ; inside | front\r\n# inside = on a floor 5+ m in from the entrance, facing it; front = step m out from the entrance\r\n# (the entrance is the building's outer door, or its outermost stairs when it has no door)\r\n# trader ; trader ; seat ; prefab\r\n# seated on that seat piece of the building (a chair, a bench, piece_bathtub), with its animation\r\n# optional: ; x ; z (the seat of that prefab nearest to it) and ; pull=0.4 (the seat moved back first)\r\n# decor ; trader ; prefab ; x ; z ; rotY [; scale]\r\n# an extra piece (a rock, a bush, a tree) on the camp ground, in the blueprint file's coordinates\r\n# caravan ; trader ; file ; x ; y ; z ; rotY or caravan ; trader ; x ; z ; rotY\r\n# where the trader's Lox caravan stands: it camp caravan (stand there, look\n# where its head should point); file = blueprint coordinates, else the camp's own frame\r\n# board ; trader ; file ; x ; y ; z ; rotY\r\n# exact board spot in file coordinates (it camp board: stand there, look where readers will stand)\r\n# trader ; trader ; file ; x ; y ; z ; rotY\r\n# exact spot in the blueprint file's coordinates, the same in every world: stand there and\r\n# type it camp trader (it camp drop leaves out the building you stand next to)\r\n# trader ; trader ; x ; y ; z ; rotY\r\n# exact spot; y = height above the camp ground, or auto = on the floor found there\r\n# board ; trader ; door\r\n# beside the path in front of the entrance, facing out\r\n# board ; trader ; near\r\n# beside the door nearest to the trader (a trader standing in a village square)\r\n# altitude ; trader ; min ; max\r\n# height above the water where the camp may be generated (e.g. low / high mountain slopes)\r\n# area ; trader ; edge | median | everywhere\r\n# where in its biome the camp may be generated (edge = the border of the biome)\r\n# board ; trader ; x ; z ; rotY\r\n# exact spot (default without a board line: in front of the trader)\r\n# skip ; trader ; prefab, prefab, ...\r\n# blueprint pieces left out of this trader's buildings (e.g. a stone floor: dirt instead)\r\n#\r\n# Buildings are local visuals: nothing is saved into the world, they cannot be damaged, built on\r\n# or looted, and doors are left out so doorways stay open. Terrain levelling, the no-build area and\r\n# the camp size apply to camps generated after the change (a new world shows everything).\r\n# Preview anywhere: it blueprint build <name> Rebuild camps after editing: it camp reload\r\n# Lines starting with # are ignored. Decimal separator: dot or comma.\r\n\r\n\r\n# Current saved positions; moved together with their original houses.\r\nbuilding ; midka ; meadows-home ; auto ; auto ; 0 ; 4 ; step=5\r\ntrader ; midka ; inside\r\nboard ; midka ; door\r\ncaravan ; midka ; file ; 3.61 ; 0 ; -5.64 ; 286.55\r\nskip ; midka ; piece_chest_wood, piece_xmastree\r\nbuilding ; troldad ; arty-mtippi ; auto ; auto ; 0 ; 3 ; step=5.5 ; side=2.5\r\ntrader ; troldad ; front\r\nboard ; troldad ; file ; 9.13 ; 0 ; 15.34 ; 20.9\r\ncaravan ; troldad ; file ; 16.94 ; 0 ; 6.35 ; 23.19\r\nbuilding ; encek ; death-mountain ; auto ; auto ; water ; none ; base=4 ; entrance=-9.5 5 ; step=3 ; side=2.5 ; board=2 6\r\ntrader ; encek ; file ; -51.09 ; 21.67 ; 10.18 ; 296.19 ; base=front\nboard ; encek ; file ; -8.08 ; 2.74 ; -13.25 ; 124.84\r\ncaravan ; encek ; file ; -3.12 ; 3.75 ; 7.81 ; 160.39\r\naltitude ; encek ; 0.5 ; 3.5\r\narea ; encek ; edge\r\nbuilding ; hrothgar ; forteca ; auto ; auto ; 0 ; 3 ; base=6.5 ; cut=5 ; foundation ; entrance=28.1 26.1 -1 0 ; step=6 ; side=2.5 ; board=10.5 3\r\ntrader ; hrothgar ; file ; 30.95 ; 6.61 ; 26.13 ; 266.5\r\nboard ; hrothgar ; file ; 26.18 ; 6.5 ; 14.87 ; 269.5\r\ncaravan ; hrothgar ; file ; 23.06 ; 6.5 ; 38.3 ; 222.1\r\naltitude ; hrothgar ; 60 ; 100\r\nbuilding ; ylva_frost ; mountainfob ; auto ; auto ; 0 ; 3 ; step=5\r\ntrader ; ylva_frost ; inside\r\nboard ; ylva_frost ; door\r\nskip ; ylva_frost ; piece_chest_wood, piece_chest_barrel\r\naltitude ; ylva_frost ; 110 ; 165\r\nbuilding ; bjarki_goldtooth ; fantasyhouse ; 1.1 ; -1.35 ; 0 ; 4\r\ntrader ; bjarki_goldtooth ; seat ; piece_chair03 ; -16.95 ; 0.05 ; pull=0.4\r\nboard ; bjarki_goldtooth ; file ; -1.17 ; 0 ; 9.42 ; 134.45\r\ncaravan ; bjarki_goldtooth ; file ; -3.3 ; 0 ; -12.23 ; 68.75\r\nbuilding ; ragnar_turnipson ; plains-house ; auto ; auto ; 0 ; 4 ; step=5\r\ntrader ; ragnar_turnipson ; seat ; piece_bathtub\r\nboard ; ragnar_turnipson ; file ; 6.83 ; 0 ; -0.58 ; 315.55\r\ncaravan ; ragnar_turnipson ; file ; 13.48 ; 0 ; 6.83 ; 245.55\r\nwagon ; midka ; file ; -1.95 ; 0 ; 15.89 ; 188.35\r\nwagon ; troldad ; file ; 15.11 ; 0.11 ; -3.58 ; 2.85\r\nwagon ; bjarki_goldtooth ; file ; -9.91 ; 0 ; -18.12 ; 38.95\r\nwagon ; ragnar_turnipson ; file ; 27.62 ; 0 ; 6.96 ; 283.8\r\nwagon ; ylva_frost ; file ; -18.93 ; 0 ; -3.3 ; 131.1\r\nwagon ; hrothgar ; file ; 17.45 ; 6.5 ; 16.07 ; 312.15\r\nskip ; hrothgar ; rune_floor_bal, stone_floor_triangle_bal, stone_platform_bal, vigvisir_floor_bal\r\nbuilding ; rudy_warg ; gumdrop-v2 ; auto ; auto ; 0 ; 8 ; foundation ; step=6\r\ntrader ; rudy_warg ; file ; -0.78 ; 4.22 ; 14.28 ; 187.35 ; base=front\nboard ; rudy_warg ; file ; -6.42 ; 0 ; -1.99 ; 214.45\r\nskip ; rudy_warg ; piece_chest_wood, piece_chest_barrel\r\nbuilding ; grelka ; sorcerer-tower ; auto ; auto ; 0 ; 4 ; foundation ; step=5\r\ntrader ; grelka ; file ; 0 ; 0.89 ; -1.35 ; 31.79\r\nboard ; grelka ; file ; 8.24 ; 0 ; 0.52 ; 43.39\r\nwagon ; grelka ; file ; -7.64 ; 0 ; 5.82 ; 70.84\r\ncaravan ; grelka ; file ; -8.39 ; 0 ; 12.42 ; 103.69\r\nwagon ; rudy_warg ; file ; 10.43 ; 0 ; -1.06 ; 241.15\r\ncaravan ; rudy_warg ; file ; -11.81 ; 0 ; 4.72 ; 175.95\r\n# camps coastal revision 20260929\r\nbuilding ; grimvald ; small-lighthouse ; 0 ; 0 ; water ; none ; entrance=3.15 -1.28 0.924 -0.383 ; step=3\r\ntrader ; grimvald ; file ; 0.89 ; 0.46 ; 1.24 ; 150.11 ; base=front\nboard ; grimvald ; file ; 0.23 ; -0.49 ; -6.03 ; 140.56\ncaravan ; grimvald ; file ; 4.85 ; -0.32 ; 5.34 ; 153.91\naltitude ; grimvald ; 1 ; 3\r\nbuilding ; cmok ; gjall-hunter-keep ; 0 ; 0 ; 0 ; 4 ; base=-0.1 ; entrance=-7.9 -4.49 0.924 0.383 ; step=5\ntrader ; cmok ; file ; -14.4 ; 11.29 ; -5.84 ; 258.69 ; base=front\nboard ; cmok ; door\r\nbuilding ; spalony_zenek ; ashen-spire ; 0 ; 0 ; 0 ; 4 ; base=-1.5 ; entrance=-12.6 5.5 -0.924 0.383 ; step=5\ntrader ; spalony_zenek ; inside\r\nboard ; spalony_zenek ; file ; -8.18 ; -2.29 ; 9.03 ; 325.41\ncaravan ; spalony_zenek ; file ; -13.68 ; -2.5 ; -5.01 ; 341.31\nbuilding ; skjold_cinderborn ; barber-shop ; 0 ; 0 ; 0 ; 4 ; base=-0.5 ; entrance=-0.33 -0.47 0.707 0.707\ntrader ; skjold_cinderborn ; file ; -7.12 ; 10.93 ; -3.98 ; 183.1 ; base=front\nboard ; skjold_cinderborn ; file ; 3 ; -0.5 ; -2 ; 45\r\ncaravan ; skjold_cinderborn ; file ; -9.46 ; -0.5 ; 4.09 ; 109.6\nskip ; skjold_cinderborn ; piece_barber, wood_floor, fire_pit_iron\nbuilding ; mokra_dzika ; home-on-stilts-dock ; 0 ; 0 ; 0 ; none ; base=0.8 ; entrance=6 0 0 -1\ntrader ; mokra_dzika ; seat ; piece_throne01\r\nboard ; mokra_dzika ; file ; 6.28 ; 1.05 ; -2.48 ; 151.3\ncaravan ; mokra_dzika ; file ; 20.77 ; 1.05 ; 3.36 ; 231.6\n# camps land-and-cellar revision 20260930\n# camps mokra-water revision 20260930\n"); } lines = File.ReadAllLines(FilePath); lines = CampLayoutUpgrade.Apply(lines, FilePath); } catch (Exception ex) { Plugin.Log.LogError((object)("Camp layout could not be read (" + FilePath + "): " + ex.Message)); lines = "# ImmersiveTrader - trader camp layout (template to paste into)\r\n# format 15\r\n#\r\n# Everything is relative to the camp centre, in metres: x = right, z = forward, rotY in degrees.\r\n#\r\n# building ; trader ; blueprint ; x ; z ; rotY ; margin\r\n# blueprint = file name without extension from BepInEx/config/ImmersiveTrader/blueprints\r\n# (.vbuild from valheimians.com or .blueprint from PlanBuild)\r\n# x, z = where the CENTRE of the building's footprint goes, or auto = moved so that the\r\n# trader spot ('inside' / 'front') is the camp centre\r\n# rotY = turn in degrees, or water = entrance turned towards the nearest water\r\n# margin = flat dirt ground around the building before the terrain slopes gently away,\r\n# or none = leave the terrain as it is\r\n# options (optional, any order):\r\n# base=11 ground level in the blueprint file (default y 0; foundations go below)\r\n# crop=60 keep only pieces within 60 m of the centre (very large builds)\r\n# entrance=x z the entrance in the blueprint file's coordinates (when it has no outer door);\r\n# entrance=x z dx dz ... and the direction out of it (x, z), e.g. -1 0 = out towards -x\r\n# step=5 side=2 trader spot this far from the entrance / off to the side\r\n# board=7.5 5 board this far out from the entrance / aside (default 7.5 5)\r\n# floor=5 an inside trader stands on a floor this high above the ground\r\n# cut=5 leave out everything below this height of the file (a cellar)\r\n# exclude=x1 z1 x2 z2 leave out everything inside this rectangle of the file\r\n# foundation extend the lowest stone walls 4 m down into the ground\r\n# fence=stake_wall a fence around the building with a gap at the entrance\r\n# fence=a@h+b@h several pieces per fence segment at these heights (Midka-like yard fence)\r\n# remove=x1 z1 x2 z2 y1 y2 leave out everything inside this box of the file (one roof, one bench)\r\n# settle every separate building goes down onto the ground (a village built on hills)\r\n# paths grass stays: dirt only under each building, paved paths between them\r\n# greenery vines on the outer walls of the building (a forest house)\r\n# nature=none leave out the blueprint's own trees, stones and bushes\r\n# (crop keeps whole buildings: those whose centre lies within the distance)\r\n# trader ; trader ; inside | front\r\n# inside = on a floor 5+ m in from the entrance, facing it; front = step m out from the entrance\r\n# (the entrance is the building's outer door, or its outermost stairs when it has no door)\r\n# trader ; trader ; seat ; prefab\r\n# seated on that seat piece of the building (a chair, a bench, piece_bathtub), with its animation\r\n# optional: ; x ; z (the seat of that prefab nearest to it) and ; pull=0.4 (the seat moved back first)\r\n# decor ; trader ; prefab ; x ; z ; rotY [; scale]\r\n# an extra piece (a rock, a bush, a tree) on the camp ground, in the blueprint file's coordinates\r\n# caravan ; trader ; file ; x ; y ; z ; rotY or caravan ; trader ; x ; z ; rotY\r\n# where the trader's Lox caravan stands: it camp caravan (stand there, look\n# where its head should point); file = blueprint coordinates, else the camp's own frame\r\n# board ; trader ; file ; x ; y ; z ; rotY\r\n# exact board spot in file coordinates (it camp board: stand there, look where readers will stand)\r\n# trader ; trader ; file ; x ; y ; z ; rotY\r\n# exact spot in the blueprint file's coordinates, the same in every world: stand there and\r\n# type it camp trader (it camp drop leaves out the building you stand next to)\r\n# trader ; trader ; x ; y ; z ; rotY\r\n# exact spot; y = height above the camp ground, or auto = on the floor found there\r\n# board ; trader ; door\r\n# beside the path in front of the entrance, facing out\r\n# board ; trader ; near\r\n# beside the door nearest to the trader (a trader standing in a village square)\r\n# altitude ; trader ; min ; max\r\n# height above the water where the camp may be generated (e.g. low / high mountain slopes)\r\n# area ; trader ; edge | median | everywhere\r\n# where in its biome the camp may be generated (edge = the border of the biome)\r\n# board ; trader ; x ; z ; rotY\r\n# exact spot (default without a board line: in front of the trader)\r\n# skip ; trader ; prefab, prefab, ...\r\n# blueprint pieces left out of this trader's buildings (e.g. a stone floor: dirt instead)\r\n#\r\n# Buildings are local visuals: nothing is saved into the world, they cannot be damaged, built on\r\n# or looted, and doors are left out so doorways stay open. Terrain levelling, the no-build area and\r\n# the camp size apply to camps generated after the change (a new world shows everything).\r\n# Preview anywhere: it blueprint build <name> Rebuild camps after editing: it camp reload\r\n# Lines starting with # are ignored. Decimal separator: dot or comma.\r\n\r\n\r\n# Current saved positions; moved together with their original houses.\r\nbuilding ; midka ; meadows-home ; auto ; auto ; 0 ; 4 ; step=5\r\ntrader ; midka ; inside\r\nboard ; midka ; door\r\ncaravan ; midka ; file ; 3.61 ; 0 ; -5.64 ; 286.55\r\nskip ; midka ; piece_chest_wood, piece_xmastree\r\nbuilding ; troldad ; arty-mtippi ; auto ; auto ; 0 ; 3 ; step=5.5 ; side=2.5\r\ntrader ; troldad ; front\r\nboard ; troldad ; file ; 9.13 ; 0 ; 15.34 ; 20.9\r\ncaravan ; troldad ; file ; 16.94 ; 0 ; 6.35 ; 23.19\r\nbuilding ; encek ; death-mountain ; auto ; auto ; water ; none ; base=4 ; entrance=-9.5 5 ; step=3 ; side=2.5 ; board=2 6\r\ntrader ; encek ; file ; -51.09 ; 21.67 ; 10.18 ; 296.19 ; base=front\nboard ; encek ; file ; -8.08 ; 2.74 ; -13.25 ; 124.84\r\ncaravan ; encek ; file ; -3.12 ; 3.75 ; 7.81 ; 160.39\r\nalti