// Consolidated decompiled source — Wubarrk-Mists_of_Avalor_BETA v0.1.0 // Generated by Hexium's decompiled-source browser. Best-effort concatenation of every type in this // version's manifest — decompiler output isn't guaranteed to compile as-is. using UnityEngine; using System.Collections.Generic; using System.Reflection; using HarmonyLib; using Jotunn; using System; using BepInEx.Configuration; using UnityEngine.UI; using UnityEngine.Rendering; using Jotunn.Entities; using System.Collections; using System.IO; using System.Text; using BepInEx; using BepInEx.Logging; using Jotunn.Managers; using Jotunn.Utils; using System.Threading; // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorAtmoKind ---- namespace MistsofAvalor { public enum AvalorAtmoKind { Mist, Torch } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorAtmoProp ---- namespace MistsofAvalor { public class AvalorAtmoProp : MonoBehaviour { public AvalorAtmoKind Kind; public const string AlwaysLitKey = "AvalorAlwaysLit"; private ParticleSystem[] _systems; private ParticleSystemRenderer[] _psRenderers; private Light[] _lights; private float[] _baseRate; private ParticleSystem.MinMaxGradient[] _baseColor; private float[] _baseIntensity; private ZNetView _nview; private float _bias; private float _timer; private bool _ready; private float _lastBright = -1f; private float _pendingBright = -1f; private float _lastIntensity = -1f; private void Start() { _systems = GetComponentsInChildren(includeInactive: true); _psRenderers = GetComponentsInChildren(includeInactive: true); _lights = GetComponentsInChildren(includeInactive: true); _nview = GetComponent(); _baseRate = new float[_systems.Length]; for (int i = 0; i < _systems.Length; i++) { _baseRate[i] = _systems[i].emission.rateOverTime.constant; } _bias = PositionBias(base.transform.position); if (Kind == AvalorAtmoKind.Torch) { TintGreen(); } _baseColor = new ParticleSystem.MinMaxGradient[_systems.Length]; for (int j = 0; j < _systems.Length; j++) { if (_systems[j] != null) { _baseColor[j] = _systems[j].main.startColor; } } _baseIntensity = new float[_lights.Length]; for (int k = 0; k < _lights.Length; k++) { if (_lights[k] != null) { _baseIntensity[k] = _lights[k].intensity; } } _ready = true; Apply(force: true); } private void Update() { if (_ready) { _timer += Time.deltaTime; if (!(_timer < 1f)) { _timer = 0f; Apply(force: false); } } } private void Apply(bool force) { bool flag = _nview != null && _nview.IsValid() && _nview.GetZDO() != null && _nview.GetZDO().GetInt("AvalorAlwaysLit") == 1; float num = 1f; float num2; if (Kind == AvalorAtmoKind.Mist) { num2 = MistFraction(MistsofAvalorPlugin.MistConfig.Value); } else { LightingLevel value = MistsofAvalorPlugin.LightingConfig.Value; num = (flag ? 1f : TorchIntensityFor(value)); num2 = ((flag || value != LightingLevel.None) ? 1f : 0f); } bool flag2 = num2 > 0f && _bias < num2; if (!flag2 && flag) { flag2 = true; } bool flag3 = Kind == AvalorAtmoKind.Torch && (force || !Mathf.Approximately(num, _lastIntensity)); for (int i = 0; i < _lights.Length; i++) { if (!(_lights[i] == null)) { if (_lights[i].enabled != flag2) { _lights[i].enabled = flag2; } if (flag3) { _lights[i].intensity = _baseIntensity[i] * num; } } } if (flag3) { _lastIntensity = num; } for (int j = 0; j < _systems.Length; j++) { ParticleSystem particleSystem = _systems[j]; if (particleSystem == null) { continue; } if (flag2) { ParticleSystem.EmissionModule emission = particleSystem.emission; float num3 = ((Kind == AvalorAtmoKind.Mist) ? Mathf.Lerp(0.15f, 1f, num2) : 1f); emission.rateOverTime = _baseRate[j] * num3; if (Kind == AvalorAtmoKind.Mist && _baseColor != null) { float num4 = AvalorCryptEnvZone.MistBrightnessFor(MistsofAvalorPlugin.LightingConfig.Value); if (force || !Mathf.Approximately(num4, _lastBright)) { ParticleSystem.MainModule main = particleSystem.main; main.startColor = Dim(_baseColor[j], num4); _pendingBright = num4; } } if (!particleSystem.isPlaying) { particleSystem.Play(withChildren: false); } } else if (particleSystem.isPlaying) { particleSystem.Stop(withChildren: false, ParticleSystemStopBehavior.StopEmittingAndClear); } } for (int k = 0; k < _psRenderers.Length; k++) { if (_psRenderers[k] != null) { _psRenderers[k].enabled = flag2; } } if (_pendingBright >= 0f) { _lastBright = _pendingBright; _pendingBright = -1f; } } private static ParticleSystem.MinMaxGradient Dim(ParticleSystem.MinMaxGradient src, float f) { if (src.mode == ParticleSystemGradientMode.TwoColors) { return new ParticleSystem.MinMaxGradient(Scale(src.colorMin, f), Scale(src.colorMax, f)); } return new ParticleSystem.MinMaxGradient(Scale(src.color, f)); } private static Color Scale(Color c, float f) { return new Color(c.r * f, c.g * f, c.b * f, c.a * f); } private void TintGreen() { Color avalorGreen = AvalorCryptEnvZone.AvalorGreen; Light[] lights = _lights; foreach (Light light in lights) { if (!(light == null)) { light.color = avalorGreen; } } ParticleSystem[] systems = _systems; foreach (ParticleSystem particleSystem in systems) { if (!(particleSystem == null)) { ParticleSystem.MainModule main = particleSystem.main; ParticleSystem.MinMaxGradient startColor = main.startColor; switch (startColor.mode) { case ParticleSystemGradientMode.Color: main.startColor = Recolor(startColor.color, avalorGreen); break; case ParticleSystemGradientMode.TwoColors: main.startColor = new ParticleSystem.MinMaxGradient(Recolor(startColor.colorMin, avalorGreen), Recolor(startColor.colorMax, avalorGreen)); break; default: main.startColor = Recolor(startColor.color, avalorGreen); break; } } } } private static Color Recolor(Color src, Color green) { float num = Mathf.Max(0.15f, src.grayscale); Color result = green * num; result.a = src.a; return result; } private static float PositionBias(Vector3 p) { int num = Mathf.RoundToInt(p.x * 4f); int num2 = Mathf.RoundToInt(p.y * 4f); int num3 = Mathf.RoundToInt(p.z * 4f); int num4 = (num * 73856093) ^ (num2 * 19349663) ^ (num3 * 83492791); int num5 = (num4 ^ (num4 >>> 13)) * -2048144789; return (float)((uint)(num5 ^ (num5 >>> 16)) % 100000u) / 100000f; } public static float MistFraction(MistDensity d) { return d switch { MistDensity.None => 0f, MistDensity.Little => 0.05f, MistDensity.Normal => 0.25f, MistDensity.More => 0.55f, MistDensity.Most => 1f, _ => 0.25f, }; } public static float TorchIntensityFor(LightingLevel l) { return l switch { LightingLevel.None => 0f, LightingLevel.Some => 0.38f, LightingLevel.More => 0.68f, LightingLevel.Most => 1f, _ => 0.38f, }; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorCryptEnvZone ---- namespace MistsofAvalor { public class AvalorCryptEnvZone : MonoBehaviour { private const string CryptEnv = "Crypt"; private float timer; private bool forcing; public static bool DebugBright = false; public static readonly Color AvalorGreen = new Color(0.3f, 1f, 0.55f); private static readonly Color FogGreen = new Color(0.1f, 0.26f, 0.19f); private const float GreenTint = 0.45f; private const float GreenBoost = 1.15f; private static readonly Color BaseAmbient = new Color(0.42f, 0.46f, 0.44f); private readonly List _suppressed = new List(); private static readonly FieldInfo CurrentPSystemsField = AccessTools.Field(typeof(EnvMan), "m_currentPSystems"); private void Update() { bool flag = Player.m_localPlayer != null && AvalorRegion.InSkyMaze(Player.m_localPlayer.transform.position); bool flag2 = !forcing && flag; timer += Time.deltaTime; if (timer < 1f && !flag2) { return; } timer = 0f; if (EnvMan.instance == null || Player.m_localPlayer == null) { forcing = false; } else if (flag) { if (!forcing && MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"[Avalor] Entered sky maze - forcing 'Crypt' environment (overriding biome)."); } EnvMan.instance.SetForceEnvironment("Crypt"); forcing = true; } else if (forcing) { if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"[Avalor] Left sky maze - releasing forced environment."); } EnvMan.instance.SetForceEnvironment(""); forcing = false; } } private void LateUpdate() { if (Player.m_localPlayer == null) { RestoreEnvParticles(); return; } if (!AvalorRegion.InSkyMaze(Player.m_localPlayer.transform.position)) { RestoreEnvParticles(); return; } if (DebugBright) { RenderSettings.ambientLight = new Color(0.9f, 0.9f, 0.9f); RenderSettings.fogDensity = 0f; return; } MistDensity value = MistsofAvalorPlugin.MistConfig.Value; bool flag = (RenderSettings.fog = value != MistDensity.None); if (flag) { RenderSettings.fogMode = FogMode.ExponentialSquared; RenderSettings.fogColor = FogGreen; RenderSettings.fogDensity = FogDensityFor(value); } ApplyEnvParticles(flag); float num = AmbientScaleFor(MistsofAvalorPlugin.LightingConfig.Value); Color a = BaseAmbient * num; RenderSettings.ambientLight = Color.Lerp(a, AvalorGreen * a.grayscale * 1.15f, 0.45f); } private void ApplyEnvParticles(bool wanted) { if (wanted) { RestoreEnvParticles(); } else { if (EnvMan.instance == null || CurrentPSystemsField == null || !(CurrentPSystemsField.GetValue(EnvMan.instance) is GameObject[] array)) { return; } GameObject[] array2 = array; foreach (GameObject gameObject in array2) { if (!(gameObject == null) && gameObject.activeSelf) { gameObject.SetActive(value: false); if (!_suppressed.Contains(gameObject)) { _suppressed.Add(gameObject); } } } } } private void RestoreEnvParticles() { if (_suppressed.Count == 0) { return; } foreach (GameObject item in _suppressed) { if (item != null) { item.SetActive(value: true); } } _suppressed.Clear(); } public static float FogDensityFor(MistDensity d) { return d switch { MistDensity.None => 0f, MistDensity.Little => 0.0016f, MistDensity.Normal => 0.0055f, MistDensity.More => 0.016f, MistDensity.Most => 0.042f, _ => 0.0055f, }; } public static float MistBrightnessFor(LightingLevel l) { return Mathf.Lerp(0.18f, 1f, AmbientScaleFor(l)); } public static float AmbientScaleFor(LightingLevel l) { return l switch { LightingLevel.None => 0.04f, LightingLevel.Some => 0.38f, LightingLevel.More => 0.7f, LightingLevel.Most => 1f, _ => 0.38f, }; } private void OnDestroy() { RestoreEnvParticles(); if (!forcing) { return; } forcing = false; try { if (EnvMan.instance != null) { EnvMan.instance.SetForceEnvironment(""); } } catch { } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorExitCompass ---- namespace MistsofAvalor { public class AvalorExitCompass : MonoBehaviour { public static ConfigEntry CompassX; public static ConfigEntry CompassY; public static ConfigEntry CompassSize; public static ConfigEntry CompassOpacity; public static ConfigEntry CompassTilt; public static ConfigEntry CompassAdjustKey; private GameObject _uiRoot; private RectTransform _rootRt; private CanvasGroup _group; private RectTransform _disc; private RectTransform _blip; private RectTransform _blipStem; private RectTransform _player; private RectTransform[] _ring; private RectTransform[] _spokes; private Text _label; private Text _hint; private Font _valheimFont; private const float RefreshInterval = 1f; private float _timer = 1f; private Vector3 _exit; private bool _haveExit; private const float RimRange = 120f; private const int RingSegments = 56; private bool _adjusting; private bool _dragging; private Vector2 _dragOffset; private bool _dirty; private float _builtSize = -1f; private float _builtSquash = -1f; private float Squash => Mathf.Max(0.12f, Mathf.Sin(CompassTilt.Value * (MathF.PI / 180f))); private void Awake() { ConfigFile config = MistsofAvalorPlugin.Instance.Config; CompassX = config.Bind("Compass", "PositionX", -240f, "Horizontal offset of the exit compass from the centre of the screen, in pixels. Set by dragging it; you should not need to edit this by hand."); CompassY = config.Bind("Compass", "PositionY", 180f, "Vertical offset of the exit compass from the centre of the screen, in pixels. Set by dragging it; you should not need to edit this by hand."); CompassSize = config.Bind("Compass", "Size", 190f, new ConfigDescription("Diameter of the exit compass disc in pixels.", new AcceptableValueRange(90f, 460f))); CompassOpacity = config.Bind("Compass", "Opacity", 0.55f, new ConfigDescription("How solid the compass is. Low values keep it as a ghost over the labyrinth; 1 is fully opaque.", new AcceptableValueRange(0.1f, 1f))); CompassTilt = config.Bind("Compass", "TiltDegrees", 60f, new ConfigDescription("The angle the disc is viewed from, in degrees above the horizontal. 90 is a flat top-down circle; lower lays the view down further and squashes it into a wider ellipse. 60 is the default raked view.", new AcceptableValueRange(15f, 90f))); CompassAdjustKey = config.Bind("Compass", "AdjustKey", new KeyboardShortcut(KeyCode.LeftAlt), "Hold this to move and resize the compass. While held: drag it with the left mouse button, or nudge it with the arrow keys, and resize with the mouse wheel or +/-."); } private void LateUpdate() { if (MistsofAvalorPlugin.ExitCompass == null || !MistsofAvalorPlugin.ExitCompass.Value) { Hide(); return; } Player localPlayer = Player.m_localPlayer; if (localPlayer == null || Hud.instance == null) { Hide(); return; } if (!AvalorRegion.InSkyMaze(localPlayer.transform.position)) { Hide(); return; } _timer += Time.deltaTime; if (_timer >= 1f) { _timer = 0f; _haveExit = AvalorMobDirector.TryFindExit(out _exit); if (!_haveExit && AvalorNet.Role == AvalorNet.NetRole.Client) { AvalorNet.SendQuery(1); if (AvalorNet.TryGetFreshAnswer(1, 30f, out var found, out var pos) && found) { _haveExit = true; _exit = pos; } } } if (!_haveExit) { Hide(); return; } if (_uiRoot == null) { CreateUI(); } if (!(_uiRoot == null)) { if (!_uiRoot.activeSelf) { _uiRoot.SetActive(value: true); } HandleAdjust(); ApplyLayout(); DrawBearing(localPlayer); } } private void HandleAdjust() { bool flag = CompassAdjustKey.Value.IsPressed(); if (flag && !_adjusting) { _adjusting = true; } else if (!flag && _adjusting) { _adjusting = false; _dragging = false; if (_dirty) { _dirty = false; MistsofAvalorPlugin.Instance.Config.Save(); } } if (_adjusting) { Cursor.lockState = CursorLockMode.None; Cursor.visible = true; float num = ((Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) ? 12f : 2f); if (Input.GetKey(KeyCode.LeftArrow)) { CompassX.Value -= num; _dirty = true; } if (Input.GetKey(KeyCode.RightArrow)) { CompassX.Value += num; _dirty = true; } if (Input.GetKey(KeyCode.DownArrow)) { CompassY.Value -= num; _dirty = true; } if (Input.GetKey(KeyCode.UpArrow)) { CompassY.Value += num; _dirty = true; } float num2 = 0f; if (Input.GetKey(KeyCode.Equals) || Input.GetKey(KeyCode.KeypadPlus)) { num2 += 1.5f; } if (Input.GetKey(KeyCode.Minus) || Input.GetKey(KeyCode.KeypadMinus)) { num2 -= 1.5f; } num2 += Input.GetAxis("Mouse ScrollWheel") * 220f; if (Mathf.Abs(num2) > 0.001f) { CompassSize.Value = Mathf.Clamp(CompassSize.Value + num2, 90f, 460f); _dirty = true; RebuildRose(); } Canvas componentInParent = _uiRoot.GetComponentInParent(); Camera cam = ((componentInParent != null && componentInParent.renderMode != RenderMode.ScreenSpaceOverlay) ? componentInParent.worldCamera : null); if (Input.GetMouseButtonDown(0) && RectTransformUtility.RectangleContainsScreenPoint(_rootRt, Input.mousePosition, cam)) { _dragging = true; RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)_rootRt.parent, Input.mousePosition, cam, out var localPoint); _dragOffset = localPoint - _rootRt.anchoredPosition; } if (Input.GetMouseButtonUp(0)) { _dragging = false; } if (_dragging && RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)_rootRt.parent, Input.mousePosition, cam, out var localPoint2)) { Vector2 vector = localPoint2 - _dragOffset; CompassX.Value = vector.x; CompassY.Value = vector.y; _dirty = true; } } } private void ApplyLayout() { float value = CompassSize.Value; if (Mathf.Abs(_builtSize - value) > 0.5f || Mathf.Abs(_builtSquash - Squash) > 0.001f) { RebuildRose(); } _rootRt.anchoredPosition = new Vector2(CompassX.Value, CompassY.Value); _rootRt.sizeDelta = new Vector2(value, value * Squash + 46f); _group.alpha = (_adjusting ? Mathf.Max(0.9f, CompassOpacity.Value) : CompassOpacity.Value); _hint.enabled = _adjusting; } private void DrawBearing(Player p) { Vector3 vector = _exit - p.transform.position; float magnitude = new Vector2(vector.x, vector.z).magnitude; Vector3 forward = ((Utils.GetMainCamera() != null) ? Utils.GetMainCamera().transform : p.transform).forward; forward.y = 0f; if (forward.sqrMagnitude < 0.0001f) { forward = Vector3.forward; } Vector3 to = vector; to.y = 0f; if (to.sqrMagnitude < 0.0001f) { to = forward; } float f = Vector3.SignedAngle(forward, to, Vector3.up) * (MathF.PI / 180f); float num = CompassSize.Value * 0.5f; float a = num * Mathf.Clamp01(magnitude / 120f); a = Mathf.Max(a, num * 0.06f); float x = Mathf.Sin(f) * a; float num2 = Mathf.Cos(f) * a * Squash; float num3 = Mathf.Clamp(vector.y * (num / 120f), (0f - num) * 0.5f, num * 0.5f); _blip.anchoredPosition = new Vector2(x, num2 + num3); _blipStem.anchoredPosition = new Vector2(x, num2 + num3 * 0.5f); _blipStem.sizeDelta = new Vector2(2f, Mathf.Abs(num3)); _blipStem.gameObject.SetActive(Mathf.Abs(num3) > 3f); string arg = ""; if (vector.y > 3f) { arg = " above"; } else if (vector.y < -3f) { arg = " below"; } _label.text = $"The Way Out {magnitude:F0}m{arg}"; } private void Hide() { if (_uiRoot != null && _uiRoot.activeSelf) { _uiRoot.SetActive(value: false); } } private void CreateUI() { if (!(Hud.instance == null) && !(Hud.instance.m_rootObject == null)) { Text componentInChildren = Hud.instance.m_rootObject.GetComponentInChildren(includeInactive: true); if (componentInChildren != null) { _valheimFont = componentInChildren.font; } Font font = ((_valheimFont != null) ? _valheimFont : Resources.GetBuiltinResource("Arial.ttf")); _uiRoot = new GameObject("AvalorExitCompass"); _uiRoot.transform.SetParent(Hud.instance.m_rootObject.transform, worldPositionStays: false); _rootRt = _uiRoot.AddComponent(); _rootRt.anchorMin = new Vector2(0.5f, 0.5f); _rootRt.anchorMax = new Vector2(0.5f, 0.5f); _rootRt.pivot = new Vector2(0.5f, 0.5f); _group = _uiRoot.AddComponent(); _group.interactable = false; _group.blocksRaycasts = false; GameObject gameObject = new GameObject("Disc"); gameObject.transform.SetParent(_uiRoot.transform, worldPositionStays: false); _disc = gameObject.AddComponent(); _disc.anchorMin = new Vector2(0.5f, 0.5f); _disc.anchorMax = new Vector2(0.5f, 0.5f); _disc.pivot = new Vector2(0.5f, 0.5f); _disc.anchoredPosition = new Vector2(0f, 14f); _ring = new RectTransform[56]; for (int i = 0; i < 56; i++) { _ring[i] = Quad("Ring" + i, _disc, Ink(0.55f)); } _spokes = new RectTransform[4]; for (int j = 0; j < 4; j++) { _spokes[j] = Quad("Spoke" + j, _disc, Ink(0.38f)); } _player = Quad("Player", _disc, Ink(0.95f)); _player.sizeDelta = new Vector2(7f, 7f); _player.localRotation = Quaternion.Euler(0f, 0f, 45f); _blipStem = Quad("BlipStem", _disc, Ink(0.45f)); _blip = Quad("Blip", _disc, new Color(0.62f, 1f, 0.78f, 1f)); _blip.sizeDelta = new Vector2(11f, 11f); _blip.localRotation = Quaternion.Euler(0f, 0f, 45f); _label = Label("Label", _uiRoot.transform, new Vector2(0f, 0f), 15, font); _hint = Label("Hint", _uiRoot.transform, new Vector2(0f, 0f), 12, font); _hint.color = new Color(0.62f, 1f, 0.78f, 0.9f); _hint.text = "drag • arrows nudge • wheel / +- resize"; _hint.enabled = false; RebuildRose(); _uiRoot.SetActive(value: false); } } private void RebuildRose() { if (_ring != null) { float value = CompassSize.Value; float num = value * 0.5f; float squash = Squash; _builtSize = value; _builtSquash = squash; for (int i = 0; i < 56; i++) { float f = (float)i / 56f * MathF.PI * 2f; float x = Mathf.Sin(f) * num; float y = Mathf.Cos(f) * num * squash; float x2 = Mathf.Cos(f) * num; float y2 = (0f - Mathf.Sin(f)) * num * squash; float a = new Vector2(x2, y2).magnitude * (MathF.PI / 28f); _ring[i].anchoredPosition = new Vector2(x, y); _ring[i].sizeDelta = new Vector2(Mathf.Max(a, 2f), 2f); _ring[i].localRotation = Quaternion.Euler(0f, 0f, Mathf.Atan2(y2, x2) * 57.29578f); } for (int j = 0; j < 4; j++) { float num2 = (float)j * MathF.PI * 0.5f; float num3 = ((j == 0) ? 0.7f : 0.82f); float num4 = Mathf.Sin(num2) * num; float num5 = Mathf.Cos(num2) * num * squash; float num6 = num * (1f - num3); _spokes[j].anchoredPosition = new Vector2(num4 * (num3 + (1f - num3) * 0.5f), num5 * (num3 + (1f - num3) * 0.5f)); _spokes[j].sizeDelta = new Vector2(2f, Mathf.Max(num6 * ((j == 0) ? (squash + 0.3f) : squash), 3f)); _spokes[j].localRotation = Quaternion.Euler(0f, 0f, (0f - num2) * 57.29578f); _spokes[j].GetComponent().color = Ink((j == 0) ? 0.8f : 0.34f); } if (_label != null) { ((RectTransform)_label.transform).anchoredPosition = new Vector2(0f, (0f - num) * squash - 4f); } if (_hint != null) { ((RectTransform)_hint.transform).anchoredPosition = new Vector2(0f, num * squash + 30f); } } } private static Color Ink(float a) { Color avalorGreen = AvalorCryptEnvZone.AvalorGreen; return new Color(avalorGreen.r, avalorGreen.g, avalorGreen.b, a); } private static RectTransform Quad(string name, Transform parent, Color colour) { GameObject gameObject = new GameObject(name); gameObject.transform.SetParent(parent, worldPositionStays: false); RectTransform rectTransform = gameObject.AddComponent(); rectTransform.anchorMin = new Vector2(0.5f, 0.5f); rectTransform.anchorMax = new Vector2(0.5f, 0.5f); rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.sizeDelta = new Vector2(2f, 2f); Image image = gameObject.AddComponent(); image.color = colour; image.raycastTarget = false; return rectTransform; } private static Text Label(string name, Transform parent, Vector2 pos, int fontSize, Font font) { GameObject gameObject = new GameObject(name); gameObject.transform.SetParent(parent, worldPositionStays: false); RectTransform rectTransform = gameObject.AddComponent(); rectTransform.anchorMin = new Vector2(0.5f, 0.5f); rectTransform.anchorMax = new Vector2(0.5f, 0.5f); rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.anchoredPosition = pos; rectTransform.sizeDelta = new Vector2(360f, 22f); Text text = gameObject.AddComponent(); text.font = font; text.fontSize = fontSize; text.alignment = TextAnchor.MiddleCenter; text.color = new Color(0.86f, 0.94f, 0.88f, 0.92f); text.raycastTarget = false; Shadow shadow = gameObject.AddComponent(); shadow.effectColor = new Color(0f, 0f, 0f, 0.85f); shadow.effectDistance = new Vector2(1f, -1f); return text; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorPortalEnvZone ---- namespace MistsofAvalor { public class AvalorPortalEnvZone : MonoBehaviour { private const float OuterRadius = 32f; private const float InnerRadius = 14f; private static readonly Color GloomFog = new Color(0.16f, 0.34f, 0.25f); private const float GloomFogDensity = 0.028f; private const float AmbientDarken = 0.75f; private const float GreenTint = 0.55f; private bool _haveBase; private Color _baseFog; private Color _baseAmbient; private float _baseDensity; private bool _baseFogEnabled; private void LateUpdate() { if (Player.m_localPlayer == null) { return; } Vector3 position = Player.m_localPlayer.transform.position; if (position.x > 25000f) { return; } float value = Vector3.Distance(position, base.transform.position); float num = Mathf.Clamp01(Mathf.InverseLerp(32f, 14f, value)); if (num <= 0f) { _baseFog = RenderSettings.fogColor; _baseAmbient = RenderSettings.ambientLight; _baseDensity = RenderSettings.fogDensity; _baseFogEnabled = RenderSettings.fog; _haveBase = true; return; } if (!_haveBase) { _baseFog = RenderSettings.fogColor; _baseAmbient = RenderSettings.ambientLight; _baseDensity = RenderSettings.fogDensity; _baseFogEnabled = RenderSettings.fog; _haveBase = true; } num = Mathf.SmoothStep(0f, 1f, num); RenderSettings.fog = true; RenderSettings.fogColor = Color.Lerp(_baseFog, GloomFog, num); RenderSettings.fogDensity = Mathf.Lerp(_baseFogEnabled ? _baseDensity : 0f, 0.028f, num); Color a = _baseAmbient * 0.75f; Color b = AvalorCryptEnvZone.AvalorGreen * a.grayscale * 1.5f; RenderSettings.ambientLight = Color.Lerp(_baseAmbient, Color.Lerp(a, b, 0.55f), num); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorPortalRuneVFX ---- namespace MistsofAvalor { public class AvalorPortalRuneVFX : MonoBehaviour { public Color Colour = new Color(0.3f, 1f, 0.55f); public float GateWidth = 3.2f; public float GateHeight = 7f; [NonSerialized] public float Charge; private ParticleSystem _veil; private ParticleSystem _motes; private LineRenderer _ringA; private LineRenderer _ringB; private LineRenderer _sigil; private LineRenderer _sigilInner; private LineRenderer[] _spokes; private ParticleSystem _sigilRunes; private float _spin; private const int RingSegments = 64; private static Texture2D _atlasTex; private static Texture2D _softTex; private void Start() { if (_atlasTex == null) { _atlasTex = AvalorRuneTexture.GenerateRuneAtlas(Color.white); } if (_softTex == null) { _softTex = AvalorRuneTexture.GenerateSoftTrail(Color.white); } Texture2D atlasTex = _atlasTex; Texture2D softTex = _softTex; Material orCreateRuneMaterial = AvalorRuneMaterial.GetOrCreateRuneMaterial(atlasTex, Colour); Material orCreateRuneMaterial2 = AvalorRuneMaterial.GetOrCreateRuneMaterial(softTex, Colour); if (orCreateRuneMaterial == null || orCreateRuneMaterial2 == null) { Logger.LogWarning((object)"[Avalor] rune VFX: no usable particle shader - portal keeps vanilla effects only."); base.enabled = false; return; } BuildVeil(orCreateRuneMaterial); BuildRings(orCreateRuneMaterial2); BuildSigil(orCreateRuneMaterial2, orCreateRuneMaterial); BuildMotes(orCreateRuneMaterial); } private void BuildVeil(Material mat) { GameObject gameObject = Child("RuneVeil", new Vector3(0f, GateHeight * 0.42f, 0f)); _veil = gameObject.AddComponent(); ParticleSystem.MainModule main = _veil.main; main.loop = true; main.playOnAwake = true; main.startSpeed = new ParticleSystem.MinMaxCurve(0.15f, 0.5f); main.startLifetime = new ParticleSystem.MinMaxCurve(1.6f, 3.2f); main.startSize = new ParticleSystem.MinMaxCurve(0.22f, 0.55f); main.startColor = Colour; main.maxParticles = 220; main.simulationSpace = ParticleSystemSimulationSpace.Local; main.startRotation = new ParticleSystem.MinMaxCurve(0f, MathF.PI * 2f); ParticleSystem.EmissionModule emission = _veil.emission; emission.rateOverTime = 26f; ParticleSystem.ShapeModule shape = _veil.shape; shape.enabled = true; shape.shapeType = ParticleSystemShapeType.Box; shape.scale = new Vector3(GateWidth * 0.82f, GateHeight * 0.72f, 0.12f); ParticleSystem.VelocityOverLifetimeModule velocityOverLifetime = _veil.velocityOverLifetime; velocityOverLifetime.enabled = true; velocityOverLifetime.space = ParticleSystemSimulationSpace.Local; velocityOverLifetime.x = new ParticleSystem.MinMaxCurve(-0.06f, 0.06f); velocityOverLifetime.y = new ParticleSystem.MinMaxCurve(0.25f, 0.75f); velocityOverLifetime.z = new ParticleSystem.MinMaxCurve(-0.06f, 0.06f); FadeInOut(_veil); Atlas(_veil); ParticleSystemRenderer component = gameObject.GetComponent(); component.renderMode = ParticleSystemRenderMode.Billboard; component.material = mat; component.shadowCastingMode = ShadowCastingMode.Off; component.receiveShadows = false; } private void BuildRings(Material glow) { float num = GateWidth * 0.44f; _ringA = BuildCircle("RuneRingA", new Vector3(0f, GateHeight * 0.45f, 0f), num, glow, 0.055f, vertical: true); _ringB = BuildCircle("RuneRingB", new Vector3(0f, GateHeight * 0.45f, 0f), num * 0.68f, glow, 0.04f, vertical: true); _spokes = new LineRenderer[4]; for (int i = 0; i < _spokes.Length; i++) { LineRenderer lineRenderer = Child("RuneSpoke" + i, new Vector3(0f, GateHeight * 0.45f, 0f)).AddComponent(); float f = MathF.PI / (float)_spokes.Length * (float)i; Vector3 vector = new Vector3(Mathf.Cos(f), Mathf.Sin(f), 0f) * (num * 0.68f); Line(lineRenderer, glow, 0.03f, new Vector3[2] { -vector, vector }); _spokes[i] = lineRenderer; } } private void BuildSigil(Material glow, Material runeMat) { float num = GateWidth * 0.85f; _sigil = BuildCircle("SigilOuter", new Vector3(0f, 0.06f, 0f), num, glow, 0.07f, vertical: false); _sigilInner = BuildCircle("SigilInner", new Vector3(0f, 0.06f, 0f), num * 0.62f, glow, 0.05f, vertical: false); GameObject gameObject = Child("SigilRunes", new Vector3(0f, 0.35f, 0f)); _sigilRunes = gameObject.AddComponent(); ParticleSystem.MainModule main = _sigilRunes.main; main.loop = true; main.playOnAwake = true; main.startSpeed = 0f; main.startLifetime = new ParticleSystem.MinMaxCurve(1.4f, 2.6f); main.startSize = new ParticleSystem.MinMaxCurve(0.3f, 0.5f); main.startColor = Colour; main.maxParticles = 90; main.simulationSpace = ParticleSystemSimulationSpace.Local; ParticleSystem.EmissionModule emission = _sigilRunes.emission; emission.rateOverTime = 14f; ParticleSystem.ShapeModule shape = _sigilRunes.shape; shape.enabled = true; shape.shapeType = ParticleSystemShapeType.Circle; shape.radius = num * 0.9f; shape.radiusThickness = 0f; shape.rotation = new Vector3(90f, 0f, 0f); FadeInOut(_sigilRunes); Atlas(_sigilRunes); ParticleSystemRenderer component = gameObject.GetComponent(); component.renderMode = ParticleSystemRenderMode.Billboard; component.material = runeMat; component.shadowCastingMode = ShadowCastingMode.Off; component.receiveShadows = false; } private void BuildMotes(Material mat) { GameObject gameObject = Child("RuneMotes", new Vector3(0f, 0.2f, 0f)); _motes = gameObject.AddComponent(); ParticleSystem.MainModule main = _motes.main; main.loop = true; main.playOnAwake = true; main.startSpeed = new ParticleSystem.MinMaxCurve(0.3f, 0.9f); main.startLifetime = new ParticleSystem.MinMaxCurve(3f, 6f); main.startSize = new ParticleSystem.MinMaxCurve(0.1f, 0.26f); main.startColor = Colour; main.maxParticles = 120; main.simulationSpace = ParticleSystemSimulationSpace.World; main.gravityModifier = -0.02f; ParticleSystem.EmissionModule emission = _motes.emission; emission.rateOverTime = 10f; ParticleSystem.ShapeModule shape = _motes.shape; shape.enabled = true; shape.shapeType = ParticleSystemShapeType.Circle; shape.radius = GateWidth * 0.9f; shape.rotation = new Vector3(90f, 0f, 0f); FadeInOut(_motes); Atlas(_motes); ParticleSystemRenderer component = gameObject.GetComponent(); component.renderMode = ParticleSystemRenderMode.Billboard; component.material = mat; component.shadowCastingMode = ShadowCastingMode.Off; component.receiveShadows = false; } private void Update() { Charge = Mathf.Max(0f, Charge - Time.deltaTime * 1.4f); float num = Mathf.Clamp01(Charge); _spin += Time.deltaTime * (18f + 150f * num); if (_ringA != null) { _ringA.transform.localRotation = Quaternion.Euler(0f, 0f, _spin); } if (_ringB != null) { _ringB.transform.localRotation = Quaternion.Euler(0f, 0f, (0f - _spin) * 1.6f); } if (_spokes != null) { LineRenderer[] spokes = _spokes; foreach (LineRenderer lineRenderer in spokes) { if (lineRenderer != null) { lineRenderer.transform.localRotation = Quaternion.Euler(0f, 0f, (0f - _spin) * 1.6f); } } } if (_sigil != null) { _sigil.transform.localRotation = Quaternion.Euler(0f, (0f - _spin) * 0.5f, 0f); } if (_sigilInner != null) { _sigilInner.transform.localRotation = Quaternion.Euler(0f, _spin * 0.8f, 0f); } if (_sigilRunes != null) { _sigilRunes.transform.localRotation = Quaternion.Euler(0f, (0f - _spin) * 0.5f, 0f); } if (_veil != null) { ParticleSystem.EmissionModule emission = _veil.emission; emission.rateOverTime = 26f + 120f * num; ParticleSystem.MainModule main = _veil.main; main.startColor = Colour * (1f + 1.6f * num); } if (_sigilRunes != null) { ParticleSystem.EmissionModule emission2 = _sigilRunes.emission; emission2.rateOverTime = 14f + 50f * num; } float num2 = 1f + 1.8f * num; Width(_ringA, 0.055f * num2); Width(_ringB, 0.04f * num2); Width(_sigil, 0.07f * num2); Width(_sigilInner, 0.05f * num2); if (_spokes != null) { LineRenderer[] spokes = _spokes; for (int i = 0; i < spokes.Length; i++) { Width(spokes[i], 0.03f * num2); } } } public void Discharge() { if (_veil != null) { _veil.Emit(70); } if (_motes != null) { _motes.Emit(45); } if (_sigilRunes != null) { _sigilRunes.Emit(40); } Charge = 1f; } private GameObject Child(string name, Vector3 localPos) { GameObject obj = new GameObject(name); obj.layer = base.gameObject.layer; obj.transform.SetParent(base.transform, worldPositionStays: false); obj.transform.localPosition = localPos; return obj; } private LineRenderer BuildCircle(string name, Vector3 pos, float radius, Material mat, float width, bool vertical) { LineRenderer lineRenderer = Child(name, pos).AddComponent(); Vector3[] array = new Vector3[65]; for (int i = 0; i <= 64; i++) { float f = MathF.PI / 32f * (float)i; float x = Mathf.Cos(f) * radius; float num = Mathf.Sin(f) * radius; array[i] = (vertical ? new Vector3(x, num, 0f) : new Vector3(x, 0f, num)); } Line(lineRenderer, mat, width, array); return lineRenderer; } private void Line(LineRenderer lr, Material mat, float width, Vector3[] pts) { lr.useWorldSpace = false; lr.loop = false; lr.material = mat; lr.positionCount = pts.Length; lr.SetPositions(pts); float startWidth = (lr.endWidth = width); lr.startWidth = startWidth; lr.startColor = Colour; lr.endColor = Colour; lr.shadowCastingMode = ShadowCastingMode.Off; lr.receiveShadows = false; lr.alignment = LineAlignment.View; } private static void Width(LineRenderer lr, float w) { if (!(lr == null)) { float startWidth = (lr.endWidth = w); lr.startWidth = startWidth; } } private static void FadeInOut(ParticleSystem ps) { ParticleSystem.ColorOverLifetimeModule colorOverLifetime = ps.colorOverLifetime; colorOverLifetime.enabled = true; Gradient gradient = new Gradient(); gradient.SetKeys(new GradientColorKey[2] { new GradientColorKey(Color.white, 0f), new GradientColorKey(Color.white, 1f) }, new GradientAlphaKey[4] { new GradientAlphaKey(0f, 0f), new GradientAlphaKey(1f, 0.25f), new GradientAlphaKey(1f, 0.65f), new GradientAlphaKey(0f, 1f) }); colorOverLifetime.color = new ParticleSystem.MinMaxGradient(gradient); } private static void Atlas(ParticleSystem ps) { ParticleSystem.TextureSheetAnimationModule textureSheetAnimation = ps.textureSheetAnimation; textureSheetAnimation.enabled = true; textureSheetAnimation.numTilesX = 4; textureSheetAnimation.numTilesY = 4; textureSheetAnimation.animation = ParticleSystemAnimationType.WholeSheet; textureSheetAnimation.timeMode = ParticleSystemAnimationTimeMode.Lifetime; textureSheetAnimation.startFrame = new ParticleSystem.MinMaxCurve(0f, 16f); textureSheetAnimation.frameOverTime = new ParticleSystem.MinMaxCurve(0f); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorRuneMaterial ---- namespace MistsofAvalor { public static class AvalorRuneMaterial { private static readonly Dictionary _materialCache = new Dictionary(); private static bool _initialized = false; private static void Init() { if (!_initialized) { _initialized = true; Logger.LogInfo((object)"[Avalor] rune VFX: Seeking glowing, additive, unlit shaders worthy of Avalor."); } } private static long MakeCacheKey(Shader shader, Texture2D tex, Color color) { int num = ((shader != null) ? shader.GetInstanceID() : 0); int num2 = ((tex != null) ? tex.GetInstanceID() : 0); int hashCode = color.GetHashCode(); return (long)((ulong)((long)num << 32) ^ ((ulong)(uint)num2 << 16) ^ (uint)hashCode); } public static Material GetOrCreateRuneMaterial(Texture2D runeTex, Color color) { Init(); Shader shader = Shader.Find("Particles/Standard Unlit") ?? Shader.Find("Legacy Shaders/Particles/Alpha Blended") ?? Shader.Find("Particles/Alpha Blended") ?? Shader.Find("Particles/Additive"); if (shader == null) { float num = float.MinValue; Material[] array = Resources.FindObjectsOfTypeAll(); foreach (Material material in array) { if (!(material == null) && !(material.shader == null)) { Shader shader2 = material.shader; string text = shader2.name.ToLower(); float num2 = 0f; if (text.Contains("particle")) { num2 += 3f; } if (text.Contains("add")) { num2 += 3f; } if (text.Contains("unlit")) { num2 += 2f; } if (text.Contains("transparent")) { num2 += 1.5f; } if ((material.HasProperty("_Color") || material.HasProperty("_TintColor")) && num2 > num) { num = num2; shader = shader2; } } } } if (shader == null) { Logger.LogError((object)"[Avalor] rune VFX: No glowing shader found. The runes refuse to shine."); return null; } long key = MakeCacheKey(shader, runeTex, color); if (_materialCache.TryGetValue(key, out var value) && value != null) { return value; } Material material2 = new Material(shader); if (runeTex != null) { if (material2.HasProperty("_MainTex")) { material2.SetTexture("_MainTex", runeTex); } if (material2.HasProperty("_BaseMap")) { material2.SetTexture("_BaseMap", runeTex); } try { material2.mainTexture = runeTex; } catch { } } if (material2.HasProperty("_Color")) { material2.SetColor("_Color", color); } if (material2.HasProperty("_TintColor")) { material2.SetColor("_TintColor", color); } if (material2.HasProperty("_EmissionColor")) { material2.SetColor("_EmissionColor", color * 3f); } material2.SetInt("_SrcBlend", 1); material2.SetInt("_DstBlend", 1); material2.SetInt("_ZWrite", 0); material2.DisableKeyword("_ALPHATEST_ON"); material2.EnableKeyword("_ALPHABLEND_ON"); material2.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material2.renderQueue = 3000; _materialCache[key] = material2; Logger.LogInfo((object)("[Avalor] rune VFX: Forged glowing additive rune material from '" + shader.name + "'.")); return material2; } public static void ClearCache() { _materialCache.Clear(); _initialized = false; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorRuneTexture ---- namespace MistsofAvalor { public static class AvalorRuneTexture { private const int Size = 128; public static Texture2D GenerateRune(Color color) { int runeIndex = Mathf.Abs(color.GetHashCode()) % 16; return GenerateRune(color, runeIndex); } public static Texture2D GenerateRuneAtlas(Color color) { int num = 128; Texture2D texture2D = new Texture2D(512, 512, TextureFormat.RGBA32, mipChain: true) { filterMode = FilterMode.Bilinear, wrapMode = TextureWrapMode.Clamp }; Color color2 = new Color(0f, 0f, 0f, 0f); Color[] array = new Color[512 * 512]; for (int i = 0; i < array.Length; i++) { array[i] = color2; } texture2D.SetPixels(array); for (int j = 0; j < 16; j++) { int num2 = j % 4; int num3 = 3 - j / 4; DrawRuneOntoAtlas(texture2D, color, j, num2 * num, num3 * num, num); } texture2D.Apply(updateMipmaps: true); return texture2D; } public static Texture2D GenerateSoftTrail(Color color) { Texture2D texture2D = new Texture2D(128, 128, TextureFormat.RGBA32, mipChain: false); texture2D.filterMode = FilterMode.Bilinear; Vector2 b = new Vector2(64f, 64f); float num = 64f; for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { float num2 = Vector2.Distance(new Vector2((float)j + 0.5f, (float)i + 0.5f), b) / num; float t = Mathf.Clamp01(1f - num2); t = Mathf.SmoothStep(0f, 1f, t); t = Mathf.Pow(t, 1.6f); Color color2 = new Color(color.r, color.g, color.b, color.a * t); texture2D.SetPixel(j, i, color2); } } texture2D.Apply(updateMipmaps: true); return texture2D; } public static Texture2D GenerateStreak(Color color) { Texture2D texture2D = new Texture2D(128, 128, TextureFormat.RGBA32, mipChain: false); texture2D.filterMode = FilterMode.Bilinear; Vector2 vector = new Vector2(64f, 64f); float num = 64f; for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { float t = Mathf.Abs((float)i + 0.5f - vector.y) / num; float num2 = Mathf.Abs((float)j + 0.5f - vector.x) / (num * 0.6f); float num3 = Mathf.Clamp01(1f - num2); float f = Mathf.SmoothStep(1f, 0f, t); num3 *= Mathf.Pow(f, 0.9f); Color color2 = new Color(color.r, color.g, color.b, color.a * num3); texture2D.SetPixel(j, i, color2); } } texture2D.Apply(updateMipmaps: true); return texture2D; } public static Texture2D GenerateBlend(Color color) { Texture2D texture2D = GenerateSoftTrail(color); Texture2D texture2D2 = GenerateRune(color); Texture2D texture2D3 = new Texture2D(128, 128, TextureFormat.RGBA32, mipChain: false); texture2D3.filterMode = FilterMode.Bilinear; for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { Color pixel = texture2D.GetPixel(j, i); Color pixel2 = texture2D2.GetPixel(j, i); float num = Mathf.Clamp01(pixel.a + pixel2.a * 0.65f); Color color2 = new Color(color.r, color.g, color.b, color.a * num); texture2D3.SetPixel(j, i, color2); } } texture2D3.Apply(updateMipmaps: true); return texture2D3; } private static void DrawRuneOntoAtlas(Texture2D tex, Color color, int runeIndex, int offsetX, int offsetY, int size) { Color c = new Color(color.r * 0.6f, color.g * 0.6f, color.b * 0.6f, 0.6f); Color c2 = new Color(color.r, color.g, color.b, 1f); float scale = (float)size / 128f; switch (runeIndex) { case 0: DrawLineLocal(20, 20, 108, 108, c, 6); DrawLineLocal(108, 20, 20, 108, c, 6); DrawLineLocal(20, 20, 108, 108, c2, 3); DrawLineLocal(108, 20, 20, 108, c2, 3); break; case 1: DrawLineLocal(64, 16, 64, 112, c, 6); DrawLineLocal(64, 64, 32, 96, c, 5); DrawLineLocal(64, 64, 96, 96, c, 5); DrawLineLocal(64, 16, 64, 112, c2, 3); DrawLineLocal(64, 64, 32, 96, c2, 2); DrawLineLocal(64, 64, 96, 96, c2, 2); break; case 2: DrawLineLocal(40, 20, 40, 108, c, 6); DrawLineLocal(40, 96, 96, 112, c, 5); DrawLineLocal(40, 72, 96, 88, c, 5); DrawLineLocal(40, 20, 40, 108, c2, 3); DrawLineLocal(40, 96, 96, 112, c2, 2); DrawLineLocal(40, 72, 96, 88, c2, 2); break; case 3: DrawLineLocal(40, 20, 40, 112, c, 6); DrawLineLocal(40, 96, 88, 112, c, 5); DrawLineLocal(88, 112, 88, 80, c, 5); DrawLineLocal(88, 80, 40, 96, c, 5); DrawLineLocal(40, 20, 40, 112, c2, 3); DrawLineLocal(40, 96, 88, 112, c2, 2); DrawLineLocal(88, 112, 88, 80, c2, 2); DrawLineLocal(88, 80, 40, 96, c2, 2); break; case 4: DrawLineLocal(64, 16, 64, 112, c, 8); DrawLineLocal(64, 16, 64, 112, c2, 4); break; case 5: DrawLineLocal(64, 16, 64, 112, c, 6); DrawLineLocal(64, 40, 32, 72, c, 5); DrawLineLocal(64, 40, 96, 72, c, 5); DrawLineLocal(64, 16, 64, 112, c2, 3); DrawLineLocal(64, 40, 32, 72, c2, 2); DrawLineLocal(64, 40, 96, 72, c2, 2); break; case 6: DrawLineLocal(36, 16, 36, 112, c, 6); DrawLineLocal(92, 16, 92, 112, c, 6); DrawLineLocal(36, 36, 92, 92, c, 5); DrawLineLocal(36, 16, 36, 112, c2, 3); DrawLineLocal(92, 16, 92, 112, c2, 3); DrawLineLocal(36, 36, 92, 92, c2, 2); break; case 7: DrawLineLocal(80, 20, 40, 20, c, 5); DrawLineLocal(80, 20, 40, 64, c, 5); DrawLineLocal(40, 64, 80, 108, c, 5); DrawLineLocal(80, 108, 40, 108, c, 5); DrawLineLocal(80, 20, 40, 20, c2, 2); DrawLineLocal(80, 20, 40, 64, c2, 2); DrawLineLocal(40, 64, 80, 108, c2, 2); DrawLineLocal(80, 108, 40, 108, c2, 2); break; case 8: DrawLineLocal(64, 16, 64, 112, c, 6); DrawLineLocal(96, 96, 32, 32, c, 5); DrawLineLocal(64, 16, 64, 112, c2, 3); DrawLineLocal(96, 96, 32, 32, c2, 2); break; case 9: DrawLineLocal(32, 48, 32, 112, c, 6); DrawLineLocal(96, 48, 96, 112, c, 6); DrawLineLocal(32, 48, 64, 16, c, 5); DrawLineLocal(64, 16, 96, 48, c, 5); DrawLineLocal(32, 48, 32, 112, c2, 3); DrawLineLocal(96, 48, 96, 112, c2, 3); DrawLineLocal(32, 48, 64, 16, c2, 2); DrawLineLocal(64, 16, 96, 48, c2, 2); break; case 10: DrawLineLocal(48, 16, 48, 112, c, 6); DrawLineLocal(48, 80, 96, 48, c, 5); DrawLineLocal(48, 16, 48, 112, c2, 3); DrawLineLocal(48, 80, 96, 48, c2, 2); break; case 11: DrawLineLocal(64, 112, 96, 72, c, 5); DrawLineLocal(96, 72, 64, 32, c, 5); DrawLineLocal(64, 32, 32, 72, c, 5); DrawLineLocal(32, 72, 64, 112, c, 5); DrawLineLocal(32, 72, 16, 16, c, 5); DrawLineLocal(96, 72, 112, 16, c, 5); DrawLineLocal(64, 112, 96, 72, c2, 2); DrawLineLocal(96, 72, 64, 32, c2, 2); DrawLineLocal(64, 32, 32, 72, c2, 2); DrawLineLocal(32, 72, 64, 112, c2, 2); DrawLineLocal(32, 72, 16, 16, c2, 2); DrawLineLocal(96, 72, 112, 16, c2, 2); break; case 12: DrawLineLocal(20, 20, 64, 64, c, 5); DrawLineLocal(20, 108, 64, 64, c, 5); DrawLineLocal(108, 20, 64, 64, c, 5); DrawLineLocal(108, 108, 64, 64, c, 5); DrawLineLocal(20, 20, 64, 64, c2, 2); DrawLineLocal(20, 108, 64, 64, c2, 2); DrawLineLocal(108, 20, 64, 64, c2, 2); DrawLineLocal(108, 108, 64, 64, c2, 2); break; case 13: DrawLineLocal(36, 16, 36, 112, c, 6); DrawLineLocal(36, 96, 80, 80, c, 5); DrawLineLocal(80, 80, 36, 64, c, 5); DrawLineLocal(36, 64, 80, 48, c, 5); DrawLineLocal(80, 48, 36, 32, c, 5); DrawLineLocal(36, 16, 36, 112, c2, 3); DrawLineLocal(36, 96, 80, 80, c2, 2); DrawLineLocal(80, 80, 36, 64, c2, 2); DrawLineLocal(36, 64, 80, 48, c2, 2); DrawLineLocal(80, 48, 36, 32, c2, 2); break; case 14: DrawLineLocal(32, 16, 32, 112, c, 6); DrawLineLocal(96, 16, 96, 112, c, 6); DrawLineLocal(32, 112, 96, 64, c, 5); DrawLineLocal(96, 112, 32, 64, c, 5); DrawLineLocal(32, 16, 32, 112, c2, 3); DrawLineLocal(96, 16, 96, 112, c2, 3); DrawLineLocal(32, 112, 96, 64, c2, 2); DrawLineLocal(96, 112, 32, 64, c2, 2); break; case 15: DrawLineLocal(64, 16, 108, 64, c, 6); DrawLineLocal(108, 64, 64, 112, c, 6); DrawLineLocal(64, 112, 20, 64, c, 6); DrawLineLocal(20, 64, 64, 16, c, 6); DrawLineLocal(64, 16, 108, 64, c2, 3); DrawLineLocal(108, 64, 64, 112, c2, 3); DrawLineLocal(64, 112, 20, 64, c2, 3); DrawLineLocal(20, 64, 64, 16, c2, 3); break; } void DrawLineLocal(int x0, int y0, int x1, int y1, Color c3, int thickness) { DrawLineAtlas(tex, offsetX + (int)((float)x0 * scale), offsetY + (int)((float)y0 * scale), offsetX + (int)((float)x1 * scale), offsetY + (int)((float)y1 * scale), c3, thickness, 512); } } public static Texture2D GenerateRune(Color color, int runeIndex) { Texture2D texture2D = new Texture2D(128, 128, TextureFormat.RGBA32, mipChain: true); texture2D.filterMode = FilterMode.Bilinear; texture2D.wrapMode = TextureWrapMode.Clamp; Color color2 = new Color(0f, 0f, 0f, 0f); Color c = new Color(color.r * 0.6f, color.g * 0.6f, color.b * 0.6f, 0.6f); Color c2 = new Color(color.r, color.g, color.b, 1f); Color[] array = new Color[16384]; for (int i = 0; i < array.Length; i++) { array[i] = color2; } texture2D.SetPixels(array); switch (runeIndex) { case 0: DrawLine(texture2D, 20, 20, 108, 108, c, 6); DrawLine(texture2D, 108, 20, 20, 108, c, 6); DrawLine(texture2D, 20, 20, 108, 108, c2, 3); DrawLine(texture2D, 108, 20, 20, 108, c2, 3); break; case 1: DrawLine(texture2D, 64, 16, 64, 112, c, 6); DrawLine(texture2D, 64, 64, 32, 96, c, 5); DrawLine(texture2D, 64, 64, 96, 96, c, 5); DrawLine(texture2D, 64, 16, 64, 112, c2, 3); DrawLine(texture2D, 64, 64, 32, 96, c2, 2); DrawLine(texture2D, 64, 64, 96, 96, c2, 2); break; case 2: DrawLine(texture2D, 40, 20, 40, 108, c, 6); DrawLine(texture2D, 40, 96, 96, 112, c, 5); DrawLine(texture2D, 40, 72, 96, 88, c, 5); DrawLine(texture2D, 40, 20, 40, 108, c2, 3); DrawLine(texture2D, 40, 96, 96, 112, c2, 2); DrawLine(texture2D, 40, 72, 96, 88, c2, 2); break; case 3: DrawLine(texture2D, 40, 20, 40, 112, c, 6); DrawLine(texture2D, 40, 96, 88, 112, c, 5); DrawLine(texture2D, 88, 112, 88, 80, c, 5); DrawLine(texture2D, 88, 80, 40, 96, c, 5); DrawLine(texture2D, 40, 20, 40, 112, c2, 3); DrawLine(texture2D, 40, 96, 88, 112, c2, 2); DrawLine(texture2D, 88, 112, 88, 80, c2, 2); DrawLine(texture2D, 88, 80, 40, 96, c2, 2); break; case 4: DrawLine(texture2D, 64, 16, 64, 112, c, 8); DrawLine(texture2D, 64, 16, 64, 112, c2, 4); break; case 5: DrawLine(texture2D, 64, 16, 64, 112, c, 6); DrawLine(texture2D, 64, 40, 32, 72, c, 5); DrawLine(texture2D, 64, 40, 96, 72, c, 5); DrawLine(texture2D, 64, 16, 64, 112, c2, 3); DrawLine(texture2D, 64, 40, 32, 72, c2, 2); DrawLine(texture2D, 64, 40, 96, 72, c2, 2); break; case 6: DrawLine(texture2D, 36, 16, 36, 112, c, 6); DrawLine(texture2D, 92, 16, 92, 112, c, 6); DrawLine(texture2D, 36, 36, 92, 92, c, 5); DrawLine(texture2D, 36, 16, 36, 112, c2, 3); DrawLine(texture2D, 92, 16, 92, 112, c2, 3); DrawLine(texture2D, 36, 36, 92, 92, c2, 2); break; default: DrawLine(texture2D, 80, 20, 40, 20, c, 5); DrawLine(texture2D, 80, 20, 40, 64, c, 5); DrawLine(texture2D, 40, 64, 80, 108, c, 5); DrawLine(texture2D, 80, 108, 40, 108, c, 5); DrawLine(texture2D, 80, 20, 40, 20, c2, 2); DrawLine(texture2D, 80, 20, 40, 64, c2, 2); DrawLine(texture2D, 40, 64, 80, 108, c2, 2); DrawLine(texture2D, 80, 108, 40, 108, c2, 2); break; case 8: DrawLine(texture2D, 64, 16, 64, 112, c, 6); DrawLine(texture2D, 96, 96, 32, 32, c, 5); DrawLine(texture2D, 64, 16, 64, 112, c2, 3); DrawLine(texture2D, 96, 96, 32, 32, c2, 2); break; case 9: DrawLine(texture2D, 32, 48, 32, 112, c, 6); DrawLine(texture2D, 96, 48, 96, 112, c, 6); DrawLine(texture2D, 32, 48, 64, 16, c, 5); DrawLine(texture2D, 64, 16, 96, 48, c, 5); DrawLine(texture2D, 32, 48, 32, 112, c2, 3); DrawLine(texture2D, 96, 48, 96, 112, c2, 3); DrawLine(texture2D, 32, 48, 64, 16, c2, 2); DrawLine(texture2D, 64, 16, 96, 48, c2, 2); break; case 10: DrawLine(texture2D, 48, 16, 48, 112, c, 6); DrawLine(texture2D, 48, 80, 96, 48, c, 5); DrawLine(texture2D, 48, 16, 48, 112, c2, 3); DrawLine(texture2D, 48, 80, 96, 48, c2, 2); break; case 11: DrawLine(texture2D, 64, 112, 96, 72, c, 5); DrawLine(texture2D, 96, 72, 64, 32, c, 5); DrawLine(texture2D, 64, 32, 32, 72, c, 5); DrawLine(texture2D, 32, 72, 64, 112, c, 5); DrawLine(texture2D, 32, 72, 16, 16, c, 5); DrawLine(texture2D, 96, 72, 112, 16, c, 5); DrawLine(texture2D, 64, 112, 96, 72, c2, 2); DrawLine(texture2D, 96, 72, 64, 32, c2, 2); DrawLine(texture2D, 64, 32, 32, 72, c2, 2); DrawLine(texture2D, 32, 72, 64, 112, c2, 2); DrawLine(texture2D, 32, 72, 16, 16, c2, 2); DrawLine(texture2D, 96, 72, 112, 16, c2, 2); break; case 12: DrawLine(texture2D, 20, 20, 64, 64, c, 5); DrawLine(texture2D, 20, 108, 64, 64, c, 5); DrawLine(texture2D, 108, 20, 64, 64, c, 5); DrawLine(texture2D, 108, 108, 64, 64, c, 5); DrawLine(texture2D, 20, 20, 64, 64, c2, 2); DrawLine(texture2D, 20, 108, 64, 64, c2, 2); DrawLine(texture2D, 108, 20, 64, 64, c2, 2); DrawLine(texture2D, 108, 108, 64, 64, c2, 2); break; case 13: DrawLine(texture2D, 36, 16, 36, 112, c, 6); DrawLine(texture2D, 36, 96, 80, 80, c, 5); DrawLine(texture2D, 80, 80, 36, 64, c, 5); DrawLine(texture2D, 36, 64, 80, 48, c, 5); DrawLine(texture2D, 80, 48, 36, 32, c, 5); DrawLine(texture2D, 36, 16, 36, 112, c2, 3); DrawLine(texture2D, 36, 96, 80, 80, c2, 2); DrawLine(texture2D, 80, 80, 36, 64, c2, 2); DrawLine(texture2D, 36, 64, 80, 48, c2, 2); DrawLine(texture2D, 80, 48, 36, 32, c2, 2); break; case 14: DrawLine(texture2D, 32, 16, 32, 112, c, 6); DrawLine(texture2D, 96, 16, 96, 112, c, 6); DrawLine(texture2D, 32, 112, 96, 64, c, 5); DrawLine(texture2D, 96, 112, 32, 64, c, 5); DrawLine(texture2D, 32, 16, 32, 112, c2, 3); DrawLine(texture2D, 96, 16, 96, 112, c2, 3); DrawLine(texture2D, 32, 112, 96, 64, c2, 2); DrawLine(texture2D, 96, 112, 32, 64, c2, 2); break; case 15: DrawLine(texture2D, 64, 16, 108, 64, c, 6); DrawLine(texture2D, 108, 64, 64, 112, c, 6); DrawLine(texture2D, 64, 112, 20, 64, c, 6); DrawLine(texture2D, 20, 64, 64, 16, c, 6); DrawLine(texture2D, 64, 16, 108, 64, c2, 3); DrawLine(texture2D, 108, 64, 64, 112, c2, 3); DrawLine(texture2D, 64, 112, 20, 64, c2, 3); DrawLine(texture2D, 20, 64, 64, 16, c2, 3); break; } texture2D.Apply(updateMipmaps: true); return texture2D; } private static void DrawLineAtlas(Texture2D tex, int x0, int y0, int x1, int y1, Color c, int thickness, int atlasSize) { int num = Mathf.Abs(x1 - x0); int num2 = Mathf.Abs(y1 - y0); int num3 = ((x0 < x1) ? 1 : (-1)); int num4 = ((y0 < y1) ? 1 : (-1)); int num5 = num - num2; while (true) { DrawCircleAtlas(tex, x0, y0, thickness, c, atlasSize); if (x0 != x1 || y0 != y1) { int num6 = 2 * num5; if (num6 > -num2) { num5 -= num2; x0 += num3; } if (num6 < num) { num5 += num; y0 += num4; } continue; } break; } } private static void DrawCircleAtlas(Texture2D tex, int cx, int cy, int r, Color c, int atlasSize) { for (int i = -r; i <= r; i++) { for (int j = -r; j <= r; j++) { if (j * j + i * i <= r * r) { int num = cx + j; int num2 = cy + i; if (num >= 0 && num < atlasSize && num2 >= 0 && num2 < atlasSize) { tex.SetPixel(num, num2, c); } } } } } private static void DrawLine(Texture2D tex, int x0, int y0, int x1, int y1, Color c, int thickness) { int num = Mathf.Abs(x1 - x0); int num2 = Mathf.Abs(y1 - y0); int num3 = ((x0 < x1) ? 1 : (-1)); int num4 = ((y0 < y1) ? 1 : (-1)); int num5 = num - num2; while (true) { DrawCircle(tex, x0, y0, thickness, c); if (x0 != x1 || y0 != y1) { int num6 = 2 * num5; if (num6 > -num2) { num5 -= num2; x0 += num3; } if (num6 < num) { num5 += num; y0 += num4; } continue; } break; } } private static void DrawCircle(Texture2D tex, int cx, int cy, int r, Color c) { for (int i = -r; i <= r; i++) { for (int j = -r; j <= r; j++) { if (j * j + i * i <= r * r) { int num = cx + j; int num2 = cy + i; if (num >= 0 && num < 128 && num2 >= 0 && num2 < 128) { tex.SetPixel(num, num2, c); } } } } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorResetCommand ---- namespace MistsofAvalor { public class AvalorResetCommand : ConsoleCommand { public override string Name => "avalor_reset"; public override string Help => "Forcefully deletes and regenerates a brand new Avalor labyrinth."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => true; public override bool OnlyServer => true; public override void Run(string[] args) { if (AvalorMazeGenerator.Instance != null) { Logger.LogInfo((object)"Avalor Labyrinth reset triggered via console."); AvalorResetManager.Instance?.ForceResetMaze(); AvalorNet.Print("Avalor Labyrinth regenerating..."); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorRemoteResetCommand ---- namespace MistsofAvalor { public class AvalorRemoteResetCommand : ConsoleCommand { public override string Name => "avalor_remote_reset"; public override string Help => "Forcefully deletes and regenerates the Avalor labyrinth from anywhere in the world."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => true; public override bool OnlyServer => true; public override void Run(string[] args) { if (AvalorMazeGenerator.Instance != null) { Logger.LogInfo((object)"Avalor Labyrinth remote reset triggered via console."); AvalorResetManager.Instance?.ForceResetMaze(remote: true); AvalorNet.Print("Avalor Labyrinth regenerating remotely..."); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorSpawnPortalCommand ---- namespace MistsofAvalor { public class AvalorSpawnPortalCommand : ConsoleCommand { public override string Name => "avalor_spawn_portal"; public override string Help => "Spawns a Decrepit Avalor Portal in front of the player."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => true; public override bool OnlyServer => false; public override void Run(string[] args) { if (Player.m_localPlayer != null) { Vector3 position = Player.m_localPlayer.transform.position + Player.m_localPlayer.transform.forward * 3f; position.y = Player.m_localPlayer.transform.position.y; GameObject prefab = ZNetScene.instance.GetPrefab("Avalor_OverworldPortal"); if (prefab != null) { Object.Instantiate(prefab, position, Player.m_localPlayer.transform.rotation); AvalorNet.Print("Avalor Portal spawned at crosshair."); } } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorIlluminateCommand ---- namespace MistsofAvalor { public class AvalorIlluminateCommand : ConsoleCommand { public override string Name => "avalor_illuminate"; public override string Help => "Toggles debug full-bright lighting inside the maze (for layout inspection)."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => false; public override bool OnlyServer => false; public override void Run(string[] args) { AvalorCryptEnvZone.DebugBright = !AvalorCryptEnvZone.DebugBright; Logger.LogInfo((object)("Avalor debug lighting toggled: " + (AvalorCryptEnvZone.DebugBright ? "BRIGHT" : "dark"))); AvalorNet.Print(AvalorCryptEnvZone.DebugBright ? "Avalor debug lighting: ON (maze forced full-bright for inspection)." : "Avalor debug lighting: OFF (crypt darkness restored)."); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorTpCommand ---- namespace MistsofAvalor { public class AvalorTpCommand : ConsoleCommand { public override string Name => "avalor_tp"; public override string Help => "Teleports the player directly to the Avalor labyrinth dimension."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => true; public override bool OnlyServer => false; public override void Run(string[] args) { if (Player.m_localPlayer != null) { Logger.LogInfo((object)"Teleporting player to Avalor via console."); AvalorPortal.TeleportToAvalor(Player.m_localPlayer); AvalorNet.Print("Entering the Mists of Avalor..."); } else { AvalorNet.Print("No Player found. You must be in-game to use this command."); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorRemoveAllCommand ---- namespace MistsofAvalor { public class AvalorRemoveAllCommand : ConsoleCommand { public override string Name => "avalor_remove_all"; public override string Help => "Completely removes all Avalor structures (maze and overworld portal)."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => true; public override bool OnlyServer => true; public override void Run(string[] args) { Logger.LogInfo((object)"Avalor: Removing all mod assets from the world..."); AvalorResetManager.Instance?.WipeSkyZone(AvalorMazeGenerator.MazeCenter, 2000f); if (ZoneSystem.instance != null && ZoneSystem.instance.GetLocationIcon("StartTemple", out var pos)) { if (!ZoneSystem.instance.IsZoneLoaded(pos)) { AvalorNet.Print("[Avalor][FAIL] the Start Temple zone is not loaded on this machine (a dedicated server only loads terrain near the world origin), so the portal-area decor could not be swept. The maze itself WAS removed. Run this again from a client standing near the temple to clear the decor."); return; } int num = 0; Collider[] array = Physics.OverlapSphere(pos, 100f); for (int i = 0; i < array.Length; i++) { ZNetView componentInParent = array[i].GetComponentInParent(); if (!(componentInParent == null) && componentInParent.IsValid()) { string text = Utils.GetPrefabName(componentInParent.gameObject).ToLower(); if (!text.Contains("bossstone") && !text.Contains("starttemple") && !text.Contains("runestone") && !text.Contains("tombstone") && (text.StartsWith("avalor_") || text.Contains("paved_road") || text.Contains("stone_floor") || text.Contains("stone_wall") || text.Contains("vfx_swamp_mist") || text.Contains("groundtorch") || text == "sign" || text.Contains("swamptree") || text.Contains("mountaingravestone") || text.Contains("statueevil") || text.Contains("giant_skull") || text.Contains("bonefragments") || text.Contains("rock_3") || text.Contains("rock_4") || text.Contains("pinetree") || text.Contains("pickable_mushroom"))) { componentInParent.ClaimOwnership(); ZNetScene.instance.Destroy(componentInParent.gameObject); num++; } } } if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)$"[Avalor] avalor_remove_all: cleared {num} portal-area object(s)."); } } AvalorNet.Print("All Avalor assets removed from the world."); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorGenerateAllCommand ---- namespace MistsofAvalor { public class AvalorGenerateAllCommand : ConsoleCommand { public override string Name => "avalor_generate_all"; public override string Help => "Forces generation of the Avalor Overworld Portal (run this after avalor_remove_all)."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => true; public override bool OnlyServer => true; public override void Run(string[] args) { if (ZoneSystem.instance != null && ZoneSystem.instance.GetLocationIcon("StartTemple", out var pos)) { if (!ZoneSystem.instance.IsZoneLoaded(pos)) { AvalorNet.Print("[Avalor][FAIL] the Start Temple zone is not loaded on this machine (a dedicated server only loads terrain near the world origin). Run avalor_generate_all from a client standing near the temple - the siting needs real physics under it."); return; } Logger.LogInfo((object)"Avalor Labyrinth: Forcing portal generation..."); GameObject gameObject = new GameObject("AvalorTempSpawner"); gameObject.transform.position = pos; gameObject.transform.rotation = Quaternion.identity; GameObject[] array = Resources.FindObjectsOfTypeAll(); foreach (GameObject gameObject2 in array) { if (gameObject2.name.StartsWith("StartTemple") && gameObject2.scene.isLoaded) { gameObject.transform.rotation = gameObject2.transform.rotation; break; } } gameObject.AddComponent(); AvalorNet.Print("Avalor Portal generation initiated. Terrain modifiers have been reset."); } else { AvalorNet.Print("Failed to locate StartTemple!"); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorTpGraveCommand ---- namespace MistsofAvalor { public class AvalorTpGraveCommand : ConsoleCommand { public override string Name => "avalor_tp_grave"; public override string Help => "Teleports you onto your nearest tombstone (in the maze or overworld) so you can loot it in place. The surest way to recover a grave."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => false; public override bool OnlyServer => false; public override void Run(string[] args) { Player localPlayer = Player.m_localPlayer; if (localPlayer == null) { AvalorNet.Print("You must be in-game."); return; } Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); if (zDODictionary == null) { AvalorNet.Print("Could not read the world object table."); return; } int stableHashCode = "Player_tombstone".GetStableHashCode(); Vector3 position = localPlayer.transform.position; Vector3 best = Vector3.zero; float num = float.MaxValue; int num2 = 0; foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value != null && value.GetPrefab() == stableHashCode) { num2++; Vector3 position2 = value.GetPosition(); float sqrMagnitude = (position2 - position).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; best = position2; } } } if (num2 == 0) { if (AvalorNet.Role == AvalorNet.NetRole.Client && AvalorMazeGenerator.Instance != null) { AvalorNet.Print("No grave in local view - asking the server for your nearest tombstone..."); AvalorNet.SendQuery(2); AvalorMazeGenerator.Instance.StartCoroutine(TpWhenAnswered(localPlayer)); } else { AvalorNet.Print("No tombstones exist in this world."); } } else { TeleportToGrave(localPlayer, best, num2); } } private static IEnumerator TpWhenAnswered(Player p) { float t = 0f; while (t < 5f) { if (AvalorNet.TryGetFreshAnswer(2, 6f, out var found, out var pos)) { if (!found) { AvalorNet.Print("The server reports no tombstones exist in this world."); } else if (p != null) { TeleportToGrave(p, pos, -1); } yield break; } t += Time.deltaTime; yield return null; } AvalorNet.Print("No answer from the server about your grave - try again."); } private static void TeleportToGrave(Player p, Vector3 best, int count) { Avalor_TombstoneToGraveyard_Patch.SuppressUntil = Time.time + 30f; AvalorSafePlatform.Drop(best, 8f, 12f); p.TeleportTo(best + new Vector3(0f, 1.5f, 0f), p.transform.rotation, distantTeleport: true); string arg = ((count >= 0) ? $" ({count} grave(s) in the world.)" : " (located by the server.)"); Logger.LogInfo((object)$"[Avalor] avalor_tp_grave: teleported player to tombstone at {best:F0}."); AvalorNet.Print($"Teleporting onto your grave at {best:F0}. Press [E] Take All.{arg}"); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorFixStonesCommand ---- namespace MistsofAvalor { public class AvalorFixStonesCommand : ConsoleCommand { public override string Name => "avalor_fix_stones"; public override string Help => "Restores a missing sacrificial altar and/or its flanking boulders at the Start Temple."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => true; public override bool OnlyServer => true; public override void Run(string[] args) { if (ZoneSystem.instance != null && ZoneSystem.instance.GetLocationIcon("StartTemple", out var pos)) { if (!ZoneSystem.instance.IsZoneLoaded(pos)) { AvalorNet.Print("[Avalor][FAIL] the Start Temple zone is not loaded on this machine (a dedicated server only loads terrain near the world origin) - running blind here would duplicate the altar. Run avalor_fix_stones from a client standing near the temple."); return; } Logger.LogInfo((object)"Avalor Labyrinth: Repairing missing sacrificial stones..."); bool flag = false; Collider[] array = Physics.OverlapSphere(pos, 30f); for (int i = 0; i < array.Length; i++) { ZNetView componentInParent = array[i].GetComponentInParent(); if (componentInParent != null && Utils.GetPrefabName(componentInParent.gameObject).ToLower().Contains("bossstone")) { flag = true; break; } } bool flag2 = false; if (!flag) { GameObject prefab = ZNetScene.instance.GetPrefab("BossStone_Eikthyr"); if (prefab != null) { Quaternion rotation = Quaternion.identity; GameObject[] array2 = Resources.FindObjectsOfTypeAll(); foreach (GameObject gameObject in array2) { if (gameObject.name.StartsWith("StartTemple") && gameObject.scene.isLoaded) { rotation = gameObject.transform.rotation; break; } } Vector3 vector = pos; if (ZoneSystem.instance.GetGroundHeight(vector, out var height)) { vector.y = height; } Object.Instantiate(prefab, vector, rotation); flag2 = true; Logger.LogInfo((object)"avalor_fix_stones: Restored missing BossStone_Eikthyr altar at StartTemple."); } else { Logger.LogWarning((object)"avalor_fix_stones: BossStone_Eikthyr prefab not found - cannot restore missing altar."); } } GameObject prefab2 = ZNetScene.instance.GetPrefab("Rock_3"); int num = 0; if (prefab2 != null) { array = Physics.OverlapSphere(pos, 30f); for (int i = 0; i < array.Length; i++) { ZNetView componentInParent2 = array[i].GetComponentInParent(); if (!(componentInParent2 != null) || !Utils.GetPrefabName(componentInParent2.gameObject).ToLower().Contains("bossstone")) { continue; } Vector3 position = componentInParent2.transform.position - componentInParent2.transform.forward * 1.5f; bool flag3 = false; Collider[] array3 = Physics.OverlapSphere(position, 2f); for (int j = 0; j < array3.Length; j++) { ZNetView componentInParent3 = array3[j].GetComponentInParent(); if (componentInParent3 != null && Utils.GetPrefabName(componentInParent3.gameObject) == "Rock_3") { flag3 = true; break; } } if (!flag3) { Object.Instantiate(prefab2, position, componentInParent2.transform.rotation).transform.localScale = new Vector3(1.5f, 2.5f, 1.5f); num++; } } } string arg = (flag2 ? "Sacrificial altar restored. " : ""); AvalorNet.Print($"{arg}Successfully restored {num} flanking boulders!"); } else { AvalorNet.Print("Failed to locate StartTemple!"); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorInspect ---- namespace MistsofAvalor { public static class AvalorInspect { private const string CombinedSuffix = "_Combined"; public static bool CutawayActive { get; private set; } public static bool HideWallsToo { get; private set; } public static void SetCutaway(bool on, bool hideWalls) { CutawayActive = on; HideWallsToo = hideWalls; Apply(); } public static int Apply() { int num = 0; MeshRenderer[] array = Object.FindObjectsByType(FindObjectsSortMode.None); foreach (MeshRenderer meshRenderer in array) { if (meshRenderer == null) { continue; } GameObject gameObject = meshRenderer.gameObject; if (gameObject.name.EndsWith("_Combined") && AvalorRegion.InSkyMaze(gameObject.transform.position)) { bool flag = gameObject.name.Contains("Wall"); bool flag2 = CutawayActive && (!flag || HideWallsToo); if (meshRenderer.enabled != !flag2) { meshRenderer.enabled = !flag2; num++; } } } return num; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorInspectDriver ---- namespace MistsofAvalor { public class AvalorInspectDriver : MonoBehaviour { private float _timer; private void Update() { if (!AvalorInspect.CutawayActive) { return; } if (Player.m_localPlayer == null) { AvalorInspect.SetCutaway(on: false, hideWalls: false); return; } _timer += Time.deltaTime; if (!(_timer < 1f)) { _timer = 0f; AvalorInspect.Apply(); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorOpenCommand ---- namespace MistsofAvalor { public class AvalorOpenCommand : ConsoleCommand { public override string Name => "avalor_open"; public override string Help => "Cutaway view of the maze for inspection. 'avalor_open' hides floors+ceilings and leaves the wall lattice (a floor plan from above - use with fly). 'avalor_open walls' hides the walls too. 'avalor_open off' restores. Local to you only."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => false; public override bool OnlyServer => false; public override void Run(string[] args) { string text = ((args != null && args.Length != 0) ? args[0].ToLower() : ""); if (text == "off") { AvalorInspect.SetCutaway(on: false, hideWalls: false); AvalorNet.Print("Avalor cutaway OFF - maze restored."); return; } bool flag = text == "walls" || text == "all"; bool num = flag || !AvalorInspect.CutawayActive; AvalorInspect.SetCutaway(num, flag); if (!num) { AvalorNet.Print("Avalor cutaway OFF - maze restored."); } else { AvalorNet.Print(flag ? "Avalor cutaway ON (walls hidden too) - only props, chests and portals remain. Use 'fly'." : "Avalor cutaway ON - floors and ceilings hidden, wall lattice remains. Use 'fly'. 'avalor_open walls' to strip the walls, 'avalor_open off' to restore."); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorTpEndCommand ---- namespace MistsofAvalor { public class AvalorTpEndCommand : ConsoleCommand { public override string Name => "avalor_tp_end"; public override string Help => "Teleports you to the labyrinth's escape portal (the deepest point of the maze), next to the prize chest."; public override bool IsCheat => true; public override bool IsSecret => false; public override bool IsNetwork => false; public override bool OnlyServer => false; public override void Run(string[] args) { Player localPlayer = Player.m_localPlayer; if (localPlayer == null) { AvalorNet.Print("You must be in-game."); return; } Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); if (zDODictionary == null) { AvalorNet.Print("Could not read the world object table."); return; } int stableHashCode = "Avalor_ExitPortal".GetStableHashCode(); int stableHashCode2 = "Avalor_OverworldPortal".GetStableHashCode(); Vector3 best = Vector3.zero; bool flag = false; foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value == null) { continue; } int prefab = value.GetPrefab(); if (prefab == stableHashCode || prefab == stableHashCode2) { Vector3 position = value.GetPosition(); if (AvalorRegion.InSkyMaze(position)) { best = position; flag = true; break; } } } if (!flag) { if (AvalorNet.Role == AvalorNet.NetRole.Client && AvalorMazeGenerator.Instance != null) { AvalorNet.Print("Exit not in local view - asking the server where it is..."); AvalorNet.SendQuery(1); AvalorMazeGenerator.Instance.StartCoroutine(TpWhenAnswered(localPlayer)); } else { AvalorNet.Print("No maze exit portal found - has the labyrinth been generated? Try avalor_reset."); } } else { TeleportToExit(localPlayer, best); } } private static IEnumerator TpWhenAnswered(Player p) { float t = 0f; while (t < 5f) { if (AvalorNet.TryGetFreshAnswer(1, 6f, out var found, out var pos)) { if (!found) { AvalorNet.Print("The server reports no maze exit exists - has the labyrinth been generated?"); } else if (p != null) { TeleportToExit(p, pos); } yield break; } t += Time.deltaTime; yield return null; } AvalorNet.Print("No answer from the server about the maze exit - try again."); } private static void TeleportToExit(Player p, Vector3 best) { AvalorSafePlatform.Drop(best, 10f, 12f); bool distantTeleport = !AvalorRegion.InSkyMaze(p.transform.position); p.TeleportTo(best + new Vector3(0f, 1.5f, -6f), p.transform.rotation, distantTeleport); AvalorNet.Print($"Teleporting to the maze exit at {best:F0}. The blackmetal prize chest is beside it."); Logger.LogInfo((object)$"[Avalor] avalor_tp_end: teleported player to maze exit {best:F0}."); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorStealthCompat ---- namespace MistsofAvalor { public static class AvalorStealthCompat { public enum CompatMode { Undetermined, NoStealthMod, FlagHonoured, Pinning } private static bool _canDriveSoM; public const float PinInterval = 0.2f; public const float VerifyInterval = 2f; public const int VerifyStrikesBeforeFallback = 3; private const float SummaryInterval = 30f; private static MethodInfo _getData; private static FieldInfo _fCurrentState; private static FieldInfo _fDetectionLevel; private static FieldInfo _fCanSense; private static FieldInfo _fCanSee; private static FieldInfo _fCanHear; private static FieldInfo _fFleeReason; private static FieldInfo _fAggression; private static object _valAlerted; private static object _valFleeNone; private static MethodInfo _isExempt; private static int _strikes; private static bool _announcedHealthy; private static float _summaryTimer; private static readonly HashSet _tracked = new HashSet(); private static bool _readingRealAlert; public static CompatMode Mode { get; private set; } = CompatMode.Undetermined; public static bool AnyTracked => _tracked.Count > 0; public static bool NeedsVerify { get { if (Mode == CompatMode.FlagHonoured) { return _isExempt != null; } return false; } } public static bool SuppressAlertForce => _readingRealAlert; public static void EnsureInit() { if (Mode == CompatMode.Undetermined) { Init(); } } public static void Init() { if (Mode != CompatMode.Undetermined) { return; } Type type = AccessTools.TypeByName("ShadowsOfMidgard.AwarenessSystem"); if (type == null) { Mode = CompatMode.NoStealthMod; Logger.LogInfo((object)"[Avalor][OK] no stealth mod detected - vanilla AI, no compat needed."); return; } _canDriveSoM = ResolvePinningHandles(type); Type type2 = AccessTools.TypeByName("ShadowsOfMidgard.StealthExemption"); if (type2 != null) { _isExempt = AccessTools.Method(type2, "IsExempt", new Type[1] { typeof(Character) }); if (_isExempt != null) { Mode = CompatMode.FlagHonoured; Logger.LogInfo((object)("[Avalor] Shadows of Midgard found, and it exposes the stealth-exemption flag - stamping it and verifying every " + $"{2f:0.#}s. " + "Avalor mobs are pinned to hunting regardless (0.0.9): the exemption is the polite contract, not something the labyrinth depends on.")); return; } Logger.LogWarning((object)"[Avalor][FAIL] Shadows of Midgard exposes StealthExemption but not IsExempt(Character) - the exemption contract has changed. The flag is still stamped, but it cannot be verified."); } Mode = CompatMode.Pinning; if (!_canDriveSoM) { Logger.LogWarning((object)"[Avalor][FAIL] Shadows of Midgard is installed but its awareness internals did not resolve, so its own attack gate cannot be opened. Avalor mobs are still pinned to alerted/hunting by the vanilla guards, but SoM may still refuse to let them SWING. This needs a code change on one side or the other."); } else { Logger.LogInfo((object)"[Avalor] Shadows of Midgard found without a usable stealth-exemption flag - driving its awareness data directly so the labyrinth still hunts."); } } private static bool ResolvePinningHandles(Type awareness) { Type type = AccessTools.TypeByName("ShadowsOfMidgard.AwarenessData"); Type type2 = AccessTools.TypeByName("ShadowsOfMidgard.VanillaAlertness"); Type type3 = AccessTools.TypeByName("ShadowsOfMidgard.FleeReason"); if (type == null || type2 == null) { return false; } _getData = AccessTools.Method(awareness, "GetData", new Type[1] { typeof(Character) }); _fCurrentState = AccessTools.Field(type, "CurrentState"); _fDetectionLevel = AccessTools.Field(type, "DetectionLevel"); _fCanSense = AccessTools.Field(type, "CanSense"); _fCanSee = AccessTools.Field(type, "CanSee"); _fCanHear = AccessTools.Field(type, "CanHear"); _fFleeReason = AccessTools.Field(type, "CurrentFleeReason"); _fAggression = AccessTools.Field(type, "AggressionLevel"); _valAlerted = Enum.Parse(type2, "Alerted"); if (type3 != null) { _valFleeNone = Enum.Parse(type3, "None"); } if (_getData != null && _fCurrentState != null && _fDetectionLevel != null && _fCanSense != null) { return _valAlerted != null; } return false; } public static void Register(Character c) { EnsureInit(); if (!(c == null)) { _tracked.Add(c); } } public static void BeginRealAlertRead() { _readingRealAlert = true; } public static void EndRealAlertRead() { _readingRealAlert = false; } public static bool IsOurs(Character c) { if (c != null) { return _tracked.Contains(c); } return false; } public static void PinNow(Character c) { if (_canDriveSoM && IsOurs(c)) { ZNetView component = c.GetComponent(); if (!(component == null) && component.IsValid() && component.IsOwner()) { Pin(c); } } } public static void PinAll() { PruneTracked(); if (_tracked.Count == 0) { return; } foreach (Character item in _tracked) { ZNetView component = item.GetComponent(); if (!(component == null) && component.IsValid() && component.IsOwner()) { ReassertVanillaHunt(item); if (_canDriveSoM) { Pin(item); } } } } private static void ReassertVanillaHunt(Character c) { try { BaseAI component = c.GetComponent(); if (component == null) { return; } ZNetView component2 = c.GetComponent(); if (component2 != null && component2.GetZDO() != null && component2.GetZDO().GetInt("AvalorWarden") == 1) { BeginRealAlertRead(); try { component.Alert(); return; } finally { EndRealAlertRead(); } } AvalorMobDirector.MakeItHunt(c.gameObject); } catch (Exception ex) { Logger.LogWarning((object)("[Avalor][FAIL] could not re-assert hunting on a maze mob: " + ex.Message)); } } private static void PruneTracked() { _tracked.RemoveWhere(delegate(Character c) { if (c == null || c.IsDead()) { return true; } ZNetView component = c.GetComponent(); return component == null || !component.IsValid(); }); } private static void Pin(Character c) { try { object obj = _getData.Invoke(null, new object[1] { c }); if (obj != null) { _fCurrentState.SetValue(obj, _valAlerted); _fDetectionLevel.SetValue(obj, 1f); _fCanSense.SetValue(obj, true); _fCanSee?.SetValue(obj, true); _fCanHear?.SetValue(obj, true); _fFleeReason?.SetValue(obj, _valFleeNone); _fAggression?.SetValue(obj, 1f); } } catch (Exception ex) { _canDriveSoM = false; Logger.LogWarning((object)("[Avalor][FAIL] driving Shadows of Midgard's awareness data failed and has been disabled: " + ex.Message + " - Avalor mobs remain pinned to alerted/hunting, but SoM may refuse to let them attack.")); } } public static void VerifyAll() { if (!NeedsVerify) { return; } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; PruneTracked(); foreach (Character item in _tracked) { ZNetView component = item.GetComponent(); if (component == null || !component.IsValid() || !component.IsOwner()) { continue; } ZDO zDO = component.GetZDO(); if (zDO == null) { continue; } num++; if (zDO.GetInt("SoMStealthExempt") != 1) { zDO.Set("SoMStealthExempt", 1); num2++; } try { if ((bool)_isExempt.Invoke(null, new object[1] { item })) { num3++; } else { num4++; } } catch (Exception ex) { Logger.LogWarning((object)("[Avalor][FAIL] StealthExemption.IsExempt threw - treating the exemption as broken: " + ex.Message)); num4++; } } if (num2 > 0) { Logger.LogWarning((object)($"[Avalor][FAIL] {num2} maze mob(s) were missing the exemption flag " + "and have been re-stamped - their Character.Awake ran before ZNetView was ready. Harmless once repaired, but they were invisible to SoM's exemption until now.")); } if (num4 > 0) { _strikes++; Logger.LogWarning((object)($"[Avalor][FAIL] Shadows of Midgard says {num4} of {num} flagged " + $"mob(s) are NOT exempt (strike {_strikes}/{3}).")); if (_strikes >= 3) { FallBackToPinning("SoM disagrees about the exemption flag"); } return; } _strikes = 0; if (!_announcedHealthy && num > 0 && num3 > 0) { _announcedHealthy = true; Logger.LogInfo((object)($"[Avalor][OK] SoM exemption verified: {num} maze mob(s) owned here, " + $"{num3} confirmed exempt by Shadows of Midgard. Clean path is live.")); } _summaryTimer += 2f; if (_summaryTimer >= 30f) { _summaryTimer = 0f; if (MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)($"[Avalor] exemption still healthy: {_tracked.Count} tracked, " + $"{num} owned here, {num3} confirmed exempt.")); } } } private static void FallBackToPinning(string why) { if (Mode != CompatMode.Pinning) { Mode = CompatMode.Pinning; _strikes = 0; Logger.LogError((object)("[Avalor][FAIL] the stealth-exemption contract is not being honoured (" + why + "). No longer verifying it. The labyrinth still hunts - Avalor mobs have been pinned to alerted/hunting throughout" + (_canDriveSoM ? ", and SoM's own awareness data is being driven directly." : ", but SoM's awareness internals never resolved, so it may still refuse to let them attack."))); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorStealthPinner ---- namespace MistsofAvalor { public class AvalorStealthPinner : MonoBehaviour { private float _pinTimer; private float _verifyTimer; private void Update() { AvalorStealthCompat.EnsureInit(); _pinTimer += Time.deltaTime; if (_pinTimer >= 0.2f) { _pinTimer = 0f; AvalorStealthCompat.PinAll(); } if (AvalorStealthCompat.NeedsVerify) { _verifyTimer += Time.deltaTime; if (_verifyTimer >= 2f) { _verifyTimer = 0f; AvalorStealthCompat.VerifyAll(); } } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorStealthGuards ---- namespace MistsofAvalor { internal static class AvalorStealthGuards { [HarmonyPatch(typeof(BaseAI), "IsAlerted")] internal static class Avalor_IsAlerted { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyBefore(new string[] { "wubarrk.shadowsofmidgard", "wubarrk.ShadowsOfMidgard" })] internal static void Prefix(BaseAI __instance) { if (AvalorStealthCompat.AnyTracked && !(__instance == null)) { AvalorStealthCompat.PinNow(__instance.GetComponent()); } } [HarmonyPostfix] [HarmonyPriority(0)] [HarmonyAfter(new string[] { "wubarrk.shadowsofmidgard", "wubarrk.ShadowsOfMidgard" })] internal static void Postfix(BaseAI __instance, ref bool __result) { if (!__result && AvalorStealthCompat.AnyTracked && !(__instance == null) && !AvalorStealthCompat.SuppressAlertForce && AvalorStealthCompat.IsOurs(__instance.GetComponent())) { __result = true; } } } [HarmonyPatch(typeof(BaseAI), "CanSenseTarget", new Type[] { typeof(Character) })] internal static class Avalor_CanSenseTarget { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyBefore(new string[] { "wubarrk.shadowsofmidgard", "wubarrk.ShadowsOfMidgard" })] internal static void Prefix(BaseAI __instance) { if (AvalorStealthCompat.AnyTracked && !(__instance == null)) { AvalorStealthCompat.PinNow(__instance.GetComponent()); } } [HarmonyPostfix] [HarmonyPriority(0)] [HarmonyAfter(new string[] { "wubarrk.shadowsofmidgard", "wubarrk.ShadowsOfMidgard" })] internal static void Postfix(BaseAI __instance, Character target, ref bool __result) { if (!__result && AvalorStealthCompat.AnyTracked && !(__instance == null) && !(target == null) && target.IsPlayer() && AvalorStealthCompat.IsOurs(__instance.GetComponent())) { float viewRange = __instance.m_viewRange; if ((target.transform.position - __instance.transform.position).sqrMagnitude <= viewRange * viewRange) { __result = true; } } } } [HarmonyPatch(typeof(Humanoid), "StartAttack")] internal static class Avalor_StartAttack { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyBefore(new string[] { "wubarrk.shadowsofmidgard", "wubarrk.ShadowsOfMidgard" })] internal static void Prefix(Humanoid __instance) { if (AvalorStealthCompat.AnyTracked) { AvalorStealthCompat.PinNow(__instance); } } } [HarmonyPatch(typeof(MonsterAI), "UpdateTarget")] internal static class Avalor_UpdateTarget { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyBefore(new string[] { "wubarrk.shadowsofmidgard", "wubarrk.ShadowsOfMidgard" })] internal static void Prefix(MonsterAI __instance) { if (AvalorStealthCompat.AnyTracked && !(__instance == null)) { AvalorStealthCompat.PinNow(__instance.GetComponent()); } } } [HarmonyPatch(typeof(MonsterAI), "UpdateAI")] internal static class Avalor_UpdateAI { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyBefore(new string[] { "wubarrk.shadowsofmidgard", "wubarrk.ShadowsOfMidgard" })] internal static void Prefix(MonsterAI __instance) { if (AvalorStealthCompat.AnyTracked && !(__instance == null)) { AvalorStealthCompat.PinNow(__instance.GetComponent()); } } } private const string SoM = "wubarrk.shadowsofmidgard"; private const string SoMAlt = "wubarrk.ShadowsOfMidgard"; } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorDiagLog ---- namespace MistsofAvalor { internal static class AvalorDiagLog { private class Sink : ILogListener, IDisposable { public void LogEvent(object sender, LogEventArgs args) { if (args != null && args.Data != null) { string text = args.Data.ToString(); if (text.IndexOf("Avalor", StringComparison.OrdinalIgnoreCase) >= 0) { Write($"[{args.Level}] {text}"); } } } public void Dispose() { } } private const string FolderName = "MistsofAvalor"; private const string FileName = "Avalor_diag.log"; private const long MaxBytes = 8388608L; private static string _path; private static bool _failed; private static readonly object _gate = new object(); public static void Init(string pluginVersion) { try { _path = ResolvePath(); FileInfo fileInfo = new FileInfo(_path); if (fileInfo.Exists && fileInfo.Length > 8388608) { string text = _path + ".1"; if (File.Exists(text)) { File.Delete(text); } File.Move(_path, text); } File.AppendAllText(_path, Environment.NewLine + "================================================================" + Environment.NewLine + $" Mists of Avalor v{pluginVersion} - session started {DateTime.Now:yyyy-MM-dd HH:mm:ss}" + Environment.NewLine + " profile: " + ProfileName() + Environment.NewLine + "================================================================" + Environment.NewLine, Encoding.UTF8); Logger.Listeners.Add(new Sink()); Logger.LogInfo((object)("[Avalor] diagnostics are also being appended to " + _path + " - that file survives a relaunch, unlike LogOutput.log.")); } catch (Exception ex) { _failed = true; Logger.LogWarning((object)("[Avalor][FAIL] could not open the persistent diagnostic log, so this session will only be recorded in LogOutput.log (which the next launch will overwrite): " + ex.Message)); } } private static string ResolvePath() { try { string text = Path.Combine(Paths.ConfigPath, "MistsofAvalor"); Directory.CreateDirectory(text); return Path.Combine(text, "Avalor_diag.log"); } catch { return Path.Combine(Paths.BepInExRootPath, "Avalor_diag.log"); } } private static string ProfileName() { try { DirectoryInfo parent = Directory.GetParent(Paths.BepInExRootPath); return (parent != null) ? (parent.Name + " (" + parent.FullName + ")") : Paths.BepInExRootPath; } catch { return "unknown"; } } private static void Write(string line) { if (_failed || _path == null) { return; } try { lock (_gate) { File.AppendAllText(_path, $"{DateTime.Now:HH:mm:ss} {line}{Environment.NewLine}", Encoding.UTF8); } } catch { _failed = true; } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorNet ---- namespace MistsofAvalor { public static class AvalorNet { public enum NetRole { Unknown, DedicatedServer, PlayerHost, Client } public struct PlayerPresence { public Vector3 pos; public Quaternion rot; public long ownerUid; } private struct CachedAnswer { public bool found; public Vector3 pos; public float at; } private static bool? _headless; private static NetRole _role = NetRole.Unknown; private const string Ns = "wubarrk.mistsofavalor."; public const string EnsureMazeRPC = "wubarrk.mistsofavalor.EnsureMaze"; public const string EvacuateRPC = "wubarrk.mistsofavalor.Evacuate"; public const string RelocateGraveRPC = "wubarrk.mistsofavalor.RelocateGrave"; public const string QueryRPC = "wubarrk.mistsofavalor.Query"; public const string AnswerRPC = "wubarrk.mistsofavalor.Answer"; public const string RequestPortalSiteRPC = "wubarrk.mistsofavalor.ReqPortalSite"; public const string GrantPortalSiteRPC = "wubarrk.mistsofavalor.GrantPortalSite"; public const string PortalSitedRPC = "wubarrk.mistsofavalor.PortalSited"; public const int QueryExit = 1; public const int QueryNearestGrave = 2; private static ZRoutedRpc _registeredOn; private static readonly Dictionary _answers = new Dictionary(); private static readonly Dictionary _lastQuerySent = new Dictionary(); private const float PortalClaimTimeout = 120f; private static long _portalClaimHolder; private static float _portalClaimAt; public static bool IsHeadless { get { if (!_headless.HasValue) { _headless = SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null; } return _headless.Value; } } public static NetRole Role { get { if (_role == NetRole.Unknown && ZNet.instance != null) { _role = ((!ZNet.instance.IsServer()) ? NetRole.Client : (IsHeadless ? NetRole.DedicatedServer : NetRole.PlayerHost)); } return _role; } } public static bool IsServerRole { get { if (Role != NetRole.DedicatedServer) { return Role == NetRole.PlayerHost; } return true; } } public static bool PortalGrantAnswered { get; private set; } public static bool PortalSiteGranted { get; private set; } public static void Print(string msg) { Console.instance?.Print(msg); Logger.LogInfo((object)("[Avalor][cmd] " + msg)); } public static void EnsureRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null || instance == _registeredOn) { return; } _registeredOn = instance; _role = NetRole.Unknown; _answers.Clear(); _lastQuerySent.Clear(); _portalClaimHolder = 0L; PortalGrantAnswered = false; PortalSiteGranted = false; NetRole role = Role; bool isServerRole = IsServerRole; RegisterSafe(instance, "wubarrk.mistsofavalor.Evacuate", delegate(ZRoutedRpc r) { r.Register("wubarrk.mistsofavalor.Evacuate", RPC_Evacuate); }); RegisterSafe(instance, "wubarrk.mistsofavalor.Answer", delegate(ZRoutedRpc r) { r.Register("wubarrk.mistsofavalor.Answer", RPC_Answer); }); RegisterSafe(instance, "wubarrk.mistsofavalor.GrantPortalSite", delegate(ZRoutedRpc r) { r.Register("wubarrk.mistsofavalor.GrantPortalSite", RPC_GrantPortalSite); }); if (isServerRole) { RegisterSafe(instance, "wubarrk.mistsofavalor.EnsureMaze", delegate(ZRoutedRpc r) { r.Register("wubarrk.mistsofavalor.EnsureMaze", RPC_EnsureMaze); }); RegisterSafe(instance, "wubarrk.mistsofavalor.RelocateGrave", delegate(ZRoutedRpc r) { r.Register("wubarrk.mistsofavalor.RelocateGrave", Avalor_TombstoneToGraveyard_Patch.ServerRelocateGrave); }); RegisterSafe(instance, "wubarrk.mistsofavalor.Query", delegate(ZRoutedRpc r) { r.Register("wubarrk.mistsofavalor.Query", RPC_Query); }); RegisterSafe(instance, "wubarrk.mistsofavalor.ReqPortalSite", delegate(ZRoutedRpc r) { r.Register("wubarrk.mistsofavalor.ReqPortalSite", RPC_RequestPortalSite); }); RegisterSafe(instance, "wubarrk.mistsofavalor.PortalSited", delegate(ZRoutedRpc r) { r.Register("wubarrk.mistsofavalor.PortalSited", RPC_PortalSited); }); } Logger.LogInfo((object)(string.Format("[Avalor][net] topology={0} - RPCs registered ({1} handlers) ", role, isServerRole ? "SERVER+client" : "client") + $"headless={IsHeadless} " + $"activeArea={((ZoneSystem.instance != null) ? ZoneSystem.instance.m_activeArea : (-1))}")); } private static void RegisterSafe(ZRoutedRpc rpc, string name, Action register) { try { register(rpc); } catch (Exception ex) { Logger.LogError((object)("[Avalor][FAIL] could not register routed RPC '" + name + "' - most likely a name collision with another mod. The feature behind it will not work this session. Reason: " + ex.Message)); } } public static List GetMazePlayers(float radius = 300f) { List list = new List(); if (ZNet.instance == null) { return list; } Vector3 mazeCenter = AvalorMazeGenerator.MazeCenter; foreach (ZDO allCharacterZDO in ZNet.instance.GetAllCharacterZDOS()) { if (allCharacterZDO != null) { Vector3 position = allCharacterZDO.GetPosition(); if (AvalorRegion.InSkyMaze(position) && !(Vector3.Distance(position, mazeCenter) > radius)) { list.Add(new PlayerPresence { pos = position, rot = allCharacterZDO.GetRotation(), ownerUid = allCharacterZDO.GetOwner() }); } } } return list; } public static bool AnyPlayersInMaze(float radius = 400f) { Vector3 mazeCenter = AvalorMazeGenerator.MazeCenter; if (ZNet.instance != null) { foreach (ZDO allCharacterZDO in ZNet.instance.GetAllCharacterZDOS()) { if (allCharacterZDO != null && Vector3.Distance(allCharacterZDO.GetPosition(), mazeCenter) < radius) { return true; } } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && Vector3.Distance(peer.m_refPos, mazeCenter) < radius) { return true; } } } if (Player.m_localPlayer != null && Vector3.Distance(Player.m_localPlayer.transform.position, mazeCenter) < radius) { return true; } return false; } private static void RPC_EnsureMaze(long sender) { if (ZNet.instance == null || !ZNet.instance.IsServer()) { return; } AvalorMazeGenerator instance = AvalorMazeGenerator.Instance; if (!(instance == null) && !instance.IsGenerating) { if (AvalorMazeGenerator.MazeExistsInWorld()) { instance.RecoverSpawnedZDOs(); return; } Logger.LogInfo((object)"[Avalor] a client stepped through with no labyrinth standing - forging it server-side."); instance.StartCoroutine(instance.GenerateMaze()); } } private static void RPC_Evacuate(long sender) { Player localPlayer = Player.m_localPlayer; if (!(localPlayer == null) && AvalorRegion.InSkyMaze(localPlayer.transform.position)) { localPlayer.Message(MessageHud.MessageType.Center, "The mists tear you free - the labyrinth is being reforged!"); AvalorPortalWalkTrigger.Suppress(); Vector3 pos2; Vector3 pos = ((ZoneSystem.instance != null && ZoneSystem.instance.GetLocationIcon("StartTemple", out pos2)) ? (pos2 + new Vector3(0f, 1.5f, 0f)) : new Vector3(0f, 50f, 0f)); localPlayer.TeleportTo(pos, localPlayer.transform.rotation, distantTeleport: false); Logger.LogInfo((object)"[Avalor] evacuated the local player from the labyrinth (maze reset incoming)."); } } public static void SendQuery(int kind) { if (ZRoutedRpc.instance != null && (!_lastQuerySent.TryGetValue(kind, out var value) || !(Time.realtimeSinceStartup - value < 2f))) { _lastQuerySent[kind] = Time.realtimeSinceStartup; ZRoutedRpc.instance.InvokeRoutedRPC("wubarrk.mistsofavalor.Query", kind); } } public static bool TryGetFreshAnswer(int kind, float maxAge, out bool found, out Vector3 pos) { found = false; pos = Vector3.zero; if (!_answers.TryGetValue(kind, out var value)) { return false; } if (Time.realtimeSinceStartup - value.at > maxAge) { return false; } found = value.found; pos = value.pos; return true; } private static void RPC_Answer(long sender, int kind, bool found, Vector3 pos) { _answers[kind] = new CachedAnswer { found = found, pos = pos, at = Time.realtimeSinceStartup }; } private static void RPC_Query(long sender, int kind) { if (!(ZNet.instance == null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { bool flag = false; Vector3 pos = Vector3.zero; switch (kind) { case 1: flag = AvalorMobDirector.TryFindExit(out pos); break; case 2: flag = TryFindNearestTombstone(GetAskerPosition(sender), out pos); break; } ZRoutedRpc.instance.InvokeRoutedRPC(sender, "wubarrk.mistsofavalor.Answer", kind, flag, pos); if (MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)$"[Avalor][net] answered query kind={kind} for {sender}: found={flag} pos={pos:F0}"); } } } private static Vector3 GetAskerPosition(long sender) { ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer != null) { return peer.m_refPos; } if (Player.m_localPlayer != null) { return Player.m_localPlayer.transform.position; } return Vector3.zero; } private static bool TryFindNearestTombstone(Vector3 from, out Vector3 pos) { pos = Vector3.zero; Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); if (zDODictionary == null) { return false; } int stableHashCode = "Player_tombstone".GetStableHashCode(); float num = float.MaxValue; bool result = false; foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value != null && value.GetPrefab() == stableHashCode) { float sqrMagnitude = (value.GetPosition() - from).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; pos = value.GetPosition(); result = true; } } } return result; } public static void RequestPortalSite() { PortalGrantAnswered = false; PortalSiteGranted = false; ZRoutedRpc.instance?.InvokeRoutedRPC("wubarrk.mistsofavalor.ReqPortalSite"); } public static void NotifyPortalSited() { ZRoutedRpc.instance?.InvokeRoutedRPC("wubarrk.mistsofavalor.PortalSited"); } public static void ServerMarkPortalSiting() { if (!(ZNet.instance == null) && ZNet.instance.IsServer()) { _portalClaimHolder = ZDOMan.GetSessionID(); _portalClaimAt = Time.realtimeSinceStartup; } } public static bool TryFindOverworldPortalNearTemple(out Vector3 pos, out ZDO zdo) { pos = Vector3.zero; zdo = null; Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); if (zDODictionary == null) { return false; } Vector3 pos2 = Vector3.zero; bool flag = ZoneSystem.instance != null && ZoneSystem.instance.GetLocationIcon("StartTemple", out pos2); int stableHashCode = "Avalor_OverworldPortal".GetStableHashCode(); foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value != null && value.GetPrefab() == stableHashCode) { Vector3 position = value.GetPosition(); if (!(position.x >= 25000f) && (!flag || !(Vector3.Distance(position, pos2) > 150f))) { pos = position; zdo = value; return true; } } } return false; } private static void RPC_RequestPortalSite(long sender) { if (ZNet.instance == null || !ZNet.instance.IsServer() || ZRoutedRpc.instance == null) { return; } bool num = MistsofAvalorPlugin.SpawnPortalAtStart == null || MistsofAvalorPlugin.SpawnPortalAtStart.Value; Vector3 pos; ZDO zdo; bool flag = TryFindOverworldPortalNearTemple(out pos, out zdo); if (!num) { if (flag && zdo != null) { if (!zdo.IsOwner()) { zdo.SetOwner(ZDOMan.GetSessionID()); } ZDOMan.instance.DestroyZDO(zdo); Logger.LogInfo((object)"[Avalor] SpawnPortalAtStart is OFF - removed the existing overworld portal."); } Grant(sender, granted: false, "config off"); } else if (flag) { Grant(sender, granted: false, "portal already exists"); } else { float realtimeSinceStartup = Time.realtimeSinceStartup; if (_portalClaimHolder != 0L && _portalClaimHolder != sender && realtimeSinceStartup - _portalClaimAt < 120f) { Grant(sender, granted: false, "another machine is siting it"); return; } _portalClaimHolder = sender; _portalClaimAt = realtimeSinceStartup; Grant(sender, granted: true, "granted"); } } private static void Grant(long sender, bool granted, string why) { Logger.LogInfo((object)string.Format("[Avalor] portal-siting request from {0}: {1} ({2}).", sender, granted ? "GRANTED" : "denied", why)); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "wubarrk.mistsofavalor.GrantPortalSite", granted); } private static void RPC_PortalSited(long sender) { if (sender == _portalClaimHolder) { _portalClaimHolder = 0L; } Logger.LogInfo((object)$"[Avalor] portal siting finished by {sender} - claim released."); } private static void RPC_GrantPortalSite(long sender, bool granted) { PortalGrantAnswered = true; PortalSiteGranted = granted; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorResetManager ---- namespace MistsofAvalor { public class AvalorResetManager : MonoBehaviour { public static ConfigEntry ResetTimerHours; private float lastGeneratedTime; private float checkTimer; public static AvalorResetManager Instance { get; private set; } private void Awake() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown Instance = this; ResetTimerHours = MistsofAvalorPlugin.Instance.Config.Bind("General", "MazeResetIntervalHours", 24f, new ConfigDescription("How often (in real hours) the maze regenerates automatically.", new AcceptableValueRange(0.5f, 720f), (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); } public void MarkGenerated() { lastGeneratedTime = Time.realtimeSinceStartup; } private void Update() { if (ZNet.instance == null || !ZNet.instance.IsServer()) { return; } checkTimer += Time.deltaTime; if (!(checkTimer < 60f)) { checkTimer = 0f; if (lastGeneratedTime != 0f && !(ResetTimerHours.Value <= 0f) && (Time.realtimeSinceStartup - lastGeneratedTime) / 3600f >= ResetTimerHours.Value) { TryResetMaze(); } } } private void TryResetMaze() { if (!AvalorNet.AnyPlayersInMaze()) { if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"Avalor Reset Timer reached. No players inside. Regenerating maze."); } WipeSkyZone(AvalorMazeGenerator.MazeCenter, 2000f); AvalorMazeGenerator.Instance?.StartCoroutine(AvalorMazeGenerator.Instance.GenerateMaze()); } else if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"Avalor Reset Timer reached, but players are inside. Waiting..."); } } public void ForceResetMaze(bool remote = false) { Vector3 mazeCenter = AvalorMazeGenerator.MazeCenter; if (Player.m_localPlayer != null && !remote && Vector3.Distance(Player.m_localPlayer.transform.position, mazeCenter) > 2000f) { Player.m_localPlayer.Message(MessageHud.MessageType.Center, "You must be inside Avalor to run avalor_reset!"); Logger.LogWarning((object)"avalor_reset failed: Player is too far away. The maze zone is not loaded."); } else if (AvalorNet.AnyPlayersInMaze() && ZRoutedRpc.instance != null) { Logger.LogInfo((object)"[Avalor] maze reset with players inside - broadcasting evacuation first."); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "wubarrk.mistsofavalor.Evacuate"); StartCoroutine(WipeAfterEvacuation(remote)); } else { DoForceReset(remote); } } private IEnumerator WipeAfterEvacuation(bool remote) { yield return new WaitForSeconds(3.5f); for (float waited = 3.5f; waited < 10f; waited += 1f) { if (!AvalorNet.AnyPlayersInMaze()) { break; } yield return new WaitForSeconds(1f); } DoForceReset(remote); } private void DoForceReset(bool remote) { if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"Forcing Maze Reset! Wiping zone safely..."); } WipeSkyZone(AvalorMazeGenerator.MazeCenter, 2000f); if (!(AvalorMazeGenerator.Instance != null)) { return; } AvalorMazeGenerator.Instance.StopAllCoroutines(); AvalorMazeGenerator.Instance.StartCoroutine(AvalorMazeGenerator.Instance.GenerateMaze(delegate { if (Player.m_localPlayer != null && !remote) { AvalorSafePlatform.Drop(AvalorMazeGenerator.StartLocation, 10f, 10f); Player.m_localPlayer.TeleportTo(AvalorMazeGenerator.StartLocation, Player.m_localPlayer.transform.rotation, distantTeleport: true); Player.m_localPlayer.Message(MessageHud.MessageType.Center, "Maze reset! Teleported to start."); } else if (Player.m_localPlayer != null && remote) { Player.m_localPlayer.Message(MessageHud.MessageType.Center, "Maze reset remotely! You have not been teleported."); } })); } public void WipeSkyZone(Vector3 center, float radius) { if (ZNetScene.instance == null || ZDOMan.instance == null) { return; } AvalorMazeGenerator.Instance?.StopAllCoroutines(); AvalorMazeGenerator.Instance?.spawnedZDOs.Clear(); AvalorMobDirector.Instance?.OnMazeWiped(); AvalorHuntEnforcer.Forget(); int stableHashCode = "Player".GetStableHashCode(); int stableHashCode2 = "Player_tombstone".GetStableHashCode(); bool flag = radius > 0f; Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); List list = new List(); if (zDODictionary != null) { foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value == null) { continue; } Vector3 position = value.GetPosition(); if (!(position.x <= 25000f) && (!flag || !(Vector3.Distance(position, center) > radius))) { int prefab = value.GetPrefab(); if (prefab != stableHashCode && prefab != stableHashCode2) { list.Add(value); } } } } long sessionID = ZDOMan.GetSessionID(); foreach (ZDO item2 in list) { ZNetView zNetView = ZNetScene.instance.FindInstance(item2); if (zNetView != null) { zNetView.ClaimOwnership(); ZNetScene.instance.Destroy(zNetView.gameObject); continue; } if (!item2.IsOwner()) { item2.SetOwner(sessionID); } ZDOMan.instance.DestroyZDO(item2); } int num = 0; ZNetView[] array = Object.FindObjectsByType(FindObjectsSortMode.None); foreach (ZNetView zNetView2 in array) { if (!(zNetView2 == null) && !(zNetView2.GetComponent() != null)) { Vector3 position2 = zNetView2.transform.position; if (!(position2.x <= 25000f) && (!flag || !(Vector3.Distance(position2, center) > radius))) { ZNetScene.instance.Destroy(zNetView2.gameObject); num++; } } } if (MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)$"[Avalor] WipeSkyZone cleared {list.Count} ZDO(s) + {num} loaded straggler(s) in the sky region."); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorSafePlatform ---- namespace MistsofAvalor { internal static class AvalorSafePlatform { private static Material _donor; private static bool _donorResolved; private static readonly string[] DonorPrefabs = new string[4] { "Avalor_MegaFloor", "stone_wall_2x2", "rock4_coast", "StoneBlock_Fracture" }; public static GameObject Drop(Vector3 feetPos, float size, float lifetime) { GameObject gameObject = GameObject.CreatePrimitive(PrimitiveType.Cube); gameObject.name = "AvalorSafePlatform"; gameObject.transform.position = feetPos - new Vector3(0f, 0.5f, 0f); gameObject.transform.localScale = new Vector3(size, 1f, size); int num = LayerMask.NameToLayer("static_solid"); if (num >= 0) { gameObject.layer = num; } MeshRenderer component = gameObject.GetComponent(); if (component != null) { Material material = ResolveDonor(); if (material != null) { component.sharedMaterial = material; } else { component.enabled = false; } component.shadowCastingMode = ShadowCastingMode.Off; component.receiveShadows = false; } Object.Destroy(gameObject, lifetime); return gameObject; } private static Material ResolveDonor() { if (_donorResolved) { return _donor; } _donorResolved = true; if (ZNetScene.instance == null) { _donorResolved = false; return null; } string[] donorPrefabs = DonorPrefabs; foreach (string text in donorPrefabs) { GameObject prefab = ZNetScene.instance.GetPrefab(text); if (prefab == null) { continue; } MeshRenderer[] componentsInChildren = prefab.GetComponentsInChildren(includeInactive: true); foreach (MeshRenderer meshRenderer in componentsInChildren) { if (!(meshRenderer == null) && !(meshRenderer.sharedMaterial == null) && !(meshRenderer.sharedMaterial.shader == null)) { _donor = meshRenderer.sharedMaterial; if (MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)("[Avalor] safe platform borrowed material '" + _donor.name + "' (shader '" + _donor.shader.name + "') from " + text + ".")); } return _donor; } } } Logger.LogWarning((object)"[Avalor][FAIL] no donor material found for the arrival safety platform - rendering it INVISIBLE so it cannot show up as a magenta block. It still catches the player; only the visual is lost."); return null; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.Avalor_SkyRegionNoVegetation_Patch ---- namespace MistsofAvalor { [HarmonyPatch(typeof(ZoneSystem), "PlaceVegetation")] public static class Avalor_SkyRegionNoVegetation_Patch { private static int _skipped; public static bool Prefix(Vector3 zoneCenterPos) { if (zoneCenterPos.x <= 25000f) { return true; } if (MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value && ++_skipped % 25 == 1) { Logger.LogInfo((object)$"[Avalor] skipped Ashlands vegetation for {_skipped} sky-region zone(s)."); } return false; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorRegion ---- namespace MistsofAvalor { public static class AvalorRegion { public const float SkyRegionX = 25000f; public static bool InSkyMaze(Vector3 p) { return p.x > 25000f; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.DifficultyLevel ---- namespace MistsofAvalor { public enum DifficultyLevel { Easy, Normal, Hardcore } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.ClutterDensity ---- namespace MistsofAvalor { public enum ClutterDensity { None, Normal, High, Higher, Extreme } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.MazeComplexity ---- namespace MistsofAvalor { public enum MazeComplexity { Normal, Hard, Extreme } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.LightingLevel ---- namespace MistsofAvalor { public enum LightingLevel { None, Some, More, Most } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.MistDensity ---- namespace MistsofAvalor { public enum MistDensity { None, Little, Normal, More, Most } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.MistsofAvalorPlugin ---- namespace MistsofAvalor { [BepInPlugin("wubarrk.mistsofavalor", "Mists of Avalor", "0.1.0")] [BepInDependency("com.jotunn.jotunn", BepInDependency.DependencyFlags.HardDependency)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class MistsofAvalorPlugin : BaseUnityPlugin { public const string PluginGUID = "wubarrk.mistsofavalor"; public const string PluginName = "Mists of Avalor"; public const string PluginVersion = "0.1.0"; public static ConfigEntry DifficultyConfig; public static ConfigEntry ClutterConfig; public static ConfigEntry ComplexityConfig; public static ConfigEntry LightingConfig; public static ConfigEntry MistConfig; public static ConfigEntry SpawnPortalAtStart; public static ConfigEntry DebugLogging; public static ConfigEntry ExitCompass; private readonly Harmony harmony = new Harmony("wubarrk.mistsofavalor"); public static MistsofAvalorPlugin Instance { get; private set; } private void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown Instance = this; AvalorDiagLog.Init("0.1.0"); ConfigurationManagerAttributes val = new ConfigurationManagerAttributes { IsAdminOnly = true }; DifficultyConfig = base.Config.Bind("General", "Difficulty", DifficultyLevel.Normal, new ConfigDescription("Sets the difficulty of the Avalor Labyrinth (Easy, Normal, Hardcore).", null, val)); ClutterConfig = base.Config.Bind("General", "ClutterDensity", ClutterDensity.Normal, new ConfigDescription("Density of debris scattered in the OPEN floor of the corridors (None disables open-floor scatter; the wall bases/faces are always dressed regardless). None, Normal, High, Higher, Extreme.", null, val)); ComplexityConfig = base.Config.Bind("General", "MazeComplexity", MazeComplexity.Normal, new ConfigDescription("Sets the size and complexity of the Labyrinth (Normal = 20x20, Hard = 40x40, Extreme = 60x60). Applies on the next maze generation.", null, val)); LightingConfig = base.Config.Bind("General", "LightingLevel", LightingLevel.Some, new ConfigDescription("Controls how BRIGHTLY the crypt torches burn AND how dark the crypt is. None = PITCH BLACK (torches out, near-zero ambient - bring your own light); Some = heavy gloom; More/Most = progressively brighter. Applies instantly, no regeneration needed - the same torches are always there, only their brightness changes.", null, val)); MistConfig = base.Config.Bind("General", "MistDensity", MistDensity.Little, new ConfigDescription("Controls the fog inside the maze (None, Little, Normal, More, Most). Fog applies instantly; the number of mist props on the next generation.", null, val)); SpawnPortalAtStart = base.Config.Bind("General", "SpawnPortalAtStart", defaultValue: true, new ConfigDescription("Spawns a decrepit Avalor Portal near the starting Sacrificial Stones.", null, val)); DebugLogging = base.Config.Bind("General", "DebugLogging", defaultValue: false, "Enable to show debug logs in the console. Local to your own client - not server-synced."); ExitCompass = base.Config.Bind("General", "ExitCompass", defaultValue: true, "Show a faint compass to the labyrinth's exit while you are inside it. Turn this OFF if you would rather be properly lost. Local to your own client - not server-synced."); AvalorWeapons.RegisterLocalization(); AvalorAssetManager.Init(); GameObject obj = new GameObject("MistsOfAvalor_Managers"); UnityEngine.Object.DontDestroyOnLoad(obj); obj.AddComponent(); obj.AddComponent(); obj.AddComponent(); obj.AddComponent(); obj.AddComponent(); obj.AddComponent(); obj.AddComponent(); obj.AddComponent(); obj.AddComponent(); obj.AddComponent(); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorResetCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorRemoteResetCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorSpawnPortalCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorFixStonesCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorIlluminateCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorTpCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorRemoveAllCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorGenerateAllCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorTpGraveCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorOpenCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new AvalorTpEndCommand()); ApplyPatch(typeof(Avalor_GlobalMazeEnemyDifficulty_Patch), "In-maze enemy health buff (+20%) + sub-boss/boss replication"); ApplyPatch(typeof(Avalor_TombstoneToGraveyard_Patch), "Maze-death tombstone -> graveyard relocation"); ApplyPatch(typeof(Avalor_NoBuildZone_Patch), "No-build protection (maze + portal clearing)"); ApplyPatch(typeof(Avalor_SkyRegionNoVegetation_Patch), "Skip Ashlands vegetation under the sky maze (FPS)"); ApplyPatch(typeof(Avalor_GreatswordVitality_Patch), "Avalorian Greatsword +100 health / +100 stamina"); ApplyPatch(typeof(Avalor_HideBladeRecipes_Patch), "Avalor blades hidden from the crafting lists"); ApplyPatch(typeof(AvalorStealthGuards.Avalor_IsAlerted), "Maze mobs always read as alerted"); ApplyPatch(typeof(AvalorStealthGuards.Avalor_CanSenseTarget), "Maze mobs sense the player through walls"); ApplyPatch(typeof(AvalorStealthGuards.Avalor_StartAttack), "Maze mobs may attack (stealth-mod gate)"); ApplyPatch(typeof(AvalorStealthGuards.Avalor_UpdateTarget), "Maze mob target acquisition pin"); ApplyPatch(typeof(AvalorStealthGuards.Avalor_UpdateAI), "Maze mob AI-tick pin"); Logger.LogInfo((object)"Mists of Avalor loaded successfully."); } private void ApplyPatch(Type patchType, string feature) { try { harmony.CreateClassProcessor(patchType).Patch(); if (DebugLogging != null && DebugLogging.Value) { Logger.LogInfo((object)("[Avalor] Patch applied OK: " + feature)); } } catch (Exception ex) { Logger.LogError((object)("!!! AVALOR PATCH FAILED - FEATURE LOST: \"" + feature + "\". The mod will keep running WITHOUT it. Reason: " + ex)); } } private void OnDestroy() { harmony.UnpatchSelf(); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.Avalor_GlobalMazeEnemyDifficulty_Patch ---- namespace MistsofAvalor { [HarmonyPatch(typeof(Character), "Awake")] public static class Avalor_GlobalMazeEnemyDifficulty_Patch { public const float AVALORIAN_HEALTH_BUFF = 1.2f; public const string HPBuffAppliedZDOKey = "Avalor_HPBuffed"; public static void Postfix(Character __instance) { try { if (__instance == null || __instance.IsPlayer()) { return; } ZNetView component = __instance.GetComponent(); if (component == null || !component.IsValid() || component.GetZDO() == null) { return; } ZDO zDO = component.GetZDO(); if (__instance.transform.position.x > 25000f && component.IsOwner() && zDO.GetInt("Avalor_HPBuffed") == 0) { zDO.Set("Avalor_HPBuffed", 1); __instance.SetMaxHealth(__instance.GetMaxHealth() * 1.2f); __instance.SetHealth(__instance.GetMaxHealth()); } bool flag = AvalorRegion.InSkyMaze(__instance.transform.position) && !__instance.IsTamed(); if (flag && component.IsOwner() && zDO.GetInt("SoMStealthExempt") != 1) { zDO.Set("SoMStealthExempt", 1); } int num; if (!flag) { num = ((zDO.GetInt("SoMStealthExempt") == 1) ? 1 : 0); if (num == 0) { goto IL_00fc; } } else { num = 1; } AvalorStealthCompat.Register(__instance); goto IL_00fc; IL_00fc: bool flag2 = zDO.GetInt("AvalorWarden") == 1; if (num != 0 && !flag2) { AvalorHuntEnforcer.Enqueue(__instance.gameObject); } int num2 = zDO.GetInt("AvalorSubBoss"); if (num2 > 0) { AvalorMobModifier.ApplyLocalMutations(__instance.gameObject, num2 - 1, (MistsofAvalorPlugin.DifficultyConfig == null) ? DifficultyLevel.Normal : MistsofAvalorPlugin.DifficultyConfig.Value); } if (flag2) { __instance.m_name = "$enemy_avalor_warden"; AvalorBossBar.Instance?.Track(__instance, "$enemy_avalor_warden", "The Avalorian Warden", AvalorMobDirector.WardenBarColour, priority: true); AvalorMobDirector.ApplyWardenLocal(__instance.gameObject, __instance); } else if (zDO.GetInt("AvalorRevenant") == 1) { __instance.m_name = "$enemy_avalor_revenant"; AvalorBossBar.Instance?.Track(__instance, "$enemy_avalor_revenant", "Avalorian Revenant", AvalorCryptEnvZone.AvalorGreen); AvalorMobDirector.AttachShortswordDrop(__instance.gameObject); } else if (flag && num2 == 0) { AvalorianName.Apply(__instance); } } catch (Exception ex) { Logger.LogError((object)("[Avalor] enemy-buff patch threw (skipped this spawn, no harm): " + ex.Message)); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.Avalor_TombstoneToGraveyard_Patch ---- namespace MistsofAvalor { [HarmonyPatch(typeof(TombStone), "Awake")] public static class Avalor_TombstoneToGraveyard_Patch { public static float SuppressUntil; private const float GraveLift = 0.95f; private const int GravePlotRows = 4; private const int GravePlotCount = 16; private const float GravePlotInnerLateral = 7f; private const float GravePlotOuterLateral = 9f; private static bool Dbg { get { if (MistsofAvalorPlugin.DebugLogging != null) { return MistsofAvalorPlugin.DebugLogging.Value; } return false; } } public static void Postfix(TombStone __instance) { try { if (!(__instance == null) && !(Time.time < SuppressUntil)) { __instance.StartCoroutine(RelocateWhenReady(__instance)); } } catch (Exception ex) { Logger.LogError((object)("[Avalor] tombstone-relocation start threw - grave LEFT where it died, fully recoverable (NO lost items): " + ex.Message)); } } private static IEnumerator RelocateWhenReady(TombStone ts) { ZNetView znv = null; float t = 0f; while (t < 3f) { if (ts == null) { yield break; } znv = ts.GetComponent(); if (znv != null && znv.IsValid() && znv.GetZDO() != null) { break; } t += Time.deltaTime; yield return null; } if (ts == null || znv == null || !znv.IsValid() || znv.GetZDO() == null) { if (Dbg) { Logger.LogWarning((object)"[Avalor] grave relocation: ZDO never became valid - leaving grave where it died."); } yield break; } Vector3 position = znv.GetZDO().GetPosition(); if (position.x <= 25000f) { if (Dbg) { Logger.LogInfo((object)$"[Avalor] grave relocation: death at {position:F0} is in the overworld (x<=25000) - left in place."); } yield break; } if (ZRoutedRpc.instance == null) { Logger.LogWarning((object)"[Avalor][FAIL] grave relocation: no routed RPC channel - grave LEFT in the maze (recoverable via avalor_tp_grave)."); yield break; } ZRoutedRpc.instance.InvokeRoutedRPC("wubarrk.mistsofavalor.RelocateGrave", znv.GetZDO().m_uid); if (Dbg) { Logger.LogInfo((object)$"[Avalor] maze grave at {position:F0} - asked the server to relocate it to the graveyard."); } } internal static void ServerRelocateGrave(long sender, ZDOID graveId) { try { if (ZNet.instance == null || !ZNet.instance.IsServer()) { return; } ZDO zDO = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(graveId) : null); if (zDO == null) { return; } Vector3 position = zDO.GetPosition(); if (!(position.x <= 25000f)) { if (!zDO.IsOwner()) { zDO.SetOwner(ZDOMan.GetSessionID()); } if (!TryGetGraveyard(out var pos)) { Logger.LogWarning((object)"[Avalor][FAIL] grave relocation: graveyard portal NOT found in the server's ZDO table - grave LEFT in the maze (never risk losing it)."); return; } Vector3 vector = FindFreePlot(pos); zDO.SetPosition(vector); zDO.Set(ZDOVars.s_spawnPoint, vector); Logger.LogInfo((object)$"[Avalor] grave relocated (server): maze {position:F0} -> graveyard {vector:F0} (portal at {pos:F0}, asked by {sender})."); } } catch (Exception ex) { Logger.LogError((object)("[Avalor] tombstone-relocation threw - grave LEFT where it died, fully recoverable (NO lost items): " + ex.Message)); } } private static Vector3 FindFreePlot(Vector3 center) { List pts = CollectGravePlots(center, 22f); List pts2 = CollectDecorBlockers(center, 26f); if (!SiteAxes(center, out var forward, out var perp)) { float f = UnityEngine.Random.Range(0f, MathF.PI * 2f); Vector3 vector = center + new Vector3(Mathf.Cos(f) * 11f, 0f, Mathf.Sin(f) * 11f); vector.y = PlotHeight(center, vector); return vector; } for (int i = 0; i < 16; i++) { bool flag = i % 2 == 0; int num = i / 2; float num2 = ((num < 4) ? 7f : 9f) * (flag ? 1f : (-1f)); float num3 = 8f + (float)(num % 4) * 3.5f; Vector3 vector2 = center + perp * num2 - forward * num3; if (Clear(vector2, pts, 2.2f) && Clear(vector2, pts2, 3.2f)) { vector2.y = PlotHeight(center, vector2); return vector2; } } Vector3 vector3 = center + forward * 12f + perp * ((UnityEngine.Random.value < 0.5f) ? 6f : (-6f)); vector3.y = PlotHeight(center, vector3); return vector3; } private static bool SiteAxes(Vector3 portal, out Vector3 forward, out Vector3 perp) { forward = Vector3.forward; perp = Vector3.right; if (ZoneSystem.instance == null || !ZoneSystem.instance.GetLocationIcon("StartTemple", out var pos)) { return false; } Vector3 vector = portal - pos; vector.y = 0f; if (vector.sqrMagnitude < 1f) { return false; } forward = vector.normalized; perp = Vector3.Cross(forward, Vector3.up).normalized; return true; } private static float PlotHeight(Vector3 portal, Vector3 plot) { if (ZoneSystem.instance != null && ZoneSystem.instance.GetGroundHeight(plot, out var height)) { return height + 0.95f; } return portal.y + 0.95f; } private static bool Clear(Vector3 cand, List pts, float minDist) { float num = minDist * minDist; foreach (Vector3 pt in pts) { float num2 = cand.x - pt.x; float num3 = cand.z - pt.z; if (num2 * num2 + num3 * num3 < num) { return false; } } return true; } private static List CollectGravePlots(Vector3 center, float radius) { return CollectByPrefab(center, radius, new string[2] { "Player_tombstone", "MountainGraveStone01" }); } private static List CollectDecorBlockers(Vector3 center, float radius) { return CollectByPrefab(center, radius, new string[3] { "Avalor_OverworldPortal", "SwampTree1", "stone_wall_2x2" }); } private static List CollectByPrefab(Vector3 center, float radius, string[] names) { List list = new List(); Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); if (zDODictionary == null) { return list; } HashSet hashSet = new HashSet(); foreach (string str in names) { hashSet.Add(str.GetStableHashCode()); } float num = radius * radius; foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value == null || !hashSet.Contains(value.GetPrefab())) { continue; } Vector3 position = value.GetPosition(); if (!(position.x >= 25000f)) { float num2 = position.x - center.x; float num3 = position.z - center.z; if (num2 * num2 + num3 * num3 <= num) { list.Add(position); } } } return list; } private static bool TryGetGraveyard(out Vector3 pos) { return AvalorMazeGenerator.TryGetGraveyardPortal(out pos); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.Avalor_NoBuildZone_Patch ---- namespace MistsofAvalor { [HarmonyPatch(typeof(Player), "PlacePiece", new Type[] { typeof(Piece), typeof(Vector3), typeof(Quaternion), typeof(bool) })] public static class Avalor_NoBuildZone_Patch { private const float PortalGuardRadius = 28f; private static Vector3 s_graveyard = Vector3.zero; private static float s_lastScan = -999f; public static bool Prefix(Player __instance, Vector3 pos) { try { if (IsProtected(pos)) { __instance?.Message(MessageHud.MessageType.Center, "The mists reject your works here."); return false; } } catch (Exception ex) { Logger.LogError((object)("[Avalor] no-build patch threw (allowing this placement): " + ex.Message)); } return true; } private static bool IsProtected(Vector3 pos) { if (pos.x > 25000f) { return true; } if (Time.time - s_lastScan > 30f) { s_lastScan = Time.time; if (TryFindOverworldPortal(out var pos2)) { s_graveyard = pos2; } } if (s_graveyard != Vector3.zero && Vector3.Distance(pos, s_graveyard) < 28f) { return true; } return false; } private static bool TryFindOverworldPortal(out Vector3 pos) { pos = Vector3.zero; Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); if (zDODictionary == null) { return false; } int stableHashCode = "Avalor_OverworldPortal".GetStableHashCode(); foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value != null && value.GetPrefab() == stableHashCode && value.GetPosition().x < 25000f) { pos = value.GetPosition(); return true; } } return false; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorLootManager ---- namespace MistsofAvalor { public static class AvalorLootManager { private static readonly string[] PoolCommon = new string[9] { "Coins", "Amber", "AmberPearl", "Iron", "Silver", "Crystal", "SurtlingCore", "LinenThread", "Ruby" }; private static readonly string[] PoolMid = new string[13] { "BlackMetal", "Eitr", "YggdrasilWood", "ScaleHide", "Carapace", "Mandible", "Wisp", "Softtissue", "BlackCore", "MoltenCore", "BlackMarble", "Bilebag", "SurtlingCore" }; private static readonly HashSet BulkMaterials = new HashSet { "SurtlingCore", "Iron", "Silver", "BlackMetal", "Grausten", "BlackMarble", "YggdrasilWood", "LinenThread", "Sulfur", "Flametal", "FlametalOre", "Carapace", "ScaleHide", "AskHide" }; private static readonly string[] PoolDeep = new string[14] { "GemstoneRed", "GemstoneBlue", "GemstoneGreen", "Flametal", "FlametalOre", "AskHide", "Grausten", "Sulfur", "ProustitePowder", "CelestialFeather", "VoltureEgg", "BonemawSerpentScale", "MorgenHeart", "Jade" }; private const int CoinsPerRollMin = 40; private const int CoinsPerRollMax = 220; public static void PopulateChest(Container container, DifficultyLevel diff, int tier, float depth01, System.Random rnd) { if (container == null || container.GetInventory() == null) { return; } if (rnd == null) { rnd = new System.Random(); } depth01 = Mathf.Clamp01(depth01); int multiplier = GetMultiplier(diff); int num = 2 + tier + Mathf.RoundToInt(depth01 * 3f); List list = BuildPool(tier, depth01); if (list.Count == 0) { return; } HashSet hashSet = new HashSet(); for (int i = 0; i < num; i++) { string text = null; for (int j = 0; j < 8; j++) { string text2 = list[rnd.Next(list.Count)]; if (hashSet.Add(text2)) { text = text2; break; } } if (text != null) { if (text == "Coins") { int num2 = rnd.Next(40, 221) * multiplier; num2 = Mathf.RoundToInt((float)num2 * Mathf.Lerp(1f, 3f, depth01)); AddItemSafe(container, "Coins", num2); } else { AddItemSafe(container, text, Quantity(rnd, multiplier, depth01, BulkMaterials.Contains(text))); } } } if (tier >= 3) { AddItemSafe(container, "Avalor_RecallRune", 1); } else if (rnd.NextDouble() < 0.1) { AddItemSafe(container, "Avalor_RecallRune", 1); } } private static List BuildPool(int tier, float depth01) { List list = new List(PoolCommon); if (tier >= 2 || depth01 > 0.25f) { list.AddRange(PoolMid); if (depth01 > 0.55f) { list.AddRange(PoolMid); } } if (tier >= 3 || depth01 > 0.6f) { list.AddRange(PoolDeep); if (depth01 > 0.8f) { list.AddRange(PoolDeep); } } return list; } private static int Quantity(System.Random rnd, int multi, float depth01, bool bulk) { float f = (float)((bulk ? rnd.Next(8, 25) : ((rnd.NextDouble() < 0.2) ? rnd.Next(1, 3) : rnd.Next(3, 6))) * multi) * Mathf.Lerp(1f, 2f, depth01); return Mathf.Max(1, Mathf.RoundToInt(f)); } public static void PopulateTier1Chest(Container container, DifficultyLevel diff) { PopulateChest(container, diff, 1, 0.15f, null); } public static void PopulateTier2Chest(Container container, DifficultyLevel diff) { PopulateChest(container, diff, 2, 0.5f, null); } public static void PopulateTier3Chest(Container container, DifficultyLevel diff) { PopulateChest(container, diff, 3, 0.85f, null); } public static bool AddGuaranteed(Container container, string prefabName, int amount = 1) { if (container == null || container.GetInventory() == null) { return false; } if (ZNetScene.instance?.GetPrefab(prefabName) == null) { return false; } AddItemSafe(container, prefabName, amount); return true; } private static int GetMultiplier(DifficultyLevel diff) { return diff switch { DifficultyLevel.Normal => 2, DifficultyLevel.Hardcore => 3, _ => 1, }; } private static void AddItemSafe(Container container, string prefabName, int amount) { if (amount <= 0) { return; } GameObject gameObject = ZNetScene.instance?.GetPrefab(prefabName); if (gameObject == null) { return; } ItemDrop component = gameObject.GetComponent(); if (component == null) { return; } ItemDrop.ItemData itemData = component.m_itemData.Clone(); int b = Mathf.Max(1, itemData.m_shared.m_maxStackSize); int num = amount; while (num > 0) { ItemDrop.ItemData itemData2 = component.m_itemData.Clone(); itemData2.m_stack = Mathf.Min(num, b); if (container.GetInventory().AddItem(itemData2)) { num -= itemData2.m_stack; continue; } break; } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorWeapons ---- namespace MistsofAvalor { public static class AvalorWeapons { public const string GreatswordPrefab = "Avalor_Greatsword"; public const string ShortswordPrefab = "Avalor_Shortsword"; private const string GreatswordDonor = "THSwordSlayerNature"; private const string ShortswordDonor = "SwordNiedhoggNature"; private const string GreatswordModule = "greatsword"; private const string ShortswordModule = "shortsword"; private const float GreatswordTipSign = 1f; private const float ShortswordTipSign = -1f; private const float GreatswordGripShift = 0f; private const float ShortswordGripShift = 0f; public const float BonusHealth = 100f; public const float BonusStamina = 100f; public static void RegisterLocalization() { try { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); string text = "item_avalor_greatsword"; localization.AddTranslation(ref text, "Avalorian Greatsword"); text = "item_avalor_greatsword_desc"; localization.AddTranslation(ref text, "Raised from the deeps of the labyrinth for the one who walked back out of it. Green fire still runs the runes, and the skulls of the hilt keep their own counsel.\n\nVigour of the Conqueror\n+" + Mathf.RoundToInt(100f) + " maximum health\n+" + Mathf.RoundToInt(100f) + " maximum stamina\nWhile the greatsword is wielded.\n\nCannot be forged. Repaired at a forge."); text = "enemy_avalor_revenant"; localization.AddTranslation(ref text, "Avalorian Revenant"); text = "enemy_avalor_warden"; localization.AddTranslation(ref text, "The Avalorian Warden"); text = "item_avalor_shortsword"; localization.AddTranslation(ref text, "Avalorian Shortsword"); text = "item_avalor_shortsword_desc"; localization.AddTranslation(ref text, "A wight's blade, quick and quiet, carried out of the mists by something that should not have had it. It drinks the dark and gives back a little green light.\n\nCannot be forged. Repaired at a forge."); Logger.LogInfo((object)"[Avalor] blade localization registered."); } catch (Exception ex) { Logger.LogError((object)("[Avalor] RegisterLocalization failed: " + ex)); } } public static void Register() { CreateGreatsword(); CreateShortsword(); } private static void CreateGreatsword() { //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_Greatsword", "THSwordSlayerNature"); if (gameObject == null) { Logger.LogWarning((object)"[Avalor] donor 'THSwordSlayerNature' missing - greatsword not created."); return; } ItemDrop component = gameObject.GetComponent(); if (component == null) { Logger.LogWarning((object)"[Avalor] greatsword donor has no ItemDrop."); return; } ItemDrop.ItemData.SharedData shared = component.m_itemData.m_shared; shared.m_name = "$item_avalor_greatsword"; shared.m_description = "$item_avalor_greatsword_desc"; ScaleDamage(ref shared.m_damages, 1.12f); ScaleDamage(ref shared.m_damagesPerLevel, 1.12f); ElementalToSpirit(ref shared.m_damages, 2.76f); ElementalToSpirit(ref shared.m_damagesPerLevel, 2.76f); shared.m_attackForce *= 1.2f; shared.m_maxDurability *= 1.25f; shared.m_durabilityPerLevel *= 1.25f; shared.m_maxQuality = 4; DressWeaponWithAuthored(gameObject, "greatsword", 1f, 0f); TintWeaponGreen(gameObject); ItemManager.Instance.AddItem(new CustomItem(gameObject, false)); AddRepairOnlyRecipe("Avalor_Greatsword", component); Logger.LogInfo((object)"[Avalor] Avalor_Greatsword registered (donor THSwordSlayerNature)."); } catch (Exception ex) { Logger.LogError((object)("[Avalor] CreateGreatsword failed: " + ex)); } } private static void CreateShortsword() { //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_Shortsword", "SwordNiedhoggNature"); if (gameObject == null) { Logger.LogWarning((object)"[Avalor] donor 'SwordNiedhoggNature' missing - shortsword not created."); return; } ItemDrop component = gameObject.GetComponent(); if (component == null) { Logger.LogWarning((object)"[Avalor] shortsword donor has no ItemDrop."); return; } ItemDrop.ItemData.SharedData shared = component.m_itemData.m_shared; shared.m_name = "$item_avalor_shortsword"; shared.m_description = "$item_avalor_shortsword_desc"; ScaleDamage(ref shared.m_damages, 0.96f); ScaleDamage(ref shared.m_damagesPerLevel, 0.96f); ElementalToSpirit(ref shared.m_damages, 2.04f); ElementalToSpirit(ref shared.m_damagesPerLevel, 2.04f); if (shared.m_attack != null) { shared.m_attack.m_attackStamina *= 0.85f; } if (shared.m_secondaryAttack != null) { shared.m_secondaryAttack.m_attackStamina *= 0.85f; } shared.m_backstabBonus *= 1.5f; shared.m_maxQuality = 3; DressWeaponWithAuthored(gameObject, "shortsword", -1f, 0f); TintWeaponGreen(gameObject); ItemManager.Instance.AddItem(new CustomItem(gameObject, false)); AddRepairOnlyRecipe("Avalor_Shortsword", component); Logger.LogInfo((object)"[Avalor] Avalor_Shortsword registered (donor SwordNiedhoggNature)."); } catch (Exception ex) { Logger.LogError((object)("[Avalor] CreateShortsword failed: " + ex)); } } private static void AddRepairOnlyRecipe(string prefabName, ItemDrop drop) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown try { GameObject prefab = PrefabManager.Instance.GetPrefab("forge"); CraftingStation craftingStation = ((prefab != null) ? prefab.GetComponent() : null); if (craftingStation == null) { Logger.LogWarning((object)("[Avalor] forge station not found - " + prefabName + " will not be repairable.")); return; } Recipe recipe = ScriptableObject.CreateInstance(); recipe.name = "Recipe_" + prefabName; recipe.m_item = drop; recipe.m_amount = 1; recipe.m_enabled = true; recipe.m_craftingStation = craftingStation; recipe.m_repairStation = craftingStation; recipe.m_minStationLevel = 1; recipe.m_resources = new Piece.Requirement[1] { new Piece.Requirement { m_resItem = drop, m_amount = 1, m_amountPerLevel = 1, m_recover = false } }; ItemManager.Instance.AddRecipe(new CustomRecipe(recipe, false, false)); } catch (Exception ex) { Logger.LogError((object)("[Avalor] AddRepairOnlyRecipe(" + prefabName + ") failed: " + ex)); } } public static bool IsAvalorBlade(string prefabName) { if (!(prefabName == "Avalor_Greatsword")) { return prefabName == "Avalor_Shortsword"; } return true; } private static void ScaleDamage(ref HitData.DamageTypes d, float f) { d.m_damage *= f; d.m_slash *= f; d.m_pierce *= f; d.m_blunt *= f; } private static void ElementalToSpirit(ref HitData.DamageTypes d, float f) { float num = d.m_fire + d.m_frost + d.m_lightning + d.m_poison; if (!(num <= 0f)) { d.m_fire = 0f; d.m_frost = 0f; d.m_lightning = 0f; d.m_poison = 0f; d.m_spirit += num * f; } } private static void DressWeaponWithAuthored(GameObject weapon, string module, float tipSignFallback, float gripShift) { if (weapon == null || !AvalorKitBundle.Available) { return; } Mesh mesh = AvalorKitBundle.GetMesh(module); if (mesh == null) { return; } Material material = AvalorKitBundle.GetMaterial(module); if (material == null) { return; } Dictionary dictionary = new Dictionary(); MeshFilter[] componentsInChildren = weapon.GetComponentsInChildren(includeInactive: true); foreach (MeshFilter meshFilter in componentsInChildren) { if (!(meshFilter.sharedMesh == null)) { Transform transform = meshFilter.transform.parent; if (transform == null || !transform.IsChildOf(weapon.transform)) { transform = weapon.transform; } if (!dictionary.TryGetValue(transform, out var value) || meshFilter.sharedMesh.bounds.size.magnitude > value.sharedMesh.bounds.size.magnitude) { dictionary[transform] = meshFilter; } } } if (dictionary.Count == 0) { return; } Bounds bounds = mesh.bounds; int num = LongestAxis(bounds.size); float num2 = MeasureTipSign(mesh, num, tipSignFallback); Vector3 vector = AxisVector(num) * num2; Vector3 upwards = AxisVector(SecondAxis(bounds.size, num)); Vector3 vector2 = bounds.center - AxisVector(num) * (bounds.extents[num] * num2); MeshRenderer[] componentsInChildren2 = weapon.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren2.Length; i++) { componentsInChildren2[i].enabled = false; } foreach (KeyValuePair item in dictionary) { Transform key = item.Key; MeshFilter value2 = item.Value; Matrix4x4 matrix4x = key.worldToLocalMatrix * value2.transform.localToWorldMatrix; Bounds bounds2 = value2.sharedMesh.bounds; int num3 = LongestAxis(bounds2.size); Vector3 vector3 = matrix4x.MultiplyPoint3x4(bounds2.center); Vector3 vector4 = matrix4x.MultiplyVector(AxisVector(num3) * bounds2.extents[num3]); Vector3 vector5 = vector3 + vector4; Vector3 vector6 = vector3 - vector4; bool num4 = vector5.sqrMagnitude <= vector6.sqrMagnitude; Vector3 vector7 = (num4 ? vector5 : vector6); Vector3 vector8 = (num4 ? vector6 : vector5) - vector7; if (!(vector8.sqrMagnitude < 1E-08f)) { Vector3 normalized = vector8.normalized; int i2 = SecondAxis(bounds2.size, num3); Vector3 vector9 = Vector3.ProjectOnPlane(matrix4x.MultiplyVector(AxisVector(i2)), normalized); float num5 = vector8.magnitude / Mathf.Max(0.001f, bounds.size[num]); Quaternion quaternion = ((!(vector9.sqrMagnitude > 1E-06f)) ? Quaternion.FromToRotation(vector, normalized) : (Quaternion.LookRotation(normalized, vector9.normalized) * Quaternion.Inverse(Quaternion.LookRotation(vector, upwards)))); GameObject gameObject = new GameObject("AvalorAuthoredBlade"); gameObject.transform.SetParent(key, worldPositionStays: false); gameObject.transform.localRotation = quaternion; gameObject.transform.localScale = Vector3.one * num5; gameObject.transform.localPosition = vector7 - quaternion * (vector2 * num5) + normalized * gripShift; gameObject.AddComponent().sharedMesh = mesh; gameObject.AddComponent().sharedMaterial = material; } } if (MistsofAvalorPlugin.DebugLogging == null || MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)($"[Avalor] authored blade '{module}' fitted to {dictionary.Count} host(s): " + string.Format("long axis {0}, tip sign {1:+0;-0} ", "XYZ"[num], num2) + "(" + (mesh.isReadable ? "measured from vertices" : "from the model render") + ").")); } } private static float MeasureTipSign(Mesh mesh, int longAxis, float fallback) { try { if (!mesh.isReadable) { return fallback; } Vector3[] vertices = mesh.vertices; if (vertices == null || vertices.Length < 32) { return fallback; } Vector3 center = mesh.bounds.center; OtherAxes(longAxis, out var a, out var b); double num = 0.0; double num2 = 0.0; int num3 = 0; int num4 = 0; for (int i = 0; i < vertices.Length; i++) { float num5 = vertices[i][a] - center[a]; float num6 = vertices[i][b] - center[b]; float num7 = Mathf.Sqrt(num5 * num5 + num6 * num6); if (vertices[i][longAxis] >= center[longAxis]) { num += (double)num7; num3++; } else { num2 += (double)num7; num4++; } } if (num3 == 0 || num4 == 0) { return fallback; } double num8 = num / (double)num3; double num9 = num2 / (double)num4; if (Mathf.Abs((float)(num8 - num9)) < 0.02f * mesh.bounds.size[longAxis]) { return fallback; } return (num8 > num9) ? (-1f) : 1f; } catch { return fallback; } } private static void TintWeaponGreen(GameObject weapon) { Color avalorGreen = AvalorCryptEnvZone.AvalorGreen; LightFlicker[] componentsInChildren = weapon.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren[i]); } LightLod[] componentsInChildren2 = weapon.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren2.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren2[i]); } Light[] componentsInChildren3 = weapon.GetComponentsInChildren(includeInactive: true); foreach (Light obj in componentsInChildren3) { obj.color = avalorGreen; obj.enabled = true; } ParticleSystem[] componentsInChildren4 = weapon.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren4.Length; i++) { ParticleSystem.MainModule main = componentsInChildren4[i].main; ParticleSystem.MinMaxGradient startColor = main.startColor; if (startColor.mode == ParticleSystemGradientMode.TwoColors) { main.startColor = new ParticleSystem.MinMaxGradient(Recolor(startColor.colorMin, avalorGreen), Recolor(startColor.colorMax, avalorGreen)); } else { main.startColor = Recolor(startColor.color, avalorGreen); } } } private static Color Recolor(Color src, Color green) { float num = Mathf.Max(0.15f, src.grayscale); Color result = green * num; result.a = src.a; return result; } private static int LongestAxis(Vector3 s) { if (!(s.x >= s.y) || !(s.x >= s.z)) { if (!(s.y >= s.z)) { return 2; } return 1; } return 0; } private static void OtherAxes(int main, out int a, out int b) { a = (main + 1) % 3; b = (main + 2) % 3; } private static int SecondAxis(Vector3 s, int main) { OtherAxes(main, out var a, out var b); if (!(s[a] >= s[b])) { return b; } return a; } private static Vector3 AxisVector(int i) { return i switch { 1 => Vector3.up, 0 => Vector3.right, _ => Vector3.forward, }; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.Avalor_HideBladeRecipes_Patch ---- namespace MistsofAvalor { [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] public static class Avalor_HideBladeRecipes_Patch { public static void Prefix(List recipes) { try { recipes?.RemoveAll((Recipe r) => r != null && r.m_item != null && AvalorWeapons.IsAvalorBlade(r.m_item.gameObject.name)); } catch (Exception ex) { Logger.LogError((object)("[Avalor] blade recipe hide patch threw: " + ex.Message)); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.Avalor_GreatswordVitality_Patch ---- namespace MistsofAvalor { [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] public static class Avalor_GreatswordVitality_Patch { public static void Postfix(Player __instance, ref float hp, ref float stamina) { try { if (!(__instance == null)) { ItemDrop.ItemData currentWeapon = __instance.GetCurrentWeapon(); if (currentWeapon != null && !(currentWeapon.m_dropPrefab == null) && !(currentWeapon.m_dropPrefab.name != "Avalor_Greatsword")) { hp += 100f; stamina += 100f; } } } catch (Exception ex) { Logger.LogError((object)("[Avalor] greatsword vitality patch threw (stats unchanged this tick): " + ex.Message)); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorMazeChunkRenderer ---- namespace MistsofAvalor { public class AvalorMazeChunkRenderer : MonoBehaviour { public const float CellSize = 6f; public const float FloorHeight = 4f; private static int s_activeBuilds; private const int MaxConcurrentBuilds = 4; private bool _holdingSlot; private void Start() { if (!AvalorNet.IsHeadless) { StartCoroutine(BuildRoutine()); } } private IEnumerator BuildRoutine() { ZNetView component = GetComponent(); if (component == null || !component.IsValid() || component.GetZDO() == null) { yield break; } byte[] byteArray = component.GetZDO().GetByteArray("Avalor_ChunkData"); if (byteArray == null || byteArray.Length == 0) { yield break; } int num = component.GetZDO().GetInt("Avalor_ChunkWidth", 5); int num2 = component.GetZDO().GetInt("Avalor_ChunkDepth", 5); int num3 = component.GetZDO().GetInt("Avalor_ChunkFloors", 3); GameObject[] wallPrefabs = new GameObject[3] { ZNetScene.instance.GetPrefab("Avalor_CompositeWall"), ZNetScene.instance.GetPrefab("Avalor_CompositeWallB"), ZNetScene.instance.GetPrefab("Avalor_CompositeWallC") }; GameObject[] floorPrefabs = new GameObject[3] { ZNetScene.instance.GetPrefab("Avalor_CompositeFloor"), ZNetScene.instance.GetPrefab("Avalor_CompositeFloorB"), ZNetScene.instance.GetPrefab("Avalor_CompositeFloorC") }; GameObject ceilPrefab = ZNetScene.instance.GetPrefab("Avalor_CompositeCeiling"); GameObject stairPrefab = ZNetScene.instance.GetPrefab("Avalor_CompositeStairTile"); List[] wallMats = new List[3] { new List(), new List(), new List() }; List[] floorMats = new List[3] { new List(), new List(), new List() }; List ceilMats = new List(); List stairMats = new List(); HashSet seen = new HashSet(); float num4 = 3f; int num5 = 0; for (int i = 0; i < num3; i++) { for (int j = 0; j < num; j++) { for (int k = 0; k < num2; k++) { if (num5 >= byteArray.Length) { break; } byte num6 = byteArray[num5++]; bool flag = (num6 & 1) != 0; bool flag2 = (num6 & 2) != 0; bool flag3 = (num6 & 4) != 0; bool flag4 = (num6 & 8) != 0; bool num7 = (num6 & 0x40) != 0; float x = ((float)j - (float)(num - 1) / 2f) * 6f; float z = ((float)k - (float)(num2 - 1) / 2f) * 6f; Vector3 vector = new Vector3(x, (float)i * 4f, z); if (flag4) { AddWall(wallMats, seen, vector + new Vector3(num4, 0f, 0f), 90); } if (flag) { AddWall(wallMats, seen, vector + new Vector3(0f, 0f, num4), 0); } if (flag2) { AddWall(wallMats, seen, vector + new Vector3(0f, 0f, 0f - num4), 0); } if (flag3) { AddWall(wallMats, seen, vector + new Vector3(0f - num4, 0f, 0f), 270); } bool num8 = !num7 || i == 0; int num9 = Variant(((long)Mathf.RoundToInt(vector.x * 100f) * 73856093L) ^ ((long)Mathf.RoundToInt(vector.y * 100f) * 19349663L) ^ ((long)Mathf.RoundToInt(vector.z * 100f) * 83492791L), floorMats.Length); if (num8) { floorMats[num9].Add(Matrix4x4.TRS(vector, Quaternion.identity, Vector3.one)); } if (num8 && i > 0) { ceilMats.Add(Matrix4x4.TRS(vector, Quaternion.identity, Vector3.one)); } if (i == num3 - 1) { Vector3 pos = vector + new Vector3(0f, 4f, 0f); floorMats[num9].Add(Matrix4x4.TRS(pos, Quaternion.identity, Vector3.one)); ceilMats.Add(Matrix4x4.TRS(pos, Quaternion.identity, Vector3.one)); } if (!num7) { continue; } float num10 = Mathf.Atan2(4f, 6f) * 57.29578f; float z2 = Mathf.Sqrt(52f) / 6f; float y = 0f; if (i > 0) { int num11 = ((i - 1) * num + j) * num2 + k; if (num11 >= 0 && num11 < byteArray.Length) { byte b = byteArray[num11]; if ((b & 2) == 0) { y = 0f; } else if ((b & 1) == 0) { y = 180f; } else if ((b & 4) == 0) { y = 90f; } else if ((b & 8) == 0) { y = -90f; } } } if (stairPrefab != null) { stairMats.Add(Matrix4x4.TRS(vector + new Vector3(0f, -4f, 0f), Quaternion.Euler(0f, y, 0f), Vector3.one)); } else { floorMats[num9].Add(Matrix4x4.TRS(vector + new Vector3(0f, -2f, 0f), Quaternion.Euler(0f, y, 0f) * Quaternion.Euler(0f - num10, 0f, 0f), new Vector3(1f, 1f, z2))); } } } } float waitStart = Time.realtimeSinceStartup; while (s_activeBuilds >= 4 && Time.realtimeSinceStartup - waitStart < 5f) { yield return null; } s_activeBuilds++; _holdingSlot = true; try { for (int v = 0; v < wallPrefabs.Length; v++) { if (!(wallPrefabs[v] == null) && wallMats[v].Count != 0) { Mesh wm = null; MeshCollider wc = null; yield return AvalorAssetManager.CombineTileMeshesThreaded(wallPrefabs[v], base.gameObject, wallMats[v], delegate(Mesh m, MeshCollider c) { wm = m; wc = c; }); yield return BakeAndAssign(wm, wc); } } for (int v = 0; v < floorPrefabs.Length; v++) { if (!(floorPrefabs[v] == null) && floorMats[v].Count != 0) { Mesh fm = null; MeshCollider fc = null; yield return AvalorAssetManager.CombineTileMeshesThreaded(floorPrefabs[v], base.gameObject, floorMats[v], delegate(Mesh m, MeshCollider c) { fm = m; fc = c; }); yield return BakeAndAssign(fm, fc); } } if (stairPrefab != null && stairMats.Count > 0) { Mesh sm = null; MeshCollider sc = null; yield return AvalorAssetManager.CombineTileMeshesThreaded(stairPrefab, base.gameObject, stairMats, delegate(Mesh m, MeshCollider c) { sm = m; sc = c; }); yield return BakeAndAssign(sm, sc); } if (ceilPrefab != null && ceilMats.Count > 0) { Mesh cm = null; MeshCollider cc = null; yield return AvalorAssetManager.CombineTileMeshesThreaded(ceilPrefab, base.gameObject, ceilMats, delegate(Mesh m, MeshCollider c) { cm = m; cc = c; }); yield return BakeAndAssign(cm, cc); } } finally { AvalorMazeChunkRenderer avalorMazeChunkRenderer = this; if (avalorMazeChunkRenderer._holdingSlot) { s_activeBuilds--; avalorMazeChunkRenderer._holdingSlot = false; } } } private static void AddWall(List[] mats, HashSet seen, Vector3 pos, int yaw) { long num = ((long)Mathf.RoundToInt(pos.x * 100f) * 73856093L) ^ ((long)Mathf.RoundToInt(pos.y * 100f) * 19349663L) ^ ((long)Mathf.RoundToInt(pos.z * 100f) * 83492791L) ^ (yaw % 180); if (seen.Add(num)) { mats[Variant(num, mats.Length)].Add(Matrix4x4.TRS(pos, Quaternion.Euler(0f, yaw, 0f), Vector3.one)); } } private static int Variant(long key, int count) { if (count <= 1) { return 0; } long num = key * -7046029254386353131L; return (int)((ulong)(num ^ (num >>> 29)) % (ulong)count); } private void OnDestroy() { if (_holdingSlot) { s_activeBuilds--; _holdingSlot = false; } } private IEnumerator BakeAndAssign(Mesh mesh, MeshCollider collider) { if (mesh == null || collider == null) { yield break; } int meshId = mesh.GetInstanceID(); bool baked = false; bool flag; try { ThreadPool.QueueUserWorkItem(delegate { try { Physics.BakeMesh(meshId, convex: false); } catch { } baked = true; }); flag = true; } catch { flag = false; } if (flag) { float guard = 0f; while (!baked && guard < 5f) { guard += Time.deltaTime; yield return null; } } if (collider != null) { collider.sharedMesh = mesh; } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorMazeGenerator ---- namespace MistsofAvalor { public class AvalorMazeGenerator : MonoBehaviour { private struct MazeCell { public bool visited = false; public bool wallTop = true; public bool wallBottom = true; public bool wallRight = true; public bool wallLeft = true; public bool wallCeiling = true; public bool wallFloor = true; public int distance = 0; public MazeCell() { } } public static readonly Vector3 MazeCenter = new Vector3(30000f, 500f, 30000f); public const float SkyWipeRadius = 2000f; private static readonly Quaternion ClutterTurn = Quaternion.Euler(0f, 90f, 0f); public List spawnedZDOs = new List(); private static FieldInfo s_zdoDictField; private const float FloorSurface = 0.1f; private const float ChestSeat = 0.02f; private const float WallDecorDrop = 0.55f; private float mazeSpan; public static AvalorMazeGenerator Instance { get; private set; } public bool IsGenerating { get; private set; } public static Vector3 StartLocation { get; private set; } internal static Dictionary GetZDODictionary() { if (ZDOMan.instance == null) { return null; } try { if (s_zdoDictField == null) { s_zdoDictField = typeof(ZDOMan).GetField("m_objectsByID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } return s_zdoDictField?.GetValue(ZDOMan.instance) as Dictionary; } catch (Exception ex) { Logger.LogError((object)("Avalor: ZDOMan reflection failed: " + ex.Message)); return null; } } public static bool MazeExistsInWorld() { if (ZDOMan.instance == null || ZoneSystem.instance == null) { return false; } if (Instance != null && Instance.spawnedZDOs.Count > 0) { return true; } Dictionary zDODictionary = GetZDODictionary(); if (zDODictionary != null) { foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value != null && value.GetPosition().x > 25000f && value.GetInt("AvalorMazePiece") == 1) { return true; } } } return false; } public static bool TryGetGraveyardPortal(out Vector3 pos) { pos = Vector3.zero; Dictionary zDODictionary = GetZDODictionary(); if (zDODictionary == null) { return false; } int stableHashCode = "Avalor_OverworldPortal".GetStableHashCode(); Vector3 pos2 = Vector3.zero; bool flag = ZoneSystem.instance != null && ZoneSystem.instance.GetLocationIcon("StartTemple", out pos2); float num = float.MaxValue; bool flag2 = false; foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value == null || value.GetPrefab() != stableHashCode) { continue; } Vector3 position = value.GetPosition(); if (position.x >= 25000f) { continue; } float num2 = (flag ? Vector3.Distance(position, pos2) : 0f); if (!flag2 || num2 < num) { num = num2; pos = position; flag2 = true; if (!flag) { break; } } } return flag2; } public void RecoverSpawnedZDOs() { if (ZDOMan.instance == null || ZoneSystem.instance == null || spawnedZDOs.Count > 0) { return; } Dictionary zDODictionary = GetZDODictionary(); if (zDODictionary != null) { foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value != null && value.GetPosition().x > 25000f && value.GetInt("AvalorMazePiece") == 1) { spawnedZDOs.Add(value.m_uid); } } } if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)("Avalor Labyrinth: Recovered " + spawnedZDOs.Count + " ZDOs from memory.")); } if (StartLocation == Vector3.zero) { StartLocation = ComputeStartLocation(); } } public static Vector3 ComputeStartLocation() { int num = 40; int num2 = 40; if (MistsofAvalorPlugin.ComplexityConfig.Value == MazeComplexity.Normal) { num = 20; num2 = 20; } else if (MistsofAvalorPlugin.ComplexityConfig.Value == MazeComplexity.Extreme) { num = 60; num2 = 60; } float num3 = 6f; float x = (0f - (float)num / 2f) * num3; float z = (0f - (float)num2 / 2f) * num3; return MazeCenter + new Vector3(x, 0.5f, z) + new Vector3(0f, 1f, 0f); } private void Awake() { Instance = this; } public IEnumerator GenerateMaze(Action onStartAreaReady = null) { Vector3 center = MazeCenter; IsGenerating = true; AvalorMazeGrid.Invalidate(); bool dbg = MistsofAvalorPlugin.DebugLogging.Value; float genStartTime = Time.realtimeSinceStartup; if (dbg) { Vector3 vector = center; Logger.LogInfo((object)("[Avalor] Generating Labyrinth at " + vector.ToString())); } StartLocation = ComputeStartLocation(); onStartAreaReady?.Invoke(); List toWipe = new List(); int stableHashCode = "Player".GetStableHashCode(); int stableHashCode2 = "Player_tombstone".GetStableHashCode(); Dictionary zDODictionary = GetZDODictionary(); if (zDODictionary != null) { foreach (KeyValuePair item2 in zDODictionary) { ZDO value = item2.Value; if (value == null) { continue; } Vector3 position = value.GetPosition(); if (!(position.x <= 25000f) && !(Vector3.Distance(position, center) > 2000f)) { int prefab = value.GetPrefab(); if (prefab != stableHashCode && prefab != stableHashCode2) { toWipe.Add(value); } } } } else { foreach (ZDOID spawnedZDO in spawnedZDOs) { if (!(spawnedZDO == ZDOID.None)) { ZDO zDO = ZDOMan.instance.GetZDO(spawnedZDO); if (zDO != null) { toWipe.Add(zDO); } } } } int destroyCount = 0; long session = ZDOMan.GetSessionID(); foreach (ZDO item3 in toWipe) { if (item3 == null) { continue; } ZNetView zNetView = ZNetScene.instance.FindInstance(item3); if (zNetView != null) { zNetView.ClaimOwnership(); ZNetScene.instance.Destroy(zNetView.gameObject); } else { if (!item3.IsOwner()) { item3.SetOwner(session); } ZDOMan.instance.DestroyZDO(item3); } destroyCount++; if (destroyCount >= 50) { destroyCount = 0; yield return null; } } spawnedZDOs.Clear(); if (dbg) { Logger.LogInfo((object)$"[Avalor] Sky-region wipe removed {toWipe.Count} old ZDO(s) before rebuild."); } int width = 40; int depth = 40; int floors = 2; int num = 15; if (MistsofAvalorPlugin.ComplexityConfig.Value == MazeComplexity.Normal) { width = 20; depth = 20; floors = 1; num = 5; } else if (MistsofAvalorPlugin.ComplexityConfig.Value == MazeComplexity.Extreme) { width = 60; depth = 60; floors = 3; num = 30; } if (dbg) { Logger.LogInfo((object)($"[Avalor] Config: complexity={MistsofAvalorPlugin.ComplexityConfig.Value} " + $"size={width}x{depth}x{floors} arenas={num} difficulty={MistsofAvalorPlugin.DifficultyConfig.Value} " + $"clutter={MistsofAvalorPlugin.ClutterConfig.Value} lighting={MistsofAvalorPlugin.LightingConfig.Value} mist={MistsofAvalorPlugin.MistConfig.Value}")); } float cellSize = 6f; float floorHeight = 4f; mazeSpan = new Vector2((float)width * cellSize, (float)depth * cellSize).magnitude; float x = (0f - (float)width / 2f) * cellSize; float z = (0f - (float)depth / 2f) * cellSize; Vector3 vector2 = center + new Vector3(x, 0f, z); StartLocation = vector2 + new Vector3(0f, 1.5f, 0f); GameObject gameObject = ZNetScene.instance?.GetPrefab("Avalor_MegaFloor"); if (gameObject != null) { InstantiatePiece(gameObject, vector2, Quaternion.identity); } GameObject gameObject2 = ZNetScene.instance?.GetPrefab("Avalor_StartDais"); if (gameObject2 != null) { InstantiatePiece(gameObject2, vector2, Quaternion.identity); } if (dbg) { Logger.LogInfo((object)$"[Avalor] Entrance platform placed at {StartLocation}."); } MazeCell[,,] grid = new MazeCell[width, floors, depth]; for (int i = 0; i < width; i++) { for (int j = 0; j < floors; j++) { for (int k = 0; k < depth; k++) { grid[i, j, k] = new MazeCell(); } } } System.Random rnd = new System.Random(); Stack stack = new Stack(); Vector3Int item = new Vector3Int(0, 0, 0); grid[item.x, item.y, item.z].visited = true; stack.Push(item); while (stack.Count > 0) { item = stack.Pop(); List unvisitedNeighbors = GetUnvisitedNeighbors(item, grid, width, floors, depth, rnd); if (unvisitedNeighbors.Count > 0) { stack.Push(item); Vector3Int vector3Int = unvisitedNeighbors[rnd.Next(unvisitedNeighbors.Count)]; RemoveWall(item, vector3Int, grid); grid[vector3Int.x, vector3Int.y, vector3Int.z].visited = true; stack.Push(vector3Int); } } for (int l = 1; l < width - 1; l++) { for (int m = 0; m < floors; m++) { for (int n = 1; n < depth - 1; n++) { int num2 = 0; if (grid[l, m, n].wallTop) { num2++; } if (grid[l, m, n].wallBottom) { num2++; } if (grid[l, m, n].wallLeft) { num2++; } if (grid[l, m, n].wallRight) { num2++; } if (num2 >= 3 && rnd.NextDouble() < 0.3499999940395355) { List list = new List(); if (grid[l, m, n].wallTop && n < depth - 1) { list.Add(new Vector3Int(l, m, n + 1)); } if (grid[l, m, n].wallBottom && n > 0) { list.Add(new Vector3Int(l, m, n - 1)); } if (grid[l, m, n].wallLeft && l > 0) { list.Add(new Vector3Int(l - 1, m, n)); } if (grid[l, m, n].wallRight && l < width - 1) { list.Add(new Vector3Int(l + 1, m, n)); } if (list.Count > 0) { Vector3Int chosen = list[rnd.Next(list.Count)]; RemoveWall(new Vector3Int(l, m, n), chosen, grid); } } } } } for (int num3 = 0; num3 < num; num3++) { int num4 = rnd.Next(1, width - 4); int num5 = rnd.Next(0, floors); int num6 = rnd.Next(1, depth - 4); int num7 = rnd.Next(2, 5); for (int num8 = num4; num8 < num4 + num7; num8++) { for (int num9 = num6; num9 < num6 + num7; num9++) { if (num8 < num4 + num7 - 1) { grid[num8, num5, num9].wallRight = false; grid[num8 + 1, num5, num9].wallLeft = false; } if (num8 > num4) { grid[num8, num5, num9].wallLeft = false; grid[num8 - 1, num5, num9].wallRight = false; } if (num9 < num6 + num7 - 1) { grid[num8, num5, num9].wallTop = false; grid[num8, num5, num9 + 1].wallBottom = false; } if (num9 > num6) { grid[num8, num5, num9].wallBottom = false; grid[num8, num5, num9 - 1].wallTop = false; } } } float x2 = ((float)num4 + (float)(num7 - 1) / 2f - (float)width / 2f) * cellSize; float z2 = ((float)num6 + (float)(num7 - 1) / 2f - (float)depth / 2f) * cellSize; Vector3 vector3 = center + new Vector3(x2, (float)num5 * floorHeight, z2); GameObject gameObject3 = ZNetScene.instance?.GetPrefab("Avalor_Chest"); string[] array = new string[2] { "Spawner_DraugrPile", "BonePileSpawner" }; string text = array[rnd.Next(array.Length)]; GameObject gameObject4 = ZNetScene.instance?.GetPrefab(text); if (gameObject3 != null) { int num10 = Mathf.Min(num7 - 1, 4); float num11 = (float)num7 * 1.5f; Vector3[] array2 = new Vector3[4] { new Vector3(num11, 0f, num11), new Vector3(0f - num11, 0f, num11), new Vector3(0f - num11, 0f, 0f - num11), new Vector3(num11, 0f, 0f - num11) }; for (int num12 = 0; num12 < num10; num12++) { Vector3 position2 = vector3 + new Vector3(array2[num12].x, 0.02f, array2[num12].z); GameObject chestObj = InstantiatePiece(gameObject3, position2, Quaternion.Euler(0f, rnd.Next(0, 360), 0f)); int lootTier = ((num7 != 4 || !(rnd.NextDouble() < 0.5)) ? 1 : 2); PopulateChest(chestObj, MistsofAvalorPlugin.DifficultyConfig.Value, lootTier); } } if (gameObject4 != null && num7 >= 2 && rnd.NextDouble() < 0.55) { int num13 = 1; for (int num14 = 0; num14 < num13; num14++) { float x3 = (float)((rnd.NextDouble() > 0.5) ? 1 : (-1)) * ((float)num7 * 1f); float z3 = (float)((rnd.NextDouble() > 0.5) ? 1 : (-1)) * ((float)num7 * 1f); Vector3 position3 = vector3 + new Vector3(x3, FloorSurfaceLift(num5), z3); InstantiatePiece(gameObject4, position3, Quaternion.identity); } } } grid[0, 0, 0].wallRight = false; if (width > 1) { grid[1, 0, 0].wallLeft = false; } grid[0, 0, 0].wallTop = false; if (depth > 1) { grid[0, 0, 1].wallBottom = false; } Queue queue = new Queue(); queue.Enqueue(new Vector3Int(0, 0, 0)); Vector3Int vector3Int2 = new Vector3Int(0, 0, 0); int num15 = 0; for (int num16 = 0; num16 < width; num16++) { for (int num17 = 0; num17 < floors; num17++) { for (int num18 = 0; num18 < depth; num18++) { grid[num16, num17, num18].visited = false; } } } grid[0, 0, 0].visited = true; while (queue.Count > 0) { Vector3Int vector3Int3 = queue.Dequeue(); int distance = grid[vector3Int3.x, vector3Int3.y, vector3Int3.z].distance; if (distance > num15) { num15 = distance; vector3Int2 = vector3Int3; } if (!grid[vector3Int3.x, vector3Int3.y, vector3Int3.z].wallTop && vector3Int3.z < depth - 1 && !grid[vector3Int3.x, vector3Int3.y, vector3Int3.z + 1].visited) { grid[vector3Int3.x, vector3Int3.y, vector3Int3.z + 1].visited = true; grid[vector3Int3.x, vector3Int3.y, vector3Int3.z + 1].distance = distance + 1; queue.Enqueue(new Vector3Int(vector3Int3.x, vector3Int3.y, vector3Int3.z + 1)); } if (!grid[vector3Int3.x, vector3Int3.y, vector3Int3.z].wallBottom && vector3Int3.z > 0 && !grid[vector3Int3.x, vector3Int3.y, vector3Int3.z - 1].visited) { grid[vector3Int3.x, vector3Int3.y, vector3Int3.z - 1].visited = true; grid[vector3Int3.x, vector3Int3.y, vector3Int3.z - 1].distance = distance + 1; queue.Enqueue(new Vector3Int(vector3Int3.x, vector3Int3.y, vector3Int3.z - 1)); } if (!grid[vector3Int3.x, vector3Int3.y, vector3Int3.z].wallRight && vector3Int3.x < width - 1 && !grid[vector3Int3.x + 1, vector3Int3.y, vector3Int3.z].visited) { grid[vector3Int3.x + 1, vector3Int3.y, vector3Int3.z].visited = true; grid[vector3Int3.x + 1, vector3Int3.y, vector3Int3.z].distance = distance + 1; queue.Enqueue(new Vector3Int(vector3Int3.x + 1, vector3Int3.y, vector3Int3.z)); } if (!grid[vector3Int3.x, vector3Int3.y, vector3Int3.z].wallLeft && vector3Int3.x > 0 && !grid[vector3Int3.x - 1, vector3Int3.y, vector3Int3.z].visited) { grid[vector3Int3.x - 1, vector3Int3.y, vector3Int3.z].visited = true; grid[vector3Int3.x - 1, vector3Int3.y, vector3Int3.z].distance = distance + 1; queue.Enqueue(new Vector3Int(vector3Int3.x - 1, vector3Int3.y, vector3Int3.z)); } if (!grid[vector3Int3.x, vector3Int3.y, vector3Int3.z].wallCeiling && vector3Int3.y < floors - 1 && !grid[vector3Int3.x, vector3Int3.y + 1, vector3Int3.z].visited) { grid[vector3Int3.x, vector3Int3.y + 1, vector3Int3.z].visited = true; grid[vector3Int3.x, vector3Int3.y + 1, vector3Int3.z].distance = distance + 1; queue.Enqueue(new Vector3Int(vector3Int3.x, vector3Int3.y + 1, vector3Int3.z)); } if (!grid[vector3Int3.x, vector3Int3.y, vector3Int3.z].wallFloor && vector3Int3.y > 0 && !grid[vector3Int3.x, vector3Int3.y - 1, vector3Int3.z].visited) { grid[vector3Int3.x, vector3Int3.y - 1, vector3Int3.z].visited = true; grid[vector3Int3.x, vector3Int3.y - 1, vector3Int3.z].distance = distance + 1; queue.Enqueue(new Vector3Int(vector3Int3.x, vector3Int3.y - 1, vector3Int3.z)); } } Vector3Int exitCell = vector3Int2; Vector3Int prizeCell = ChooseAdjacentOpenCell(grid, vector3Int2, width, depth); yield return PlaceExitAndPrize(center, exitCell, prizeCell, width, depth, cellSize, floorHeight); GameObject chunkPrefab = ZNetScene.instance?.GetPrefab("Avalor_MazeChunk"); int chunkW = 4; int chunkD = 4; int num19 = Mathf.CeilToInt((float)width / (float)chunkW); int num20 = Mathf.CeilToInt((float)depth / (float)chunkD); List list2 = new List(); for (int num21 = 0; num21 < num19; num21++) { for (int num22 = 0; num22 < num20; num22++) { list2.Add(new Vector2Int(num21, num22)); } } list2.Sort((Vector2Int a, Vector2Int vector2Int) => (a.x * a.x + a.y * a.y).CompareTo(vector2Int.x * vector2Int.x + vector2Int.y * vector2Int.y)); int chunkCount = 0; int vaultCount = 0; foreach (Vector2Int item4 in list2) { int x4 = item4.x; int y = item4.y; byte[] array3 = new byte[chunkW * chunkD * floors]; int num23 = 0; for (int num24 = 0; num24 < floors; num24++) { for (int num25 = 0; num25 < chunkW; num25++) { for (int num26 = 0; num26 < chunkD; num26++) { int num27 = x4 * chunkW + num25; int num28 = y * chunkD + num26; byte b = 0; if (num27 < width && num28 < depth) { MazeCell mazeCell = grid[num27, num24, num28]; if (mazeCell.wallTop) { b |= 1; } if (mazeCell.wallBottom) { b |= 2; } if (mazeCell.wallLeft) { b |= 4; } if (mazeCell.wallRight) { b |= 8; } if (mazeCell.wallFloor) { b |= 0x10; } if (mazeCell.wallCeiling) { b |= 0x20; } if (!mazeCell.wallFloor && num24 > 0) { b |= 0x40; } } array3[num23++] = b; } } } if (chunkPrefab != null) { float x5 = ((float)(x4 * chunkW) + (float)(chunkW - 1) / 2f - (float)width / 2f) * cellSize; float z4 = ((float)(y * chunkD) + (float)(chunkD - 1) / 2f - (float)depth / 2f) * cellSize; Vector3 position4 = center + new Vector3(x5, 0f, z4); GameObject gameObject5 = InstantiatePiece(chunkPrefab, position4, Quaternion.identity); if (gameObject5 != null) { chunkCount++; ZNetView component = gameObject5.GetComponent(); if (component != null && component.GetZDO() != null) { component.GetZDO().Set("Avalor_ChunkData", array3); component.GetZDO().Set("Avalor_ChunkWidth", chunkW); component.GetZDO().Set("Avalor_ChunkDepth", chunkD); component.GetZDO().Set("Avalor_ChunkFloors", floors); } } } yield return null; } GameObject mistPrefab = ZNetScene.instance?.GetPrefab("Avalor_Mist") ?? ZNetScene.instance?.GetPrefab("vfx_swamp_mist"); GameObject torchPrefab = ZNetScene.instance?.GetPrefab("Avalor_CryptTorch") ?? ZNetScene.instance?.GetPrefab("piece_walltorch"); GameObject trapPrefab = ZNetScene.instance?.GetPrefab("Avalor_SpikeTrap"); GameObject poisonTrapPrefab = ZNetScene.instance?.GetPrefab("Avalor_PoisonTrap"); GameObject chainPrefab = ZNetScene.instance?.GetPrefab("Avalor_Clutter_Chain"); GameObject vaultGatePrefab = ZNetScene.instance?.GetPrefab("Avalor_Gate"); GameObject vaultChestPrefab = ZNetScene.instance?.GetPrefab("Avalor_VaultChest") ?? ZNetScene.instance?.GetPrefab("Avalor_Chest"); GameObject archwayPrefab = ZNetScene.instance?.GetPrefab("Avalor_Archway"); string[] array4 = new string[8] { "Avalor_Clutter_stone_pile", "Avalor_Clutter_BoneFragments", "Avalor_Clutter_Pickable_SunkenCryptRandom", "Avalor_Clutter_Pickable_ForestCryptRandom", "Avalor_Clutter_Pickable_ForestCryptRemains01", "Avalor_Clutter_Pickable_ForestCryptRemains02", "Avalor_Clutter_Pickable_ForestCryptRemains03", "Avalor_Clutter_Pickable_ForestCryptRemains04" }; string[] array5 = new string[6] { "Avalor_Clutter_dvergrprops_crate", "Avalor_Clutter_dvergrprops_barrel", "Avalor_Clutter_dvergrprops_crate_long", "Avalor_Clutter_crypt_skeleton_chest", "Avalor_Clutter_wood_stack", "Avalor_Clutter_bone_stack" }; string[] array6 = new string[7] { "Avalor_Clutter_piece_banner01", "Avalor_Clutter_piece_banner02", "Avalor_Clutter_piece_banner03", "Avalor_Clutter_piece_banner04", "Avalor_Clutter_piece_banner05", "Avalor_Clutter_dvergrprops_banner", "Avalor_Clutter_dvergrprops_hooknchain" }; string[] array7 = new string[5] { "Avalor_Clutter_ShieldWood", "Avalor_Clutter_ShieldBronzeBuckler", "Avalor_Clutter_ShieldIronBuckler", "Avalor_Clutter_ShieldSerpentscale", "Avalor_Clutter_ShieldCarapace" }; GameObject[] floorPropPrefabs = ResolvePrefabs(array4); GameObject[] wallBasePropPrefabs = ResolvePrefabs(array5); GameObject[] wallHangPrefabs = ResolvePrefabs(array6); GameObject[] wallShieldPrefabs = ResolvePrefabs(array7); if (dbg) { Logger.LogInfo((object)($"[Avalor] Clutter resolved: floorProps={floorPropPrefabs.Length}/{array4.Length} " + $"wallBase={wallBasePropPrefabs.Length}/{array5.Length} hang={wallHangPrefabs.Length}/{array6.Length} shields={wallShieldPrefabs.Length}/{array7.Length}")); if (floorPropPrefabs.Length < array4.Length || wallBasePropPrefabs.Length < array5.Length || wallHangPrefabs.Length < array6.Length || wallShieldPrefabs.Length < array7.Length) { Logger.LogWarning((object)"[Avalor] Some clutter prefabs failed to resolve - check the Avalor_Clutter_* clones in ClonePrefabs."); } } float offset = cellSize / 2f; float torchChance = 0.35f; int torchCount = 0; int mistCount = 0; int chainCount = 0; int clutterCount = 0; int maxClutter = 480; if (MistsofAvalorPlugin.ClutterConfig.Value == ClutterDensity.Extreme) { maxClutter = 850; } else if (MistsofAvalorPlugin.ClutterConfig.Value == ClutterDensity.None) { maxClutter = 0; } int instantiationCount = 0; for (int y2 = 0; y2 < floors; y2++) { for (int x6 = 0; x6 < width; x6++) { for (int z5 = 0; z5 < depth; z5++) { MazeCell mazeCell2 = grid[x6, y2, z5]; float x7 = ((float)x6 - (float)width / 2f) * cellSize; float z6 = ((float)z5 - (float)depth / 2f) * cellSize; Vector3 cellPos = center + new Vector3(x7, (float)y2 * floorHeight, z6); if (mistPrefab != null && mistCount < 16 && rnd.NextDouble() < 0.014999999664723873) { mistCount++; InstantiatePiece(mistPrefab, cellPos, Quaternion.identity); } if (MistsofAvalorPlugin.ClutterConfig.Value != ClutterDensity.None && chainPrefab != null && chainCount < 40 && rnd.NextDouble() < 0.05) { chainCount++; float x8 = (float)(rnd.NextDouble() * (double)(cellSize - 2f) - (double)(cellSize / 2f - 1f)); float z7 = (float)(rnd.NextDouble() * (double)(cellSize - 2f) - (double)(cellSize / 2f - 1f)); InstantiatePiece(chainPrefab, cellPos + new Vector3(x8, floorHeight - 0.1f, z7), Quaternion.Euler(0f, rnd.Next(0, 360), 0f)); } float num29 = 0.02f; if (MistsofAvalorPlugin.DifficultyConfig.Value == DifficultyLevel.Normal) { num29 = 0.08f; } else if (MistsofAvalorPlugin.DifficultyConfig.Value == DifficultyLevel.Hardcore) { num29 = 0.15f; } if (y2 == 0 && Vector3.Distance(cellPos, StartLocation) > 30f && rnd.NextDouble() < (double)num29) { GameObject gameObject6 = ((rnd.NextDouble() < 0.5) ? trapPrefab : poisonTrapPrefab); if (gameObject6 != null) { InstantiatePiece(gameObject6, cellPos + new Vector3(0f, -2.5f, 0f), Quaternion.identity); } } int num30; double num31; switch (MistsofAvalorPlugin.ClutterConfig.Value) { case ClutterDensity.None: num30 = 0; num31 = 0.0; break; case ClutterDensity.High: num30 = 1; num31 = 0.75; break; case ClutterDensity.Higher: num30 = 2; num31 = 0.75; break; case ClutterDensity.Extreme: num30 = 3; num31 = 0.9; break; default: num30 = 1; num31 = 0.45; break; } for (int num32 = 0; num32 < num30; num32++) { if (rnd.NextDouble() < num31) { SpawnFloorProp((float)(rnd.NextDouble() * (double)(cellSize - 3f) - (double)(cellSize / 2f - 1.5f)), (float)(rnd.NextDouble() * (double)(cellSize - 3f) - (double)(cellSize / 2f - 1.5f))); } } int num33 = 0; if (mazeCell2.wallRight) { num33++; if (rnd.NextDouble() < (double)torchChance) { SpawnTorch(cellPos + new Vector3(offset - 0.8f, 0.1f, 0f), Quaternion.Euler(0f, -90f, 0f)); } DressWall(Vector3.left, Quaternion.LookRotation(Vector3.left, Vector3.up)); } if (mazeCell2.wallTop) { num33++; if (rnd.NextDouble() < (double)torchChance) { SpawnTorch(cellPos + new Vector3(0f, 0.1f, offset - 0.8f), Quaternion.Euler(0f, 180f, 0f)); } DressWall(Vector3.back, Quaternion.LookRotation(Vector3.back, Vector3.up)); } if (mazeCell2.wallBottom) { num33++; DressWall(Vector3.forward, Quaternion.LookRotation(Vector3.forward, Vector3.up)); } if (mazeCell2.wallLeft) { num33++; if (rnd.NextDouble() < (double)torchChance) { SpawnTorch(cellPos + new Vector3(0f - offset + 0.8f, 0.1f, 0f), Quaternion.Euler(0f, 90f, 0f)); } DressWall(Vector3.right, Quaternion.LookRotation(Vector3.right, Vector3.up)); } bool flag = !mazeCell2.wallFloor || !mazeCell2.wallCeiling; bool flag2 = Vector3.Distance(cellPos, StartLocation) < 30f; bool flag3 = x6 == exitCell.x && y2 == exitCell.y && z5 == exitCell.z; bool flag4 = x6 == prizeCell.x && y2 == prizeCell.y && z5 == prizeCell.z; if (num33 >= 3 && !flag && !flag2 && !flag3 && !flag4 && vaultGatePrefab != null && vaultChestPrefab != null && rnd.NextDouble() < 0.4000000059604645) { Vector3 position5 = cellPos; Quaternion rotation = Quaternion.identity; if (!mazeCell2.wallTop) { position5 += new Vector3(0f, 0f, offset); rotation = Quaternion.identity; } else if (!mazeCell2.wallBottom) { position5 += new Vector3(0f, 0f, 0f - offset); rotation = Quaternion.identity; } else if (!mazeCell2.wallRight) { position5 += new Vector3(offset, 0f, 0f); rotation = Quaternion.Euler(0f, 90f, 0f); } else if (!mazeCell2.wallLeft) { position5 += new Vector3(0f - offset, 0f, 0f); rotation = Quaternion.Euler(0f, 90f, 0f); } position5.y = cellPos.y; InstantiatePiece(vaultGatePrefab, position5, rotation); if (archwayPrefab != null) { InstantiatePiece(archwayPrefab, position5, rotation); } GameObject chestObj2 = InstantiatePiece(vaultChestPrefab, cellPos + new Vector3(0f, 0.02f, 0f), Quaternion.Euler(0f, rnd.Next(0, 360), 0f)); PopulateChest(chestObj2, MistsofAvalorPlugin.DifficultyConfig.Value, 3); vaultCount++; } instantiationCount++; if (instantiationCount >= 30) { instantiationCount = 0; yield return null; } void DressWall(Vector3 inward, Quaternion faceRot) { Vector3 vector4 = new Vector3(0f - inward.z, 0f, inward.x); Vector3 vector5 = -inward; if (rnd.NextDouble() < 0.85) { GameObject[] array8 = ((wallBasePropPrefabs.Length != 0 && rnd.NextDouble() < 0.7) ? wallBasePropPrefabs : floorPropPrefabs); if (array8.Length != 0) { float num34 = (float)(rnd.NextDouble() * 3.4 - 1.7); Vector3 pos = cellPos + vector5 * (offset - 0.4f) + vector4 * num34 + new Vector3(0f, 0.02f, 0f); SpawnClutter(array8[rnd.Next(array8.Length)], pos, faceRot * Quaternion.Euler(0f, rnd.Next(-20, 20), 0f)); } } if (rnd.NextDouble() < 0.5) { float num35 = (float)(rnd.NextDouble() * 3.0 - 1.5); if (wallShieldPrefabs.Length != 0 && (wallHangPrefabs.Length == 0 || rnd.NextDouble() < 0.5)) { Vector3 pos2 = cellPos + vector5 * (offset - 0.22f) + vector4 * num35 + new Vector3(0f, 1.55f, 0f); SpawnClutter(wallShieldPrefabs[rnd.Next(wallShieldPrefabs.Length)], pos2, Quaternion.FromToRotation(Vector3.up, inward)); } else if (wallHangPrefabs.Length != 0) { Vector3 pos3 = cellPos + vector5 * (offset - 0.15f) + vector4 * num35 + new Vector3(0f, 1.45f, 0f); SpawnClutter(wallHangPrefabs[rnd.Next(wallHangPrefabs.Length)], pos3, faceRot * Quaternion.Inverse(ClutterTurn)); } } } void SpawnFloorProp(float xOff, float zOff) { if (floorPropPrefabs.Length != 0) { GameObject prefab2 = floorPropPrefabs[rnd.Next(floorPropPrefabs.Length)]; SpawnClutter(prefab2, cellPos + new Vector3(xOff, 0.02f, zOff), Quaternion.Euler(0f, rnd.Next(0, 360), 0f)); } } } } } if (dbg) { Logger.LogInfo((object)($"[Avalor] Generation complete in {Time.realtimeSinceStartup - genStartTime:F1}s: " + $"chunks={chunkCount} torches={torchCount}/{200} vaults={vaultCount} tracked-ZDOs={spawnedZDOs.Count}")); } AvalorResetManager.Instance?.MarkGenerated(); IsGenerating = false; AvalorMazeGrid.Invalidate(); GameObject SpawnClutter(GameObject gameObject7, Vector3 pos, Quaternion rot) { if (gameObject7 == null || clutterCount >= maxClutter) { return null; } clutterCount++; return InstantiatePiece(gameObject7, pos, rot * ClutterTurn); } GameObject SpawnTorch(Vector3 pos, Quaternion rot) { if (torchPrefab == null || torchCount >= 200) { return null; } torchCount++; return InstantiatePiece(torchPrefab, pos, rot); } } private static Vector3Int ChooseAdjacentOpenCell(MazeCell[,,] grid, Vector3Int cell, int width, int depth) { MazeCell mazeCell = grid[cell.x, cell.y, cell.z]; if (!mazeCell.wallRight && cell.x < width - 1) { return new Vector3Int(cell.x + 1, cell.y, cell.z); } if (!mazeCell.wallLeft && cell.x > 0) { return new Vector3Int(cell.x - 1, cell.y, cell.z); } if (!mazeCell.wallTop && cell.z < depth - 1) { return new Vector3Int(cell.x, cell.y, cell.z + 1); } if (!mazeCell.wallBottom && cell.z > 0) { return new Vector3Int(cell.x, cell.y, cell.z - 1); } return cell; } private IEnumerator PlaceExitAndPrize(Vector3 center, Vector3Int exitCell, Vector3Int prizeCell, int width, int depth, float cellSize, float floorHeight) { float x = ((float)exitCell.x - (float)width / 2f) * cellSize; float z = ((float)exitCell.z - (float)depth / 2f) * cellSize; float exitFloorY = (float)exitCell.y * floorHeight; Vector3 exitPos = center + new Vector3(x, exitFloorY + 0.5f, z); GameObject gameObject = ZNetScene.instance?.GetPrefab("Avalor_EndShrine"); if (gameObject != null) { InstantiatePiece(gameObject, new Vector3(exitPos.x, exitFloorY, exitPos.z + 2.4f), Quaternion.identity); } GameObject gameObject2 = ZNetScene.instance?.GetPrefab("Avalor_ExitPortal"); if (gameObject2 == null) { gameObject2 = ZNetScene.instance?.GetPrefab("Avalor_OverworldPortal"); Logger.LogWarning((object)"[Avalor][FAIL] Avalor_ExitPortal is not registered - falling back to the 7m overworld monument at the maze exit. It will clip the ceiling, but the labyrinth stays completable. Check CreateExitPortal in AvalorAssetManager."); } if (gameObject2 != null) { if (InstantiatePiece(gameObject2, exitPos, Quaternion.identity) == null) { Logger.LogError((object)"[Avalor][FAIL] the exit portal failed to instantiate - THIS LABYRINTH HAS NO WAY OUT. Use avalor_reset to regenerate it."); } else { Logger.LogInfo((object)$"[Avalor] exit portal placed at cell {exitCell} -> {exitPos:F1}."); } GameObject gameObject3 = ZNetScene.instance?.GetPrefab("Avalor_RecallRune"); if (gameObject3 != null) { InstantiatePiece(gameObject3, exitPos + new Vector3(0f, 0f, -2f), Quaternion.identity); } } else { Logger.LogError((object)"[Avalor][FAIL] neither Avalor_ExitPortal nor Avalor_OverworldPortal is registered - THIS LABYRINTH HAS NO WAY OUT. Prefab cloning failed; check the load log for 'CreateExitPortal failed'."); } yield return null; GameObject gameObject4 = ZNetScene.instance?.GetPrefab("Avalor_PrizeChest"); if (gameObject4 == null) { gameObject4 = ZNetScene.instance?.GetPrefab("piece_chest_blackmetal"); Logger.LogWarning((object)"[Avalor][FAIL] Avalor_PrizeChest is not registered - falling back to raw piece_chest_blackmetal, which WILL destroy itself on the maze floor. Check CreatePrizeChest in AvalorAssetManager."); } if (gameObject4 == null) { Logger.LogError((object)"[Avalor][FAIL] no prize chest prefab at all - the Avalorian Greatsword was NOT placed in this labyrinth."); yield break; } Vector3 vector; if (prizeCell != exitCell) { vector = center + new Vector3(((float)prizeCell.x - (float)width / 2f) * cellSize, (float)prizeCell.y * floorHeight + 0.02f, ((float)prizeCell.z - (float)depth / 2f) * cellSize); } else { vector = new Vector3(exitPos.x + 1.6f, exitFloorY + 0.02f, exitPos.z); Logger.LogWarning((object)"[Avalor][FAIL] exit cell reports no open side - prize chest placed in-cell as a fallback. It shares the portal's cell but is still reachable."); } GameObject gameObject5 = InstantiatePiece(gameObject4, vector, Quaternion.Euler(0f, 90f, 0f)); if (gameObject5 == null) { Logger.LogError((object)"[Avalor][FAIL] the greatsword prize chest failed to instantiate."); } else { Logger.LogInfo((object)$"[Avalor] greatsword prize chest placed at cell {prizeCell} -> {vector:F1}."); } PlaceGreatswordPrize(gameObject5); } public IEnumerator RemoveCeiling() { int destroyCount = 0; foreach (ZDOID spawnedZDO in spawnedZDOs) { ZDO zDO = ZDOMan.instance.GetZDO(spawnedZDO); if (zDO != null && zDO.GetPosition().x > 25000f && zDO.GetPosition().y > 505f) { ZDOMan.instance.DestroyZDO(zDO); } destroyCount++; if (destroyCount >= 50) { destroyCount = 0; yield return null; } } } private List GetUnvisitedNeighbors(Vector3Int current, MazeCell[,,] grid, int width, int floors, int depth, System.Random rnd) { List list = new List(); if (current.x > 0 && !grid[current.x - 1, current.y, current.z].visited) { list.Add(new Vector3Int(current.x - 1, current.y, current.z)); } if (current.x < width - 1 && !grid[current.x + 1, current.y, current.z].visited) { list.Add(new Vector3Int(current.x + 1, current.y, current.z)); } if (current.z > 0 && !grid[current.x, current.y, current.z - 1].visited) { list.Add(new Vector3Int(current.x, current.y, current.z - 1)); } if (current.z < depth - 1 && !grid[current.x, current.y, current.z + 1].visited) { list.Add(new Vector3Int(current.x, current.y, current.z + 1)); } if (rnd.NextDouble() < 0.15000000596046448) { if (current.y > 0 && !grid[current.x, current.y - 1, current.z].visited) { list.Add(new Vector3Int(current.x, current.y - 1, current.z)); } if (current.y < floors - 1 && !grid[current.x, current.y + 1, current.z].visited) { list.Add(new Vector3Int(current.x, current.y + 1, current.z)); } } return list; } private void RemoveWall(Vector3Int current, Vector3Int chosen, MazeCell[,,] grid) { if (current.x == chosen.x && current.z == chosen.z) { if (current.y < chosen.y) { grid[current.x, current.y, current.z].wallCeiling = false; grid[chosen.x, chosen.y, chosen.z].wallFloor = false; } else if (current.y > chosen.y) { grid[current.x, current.y, current.z].wallFloor = false; grid[chosen.x, chosen.y, chosen.z].wallCeiling = false; } } else if (current.y == chosen.y) { if (current.x == chosen.x && current.z < chosen.z) { grid[current.x, current.y, current.z].wallTop = false; grid[chosen.x, chosen.y, chosen.z].wallBottom = false; } else if (current.x == chosen.x && current.z > chosen.z) { grid[current.x, current.y, current.z].wallBottom = false; grid[chosen.x, chosen.y, chosen.z].wallTop = false; } else if (current.x < chosen.x && current.z == chosen.z) { grid[current.x, current.y, current.z].wallRight = false; grid[chosen.x, chosen.y, chosen.z].wallLeft = false; } else if (current.x > chosen.x && current.z == chosen.z) { grid[current.x, current.y, current.z].wallLeft = false; grid[chosen.x, chosen.y, chosen.z].wallRight = false; } } } private static float FloorSurfaceLift(int floorIndex) { return 0.1f; } private static GameObject[] ResolvePrefabs(string[] names) { List list = new List(); for (int i = 0; i < names.Length; i++) { GameObject gameObject = ZNetScene.instance?.GetPrefab(names[i]); if (gameObject != null) { list.Add(gameObject); } } return list.ToArray(); } private GameObject InstantiatePiece(GameObject prefab, Vector3 position, Quaternion rotation) { if (prefab == null) { return null; } GameObject obj = UnityEngine.Object.Instantiate(prefab, position, rotation); ZNetView component = obj.GetComponent(); if (component != null && component.GetZDO() != null) { component.GetZDO().Set("AvalorMazePiece", 1); spawnedZDOs.Add(component.GetZDO().m_uid); } return obj; } private void PlaceGreatswordPrize(GameObject chestObj) { if (chestObj == null) { return; } Container component = chestObj.GetComponent(); if (!(component == null)) { component.m_defaultItems.m_drops.Clear(); if (!AvalorLootManager.AddGuaranteed(component, "Avalor_Greatsword")) { Logger.LogWarning((object)"[Avalor] Avalor_Greatsword not registered - prize chest left EMPTY."); } else if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"[Avalor] greatsword prize placed in the blackmetal chest at the escape portal."); } } } private void PopulateChest(GameObject chestObj, DifficultyLevel diff, int lootTier = 1) { if (!(chestObj == null)) { Container component = chestObj.GetComponent(); if (!(component == null) && component.GetInventory() != null) { component.m_defaultItems.m_drops.Clear(); AvalorLootManager.PopulateChest(component, diff, lootTier, Depth01(chestObj.transform.position), null); } } } private float Depth01(Vector3 pos) { if (mazeSpan <= 0f) { return 0.5f; } return Mathf.Clamp01(Vector3.Distance(pos, StartLocation) / mazeSpan); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorMazeGrid ---- namespace MistsofAvalor { public static class AvalorMazeGrid { public const float CellSize = 6f; public const float FloorHeight = 4f; public const float SpawnLift = 0.3f; private const float CacheTTL = 60f; private static readonly Dictionary _cells = new Dictionary(); private static float _builtAt = -999f; private static bool _valid; private static int _chunkCount; public static int CachedCellCount { get { if (!_valid) { return 0; } return _cells.Count; } } public static void Invalidate() { _valid = false; _cells.Clear(); _chunkCount = 0; } private static void EnsureBuilt() { if ((_valid && Time.realtimeSinceStartup - _builtAt < 60f) || (AvalorMazeGenerator.Instance != null && AvalorMazeGenerator.Instance.IsGenerating)) { return; } Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); if (zDODictionary == null) { return; } _cells.Clear(); _chunkCount = 0; Vector3 mazeCenter = AvalorMazeGenerator.MazeCenter; int stableHashCode = "Avalor_MazeChunk".GetStableHashCode(); foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value == null || value.GetPrefab() != stableHashCode) { continue; } byte[] byteArray = value.GetByteArray("Avalor_ChunkData"); if (byteArray == null || byteArray.Length == 0) { continue; } int num = value.GetInt("Avalor_ChunkWidth", 5); int num2 = value.GetInt("Avalor_ChunkDepth", 5); int num3 = value.GetInt("Avalor_ChunkFloors", 3); if (num <= 0 || num2 <= 0 || num3 <= 0 || byteArray.Length < num * num2 * num3) { continue; } Vector3 position = value.GetPosition(); _chunkCount++; for (int i = 0; i < num3; i++) { for (int j = 0; j < num; j++) { for (int k = 0; k < num2; k++) { byte value2 = byteArray[(i * num + j) * num2 + k]; float num4 = position.x + ((float)j - (float)(num - 1) / 2f) * 6f; float num5 = position.z + ((float)k - (float)(num2 - 1) / 2f) * 6f; Vector3Int key = new Vector3Int(Mathf.RoundToInt((num4 - mazeCenter.x) / 6f), i, Mathf.RoundToInt((num5 - mazeCenter.z) / 6f)); _cells[key] = value2; } } } } _builtAt = Time.realtimeSinceStartup; _valid = _cells.Count > 0; if (_valid) { Logger.LogInfo((object)$"[Avalor] maze grid decoded {_cells.Count} cell(s) from {_chunkCount} chunk ZDO(s)."); } } public static bool TryGetCell(Vector3 world, out byte bits, out Vector3 cellCenter) { bits = 0; cellCenter = Vector3.zero; EnsureBuilt(); if (!_valid) { return false; } Vector3 mazeCenter = AvalorMazeGenerator.MazeCenter; Vector3Int key = new Vector3Int(Mathf.RoundToInt((world.x - mazeCenter.x) / 6f), Mathf.RoundToInt((world.y - mazeCenter.y) / 4f), Mathf.RoundToInt((world.z - mazeCenter.z) / 6f)); if (!_cells.TryGetValue(key, out bits)) { return false; } cellCenter = new Vector3(mazeCenter.x + (float)key.x * 6f, mazeCenter.y + (float)key.y * 4f, mazeCenter.z + (float)key.z * 6f); return true; } public static bool IsOpenFloor(byte bits, int y) { if (bits == 0) { return false; } if ((bits & 0xF) == 15) { return false; } if (y > 0 && (bits & 0x40) != 0) { return false; } return true; } public static bool TryFindOpenSpawnNear(Vector3 playerPos, Quaternion playerRot, out Vector3 spawn, HashSet usedThisTick = null) { spawn = Vector3.zero; EnsureBuilt(); if (!_valid) { return false; } Vector3 vector = playerRot * Vector3.forward; vector.y = 0f; vector = ((vector.sqrMagnitude > 0.01f) ? vector.normalized : Vector3.forward); Vector3 mazeCenter = AvalorMazeGenerator.MazeCenter; int num = Mathf.Clamp(Mathf.RoundToInt((playerPos.y - mazeCenter.y) / 4f), 0, 8); for (int i = 0; i < 20; i++) { Vector3 vector2; float num2; if (i < 13) { vector2 = Quaternion.Euler(0f, Random.Range(-70f, 70f), 0f) * vector; num2 = Random.Range(10f, 26f); } else { Vector2 normalized = Random.insideUnitCircle.normalized; vector2 = new Vector3(normalized.x, 0f, normalized.y); num2 = Random.Range(6f, 16f); } Vector3 world = playerPos + vector2 * num2; world.y = mazeCenter.y + (float)num * 4f; if (!TryGetCell(world, out var bits, out var cellCenter) || !IsOpenFloor(bits, num)) { continue; } if (usedThisTick != null) { Vector3Int item = new Vector3Int(Mathf.RoundToInt((cellCenter.x - mazeCenter.x) / 6f), num, Mathf.RoundToInt((cellCenter.z - mazeCenter.z) / 6f)); if (!usedThisTick.Add(item)) { continue; } } spawn = cellCenter + Vector3.up * 0.3f; return true; } return false; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorGate ---- namespace MistsofAvalor { public class AvalorGate : MonoBehaviour, Interactable { public bool Interact(Humanoid user, bool hold, bool alt) { if (hold) { return false; } string text = "Avalor Crypt Key"; bool flag = false; foreach (ItemDrop.ItemData allItem in user.GetInventory().GetAllItems()) { if (allItem.m_shared.m_name == text) { flag = true; break; } } if (flag) { user.GetInventory().RemoveItem(text, 1); user.Message(MessageHud.MessageType.Center, "Crypt Gate unlocked."); ZNetView component = GetComponent(); if (component != null && component.IsValid()) { component.ClaimOwnership(); ZNetScene.instance.Destroy(component.gameObject); } return true; } user.Message(MessageHud.MessageType.Center, "Requires Avalor Crypt Key!"); return false; } public bool UseItem(Humanoid user, ItemDrop.ItemData item) { return false; } public string GetHoverText() { return "Crypt Gate\n[Requires Avalor Crypt Key]"; } public string GetHoverName() { return "Crypt Gate"; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorPressurePlate ---- namespace MistsofAvalor { public class AvalorPressurePlate : MonoBehaviour { public GameObject SpikePrefab; public bool IsPoison; private bool triggered; private void OnTriggerEnter(Collider other) { if (triggered) { return; } Player component = other.GetComponent(); if (!(component != null) || component != Player.m_localPlayer) { return; } triggered = true; float health = component.GetHealth(); float num = 0.4f; if (MistsofAvalorPlugin.DifficultyConfig != null) { if (MistsofAvalorPlugin.DifficultyConfig.Value == DifficultyLevel.Normal) { num = 0.6f; } else if (MistsofAvalorPlugin.DifficultyConfig.Value == DifficultyLevel.Hardcore) { num = 0.85f; } } float num2 = health * num; HitData hitData = new HitData(); hitData.m_point = component.transform.position; if (IsPoison) { hitData.m_damage.m_poison = num2; GameObject gameObject = ZNetScene.instance?.GetPrefab("vfx_Blob_hit"); if (gameObject != null) { Object.Instantiate(gameObject, component.transform.position, Quaternion.identity); } } else { hitData.m_damage.m_damage = num2; } component.Damage(hitData); if (SpikePrefab != null) { ZNetView component2 = SpikePrefab.GetComponent(); if (component2 != null && component2.IsValid()) { component2.InvokeRPC(ZNetView.Everybody, "TriggerSpikeAnim"); } } StartCoroutine(ResetCooldown()); } private IEnumerator ResetCooldown() { yield return new WaitForSeconds(5f); triggered = false; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorSpikeAnim ---- namespace MistsofAvalor { public class AvalorSpikeAnim : MonoBehaviour { private ZNetView znv; private void Awake() { znv = GetComponent(); if (znv != null && znv.IsValid()) { znv.Register("TriggerSpikeAnim", RPC_TriggerSpikeAnim); } } private void RPC_TriggerSpikeAnim(long sender) { StartCoroutine(AnimateSpikes()); } private IEnumerator AnimateSpikes() { Vector3 start = base.transform.position; Vector3 end = start + new Vector3(0f, 2f, 0f); float t = 0f; while (t < 1f) { t += Time.deltaTime * 5f; base.transform.position = Vector3.Lerp(start, end, t); yield return null; } yield return new WaitForSeconds(2f); base.transform.position = start; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorBossBar ---- namespace MistsofAvalor { public class AvalorBossBar : MonoBehaviour { private GameObject _uiRoot; private RectTransform _barRt; private Image _fill; private Text _nameText; private Font _valheimFont; private Character _tracked; private Vector3 _lastKnownPos; private string _displayName; private string _token; private string _fallback; private Color _fillColour = AvalorCryptEnvZone.AvalorGreen; private const float ShowRange = 30f; private bool _priorityHeld; public static AvalorBossBar Instance { get; private set; } private void Awake() { Instance = this; } public void Track(Character c, string nameToken, string fallback, Color fillColour, bool priority = false) { if (!(c == null) && (priority || !(_tracked != null) || !(_tracked != c) || _tracked.IsDead() || !_priorityHeld)) { _tracked = c; _token = nameToken; _fallback = fallback; _fillColour = fillColour; _priorityHeld = priority; _displayName = null; _lastKnownPos = c.transform.position; if (_uiRoot == null && Hud.instance != null) { CreateUI(); } if (_fill != null) { _fill.color = _fillColour; } if (MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)($"[Avalor] boss bar tracking '{fallback}' (priority={priority}) - " + $"hud={Hud.instance != null} ui={_uiRoot != null}")); } } } private string ResolveName() { if (!string.IsNullOrEmpty(_displayName)) { return _displayName; } string result = (string.IsNullOrEmpty(_fallback) ? "Avalorian Revenant" : _fallback); if (string.IsNullOrEmpty(_token)) { return result; } string text = null; if (Localization.instance != null) { text = Localization.instance.Localize(_token); } if (string.IsNullOrEmpty(text) || text[0] == '$' || text[0] == '[') { return result; } _displayName = text; return _displayName; } private void CreateUI() { if (!(Hud.instance == null) && !(Hud.instance.m_rootObject == null)) { Text componentInChildren = Hud.instance.m_rootObject.GetComponentInChildren(includeInactive: true); if (componentInChildren != null) { _valheimFont = componentInChildren.font; } _uiRoot = new GameObject("AvalorBossUI"); _uiRoot.transform.SetParent(Hud.instance.m_rootObject.transform, worldPositionStays: false); RectTransform rectTransform = _uiRoot.AddComponent(); rectTransform.anchorMin = new Vector2(0.5f, 1f); rectTransform.anchorMax = new Vector2(0.5f, 1f); rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.anchoredPosition = new Vector2(0f, -50f); BuildBar("AvalorRevenantBar", _uiRoot.transform, Vector2.zero, new Vector2(600f, 30f)); _uiRoot.SetActive(value: false); } } private void BuildBar(string barName, Transform parent, Vector2 anchoredPos, Vector2 size) { GameObject gameObject = new GameObject(barName); gameObject.transform.SetParent(parent, worldPositionStays: false); _barRt = gameObject.AddComponent(); _barRt.anchorMin = new Vector2(0.5f, 1f); _barRt.anchorMax = new Vector2(0.5f, 1f); _barRt.pivot = new Vector2(0.5f, 1f); _barRt.anchoredPosition = anchoredPos; _barRt.sizeDelta = size; GameObject obj = new GameObject("Trim"); obj.transform.SetParent(gameObject.transform, worldPositionStays: false); RectTransform rectTransform = obj.AddComponent(); rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = new Vector2(-2f, -2f); rectTransform.offsetMax = new Vector2(2f, 2f); obj.AddComponent().color = new Color(0.72f, 0.62f, 0.24f, 1f); GameObject obj2 = new GameObject("Bkg"); obj2.transform.SetParent(gameObject.transform, worldPositionStays: false); RectTransform rectTransform2 = obj2.AddComponent(); rectTransform2.anchorMin = Vector2.zero; rectTransform2.anchorMax = Vector2.one; rectTransform2.sizeDelta = Vector2.zero; obj2.AddComponent().color = new Color(0f, 0f, 0f, 0.9f); GameObject gameObject2 = new GameObject("Fill"); gameObject2.transform.SetParent(gameObject.transform, worldPositionStays: false); RectTransform rectTransform3 = gameObject2.AddComponent(); rectTransform3.anchorMin = Vector2.zero; rectTransform3.anchorMax = Vector2.one; rectTransform3.sizeDelta = Vector2.zero; rectTransform3.pivot = new Vector2(0f, 0.5f); _fill = gameObject2.AddComponent(); _fill.color = AvalorCryptEnvZone.AvalorGreen; _fill.raycastTarget = false; GameObject gameObject3 = new GameObject("NameText"); gameObject3.transform.SetParent(gameObject.transform, worldPositionStays: false); RectTransform rectTransform4 = gameObject3.AddComponent(); rectTransform4.anchorMin = new Vector2(0.5f, 0.5f); rectTransform4.anchorMax = new Vector2(0.5f, 0.5f); rectTransform4.pivot = new Vector2(0.5f, 0.5f); rectTransform4.anchoredPosition = Vector2.zero; rectTransform4.sizeDelta = new Vector2(560f, 30f); _nameText = gameObject3.AddComponent(); _nameText.alignment = TextAnchor.MiddleCenter; _nameText.color = Color.white; _nameText.fontSize = 22; _nameText.raycastTarget = false; _nameText.font = ((_valheimFont != null) ? _valheimFont : Resources.GetBuiltinResource("Arial.ttf")); Shadow shadow = gameObject3.AddComponent(); shadow.effectColor = Color.black; shadow.effectDistance = new Vector2(1f, -1f); } private void LateUpdate() { if (Hud.instance == null) { return; } int num; if (_tracked != null && !_tracked.IsDead()) { num = ((_tracked.GetHealth() > 0f) ? 1 : 0); if (num != 0) { _lastKnownPos = _tracked.transform.position; } } else { num = 0; } if (num == 0 || Player.m_localPlayer == null || Vector3.Distance(Player.m_localPlayer.transform.position, _lastKnownPos) > 30f) { if (_uiRoot != null && _uiRoot.activeSelf) { _uiRoot.SetActive(value: false); } return; } if (_uiRoot == null) { CreateUI(); } if (!(_uiRoot == null)) { if (!_uiRoot.activeSelf) { _uiRoot.SetActive(value: true); } if (_fill.color != _fillColour) { _fill.color = _fillColour; } _nameText.text = ResolveName(); float maxHealth = _tracked.GetMaxHealth(); float x = Mathf.Clamp01((maxHealth > 0f) ? (_tracked.GetHealth() / maxHealth) : 0f); _fill.transform.localScale = new Vector3(x, 1f, 1f); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorGraceCountdown ---- namespace MistsofAvalor { public class AvalorGraceCountdown : MonoBehaviour { private GameObject _uiRoot; private Text _line; private Text _sub; private CanvasGroup _group; private Font _valheimFont; private bool _wasInside; private float _remaining; private float _fade; private bool _finished; private const float GoodLuckHold = 1.6f; private const float GoodLuckFade = 1.4f; private void LateUpdate() { Player localPlayer = Player.m_localPlayer; if (!(localPlayer != null) || !AvalorRegion.InSkyMaze(localPlayer.transform.position)) { if (_wasInside) { Reset(); } _wasInside = false; return; } if (!_wasInside) { _wasInside = true; _remaining = 20f; _fade = 0f; _finished = false; } if (_uiRoot == null) { CreateUI(); } if (_uiRoot == null) { return; } if (!_finished) { _remaining -= Time.deltaTime; if (_remaining <= 0f) { _finished = true; _remaining = 0f; } } else { _fade += Time.deltaTime; if (_fade >= 3f) { if (_uiRoot.activeSelf) { _uiRoot.SetActive(value: false); } return; } } if (!_uiRoot.activeSelf) { _uiRoot.SetActive(value: true); } if (!_finished) { int num = Mathf.CeilToInt(_remaining); _line.text = num.ToString(); _sub.text = "The dead are stirring"; float t = 1f - Mathf.Clamp01(_remaining / 20f); _line.fontSize = Mathf.RoundToInt(Mathf.Lerp(54f, 78f, t)); _line.color = Color.Lerp(AvalorCryptEnvZone.AvalorGreen, new Color(0.95f, 0.72f, 0.3f), t); _group.alpha = 1f; } else { _line.text = "Good luck."; _line.fontSize = 62; _line.color = AvalorCryptEnvZone.AvalorGreen; _sub.text = ""; _group.alpha = 1f - Mathf.Clamp01((_fade - 1.6f) / 1.4f); } } private void Reset() { _finished = false; _fade = 0f; _remaining = 20f; if (_uiRoot != null && _uiRoot.activeSelf) { _uiRoot.SetActive(value: false); } } private void CreateUI() { if (!(Hud.instance == null) && !(Hud.instance.m_rootObject == null)) { Text componentInChildren = Hud.instance.m_rootObject.GetComponentInChildren(includeInactive: true); if (componentInChildren != null) { _valheimFont = componentInChildren.font; } Font font = ((_valheimFont != null) ? _valheimFont : Resources.GetBuiltinResource("Arial.ttf")); _uiRoot = new GameObject("AvalorGraceCountdown"); _uiRoot.transform.SetParent(Hud.instance.m_rootObject.transform, worldPositionStays: false); RectTransform rectTransform = _uiRoot.AddComponent(); rectTransform.anchorMin = new Vector2(0.5f, 0.5f); rectTransform.anchorMax = new Vector2(0.5f, 0.5f); rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.anchoredPosition = new Vector2(0f, 150f); rectTransform.sizeDelta = new Vector2(700f, 140f); _group = _uiRoot.AddComponent(); _group.interactable = false; _group.blocksRaycasts = false; _line = MakeLabel("Count", _uiRoot.transform, new Vector2(0f, 12f), new Vector2(700f, 96f), 64, font); _sub = MakeLabel("Sub", _uiRoot.transform, new Vector2(0f, -44f), new Vector2(700f, 30f), 20, font); _sub.color = new Color(0.86f, 0.9f, 0.86f, 0.75f); _uiRoot.SetActive(value: false); } } private static Text MakeLabel(string name, Transform parent, Vector2 pos, Vector2 size, int fontSize, Font font) { GameObject gameObject = new GameObject(name); gameObject.transform.SetParent(parent, worldPositionStays: false); RectTransform rectTransform = gameObject.AddComponent(); rectTransform.anchorMin = new Vector2(0.5f, 0.5f); rectTransform.anchorMax = new Vector2(0.5f, 0.5f); rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.anchoredPosition = pos; rectTransform.sizeDelta = size; Text text = gameObject.AddComponent(); text.font = font; text.fontSize = fontSize; text.alignment = TextAnchor.MiddleCenter; text.color = AvalorCryptEnvZone.AvalorGreen; text.raycastTarget = false; Outline outline = gameObject.AddComponent(); outline.effectColor = new Color(0f, 0f, 0f, 0.95f); outline.effectDistance = new Vector2(2f, -2f); return text; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorHuntEnforcer ---- namespace MistsofAvalor { public class AvalorHuntEnforcer : MonoBehaviour { private static readonly List _pending = new List(); private static readonly HashSet _done = new HashSet(); public static void Enqueue(GameObject mob) { if (!(mob == null) && !_done.Contains(mob.GetInstanceID())) { _pending.Add(mob); } } private void Update() { if (_pending.Count == 0) { return; } GameObject[] array = _pending.ToArray(); _pending.Clear(); int num = 0; GameObject[] array2 = array; foreach (GameObject gameObject in array2) { if (gameObject == null) { continue; } int instanceID = gameObject.GetInstanceID(); if (!_done.Add(instanceID)) { continue; } ZNetView component = gameObject.GetComponent(); if (!(component != null) || !component.IsValid() || component.GetZDO() == null || component.GetZDO().GetInt("AvalorWarden") != 1) { try { AvalorMobDirector.MakeItHunt(gameObject); num++; } catch (Exception ex) { Logger.LogWarning((object)("[Avalor][FAIL] could not make '" + gameObject.name + "' hunt: " + ex.Message)); } } } if (num > 0 && MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)($"[Avalor] {num} maze mob(s) not spawned by the director were given " + "Avalorian senses and alerted.")); } } public static void Forget() { _pending.Clear(); _done.Clear(); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorianName ---- namespace MistsofAvalor { public static class AvalorianName { public const string Prefix = "Avalorian "; public static void Apply(Character c) { if (c == null) { return; } string name = c.m_name; if (!string.IsNullOrEmpty(name) && !name.StartsWith("Avalorian ", StringComparison.Ordinal) && Localization.instance != null) { string text = Localization.instance.Localize(name); if (!string.IsNullOrEmpty(text) && text[0] != '$' && text[0] != '[' && !text.StartsWith("Avalorian ", StringComparison.Ordinal)) { c.m_name = "Avalorian " + text; } } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorMobDirector ---- namespace MistsofAvalor { public class AvalorMobDirector : MonoBehaviour { private float spawnTimer; private float firstPresentTime = -1f; private const float MAX_MOB_DISTANCE = 50f; private const float MIN_MOB_DISTANCE = 20f; private const float SUBBOSS_CHANCE = 0.15f; public const float SPAWN_GRACE = 20f; private List _activeMobIds = new List(); private bool _recoveredThisMaze; private static readonly HashSet _reportedMissingPrefabs = new HashSet(); private const float REVENANT_CHANCE = 0.02f; private const float SHORTSWORD_DROP_CHANCE = 0.6f; public const string RevenantNameToken = "$enemy_avalor_revenant"; public const string RevenantZDOKey = "AvalorRevenant"; public const string SoMStealthExemptKey = "SoMStealthExempt"; private static readonly string[] RevenantVariants = new string[3] { "Draugr_Elite", "Charred_Melee", "Skeleton_Poison" }; private static readonly string[] EndBossVariants = new string[3] { "Skeleton_Hildir_nochest", "Charred_Melee_Dyrnwyn", "Draugr_Elite" }; public const string WardenNameToken = "$enemy_avalor_warden"; public const string WardenFallbackName = "The Avalorian Warden"; public const string WardenZDOKey = "AvalorWarden"; private const string WardenKilledZDOKey = "AvalorWardenDown"; public static readonly Color WardenBarColour = new Color(0.85f, 0.72f, 0.2f); private const float BossTriggerRange = 34f; private ZDOID _endBossId = ZDOID.None; private bool _endBossKilled; private static float _exitCacheTime = -999f; private static Vector3 _exitCache; private static bool _exitCacheValid; private const float HuntSenseRange = 60f; private static bool _spawnDiagLogged; public static AvalorMobDirector Instance { get; private set; } private void Awake() { Instance = this; } private void Update() { AvalorNet.EnsureRegistered(); spawnTimer += Time.deltaTime; if (spawnTimer >= 5f) { spawnTimer = 0f; ManageMobs(); } } private void ManageMobs() { if (ZNet.instance == null || !ZNet.instance.IsServer()) { return; } List mazePlayers = AvalorNet.GetMazePlayers(); if (mazePlayers.Count == 0) { firstPresentTime = -1f; ClearAllMobs(); return; } if (firstPresentTime < 0f) { firstPresentTime = Time.time; } if (!_recoveredThisMaze) { _recoveredThisMaze = true; if (_activeMobIds.Count == 0) { RecoverActiveMobs(); } } _activeMobIds.RemoveAll((ZDOID id) => id == ZDOID.None || ZDOMan.instance.GetZDO(id) == null); for (int num = _activeMobIds.Count - 1; num >= 0; num--) { ZDO zDO = ZDOMan.instance.GetZDO(_activeMobIds[num]); if (zDO != null) { Vector3 position = zDO.GetPosition(); bool flag = false; for (int num2 = 0; num2 < mazePlayers.Count; num2++) { if (Vector3.Distance(mazePlayers[num2].pos, position) < 50f) { flag = true; break; } } if (!flag) { DestroyMobZdo(zDO); _activeMobIds.RemoveAt(num); } } } if (Time.time - firstPresentTime < 20f) { return; } ManageEndBoss(mazePlayers); int num3 = ((MistsofAvalorPlugin.DifficultyConfig == null) ? DifficultyLevel.Normal : MistsofAvalorPlugin.DifficultyConfig.Value) switch { DifficultyLevel.Easy => 5, DifficultyLevel.Hardcore => 14, _ => 7, }; int num4 = mazePlayers.Count * num3; if (_activeMobIds.Count < num4) { HashSet usedCells = new HashSet(); foreach (AvalorNet.PlayerPresence item in mazePlayers) { int num5 = 0; for (int num6 = 0; num6 < _activeMobIds.Count; num6++) { ZDO zDO2 = ZDOMan.instance.GetZDO(_activeMobIds[num6]); if (zDO2 != null && Vector3.Distance(zDO2.GetPosition(), item.pos) < 50f) { num5++; } } int num7 = Mathf.Min(3, num3 - num5); for (int num8 = 0; num8 < num7; num8++) { SpawnMobNearPlayer(item, usedCells); } } } if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)($"[Avalor] Mobs: mode={AvalorNet.Role} active={_activeMobIds.Count} " + $"target={num4} players-in-maze={mazePlayers.Count} grid-cells={AvalorMazeGrid.CachedCellCount}")); } } private static void DestroyMobZdo(ZDO zdo) { if (zdo == null) { return; } ZNetView zNetView = ((ZNetScene.instance != null) ? ZNetScene.instance.FindInstance(zdo) : null); if (zNetView != null) { zNetView.ClaimOwnership(); ZNetScene.instance.Destroy(zNetView.gameObject); return; } if (!zdo.IsOwner()) { zdo.SetOwner(ZDOMan.GetSessionID()); } ZDOMan.instance.DestroyZDO(zdo); } private void ClearAllMobs() { for (int num = _activeMobIds.Count - 1; num >= 0; num--) { ZDO zDO = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(_activeMobIds[num]) : null); if (zDO != null) { DestroyMobZdo(zDO); } } _activeMobIds.Clear(); } private void RecoverActiveMobs() { Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); if (zDODictionary == null) { return; } int num = 0; foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value != null && AvalorRegion.InSkyMaze(value.GetPosition()) && value.GetInt("SoMStealthExempt") == 1 && value.GetInt("AvalorWarden") != 1) { GameObject gameObject = ((ZNetScene.instance != null) ? ZNetScene.instance.GetPrefab(value.GetPrefab()) : null); if (!(gameObject == null) && !(gameObject.GetComponent() == null)) { _activeMobIds.Add(value.m_uid); num++; } } } if (num > 0) { Logger.LogInfo((object)$"[Avalor] director recovered {num} live maze mob(s) after a restart."); } } private static void MarkAsAvalorMob(GameObject go, Character c) { ZNetView component = go.GetComponent(); if (component != null && component.IsValid() && component.GetZDO() != null) { component.GetZDO().Set("SoMStealthExempt", 1); } if (c != null) { AvalorStealthCompat.Register(c); } } internal static void AttachShortswordDrop(GameObject mob) { GameObject gameObject = ZNetScene.instance?.GetPrefab("Avalor_Shortsword"); if (gameObject == null) { return; } CharacterDrop characterDrop = mob.GetComponent(); if (characterDrop == null) { characterDrop = mob.AddComponent(); } if (characterDrop.m_drops == null) { characterDrop.m_drops = new List(); } foreach (CharacterDrop.Drop drop in characterDrop.m_drops) { if (drop.m_prefab == gameObject) { return; } } characterDrop.m_drops.Add(new CharacterDrop.Drop { m_prefab = gameObject, m_amountMin = 1, m_amountMax = 1, m_chance = 0.6f, m_onePerPlayer = true, m_levelMultiplier = false, m_dontScale = true }); } public void OnMazeWiped() { _endBossId = ZDOID.None; _endBossKilled = false; _exitCacheValid = false; _exitCacheTime = -999f; _activeMobIds.Clear(); _recoveredThisMaze = false; AvalorMazeGrid.Invalidate(); } private static void MarkWardenDown() { ScanWardenState(out var exitZdo, out var _, out var _); if (exitZdo != null) { if (!exitZdo.IsOwner()) { exitZdo.SetOwner(ZDOMan.GetSessionID()); } exitZdo.Set("AvalorWardenDown", 1); } } private static void ScanWardenState(out ZDO exitZdo, out bool wardenExists, out ZDOID wardenId) { exitZdo = null; wardenExists = false; wardenId = ZDOID.None; Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); if (zDODictionary == null) { return; } int stableHashCode = "Avalor_ExitPortal".GetStableHashCode(); int stableHashCode2 = "Avalor_OverworldPortal".GetStableHashCode(); foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value == null || !AvalorRegion.InSkyMaze(value.GetPosition())) { continue; } if (!wardenExists && value.GetInt("AvalorWarden") == 1) { wardenExists = true; wardenId = value.m_uid; } if (exitZdo == null) { int prefab = value.GetPrefab(); if (prefab == stableHashCode || prefab == stableHashCode2) { exitZdo = value; } } if (wardenExists && exitZdo != null) { break; } } } private void ManageEndBoss(List players) { if (!_endBossId.IsNone() && ZDOMan.instance.GetZDO(_endBossId) == null) { _endBossKilled = true; _endBossId = ZDOID.None; MarkWardenDown(); if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"[Avalor] the Avalorian Warden has fallen - the way out is unguarded."); } } if (!_endBossId.IsNone() || _endBossKilled || !TryFindExit(out var pos)) { return; } bool flag = false; for (int i = 0; i < players.Count; i++) { if (Vector3.Distance(players[i].pos, pos) < 34f) { flag = true; break; } } if (!flag) { return; } ScanWardenState(out var exitZdo, out var wardenExists, out var wardenId); if (wardenExists) { _endBossId = wardenId; return; } if (exitZdo != null && exitZdo.GetInt("AvalorWardenDown") == 1) { _endBossKilled = true; return; } string text = EndBossVariants[Random.Range(0, EndBossVariants.Length)]; GameObject gameObject = ZNetScene.instance?.GetPrefab(text); if (gameObject == null) { if (_reportedMissingPrefabs.Add(text)) { Logger.LogWarning((object)("[Avalor][FAIL] end-boss prefab '" + text + "' not found - no Warden will guard this maze while that variant keeps being rolled.")); } return; } GameObject gameObject2 = Object.Instantiate(gameObject, pos + new Vector3(0f, 0.5f, -3f), Quaternion.identity); Character component = gameObject2.GetComponent(); if (component == null) { return; } ZNetView component2 = gameObject2.GetComponent(); if (component2 != null && component2.IsValid() && component2.GetZDO() != null) { component2.GetZDO().Set("AvalorWarden", 1); } MarkAsAvalorMob(gameObject2, component); MutateIntoEndBoss(gameObject2, component, (MistsofAvalorPlugin.DifficultyConfig == null) ? DifficultyLevel.Normal : MistsofAvalorPlugin.DifficultyConfig.Value); if (component2 != null && component2.IsValid() && component2.GetZDO() != null) { _endBossId = component2.GetZDO().m_uid; long num = NearestPlayerUid(players, gameObject2.transform.position); if (num != 0L) { component2.GetZDO().SetOwner(num); } } if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)("[Avalor] the Avalorian Warden rises at the exit as '" + text + "' " + $"({component.GetMaxHealth():F0} hp) at {gameObject2.transform.position:F0}.")); } } private static long NearestPlayerUid(List players, Vector3 pos) { long result = 0L; float num = float.MaxValue; for (int i = 0; i < players.Count; i++) { float num2 = Vector3.Distance(players[i].pos, pos); if (num2 < num && players[i].ownerUid != 0L) { num = num2; result = players[i].ownerUid; } } return result; } private static void MutateIntoEndBoss(GameObject go, Character c, DifficultyLevel diff) { c.SetLevel(3); c.SetMaxHealth(c.GetMaxHealth() * 1.2f); c.SetMaxHealth(c.GetMaxHealth() * diff switch { DifficultyLevel.Easy => 3f, DifficultyLevel.Hardcore => 7f, _ => 4.5f, }); c.SetHealth(c.GetMaxHealth()); c.m_name = "$enemy_avalor_warden"; AvalorBossBar.Instance?.Track(c, "$enemy_avalor_warden", "The Avalorian Warden", WardenBarColour, priority: true); ApplyWardenLocal(go, c); BaseAI component = go.GetComponent(); if (component != null) { component.Alert(); } } internal static void ApplyWardenLocal(GameObject go, Character c) { if (!(c == null)) { c.m_staggerDamageFactor = 0f; MonsterAI monsterAI = go.GetComponent() as MonsterAI; if (monsterAI != null) { monsterAI.m_fleeIfNotAlerted = false; monsterAI.m_fleeIfHurtWhenTargetCantBeReached = false; } AddWardenDrops(go); } } private static void AddWardenDrops(GameObject mob) { CharacterDrop characterDrop = mob.GetComponent(); if (characterDrop == null) { characterDrop = mob.AddComponent(); } if (characterDrop.m_drops == null) { characterDrop.m_drops = new List(); } GameObject gameObject = ZNetScene.instance?.GetPrefab("Avalor_RecallRune"); if (gameObject != null) { foreach (CharacterDrop.Drop drop in characterDrop.m_drops) { if (drop.m_prefab == gameObject) { return; } } } AddDrop(characterDrop, "Avalor_RecallRune", 1, 1, 1f); AddDrop(characterDrop, "Avalor_Shortsword", 1, 1, 0.25f); AddDrop(characterDrop, "GemstoneRed", 1, 2, 0.5f); AddDrop(characterDrop, "GemstoneBlue", 1, 2, 0.5f); AddDrop(characterDrop, "GemstoneGreen", 1, 2, 0.5f); AddDrop(characterDrop, "SurtlingCore", 6, 14, 0.8f); AddDrop(characterDrop, "BlackMetal", 8, 20, 0.7f); AddDrop(characterDrop, "Flametal", 4, 10, 0.45f); AddDrop(characterDrop, "Coins", 200, 600, 0.9f); } private static void AddDrop(CharacterDrop cd, string prefabName, int min, int max, float chance) { GameObject gameObject = ZNetScene.instance?.GetPrefab(prefabName); if (!(gameObject == null)) { cd.m_drops.Add(new CharacterDrop.Drop { m_prefab = gameObject, m_amountMin = min, m_amountMax = max, m_chance = chance, m_onePerPlayer = false, m_levelMultiplier = false, m_dontScale = true }); } } public static bool TryFindExit(out Vector3 pos) { if (_exitCacheValid && Time.time - _exitCacheTime < 30f) { pos = _exitCache; return true; } pos = Vector3.zero; Dictionary zDODictionary = AvalorMazeGenerator.GetZDODictionary(); if (zDODictionary == null) { return false; } int stableHashCode = "Avalor_ExitPortal".GetStableHashCode(); int stableHashCode2 = "Avalor_OverworldPortal".GetStableHashCode(); foreach (KeyValuePair item in zDODictionary) { ZDO value = item.Value; if (value == null) { continue; } int prefab = value.GetPrefab(); if (prefab == stableHashCode || prefab == stableHashCode2) { Vector3 position = value.GetPosition(); if (AvalorRegion.InSkyMaze(position)) { _exitCache = position; _exitCacheValid = true; _exitCacheTime = Time.time; pos = position; return true; } } } _exitCacheTime = Time.time; return false; } public static void MakeItHunt(GameObject mob) { BaseAI component = mob.GetComponent(); if (component == null) { return; } component.SetHuntPlayer(hunt: true); component.m_viewRange = Mathf.Max(component.m_viewRange, 60f); component.m_hearRange = Mathf.Max(component.m_hearRange, 60f); MonsterAI monsterAI = component as MonsterAI; if (monsterAI != null) { monsterAI.m_enableHuntPlayer = true; monsterAI.m_fleeIfNotAlerted = false; monsterAI.m_fleeIfHurtWhenTargetCantBeReached = false; monsterAI.m_alertRange = Mathf.Max(monsterAI.m_alertRange, 60f); } AvalorStealthCompat.BeginRealAlertRead(); try { component.Alert(); } finally { AvalorStealthCompat.EndRealAlertRead(); } } private void SpawnMobNearPlayer(AvalorNet.PlayerPresence p, HashSet usedCells) { if (!AvalorMazeGrid.TryFindOpenSpawnNear(p.pos, p.rot, out var spawn, usedCells)) { return; } List list = new List { "Draugr", "Draugr_Elite", "Skeleton", "Skeleton_Poison", "Wraith", "Ghost", "Draugr_Ranged" }; if (MistsofAvalorPlugin.DifficultyConfig.Value == DifficultyLevel.Hardcore) { list.AddRange(new string[4] { "Charred_Melee", "Charred_Archer", "Charred_Mage", "Charred_Twitcher" }); } bool flag = Random.value < 0.02f; string text = (flag ? RevenantVariants[Random.Range(0, RevenantVariants.Length)] : list[Random.Range(0, list.Count)]); GameObject prefab = ZNetScene.instance.GetPrefab(text); if (prefab == null) { if (_reportedMissingPrefabs.Add(text)) { Logger.LogWarning((object)("[Avalor][FAIL] spawn-pool prefab '" + text + "' does not exist - every roll of it spawns NOTHING. Fix the variant list.")); } return; } GameObject gameObject = Object.Instantiate(prefab, spawn, Quaternion.identity); if (flag || Random.value < 0.15f) { AvalorMobModifier.MutateIntoSubBoss(gameObject, MistsofAvalorPlugin.DifficultyConfig.Value, flag ? 3 : 0); } Character component = gameObject.GetComponent(); ZNetView component2 = gameObject.GetComponent(); if (component2 != null && component2.IsValid() && component2.GetZDO() != null) { _activeMobIds.Add(component2.GetZDO().m_uid); } MarkAsAvalorMob(gameObject, component); if (flag) { ZNetView component3 = gameObject.GetComponent(); if (component3 != null && component3.IsValid() && component3.GetZDO() != null) { component3.GetZDO().Set("AvalorRevenant", 1); } if (component != null) { component.m_name = "$enemy_avalor_revenant"; AvalorBossBar.Instance?.Track(component, "$enemy_avalor_revenant", "Avalorian Revenant", AvalorCryptEnvZone.AvalorGreen); } AttachShortswordDrop(gameObject); if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)($"[Avalor] Avalorian Revenant ('{text}') spawned at {spawn:F0} " + $"carrying the shortsword drop at {0.6f:P0}.")); } } MakeItHunt(gameObject); if (component2 != null && component2.IsValid() && component2.GetZDO() != null && p.ownerUid != 0L) { component2.GetZDO().SetOwner(p.ownerUid); } if (!_spawnDiagLogged && component2 != null && component2.GetZDO() != null) { _spawnDiagLogged = true; bool persistent = component2.GetZDO().Persistent; string text2 = $"[Avalor] first director spawn '{text}': persistent={persistent} " + $"owner={component2.GetZDO().GetOwner()} (this session={ZDOMan.GetSessionID()})"; if (persistent) { Logger.LogInfo((object)text2); } else { Logger.LogWarning((object)("[Avalor][FAIL] " + text2 + " - NON-PERSISTENT mob ZDOs are never ownership-assigned to clients; on a dedicated server this mob will neither simulate nor be culled. The prefab's ZNetView must be persistent.")); } } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorMobModifier ---- namespace MistsofAvalor { public static class AvalorMobModifier { public const string SubBossZDOKey = "AvalorSubBoss"; public static void MutateIntoSubBoss(GameObject mob, DifficultyLevel diff, int forceLevel = 0) { Character component = mob.GetComponent(); if (!(component == null)) { component.SetLevel((forceLevel > 0) ? forceLevel : ((diff == DifficultyLevel.Hardcore) ? 3 : 2)); component.SetMaxHealth(component.GetMaxHealth() * 1.2f); int num = UnityEngine.Random.Range(0, 5); ZNetView component2 = mob.GetComponent(); if (component2 != null && component2.IsValid() && component2.GetZDO() != null) { component2.GetZDO().Set("AvalorSubBoss", num + 1); } if (num == 0) { component.SetMaxHealth(component.GetMaxHealth() * 2.5f); } switch (diff) { case DifficultyLevel.Normal: component.SetMaxHealth(component.GetMaxHealth() * 1.5f); break; case DifficultyLevel.Hardcore: component.SetMaxHealth(component.GetMaxHealth() * 2.5f); break; } component.SetHealth(component.GetMaxHealth()); ApplyLocalMutations(mob, num, diff); } } public static void ApplyLocalMutations(GameObject mob, int variant, DifficultyLevel diff) { Character component = mob.GetComponent(); if (!(component == null)) { float num = 1f; string text = null; switch (variant) { case 0: num = 1.2f; component.m_staggerDamageFactor = 0f; text = "Goliath "; break; case 1: num = 1.1f; component.m_damageModifiers.m_poison = HitData.DamageModifier.Immune; text = "Venomous "; break; case 2: num = 1.1f; component.m_damageModifiers.m_frost = HitData.DamageModifier.Immune; text = "Glacial "; break; case 3: num = 1.05f; component.m_damageModifiers.m_blunt = HitData.DamageModifier.Resistant; component.m_damageModifiers.m_slash = HitData.DamageModifier.Resistant; component.m_damageModifiers.m_pierce = HitData.DamageModifier.Resistant; text = "Vampiric "; break; case 4: num = 1.1f; component.m_damageModifiers.m_lightning = HitData.DamageModifier.Immune; component.m_runSpeed *= 1.3f; component.m_speed *= 1.3f; text = "Lightning "; break; } mob.transform.localScale *= num; AvalorianName.Apply(component); if (text != null && !component.m_name.StartsWith(text, StringComparison.Ordinal)) { component.m_name = text + component.m_name; } component.m_faction = Character.Faction.Undead; component.m_group = "Avalor_Undead"; switch (diff) { case DifficultyLevel.Normal: component.m_speed *= 1.15f; component.m_walkSpeed *= 1.15f; component.m_runSpeed *= 1.15f; break; case DifficultyLevel.Hardcore: component.m_speed *= 1.35f; component.m_walkSpeed *= 1.35f; component.m_runSpeed *= 1.35f; break; } if (mob.GetComponent() == null) { mob.AddComponent(); } CharacterDrop characterDrop = mob.GetComponent(); if (characterDrop == null) { characterDrop = mob.gameObject.AddComponent(); } GameObject gameObject = ZNetScene.instance?.GetPrefab("Avalor_Key"); if (gameObject != null) { CharacterDrop.Drop item = new CharacterDrop.Drop { m_prefab = gameObject, m_amountMin = 1, m_amountMax = 1, m_chance = 0.18f, m_levelMultiplier = false, m_dontScale = true }; characterDrop.m_drops.Add(item); } } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorMobBehavior ---- namespace MistsofAvalor { public class AvalorMobBehavior : MonoBehaviour { private BaseAI ai; private Character character; private float originalRunSpeed; private float originalWalkSpeed; private float updateTimer; private void Start() { ai = GetComponent(); character = GetComponent(); if (character != null) { originalRunSpeed = character.m_runSpeed; originalWalkSpeed = character.m_speed; } } private void Update() { updateTimer += Time.deltaTime; if (updateTimer < 1f) { return; } updateTimer = 0f; if (ai != null && character != null) { if (ai.IsAlerted()) { character.m_runSpeed = originalRunSpeed; } else { character.m_runSpeed = originalWalkSpeed; } } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorPathLeveler ---- namespace MistsofAvalor { public class AvalorPathLeveler : MonoBehaviour { public float LevelRadius = 4f; public float SmoothRadius = 16f; private void Start() { if (!(ZNet.instance == null) && ZNet.instance.IsServer()) { TerrainModifier terrainModifier = base.gameObject.AddComponent(); terrainModifier.m_level = true; terrainModifier.m_levelRadius = LevelRadius; terrainModifier.m_smooth = true; terrainModifier.m_smoothRadius = SmoothRadius; terrainModifier.m_square = false; terrainModifier.m_paintCleared = true; terrainModifier.m_paintRadius = LevelRadius + 1f; } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorPortal ---- namespace MistsofAvalor { public class AvalorPortal : MonoBehaviour { public static void SpawnPortal(Vector3 position) { GameObject gameObject = ZNetScene.instance?.GetPrefab("portal_stone"); if (gameObject != null) { float height = position.y; if (ZoneSystem.instance != null && ZoneSystem.instance.GetGroundHeight(position, out height)) { position.y = height; } GameObject obj = Object.Instantiate(gameObject, position, Quaternion.identity); if (MistsofAvalorPlugin.DebugLogging.Value) { Vector3 vector = position; Logger.LogInfo((object)("Spawned Avalor Portal at " + vector.ToString())); } obj.AddComponent(); } } public static void TeleportToAvalor(Player player) { if (AvalorMazeGenerator.Instance != null) { AvalorMazeGenerator.Instance.StartCoroutine(TeleportRoutine(player)); } else { player.TeleportTo(AvalorMazeGenerator.MazeCenter, player.transform.rotation, distantTeleport: true); } } private static IEnumerator TeleportRoutine(Player player) { if (ZNet.instance != null && !ZNet.instance.IsServer()) { player.Message(MessageHud.MessageType.Center, "Entering the Mists of Avalor..."); ZRoutedRpc.instance?.InvokeRoutedRPC("wubarrk.mistsofavalor.EnsureMaze"); yield return new WaitForSeconds(1.5f); Vector3 vector = AvalorMazeGenerator.ComputeStartLocation(); AvalorSafePlatform.Drop(vector, 8f, 30f); player.TeleportTo(vector, player.transform.rotation, distantTeleport: true); yield break; } if (!AvalorMazeGenerator.MazeExistsInWorld()) { player.Message(MessageHud.MessageType.Center, "The mists gather...\nThe labyrinth is being forged. Hold fast."); AvalorMazeGenerator.Instance.StartCoroutine(AvalorMazeGenerator.Instance.GenerateMaze()); } else { player.Message(MessageHud.MessageType.Center, "Entering the Mists of Avalor..."); AvalorMazeGenerator.Instance.RecoverSpawnedZDOs(); } yield return new WaitUntil(() => !AvalorMazeGenerator.Instance.IsGenerating); Vector3 vector2 = ((AvalorMazeGenerator.StartLocation != Vector3.zero) ? AvalorMazeGenerator.StartLocation : AvalorMazeGenerator.ComputeStartLocation()); AvalorSafePlatform.Drop(vector2, 8f, 12f); player.TeleportTo(vector2, player.transform.rotation, distantTeleport: true); } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorPortalWalkTrigger ---- namespace MistsofAvalor { public class AvalorPortalWalkTrigger : MonoBehaviour { private const float ChargeTime = 1.1f; private const float ReArmDelay = 6f; private static float s_suppressUntil; private float _charge; private bool _fired; public static void Suppress() { s_suppressUntil = Time.time + 6f; } private void OnTriggerExit(Collider other) { if (IsLocalPlayer(other)) { _charge = 0f; } } private void OnTriggerStay(Collider other) { if (_fired || Time.time < s_suppressUntil || !IsLocalPlayer(other)) { return; } Player localPlayer = Player.m_localPlayer; if (localPlayer == null) { return; } _charge += Time.deltaTime; AvalorPortalRuneVFX componentInParent = GetComponentInParent(); if (componentInParent != null) { componentInParent.Charge = Mathf.Clamp01(_charge / 1.1f); } if (_charge < 1.1f) { localPlayer.Message(MessageHud.MessageType.Center, "The mists reach for you..."); return; } _fired = true; Suppress(); if (componentInParent != null) { componentInParent.Discharge(); } AvalorPortalTrigger.Travel(localPlayer, base.transform.position); _charge = 0f; Invoke("ReArm", 6f); } private void ReArm() { _fired = false; } private static bool IsLocalPlayer(Collider other) { if (other == null || Player.m_localPlayer == null) { return false; } Player componentInParent = other.GetComponentInParent(); if (componentInParent != null) { return componentInParent == Player.m_localPlayer; } return false; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorPortalGlow ---- namespace MistsofAvalor { public class AvalorPortalGlow : MonoBehaviour { private Light _light; private float _base; private void Awake() { _light = GetComponent(); if (_light != null) { _base = _light.intensity; } } private void Update() { if (!(_light == null)) { float time = Time.time; float num = 0.82f + 0.18f * Mathf.Sin(time * 1.6f) + 0.08f * Mathf.Sin(time * 0.53f); _light.intensity = _base * num; } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorPortalTrigger ---- namespace MistsofAvalor { public class AvalorPortalTrigger : MonoBehaviour, Interactable, Hoverable { public static void Travel(Player player, Vector3 gatePos) { if (!(player == null)) { if (AvalorRegion.InSkyMaze(gatePos)) { player.Message(MessageHud.MessageType.Center, "Returning to the Waking World..."); Vector3 pos2; Vector3 pos = ((!(ZoneSystem.instance != null) || !ZoneSystem.instance.GetLocationIcon("StartTemple", out pos2)) ? new Vector3(0f, 50f, 0f) : (pos2 + new Vector3(0f, 1.5f, 0f))); AvalorPortalWalkTrigger.Suppress(); player.TeleportTo(pos, player.transform.rotation, distantTeleport: false); } else { AvalorPortalWalkTrigger.Suppress(); AvalorPortal.TeleportToAvalor(player); } } } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold || alt) { return false; } Player player = user as Player; if (player == null) { return false; } Travel(player, base.transform.position); return true; } public bool UseItem(Humanoid user, ItemDrop.ItemData item) { return false; } public string GetHoverText() { if (AvalorRegion.InSkyMaze(base.transform.position)) { return Localization.instance.Localize("Step through to return to the Waking World\n[$KEY_Use] Return now"); } return Localization.instance.Localize("Step through to enter the Mists of Avalor\n[$KEY_Use] Enter now"); } public string GetHoverName() { return "Decrepit Avalor Portal"; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorPortalSpawner ---- namespace MistsofAvalor { public class AvalorPortalSpawner : MonoBehaviour { private const float TorchSink = 0.06f; private const float TorchSpacing = 10f; private const int TorchSetsSkipped = 4; private const float WaterLevel = 30f; private const float DryMargin = 1.2f; private const float FlatSampleR = 6f; private const float FlatThreshold = 2.5f; private const float StoneMarginD = 12f; private const float PathLevelerReach = 16f; private const float PortalMinDist = 68f; private const float PortalMaxDist = 96f; private const float PathCorridorHalfWidth = 5.5f; private void Start() { StartCoroutine(SpawnRoutine()); } private IEnumerator SpawnRoutine() { while (ZNet.instance == null || ZNetScene.instance == null) { yield return new WaitForSeconds(1f); } yield return new WaitForSeconds(2f); AvalorNet.EnsureRegistered(); Vector3 pos; ZDO zdo; bool portalExists = AvalorNet.TryFindOverworldPortalNearTemple(out pos, out zdo); if (!MistsofAvalorPlugin.SpawnPortalAtStart.Value) { if (!portalExists) { yield break; } if (AvalorNet.IsServerRole) { if (zdo != null) { if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"Avalor Labyrinth: Config set to false. Destroying existing Overworld Portal at spawn."); } if (!zdo.IsOwner()) { zdo.SetOwner(ZDOMan.GetSessionID()); } ZDOMan.instance.DestroyZDO(zdo); } } else { AvalorNet.RequestPortalSite(); } yield break; } GameObject portalPrefab = ZNetScene.instance.GetPrefab("Avalor_OverworldPortal"); if (portalPrefab == null) { yield break; } Vector3 spawnPos = Vector3.zero; float waitT; switch (AvalorNet.Role) { default: yield break; case AvalorNet.NetRole.PlayerHost: AvalorNet.ServerMarkPortalSiting(); break; case AvalorNet.NetRole.Client: if (portalExists) { yield break; } AvalorNet.RequestPortalSite(); for (waitT = 0f; waitT < 10f; waitT += 0.5f) { if (AvalorNet.PortalGrantAnswered) { break; } yield return new WaitForSeconds(0.5f); } if (!AvalorNet.PortalSiteGranted) { if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"[Avalor] portal siting not granted to this client (exists, claimed elsewhere, or no answer)."); } yield break; } Logger.LogInfo((object)"[Avalor] portal siting GRANTED to this client - building the temple site locally."); break; } if (portalExists) { yield break; } if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)$"Avalor Labyrinth: siting the Overworld Portal {68f:F0}-{96f:F0}m from StartTemple."); } if (!TryFindPortalPlacement(base.transform.position, out spawnPos, out var pathDir3)) { spawnPos = base.transform.position - base.transform.forward * 78f; GroundAt(spawnPos, out spawnPos); pathDir3 = FlatDir(base.transform.position, spawnPos); if (spawnPos.y < 30.5f) { spawnPos.y = 30.5f; } Logger.LogWarning((object)"[Avalor] Portal placement search failed; used raised fallback pad."); } float y = spawnPos.y; spawnPos.y = AvalorTerrainOps.MatchedGroundHeight(spawnPos, 6f, 52f); if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)($"[Avalor] Portal placed at {spawnPos:F0} (natural {y:F1} -> matched " + $"{spawnPos.y:F1}, water {30f}).")); } GameObject prefab = ZNetScene.instance.GetPrefab("Avalor_PortalDecor"); if (prefab != null) { UnityEngine.Object.Instantiate(prefab, spawnPos, Quaternion.identity); } yield return new WaitForSeconds(1.5f); Quaternion rotation = Quaternion.LookRotation(-pathDir3); UnityEngine.Object.Instantiate(portalPrefab, spawnPos, rotation); if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"Avalor Labyrinth: Spawning spooky geometric decor around Overworld Portal."); } ClearAreaVegetation(spawnPos, 24f, base.transform.position); Vector3 dirToPortal = (spawnPos - base.transform.position).normalized; Vector3 sidePerp = Vector3.Cross(dirToPortal, Vector3.up).normalized; int spooky = 0; GameObject gameObject = ZNetScene.instance.GetPrefab("paved_road_v2") ?? ZNetScene.instance.GetPrefab("paved_road"); GameObject ghostTorch = ZNetScene.instance.GetPrefab("Avalor_CryptTorch") ?? ZNetScene.instance.GetPrefab("piece_groundtorch_blue"); GameObject prefab2 = ZNetScene.instance.GetPrefab("Avalor_PathLeveler"); waitT = Vector3.Distance(base.transform.position, spawnPos); GroundAt(base.transform.position, out var grounded); float y2 = grounded.y; float y3 = spawnPos.y; ComputeStoneGap(base.transform.position, out var _, out var _, out var ringRadius); float startD = Mathf.Max(8f, ringRadius + 3f); float num = ringRadius + 16f + 4f; float torchStartD = Mathf.Max(num, startD + 40f); Quaternion torchFacing = Quaternion.LookRotation(sidePerp); for (float num2 = startD; num2 < waitT - 3f; num2 += 2f) { float t = Mathf.Clamp01(num2 / waitT); float num3 = Mathf.Lerp(y2, y3, t); float t2 = Mathf.Clamp01((num2 - startD) / Mathf.Max(1f, num - startD)); Vector3 vector = base.transform.position + dirToPortal * num2; GroundAt(vector, out var grounded2); vector.y = Mathf.Lerp(grounded2.y, num3, t2); if (prefab2 != null && num2 >= num && (int)num2 % 4 == 0) { UnityEngine.Object.Instantiate(prefab2, new Vector3(vector.x, num3, vector.z), Quaternion.identity); } if (num2 < waitT - 10f) { ClearObstacles(vector, 5.5f); } if (gameObject != null) { SpawnDecor(gameObject, vector, Quaternion.LookRotation(dirToPortal)); } } yield return new WaitForSeconds(2.5f); if (ghostTorch != null) { for (float num4 = torchStartD; num4 < waitT - 3f; num4 += 10f) { Vector3 vector2 = base.transform.position + dirToPortal * num4; SpawnDecor(ghostTorch, TorchSeat(vector2 + sidePerp * 2.6f), torchFacing, alwaysLit: true); spooky++; SpawnDecor(ghostTorch, TorchSeat(vector2 - sidePerp * 2.6f), torchFacing, alwaysLit: true); spooky++; } } GameObject prefab3 = ZNetScene.instance.GetPrefab("SwampTree1"); if (prefab3 != null) { for (int i = 0; i < 13; i++) { Vector3 grounded3 = Vector3.zero; bool flag = false; for (int j = 0; j < 8; j++) { if (flag) { break; } float f = ((float)i * 27.692308f + UnityEngine.Random.Range(-14f, 14f) + (float)j * 9f) * (MathF.PI / 180f); float num5 = UnityEngine.Random.Range(16f, 22f); Vector3 vector3 = spawnPos + new Vector3(Mathf.Cos(f) * num5, 0f, Mathf.Sin(f) * num5); if (!InPathCorridor(vector3, spawnPos, dirToPortal, sidePerp)) { grounded3 = vector3; flag = true; } } if (flag) { GroundAt(grounded3, out grounded3); grounded3.y -= 2.2f; SpawnDecor(prefab3, grounded3, Quaternion.Euler(0f, UnityEngine.Random.Range(0, 360), 0f)); spooky++; } } } GameObject prefab4 = ZNetScene.instance.GetPrefab("stone_wall_2x2"); if (prefab4 != null) { Vector2[] array = new Vector2[6] { new Vector2(9f, -5f), new Vector2(-9f, -5f), new Vector2(13f, 3f), new Vector2(-13f, 3f), new Vector2(10f, 13f), new Vector2(-10f, 13f) }; for (int k = 0; k < array.Length; k++) { Vector3 pos2 = SitePoint(spawnPos, dirToPortal, sidePerp, array[k].x, array[k].y); pos2.y -= 0.45f + (float)(k % 3) * 0.18f; float y4 = ((array[k].x > 0f) ? 1f : (-1f)) * (18f + (float)(k % 3) * 9f); SpawnDecor(prefab4, pos2, Quaternion.Euler(6f - (float)(k % 3) * 4f, y4, (k % 2 == 0) ? 5f : (-5f))); spooky++; } } GameObject prefab5 = ZNetScene.instance.GetPrefab("MountainGraveStone01"); if (prefab5 != null) { for (int l = 0; l < 7; l++) { bool flag2 = l % 2 == 0; float lateral = (flag2 ? 12f : (-12f)); float along = 6f + (float)(l / 2) * 4f; Vector3 pos3 = SitePoint(spawnPos, dirToPortal, sidePerp, lateral, along); Quaternion quaternion = Quaternion.LookRotation(flag2 ? (-sidePerp) : sidePerp); SpawnDecor(prefab5, pos3, quaternion * Quaternion.Euler(7f - (float)(l % 3) * 5f, 0f, flag2 ? 4f : (-4f))); spooky++; } } string[] array2 = new string[4] { "BoneFragments", "BoneFragments", "BoneFragments", "BoneFragments" }; foreach (string text in array2) { GameObject prefab6 = ZNetScene.instance.GetPrefab(text); if (!(prefab6 == null)) { float f2 = UnityEngine.Random.Range(0f, MathF.PI * 2f); float num6 = UnityEngine.Random.Range(5f, 10f); Vector3 grounded4 = spawnPos + new Vector3(Mathf.Cos(f2) * num6, 0f, Mathf.Sin(f2) * num6); GroundAt(grounded4, out grounded4); SpawnDecor(prefab6, grounded4, Quaternion.Euler(0f, UnityEngine.Random.Range(0, 360), 0f)); spooky++; } } GameObject gameObject2 = ZNetScene.instance.GetPrefab("Avalor_Mist") ?? ZNetScene.instance.GetPrefab("vfx_swamp_mist"); if (gameObject2 != null) { for (int n = 0; n < 5; n++) { float f3 = UnityEngine.Random.Range(0f, MathF.PI * 2f); float num7 = UnityEngine.Random.Range(0.5f, 3.5f); Vector3 pos4 = spawnPos + new Vector3(Mathf.Cos(f3) * num7, 0.1f, Mathf.Sin(f3) * num7); SpawnDecor(gameObject2, pos4, Quaternion.identity); spooky++; } } if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)$"Avalor Labyrinth: portal area dressed with {spooky} spooky decor pieces."); } for (int pass = 0; pass < 4; pass++) { yield return new WaitForSeconds(3f); ClearAreaVegetation(spawnPos, 24f, base.transform.position); for (float num8 = startD; num8 < waitT - 10f; num8 += 4f) { GroundAt(base.transform.position + dirToPortal * num8, out var grounded5); ClearObstacles(grounded5, 5.5f); } } if (MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)"Avalor Labyrinth: portal clearing re-swept for streamed-in vegetation."); } if (AvalorNet.Role == AvalorNet.NetRole.Client) { AvalorNet.NotifyPortalSited(); } } private GameObject SpawnDecor(GameObject prefab, Vector3 pos, Quaternion rot, bool alwaysLit = false) { if (prefab == null) { return null; } GameObject obj = UnityEngine.Object.Instantiate(prefab, pos, rot); ZNetView component = obj.GetComponent(); if (component != null) { component.m_persistent = true; if (component.GetZDO() != null) { component.GetZDO().Set("AvalorDecor", 1); if (alwaysLit) { component.GetZDO().Set("AvalorAlwaysLit", 1); } } } return obj; } private void ClearAreaVegetation(Vector3 center, float radius, Vector3 stones) { Collider[] array = Physics.OverlapSphere(center, radius); for (int i = 0; i < array.Length; i++) { ZNetView componentInParent = array[i].GetComponentInParent(); if (!(componentInParent == null) && componentInParent.IsValid() && componentInParent.GetZDO() != null && !IsProtected(componentInParent) && componentInParent.GetZDO().GetInt("AvalorDecor") != 1) { string ln = Utils.GetPrefabName(componentInParent.gameObject).ToLower(); if (!(Vector3.Distance(componentInParent.transform.position, stones) < 12f) && IsClearableVegetation(ln)) { ZDOMan.instance.DestroyZDO(componentInParent.GetZDO()); } } } } private static bool IsProtected(ZNetView view) { if (view.GetComponentInChildren(includeInactive: true) != null) { return true; } if (view.GetComponentInChildren(includeInactive: true) != null) { return true; } if (view.GetComponentInChildren(includeInactive: true) != null) { return true; } string text = Utils.GetPrefabName(view.gameObject).ToLower(); if (!text.Contains("tombstone") && !text.Contains("bossstone") && !text.Contains("starttemple") && !text.Contains("runestone")) { return text.Contains("portal"); } return true; } private static bool IsClearableVegetation(string ln) { if (!ln.Contains("tree") && !ln.Contains("beech") && !ln.Contains("birch") && !ln.Contains("oak") && !ln.Contains("fir") && !ln.Contains("pine") && !ln.Contains("rock") && !ln.Contains("bush") && !ln.Contains("shrub") && !ln.Contains("stone") && !ln.Contains("log") && !ln.Contains("stump") && !ln.Contains("copper") && !ln.Contains("tin") && !ln.Contains("ore") && !ln.Contains("pickable") && !ln.Contains("mushroom") && !ln.Contains("beehive") && !ln.Contains("spawner") && !ln.Contains("stubbe") && !ln.Contains("vines") && !ln.Contains("cloudberry") && !ln.Contains("raspberry") && !ln.Contains("blueberry")) { return ln.Contains("bramble"); } return true; } private void ClearObstacles(Vector3 pos, float radius) { Collider[] array = Physics.OverlapSphere(pos, radius); for (int i = 0; i < array.Length; i++) { ZNetView componentInParent = array[i].GetComponentInParent(); if (!(componentInParent == null) && componentInParent.IsValid() && componentInParent.GetZDO() != null && !IsProtected(componentInParent) && componentInParent.GetZDO().GetInt("AvalorDecor") != 1) { string text = Utils.GetPrefabName(componentInParent.gameObject).ToLower(); if (text.Contains("tree") || text.Contains("beech") || text.Contains("birch") || text.Contains("oak") || text.Contains("fir") || text.Contains("pine") || text.Contains("rock") || text.Contains("bush") || text.Contains("shrub") || text.Contains("stone") || text.Contains("log") || text.Contains("stump") || text.Contains("stubbe")) { ZDOMan.instance.DestroyZDO(componentInParent.GetZDO()); } } } } private Vector3 TorchSeat(Vector3 xz) { GroundAt(xz, out var grounded); xz.y = grounded.y - 0.06f; return xz; } private void GroundAt(Vector3 xz, out Vector3 grounded) { grounded = xz; if (ZoneSystem.instance != null && ZoneSystem.instance.GetGroundHeight(new Vector3(xz.x, 0f, xz.z), out var height)) { grounded.y = height; } else if (WorldGenerator.instance != null) { grounded.y = WorldGenerator.instance.GetHeight(xz.x, xz.z); } } private bool TryFindPortalPlacement(Vector3 center, out Vector3 portalPos, out Vector3 pathDir) { ComputeStoneGap(center, out var gapCenter, out var gapHalf, out var _); float num = Mathf.Max(0f, gapHalf - MathF.PI / 15f); float[] array = new float[6] { 78f, 84f, 72f, 90f, 68f, 96f }; float[] array2 = new float[5] { 0f, 0.5f, -0.5f, 1f, -1f }; Vector3 vector = Vector3.zero; float num2 = float.MaxValue; float[] array3 = array; for (int i = 0; i < array3.Length; i++) { float num3 = Mathf.Clamp(array3[i], 68f, 96f); float[] array4 = array2; foreach (float num4 in array4) { float f = gapCenter + num4 * num; Vector3 grounded = center + new Vector3(Mathf.Cos(f), 0f, Mathf.Sin(f)) * num3; GroundAt(grounded, out grounded); float num5 = ScoreSpot(grounded); if (num5 < num2) { num2 = num5; vector = grounded; } if (num5 <= 0f) { portalPos = grounded; pathDir = FlatDir(center, grounded); return true; } } } if (num2 > 0f) { for (int k = 0; k < 360; k += 12) { float num6 = (float)k * (MathF.PI / 180f); if (BearingHitsStone(center, num6)) { continue; } array3 = array; for (int i = 0; i < array3.Length; i++) { float num7 = Mathf.Clamp(array3[i], 68f, 96f); Vector3 grounded2 = center + new Vector3(Mathf.Cos(num6), 0f, Mathf.Sin(num6)) * num7; GroundAt(grounded2, out grounded2); float num8 = ScoreSpot(grounded2); if (num8 < num2) { num2 = num8; vector = grounded2; } } } } portalPos = vector; pathDir = FlatDir(center, vector); return vector != Vector3.zero; } private float ScoreSpot(Vector3 pos) { float num = 0f; if (pos.y < 31.2f) { num += 1000f + (31.2f - pos.y) * 50f; } float num2 = Flatness(pos); if (num2 > 2.5f) { num += 100f + (num2 - 2.5f) * 20f; } return num; } private float Flatness(Vector3 c) { float a = float.MaxValue; float a2 = float.MinValue; for (int i = 0; i < 8; i++) { float f = (float)i * (MathF.PI / 4f); Vector3 grounded = c + new Vector3(Mathf.Cos(f), 0f, Mathf.Sin(f)) * 6f; GroundAt(grounded, out grounded); a = Mathf.Min(a, grounded.y); a2 = Mathf.Max(a2, grounded.y); } a = Mathf.Min(a, c.y); a2 = Mathf.Max(a2, c.y); return a2 - a; } private Vector3 SitePoint(Vector3 portal, Vector3 dirToPortal, Vector3 sidePerp, float lateral, float along) { Vector3 grounded = portal + sidePerp * lateral - dirToPortal * along; GroundAt(grounded, out grounded); return grounded; } private static bool InPathCorridor(Vector3 p, Vector3 portal, Vector3 dirToPortal, Vector3 sidePerp) { Vector3 lhs = p - portal; lhs.y = 0f; if (Vector3.Dot(lhs, dirToPortal) > 0f) { return false; } return Mathf.Abs(Vector3.Dot(lhs, sidePerp)) < 5.5f; } private Vector3 FlatDir(Vector3 from, Vector3 to) { Vector3 vector = to - from; vector.y = 0f; if (!(vector.sqrMagnitude > 0.001f)) { return Vector3.forward; } return vector.normalized; } private void ComputeStoneGap(Vector3 center, out float gapCenter, out float gapHalf, out float ringRadius) { List list = new List(); ringRadius = 8f; Collider[] array = Physics.OverlapSphere(center, 13f); for (int i = 0; i < array.Length; i++) { ZNetView componentInParent = array[i].GetComponentInParent(); if (!(componentInParent == null) && componentInParent.IsValid() && IsRingStone(Utils.GetPrefabName(componentInParent.gameObject))) { Vector3 vector = componentInParent.transform.position - center; vector.y = 0f; if (!(vector.sqrMagnitude < 4f)) { list.Add(Mathf.Atan2(vector.z, vector.x)); ringRadius = Mathf.Max(ringRadius, vector.magnitude); } } } if (list.Count < 2) { Vector3 vector2 = -base.transform.forward; vector2.y = 0f; vector2 = ((vector2.sqrMagnitude > 0.001f) ? vector2.normalized : Vector3.forward); gapCenter = Mathf.Atan2(vector2.z, vector2.x); gapHalf = 0.43633232f; return; } list.Sort(); float num = -1f; float num2 = 0f; for (int j = 0; j < list.Count; j++) { float num3 = list[j]; float num4 = ((j + 1 < list.Count) ? list[j + 1] : (list[0] + MathF.PI * 2f)); float num5 = num4 - num3; if (num5 > num) { num = num5; num2 = (num3 + num4) * 0.5f; } } gapCenter = num2; gapHalf = num * 0.5f; } private bool BearingHitsStone(Vector3 center, float bearing) { float current = bearing * 57.29578f; Collider[] array = Physics.OverlapSphere(center, 13f); for (int i = 0; i < array.Length; i++) { ZNetView componentInParent = array[i].GetComponentInParent(); if (componentInParent == null || !componentInParent.IsValid() || !IsRingStone(Utils.GetPrefabName(componentInParent.gameObject))) { continue; } Vector3 vector = componentInParent.transform.position - center; vector.y = 0f; if (!(vector.sqrMagnitude < 4f)) { float target = Mathf.Atan2(vector.z, vector.x) * 57.29578f; if (Mathf.Abs(Mathf.DeltaAngle(current, target)) < 12f) { return true; } } } return false; } private bool IsRingStone(string prefabName) { string text = prefabName.ToLower(); if (!text.Contains("runestone") && !text.Contains("rock_3") && !text.Contains("bossstone") && !text.Contains("stonepillar")) { return text.Contains("stone_pile"); } return true; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorTerrainFlattener ---- namespace MistsofAvalor { public class AvalorTerrainFlattener : MonoBehaviour { public const float TempleProtectRadius = 16f; private void Start() { if (!(ZNet.instance == null) && ZNet.instance.IsServer()) { Vector3 pos = Vector3.zero; float keepOutRadius = 0f; if (ZoneSystem.instance != null && ZoneSystem.instance.GetLocationIcon("StartTemple", out pos)) { keepOutRadius = 16f; } AvalorTerrainOps.Apply(base.gameObject, base.transform.position, 22f, 52f, 30f, pos, keepOutRadius); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorTerrainOps ---- namespace MistsofAvalor { public static class AvalorTerrainOps { private const float PadSpacing = 6f; private const float RingSpacing = 12f; private const float OpLevelRadius = 7f; private const float OpSmoothRadius = 11f; private const float WaterLevel = 30f; public static void Apply(GameObject host, Vector3 centre, float levelRadius, float smoothRadius, float paintRadius) { Apply(host, centre, levelRadius, smoothRadius, paintRadius, Vector3.zero, 0f); } public static void Apply(GameObject host, Vector3 centre, float levelRadius, float smoothRadius, float paintRadius, Vector3 keepOutCentre, float keepOutRadius) { if (!(ZNet.instance == null) && ZNet.instance.IsServer()) { float fence = ((keepOutRadius > 0f) ? (keepOutRadius + 11f) : 0f); int num = 0; num += Emit(host, centre, 0f, levelRadius, 6f, level: true, paintRadius, keepOutCentre, fence); num += Emit(host, centre, levelRadius, smoothRadius, 12f, level: false, paintRadius, keepOutCentre, fence); if (MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value) { Vector2i zone = ZoneSystem.GetZone(centre); Logger.LogInfo((object)($"[Avalor] terrain: {num} op(s) spanning {ZonesCovered(centre, smoothRadius)} zone(s) " + $"at {centre:F0} (zone {zone.x},{zone.y}); level r={levelRadius} smooth r={smoothRadius}.")); } } } private static int Emit(GameObject host, Vector3 centre, float innerRadius, float outerRadius, float spacing, bool level, float paintRadius, Vector3 keepOutCentre, float fence) { int num = Mathf.CeilToInt(outerRadius / spacing); int num2 = 0; for (int i = -num; i <= num; i++) { for (int j = -num; j <= num; j++) { Vector3 vector = new Vector3((float)i * spacing, 0f, (float)j * spacing); float magnitude = vector.magnitude; if (magnitude > outerRadius || (innerRadius > 0f && magnitude <= innerRadius - 7f)) { continue; } Vector3 position = centre + vector; if (fence > 0f) { float num3 = position.x - keepOutCentre.x; float num4 = position.z - keepOutCentre.z; if (num3 * num3 + num4 * num4 < fence * fence) { continue; } } GameObject gameObject = new GameObject("AvalorTerrainOp"); gameObject.transform.SetParent(host.transform, worldPositionStays: false); gameObject.transform.position = position; TerrainModifier terrainModifier = gameObject.AddComponent(); terrainModifier.m_level = level; terrainModifier.m_levelRadius = (level ? 7f : 0f); terrainModifier.m_smooth = true; terrainModifier.m_smoothRadius = 11f; terrainModifier.m_square = false; terrainModifier.m_paintCleared = magnitude <= paintRadius; terrainModifier.m_paintRadius = ((magnitude <= paintRadius) ? 8f : 0f); num2++; } } return num2; } public static float MatchedGroundHeight(Vector3 centre, float innerRadius, float outerRadius) { if (ZoneSystem.instance == null) { return centre.y; } double num = 0.0; double num2 = 0.0; for (float num3 = innerRadius; num3 <= outerRadius + 0.01f; num3 += 4f) { int num4 = Mathf.Max(8, Mathf.RoundToInt(num3)); float num5 = Mathf.Max(0.25f, num3 / Mathf.Max(outerRadius, 1f)); for (int i = 0; i < num4; i++) { float y = 360f / (float)num4 * (float)i; Vector3 p = centre + Quaternion.Euler(0f, y, 0f) * (Vector3.forward * num3); if (ZoneSystem.instance.GetGroundHeight(p, out var height) && !(height <= 30f)) { num += (double)(height * num5); num2 += (double)num5; } } } if (num2 <= 0.0) { return centre.y; } return Mathf.Max((float)(num / num2), 31.2f); } private static int ZonesCovered(Vector3 centre, float radius) { HashSet hashSet = new HashSet(); Vector2i zone = ZoneSystem.GetZone(centre); hashSet.Add(((long)zone.x << 32) ^ (uint)zone.y); for (float num = 0f; num < 360f; num += 45f) { Vector2i zone2 = ZoneSystem.GetZone(centre + Quaternion.Euler(0f, num, 0f) * (Vector3.forward * radius)); hashSet.Add(((long)zone2.x << 32) ^ (uint)zone2.y); } return hashSet.Count; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorAssetManager ---- namespace MistsofAvalor { public class AvalorAssetManager { private const float TorchHeight = 1.9f; private const float TorchSkullAt = 0.9f; private const float CELL = 6f; private const float STOREY = 4f; private const float SLAB = 0.4f; private const float WALL_HALF = 0.4f; private const float SEAM = 0.4f; private const float WALL_OVERHANG = 0.90000004f; private const float WALL_BLEED = 0.3f; private const float PortalHeight = 7f; public static readonly Color AvalorGreen = new Color(0.3f, 1f, 0.55f); private const float PortalGap = 3.2f; private const float PortalToeIn = 6f; private static readonly Dictionary _detailSign = new Dictionary(); public static void Init() { PrefabManager.OnVanillaPrefabsAvailable += ClonePrefabs; ZoneManager.OnVanillaLocationsAvailable += RegisterLocations; } private static void ClonePrefabs() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Expected O, but got Unknown //IL_03fb: Unknown result type (might be due to invalid IL or missing references) //IL_0405: Expected O, but got Unknown //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_0466: Expected O, but got Unknown //IL_056d: Unknown result type (might be due to invalid IL or missing references) //IL_0577: Expected O, but got Unknown //IL_0684: Unknown result type (might be due to invalid IL or missing references) //IL_068e: Expected O, but got Unknown //IL_0716: Unknown result type (might be due to invalid IL or missing references) //IL_0720: Expected O, but got Unknown //IL_080a: Unknown result type (might be due to invalid IL or missing references) //IL_0814: Expected O, but got Unknown //IL_0d82: Unknown result type (might be due to invalid IL or missing references) //IL_0d8c: Expected O, but got Unknown //IL_07ca: Unknown result type (might be due to invalid IL or missing references) //IL_07d4: Expected O, but got Unknown //IL_08d0: Unknown result type (might be due to invalid IL or missing references) //IL_08da: Expected O, but got Unknown //IL_09c1: Unknown result type (might be due to invalid IL or missing references) //IL_09cb: Expected O, but got Unknown AvalorKitBundle.Load(); GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_Wall", "stone_wall_2x1"); if (gameObject != null) { InfuseCryptMaterial(gameObject); } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); GameObject gameObject2 = PrefabManager.Instance.CreateClonedPrefab("Avalor_Floor", "stone_floor_2x2"); if (gameObject2 != null) { InfuseCryptMaterial(gameObject2); } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject2, false)); GameObject gameObject3 = PrefabManager.Instance.CreateClonedPrefab("Avalor_Stair", "wood_stair"); if (gameObject3 != null) { InfuseCryptMaterial(gameObject3); } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject3, false)); GameObject gameObject4 = PrefabManager.Instance.CreateEmptyPrefab("Avalor_MegaFloor", true); UnityEngine.Object.DestroyImmediate(gameObject4.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject4.GetComponent()); StripColliders(gameObject4); gameObject4.layer = 10; gameObject4.GetComponent().m_persistent = true; BoxCollider boxCollider = gameObject4.AddComponent(); boxCollider.size = new Vector3(30f, 0.5f, 30f); boxCollider.center = new Vector3(0f, -0.25f, 0f); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject4, false)); GameObject gameObject5 = PrefabManager.Instance.CreateEmptyPrefab("Avalor_CompositeWall", true); UnityEngine.Object.DestroyImmediate(gameObject5.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject5.GetComponent()); gameObject5.layer = 10; gameObject5.GetComponent().m_persistent = true; BoxCollider boxCollider2 = gameObject5.AddComponent(); boxCollider2.size = new Vector3(6f, 4f, 0.5f); boxCollider2.center = new Vector3(0f, 2f, 0f); if (gameObject != null) { List list = new List(); for (float num = -2f; num <= 2f; num += 2f) { for (float num2 = 0f; num2 <= 3f; num2 += 1f) { list.Add(Matrix4x4.TRS(new Vector3(num, num2, 0f), Quaternion.identity, Vector3.one)); } } CombineTileMeshes(gameObject, gameObject5, list); } DressCompositeWithAuthored(gameObject5, "wallA", 'W'); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject5, false)); CreateWallVariant("Avalor_CompositeWallB", "wallB", gameObject); CreateWallVariant("Avalor_CompositeWallC", "wallC", gameObject); GameObject gameObject6 = PrefabManager.Instance.CreateEmptyPrefab("Avalor_CompositeStair", true); UnityEngine.Object.DestroyImmediate(gameObject6.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject6.GetComponent()); gameObject6.layer = 10; gameObject6.GetComponent().m_persistent = true; if (gameObject3 != null && gameObject2 != null) { List list2 = new List(); list2.Add(Matrix4x4.TRS(new Vector3(0f, 0f, 0f), Quaternion.identity, Vector3.one)); list2.Add(Matrix4x4.TRS(new Vector3(0f, 2f, 2f), Quaternion.identity, Vector3.one)); list2.Add(Matrix4x4.TRS(new Vector3(2f, 0f, 0f), Quaternion.identity, Vector3.one)); list2.Add(Matrix4x4.TRS(new Vector3(2f, 2f, 2f), Quaternion.identity, Vector3.one)); CombineTileMeshes(gameObject3, gameObject6, list2); List list3 = new List(); for (float num3 = 0f; num3 <= 4f; num3 += 2f) { for (float num4 = 0f; num4 <= 4f; num4 += 2f) { if (!(num3 < 4f) || !(num4 < 4f)) { list3.Add(Matrix4x4.TRS(new Vector3(num3, 4f, num4), Quaternion.identity, Vector3.one)); } } } CombineTileMeshes(gameObject2, gameObject6, list3); } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject6, false)); GameObject gameObject7 = PrefabManager.Instance.CreateEmptyPrefab("Avalor_MazeChunk", true); UnityEngine.Object.DestroyImmediate(gameObject7.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject7.GetComponent()); StripColliders(gameObject7); gameObject7.layer = 10; gameObject7.GetComponent().m_persistent = true; gameObject7.AddComponent(); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject7, false)); GameObject gameObject8 = PrefabManager.Instance.CreateEmptyPrefab("Avalor_CompositeFloor", true); UnityEngine.Object.DestroyImmediate(gameObject8.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject8.GetComponent()); gameObject8.layer = 10; gameObject8.GetComponent().m_persistent = true; BoxCollider boxCollider3 = gameObject8.AddComponent(); boxCollider3.size = new Vector3(6f, 0.5f, 6f); boxCollider3.center = new Vector3(0f, 0.25f, 0f); if (gameObject2 != null) { List list4 = new List(); for (float num5 = -2f; num5 <= 2f; num5 += 2f) { for (float num6 = -2f; num6 <= 2f; num6 += 2f) { list4.Add(Matrix4x4.TRS(new Vector3(num5, 0f, num6), Quaternion.identity, Vector3.one)); } } CombineTileMeshes(gameObject2, gameObject8, list4); } DressCompositeWithAuthored(gameObject8, "floorA", 'F'); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject8, false)); CreateFloorVariant("Avalor_CompositeFloorB", "floorB", gameObject2); CreateFloorVariant("Avalor_CompositeFloorC", "floorC", gameObject2); GameObject gameObject9 = PrefabManager.Instance.CreateEmptyPrefab("Avalor_CompositeCeiling", true); UnityEngine.Object.DestroyImmediate(gameObject9.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject9.GetComponent()); StripColliders(gameObject9); gameObject9.layer = 10; gameObject9.GetComponent().m_persistent = true; if (gameObject2 != null) { List list5 = new List(); for (float num7 = -2f; num7 <= 2f; num7 += 2f) { for (float num8 = -2f; num8 <= 2f; num8 += 2f) { list5.Add(Matrix4x4.TRS(new Vector3(num7, -0.5f, num8), Quaternion.identity, Vector3.one)); } } CombineTileMeshes(gameObject2, gameObject9, list5); } DressCompositeWithAuthored(gameObject9, (AvalorKitBundle.GetMesh("ceilingA") != null) ? "ceilingA" : "floorA", 'C'); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject9, false)); GameObject gameObject10 = PrefabManager.Instance.CreateClonedPrefab("Avalor_Key", "CryptKey"); if (gameObject10 != null) { ItemDrop component = gameObject10.GetComponent(); if (component != null) { component.m_itemData.m_shared.m_name = "Avalor Crypt Key"; component.m_itemData.m_shared.m_description = "Unlocks the vault gates in the Mists of Avalor."; component.m_itemData.m_shared.m_maxStackSize = 20; component.m_itemData.m_dropPrefab = gameObject10; } ItemManager.Instance.AddItem(new CustomItem(gameObject10, false)); } GameObject gameObject11 = PrefabManager.Instance.CreateClonedPrefab("Avalor_Gate", "iron_grate"); if (gameObject11 != null) { Door component2 = gameObject11.GetComponent(); if (component2 != null) { UnityEngine.Object.DestroyImmediate(component2); } WearNTear component3 = gameObject11.GetComponent(); if (component3 != null) { UnityEngine.Object.DestroyImmediate(component3); } gameObject11.AddComponent(); gameObject11.transform.localScale = new Vector3(3f, 2f, 1f); InfuseCryptMaterial(gameObject11); ZNetView component4 = gameObject11.GetComponent(); if (component4 != null) { component4.m_persistent = true; } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject11, false)); } GameObject gameObject12 = PrefabManager.Instance.CreateClonedPrefab("Avalor_Chest", "piece_chest_wood"); if (gameObject12 != null) { MakeIndestructibleContainer(gameObject12); InfuseCryptMaterial(gameObject12); } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject12, false)); GameObject gameObject13 = PrefabManager.Instance.CreateClonedPrefab("Avalor_SpikeTrap", "piece_sharpstakes"); if (gameObject13 != null) { WearNTear component5 = gameObject13.GetComponent(); if (component5 != null) { UnityEngine.Object.DestroyImmediate(component5); } BoxCollider boxCollider4 = gameObject13.GetComponent(); if (boxCollider4 == null) { boxCollider4 = gameObject13.AddComponent(); } boxCollider4.isTrigger = true; boxCollider4.size = new Vector3(4f, 7f, 4f); boxCollider4.center = new Vector3(0f, 3.5f, 0f); gameObject13.AddComponent().SpikePrefab = gameObject13; gameObject13.AddComponent(); InfuseCryptMaterial(gameObject13); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject13, false)); } GameObject gameObject14 = PrefabManager.Instance.CreateClonedPrefab("Avalor_PoisonTrap", "piece_chest_wood"); if (gameObject14 != null) { WearNTear component6 = gameObject14.GetComponent(); if (component6 != null) { UnityEngine.Object.DestroyImmediate(component6); } Piece component7 = gameObject14.GetComponent(); if (component7 != null) { UnityEngine.Object.DestroyImmediate(component7); } Container component8 = gameObject14.GetComponent(); if (component8 != null) { UnityEngine.Object.DestroyImmediate(component8); } BoxCollider boxCollider5 = gameObject14.GetComponent(); if (boxCollider5 == null) { boxCollider5 = gameObject14.AddComponent(); } boxCollider5.isTrigger = true; boxCollider5.size = new Vector3(4f, 7f, 4f); boxCollider5.center = new Vector3(0f, 3.5f, 0f); gameObject14.AddComponent().IsPoison = true; InfuseCryptMaterial(gameObject14); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject14, false)); } CreateOverworldPortal(); CreateExitPortal(); CreateEnvZone(); CreateRecallRune(); CreatePortalDecor(); CreatePathLeveler(); CreateCryptTorch(); CreateMistVolume(); CreateStairTile(); CreateArchway(); CreateVaultChest(); CreatePrizeChest(); CreateStartDais(); CreateEndShrine(); string[] array = new string[27] { "bone_stack", "stone_pile", "wood_stack", "BoneFragments", "Pickable_SunkenCryptRandom", "Pickable_ForestCryptRandom", "Pickable_ForestCryptRemains01", "Pickable_ForestCryptRemains02", "Pickable_ForestCryptRemains03", "Pickable_ForestCryptRemains04", "dvergrprops_crate", "dvergrprops_barrel", "dvergrprops_crate_long", "crypt_skeleton_chest", "Chain", "piece_banner01", "piece_banner02", "piece_banner03", "piece_banner04", "piece_banner05", "dvergrprops_banner", "dvergrprops_hooknchain", "ShieldWood", "ShieldBronzeBuckler", "ShieldIronBuckler", "ShieldSerpentscale", "ShieldCarapace" }; foreach (string text in array) { try { GameObject gameObject15 = PrefabManager.Instance.CreateClonedPrefab("Avalor_Clutter_" + text, text); if (gameObject15 == null) { continue; } MakeStaticDecor(gameObject15); if (text != null) { int length = text.Length; if (length <= 22) { switch (length) { case 20: if (text == "crypt_skeleton_chest") { gameObject15.transform.localScale = Vector3.one * 0.7f; } break; case 22: if (text == "dvergrprops_crate_long") { gameObject15.transform.localScale = Vector3.one * 0.8f; } break; } } else if (length != 26) { if (length == 29) { switch (text[28]) { case '1': break; case '2': goto IL_0c2c; case '3': goto IL_0c3c; case '4': goto IL_0c4c; default: goto IL_0cb1; } if (text == "Pickable_ForestCryptRemains01") { goto IL_0c96; } } } else { char c = text[9]; if (c != 'F') { if (c == 'S' && text == "Pickable_SunkenCryptRandom") { goto IL_0c96; } } else if (text == "Pickable_ForestCryptRandom") { goto IL_0c96; } } } goto IL_0cb1; IL_0c96: gameObject15.transform.localScale = Vector3.one * 1.3f; goto IL_0cb1; IL_0cb1: List list6 = new List(); Transform[] componentsInChildren = gameObject15.GetComponentsInChildren(includeInactive: true); foreach (Transform transform in componentsInChildren) { if (!(transform == null) && !(transform == gameObject15.transform)) { string text2 = transform.name.ToLower(); if (text2.Contains("particle") || text2.Contains("vfx") || text2.Contains("sparkle")) { list6.Add(transform.gameObject); } } } foreach (GameObject item in list6) { if (item != null) { UnityEngine.Object.DestroyImmediate(item); } } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject15, false)); goto end_IL_0b13; IL_0c3c: if (text == "Pickable_ForestCryptRemains03") { goto IL_0c96; } goto IL_0cb1; IL_0c2c: if (text == "Pickable_ForestCryptRemains02") { goto IL_0c96; } goto IL_0cb1; IL_0c4c: if (text == "Pickable_ForestCryptRemains04") { goto IL_0c96; } goto IL_0cb1; end_IL_0b13:; } catch (Exception ex) { Logger.LogWarning((object)("Avalor: failed to build clutter clone for '" + text + "': " + ex.Message)); } } AvalorWeapons.Register(); PrefabManager.OnVanillaPrefabsAvailable -= ClonePrefabs; } private static void CreateOverworldPortal() { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_OverworldPortal", "portal_stone"); if (!(gameObject == null)) { TeleportWorld component = gameObject.GetComponent(); if (component != null) { UnityEngine.Object.DestroyImmediate(component); } TeleportWorldTrigger[] componentsInChildren = gameObject.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren[i]); } WearNTear component2 = gameObject.GetComponent(); if (component2 != null) { UnityEngine.Object.DestroyImmediate(component2); } gameObject.AddComponent(); float targetHeight = (DressPortalWithAuthored(gameObject, 7f) ? 7f : 2.5f); AddPortalPresence(gameObject, targetHeight); ZNetView component3 = gameObject.GetComponent(); if (component3 != null) { component3.m_persistent = true; } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateOverworldPortal failed: " + ex)); } } private static void CreateExitPortal() { //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_ExitPortal", "portal_stone"); if (gameObject == null) { return; } TeleportWorld component = gameObject.GetComponent(); if (component != null) { UnityEngine.Object.DestroyImmediate(component); } TeleportWorldTrigger[] componentsInChildren = gameObject.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren[i]); } WearNTear component2 = gameObject.GetComponent(); if (component2 != null) { UnityEngine.Object.DestroyImmediate(component2); } Transform[] componentsInChildren2 = gameObject.GetComponentsInChildren(includeInactive: true); foreach (Transform transform in componentsInChildren2) { if (!(transform == null) && !(transform == gameObject.transform) && transform.name.StartsWith("_target_found")) { transform.gameObject.SetActive(value: true); } } Collider[] componentsInChildren3 = gameObject.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren3.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren3[i]); } gameObject.AddComponent(); AddPortalPresence(gameObject, 2.5f); ZNetView component3 = gameObject.GetComponent(); if (component3 != null) { component3.m_persistent = true; } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateExitPortal failed: " + ex)); } } private static void CreateEnvZone() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateEmptyPrefab("Avalor_EnvZone", true); if (!(gameObject == null)) { UnityEngine.Object.DestroyImmediate(gameObject.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject.GetComponent()); StripColliders(gameObject); ZNetView component = gameObject.GetComponent(); if (component != null) { component.m_persistent = true; } gameObject.AddComponent(); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateEnvZone failed: " + ex)); } } private static void CreateRecallRune() { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_RecallRune", "Ruby"); if (!(gameObject == null)) { ItemDrop component = gameObject.GetComponent(); if (component != null) { component.m_itemData.m_shared.m_name = "Avalor Recall Rune"; component.m_itemData.m_shared.m_description = "Channel to return to your last bed. Consumed on use."; component.m_itemData.m_shared.m_itemType = ItemDrop.ItemData.ItemType.Consumable; component.m_itemData.m_shared.m_maxStackSize = 10; component.m_itemData.m_dropPrefab = gameObject; SE_AvalorRecall sE_AvalorRecall = ScriptableObject.CreateInstance(); sE_AvalorRecall.name = "SE_AvalorRecall"; sE_AvalorRecall.m_name = "Recalling"; sE_AvalorRecall.m_ttl = 5f; ItemManager.Instance.AddStatusEffect(new CustomStatusEffect((StatusEffect)sE_AvalorRecall, false)); component.m_itemData.m_shared.m_consumeStatusEffect = sE_AvalorRecall; } ItemManager.Instance.AddItem(new CustomItem(gameObject, false)); } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateRecallRune failed: " + ex)); } } private static void CreatePortalDecor() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateEmptyPrefab("Avalor_PortalDecor", true); if (!(gameObject == null)) { UnityEngine.Object.DestroyImmediate(gameObject.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject.GetComponent()); StripColliders(gameObject); ZNetView component = gameObject.GetComponent(); if (component != null) { component.m_persistent = true; } gameObject.AddComponent(); gameObject.AddComponent(); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreatePortalDecor failed: " + ex)); } } private static void CreatePathLeveler() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateEmptyPrefab("Avalor_PathLeveler", true); if (!(gameObject == null)) { UnityEngine.Object.DestroyImmediate(gameObject.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject.GetComponent()); StripColliders(gameObject); ZNetView component = gameObject.GetComponent(); if (component != null) { component.m_persistent = true; } gameObject.AddComponent(); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreatePathLeveler failed: " + ex)); } } private static GameObject FitAuthoredVisual(GameObject parent, string module, Vector3 targetSize, Vector3 localCentre, float yaw) { if (parent == null || !AvalorKitBundle.Available) { return null; } Mesh mesh = AvalorKitBundle.GetMesh(module); if (mesh == null) { return null; } Material material = AvalorKitBundle.GetMaterial(module); if (material == null) { return null; } Bounds bounds = mesh.bounds; Vector3 vector = new Vector3(targetSize.x / Mathf.Max(0.001f, bounds.size.x), targetSize.y / Mathf.Max(0.001f, bounds.size.y), targetSize.z / Mathf.Max(0.001f, bounds.size.z)); Quaternion quaternion = Quaternion.Euler(0f, yaw, 0f); GameObject gameObject = new GameObject("Avalor_Authored_" + module); gameObject.transform.SetParent(parent.transform, worldPositionStays: false); gameObject.transform.localRotation = quaternion; gameObject.transform.localScale = vector; gameObject.transform.localPosition = localCentre - quaternion * Vector3.Scale(bounds.center, vector); gameObject.AddComponent().sharedMesh = mesh; gameObject.AddComponent().sharedMaterial = material; if (MistsofAvalorPlugin.DebugLogging == null || MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)$"[Avalor] authored '{module}' -> {parent.name}: raw {bounds.size:F2} fit {targetSize:F2} yaw {yaw:F0}"); } return gameObject; } private static void StripColliders(GameObject prefab) { if (!(prefab == null)) { Collider[] componentsInChildren = prefab.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren[i]); } EffectArea[] componentsInChildren2 = prefab.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren2.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren2[i]); } } } private static GameObject CreateBarePrefab(string name) { GameObject gameObject = PrefabManager.Instance.CreateEmptyPrefab(name, true); if (gameObject == null) { return null; } MeshRenderer component = gameObject.GetComponent(); if (component != null) { UnityEngine.Object.DestroyImmediate(component); } MeshFilter component2 = gameObject.GetComponent(); if (component2 != null) { UnityEngine.Object.DestroyImmediate(component2); } StripColliders(gameObject); gameObject.layer = 10; ZNetView component3 = gameObject.GetComponent(); if (component3 != null) { component3.m_persistent = true; } return gameObject; } private static void CreateStairTile() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown try { GameObject gameObject = CreateBarePrefab("Avalor_CompositeStairTile"); if (!(gameObject == null)) { GameObject gameObject2 = FitAuthoredVisual(gameObject, "stairs", new Vector3(6f, 4f, 6f), new Vector3(0f, 2f, 0f), 0f); if (gameObject2 == null) { UnityEngine.Object.DestroyImmediate(gameObject); return; } gameObject2.AddComponent().sharedMesh = gameObject2.GetComponent().sharedMesh; PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateStairTile failed: " + ex)); } } private static void CreateArchway() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown try { GameObject gameObject = CreateBarePrefab("Avalor_Archway"); if (!(gameObject == null)) { if (FitAuthoredVisual(gameObject, "archway", new Vector3(6.4f, 4.3f, 0.55f), new Vector3(0f, 2.15f, 0f), 0f) == null) { UnityEngine.Object.DestroyImmediate(gameObject); } else { PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateArchway failed: " + ex)); } } private static void CreateVaultChest() { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown try { GameObject prefab = PrefabManager.Instance.GetPrefab("Avalor_Chest"); if (prefab == null) { Logger.LogWarning((object)"[Avalor] Avalor_Chest not yet registered - vault chests keep the plain look. (Ordering: CreateVaultChest must run after the Avalor_Chest block.)"); return; } GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_VaultChest", prefab); if (!(gameObject == null)) { MeshRenderer[] componentsInChildren = gameObject.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } FitAuthoredVisual(gameObject, "vault", new Vector3(1.7f, 1.05f, 1.45f), new Vector3(0f, 0.52f, 0f), 0f); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateVaultChest failed: " + ex)); } } private static void CreatePrizeChest() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_PrizeChest", "piece_chest_blackmetal"); if (gameObject == null) { Logger.LogWarning((object)"[Avalor][FAIL] could not clone piece_chest_blackmetal - the greatsword prize chest will fall back to the raw vanilla piece, which DESTROYS ITSELF on the maze floor."); return; } MakeIndestructibleContainer(gameObject); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } catch (Exception ex) { Logger.LogError((object)("Avalor: CreatePrizeChest failed: " + ex)); } } private static void MakeIndestructibleContainer(GameObject chest) { if (!(chest == null)) { WearNTear[] componentsInChildren = chest.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren[i]); } Container[] componentsInChildren2 = chest.GetComponentsInChildren(includeInactive: true); foreach (Container obj in componentsInChildren2) { obj.m_privacy = Container.PrivacySetting.Public; obj.m_checkGuardStone = false; } Piece[] componentsInChildren3 = chest.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren3.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren3[i]); } ZNetView component = chest.GetComponent(); if (component != null) { component.m_persistent = true; } } } private static void CreateStartDais() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown try { GameObject gameObject = CreateBarePrefab("Avalor_StartDais"); if (!(gameObject == null)) { GameObject gameObject2 = FitAuthoredVisual(gameObject, "start", new Vector3(14f, 0.35f, 14f), new Vector3(0f, 0.175f, 0f), 0f); if (gameObject2 == null) { UnityEngine.Object.DestroyImmediate(gameObject); return; } gameObject2.AddComponent().sharedMesh = gameObject2.GetComponent().sharedMesh; PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateStartDais failed: " + ex)); } } private static void CreateEndShrine() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown try { GameObject gameObject = CreateBarePrefab("Avalor_EndShrine"); if (!(gameObject == null)) { if (FitAuthoredVisual(gameObject, "end", new Vector3(6.6f, 3.9f, 2.6f), new Vector3(0f, 1.95f, 0f), 0f) == null) { UnityEngine.Object.DestroyImmediate(gameObject); } else { PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateEndShrine failed: " + ex)); } } private static bool DressTorchWithAuthored(GameObject torch) { if (torch == null || !AvalorKitBundle.Available) { return false; } Mesh mesh = AvalorKitBundle.GetMesh("torch"); if (mesh == null) { return false; } Material material = AvalorKitBundle.GetMaterial("torch"); if (material == null) { return false; } MeshRenderer[] componentsInChildren = torch.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } float num = 1.7099999f; foreach (Transform item in torch.transform) { if (item.GetComponentInChildren(includeInactive: true) != null || item.GetComponentInChildren(includeInactive: true) != null) { item.localPosition = new Vector3(0f, num, 0f); } } Bounds bounds = mesh.bounds; int num2 = LongAxis(bounds.size); OtherAxes(num2, bounds.size, out var _, out var wi); Quaternion quaternion = Quaternion.Inverse(Quaternion.LookRotation(Axis(wi), Axis(num2))); float num3 = 1.9f / Mathf.Max(0.001f, bounds.size[num2]); GameObject gameObject = new GameObject("AvalorAuthoredTorch"); gameObject.transform.SetParent(torch.transform, worldPositionStays: false); gameObject.transform.localRotation = quaternion; gameObject.transform.localScale = Vector3.one * num3; gameObject.transform.localPosition = new Vector3(0f, 0.95f, 0f) - quaternion * (bounds.center * num3); gameObject.AddComponent().sharedMesh = mesh; gameObject.AddComponent().sharedMaterial = material; if (MistsofAvalorPlugin.DebugLogging == null || MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)$"[Avalor] authored torch fitted (mesh {bounds.size:F2} -> {1.9f}m, flame at y={num:F2})."); } return true; } private static void CreateMistVolume() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_Mist", "vfx_swamp_mist"); if (!(gameObject == null)) { gameObject.AddComponent().Kind = AvalorAtmoKind.Mist; ZNetView component = gameObject.GetComponent(); if (component != null) { component.m_persistent = true; } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateMistVolume failed: " + ex)); } } private static void CreateCryptTorch() { //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_CryptTorch", "piece_groundtorch_blue"); if (gameObject == null) { return; } WearNTear component = gameObject.GetComponent(); if (component != null) { UnityEngine.Object.DestroyImmediate(component); } Fireplace component2 = gameObject.GetComponent(); if (component2 != null) { UnityEngine.Object.DestroyImmediate(component2); } ParticleSystem[] componentsInChildren = gameObject.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren[i]); } ParticleSystemRenderer[] componentsInChildren2 = gameObject.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren2.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren2[i]); } LightFlicker[] componentsInChildren3 = gameObject.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren3.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren3[i]); } LightLod[] componentsInChildren4 = gameObject.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren4.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren4[i]); } Light[] componentsInChildren5 = gameObject.GetComponentsInChildren(includeInactive: true); foreach (Light light in componentsInChildren5) { light.shadows = LightShadows.None; light.color = AvalorCryptEnvZone.AvalorGreen; light.range = 12f; light.intensity = 1.6f; light.enabled = true; if (light.gameObject != gameObject) { light.gameObject.SetActive(value: true); } } DressTorchWithAuthored(gameObject); gameObject.AddComponent().Kind = AvalorAtmoKind.Torch; ZNetView component3 = gameObject.GetComponent(); if (component3 != null) { component3.m_persistent = true; } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } catch (Exception ex) { Logger.LogError((object)("Avalor: CreateCryptTorch failed: " + ex)); } } private static void CreatePuddle() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown try { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("Avalor_Puddle", "TarLiquid"); if (gameObject == null) { return; } Aoe[] componentsInChildren = gameObject.GetComponentsInChildren(includeInactive: true); foreach (Aoe aoe in componentsInChildren) { if (aoe != null) { UnityEngine.Object.DestroyImmediate(aoe); } } gameObject.transform.localScale = new Vector3(0.12f, 0.35f, 0.12f); ZNetView component = gameObject.GetComponent(); if (component != null) { component.m_persistent = true; } PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } catch (Exception ex) { Logger.LogError((object)("Avalor: CreatePuddle failed: " + ex)); } } private static void MakeStaticDecor(GameObject go) { if (go == null) { return; } ZNetView component = go.GetComponent(); if (component != null) { component.m_persistent = true; } Component[] componentsInChildren = go.GetComponentsInChildren(includeInactive: true); foreach (Component component2 in componentsInChildren) { if (component2 is ItemDrop || component2 is Piece || component2 is Destructible || component2 is Pickable || component2 is Floating || component2 is CharacterDrop || component2 is HoverText || component2 is EggHatch || component2 is WearNTear || component2 is MineRock || component2 is MineRock5) { UnityEngine.Object.DestroyImmediate(component2); } } Rigidbody component3 = go.GetComponent(); if (component3 != null) { component3.isKinematic = true; component3.useGravity = false; UnityEngine.Object.DestroyImmediate(component3); } ParticleSystemRenderer[] componentsInChildren2 = go.GetComponentsInChildren(includeInactive: true); foreach (ParticleSystemRenderer particleSystemRenderer in componentsInChildren2) { if (particleSystemRenderer != null) { UnityEngine.Object.DestroyImmediate(particleSystemRenderer); } } ParticleSystem[] componentsInChildren3 = go.GetComponentsInChildren(includeInactive: true); foreach (ParticleSystem particleSystem in componentsInChildren3) { if (particleSystem != null) { UnityEngine.Object.DestroyImmediate(particleSystem); } } Light[] componentsInChildren4 = go.GetComponentsInChildren(includeInactive: true); foreach (Light light in componentsInChildren4) { if (light != null) { UnityEngine.Object.DestroyImmediate(light); } } Projector[] componentsInChildren5 = go.GetComponentsInChildren(includeInactive: true); foreach (Projector projector in componentsInChildren5) { if (projector != null) { UnityEngine.Object.DestroyImmediate(projector); } } } public static void CombineTileMeshes(GameObject sourcePrefab, GameObject compositeParent, List transforms, bool copyColliders = false) { GameObject meshObj; Mesh mesh = BuildCombined(sourcePrefab, compositeParent, transforms, out meshObj); if (!(mesh == null) && copyColliders) { meshObj.AddComponent().sharedMesh = mesh; } } public static IEnumerator CombineTileMeshesThreaded(GameObject sourcePrefab, GameObject compositeParent, List transforms, Action onComplete) { if (transforms.Count == 0) { onComplete?.Invoke(null, null); yield break; } MeshFilter meshFilter = FindLOD0(sourcePrefab); if (meshFilter == null) { onComplete?.Invoke(null, null); yield break; } Mesh sharedMesh = meshFilter.sharedMesh; Material[] sourceMaterials = meshFilter.GetComponent().sharedMaterials; Vector3[] sVerts = sharedMesh.vertices; if (sVerts == null || sVerts.Length == 0) { onComplete?.Invoke(null, null); yield break; } Vector3[] sNormals = sharedMesh.normals; Vector4[] sTangents = sharedMesh.tangents; Vector2[] sUV = sharedMesh.uv; int[] sTris = sharedMesh.triangles; Matrix4x4 matrix4x = Matrix4x4.TRS(meshFilter.transform.localPosition, meshFilter.transform.localRotation, meshFilter.transform.localScale); int n = transforms.Count; Matrix4x4[] mats = new Matrix4x4[n]; for (int i = 0; i < n; i++) { mats[i] = transforms[i] * matrix4x; } int sv = sVerts.Length; int st = sTris.Length; bool hasNormals = sNormals != null && sNormals.Length == sv; bool hasTangents = sTangents != null && sTangents.Length == sv; bool hasUV = sUV != null && sUV.Length == sv; Vector3[] verts = new Vector3[sv * n]; Vector3[] normals = (hasNormals ? new Vector3[sv * n] : null); Vector4[] tangents = (hasTangents ? new Vector4[sv * n] : null); Vector2[] uvs = (hasUV ? new Vector2[sv * n] : null); int[] tris = new int[st * n]; bool done = false; ThreadPool.QueueUserWorkItem(delegate { try { for (int j = 0; j < n; j++) { Matrix4x4 matrix4x2 = mats[j]; int num = j * sv; for (int k = 0; k < sv; k++) { verts[num + k] = matrix4x2.MultiplyPoint3x4(sVerts[k]); if (hasNormals) { normals[num + k] = matrix4x2.MultiplyVector(sNormals[k]).normalized; } if (hasTangents) { Vector4 vector = sTangents[k]; Vector3 normalized = matrix4x2.MultiplyVector(new Vector3(vector.x, vector.y, vector.z)).normalized; tangents[num + k] = new Vector4(normalized.x, normalized.y, normalized.z, vector.w); } if (hasUV) { uvs[num + k] = sUV[k]; } } int num2 = j * st; for (int l = 0; l < st; l++) { tris[num2 + l] = sTris[l] + num; } } } catch { } done = true; }); while (!done) { yield return null; } Mesh mesh = new Mesh(); mesh.indexFormat = IndexFormat.UInt32; mesh.SetVertices(verts); if (normals != null) { mesh.SetNormals(normals); } if (tangents != null) { mesh.SetTangents(tangents); } if (uvs != null) { mesh.SetUVs(0, uvs); } mesh.SetTriangles(tris, 0); if (normals == null) { mesh.RecalculateNormals(); } if (tangents == null) { mesh.RecalculateTangents(); } mesh.RecalculateBounds(); GameObject gameObject = new GameObject(sourcePrefab.name + "_Combined"); gameObject.layer = sourcePrefab.layer; gameObject.transform.SetParent(compositeParent.transform, worldPositionStays: false); gameObject.AddComponent().sharedMesh = mesh; gameObject.AddComponent().sharedMaterials = sourceMaterials; MeshCollider arg = gameObject.AddComponent(); onComplete?.Invoke(mesh, arg); } private static MeshFilter FindLOD0(GameObject sourcePrefab) { MeshFilter result = null; int num = -1; MeshFilter[] componentsInChildren = sourcePrefab.GetComponentsInChildren(includeInactive: true); foreach (MeshFilter meshFilter in componentsInChildren) { string text = meshFilter.gameObject.name.ToLower(); if (!text.Contains("shadow") && !text.Contains("proxy") && meshFilter.sharedMesh != null && meshFilter.sharedMesh.vertexCount > num) { MeshRenderer component = meshFilter.GetComponent(); if (component != null && component.sharedMaterials.Length != 0) { result = meshFilter; num = meshFilter.sharedMesh.vertexCount; } } } return result; } private static Mesh BuildCombined(GameObject sourcePrefab, GameObject compositeParent, List transforms, out GameObject meshObj) { meshObj = null; if (transforms.Count == 0) { return null; } MeshFilter meshFilter = FindLOD0(sourcePrefab); if (meshFilter == null) { return null; } Mesh sharedMesh = meshFilter.sharedMesh; Material[] sharedMaterials = meshFilter.GetComponent().sharedMaterials; Matrix4x4 matrix4x = Matrix4x4.TRS(meshFilter.transform.localPosition, meshFilter.transform.localRotation, meshFilter.transform.localScale); List list = new List(); foreach (Matrix4x4 transform in transforms) { list.Add(new CombineInstance { mesh = sharedMesh, transform = transform * matrix4x }); } Mesh mesh = new Mesh(); mesh.indexFormat = IndexFormat.UInt32; mesh.CombineMeshes(list.ToArray(), mergeSubMeshes: true, useMatrices: true); meshObj = new GameObject(sourcePrefab.name + "_Combined"); meshObj.layer = sourcePrefab.layer; meshObj.transform.SetParent(compositeParent.transform, worldPositionStays: false); meshObj.AddComponent().sharedMesh = mesh; meshObj.AddComponent().sharedMaterials = sharedMaterials; return mesh; } private static void InfuseCryptMaterial(GameObject prefab) { if (prefab == null) { return; } Material material = null; GameObject prefab2 = PrefabManager.Instance.GetPrefab("blackmarble_2x2x1"); if (prefab2 != null) { MeshRenderer componentInChildren = prefab2.GetComponentInChildren(); if (componentInChildren != null) { material = componentInChildren.sharedMaterial; } } if (material == null) { Logger.LogWarning((object)("Avalor: InfuseCryptMaterial could not find a donor material for " + prefab.name + " - it will keep its original vanilla appearance.")); return; } MeshRenderer[] componentsInChildren = prefab.GetComponentsInChildren(includeInactive: true); foreach (MeshRenderer meshRenderer in componentsInChildren) { Material[] sharedMaterials = meshRenderer.sharedMaterials; for (int j = 0; j < sharedMaterials.Length; j++) { sharedMaterials[j] = material; } meshRenderer.sharedMaterials = sharedMaterials; } } private static float Sz(float v) { return Mathf.Max(0.001f, v); } private static void DressCompositeWithAuthored(GameObject composite, string moduleName, char kind) { if (composite == null || !AvalorKitBundle.Available) { return; } Mesh mesh = AvalorKitBundle.GetMesh(moduleName); if (mesh == null) { return; } Material material = AvalorKitBundle.GetMaterial(moduleName); if (material == null) { return; } List list = new List(); foreach (Transform item in composite.transform) { list.Add(item.gameObject); } foreach (GameObject item2 in list) { UnityEngine.Object.DestroyImmediate(item2); } Bounds bounds = mesh.bounds; int num = ThinAxis(bounds.size); OtherAxes(num, bounds.size, out var ui, out var wi); Vector3 forward = Axis(num) * DetailSign(mesh, num); Vector3 upwards = Axis(ui); Vector3 forward2; Vector3 right; float num2; float num3; float num4; Vector3 vector; switch (kind) { case 'W': forward2 = Vector3.forward; right = Vector3.right; num2 = 6.9f; num3 = 4.3f; num4 = 0.4f; vector = new Vector3(0f, 2f, 0.2f); break; case 'C': forward2 = Vector3.down; right = Vector3.right; num2 = 6.4f; num3 = 6.4f; num4 = 0.4f; vector = new Vector3(0f, -0.56f, 0f); break; default: forward2 = Vector3.up; right = Vector3.right; num2 = 6.4f; num3 = 6.4f; num4 = 0.4f; vector = new Vector3(0f, -0.2f, 0f); break; } Quaternion quaternion = Quaternion.LookRotation(forward2, right) * Quaternion.Inverse(Quaternion.LookRotation(forward, upwards)); Vector3 one = Vector3.one; one[ui] = num2 / Sz(bounds.size[ui]); one[wi] = num3 / Sz(bounds.size[wi]); one[num] = num4 / Sz(bounds.size[num]); Vector3 vector2 = vector - quaternion * Vector3.Scale(one, bounds.center); Mesh mesh2 = mesh; _ = Matrix4x4.identity; if (kind == 'W') { Matrix4x4 matrix4x = Matrix4x4.TRS(vector2, quaternion, one); Matrix4x4 transform2 = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 180f, 0f), Vector3.one) * matrix4x; mesh2 = new Mesh { name = moduleName + "_twosided", indexFormat = IndexFormat.UInt32 }; mesh2.CombineMeshes(new CombineInstance[2] { new CombineInstance { mesh = mesh, transform = matrix4x }, new CombineInstance { mesh = mesh, transform = transform2 } }, mergeSubMeshes: true, useMatrices: true); mesh2.RecalculateBounds(); } else { Matrix4x4.TRS(vector2, quaternion, one); } GameObject gameObject = new GameObject(moduleName + "_LOD0"); gameObject.transform.SetParent(composite.transform, worldPositionStays: false); if (kind != 'W') { gameObject.transform.localScale = one; gameObject.transform.localRotation = quaternion; gameObject.transform.localPosition = vector2; } gameObject.AddComponent().sharedMesh = mesh2; gameObject.AddComponent().sharedMaterial = material; gameObject.layer = composite.layer; if (MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value) { Vector3 vector3 = ((kind == 'W') ? mesh2.bounds.size : Vector3.Scale(one, bounds.size)); Logger.LogInfo((object)($"[Avalor] authored '{moduleName}' -> {composite.name} [{kind}]: raw {bounds.size:F2} " + string.Format("thinAxis={0} detail={1}{2} fit {3:F2}", "XYZ"[num], (DetailSign(mesh, num) > 0) ? "+" : "-", "XYZ"[num], vector3))); } } private static bool DressPortalWithAuthored(GameObject portal, float targetHeight) { if (portal == null || !AvalorKitBundle.Available) { return false; } string text = ((AvalorKitBundle.GetMesh("portalpillar") != null) ? "portalpillar" : "portalArch"); Mesh mesh = AvalorKitBundle.GetMesh(text); if (mesh == null) { return false; } Material material = AvalorKitBundle.GetMaterial(text); if (material == null) { return false; } MeshRenderer[] componentsInChildren = portal.GetComponentsInChildren(includeInactive: true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } Bounds bounds = mesh.bounds; int num = LongAxis(bounds.size); OtherAxes(num, bounds.size, out var ui, out var wi); Quaternion rotation = Quaternion.LookRotation(Axis(wi), Axis(num)); Quaternion quaternion = Quaternion.LookRotation(Vector3.forward, Vector3.up) * Quaternion.Inverse(rotation); float num2 = targetHeight / Sz(bounds.size[num]); float num3 = bounds.size[ui] * num2 * 0.5f; float num4 = 1.6f + num3; for (int j = 0; j < 2; j++) { float num5 = ((j == 0) ? (-1f) : 1f); GameObject gameObject = new GameObject((j == 0) ? "AvalorPillar_L" : "AvalorPillar_R"); gameObject.transform.SetParent(portal.transform, worldPositionStays: false); gameObject.transform.localScale = new Vector3(num2, num2, num2); Quaternion quaternion2 = Quaternion.Euler(0f, (0f - num5) * 6f, 0f) * quaternion; gameObject.transform.localRotation = quaternion2; Vector3 vector = quaternion2 * Vector3.Scale(Vector3.one * num2, bounds.center); gameObject.transform.localPosition = new Vector3(num5 * num4 - vector.x, targetHeight * 0.5f - vector.y, 0f - vector.z); gameObject.AddComponent().sharedMesh = mesh; gameObject.AddComponent().sharedMaterial = material; gameObject.layer = portal.layer; BoxCollider boxCollider = portal.AddComponent(); boxCollider.size = new Vector3(bounds.size[ui] * num2 * 0.85f, targetHeight, bounds.size[wi] * num2 * 0.7f); boxCollider.center = new Vector3(num5 * num4, targetHeight * 0.5f, 0f); } if (MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)($"[Avalor] authored '{text}' -> twin-pillar gateway: raw {bounds.size:F2} " + string.Format("(up={0} face={1}) scale {2:F2}, pillar {3:F2}w x ", "XYZ"[num], "XYZ"[ui], num2, bounds.size[ui] * num2) + $"{targetHeight:F2}h, gap {3.2f:F2}, span {2f * num4:F2}m")); } return true; } private static void AddPortalPresence(GameObject portal, float targetHeight) { GameObject gameObject = new GameObject("AvalorPortalGlow"); gameObject.transform.SetParent(portal.transform, worldPositionStays: false); gameObject.transform.localPosition = new Vector3(0f, targetHeight * 0.5f, 0f); Light light = gameObject.AddComponent(); light.type = LightType.Point; light.color = AvalorGreen; light.range = 26f; light.intensity = 2.4f; light.shadows = LightShadows.None; gameObject.AddComponent(); GameObject gameObject2 = new GameObject("AvalorPortalApproach"); gameObject2.transform.SetParent(portal.transform, worldPositionStays: false); gameObject2.transform.localPosition = new Vector3(0f, targetHeight * 0.35f, 0f); gameObject2.layer = portal.layer; BoxCollider boxCollider = gameObject2.AddComponent(); boxCollider.isTrigger = true; boxCollider.size = new Vector3(2.8799999f, targetHeight * 0.7f, 3f); gameObject2.AddComponent(); AvalorPortalRuneVFX avalorPortalRuneVFX = portal.AddComponent(); avalorPortalRuneVFX.Colour = AvalorGreen; avalorPortalRuneVFX.GateWidth = 3.2f; avalorPortalRuneVFX.GateHeight = targetHeight; TintPortalVfx(portal, targetHeight); } private static void TintPortalVfx(GameObject portal, float targetHeight) { Light[] componentsInChildren = portal.GetComponentsInChildren(includeInactive: true); foreach (Light light in componentsInChildren) { if (!(light.gameObject.name == "AvalorPortalGlow")) { light.color = AvalorGreen; } } ParticleSystem[] componentsInChildren2 = portal.GetComponentsInChildren(includeInactive: true); foreach (ParticleSystem obj in componentsInChildren2) { obj.transform.localScale = Vector3.one * 2.2f; obj.transform.localPosition = new Vector3(0f, targetHeight * 0.35f, 0f); ParticleSystem.MainModule main = obj.main; main.scalingMode = ParticleSystemScalingMode.Hierarchy; main.startColor = new ParticleSystem.MinMaxGradient(AvalorGreen); ParticleSystem.ColorOverLifetimeModule colorOverLifetime = obj.colorOverLifetime; if (colorOverLifetime.enabled) { Gradient gradient = new Gradient(); gradient.SetKeys(new GradientColorKey[2] { new GradientColorKey(Color.white, 0f), new GradientColorKey(Color.white, 1f) }, new GradientAlphaKey[2] { new GradientAlphaKey(1f, 0f), new GradientAlphaKey(0f, 1f) }); colorOverLifetime.color = new ParticleSystem.MinMaxGradient(gradient); } } } private static void CreateWallVariant(string prefabName, string moduleName, GameObject wallPrefab) { //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown GameObject gameObject = PrefabManager.Instance.CreateEmptyPrefab(prefabName, true); if (gameObject == null) { return; } UnityEngine.Object.DestroyImmediate(gameObject.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject.GetComponent()); gameObject.layer = 10; ZNetView component = gameObject.GetComponent(); if (component != null) { component.m_persistent = true; } BoxCollider boxCollider = gameObject.AddComponent(); boxCollider.size = new Vector3(6f, 4f, 0.5f); boxCollider.center = new Vector3(0f, 2f, 0f); if (wallPrefab != null) { List list = new List(); for (float num = -2f; num <= 2f; num += 2f) { for (float num2 = 0f; num2 <= 3f; num2 += 1f) { list.Add(Matrix4x4.TRS(new Vector3(num, num2, 0f), Quaternion.identity, Vector3.one)); } } CombineTileMeshes(wallPrefab, gameObject, list); } DressCompositeWithAuthored(gameObject, moduleName, 'W'); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } private static void CreateFloorVariant(string prefabName, string moduleName, GameObject floorPrefab) { //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown GameObject gameObject = PrefabManager.Instance.CreateEmptyPrefab(prefabName, true); if (gameObject == null) { return; } UnityEngine.Object.DestroyImmediate(gameObject.GetComponent()); UnityEngine.Object.DestroyImmediate(gameObject.GetComponent()); gameObject.layer = 10; ZNetView component = gameObject.GetComponent(); if (component != null) { component.m_persistent = true; } BoxCollider boxCollider = gameObject.AddComponent(); boxCollider.size = new Vector3(6f, 0.5f, 6f); boxCollider.center = new Vector3(0f, 0.25f, 0f); if (floorPrefab != null) { List list = new List(); for (float num = -2f; num <= 2f; num += 2f) { for (float num2 = -2f; num2 <= 2f; num2 += 2f) { list.Add(Matrix4x4.TRS(new Vector3(num, 0f, num2), Quaternion.identity, Vector3.one)); } } CombineTileMeshes(floorPrefab, gameObject, list); } DressCompositeWithAuthored(gameObject, moduleName, 'F'); PrefabManager.Instance.AddPrefab(new CustomPrefab(gameObject, false)); } private static Vector3 Axis(int i) { return i switch { 1 => Vector3.up, 0 => Vector3.right, _ => Vector3.forward, }; } private static int ThinAxis(Vector3 s) { if (!(s.x <= s.y) || !(s.x <= s.z)) { if (!(s.y <= s.z)) { return 2; } return 1; } return 0; } private static int LongAxis(Vector3 s) { if (!(s.x >= s.y) || !(s.x >= s.z)) { if (!(s.y >= s.z)) { return 2; } return 1; } return 0; } private static void OtherAxes(int ni, Vector3 s, out int ui, out int wi) { int num = (ni + 1) % 3; int num2 = (ni + 2) % 3; if (s[num] >= s[num2]) { ui = num; wi = num2; } else { ui = num2; wi = num; } } private static int DetailSign(Mesh mesh, int axis) { int key = mesh.GetInstanceID() * 4 + axis; if (_detailSign.TryGetValue(key, out var value)) { return value; } int num = 1; try { Vector3[] vertices = mesh.vertices; int[] triangles = mesh.triangles; Vector3 rhs = Axis(axis); double num2 = 0.0; double num3 = 0.0; for (int i = 0; i + 2 < triangles.Length; i += 3) { Vector3 vector = Vector3.Cross(vertices[triangles[i + 1]] - vertices[triangles[i]], vertices[triangles[i + 2]] - vertices[triangles[i]]); float num4 = vector.magnitude * 0.5f; if (!(num4 <= 0f)) { float num5 = Vector3.Dot(vector.normalized, rhs); if (num5 > 0.4f) { num2 += (double)num4; } else if (num5 < -0.4f) { num3 += (double)num4; } } } if (num3 > num2) { num = -1; } } catch (Exception ex) { Logger.LogWarning((object)("[Avalor] could not measure detail face of '" + mesh.name + "' - assuming +axis. " + ex.Message)); } _detailSign[key] = num; return num; } private static void RegisterLocations() { if (PrefabManager.Instance.GetPrefab("Avalor_OverworldPortal") != null) { GameObject prefab = PrefabManager.Instance.GetPrefab("StartTemple"); if (prefab != null) { prefab.AddComponent(); } } ZoneManager.OnVanillaLocationsAvailable -= RegisterLocations; } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.SE_AvalorRecall ---- namespace MistsofAvalor { public class SE_AvalorRecall : StatusEffect { public override void Stop() { base.Stop(); if (m_character != null && m_character.IsPlayer()) { Player player = m_character as Player; Vector3 vector = Game.instance.GetPlayerProfile().GetCustomSpawnPoint(); if (vector == Vector3.zero) { vector = new Vector3(0f, 50f, 0f); } player.TeleportTo(vector, player.transform.rotation, distantTeleport: true); player.Message(MessageHud.MessageType.Center, "Returned safely!"); } } } } // ---- plugins/MistsofAvalor.dll :: MistsofAvalor.AvalorKitBundle ---- namespace MistsofAvalor { public static class AvalorKitBundle { private const string BundleName = "avalor_kit"; private static bool _tried; private static AssetBundle _bundle; private static Material _donor; private static readonly Dictionary _meshCache = new Dictionary(); private static readonly Dictionary _matCache = new Dictionary(); public static bool Available => _bundle != null; public static bool Load() { if (_tried) { return _bundle != null; } _tried = true; try { _bundle = AssetUtils.LoadAssetBundleFromResources("avalor_kit", Assembly.GetExecutingAssembly()); if (_bundle == null) { Logger.LogWarning((object)"[Avalor] Kit bundle 'avalor_kit' not found as an embedded resource - using vanilla fallback geometry."); return false; } if (MistsofAvalorPlugin.DebugLogging != null && MistsofAvalorPlugin.DebugLogging.Value) { Logger.LogInfo((object)("[Avalor] Kit bundle loaded. Assets: " + string.Join(", ", _bundle.GetAllAssetNames()))); } else { Logger.LogInfo((object)("[Avalor] Kit bundle loaded (" + _bundle.GetAllAssetNames().Length + " assets).")); } return true; } catch (Exception ex) { Logger.LogError((object)("[Avalor] Failed to load kit bundle - using vanilla fallback geometry. " + ex)); _bundle = null; return false; } } public static Mesh GetMesh(string moduleName) { if (_bundle == null) { return null; } if (_meshCache.TryGetValue(moduleName, out var value)) { return value; } Mesh mesh = null; try { mesh = _bundle.LoadAsset(moduleName); } catch { } if (mesh == null) { Logger.LogWarning((object)("[Avalor] kit mesh '" + moduleName + "' not in bundle.")); } _meshCache[moduleName] = mesh; return mesh; } public static Material GetMaterial(string moduleName) { if (_matCache.TryGetValue(moduleName, out var value)) { return value; } Material donor = GetDonor(); Material material = ((donor != null) ? new Material(donor) : null); if (material == null) { _matCache[moduleName] = null; return null; } material.name = "Avalor_Mat_" + moduleName; Texture2D texture2D = null; if (_bundle != null) { try { texture2D = _bundle.LoadAsset(moduleName + "_albedo"); } catch { } } if (texture2D != null) { material.mainTexture = texture2D; if (material.HasProperty("_MainTex")) { material.SetTexture("_MainTex", texture2D); } if (material.HasProperty("_Color")) { material.SetColor("_Color", Color.white); } if (material.HasProperty("_MainColor")) { material.SetColor("_MainColor", Color.white); } } else { Logger.LogWarning((object)("[Avalor] kit albedo '" + moduleName + "_albedo' missing - module keeps plain dark stone.")); } _matCache[moduleName] = material; return material; } private static Material GetDonor() { if (_donor != null) { return _donor; } string[] array = new string[3] { "stone_wall_2x1", "stone_wall_4x2", "blackmarble_2x2x1" }; foreach (string text in array) { GameObject prefab = PrefabManager.Instance.GetPrefab(text); if (!(prefab == null)) { MeshRenderer componentInChildren = prefab.GetComponentInChildren(); if (componentInChildren != null && componentInChildren.sharedMaterial != null) { _donor = componentInChildren.sharedMaterial; break; } } } if (_donor == null) { Logger.LogWarning((object)"[Avalor] no donor stone material found for kit materials."); } return _donor; } } }