Decompiled source of KnifeThrowAnimation v0.1.0

KnifeThrowAnim.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("KnifeThrowAnim")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("KnifeThrowAnim")]
[assembly: AssemblyTitle("KnifeThrowAnim")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace KnifeThrowAnim;

[BepInPlugin("valheim.knifethrowanim", "Knife Throw Animation", "0.1.0")]
public class Plugin : BaseUnityPlugin
{
	public const string Guid = "valheim.knifethrowanim";

	public const string Name = "Knife Throw Animation";

	public const string Version = "0.1.0";

	internal const string BundleFile = "knifethrow.bundle";

	internal const string ClipAsset = "KnifeThrow";

	internal const string SpearTrigger = "spear_throw";

	internal const string SpearClip = "throw_spear";

	internal static ManualLogSource Log;

	internal static AnimationClip KnifeClip;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<bool> RemotePlayers;

	internal static ConfigEntry<bool> SpawnFromHand;

	internal static ConfigEntry<float> SpawnForward;

	internal static ConfigEntry<float> SpawnUp;

	internal const string HandBone = "RightHand";

	private void Awake()
	{
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Use the custom dagger-throw animation for thrown knives.");
		RemotePlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RemotePlayers", true, "Also show it for other players' knife throws (judged by the weapon in their right hand).");
		SpawnFromHand = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawn", "FromHand", true, "Launch the thrown knife from the right hand (at the clip's release frame) instead of the spear's spawn point.");
		SpawnForward = ((BaseUnityPlugin)this).Config.Bind<float>("Spawn", "Forward", 0.2f, "Metres ahead of the hand (along the character's facing) where the knife appears.");
		SpawnUp = ((BaseUnityPlugin)this).Config.Bind<float>("Spawn", "Up", 0f, "Metres above the hand where the knife appears.");
		KnifeClip = LoadClip();
		if (!((Object)(object)KnifeClip == (Object)null))
		{
			new Harmony("valheim.knifethrowanim").PatchAll(typeof(Plugin).Assembly);
		}
	}

	private AnimationClip LoadClip()
	{
		string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "knifethrow.bundle");
		if (!File.Exists(text))
		{
			Log.LogError((object)("Bundle not found: " + text));
			return null;
		}
		AssetBundle val = AssetBundle.LoadFromFile(text);
		AnimationClip val2 = (((Object)(object)val != (Object)null) ? val.LoadAsset<AnimationClip>("KnifeThrow") : null);
		if ((Object)(object)val2 == (Object)null)
		{
			Log.LogError((object)("Clip 'KnifeThrow' not found in " + text));
			return null;
		}
		if (!val2.events.Any((AnimationEvent e) => e.functionName == "OnAttackTrigger"))
		{
			Log.LogError((object)("Clip '" + ((Object)val2).name + "' has no OnAttackTrigger event; keeping the spear animation."));
			return null;
		}
		Log.LogInfo((object)$"Loaded '{((Object)val2).name}' ({val2.length:0.###}s, {val2.events.Length} events).");
		return val2;
	}
}
[HarmonyPatch(typeof(Attack), "Start")]
internal static class SpawnFromHand
{
	[HarmonyPriority(200)]
	private static void Prefix(Attack __instance, Humanoid character, ItemData weapon)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Invalid comparison between Unknown and I4
		if (Plugin.Enabled.Value && Plugin.SpawnFromHand.Value && !((Object)(object)character != (Object)(object)Player.m_localPlayer) && weapon != null && (int)weapon.m_shared.m_skillType == 2 && !(__instance.m_attackAnimation != "spear_throw") && !((Object)(object)__instance.m_attackProjectile == (Object)null))
		{
			__instance.m_attackOriginJoint = "RightHand";
			__instance.m_attackHeight = Plugin.SpawnUp.Value;
			__instance.m_attackRange = Plugin.SpawnForward.Value;
			__instance.m_attackOffset = 0f;
		}
	}
}
[HarmonyPatch(typeof(ZSyncAnimation), "RPC_SetTrigger")]
internal static class ThrowTriggerPatch
{
	private static readonly FieldRef<ZSyncAnimation, Animator> AnimatorOf = AccessTools.FieldRefAccess<ZSyncAnimation, Animator>("m_animator");

	private static readonly FieldRef<VisEquipment, int> RightItemHashOf = AccessTools.FieldRefAccess<VisEquipment, int>("m_currentRightItemHash");

	private static void Prefix(ZSyncAnimation __instance, string name)
	{
		if (!(name != "spear_throw"))
		{
			Animator val = AnimatorOf.Invoke(__instance);
			if (!((Object)(object)val == (Object)null))
			{
				Humanoid component = ((Component)__instance).GetComponent<Humanoid>();
				ThrowOverride.Apply(val, Plugin.Enabled.Value && (Object)(object)component != (Object)null && HoldsKnife(component));
			}
		}
	}

	private static bool HoldsKnife(Humanoid humanoid)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Invalid comparison between Unknown and I4
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Invalid comparison between Unknown and I4
		if ((Object)(object)humanoid == (Object)(object)Player.m_localPlayer)
		{
			ItemData currentWeapon = humanoid.GetCurrentWeapon();
			if (currentWeapon == null)
			{
				return false;
			}
			return (int)currentWeapon.m_shared.m_skillType == 2;
		}
		if (!Plugin.RemotePlayers.Value || !(humanoid is Player))
		{
			return false;
		}
		VisEquipment visEquipment = humanoid.GetVisEquipment();
		if ((Object)(object)visEquipment == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
		{
			return false;
		}
		int num = RightItemHashOf.Invoke(visEquipment);
		GameObject val = ((num != 0) ? ObjectDB.instance.GetItemPrefab(num) : null);
		ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
		if ((Object)(object)val2 != (Object)null)
		{
			return (int)val2.m_itemData.m_shared.m_skillType == 2;
		}
		return false;
	}
}
internal static class ThrowOverride
{
	private sealed class Slot
	{
		public AnimatorOverrideController Controller;

		public AnimationClip Original;

		public bool KnifeOn;
	}

	private static readonly ConditionalWeakTable<Animator, Slot> Slots = new ConditionalWeakTable<Animator, Slot>();

	private static bool warnedNoClip;

	public static void Apply(Animator animator, bool knife)
	{
		Slots.TryGetValue(animator, out var slot);
		if (slot != null && (Object)(object)animator.runtimeAnimatorController != (Object)(object)slot.Controller)
		{
			Slots.Remove(animator);
			slot = null;
		}
		if (slot == null)
		{
			if (!knife)
			{
				return;
			}
			slot = Wrap(animator);
			if (slot == null)
			{
				return;
			}
		}
		if (slot.KnifeOn != knife)
		{
			slot.KnifeOn = knife;
			Preserving(animator, delegate
			{
				slot.Controller[slot.Original] = (knife ? Plugin.KnifeClip : null);
			});
		}
	}

	private static Slot Wrap(Animator animator)
	{
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Expected O, but got Unknown
		RuntimeAnimatorController runtimeAnimatorController = animator.runtimeAnimatorController;
		if ((Object)(object)runtimeAnimatorController == (Object)null)
		{
			return null;
		}
		AnimatorOverrideController val = (AnimatorOverrideController)(object)((runtimeAnimatorController is AnimatorOverrideController) ? runtimeAnimatorController : null);
		RuntimeAnimatorController val2 = (((Object)(object)val != (Object)null) ? val.runtimeAnimatorController : runtimeAnimatorController);
		AnimationClip val3 = null;
		AnimationClip[] animationClips = val2.animationClips;
		foreach (AnimationClip val4 in animationClips)
		{
			if ((Object)(object)val4 != (Object)null && ((Object)val4).name == "throw_spear")
			{
				val3 = val4;
				break;
			}
		}
		if ((Object)(object)val3 == (Object)null)
		{
			if (!warnedNoClip)
			{
				Plugin.Log.LogWarning((object)("'throw_spear' not found in '" + ((Object)val2).name + "'; leaving the animation alone."));
			}
			warnedNoClip = true;
			return null;
		}
		AnimatorOverrideController controller = new AnimatorOverrideController(val2)
		{
			name = ((Object)val2).name + " (knife throw)"
		};
		if ((Object)(object)val != (Object)null)
		{
			List<KeyValuePair<AnimationClip, AnimationClip>> list = new List<KeyValuePair<AnimationClip, AnimationClip>>(val.overridesCount);
			val.GetOverrides(list);
			controller.ApplyOverrides((IList<KeyValuePair<AnimationClip, AnimationClip>>)list);
		}
		Preserving(animator, delegate
		{
			animator.runtimeAnimatorController = (RuntimeAnimatorController)(object)controller;
		});
		Slot slot = new Slot
		{
			Controller = controller,
			Original = val3
		};
		Slots.Add(animator, slot);
		return slot;
	}

	private static void Preserving(Animator animator, Action change)
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		int layerCount = animator.layerCount;
		AnimatorStateInfo[] array = (AnimatorStateInfo[])(object)new AnimatorStateInfo[layerCount];
		float[] array2 = new float[layerCount];
		for (int i = 0; i < layerCount; i++)
		{
			array[i] = (animator.IsInTransition(i) ? animator.GetNextAnimatorStateInfo(i) : animator.GetCurrentAnimatorStateInfo(i));
			array2[i] = animator.GetLayerWeight(i);
		}
		AnimatorControllerParameter[] parameters = animator.parameters;
		float[] array3 = new float[parameters.Length];
		for (int j = 0; j < parameters.Length; j++)
		{
			array3[j] = Read(animator, parameters[j]);
		}
		float speed = animator.speed;
		change();
		animator.Update(0f);
		for (int k = 0; k < parameters.Length; k++)
		{
			Write(animator, parameters[k], array3[k]);
		}
		for (int l = 0; l < layerCount && l < animator.layerCount; l++)
		{
			animator.SetLayerWeight(l, array2[l]);
			AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(l);
			if (((AnimatorStateInfo)(ref currentAnimatorStateInfo)).fullPathHash != ((AnimatorStateInfo)(ref array[l])).fullPathHash)
			{
				animator.Play(((AnimatorStateInfo)(ref array[l])).fullPathHash, l, ((AnimatorStateInfo)(ref array[l])).normalizedTime);
			}
		}
		animator.speed = speed;
	}

	private static float Read(Animator animator, AnimatorControllerParameter p)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected I4, but got Unknown
		AnimatorControllerParameterType type = p.type;
		switch (type - 1)
		{
		case 0:
			return animator.GetFloat(p.nameHash);
		case 2:
			return animator.GetInteger(p.nameHash);
		case 3:
			if (!animator.GetBool(p.nameHash))
			{
				return 0f;
			}
			return 1f;
		default:
			return 0f;
		}
	}

	private static void Write(Animator animator, AnimatorControllerParameter p, float value)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected I4, but got Unknown
		AnimatorControllerParameterType type = p.type;
		switch (type - 1)
		{
		case 0:
			animator.SetFloat(p.nameHash, value);
			break;
		case 2:
			animator.SetInteger(p.nameHash, (int)value);
			break;
		case 3:
			animator.SetBool(p.nameHash, value != 0f);
			break;
		case 1:
			break;
		}
	}
}