Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ShrunkenSurvivor v1.1.0
ShrunkenSurvivor.dll
Decompiled 20 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HG.BlendableTypes; using Microsoft.CodeAnalysis; using On.RoR2; using On.RoR2.Projectile; using RoR2; using RoR2.Projectile; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ShrunkenSurvivor")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("ShrunkenSurvivor")] [assembly: AssemblyTitle("ShrunkenSurvivor")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ShrunkenSurvivor { [BepInPlugin("dileppy.shrunkensurvivor", "ShrunkenSurvivor", "1.1.0")] public class ShrunkenSurvivorPlugin : BaseUnityPlugin { public enum SizeTier { Small, ExtraSmall, ExtremelySmall } private struct TierValues { public float size; public float move; public float jump; public float damage; public float attackSpeed; public TierValues(float s, float m, float j, float d, float a) { size = s; move = m; jump = j; damage = d; attackSpeed = a; } } private class ShrunkenMarker : MonoBehaviour { public Vector3 baseScale; } public const string PluginGUID = "dileppy.shrunkensurvivor"; public const string PluginName = "ShrunkenSurvivor"; public const string PluginVersion = "1.1.0"; private const string BiggerBulletsGUID = "dileppy.biggerbullets"; private static bool biggerBulletsPresent; private static readonly Dictionary<SizeTier, TierValues> Defaults = new Dictionary<SizeTier, TierValues> { { SizeTier.Small, new TierValues(0.7f, 0.85f, 0.85f, 0.75f, 1.25f) }, { SizeTier.ExtraSmall, new TierValues(0.5f, 0.7f, 0.7f, 0.55f, 1.6f) }, { SizeTier.ExtremelySmall, new TierValues(0.3f, 0.55f, 0.55f, 0.4f, 2f) } }; public static ConfigEntry<SizeTier> Tier; public static ConfigEntry<bool> ShrinkProjectiles; public static ConfigEntry<bool> UseCustomValues; public static ConfigEntry<float> CustomSize; public static ConfigEntry<float> CustomMove; public static ConfigEntry<float> CustomJump; public static ConfigEntry<float> CustomDamage; public static ConfigEntry<float> CustomAttackSpeed; public static ConfigEntry<bool> DebugLogging; public static ConfigEntry<bool> GiantWeapon; public static ConfigEntry<float> WeaponScale; public static ConfigEntry<float> CameraPullIn; public static ConfigEntry<bool> BonusRewards; public static ConfigEntry<bool> CompensateEvasion; public static ConfigEntry<bool> SurvivorSounds; public static ConfigEntry<float> SoundVolume; private static readonly Dictionary<SizeTier, float> RewardBonus = new Dictionary<SizeTier, float> { { SizeTier.Small, 1.15f }, { SizeTier.ExtraSmall, 1.35f }, { SizeTier.ExtremelySmall, 1.6f } }; private static readonly Dictionary<SizeTier, float> EvasionHealthComp = new Dictionary<SizeTier, float> { { SizeTier.Small, 0.9f }, { SizeTier.ExtraSmall, 0.8f }, { SizeTier.ExtremelySmall, 0.7f } }; private void Awake() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Expected O, but got Unknown //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Expected O, but got Unknown //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Expected O, but got Unknown //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Expected O, but got Unknown //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Expected O, but got Unknown //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Expected O, but got Unknown //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Expected O, but got Unknown //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Expected O, but got Unknown //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Expected O, but got Unknown //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Expected O, but got Unknown Tier = ((BaseUnityPlugin)this).Config.Bind<SizeTier>("General", "SizeTier", SizeTier.Small, "How shrunken you are. Small = 70% size, ExtraSmall = 50%, ExtremelySmall = 30%. Each tier scales the challenge: smaller = slower, weaker jumps, much less damage, but a faster fire rate to partially compensate."); ShrinkProjectiles = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShrinkProjectiles", true, "Also physically shrink your projectiles to match your size."); UseCustomValues = ((BaseUnityPlugin)this).Config.Bind<bool>("Custom", "UseCustomValues", false, "Ignore the tier presets and use the Custom* values below instead."); CustomSize = ((BaseUnityPlugin)this).Config.Bind<float>("Custom", "CustomSize", 0.5f, new ConfigDescription("Model size multiplier", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>())); CustomMove = ((BaseUnityPlugin)this).Config.Bind<float>("Custom", "CustomMove", 0.7f, new ConfigDescription("Move speed multiplier", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>())); CustomJump = ((BaseUnityPlugin)this).Config.Bind<float>("Custom", "CustomJump", 0.7f, new ConfigDescription("Jump power multiplier", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>())); CustomDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Custom", "CustomDamage", 0.55f, new ConfigDescription("Damage multiplier", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>())); CustomAttackSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Custom", "CustomAttackSpeed", 1.6f, new ConfigDescription("Attack speed multiplier", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>())); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "DebugLogging", false, "Log applied multipliers to the console for verification."); GiantWeapon = ((BaseUnityPlugin)this).Config.Bind<bool>("Fun", "GiantWeapon", true, "Scale the survivor's weapon UP relative to their shrunken body, so your tiny character lugs a comically oversized gun. Works best on Commando/Bandit/Railgunner-style survivors; best-effort on others."); WeaponScale = ((BaseUnityPlugin)this).Config.Bind<float>("Fun", "WeaponScale", 1.6f, new ConfigDescription("How oversized the weapon is relative to the shrunken body.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), Array.Empty<object>())); CameraPullIn = ((BaseUnityPlugin)this).Config.Bind<float>("Fun", "CameraPullIn", 0.6f, new ConfigDescription("How much the camera moves down toward your tiny survivor (0 = normal camera, 1 = fully scaled to your size). Makes enemies TOWER over you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); BonusRewards = ((BaseUnityPlugin)this).Config.Bind<bool>("Challenge", "BonusRewards", true, "Earn bonus gold and XP for playing shrunken: +15% (Small), +35% (ExtraSmall), +60% (ExtremelySmall). Risk, meet reward."); CompensateEvasion = ((BaseUnityPlugin)this).Config.Bind<bool>("Challenge", "CompensateEvasion", false, "Being tiny makes enemies miss you more. Enable this to trim max HP per tier (-10%/-20%/-30%) so the challenge stays honest."); SurvivorSounds = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "SurvivorSounds", true, "Play persona-matched effort sounds when your tiny survivor jumps, lands, and takes hits. Each survivor has a sound personality (soldier grunts, agile breaths, machine servos, beast growls, arcane hums)."); SoundVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "SoundVolume", 0.6f, new ConfigDescription("Volume of the survivor sounds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); CharacterBody.Start += new hook_Start(CharacterBody_Start); CharacterBody.RecalculateStats += new hook_RecalculateStats(CharacterBody_RecalculateStats); ProjectileController.Start += new hook_Start(ProjectileController_Start); DeathRewards.OnKilledServer += new hook_OnKilledServer(DeathRewards_OnKilledServer); HealthComponent.TakeDamage += new hook_TakeDamage(HealthComponent_TakeDamage); SoundBank.Load(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), ((BaseUnityPlugin)this).Logger); biggerBulletsPresent = Chainloader.PluginInfos.ContainsKey("dileppy.biggerbullets"); if (biggerBulletsPresent) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"ShrunkenSurvivor: BiggerBullets detected - deferring all bullet sizing to it. Tune bullet size in BiggerBullets' config for the full tiny-survivor-giant-bullets experience."); } ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} v{1} loaded. Tier={2}", "ShrunkenSurvivor", "1.1.0", Tier.Value)); } private void OnDestroy() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown CharacterBody.Start -= new hook_Start(CharacterBody_Start); CharacterBody.RecalculateStats -= new hook_RecalculateStats(CharacterBody_RecalculateStats); ProjectileController.Start -= new hook_Start(ProjectileController_Start); } private static TierValues Current() { if (UseCustomValues.Value) { return new TierValues(CustomSize.Value, CustomMove.Value, CustomJump.Value, CustomDamage.Value, CustomAttackSpeed.Value); } return Defaults[Tier.Value]; } private static bool IsPlayer(CharacterBody body) { if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.master)) { return Object.op_Implicit((Object)(object)body.master.playerCharacterMasterController); } return false; } private static void ApplyAbsoluteScale(GameObject go, float factor) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)go)) { return; } Scene scene = go.scene; if (((Scene)(ref scene)).IsValid()) { ShrunkenMarker shrunkenMarker = go.GetComponent<ShrunkenMarker>(); if (!Object.op_Implicit((Object)(object)shrunkenMarker)) { shrunkenMarker = go.AddComponent<ShrunkenMarker>(); shrunkenMarker.baseScale = go.transform.localScale; } go.transform.localScale = shrunkenMarker.baseScale * factor; } } private void CharacterBody_Start(orig_Start orig, CharacterBody self) { //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) orig.Invoke(self); if (!IsPlayer(self)) { return; } try { TierValues tierValues = Current(); ModelLocator modelLocator = self.modelLocator; if (Object.op_Implicit((Object)(object)modelLocator) && Object.op_Implicit((Object)(object)modelLocator.modelTransform)) { ApplyAbsoluteScale(((Component)modelLocator.modelTransform).gameObject, tierValues.size); if (GiantWeapon.Value && WeaponScale.Value > 1.001f) { string[] array = new string[15] { "gun", "weapon", "pistol", "rifle", "musket", "launcher", "crossbow", "bow", "sword", "blade", "hammer", "cleaver", "knife", "railgun", "shotgun" }; Transform[] componentsInChildren = ((Component)modelLocator.modelTransform).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { string text = ((Object)val).name.ToLowerInvariant(); string[] array2 = array; foreach (string value in array2) { if (text.Contains(value)) { ApplyAbsoluteScale(((Component)val).gameObject, WeaponScale.Value); if (DebugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ShrunkenSurvivor: giant-weapon scaled bone '" + ((Object)val).name + "'")); } break; } } } } if (DebugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"ShrunkenSurvivor: shrank {self.GetDisplayName()} model to {tierValues.size:P0}"); } } if (CameraPullIn.Value > 0.001f) { CameraTargetParams component = ((Component)self).GetComponent<CameraTargetParams>(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.cameraParams)) { float num = Mathf.Lerp(1f, tierValues.size, CameraPullIn.Value); CharacterCameraParams val2 = Object.Instantiate<CharacterCameraParams>(component.cameraParams); val2.data.idealLocalCameraPos = BlendableVector3.op_Implicit(component.cameraParams.data.idealLocalCameraPos.value * num); val2.data.pivotVerticalOffset = BlendableFloat.op_Implicit(component.cameraParams.data.pivotVerticalOffset.value * num); component.cameraParams = val2; if (DebugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"ShrunkenSurvivor: camera pulled in to {num:P0}"); } } } SurvivorAudio.Attach(self, ((BaseUnityPlugin)this).Logger); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"ShrunkenSurvivor: failed to shrink model: {arg}"); } } private void CharacterBody_RecalculateStats(orig_RecalculateStats orig, CharacterBody self) { orig.Invoke(self); if (IsPlayer(self)) { TierValues tierValues = Current(); self.moveSpeed *= tierValues.move; self.jumpPower *= tierValues.jump; self.damage *= tierValues.damage; self.attackSpeed *= tierValues.attackSpeed; if (CompensateEvasion.Value && !UseCustomValues.Value) { self.maxHealth *= EvasionHealthComp[Tier.Value]; } } } private void DeathRewards_OnKilledServer(orig_OnKilledServer orig, DeathRewards self, DamageReport damageReport) { if (BonusRewards.Value && !UseCustomValues.Value && damageReport != null && Object.op_Implicit((Object)(object)damageReport.attackerBody) && IsPlayer(damageReport.attackerBody)) { float num = RewardBonus[Tier.Value]; self.goldReward = (uint)((float)self.goldReward * num); self.expReward = (uint)((float)self.expReward * num); } orig.Invoke(self, damageReport); } private void HealthComponent_TakeDamage(orig_TakeDamage orig, HealthComponent self, DamageInfo damageInfo) { orig.Invoke(self, damageInfo); if (SurvivorSounds.Value && damageInfo != null && damageInfo.damage > 0f && Object.op_Implicit((Object)(object)self.body) && IsPlayer(self.body)) { SurvivorAudio component = ((Component)self.body).GetComponent<SurvivorAudio>(); if (Object.op_Implicit((Object)(object)component)) { component.PlayHurt(); } } } private void ProjectileController_Start(orig_Start orig, ProjectileController self) { orig.Invoke(self); if (!ShrinkProjectiles.Value || biggerBulletsPresent || !Object.op_Implicit((Object)(object)self.owner)) { return; } CharacterBody component = self.owner.GetComponent<CharacterBody>(); if (!Object.op_Implicit((Object)(object)component) || !IsPlayer(component)) { return; } try { TierValues tierValues = Current(); ApplyAbsoluteScale(((Component)self).gameObject, tierValues.size); if (Object.op_Implicit((Object)(object)self.ghost)) { ApplyAbsoluteScale(((Component)self.ghost).gameObject, tierValues.size); } if (DebugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"ShrunkenSurvivor: shrank projectile {((Object)((Component)self).gameObject).name} to {tierValues.size:P0}"); } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"ShrunkenSurvivor: failed to shrink projectile: {arg}"); } } } internal static class SoundBank { private static readonly Dictionary<string, List<AudioClip>> clips = new Dictionary<string, List<AudioClip>>(); private static readonly (string match, string persona, float pitch)[] Personas = new(string, string, float)[19] { ("CommandoBody", "grunt", 1f), ("Bandit2Body", "grunt", 0.92f), ("EngiBody", "grunt", 0.95f), ("LoaderBody", "grunt", 0.78f), ("CaptainBody", "grunt", 0.85f), ("OperatorBody", "grunt", 1.05f), ("HuntressBody", "light", 1.2f), ("MercBody", "light", 1.05f), ("RailgunnerBody", "light", 1.12f), ("SeekerBody", "light", 1.25f), ("ToolbotBody", "robot", 0.9f), ("TreebotBody", "robot", 0.75f), ("ChefBody", "robot", 1.15f), ("CrocoBody", "beast", 1f), ("VoidSurvivorBody", "beast", 0.65f), ("ChirrBody", "beast", 1.4f), ("MageBody", "arcane", 1.15f), ("HereticBody", "arcane", 0.85f), ("FalseSonBody", "arcane", 0.6f) }; public static bool Loaded { get; private set; } public static (string persona, float pitch) PersonaFor(string bodyName) { (string, string, float)[] personas = Personas; for (int i = 0; i < personas.Length; i++) { (string, string, float) tuple = personas[i]; if (bodyName.StartsWith(tuple.Item1, StringComparison.OrdinalIgnoreCase)) { return (persona: tuple.Item2, pitch: tuple.Item3); } } return (persona: "grunt", pitch: 1f); } public static void Load(string pluginDir, ManualLogSource log) { try { string path = Path.Combine(pluginDir, "sounds"); if (!Directory.Exists(path)) { log.LogWarning((object)"ShrunkenSurvivor: sounds folder not found - survivor sounds disabled."); return; } string[] files = Directory.GetFiles(path, "*.wav"); foreach (string path2 in files) { AudioClip val = LoadWav(path2); if (!((Object)(object)val == (Object)null)) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path2); int num = fileNameWithoutExtension.LastIndexOf('_'); string key = ((num > 0) ? fileNameWithoutExtension.Substring(0, num) : fileNameWithoutExtension); if (!clips.TryGetValue(key, out var value)) { value = new List<AudioClip>(); clips[key] = value; } value.Add(val); } } Loaded = clips.Count > 0; log.LogInfo((object)$"ShrunkenSurvivor: loaded {clips.Count} sound groups."); } catch (Exception arg) { log.LogError((object)$"ShrunkenSurvivor: sound load failed: {arg}"); } } public static AudioClip Pick(string persona, string evt) { if (clips.TryGetValue(persona + "_" + evt, out var value) && value.Count > 0) { return value[Random.Range(0, value.Count)]; } return null; } private static AudioClip LoadWav(string path) { byte[] array = File.ReadAllBytes(path); if (array.Length < 44) { return null; } int num = BitConverter.ToInt16(array, 22); int num2 = BitConverter.ToInt32(array, 24); int num3; for (int i = 12; i + 8 < array.Length; i += 8 + num3) { string text = Encoding.ASCII.GetString(array, i, 4); num3 = BitConverter.ToInt32(array, i + 4); if (text == "data") { int num4 = num3 / 2; float[] array2 = new float[num4]; for (int j = 0; j < num4; j++) { array2[j] = (float)BitConverter.ToInt16(array, i + 8 + j * 2) / 32768f; } AudioClip obj = AudioClip.Create(Path.GetFileNameWithoutExtension(path), num4 / num, num, num2, false); obj.SetData(array2, 0); return obj; } } return null; } } internal class SurvivorAudio : MonoBehaviour { private CharacterBody body; private AudioSource source; private string persona; private float basePitch; private bool wasGrounded = true; private float lastHurt; public static void Attach(CharacterBody b, ManualLogSource log) { if (ShrunkenSurvivorPlugin.SurvivorSounds.Value && SoundBank.Loaded && !Object.op_Implicit((Object)(object)((Component)b).GetComponent<SurvivorAudio>())) { SurvivorAudio survivorAudio = ((Component)b).gameObject.AddComponent<SurvivorAudio>(); survivorAudio.body = b; SurvivorAudio survivorAudio2 = survivorAudio; (string, float) tuple = SoundBank.PersonaFor(((Object)b).name); survivorAudio.persona = tuple.Item1; survivorAudio2.basePitch = tuple.Item2; survivorAudio.source = ((Component)b).gameObject.AddComponent<AudioSource>(); survivorAudio.source.spatialBlend = 0f; survivorAudio.source.playOnAwake = false; log.LogInfo((object)$"ShrunkenSurvivor: audio persona '{survivorAudio.persona}' (pitch {survivorAudio.basePitch:F2}) attached to {((Object)b).name}"); } } private void Play(string evt, float volumeScale = 1f) { AudioClip val = SoundBank.Pick(persona, evt); if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)source)) { source.pitch = basePitch * Random.Range(0.93f, 1.07f); source.PlayOneShot(val, ShrunkenSurvivorPlugin.SoundVolume.Value * volumeScale); } } public void PlayHurt() { if (!(Time.time - lastHurt < 0.6f)) { lastHurt = Time.time; Play("hurt"); } } private void Update() { if (Object.op_Implicit((Object)(object)body) && !((Object)(object)body.characterMotor == (Object)null)) { bool isGrounded = body.characterMotor.isGrounded; if (wasGrounded && !isGrounded && body.characterMotor.velocity.y > 1f) { Play("jump"); } else if (!wasGrounded && isGrounded) { Play("land", 0.8f); } wasGrounded = isGrounded; } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "ShrunkenSurvivor"; public const string PLUGIN_NAME = "ShrunkenSurvivor"; public const string PLUGIN_VERSION = "1.0.1"; } }