Decompiled source of AvatarStatExtender v1.0.0
Mods/AvatarStatExtender.dll
Decompiled 10 months ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using System.Threading; using AvatarStatExtender; using AvatarStatExtender.API; using AvatarStatExtender.BuiltInEvents; using AvatarStatExtender.Components; using AvatarStatExtender.Data; using AvatarStatExtender.Tools; using AvatarStatExtender.Tools.Assets; using BoneLib; using FieldInjector; using HarmonyLib; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using SLZ.Marrow.Data; using SLZ.Marrow.Warehouse; using SLZ.Rig; using SLZ.VRMK; using UnhollowerBaseLib; using UnhollowerBaseLib.Attributes; using UnityEngine; using UnityEngine.Audio; using UnityEngine.SceneManagement; using XansTools.AvatarInteroperability; using XansTools.Data; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("AvatarStatExtender")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("AvatarStatExtender")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("74cf3aaf-5536-4556-9992-906c2cdb571b")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: MelonInfo(typeof(AvatarStatExtensionMod), "Extended Avatar Driver", "1.0.0", "Xan", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.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; } } } public class AvatarExtendedAudioContainer : MonoBehaviour { public string[] names = Array.Empty<string>(); public SerializableAudioArray2D sounds = new SerializableAudioArray2D(); public AudioClip[] allOfTheSounds = Array.Empty<AudioClip>(); public int[] lengthsOfEachSegment = Array.Empty<int>(); public AudioEventType[] eventTypes = Array.Empty<AudioEventType>(); public AudioPlayType[] playTypes = Array.Empty<AudioPlayType>(); public string?[] customEventTypes = Array.Empty<string>(); public string?[] customPlayTypes = Array.Empty<string>(); public Vector2[] pitchRanges = Array.Empty<Vector2>(); public float[] volumes = Array.Empty<float>(); public AudioMixerTarget[] mixers = Array.Empty<AudioMixerTarget>(); public SoundFlags[] soundFlags = Array.Empty<SoundFlags>(); public AudioSource?[] templateSources = Array.Empty<AudioSource>(); private IReadOnlyList<ReadOnlyAudioEntry>? _ROEntriesCache; internal List<AudioEntry> Entries { [HideFromIl2Cpp] get; [HideFromIl2Cpp] private set; } = new List<AudioEntry>(); public AvatarExtendedAudioContainer(IntPtr @this) : base(@this) { } public void EnsureSymmetry() { //IL_0126: Unknown result type (might be due to invalid IL or missing references) int val = names.Length; val = Math.Max(val, names.Length); val = Math.Max(val, sounds.Children.Length); val = Math.Max(val, eventTypes.Length); val = Math.Max(val, playTypes.Length); val = Math.Max(val, customEventTypes.Length); val = Math.Max(val, customPlayTypes.Length); val = Math.Max(val, pitchRanges.Length); val = Math.Max(val, volumes.Length); val = Math.Max(val, mixers.Length); val = Math.Max(val, soundFlags.Length); val = Math.Max(val, templateSources.Length); Tools.ResizeArrayDefault(ref names, val, "New Sound Group"); Tools.ResizeArrayDefault(ref sounds.children, val, new SerializableAudioArray()); Tools.ResizeArrayDefault(ref eventTypes, val, AudioEventType.HitBullet); Tools.ResizeArrayDefault(ref playTypes, val, AudioPlayType.Random); Tools.ResizeArrayDefault(ref customEventTypes, val, null); Tools.ResizeArrayDefault(ref customPlayTypes, val, null); Tools.ResizeArrayDefault(ref pitchRanges, val, new Vector2(1f, 1f)); Tools.ResizeArrayDefault(ref volumes, val, 0.5f); Tools.ResizeArrayDefault(ref mixers, val, AudioMixerTarget.SFX); Tools.ResizeArrayDefault(ref soundFlags, val, SoundFlags.RealtimePitchShift); Tools.ResizeArrayDefault(ref templateSources, val, null); } public void Sounds2DToAbyssmalMappedArray() { List<AudioClip> list = new List<AudioClip>(128); List<int> list2 = new List<int>(128); SerializableAudioArray[] children = sounds.Children; foreach (SerializableAudioArray serializableAudioArray in children) { list.AddRange(serializableAudioArray.clips); list2.Add(serializableAudioArray.clips.Count); } allOfTheSounds = list.ToArray(); lengthsOfEachSegment = list2.ToArray(); } public void AbyssmalMappedArrayToSounds2D() { sounds.Children = new SerializableAudioArray[lengthsOfEachSegment.Length]; int num = 0; for (int i = 0; i < lengthsOfEachSegment.Length; i++) { int num2 = lengthsOfEachSegment[i]; sounds.Children[i] = new SerializableAudioArray(); for (int j = 0; j < num2; j++) { sounds.Children[i].clips.Add(allOfTheSounds[num + j]); } num += num2; } } [HideFromIl2Cpp] internal IReadOnlyList<ReadOnlyAudioEntry> GetEntries() { if (_ROEntriesCache == null) { AbyssmalMappedArrayToSounds2D(); UpdateEntries(); _ROEntriesCache = new ReadOnlyCollection<ReadOnlyAudioEntry>(Entries.Select((AudioEntry entry) => new ReadOnlyAudioEntry(entry)).ToList()); } return _ROEntriesCache; } public void UpdateEntries() { Entries = this.EntriesFromContainer().ToList(); } } public class AvatarStatDriver : MonoBehaviour { public bool useCustomStats; public bool useCustomMass; public bool useProportionalMassEditing; public bool useCustomAgility; public bool useCustomSpeed; public bool useCustomUpper; public bool useCustomLower; public bool useCustomVitality; public bool useCustomIntelligence; public float customAgility; public float customSpeed; public float customUpper; public float customLower; public float customVitality; public float customIntelligence; public bool useCustomChestMass; public bool useCustomPelvisMass; public bool useCustomArmMass; public bool useCustomLegMass; public bool useCustomHeadMass; public float customTotalMass; public float customChestMass; public float customPelvisMass; public float customArmMass; public float customLegMass; public float customHeadMass; public float extraJumpVerticalVelocity; [AllowNull] private Avatar _avatar; public float VanillaAgility { get { _avatar.ComputeAllStats(out var _, out var stats); return stats.agility; } } public float VanillaSpeed { get { _avatar.ComputeAllStats(out var _, out var stats); return stats.speed; } } public float VanillaUpperStrength { get { _avatar.ComputeAllStats(out var _, out var stats); return stats.strengthUpper; } } public float VanillaLowerStrength { get { _avatar.ComputeAllStats(out var _, out var stats); return stats.strengthLower; } } public float VanillaVitality { get { _avatar.ComputeAllStats(out var _, out var stats); return stats.vitality; } } public float VanillaIntelligence { get { _avatar.ComputeAllStats(out var _, out var stats); return stats.intelligence; } } public float VanillaTotalMass { get { AvatarStatCalculationExtension.AvatarComputationProperties additionalInfo; return _avatar.ComputeMass(out additionalInfo).massTotal; } } public float VanillaChestMass { get { AvatarStatCalculationExtension.AvatarComputationProperties additionalInfo; return _avatar.ComputeMass(out additionalInfo).massChest; } } public float VanillaPelvisMass { get { AvatarStatCalculationExtension.AvatarComputationProperties additionalInfo; return _avatar.ComputeMass(out additionalInfo).massPelvis; } } public float VanillaArmMass { get { AvatarStatCalculationExtension.AvatarComputationProperties additionalInfo; return _avatar.ComputeMass(out additionalInfo).massArm; } } public float VanillaLegMass { get { AvatarStatCalculationExtension.AvatarComputationProperties additionalInfo; return _avatar.ComputeMass(out additionalInfo).massLeg; } } public float VanillaHeadMass { get { AvatarStatCalculationExtension.AvatarComputationProperties additionalInfo; return _avatar.ComputeMass(out additionalInfo).massHead; } } public float EffectiveAgility { get { if (useCustomStats && useCustomAgility) { return customAgility; } return VanillaAgility; } } public float EffectiveSpeed { get { if (useCustomStats && useCustomSpeed) { return customSpeed; } return VanillaSpeed; } } public float EffectiveUpperStrength { get { if (useCustomStats && useCustomUpper) { return customUpper; } return VanillaUpperStrength; } } public float EffectiveLowerStrength { get { if (useCustomStats && useCustomLower) { return customLower; } return VanillaLowerStrength; } } public float EffectiveVitality { get { if (useCustomStats && useCustomVitality) { return customVitality; } return VanillaVitality; } } public float EffectiveIntelligence { get { if (useCustomStats && useCustomIntelligence) { return customIntelligence; } return VanillaIntelligence; } } public float MassProportion { get { if (useCustomMass && useProportionalMassEditing) { AvatarStatCalculationExtension.AvatarComputationProperties additionalInfo; AvatarStatCalculationExtension.AvatarMasses avatarMasses = _avatar.ComputeMass(out additionalInfo); return customTotalMass / avatarMasses.massTotal; } return 1f; } } public float EffectiveTotalMass { get { if (useCustomMass) { if (useProportionalMassEditing) { return customTotalMass; } return EffectiveChestMass + EffectivePelvisMass + EffectiveArmMass * 2f + EffectiveLegMass * 2f + EffectiveHeadMass; } return VanillaTotalMass; } } public float EffectivePelvisMass { get { if (useCustomMass) { if (useProportionalMassEditing && useCustomPelvisMass) { return VanillaPelvisMass * MassProportion; } if (useCustomPelvisMass) { return customPelvisMass; } } return VanillaPelvisMass; } } public float EffectiveChestMass { get { if (useCustomMass) { if (useProportionalMassEditing && useCustomChestMass) { return VanillaChestMass * MassProportion; } if (useCustomChestMass) { return customChestMass; } } return VanillaChestMass; } } public float EffectiveArmMass { get { if (useCustomMass) { if (useProportionalMassEditing && useCustomArmMass) { return VanillaArmMass * MassProportion; } if (useCustomArmMass) { return customArmMass; } } return VanillaArmMass; } } public float EffectiveLegMass { get { if (useCustomMass) { if (useProportionalMassEditing && useCustomLegMass) { return VanillaLegMass * MassProportion; } if (useCustomLegMass) { return customLegMass; } } return VanillaLegMass; } } public float EffectiveHeadMass { get { if (useCustomMass) { if (useProportionalMassEditing && useCustomHeadMass) { return VanillaHeadMass * MassProportion; } if (useCustomHeadMass) { return customHeadMass; } } return VanillaHeadMass; } } public AvatarStatDriver(IntPtr @this) : base(@this) { } private void Awake() { _avatar = ((Component)this).GetComponent<Avatar>(); } } namespace AvatarStatExtender { internal class AvatarStatExtensionMod : MelonMod { public override void OnInitializeMelon() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown ((MelonBase)this).OnInitializeMelon(); Prefs.Initialize(); Log.Initialize(((MelonBase)this).LoggerInstance); Log.Info("Initializing the avatar extender..."); if (Prefs.TraceLogging) { Log.Warn("TRACE LOGGING IS ENABLED. Your log will be spammed with highly verbose debug statements."); Log.Warn("You can disable trace logging in the mod's preferences."); } Log.Debug("Creating Harmony..."); Harmony harmony = new Harmony("Extended Avatar Driver"); Log.Debug("Injecting fields from the stat component..."); SerialisationHandler.Inject<AvatarStatDriver>(0); SerialisationHandler.Inject<AvatarExtendedAudioContainer>(0); Log.Debug("Injection complete. Preparing the stat marshaller and audio driver..."); StatMarshaller.Initialize(harmony); SoundBroadcastMarshaller.Initialize(); } } public static class Log { [AllowNull] private static Instance _log; internal static void Initialize(Instance log) { _log = log; } public static void Info(object msg) { _log.Msg(msg); } public static void Info(string msg) { _log.Msg(msg); } public static void Warn(object msg) { _log.Warning(msg); } public static void Warn(string msg) { _log.Warning(msg); } public static void Error(object msg) { _log.Error(msg); } public static void Error(string msg) { _log.Error(msg); } public static void Debug(object msg) { _log.Msg(ConsoleColor.Gray, $"[DEBUG] {msg}"); } public static void Debug(string msg) { _log.Msg(ConsoleColor.Gray, "[DEBUG] " + msg); } public static void Trace(object msg) { if (Prefs.TraceLogging) { _log.Msg(ConsoleColor.DarkGray, $"[TRACE] {msg}"); } } public static void Trace(string msg) { if (Prefs.TraceLogging) { _log.Msg(ConsoleColor.DarkGray, "[TRACE] " + msg); } } } internal static class Prefs { private static MelonPreferences_Entry<bool> _traceLogging; public static bool TraceLogging => _traceLogging.Value; internal static void Initialize() { MelonPreferences_Category val = MelonPreferences.CreateCategory("extendedAvyDriver_main"); val.DisplayName = "Main Settings"; _traceLogging = val.CreateEntry<bool>("traceLogging", true, "Trace Logging", "Trace Logging causes extremely (like, comically) specific things to be written into the console." + Environment.NewLine + "This tends to flood the console with information that is useless normally, but it is very useful for debugging.", false, false, (ValueValidator)null, (string)null); } } } namespace AvatarStatExtender.Tools { public static class ListTools { private sealed class StubbedListImpl<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IReadOnlyList<T>, IReadOnlyCollection<T> { private sealed class EmptyEnumerator : IEnumerator<T>, IDisposable, IEnumerator { public T Current { get; } = default(T); object IEnumerator.Current => Current; public void Dispose() { } public bool MoveNext() { return false; } public void Reset() { } } public static readonly IReadOnlyList<T> INSTANCE = new StubbedListImpl<T>(); private static readonly EmptyEnumerator EMPTY_ENUMERATOR = new EmptyEnumerator(); public T this[int index] { get { throw new IndexOutOfRangeException(); } } T IList<T>.this[int index] { get { throw new IndexOutOfRangeException(); } set { throw new NotSupportedException(); } } public int Count { get; } = 0; public bool IsReadOnly { get; } = true; public IEnumerator<T> GetEnumerator() { return EMPTY_ENUMERATOR; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public int IndexOf(T item) { return -1; } public void Insert(int index, T item) { throw new NotSupportedException(); } public void RemoveAt(int index) { throw new NotSupportedException(); } public void Add(T item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(T item) { return false; } public void CopyTo(T[] array, int arrayIndex) { if (array == null) { throw new ArgumentNullException("array"); } if (arrayIndex >= array.Length) { throw new ArgumentOutOfRangeException("arrayIndex"); } } public bool Remove(T item) { throw new NotSupportedException(); } } public static IReadOnlyList<T> EmptyReadOnly<T>() { return StubbedListImpl<T>.INSTANCE; } } public static class PlayerObjectExtensions { private const string FUSION_PLAYER_REP_NAME = "[RigManager (FUSION PlayerRep)]"; private static readonly HashSet<Avatar> _reusableUniqueAvatarSet = new HashSet<Avatar>(64); public static IEnumerable<RigManager> GetRemotePlayers() { return ((IEnumerable<RigManager>)Object.FindObjectsOfType<RigManager>()).Where((RigManager rg) => ((Object)((Component)rg).gameObject).name == "[RigManager (FUSION PlayerRep)]"); } public static IEnumerable<RigManager> GetAllPlayers() { yield return Player.rigManager; foreach (RigManager remotePlayer in GetRemotePlayers()) { yield return remotePlayer; } } public static RigManager? GetRigManager(this Avatar avatar) { if ((Object)(object)avatar == (Object)null) { return null; } if ((Object)(object)((Component)avatar).transform.parent == (Object)null) { return null; } return ((Component)((Component)avatar).transform.parent).GetComponent<RigManager>(); } public static Avatar[] GetAllUniqueActiveAvatarPrefabs() { lock (_reusableUniqueAvatarSet) { _reusableUniqueAvatarSet.Clear(); IEnumerable<Avatar> source = from player in GetAllPlayers() where (Object)(object)player.avatar != (Object)null select player.avatar.GetOriginalPrefab() into avatar where (Object)(object)avatar != (Object)null && _reusableUniqueAvatarSet.Add(avatar) select avatar; return source.ToArray(); } } public static IEnumerable<Avatar> GetAllActiveAvatars() { return from player in GetAllPlayers() where (Object)(object)player.avatar != (Object)null select player.avatar; } public static Player_Health? GetHealth(this Avatar avatar) { if ((Object)(object)avatar == (Object)null) { return null; } RigManager rigManager = avatar.GetRigManager(); if ((Object)(object)rigManager == (Object)null) { return null; } if ((Object)(object)rigManager.health == (Object)null) { return null; } return ((Il2CppObjectBase)rigManager.health).Cast<Player_Health>(); } public static Avatar? GetAvatar(this Player_Health health) { if ((Object)(object)((Health)health)._rigManager == (Object)null) { return null; } return ((Health)health)._rigManager.avatar; } } public static class AvatarStatCalculationExtension { public struct AvatarMasses { public float massTotal; public float massPelvis; public float massChest; public float massArm; public float massLeg; public float massBody; public float massHead; public override string ToString() { return $"Masses[Total={massTotal}kg, [Head={massHead}kg, Chest={massChest}kg, Pelvis={massPelvis}kg, One Arm={massArm}kg, One Leg={massLeg}]]"; } } public struct AvatarStats { public float agility; public float speed; public float strengthUpper; public float strengthLower; [Obsolete("This value is computed lazily; it is very likely wrong. It is strongly advised that you do NOT use this.")] public float strengthGrip; public float vitality; public float intelligence; public override string ToString() { return $"Stats[Agility={agility}Ns, Speed={speed}m/s, Lower Strength={strengthLower}, Upper Strength={strengthUpper}, Grip Strength (Secret)={strengthGrip}, Vitality={vitality}, Intelligence={intelligence}]"; } } public struct AvatarComputationProperties { public float eyeHeight; public float t1HeightPercent; public Vector3 sternumOffset; public Vector3 sternumOffsetPercent; public Vector3 hipOffset; public Vector3 hipOffsetPercent; public Vector3 hipsPosition; public float sacrumHeightPercent; public float upperArmLength; public float upperLegLength; public float lowerArmLength; public float lowerLegLength; public float chestToShoulderPerc; [Obsolete("This value is computed lazily; it is very likely wrong. It is strongly advised that you do NOT use this.")] public float handSizeMult; } private const float PI = (float)Math.PI; private static Vector3 GetPositionBetween(Animator animator, HumanBodyBones first, HumanBodyBones second) { //IL_0002: 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) //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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) return Vector3.LerpUnclamped(animator.GetBoneTransform(first).position, animator.GetBoneTransform(second).position, 0.5f); } public static Vector3 EyePosition(this Avatar avatar) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_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_0030: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)avatar.eyeCenterOverride)) { return avatar.eyeCenterOverride.position; } return GetPositionBetween(avatar.animator, (HumanBodyBones)21, (HumanBodyBones)22); } public static float GetEyeHeight(this Avatar avatar) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return avatar.EyePosition().y - ((Component)avatar).transform.position.y; } public static void ComputeAllStats(this Avatar avatar, out AvatarMasses mass, out AvatarStats stats) { mass = avatar.ComputeMass(out var additionalInfo); stats = avatar.ComputeBaseStats(mass, additionalInfo); } public static AvatarMasses ComputeMass(this Avatar avatar, out AvatarComputationProperties additionalInfo, float normalizeTo82 = 0.5f) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_006e: 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_0077: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: 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_0131: 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_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016a: 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_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_0330: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_0387: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_04c2: Unknown result type (might be due to invalid IL or missing references) //IL_04cf: Unknown result type (might be due to invalid IL or missing references) //IL_04d4: Unknown result type (might be due to invalid IL or missing references) //IL_04d9: Unknown result type (might be due to invalid IL or missing references) //IL_04ec: Unknown result type (might be due to invalid IL or missing references) //IL_04f9: Unknown result type (might be due to invalid IL or missing references) //IL_04fe: Unknown result type (might be due to invalid IL or missing references) //IL_0503: Unknown result type (might be due to invalid IL or missing references) //IL_0516: Unknown result type (might be due to invalid IL or missing references) //IL_0523: Unknown result type (might be due to invalid IL or missing references) //IL_0528: Unknown result type (might be due to invalid IL or missing references) //IL_052d: Unknown result type (might be due to invalid IL or missing references) //IL_055c: Unknown result type (might be due to invalid IL or missing references) //IL_055e: Unknown result type (might be due to invalid IL or missing references) //IL_0565: Unknown result type (might be due to invalid IL or missing references) //IL_0567: Unknown result type (might be due to invalid IL or missing references) //IL_056e: Unknown result type (might be due to invalid IL or missing references) //IL_0570: Unknown result type (might be due to invalid IL or missing references) //IL_0592: Unknown result type (might be due to invalid IL or missing references) //IL_0594: Unknown result type (might be due to invalid IL or missing references) //IL_059b: Unknown result type (might be due to invalid IL or missing references) //IL_059d: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)avatar).transform; Animator animator = avatar.animator; Quaternion val = Quaternion.Inverse(transform.rotation); Vector3 position = animator.GetBoneTransform((HumanBodyBones)9).position; float eyeHeight = avatar.GetEyeHeight(); Vector3 position2 = animator.GetBoneTransform((HumanBodyBones)0).position; float num = (position.y - transform.position.y) / eyeHeight; Vector3 val2 = val * (GetPositionBetween(animator, (HumanBodyBones)13, (HumanBodyBones)14) - position); Vector3 val3 = val2 / eyeHeight; Vector3 val4 = val * (GetPositionBetween(animator, (HumanBodyBones)1, (HumanBodyBones)2) - position2); Vector3 val5 = val4 / eyeHeight; float num2 = (position2.y - transform.position.y) / eyeHeight; Vector3 val6 = animator.GetBoneTransform((HumanBodyBones)14).position - animator.GetBoneTransform((HumanBodyBones)8).position; float chestToShoulderPerc = (val * val6).x / eyeHeight; Vector3 val7 = val * (animator.GetBoneTransform((HumanBodyBones)16).position - animator.GetBoneTransform((HumanBodyBones)14).position); Vector3 val8 = val * (avatar.wristRt.position - animator.GetBoneTransform((HumanBodyBones)16).position); Vector3 val9 = val * (animator.GetBoneTransform((HumanBodyBones)4).position - animator.GetBoneTransform((HumanBodyBones)2).position); Vector3 val10 = val * (animator.GetBoneTransform((HumanBodyBones)6).position - animator.GetBoneTransform((HumanBodyBones)4).position); float magnitude = ((Vector3)(ref val7)).magnitude; float magnitude2 = ((Vector3)(ref val9)).magnitude; float magnitude3 = ((Vector3)(ref val8)).magnitude; float magnitude4 = ((Vector3)(ref val10)).magnitude; float num3 = (val3.y + num - (val5.y + num2)) * eyeHeight * 0.55f; float num4 = (avatar.ChestEllipseZ + avatar.ChestEllipseNegZ + avatar.SternumEllipseZ + avatar.SternumEllipseNegZ + avatar.bulgeBreast.apexZ + avatar.bulgeUpperBack.apexZ) * 0.5f * eyeHeight * (avatar.ChestEllipseX + avatar.ChestEllipseX) * eyeHeight * num3 * 0.7854f * 1000f; float num5 = (avatar.WaistEllipseZ + avatar.WaistEllipseNegZ + avatar.HipsEllipseZ + avatar.HipsEllipseNegZ + avatar.bulgeButt.apexZ + avatar.bulgeAbdomen.apexZ + avatar.bulgeLowerBack.apexZ) * 0.5f * eyeHeight * (eyeHeight * avatar.HipsEllipseX + avatar.WaistEllipseX) * num3 * 0.7854f * 1000f; float num6 = Mathf.Pow(AverageTotalRadius(avatar.elbowEllipse, avatar.forearmEllipse, avatar.wristEllipse) * eyeHeight, 2f) * (float)Math.PI * magnitude3 * 1000f; float num7 = Mathf.Pow(AverageTotalRadius(avatar.upperarmEllipse, avatar.elbowEllipse) * eyeHeight, 2f); float num8 = (num6 + num7 * (float)Math.PI * magnitude * 1000f) * 1.11538f; float num9 = Mathf.Pow(AverageTotalRadius(avatar.kneeEllipse, avatar.calfEllipse, avatar.ankleEllipse) * eyeHeight, 2f) * (float)Math.PI * magnitude4 * 1000f; float num10 = num9 + Mathf.Pow(AverageTotalRadius(avatar.thighUpperEllipse, avatar.kneeEllipse) * eyeHeight, 2f) * (float)Math.PI * magnitude2 * 1100f; float num11 = (num8 + num8 + (num5 + num4) + (num10 + num10)) * 1.0764263f; float num12 = Mathf.Clamp((avatar.ChestEllipseNegZ + avatar.ChestEllipseZ) / (avatar.WaistEllipseNegZ + avatar.WaistEllipseZ) - 0.1f, 0.7f, 1f); float num13 = ((82f - num11) * Mathf.Clamp01(normalizeTo82) + num11) * num12 / num11; float massTotal = num11 * num13; AvatarMasses avatarMasses = default(AvatarMasses); avatarMasses.massPelvis = num5 * num13; avatarMasses.massTotal = massTotal; avatarMasses.massChest = num4 * num13; avatarMasses.massArm = num13 * num8; avatarMasses.massLeg = num13 * num10; AvatarMasses result = avatarMasses; result.massBody = result.massPelvis + result.massChest + result.massArm + result.massArm + result.massLeg + result.massLeg; result.massHead = result.massTotal - result.massBody; Vector3 val11 = animator.GetBoneTransform((HumanBodyBones)39).position - animator.GetBoneTransform((HumanBodyBones)42).position; float magnitude5 = ((Vector3)(ref val11)).magnitude; val11 = animator.GetBoneTransform((HumanBodyBones)42).position - animator.GetBoneTransform((HumanBodyBones)43).position; float magnitude6 = ((Vector3)(ref val11)).magnitude; val11 = animator.GetBoneTransform((HumanBodyBones)43).position - animator.GetBoneTransform((HumanBodyBones)44).position; float magnitude7 = ((Vector3)(ref val11)).magnitude; float handSizeMult = (magnitude5 + magnitude6 + magnitude7) / 0.12762f; additionalInfo = new AvatarComputationProperties { eyeHeight = eyeHeight, hipOffset = val4, hipOffsetPercent = val5, hipsPosition = position2, lowerArmLength = magnitude3, lowerLegLength = magnitude4, sacrumHeightPercent = num2, sternumOffset = val2, sternumOffsetPercent = val3, upperArmLength = magnitude, upperLegLength = magnitude2, t1HeightPercent = num, chestToShoulderPerc = chestToShoulderPerc, handSizeMult = handSizeMult }; return result; } public static AvatarStats ComputeBaseStats(this Avatar avatar, AvatarMasses masses, AvatarComputationProperties additionalInfo) { //IL_004c: 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_0081: 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_008e: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: 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_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) float eyeHeight = additionalInfo.eyeHeight; float num = avatar.ForeheadEllipseX / 0.044f; float num2 = eyeHeight / 1.638f; float num3 = masses.massTotal / 75f; float num4 = (additionalInfo.chestToShoulderPerc * 10.354f + avatar.ChestEllipseX * 10.354f) * 0.5f; float num5 = (additionalInfo.sternumOffsetPercent.y + additionalInfo.t1HeightPercent - (additionalInfo.hipOffsetPercent.y + additionalInfo.sacrumHeightPercent)) / 0.31253165f; float num6 = AverageTotalRadius(avatar.upperarmEllipse, avatar.elbowEllipse) * (float)Math.PI; num6 /= 0.10681416f; if ((double)num6 < 1.0) { num6 += (1f - num6) * 0.6f; } float num7 = AverageTotalRadius(avatar.forearmEllipse, avatar.wristEllipse) * (float)Math.PI; num7 /= 0.081681415f; if ((double)num7 < 1.0) { num7 += (1f - num7) * 0.6f; } float num8 = AverageTotalRadius(avatar.thighUpperEllipse, avatar.kneeEllipse) * (float)Math.PI; num8 /= 0.14215706f; float num9 = additionalInfo.lowerLegLength / additionalInfo.eyeHeight; float num10 = additionalInfo.upperLegLength / additionalInfo.eyeHeight; float num11 = additionalInfo.lowerArmLength / additionalInfo.eyeHeight; float num12 = additionalInfo.upperArmLength / additionalInfo.eyeHeight; float num13 = (num9 + num10) / 0.506831f; float num14 = GrandTotalRadius(avatar.calfEllipse, avatar.ankleEllipse); float num15 = num7 * num3 * additionalInfo.handSizeMult; float num16 = num14 * 0.25f; float num17 = (num16 * (float)Math.PI / 0.09738937f + num8) * 0.5f * (num13 * num13) * num2 / num3; if ((double)num17 < 1.0) { num17 = Mathf.Pow(num17, 1f / 3f); } float num18 = Mathf.Clamp01((Mathf.Abs(num13 - 1f) - 1f) * -1f); float num19 = Mathf.Sqrt(Mathf.Abs(num)); float num20 = Mathf.Clamp01((Mathf.Abs(num19 - 1f) - 1f) * -1f); AvatarStats result = default(AvatarStats); result.speed = num17 * num2; result.vitality = num5 * num3 * num2; result.intelligence = num; result.agility = Mathf.Max((num20 + 1f) * 0.5f, 0.5f) * Mathf.Max(num18, 0.5f); result.strengthUpper = num4 * (num3 * (num6 * (num2 / ((num11 + num12) / 0.317f)))); result.strengthLower = num8 * num3 * num2 / num13; result.strengthGrip = num15 * num2; return result; } private static float RadiusTotal(SoftEllipse ellipse) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) return ellipse.XRadius + ellipse.ZRadius; } private static float AverageTotalRadius(params SoftEllipse[] ellipses) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) float num = 0f; for (int i = 0; i < ellipses.Length; i++) { num += RadiusTotal(ellipses[i]); } return num / (float)(ellipses.Length << 1); } private static float GrandTotalRadius(params SoftEllipse[] ellipses) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) float num = 0f; for (int i = 0; i < ellipses.Length; i++) { num += RadiusTotal(ellipses[i]); } return num; } } public static class SoundPlayerSystem { public static GameObject PlaySound(Transform on, AudioClip sound, float volume = 1f, float pitch = 1f, bool playAs2D = false, SoundFlags flags = SoundFlags.FollowEmitter | SoundFlags.RealtimePitchShift, AudioMixerGroup? mixer = null) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) Log.Trace("Playing sound: " + ((Object)sound).name + "..."); GameObject val = new GameObject(); if ((Object)(object)on == (Object)null) { on = ((Component)Camera.main).gameObject.transform; } val.transform.parent = on; val.transform.localPosition = Vector3.zero; bool flag = flags.HasFlag(SoundFlags.RealtimePitchShift); bool flag2 = flag || flags.HasFlag(SoundFlags.PitchShift); bool flag3 = flags.HasFlag(SoundFlags.FollowEmitter); float num = pitch; if (flag2) { num *= Time.timeScale; } AudioSource val2 = val.AddComponent<AudioSource>(); val2.outputAudioMixerGroup = (AudioMixerGroup)(Object.op_Implicit((Object)(object)mixer) ? ((object)mixer) : ((object)Audio.SFXMixer)); val2.volume = volume; val2.pitch = num; val2.loop = false; val2.playOnAwake = false; val2.clip = sound; SetupDefaultNearbyCurves(val2, playAs2D); val2.Play(); if (!flag && !flag3) { Object.Destroy((Object)(object)val, sound.length + 0.2f); } else { RealtimeSoundDriver.StartDriving(val2, flag3, flag); } ((Object)val).name = ((Object)sound).name; return val; } public static GameObject PlaySound(Transform on, PlayableSoundData sound, AudioSource? template) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) Log.Trace("Playing sound: " + ((Object)sound.sound).name + "..."); GameObject val = new GameObject(); if ((Object)(object)on == (Object)null) { on = ((Component)Camera.main).gameObject.transform; } val.transform.parent = on; val.transform.localPosition = Vector3.zero; SoundFlags playTechnique = sound.playTechnique; AudioMixerGroup mixer = sound.mixer; bool flag = playTechnique.HasFlag(SoundFlags.RealtimePitchShift); bool flag2 = flag || playTechnique.HasFlag(SoundFlags.PitchShift); bool flag3 = playTechnique.HasFlag(SoundFlags.FollowEmitter); float num = sound.pitch; if (flag2) { num *= Time.timeScale; } AudioSource val2 = val.AddComponent<AudioSource>(); val2.outputAudioMixerGroup = (Object.op_Implicit((Object)(object)mixer) ? mixer : Audio.SFXMixer); val2.volume = sound.volume; val2.pitch = num; val2.loop = false; val2.playOnAwake = false; val2.clip = sound.sound; if ((Object)(object)template == (Object)null || ((Object)template).name == "Nearby Spatial Source" || ((Object)template).name == "Global Source") { SetupDefaultNearbyCurves(val2, isPlayingAs2D: false); } else { CopyCurves(val2, template); } val2.Play(); if (!flag && !flag3 && !val2.loop) { Object.Destroy((Object)(object)val, sound.sound.length + 0.2f); } else { RealtimeSoundDriver.StartDriving(val2, flag3, flag); } ((Object)val).name = ((Object)val2.clip).name; return val; } private static void SetupDefaultNearbyCurves(AudioSource src, bool isPlayingAs2D) { //IL_003d: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Expected O, but got Unknown //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Expected O, but got Unknown //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Expected O, but got Unknown //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Expected O, but got Unknown src.rolloffMode = (AudioRolloffMode)2; if (!isPlayingAs2D) { Il2CppStructArray<Keyframe> val = new Il2CppStructArray<Keyframe>(3L); ((Il2CppArrayBase<Keyframe>)(object)val)[0] = new Keyframe(0.1f, 1f, 0f, 0f, 0.3333333f, 0.3333333f); ((Il2CppArrayBase<Keyframe>)(object)val)[1] = new Keyframe(0.3540465f, 0.2750549f, -1.329637f, -1.329637f, 0.3333333f, 0.3333333f); ((Il2CppArrayBase<Keyframe>)(object)val)[2] = new Keyframe(1f, 0f, -0.1762998f, -0.1762998f, 0.3204496f, 0.3333333f); Il2CppStructArray<Keyframe> val2 = new Il2CppStructArray<Keyframe>(2L); ((Il2CppArrayBase<Keyframe>)(object)val2)[0] = new Keyframe(0f, 0.5f, -0.3094383f, -0.3094383f, 0.4783377f, 0f); ((Il2CppArrayBase<Keyframe>)(object)val2)[1] = new Keyframe(1f, 0f, 0f, 0f, 0.3333333f, 0.3333333f); Il2CppStructArray<Keyframe> val3 = new Il2CppStructArray<Keyframe>(4L); ((Il2CppArrayBase<Keyframe>)(object)val3)[0] = new Keyframe(0f, 0f, 0f, 0f, 0.3333333f, 0.3333333f); ((Il2CppArrayBase<Keyframe>)(object)val3)[1] = new Keyframe(0.1896423f, 0.8151474f, 1.837119f, 1.837119f, 0.07648899f, 0.07007767f); ((Il2CppArrayBase<Keyframe>)(object)val3)[2] = new Keyframe(0.4214395f, 0.9818153f, 0.1665137f, 0.1665137f, 0.6917831f, 0f); ((Il2CppArrayBase<Keyframe>)(object)val3)[3] = new Keyframe(1f, 1f, 0f, 0f, 0.3333333f, 0.3333333f); Il2CppStructArray<Keyframe> val4 = new Il2CppStructArray<Keyframe>(3L); ((Il2CppArrayBase<Keyframe>)(object)val4)[0] = new Keyframe(0f, 0f, 0f, 0f, 0.3333333f, 0.3333333f); ((Il2CppArrayBase<Keyframe>)(object)val4)[1] = new Keyframe(0.3287202f, 0.1654926f, 1.535669f, 1.535669f, 0.3333333f, 0.3333333f); ((Il2CppArrayBase<Keyframe>)(object)val4)[2] = new Keyframe(0.4605339f, 1f, 0.04093663f, 0.04093663f, 0.1604803f, 0f); src.rolloffMode = (AudioRolloffMode)2; src.SetCustomCurve((AudioSourceCurveType)0, new AnimationCurve(val)); src.SetCustomCurve((AudioSourceCurveType)3, new AnimationCurve(val2)); src.SetCustomCurve((AudioSourceCurveType)1, new AnimationCurve(val3)); src.SetCustomCurve((AudioSourceCurveType)2, new AnimationCurve(val4)); } else { src.spatialBlend = 0f; src.reverbZoneMix = 0f; } src.maxDistance = 7f; } private static void CopyCurves(AudioSource destination, AudioSource template) { //IL_0059: 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) destination.rolloffMode = (AudioRolloffMode)2; destination.priority = template.priority; destination.bypassEffects = template.bypassEffects; destination.bypassListenerEffects = template.bypassListenerEffects; destination.bypassReverbZones = template.bypassReverbZones; destination.maxDistance = template.maxDistance; destination.minDistance = template.minDistance; destination.rolloffMode = template.rolloffMode; destination.velocityUpdateMode = template.velocityUpdateMode; destination.loop = template.loop; destination.SetCustomCurve((AudioSourceCurveType)0, destination.GetCustomCurve((AudioSourceCurveType)0)); destination.SetCustomCurve((AudioSourceCurveType)3, destination.GetCustomCurve((AudioSourceCurveType)3)); destination.SetCustomCurve((AudioSourceCurveType)1, destination.GetCustomCurve((AudioSourceCurveType)1)); destination.SetCustomCurve((AudioSourceCurveType)2, destination.GetCustomCurve((AudioSourceCurveType)2)); } } public sealed class ReadOnlyHashSet<T> : ISet<T>, ICollection<T>, IEnumerable<T>, IEnumerable { private readonly HashSet<T> _set; public int Count { get; } public bool IsReadOnly { get; } = true; public ReadOnlyHashSet() { _set = new HashSet<T>(); Count = 0; } public ReadOnlyHashSet(IEnumerable<T> set) { _set = new HashSet<T>(set); Count = _set.Count; } public bool ContainsAny(T[] of) { for (int i = 0; i < of.Length; i++) { if (Contains(of[i])) { return true; } } return false; } public bool Add(T item) { throw new NotSupportedException(); } public void UnionWith(IEnumerable<T> other) { throw new NotSupportedException(); } public void IntersectWith(IEnumerable<T> other) { throw new NotSupportedException(); } public void ExceptWith(IEnumerable<T> other) { throw new NotSupportedException(); } public void SymmetricExceptWith(IEnumerable<T> other) { throw new NotSupportedException(); } public bool IsSubsetOf(IEnumerable<T> other) { return _set.IsSubsetOf(other); } public bool IsSupersetOf(IEnumerable<T> other) { return _set.IsSupersetOf(other); } public bool IsProperSupersetOf(IEnumerable<T> other) { return _set.IsProperSupersetOf(other); } public bool IsProperSubsetOf(IEnumerable<T> other) { return _set.IsProperSubsetOf(other); } public bool Overlaps(IEnumerable<T> other) { return _set.Overlaps(other); } public bool SetEquals(IEnumerable<T> other) { return _set.SetEquals(other); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(T item) { return _set.Contains(item); } public void CopyTo(T[] array, int arrayIndex) { _set.CopyTo(array, arrayIndex); } public bool Remove(T item) { throw new NotSupportedException(); } public IEnumerator<T> GetEnumerator() { return _set.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public static class SoundBlockNameSanitizer { private static readonly char[] SEMICOLON = new char[1] { ';' }; public static readonly Regex SOUND_REGEX = new Regex("[a-z0-9_\\-\\.]+(?:(?:\\:)[a-z0-9_\\-\\.]+)?"); public static bool IsSane(string unfilteredSingle) { Match match = SOUND_REGEX.Match(unfilteredSingle); return match.Success && match.Value == unfilteredSingle; } public static string[] GetSanitizedEventList(string unfiltered) { if (unfiltered.Length == 0) { return Array.Empty<string>(); } string[] array = unfiltered.Split(SEMICOLON, StringSplitOptions.RemoveEmptyEntries); string[] array2 = new string[array.Length]; int newSize = 0; for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (!string.IsNullOrEmpty(text) && text.Length <= 50) { text = text.ToLower(); Match match = SOUND_REGEX.Match(text); if (match.Success && match.Value == text) { array2[newSize++] = text; } } } Array.Resize(ref array2, newSize); return array2; } public static string[] GetSanitizedEventList(string[] names) { if (names.Length == 0) { return Array.Empty<string>(); } string[] array = new string[names.Length]; int newSize = 0; for (int i = 0; i < names.Length; i++) { string text = names[i].Trim(); if (!string.IsNullOrEmpty(text) && text.Length <= 50) { text = text.ToLower(); Match match = SOUND_REGEX.Match(text); if (match.Success && match.Value == text) { array[newSize++] = text; } } } Array.Resize(ref array, newSize); return array; } public static string GetSanitizedEventListJoined(string unfiltered) { string[] sanitizedEventList = GetSanitizedEventList(unfiltered); if (sanitizedEventList.Length == 0) { return string.Empty; } if (sanitizedEventList.Length == 1) { return sanitizedEventList[0]; } return string.Join(";", sanitizedEventList); } public static void CreateStringAndEnumMix(string unfiltered, out string[] customs, out AudioEventType enums) { customs = GetSanitizedEventList(unfiltered); enums = AudioEventType.Custom; CreateStringAndEnumMix(ref customs, ref enums); } public static void CreateStringAndEnumMix(string[] unfilteredArray, out string[] customs, out AudioEventType enums) { customs = GetSanitizedEventList(unfilteredArray); enums = AudioEventType.Custom; CreateStringAndEnumMix(ref customs, ref enums); } private static void CreateStringAndEnumMix(ref string[] customs, ref AudioEventType enums) { int newSize = 0; for (int i = 0; i < customs.Length; i++) { int num = AudioEventTypeExt.ALL_FLAGS_EVENT_NAMES.IndexOf(customs[i]); if (num < 0) { customs[newSize++] = customs[i]; } else { enums |= AudioEventTypeExt.ALL_FLAGS[num]; } } Array.Resize(ref customs, newSize); } } public sealed class StatMarshaller { internal static void Initialize(Harmony harmony) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown Log.Info("Patching SLZ::VRMK::Avatar::ComputeBaseStats..."); harmony.Patch((MethodBase)QuickGetMethod<Avatar>("ComputeBaseStats"), (HarmonyMethod)null, new HarmonyMethod(QuickGetMethod<StatMarshaller>("ComputeStatsPostfix")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.Info("Patching SLZ::VRMK::Avatar::ComputeMass..."); harmony.Patch((MethodBase)QuickGetMethod<Avatar>("ComputeMass"), (HarmonyMethod)null, new HarmonyMethod(QuickGetMethod<StatMarshaller>("ComputeMassPostfix")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.Info("Patching SLZ::Rig::RemapRig::JumpCharge..."); harmony.Patch((MethodBase)QuickGetMethod<RemapRig>("JumpCharge"), (HarmonyMethod)null, new HarmonyMethod(QuickGetMethod<StatMarshaller>("JumpChargePostfix")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.Info("Patches performed."); } private static void ComputeStatsPostfix(Avatar __instance) { ApplyStats(__instance); } private static void ComputeMassPostfix(Avatar __instance) { ApplyStats(__instance); } private static void JumpChargePostfix(bool chargeInput = true) { Avatar currentAvatar = Player.GetCurrentAvatar(); OnJump(currentAvatar, chargeInput); } private static void OnJump(Avatar avatar, bool isJumpButtonDown) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) AvatarStatDriver component = ((Component)avatar).gameObject.GetComponent<AvatarStatDriver>(); if (!((Object)(object)component == (Object)null)) { JumpTracker jumpTracker = ((Component)avatar).GetComponent<JumpTracker>(); if ((Object)(object)jumpTracker == (Object)null) { jumpTracker = ((Component)avatar).gameObject.AddComponent<JumpTracker>(); } PhysicsRig physicsRig = Player.GetPhysicsRig(); PhysGrounder physG = physicsRig.physG; jumpTracker.MarkPlayerOnGround(physG.isGrounded); if (jumpTracker.TryJump(isJumpButtonDown)) { physicsRig.torso.rbPelvis.AddForce(Vector3.up * component.extraJumpVerticalVelocity, (ForceMode)2); } } } private static void ApplyStats(Avatar avatar) { AvatarStatDriver component = ((Component)avatar).gameObject.GetComponent<AvatarStatDriver>(); if (!((Object)(object)component == (Object)null)) { Log.Debug("Enforcing that stats are applied to the desired avatar."); avatar._agility = component.EffectiveAgility; avatar._speed = component.EffectiveSpeed; avatar._strengthUpper = component.EffectiveUpperStrength; avatar._strengthLower = component.EffectiveLowerStrength; avatar._vitality = component.EffectiveVitality; avatar._intelligence = component.EffectiveIntelligence; avatar._massChest = component.EffectiveChestMass; avatar._massPelvis = component.EffectivePelvisMass; avatar._massLeg = component.EffectiveLegMass; avatar._massArm = component.EffectiveArmMass; avatar._massHead = component.EffectiveHeadMass; avatar._massTotal = component.EffectiveTotalMass; Log.Debug("Stats and masses have been applied to " + ((Object)avatar).name); } } private static MethodInfo QuickGetMethod<T>(string name) { return typeof(T).GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } } public static class Tools { private static readonly CultureInfo PERCENT_CULTURE; static Tools() { PERCENT_CULTURE = (CultureInfo)Thread.CurrentThread.CurrentCulture.Clone(); PERCENT_CULTURE.NumberFormat.PercentPositivePattern = 1; } public static string FormatPercentage(this float value, int digits = 2) { string text = $"P{digits}"; return value.ToString(text, PERCENT_CULTURE); } public static void ResizeArrayDefault<T>(ref T[] array, int newLength, T fillWith) { int num = array.Length; Array.Resize(ref array, newLength); if (num < newLength) { for (int i = num; i < newLength; i++) { array[i] = fillWith; } } } } internal static class XPlat { public const CompatiblePlatforms META_QUEST = 3; public static bool IsQuestie => (int)MelonUtils.CurrentPlatform == 3; } } namespace AvatarStatExtender.Tools.Assets { public static class AssetLocator { public static bool IsPrefabAvatar(this Avatar avatar) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) int result; if (((Component)avatar).gameObject.scene != SceneManager.GetActiveScene()) { Scene scene = ((Component)avatar).gameObject.scene; result = ((!((Scene)(ref scene)).IsValid()) ? 1 : 0); } else { result = 0; } return (byte)result != 0; } private static Avatar? BruteForceFindCrateOfAvatar(Avatar avatar) { if (avatar.IsPrefabAvatar()) { return avatar; } QuickPrefabIdentifier quickPrefabIdentifier = null; QuickPrefabIdentifier component = ((Component)avatar).GetComponent<QuickPrefabIdentifier>(); if (component != null && (Object)(object)component != (Object)null) { quickPrefabIdentifier = component; if ((Object)(object)quickPrefabIdentifier.Prefab != (Object)null) { return quickPrefabIdentifier.Prefab; } } Log.Trace("Searching the warehouse for the crate of " + ((Object)avatar).name + "..."); Animator component2 = ((Component)avatar).GetComponent<Animator>(); AvatarCrate[] array = Il2CppArrayBase<AvatarCrate>.op_Implicit(AssetWarehouse.Instance.GetCrates<AvatarCrate>((ICrateFilter<AvatarCrate>)null).ToArray()); foreach (AvatarCrate val in array) { if (!((MarrowAsset)(object)((GameObjectCrate)val).MainGameObject != (MarrowAsset)null)) { continue; } GameObject asset = ((MarrowAssetT<GameObject>)(object)((GameObjectCrate)val).MainGameObject).Asset; if (asset == null || !((Object)(object)asset != (Object)null)) { continue; } Animator component3 = asset.GetComponent<Animator>(); if (component3 != null && (Object)(object)component3 != (Object)null && (Object)(object)component3.avatar == (Object)(object)component2.avatar) { Log.Trace("Found the prefab! I'll cache it now for this clone."); Avatar component4 = asset.GetComponent<Avatar>(); if ((Object)(object)quickPrefabIdentifier == (Object)null) { quickPrefabIdentifier = ((Component)avatar).gameObject.AddComponent<QuickPrefabIdentifier>(); } quickPrefabIdentifier.Prefab = component4; return component4; } } Log.Trace("No luck, chief."); return null; } public static Avatar? GetOriginalPrefab(this Avatar avatar) { return BruteForceFindCrateOfAvatar(avatar); } } } namespace AvatarStatExtender.Components { [RegisterTypeInIl2Cpp] public sealed class JumpTracker : MonoBehaviour { private bool _hasPressedJumpButton = false; private bool _hasAlreadyJumped = false; private bool _isOnGround = false; public JumpTracker(IntPtr @this) : base(@this) { } public void MarkPlayerOnGround(bool isGrounded) { _isOnGround = isGrounded; } public bool TryJump(bool isJumpButtonPressed) { if (isJumpButtonPressed) { _hasPressedJumpButton = true; if (_isOnGround) { _hasAlreadyJumped = false; } return false; } bool result = false; if (_isOnGround && _hasPressedJumpButton && !_hasAlreadyJumped) { _hasAlreadyJumped = true; result = true; } else if (_isOnGround && !_hasPressedJumpButton) { _hasAlreadyJumped = false; } _hasPressedJumpButton = false; return result; } } [RegisterTypeInIl2Cpp] internal sealed class QuickPrefabIdentifier : MonoBehaviour { public Avatar? Prefab { get; set; } public QuickPrefabIdentifier(IntPtr @this) : base(@this) { } } [RegisterTypeInIl2Cpp] public sealed class RealtimeSoundDriver : MonoBehaviour { [AllowNull] private AudioSource _src; private float _originalPitch = 1f; public bool pitchShiftInRealtime = false; public bool followParent = false; private Vector3? _globalOffset; public Vector3 LocalOffset { get; set; } public Vector3 GlobalOffset { get { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return _globalOffset.GetValueOrDefault(); } set { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (value == Vector3.zero) { _globalOffset = null; } else { _globalOffset = value; } } } public RealtimeSoundDriver(IntPtr @this) : base(@this) { } public static void StartDriving(AudioSource sound, bool followParent, bool pitchShiftInRealtime) { RealtimeSoundDriver realtimeSoundDriver = ((Component)sound).gameObject.AddComponent<RealtimeSoundDriver>(); realtimeSoundDriver.pitchShiftInRealtime = pitchShiftInRealtime; realtimeSoundDriver.followParent = followParent; } private void Start() { _src = ((Component)this).GetComponent<AudioSource>(); _originalPitch = _src.pitch; } private void Update() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_009d: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_src == (Object)null) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } if (!_src.isPlaying) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } if (followParent) { ((Component)this).transform.localPosition = LocalOffset; if (_globalOffset.HasValue) { if ((Object)(object)((Component)this).transform.parent != (Object)null) { Vector3 val = Vector4.op_Implicit(((Component)this).transform.parent.localToWorldMatrix * Vector4.op_Implicit(_globalOffset.Value)); Transform transform = ((Component)this).transform; transform.localPosition += val; } else { Transform transform2 = ((Component)this).transform; transform2.localPosition += _globalOffset.Value; } } } if (pitchShiftInRealtime) { _src.pitch = _originalPitch * Time.timeScale; } } } } namespace AvatarStatExtender.Data { internal static class AudioEntries { public static IEnumerable<AudioEntry> EntriesFromContainer(this AvatarExtendedAudioContainer container) { if ((Object)(object)container == (Object)null) { throw new ArgumentNullException("container", "The audio container cannot be null."); } container.EnsureSymmetry(); int length = container.names.Length; for (int index = 0; index < length; index++) { yield return container.EntryFromContainer(index, skipSymmetry: true); } } public static bool TryRemoveEntry(this AvatarExtendedAudioContainer container, AudioEntry entry) { if ((Object)(object)container == (Object)null) { throw new ArgumentNullException("container", "The audio container cannot be null."); } if (entry == null) { throw new ArgumentNullException("entry", "The audio entry cannot be null."); } int num = container.Entries.IndexOf(entry); if (num >= 0) { container.Entries.RemoveAt(num); container.OverwriteFromEntries(container.Entries); return true; } return false; } public static bool TryRemoveEntryAt(this AvatarExtendedAudioContainer container, int index) { if ((Object)(object)container == (Object)null) { throw new ArgumentNullException("container", "The audio container cannot be null."); } if (index < 0 || index >= container.Entries.Count) { return false; } container.Entries.RemoveAt(index); container.OverwriteFromEntries(container.Entries); return true; } public static AudioEntry EntryFromContainer(this AvatarExtendedAudioContainer container, int index, bool skipSymmetry = false) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container == (Object)null) { throw new ArgumentNullException("container", "The audio container cannot be null."); } if (!skipSymmetry) { container.EnsureSymmetry(); } string name = container.names[index] ?? string.Empty; SerializableAudioArray serializableAudioArray = container.sounds[index]; if (serializableAudioArray == null) { serializableAudioArray = new SerializableAudioArray(); container.sounds[index] = serializableAudioArray; } AudioClip[] source = serializableAudioArray.Clips.ToArray(); AudioPlayType playType = container.playTypes[index]; AudioEventType eventType = container.eventTypes[index]; string customPlayType = container.customPlayTypes[index]; string customEventTypes = container.customEventTypes[index]; Vector2 pitchRange = container.pitchRanges[index]; float volume = container.volumes[index]; AudioMixerTarget mixerTarget = container.mixers[index]; SoundFlags soundFlags = container.soundFlags[index]; AudioSource overrideTemplateAudioSrc = container.templateSources[index]; return new AudioEntry(name, playType, customPlayType, eventType, customEventTypes, source.ToList(), pitchRange, volume, mixerTarget, soundFlags, overrideTemplateAudioSrc); } public static void OverwriteFromEntries(this AvatarExtendedAudioContainer container, IEnumerable<AudioEntry> entriesEnumerable) { if ((Object)(object)container == (Object)null) { throw new ArgumentNullException("container", "The audio container cannot be null."); } AudioEntry[] array = entriesEnumerable.ToArray(); int num = array.Length; Array.Resize(ref container.names, num); Array.Resize(ref container.sounds.children, num); Array.Resize(ref container.playTypes, num); Array.Resize(ref container.eventTypes, num); Array.Resize(ref container.customPlayTypes, num); Array.Resize(ref container.customEventTypes, num); Array.Resize(ref container.pitchRanges, num); Array.Resize(ref container.volumes, num); Array.Resize(ref container.mixers, num); Array.Resize(ref container.soundFlags, num); Array.Resize(ref container.templateSources, num); for (int i = 0; i < num; i++) { container.OverwriteFromEntry(array[i], i, skipSymmetry: true); } } public static void OverwriteFromEntry(this AvatarExtendedAudioContainer container, AudioEntry entry, int index, bool skipSymmetry = false) { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container == (Object)null) { throw new ArgumentNullException("container", "The audio container cannot be null."); } if (!skipSymmetry) { container.EnsureSymmetry(); } if (entry == null) { throw new InvalidOperationException($"AudioEntry #{index} is null."); } container.names[index] = entry.Name; if (container.sounds[index] == null) { container.sounds[index] = new SerializableAudioArray(); } container.sounds[index].Clips = entry.Sounds ?? new List<AudioClip>(); container.playTypes[index] = entry.PlayType; container.eventTypes[index] = entry.EventType; container.customPlayTypes[index] = entry.CustomPlayType; container.customEventTypes[index] = entry.CustomEventTypes; container.pitchRanges[index] = entry.PitchRange; container.volumes[index] = entry.Volume; container.mixers[index] = entry.Mixer; container.soundFlags[index] = entry.SoundFlags; container.templateSources[index] = entry.OverrideTemplateAudioSource; } } internal class AudioEntry { private string? _customEventTypes = null; public string Name { get; set; } = "New Sound Group"; public bool UseCustomPlayType => PlayType == AudioPlayType.Custom; public AudioPlayType PlayType { get; set; } public string? CustomPlayType { get; set; } public AudioEventType EventType { get; set; } public string? CustomEventTypes { get { return _customEventTypes?.ToLower(); } set { _customEventTypes = value?.ToLower(); } } public List<AudioClip> Sounds { get; set; } = new List<AudioClip>(); public Vector2 PitchRange { get; set; } = new Vector2(1f, 1f); public float Volume { get; set; } = 0.5f; public AudioMixerTarget Mixer { get; set; } = AudioMixerTarget.SFX; public SoundFlags SoundFlags { get; set; } = SoundFlags.FollowEmitter | SoundFlags.RealtimePitchShift; public AudioSource? OverrideTemplateAudioSource { get; set; } public AudioEntry() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) Name = "New Sound Group"; PlayType = AudioPlayType.Random; EventType = AudioEventType.HitBullet; Sounds = new List<AudioClip>(); } public AudioEntry(string name, AudioPlayType playType, string? customPlayType, AudioEventType eventType, string? customEventTypes, List<AudioClip> sounds, Vector2 pitchRange, float volume, AudioMixerTarget mixerTarget, SoundFlags soundFlags, AudioSource overrideTemplateAudioSrc) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) Name = name ?? throw new ArgumentNullException("name"); PlayType = playType; CustomPlayType = customPlayType; EventType = eventType; CustomEventTypes = customEventTypes; Sounds = sounds ?? throw new ArgumentNullException("sounds"); PitchRange = pitchRange; Volume = volume; Mixer = mixerTarget; SoundFlags = soundFlags; OverrideTemplateAudioSource = overrideTemplateAudioSrc; } } internal static class AudioCache { private static readonly ISet<ReadOnlyAudioEntry> EMPTY = new ReadOnlyHashSet<ReadOnlyAudioEntry>(); private static readonly Dictionary<IntPtr, ISet<ReadOnlyAudioEntry>> _cachePerAvatar = new Dictionary<IntPtr, ISet<ReadOnlyAudioEntry>>(); private static readonly Dictionary<IntPtr, Dictionary<string, ISet<ReadOnlyAudioEntry>>> _cachePerAvatarByEventName = new Dictionary<IntPtr, Dictionary<string, ISet<ReadOnlyAudioEntry>>>(); private static readonly Dictionary<IntPtr, Dictionary<AudioEventType, ISet<ReadOnlyAudioEntry>>> _cachePerAvatarByEventEnum = new Dictionary<IntPtr, Dictionary<AudioEventType, ISet<ReadOnlyAudioEntry>>>(); public static ISet<ReadOnlyAudioEntry> GetAllSoundsOfAvatar(Avatar avatar) { Avatar originalPrefab = avatar.GetOriginalPrefab(); if ((Object)(object)originalPrefab == (Object)null) { Log.Error("Failed to find original prefab for avatar " + ((Object)avatar).name + " when trying to collect all sounds. An empty set will be returned."); return EMPTY; } return GetAllSoundsOfAvatarPrefab(originalPrefab); } private static ISet<ReadOnlyAudioEntry> GetAllSoundsOfAvatarPrefab(Avatar originalAvy) { if (!originalAvy.IsPrefabAvatar()) { throw new ArgumentException("The avatar that called this method was not a prefab (" + ((Object)originalAvy).name + ")"); } IntPtr pointer = ((Il2CppObjectBase)originalAvy).Pointer; if (_cachePerAvatar.TryGetValue(pointer, out ISet<ReadOnlyAudioEntry> value)) { return value; } AvatarExtendedAudioContainer component = ((Component)originalAvy).GetComponent<AvatarExtendedAudioContainer>(); if (component != null && (Object)(object)component != (Object)null) { Log.Trace("Avatar has a container for custom sounds..."); IReadOnlyList<ReadOnlyAudioEntry> entries = component.GetEntries(); _cachePerAvatar[pointer] = new ReadOnlyHashSet<ReadOnlyAudioEntry>(entries); _cachePerAvatarByEventName[pointer] = new Dictionary<string, ISet<ReadOnlyAudioEntry>>(); _cachePerAvatarByEventEnum[pointer] = new Dictionary<AudioEventType, ISet<ReadOnlyAudioEntry>>(); foreach (ReadOnlyAudioEntry item in entries) { Log.Trace("Handling entry: " + item.Name); foreach (string resolvedCustomEvent in item.ResolvedCustomEvents) { if (!_cachePerAvatarByEventName[pointer].TryGetValue(resolvedCustomEvent, out ISet<ReadOnlyAudioEntry> value2)) { value2 = (_cachePerAvatarByEventName[pointer][resolvedCustomEvent] = new HashSet<ReadOnlyAudioEntry>()); } value2.Add(item); Log.Trace("Entry " + item.Name + " has registered a custom event: " + resolvedCustomEvent); } if (item.EventType == AudioEventType.Custom) { continue; } Log.Trace("Entry " + item.Name + " declares one or more vanilla event types..."); for (int i = 0; i < AudioEventTypeExt.ALL_FLAGS.Length; i++) { AudioEventType audioEventType = AudioEventTypeExt.ALL_FLAGS[i]; if ((item.EventType & audioEventType) != 0) { if (!_cachePerAvatarByEventEnum[pointer].TryGetValue(audioEventType, out ISet<ReadOnlyAudioEntry> value3)) { value3 = (_cachePerAvatarByEventEnum[pointer][audioEventType] = new HashSet<ReadOnlyAudioEntry>()); } value3.Add(item); Log.Trace($"Entry {item.Name} has registered a vanilla event: {audioEventType}"); } } } Log.Trace("The cache has been constructed for " + ((Object)component).name); return _cachePerAvatar[pointer]; } Log.Trace("Avatar does not have a container for custom sounds."); _cachePerAvatarByEventName[pointer] = new Dictionary<string, ISet<ReadOnlyAudioEntry>>(); _cachePerAvatarByEventEnum[pointer] = new Dictionary<AudioEventType, ISet<ReadOnlyAudioEntry>>(); _cachePerAvatar[pointer] = EMPTY; return EMPTY; } internal static IEnumerable<ReadOnlyAudioEntry> GetMatchingSoundsFromAvatarSingle(Avatar prefab, string singleCustomEventType) { IntPtr onAvatar = ((Il2CppObjectBase)prefab).Pointer; GetAllSoundsOfAvatarPrefab(prefab); if (_cachePerAvatarByEventName.TryGetValue(onAvatar, out Dictionary<string, ISet<ReadOnlyAudioEntry>> entriesLookup)) { if (entriesLookup.TryGetValue(singleCustomEventType, out ISet<ReadOnlyAudioEntry> entries)) { foreach (ReadOnlyAudioEntry item in entries) { yield return item; } } else { Log.Trace("Missing sound set in string cache. Skipping."); } } else { Log.Trace("Missing avatar in string cache. Skipping."); } } internal static IEnumerable<ReadOnlyAudioEntry> GetMatchingSoundsFromAvatarSingle(Avatar prefab, AudioEventType singleEventType) { IntPtr onAvatar = ((Il2CppObjectBase)prefab).Pointer; GetAllSoundsOfAvatarPrefab(prefab); if (_cachePerAvatarByEventEnum.TryGetValue(onAvatar, out Dictionary<AudioEventType, ISet<ReadOnlyAudioEntry>> entriesLookup)) { if (entriesLookup.TryGetValue(singleEventType, out ISet<ReadOnlyAudioEntry> entries)) { foreach (ReadOnlyAudioEntry item in entries) { yield return item; } } else { Log.Trace("Missing sound set in enum cache. Skipping."); } } else { Log.Trace("Missing avatar in enum cache. Skipping."); } } } [Flags] public enum AudioEventType { [InspectorName("Declare a custom event name...")] Custom = 0, [InspectorName("Projectile Damage (hitbullet)")] [EventName("hitbullet")] HitBullet = 1, [InspectorName("Non-Projectile Damage (hitblunt)")] [EventName("hitblunt")] HitBlunt = 2, [InspectorName("Any Damage (hitany)")] [EventName("hitany")] HitAny = 4, [InspectorName("Switching Avatar (spawn)")] [EventName("spawn")] Spawn = 8 } [AttributeUsage(AttributeTargets.Field)] public sealed class EventNameAttribute : Attribute { public readonly string name; public EventNameAttribute(string name) { this.name = name; } } public static class AudioEventTypeExt { public static readonly int MAX_FLAG; public static readonly ReadOnlyReferenceSpan<AudioEventType> ALL_FLAGS; public static readonly ReadOnlyReferenceSpan<string> ALL_FLAGS_EVENT_NAMES; private static readonly Dictionary<AudioEventType, string> _nameLookup; private static readonly Dictionary<string, AudioEventType> _valueLookup; static AudioEventTypeExt() { Type typeFromHandle = typeof(AudioEventType); AudioEventType[] array = new AudioEventType[32]; string[] array2 = new string[32]; Log.Trace("Starting up the audio type extension."); int num = 0; for (int i = 0; i < 32; i++) { AudioEventType audioEventType = (AudioEventType)(1 << i); if (Enum.IsDefined(typeFromHandle, audioEventType)) { num = i; array[i] = audioEventType; FieldInfo field = typeFromHandle.GetField(Enum.GetName(typeFromHandle, audioEventType)); EventNameAttribute customAttribute = field.GetCustomAttribute<EventNameAttribute>(); if (customAttribute != null) { array2[i] = customAttribute.name; } else { array2[i] = string.Empty; } Log.Trace($"Associated {audioEventType} as {array2[i]}."); continue; } Log.Trace($"Found all of the types ({audioEventType} is not a defined value)"); break; } Array.Resize(ref array, num + 1); Array.Resize(ref array2, num + 1); MAX_FLAG = num; ALL_FLAGS = new ReadOnlyReferenceSpan<AudioEventType>(array); ALL_FLAGS_EVENT_NAMES = new ReadOnlyReferenceSpan<string>(array2); _nameLookup = new Dictionary<AudioEventType, string>(); _valueLookup = new Dictionary<string, AudioEventType>(); for (int j = 0; j <= num; j++) { _nameLookup[ALL_FLAGS[j]] = ALL_FLAGS_EVENT_NAMES[j]; _valueLookup[ALL_FLAGS_EVENT_NAMES[j]] = ALL_FLAGS[j]; } } public static bool TryGetNameFromSingleFlag(AudioEventType flag, out string? result) { if ((flag & (flag - 1)) != 0) { throw new ArgumentException($"There is more than one flag set ({flag:G})."); } if (flag > ALL_FLAGS[ALL_FLAGS.Length - 1]) { result = null; return false; } return _nameLookup.TryGetValue(flag, out result); } public static bool TryGetFromName(string name, out AudioEventType result) { return _valueLookup.TryGetValue(name, out result); } } public enum AudioMixerTarget { [InspectorName("Master")] Master, [InspectorName("Gunshot")] Gunshot, [InspectorName("Music")] Music, [InspectorName("Other SFX")] SFX } public static class AudioMixerTargetExt { public static AudioMixerGroup GetMixerForTarget(this AudioMixerTarget target) { return (AudioMixerGroup)(target switch { AudioMixerTarget.Gunshot => Audio.GunshotMixer, AudioMixerTarget.Music => Audio.MusicMixer, AudioMixerTarget.SFX => Audio.SFXMixer, _ => Audio.MasterMixer, }); } } public enum AudioPlayType { [InspectorName("Declare a custom delegate...")] Custom, [InspectorName("Choose One Randomly")] Random, [InspectorName("Play All At Once")] AllTogether } public readonly ref struct AvatarAndPrefabPair { public readonly Avatar prefab; public readonly Avatar clone; public AvatarAndPrefabPair(Avatar prefab, Avatar clone) { if (!prefab.IsPrefabAvatar()) { throw new ArgumentException("The provided prefab was not actually a prefab; it seems to be a runtime instance.", "prefab"); } if (clone.IsPrefabAvatar()) { throw new ArgumentException("The provided clone was actually a prefab; it seems to be a stored crate.", "clone"); } this.prefab = prefab; this.clone = clone; } public AvatarAndPrefabPair(Avatar clone) { if (clone.IsPrefabAvatar()) { throw new ArgumentException("The provided clone was actually a prefab; it seems to be a stored crate.", "clone"); } Avatar originalPrefab = clone.GetOriginalPrefab(); if ((Object)(object)originalPrefab == (Object)null) { throw new NullReferenceException("Failed to resolve the original prefab from the provided clone avatar " + ((Object)clone).name); } prefab = originalPrefab; this.clone = clone; } } [AttributeUsage(AttributeTargets.Field)] internal sealed class InspectorNameAttribute : Attribute { public readonly string name; public InspectorNameAttribute(string name) { this.name = name; } } public sealed class ReadOnlyReferenceSpan<T> : IEnumerable<T>, IEnumerable { private readonly T[] _array; public int Length => _array.Length; public T this[int index] => _array[index]; public ReadOnlyReferenceSpan(T[] array) { _array = array; } public int IndexOf(T item) { return Array.IndexOf(_array, item); } public IEnumerator<T> GetEnumerator() { return ((IEnumerable<T>)_array).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return _array.GetEnumerator(); } } public sealed class ReadOnlyAudioEntry { private readonly Il2CppReferenceArray<AudioClip> _keepAlive; private readonly Il2CppReferenceArray<AudioSource>? _sourceKeepAlive; public string Name { get; } public bool UseCustomPlayType => PlayType == AudioPlayType.Custom; public AudioPlayType PlayType { get; } public string? CustomPlayType { get; } public AudioEventType EventType { get; } public string? CustomEventTypes { get; } public ReadOnlyHashSet<string> ResolvedCustomEvents { get; } public IReadOnlyList<AudioClip> Sounds { get; } public Vector2 PitchRange { get; } public float Volume { get; } public AudioMixerTarget Mixer { get; } = AudioMixerTarget.SFX; public SoundFlags SoundFlags { get; } = SoundFlags.FollowEmitter | SoundFlags.RealtimePitchShift; public AudioSource? OverrideTemplateAudioSource { get; } internal ReadOnlyAudioEntry(AudioEntry real) { //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) Name = real.Name; PlayType = real.PlayType; CustomPlayType = real.CustomPlayType; CustomEventTypes = real.CustomEventTypes; Sounds = new ReadOnlyCollection<AudioClip>(new List<AudioClip>(real.Sounds)); PitchRange = real.PitchRange; Volume = real.Volume; OverrideTemplateAudioSource = (Object.op_Implicit((Object)(object)OverrideTemplateAudioSource) ? Object.Instantiate<AudioSource>(OverrideTemplateAudioSource) : null); Mixer = real.Mixer; SoundFlags soundFlags = real.SoundFlags; if (soundFlags.HasFlag(SoundFlags.RealtimePitchShift)) { soundFlags |= SoundFlags.PitchShift; } SoundFlags = soundFlags; SoundBlockNameSanitizer.CreateStringAndEnumMix(CustomEventTypes ?? string.Empty, out string[] customs, out AudioEventType enums); EventType = enums | real.EventType; ResolvedCustomEvents = new ReadOnlyHashSet<string>(customs); _keepAlive = new Il2CppReferenceArray<AudioClip>(Sounds.ToArray()); if ((Object)(object)OverrideTemplateAudioSource != (Object)null) { _sourceKeepAlive = new Il2CppReferenceArray<AudioSource>((AudioSource[])(object)new AudioSource[1] { OverrideTemplateAudioSource }); } } } [Serializable] public sealed class SerializableAudioArray { public List<AudioClip> clips; public List<AudioClip> Clips { [MemberNotNull("clips")] get { return clips ?? (clips = new List<AudioClip>()); } [MemberNotNull("clips")] set { clips = value ?? new List<AudioClip>(); } } public AudioClip this[int index] { get { return Clips[index]; } set { Clips[index] = value; } } internal static void CallToStaticallyInitialize() { } public SerializableAudioArray() { clips = new List<AudioClip>(); } } [Serializable] public sealed class SerializableAudioArray2D { public SerializableAudioArray[] children; public SerializableAudioArray[] Children { [MemberNotNull("children")] get { return children ?? (children = Array.Empty<SerializableAudioArray>()); } [MemberNotNull("children")] set { children = value ?? Array.Empty<SerializableAudioArray>(); } } public SerializableAudioArray this[int index] { get { return Children[index]; } set { Children[index] = value; } } internal static void CallToStaticallyInitialize() { } public SerializableAudioArray2D() { children = Array.Empty<SerializableAudioArray>(); } } [Flags] public enum SoundFlags { None = 0, FollowEmitter = 1, PitchShift = 2, RealtimePitchShift = 4 } } namespace AvatarStatExtender.BuiltInEvents { internal static class SoundBroadcastMarshaller { [CompilerGenerated] private static class <>O { public static DamageReceptionDelegate <0>__OnDamageTaken; public static Action<Avatar> <1>__AfterSwitchingAvatar; } internal static void Initialize() { //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_0027: Expected O, but got Unknown Log.Info("Initializing the sound broadcast marshaller."); object obj = <>O.<0>__OnDamageTaken; if (obj == null) { DamageReceptionDelegate val = OnDamageTaken; <>O.<0>__OnDamageTaken = val; obj = (object)val; } DamageReceptionHelper.OnDamageTaken += (DamageReceptionDelegate)obj; Hooking.OnSwitchAvatarPostfix += AfterSwitchingAvatar; } private static void OnDamageTaken(Player_Health health, ref bool attackInfoAndPartAvailable, in ImmutableAttackInfo originalAttack, ref AttackInfo attack, in BodyPart originalPart, ref BodyPart part, EventPhase phase) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) if ((int)phase != 1) { return; } Log.Trace("Damage event phase, after."); if (!(((AttackInfo)(ref attack)).Damage > 0f)) { return; } Avatar avatar = health.GetAvatar(); if ((Object)(object)avatar == (Object)null) { Log.Error("Player damage was reported however there was no known victim (the health object does not belong to an avatar?) Name: " + ((Object)health).name); return; } if (((Enum)((AttackInfo)(ref attack)).DamageType).HasFlag((Enum)(object)(AttackType)1)) { Log.Trace("Broadcasting hit bullet impact sound to " + ((Object)avatar).name + "."); SoundAPI.BroadcastBuiltInSoundEvent(AudioEventType.HitBullet, avatar); } else { Log.Trace("Broadcasting hit non-bullet impact sound to " + ((Object)avatar).name + "."); SoundAPI.BroadcastBuiltInSoundEvent(AudioEventType.HitBlunt, avatar); } Log.Trace("Broadcasting general hit sound to " + ((Object)avatar).name + "."); SoundAPI.BroadcastBuiltInSoundEvent(AudioEventType.HitAny, avatar); } private static void AfterSwitchingAvatar(Avatar avatar) { if (!((Object)(object)avatar == (Object)null) && !((Object)(object)avatar.GetRigManager() == (Object)null) && !avatar.IsPrefabAvatar()) { Log.Info("Broadcasting spawn sound to " + ((Object)avatar).name + "."); SoundAPI.BroadcastBuiltInSoundEvent(AudioEventType.Spawn, avatar); } } } } namespace AvatarStatExtender.API { public static class SoundAPI { public delegate PlayableSoundData[] AudioSelectionDelegate(Avatar avatar, string playTypeName, AudioEventType vanillaEvent, string? customEvent, ReadOnlyAudioEntry triggeredGroup); public static readonly AudioSelectionDelegate PLAYTYPE_DEFAULT_RANDOM = (Avatar _, string _, AudioEventType _, string? _, ReadOnlyAudioEntry group) => new PlayableSoundData[1] { new PlayableSoundData(group, Random.RandomRangeInt(0, group.Sounds.Count - 1), group.SoundFlags, group.Mixer.GetMixerForTarget()) }; public static readonly AudioSelectionDelegate PLAYTYPE_DEFAULT_ALL = delegate(Avatar _, string _, AudioEventType _, string? _, ReadOnlyAudioEntry group) { ReadOnlyAudioEntry group2 = group; return group2.Sounds.Select((AudioClip sound) => new PlayableSoundData(sound, group2.Volume, group2.PitchRange, group2.SoundFlags, group2.Mixer.GetMixerForTarget())).ToArray(); }; private static readonly Dictionary<string, AudioSelectionDelegate> _delegates = new Dictionary<string, AudioSelectionDelegate>(); public static IList<GameObject> BroadcastSoundEvent(string name, params Avatar[] toAvatars) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException("name"); } if (!SoundBlockNameSanitizer.IsSane(name)) { throw new ArgumentOutOfRangeException("name", "Only one name is allowed, not a list. Names must be all lowercase, and contain only alphanumeric characters, periods, dashes, or underscores. Names may also have a single colon (:) splitting the name, for namespace:sound."); } string text = name; if (AudioEventTypeExt.TryGetFromName(name, out var result)) { text = null; } List<GameObject> list = new List<GameObject>(); object obj; if (toAvatars == null || toAvatars.Length == 0 || !toAvatars.Any((Avatar avy) => (Object)(object)avy != (Object)null)) { obj = PlayerObjectExtensions.GetAllActiveAvatars(); } else { obj = toAvatars; } IEnumerable<Avatar> enumerable = (IEnumerable<Avatar>)obj; foreach (Avatar item in enumerable) { if (!((Object)(object)item == (Object)null)) { AvatarAndPrefabPair avatarAndPrefabPair = new AvatarAndPrefabPair(item); IEnumerable<ReadOnlyAudioEntry> enumerable2; if (text != null) { enumerable2 = AudioCache.GetMatchingSoundsFromAvatarSingle(avatarAndPrefabPair.prefab, text); } else { IEnumerable<ReadOnlyAudioEntry> enumerable3 = Array.Empty<ReadOnlyAudioEntry>(); enumerable2 = enumerable3; } IEnumerable<ReadOnlyAudioEntry> entries = enumerable2; IEnumerable<GameObject> collection = BroadcastEntriesOf(avatarAndPrefabPair.prefab, avatarAndPrefabPair.clone, entries, result, text); list.AddRange(collection); } } return list; } internal static IList<GameObject> BroadcastBuiltInSoundEvent(AudioEventType builtInEvents, params Avatar[] toAvatars) { if (builtInEvents != 0) { if ((builtInEvents & (builtInEvents - 1)) != 0) { throw new ArgumentOutOfRangeException("builtInEvents", "Only one flag can be set at a time."); } List<GameObject> list = new List<GameObject>(); object obj; if (toAvatars == null || toAvatars.Length == 0 || !toAvatars.Any((Avatar avy) => (Object)(object)avy != (Object)null)) { obj = PlayerObjectExtensions.GetAllActiveAvatars(); } else { obj = toAvatars; } IEnumerable<Avatar> enumerable = (IEnumerable<Avatar>)obj; foreach (Avatar item in enumerable) { if (!((Object)(object)item == (Object)null)) { AvatarAndPrefabPair avatarAndPrefabPair = new AvatarAndPrefabPair(item); IEnumerable<ReadOnlyAudioEntry> matchingSoundsFromAvatarSingle = AudioCache.GetMatchingSoundsFromAvatarSingle(avatarAndPrefabPair.prefab, builtInEvents); IEnumerable<GameObject> collection = BroadcastEntriesOf(avatarAndPrefabPair.prefab, avatarAndPrefabPair.clone, matchingSoundsFromAvatarSingle, builtInEvents, null); list.AddRange(collection); } } return list; } return (IList<GameObject>)ListTools.EmptyReadOnly<GameObject>(); } private static IEnumerable<GameObject> BroadcastEntriesOf(Avatar prefab, Avatar clone, IEnumerable<ReadOnlyAudioEntry> entries, AudioEventType vanillaSource, string? customSource) { Log.Trace($"Broadcasting sounds to {((Object)prefab).name} ({vanillaSource}, {customSource})"); foreach (ReadOnlyAudioEntry entry in entries) { PlayableSoundData[] sounds = Array.Empty<PlayableSoundData>(); if (entry.UseCustomPlayType) { string customPlayType = entry.CustomPlayType; if (!string.IsNullOrWhiteSpace(customPlayType)) { if (_delegates.TryGetValue(customPlayType, out AudioSelectionDelegate selector)) { try { sounds = selector(clone, customPlayType, vanillaSource, customSource, entry); Log.Trace($"Custom selector picked {sounds.Length} sound(s)."); } catch (Exception ex) { Exception err = ex; Log.Error("A custom sound selector for sound group " + entry.Name + " (of " + ((Object)prefab).name + ") raised an exception while picking audio."); Log.Error(err); } } else { Log.Warn("Sound group " + entry.Name + " (of " + ((Object)prefab).name + ") wanted to play a sound with a custom selector, but no such selector '" + entry.CustomPlayType + "' exists. This sound will be skipped."); } selector = null; } else { Log.Warn("Sound group " + entry.Name + " (of " + ((Object)prefab).name + ") wanted to play a sound with a custom selector, but its selector string was null or whitespace."); } } else if (entry.PlayType == AudioPlayType.Random) { Log.Trace("Choosing one random sound."); sounds = PLAYTYPE_DEFAULT_RANDOM(clone, "Internal Random Selector", vanillaSource, customSource, entry); } else if (entry.PlayType == AudioPlayType.AllTogether) { Log.Trace("Choosing all sounds."); sounds = PLAYTYPE_DEFAULT_ALL(clone, "Internal All Selector", vanillaSource, customSource, entry); } else { Log.Warn($"Sound group {entry.Name} (of {((Object)prefab).name}) wanted to play a sound with a built-in selector, but the index of the selector ({entry.PlayType:X8}) was undefined. This sound will be skipped."); } RigManager mgr = clone.GetRigManager(); if ((Object)(object)mgr == (Object)null) { Log.Error($"Avatar {clone} has no rig manager? Failed to play sound."); continue; } RealtimeSkeletonRig realHepta = mgr.realHeptaRig; if ((Object)(object)realHepta == (Object)null) { Log.Error($"Avatar {clone} has no real hepta rig? Failed to play sound."); continue; } Transform chest = ((Rig)realHepta).m_chest; if ((Object)(object)chest == (Object)null) { Log.Error($"Avatar {clone} has no chest set in its real hepta rig? Failed to play sound."); continue; } Log.Trace($"Playing {sounds.Length} sound(s)."); for (int i = 0; i < sounds.Length; i++) { PlayableSoundData sound = sounds[i]; if ((Object)(object)sound.sound == (Object)null) { Log.Error("A playable sound clip's sound was missing for entry " + entry.Name + " (on avatar " + ((Object)prefab).name + "); was it returned as null? Was it garbage collected on the il2cpp side?"); } else { ref AudioMixerGroup mixer = ref sound.mixer; if (mixer == null) { mixer = entry.Mixer.GetMixerForTarget(); } yield return SoundPlayerSystem.PlaySound(chest, sound, entry.OverrideTemplateAudioSource); } } } } public static void RegisterSoundPlayTypeSelectionHandler(string playType, AudioSelectionDelegate
Mods/XansTools.dll
Decompiled 10 months agousing System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; 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; using System.Security.Permissions; using System.Threading; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using SLZ.AI; using SLZ.Combat; using SLZ.Marrow.Data; using UnhollowerBaseLib; using UnityEngine; using XansTools; using XansTools.Data; using XansTools.Patching; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("DamageReactivity")] [assembly: ComVisible(false)] [assembly: Guid("a55e452e-2b5a-4b67-bfbc-8d84d3c461e2")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: MelonInfo(typeof(XTCore), "Xan's Tools", "1.0.0", "Xan", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: MelonPriority(1)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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; } } [EditorBrowsable(EditorBrowsableState.Never)] [AttributeUsage(AttributeTargets.All)] public sealed class IsUnmanagedAttribute : Attribute { } } namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)] public sealed class AllowNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] public sealed class MemberNotNullAttribute : Attribute { public string[] Members { get; } public MemberNotNullAttribute(string member) { Members = new string[1] { member }; } public MemberNotNullAttribute(params string[] members) { Members = members; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] public sealed class MemberNotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public string[] Members { get; } public MemberNotNullWhenAttribute(bool returnValue, string member) { ReturnValue = returnValue; Members = new string[1] { member }; } public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { ReturnValue = returnValue; Members = members; } } } namespace XansTools { public static class Log { private static Instance _log; internal static void Initialize(Instance log) { _log = log; } public static void Info(object msg) { _log.Msg(msg); } public static void Info(string msg) { _log.Msg(msg); } public static void Warn(object msg) { _log.Warning(msg); } public static void Warn(string msg) { _log.Warning(msg); } public static void Error(object msg) { _log.Error(msg); } public static void Error(string msg) { _log.Error(msg); } public static void Debug(object msg) { _log.Msg(ConsoleColor.Gray, msg); } public static void Debug(string msg) { _log.Msg(ConsoleColor.Gray, msg); } public static void Trace(object msg) { if (Prefs.TraceLogging) { _log.Msg(ConsoleColor.DarkGray, msg); } } public static void Trace(string msg) { if (Prefs.TraceLogging) { _log.Msg(ConsoleColor.DarkGray, msg); } } } public static class Prefs { private static MelonPreferences_Entry<bool> _traceLogging; public static bool TraceLogging => _traceLogging.Value; internal static void Initialize() { MelonPreferences_Category val = MelonPreferences.CreateCategory("xanstools_main"); val.DisplayName = "Main Settings"; _traceLogging = val.CreateEntry<bool>("traceLogging", true, "Trace Logging", "Trace Logging causes extremely (like, comically) specific things to be written into the console." + Environment.NewLine + "This tends to flood the console with information that is useless normally, but it is very useful for debugging.", false, false, (ValueValidator)null, (string)null); } } public class XTCore : MelonMod { public override void OnInitializeMelon() { ((MelonBase)this).OnInitializeMelon(); Prefs.Initialize(); Log.Initialize(((MelonBase)this).LoggerInstance); DamageReactionFacilitator.Patch(); } } } namespace XansTools.Patching { internal static class DamageReactionFacilitator { public delegate void PlayerDamageTakenDelegate(Player_Health @this, in ImmutableAttackInfo originalAttack, ref AttackInfo attack, in BodyPart originalPart, ref BodyPart part, EventPhase phase); public delegate void PlayerTAKEDAMAGEDelegate(Player_Health @this, in float originalAmount, ref float amount, EventPhase phase); private delegate void CommonDamageTakenDelegate(IntPtr @this, IntPtr attack, BodyPart part, IntPtr method); private delegate void CommonTAKEDAMAGEDelegate(IntPtr @this, float damage, IntPtr method); private const string METHOD_PTR_FLD_NAME_PLAYER = "NativeMethodInfoPtr_OnReceivedDamage_Public_Virtual_Void_Attack_BodyPart_0"; private const string METHOD_PTR_FLD_NAME_ENEMY = "NativeMethodInfoPtr_OnReceivedDamage_Public_Void_Attack_BodyPart_0"; private const string METHOD_PTR_FLD_NAME_PLAYER_TAKEDAMAGE = "NativeMethodInfoPtr_TAKEDAMAGE_Public_Virtual_Void_Single_0"; private static CommonDamageTakenDelegate? _originalPlayerDamageTaken; private static CommonTAKEDAMAGEDelegate? _originalPlayerTAKEDAMAGE; public static event PlayerDamageTakenDelegate? OnPlayerDamageTaken; public static event PlayerTAKEDAMAGEDelegate? OnPlayerTAKEDAMAGECalled; internal static void Patch() { Log.Info("Damage Reaction Facilitator is performing patches."); CommonDamageTakenDelegate detour = OnPlayerDamageReceived; CommonTAKEDAMAGEDelegate detour2 = PlayerTAKEDAMAGECalled; _originalPlayerDamageTaken = Utils.NativeHookAttachFrom<Player_Health, CommonDamageTakenDelegate>("NativeMethodInfoPtr_OnReceivedDamage_Public_Virtual_Void_Attack_BodyPart_0", detour); _originalPlayerTAKEDAMAGE = Utils.NativeHookAttachFrom<Player_Health, CommonTAKEDAMAGEDelegate>("NativeMethodInfoPtr_TAKEDAMAGE_Public_Virtual_Void_Single_0", detour2); } private static void PlayerTAKEDAMAGECalled(IntPtr @this, float damage, IntPtr method) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown Log.Trace("Executing TAKEDAMAGE phase: Before..."); Thread.BeginCriticalRegion(); Player_Health this2 = new Player_Health(@this); float originalAmount = damage; try { DamageReactionFacilitator.OnPlayerTAKEDAMAGECalled?.Invoke(this2, in originalAmount, ref damage, EventPhase.Before); } catch (Exception msg) { Log.Error("Failed to execute OnPlayerTAKEDAMAGECalled phase: Before!"); Log.Error(msg); } Log.Trace("Invoking original TAKEDAMAGE..."); _originalPlayerTAKEDAMAGE(@this, damage, method); Log.Trace("Executing TAKEDAMAGE phase: After..."); try { DamageReactionFacilitator.OnPlayerTAKEDAMAGECalled?.Invoke(this2, in originalAmount, ref damage, EventPhase.After); } catch (Exception msg2) { Log.Error("Failed to execute OnPlayerTAKEDAMAGECalled phase: After!"); Log.Error(msg2); } Thread.EndCriticalRegion(); } private unsafe static void OnPlayerDamageReceived(IntPtr @this, IntPtr attack, BodyPart part, IntPtr method) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) Thread.BeginCriticalRegion(); Player_Health this2 = new Player_Health(@this); AttackInfo* ptr = (AttackInfo*)(void*)attack; AttackInfo attack2 = *ptr; AttackInfo attackInfo = attack2; ImmutableAttackInfo originalAttack = System.Runtime.CompilerServices.Unsafe.Read<ImmutableAttackInfo>(&attackInfo); BodyPart originalPart = part; Log.Trace("Executing player damage phase: Before..."); try { DamageReactionFacilitator.OnPlayerDamageTaken?.Invoke(this2, in originalAttack, ref attack2, in originalPart, ref part, EventPhase.Before); } catch (Exception msg) { Log.Error("Failed to execute OnPlayerDamageTaken phase: Before!"); Log.Error(msg); } System.Runtime.CompilerServices.Unsafe.Write(ptr, attack2); Log.Trace("Executing original player damage method..."); _originalPlayerDamageTaken(@this, (IntPtr)ptr, part, method); attack2 = *ptr; Log.Trace("Executing player damage phase: After..."); try { DamageReactionFacilitator.OnPlayerDamageTaken?.Invoke(this2, in originalAttack, ref attack2, in originalPart, ref part, EventPhase.After); } catch (Exception msg2) { Log.Error("Failed to execute OnPlayerDamageTaken phase: After!"); Log.Error(msg2); } Thread.EndCriticalRegion(); } } } namespace XansTools.Data { public struct AttackInfo { private float _damage; private Vector3 _normal; private Vector3 _origin; private Vector3 _direction; private byte _backFacing; private int _orderInPool; private IntPtr _collider; private AttackType _attackType; private IntPtr _proxy; public float Damage { get { return _damage; } set { _damage = value; } } public Vector3 Normal { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return _normal; } set { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _normal = value; } } public Vector3 Direction { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return _direction; } set { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _direction = value; } } public Vector3 Origin { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return _origin; } set { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _origin = value; } } public bool IsBackFacing { get { return _backFacing == 1; } set { _backFacing = (byte)(value ? 1u : 0u); } } public int OrderInAttackPool { get { return _orderInPool; } set { _orderInPool = value; } } public Collider Collider { get { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown return new Collider(_collider); } set { _collider = ((Il2CppObjectBase)value).Pointer; } } public AttackType DamageType { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return _attackType; } set { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _attackType = value; } } public TriggerRefProxy Proxy { get { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown return new TriggerRefProxy(_proxy); } set { _proxy = ((Il2CppObjectBase)value).Pointer; } } public unsafe static AttackInfo Fix(Attack attack) { AttackInfo* ptr = (AttackInfo*)(void*)((Il2CppObjectBase)attack).Pointer; return *ptr; } public unsafe static AttackInfo From(IntPtr attack) { AttackInfo* ptr = (AttackInfo*)(void*)attack; return *ptr; } } public readonly struct ImmutableAttackInfo { private readonly float _damage; private readonly Vector3 _normal; private readonly Vector3 _origin; private readonly Vector3 _direction; private readonly byte _backFacing; private readonly int _orderInPool; private readonly IntPtr _collider; private readonly AttackType _attackType; private readonly IntPtr _proxy; public float Damage => _damage; public Vector3 Normal => _normal; public Vector3 Direction => _direction; public Vector3 Origin => _origin; public bool IsBackFacing => _backFacing == 1; public int OrderInAttackPool => _orderInPool; public Collider Collider => new Collider(_collider); public AttackType DamageType => _attackType; public TriggerRefProxy Proxy => new TriggerRefProxy(_proxy); } public enum EventPhase { Before, After } public static class MethodOfProvider { private static readonly ConditionalWeakTable<MethodInfo, MethodBase> _methods = new ConditionalWeakTable<MethodInfo, MethodBase>(); private static readonly Type[] _getMethodFromHandleParams = new Type[1] { typeof(RuntimeMethodHandle) }; private static MethodInfo? _getMethodFromHandle = null; public static MethodBase methodof<T>(T del) where T : Delegate { if ((Delegate?)del == (Delegate?)null) { throw new ArgumentNullException("del"); } MethodInfo methodInfo = del.GetMethodInfo(); if ((object)methodInfo == null) { throw new ArgumentException("The provided delegate's method was null!"); } if (_methods.TryGetValue(methodInfo, out MethodBase value)) { return value; } DynamicMethod dynamicMethod = new DynamicMethod($"<{methodInfo}>methodof", typeof(MethodBase), Array.Empty<Type>()); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); if ((object)_getMethodFromHandle == null) { _getMethodFromHandle = typeof(MethodBase).GetMethod("GetMethodFromHandle", BindingFlags.Static | BindingFlags.Public, null, _getMethodFromHandleParams, null); } iLGenerator.Emit(OpCodes.Ldtoken, methodInfo); iLGenerator.Emit(OpCodes.Call, _getMethodFromHandle); iLGenerator.Emit(OpCodes.Ret); Func<MethodBase> func = (Func<MethodBase>)dynamicMethod.CreateDelegate(typeof(Func<MethodBase>)); MethodBase methodBase = func(); _methods.Add(methodInfo, methodBase); return methodBase; } } [Obsolete("This is not yet ready and may not actually be used.")] public sealed class BlamedEvent<TArgs, TReturn> { internal delegate void UpdateArgsPackageDelegate(in TReturn retIn, ref TArgs argsPackage); public class Accessor { internal sealed class ModOwnedSubscriber : IEquatable<ModOwnedSubscriber> { public Accessor Owner { get; } public MelonMod Mod { get; } public Func<TArgs, EventPhase, TReturn> Callback { get; } public ModOwnedSubscriber(Accessor owner, MelonMod mod, Func<TArgs, EventPhase, TReturn> callback, bool unsubscribeAutomaticallyIfMelonUnloaded = true) { Owner = owner; Mod = mod; Callback = callback; if (unsubscribeAutomaticallyIfMelonUnloaded) { SetupAutoUnsubscribe(); } } internal void SetupAutoUnsubscribe() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown ((MelonEventBase<LemonAction>)(object)((MelonBase)Mod).OnUnregister).Subscribe((LemonAction)delegate { Owner.UnsubscribeAll(this); }, 0, true); } public override bool Equals(object obj) { if (obj is Accessor obj2) { return Equals(obj2); } return false; } public override int GetHashCode() { return (int)((long)((object)Mod).GetHashCode() * (long)Callback.GetHashCode()); } public bool Equals(ModOwnedSubscriber? other) { if ((object)other == null) { return false; } return Owner == other.Owner && Mod == other.Mod; } public static bool operator ==(ModOwnedSubscriber? left, ModOwnedSubscriber? right) { return left?.Equals(right) ?? false; } public static bool operator !=(ModOwnedSubscriber? left, ModOwnedSubscriber? right) { return !(left == right); } } private readonly Dictionary<Func<TArgs, EventPhase, TReturn>, (ModOwnedSubscriber?, ModOwnedSubscriber?)> _subscribersByDelegate = new Dictionary<Func<TArgs, EventPhase, TReturn>, (ModOwnedSubscriber, ModOwnedSubscriber)>(); public BlamedEvent<TArgs, TReturn> Owner { get; } public MelonMod Mod { get; } internal Accessor(BlamedEvent<TArgs, TReturn> owner, MelonMod mod) { Owner = owner; Mod = mod; } internal IEnumerable<ModOwnedSubscriber> GetSubscribers(EventPhase phase) { foreach (var subscriber in _subscribersByDelegate.Values) { ModOwnedSubscriber target = null; switch (phase) { case EventPhase.Before: (target, _) = subscriber; break; case EventPhase.After: target = subscriber.Item2; break; } if (target != null) { yield return target; } } } public void Subscribe(Func<TArgs, EventPhase, TReturn> @delegate, EventPhase phase, bool unsubscribeAutomaticallyIfMelonUnloaded = true) { if (@delegate == null) { throw new ArgumentNullException("delegate"); } if (phase != 0 && phase != EventPhase.After) { throw new ArgumentOutOfRangeException("phase", "Invalid event phase."); } if (_subscribersByDelegate.TryGetValue(@delegate, out (ModOwnedSubscriber, ModOwnedSubscriber) value)) { bool flag = false; switch (phase) { case EventPhase.Before: flag = value.Item1 != null; break; case EventPhase.After: flag = value.Item2 != null; break; } if (flag) { throw new InvalidOperationException($"Mod [{((MelonBase)Mod).ID}] tried to subscribe to event {Owner.Name} with the same delegate more than once for the same phase ({phase}). You can only subscribe any given method once for a phase."); } } ModOwnedSubscriber modOwnedSubscriber = new ModOwnedSubscriber(this, Mod, @delegate, unsubscribeAutomaticallyIfMelonUnloaded); value = default((ModOwnedSubscriber, ModOwnedSubscriber)); if (phase == EventPhase.Before) { value.Item1 = modOwnedSubscriber; } else { value.Item2 = modOwnedSubscriber; } _subscribersByDelegate[@delegate] = value; } public void Unsubscribe(Func<TArgs, EventPhase, TReturn> @delegate, EventPhase phase) { Unsubscribe(@delegate, phase, softFail: false); } private void Unsubscribe(Func<TArgs, EventPhase, TReturn> @delegate, EventPhase phase, bool softFail) { if (@delegate == null) { throw new ArgumentNullException("delegate"); } if (phase != 0 && phase != EventPhase.After) { throw new ArgumentOutOfRangeException("phase", "Invalid event phase."); } if (_subscribersByDelegate.TryGetValue(@delegate, out (ModOwnedSubscriber, ModOwnedSubscriber) value)) { if (phase == EventPhase.Before) { if (value.Item1 != null) { value.Item1 = null; } _subscribersByDelegate[@delegate] = value; } else { if (value.Item2 != null) { value.Item2 = null; } _subscribersByDelegate[@delegate] = value; } } else if (!softFail) { throw new InvalidOperationException("Mod [" + ((MelonBase)Mod).ID + "] tried to unsubscribe from event " + Owner.Name + " but the provided delegate (Method: " + (@delegate.Method?.Name ?? "<no method>") + ") was not a subscriber."); } } private void UnsubscribeAll(ModOwnedSubscriber subscriber) { Unsubscribe(subscriber.Callback, EventPhase.Before, softFail: true); Unsubscribe(subscriber.Callback, EventPhase.After, softFail: true); } } private readonly Dictionary<MelonMod, Accessor> _accessorCache = new Dictionary<MelonMod, Accessor>(); public string Name { get; } internal UpdateArgsPackageDelegate? UpdateArgsWithReturnValue { get; set; } internal BlamedEvent(string name) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("The name of an event must be a non-null name with at least one or more non-whitespace character(s)."); } Name = name; } internal void InvokeAll(ref TArgs argsPackage, in EventPhase phase, ref TReturn currentReturnValue) { foreach (Accessor value in _accessorCache.Values) { foreach (Accessor.ModOwnedSubscriber subscriber in value.GetSubscribers(phase)) { try { currentReturnValue = subscriber.Callback(argsPackage, phase); UpdateArgsWithReturnValue?.Invoke(in currentReturnValue, ref argsPackage); } catch (Exception msg) { try { ((MelonBase)subscriber.Mod).LoggerInstance.Error("This error message has been sent by XansTools so that the Lum log analyzer bot can detect this error as part of this mod. The real error is right below."); } catch { } Log.Error($"Mod [{((MelonBase)value.Mod).ID}]'s event callback [{subscriber.Callback} ({subscriber.Callback.Method?.Name})] has raised an exception while {Name} was firing {phase} the original method. Exception follows:"); Log.Error(msg); } } } } public Accessor GetSubscriptionService(MelonMod yourMod) { if (yourMod == null) { throw new ArgumentNullException("yourMod", "You must provide an instance of your mod for error debugging purposes."); } if (!_accessorCache.TryGetValue(yourMod, out Accessor value)) { return new Accessor(this, yourMod); } return value; } } public static class Utils { [Obsolete("Not yet available.")] public static string TryGuessMethodNameOf<TDelegate>(TDelegate del) where TDelegate : Delegate { throw new NotImplementedException("This method is not yet available."); } public unsafe static TDelegate NativeHookAttachFrom<TMethodOwner, TDelegate>(string ptrFieldName, TDelegate detour) where TDelegate : Delegate { if ((Delegate?)detour == (Delegate?)null) { throw new ArgumentNullException("detour"); } if (detour.Method == null) { throw new InvalidOperationException("Parameter 'detour' does not have a Method?"); } ParameterInfo[] parameters = detour.Method.GetParameters(); if (parameters.Any((ParameterInfo param) => !param.ParameterType.IsValueType)) { throw new InvalidOperationException("For patch methods, all parameters should be value types. To receive an object type, instead receive IntPtr and then create a pointer to that object."); } IntPtr ptr = *(IntPtr*)(void*)(IntPtr)typeof(TMethodOwner).GetField(ptrFieldName, BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); IntPtr functionPointer = detour.Method.MethodHandle.GetFunctionPointer(); MelonUtils.NativeHookAttach((IntPtr)(&ptr), functionPointer); return Marshal.GetDelegateForFunctionPointer<TDelegate>(ptr); } } } namespace XansTools.AvatarInteroperability { public static class DamageReceptionHelper { public delegate void DamageReceptionDelegate(Player_Health health, ref bool attackInfoAndPartAvailable, in ImmutableAttackInfo originalAttack, ref AttackInfo attack, in BodyPart originalPart, ref BodyPart part, EventPhase phase); private static bool _isConnectedToEvents = false; private static readonly Stack<Player_Health> _isAboutToExecuteTAKEDAMAGEFromRecv = new Stack<Player_Health>(); private static readonly HashSet<DamageReceptionDelegate> _delegates = new HashSet<DamageReceptionDelegate>(); public static event DamageReceptionDelegate OnDamageTaken { add { if (value == null) { throw new ArgumentNullException("value"); } if (!_delegates.Add(value)) { throw new InvalidOperationException($"Delegate {value} is already subscribed; cannot subscribe it."); } if (!_isConnectedToEvents) { _isConnectedToEvents = true; DamageReactionFacilitator.OnPlayerDamageTaken += OnContextualDamageTaken; DamageReactionFacilitator.OnPlayerTAKEDAMAGECalled += OnAmbiguousDamageTaken; } } remove { if (value == null) { throw new ArgumentNullException("value"); } if (!_delegates.Remove(value)) { throw new InvalidOperationException($"Delegate {value} was not subscribed; cannot unsubscribe it."); } if (_isConnectedToEvents && _delegates.Count == 0) { _isConnectedToEvents = false; DamageReactionFacilitator.OnPlayerDamageTaken -= OnContextualDamageTaken; DamageReactionFacilitator.OnPlayerTAKEDAMAGECalled -= OnAmbiguousDamageTaken; } } } private static void ExecuteAllDelegates(Player_Health health, bool attackInfoAndPartAvailable, in ImmutableAttackInfo originalAttack, ref AttackInfo attack, in BodyPart originalPart, ref BodyPart part, EventPhase phase) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected I4, but got Unknown //IL_0025: Unknown result type (might be due to invalid IL or missing references) foreach (DamageReceptionDelegate @delegate in _delegates) { AttackInfo attackInfo = attack; BodyPart val = part; try { @delegate(health, ref attackInfoAndPartAvailable, in originalAttack, ref attack, in originalPart, ref part, phase); } catch (Exception msg) { part = (BodyPart)(int)val; attack = attackInfo; MelonAssembly melonAssemblyOfMember = MelonAssembly.GetMelonAssemblyOfMember((MemberInfo)@delegate.GetMethodInfo(), (object)null); if (melonAssemblyOfMember != null) { Log.Error(string.Format("[Event: {0}] Delegate {1}, which is included in [{2}], raised an exception during execution.", "OnDamageTaken", @delegate, melonAssemblyOfMember.Assembly)); } else { Log.Error(string.Format("[Event: {0}] Delegate {1} raised an exception during execution. This method is not part of a Melon, however, so your guess as to who did it is as good as mine.", "OnDamageTaken", @delegate)); } Log.Error(msg); } } } private static void OnContextualDamageTaken(Player_Health @this, in ImmutableAttackInfo originalAttack, ref AttackInfo attack, in BodyPart originalPart, ref BodyPart part, EventPhase phase) { if (phase == EventPhase.After) { if (_isAboutToExecuteTAKEDAMAGEFromRecv.Count > 0 && (Object)(object)_isAboutToExecuteTAKEDAMAGEFromRecv.Peek() == (Object)(object)@this) { _isAboutToExecuteTAKEDAMAGEFromRecv.Pop(); } } else if (_isAboutToExecuteTAKEDAMAGEFromRecv.Count > 0 && (Object)(object)_isAboutToExecuteTAKEDAMAGEFromRecv.Peek() == (Object)null) { Log.Error("DANGER: The stack to prevent damage function re-entry contained a destroyed Player_Health instance. This should be impossible (unless someone destroyed it while taking damage?) and might cause serious bugs / failures in this event."); _isAboutToExecuteTAKEDAMAGEFromRecv.Pop(); } ExecuteAllDelegates(@this, attackInfoAndPartAvailable: true, in originalAttack, ref attack, in originalPart, ref part, phase); if (phase == EventPhase.Before) { _isAboutToExecuteTAKEDAMAGEFromRecv.Push(@this); } } private static void OnAmbiguousDamageTaken(Player_Health @this, in float originalAmount, ref float amount, EventPhase phase) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (_isAboutToExecuteTAKEDAMAGEFromRecv.Count <= 0 || !((Object)(object)_isAboutToExecuteTAKEDAMAGEFromRecv.Peek() == (Object)(object)@this)) { AttackInfo attack = default(AttackInfo); BodyPart part = (BodyPart)0; ImmutableAttackInfo originalAttack = default(ImmutableAttackInfo); BodyPart originalPart = (BodyPart)0; ExecuteAllDelegates(@this, attackInfoAndPartAvailable: false, in originalAttack, ref attack, in originalPart, ref part, phase); } } } }