using 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 Defaults = new Dictionary { { 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 Tier; public static ConfigEntry ShrinkProjectiles; public static ConfigEntry UseCustomValues; public static ConfigEntry CustomSize; public static ConfigEntry CustomMove; public static ConfigEntry CustomJump; public static ConfigEntry CustomDamage; public static ConfigEntry CustomAttackSpeed; public static ConfigEntry DebugLogging; public static ConfigEntry GiantWeapon; public static ConfigEntry WeaponScale; public static ConfigEntry CameraPullIn; public static ConfigEntry BonusRewards; public static ConfigEntry CompensateEvasion; public static ConfigEntry SurvivorSounds; public static ConfigEntry SoundVolume; private static readonly Dictionary RewardBonus = new Dictionary { { SizeTier.Small, 1.15f }, { SizeTier.ExtraSmall, 1.35f }, { SizeTier.ExtremelySmall, 1.6f } }; private static readonly Dictionary EvasionHealthComp = new Dictionary { { 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("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("General", "ShrinkProjectiles", true, "Also physically shrink your projectiles to match your size."); UseCustomValues = ((BaseUnityPlugin)this).Config.Bind("Custom", "UseCustomValues", false, "Ignore the tier presets and use the Custom* values below instead."); CustomSize = ((BaseUnityPlugin)this).Config.Bind("Custom", "CustomSize", 0.5f, new ConfigDescription("Model size multiplier", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 1f), Array.Empty())); CustomMove = ((BaseUnityPlugin)this).Config.Bind("Custom", "CustomMove", 0.7f, new ConfigDescription("Move speed multiplier", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 1f), Array.Empty())); CustomJump = ((BaseUnityPlugin)this).Config.Bind("Custom", "CustomJump", 0.7f, new ConfigDescription("Jump power multiplier", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 1f), Array.Empty())); CustomDamage = ((BaseUnityPlugin)this).Config.Bind("Custom", "CustomDamage", 0.55f, new ConfigDescription("Damage multiplier", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 1f), Array.Empty())); CustomAttackSpeed = ((BaseUnityPlugin)this).Config.Bind("Custom", "CustomAttackSpeed", 1.6f, new ConfigDescription("Attack speed multiplier", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 5f), Array.Empty())); DebugLogging = ((BaseUnityPlugin)this).Config.Bind("Advanced", "DebugLogging", false, "Log applied multipliers to the console for verification."); GiantWeapon = ((BaseUnityPlugin)this).Config.Bind("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("Fun", "WeaponScale", 1.6f, new ConfigDescription("How oversized the weapon is relative to the shrunken body.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 3f), Array.Empty())); CameraPullIn = ((BaseUnityPlugin)this).Config.Bind("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(0f, 1f), Array.Empty())); BonusRewards = ((BaseUnityPlugin)this).Config.Bind("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("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("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("Audio", "SoundVolume", 0.6f, new ConfigDescription("Volume of the survivor sounds.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); 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(); if (!Object.op_Implicit((Object)(object)shrunkenMarker)) { shrunkenMarker = go.AddComponent(); 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(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(); 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(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(); 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(); 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> clips = new Dictionary>(); 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(); 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 = ((Component)b).gameObject.AddComponent(); 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(); 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"; } }