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("0.9.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.9.0.0")] namespace SniperStaff; [BepInPlugin("dots.icesniperstaff", "Ice Sniper Staff", "0.9.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 = "0.9.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 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; 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 void Awake() { Log = ((BaseUnityPlugin)this).Logger; BindConfig(); PrefabManager.OnVanillaPrefabsAvailable += RegisterStaff; _harmony.PatchAll(); Log.LogInfo((object)"Ice Sniper Staff v0.9.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)."); } private void RegisterStaff() { //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Expected O, but got Unknown try { _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_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: 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_014b: 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_015c: Expected O, but got Unknown //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: 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_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0059: 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_006a: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown //IL_019a: 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_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Expected O, but got Unknown string craftingStation; int minStationLevel; RequirementConfig[] requirements; if (Chainloader.PluginInfos.ContainsKey("Therzie.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 = "forge"; minStationLevel = 1; requirements = (RequirementConfig[])(object)new RequirementConfig[3] { new RequirementConfig { Item = "YggdrasilWood", Amount = 20, AmountPerLevel = 10, Recover = true }, new RequirementConfig { Item = "Eitr", Amount = 16, AmountPerLevel = 8, Recover = true }, new RequirementConfig { Item = "Crystal", Amount = 6, AmountPerLevel = 3, Recover = true } }; Log.LogWarning((object)"Recipe: Wizardry NOT installed -> using vanilla fallback recipe at forge."); } 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_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_027f: 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) 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 = ReloadTime.Value; 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 = PierceDamage.Value; damages.m_chop = 0f; damages.m_pickaxe = 0f; damages.m_fire = 0f; damages.m_frost = FrostDamage.Value; 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 = PierceDamagePerLevel.Value; damagesPerLevel.m_chop = 0f; damagesPerLevel.m_pickaxe = 0f; damagesPerLevel.m_fire = 0f; damagesPerLevel.m_frost = FrostDamagePerLevel.Value; 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 = EitrCost.Value; shared2.m_attack.m_attackStamina = 0f; shared2.m_attack.m_drawStaminaDrain = 0f; StaffProjectile = ResolveProjectile(); if ((Object)(object)StaffProjectile != (Object)null) { shared2.m_attack.m_attackProjectile = StaffProjectile; } else { Log.LogError((object)"No projectile candidate found in ZNetScene! Staff cannot fire. Check candidate names against LogOutput / prefab list."); } 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 + ", projectile=" + (((Object)(object)shared2.m_attack.m_attackProjectile != (Object)null) ? ((Object)shared2.m_attack.m_attackProjectile).name : "NULL"))); Log.LogDebug((object)("VERIFY damage -> 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 + " | maxQuality=" + shared2.m_maxQuality + " (multiplier at fire=" + Damage.Value + ")")); } internal static void EnsureProjectileReady() { if (!_projectileReady && !((Object)(object)ZNetScene.instance == (Object)null)) { 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") + "'.")); } 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)) { 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) { attack.m_attackProjectile = SniperStaffPlugin.StaffProjectile; } float value = SniperStaffPlugin.Velocity.Value; float value2 = SniperStaffPlugin.Accuracy.Value; __instance.m_projectileVel = value; __instance.m_projectileVelMin = value; __instance.m_projectileAccuracy = value2; __instance.m_projectileAccuracyMin = value2; __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); } } } } [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); } }