using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using Agents;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Gear;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Player;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace HawChow.RMBPLAYER;
internal sealed class FireScope
{
internal readonly FireScope Previous;
internal long ShotId;
internal IntPtr WeaponPointer;
internal IntPtr ShooterPointer;
internal ItemEquippable Weapon;
internal PlayerAgent Shooter;
internal WeaponSlot Slot;
internal uint ArchetypeId;
internal string ArchetypeName;
internal string PublicName;
internal bool IsLocalPlayer;
internal bool IsWeapon;
internal readonly ShotRewardGate RewardGate = new ShotRewardGate();
internal FireScope(FireScope previous)
{
Previous = previous;
}
}
internal sealed class DamageScope
{
internal IntPtr EnemyPointer;
internal IntPtr ShooterPointer;
internal float HealthBefore;
internal bool LocalPlayerSource;
internal bool LimbObserved;
internal bool Weakspot;
internal FireScope Shot;
}
internal static class ShotRewardTracker
{
[ThreadStatic]
private static FireScope _activeFire;
private static readonly HashSet<uint> ReportedWeaponIds = new HashSet<uint>();
private static long _nextShotId;
private static int _warningBudget = 12;
internal static FireScope ActiveFire => _activeFire;
internal static FireScope PushExternalFire(FireScope scope)
{
FireScope activeFire = _activeFire;
_activeFire = scope;
return activeFire;
}
internal static void RestoreExternalFire(FireScope previous)
{
_activeFire = previous;
}
internal static FireScope BeginFire(ItemEquippable weapon)
{
//IL_00f8: 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_017e: Expected O, but got Unknown
//IL_0192: Unknown result type (might be due to invalid IL or missing references)
FireScope fireScope = (_activeFire = new FireScope(_activeFire)
{
ShotId = Interlocked.Increment(ref _nextShotId)
});
try
{
if ((Object)(object)weapon == (Object)null)
{
return fireScope;
}
PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent();
if ((Object)(object)localPlayerAgent == (Object)null || (Object)(object)localPlayerAgent.Owner == (Object)null || !localPlayerAgent.Owner.IsLocal)
{
return fireScope;
}
FirstPersonItemHolder fPItemHolder = localPlayerAgent.FPItemHolder;
ItemEquippable val = (((Object)(object)fPItemHolder != (Object)null) ? fPItemHolder.WieldedItem : null);
if ((Object)(object)val == (Object)null || ((Il2CppObjectBase)val).Pointer != ((Il2CppObjectBase)weapon).Pointer)
{
return fireScope;
}
PlayerInventoryBase inventory = localPlayerAgent.Inventory;
if ((Object)(object)inventory == (Object)null)
{
return fireScope;
}
fireScope.Weapon = val;
fireScope.WeaponPointer = ((Il2CppObjectBase)val).Pointer;
fireScope.Shooter = localPlayerAgent;
fireScope.ShooterPointer = ((Il2CppObjectBase)localPlayerAgent).Pointer;
fireScope.IsLocalPlayer = true;
fireScope.IsWeapon = val.IsWeapon;
fireScope.Slot = ToWeaponSlot(inventory.WieldedSlot);
fireScope.ArchetypeId = val.ArchetypeID;
fireScope.ArchetypeName = ((Item)val).ArchetypeName ?? string.Empty;
fireScope.PublicName = ((Item)val).PublicName ?? string.Empty;
if (fireScope.IsWeapon && ReportedWeaponIds.Add(fireScope.ArchetypeId))
{
Plugin instance = Plugin.Instance;
if (instance != null)
{
ManualLogSource log = ((BasePlugin)instance).Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(65, 4, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[RMBPLAYER] weapon observed: slot=");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<InventorySlot>(inventory.WieldedSlot);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(";archetypeId=");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<uint>(fireScope.ArchetypeId);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(";archetype=");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(fireScope.ArchetypeName);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(";name=");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(fireScope.PublicName);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(".");
}
log.LogInfo(val2);
}
}
}
catch (Exception ex)
{
Note("fire capture skipped: " + ex.GetType().Name);
}
return fireScope;
}
internal static Exception EndFire(FireScope scope, Exception error)
{
try
{
if (_activeFire == scope)
{
_activeFire = scope?.Previous;
}
}
catch (Exception ex)
{
Note("fire scope cleanup skipped: " + ex.GetType().Name);
}
return error;
}
internal static DamageScope BeginDamage(Dam_EnemyDamageBase enemy, pBulletDamageData data)
{
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
//IL_00e2: Invalid comparison between Unknown and I4
DamageScope damageScope = new DamageScope
{
Shot = _activeFire
};
try
{
Agent val = null;
((pAgent)(ref data.source)).TryGet(ref val);
PlayerAgent val2 = (((Object)(object)val != (Object)null) ? ((Il2CppObjectBase)val).TryCast<PlayerAgent>() : null);
if ((Object)(object)val2 == (Object)null || (Object)(object)val2.Owner == (Object)null || !val2.Owner.IsLocal)
{
return damageScope;
}
damageScope.LocalPlayerSource = true;
damageScope.ShooterPointer = ((Il2CppObjectBase)val2).Pointer;
if ((Object)(object)enemy == (Object)null)
{
return damageScope;
}
damageScope.EnemyPointer = ((Il2CppObjectBase)enemy).Pointer;
damageScope.HealthBefore = ((Dam_SyncedDamageBase)enemy).Health;
int limbID = data.limbID;
Il2CppReferenceArray<Dam_EnemyDamageLimb> damageLimbs = enemy.DamageLimbs;
if (damageLimbs != null && limbID >= 0 && limbID < ((Il2CppArrayBase<Dam_EnemyDamageLimb>)(object)damageLimbs).Length)
{
Dam_EnemyDamageLimb val3 = ((Il2CppArrayBase<Dam_EnemyDamageLimb>)(object)damageLimbs)[limbID];
if ((Object)(object)val3 != (Object)null)
{
damageScope.LimbObserved = true;
damageScope.Weakspot = (int)val3.m_type == 1;
}
}
}
catch (Exception ex)
{
Note("damage capture skipped: " + ex.GetType().Name);
}
return damageScope;
}
internal static DamageScope BeginLimbDamage(Dam_EnemyDamageLimb limb, Agent sourceAgent)
{
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: Invalid comparison between Unknown and I4
DamageScope damageScope = new DamageScope
{
Shot = _activeFire
};
try
{
PlayerAgent val = (((Object)(object)sourceAgent != (Object)null) ? ((Il2CppObjectBase)sourceAgent).TryCast<PlayerAgent>() : null);
if ((Object)(object)val == (Object)null || (Object)(object)val.Owner == (Object)null || !val.Owner.IsLocal || (Object)(object)limb == (Object)null)
{
return damageScope;
}
damageScope.LocalPlayerSource = true;
damageScope.ShooterPointer = ((Il2CppObjectBase)val).Pointer;
Dam_EnemyDamageBase val2 = limb.m_base;
if ((Object)(object)val2 == (Object)null)
{
return damageScope;
}
damageScope.EnemyPointer = ((Il2CppObjectBase)val2).Pointer;
damageScope.HealthBefore = ((Dam_SyncedDamageBase)val2).Health;
damageScope.LimbObserved = true;
damageScope.Weakspot = (int)limb.m_type == 1;
}
catch (Exception ex)
{
Note("limb damage capture skipped: " + ex.GetType().Name);
}
return damageScope;
}
internal static Exception EndLimbDamage(Dam_EnemyDamageLimb limb, DamageScope scope, Exception error)
{
Dam_EnemyDamageBase enemy = null;
try
{
enemy = (((Object)(object)limb != (Object)null) ? limb.m_base : null);
}
catch (Exception ex)
{
Note("limb base lookup skipped: " + ex.GetType().Name);
}
return EndDamage(enemy, scope, error);
}
internal static Exception EndDamage(Dam_EnemyDamageBase enemy, DamageScope scope, Exception error)
{
//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
//IL_01f7: Expected O, but got Unknown
//IL_0148: Unknown result type (might be due to invalid IL or missing references)
//IL_014f: Expected O, but got Unknown
try
{
if (error != null || (Object)(object)enemy == (Object)null || scope == null || scope.Shot == null)
{
return error;
}
FireScope shot = scope.Shot;
if (((Il2CppObjectBase)enemy).Pointer != scope.EnemyPointer || !scope.LocalPlayerSource || !shot.IsLocalPlayer || shot.ShooterPointer != scope.ShooterPointer || shot.WeaponPointer == IntPtr.Zero || (Object)(object)shot.Weapon == (Object)null)
{
return error;
}
bool weaponAllowed = RMBPLAYERPolicy.IsAllowedWeaponId((Plugin.AllowedWeaponArchetypeIDs != null) ? Plugin.AllowedWeaponArchetypeIDs.Value : string.Empty, shot.ArchetypeId);
bool qualifyingKill = RMBPLAYERPolicy.ShouldRefund(Plugin.Enabled != null && Plugin.Enabled.Value, scope.LocalPlayerSource, scope.HealthBefore, ((Dam_SyncedDamageBase)enemy).Health, scope.LimbObserved, scope.Weakspot, shot.IsWeapon, shot.Slot, weaponAllowed);
if (!shot.RewardGate.TryClaim(qualifyingKill))
{
return error;
}
int currentClip = shot.Weapon.GetCurrentClip();
int maxClip = shot.Weapon.GetMaxClip();
int num = RMBPLAYERPolicy.RefundOneRound(currentClip, maxClip);
bool flag = default(bool);
Plugin instance;
if (num <= currentClip)
{
instance = Plugin.Instance;
if (instance != null)
{
ManualLogSource log = ((BasePlugin)instance).Log;
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(76, 4, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[RMBPLAYER] weakspot kill; magazine already full: shot=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<long>(shot.ShotId);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(";archetypeId=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<uint>(shot.ArchetypeId);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(";clip=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(currentClip);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(maxClip);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
}
log.LogInfo(val);
}
return error;
}
shot.Weapon.SetCurrentClip(num);
instance = Plugin.Instance;
if (instance != null)
{
ManualLogSource log2 = ((BasePlugin)instance).Log;
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(89, 6, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[RMBPLAYER] weakspot kill refunded one round: shot=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<long>(shot.ShotId);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(";slot=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<WeaponSlot>(shot.Slot);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(";archetypeId=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<uint>(shot.ArchetypeId);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(";archetype=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(shot.ArchetypeName);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(";clip=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(maxClip);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
}
log2.LogInfo(val);
}
}
catch (Exception ex)
{
Note("magazine refund skipped: " + ex.GetType().Name);
}
return error;
}
internal static void Reset()
{
_activeFire = null;
ReportedWeaponIds.Clear();
EwcShotBridge.Reset();
}
internal static void LogPatchReceipts(Harmony harmony, ManualLogSource log)
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Expected O, but got Unknown
bool flag = default(bool);
foreach (MethodBase patchedMethod in harmony.GetPatchedMethods())
{
Patches patchInfo = Harmony.GetPatchInfo(patchedMethod);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(44, 5, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[RMBPLAYER][DIAG] patch ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(patchedMethod.DeclaringType.Name);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(patchedMethod.Name);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": pre=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(patchInfo.Prefixes.Count);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(",post=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(patchInfo.Postfixes.Count);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(",final=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(patchInfo.Finalizers.Count);
}
log.LogInfo(val);
}
}
private static WeaponSlot ToWeaponSlot(InventorySlot slot)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Invalid comparison between Unknown and I4
if ((int)slot == 1)
{
return WeaponSlot.Primary;
}
if ((int)slot == 2)
{
return WeaponSlot.Secondary;
}
return WeaponSlot.Other;
}
private static void Note(string message)
{
if (_warningBudget-- > 0)
{
Plugin instance = Plugin.Instance;
if (instance != null)
{
((BasePlugin)instance).Log.LogWarning((object)("[RMBPLAYER][DIAG] " + message));
}
}
}
}
internal static class WeaponFireScopePatch
{
private static readonly object PatchLock = new object();
private static readonly HashSet<MethodBase> PatchedMethods = new HashSet<MethodBase>();
private static Harmony _harmony;
private static bool _watchingAssemblyLoad;
internal static void Install(Harmony harmony)
{
_harmony = harmony;
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < assemblies.Length; i++)
{
PatchAssembly(assemblies[i]);
}
if (!_watchingAssemblyLoad)
{
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
_watchingAssemblyLoad = true;
}
}
private static void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
try
{
PatchAssembly(args.LoadedAssembly);
}
catch (Exception ex)
{
Plugin instance = Plugin.Instance;
if (instance != null)
{
((BasePlugin)instance).Log.LogWarning((object)("[RMBPLAYER][DIAG] late weapon scan skipped: " + ex.GetType().Name));
}
}
}
private static void PatchAssembly(Assembly assembly)
{
Type[] types;
try
{
types = assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
types = ex.Types;
}
catch
{
return;
}
Type[] array = types;
foreach (Type type in array)
{
if (type != null && !type.IsAbstract && typeof(BulletWeapon).IsAssignableFrom(type))
{
PatchFire(type);
}
}
}
private static void PatchFire(Type type)
{
//IL_013f: Unknown result type (might be due to invalid IL or missing references)
//IL_0146: Expected O, but got Unknown
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Expected O, but got Unknown
//IL_00ab: Expected O, but got Unknown
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Expected O, but got Unknown
MethodInfo methodInfo;
try
{
methodInfo = AccessTools.DeclaredMethod(type, "Fire", new Type[1] { typeof(bool) }, (Type[])null);
}
catch
{
return;
}
if (methodInfo == null)
{
return;
}
lock (PatchLock)
{
if (!PatchedMethods.Add(methodInfo))
{
return;
}
}
try
{
MethodInfo method = typeof(WeaponFireScopePatch).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic);
MethodInfo method2 = typeof(WeaponFireScopePatch).GetMethod("Finalizer", BindingFlags.Static | BindingFlags.NonPublic);
_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method2), (HarmonyMethod)null);
Plugin instance = Plugin.Instance;
if (instance != null)
{
ManualLogSource log = ((BasePlugin)instance).Log;
bool flag;
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(50, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[RMBPLAYER][DIAG] Fire scope patched: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(type.FullName);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".Fire(bool).");
}
log.LogInfo(val);
}
}
catch (Exception ex)
{
lock (PatchLock)
{
PatchedMethods.Remove(methodInfo);
}
Plugin instance = Plugin.Instance;
if (instance != null)
{
ManualLogSource log2 = ((BasePlugin)instance).Log;
bool flag;
BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(41, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[RMBPLAYER][DIAG] Fire patch skipped: ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(type.FullName);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.GetType().Name);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(".");
}
log2.LogWarning(val2);
}
}
}
private static void Prefix(ItemEquippable __instance, out FireScope __state)
{
__state = ShotRewardTracker.BeginFire(__instance);
}
private static Exception Finalizer(FireScope __state, Exception __exception)
{
return ShotRewardTracker.EndFire(__state, __exception);
}
}
[HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveBulletDamage")]
internal static class WeakspotKillRewardPatch
{
[HarmonyPrefix]
private static void Prefix(Dam_EnemyDamageBase __instance, pBulletDamageData data, out DamageScope __state)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
__state = ShotRewardTracker.BeginDamage(__instance, data);
}
[HarmonyFinalizer]
private static Exception Finalizer(Dam_EnemyDamageBase __instance, DamageScope __state, Exception __exception)
{
return ShotRewardTracker.EndDamage(__instance, __state, __exception);
}
}
[HarmonyPatch(typeof(GameStateManager), "DoChangeState")]
internal static class RMBPLAYERStatePatch
{
[HarmonyPrefix]
private static void Prefix(eGameStateName nextState)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0003: Invalid comparison between Unknown and I4
if ((int)nextState != 10)
{
ShotRewardTracker.Reset();
}
}
}
[HarmonyPatch(typeof(Dam_EnemyDamageLimb), "BulletDamage")]
internal static class WeakspotLimbDamageRewardPatch
{
[HarmonyPrefix]
private static void Prefix(Dam_EnemyDamageLimb __instance, Agent sourceAgent, out DamageScope __state)
{
__state = ShotRewardTracker.BeginLimbDamage(__instance, sourceAgent);
}
[HarmonyFinalizer]
private static Exception Finalizer(Dam_EnemyDamageLimb __instance, DamageScope __state, Exception __exception)
{
return ShotRewardTracker.EndLimbDamage(__instance, __state, __exception);
}
}
internal static class EwcShotBridge
{
private sealed class ProjectileLink
{
internal FireScope Shot;
internal readonly long Sequence;
internal ProjectileLink(FireScope shot, long sequence)
{
Shot = shot;
Sequence = sequence;
}
}
internal sealed class ExternalHitScope
{
internal readonly FireScope Previous;
internal ExternalHitScope(FireScope previous)
{
Previous = previous;
}
}
private const string EwcAssemblyName = "ExtraWeaponCustomization";
private const string ProjectileBaseTypeName = "EWC.CustomWeapon.Properties.Traits.CustomProjectile.Components.EWCProjectileComponentBase";
private const string ProjectileHitboxTypeName = "EWC.CustomWeapon.Properties.Traits.CustomProjectile.Components.EWCProjectileHitbox";
private const int MaxTrackedProjectiles = 2048;
private static readonly object Sync = new object();
private static readonly Dictionary<IntPtr, ProjectileLink> ProjectileScopes = new Dictionary<IntPtr, ProjectileLink>();
private static readonly Queue<KeyValuePair<IntPtr, long>> EvictionOrder = new Queue<KeyValuePair<IntPtr, long>>();
private static readonly Dictionary<Type, FieldInfo> BaseFieldCache = new Dictionary<Type, FieldInfo>();
private static readonly HashSet<MethodBase> PatchedMethods = new HashSet<MethodBase>();
private static Harmony _harmony;
private static ManualLogSource _log;
private static long _nextLink;
private static bool _watchingAssemblyLoad;
private static int _warningBudget = 8;
internal static void Install(Harmony harmony, ManualLogSource log)
{
_harmony = harmony;
_log = log;
bool flag = false;
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
if (IsEwcAssembly(assembly))
{
flag = true;
TryInstall(assembly);
}
}
if (!_watchingAssemblyLoad)
{
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
_watchingAssemblyLoad = true;
}
if (!flag)
{
ManualLogSource log2 = _log;
if (log2 != null)
{
log2.LogInfo((object)"[RMBPLAYER][DIAG] EWC is not loaded yet; reflective projectile bridge is waiting for its assembly.");
}
}
}
internal static void Reset()
{
lock (Sync)
{
ProjectileScopes.Clear();
EvictionOrder.Clear();
}
}
private static void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
try
{
if (IsEwcAssembly(args.LoadedAssembly))
{
TryInstall(args.LoadedAssembly);
}
}
catch (Exception ex)
{
Note("EWC assembly-load bridge skipped: " + ex.GetType().Name);
}
}
private static bool IsEwcAssembly(Assembly assembly)
{
try
{
return assembly != null && string.Equals(assembly.GetName().Name, "ExtraWeaponCustomization", StringComparison.OrdinalIgnoreCase);
}
catch
{
return false;
}
}
private static void TryInstall(Assembly assembly)
{
//IL_015b: Unknown result type (might be due to invalid IL or missing references)
//IL_0162: Expected O, but got Unknown
Type type = assembly.GetType("EWC.CustomWeapon.Properties.Traits.CustomProjectile.Components.EWCProjectileComponentBase", throwOnError: false, ignoreCase: false);
Type? type2 = assembly.GetType("EWC.CustomWeapon.Properties.Traits.CustomProjectile.Components.EWCProjectileHitbox", throwOnError: false, ignoreCase: false);
MethodInfo methodInfo = FindMethod(type, "Init", delegate(MethodInfo method)
{
ParameterInfo[] parameters = method.GetParameters();
return parameters.Length == 9 && parameters[7].ParameterType.FullName == "EWC.Utils.HitData";
});
MethodInfo methodInfo2 = FindMethod(type2, "BulletHit", delegate(MethodInfo method)
{
ParameterInfo[] parameters = method.GetParameters();
return parameters.Length == 1 && parameters[0].ParameterType.Name == "IDamageable";
});
int num = 0;
if (methodInfo != null && Patch(methodInfo, null, Method("ProjectileInitPostfix"), null))
{
num++;
}
if (methodInfo2 != null && Patch(methodInfo2, Method("ProjectileHitPrefix"), null, Method("ProjectileHitFinalizer")))
{
num++;
}
if (type != null)
{
MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methodInfo3 in methods)
{
if ((methodInfo3.Name == "OnDisable" || methodInfo3.Name == "OnDestroy") && methodInfo3.GetParameters().Length == 0 && Patch(methodInfo3, null, Method("ProjectileCleanupPostfix"), null))
{
num++;
}
}
}
if (methodInfo != null && methodInfo2 != null)
{
ManualLogSource log = _log;
if (log != null)
{
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(84, 3, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[RMBPLAYER][DIAG] EWC delayed-projectile bridge installed: Init=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(methodInfo.Name);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(";hit=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(methodInfo2.Name);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(";cleanupHooks=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num - 2);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
}
log.LogInfo(val);
}
}
else
{
Note($"EWC bridge signature not found: projectileInit={methodInfo != null};delayedHit={methodInfo2 != null}.");
}
}
private static MethodInfo FindMethod(Type type, string name, Predicate<MethodInfo> matches)
{
if (type == null)
{
return null;
}
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methodInfo in methods)
{
if (methodInfo.Name == name && matches(methodInfo))
{
return methodInfo;
}
}
return null;
}
private static MethodInfo Method(string name)
{
return typeof(EwcShotBridge).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic);
}
private static bool Patch(MethodBase original, MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer)
{
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
if (original == null || _harmony == null)
{
return false;
}
lock (PatchedMethods)
{
if (!PatchedMethods.Add(original))
{
return false;
}
}
try
{
_harmony.Patch(original, (!(prefix != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(prefix), (!(postfix != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(postfix), (HarmonyMethod)null, (!(finalizer != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(finalizer), (HarmonyMethod)null);
return true;
}
catch (Exception ex)
{
lock (PatchedMethods)
{
PatchedMethods.Remove(original);
}
Note("EWC patch failed for " + original.DeclaringType?.Name + "." + original.Name + ": " + ex.GetType().Name);
return false;
}
}
private static void ProjectileInitPostfix(object __instance)
{
try
{
FireScope activeFire = ShotRewardTracker.ActiveFire;
if (activeFire == null || !activeFire.IsLocalPlayer || !activeFire.IsWeapon || activeFire.WeaponPointer == IntPtr.Zero || __instance == null)
{
return;
}
Il2CppObjectBase val = (Il2CppObjectBase)((__instance is Il2CppObjectBase) ? __instance : null);
if (val == null || val.Pointer == IntPtr.Zero)
{
return;
}
lock (Sync)
{
IntPtr pointer = val.Pointer;
if (ProjectileScopes.TryGetValue(pointer, out var value))
{
value.Shot = activeFire;
return;
}
long num = Interlocked.Increment(ref _nextLink);
ProjectileScopes[pointer] = new ProjectileLink(activeFire, num);
EvictionOrder.Enqueue(new KeyValuePair<IntPtr, long>(pointer, num));
while (ProjectileScopes.Count > 2048 && EvictionOrder.Count > 0)
{
KeyValuePair<IntPtr, long> keyValuePair = EvictionOrder.Dequeue();
if (ProjectileScopes.TryGetValue(keyValuePair.Key, out var value2) && value2.Sequence == keyValuePair.Value)
{
ProjectileScopes.Remove(keyValuePair.Key);
}
}
}
}
catch (Exception ex)
{
Note("EWC projectile registration skipped: " + ex.GetType().Name);
}
}
private static void ProjectileHitPrefix(object __instance, out ExternalHitScope __state)
{
__state = null;
try
{
if (__instance == null)
{
return;
}
FieldInfo baseField = GetBaseField(__instance.GetType());
if (baseField == null)
{
return;
}
object? value = baseField.GetValue(__instance);
Il2CppObjectBase val = (Il2CppObjectBase)((value is Il2CppObjectBase) ? value : null);
if (val == null || val.Pointer == IntPtr.Zero)
{
return;
}
FireScope shot;
lock (Sync)
{
if (!ProjectileScopes.TryGetValue(val.Pointer, out var value2))
{
return;
}
shot = value2.Shot;
}
if (shot != null)
{
__state = new ExternalHitScope(ShotRewardTracker.PushExternalFire(shot));
}
}
catch (Exception ex)
{
Note("EWC delayed hit scope skipped: " + ex.GetType().Name);
}
}
private static Exception ProjectileHitFinalizer(ExternalHitScope __state, Exception __exception)
{
try
{
if (__state != null)
{
ShotRewardTracker.RestoreExternalFire(__state.Previous);
}
}
catch (Exception ex)
{
Note("EWC delayed hit scope restore skipped: " + ex.GetType().Name);
}
return __exception;
}
private static void ProjectileCleanupPostfix(object __instance)
{
try
{
Il2CppObjectBase val = (Il2CppObjectBase)((__instance is Il2CppObjectBase) ? __instance : null);
if (val == null)
{
return;
}
lock (Sync)
{
ProjectileScopes.Remove(val.Pointer);
}
}
catch (Exception ex)
{
Note("EWC projectile cleanup skipped: " + ex.GetType().Name);
}
}
private static FieldInfo GetBaseField(Type type)
{
lock (Sync)
{
if (BaseFieldCache.TryGetValue(type, out var value))
{
return value;
}
FieldInfo field = type.GetField("_base", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
BaseFieldCache[type] = field;
return field;
}
}
private static void Note(string message)
{
if (_warningBudget-- > 0)
{
ManualLogSource log = _log;
if (log != null)
{
log.LogWarning((object)("[RMBPLAYER][DIAG] " + message));
}
}
}
}
[BepInPlugin("HawChow.RMBPLAYER", "RMBPLAYER", "1.0.0")]
public sealed class Plugin : BasePlugin
{
internal static Plugin Instance;
internal static ConfigEntry<bool> Enabled;
internal static ConfigEntry<string> AllowedWeaponArchetypeIDs;
public override void Load()
{
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Expected O, but got Unknown
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Expected O, but got Unknown
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Expected O, but got Unknown
//IL_00ea: Expected O, but got Unknown
Instance = this;
Enabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable one-round magazine refunds for local weakspot kills.");
AllowedWeaponArchetypeIDs = ((BasePlugin)this).Config.Bind<string>("Weapons", "AllowedWeaponArchetypeIDs", "*", "Comma-separated ItemEquippable ArchetypeID values; * enables every primary/secondary weapon. Hex IDs may use 0x prefix.");
Harmony val = new Harmony("HawChow.RMBPLAYER");
val.PatchAll(typeof(Plugin).Assembly);
WeaponFireScopePatch.Install(val);
EwcShotBridge.Install(val, ((BasePlugin)this).Log);
ManualLogSource log = ((BasePlugin)this).Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(142, 3, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("RMBPLAYER");
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("1.0.0");
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" loaded; local weakspot kills only; slots=GearStandard/GearSpecial; allowedArchetypeIDs=");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(AllowedWeaponArchetypeIDs.Value);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("; EWC path=ItemEquippable; no EWC assembly reference.");
}
log.LogInfo(val2);
ShotRewardTracker.LogPatchReceipts(val, ((BasePlugin)this).Log);
}
}
internal static class PluginInfo
{
internal const string Guid = "HawChow.RMBPLAYER";
internal const string Name = "RMBPLAYER";
internal const string Version = "1.0.0";
}
internal enum WeaponSlot
{
Other,
Primary,
Secondary
}
internal static class RMBPLAYERPolicy
{
internal static bool IsEligibleSlot(WeaponSlot slot)
{
if (slot != WeaponSlot.Primary)
{
return slot == WeaponSlot.Secondary;
}
return true;
}
internal static bool IsAllowedWeaponId(string configuredIds, uint archetypeId)
{
if (string.IsNullOrWhiteSpace(configuredIds))
{
return false;
}
string[] array = configuredIds.Split(new char[4] { ',', ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < array.Length; i++)
{
string text = array[i].Trim();
if (text == "*")
{
return true;
}
if (text.StartsWith("id:", StringComparison.OrdinalIgnoreCase))
{
text = text.Substring(3).Trim();
}
uint result;
if (text.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
if (uint.TryParse(text.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out result) && result == archetypeId)
{
return true;
}
}
else if (uint.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out result) && result == archetypeId)
{
return true;
}
}
return false;
}
internal static bool ShouldRefund(bool enabled, bool localPlayerSource, float healthBefore, float healthAfter, bool limbObserved, bool limbIsWeakspot, bool isWeapon, WeaponSlot slot, bool weaponAllowed)
{
return enabled && localPlayerSource && IsFinite(healthBefore) && IsFinite(healthAfter) && healthBefore > 0f && healthAfter <= 0f && limbObserved && limbIsWeakspot && isWeapon && IsEligibleSlot(slot) && weaponAllowed;
}
internal static int RefundOneRound(int currentClip, int clipSize)
{
if (clipSize <= 0)
{
return currentClip;
}
if (currentClip < 0)
{
currentClip = 0;
}
if (currentClip >= clipSize)
{
return clipSize;
}
return currentClip + 1;
}
internal static bool IsFinite(float value)
{
if (!float.IsNaN(value))
{
return !float.IsInfinity(value);
}
return false;
}
}
internal sealed class ShotRewardGate
{
private bool _claimed;
internal bool Claimed => _claimed;
internal bool TryClaim(bool qualifyingKill)
{
if (!qualifyingKill || _claimed)
{
return false;
}
_claimed = true;
return true;
}
}