using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using HoldToCommand.ValheimAPI;
using HoldToCommand.ValheimAPI.LowLevel;
using HoldToCommand.ValheimAPI.LowLevel.Core;
using HoldToCommand.ValheimAPI.LowLevel.Core.Invokers;
using HoldToCommand.ValheimAPI.LowLevel.Core.Signatures;
using HoldToCommand.ValheimAPI.LowLevel.UnityEngine;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Yvonne Pautz")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © Yvonne Pautz 2026")]
[assembly: AssemblyFileVersion("0.0.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+build.202602150113.98b2dd55ddde1cffdba93f64c4ae8fc97c4b57f4")]
[assembly: AssemblyProduct("HoldToCommand")]
[assembly: AssemblyTitle("HoldToCommand")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace HoldToCommand
{
[BepInPlugin("Vonny1412.HoldToCommand", "HoldToCommand", "0.2.0")]
public sealed class Plugin : BaseUnityPlugin
{
internal const string LangFileName = "HoldToCommand.Translations.txt";
internal static readonly Dictionary<string, (string holdTpl, string commandVerb)> TranslationsByLanguage = new Dictionary<string, (string, string)>(StringComparer.OrdinalIgnoreCase);
public const string Version = "0.2.0";
public const string ModName = "HoldToCommand";
public const string ModGuid = "Vonny1412.HoldToCommand";
internal static ManualLogSource Log { get; private set; }
private void Awake()
{
Log = ((BaseUnityPlugin)this).Logger;
Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
LoadTranslationsFile();
}
private static void LoadTranslationsFile()
{
try
{
string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "HoldToCommand.Translations.txt");
if (!File.Exists(text))
{
ManualLogSource log = Log;
if (log != null)
{
log.LogWarning((object)("Translations file not found: " + text + " (will use English fallback)"));
}
return;
}
string[] array = File.ReadAllLines(text);
foreach (string text2 in array)
{
string text3 = text2.Trim();
if (text3.Length == 0 || text3.StartsWith("#"))
{
continue;
}
string[] array2 = text3.Split(new char[1] { '|' });
if (array2.Length < 3)
{
ManualLogSource log2 = Log;
if (log2 != null)
{
log2.LogWarning((object)("Invalid translations line (expected 3 columns): " + text2));
}
continue;
}
string text4 = array2[0].Trim();
string text5 = array2[1].Trim();
string text6 = array2[2].Trim();
if (text4.Length == 0)
{
continue;
}
if (text5.Length == 0 || text6.Length == 0)
{
ManualLogSource log3 = Log;
if (log3 != null)
{
log3.LogWarning((object)("Empty values in translations line: " + text2));
}
}
else
{
TranslationsByLanguage[text4] = (text5, text6);
}
}
ManualLogSource log4 = Log;
if (log4 != null)
{
log4.LogInfo((object)$"Loaded {TranslationsByLanguage.Count} translation entries from HoldToCommand.Translations.txt");
}
}
catch (Exception arg)
{
ManualLogSource log5 = Log;
if (log5 != null)
{
log5.LogError((object)$"Failed to load translations file: {arg}");
}
}
}
}
}
namespace HoldToCommand.ValheimAPI
{
internal static class LocalizationExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AddWord(this Localization local, string key, string text)
{
Localization.__IAPI_AddWord_Invoker1.Invoke(local, key, text);
}
}
internal static class TameableExtensions
{
private const float HoldThreshold = 0.45f;
private static Tameable lastTarget;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetName(this Tameable tameable)
{
Tameable.__IAPI_SetName_Invoker1.Invoke(tameable);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float GetLastPetTime(this Tameable tameable)
{
return Tameable.__IAPI_m_lastPetTime_Invoker.Get(tameable);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetLastPetTime(this Tameable tameable, float t)
{
Tameable.__IAPI_m_lastPetTime_Invoker.Set(tameable, t);
}
public static bool CustomInteract(this Tameable tameable, Humanoid user, bool hold, bool alt)
{
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
ZNetView component = ((Component)tameable).GetComponent<ZNetView>();
if ((Object)(object)component == (Object)null || !component.IsValid())
{
return false;
}
if (alt)
{
tameable.SetName();
return true;
}
if (!tameable.IsTamed())
{
return false;
}
float time = Time.time;
float lastPetTime = tameable.GetLastPetTime();
if (hold)
{
if (!tameable.m_commandable)
{
return false;
}
if (time - lastPetTime < 0.45f)
{
return false;
}
if ((Object)(object)lastTarget == (Object)(object)tameable)
{
return false;
}
lastTarget = tameable;
tameable.Command(user, true);
return true;
}
lastTarget = null;
if (time - lastPetTime <= 1f)
{
return false;
}
tameable.SetLastPetTime(time);
EffectList petEffect = tameable.m_petEffect;
if (petEffect != null)
{
petEffect.Create(((Component)tameable).transform.position, ((Component)tameable).transform.rotation, (Transform)null, 1f, -1);
}
string text = null;
if (tameable.m_tameTextGetter != null)
{
string text2 = tameable.m_tameTextGetter.Invoke();
if (!string.IsNullOrEmpty(text2))
{
text = text2;
}
}
if (string.IsNullOrEmpty(text))
{
string hoverName = tameable.GetHoverName();
string text3 = tameable.m_tameText ?? "";
text = (tameable.m_nameBeforeText ? (hoverName + " " + text3) : text3);
}
if (!string.IsNullOrEmpty(text))
{
((Character)user).Message((MessageType)2, text, 0, (Sprite)null);
}
return true;
}
}
}
namespace HoldToCommand.ValheimAPI.LowLevel
{
public class AnimalAI : BaseAI
{
public AnimalAI(AnimalAI instance)
: base((BaseAI)(object)instance)
{
}
}
public class BaseAI : MonoBehaviour
{
public BaseAI(BaseAI instance)
: base((MonoBehaviour)(object)instance)
{
}
}
public class Localization : ClassPublicizer
{
public static readonly VoidMethodInvoker __IAPI_AddWord_Invoker1 = new VoidMethodInvoker(typeof(Localization), "AddWord", new ParamSig[2]
{
new NonGenericParamSig(typeof(string), IsByRef: false),
new NonGenericParamSig(typeof(string), IsByRef: false)
});
public Localization(Localization instance)
: base(instance)
{
}
}
public class MonsterAI : BaseAI
{
public MonsterAI(MonsterAI instance)
: base((BaseAI)(object)instance)
{
}
}
public class Tameable : MonoBehaviour
{
public static readonly FieldMutateInvoker<float> __IAPI_m_lastPetTime_Invoker = new FieldMutateInvoker<float>(typeof(Tameable), "m_lastPetTime");
public static readonly VoidMethodInvoker __IAPI_SetName_Invoker1 = new VoidMethodInvoker(typeof(Tameable), "SetName", new ParamSig[0]);
public Tameable(Tameable instance)
: base((MonoBehaviour)(object)instance)
{
}
}
}
namespace HoldToCommand.ValheimAPI.LowLevel.UnityEngine
{
public class Behaviour : Component
{
public Behaviour(Behaviour instance)
: base((Component)(object)instance)
{
}
}
public class Component : Object
{
public Component(Component instance)
: base((Object)(object)instance)
{
}
}
public class MonoBehaviour : Behaviour
{
public MonoBehaviour(MonoBehaviour instance)
: base((Behaviour)(object)instance)
{
}
}
public class Object : ClassPublicizer
{
public Object(Object instance)
: base(instance)
{
}
}
}
namespace HoldToCommand.ValheimAPI.LowLevel.Core
{
public abstract class ClassPublicizer
{
protected object __IAPI_instance;
public ClassPublicizer(object instance)
{
__IAPI_instance = instance;
}
public ClassPublicizer()
{
__IAPI_instance = null;
}
public static Type __IAPI_GetInstanceType()
{
return typeof(void);
}
public object __IAPI_GetInstance()
{
return __IAPI_instance;
}
public void __IAPI_SetInstance(object instance)
{
__IAPI_instance = instance;
}
}
}
namespace HoldToCommand.ValheimAPI.LowLevel.Core.Signatures
{
public abstract class CommonParamSig : ParamSig
{
public override bool Matches(ParameterInfo p)
{
if (p.IsOut != IsByRef && p.ParameterType.IsByRef != IsByRef)
{
return false;
}
Type type = p.ParameterType;
if (type.IsByRef)
{
type = type.GetElementType();
}
if (IsGeneric)
{
if (!type.IsGenericParameter || type.GenericParameterPosition != GenericIndex)
{
return false;
}
}
else if (type != ConcreteType)
{
return false;
}
return true;
}
}
public class GenericContainerParamSig : ParamSig
{
private readonly Type _genericDef;
public GenericContainerParamSig(Type genericDef, bool isByRef)
{
_genericDef = genericDef;
IsByRef = isByRef;
}
public override bool Matches(ParameterInfo p)
{
Type parameterType = p.ParameterType;
if (parameterType.IsByRef != IsByRef)
{
return false;
}
if (!parameterType.IsGenericType)
{
return false;
}
if (parameterType.GetGenericTypeDefinition() != _genericDef)
{
return false;
}
return parameterType.GetGenericArguments().All((Type a) => a.IsGenericParameter);
}
}
public class GenericParamSig : CommonParamSig
{
public GenericParamSig(int GenericIndex, bool IsByRef)
{
IsGeneric = true;
base.GenericIndex = GenericIndex;
ConcreteType = null;
base.IsByRef = IsByRef;
}
}
public class NonGenericParamSig : CommonParamSig
{
public NonGenericParamSig(Type ConcreteType, bool IsByRef)
{
IsGeneric = false;
GenericIndex = -1;
base.ConcreteType = ConcreteType;
base.IsByRef = IsByRef;
}
}
public abstract class ParamSig
{
public bool IsGeneric;
public int GenericIndex;
public Type ConcreteType;
public bool IsByRef;
public abstract bool Matches(ParameterInfo p);
}
}
namespace HoldToCommand.ValheimAPI.LowLevel.Core.Invokers
{
public class ConstFieldInvoker<R>
{
protected readonly FieldInfo member;
protected readonly Func<object, R> _getter;
public ConstFieldInvoker(Type type, string name)
{
BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
member = type.GetField(name, bindingAttr);
if (member == null)
{
throw new MissingFieldException(type.FullName, name);
}
_ = member.IsLiteral;
object rawConstantValue = member.GetRawConstantValue();
R casted = (R)rawConstantValue;
_getter = (object _) => casted;
}
public R Get(object instance)
{
return _getter(instance);
}
}
public class FieldAccessInvoker<R>
{
protected readonly FieldInfo member;
protected readonly Func<object, R> _getter;
public FieldAccessInvoker(Type type, string name)
{
BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
member = type.GetField(name, bindingAttr);
if (member == null)
{
throw new MissingFieldException(type.FullName, name);
}
_ = member.IsLiteral;
_getter = CreateGetter(member);
}
public R Get(object instance)
{
return _getter(instance);
}
protected static Func<object, R> CreateGetter(FieldInfo field)
{
DynamicMethod dynamicMethod = new DynamicMethod(field.Name + "_Getter", typeof(R), new Type[1] { typeof(object) }, field.DeclaringType, skipVisibility: true);
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
if (!field.IsStatic)
{
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Castclass, field.DeclaringType);
}
iLGenerator.Emit(field.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, field);
if (field.FieldType.IsValueType && typeof(R) == typeof(object))
{
iLGenerator.Emit(OpCodes.Box, field.FieldType);
}
iLGenerator.Emit(OpCodes.Ret);
return (Func<object, R>)dynamicMethod.CreateDelegate(typeof(Func<object, R>));
}
}
public class FieldMutateInvoker<R> : FieldAccessInvoker<R>
{
protected readonly Action<object, R> _setter;
public FieldMutateInvoker(Type type, string name)
: base(type, name)
{
_setter = CreateSetter(member);
}
public void Set(object instance, R value)
{
_setter(instance, value);
}
protected static Action<object, R> CreateSetter(FieldInfo field)
{
DynamicMethod dynamicMethod = new DynamicMethod(field.Name + "_Setter", typeof(void), new Type[2]
{
typeof(object),
typeof(R)
}, field.DeclaringType, skipVisibility: true);
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
if (!field.IsStatic)
{
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Castclass, field.DeclaringType);
}
iLGenerator.Emit(OpCodes.Ldarg_1);
if (field.FieldType.IsValueType && typeof(R) == typeof(object))
{
iLGenerator.Emit(OpCodes.Unbox_Any, field.FieldType);
}
iLGenerator.Emit(field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, field);
iLGenerator.Emit(OpCodes.Ret);
return (Action<object, R>)dynamicMethod.CreateDelegate(typeof(Action<object, R>));
}
}
public abstract class GenericMethodInvoker : MethodInvoker
{
public static MethodInfo FindGenericMethod(Type type, string name, int genericCount, ParamSig[] signature)
{
MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methodInfo in methods)
{
if (methodInfo.Name != name || !methodInfo.IsGenericMethodDefinition || methodInfo.GetGenericArguments().Length != genericCount)
{
continue;
}
ParameterInfo[] parameters = methodInfo.GetParameters();
if (parameters.Length != signature.Length)
{
continue;
}
bool flag = true;
for (int j = 0; j < parameters.Length; j++)
{
ParameterInfo p = parameters[j];
if (!signature[j].Matches(p))
{
flag = false;
break;
}
}
if (flag)
{
return methodInfo;
}
}
throw new MissingMethodException(type.FullName, name);
}
}
public abstract class MethodInvoker
{
protected delegate object MethodInvokerDelegate(object instance, object[] args);
protected static MethodInvokerDelegate CreateInvokerDelegate(MethodInfo method)
{
ParameterInfo[] parameters = method.GetParameters();
DynamicMethod dynamicMethod = new DynamicMethod(method.Name + "_Invoker", typeof(object), new Type[2]
{
typeof(object),
typeof(object[])
}, restrictedSkipVisibility: true);
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
if (!method.IsStatic)
{
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Castclass, method.DeclaringType);
}
for (int i = 0; i < parameters.Length; i++)
{
iLGenerator.Emit(OpCodes.Ldarg_1);
iLGenerator.Emit(OpCodes.Ldc_I4, i);
iLGenerator.Emit(OpCodes.Ldelem_Ref);
Type parameterType = parameters[i].ParameterType;
if (parameterType.IsByRef)
{
Type elementType = parameterType.GetElementType();
iLGenerator.Emit(OpCodes.Unbox, elementType);
}
else
{
iLGenerator.Emit(OpCodes.Unbox_Any, parameterType);
}
}
iLGenerator.Emit(method.IsStatic ? OpCodes.Call : OpCodes.Callvirt, method);
if (method.ReturnType == typeof(void))
{
iLGenerator.Emit(OpCodes.Ldnull);
}
else
{
iLGenerator.Emit(OpCodes.Box, method.ReturnType);
}
iLGenerator.Emit(OpCodes.Ret);
return (MethodInvokerDelegate)dynamicMethod.CreateDelegate(typeof(MethodInvokerDelegate));
}
}
public class PropertyInvoker<R>
{
protected readonly PropertyInfo member;
private readonly Func<object, R> _getter;
private readonly Action<object, R> _setter;
public PropertyInvoker(Type type, string name)
{
BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
member = type.GetProperty(name, bindingAttr);
if (member == null)
{
throw new MissingFieldException(type.FullName, name);
}
if (member.CanRead)
{
_getter = CreateGetter(member);
}
if (member.CanWrite)
{
_setter = CreateSetter(member);
}
}
public R Get(object instance)
{
if (_getter == null)
{
throw new InvalidOperationException("Property has no getter");
}
return _getter(instance);
}
public void Set(object instance, R value)
{
if (_setter == null)
{
throw new InvalidOperationException("Property has no setter");
}
_setter(instance, value);
}
private static Func<object, R> CreateGetter(PropertyInfo prop)
{
MethodInfo getMethod = prop.GetGetMethod(nonPublic: true);
if (getMethod == null)
{
return null;
}
DynamicMethod dynamicMethod = new DynamicMethod(prop.Name + "_Getter", typeof(R), new Type[1] { typeof(object) }, prop.DeclaringType, skipVisibility: true);
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
if (!getMethod.IsStatic)
{
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Castclass, prop.DeclaringType);
}
iLGenerator.Emit(getMethod.IsStatic ? OpCodes.Call : OpCodes.Callvirt, getMethod);
if (prop.PropertyType.IsValueType && typeof(R) == typeof(object))
{
iLGenerator.Emit(OpCodes.Box, prop.PropertyType);
}
iLGenerator.Emit(OpCodes.Ret);
return (Func<object, R>)dynamicMethod.CreateDelegate(typeof(Func<object, R>));
}
private static Action<object, R> CreateSetter(PropertyInfo prop)
{
MethodInfo setMethod = prop.GetSetMethod(nonPublic: true);
if (setMethod == null)
{
return null;
}
DynamicMethod dynamicMethod = new DynamicMethod(prop.Name + "_Setter", typeof(void), new Type[2]
{
typeof(object),
typeof(R)
}, prop.DeclaringType, skipVisibility: true);
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
if (!setMethod.IsStatic)
{
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Castclass, prop.DeclaringType);
}
iLGenerator.Emit(OpCodes.Ldarg_1);
if (prop.PropertyType.IsValueType && typeof(R) == typeof(object))
{
iLGenerator.Emit(OpCodes.Unbox_Any, prop.PropertyType);
}
iLGenerator.Emit(setMethod.IsStatic ? OpCodes.Call : OpCodes.Callvirt, setMethod);
iLGenerator.Emit(OpCodes.Ret);
return (Action<object, R>)dynamicMethod.CreateDelegate(typeof(Action<object, R>));
}
}
public class TypedMethodInvoker<R> : MethodInvoker
{
protected readonly MethodInfo member;
private readonly MethodInvokerDelegate _invoker;
private readonly bool anyByRef;
private static readonly object[] _args0 = Array.Empty<object>();
[ThreadStatic]
private static object[] _args1;
[ThreadStatic]
private static object[] _args2;
[ThreadStatic]
private static object[] _args3;
[ThreadStatic]
private static object[] _args4;
[ThreadStatic]
private static object[] _args5;
public TypedMethodInvoker(Type type, string name, ParamSig[] parameters)
{
BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
anyByRef = parameters.Any((ParamSig p) => p.IsByRef);
Type[] types = parameters.Select((ParamSig p) => (!p.IsByRef) ? p.ConcreteType : p.ConcreteType.MakeByRefType()).ToArray();
member = type.GetMethod(name, bindingAttr, null, types, null);
if (member == null)
{
throw new MissingMethodException(type.FullName, name);
}
_invoker = MethodInvoker.CreateInvokerDelegate(member);
}
public R Invoke(object instance, object[] args)
{
return (R)_invoker(instance, args);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public R Invoke(object instance)
{
return (R)_invoker(instance, _args0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public R Invoke(object instance, object arg0)
{
if (anyByRef)
{
return (R)_invoker(instance, new object[1] { arg0 });
}
object[] array = _args1 ?? (_args1 = new object[1]);
array[0] = arg0;
return (R)_invoker(instance, array);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public R Invoke(object instance, object arg0, object arg1)
{
if (anyByRef)
{
return (R)_invoker(instance, new object[2] { arg0, arg1 });
}
object[] array = _args2 ?? (_args2 = new object[2]);
array[0] = arg0;
array[1] = arg1;
return (R)_invoker(instance, array);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public R Invoke(object instance, object arg0, object arg1, object arg2)
{
if (anyByRef)
{
return (R)_invoker(instance, new object[3] { arg0, arg1, arg2 });
}
object[] array = _args3 ?? (_args3 = new object[3]);
array[0] = arg0;
array[1] = arg1;
array[2] = arg2;
return (R)_invoker(instance, array);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public R Invoke(object instance, object arg0, object arg1, object arg2, object arg3)
{
if (anyByRef)
{
return (R)_invoker(instance, new object[4] { arg0, arg1, arg2, arg3 });
}
object[] array = _args4 ?? (_args4 = new object[4]);
array[0] = arg0;
array[1] = arg1;
array[2] = arg2;
array[3] = arg3;
return (R)_invoker(instance, array);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public R Invoke(object instance, object arg0, object arg1, object arg2, object arg3, object arg4)
{
if (anyByRef)
{
return (R)_invoker(instance, new object[5] { arg0, arg1, arg2, arg3, arg4 });
}
object[] array = _args5 ?? (_args5 = new object[5]);
array[0] = arg0;
array[1] = arg1;
array[2] = arg2;
array[3] = arg3;
array[4] = arg4;
return (R)_invoker(instance, array);
}
}
public class VoidMethodInvoker : MethodInvoker
{
protected readonly MethodInfo member;
private readonly MethodInvokerDelegate _invoker;
private readonly bool anyByRef;
private static readonly object[] _args0 = Array.Empty<object>();
[ThreadStatic]
private static object[] _args1;
[ThreadStatic]
private static object[] _args2;
[ThreadStatic]
private static object[] _args3;
[ThreadStatic]
private static object[] _args4;
[ThreadStatic]
private static object[] _args5;
public VoidMethodInvoker(Type type, string name, ParamSig[] parameters)
{
BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
anyByRef = parameters.Any((ParamSig p) => p.IsByRef);
Type[] types = parameters.Select((ParamSig p) => (!p.IsByRef) ? p.ConcreteType : p.ConcreteType.MakeByRefType()).ToArray();
member = type.GetMethod(name, bindingAttr, null, types, null);
if (member == null)
{
throw new MissingMethodException(type.FullName, name);
}
_invoker = MethodInvoker.CreateInvokerDelegate(member);
}
public void Invoke(object instance, object[] args)
{
_invoker(instance, args);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(object instance)
{
_invoker(instance, _args0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(object instance, object arg0)
{
if (anyByRef)
{
_invoker(instance, new object[1] { arg0 });
}
else
{
object[] array = _args1 ?? (_args1 = new object[1]);
array[0] = arg0;
_invoker(instance, array);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(object instance, object arg0, object arg1)
{
if (anyByRef)
{
_invoker(instance, new object[2] { arg0, arg1 });
}
else
{
object[] array = _args2 ?? (_args2 = new object[2]);
array[0] = arg0;
array[1] = arg1;
_invoker(instance, array);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(object instance, object arg0, object arg1, object arg2)
{
if (anyByRef)
{
_invoker(instance, new object[3] { arg0, arg1, arg2 });
}
else
{
object[] array = _args3 ?? (_args3 = new object[3]);
array[0] = arg0;
array[1] = arg1;
array[2] = arg2;
_invoker(instance, array);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(object instance, object arg0, object arg1, object arg2, object arg3)
{
if (anyByRef)
{
_invoker(instance, new object[4] { arg0, arg1, arg2, arg3 });
}
else
{
object[] array = _args4 ?? (_args4 = new object[4]);
array[0] = arg0;
array[1] = arg1;
array[2] = arg2;
array[3] = arg3;
_invoker(instance, array);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(object instance, object arg0, object arg1, object arg2, object arg3, object arg4)
{
if (anyByRef)
{
_invoker(instance, new object[5] { arg0, arg1, arg2, arg3, arg4 });
}
else
{
object[] array = _args5 ?? (_args5 = new object[5]);
array[0] = arg0;
array[1] = arg1;
array[2] = arg2;
array[3] = arg3;
array[4] = arg4;
_invoker(instance, array);
}
}
}
}
namespace HoldToCommand.Patches
{
[HarmonyPatch(typeof(Localization), "SetupLanguage")]
internal static class Localization_SetupLanguage_Patch
{
internal const string keyHold = "htc_hold";
internal const string keyCommand = "htc_command";
private static void Postfix(Localization __instance, string language)
{
if (!Plugin.TranslationsByLanguage.TryGetValue(language, out (string, string) value) && !Plugin.TranslationsByLanguage.TryGetValue("English", out value))
{
value = ("Hold $1", "Command");
}
__instance.AddWord("htc_hold", value.Item1);
__instance.AddWord("htc_command", value.Item2);
}
}
[HarmonyPatch(typeof(Tameable), "GetHoverText")]
internal static class Tameable_GetHoverText_Patch
{
[HarmonyPriority(0)]
private static void Postfix(Tameable __instance, ref string __result)
{
if (__instance.IsTamed() && __instance.m_commandable)
{
string text = Localization.instance.Localize("$hud_pet");
int num = __result.IndexOf(text, StringComparison.Ordinal);
if (num >= 0)
{
string text2 = Localization.instance.Localize("$KEY_Use");
string text3 = Localization.instance.Localize("$htc_hold", new string[1] { text2 });
string value = Localization.instance.Localize(" [<color=yellow><b>" + text3 + "</b></color>] $htc_command");
__result = __result.Insert(num + text.Length, value);
}
}
}
}
[HarmonyPatch(typeof(Tameable), "Interact")]
internal static class Tameable_Interact_Patch
{
private static bool Prefix(Tameable __instance, Humanoid user, bool hold, bool alt, ref bool __result)
{
__result = __instance.CustomInteract(user, hold, alt);
return false;
}
}
}