Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of UnstablePortalsMetalTransport v1.2.2
UnstablePortalsMetalTransport.dll
Decompiled 3 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("KernelPanik")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.2.0")] [assembly: AssemblyInformationalVersion("1.2.2")] [assembly: AssemblyProduct("UnstablePortalsMetalTransport")] [assembly: AssemblyTitle("UnstablePortalsMetalTransport")] [assembly: AssemblyVersion("1.2.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace UnstablePortalsMetalTransport { [BepInPlugin("com.kernelpanik.unstableportalsmetaltransport", "Unstable Portals: Metal Transport", "1.2.2")] public sealed class UnstablePortalsMetalTransportPlugin : BaseUnityPlugin { public const string PluginGuid = "com.kernelpanik.unstableportalsmetaltransport"; public const string PluginName = "Unstable Portals: Metal Transport"; public const string PluginVersion = "1.2.2"; private Harmony _harmony; internal static ConfigEntry<int> SurtlingCoreCost { get; private set; } internal static ConfigEntry<int> GreydwarfEyeCost { get; private set; } internal static ConfigEntry<float> PortalDamagePercent { get; private set; } internal static ConfigEntry<bool> DamageSourcePortal { get; private set; } internal static ConfigEntry<bool> DamageDestinationPortal { get; private set; } internal static ConfigEntry<bool> InverseRunePulse { get; private set; } internal static ConfigEntry<float> RuneBrightnessMultiplier { get; private set; } internal static ConfigEntry<bool> EnableUnstableSound { get; private set; } internal static ConfigEntry<float> MinimumPitchMultiplier { get; private set; } internal static ConfigEntry<float> MaximumPitchMultiplier { get; private set; } private void Awake() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Expected O, but got Unknown //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Expected O, but got Unknown SurtlingCoreCost = ((BaseUnityPlugin)this).Config.Bind<int>("Toll", "SurtlingCoreCost", 1, new ConfigDescription("Surtling Cores consumed after a successful restricted-item portal trip.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); GreydwarfEyeCost = ((BaseUnityPlugin)this).Config.Bind<int>("Toll", "GreydwarfEyeCost", 5, new ConfigDescription("Greydwarf Eyes consumed after a successful restricted-item portal trip.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>())); PortalDamagePercent = ((BaseUnityPlugin)this).Config.Bind<float>("Portal Damage", "DamagePercent", 10f, new ConfigDescription("Percentage of each affected portal's maximum health removed per paid trip.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); DamageSourcePortal = ((BaseUnityPlugin)this).Config.Bind<bool>("Portal Damage", "DamageSourcePortal", true, "Damage the portal the player enters after a successful paid trip."); DamageDestinationPortal = ((BaseUnityPlugin)this).Config.Bind<bool>("Portal Damage", "DamageDestinationPortal", true, "Damage the linked destination portal after a successful paid trip."); InverseRunePulse = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "InverseRunePulse", true, "Pulse the portal frame emission/runes in the opposite color phase from the particles and light."); RuneBrightnessMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals", "RuneBrightnessMultiplier", 3f, new ConfigDescription("HDR brightness multiplier for the pulsing frame emission/runes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>())); EnableUnstableSound = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "EnableUnstableSound", true, "Pitch-wobble the portal's existing sound while the restricted-item toll effect is active."); MinimumPitchMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "MinimumPitchMultiplier", 0.82f, new ConfigDescription("Lowest pitch multiplier used by the instability wobble. Values below 1 sound deeper.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1.5f), Array.Empty<object>())); MaximumPitchMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "MaximumPitchMultiplier", 0.96f, new ConfigDescription("Highest pitch multiplier used by the instability wobble. Values above 1 sound higher.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1.5f), Array.Empty<object>())); _harmony = new Harmony("com.kernelpanik.unstableportalsmetaltransport"); try { PortalPatches.Install(_harmony, ((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Unstable Portals: Metal Transport 1.2.2 loaded."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not install portal patches: " + ex)); } } private void Update() { PortalPatches.UpdatePortalVisuals(); PortalPatches.UpdatePendingPortalDamage(); } private void OnDestroy() { PortalPatches.RestorePortalVisuals(); PortalPatches.ClearPendingPortalDamage(); if (_harmony != null) { _harmony.UnpatchSelf(); } } } internal static class PortalPatches { private sealed class PendingTrip { public DateTime ExpiresUtc; public object Inventory; public object SourcePortal; public object DestinationPortalId; public bool EligibilityOverrideUsed; } private sealed class PendingPortalDamage { public DateTime ExpiresUtc; public object DestinationPortalId; public float DamagePercent; } private sealed class InventorySnapshot { public object Inventory; public bool HasEligibleRestrictedItem; public bool HasAbsoluteRestriction; public int CoreCount; public int EyeCount; } private sealed class ParticleVisualState { public object Particle; public PropertyInfo MainProperty; public PropertyInfo StartColorProperty; public ConstructorInfo GradientConstructor; public object OriginalStartColor; } private sealed class AudioVisualState { public object AudioSource; public PropertyInfo PitchProperty; public float OriginalPitch; } private sealed class PortalVisualState { public FieldInfo TargetColorField; public object OriginalTargetColor; public ConstructorInfo ColorConstructor; public object Light; public PropertyInfo LightColorProperty; public object OriginalLightColor; public readonly List<ParticleVisualState> Particles = new List<ParticleVisualState>(); public readonly List<AudioVisualState> AudioSources = new List<AudioVisualState>(); public bool TollEffectActive; } private const string CorePrefabName = "SurtlingCore"; private const string EyePrefabName = "GreydwarfEye"; private static readonly TimeSpan PendingTripLifetime = TimeSpan.FromSeconds(3.0); private static readonly TimeSpan PendingDamageLifetime = TimeSpan.FromSeconds(20.0); private static readonly ConditionalWeakTable<object, PendingTrip> PendingTrips = new ConditionalWeakTable<object, PendingTrip>(); private static readonly ConditionalWeakTable<object, PortalVisualState> PortalVisualStates = new ConditionalWeakTable<object, PortalVisualState>(); private static readonly List<WeakReference> VisualPortals = new List<WeakReference>(); private static readonly List<PendingPortalDamage> PendingDestinationDamage = new List<PendingPortalDamage>(); private static ManualLogSource _log; private static Type _playerType; private static Type _wearNTearType; private static Type _hitDataType; private static Type _zNetSceneType; private static Type _audioSourceType; private static DateTime _nextDamagePollUtc; [ThreadStatic] private static object _portalBeingUpdated; [ThreadStatic] private static bool _portalUpdateUsesToll; private static int ConfiguredCoreCost => Math.Max(0, (UnstablePortalsMetalTransportPlugin.SurtlingCoreCost == null) ? 1 : UnstablePortalsMetalTransportPlugin.SurtlingCoreCost.Value); private static int ConfiguredEyeCost => Math.Max(0, (UnstablePortalsMetalTransportPlugin.GreydwarfEyeCost == null) ? 5 : UnstablePortalsMetalTransportPlugin.GreydwarfEyeCost.Value); private static float ConfiguredDamagePercent { get { float val = ((UnstablePortalsMetalTransportPlugin.PortalDamagePercent == null) ? 10f : UnstablePortalsMetalTransportPlugin.PortalDamagePercent.Value); return Math.Max(0f, Math.Min(100f, val)); } } private static bool ConfiguredDamageSource { get { if (UnstablePortalsMetalTransportPlugin.DamageSourcePortal != null) { return UnstablePortalsMetalTransportPlugin.DamageSourcePortal.Value; } return true; } } private static bool ConfiguredDamageDestination { get { if (UnstablePortalsMetalTransportPlugin.DamageDestinationPortal != null) { return UnstablePortalsMetalTransportPlugin.DamageDestinationPortal.Value; } return true; } } private static bool ConfiguredInverseRunePulse { get { if (UnstablePortalsMetalTransportPlugin.InverseRunePulse != null) { return UnstablePortalsMetalTransportPlugin.InverseRunePulse.Value; } return true; } } private static float ConfiguredRuneBrightnessMultiplier { get { float val = ((UnstablePortalsMetalTransportPlugin.RuneBrightnessMultiplier == null) ? 3f : UnstablePortalsMetalTransportPlugin.RuneBrightnessMultiplier.Value); return Math.Max(0.5f, Math.Min(10f, val)); } } private static bool ConfiguredUnstableSound { get { if (UnstablePortalsMetalTransportPlugin.EnableUnstableSound != null) { return UnstablePortalsMetalTransportPlugin.EnableUnstableSound.Value; } return true; } } private static float ConfiguredMinimumPitchMultiplier { get { float val = ((UnstablePortalsMetalTransportPlugin.MinimumPitchMultiplier == null) ? 0.82f : UnstablePortalsMetalTransportPlugin.MinimumPitchMultiplier.Value); return Math.Max(0.5f, Math.Min(1.5f, val)); } } private static float ConfiguredMaximumPitchMultiplier { get { float val = ((UnstablePortalsMetalTransportPlugin.MaximumPitchMultiplier == null) ? 0.96f : UnstablePortalsMetalTransportPlugin.MaximumPitchMultiplier.Value); return Math.Max(0.5f, Math.Min(1.5f, val)); } } internal static void Install(Harmony harmony, ManualLogSource log) { _log = log; Type type = RequireType("TeleportWorld"); Type type2 = RequireType("Humanoid"); _playerType = RequireType("Player"); _wearNTearType = RequireType("WearNTear"); _hitDataType = RequireType("HitData"); _zNetSceneType = RequireType("ZNetScene"); _audioSourceType = AccessTools.TypeByName("UnityEngine.AudioSource"); MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo methodInfo3) => methodInfo3.Name == "Teleport" && methodInfo3.GetParameters().Any((ParameterInfo parameter) => parameter.ParameterType.IsAssignableFrom(_playerType) || _playerType.IsAssignableFrom(parameter.ParameterType))); if (methodInfo == null) { throw new MissingMethodException("TeleportWorld.Teleport(Player)"); } MethodInfo method = type2.GetMethod("IsTeleportable", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(bool) }, null); if (method == null || method.ReturnType != typeof(bool)) { throw new MissingMethodException("Humanoid.IsTeleportable(bool)"); } MethodInfo methodInfo2 = _playerType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo methodInfo3) => methodInfo3.Name == "TeleportTo" && methodInfo3.DeclaringType == _playerType && methodInfo3.ReturnType == typeof(bool)); if (methodInfo2 == null) { throw new MissingMethodException("Player.TeleportTo(...)"); } MethodInfo method2 = type.GetMethod("UpdatePortal", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method2 == null) { throw new MissingMethodException("TeleportWorld.UpdatePortal()"); } HarmonyMethod val = PatchMethod("PortalTeleportPrefix"); HarmonyMethod val2 = PatchMethod("PortalTeleportPostfix"); HarmonyMethod val3 = PatchMethod("HumanoidIsTeleportablePostfix"); HarmonyMethod val4 = PatchMethod("PlayerTeleportToPostfix"); HarmonyMethod val5 = PatchMethod("PortalUpdatePrefix"); HarmonyMethod val6 = PatchMethod("PortalUpdatePostfix"); harmony.Patch((MethodBase)methodInfo, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method, (HarmonyMethod)null, val3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val4, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method2, val5, val6, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _log.LogInfo((object)("Patched " + methodInfo.DeclaringType.Name + "." + methodInfo.Name + ", " + method.DeclaringType.Name + "." + method.Name + ", and " + methodInfo2.DeclaringType.Name + "." + methodInfo2.Name + "; toll eligibility is tracked through " + method2.DeclaringType.Name + "." + method2.Name + " and pulsing visuals use the plugin frame loop.")); } private static HarmonyMethod PatchMethod(string name) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return new HarmonyMethod(typeof(PortalPatches).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic)); } private static Type RequireType(string name) { Type type = AccessTools.TypeByName(name); if (type == null) { throw new TypeLoadException("Could not find Valheim type " + name + "."); } return type; } private static void PortalTeleportPrefix(object __instance, object[] __args) { object obj = FindPlayer(__args); if (obj == null || !IsLocalPlayer(obj)) { return; } RemovePendingTrip(obj); if (ReadBoolField(__instance, "m_allowAllItems")) { return; } InventorySnapshot inventorySnapshot = ReadInventory(obj); if (inventorySnapshot != null && inventorySnapshot.HasEligibleRestrictedItem && !inventorySnapshot.HasAbsoluteRestriction) { if (!HasRequiredToll(inventorySnapshot)) { ShowCenterMessage(obj, "You need " + DescribeToll() + " to use a portal while carrying metal."); } else { SetPendingTrip(obj, inventorySnapshot.Inventory, __instance, GetConnectedPortalId(__instance)); } } } private static void PortalTeleportPostfix(object[] __args) { object obj = FindPlayer(__args); if (obj != null && HasPendingTrip(obj)) { RemovePendingTrip(obj); } } private static void PortalUpdatePrefix(object __instance) { _portalBeingUpdated = __instance; _portalUpdateUsesToll = false; } private static void PortalUpdatePostfix(object __instance) { try { bool active = _portalBeingUpdated == __instance && _portalUpdateUsesToll; SetPortalTollEffect(__instance, active); } catch (Exception ex) { _log.LogDebug((object)("Could not update the unstable portal effect: " + ex.Message)); } finally { _portalBeingUpdated = null; _portalUpdateUsesToll = false; } } internal static void UpdatePortalVisuals() { if (VisualPortals.Count == 0) { return; } float pulseBlend = GetPulseBlend(DateTime.UtcNow); for (int num = VisualPortals.Count - 1; num >= 0; num--) { object target = VisualPortals[num].Target; PortalVisualState value; if (target == null) { VisualPortals.RemoveAt(num); } else if (PortalVisualStates.TryGetValue(target, out value) && value.TollEffectActive) { try { ApplyPortalPulseFrame(target, value, pulseBlend); } catch (Exception ex) { _log.LogDebug((object)("Stopped animating a portal visual: " + ex.Message)); VisualPortals.RemoveAt(num); } } } } private static void HumanoidIsTeleportablePostfix(object __instance, ref bool __result) { if (__result || __instance == null || !_playerType.IsInstanceOfType(__instance) || !IsLocalPlayer(__instance)) { return; } InventorySnapshot inventorySnapshot = ReadInventory(__instance); if (inventorySnapshot != null && inventorySnapshot.HasEligibleRestrictedItem && !inventorySnapshot.HasAbsoluteRestriction && HasRequiredToll(inventorySnapshot)) { __result = true; if (_portalBeingUpdated != null) { _portalUpdateUsesToll = true; } PendingTrip pendingTrip = GetPendingTrip(__instance); if (pendingTrip != null) { pendingTrip.EligibilityOverrideUsed = true; } } } private static void PlayerTeleportToPostfix(object __instance, ref bool __result) { if (!__result || __instance == null) { return; } PendingTrip pendingTrip = GetPendingTrip(__instance); if (pendingTrip == null || !pendingTrip.EligibilityOverrideUsed) { return; } if (!ConsumeToll(pendingTrip.Inventory)) { _log.LogWarning((object)"Portal travel succeeded, but the configured toll could not be removed."); RemovePendingTrip(__instance); return; } float configuredDamagePercent = ConfiguredDamagePercent; if (configuredDamagePercent > 0f && ConfiguredDamageSource) { ApplyPortalDamage(pendingTrip.SourcePortal, configuredDamagePercent); } if (configuredDamagePercent > 0f && ConfiguredDamageDestination && pendingTrip.DestinationPortalId != null) { QueueDestinationDamage(pendingTrip.DestinationPortalId, configuredDamagePercent); } RemovePendingTrip(__instance); string text = DescribeToll(); if (ConfiguredCoreCost > 0 || ConfiguredEyeCost > 0) { ShowCenterMessage(__instance, "The portal consumes " + text + "."); } _log.LogInfo((object)("Charged " + text + " for restricted-item portal travel.")); } private static object FindPlayer(object[] arguments) { if (arguments == null || _playerType == null) { return null; } return arguments.FirstOrDefault((object argument) => argument != null && _playerType.IsInstanceOfType(argument)); } private static bool IsLocalPlayer(object player) { FieldInfo field = _playerType.GetField("m_localPlayer", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((field == null) ? null : field.GetValue(null)); if (obj != null) { return obj == player; } return true; } private static void SetPortalTollEffect(object portal, bool active) { if (portal == null) { return; } if (!PortalVisualStates.TryGetValue(portal, out var value)) { value = CapturePortalVisualState(portal); PortalVisualStates.Add(portal, value); VisualPortals.Add(new WeakReference(portal)); } if (value.TollEffectActive == active || (active && value.ColorConstructor == null)) { return; } value.TollEffectActive = active; if (active) { ApplyPortalPulseFrame(portal, value, GetPulseBlend(DateTime.UtcNow)); return; } if (value.TargetColorField != null && value.OriginalTargetColor != null) { value.TargetColorField.SetValue(portal, value.OriginalTargetColor); } foreach (ParticleVisualState particle in value.Particles) { object value2 = particle.MainProperty.GetValue(particle.Particle, null); particle.StartColorProperty.SetValue(value2, particle.OriginalStartColor, null); } if (value.Light != null && value.LightColorProperty != null) { value.LightColorProperty.SetValue(value.Light, value.OriginalLightColor, null); } RestorePortalAudio(value); } private static void ApplyPortalPulseFrame(object portal, PortalVisualState state, float purpleBlend) { if (state.ColorConstructor == null) { return; } object obj = CreateBlendedPulseColor(state, purpleBlend); float purpleBlend2 = (ConfiguredInverseRunePulse ? (1f - purpleBlend) : purpleBlend); object obj2 = CreateBlendedPulseColor(state, purpleBlend2, ConfiguredRuneBrightnessMultiplier); if (obj == null || obj2 == null) { return; } if (state.TargetColorField != null) { state.TargetColorField.SetValue(portal, obj2); } foreach (ParticleVisualState particle in state.Particles) { object value = particle.MainProperty.GetValue(particle.Particle, null); object value2 = particle.GradientConstructor.Invoke(new object[1] { obj }); particle.StartColorProperty.SetValue(value, value2, null); } if (state.Light != null && state.LightColorProperty != null) { state.LightColorProperty.SetValue(state.Light, obj, null); } ApplyPortalAudioPitch(state, purpleBlend); } private static void ApplyPortalAudioPitch(PortalVisualState state, float purpleBlend) { float num = Math.Min(ConfiguredMinimumPitchMultiplier, ConfiguredMaximumPitchMultiplier); float num2 = Math.Max(ConfiguredMinimumPitchMultiplier, ConfiguredMaximumPitchMultiplier); float num3 = (ConfiguredUnstableSound ? (num + (num2 - num) * purpleBlend) : 1f); foreach (AudioVisualState audioSource in state.AudioSources) { try { audioSource.PitchProperty.SetValue(audioSource.AudioSource, audioSource.OriginalPitch * num3, null); } catch { } } } private static void RestorePortalAudio(PortalVisualState state) { foreach (AudioVisualState audioSource in state.AudioSources) { try { audioSource.PitchProperty.SetValue(audioSource.AudioSource, audioSource.OriginalPitch, null); } catch { } } } private static float GetPulseBlend(DateTime utcNow) { double num = (double)utcNow.Ticks / 10000000.0 % 2.6 / 2.6; return (float)(0.5 - 0.5 * Math.Cos(num * Math.PI * 2.0)); } private static object CreateBlendedPulseColor(PortalVisualState state, float purpleBlend, float brightnessMultiplier = 1f) { float num = 1f - purpleBlend; return state.ColorConstructor.Invoke(new object[4] { (1f * num + 0.62f * purpleBlend) * brightnessMultiplier, (0.12f * num + 0.06f * purpleBlend) * brightnessMultiplier, (0.015f * num + 1.05f * purpleBlend) * brightnessMultiplier, 1f }); } private static PortalVisualState CapturePortalVisualState(object portal) { PortalVisualState portalVisualState = new PortalVisualState(); portalVisualState.TargetColorField = FindField(portal.GetType(), "m_colorTargetfound"); if (portalVisualState.TargetColorField == null) { return portalVisualState; } portalVisualState.OriginalTargetColor = portalVisualState.TargetColorField.GetValue(portal); portalVisualState.ColorConstructor = portalVisualState.TargetColorField.FieldType.GetConstructor(new Type[4] { typeof(float), typeof(float), typeof(float), typeof(float) }); if (portalVisualState.ColorConstructor == null) { return portalVisualState; } object obj = ReadField(portal, "m_target_found"); IEnumerable enumerable = ((obj == null) ? null : (ReadField(obj, "m_particles") as IEnumerable)); if (enumerable != null) { foreach (object item in enumerable) { if (item == null) { continue; } PropertyInfo property = item.GetType().GetProperty("main", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj2 = ((property == null) ? null : property.GetValue(item, null)); PropertyInfo propertyInfo = obj2?.GetType().GetProperty("startColor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(propertyInfo == null) && propertyInfo.CanRead && propertyInfo.CanWrite) { ConstructorInfo constructor = propertyInfo.PropertyType.GetConstructor(new Type[1] { portalVisualState.TargetColorField.FieldType }); if (!(constructor == null)) { portalVisualState.Particles.Add(new ParticleVisualState { Particle = item, MainProperty = property, StartColorProperty = propertyInfo, GradientConstructor = constructor, OriginalStartColor = propertyInfo.GetValue(obj2, null) }); } } } } portalVisualState.Light = ((obj == null) ? null : ReadField(obj, "m_light")); if (portalVisualState.Light != null) { portalVisualState.LightColorProperty = portalVisualState.Light.GetType().GetProperty("color", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (portalVisualState.LightColorProperty != null && portalVisualState.LightColorProperty.CanRead && portalVisualState.LightColorProperty.CanWrite) { portalVisualState.OriginalLightColor = portalVisualState.LightColorProperty.GetValue(portalVisualState.Light, null); } else { portalVisualState.LightColorProperty = null; } } CapturePortalAudioSources(portal, portalVisualState); return portalVisualState; } private static void CapturePortalAudioSources(object portal, PortalVisualState state) { if (_audioSourceType == null) { return; } MethodInfo methodInfo = portal.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method) { ParameterInfo[] parameters2 = method.GetParameters(); return method.Name == "GetComponentsInChildren" && !method.IsGenericMethod && parameters2.Length == 2 && parameters2[0].ParameterType == typeof(Type) && parameters2[1].ParameterType == typeof(bool); }); object[] parameters = new object[2] { _audioSourceType, true }; if (methodInfo == null) { methodInfo = portal.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method) { ParameterInfo[] parameters2 = method.GetParameters(); return method.Name == "GetComponentsInChildren" && !method.IsGenericMethod && parameters2.Length == 1 && parameters2[0].ParameterType == typeof(Type); }); parameters = new object[1] { _audioSourceType }; } if (methodInfo == null || !(methodInfo.Invoke(portal, parameters) is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item != null) { PropertyInfo property = item.GetType().GetProperty("pitch", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(property == null) && property.CanRead && property.CanWrite) { state.AudioSources.Add(new AudioVisualState { AudioSource = item, PitchProperty = property, OriginalPitch = Convert.ToSingle(property.GetValue(item, null)) }); } } } } internal static void RestorePortalVisuals() { foreach (WeakReference visualPortal in VisualPortals) { object target = visualPortal.Target; if (target != null) { try { SetPortalTollEffect(target, active: false); } catch { } } } VisualPortals.Clear(); } private static bool HasRequiredToll(InventorySnapshot snapshot) { if (snapshot.CoreCount >= ConfiguredCoreCost) { return snapshot.EyeCount >= ConfiguredEyeCost; } return false; } private static string DescribeToll() { List<string> list = new List<string>(); if (ConfiguredCoreCost > 0) { list.Add(ConfiguredCoreCost + " Surtling " + ((ConfiguredCoreCost == 1) ? "Core" : "Cores")); } if (ConfiguredEyeCost > 0) { list.Add(ConfiguredEyeCost + " Greydwarf " + ((ConfiguredEyeCost == 1) ? "Eye" : "Eyes")); } if (list.Count != 0) { return string.Join(" and ", list.ToArray()); } return "no items"; } private static object GetConnectedPortalId(object portal) { try { object obj = ReadField(portal, "m_nview"); if (obj == null) { return null; } MethodInfo method = obj.GetType().GetMethod("GetZDO", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); object obj2 = ((method == null) ? null : method.Invoke(obj, null)); if (obj2 == null) { return null; } MethodInfo methodInfo = obj2.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo methodInfo2) { ParameterInfo[] parameters = methodInfo2.GetParameters(); return methodInfo2.Name == "GetConnectionZDOID" && parameters.Length == 1 && parameters[0].ParameterType.IsEnum; }); if (methodInfo == null) { return null; } object obj3 = Enum.Parse(methodInfo.GetParameters()[0].ParameterType, "Portal", ignoreCase: true); object obj4 = methodInfo.Invoke(obj2, new object[1] { obj3 }); return IsNoneZdoId(obj4) ? null : obj4; } catch (Exception ex) { _log.LogDebug((object)("Could not resolve the destination portal: " + ex.Message)); return null; } } private static bool IsNoneZdoId(object zdoId) { if (zdoId == null) { return true; } MethodInfo method = zdoId.GetType().GetMethod("IsNone", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null) { return Convert.ToBoolean(method.Invoke(zdoId, null)); } return false; } private static void QueueDestinationDamage(object destinationPortalId, float damagePercent) { PendingDestinationDamage.Add(new PendingPortalDamage { ExpiresUtc = DateTime.UtcNow + PendingDamageLifetime, DestinationPortalId = destinationPortalId, DamagePercent = damagePercent }); _nextDamagePollUtc = DateTime.MinValue; } internal static void UpdatePendingPortalDamage() { if (PendingDestinationDamage.Count == 0 || DateTime.UtcNow < _nextDamagePollUtc) { return; } _nextDamagePollUtc = DateTime.UtcNow + TimeSpan.FromMilliseconds(250.0); for (int num = PendingDestinationDamage.Count - 1; num >= 0; num--) { PendingPortalDamage pendingPortalDamage = PendingDestinationDamage[num]; if (pendingPortalDamage.ExpiresUtc < DateTime.UtcNow) { _log.LogWarning((object)"Destination portal did not load in time; its toll damage was not applied."); PendingDestinationDamage.RemoveAt(num); } else { object obj = FindLoadedZNetObject(pendingPortalDamage.DestinationPortalId); if (obj != null && ApplyPortalDamage(obj, pendingPortalDamage.DamagePercent)) { PendingDestinationDamage.RemoveAt(num); } } } } internal static void ClearPendingPortalDamage() { PendingDestinationDamage.Clear(); _nextDamagePollUtc = DateTime.MinValue; } private static object FindLoadedZNetObject(object zdoId) { try { if (_zNetSceneType == null || zdoId == null) { return null; } FieldInfo field = _zNetSceneType.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); PropertyInfo property = _zNetSceneType.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((field != null) ? field.GetValue(null) : ((property == null) ? null : property.GetValue(null, null))); if (obj == null) { return null; } MethodInfo methodInfo = _zNetSceneType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); return method.Name == "FindInstance" && parameters.Length == 1 && parameters[0].ParameterType.IsInstanceOfType(zdoId); }); return (methodInfo == null) ? null : methodInfo.Invoke(obj, new object[1] { zdoId }); } catch (Exception ex) { _log.LogDebug((object)("Could not look up the destination portal: " + ex.Message)); return null; } } private static bool ApplyPortalDamage(object portalOrGameObject, float damagePercent) { try { object obj = FindComponent(portalOrGameObject, _wearNTearType); if (obj == null || _hitDataType == null) { return false; } object obj2 = ReadField(obj, "m_health"); float num = ((obj2 == null) ? 0f : Convert.ToSingle(obj2)) * damagePercent / 100f; if (num <= 0f) { return true; } ConstructorInfo constructor = _hitDataType.GetConstructor(new Type[1] { typeof(float) }); object obj3 = ((constructor == null) ? Activator.CreateInstance(_hitDataType) : constructor.Invoke(new object[1] { num })); if (constructor == null) { object obj4 = ReadField(obj3, "m_damage"); FieldInfo fieldInfo = ((obj4 == null) ? null : FindField(obj4.GetType(), "m_damage")); if (fieldInfo == null) { return false; } fieldInfo.SetValue(obj4, num); FindField(obj3.GetType(), "m_damage").SetValue(obj3, obj4); } SetHitPoint(obj3, obj); SetEnumField(obj3, "m_hitType", "Structural"); MethodInfo method = obj.GetType().GetMethod("Damage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { _hitDataType }, null); if (method == null) { return false; } method.Invoke(obj, new object[1] { obj3 }); return true; } catch (Exception ex) { _log.LogWarning((object)("Could not damage a toll portal: " + ex.Message)); return false; } } private static object FindComponent(object instance, Type componentType) { if (instance == null || componentType == null) { return null; } if (componentType.IsInstanceOfType(instance)) { return instance; } MethodInfo method = instance.GetType().GetMethod("GetComponent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Type) }, null); object obj = ((method == null) ? null : method.Invoke(instance, new object[1] { componentType })); if (obj != null) { return obj; } PropertyInfo property = instance.GetType().GetProperty("gameObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj2 = ((property == null) ? null : property.GetValue(instance, null)); if (obj2 == null || obj2 == instance) { return null; } return FindComponent(obj2, componentType); } private static void SetHitPoint(object hit, object component) { FieldInfo fieldInfo = FindField(hit.GetType(), "m_point"); PropertyInfo property = component.GetType().GetProperty("transform", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((property == null) ? null : property.GetValue(component, null)); PropertyInfo propertyInfo = obj?.GetType().GetProperty("position", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj2 = ((propertyInfo == null) ? null : propertyInfo.GetValue(obj, null)); if (fieldInfo != null && obj2 != null && fieldInfo.FieldType.IsInstanceOfType(obj2)) { fieldInfo.SetValue(hit, obj2); } } private static void SetEnumField(object instance, string fieldName, string value) { FieldInfo fieldInfo = FindField(instance.GetType(), fieldName); if (fieldInfo != null && fieldInfo.FieldType.IsEnum) { fieldInfo.SetValue(instance, Enum.Parse(fieldInfo.FieldType, value, ignoreCase: true)); } } private static InventorySnapshot ReadInventory(object humanoid) { try { MethodInfo method = humanoid.GetType().GetMethod("GetInventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((method == null) ? null : method.Invoke(humanoid, null)); if (obj == null) { return null; } IEnumerable allItems = GetAllItems(obj); if (allItems == null) { return null; } InventorySnapshot inventorySnapshot = new InventorySnapshot { Inventory = obj }; foreach (object item in allItems) { if (item == null) { continue; } int num = ReadIntField(item, "m_stack", 1); if (num <= 0) { continue; } if (IsSurtlingCore(item)) { inventorySnapshot.CoreCount += num; } if (IsGreydwarfEye(item)) { inventorySnapshot.EyeCount += num; } object obj2 = ReadField(item, "m_shared"); if (obj2 != null) { if (ReadIntField(obj2, "m_toolTier", 0) >= 1000) { inventorySnapshot.HasAbsoluteRestriction = true; } else if (!ReadBoolField(obj2, "m_teleportable")) { inventorySnapshot.HasEligibleRestrictedItem = true; } } } return inventorySnapshot; } catch (Exception ex) { _log.LogWarning((object)("Could not inspect player inventory: " + ex.Message)); return null; } } private static IEnumerable GetAllItems(object inventory) { MethodInfo method = inventory.GetType().GetMethod("GetAllItems", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (!(method == null)) { return method.Invoke(inventory, null) as IEnumerable; } return null; } private static bool ConsumeToll(object inventory) { try { IEnumerable allItems = GetAllItems(inventory); if (allItems == null) { return false; } List<object> itemList = (from object item in allItems where item != null select item).ToList(); int configuredCoreCost = ConfiguredCoreCost; int configuredEyeCost = ConfiguredEyeCost; if (CountItems(itemList, IsSurtlingCore) < configuredCoreCost || CountItems(itemList, IsGreydwarfEye) < configuredEyeCost) { return false; } MethodInfo methodInfo = inventory.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo methodInfo2) { if (methodInfo2.Name != "RemoveItem") { return false; } ParameterInfo[] parameters = methodInfo2.GetParameters(); return parameters.Length == 1 && itemList.Any((object item) => parameters[0].ParameterType.IsInstanceOfType(item)); }); if ((configuredCoreCost > 0 || configuredEyeCost > 0) && methodInfo == null) { return false; } if (!ConsumeItemAmount(inventory, itemList, IsSurtlingCore, configuredCoreCost, methodInfo) || !ConsumeItemAmount(inventory, itemList, IsGreydwarfEye, configuredEyeCost, methodInfo)) { return false; } MethodInfo method = inventory.GetType().GetMethod("Changed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null) { method.Invoke(inventory, null); } return true; } catch (Exception ex) { _log.LogWarning((object)("Could not consume the configured portal toll: " + ex.Message)); return false; } } private static int CountItems(IEnumerable<object> items, Func<object, bool> predicate) { return items.Where(predicate).Sum((object item) => Math.Max(0, ReadIntField(item, "m_stack", 1))); } private static bool ConsumeItemAmount(object inventory, IEnumerable<object> items, Func<object, bool> predicate, int amount, MethodInfo removeItem) { int num = amount; foreach (object item in items.Where(predicate).ToList()) { if (num <= 0) { break; } FieldInfo fieldInfo = FindField(item.GetType(), "m_stack"); if (fieldInfo == null) { return false; } int num2 = Math.Max(0, Convert.ToInt32(fieldInfo.GetValue(item))); if (num2 <= num) { if (removeItem == null || !removeItem.GetParameters()[0].ParameterType.IsInstanceOfType(item)) { return false; } removeItem.Invoke(inventory, new object[1] { item }); num -= num2; } else { fieldInfo.SetValue(item, num2 - num); num = 0; } } return num == 0; } private static bool IsSurtlingCore(object item) { if (string.Equals(ReadName(ReadField(item, "m_dropPrefab")), "SurtlingCore", StringComparison.OrdinalIgnoreCase)) { return true; } object obj = ReadField(item, "m_shared"); string a = ((obj == null) ? null : ReadField(obj, "m_name")) as string; if (!string.Equals(a, "$item_surtlingcore", StringComparison.OrdinalIgnoreCase)) { return string.Equals(a, "Surtling core", StringComparison.OrdinalIgnoreCase); } return true; } private static bool IsGreydwarfEye(object item) { if (string.Equals(ReadName(ReadField(item, "m_dropPrefab")), "GreydwarfEye", StringComparison.OrdinalIgnoreCase)) { return true; } object obj = ReadField(item, "m_shared"); string a = ((obj == null) ? null : ReadField(obj, "m_name")) as string; if (!string.Equals(a, "$item_greydwarfeye", StringComparison.OrdinalIgnoreCase) && !string.Equals(a, "$item_greydwarf_eye", StringComparison.OrdinalIgnoreCase)) { return string.Equals(a, "Greydwarf eye", StringComparison.OrdinalIgnoreCase); } return true; } private static string ReadName(object value) { if (value == null) { return null; } PropertyInfo property = value.GetType().GetProperty("name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(property == null)) { return property.GetValue(value, null) as string; } return null; } private static object ReadField(object instance, string fieldName) { if (instance == null) { return null; } FieldInfo fieldInfo = FindField(instance.GetType(), fieldName); if (!(fieldInfo == null)) { return fieldInfo.GetValue(instance); } return null; } private static bool ReadBoolField(object instance, string fieldName) { object obj = ReadField(instance, fieldName); if (obj is bool) { return (bool)obj; } return false; } private static int ReadIntField(object instance, string fieldName, int fallback) { object obj = ReadField(instance, fieldName); if (obj != null) { return Convert.ToInt32(obj); } return fallback; } private static FieldInfo FindField(Type type, string fieldName) { while (type != null) { FieldInfo field = type.GetField(fieldName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field; } type = type.BaseType; } return null; } private static void ShowCenterMessage(object player, string text) { try { MethodInfo methodInfo = (from method in player.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where method.Name == "Message" select method).FirstOrDefault(delegate(MethodInfo method) { ParameterInfo[] parameters2 = method.GetParameters(); return parameters2.Length >= 2 && parameters2[0].ParameterType.IsEnum && parameters2[1].ParameterType == typeof(string); }); if (!(methodInfo == null)) { ParameterInfo[] parameters = methodInfo.GetParameters(); object[] array = new object[parameters.Length]; array[0] = Enum.Parse(parameters[0].ParameterType, "Center", ignoreCase: true); array[1] = text; for (int num = 2; num < parameters.Length; num++) { array[num] = (parameters[num].HasDefaultValue ? parameters[num].DefaultValue : (parameters[num].ParameterType.IsValueType ? Activator.CreateInstance(parameters[num].ParameterType) : null)); } methodInfo.Invoke(player, array); } } catch (Exception ex) { _log.LogDebug((object)("Could not show portal message: " + ex.Message)); } } private static void SetPendingTrip(object player, object inventory, object sourcePortal, object destinationPortalId) { RemovePendingTrip(player); PendingTrips.Add(player, new PendingTrip { ExpiresUtc = DateTime.UtcNow + PendingTripLifetime, Inventory = inventory, SourcePortal = sourcePortal, DestinationPortalId = destinationPortalId }); } private static PendingTrip GetPendingTrip(object player) { if (!PendingTrips.TryGetValue(player, out var value)) { return null; } if (value.ExpiresUtc >= DateTime.UtcNow) { return value; } RemovePendingTrip(player); return null; } private static bool HasPendingTrip(object player) { return GetPendingTrip(player) != null; } private static void RemovePendingTrip(object player) { if (player != null) { PendingTrips.Remove(player); } } } }