using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("SniperStaff")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("SniperStaff")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] namespace SniperStaff; [BepInPlugin("dots.icesniperstaff", "Ice Sniper Staff", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class SniperStaffPlugin : BaseUnityPlugin { public const string PluginGUID = "dots.icesniperstaff"; public const string WizardryGUID = "Therzie.Wizardry"; public const string PluginName = "Ice Sniper Staff"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; private readonly Harmony _harmony = new Harmony("dots.icesniperstaff"); internal const string NewPrefab = "RuneStaffSniper"; internal const string VanillaReference = "CrossbowArbalest"; private const string BundleResource = "SniperStaff.Assets.bundles.sniperstaff"; private const string BundleName = "sniperstaff"; internal const string CustomProjectile = "RuneStaffSniper_projectile"; private const string ReloadClipName = "reload_sfx"; private const string IconName = "icon_icestaff"; internal static GameObject StaffProjectile; internal static AudioClip ReloadClip; private static bool _wizardry; private static readonly string[] ProjectileCandidates = new string[3] { "RuneStaffSniper_projectile", "staff_iceshard_projectile", "staff_fireball_projectile" }; private static readonly string[] ProjectileBaseCandidates = new string[3] { "staff_iceshard_projectile", "DvergerStaffIce_projectile", "staff_fireball_projectile" }; internal static ConfigEntry Damage; internal static ConfigEntry PierceDamage; internal static ConfigEntry FrostDamage; internal static ConfigEntry PierceDamagePerLevel; internal static ConfigEntry FrostDamagePerLevel; internal static ConfigEntry Velocity; internal static ConfigEntry Accuracy; internal static ConfigEntry EitrCost; internal static ConfigEntry ReloadTime; internal static ConfigEntry ReloadVolume; internal static ConfigEntry SilenceReloadAnim; internal static ConfigEntry FlatShot; internal static ConfigEntry NoExplosion; internal static ConfigEntry ProjectileScale; internal static ConfigEntry NW_PierceDamage; internal static ConfigEntry NW_FrostDamage; internal static ConfigEntry NW_PierceDamagePerLevel; internal static ConfigEntry NW_FrostDamagePerLevel; internal static ConfigEntry NW_Velocity; internal static ConfigEntry NW_EitrCost; internal static ConfigEntry NW_ReloadTime; private static readonly Dictionary MaterialColors = new Dictionary { { "Mat_Wood", new Color(0.45f, 0.3f, 0.16f, 1f) }, { "Mat_Metal", new Color(0.55f, 0.57f, 0.6f, 1f) } }; private static readonly HashSet PreserveMaterials = new HashSet { "Mat_Crystal" }; private AssetBundle _bundle; private static bool _projectileReady; private static bool _soundsApplied; private static readonly string[] FireSfxCandidates = new string[3] { "fx_iceshard_launch", "sfx_dverger_ice_projectile_start", "sfx_icestaff_start" }; private static float EffPierce => _wizardry ? PierceDamage.Value : NW_PierceDamage.Value; private static float EffFrost => _wizardry ? FrostDamage.Value : NW_FrostDamage.Value; private static float EffPiercePerLvl => _wizardry ? PierceDamagePerLevel.Value : NW_PierceDamagePerLevel.Value; private static float EffFrostPerLvl => _wizardry ? FrostDamagePerLevel.Value : NW_FrostDamagePerLevel.Value; private static float EffEitr => _wizardry ? EitrCost.Value : NW_EitrCost.Value; private static float EffReload => _wizardry ? ReloadTime.Value : NW_ReloadTime.Value; internal static float EffVelocity => _wizardry ? Velocity.Value : NW_Velocity.Value; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; BindConfig(); PrefabManager.OnVanillaPrefabsAvailable += RegisterStaff; _harmony.PatchAll(); Log.LogInfo((object)"Ice Sniper Staff v1.0.0 loaded."); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } if ((Object)(object)_bundle != (Object)null) { _bundle.Unload(false); } } private void BindConfig() { Damage = ((BaseUnityPlugin)this).Config.Bind("Sniper", "DamageMultiplier", 1f, "Damage multiplier applied at fire time (1 = use raw values below)."); PierceDamage = ((BaseUnityPlugin)this).Config.Bind("Sniper", "PierceDamage", 70f, "Pierce damage per shot (quality 1)."); FrostDamage = ((BaseUnityPlugin)this).Config.Bind("Sniper", "FrostDamage", 170f, "Frost damage per shot (quality 1)."); PierceDamagePerLevel = ((BaseUnityPlugin)this).Config.Bind("Sniper", "PierceDamagePerLevel", 10f, "Extra pierce damage gained per upgrade level."); FrostDamagePerLevel = ((BaseUnityPlugin)this).Config.Bind("Sniper", "FrostDamagePerLevel", 15f, "Extra frost damage gained per upgrade level."); Velocity = ((BaseUnityPlugin)this).Config.Bind("Sniper", "Velocity", 240f, "Projectile speed at fire time (sniper = fast)."); Accuracy = ((BaseUnityPlugin)this).Config.Bind("Sniper", "Accuracy", 0f, "Aim spread in degrees."); EitrCost = ((BaseUnityPlugin)this).Config.Bind("Sniper", "EitrCost", 40f, "Eitr cost per shot."); ReloadTime = ((BaseUnityPlugin)this).Config.Bind("Sniper", "ReloadTime", 2f, "Crossbow-style reload/draw time in seconds."); ReloadVolume = ((BaseUnityPlugin)this).Config.Bind("Sniper", "ReloadVolume", 1f, "Reload sound volume (0 = silent, 1 = full)."); SilenceReloadAnim = ((BaseUnityPlugin)this).Config.Bind("Sniper", "SilenceReloadAnim", true, "Clear the reload animation to silence the vanilla crossbow reload sound. May also remove the visible reload animation."); FlatShot = ((BaseUnityPlugin)this).Config.Bind("Sniper", "FlatShot", true, "Remove projectile gravity for a flat line."); NoExplosion = ((BaseUnityPlugin)this).Config.Bind("Sniper", "NoExplosion", true, "Strip the AoE so it's a clean single-target dart."); ProjectileScale = ((BaseUnityPlugin)this).Config.Bind("Sniper", "ProjectileScale", 1.4f, "Visual size multiplier for the projectile (1 = vanilla)."); NW_PierceDamage = ((BaseUnityPlugin)this).Config.Bind("Sniper.NoWizardry", "PierceDamage", 95f, "Pierce damage per shot (quality 1) when Wizardry is absent."); NW_FrostDamage = ((BaseUnityPlugin)this).Config.Bind("Sniper.NoWizardry", "FrostDamage", 230f, "Frost damage per shot (quality 1) when Wizardry is absent."); NW_PierceDamagePerLevel = ((BaseUnityPlugin)this).Config.Bind("Sniper.NoWizardry", "PierceDamagePerLevel", 14f, "Extra pierce per upgrade level when Wizardry is absent."); NW_FrostDamagePerLevel = ((BaseUnityPlugin)this).Config.Bind("Sniper.NoWizardry", "FrostDamagePerLevel", 20f, "Extra frost per upgrade level when Wizardry is absent."); NW_Velocity = ((BaseUnityPlugin)this).Config.Bind("Sniper.NoWizardry", "Velocity", 300f, "Projectile speed when Wizardry is absent."); NW_EitrCost = ((BaseUnityPlugin)this).Config.Bind("Sniper.NoWizardry", "EitrCost", 40f, "Eitr cost per shot when Wizardry is absent."); NW_ReloadTime = ((BaseUnityPlugin)this).Config.Bind("Sniper.NoWizardry", "ReloadTime", 1.7f, "Reload time when Wizardry is absent (lower = faster)."); } private void RegisterStaff() { //IL_02a0: 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_02ea: Expected O, but got Unknown try { _wizardry = Chainloader.PluginInfos.ContainsKey("Therzie.Wizardry"); Log.LogInfo((object)("Wizardry detected: " + _wizardry + " (mode: " + (_wizardry ? "Wizardry baseline" : "standalone / Galdr Table, buffed") + ").")); _bundle = AssetUtils.LoadAssetBundleFromResources("sniperstaff", Assembly.GetExecutingAssembly()); if ((Object)(object)_bundle == (Object)null) { Log.LogError((object)"AssetBundle 'sniperstaff' not found as embedded resource 'SniperStaff.Assets.bundles.sniperstaff'."); return; } GameObject val = _bundle.LoadAsset("RuneStaffSniper"); if ((Object)(object)val == (Object)null) { Log.LogError((object)"Prefab 'RuneStaffSniper' not found inside bundle 'sniperstaff'."); return; } ReloadClip = _bundle.LoadAsset("reload_sfx"); if ((Object)(object)ReloadClip == (Object)null) { Log.LogError((object)"Reload clip 'reload_sfx' NOT found in bundle 'sniperstaff'. Check the AssetBundle assignment in Unity and the clip name."); } else { Log.LogInfo((object)("Reload clip 'reload_sfx' loaded, length=" + ReloadClip.length + "s.")); } Shader val2 = Shader.Find("Standard"); Log.LogDebug((object)("Game Shader.Find(\"Standard\") = " + (((Object)(object)val2 == (Object)null) ? "NULL" : (((Object)val2).name + " supported=" + val2.isSupported)))); GameObject prefab = PrefabManager.Instance.GetPrefab("CrossbowArbalest"); if ((Object)(object)prefab == (Object)null) { Log.LogError((object)"Vanilla reference 'CrossbowArbalest' not found! Check the exact prefab name in LogOutput.log."); } MeshRenderer val3 = (((Object)(object)prefab != (Object)null) ? prefab.GetComponentInChildren(true) : null); Shader val4 = (((Object)(object)val3 != (Object)null && (Object)(object)((Renderer)val3).sharedMaterial != (Object)null) ? ((Renderer)val3).sharedMaterial.shader : null); Log.LogDebug((object)("Vanilla mesh shader = " + (((Object)(object)val4 == (Object)null) ? "NULL" : (((Object)val4).name + " supported=" + val4.isSupported)))); FixMaterials(val, val2, val4); ItemConfig val5 = BuildRecipe(); ConfigureWeapon(val, prefab); Sprite val6 = _bundle.LoadAsset("icon_icestaff"); if ((Object)(object)val6 != (Object)null) { Log.LogInfo((object)"Custom icon 'icon_icestaff' loaded from bundle."); } else { Log.LogWarning((object)"Custom icon 'icon_icestaff' NOT found in bundle; rendering a fallback. Check the sprite's Texture Type = Sprite and its AssetBundle assignment in Unity."); val6 = RenderManager.Instance.Render(val, RenderManager.IsometricRotation); } if ((Object)(object)val6 != (Object)null) { val5.Icon = val6; } else { Log.LogWarning((object)"No icon available (bundle sprite missing and render returned null)."); } ItemManager.Instance.AddItem(new CustomItem(val, true, val5)); AddLocalization(); Log.LogInfo((object)"Registered RuneStaffSniper from bundle."); } catch (Exception ex) { Log.LogError((object)("RegisterStaff failed: " + ex)); } finally { PrefabManager.OnVanillaPrefabsAvailable -= RegisterStaff; } } private static ItemConfig BuildRecipe() { //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Expected O, but got Unknown //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Expected O, but got Unknown //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Expected O, but got Unknown //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_002f: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_0077: 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) //IL_0088: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_00a3: 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_00b4: 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_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Expected O, but got Unknown string craftingStation; int minStationLevel; RequirementConfig[] requirements; if (_wizardry) { craftingStation = "WizardTable_TW"; minStationLevel = 4; requirements = (RequirementConfig[])(object)new RequirementConfig[4] { new RequirementConfig { Item = "EnchantedWoodMountain_TW", Amount = 20, AmountPerLevel = 0, Recover = true }, new RequirementConfig { Item = "Staffcore_Mountain_TW", Amount = 3, AmountPerLevel = 0, Recover = true }, new RequirementConfig { Item = "Silver", Amount = 5, AmountPerLevel = 0, Recover = true }, new RequirementConfig { Item = "ArcaneScroll_Mountain_TW", Amount = 0, AmountPerLevel = 2, Recover = false } }; Log.LogDebug((object)"Recipe: Wizardry detected -> WizardTable_TW lvl 4, scroll upgrades."); } else { craftingStation = "piece_magetable"; minStationLevel = 1; requirements = (RequirementConfig[])(object)new RequirementConfig[4] { new RequirementConfig { Item = "YggdrasilWood", Amount = 10, AmountPerLevel = 5, Recover = true }, new RequirementConfig { Item = "FreezeGland", Amount = 6, AmountPerLevel = 3, Recover = true }, new RequirementConfig { Item = "Eitr", Amount = 12, AmountPerLevel = 5, Recover = true }, new RequirementConfig { Item = "Iron", Amount = 4, AmountPerLevel = 2, Recover = true } }; Log.LogWarning((object)"Recipe: Wizardry NOT installed -> Galdr Table (piece_magetable) lvl 1, frost-staff recipe."); } return new ItemConfig { Name = "$item_runestaff_sniper", Description = "$item_runestaff_sniper_desc", CraftingStation = craftingStation, MinStationLevel = minStationLevel, Requirements = requirements }; } private void ConfigureWeapon(GameObject prefab, GameObject vanilla) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: 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_01df: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) ItemDrop component = prefab.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)vanilla == (Object)null) { Log.LogWarning((object)("Could not copy weapon data: drop=" + ((Object)(object)component != (Object)null) + " vanilla=" + ((Object)(object)vanilla != (Object)null))); return; } SharedData shared = vanilla.GetComponent().m_itemData.m_shared; SharedData shared2 = component.m_itemData.m_shared; shared2.m_itemType = shared.m_itemType; shared2.m_animationState = shared.m_animationState; shared2.m_attack = shared.m_attack.Clone(); shared2.m_secondaryAttack = shared.m_secondaryAttack.Clone(); shared2.m_skillType = (SkillType)9; shared2.m_attack.m_reloadTime = EffReload; shared2.m_attack.m_reloadStaminaDrain = 0f; shared2.m_attack.m_reloadEitrDrain = 0f; Log.LogDebug((object)("Reload animation (original) = '" + shared2.m_attack.m_reloadAnimation + "'")); if (SilenceReloadAnim.Value) { shared2.m_attack.m_reloadAnimation = ""; Log.LogDebug((object)"SilenceReloadAnim: cleared m_reloadAnimation."); } DamageTypes damages = shared.m_damages; damages.m_damage = 0f; damages.m_blunt = 0f; damages.m_slash = 0f; damages.m_pierce = EffPierce; damages.m_chop = 0f; damages.m_pickaxe = 0f; damages.m_fire = 0f; damages.m_frost = EffFrost; damages.m_lightning = 0f; damages.m_poison = 0f; damages.m_spirit = 0f; shared2.m_damages = damages; DamageTypes damagesPerLevel = shared.m_damagesPerLevel; damagesPerLevel.m_damage = 0f; damagesPerLevel.m_blunt = 0f; damagesPerLevel.m_slash = 0f; damagesPerLevel.m_pierce = EffPiercePerLvl; damagesPerLevel.m_chop = 0f; damagesPerLevel.m_pickaxe = 0f; damagesPerLevel.m_fire = 0f; damagesPerLevel.m_frost = EffFrostPerLvl; damagesPerLevel.m_lightning = 0f; damagesPerLevel.m_poison = 0f; damagesPerLevel.m_spirit = 0f; shared2.m_damagesPerLevel = damagesPerLevel; shared2.m_attackForce = shared.m_attackForce; shared2.m_useDurability = shared.m_useDurability; shared2.m_maxDurability = shared.m_maxDurability; shared2.m_destroyBroken = shared.m_destroyBroken; shared2.m_equipDuration = shared.m_equipDuration; shared2.m_aiAttackRange = shared.m_aiAttackRange; shared2.m_ammoType = ""; shared2.m_attack.m_attackEitr = EffEitr; shared2.m_attack.m_attackStamina = 0f; shared2.m_attack.m_drawStaminaDrain = 0f; shared2.m_name = "$item_runestaff_sniper"; shared2.m_description = "$item_runestaff_sniper_desc"; shared2.m_maxQuality = 4; shared2.m_attack.m_projectiles = 1; shared2.m_attack.m_projectileBursts = 1; shared2.m_attack.m_burstInterval = 0f; Log.LogDebug((object)("Setup -> attackAnim='" + shared2.m_attack.m_attackAnimation + "', skill=" + ((object)Unsafe.As(ref shared2.m_skillType)/*cast due to .constrained prefix*/).ToString() + ", animState=" + ((object)Unsafe.As(ref shared2.m_animationState)/*cast due to .constrained prefix*/).ToString() + ", ammoType='" + shared2.m_ammoType + "', attackEitr=" + shared2.m_attack.m_attackEitr + ", reloadTime=" + shared2.m_attack.m_reloadTime + ").")); Log.LogDebug((object)("VERIFY stats (" + (_wizardry ? "Wizardry" : "NoWizardry") + ") -> pierce=" + shared2.m_damages.m_pierce + " frost=" + shared2.m_damages.m_frost + " | perLevel pierce=" + shared2.m_damagesPerLevel.m_pierce + " frost=" + shared2.m_damagesPerLevel.m_frost + " | velocity=" + EffVelocity + " | maxQuality=" + shared2.m_maxQuality + " (multiplier at fire=" + Damage.Value + ").")); } internal static void EnsureProjectileReady() { if (_projectileReady || (Object)(object)ZNetScene.instance == (Object)null) { return; } BuildCustomProjectile(); StaffProjectile = ResolveProjectile(); if ((Object)(object)StaffProjectile != (Object)null && ((Object)StaffProjectile).name == "RuneStaffSniper_projectile") { _projectileReady = true; Log.LogDebug((object)"EnsureProjectileReady: custom projectile is live."); } else { Log.LogWarning((object)("EnsureProjectileReady: still using fallback '" + (((Object)(object)StaffProjectile != (Object)null) ? ((Object)StaffProjectile).name : "NULL") + "'.")); } if ((Object)(object)StaffProjectile != (Object)null) { GameObject prefab = PrefabManager.Instance.GetPrefab("RuneStaffSniper"); ItemDrop val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null); if ((Object)(object)val != (Object)null) { val.m_itemData.m_shared.m_attack.m_attackProjectile = StaffProjectile; Log.LogDebug((object)("EnsureProjectileReady: shared m_attackProjectile set -> " + ((Object)StaffProjectile).name + ".")); } } ApplyFireSound(); } private static void ApplyFireSound() { if (_soundsApplied) { return; } GameObject prefab = PrefabManager.Instance.GetPrefab("RuneStaffSniper"); if ((Object)(object)prefab == (Object)null) { return; } ItemDrop component = prefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { SharedData shared = component.m_itemData.m_shared; GameObject val = FirstExisting(FireSfxCandidates); if ((Object)(object)val != (Object)null) { SetEffect(shared.m_attack.m_startEffect, val); SetEffect(shared.m_attack.m_triggerEffect, val); Log.LogDebug((object)("ApplyFireSound: fire sound -> " + ((Object)val).name)); } else { Log.LogWarning((object)"ApplyFireSound: no fire sfx candidate found; keeping vanilla."); } _soundsApplied = true; } } private static GameObject FirstExisting(string[] names) { foreach (string text in names) { GameObject prefab = PrefabManager.Instance.GetPrefab(text); if ((Object)(object)prefab != (Object)null) { return prefab; } } return null; } private static void SetEffect(EffectList list, GameObject prefab) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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) //IL_0038: Expected O, but got Unknown if (list != null && !((Object)(object)prefab == (Object)null)) { list.m_effectPrefabs = (EffectData[])(object)new EffectData[1] { new EffectData { m_prefab = prefab, m_enabled = true, m_variant = -1 } }; } } private static void BuildCustomProjectile() { //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown //IL_0174: 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) if ((Object)(object)PrefabManager.Instance.GetPrefab("RuneStaffSniper_projectile") != (Object)null) { return; } GameObject val = null; string text = null; string[] projectileBaseCandidates = ProjectileBaseCandidates; foreach (string text2 in projectileBaseCandidates) { GameObject prefab = PrefabManager.Instance.GetPrefab(text2); if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent() != (Object)null) { val = prefab; text = text2; break; } Log.LogDebug((object)("BuildCustomProjectile: base candidate '" + text2 + "' -> " + (((Object)(object)prefab == (Object)null) ? "not found" : "no Projectile component"))); } if ((Object)(object)val == (Object)null) { Log.LogError((object)"BuildCustomProjectile: no usable base projectile found; staff will use fallback."); return; } Log.LogDebug((object)("BuildCustomProjectile: cloning base '" + text + "'.")); GameObject val2 = PrefabManager.Instance.CreateClonedPrefab("RuneStaffSniper_projectile", val); if ((Object)(object)val2 == (Object)null) { Log.LogError((object)"BuildCustomProjectile: CreateClonedPrefab returned null."); return; } float num = Mathf.Max(0.1f, ProjectileScale.Value); if (Mathf.Abs(num - 1f) > 0.001f) { foreach (Transform item in val2.transform) { Transform val3 = item; val3.localScale *= num; } } Projectile component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { ApplyProjectilePhysics(component); if (NoExplosion.Value) { StripSpawnOnHit(component); } } else { Log.LogWarning((object)"BuildCustomProjectile: clone has no Projectile component?!"); } Log.LogDebug((object)("BuildCustomProjectile: created 'RuneStaffSniper_projectile' from base, gravity=" + (((Object)(object)component != (Object)null) ? component.m_gravity.ToString() : "n/a") + ", noExplosion=" + NoExplosion.Value)); } internal static void ApplyProjectilePhysics(Projectile proj) { if (!((Object)(object)proj == (Object)null)) { if (FlatShot.Value) { proj.m_gravity = 0f; } proj.m_rayRadius = Mathf.Max(proj.m_rayRadius, 0.2f); } } private static void StripSpawnOnHit(Projectile proj) { FieldInfo[] fields = typeof(Projectile).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { if (fieldInfo.Name.IndexOf("spawnOnHit", StringComparison.OrdinalIgnoreCase) < 0) { continue; } try { if (typeof(GameObject).IsAssignableFrom(fieldInfo.FieldType)) { fieldInfo.SetValue(proj, null); } else if (typeof(IList).IsAssignableFrom(fieldInfo.FieldType) && fieldInfo.GetValue(proj) is IList list) { list.Clear(); } Log.LogDebug((object)("StripSpawnOnHit: cleared field '" + fieldInfo.Name + "'.")); } catch (Exception ex) { Log.LogWarning((object)("StripSpawnOnHit: could not clear '" + fieldInfo.Name + "': " + ex.Message)); } } } private static GameObject ResolveProjectile() { string[] projectileCandidates = ProjectileCandidates; foreach (string text in projectileCandidates) { GameObject prefab = PrefabManager.Instance.GetPrefab(text); if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent() != (Object)null) { Log.LogDebug((object)("ResolveProjectile: using '" + text + "'.")); return prefab; } } return null; } private static void FixMaterials(GameObject root, Shader gameStandard, Shader vanillaShader) { //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) Shader val = null; val = (((Object)(object)gameStandard != (Object)null && gameStandard.isSupported) ? gameStandard : ((!((Object)(object)vanillaShader != (Object)null) || !vanillaShader.isSupported) ? Shader.Find("Custom/Creature") : vanillaShader)); if ((Object)(object)val == (Object)null) { Log.LogError((object)"FixMaterials: no usable shader found; staff will render pink/white."); return; } Log.LogDebug((object)("FixMaterials: using shader '" + ((Object)val).name + "'.")); int num = 0; Renderer[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { Material[] sharedMaterials = val2.sharedMaterials; foreach (Material val3 in sharedMaterials) { if ((Object)(object)val3 == (Object)null) { continue; } val3.shader = val; if (PreserveMaterials.Contains(((Object)val3).name)) { num++; continue; } if (!MaterialColors.TryGetValue(((Object)val3).name, out var value)) { ((Color)(ref value))..ctor(0.7f, 0.7f, 0.7f, 1f); } if (val3.HasProperty("_Color")) { val3.SetColor("_Color", value); } if (val3.HasProperty("_MainColor")) { val3.SetColor("_MainColor", value); } if (val3.HasProperty("_Glossiness")) { val3.SetFloat("_Glossiness", 0.2f); } if (val3.HasProperty("_Smoothness")) { val3.SetFloat("_Smoothness", 0.2f); } if (val3.HasProperty("_Metallic")) { val3.SetFloat("_Metallic", 0f); } num++; } } Log.LogDebug((object)("FixMaterials: repaired " + num + " materials (preserved color on: " + string.Join(", ", PreserveMaterials) + ").")); } private void AddLocalization() { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); string text = "English"; localization.AddTranslation(ref text, new Dictionary { { "item_runestaff_sniper", "Ice Sniper Staff" }, { "item_runestaff_sniper_desc", "A focusing rune mounted on a crossbow-stock. Draws like an arbalest, looses a single ice bolt in a dead-straight line. Slow to load, devastating on a hit." } }); } internal static bool IsSniperWeapon(ItemData weapon) { return weapon != null && (Object)(object)weapon.m_dropPrefab != (Object)null && ((Object)weapon.m_dropPrefab).name == "RuneStaffSniper"; } } [HarmonyPatch(typeof(DungeonDB), "Start")] internal static class ProjectileWarmupPatch { private static void Postfix() { try { SniperStaffPlugin.EnsureProjectileReady(); } catch (Exception ex) { SniperStaffPlugin.Log.LogWarning((object)("Warmup failed (will retry on fire): " + ex.Message)); } } } [HarmonyPatch(typeof(Attack), "FireProjectileBurst")] internal static class AttackFirePatch { private static void Prefix(Attack __instance) { if (__instance == null || __instance.m_weapon == null || !SniperStaffPlugin.IsSniperWeapon(__instance.m_weapon)) { return; } Attack attack = __instance.m_weapon.m_shared.m_attack; if ((Object)(object)SniperStaffPlugin.StaffProjectile == (Object)null || ((Object)SniperStaffPlugin.StaffProjectile).name != "RuneStaffSniper_projectile") { SniperStaffPlugin.EnsureProjectileReady(); } if ((Object)(object)SniperStaffPlugin.StaffProjectile != (Object)null) { __instance.m_attackProjectile = SniperStaffPlugin.StaffProjectile; attack.m_attackProjectile = SniperStaffPlugin.StaffProjectile; float effVelocity = SniperStaffPlugin.EffVelocity; float value = SniperStaffPlugin.Accuracy.Value; __instance.m_projectileVel = effVelocity; __instance.m_projectileVelMin = effVelocity; __instance.m_projectileAccuracy = value; __instance.m_projectileAccuracyMin = value; __instance.m_damageMultiplier = SniperStaffPlugin.Damage.Value; if (SniperStaffPlugin.FlatShot.Value && (Object)(object)__instance.m_attackProjectile != (Object)null) { Projectile component = __instance.m_attackProjectile.GetComponent(); SniperStaffPlugin.ApplyProjectilePhysics(component); } } else { SniperStaffPlugin.Log.LogWarning((object)"AttackFirePatch: projectile still null at fire; shot skipped."); } } } [HarmonyPatch(typeof(Attack), "HaveAmmo")] internal static class AmmoBypassPatch { private static void Postfix(Attack __instance, ref bool __result) { if (__instance != null && __instance.m_weapon != null && SniperStaffPlugin.IsSniperWeapon(__instance.m_weapon)) { __result = true; } } } [HarmonyPatch(typeof(Player), "UpdateActionQueue")] internal static class ReloadSoundPatch { private static bool _wasReloading; private static void Postfix(Player __instance) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)SniperStaffPlugin.ReloadClip == (Object)null)) { ItemData currentWeapon = ((Humanoid)__instance).GetCurrentWeapon(); bool flag = SniperStaffPlugin.IsSniperWeapon(currentWeapon); string text = default(string); float num = default(float); MinorActionData val = default(MinorActionData); __instance.GetActionProgress(ref text, ref num, ref val); bool flag2 = flag && val != null && (int)val.m_type == 2; if (_wasReloading && !flag2 && flag && ((Humanoid)__instance).IsWeaponLoaded()) { PlayReloadSound(((Component)__instance).transform.position); } _wasReloading = flag2; } } private static void PlayReloadSound(Vector3 pos) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("SniperReloadSfx"); val.transform.position = pos; AudioSource val2 = val.AddComponent(); val2.clip = SniperStaffPlugin.ReloadClip; val2.volume = Mathf.Clamp01(SniperStaffPlugin.ReloadVolume.Value); val2.spatialBlend = 1f; val2.minDistance = 3f; val2.maxDistance = 30f; val2.Play(); Object.Destroy((Object)(object)val, SniperStaffPlugin.ReloadClip.length + 0.1f); } }