using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Splatform; using UnityEngine; using UnityEngine.PostProcessing; using Zen; using Zen.Interop; using Zen.Lib; using Zen.Lib.Config; using ZenPortal.StatusEffects; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ZenPortal")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ZenPortal")] [assembly: AssemblyCopyright("Copyright \ufffd 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ZenPortal { internal static class Animations { private static PostProcessingBehaviour? _cameraPost; private static Incinerator IncineratorPrefab => GlobalStatic.GetPrefab("incinerator", true); private static PostProcessingBehaviour CameraPost { get { if (!Object.op_Implicit((Object)(object)_cameraPost)) { return _cameraPost = ((Component)GameCamera.instance.m_camera).GetComponent(); } return _cameraPost; } } public static void Eat(Humanoid human, ItemData item) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) human.m_consumeItemEffects.Create(((Component)human).transform.position, Quaternion.identity, (Transform)null, 1f, -1); ((Character)human).m_zanim.SetTrigger("eat"); GameObject val = default(GameObject); if (ObjectDB.instance.TryGetItemPrefab(item.m_shared, ref val)) { human.SetUseHandVisual(val, item.m_shared.m_foodEatAnimTime); } } public static void Kneel(this Player player, float seconds, Action? onFinished = null, bool autoStand = true) { Player player2 = player; Action onFinished2 = onFinished; player2.StartEmote("Kneel".ToLowerInvariant(), true); PlayerController.SetTakeInputDelay(seconds); Timing.Delay((MonoBehaviour)(object)player2, seconds, (Action)delegate { if (Object.op_Implicit((Object)(object)player2) && autoStand) { ((Character)player2).StopEmote(); } onFinished2?.Invoke(); }); } public static void DistantThunder(this Character character) { DistantThunder(((Component)character).transform); } public static void DistantThunder(this RuneStone runestone) { DistantThunder(((Component)runestone).transform); } private static void DistantThunder(Transform transform) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown GameObject[] array = IncineratorPrefab.m_leverEffects.Create(transform.position - transform.forward, Quaternion.identity, (Transform)null, 1f, -1); for (int i = 0; i < array.Length; i++) { foreach (Transform item in array[i].transform) { Transform val = item; string name = ((Object)val).name; if ((name == "Sparks" || name == "sfx_lever") ? true : false) { ((Component)val).gameObject.SetActive(false); } else if (((Object)val).name.StartsWith("Point light")) { ((Component)val).gameObject.SetActive(true); } } } } public static void LightningStrike(this Character character, bool knockback = false) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) LightningStrike(((Component)character).transform.position, Quaternion.identity, knockback); } public static void LightningStrike(this RuneStone runestone, bool knockback = true) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)runestone).transform; LightningStrike(transform.position - transform.forward * 0.5f, Quaternion.identity, knockback); } private static void LightningStrike(Vector3 position, Quaternion rotation, bool knockback) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown foreach (Transform item in Object.Instantiate(IncineratorPrefab.m_lightingAOEs, position, rotation).transform) { Transform val = item; if (((Object)val).name.StartsWith("vfx_RockHit")) { ((Component)val).gameObject.SetActive(true); } else if (((Object)val).name == "AOE_ROD") { ((Component)val).gameObject.SetActive(false); } else if (((Object)val).name == "AOE_AREA") { ((Component)val).gameObject.SetActive(knockback); } } } public static void SetCameraState(bool inverted) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) Vector3 val = default(Vector3); val.x = 1f; val.y = 0f; val.z = 0f; Vector3 red = val; val = default(Vector3); val.x = 0f; val.y = 1f; val.z = 0f; Vector3 green = val; val = default(Vector3); val.x = 0f; val.y = 0f; val.z = 1f; Vector3 blue = val; val = default(Vector3); val.x = 0f; val.y = 1f; val.z = 1f; Vector3 red2 = val; val = default(Vector3); val.x = 1f; val.y = 0f; val.z = 1f; Vector3 green2 = val; val = default(Vector3); val.x = 1f; val.y = 1f; val.z = 0f; Vector3 blue2 = val; PostProcessingBehaviour cameraPost = CameraPost; Logging.Info((object)("frameBlending: " + cameraPost.profile.motionBlur.settings.frameBlending), 0); Settings settings = cameraPost.profile.colorGrading.settings; if (inverted) { settings.channelMixer.red = red2; settings.channelMixer.green = green2; settings.channelMixer.blue = blue2; } else { settings.channelMixer.red = red; settings.channelMixer.green = green; settings.channelMixer.blue = blue; } cameraPost.profile.colorGrading.settings = settings; } } [HarmonyPatch] internal static class BiomeTeleport { private static readonly Dictionary> SafeBiomeTeleportPositions = new Dictionary>(); private static string? _tombstoneText; public static bool IsTeleportBiome(this Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 if ((int)biome != 256 && (int)biome != 0) { return (int)biome != 895; } return false; } private static List GetZones(Biome biome) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (SafeBiomeTeleportPositions.TryGetValue(biome, out List value)) { return value; } if ((int)biome == 0) { Logging.Info((object)"Biome: None, returning location 0,0 (start temple)", 0); return new List(1) { Vector2i.zero }; } Logging.Info((object)$"Biome {biome}, returning all possible locations", 0); return SafeBiomeTeleportPositions.Values.SelectMany((List z) => z).ToList(); } public static void InitTeleportZoneCache() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) Logging.Info((object)"Building biome zone cache...", 0); int num = Mathf.CeilToInt((float)Configs.CompatibilityWorldSize.Value); int num2 = Mathf.CeilToInt(ZoneSystem.instance.m_zoneSize); SafeBiomeTeleportPositions.Clear(); int num3 = 0; Vector3 val = default(Vector3); Color val2 = default(Color); for (int i = -num; i <= num; i += num2) { for (int j = -num; j <= num; j += num2) { ((Vector3)(ref val))..ctor((float)i, 0f, (float)j); Biome biome = WorldGenerator.instance.GetBiome(val); if (biome.IsTeleportBiome() && !(WorldGenerator.instance.GetBiomeHeight(biome, (float)i, (float)j, ref val2, false) <= 30f) && !ZoneSystem.IsLavaPreHeightmap(val, 0.6f)) { if (!SafeBiomeTeleportPositions.TryGetValue(biome, out List value)) { value = (SafeBiomeTeleportPositions[biome] = new List()); } value.Add(new Vector2i(i, j)); num3++; } } } Logging.Info((object)$"Zones added to cache: {num3}", 0); } public static void DropAllNoTeleport(this Player player) { Logging.Info((object)"Drop all no teleport items", 0); Inventory inventory = ((Humanoid)player).GetInventory(); ItemData[] array = (from item in inventory.GetAllItems() where !item.m_shared.m_teleportable select item).ToArray(); foreach (ItemData val in array) { ((Humanoid)player).DropItem(inventory, val, val.m_stack); } } public static Vector3 GetRandomTeleportPosition(Biome biome, float yOffset = 0f) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) List zones = GetZones(biome); Vector2i val = zones[Random.Range(0, zones.Count)]; float height = WorldGenerator.instance.GetHeight((float)val.x, (float)val.y); return new Vector3((float)val.x, height + yOffset, (float)val.y); } private static Vector3 GetRuneTeleportPosition(RuneID runeID, float yOffset = 0f) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) List zones = GetZones(runeID.Biome); State state = Random.state; Random.InitState(runeID.Identity); int index = Random.Range(0, zones.Count); Random.state = state; Vector2i val = zones[index]; float height = WorldGenerator.instance.GetHeight((float)val.x, (float)val.y); return new Vector3((float)val.x, height + yOffset, (float)val.y); } public static void TeleportToRuneAddress(this Player? player, RuneID runeID) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) Player player2 = player; if (Object.op_Implicit((Object)(object)player2)) { Logging.Info((object)$"Teleport to {runeID} biome {runeID.Biome}", 0); Vector3 zero = Vector3.zero; zero.y = 50f; Vector3 val = zero; if ((int)runeID.Biome != 0) { val = GetRuneTeleportPosition(runeID, 30f); } ((Character)player2).TeleportTo(val, Quaternion.identity, true); Timing.Delay((MonoBehaviour)(object)player2, 4f, (Action)delegate { SlowFallUngrounded.ApplyTo(player2); }); } } public static void LightningTeleport(this Player? player, Vector3 position, string? tombstoneText) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) Player player2 = player; string tombstoneText2 = tombstoneText; if (!Object.op_Implicit((Object)(object)player2)) { return; } Logging.Info((object)$"Lightning teleport to: {position}", 0); player2.Kneel(8f); ((Character)(object)player2).DistantThunder(); Timing.Delay((MonoBehaviour)(object)player2, 4.5f, (Action)delegate { if (Object.op_Implicit((Object)(object)player2)) { if (tombstoneText2 != null) { player2.DropTombstone(tombstoneText2); } ((Character)(object)player2).LightningStrike(); ((Character)player2).m_visual.SetActive(false); Timing.Delay((MonoBehaviour)(object)player2, 0.5f, (Action)delegate { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)player2)) { ((Character)player2).TeleportTo(position, Quaternion.identity, true); Timing.Delay((MonoBehaviour)(object)player2, 4f, (Action)delegate { SlowFallUngrounded.ApplyTo(player2); if (Object.op_Implicit((Object)(object)player2)) { ((Character)player2).m_visual.SetActive(true); } }); } }); } }); } private static void DropTombstone(this Player player, string? text) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (((Humanoid)player).GetInventory().NrOfItems() != 0) { Vector3 position = ((Component)player).transform.position; ((Humanoid)player).UnequipAllItems(); ((Humanoid)player).m_hiddenLeftItem = null; ((Humanoid)player).m_hiddenRightItem = null; ((Humanoid)player).SetupVisEquipment(((Humanoid)player).m_visEquipment, false); _tombstoneText = text; player.CreateTombStone(); _tombstoneText = null; Minimap.instance.AddPin(position, (PinType)4, ItemDataExt.GetName(ItemRuneShard.Prefab), true, false, 0L, default(PlatformUserID)); } } [HarmonyPostfix] [HarmonyPatch(typeof(TombStone), "Setup")] [HarmonyPriority(200)] private static void TombStone_Setup(TombStone __instance) { if (_tombstoneText != null) { __instance.m_nview.GetZDO().Set(ZDOVars.s_text, _tombstoneText); } } } internal static class BitUtils { private static class BitPermuter { public static uint Mix(uint value, uint seed, int bits) { if ((bits < 2 || bits > 32) ? true : false) { throw new ArgumentOutOfRangeException("bits"); } uint num = Mask(bits); value &= num; seed &= num; int num2 = bits / 2; int rightBits = bits - num2; (int, int)[] array = BuildWidths(num2, rightBits); uint num3 = value; for (int i = 0; i < 4; i++) { num3 = MixRound(num3, seed, i, array[i].Item1, array[i].Item2); } return num3 & num; } public static uint Unmix(uint value, uint seed, int bits) { if ((bits < 2 || bits > 32) ? true : false) { throw new ArgumentOutOfRangeException("bits"); } uint num = Mask(bits); value &= num; seed &= num; int num2 = bits / 2; int rightBits = bits - num2; (int, int)[] array = BuildWidths(num2, rightBits); uint num3 = value; for (int num4 = 3; num4 >= 0; num4--) { num3 = UnmixRound(num3, seed, num4, array[num4].Item1, array[num4].Item2); } return num3 & num; } private static (int leftBits, int rightBits)[] BuildWidths(int leftBits0, int rightBits0) { (int, int)[] array = new(int, int)[4]; int num = leftBits0; int num2 = rightBits0; for (int i = 0; i < 4; i++) { array[i] = (num, num2); int num3 = num2; num2 = num; num = num3; } return array; } private static uint MixRound(uint value, uint seed, int round, int leftBits, int rightBits) { uint num = Mask(leftBits); uint num2 = Mask(rightBits); uint num3 = (value >> rightBits) & num; uint num4 = value & num2; uint key = RoundKey(seed, round, rightBits); uint num5 = RoundFunction(num4, key, leftBits); uint num6 = num4 & num2; uint num7 = (num3 ^ num5) & num; return (num6 << leftBits) | num7; } private static uint UnmixRound(uint value, uint seed, int round, int leftBits, int rightBits) { uint num = Mask(leftBits); uint num2 = Mask(rightBits); uint num3 = (value >> leftBits) & num2; uint num4 = value & num; uint key = RoundKey(seed, round, rightBits); uint num5 = num3; return (((num4 ^ RoundFunction(num5, key, leftBits)) & num) << rightBits) | num5; } private static uint RoundFunction(uint x, uint key, int outBits) { uint num = Mask(outBits); x ^= key; x = (x * 17881) & 0xFFFFFFFFu; x ^= x >> 7; x = (x * 668265261) & 0xFFFFFFFFu; x ^= x >> 9; return x & num; } private static uint RoundKey(uint seed, int round, int bits) { int num = (int)seed + -1640531527 + (int)(round * 2246822507u); int num2 = (num ^ (num >>> 16)) * 2146121005; return ((uint)num2 ^ ((uint)num2 >> 15)) & Mask(bits); } public static uint Mask(int bits) { if (bits == 32) { return uint.MaxValue; } return (uint)((1 << bits) - 1); } } public static uint GetMask(int bits) { return BitPermuter.Mask(bits); } public static uint Mix10(uint value, uint salt) { return BitPermuter.Mix(value, salt, 10); } public static uint Unmix10(uint value, uint salt) { return BitPermuter.Unmix(value, salt, 10); } public static uint Mix15(uint value, uint salt) { return BitPermuter.Mix(value, salt, 15); } public static uint Unmix15(uint value, uint salt) { return BitPermuter.Unmix(value, salt, 15); } public static uint Mix12(uint value, uint salt) { return BitPermuter.Mix(value, salt, 12); } public static uint Unmix12(uint value, uint salt) { return BitPermuter.Unmix(value, salt, 12); } public static uint Mix32(uint value, uint salt) { return BitPermuter.Mix(value, salt, 32); } public static uint Unmix32(uint value, uint salt) { return BitPermuter.Unmix(value, salt, 32); } public static bool VerifyMixer() { for (int i = 4; i <= 16; i++) { if (!VerifyMixer(i)) { Logging.Error((object)$"Failed to verify {i} bits", (ushort)0); return false; } } return true; } private static bool VerifyMixer(int bits) { if (bits > 20) { Logging.Error((object)$"Max test range 2-{20}, aborting", (ushort)0); return false; } uint mask = GetMask(bits); Logging.Message((object)$"Verify mixer for {bits} bits", 0); return VerifyMixer("BitPermuter", WrapMix, WrapUnmix, mask); uint WrapMix(uint x, uint seed) { return BitPermuter.Mix(x, seed, bits); } uint WrapUnmix(uint x, uint seed) { return BitPermuter.Unmix(x, seed, bits); } } private static bool VerifyMixer(string label, Func mix, Func unmix, uint mask) { Logging.Message((object)$"Testing {label} for mask: {mask:X}", 0); HashSet hashSet = new HashSet(); uint num = mask + 1; Logging.Message((object)$"Test 1: Verify bijection (one-to-one mapping) using salt: {1234567890u}", 0); for (uint num2 = 0u; num2 < num; num2++) { uint num3 = mix(num2, 1234567890u); uint num4 = unmix(num3, 1234567890u); if (num3 > mask) { Logging.Error((object)$"FAIL: permuted value {num3:X8} is outside the mask range: {mask:X8}", (ushort)0); return false; } if (num2 != num4) { Logging.Error((object)$"FAIL: The inverse {num4} does not equal the original value: {num2}, permuted: {num3}", (ushort)0); return false; } if (!hashSet.Add(num3)) { Logging.Error((object)$"FAIL: collision detected at input {num2}, output {num3}", (ushort)0); return false; } if (num2 < 10 || num2 == mask || num3 == mask) { Logging.Message((object)$"Data - Original: {num2:X8}, Permuted: {num3:X8}, Inverse: {num4:X8}", 0); } } Logging.Message((object)"Test 2: Verify all values are covered", 0); if (hashSet.Count != num) { Logging.Error((object)$"FAIL: only {hashSet.Count} unique values out of {num}", (ushort)0); return false; } Logging.Message((object)"Test 3: Verify distribution (check that sequential inputs produce non-sequential outputs)", 0); int num5 = 0; for (uint num6 = 0u; num6 < 1000; num6++) { uint num7 = mix(num6, 1234567890u); uint num8 = mix(num6 + 1, 1234567890u); if (Math.Abs(num7 - num8) <= 1) { num5++; } } Logging.Message((object)"Test 4: Less than 5% of sequential pairs should be adjacent", 0); if (num5 > 50) { Logging.Warning((object)$"Permute: {num5} adjacent outputs detected (distribution may be poor)", 0); } Logging.Message((object)$"PASSED: bijection verified, all values covered [0000-{mask:X8}]", 0); return true; } } internal static class Commands { private sealed class MakeRuneCmd : TerminalCommand { public override string Name => "portalrune"; public override string Syntax => "Syntax: " + ((TerminalCommand)this).SyntaxName + " -random [biome] [channel] [group] | [hexID]"; public override string Description => "Create new runestones with a given ID and optional biome, or random ID if random is specified."; public override bool IsCheat => true; public override void Exec(string[] args) { //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) if (!PlayerExt.IsReady(Player.m_localPlayer)) { Console.instance.Print("Player not ready"); return; } RuneID runeID; switch (args.Length) { case 1: Console.instance.Print(((TerminalCommand)this).Syntax); return; case 2: { string text = args[1]; if (text.ToLowerInvariant().StartsWith("-rand")) { Biome[] array = (from Biome b in Enum.GetValues(typeof(Biome)) where b.IsTeleportBiome() select b).ToArray(); Biome biome2 = array[Random.Range(0, array.Length)]; int channel = Random.Range(0, 32); int group = Random.Range(0, PortalGroup.Max); runeID = new RuneID(biome2, channel, group); } else if (!RuneID.TryParse(text, out runeID, logError: false)) { Console.instance.Print("Invalid argument: " + text); Console.instance.Print(((TerminalCommand)this).Syntax); return; } break; } case 4: { if (!TryParseBiomeName(args[1], out var biome)) { Console.instance.Print("Invalid biome: " + args[2]); Console.instance.Print(((TerminalCommand)this).Syntax); return; } if (!byte.TryParse(args[2], out var result)) { Console.instance.Print("Invalid chan: " + args[1]); Console.instance.Print(((TerminalCommand)this).Syntax); return; } if (!byte.TryParse(args[3], out var result2)) { Console.instance.Print("Invalid group: " + args[3]); Console.instance.Print(((TerminalCommand)this).Syntax); return; } runeID = new RuneID(biome, result, result2); break; } default: Console.instance.Print("Invalid number of arguments"); Console.instance.Print(((TerminalCommand)this).Syntax); return; } Transform transform = ((Component)Player.m_localPlayer).transform; ItemRuneShard.Spawn(runeID, transform.position + transform.forward, 2); } } private sealed class CureSicknessCmd : TerminalCommand { public override string Name => "portalcure"; public override string Syntax => "Syntax: " + ((TerminalCommand)this).SyntaxName; public override string Description => "Cure the player of portal sickness " + ((TerminalCommand)this).Syntax; public override bool IsCheat => true; public override void Exec(string[] args) { if (!PlayerExt.IsReady(Player.m_localPlayer)) { Console.instance.Print("Player not ready"); } else { PortalSickness.Cure(Player.m_localPlayer); } } } private sealed class RuneDebugCmd : TerminalCommand { public override string Name => "portalrunedebug"; public override string Syntax => "Syntax: " + ((TerminalCommand)this).SyntaxName; public override string Description => "Print the raw data of every rune in your inventory to the console."; public override bool IsCheat => true; public override void Exec(string[] args) { //IL_0128: Unknown result type (might be due to invalid IL or missing references) if (!PlayerExt.IsReady(Player.m_localPlayer)) { Console.instance.Print("Player not ready"); return; } string[] symbols = RuneID.Symbols; HashSet unique = new HashSet(); foreach (string item in symbols.Where((string s) => !unique.Add(s))) { string text = "Symbol duplicate: " + item; Console.instance.Print("ERROR: " + text); Logging.Error((object)text, (ushort)0); } Console.instance.Print(string.Format("{0} Glyphs: {1}", symbols.Length, GeneralExtensions.Join((IEnumerable)symbols, (Func)null, ""))); foreach (ItemData allItem in ((Humanoid)Player.m_localPlayer).GetInventory().GetAllItems()) { string prefabName = ItemDataExt.GetPrefabName(allItem); if (!(prefabName != ((Object)ItemRuneShard.Prefab).name)) { string arg = allItem.GetRuneDataInternal() ?? "(null)"; string text2 = $"{prefabName} ({allItem.m_gridPos}) Data: {arg}"; Console.instance.Print(text2); } } } } public sealed class PortalGroupCmd : TerminalCommand { public const string CmdName = "portalgroup"; public override string Name => "portalgroup"; public override string Description => "Assign a portal group to the current player."; public override string Syntax => $"Syntax: {((TerminalCommand)this).SyntaxName} [0-{PortalGroup.Max - 1}]"; public override bool IsCheat => true; public override void Exec(string[] args) { int num = -1; if (args.Length > 1 && int.TryParse(args[1], out var result)) { num = result; } if (num < 0 || num > PortalGroup.Max - 1) { Console.instance.Print(((TerminalCommand)this).Syntax); return; } Player.m_localPlayer.SetPortalGroup(num); Console.instance.Print($"Assigned to group: {num}"); } } public static void Init() { ZenMod.Terminal.CreateCommand((TerminalCommand)(object)new MakeRuneCmd()); ZenMod.Terminal.CreateCommand((TerminalCommand)(object)new CureSicknessCmd()); ZenMod.Terminal.CreateCommand((TerminalCommand)(object)new RuneDebugCmd()); ZenMod.Terminal.CreateCommand((TerminalCommand)(object)new PortalGroupCmd()); } private static bool TryParseBiomeName(string name, out Biome biome) { biome = (Biome)0; if (!Enum.TryParse(name, ignoreCase: true, out biome)) { return false; } if ((int)biome == 895) { biome = (Biome)0; } return true; } } internal static class Configs { public static readonly ConfigEntry CompatibilityWorldSize; public static readonly ConfigEntry ColorPortalHoverTextLinkConnected; public static readonly ConfigEntry ColorRuneHoverTextLinkPending; public static readonly ConfigEntry ColorRuneHoverTextLinkAvailable; public static readonly ConfigEntry ColorRuneHoverTextLinkUnavailable; public static readonly ConfigEntry ColorRuneInventoryLinkAvailable; public static readonly ConfigEntry ColorRuneInventoryLinkPending; public static readonly ConfigEntry ColorRuneInventoryLinkUnavailable; public static readonly ConfigEntry PortalSicknessDuration; public static readonly ConfigEntry PortalSicknessSpeedAdj; public static readonly ConfigEntry PortalSicknessStaminaRegen; public static readonly ConfigEntry PortalSicknessHealthRegen; public static readonly ConfigEntry PortalSicknessHealthPerTick; public static readonly ConfigEntry PortalSicknessPukeOnUse; public static readonly ConfigEntry PortalSicknessPukeOnEat; public static readonly ConfigEntry PortalSicknessPreventTeleport; public static readonly ConfigEntry TravelRequireRoof; public static readonly ConfigEntry TravelRequireWalls; public static readonly ConfigEntry TravelKnownBiome; public static readonly ConfigEntry LinkSameTypeOnly; public static readonly ConfigEntry PreventNearDungeonRange; public static readonly ConfigEntry AllowBiomesPortalWood; public static readonly ConfigEntry AllowBiomesPortalStone; public static readonly ConfigEntry AllowBiomesMargin; private static readonly Dictionary> AllowBiomeMinAltitude; public static readonly ConfigEntry CraftPortalWoodResources; public static readonly ConfigEntry CraftPortalWoodStation; public static readonly ConfigEntry CraftPortalStoneResources; public static readonly ConfigEntry CraftPortalStoneStation; public static readonly ConfigEntry CraftRequiresHeavySupport; public static readonly ConfigEntry FractureItem; public static readonly ConfigEntry FractureItemAmount; public static readonly ConfigEntry RuneShardConsumable; public static readonly ConfigEntry RuneShardMageTable; public static readonly ConfigEntry RuneShardDisplayBiome; public static readonly ConfigEntry RuneShardWeight; public static readonly ConfigEntry RuneStonePortalGroups; public static readonly ConfigEntry RuneStoneChannelsPerBiome; public static readonly ConfigEntry FuelItemPortalWood; public static readonly ConfigEntry FuelItemPortalStone; public static readonly ConfigEntry FuelMax; public static int GetBiomeMinAltitude(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) return GetBiomeMinAltitude(Heightmap.FindBiome(position)); } public static int GetBiomeMinAltitude(Biome biome) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (!AllowBiomeMinAltitude.TryGetValue(biome, out ConfigEntry value)) { return 0; } return value.Value; } static Configs() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03d4: Unknown result type (might be due to invalid IL or missing references) //IL_03e0: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Unknown result type (might be due to invalid IL or missing references) //IL_047a: Unknown result type (might be due to invalid IL or missing references) //IL_047f: Unknown result type (might be due to invalid IL or missing references) //IL_048a: Expected O, but got Unknown //IL_048a: Unknown result type (might be due to invalid IL or missing references) //IL_0495: Expected O, but got Unknown //IL_0495: Unknown result type (might be due to invalid IL or missing references) //IL_04a0: Expected O, but got Unknown //IL_04aa: Expected O, but got Unknown //IL_04de: Unknown result type (might be due to invalid IL or missing references) //IL_04e3: Unknown result type (might be due to invalid IL or missing references) //IL_04ee: Expected O, but got Unknown //IL_04ee: Unknown result type (might be due to invalid IL or missing references) //IL_04f9: Expected O, but got Unknown //IL_04f9: Unknown result type (might be due to invalid IL or missing references) //IL_0504: Expected O, but got Unknown //IL_050e: Expected O, but got Unknown AllowBiomeMinAltitude = new Dictionary>(); CompatibilityWorldSize = Config.Define(true, "Compatibility", "World Size", 10000, Config.AcceptRange(5000, 50000), $"Size of the world if modded from default. Vanilla: {10000f}"); ColorPortalHoverTextLinkConnected = Config.Define(false, "Rune - Colors", "Portal HoverText - Link Connected", UIColor.ValheimYellow.Color, "Portal rune's text color when connected to another portal."); ColorRuneHoverTextLinkPending = Config.Define(false, "Rune - Colors", "Rune HoverText - Link Pending", UIColor.Clear.Color, "Text color when pending a connection to another portal.\r\nNOTE: Clear means that it will fallback to the default color (white)"); ColorRuneHoverTextLinkAvailable = Config.Define(false, "Rune - Colors", "Rune HoverText - Link Available", UIColor.ValheimYellow.Color, "Text color when not in use in any portals."); ColorRuneHoverTextLinkUnavailable = Config.Define(false, "Rune - Colors", "Rune HoverText - Link Unavailable", UIColor.Gray.Color, "Rune text color when they are already connected to another portal somewhere in the world."); ColorRuneInventoryLinkAvailable = Config.Define(false, "Rune - Colors", "Rune Inventory - Link Available", UIColor.ValheimYellow.Color, "Inventory item rune text color when not in use in any portals"); ColorRuneInventoryLinkUnavailable = Config.Define(false, "Rune - Colors", "Rune Inventory - Link Unavailable", UIColor.Gray.Color, "Inventory item rune text color when in use by connected portals somewhere in the world"); ColorRuneInventoryLinkPending = Config.Define(false, "Rune - Colors", "Rune Inventory - Link Pending", UIColor.Clear.Color, "Inventory item rune text color when the rune is in only one portal somewhere in the world"); PortalSicknessPukeOnUse = Config.Define(true, "Portal - Sickness", "Puke On Use", true, "Vomit and lose all food after using a portal."); PortalSicknessPukeOnEat = Config.Define(true, "Portal - Sickness", "Puke On Eat", false, "If true then the player is unable to eat until portal sickness wears off.\r\nHowever, they can still drink meads and potions."); PortalSicknessPreventTeleport = Config.Define(true, "Portal - Sickness", "Prevent Teleport", false, "Can not teleport while portal sickness is in effect. \r\n[If false the portal sickness timer will be reset after each portal jump]"); PortalSicknessDuration = Config.Define(true, "Portal - Sickness", "Duration (Minutes)", 5, Config.AcceptRange(0, 120), "Number of minutes that players will be sick after using a portal.\r\nSleep will remove the sickness instantly.\r\n[Set to 0 to disable portal sickness]"); PortalSicknessSpeedAdj = Config.Define(true, "Portal - Sickness", "Debuff - Movement Speed", 0.8f, Config.AcceptRange(0f, 1f), "Movement speed adjustment when portal sickness is in effect.\r\nSet to 1 to for normal movement speed."); PortalSicknessStaminaRegen = Config.Define(true, "Portal - Sickness", "Debuff - Stamina Regen", 0.5f, Config.AcceptRange(0f, 1f), "Stamina regen multiplier when portal sickness is in effect.\r\nSet to 1 for normal regen rate."); PortalSicknessHealthRegen = Config.Define(true, "Portal - Sickness", "Debuff - Health Regen", 0.5f, Config.AcceptRange(0f, 1f), "Health regen multiplier when portal sickness is in effect.\r\nSet to 1 to for normal regen rate."); PortalSicknessHealthPerTick = Config.Define(true, "Portal - Sickness", "Debuff - Health Per Second", 0f, Config.AcceptRange(-2f, 2f), "Health regen amount per tick when portal sickness is in effect. \r\nNegative numbers do harm, positive numbers heal.\r\nThis will run for aproximately 10s whenever the player walks through a portal.\r\nSet to 0 to disable."); TravelKnownBiome = Config.Define(true, "Portal - Travel", "Require Known Destination Biome", Portal.PortalType.PortalWood | Portal.PortalType.PortalStone, "Restrict travel so that you can only enter a portal if the destination biome is already discovered.\r\n[This is helpful to require the first voyage into a new biome to always be done manually once. For example: Ashlands]"); TravelRequireRoof = Config.Define(true, "Portal - Travel", "Require Roof", Portal.PortalType.None, "Restrict travel so that you can only enter a portal if there is a roof over it."); TravelRequireWalls = Config.Define(true, "Portal - Travel", "Require Walls", Portal.PortalType.PortalWood, "Restrict travel so that you can only enter a portal if it has walls around it."); LinkSameTypeOnly = Config.Define(true, "Portal - Travel", "Connection - Same Type Only", true, "Wood portals can only link to other wood portals, stone to stone. (Vanilla: false)\r\n[This has no effect on admin created \"locked!\" tags, they can always link to any type of portal]"); PreventNearDungeonRange = Config.Define(true, "Build - Restrictions", "Prevent Near Dungeon - Range", 10, Config.AcceptRange(0, 30), "Anti-cheese to prevent portals from being built near dungeon entrances.\r\nSet to 0 to disable this feature and allow building near the entrances."); AllowBiomesPortalWood = Config.Define(true, "Build - Restrictions", "Allow Biomes - Wood", (Biome)6, "Biomes which allow wooden portals to be built. (Vanilla: All)"); AllowBiomesPortalStone = Config.Define(true, "Build - Restrictions", "Allow Biomes - Stone", (Biome)36, "Biomes which allow stone portals to be built. (Vanilla: All)"); AllowBiomesMargin = Config.Define(true, "Build - Restrictions", "Allow Biomes - Margin", 25, Config.AcceptRange(0, 100), "Minimum distance from any disallowed biome before a portal can be built.\r\nThis prevents placing a portal just inside the edge of an allowed biome."); Dictionary dictionary = new Dictionary { [(Biome)1] = 0, [(Biome)8] = 0, [(Biome)2] = 45, [(Biome)4] = 150, [(Biome)16] = 0, [(Biome)512] = 0, [(Biome)32] = 80, [(Biome)64] = 0, [(Biome)256] = 0 }; foreach (Biome key in dictionary.Keys) { AllowBiomeMinAltitude[key] = Config.Define(true, "Build - Restrictions", $"Min Altitude - {key}", dictionary[key], Config.AcceptRange(0, 500), $"Only allow portals to be built above this altitude in the {key}."); } CraftRequiresHeavySupport = Config.Define(true, "Build - Cost", "Requires Heavy Support", Portal.PortalType.PortalStone, "For new games it is recommended to enable this for Wood and Stone portals. It is disabled by default for Wood portals so as not to break existing worlds.\r\nRequire portals of the selected type to have heavy support.\r\nIf checked then the portal type requires heavy building materials for support.\r\nIf unchecked then light materials can be used to support the portal.\r\nHeavy mats are ground/stone/iron/etc. Light mats are woods.\r\nIncrease or decrease portal tower construction challenge.\r\nWARNING: Turning this on for wood portals on existing worlds will cause any wood portals with weak support to collapse."); CraftPortalWoodStation = Config.Define(true, "Build - Cost", "Portal Wood - Station", "Workbench", CraftingStations.GetAcceptableValueList(), "Crafting station required to build the wood portal"); StringList val = new StringList(); ((List)val).Add("GreydwarfEye:10"); ((List)val).Add("ElderBark:20"); ((List)val).Add("SurtlingCore:2"); CraftPortalWoodResources = Config.Define(true, "Build - Cost", "Portal Wood - Resources", val, "List of resources required to build a Wood Portal (Vanilla: GreydwarfEye:10, FineWood:20, SurtlingCore:2)"); CraftPortalStoneStation = Config.Define(true, "Build - Cost", "Portal Stone - Station", "Stonecutter", CraftingStations.GetAcceptableValueList(), "Crafting station required to build the stone portal"); StringList val2 = new StringList(); ((List)val2).Add("GreydwarfEye:10"); ((List)val2).Add("Grausten:30"); ((List)val2).Add("MoltenCore:2"); CraftPortalStoneResources = Config.Define(true, "Build - Cost", "Portal Stone - Resources", val2, "List of resources required to build a Stone Portal (Vanilla: GreydwarfEye:10, Grausten:30, MoltenCore:2)"); FractureItem = Config.Define(true, "Portal - Rune", "Fracture Item", "Obsidian", "Prefab name of the item that is required to fracture Runestones."); FractureItemAmount = Config.Define(true, "Portal - Rune", "Fracture Item - Amount", 2, Config.AcceptRange(1, 50), "The amount of \"" + ((ConfigEntryBase)FractureItem).Definition.Key + "\" required."); RuneShardConsumable = Config.Define(true, "Portal - Rune", "Rune Shard - Consumable", true, "Do you want to eat this rock?\r\n[logout required for changes to take effect]"); RuneShardMageTable = Config.Define(true, "Portal - Rune", "Rune Shard - Mage Table", true, "Can the shard be used on mage tables?\r\n[logout required for changes to take effect]"); RuneShardDisplayBiome = Config.Define(true, "Portal - Rune", "Rune Shard - Display Biome", false, "Display the biome that the rune shard represents on the tooltip?\r\nNOTE: Admin can always see this by being in god mode and holding the Alt key."); RuneShardWeight = Config.Define(true, "Portal - Rune", "Rune Shard - Weight", 2f, Config.AcceptRange(0.1f, 300f), "The weight of a rune shard.\r\n[logout required for changes to take effect]"); RuneStonePortalGroups = Config.Define(true, "Portal - Rune", "Rune Shard - Portal Groups", 1, Config.AcceptRange(1, 32), "Total number of portal groups available. Each group is a slightly diff version of reality.\r\nLarger servers with many players can have the players in diff groups so they don't all see the same symbols.\r\nSmall friends may want to all share the same reality, in which case one shared group is enough.\r\nEach Runestone will look the same to all players who are part of the same group.\r\nIf two players are in different groups and stand at the same Runestone they will see diff symbols.\r\nIf they are in the same group they will see the same symbols at that Runestone.\r\nGroups are controlled via the player key: portalgroup\r\nNote, group 0 is the default group if no group is assigned.\r\nTo have all players see the same symbols set this to 1 to force all players into the same group.\r\nIf this config is greater than 1 then players are assigned to a random group if they do not have one when they next login.\r\nAdmin command syntax:\r\n- zen_portalgroup N\r\nSet another player's group who is logged on:\r\n- Turn on command relay in Zen.ModLib configs:\r\n- relay playername zen_portalgroup N\r\nRemove a player from all groups and let the system assign them a random one on next login:\r\n- first, figure out what group they are in using listkeys, then remove their key:\r\n- relay playername removekeyplayer portalgroup N\r\n- ask the player to logout/login to allow the system to assign a new group, or kick them to force it."); RuneStoneChannelsPerBiome = Config.Define(true, "Portal - Rune", "Rune Shard - Channels Per Biome", 4, Config.AcceptRange(1, 32), "Total number of unique channels available per biome.\r\nHigher numbers make it easier to find open channels on Runestone.\r\nLower numbers make it harder and requires venturing into different biomes to find open channels.\r\nNOTE: Changing this on existing worlds may cause existing Runestones to be assigned different symbols."); FuelItemPortalWood = Config.Define(true, "Portal - Fuel", "Wood Portal Fuel Item", "BoneFragments", "Prefab name of the fuel item for wood portals, leave blank for no fuel requirement"); FuelItemPortalStone = Config.Define(true, "Portal - Fuel", "Stone Portal Fuel Item", "CharredBone", "Prefab name of the fuel item for stone portals, leave blank for no fuel requirement"); FuelMax = Config.Define(true, "Portal - Fuel", "Max Fuel (Minutes)", 10, Config.AcceptRange(1, 60), "Max amount of units of fuel a portal can hold."); } public static T GetConfigValue(Portal.PortalType portalType, ConfigEntry configWood, ConfigEntry configStone) { return portalType switch { Portal.PortalType.PortalWood => configWood.Value, Portal.PortalType.PortalStone => configStone.Value, _ => throw new ArgumentOutOfRangeException("portalType"), }; } } [HarmonyPatch] internal static class ItemRuneShard { private const string DataRuneID = "ZenRuneID"; public const string PrefabName = "ZenRuneShard"; public static ItemDrop Prefab => GlobalStatic.GetPrefab("ZenRuneShard", true); public static bool HaveRuneShard(this Inventory inventory) { return InventoryExt.ContainsItemByPrefab(inventory, "ZenRuneShard"); } public static Action AddCraftingItem() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) Logging.Info((object)"Add crafting item: ZenRuneShard, based on Thunderstone", 0); Sprite val = AssetIO.LoadSpriteFromResource((BaseUnityPlugin)(object)ZenMod.Instance, "rune_shard.png"); ItemConfig val2 = new ItemConfig(); val2.Name = "$item_rune_shard"; val2.Description = "$item_rune_shard_description"; val2.CraftingStation = CraftingStations.None; val2.MinStationLevel = 1; val2.Requirements = Array.Empty(); val2.Icons = (Sprite[])(object)((!Object.op_Implicit((Object)(object)val)) ? null : new Sprite[1] { val }); val2.StackSize = 1; CustomItem val3 = new CustomItem("ZenRuneShard", "Thunderstone", val2); ItemManager.Instance.AddItem(val3); Transform obj = ((Component)Prefab).transform.Find("model"); ((Renderer)((Component)obj).GetComponent()).material.SetColor("_EmissionColor", Color.red * 2f); Object.Destroy((Object)(object)((Component)obj).GetComponent()); Object.Destroy((Object)(object)((Component)obj).GetComponent()); Object.Destroy((Object)(object)((Component)obj.parent.GetChild(1)).gameObject); return ConfigSync; static void ConfigSync() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) Prefab.m_itemData.m_shared.m_weight = Configs.RuneShardWeight.Value; Prefab.m_itemData.m_shared.m_itemType = (ItemType)16; } } internal static string? GetRuneDataInternal(this ItemData itemData) { if (!itemData.m_customData.TryGetValue("ZenRuneID", out var value)) { return null; } return value; } public static bool TryGetRuneID(this ItemData? itemData, out RuneID runeID) { runeID = default(RuneID); string text = itemData?.GetRuneDataInternal(); if (text != null) { return RuneID.TryParse(text, out runeID); } return false; } public static bool HasRuneID(this ItemData? itemData) { return itemData?.GetRuneDataInternal() != null; } public static void SetRuneID(this ItemData itemData, RuneID runeID) { itemData.m_customData["ZenRuneID"] = runeID.ToString(); } public static void Spawn(RuneID runeID, Vector3 origin, int amount, bool autoPickup = false) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < amount; i++) { ItemData obj = Prefab.m_itemData.Clone(); obj.SetRuneID(runeID); Vector3 val = origin + Vector3.up + Random.insideUnitSphere * 0.3f; ItemDrop.DropItem(obj, 1, val, Random.rotation).m_autoPickup = autoPickup; } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemDrop), "GetHoverText")] [HarmonyPriority(0)] private static void ItemDrop_GetHoverText(ItemDrop __instance, ref string __result) { if (__instance.m_itemData.TryGetRuneID(out var runeID)) { UIColor linkStatusColorHoverText = runeID.GetLinkStatusColorHoverText(); string hoverTextMysticSymbols = runeID.GetHoverTextMysticSymbols(linkStatusColorHoverText); if (HoverItem.IsLoaded) { HoverItem.UpdateIcons(ItemDataExt.GetIcon(__instance), hoverTextMysticSymbols); } else { __result += hoverTextMysticSymbols; } } } [HarmonyPrefix] [HarmonyPatch(typeof(Humanoid), "UseItem")] private static void Humanoid_UseItem(Humanoid __instance, Inventory inventory, ItemData item, bool fromInventoryGui, ref bool __runOriginal) { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_00c4: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown Humanoid __instance2 = __instance; ItemData item2 = item; if (Configs.RuneShardConsumable.Value && fromInventoryGui && item2.TryGetRuneID(out var runeID)) { __runOriginal = false; if (PortalSickness.IsSick((Character?)(object)__instance2)) { ((Character)__instance2).Message((MessageType)2, "$se_puke_start", 0, (Sprite)null); return; } Logging.Warning((object)((object)UIColor.ValheimYellow).ToString(), 0); string text = StringExt.Localize("$item_rune_shard_consume_warning", new string[2] { ((object)UIColor.ValheimYellow).ToString(), ItemDataExt.GetName(item2) }); UnifiedPopup.Push((PopupBase)new YesNoPopup(runeID.ToMysticSymbols(), text, new PopupButtonCallback(Accept), new PopupButtonCallback(Reject), true)); } void Accept() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown UnifiedPopup.Pop(); InventoryGui.instance.Hide(); ((Character)__instance2).Message((MessageType)2, "$msg_consumed " + ItemDataExt.GetName(item2), 1, item2.GetIcon()); __instance2.GetInventory().RemoveOneItem(item2); Animations.Eat(__instance2, item2); Stack stack = new Stack(); string tombstoneText = "$tombstone_text " + runeID.ToMysticSymbols(); Vector3 position = ((Component)__instance2).transform.position; position.y += 30f; stack.Push(position); Biome biome = (Biome)(((int)runeID.Biome == 0) ? 895 : ((int)runeID.Biome)); for (int i = 0; i < 2; i++) { stack.Push(BiomeTeleport.GetRandomTeleportPosition(biome, 30f)); } PortalDrift.ApplyTo((Player)__instance2, stack, tombstoneText); } static void Reject() { UnifiedPopup.Pop(); } } } [HarmonyPatch] internal static class ItemRuneShardInventoryGrid { [HarmonyTranspiler] [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] private static IEnumerable InventoryGrid_UpdateGui(IEnumerable codes, ILGenerator gen) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected O, but got Unknown //IL_0151: Unknown result type (might be due to invalid IL or missing references) CodeMatch[] array = (CodeMatch[])(object)new CodeMatch[5] { new CodeMatch((OpCode?)OpCodes.Ldloc_S, (object)null, (string)null), new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.DeclaredField(typeof(ItemData), "m_shared"), (string)null), new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.DeclaredField(typeof(SharedData), "m_maxStackSize"), (string)null), new CodeMatch((OpCode?)OpCodes.Ldc_I4_1, (object)null, (string)null), new CodeMatch((OpCode?)OpCodes.Ble, (object)null, (string)null) }; MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(List), "GetEnumerator", (Type[])null, (Type[])null); CodeMatch[] array2 = (CodeMatch[])(object)new CodeMatch[3] { new CodeMatch((OpCode?)OpCodes.Ldloca_S, (object)null, (string)null), new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.EnumeratorMoveNext((MethodBase)methodInfo), (string)null), new CodeMatch((OpCode?)OpCodes.Brtrue, (object)null, (string)null) }; Action action = HandleRuneShard; CodeInstruction[] array3 = (CodeInstruction[])(object)new CodeInstruction[3] { new CodeInstruction(OpCodes.Ldloc_S, (object)(byte)18), new CodeInstruction(OpCodes.Ldloc_S, (object)(byte)19), new CodeInstruction(OpCodes.Call, (object)action.Method) }; Label label = default(Label); return new CodeMatcher(codes, gen).MatchStartForward(array2).ThrowIfInvalid("Unable to match IL sequence: end of loop").Insert(array3) .CreateLabel(ref label) .MatchEndBackwards(array) .ThrowIfInvalid("Unable to match IL sequence: branch correction") .SetOperandAndAdvance((object)label) .InstructionEnumeration(); static void HandleRuneShard(ItemData item, Element element) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (!(ItemDataExt.GetPrefabName(item) != "ZenRuneShard")) { string text = "INVALID"; if (item.TryGetRuneID(out var runeID)) { UIColor linkStatusColorInventory = runeID.GetLinkStatusColorInventory(); string text2 = runeID.ToMysticSymbols(); text = ((UIColor.op_Implicit(linkStatusColorInventory) != Color.clear) ? $"{text2}" : text2); } ((Behaviour)element.m_amount).enabled = true; element.m_amount.text = text; } } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(int) })] [HarmonyPriority(100)] private static void ItemData_GetTooltip(ItemData __instance, ref string __result) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (__instance.TryGetRuneID(out var runeID)) { string text = string.Empty; if (Configs.RuneShardDisplayBiome.Value && runeID.Biome.IsTeleportBiome()) { text = "\n" + BiomeExt.Localize(runeID.Biome); } string text2 = "" + runeID.ToMysticSymbols() + "" + text; ItemDataExt.SetTooltipExtra(__instance, text2); string description = __instance.m_shared.m_description; __result = __result.Replace(description, $"{description}\n\n{text2}"); if (PlayerExt.IsGodMode(Player.m_localPlayer)) { __result += $"\n\nRune ID: {runeID}\nBiome: {runeID.Biome}\nGroup: {runeID.Group}\nChan: {runeID.Channel}"; } } } } [HarmonyPatch] internal static class MageTable { private const float EitrUseAmount = 20f; private static bool IsMageTable(this CraftingStation station) { return Utils.GetPrefabName(((Object)station).name) == CraftingStations.GaldrTable; } [HarmonyPostfix] [HarmonyPatch(typeof(CraftingStation), "GetHoverText")] private static void CraftingStation_GetHoverText(CraftingStation __instance, ref string __result) { if (__instance.IsMageTable() && __instance.InUseDistance((Humanoid)(object)Player.m_localPlayer) && __instance.CheckUsable(Player.m_localPlayer, false) && ((Humanoid)Player.m_localPlayer).GetInventory().HaveRuneShard()) { __result += StringExt.Localize("\n" + UI.PromptUseItem + " $piece_use " + ItemDataExt.GetName(ItemRuneShard.Prefab)); } } [HarmonyPostfix] [HarmonyPatch(typeof(CraftingStation), "UseItem")] private static void CraftingStation_UseItem(CraftingStation __instance, Humanoid user, ItemData item, ref bool __result, ref bool __runOriginal) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown //IL_0105: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Expected O, but got Unknown ItemData item2 = item; if (!Configs.RuneShardMageTable.Value || !__instance.IsMageTable() || !item2.TryGetRuneID(out var runeID)) { return; } Player player = (Player)(object)((user is Player) ? user : null); if (player != null && __instance.CheckUsable(player, true)) { __runOriginal = false; __result = true; if (!((Character)player).TryUseEitr(20f)) { ((Character)player).Message((MessageType)2, "$hud_eitrrequired", 0, (Sprite)null); return; } string text = runeID.ToMysticSymbols(); string text2 = (((int)runeID.Biome == 0) ? "$location_start_temple" : BiomeExt.Localize(runeID.Biome)); string text3 = StringExt.Localize("$item_rune_shard_magetable_warning", new string[3] { __instance.GetHoverName(), ItemDataExt.GetName(ItemRuneShard.Prefab), text2 }); UnifiedPopup.Push((PopupBase)new YesNoPopup(text, text3, new PopupButtonCallback(Accept), new PopupButtonCallback(Reject), true)); } void Accept() { InventoryGui.instance.Hide(); UnifiedPopup.Pop(); if (Object.op_Implicit((Object)(object)player)) { if (!((Humanoid)player).IsTeleportable()) { ((Character)player).Message((MessageType)2, "$msg_noteleport", 0, (Sprite)null); } else { ((Character)player).UseEitr(20f); ((Humanoid)player).GetInventory().RemoveItem(item2); player.TeleportToRuneAddress(runeID); } } } static void Reject() { UnifiedPopup.Pop(); } } } [BepInPlugin("ZenDragon.ZenPortal", "ZenPortal", "0.6.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class Plugin : ZenMod, IMessageListener { public const string PluginName = "ZenPortal"; public const string PluginVersion = "0.6.0"; public const string PluginGUID = "ZenDragon.ZenPortal"; public static bool IsAdminKeyPressed { get { if (PlayerExt.IsGodMode(Player.m_localPlayer)) { return ZInput.GetKey((KeyCode)308, true); } return false; } } protected override void Setup() { ((ZenMod)this).RunOnServer = true; base.RegisterCraftingItems += ItemRuneShard.AddCraftingItem; base.ConfigSync += Portal.InitPrefabs; Commands.Init(); if (Logging.IsEnabled && !BitUtils.VerifyMixer()) { throw new Exception("Permutation function failed to pass all tests."); } } protected override void TitleScene(bool isFirstBoot) { } protected override void WorldStart() { PortalFuel.Init(); PortalConnect.Init(); BiomeTeleport.InitTeleportZoneCache(); PortalSickness.Init(); PortalDrift.Init(); SlowFallUngrounded.Init(); } protected override void Shutdown() { PortalFuel.Shutdown(); PortalConnect.Shutdown(); PortalSickness.Shutdown(); PortalDrift.Shutdown(); SlowFallUngrounded.Shutdown(); } protected override void HotRestore() { if (Object.op_Implicit((Object)(object)ZNetScene.instance)) { Portal.InitPrefabs(); PortalSickness.HotRestore(); PortalFuel.HotRestore(); } } public void ReceiveMessage(string message, object? arg) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown if (!(message != Portal.Message_PortalSymbols) && arg != null) { object[] obj = (object[])arg; ZDO portalZDO = (ZDO)obj[0]; obj[1] = (Portal.TryGetRuneID(portalZDO, out var runeID) ? runeID.ToMysticSymbols() : null); } } protected override HelpInfo? GetHelpInfo() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(HelpInfo.Format("Description", "Portals behave differently than in Vanilla, they are more immersive and mysterious.\r\nVisit the Runestones scattered throughout the lands to discover their secrets.", (string)null, true, true)); if (Configs.LinkSameTypeOnly.Value) { stringBuilder.AppendLine(HelpInfo.Format(Configs.LinkSameTypeOnly, false, true, true)); } if (Configs.TravelKnownBiome.Value != 0) { stringBuilder.AppendLine(HelpInfo.Format(Configs.TravelKnownBiome, false, true, true)); } return new HelpInfo("Portal", stringBuilder.ToString()); } } [HarmonyPatch] internal static class Portal { [Flags] public enum PortalType : byte { None = 0, PortalWood = 1, PortalStone = 2 } private enum LinkStatus : byte { Pending, Connected, AlreadyInUse } private static class PrefabName { public const string Wood = "portal_wood"; public const string Stone = "portal_stone"; } private const string RuneTagPrefix = "ZEN:"; public static bool IsRuneTag(this string tag) { return tag.StartsWith("ZEN:"); } public static bool IsPortal(this Component component) { PortalType portalType; return component.IsPortal(out portalType); } public static bool IsPortal(this Component component, out PortalType portalType) { return component.gameObject.IsPortal(out portalType); } public static bool IsPortal(this GameObject g) { PortalType portalType; return g.IsPortal(out portalType); } public static bool IsPortal(this GameObject g, out PortalType portalType) { return (portalType = GetPortalType(GameObjectExt.GetPrefabName(g))) != PortalType.None; } public static bool HaveRune(this TeleportWorld teleport) { RuneID runeID; return teleport.TryGetRuneID(out runeID); } public static Biome GetAllowedBiomes(Component portal) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return GetAllowedBiomes(portal.gameObject); } public static Biome GetAllowedBiomes(GameObject portal) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) return Configs.GetConfigValue(GetPortalType(((Object)portal).name), Configs.AllowBiomesPortalWood, Configs.AllowBiomesPortalStone); } private static LinkStatus GetLinkStatus(this TeleportWorld teleport) { if (!teleport.HaveTarget()) { if (!PortalConnect.IsTagConnected(teleport.GetPortalTag(includeRunes: true))) { return LinkStatus.Pending; } return LinkStatus.AlreadyInUse; } return LinkStatus.Connected; } public static UIColor GetLinkStatusColor(this TeleportWorld teleport) { //IL_0006: 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_0024: Unknown result type (might be due to invalid IL or missing references) return teleport.GetLinkStatusColor(UIColor.op_Implicit(Configs.ColorPortalHoverTextLinkConnected.Value), UIColor.op_Implicit(Configs.ColorRuneHoverTextLinkPending.Value), UIColor.op_Implicit(Configs.ColorRuneHoverTextLinkUnavailable.Value)); } public static UIColor GetLinkStatusColor(this TeleportWorld teleport, UIColor connected, UIColor pending, UIColor alreadyInUse) { return (UIColor)(teleport.GetLinkStatus() switch { LinkStatus.Connected => connected, LinkStatus.Pending => pending, LinkStatus.AlreadyInUse => alreadyInUse, _ => throw new ArgumentOutOfRangeException(), }); } public static bool TryGetRuneID(ZDO portalZDO, out RuneID runeID) { if (portalZDO.IsValid()) { string @string = portalZDO.GetString(ZDOVars.s_tag, ""); if (!@string.IsRuneTag()) { runeID = default(RuneID); return false; } return RuneID.TryParse(@string.Substring("ZEN:".Length, 4), out runeID); } runeID = default(RuneID); return false; } public static bool TryGetRuneID(this TeleportWorld teleport, out RuneID runeID) { if (Object.op_Implicit((Object)(object)teleport.m_nview) && teleport.m_nview.IsValid()) { return TryGetRuneID(teleport.m_nview.GetZDO(), out runeID); } runeID = default(RuneID); return false; } public static bool TryTakeRune(this TeleportWorld teleport, Humanoid human) { ItemData rune; return teleport.TryTakeRune(human, out rune); } public static bool TryTakeRune(this TeleportWorld teleport, Humanoid human, out ItemData rune) { rune = null; if (!teleport.TryGetRuneID(out var runeID)) { return false; } GameObject gameObject = ((Component)ItemRuneShard.Prefab).gameObject; if (!InventoryExt.TryAddItem(human.GetInventory(), gameObject, 1, ref rune)) { ((Character)human).Message((MessageType)2, "$inventory_full", 0, (Sprite)null); return false; } rune.SetRuneID(runeID); ((Character)human).ShowRemovedMessage(rune, 1); PlayInventoryFX(); teleport.SetText(string.Empty); PortalFuel portalFuel = default(PortalFuel); if (((Component)teleport).TryGetComponent(ref portalFuel)) { portalFuel.SetExpireTime(0.0); } return true; } public static string CreatePortalTag(RuneID runeID) { return "ZEN:" + runeID; } public static string GetPortalTag(this TeleportWorld teleport, bool includeRunes = false) { string text = default(string); string text2 = default(string); teleport.GetTagSignature(ref text, ref text2); if (!includeRunes && text.IsRuneTag()) { return string.Empty; } return text; } internal static void AssignRune(this TeleportWorld teleport, RuneID runeID) { teleport.SetText(CreatePortalTag(runeID)); teleport.PlayInteractFX(); PlayInventoryFX(); } public static bool IsFuelEmpty(this TeleportWorld teleport) { PortalFuel portalFuel = default(PortalFuel); if (((Component)teleport).TryGetComponent(ref portalFuel)) { return portalFuel.IsFuelEmpty(); } return false; } public static bool AddFuel(this TeleportWorld teleport, Humanoid human) { PortalFuel portalFuel = default(PortalFuel); if (!((Component)teleport).TryGetComponent(ref portalFuel)) { return false; } ItemDrop fuelItem = portalFuel.FuelItem; if (!Object.op_Implicit((Object)(object)fuelItem)) { return false; } Inventory inventory = human.GetInventory(); string name = ItemDataExt.GetName(fuelItem); if (!inventory.HaveItem(name, true)) { ((Character)human).Message((MessageType)2, "$msg_donthaveany " + name, 0, (Sprite)null); return false; } if (!teleport.HaveTarget()) { ((Character)human).Message((MessageType)2, "$error_disconnected", 0, (Sprite)null); ((Character)human).Message((MessageType)1, StringExt.Localize("$portal_need", new string[1] { ItemDataExt.GetName(ItemRuneShard.Prefab) }), 0, ItemDataExt.GetIcon(ItemRuneShard.Prefab)); return false; } if (portalFuel.GetFuelUnits() >= PortalFuel.MaxUnits) { ((Character)human).Message((MessageType)2, StringExt.Localize("$msg_cantaddmore", new string[1] { string.Empty }), 0, (Sprite)null); return false; } portalFuel.AddFuelUnits(1); inventory.RemoveItem(ItemDataExt.GetName(fuelItem), 1, -1, true); ((Character)human).Message((MessageType)2, StringExt.Localize("$portal_add_fuel_use", new string[1] { ItemDataExt.GetName(fuelItem) }), 0, (Sprite)null); return true; } private static void PlayInventoryFX() { FX.PlayFX((MonoBehaviour)(object)Player.m_localPlayer, "sfx_equip"); } private static bool IsCovered(this TeleportWorld teleport, bool showMessage) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!PlayerExt.IsReady(localPlayer)) { return false; } PortalType portalType = teleport.GetPortalType(); bool flag = Configs.TravelRequireRoof.Value.HasFlag(portalType); bool flag2 = Configs.TravelRequireWalls.Value.HasFlag(portalType); if (!flag && !flag2) { return true; } Transform transform = ((Component)teleport).transform; float num = default(float); bool flag3 = default(bool); Cover.GetCoverForPoint(transform.position + transform.up * 0.25f, ref num, ref flag3, 0.5f); if (flag && !flag3) { if (showMessage) { ((Character)localPlayer).Message((MessageType)2, "$portal_need_roof", 0, (Sprite)null); } return false; } if (flag2 && num < 0.8f) { if (showMessage) { ((Character)localPlayer).Message((MessageType)2, "$portal_need_walls", 0, (Sprite)null); } return false; } return true; } public static Biome GetConnectedBiome(this TeleportWorld teleport) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!((Component)(object)teleport).IsPortal() || !Object.op_Implicit((Object)(object)teleport.m_nview) || !teleport.m_nview.IsValid()) { return (Biome)0; } ZDOID connectionZDOID = teleport.m_nview.GetZDO().GetConnectionZDOID((ConnectionType)1); ZDO zDO = ZDOMan.instance.GetZDO(connectionZDOID); if (zDO == null) { return (Biome)0; } return WorldGenerator.instance.GetBiome(zDO.GetPosition()); } public static void PlayInteractFX(this TeleportWorld teleport) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) EffectList removedPermittedEffect = GlobalStatic.GetPrefab("guard_stone", true).m_removedPermittedEffect; Vector3 val = (Vector3)(teleport.GetPortalType() switch { PortalType.PortalWood => ((Component)teleport).transform.position + ((Component)teleport).transform.up * 0.35f, PortalType.PortalStone => ((StaticTarget)((Component)teleport).GetComponent()).GetCenter(), _ => ((Component)teleport).transform.position, }); Logging.Info((object)"Applying client-side portal effects", 0); removedPermittedEffect.Create(val, ((Component)teleport).transform.rotation, (Transform)null, 1f, -1); } [HarmonyPostfix] [HarmonyPatch(typeof(Piece), "DropResources")] private static void Piece_DropResources(Piece __instance) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) TeleportWorld val = default(TeleportWorld); if (((Component)(object)__instance).IsPortal() && ((Component)__instance).TryGetComponent(ref val) && val.TryGetRuneID(out var runeID)) { ItemRuneShard.Spawn(runeID, ((Component)val).transform.position, 1, autoPickup: true); } } [HarmonyPrefix] [HarmonyPatch(typeof(TeleportWorld), "Teleport")] private static void TeleportWorld_Teleport(TeleportWorld __instance, Player player, ref bool __runOriginal) { if (!__instance.IsFuelEmpty() && !__instance.CanTeleport(player, showMessage: true)) { __runOriginal = false; } } [HarmonyPostfix] [HarmonyPatch(typeof(TeleportWorld), "TargetFound")] private static void TeleportWorld_TargetFound(TeleportWorld __instance, ref bool __result) { __result = __result && !__instance.IsFuelEmpty() && __instance.CanTeleport(Player.m_localPlayer, showMessage: false); } private static bool CanTeleport(this TeleportWorld teleport, Player? player, bool showMessage) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (!Configs.TravelKnownBiome.Value.HasFlag(teleport.GetPortalType())) { return true; } if (!Object.op_Implicit((Object)(object)player)) { return false; } if (PlayerExt.IsGodMode(player)) { Logging.Info((object)"Admin god mode always allowed", 0); return true; } if (!teleport.IsCovered(showMessage)) { return false; } Biome connectedBiome = teleport.GetConnectedBiome(); if ((int)connectedBiome == 0 || !player.IsBiomeKnown(connectedBiome)) { if (showMessage) { ((Character)player).Message((MessageType)2, "$portal_travel_unknown", 0, (Sprite)null); } return false; } return true; } [HarmonyTranspiler] [HarmonyPatch(typeof(WearNTear), "HaveSupport")] [HarmonyPriority(100)] private static IEnumerable WearNTear_HaveSupport_Transpiler(IEnumerable codes) { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(WearNTear), "GetMinSupport", (Type[])null, (Type[])null); Func func = GetMinSupportIntercept; return Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)func.Method); static float GetMinSupportIntercept(WearNTear wnt) { if (!((Component)(object)wnt).IsPortal(out var portalType)) { return wnt.GetMinSupport(); } if (Configs.CraftRequiresHeavySupport.Value == PortalType.PortalStone) { return wnt.GetMinSupport(); } return Configs.CraftRequiresHeavySupport.Value.HasFlag(portalType) ? 100 : 10; } } public static PortalType GetPortalType(this TeleportWorld teleport) { return GetPortalType(((Object)teleport).name); } public static PortalType GetPortalType(string name) { string prefabName = Utils.GetPrefabName(name); if (!(prefabName == "portal_wood")) { if (prefabName == "portal_stone") { return PortalType.PortalStone; } return PortalType.None; } return PortalType.PortalWood; } public static void InitPrefabs() { InitPortalPrefab("portal_wood"); InitPortalPrefab("portal_stone"); } private static void InitPortalPrefab(string prefabName) { Logging.Info((object)("Init portal: " + prefabName), 0); TeleportWorld prefab = GlobalStatic.GetPrefab(prefabName, false); PortalType portalType = GetPortalType(prefabName); ((Component)prefab).gameObject.AddComponent(); string configValue = Configs.GetConfigValue(portalType, Configs.CraftPortalWoodStation, Configs.CraftPortalStoneStation); Requirement[] array = Configs.GetConfigValue(portalType, Configs.CraftPortalWoodResources, Configs.CraftPortalStoneResources).ToRequirements(':'); Piece component = ((Component)prefab).GetComponent(); if (array.Length != 0) { component.m_resources = array; } if (!Utility.IsNullOrWhiteSpace(configValue)) { component.m_craftingStation = PrefabManagerExt.GetCraftingStation(PrefabManager.Instance, configValue); } } } [HarmonyPatch] internal static class PortalConnect { public enum TagStatus : byte { Pending, Connected, Unconnected } private static readonly Dictionary PortalTagStatus = new Dictionary(); public static TagStatus GetTagStatus(string tag) { return GetTagStatus(StringExtensionMethods.GetStableHashCode(tag)); } public static TagStatus GetTagStatus(int tagHash) { if (!PortalTagStatus.TryGetValue(tagHash, out var value)) { return TagStatus.Unconnected; } return value; } public static bool IsTagConnected(int tagHash) { return GetTagStatus(tagHash) == TagStatus.Connected; } public static bool IsTagConnected(string tag) { return IsTagConnected(StringExtensionMethods.GetStableHashCode(tag)); } public static bool IsLockedTag(this string tag) { return tag.EndsWith("!"); } public static bool IsLockedTag(this string tag, out string text) { return (tag.IsLockedTag() ? (text = tag.Remove(tag.Length - 1)) : (text = null)) != null; } public static void Init() { PortalTagStatus.Clear(); ZRoutedRpc.instance.Register("RPC_UpdatePortalTagStatus", (Action)RPC_UpdatePortalTagStatus); } public static void Shutdown() { ZRoutedRpcExt.Unregister(ZRoutedRpc.instance, "RPC_UpdatePortalTagStatus"); } private static void SendPortalTagStatusUpdate(long target) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (ZNet.instance.GetPeerConnections() == 0) { Logging.Debug((object)"No clients connected, aborting send", 0); return; } ZPackage val = new ZPackage(); foreach (KeyValuePair item in PortalTagStatus) { val.Write(item.Key); val.Write((byte)item.Value); } Logging.Info((object)$"Sending connected portal tag satus to client: {target}", 0); ZRoutedRpc.instance.InvokeRoutedRPC(target, "RPC_UpdatePortalTagStatus", new object[1] { val }); } private static void RPC_UpdatePortalTagStatus(long sender, ZPackage pkg) { if (!ZNet.instance.IsServer()) { Logging.Info((object)"Receive portal tag status update from server", 0); PortalTagStatus.Clear(); while (pkg.GetPos() < pkg.Size()) { PortalTagStatus.Add(pkg.ReadInt(), (TagStatus)pkg.ReadByte()); } } } [HarmonyPrefix] [HarmonyPatch(typeof(Game), "ConnectPortals")] private static void Game_ConnectPortals_Prefix(Game __instance) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) PortalTagStatus.Clear(); foreach (ZDO portal in ZDOMan.instance.GetPortals()) { int stableHashCode = StringExtensionMethods.GetStableHashCode(portal.GetString(ZDOVars.s_tag, "")); if (!IsTagConnected(stableHashCode)) { ZDOID connectionZDOID = portal.GetConnectionZDOID((ConnectionType)1); TagStatus value = ((!((ZDOID)(ref connectionZDOID)).IsNone()) ? TagStatus.Connected : TagStatus.Pending); PortalTagStatus[stableHashCode] = value; } } if (ZNet.instance.IsServer()) { SendPortalTagStatusUpdate(ZRoutedRpc.Everybody); } } [HarmonyTranspiler] [HarmonyPatch(typeof(Game), "ConnectPortals")] private static IEnumerable Game_ConnectPortals_Transpiler(IEnumerable codes) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZDO), "GetString", new Type[2] { typeof(int), typeof(string) }, (Type[])null); Func func = GetStringIntercept; return new CodeMatcher(codes, (ILGenerator)null).MatchStartForward((CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Callvirt, (object)methodInfo, (string)null) }).ThrowIfInvalid("Unable to match IL").Set(OpCodes.Call, (object)func.Method) .InstructionEnumeration(); static string? GetStringIntercept(ZDO zdo, int hash, string defaultValue) { bool num = hash == ZDOVars.s_tag; string @string = zdo.GetString(hash, defaultValue); if (num && Utility.IsNullOrWhiteSpace(@string)) { return null; } return @string; } } [HarmonyPrefix] [HarmonyPatch(typeof(Game), "FindRandomUnconnectedPortal")] [HarmonyPriority(800)] private static bool Prefix_Game_FindRandomUnconnectedPortal(string tag) { if (Utility.IsNullOrWhiteSpace(tag)) { return false; } return !IsTagConnected(tag); } [HarmonyTranspiler] [HarmonyPatch(typeof(Game), "FindRandomUnconnectedPortal")] private static IEnumerable Transpiler_Game_FindRandomUnconnectedPortal(IEnumerable codes) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZDO), "GetString", new Type[2] { typeof(int), typeof(string) }, (Type[])null); Func func = GetStringIntercept; return new CodeMatcher(codes, (ILGenerator)null).MatchStartForward((CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Callvirt, (object)methodInfo, (string)null) }).ThrowIfInvalid("Unable to match IL").Set(OpCodes.Call, (object)func.Method) .Insert((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldarg_2, (object)null) }) .InstructionEnumeration(); static string? GetStringIntercept(ZDO dest, int hash, string defaultValue, ZDO source) { string @string = dest.GetString(hash, defaultValue); if (Configs.LinkSameTypeOnly.Value && !@string.IsLockedTag() && dest.GetPrefab() != source.GetPrefab()) { return null; } return @string; } } } internal class PortalFuel : MonoBehaviour { public const float SecPerFuelUnit = 60f; private ZNetView _nview; private Portal.PortalType _portalType; public ItemDrop? FuelItem => (ItemDrop?)(_portalType switch { Portal.PortalType.PortalWood => GlobalStatic.GetPrefab(Configs.FuelItemPortalWood.Value, true), Portal.PortalType.PortalStone => GlobalStatic.GetPrefab(Configs.FuelItemPortalStone.Value, true), _ => null, }); public static int MaxUnits => Configs.FuelMax.Value; public double FuelTimeRemaining => Math.Max(0.0, ExpireTime - ZNet.instance.GetTimeSeconds()); private double ExpireTime => ZdoExt.GetDouble(_nview.GetZDO(), ZDOVar.ExpireTime, 0.0); private void Awake() { _nview = ((Component)this).GetComponent(); _portalType = Portal.GetPortalType(((Object)this).name); } public static void Init() { RegisterGlobalRPC(); } private static void RegisterGlobalRPC() { ZRoutedRpc.instance.Register("RPC_SetRemotePortalExpireTime", (Action)RPC_SetRemotePortalExpireTime); } private static void UnregisterGlobalRPC() { ZRoutedRpcExt.Unregister(ZRoutedRpc.instance, "RPC_SetRemotePortalExpireTime"); } public static void HotRestore() { TeleportWorld[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { ((Component)array[i]).gameObject.AddComponent(); } } public static void Shutdown() { TeleportWorld[] array = Object.FindObjectsByType((FindObjectsSortMode)0); PortalFuel portalFuel = default(PortalFuel); for (int i = 0; i < array.Length; i++) { if (((Component)array[i]).TryGetComponent(ref portalFuel)) { Object.Destroy((Object)(object)portalFuel); } } UnregisterGlobalRPC(); } public void AddFuelUnits(int units) { double timeSeconds = ZNet.instance.GetTimeSeconds(); float num = (float)units * 60f; double num2 = Math.Max(0.0, ExpireTime - timeSeconds) + (double)num; SetExpireTime(timeSeconds + num2); } public int GetFuelUnits() { double fuelTimeRemaining = FuelTimeRemaining; if (!(fuelTimeRemaining <= 0.0)) { return Mathf.CeilToInt((float)(fuelTimeRemaining / 60.0)); } return 0; } public void SetExpireTime(double expireTime) { UpdateConnectedPortalFuel(expireTime); SetZDOExpireTime(_nview.GetZDO(), expireTime); PlayFuelAddFX(); } private static void SetZDOExpireTime(ZDO zdo, double expireTime) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) Logging.Info((object)$"Set ZDO fuel expire time {expireTime}", 0); if (!zdo.IsOwner()) { zdo.SetOwner(ZDOMan.GetSessionID()); } ZdoExt.Set(zdo, ZDOVar.ExpireTime, expireTime); if (expireTime <= 0.0) { zdo.SetConnection((ConnectionType)1, ZDOID.None); } } public bool IsFuelEmpty() { if (Object.op_Implicit((Object)(object)FuelItem)) { return ZNet.instance.GetTimeSeconds() >= ExpireTime; } return false; } private void PlayFuelAddFX() { ((Component)this).GetComponent().PlayInteractFX(); } private void UpdateConnectedPortalFuel(double expireTime) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) ZDOID connectionZDOID = _nview.GetZDO().GetConnectionZDOID((ConnectionType)1); if (!((ZDOID)(ref connectionZDOID)).IsNone()) { Logging.Info((object)"Update connected portal fuel levels to match this portal.", 0); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "RPC_SetRemotePortalExpireTime", new object[2] { connectionZDOID, expireTime }); } } private static void RPC_SetRemotePortalExpireTime(long sender, ZDOID portalID, double expireTime) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) ZDO zDO = ZDOMan.instance.GetZDO(portalID); if (!zDO.IsOwner()) { return; } Logging.Info((object)$"Set expire time on remote portal at {zDO.GetPosition()}: {expireTime:N}", 0); SetZDOExpireTime(zDO, expireTime); if (!ZNet.instance.IsDedicated()) { GameObject val = ZNetScene.instance.FindInstance(portalID); PortalFuel portalFuel = default(PortalFuel); if (Object.op_Implicit((Object)(object)val) && val.TryGetComponent(ref portalFuel)) { portalFuel.PlayFuelAddFX(); } } } } [HarmonyPatch] internal static class PortalGroup { public const string PlayerKey = "portalgroup"; public static int Max => Configs.RuneStonePortalGroups.Value; public static int GetPortalGroup(this Player player) { string s = default(string); if (!player.TryGetUniqueKeyValue("portalgroup", ref s)) { return 0; } if (!int.TryParse(s, out var result)) { return 0; } return result % Max; } public static void SetPortalGroup(this Player player, int group) { player.RemoveUniqueKeyValue("portalgroup"); player.AddUniqueKeyValue("portalgroup", group.ToString()); } public static bool HasPortalGroup(this Player player) { return player.m_uniques.Any((string k) => k.StartsWith("portalgroup")); } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "OnSpawned")] private static void Player_OnSpawned(Player __instance) { if (__instance.HasPortalGroup()) { int portalGroup = __instance.GetPortalGroup(); Logging.Message((object)$"Player is using portal group {portalGroup}", 0); } else if (Max > 1) { int group = Random.Range(0, Max); __instance.SetPortalGroup(group); } } } [HarmonyPatch] internal static class PortalPlacement { private static readonly List BiomeEdgesCache = new List(); private static Biome _invalidEdgeBiome; private static int _altitudeDelta; private static void ResetState() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) _invalidEdgeBiome = (Biome)0; _altitudeDelta = 0; } private static bool IsNearDungeonEntrance(Vector3 position) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) int range = Configs.PreventNearDungeonRange.Value; if (range > 0) { return Object.op_Implicit((Object)(object)((IEnumerable)Location.s_allLocations).FirstOrDefault((Func)((Location loc) => loc.m_hasInterior && MathExt.InRange((MonoBehaviour)(object)loc, position, (float)range)))); } return false; } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "SetPlacementGhostValid")] private static void Player_SetPlacementGhostValid(Player __instance, ref bool valid) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) ResetState(); if (!__instance.m_placementGhost.IsPortal() || (int)Portal.GetAllowedBiomes(__instance.m_placementGhost) == 895 || Plugin.IsAdminKeyPressed || !valid) { return; } valid = false; Vector3 position = __instance.m_placementGhost.transform.position; Biome val = Heightmap.FindBiome(position); Biome allowedBiomes = Portal.GetAllowedBiomes(__instance.m_placementGhost); if (!((Enum)allowedBiomes).HasFlag((Enum)(object)val)) { __instance.m_placementStatus = (PlacementStatus)8; return; } if (Configs.AllowBiomesMargin.Value > 0) { Biome val2 = ~allowedBiomes; BiomeEdgesCache.Clear(); Heightmap.FindHeightmap(position, (float)Configs.AllowBiomesMargin.Value, BiomeEdgesCache); foreach (Heightmap item in BiomeEdgesCache) { if (!item.HaveBiome(val2)) { continue; } Biome[] cornerBiomes = item.m_cornerBiomes; foreach (Biome val3 in cornerBiomes) { if (!((Enum)allowedBiomes).HasFlag((Enum)(object)val3)) { _invalidEdgeBiome = val3; __instance.m_placementStatus = (PlacementStatus)1; return; } } } } int biomeMinAltitude = Configs.GetBiomeMinAltitude(position); if (position.y < (float)biomeMinAltitude) { _altitudeDelta = Mathf.CeilToInt((float)biomeMinAltitude - position.y); __instance.m_placementStatus = (PlacementStatus)1; } else if (IsNearDungeonEntrance(__instance.m_placementGhost.transform.position)) { __instance.m_placementStatus = (PlacementStatus)1; } else { valid = true; } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "TryPlacePiece")] private static void TryPlacePiece(Player __instance, Piece piece) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Invalid comparison between Unknown and I4 Piece piece2 = piece; if ((int)__instance.m_placementStatus == 0 || !((Component)(object)piece2).IsPortal()) { return; } if ((int)_invalidEdgeBiome != 0) { string text = StringExt.Localize("$portal_too_close", new string[1] { BiomeExt.Localize(_invalidEdgeBiome) }); ((Character)__instance).Message((MessageType)2, text, 0, (Sprite)null); } else if (_altitudeDelta > 0) { string text2 = StringExt.Localize("$portal_altitude_error", new string[1] { _altitudeDelta + "m" }); ((Character)__instance).Message((MessageType)2, text2, 0, (Sprite)null); } else if ((int)__instance.m_placementStatus == 8) { string text3 = GeneralExtensions.Join(from Biome b in Enum.GetValues(typeof(Biome)) where (int)b != 0 && ((Enum)Portal.GetAllowedBiomes((Component)(object)piece2)).HasFlag((Enum)(object)b) select BiomeExt.Localize(b), (Func)null, ", "); ((Character)__instance).Message((MessageType)2, "$msg_wrongbiome" + StringExt.Localize("\n$portal_need", new string[1] { text3 }), 0, (Sprite)null); } else { Logging.Debug((object)"OK", 0); } } } [HarmonyPatch] internal static class PortalUI { private const string HiddenTagPrefix = "."; [HarmonyPostfix] [HarmonyPatch(typeof(TeleportWorld), "GetHoverName")] private static void TeleportWorld_GetHoverName(ref string __result) { __result = "$piece_portal"; } [HarmonyPostfix] [HarmonyPatch(typeof(TeleportWorld), "GetHoverText")] private static void TeleportWorld_GetHoverText(TeleportWorld __instance, ref string __result) { //IL_01cd: Unknown result type (might be due to invalid IL or missing references) PortalFuel portalFuel = default(PortalFuel); if (!((Component)__instance).TryGetComponent(ref portalFuel)) { return; } double fuelTimeRemaining = portalFuel.FuelTimeRemaining; Logging.Debug((object)$"Fuel time remaining: {fuelTimeRemaining}", 0); string arg = UI.RemainingTimeText(fuelTimeRemaining, 60f); string text = ((fuelTimeRemaining > 1.0) ? $"\n{arg}" : string.Empty); ItemDrop? fuelItem = portalFuel.FuelItem; string text2 = (Plugin.IsAdminKeyPressed ? "$piece_portal_settag" : "$portal_add_fuel_label"); string text3 = (Object.op_Implicit((Object)(object)fuelItem) ? ("\n" + UI.PromptInteract + " " + text2) : string.Empty); string text4 = __instance.GetPortalTag(); string text5 = "\n" + UI.PromptInteractAlt + " $hud_remove"; bool flag = true; if (!__instance.TryGetRuneID(out var runeID)) { text5 = (__instance.TargetFound() ? string.Empty : ("\n" + UI.PromptUseItem + " $piece_useitem")); flag = false; } if (text4.IsLockedTag()) { text5 = string.Empty; } bool flag2 = false; if (text4.StartsWith(".")) { flag2 = true; text4 = text4.Substring(1); } if (text4.IsLockedTag(out string text6)) { text4 = text6; } string text7 = ((flag2 || Utility.IsNullOrWhiteSpace(text4)) ? string.Empty : (" - " + text4)); string text8 = __instance.GetHoverName() + text7 + text3 + text5 + text; if (flag) { UIColor linkStatusColor = __instance.GetLinkStatusColor(); string hoverTextMysticSymbols = runeID.GetHoverTextMysticSymbols(linkStatusColor); HoverItem.UpdateIcons(ItemDataExt.GetIcon(ItemRuneShard.Prefab), hoverTextMysticSymbols); if (!HoverItem.IsLoaded) { text8 += hoverTextMysticSymbols; } } __result = StringExt.Localize(text8); if (Plugin.IsAdminKeyPressed) { __result += $"\n\nRemote: {__instance.GetConnectedBiome()}"; } } [HarmonyPrefix] [HarmonyPatch(typeof(TeleportWorld), "UseItem")] private static void TeleportWorld_UseItem(TeleportWorld __instance, Humanoid user, ItemData item, ref bool __runOriginal, ref bool __result) { __runOriginal = false; __result = false; if (ItemDataExt.GetPrefabName(item) != "ZenRuneShard" || !WardAccessExt.CanAccessWard((MonoBehaviour)(object)__instance, true)) { return; } RuneID runeID; if (__instance.GetPortalTag().IsLockedTag()) { Logging.Message((object)"Admin only portal, can not change via rune interaction", 0); ((Character)user).Message((MessageType)1, "$msg_fireportalalready", 0, ((Component)__instance).GetComponent().m_icon); } else if (item.TryGetRuneID(out runeID)) { if (__instance.TryGetRuneID(out var runeID2)) { item.SetRuneID(runeID2); } else if (!user.GetInventory().RemoveOneItem(item)) { return; } __instance.AssignRune(runeID); __result = true; } } [HarmonyPrefix] [HarmonyPatch(typeof(TeleportWorld), "Interact")] private static void TeleportWorld_Interact(TeleportWorld __instance, Humanoid human, bool hold, bool alt, ref bool __result, ref bool __runOriginal) { if (Plugin.IsAdminKeyPressed) { return; } __runOriginal = false; __result = false; if (alt) { if (WardAccessExt.CanAccessWard((MonoBehaviour)(object)__instance, true)) { __result = __instance.TryTakeRune(human); } else { __result = true; } } else { __result = __instance.AddFuel(human); } } [HarmonyPrefix] [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] private static bool InventoryGui_OnSelectedItem(InventoryGui __instance, ItemData? item, Modifier mod) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if (!item.TryGetRuneID(out var runeID)) { return true; } if ((int)mod != 1) { return true; } string text = runeID.ToMysticSymbols(); Logging.Info((object)$"Copying symbols to clipboard for RuneID: {runeID}", 0); GUIUtility.systemCopyBuffer = text; ((Character)Player.m_localPlayer).Message((MessageType)2, "$clipboard_copy " + text, 0, (Sprite)null); return false; } [HarmonyPostfix] [HarmonyPatch(typeof(InventoryGrid), "CreateItemTooltip")] private static void InventoryGrid_CreateItemTooltip(ItemData? item) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if (item.HasRuneID()) { HintType activeType = KeyHint.ActiveType; if (activeType - 4 <= 1) { KeyHint.SetLabelOneFrame((HintName)15, StringExt.Localize("$clipboard_copy")); } } } } internal readonly record struct RuneID { public Biome Biome => ByteToBiome(_biome); public int Channel => _index; public int Group => _group; public int Identity => ToUInt16(); private static uint Seed => (uint)WorldGenerator.instance.GetSeed(); public static readonly string[] Symbols = new string[32] { "⚡", "\ud83d\udc41", "\ud83e\ude90", "\ud83d\udcab", "\ud83c\udf19", "☯", "➰", "✋", "\ud83d\udd25", "\ud83d\udc80", "✨", "\ud83d\udca5", "♂", "♀", "\ud83d\udca0", "\ud83c\udf00", "\ud83d\udcdc", "\ud83d\udd31", "\ud83d\udca7", "\ud83c\udff9", "\ud83c\udf32", "\ud83d\udc8e", "\ud83c\udf44", "\ud83c\udf0a", "\ud83d\udc1f\ufe0e", "\ud83c\udf38", "\ud83c\udf29", "☀", "♡", "\ud83e\udd8b", "⚓\ufe0e", "\ud83d\udee1" }; private const int Mask5Bit = 31; private readonly byte _group; private readonly byte _index; private readonly byte _biome; public const int MaxGroup = 32; public const int MaxChannel = 32; private RuneID(ushort packed) { UnpackData(packed, out _biome, out _index, out _group); } public RuneID(Biome biome, int channel, int group) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) _biome = BiomeToByte(biome); _index = (byte)(channel % 32); _group = (byte)(group % 32); } public RuneID(Vector3 position, int group) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) ulong cantorID = MathExt.GetCantorID(ZoneSystem.GetZone(position)); _biome = BiomeToByte(WorldGenerator.instance.GetBiome(position)); _index = (byte)(cantorID % (uint)Configs.RuneStoneChannelsPerBiome.Value); _group = (byte)(group % 32); } private ushort PackData() { return (ushort)((_biome << 10) | (_group << 5) | _index); } private static void UnpackData(ushort data, out byte biome, out byte index, out byte group) { biome = (byte)((uint)(data >> 10) & 0x1Fu); group = (byte)((uint)(data >> 5) & 0x1Fu); index = (byte)(data & 0x1Fu); } public UIColor GetLinkStatusColorInventory() { //IL_0006: 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_0024: Unknown result type (might be due to invalid IL or missing references) return GetLinkStatusColor(UIColor.op_Implicit(Configs.ColorRuneInventoryLinkAvailable.Value), UIColor.op_Implicit(Configs.ColorRuneInventoryLinkUnavailable.Value), UIColor.op_Implicit(Configs.ColorRuneInventoryLinkPending.Value)); } public UIColor GetLinkStatusColorHoverText() { //IL_0006: 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_0024: Unknown result type (might be due to invalid IL or missing references) return GetLinkStatusColor(UIColor.op_Implicit(Configs.ColorRuneHoverTextLinkAvailable.Value), UIColor.op_Implicit(Configs.ColorRuneHoverTextLinkUnavailable.Value), UIColor.op_Implicit(Configs.ColorRuneHoverTextLinkPending.Value)); } public UIColor GetLinkStatusColor(UIColor avail, UIColor connected, UIColor pending) { return (UIColor)(PortalConnect.GetTagStatus(Portal.CreatePortalTag(this)) switch { PortalConnect.TagStatus.Connected => connected, PortalConnect.TagStatus.Pending => pending, _ => avail, }); } public override string ToString() { return ToUInt16().ToString("X4"); } private ushort ToUInt16() { return PackData(); } public string ToMysticSymbols() { uint num = BitUtils.Mix15(ToUInt16(), Seed); string text = Symbols[num & 0x1F]; string text2 = Symbols[(num >> 5) & 0x1F]; return Symbols[(num >> 10) & 0x1F] + text2 + text; } public string GetHoverTextMysticSymbols(UIColor color) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) string text = "" + ToMysticSymbols() + ""; string text2 = "\n\n" + ((UIColor.op_Implicit(color) == Color.clear) ? text : $"{text}"); if (Plugin.IsAdminKeyPressed) { text2 += $"\n\nRune ID: {this}\n{Biome}"; } return text2; } public static bool TryParse(string dataTxt, out RuneID runeID, bool logError = true) { if (ushort.TryParse(dataTxt, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var result)) { runeID = new RuneID(result); return true; } if (logError) { Logging.Error((object)("Unable to parse string: " + dataTxt), (ushort)0); } runeID = default(RuneID); return false; } private static Biome ByteToBiome(byte code) { code = (byte)(code & 0x1Fu); if (code > Heightmap.s_indexToBiome.Length - 1) { return (Biome)0; } return Heightmap.s_indexToBiome[code]; } private static byte BiomeToByte(Biome biome) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) int value; return (byte)(Heightmap.s_biomeToIndex.TryGetValue(biome, out value) ? ((uint)value) : 0u); } [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) builder.Append("Biome = "); Biome biome = Biome; builder.Append(((object)(Biome)(ref biome)).ToString()); builder.Append(", Channel = "); builder.Append(Channel.ToString()); builder.Append(", Group = "); builder.Append(Group.ToString()); builder.Append(", Identity = "); builder.Append(Identity.ToString()); return true; } } [HarmonyPatch] internal static class RuneLocation { private static readonly Dictionary ValidLocationByHash = new string[11] { "Runestone_Meadows", "Runestone_BlackForest", "Runestone_Swamps", "Runestone_Mountains", "Runestone_Plains", "Runestone_Mistlands", "Runestone_Ashlands", "Runestone_Boars", "Runestone_Greydwarfs", "Runestone_Draugr", "DrakeLorestone" }.ToDictionary((string n) => StringExtensionMethods.GetStableHashCode(n)); private const string SFX_PlaceItem = "sfx_build_hammer_stone"; private static double _lastUsedTime; private static ItemDrop? FractureItemPrefab => GlobalStatic.GetPrefab(Configs.FractureItem.Value, true); private static bool IsValidLocation(int hash) { return ValidLocationByHash.ContainsKey(hash); } private static bool IsValidLocation(string locationName) { return IsValidLocation(StringExtensionMethods.GetStableHashCode(Utils.GetPrefabName(locationName))); } private static bool IsValidLocation(this RuneStone runestone) { return IsValidLocation(((Component)runestone).gameObject); } private static bool IsValidLocation(GameObject gameObject) { Transform parent = gameObject.transform.parent; if (Object.op_Implicit((Object)(object)parent) && IsValidLocation(((Object)parent).name)) { return true; } if (IsValidLocation(((Object)gameObject).name)) { return true; } ZNetView componentInParent = gameObject.GetComponentInParent(); if (!Object.op_Implicit((Object)(object)componentInParent) || !componentInParent.IsValid()) { return false; } return IsValidLocation(componentInParent.GetZDO().GetInt(ZDOVars.s_location, 0)); } private static void SetLastUsedTime() { _lastUsedTime = ZNet.instance.GetTimeSeconds(); } private static bool IsInteractCooldownExpired() { return ZNet.instance.GetTimeSeconds() > _lastUsedTime + 12.0; } private static void FractureShard(this RuneStone? runestone) { if (Object.op_Implicit((Object)(object)runestone)) { runestone.LightningStrike(); runestone.CreateShards(2); } } private static void CreateShards(this RuneStone runestone, int amount) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) RuneID runeID = runestone.GetRuneID(); Transform transform = ((Component)runestone).transform; ItemRuneShard.Spawn(runeID, transform.position - transform.forward, amount); } private static bool PlaceItem(this RuneStone runestone) { RuneStone runestone2 = runestone; if (!Object.op_Implicit((Object)(object)FractureItemPrefab)) { Logging.Info((object)"No Fracture Item defined", 0); return false; } Player player = Player.m_localPlayer; string itemName = ItemDataExt.GetName(FractureItemPrefab); if (((Humanoid)player).GetInventory().CountItems(itemName, -1, true) < Configs.FractureItemAmount.Value) { ((Character)player).Message((MessageType)2, "$msg_outof " + itemName, 0, (Sprite)null); return false; } SetLastUsedTime(); player.Kneel(2f, delegate { if (PlayerExt.IsReady(player)) { ((Humanoid)player).GetInventory().RemoveItem(itemName, Configs.FractureItemAmount.Value, -1, true); ((Character)player).ShowRemovedMessage(FractureItemPrefab.m_itemData, Configs.FractureItemAmount.Value); FX.PlayFX((MonoBehaviour)(object)player, "sfx_build_hammer_stone"); runestone2.DistantThunder(); Timing.Delay((MonoBehaviour)(object)runestone2, 4.5f, (Action)runestone2.FractureShard); } }); return true; } private static RuneID GetRuneID(this RuneStone runestone) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return new RuneID(((Component)runestone).transform.position, Player.m_localPlayer.GetPortalGroup()); } [HarmonyPostfix] [HarmonyPatch(typeof(RuneStone), "GetHoverText")] private static void RuneStone_GetHoverText(RuneStone __instance, ref string __result) { //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) if (!__instance.IsValidLocation()) { return; } if (((Character)Player.m_localPlayer).InEmote()) { __result = string.Empty; } else if (Object.op_Implicit((Object)(object)FractureItemPrefab)) { int value = Configs.FractureItemAmount.Value; string text = ((value > 1) ? $" x{value}" : string.Empty); if (IsInteractCooldownExpired()) { __result += StringExt.Localize("\n" + UI.PromptInteractAlt + " $piece_use " + ItemDataExt.GetName(FractureItemPrefab) + text); } RuneID runeID = __instance.GetRuneID(); UIColor linkStatusColorHoverText = runeID.GetLinkStatusColorHoverText(); string hoverTextMysticSymbols = runeID.GetHoverTextMysticSymbols(linkStatusColorHoverText); if (HoverItem.IsLoaded) { HoverItem.UpdateIcons(ItemDataExt.GetIcon(ItemRuneShard.Prefab), hoverTextMysticSymbols); } else { __result += hoverTextMysticSymbols; } if (Plugin.IsAdminKeyPressed) { Vector2i zone = ZoneSystem.GetZone(((Component)__instance).transform.position); ulong cantorID = MathExt.GetCantorID(zone); __result += $"\n\nCantor: {cantorID}, Zone: ({zone}), Group: {runeID.Group}"; } } } [HarmonyPrefix] [HarmonyPatch(typeof(RuneStone), "UseItem")] [HarmonyPriority(800)] private static void RuneStone_UseItem(RuneStone __instance, Humanoid user, ItemData item, ref bool __result, ref bool __runOriginal) { if (__instance.IsValidLocation() && !((Object)(object)FractureItemPrefab != (Object)(object)ItemDataExt.GetPrefabItemDrop(item))) { __runOriginal = false; __result = __instance.Interact(user, false, true); } } [HarmonyPrefix] [HarmonyPatch(typeof(RuneStone), "Interact")] private static void RuneStone_Interact(RuneStone __instance, Humanoid character, bool alt, bool hold, ref bool __result, ref bool __runOriginal) { if (__instance.IsValidLocation() && PlayerExt.Is(Player.m_localPlayer, (Character)(object)character) && !(!alt || hold) && Object.op_Implicit((Object)(object)FractureItemPrefab)) { __runOriginal = false; if (IsInteractCooldownExpired()) { __result = __instance.PlaceItem(); } } } } internal static class ZDOVar { public static readonly int ExpireTime = StringExtensionMethods.GetStableHashCode("ZenFuelExpireTime"); } } namespace ZenPortal.VanillaFix { [HarmonyPatch] internal static class FixPlayerControllerTakeInput { [HarmonyPostfix] [HarmonyPatch(typeof(PlayerController), "TakeInput")] private static void PlayerController_TakeInput(PlayerController __instance, bool look, ref bool __result) { if (__result) { __result = look || PlayerController.takeInputDelay <= 0f; } } } } namespace ZenPortal.StatusEffects { [HarmonyPatch] internal class PortalDrift : SE_Stats { [HarmonyPatch] private static class ProtectFreeze { [HarmonyPrefix] [HarmonyPatch(typeof(SEMan), "AddStatusEffect", new Type[] { typeof(int), typeof(bool), typeof(int), typeof(float) })] private static void SEMan_AddStatusEffect(SEMan __instance, int nameHash, ref bool __runOriginal) { if (nameHash == SEMan.s_statusEffectFreezing && __instance == ((Character)Player.m_localPlayer).GetSEMan() && __instance.HaveStatusEffect(SEHash)) { __runOriginal = false; } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "UpdateEnvStatusEffects")] private static void Player_UpdateEnvStatusEffects(Player __instance) { SEMan sEMan = ((Character)__instance).GetSEMan(); if (sEMan.HaveStatusEffect(SEMan.s_statusEffectFreezing) && sEMan.HaveStatusEffect(SEHash)) { sEMan.RemoveStatusEffect(SEMan.s_statusEffectFreezing, false); } } } private const string Name = "SE_ZenPortal_PortalDrift"; private static readonly int SEHash = StringExtensionMethods.GetStableHashCode("SE_ZenPortal_PortalDrift"); private const int WarningSeconds = 10; private Stack _jumpTargets = new Stack(); private bool _warned; private static PortalDrift Prefab => (PortalDrift)(object)ObjectDB.instance.GetStatusEffect(SEHash); private PortalDrift() { ((Object)this).name = "SE_ZenPortal_PortalDrift"; ((StatusEffect)this).m_name = "$se_portal_drift_name"; ((StatusEffect)this).m_icon = ItemDataExt.GetIcon(ItemRuneShard.Prefab); } public override void Setup(Character character) { ((StatusEffect)this).m_ttl = 60f; ((SE_Stats)this).Setup(character); } public static void ApplyTo(Player? player, Stack jumps, string? tombstoneText) { if (Object.op_Implicit((Object)(object)player)) { ((Character)player).GetSEMan().RemoveStatusEffect(SEHash, true); PortalDrift obj = (PortalDrift)(object)((Character)player).GetSEMan().AddStatusEffect((StatusEffect)(object)Prefab, false, 0, 0f); obj._jumpTargets = jumps; obj.JumpNext(tombstoneText); } } private void JumpNext(string? tombstoneText = null) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) Player player = (Player)((StatusEffect)this).m_character; if (_jumpTargets.Count > 0) { player.LightningTeleport(_jumpTargets.Pop(), tombstoneText); } if (_jumpTargets.Count != 0) { ((StatusEffect)this).ResetTime(); } } public static void Init() { PortalDrift item = ScriptableObject.CreateInstance(); ObjectDB.instance.m_StatusEffects.Add((StatusEffect)(object)item); } public static void Shutdown() { if (Object.op_Implicit((Object)(object)ObjectDB.instance) && Object.op_Implicit((Object)(object)Prefab)) { PortalDrift prefab = Prefab; ObjectDB.instance.m_StatusEffects.Remove((StatusEffect)(object)prefab); Object.Destroy((Object)(object)prefab); } } public override void ResetTime() { ((SE_Stats)this).ResetTime(); _warned = false; } public override void UpdateStatusEffect(float dt) { ((SE_Stats)this).UpdateStatusEffect(dt); Character character = ((StatusEffect)this).m_character; Player val = (Player)(object)((character is Player) ? character : null); if (val == null) { return; } if (!((StatusEffect)this).IsDone()) { if (!_warned && ((StatusEffect)this).m_ttl - ((StatusEffect)this).m_time < 10f) { ((StatusEffect)this).m_character.Message((MessageType)2, StringExt.Localize("$se_portal_drift_warning", new string[1] { 10.ToString() }), 0, (Sprite)null); _warned = true; } } else if (_jumpTargets.Count != 0) { val.DropAllNoTeleport(); JumpNext(); } } } [HarmonyPatch] internal class PortalSickness : SE_Stats { [HarmonyPatch] private static class TeleportUse { private static bool _isEnterPortal; [HarmonyPrefix] [HarmonyPatch(typeof(TeleportWorldTrigger), "OnTriggerEnter")] private static void TeleportWorldTrigger_OnTriggerEnter_Prefix() { _isEnterPortal = true; } [HarmonyPostfix] [HarmonyPatch(typeof(TeleportWorldTrigger), "OnTriggerEnter")] private static void TeleportWorldTrigger_OnTriggerEnter_Postfix() { _isEnterPortal = false; } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "TeleportTo")] private static void Player_TeleportTo(Player __instance, bool distantTeleport, bool __result) { Player __instance2 = __instance; if (_isEnterPortal && __result && distantTeleport && ((Character)__instance2).m_nview.IsOwner()) { Timing.When((MonoBehaviour)(object)__instance2, (Func)(() => !((Character)__instance2).IsTeleporting()), (Action)delegate { Inflict(__instance2, -1f, Configs.PortalSicknessPukeOnUse.Value); }); } } } [StructLayout(LayoutKind.Auto)] [CompilerGenerated] private struct <>c__DisplayClass31_0 { public Player player; public StatusEffect sickness; } [CompilerGenerated] private sealed class d__31 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Player player; public StatusEffect sickness; private <>c__DisplayClass31_0 <>8__1; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__31(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = default(<>c__DisplayClass31_0); <>1__state = -2; } private bool MoveNext() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1.player = player; <>8__1.sickness = sickness; break; case 1: <>1__state = -1; break; } if (g__UpdateTimeLeft|31_0(ref <>8__1)) { <>2__current = (object)new WaitForSeconds(2f); <>1__state = 1; return true; } if (!((Character)<>8__1.player).IsDead()) { <>8__1.player.m_customData.Remove("ZenPortalSicknessTimeLeft"); } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const string Name = "SE_ZenPortal_PortalSickness"; private static readonly int SEHash = StringExtensionMethods.GetStableHashCode("SE_ZenPortal_PortalSickness"); private const string KeyTimeLeft = "ZenPortalSicknessTimeLeft"; private static Coroutine? _persistCoroutine; private static bool _runStartEffects; private const float PukeTimeLimit = 10f; private const float PukeInterval = 1f; private float _pukeIntervalTimer; private float _pukeStartTime; private bool _removeFood; private static bool IsEnabled => Configs.PortalSicknessDuration.Value > 0; private static PortalSickness Prefab => GetOrRegisterEffect(); public static bool IsSick(Character? c) { PortalSickness se; return IsSick(c, out se); } public static bool IsSick(Character? c, out PortalSickness se) { return Object.op_Implicit((Object)(object)(Object.op_Implicit((Object)(object)c) ? (se = (PortalSickness)(object)c.GetSEMan().GetStatusEffect(SEHash)) : (se = null))); } public static void Init() { _persistCoroutine = null; GetOrRegisterEffect(); } private static PortalSickness GetOrRegisterEffect() { PortalSickness portalSickness = (PortalSickness)(object)ObjectDB.instance.GetStatusEffect(SEHash); if (Object.op_Implicit((Object)(object)portalSickness)) { return portalSickness; } portalSickness = ScriptableObject.CreateInstance(); ObjectDB.instance.m_StatusEffects.Add((StatusEffect)(object)portalSickness); return portalSickness; } public static void Shutdown() { StopPersistSickness(Player.m_localPlayer); if (Object.op_Implicit((Object)(object)ObjectDB.instance) && Object.op_Implicit((Object)(object)Prefab)) { PortalSickness prefab = Prefab; ObjectDB.instance.m_StatusEffects.Remove((StatusEffect)(object)prefab); Object.Destroy((Object)(object)prefab); } } public static void HotRestore() { if (IsSick((Character?)(object)Player.m_localPlayer)) { StatusEffect statusEffect = ((Character)Player.m_localPlayer).GetSEMan().GetStatusEffect(SEHash); StartPersistSickness(Player.m_localPlayer, statusEffect); } } private PortalSickness() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) ((Object)this).name = "SE_ZenPortal_PortalSickness"; ((StatusEffect)this).m_name = "$se_portal_sickness_name"; ((StatusEffect)this).m_tooltip = "$se_portal_sickness_tooltip"; ((StatusEffect)this).m_icon = GUIManager.Instance.GetSprite("mapicon_portal"); StatusEffect consumeStatusEffect = GlobalStatic.GetPrefab("Pukeberries", true).m_itemData.m_shared.m_consumeStatusEffect; ((StatusEffect)this).m_startEffects = consumeStatusEffect.m_startEffects; ((StatusEffect)this).m_startMessage = consumeStatusEffect.m_startMessage; ((StatusEffect)this).m_startMessageType = consumeStatusEffect.m_startMessageType; } public override void Setup(Character character) { ((StatusEffect)this).m_ttl = Configs.PortalSicknessDuration.Value * 60; base.m_speedModifier = Configs.PortalSicknessSpeedAdj.Value - 1f; base.m_staminaRegenMultiplier = Configs.PortalSicknessStaminaRegen.Value; base.m_healthRegenMultiplier = Configs.PortalSicknessHealthRegen.Value; base.m_tickInterval = 1f; ((SE_Stats)this).Setup(character); } public override void UpdateStatusEffect(float dt) { if (!IsEnabled) { Character character = ((StatusEffect)this).m_character; Player val = (Player)(object)((character is Player) ? character : null); if (val != null) { Cure(val); return; } } base.m_healthPerTick = (_removeFood ? Configs.PortalSicknessHealthPerTick.Value : 0f); ((SE_Stats)this).UpdateStatusEffect(dt); if (base.m_tickTimer == 0f && base.m_healthPerTick < 0f) { Hud.instance.DamageFlash(); } if (((StatusEffect)this).m_time < _pukeStartTime + 10f) { _pukeIntervalTimer += dt; if (!(_pukeIntervalTimer > 1f)) { return; } _pukeIntervalTimer = 0f; if (_removeFood && Object.op_Implicit((Object)(object)((StatusEffect)this).m_character)) { Character character2 = ((StatusEffect)this).m_character; Player val2 = (Player)(object)((character2 is Player) ? character2 : null); if (val2 != null) { val2.RemoveOneFood(); } } } else { _removeFood = false; } } private void PukeResetTimers() { Logging.Info((object)"Reset puke timers", 0); _pukeStartTime = ((StatusEffect)this).m_time; _pukeIntervalTimer = 1f; _removeFood = true; } private void Puke() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (PlayerExt.IsReady(localPlayer)) { Logging.Info((object)"Puking...", 0); ((Character)localPlayer).Message(((StatusEffect)Prefab).m_startMessageType, ((StatusEffect)Prefab).m_startMessage, 0, (Sprite)null); PukeResetTimers(); _runStartEffects = true; ((StatusEffect)this).TriggerStartEffects(); _runStartEffects = false; } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "OnSpawned")] private static void Player_OnSpawned(Player __instance) { CheckRestoreSickness(__instance); } private static void CheckRestoreSickness(Player player) { if (!IsEnabled) { return; } Logging.Info((object)"Checking if sickness needs to be restored", 0); if (IsSick((Character?)(object)player)) { Logging.Info((object)"Already have portal sickness", 0); return; } if (!player.m_customData.TryGetValue("ZenPortalSicknessTimeLeft", out var value)) { Logging.Info((object)"No persistant sickness key found, nothing to restore.", 0); return; } float num = float.Parse(value); if (num > 0f) { Inflict(player, num, puke: false); } else { Logging.Info((object)"Time remaining 0, nothing to restore", 0); } } public static void Cure(Player player) { Logging.Info((object)"Portal sickness cured!", 0); ((Character)player).GetSEMan().RemoveStatusEffect(SEHash, false); player.m_customData.Remove("ZenPortalSicknessTimeLeft"); } public static void Inflict(Player? player, float timeLeft = -1f, bool puke = true) { if (!IsEnabled || !Object.op_Implicit((Object)(object)player)) { return; } if (PlayerExt.IsGodMode(player)) { Logging.Message((object)"God mode active, no portal sickness applied.", 0); return; } Logging.Info((object)$"Applying portal sickness... puke? {puke}", 0); SEMan sEMan = ((Character)player).GetSEMan(); sEMan.RemoveStatusEffect(SEHash, true); PortalSickness portalSickness = (PortalSickness)(object)sEMan.AddStatusEffect((StatusEffect)(object)Prefab, false, 0, 0f); ((StatusEffect)portalSickness).m_ttl = Configs.PortalSicknessDuration.Value * 60; ((StatusEffect)portalSickness).m_time = ((timeLeft < 0f) ? 0f : Mathf.Max(0f, ((StatusEffect)portalSickness).m_ttl - timeLeft)); if (puke) { portalSickness.Puke(); } StartPersistSickness(player, (StatusEffect)(object)portalSickness); } private static void StartPersistSickness(Player player, StatusEffect sickness) { StopPersistSickness(player); _persistCoroutine = ((MonoBehaviour)player).StartCoroutine(PersistSickness(player, sickness)); } private static void StopPersistSickness(Player player) { if (Object.op_Implicit((Object)(object)player) && _persistCoroutine != null) { ((MonoBehaviour)player).StopCoroutine(_persistCoroutine); } _persistCoroutine = null; } [IteratorStateMachine(typeof(d__31))] private static IEnumerator PersistSickness(Player player, StatusEffect sickness) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__31(0) { player = player, sickness = sickness }; } [HarmonyPrefix] [HarmonyPatch(typeof(StatusEffect), "TriggerStartEffects")] private static bool StatusEffect_TriggerStartEffects(StatusEffect __instance) { if (!(__instance is PortalSickness)) { return true; } if (!_runStartEffects) { return false; } Logging.Info((object)"Triggering puke FX", 0); Character character = __instance.m_character; Player player = (Player)(object)((character is Player) ? character : null); if (player != null) { Timing.Delay((MonoBehaviour)(object)player, 2.5f, (Action)delegate { ((Humanoid)player).HideHandItems(false, true); player.Kneel(12f); }); } return true; } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "EatFood")] private static void Player_EatFood(Player __instance, ItemData item, ref bool __result) { if (Configs.PortalSicknessPukeOnEat.Value && __result && !Object.op_Implicit((Object)(object)item.m_shared.m_consumeStatusEffect) && IsSick((Character?)(object)__instance, out PortalSickness se)) { se.Puke(); } } [HarmonyPostfix] [HarmonyPatch(typeof(Humanoid), "IsTeleportable")] [HarmonyPriority(100)] private static void Humanoid_IsTeleportable(Humanoid __instance, ref bool __result) { if (Configs.PortalSicknessPreventTeleport.Value) { __result = __result && !IsSick((Character?)(object)__instance); } } [HarmonyTranspiler] [HarmonyPatch(typeof(TeleportWorld), "Teleport")] [HarmonyPriority(100)] private static IEnumerable TeleportWorld_Teleport(IEnumerable codes) { Func intercept = GetNoTeleportText; return Transpilers.Manipulator(codes, (Func)((CodeInstruction c) => CodeInstructionExtensions.Is(c, OpCodes.Ldstr, (object)"$msg_noteleport")), (Action)delegate(CodeInstruction c) { c.opcode = OpCodes.Call; c.operand = intercept.Method; }); static string GetNoTeleportText() { if (IsSick((Character?)(object)Player.m_localPlayer)) { return "$se_portal_sickness_noteleport"; } return "$msg_noteleport"; } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "SetSleeping")] private static void Player_SetSleeping(Player __instance, bool sleep) { if (sleep) { Cure(__instance); } } [CompilerGenerated] internal static bool g__UpdateTimeLeft|31_0(ref <>c__DisplayClass31_0 P_0) { if (!IsEnabled) { return false; } if (!IsSick((Character?)(object)P_0.player)) { return false; } float num = Mathf.Max(0f, P_0.sickness.GetRemaningTime()); if (num <= 0f) { return false; } Logging.Info((object)$"Persist portal sickness seconds remaining: {num:N}", 0); P_0.player.m_customData["ZenPortalSicknessTimeLeft"] = num.ToString("R"); return true; } } internal class SlowFallUngrounded : SE_Stats { private const string Name = "SE_ZenPortal_SlowFallUngrounded"; private static readonly int SEHash = StringExtensionMethods.GetStableHashCode("SE_ZenPortal_SlowFallUngrounded"); private const float MaxTimeOnGround = 8f; private float _timeOnGround; private static SlowFallUngrounded Prefab => (SlowFallUngrounded)(object)ObjectDB.instance.GetStatusEffect(SEHash); public static void ApplyTo(Player? player) { if (Object.op_Implicit((Object)(object)player)) { ((Character)player).GetSEMan().RemoveStatusEffect(SEHash, true); ((Character)player).GetSEMan().AddStatusEffect((StatusEffect)(object)Prefab, false, 0, 0f); } } public static void Init() { SlowFallUngrounded item = ScriptableObject.CreateInstance(); ObjectDB.instance.m_StatusEffects.Add((StatusEffect)(object)item); } public static void Shutdown() { if (Object.op_Implicit((Object)(object)ObjectDB.instance) && Object.op_Implicit((Object)(object)Prefab)) { SlowFallUngrounded prefab = Prefab; ObjectDB.instance.m_StatusEffects.Remove((StatusEffect)(object)prefab); Object.Destroy((Object)(object)prefab); } } private SlowFallUngrounded() { ((Object)this).name = "SE_ZenPortal_SlowFallUngrounded"; ((StatusEffect)this).m_name = "$se_slowfall_name *"; base.m_maxMaxFallSpeed = 5f; base.m_fallDamageModifier = -1f; ((StatusEffect)this).m_icon = GUIManager.Instance.GetSprite("SlowFall"); } public override void UpdateStatusEffect(float dt) { if (((StatusEffect)this).m_character.IsOnGround()) { _timeOnGround += dt; } if (_timeOnGround > 8f) { ((StatusEffect)this).m_ttl = 1f; ((StatusEffect)this).m_time = 1f; } ((SE_Stats)this).UpdateStatusEffect(dt); } } }