Decompiled source of IceSniperStaff v0.9.0

plugins\SniperStaff.dll

Decompiled a day ago
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<float> Damage;

	internal static ConfigEntry<float> PierceDamage;

	internal static ConfigEntry<float> FrostDamage;

	internal static ConfigEntry<float> PierceDamagePerLevel;

	internal static ConfigEntry<float> FrostDamagePerLevel;

	internal static ConfigEntry<float> Velocity;

	internal static ConfigEntry<float> Accuracy;

	internal static ConfigEntry<float> EitrCost;

	internal static ConfigEntry<float> ReloadTime;

	internal static ConfigEntry<float> ReloadVolume;

	internal static ConfigEntry<bool> SilenceReloadAnim;

	internal static ConfigEntry<bool> FlatShot;

	internal static ConfigEntry<bool> NoExplosion;

	internal static ConfigEntry<float> ProjectileScale;

	private static readonly Dictionary<string, Color> MaterialColors = new Dictionary<string, Color>
	{
		{
			"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<string> PreserveMaterials = new HashSet<string> { "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<float>("Sniper", "DamageMultiplier", 1f, "Damage multiplier applied at fire time (1 = use raw values below).");
		PierceDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Sniper", "PierceDamage", 70f, "Pierce damage per shot (quality 1).");
		FrostDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Sniper", "FrostDamage", 170f, "Frost damage per shot (quality 1).");
		PierceDamagePerLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Sniper", "PierceDamagePerLevel", 10f, "Extra pierce damage gained per upgrade level.");
		FrostDamagePerLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Sniper", "FrostDamagePerLevel", 15f, "Extra frost damage gained per upgrade level.");
		Velocity = ((BaseUnityPlugin)this).Config.Bind<float>("Sniper", "Velocity", 240f, "Projectile speed at fire time (sniper = fast).");
		Accuracy = ((BaseUnityPlugin)this).Config.Bind<float>("Sniper", "Accuracy", 0f, "Aim spread in degrees.");
		EitrCost = ((BaseUnityPlugin)this).Config.Bind<float>("Sniper", "EitrCost", 40f, "Eitr cost per shot.");
		ReloadTime = ((BaseUnityPlugin)this).Config.Bind<float>("Sniper", "ReloadTime", 2f, "Crossbow-style reload/draw time in seconds.");
		ReloadVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Sniper", "ReloadVolume", 1f, "Reload sound volume (0 = silent, 1 = full).");
		SilenceReloadAnim = ((BaseUnityPlugin)this).Config.Bind<bool>("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<bool>("Sniper", "FlatShot", true, "Remove projectile gravity for a flat line.");
		NoExplosion = ((BaseUnityPlugin)this).Config.Bind<bool>("Sniper", "NoExplosion", true, "Strip the AoE so it's a clean single-target dart.");
		ProjectileScale = ((BaseUnityPlugin)this).Config.Bind<float>("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<GameObject>("RuneStaffSniper");
			if ((Object)(object)val == (Object)null)
			{
				Log.LogError((object)"Prefab 'RuneStaffSniper' not found inside bundle 'sniperstaff'.");
				return;
			}
			ReloadClip = _bundle.LoadAsset<AudioClip>("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<MeshRenderer>(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<Sprite>("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<ItemDrop>();
		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<ItemDrop>().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<SkillType, SkillType>(ref shared2.m_skillType)/*cast due to .constrained prefix*/).ToString() + ", animState=" + ((object)Unsafe.As<AnimationState, AnimationState>(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<ItemDrop>();
		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<Projectile>() != (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<Projectile>();
		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<Projectile>() != (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<Renderer>(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<string, string>
		{
			{ "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<Projectile>();
				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<AudioSource>();
		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);
	}
}