using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Pigeon.Math; using Pigeon.Movement; using Unity.Netcode; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Sparroh")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("FriendinaBox")] [assembly: AssemblyTitle("FriendinaBox")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } [HarmonyPatch] internal static class CoopUnlockHook { private static MethodBase TargetMethod() { MethodInfo methodInfo = AccessTools.Method(typeof(PlayerData), "FilterUpgrades", (Type[])null, (Type[])null); if (methodInfo == null) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogError((object)"[FriendinaBox] Could not find PlayerData.FilterUpgrades."); return methodInfo; } return methodInfo; } ManualLogSource logger2 = FriendinaBoxPlugin.Logger; if (logger2 != null) { logger2.LogInfo((object)"[FriendinaBox] Patching PlayerData.FilterUpgrades for Coop unlock."); return methodInfo; } return methodInfo; } [HarmonyTranspiler] private static IEnumerable Transpiler(IEnumerable instructions) { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(List), "Count"); FieldInfo fieldInfo = AccessTools.Field(typeof(GameManager), "players"); MethodInfo methodInfo2 = AccessTools.Method(typeof(CoopUnlockHook), "GetEffectivePlayerCount", (Type[])null, (Type[])null); if (methodInfo == null || fieldInfo == null || methodInfo2 == null) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogError((object)"[FriendinaBox] CoopUnlockHook transpiler missing members — coop unlock disabled."); } return instructions; } List list = new List(instructions); int num = 0; for (int i = 0; i < list.Count - 1; i++) { if (list[i].opcode == OpCodes.Ldsfld && object.Equals(list[i].operand, fieldInfo) && list[i + 1].opcode == OpCodes.Callvirt && object.Equals(list[i + 1].operand, methodInfo)) { list[i + 1] = new CodeInstruction(OpCodes.Call, (object)methodInfo2); num++; } } ManualLogSource logger2 = FriendinaBoxPlugin.Logger; if (logger2 != null) { logger2.LogInfo((object)$"[FriendinaBox] CoopUnlockHook transpiler applied {num} player-count replacement(s)."); } return list; } public static int GetEffectivePlayerCount(List players) { int num = players?.Count ?? 0; if (num <= 1 && FriendinaBoxBehaviour.IsEquippedOnLocalPlayer()) { return 2; } return num; } } public static class FriendBulletCache { private static bool _attempted; private static GameObject _railPrefab; private static GameObject _mortarPrefab; public static GameObject RailPrefab { get { EnsureCached(); return _railPrefab; } } public static GameObject MortarPrefab { get { EnsureCached(); return _mortarPrefab; } } public static void EnsureCached() { if (_attempted) { return; } _attempted = true; TryCacheRailFromCycler(); TryCacheMortar(); if ((Object)(object)_railPrefab != (Object)null) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)("[FriendinaBox] Cached RailBullet prefab '" + ((Object)_railPrefab).name + "'.")); } } else { ManualLogSource logger2 = FriendinaBoxPlugin.Logger; if (logger2 != null) { logger2.LogWarning((object)"[FriendinaBox] Could not cache RailBullet — turret fire will use explosion fallback."); } } if ((Object)(object)_mortarPrefab != (Object)null) { ManualLogSource logger3 = FriendinaBoxPlugin.Logger; if (logger3 != null) { logger3.LogInfo((object)("[FriendinaBox] Cached MortarBullet prefab '" + ((Object)_mortarPrefab).name + "'.")); } } else { ManualLogSource logger4 = FriendinaBoxPlugin.Logger; if (logger4 != null) { logger4.LogWarning((object)"[FriendinaBox] Could not cache MortarBullet — mortar fire will use explosion fallback."); } } } private static void TryCacheRailFromCycler() { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) if (Global.Instance?.AllGear != null) { for (int i = 0; i < Global.Instance.AllGear.Length; i++) { IUpgradable obj = Global.Instance.AllGear[i]; CartridgeSMG val = (CartridgeSMG)(object)((obj is CartridgeSMG) ? obj : null); if (val != null && TryReadGunBulletPrefab((Gun)(object)val, out var prefab) && (Object)(object)prefab != (Object)null && ((Object)(object)prefab.GetComponent() != (Object)null || prefab.GetComponent() != null)) { _railPrefab = prefab; return; } } for (int j = 0; j < Global.Instance.AllGear.Length; j++) { IUpgradable obj2 = Global.Instance.AllGear[j]; Gun val2 = (Gun)(object)((obj2 is Gun) ? obj2 : null); if (val2 != null && TryReadGunBulletPrefab(val2, out var prefab2) && (Object)(object)prefab2 != (Object)null && (Object)(object)prefab2.GetComponent() != (Object)null) { _railPrefab = prefab2; return; } } } RailBullet[] array = Resources.FindObjectsOfTypeAll(); foreach (RailBullet val3 in array) { if ((Object)(object)val3 == (Object)null) { continue; } GameObject gameObject = ((Component)val3).gameObject; Scene scene = gameObject.scene; if (((Scene)(ref scene)).IsValid()) { scene = gameObject.scene; if (((Scene)(ref scene)).isLoaded && gameObject.activeInHierarchy) { continue; } } _railPrefab = gameObject; return; } if (array.Length != 0 && (Object)(object)array[0] != (Object)null) { _railPrefab = ((Component)array[0]).gameObject; } } private static void TryCacheMortar() { //IL_0024: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) MortarBullet[] array = Resources.FindObjectsOfTypeAll(); foreach (MortarBullet val in array) { if ((Object)(object)val == (Object)null) { continue; } GameObject gameObject = ((Component)val).gameObject; Scene scene = gameObject.scene; if (((Scene)(ref scene)).IsValid()) { scene = gameObject.scene; if (((Scene)(ref scene)).isLoaded && gameObject.activeInHierarchy) { continue; } } _mortarPrefab = gameObject; return; } if (array.Length != 0 && (Object)(object)array[0] != (Object)null) { _mortarPrefab = ((Component)array[0]).gameObject; return; } RocketSalvoBullet[] array2 = Resources.FindObjectsOfTypeAll(); foreach (RocketSalvoBullet val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { _ = val2 is MortarBullet; } } if (Global.Instance?.AllGear == null) { return; } ProjectileGunArmTip[] array3 = Resources.FindObjectsOfTypeAll(); for (int k = 0; k < array3.Length; k++) { if (TryReadArmTipBulletPrefab(array3[k], out var prefab) && (Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent() != (Object)null) { _mortarPrefab = prefab; break; } } } private static bool TryReadGunBulletPrefab(Gun gun, out GameObject prefab) { prefab = null; if ((Object)(object)gun == (Object)null) { return false; } object? obj = typeof(Gun).GetField("_bulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(gun); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { prefab = val; return true; } object? obj2 = (typeof(Gun).GetField("bulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(Gun).GetField("defaultBulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(gun); IBullet val2 = (IBullet)((obj2 is IBullet) ? obj2 : null); if (val2 != null) { Component val3 = (Component)(object)((val2 is Component) ? val2 : null); if (val3 != null && (Object)(object)val3 != (Object)null) { prefab = val3.gameObject; return true; } } RailBullet componentInChildren = ((Component)gun).GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { prefab = ((Component)componentInChildren).gameObject; return true; } return false; } private static bool TryReadArmTipBulletPrefab(ProjectileGunArmTip tip, out GameObject prefab) { prefab = null; if ((Object)(object)tip == (Object)null) { return false; } object? obj = (typeof(GunArmTip).GetField("bulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? ((object)tip).GetType().GetField("bulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(tip); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { prefab = val; return true; } return false; } } public static class FriendCombatHooks { [CompilerGenerated] private static class <>O { public static DamageCallback <0>__OnFriendDamage; public static KillCallback <1>__OnFriendKill; public static MutableDamageCallback <2>__OnPlayerBeforeDamageOutgoing; public static RefAction <3>__OnPlayerTookDamage; } private static bool _playerHooksBound; private static DamageCallback _onGearDamage; private static KillCallback _onGearKill; private static MutableDamageCallback _onPlayerBeforeDamage; private static RefAction _onPlayerAfterTakeDamage; private static float _nextOverhealTime; private static IGear _hookedGear; public static void EnsureBound(IGear gear, FriendinaBoxBehaviour.Data data) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown IDamageSource val = (IDamageSource)(object)((gear is IDamageSource) ? gear : null); if (val == null) { return; } if (_hookedGear != gear) { UnbindGear(); _hookedGear = gear; object obj = <>O.<0>__OnFriendDamage; if (obj == null) { DamageCallback val2 = OnFriendDamage; <>O.<0>__OnFriendDamage = val2; obj = (object)val2; } _onGearDamage = (DamageCallback)obj; object obj2 = <>O.<1>__OnFriendKill; if (obj2 == null) { KillCallback val3 = OnFriendKill; <>O.<1>__OnFriendKill = val3; obj2 = (object)val3; } _onGearKill = (KillCallback)obj2; try { val.OnDamageTarget = (DamageCallback)Delegate.Combine((Delegate?)(object)val.OnDamageTarget, (Delegate?)(object)_onGearDamage); val.OnKillTarget = (KillCallback)Delegate.Combine((Delegate?)(object)val.OnKillTarget, (Delegate?)(object)_onGearKill); } catch (Exception ex) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)("[FriendinaBox] Gear combat bind failed: " + ex.Message)); } } } EnsurePlayerHooks(); } public static void UnbindGear() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown if (_hookedGear == null) { return; } try { IGear hookedGear = _hookedGear; IDamageSource val = (IDamageSource)(object)((hookedGear is IDamageSource) ? hookedGear : null); if (val != null) { if (_onGearDamage != null) { val.OnDamageTarget = (DamageCallback)Delegate.Remove((Delegate?)(object)val.OnDamageTarget, (Delegate?)(object)_onGearDamage); } if (_onGearKill != null) { val.OnKillTarget = (KillCallback)Delegate.Remove((Delegate?)(object)val.OnKillTarget, (Delegate?)(object)_onGearKill); } } } catch { } _hookedGear = null; } private static void EnsurePlayerHooks() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown if (_playerHooksBound) { return; } Player localPlayer = Player.LocalPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } object obj = <>O.<2>__OnPlayerBeforeDamageOutgoing; if (obj == null) { MutableDamageCallback val = OnPlayerBeforeDamageOutgoing; <>O.<2>__OnPlayerBeforeDamageOutgoing = val; obj = (object)val; } _onPlayerBeforeDamage = (MutableDamageCallback)obj; _onPlayerAfterTakeDamage = OnPlayerTookDamage; try { localPlayer.OnBeforeDamage = (MutableDamageCallback)Delegate.Combine((Delegate?)(object)localPlayer.OnBeforeDamage, (Delegate?)(object)_onPlayerBeforeDamage); localPlayer.OnAfterTakeDamage += _onPlayerAfterTakeDamage; _playerHooksBound = true; } catch (Exception ex) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)("[FriendinaBox] Player combat bind failed: " + ex.Message)); } } } private static bool TryGetLiveData(out FriendinaBoxBehaviour.Data data, out IGear gear, out Player player) { data = default(FriendinaBoxBehaviour.Data); gear = null; player = Player.LocalPlayer; if (!FriendinaBoxBehaviour.TryGetEquipped(out var behaviour, out gear) || (Object)(object)behaviour == (Object)null) { return false; } data = behaviour.GrenadeData; EnsureBound(gear, data); return true; } private static bool IsFriendSourced(IDamageSource source) { if (source == null) { return false; } if (_hookedGear != null && (object)source == _hookedGear) { return true; } for (IDamageSource val = source; val != null; val = val.ParentSource) { if (_hookedGear != null && (object)val == _hookedGear) { return true; } IGear val2 = (IGear)(object)((val is IGear) ? val : null); if (val2 != null && (Object)(object)((IUpgradable)val2).Info != (Object)null && (((IUpgradable)val2).Info.APIName == "friend_in_a_box" || ((IUpgradable)val2).Info.ID == 92100)) { return true; } } return false; } private static void GrantBlueOverhealth(Player player, IDamageSource source, float amount) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || !player.IsAlive || amount <= 0f) { return; } try { player.HealWithOverhealth(amount, source); } catch { try { IDamageSource.HealTarget(source, (ITarget)(object)player, 0f - amount, player.InterpolatedPosition); } catch (Exception ex) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)("[FriendinaBox] Overheal failed: " + ex.Message)); } } } } private static void OnFriendDamage(in DamageCallbackData data) { if (!TryGetLiveData(out var data2, out var gear, out var player) || !IsFriendSourced(data.source)) { return; } float damage = data.damageData.damage; if (damage <= 0f) { return; } if (data2.lifestealFraction > 0f && (Object)(object)player != (Object)null && ((NetworkBehaviour)player).IsLocalPlayer) { float num = damage * data2.lifestealFraction; if (num > 0f) { IDamageSource val = (IDamageSource)(object)((gear is IDamageSource) ? gear : null); if (val != null) { GrantBlueOverhealth(player, val, num); } } } if (data2.markDamageBonus > 0f && data2.markDuration > 0f && data.target != null) { FriendMarkedTarget.Apply(data.target, data2.markDamageBonus, data2.markDuration); } } private static void OnFriendKill(in KillCallbackData data) { if (!TryGetLiveData(out var data2, out var gear, out var _) || !IsFriendSourced(data.source)) { return; } if (data2.chargeOnKill > 0f) { Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null); if (val != null) { try { ((CooldownData)(ref val.CooldownData)).AddCharge(data2.chargeOnKill); } catch { try { val.CooldownData.charge = Mathf.Min((float)val.CooldownData.maxCharges, val.CooldownData.charge + data2.chargeOnKill); } catch { } } } } if (data2.durationOnKill > 0f) { FriendDeployTracker.ExtendAllDurations(data2.durationOnKill); } } private static void OnPlayerBeforeDamageOutgoing(ref DamageCallbackData data) { if (data.target != null && !(data.damageData.damage <= 0f)) { if (TryGetLiveData(out var data2, out var _, out var _) && data2.sharePlayerTarget && data2.sharedTargetDuration > 0f && !IsFriendSourced(data.source)) { FriendSharedTarget.Set(data.target, data2.sharedTargetDuration); } if (FriendMarkedTarget.TryGetBonus(data.target, out var bonus) && bonus > 0f) { data.damageData.damage *= 1f + bonus; } } } private static void OnPlayerTookDamage(ref DamageData damage, ref IDamageSource source) { if (!TryGetLiveData(out var data, out var gear, out var player) || data.overhealOnDamaged <= 0f || (Object)(object)player == (Object)null || !((NetworkBehaviour)player).IsLocalPlayer || !player.IsAlive || Time.time < _nextOverhealTime) { return; } _nextOverhealTime = Time.time + Mathf.Max(0.5f, data.overhealOnDamagedCooldown); IDamageSource val = (IDamageSource)(object)((gear is IDamageSource) ? gear : null); if (val != null) { GrantBlueOverhealth(player, val, data.overhealOnDamaged); ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)$"[FriendinaBox] Reactive Shell overheal +{data.overhealOnDamaged:0.#} blue"); } } } public static void TickShootToDetonate() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_00a1: 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_00be: Unknown result type (might be due to invalid IL or missing references) if (!TryGetLiveData(out var data, out var _, out var player) || !data.shootToDetonate || (Object)(object)player == (Object)null || !((NetworkBehaviour)player).IsLocalPlayer || !FriendDeployTracker.HasActive) { return; } try { if (PlayerInput.Controls == null) { return; } PlayerActions player2 = PlayerInput.Controls.Player; if (!((PlayerActions)(ref player2)).Fire.IsPressed()) { return; } } catch { return; } Camera val = Camera.main; if ((Object)(object)val == (Object)null && (Object)(object)PlayerLook.Instance != (Object)null) { val = ((Component)PlayerLook.Instance).GetComponentInChildren(); } RaycastHit val2 = default(RaycastHit); if (!((Object)(object)val == (Object)null) && Physics.Raycast(new Ray(((Component)val).transform.position, ((Component)val).transform.forward), ref val2, 200f, -1, (QueryTriggerInteraction)2)) { FriendDeployable friendDeployable = FriendDeployTracker.FindNearest(((RaycastHit)(ref val2)).point, 1.75f); if ((Object)(object)friendDeployable != (Object)null) { friendDeployable.DetonateFromPlayerShot(); } } } } public sealed class FriendCombatRunner : MonoBehaviour { private static FriendCombatRunner _instance; public static void Ensure() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("[FriendinaBox] CombatRunner"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); } } private void Update() { try { FriendCombatHooks.TickShootToDetonate(); } catch { } } } [HarmonyPatch(typeof(Player), "OnNetworkSpawn")] internal static class FriendPlayerSpawnHook { [HarmonyPostfix] private static void Postfix(Player __instance) { if (!((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsLocalPlayer) { FriendCombatRunner.Ensure(); if (FriendinaBoxBehaviour.TryGetEquipped(out var behaviour, out var gear)) { FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); } } } } public static class FriendDebugVisual { public static readonly Color DefaultColor = Color.magenta; private static readonly Dictionary MaterialsByColor = new Dictionary(); public static GameObject Attach(Transform parent, Color? color = null, float scale = 0.75f) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)parent == (Object)null) { return null; } Transform val = parent.Find("FriendDebugVisual"); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } Color color2 = (Color)(((??)color) ?? DefaultColor); GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj).name = "FriendDebugVisual"; obj.transform.SetParent(parent, false); obj.transform.localPosition = Vector3.up * (scale * 0.5f); obj.transform.localRotation = Quaternion.identity; obj.transform.localScale = Vector3.one * Mathf.Max(0.1f, scale); Collider component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Renderer component2 = obj.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.sharedMaterial = GetOrCreateMaterial(color2); component2.shadowCastingMode = (ShadowCastingMode)0; component2.receiveShadows = false; } return obj; } private unsafe static Material GetOrCreateMaterial(Color color) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) int hashCode = ((object)(*(Color*)(&color))/*cast due to .constrained prefix*/).GetHashCode(); if (MaterialsByColor.TryGetValue(hashCode, out var value) && (Object)(object)value != (Object)null) { return value; } Shader val = Shader.Find("Unlit/Color") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Sprites/Default") ?? Shader.Find("Standard"); Material val2 = new Material(((Object)(object)val != (Object)null) ? val : Shader.Find("Hidden/InternalErrorShader")); ((Object)val2).name = $"FriendinaBox_Debug_{hashCode:X8}"; ((Object)val2).hideFlags = (HideFlags)61; if (val2.HasProperty("_Color")) { val2.color = color; } else if (val2.HasProperty("_BaseColor")) { val2.SetColor("_BaseColor", color); } MaterialsByColor[hashCode] = val2; return val2; } } public sealed class FriendDeployable : MonoBehaviour { private IGear ownerGear; private Player ownerPlayer; private FriendinaBoxBehaviour.Data friendData; private FriendDeployMode mode; private float remainingDuration; private float detectRadius; private float explosionRadius; private DamageData damage; private float explosionShake; private bool finished; private float nextScanTime; private float nextFireTime; private bool droneDiving; private Vector3 droneDiveTarget; private float maxDuration; private SphereCollider hitProxy; private float effectiveFireRate; private const float ScanInterval = 0.1f; private const float HoverHeight = 2.4f; private const float ModeEngageRange = 50f; private static readonly RangeData LongRangeData = new RangeData { falloffStartDistance = 799f, falloffEndDistance = 800f, maxFalloffDamageMultiplier = 1f, maxDamageRange = 800f }; private const int SurfaceMask = 10241; private const int TargetMaskNonPlayer = 345216; public FriendDeployMode Mode => mode; public float RemainingDuration => remainingDuration; public float MaxDuration => maxDuration; public static FriendDeployable Spawn(Vector3 position, IGear gear, FriendinaBoxBehaviour.Data friendData, float explosionRadius, DamageData damage, float explosionShake, float selfEffectMultiplier) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("[FriendinaBox] Deployable"); val.transform.position = position; FriendDeployable friendDeployable = val.AddComponent(); friendDeployable.Arm(gear, friendData, explosionRadius, damage, explosionShake); return friendDeployable; } private void Arm(IGear gear, FriendinaBoxBehaviour.Data friendData, float explosionRadius, DamageData damage, float explosionShake) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) ownerGear = gear; Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null); ownerPlayer = ((val != null) ? val.Player : Player.LocalPlayer); this.friendData = friendData; mode = friendData.deployMode; remainingDuration = Mathf.Max(0.5f, friendData.deployDuration); maxDuration = remainingDuration; this.explosionRadius = Mathf.Max(0.1f, explosionRadius); if (friendData.maxConcurrentDeploys > FriendDeployTracker.MaxConcurrentDeploys) { FriendDeployTracker.MaxConcurrentDeploys = friendData.maxConcurrentDeploys; } float num = ((friendData.detectRadius > 0f) ? friendData.detectRadius : (this.explosionRadius * 0.75f)); detectRadius = num * Mathf.Max(0.01f, friendData.detectRadiusMultiplier); this.damage = damage; this.explosionShake = explosionShake; finished = false; nextScanTime = 0f; nextFireTime = Time.time + 0.35f; droneDiving = false; effectiveFireRate = Mathf.Max(0.25f, friendData.fireRate); if ((mode & FriendDeployMode.Drone) != FriendDeployMode.None) { Vector3 position = ((Component)this).transform.position; position.y += 2.4f; ((Component)this).transform.position = position; } FriendDeployTracker.Register(this); FriendDebugVisual.Attach(((Component)this).transform, FriendDeployModeUtil.GetDebugColor(mode), ((mode & FriendDeployMode.Drone) != FriendDeployMode.None) ? 0.55f : 0.75f); if (friendData.shootToDetonate) { hitProxy = ((Component)this).gameObject.AddComponent(); ((Collider)hitProxy).isTrigger = true; hitProxy.radius = 0.9f; hitProxy.center = Vector3.up * 0.4f; } if (gear != null) { FriendCombatHooks.EnsureBound(gear, friendData); } FriendCombatRunner.Ensure(); ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)($"[FriendinaBox] {FriendDeployModeUtil.GetLabel(mode)} armed at {((Component)this).transform.position} " + $"duration={remainingDuration:0.#}s detect={detectRadius:0.##} explode={this.explosionRadius:0.##}")); } } public void ExtendDuration(float seconds) { if (!finished && !(seconds <= 0f)) { remainingDuration += seconds; maxDuration = Mathf.Max(maxDuration, remainingDuration); ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)$"[FriendinaBox] Duration +{seconds:0.#}s → {remainingDuration:0.#}s remaining"); } } } public void DetonateFromPlayerShot() { if (!finished) { float num = ((maxDuration > 0.01f) ? Mathf.Clamp01(remainingDuration / maxDuration) : 1f); float num2 = Mathf.Lerp(0.35f, 1f, num); explosionRadius *= num2; damage.damage *= num2; damage.effectAmount *= num2; ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)$"[FriendinaBox] Scuttle shot detonate power={num2:P0} remaining={remainingDuration:0.#}s"); } Detonate("player shot"); } } private void OnDestroy() { FriendDeployTracker.Unregister(this); } public void ForceDespawn() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (!finished) { finished = true; ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)$"[FriendinaBox] {FriendDeployModeUtil.GetLabel(mode)} force-despawned at {((Component)this).transform.position}"); } FriendDeployTracker.Unregister(this); Object.Destroy((Object)(object)((Component)this).gameObject); } } private void Update() { if (finished) { return; } remainingDuration -= Time.deltaTime; if (remainingDuration <= 0f) { Expire(); return; } switch (FriendDeployModeUtil.GetPrimary(mode)) { case FriendDeployMode.None: TickMine(); break; case FriendDeployMode.Turret: TickTurret(); break; case FriendDeployMode.Mortar: TickMortar(); break; case FriendDeployMode.Drone: TickDrone(); break; case FriendDeployMode.Turret | FriendDeployMode.Mortar: break; } } private void TickMine() { if (!(Time.time < nextScanTime)) { nextScanTime = Time.time + 0.1f; if (TryFindEnemy(detectRadius, out var _)) { Detonate("proximity"); } } } private float GetTurretRange() { return Mathf.Max(detectRadius, 50f); } private float GetMortarRange() { return Mathf.Max(detectRadius * Mathf.Max(1f, friendData.mortarRangeMultiplier), 50f); } private void TickTurret() { if (!(Time.time < nextFireTime)) { float num = 1f / Mathf.Max(0.25f, effectiveFireRate); if (!TryFindEnemy(GetTurretRange(), out var found)) { nextFireTime = Time.time + 0.1f; return; } nextFireTime = Time.time + num; FireAtTarget(found, lobbed: false); } } private void TickMortar() { if (!(Time.time < nextFireTime)) { float num = 1f / Mathf.Max(0.15f, effectiveFireRate * 0.55f); if (!TryFindEnemy(GetMortarRange(), out var found)) { nextFireTime = Time.time + 0.1f; return; } nextFireTime = Time.time + num; FireAtTarget(found, lobbed: true); } } private void TickDrone() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0219: 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) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) if (!droneDiving && (Object)(object)ownerPlayer != (Object)null) { Vector3 interpolatedPosition = ownerPlayer.InterpolatedPosition; FriendDeployTracker.GetDroneFormationSlot(this, out var index, out var count); float num = Mathf.Clamp(friendData.droneFollowDistance, 0.75f, 1.6f); if (count > 1) { num = Mathf.Max(num, 1.1f + 0.15f * (float)(count - 1)); } Vector3 droneFormationOffset = FriendDeployTracker.GetDroneFormationOffset(index, count, num); float num2 = ((count > 1) ? (((float)index - (float)(count - 1) * 0.5f) * 0.2f) : 0f); Vector3 val = Quaternion.Euler(0f, ((Component)ownerPlayer).transform.eulerAngles.y, 0f) * droneFormationOffset; Vector3 val2 = interpolatedPosition + Vector3.up * (2.4f + num2) + val; float num3 = Mathf.Max(1f, friendData.droneMoveSpeed); ((Component)this).transform.position = Vector3.MoveTowards(((Component)this).transform.position, val2, num3 * Time.deltaTime); } bool flag = (mode & FriendDeployMode.Turret) != 0; bool flag2 = (mode & FriendDeployMode.Mortar) != 0; if (flag || flag2) { if (Time.time >= nextFireTime) { float radius = (flag2 ? GetMortarRange() : GetTurretRange()); if (TryFindEnemy(radius, out var found)) { float num4 = effectiveFireRate * ((flag2 && !flag) ? 0.55f : 1f); nextFireTime = Time.time + 1f / Mathf.Max(0.25f, num4); FireAtTarget(found, flag2 && !flag); } else { nextFireTime = Time.time + 0.1f; } } } else if (!droneDiving) { if (Time.time < nextScanTime) { return; } nextScanTime = Time.time + 0.1f; if (TryFindEnemy(detectRadius * 1.5f, out var found2)) { droneDiving = true; droneDiveTarget = ((IFollowable)found2).GetHealthbarPosition(); ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)$"[FriendinaBox] Drone diving toward {droneDiveTarget}"); } } } else { float num5 = Mathf.Max(1f, friendData.droneMoveSpeed * 1.75f); ((Component)this).transform.position = Vector3.MoveTowards(((Component)this).transform.position, droneDiveTarget, num5 * Time.deltaTime); Vector3 val3 = ((Component)this).transform.position - droneDiveTarget; if (((Vector3)(ref val3)).sqrMagnitude <= 0.122499995f) { Detonate("suicide dive"); } } } private void FireAtTarget(ITarget target, bool lobbed) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //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_02cb: 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_0075: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Invalid comparison between Unknown and I4 //IL_0293: 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_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) if (target == null) { return; } IGear obj = ownerGear; IDamageSource val = (IDamageSource)(object)((obj is IDamageSource) ? obj : null); if (val == null) { FireExplosionFallback(target, lobbed); return; } Vector3 val2 = ((Component)this).transform.position + Vector3.up * 0.35f; Vector3 healthbarPosition = ((IFollowable)target).GetHealthbarPosition(); Vector3 val3 = healthbarPosition - val2; float magnitude = ((Vector3)(ref val3)).magnitude; if (magnitude < 0.05f) { return; } Vector3 val4 = val3 / magnitude; Quaternion val5 = Quaternion.LookRotation(val4, Vector3.up); float num = Mathf.Max(0.05f, friendData.modeDamageScale); float num2 = damage.damage * num; float num3 = damage.effectAmount * num; EffectType val6 = damage.effect; int num4 = 1; if (friendData.copyPlayerWeaponStats && friendData.weaponStatPortion > 0f && TryGetPrimaryWeaponStats(out var primaryDamage, out var primaryEffect, out var primaryEffectAmount, out var primaryFireInterval, out var primaryBulletsPerShot)) { float num5 = Mathf.Clamp01(friendData.weaponStatPortion); num2 = Mathf.Lerp(num2, primaryDamage * num, num5); num3 = Mathf.Lerp(num3, primaryEffectAmount * num, num5); if (num5 >= 0.25f && (int)primaryEffect > 0) { val6 = primaryEffect; } if (primaryFireInterval > 0.01f) { float num6 = 1f / primaryFireInterval; float num7 = Mathf.Lerp(Mathf.Max(0.25f, friendData.fireRate), num6, num5 * 0.65f); effectiveFireRate = Mathf.Clamp(num7, 0.5f, 12f); } if (!lobbed && primaryBulletsPerShot > 1) { num4 = Mathf.Clamp(Mathf.RoundToInt(Mathf.Lerp(1f, (float)primaryBulletsPerShot, num5)), 1, 8); } } try { bool flag; if (lobbed) { flag = TryFireMortar(val, val2, val5, val4, healthbarPosition, target, num2, num3, val6); } else { flag = false; float num8 = ((num4 > 1) ? Mathf.Lerp(1.2f, 4.5f, (float)(num4 - 1) / 7f) : 0f); for (int i = 0; i < num4; i++) { Quaternion val7 = val5; if (num4 > 1) { float num9 = Random.Range(0f - num8, num8); float num10 = Random.Range((0f - num8) * 0.65f, num8 * 0.65f); val7 = val5 * Quaternion.Euler(num10, num9, 0f); } Vector3 dir = val7 * Vector3.forward; if (TryFireRail(val, val2, val7, dir, num2, num3, val6)) { flag = true; } } } if (!flag) { FireExplosionFallback(target, lobbed, num2, num3, val6); } } catch (Exception ex) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)("[FriendinaBox] Mode fire failed: " + ex.Message)); } FireExplosionFallback(target, lobbed, num2, num3, val6); } } private bool TryGetPrimaryWeaponStats(out float primaryDamage, out EffectType primaryEffect, out float primaryEffectAmount, out float primaryFireInterval, out int primaryBulletsPerShot) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected I4, but got Unknown primaryDamage = 0f; primaryEffect = (EffectType)0; primaryEffectAmount = 0f; primaryFireInterval = 0f; primaryBulletsPerShot = 1; Player val = (((Object)(object)ownerPlayer != (Object)null) ? ownerPlayer : Player.LocalPlayer); if (((val != null) ? val.Gear : null) == null) { return false; } for (int i = 0; i < val.Gear.Length; i++) { IGear val2 = val.Gear[i]; if (val2 != null && !(val2 is Throwable) && (!((Object)(object)((IUpgradable)val2).Info != (Object)null) || (!(((IUpgradable)val2).Info.APIName == "friend_in_a_box") && ((IUpgradable)val2).Info.ID != 92100))) { IWeapon val3 = (IWeapon)(object)((val2 is IWeapon) ? val2 : null); if (val3 != null) { ref GunData gunData = ref val3.GunData; primaryDamage = gunData.damage; primaryEffect = (EffectType)(int)gunData.damageEffect; primaryEffectAmount = gunData.damageEffectAmount; primaryFireInterval = gunData.fireInterval; primaryBulletsPerShot = Mathf.Max(1, gunData.bulletsPerShot); return primaryDamage > 0f; } } } return false; } private bool TryFireRail(IDamageSource source, Vector3 origin, Quaternion rot, Vector3 dir, float shotDamage, float shotEffect, EffectType shotEffectType) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = FriendBulletCache.RailPrefab; if ((Object)(object)prefab == (Object)null) { return false; } GameObject val = SimplePool.Get(prefab); if ((Object)(object)val == (Object)null) { return false; } IBullet component = val.GetComponent(); if (component == null) { SimplePool.Release(prefab, val); return false; } BulletData val2 = new BulletData { position = origin, rotation = rot, direction = dir, gravity = 0f, speed = 400f, damage = shotDamage, damageEffect = shotEffectType, damageEffectAmount = shotEffect, damageFlags = damage.damageFlags, maxBounces = 0, surfaceCollisionMask = 10241, targetCollisionMask = 345216, surfaceMagnetism = 0f, targetMagnetism = 0f, range = LongRangeData, force = 0f, impactSize = 1f }; BulletFlags val3 = (BulletFlags)73; Action action = delegate(IBullet b) { Component val4 = (Component)(object)((b is Component) ? b : null); if (val4 != null && (Object)(object)val4 != (Object)null) { SimplePool.Release(prefab, val4.gameObject); } }; component.Initialize(val2, source, action, val3); return true; } private bool TryFireMortar(IDamageSource source, Vector3 origin, Quaternion rot, Vector3 dir, Vector3 aimPoint, ITarget target, float shotDamage, float shotEffect, EffectType shotEffectType) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_0079: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = FriendBulletCache.MortarPrefab; if ((Object)(object)prefab == (Object)null) { return false; } GameObject val = SimplePool.Get(prefab); if ((Object)(object)val == (Object)null) { return false; } IBullet component = val.GetComponent(); if (component == null) { SimplePool.Release(prefab, val); return false; } float force = Mathf.Max(0.75f, explosionRadius * 0.65f); BulletData val2 = new BulletData { position = origin, rotation = rot, direction = dir, gravity = 18f, speed = 28f, damage = shotDamage, damageEffect = shotEffectType, damageEffectAmount = shotEffect, damageFlags = (DamageFlags)(damage.damageFlags | 0x10), maxBounces = 0, surfaceCollisionMask = 10241, targetCollisionMask = 345216, surfaceMagnetism = 0f, targetMagnetism = 1f, range = LongRangeData, force = force, impactSize = 1f }; BulletFlags val3 = (BulletFlags)73; Action action = delegate(IBullet b) { Component val5 = (Component)(object)((b is Component) ? b : null); if (val5 != null && (Object)(object)val5 != (Object)null) { SimplePool.Release(prefab, val5.gameObject); } }; component.Initialize(val2, source, action, val3); RocketSalvoBullet val4 = (RocketSalvoBullet)(object)((component is RocketSalvoBullet) ? component : null); if (val4 != null) { val4.SetTarget(target, aimPoint, -1f); } return true; } private void FireExplosionFallback(ITarget target, bool lobbed, float shotDamageOverride = -1f, float shotEffectOverride = -1f, EffectType? shotEffectTypeOverride = null) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) if (target == null || (Object)(object)GameManager.Instance == (Object)null) { return; } Vector3 healthbarPosition = ((IFollowable)target).GetHealthbarPosition(); float num = Mathf.Max(0.05f, friendData.modeDamageScale); float num2 = ((shotDamageOverride >= 0f) ? shotDamageOverride : (damage.damage * num)); float num3 = ((shotEffectOverride >= 0f) ? shotEffectOverride : (damage.effectAmount * num)); EffectType val = (EffectType)(((??)shotEffectTypeOverride) ?? damage.effect); DamageData val2 = default(DamageData); ((DamageData)(ref val2))..ctor(num2, val, num3, damage.damageFlags); float num4 = (lobbed ? (explosionRadius * 0.65f) : Mathf.Max(0.4f, explosionRadius * 0.2f)); try { IGear obj = ownerGear; Throwable val3 = (Throwable)(object)((obj is Throwable) ? obj : null); if (val3 != null) { GameManager.Instance.SpawnExplosionFirstPerson((IDamageSource)(object)val3, healthbarPosition, num4, (TargetType)5, val2, lobbed ? (explosionShake * 0.35f) : (explosionShake * 0.15f)); return; } IGear obj2 = ownerGear; IDamageSource val4 = (IDamageSource)(object)((obj2 is IDamageSource) ? obj2 : null); if (val4 != null) { GameManager.Instance.SpawnExplosionFirstPerson(val4, healthbarPosition, num4, (TargetType)5, val2, lobbed ? (explosionShake * 0.35f) : (explosionShake * 0.15f)); } } catch (Exception ex) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)("[FriendinaBox] Fallback fire failed: " + ex.Message)); } } } private bool TryFindEnemy(float radius, out ITarget found) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) found = null; float num = float.MaxValue; Vector3 position = ((Component)this).transform.position; if (friendData.sharePlayerTarget && FriendSharedTarget.TryGet(position, radius, out var target) && IsValidEnemy(target)) { found = target; return true; } Vector3 val2; try { TargetEnumerator val = default(TargetEnumerator); try { if (((TargetEnumerator)(ref val)).GetTargetsInSphere(position, radius, -1, (TargetType)1)) { while (((TargetEnumerator)(ref val)).MoveNext()) { ITarget current = ((TargetEnumerator)(ref val)).Current; if (IsValidEnemy(current)) { val2 = ((IFollowable)current).GetHealthbarPosition() - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; found = current; } } } } } finally { ((IDisposable)val).Dispose(); } } catch { } if (found != null) { return true; } Collider[] array = Physics.OverlapSphere(position, radius, -1, (QueryTriggerInteraction)2); for (int i = 0; i < array.Length; i++) { ITarget target2 = IDamageSource.GetTarget((Component)(object)array[i]); if (IsValidEnemy(target2)) { val2 = ((IFollowable)target2).GetHealthbarPosition() - position; float sqrMagnitude2 = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude2 < num) { num = sqrMagnitude2; found = target2; } } } return found != null; } private static bool IsValidEnemy(ITarget target) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0028: Invalid comparison between Unknown and I4 //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (target == null || !target.IsAlive) { return false; } if (target.IsPlayer()) { return false; } if ((((IFollowable)target).Type & 1) != 0 || (int)((IFollowable)target).Type == 1) { return true; } if ((((IFollowable)target).Type & 2) == 0 && target.MaxHealth > 0f) { return true; } return false; } private void Detonate(string reason) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) if (finished) { return; } finished = true; Vector3 position = ((Component)this).transform.position; ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)$"[FriendinaBox] {FriendDeployModeUtil.GetLabel(mode)} detonated ({reason}) at {position}"); } try { if (!((Object)(object)GameManager.Instance != (Object)null)) { goto IL_0086; } IGear obj = ownerGear; Throwable val = (Throwable)(object)((obj is Throwable) ? obj : null); if (val == null) { goto IL_0086; } GameManager.Instance.SpawnExplosionFirstPerson((IDamageSource)(object)val, position, explosionRadius, (TargetType)5, damage, explosionShake); goto end_IL_0049; IL_0086: if ((Object)(object)GameManager.Instance != (Object)null) { IGear obj2 = ownerGear; IDamageSource val2 = (IDamageSource)(object)((obj2 is IDamageSource) ? obj2 : null); if (val2 != null) { GameManager.Instance.SpawnExplosionFirstPerson(val2, position, explosionRadius, (TargetType)5, damage, explosionShake); } } end_IL_0049:; } catch (Exception arg) { ManualLogSource logger2 = FriendinaBoxPlugin.Logger; if (logger2 != null) { logger2.LogError((object)$"[FriendinaBox] Detonate failed: {arg}"); } } FriendDeployTracker.Unregister(this); Object.Destroy((Object)(object)((Component)this).gameObject); } private void Expire() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) if (!finished) { finished = true; Vector3 position = ((Component)this).transform.position; ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)$"[FriendinaBox] {FriendDeployModeUtil.GetLabel(mode)} expired at {position}"); } TrySpawnAcidPuddle(position); TryGrantExpireMoveSpeed(); FriendDeployTracker.Unregister(this); Object.Destroy((Object)(object)((Component)this).gameObject); } } private void TrySpawnAcidPuddle(Vector3 pos) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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) if (friendData.acidPuddleOnExpireDuration <= 0f) { return; } try { if (!((Object)(object)GameManager.Instance == (Object)null) && ownerGear is IDamageSource) { float num = Mathf.Max(1f, explosionRadius * 0.85f); IGear obj = ownerGear; NetworkBehaviourReference networkRef = IDamageSource.GetNetworkRef((IDamageSource)(object)((obj is IDamageSource) ? obj : null)); GameManager.Instance.SpawnAcidPuddle_Rpc(networkRef, pos, friendData.acidPuddleOnExpireDuration, (TargetType)5, num); } } catch (Exception ex) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)("[FriendinaBox] Acid puddle spawn failed: " + ex.Message)); } } } private void TryGrantExpireMoveSpeed() { if (!(friendData.expireMoveSpeedBonus <= 0f) && !(friendData.expireMoveSpeedDuration <= 0f)) { Player val = (((Object)(object)ownerPlayer != (Object)null) ? ownerPlayer : Player.LocalPlayer); if (!((Object)(object)val == (Object)null) && ((NetworkBehaviour)val).IsLocalPlayer) { FriendExpireSpeedBuff.Apply(val, friendData.expireMoveSpeedBonus, friendData.expireMoveSpeedDuration); } } } } [Flags] public enum FriendDeployMode : byte { None = 0, Turret = 1, Mortar = 2, Drone = 4 } public static class FriendDeployModeUtil { public static FriendDeployMode GetPrimary(FriendDeployMode flags) { if ((flags & FriendDeployMode.Drone) != FriendDeployMode.None) { return FriendDeployMode.Drone; } if ((flags & FriendDeployMode.Mortar) != FriendDeployMode.None) { return FriendDeployMode.Mortar; } if ((flags & FriendDeployMode.Turret) != FriendDeployMode.None) { return FriendDeployMode.Turret; } return FriendDeployMode.None; } public static string GetLabel(FriendDeployMode flags) { switch (GetPrimary(flags)) { case FriendDeployMode.Drone: if ((flags & FriendDeployMode.Turret) != FriendDeployMode.None && (flags & FriendDeployMode.Mortar) != FriendDeployMode.None) { return "Drone+Turret+Mortar"; } if ((flags & FriendDeployMode.Turret) != FriendDeployMode.None) { return "Drone+Turret"; } if ((flags & FriendDeployMode.Mortar) != FriendDeployMode.None) { return "Drone+Mortar"; } return "Drone"; case FriendDeployMode.Mortar: return "Mortar"; case FriendDeployMode.Turret: return "Turret"; default: return "Mine"; } } public static Color GetDebugColor(FriendDeployMode flags) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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) return (Color)(GetPrimary(flags) switch { FriendDeployMode.Drone => new Color(0.2f, 0.85f, 1f), FriendDeployMode.Mortar => new Color(1f, 0.55f, 0.1f), FriendDeployMode.Turret => new Color(0.3f, 1f, 0.35f), _ => Color.magenta, }); } } public static class FriendDeployTracker { private static readonly List Active = new List(); public static int MaxConcurrentDeploys { get; set; } = 1; public static int ActiveCount { get { PruneNulls(); return Active.Count; } } public static bool HasActive => ActiveCount > 0; public static void Register(FriendDeployable deployable) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)deployable == (Object)null) { return; } PruneNulls(); if (Active.Contains(deployable)) { return; } int num = Mathf.Max(1, MaxConcurrentDeploys); while (Active.Count >= num) { FriendDeployable friendDeployable = Active[0]; Active.RemoveAt(0); if ((Object)(object)friendDeployable != (Object)null) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)$"[FriendinaBox] Replacing oldest deployable at {((Component)friendDeployable).transform.position} (max concurrent={num})."); } friendDeployable.ForceDespawn(); } } Active.Add(deployable); } public static void Unregister(FriendDeployable deployable) { if (!((Object)(object)deployable == (Object)null)) { Active.Remove(deployable); } } public static void Clear() { for (int num = Active.Count - 1; num >= 0; num--) { FriendDeployable friendDeployable = Active[num]; if ((Object)(object)friendDeployable != (Object)null) { friendDeployable.ForceDespawn(); } } Active.Clear(); } public static void ExtendAllDurations(float seconds) { if (!(seconds <= 0f)) { PruneNulls(); for (int i = 0; i < Active.Count; i++) { Active[i]?.ExtendDuration(seconds); } } } public static FriendDeployable FindNearest(Vector3 point, float maxDistance) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0035: Unknown result type (might be due to invalid IL or missing references) PruneNulls(); FriendDeployable result = null; float num = maxDistance * maxDistance; for (int i = 0; i < Active.Count; i++) { FriendDeployable friendDeployable = Active[i]; if (!((Object)(object)friendDeployable == (Object)null)) { Vector3 val = ((Component)friendDeployable).transform.position - point; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude <= num) { num = sqrMagnitude; result = friendDeployable; } } } return result; } public static void GetDroneFormationSlot(FriendDeployable self, out int index, out int count) { index = 0; count = 0; if ((Object)(object)self == (Object)null) { return; } PruneNulls(); int num = 0; for (int i = 0; i < Active.Count; i++) { FriendDeployable friendDeployable = Active[i]; if (!((Object)(object)friendDeployable == (Object)null) && (friendDeployable.Mode & FriendDeployMode.Drone) != FriendDeployMode.None) { if ((Object)(object)friendDeployable == (Object)(object)self) { num = count; } count++; } } index = num; if (count < 1) { count = 1; } } public static void GetAlliesInRadius(Vector3 origin, float radius, List buffer) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0042: Unknown result type (might be due to invalid IL or missing references) buffer.Clear(); if (radius <= 0f) { return; } PruneNulls(); float num = radius * radius; for (int i = 0; i < Active.Count; i++) { FriendDeployable friendDeployable = Active[i]; if (!((Object)(object)friendDeployable == (Object)null)) { Vector3 val = ((Component)friendDeployable).transform.position - origin; if (((Vector3)(ref val)).sqrMagnitude <= num) { buffer.Add(friendDeployable); } } } } public static void GetDronesInRadius(Vector3 origin, float radius, List buffer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) GetAlliesInRadius(origin, radius, buffer); } public static Vector3 GetDroneFormationOffset(int index, int count, float radius) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) count = Mathf.Max(1, count); radius = Mathf.Max(0.5f, radius); if (count == 1) { return new Vector3(0.45f, 0f, 0f - radius); } float num = (float)index / (float)count * MathF.PI * 2f + MathF.PI; float num2 = Mathf.Sin(num) * radius; float num3 = Mathf.Cos(num) * radius; return new Vector3(num2, 0f, num3); } private static void PruneNulls() { for (int num = Active.Count - 1; num >= 0; num--) { if ((Object)(object)Active[num] == (Object)null) { Active.RemoveAt(num); } } } } public sealed class FriendExpireSpeedBuff : MonoBehaviour { private Player player; private float bonus; private float remaining; private RefAction onSetSpeed; private bool ended; public static void Apply(Player target, float speedBonus, float duration) { if (!((Object)(object)target == (Object)null) && !(speedBonus <= 0f) && !(duration <= 0f)) { FriendExpireSpeedBuff component = ((Component)target).GetComponent(); if ((Object)(object)component != (Object)null) { component.bonus = Mathf.Max(component.bonus, speedBonus); component.remaining = Mathf.Max(component.remaining, duration); } else { ((Component)target).gameObject.AddComponent().StartBuff(target, speedBonus, duration); } } } private void StartBuff(Player target, float speedBonus, float duration) { //IL_002e: 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_003e: Unknown result type (might be due to invalid IL or missing references) player = target; bonus = speedBonus; remaining = duration; onSetSpeed = ModifyMoveSpeed; Player obj = player; obj.OnSetMovementSpeed += onSetSpeed; ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)$"[FriendinaBox] Expire speed buff +{bonus:P0} for {duration:0.#}s"); } } private void ModifyMoveSpeed(ref float speed) { speed *= 1f + bonus; } private void Update() { remaining -= Time.deltaTime; if (remaining <= 0f) { EndBuff(); } } private void OnDestroy() { EndBuff(); } private void EndBuff() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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) if (ended) { return; } ended = true; if ((Object)(object)player != (Object)null && onSetSpeed != null) { try { Player obj = player; obj.OnSetMovementSpeed -= onSetSpeed; } catch { } } player = null; onSetSpeed = null; if ((Object)(object)this != (Object)null) { Object.Destroy((Object)(object)this); } } } [HarmonyPatch(typeof(GrenadeBullet), "Detonate")] internal static class FriendGrenadeHooks { [HarmonyPrefix] private static bool Prefix(GrenadeBullet __instance) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: 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) //IL_0178: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__instance == (Object)null) { return true; } IDamageSource parentSource = ((SimpleProjectileBullet)__instance).ParentSource; IGear val = (IGear)(object)((parentSource is IGear) ? parentSource : null); if (val == null) { return true; } if (!FriendinaBoxBehaviour.TryGet(val, out var behaviour)) { return true; } Throwable val2 = (Throwable)(object)((val is Throwable) ? val : null); if (val2 != null && ((Object)(object)val2.Player == (Object)null || !((NetworkBehaviour)val2.Player).IsLocalPlayer)) { return false; } Vector3 pos = (Vector3)(((Object)(object)((Component)__instance).transform != (Object)null) ? ((Component)__instance).transform.position : default(Vector3)); TryReadBulletPosition(__instance, ref pos); ref FriendinaBoxBehaviour.Data grenadeData = ref behaviour.GrenadeData; IWeapon val3 = (IWeapon)(object)((val is IWeapon) ? val : null); if (val3 == null) { return true; } float num = Mathf.Max(0.01f, grenadeData.explosionRadiusMultiplier); float explosionRadius = val3.GunData.hitForce * num; DamageData damage = default(DamageData); ((DamageData)(ref damage))..ctor(val3.GunData.damage, val3.GunData.damageEffect, val3.GunData.damageEffectAmount, (DamageFlags)(val3.GunData.damageFlags | 0x10)); float explosionShake = 12f; float selfEffectMultiplier = 1f; GrenadeGear val4 = (GrenadeGear)(object)((val is GrenadeGear) ? val : null); if (val4 != null) { explosionShake = val4.ExplosionShake; selfEffectMultiplier = val4.SelfEffectMultiplier; } int num2 = Mathf.Max(1, grenadeData.maxConcurrentDeploys); if (num2 > FriendDeployTracker.MaxConcurrentDeploys) { FriendDeployTracker.MaxConcurrentDeploys = num2; } for (int i = 0; i < num2; i++) { FriendDeployable.Spawn(pos + GetSquadSpreadOffset(i, num2), val, grenadeData, explosionRadius, damage, explosionShake, selfEffectMultiplier); } try { ((SimpleProjectileBullet)__instance).Kill(); } catch { } return false; } catch (Exception arg) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogError((object)$"[FriendinaBox] Detonate prefix failed: {arg}"); } return true; } } private static void TryReadBulletPosition(GrenadeBullet bullet, ref Vector3 pos) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) FieldInfo fieldInfo = AccessTools.Field(((object)bullet).GetType(), "positionNext") ?? AccessTools.Field(typeof(SimpleProjectileBullet), "positionNext"); if (fieldInfo != null && fieldInfo.GetValue(bullet) is Vector3 val) { pos = val; } } private static Vector3 GetSquadSpreadOffset(int index, int count) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (count <= 1 || index <= 0) { return Vector3.zero; } float num = Mathf.Clamp(1.6f + 0.35f * (float)(count - 2), 1.4f, 3.2f); float num2 = (float)index / (float)count * MathF.PI * 2f + 0.35f; float num3 = (float)index * 0.17f % 0.25f; float num4 = num + num3; return new Vector3(Mathf.Cos(num2) * num4, 0f, Mathf.Sin(num2) * num4); } } public sealed class FriendinaBoxBehaviour : MonoBehaviour { [Serializable] public struct Data { public float deployDuration; public float detectRadius; public float detectRadiusMultiplier; public float explosionRadiusMultiplier; public float acidPuddleOnExpireDuration; public float expireMoveSpeedBonus; public float expireMoveSpeedDuration; public FriendDeployMode deployMode; public float fireRate; public float modeDamageScale; public float mortarRangeMultiplier; public float droneFollowDistance; public float droneMoveSpeed; public int maxConcurrentDeploys; public float lifestealFraction; public float chargeOnKill; public float durationOnKill; public float markDamageBonus; public float markDuration; public bool shootToDetonate; public float overhealOnDamaged; public float overhealOnDamagedCooldown; public bool copyPlayerWeaponStats; public float weaponStatPortion; public bool sharePlayerTarget; public float sharedTargetDuration; public bool countsAsSwarmAlly; } [SerializeField] private Data data = CreateDefaultData(); private Data prefabSnapshot = CreateDefaultData(); private string description = "Friend in a Box"; public ref Data GrenadeData => ref data; public string Description => description; public static Data CreateDefaultData() { return new Data { deployDuration = 20f, detectRadius = 0f, detectRadiusMultiplier = 1f, explosionRadiusMultiplier = 1f, acidPuddleOnExpireDuration = 0f, expireMoveSpeedBonus = 0f, expireMoveSpeedDuration = 0f, deployMode = FriendDeployMode.None, fireRate = 2f, modeDamageScale = 0.35f, mortarRangeMultiplier = 1f, droneFollowDistance = 0.85f, droneMoveSpeed = 14f, maxConcurrentDeploys = 1, lifestealFraction = 0f, chargeOnKill = 0f, durationOnKill = 0f, markDamageBonus = 0f, markDuration = 0f, shootToDetonate = false, overhealOnDamaged = 0f, overhealOnDamagedCooldown = 3f, copyPlayerWeaponStats = false, weaponStatPortion = 0f, sharePlayerTarget = false, sharedTargetDuration = 0f, countsAsSwarmAlly = false }; } public void InitializeAsPrefab(string desc) { description = desc ?? "Friend in a Box"; data = CreateDefaultData(); prefabSnapshot = data; } public void RestoreFromPrefab() { data = prefabSnapshot; } public void CapturePrefabSnapshot() { prefabSnapshot = data; } public void CopySnapshotFrom(FriendinaBoxBehaviour template) { if (!((Object)(object)template == (Object)null)) { prefabSnapshot = template.prefabSnapshot; data = prefabSnapshot; description = template.description; } } public static bool TryGet(IGear gear, out FriendinaBoxBehaviour behaviour) { behaviour = null; if ((Object)(object)((gear != null) ? gear.gameObject : null) == (Object)null) { return false; } behaviour = gear.gameObject.GetComponent(); if ((Object)(object)behaviour != (Object)null) { return true; } bool num = (Object)(object)((IUpgradable)gear).Info != (Object)null && ((IUpgradable)gear).Info.APIName == "friend_in_a_box"; FriendinaBoxBehaviour friendinaBoxBehaviour = null; IUpgradable prefab = ((IUpgradable)gear).Prefab; Component val = (Component)(object)((prefab is Component) ? prefab : null); if (val != null) { friendinaBoxBehaviour = val.GetComponent(); } if (!num && (Object)(object)friendinaBoxBehaviour == (Object)null) { return false; } string desc = (((Object)(object)friendinaBoxBehaviour != (Object)null) ? friendinaBoxBehaviour.Description : "Deployable ally grenade. Lands as a proximity mine. While equipped, enables multiplayer-only upgrades in Ouroboros."); behaviour = gear.gameObject.AddComponent(); behaviour.InitializeAsPrefab(desc); if ((Object)(object)friendinaBoxBehaviour != (Object)null) { behaviour.data = friendinaBoxBehaviour.prefabSnapshot; } behaviour.CapturePrefabSnapshot(); return true; } public static bool IsEquippedOnLocalPlayer() { Player localPlayer = Player.LocalPlayer; if (((localPlayer != null) ? localPlayer.Gear : null) == null) { return false; } for (int i = 0; i < localPlayer.Gear.Length; i++) { IGear val = localPlayer.Gear[i]; if (val != null) { if ((Object)(object)((IUpgradable)val).Info != (Object)null && ((IUpgradable)val).Info.APIName == "friend_in_a_box") { return true; } if ((Object)(object)((IUpgradable)val).Info != (Object)null && ((IUpgradable)val).Info.ID == 92100) { return true; } if ((Object)(object)val.gameObject != (Object)null && (Object)(object)val.gameObject.GetComponent() != (Object)null) { return true; } } } return false; } public static bool TryGetEquipped(out FriendinaBoxBehaviour behaviour, out IGear gear) { behaviour = null; gear = null; Player localPlayer = Player.LocalPlayer; if (((localPlayer != null) ? localPlayer.Gear : null) == null) { return false; } for (int i = 0; i < localPlayer.Gear.Length; i++) { IGear val = localPlayer.Gear[i]; if (val != null && TryGet(val, out var behaviour2)) { if ((Object)(object)((IUpgradable)val).Info != (Object)null && (((IUpgradable)val).Info.APIName == "friend_in_a_box" || ((IUpgradable)val).Info.ID == 92100)) { behaviour = behaviour2; gear = val; return true; } if ((Object)(object)behaviour == (Object)null) { behaviour = behaviour2; gear = val; } } } return (Object)(object)behaviour != (Object)null; } } public sealed class FriendMarkedTarget : MonoBehaviour { private static readonly List Active = new List(); private ITarget target; private float bonus; private float remaining; public static void Apply(ITarget enemy, float damageBonus, float duration) { if (enemy == null || !enemy.IsAlive || damageBonus <= 0f || duration <= 0f) { return; } Component val = (Component)(object)((enemy is Component) ? enemy : null); if (!((Object)(object)val == (Object)null)) { FriendMarkedTarget component = val.GetComponent(); if ((Object)(object)component != (Object)null) { component.bonus = Mathf.Max(component.bonus, damageBonus); component.remaining = Mathf.Max(component.remaining, duration); return; } FriendMarkedTarget friendMarkedTarget = val.gameObject.AddComponent(); friendMarkedTarget.target = enemy; friendMarkedTarget.bonus = damageBonus; friendMarkedTarget.remaining = duration; Active.Add(friendMarkedTarget); } } public static bool TryGetBonus(ITarget enemy, out float bonus) { bonus = 0f; if (enemy == null) { return false; } for (int num = Active.Count - 1; num >= 0; num--) { FriendMarkedTarget friendMarkedTarget = Active[num]; if ((Object)(object)friendMarkedTarget == (Object)null) { Active.RemoveAt(num); } else if (friendMarkedTarget.target == enemy && friendMarkedTarget.remaining > 0f) { bonus = friendMarkedTarget.bonus; return true; } } return false; } private void Update() { remaining -= Time.deltaTime; if (remaining <= 0f || target == null || !target.IsAlive) { Object.Destroy((Object)(object)this); } } private void OnDestroy() { Active.Remove(this); } } public static class FriendSharedTarget { private static ITarget current; private static float expireTime; public static bool IsActive { get { if (current != null && current.IsAlive) { return Time.time <= expireTime; } return false; } } public static void Set(ITarget target, float duration) { if (target != null && target.IsAlive && !(duration <= 0f) && !target.IsPlayer()) { current = target; expireTime = Time.time + duration; } } public static void Clear() { current = null; expireTime = 0f; } public static bool TryGet(Vector3 origin, float maxRange, out ITarget target) { //IL_0038: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) target = null; if (current == null) { return false; } if (Time.time > expireTime || !current.IsAlive) { Clear(); return false; } if (maxRange > 0f) { Vector3 val = ((IFollowable)current).GetHealthbarPosition() - origin; if (((Vector3)(ref val)).sqrMagnitude > maxRange * maxRange) { return false; } } target = current; return true; } } [Serializable] public class FriendWiderNetProperty : UpgradeProperty { public Range radiusBonus = new Range(0.2f, 0.35f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Mine radius:", radiusBonus, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = radiusBonus.GetValue(ref rand, upgrade, default(BoostParams)); float num = 1f + value; behaviour.GrenadeData.explosionRadiusMultiplier *= num; behaviour.GrenadeData.detectRadiusMultiplier *= num; IWeapon val = (IWeapon)(object)((gear is IWeapon) ? gear : null); if (val != null) { val.GunData.hitForce *= num; } } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } IWeapon val = (IWeapon)(object)((gear is IWeapon) ? gear : null); if (val != null) { IWeapon val2 = (IWeapon)(object)((prefab is IWeapon) ? prefab : null); if (val2 != null) { val.GunData.hitForce = val2.GunData.hitForce; } } } } [Serializable] public class FriendLongWatchProperty : UpgradeProperty { public Range durationBonus = new Range(0.35f, 0.5f); public Range cooldownPenalty = new Range(0.2f, 0.3f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Mine duration:", durationBonus, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range?)null); yield return StatData.Create("Cooldown:", cooldownPenalty, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = durationBonus.GetValue(ref rand, upgrade, default(BoostParams)); float value2 = cooldownPenalty.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.deployDuration *= 1f + value; Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null); if (val != null) { val.CooldownData.rechargeDuration *= 1f + value2; } } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null); if (val != null) { Throwable val2 = (Throwable)(object)((prefab is Throwable) ? prefab : null); if (val2 != null) { val.CooldownData.rechargeDuration = val2.CooldownData.rechargeDuration; } } } } [Serializable] public class FriendQuickDeployProperty : UpgradeProperty { public Range cooldownBonus = new Range(0.2f, 0.3f); public Range durationPenalty = new Range(0.15f, 0.25f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Cooldown:", cooldownBonus, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range?)null); yield return StatData.Create("Mine duration:", durationPenalty, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = cooldownBonus.GetValue(ref rand, upgrade, default(BoostParams)); float value2 = durationPenalty.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.deployDuration *= Mathf.Max(0.25f, 1f - value2); Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null); if (val != null) { val.CooldownData.rechargeDuration *= Mathf.Max(0.25f, 1f - value); } } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null); if (val != null) { Throwable val2 = (Throwable)(object)((prefab is Throwable) ? prefab : null); if (val2 != null) { val.CooldownData.rechargeDuration = val2.CooldownData.rechargeDuration; } } } } [Serializable] public class FriendLingeringGiftProperty : UpgradeProperty { public Range puddleDuration = new Range(4f, 6f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Expire acid puddle:", puddleDuration, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = puddleDuration.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.acidPuddleOnExpireDuration = Mathf.Max(behaviour.GrenadeData.acidPuddleOnExpireDuration, value); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendPartingBoostProperty : UpgradeProperty { public Range speedBonus = new Range(0.2f, 0.3f); public Range buffDuration = new Range(4f, 6f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Expire move speed:", speedBonus, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range?)null); yield return StatData.Create("Boost duration:", buffDuration, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = speedBonus.GetValue(ref rand, upgrade, default(BoostParams)); float value2 = buffDuration.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.expireMoveSpeedBonus = Mathf.Max(behaviour.GrenadeData.expireMoveSpeedBonus, value); behaviour.GrenadeData.expireMoveSpeedDuration = Mathf.Max(behaviour.GrenadeData.expireMoveSpeedDuration, value2); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendTurretModeProperty : UpgradeProperty { public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Deploy mode:", new Range(1, 1), upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams)); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.GrenadeData.deployMode |= FriendDeployMode.Turret; } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendMortarModeProperty : UpgradeProperty { public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Deploy mode:", new Range(1, 1), upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams)); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.GrenadeData.deployMode |= FriendDeployMode.Mortar; } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendDroneModeProperty : UpgradeProperty { public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Deploy mode:", new Range(1, 1), upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams)); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.GrenadeData.deployMode |= FriendDeployMode.Drone; } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendSquadDropProperty : UpgradeProperty { public Range extraDeploys = new Range(1, 1); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Deploys per throw:", extraDeploys, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams)); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { int value = extraDeploys.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.maxConcurrentDeploys = Mathf.Max(1, behaviour.GrenadeData.maxConcurrentDeploys + value); FriendDeployTracker.MaxConcurrentDeploys = Mathf.Max(FriendDeployTracker.MaxConcurrentDeploys, behaviour.GrenadeData.maxConcurrentDeploys); FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } FriendDeployTracker.MaxConcurrentDeploys = 1; } } [Serializable] public class FriendSympatheticLinkProperty : UpgradeProperty { public Range lifesteal = new Range(0.08f, 0.15f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Overheal from damage:", lifesteal, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = lifesteal.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.lifestealFraction = Mathf.Max(behaviour.GrenadeData.lifestealFraction, value); FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendFieldRechargeProperty : UpgradeProperty { public Range charge = new Range(0.25f, 0.4f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Charge on kill:", charge, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = charge.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.chargeOnKill = Mathf.Max(behaviour.GrenadeData.chargeOnKill, value); FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendOvertimeProperty : UpgradeProperty { public Range seconds = new Range(1.5f, 2.5f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Duration on kill:", seconds, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = seconds.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.durationOnKill = Mathf.Max(behaviour.GrenadeData.durationOnKill, value); FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendPaintedTargetsProperty : UpgradeProperty { public Range bonus = new Range(0.15f, 0.25f); public Range duration = new Range(4f, 6f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Mark damage:", bonus, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range?)null); yield return StatData.Create("Mark duration:", duration, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = bonus.GetValue(ref rand, upgrade, default(BoostParams)); float value2 = duration.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.markDamageBonus = Mathf.Max(behaviour.GrenadeData.markDamageBonus, value); behaviour.GrenadeData.markDuration = Mathf.Max(behaviour.GrenadeData.markDuration, value2); FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendScuttleChargeProperty : UpgradeProperty { public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Shoot to detonate:", new Range(1, 1), upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams)); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.GrenadeData.shootToDetonate = true; FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); FriendCombatRunner.Ensure(); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendCalibratedLinkProperty : UpgradeProperty { public Range portion = new Range(0.35f, 0.55f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Weapon stat share:", portion, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = portion.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.copyPlayerWeaponStats = true; behaviour.GrenadeData.weaponStatPortion = Mathf.Max(behaviour.GrenadeData.weaponStatPortion, Mathf.Clamp01(value)); FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendDesignatedTargetProperty : UpgradeProperty { public Range duration = new Range(4f, 6f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Focus duration:", duration, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = duration.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.sharePlayerTarget = true; behaviour.GrenadeData.sharedTargetDuration = Mathf.Max(behaviour.GrenadeData.sharedTargetDuration, value); FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendHiveKinProperty : UpgradeProperty { public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Swarm ally deploys:", new Range(1, 1), upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams)); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.GrenadeData.countsAsSwarmAlly = true; FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } [Serializable] public class FriendReactiveShellProperty : UpgradeProperty { public Range overheal = new Range(8f, 14f); public Range cooldown = new Range(2.5f, 4f); public override IEnumerator GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) yield return StatData.Create("Overheal on hit:", overheal, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range?)null); yield return StatData.Create("Overheal cooldown:", cooldown, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range?)null); } public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { float value = overheal.GetValue(ref rand, upgrade, default(BoostParams)); float value2 = cooldown.GetValue(ref rand, upgrade, default(BoostParams)); behaviour.GrenadeData.overhealOnDamaged = Mathf.Max(behaviour.GrenadeData.overhealOnDamaged, value); if (behaviour.GrenadeData.overhealOnDamagedCooldown <= 0f) { behaviour.GrenadeData.overhealOnDamagedCooldown = value2; } else { behaviour.GrenadeData.overhealOnDamagedCooldown = Mathf.Min(behaviour.GrenadeData.overhealOnDamagedCooldown, value2); } FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData); } } public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand) { if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour)) { behaviour.RestoreFromPrefab(); } } } public static class GrenadeRegistration { public static IUpgradable CatalogGear { get; private set; } public static GrenadeGear BaseGrenadePrefab { get; private set; } public static GameObject BaseNetworkPrefab { get; private set; } public static int BaseAllGearIndex { get; private set; } = -1; public static void SetBaseAllGearIndex(int index) { BaseAllGearIndex = index; } public static bool TryCreateAndRegister(string modGuid, int gearId, string apiName, string displayName, string description, string baseTypeName, bool autoUnlock, ManualLogSource log, out IUpgradable registeredGear) { registeredGear = null; if (string.IsNullOrEmpty(modGuid) || string.IsNullOrEmpty(apiName)) { if (log != null) { log.LogError((object)"[GrenadeRegistration] modGuid / apiName required."); } return false; } if ((Object)(object)Global.Instance == (Object)null || Global.Instance.AllGear == null) { if (log != null) { log.LogError((object)"[GrenadeRegistration] Global.Instance.AllGear is null."); } return false; } IUpgradable val = FindExistingInAllGear(apiName, gearId); if (val != null) { CatalogGear = val; registeredGear = val; TryRefreshBaseIndex(baseTypeName, log); InjectIntoPlayerData(val, autoUnlock, log); if (log != null) { log.LogInfo((object)("[GrenadeRegistration] Gear '" + apiName + "' already present — reusing.")); } return true; } if (!TryFindBaseGrenade(baseTypeName, log, out var gear, out var go, out var allGearIndex)) { return false; } BaseGrenadePrefab = gear; BaseNetworkPrefab = go; BaseAllGearIndex = allGearIndex; if (log != null) { log.LogInfo((object)$"[GrenadeRegistration] Base spawn prefab index={allGearIndex} type={((object)gear).GetType().Name}."); } GameObject val2 = Object.Instantiate(go); ((Object)val2).name = "[" + modGuid + "] " + displayName; val2.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val2); NetworkObject val3 = default(NetworkObject); if (val2.TryGetComponent(ref val3)) { Object.DestroyImmediate((Object)(object)val3); if (log != null) { log.LogDebug((object)"[GrenadeRegistration] Stripped NetworkObject from catalog clone (spawn uses base prefab)."); } } GrenadeGear component = val2.GetComponent(); if ((Object)(object)component == (Object)null) { if (log != null) { log.LogError((object)"[GrenadeRegistration] Clone lost GrenadeGear component."); } Object.Destroy((Object)(object)val2); return false; } GearInfo val4 = CreateGearInfo(gearId, apiName, displayName, ((Throwable)gear).Info, autoUnlock, log); if ((Object)(object)val4 == (Object)null) { Object.Destroy((Object)(object)val2); return false; } if (!TryAssignGearInfo(component, val4, log)) { Object.Destroy((Object)(object)val2); return false; } if ((Object)(object)((Throwable)component).Info == (Object)null || ((Throwable)component).Info.ID != gearId || ((Throwable)component).Info.APIName != apiName) { if (log != null) { log.LogError((object)("[GrenadeRegistration] GearInfo verification failed (Info=" + (((Object)(object)((Throwable)component).Info == (Object)null) ? "null" : (((Throwable)component).Info.APIName + "/" + ((Throwable)component).Info.ID)) + ").")); } Object.Destroy((Object)(object)val2); return false; } FriendinaBoxBehaviour friendinaBoxBehaviour = val2.GetComponent(); if ((Object)(object)friendinaBoxBehaviour == (Object)null) { friendinaBoxBehaviour = val2.AddComponent(); } friendinaBoxBehaviour.InitializeAsPrefab(description); ref GunData gunData = ref ((Throwable)component).GunData; gunData.damage = Mathf.Max(gunData.damage, 1f); if (!InjectIntoAllGear((IUpgradable)(object)component, log)) { Object.Destroy((Object)(object)val2); return false; } InjectIntoPlayerData((IUpgradable)(object)component, autoUnlock, log); ModelImportHooks.ApplyPlaceholderHooks(val2, log); CatalogGear = (IUpgradable)(object)component; registeredGear = (IUpgradable)(object)component; return true; } private static IUpgradable FindExistingInAllGear(string apiName, int gearId) { if (CatalogGear != null && (Object)(object)CatalogGear.Info != (Object)null && (CatalogGear.Info.APIName == apiName || CatalogGear.Info.ID == gearId)) { return CatalogGear; } IUpgradable[] array = Global.Instance?.AllGear; if (array == null) { return null; } foreach (IUpgradable val in array) { if (!((Object)(object)((val != null) ? val.Info : null) == (Object)null) && (val.Info.APIName == apiName || val.Info.ID == gearId)) { return val; } } return null; } public static void EnsurePlayerDataEntry(bool autoUnlock, ManualLogSource log) { if (CatalogGear != null) { InjectIntoPlayerData(CatalogGear, autoUnlock, log); } } private static void TryRefreshBaseIndex(string preferredTypeName, ManualLogSource log) { if (Global.Instance?.AllGear == null) { return; } if ((Object)(object)BaseGrenadePrefab != (Object)null) { int num = Array.IndexOf(Global.Instance.AllGear, (IUpgradable)(object)BaseGrenadePrefab); if (num >= 0) { BaseAllGearIndex = num; return; } } if (TryFindBaseGrenade(preferredTypeName, log, out var gear, out var go, out var allGearIndex)) { BaseGrenadePrefab = gear; BaseNetworkPrefab = go; BaseAllGearIndex = allGearIndex; } } private static bool TryFindBaseGrenade(string preferredTypeName, ManualLogSource log, out GrenadeGear gear, out GameObject go, out int allGearIndex) { gear = null; go = null; allGearIndex = -1; GrenadeGear val = null; GameObject val2 = null; int num = -1; IUpgradable[] allGear = Global.Instance.AllGear; for (int i = 0; i < allGear.Length; i++) { IUpgradable obj = allGear[i]; GrenadeGear val3 = (GrenadeGear)(object)((obj is GrenadeGear) ? obj : null); if (val3 == null) { continue; } GameObject gameObject = ((Component)val3).gameObject; string name = ((object)val3).GetType().Name; if (!string.IsNullOrEmpty(preferredTypeName) && string.Equals(name, preferredTypeName, StringComparison.Ordinal)) { gear = val3; go = gameObject; allGearIndex = i; if (log != null) { log.LogInfo((object)$"[GrenadeRegistration] Base grenade: {name} ({((Object)gameObject).name}) index={i}."); } return true; } if ((Object)(object)val == (Object)null) { val = val3; val2 = gameObject; num = i; } } if ((Object)(object)val != (Object)null) { gear = val; go = val2; allGearIndex = num; if (log != null) { log.LogWarning((object)("[GrenadeRegistration] '" + preferredTypeName + "' not found — " + $"falling back to {((object)val).GetType().Name} ({((Object)val2).name}) index={num}.")); } return true; } if (log != null) { log.LogError((object)"[GrenadeRegistration] No GrenadeGear found in Global.AllGear."); } return false; } private static GearInfo CreateGearInfo(int gearId, string apiName, string displayName, GearInfo template, bool autoUnlock, ManualLogSource log) { //IL_0126: Unknown result type (might be due to invalid IL or missing references) GearInfo val = ScriptableObject.CreateInstance(); ((Object)val).name = apiName; TrySetMember(val, "ID", gearId); TrySetMember(val, "k__BackingField", gearId); TrySetMember(val, "_name", apiName); TrySetMember(val, "id", gearId); if ((Object)(object)template != (Object)null) { object member = GetMember(template, "grid"); if (member != null) { TrySetMember(val, "grid", member); } if ((Object)(object)template.Icon != (Object)null) { TrySetMember(val, "k__BackingField", template.Icon); } if (template.UnlockCost != null) { val.UnlockCost = template.UnlockCost; } val.CanGainXP = template.CanGainXP; val.XPGainMultilier = template.XPGainMultilier; val.MaxLevel = template.MaxLevel; val.MinUnlockLevel = 0; val.HideWhenNotCollected = false; } else if ((Object)(object)Global.Instance != (Object)null && (Object)(object)Global.Instance.WarningIcon != (Object)null) { TrySetMember(val, "k__BackingField", Global.Instance.WarningIcon); } val.UnlockAutomatically = autoUnlock; val.UnlockState = (UnlockState)(autoUnlock ? 2 : 0); TrySetMember(val, "upgrades", Array.Empty()); TrySetMember(val, "skins", Array.Empty()); if (log != null) { log.LogDebug((object)$"[GrenadeRegistration] GearInfo created id={gearId} api={apiName} name={displayName}"); } return val; } private static bool TryAssignGearInfo(GrenadeGear gear, GearInfo info, ManualLogSource log) { if (TrySetMember(gear, "k__BackingField", info) || TrySetMember(gear, "Info", info)) { return true; } if (log != null) { log.LogError((object)"[GrenadeRegistration] Failed to assign GearInfo onto clone (reflection)."); } return false; } private static bool InjectIntoAllGear(IUpgradable gear, ManualLogSource log) { IUpgradable[] allGear = Global.Instance.AllGear; for (int i = 0; i < allGear.Length; i++) { if (allGear[i] != null && (Object)(object)allGear[i].Info != (Object)null && allGear[i].Info.ID == gear.Info.ID) { if (log != null) { log.LogWarning((object)$"[GrenadeRegistration] AllGear already contains id={gear.Info.ID}."); } return true; } } IUpgradable[] array = (IUpgradable[])(object)new IUpgradable[allGear.Length + 1]; Array.Copy(allGear, array, allGear.Length); array[allGear.Length] = gear; Global.Instance.AllGear = array; Component val = (Component)(object)((gear is Component) ? gear : null); if (val != null) { TryAppendObjectArray(Global.Instance, "_allGear", (Object)(object)val.gameObject); } if (log != null) { log.LogInfo((object)$"[GrenadeRegistration] Injected into AllGear (count={array.Length})."); } return true; } private static void InjectIntoPlayerData(IUpgradable gear, bool autoUnlock, ManualLogSource log) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown if ((Object)(object)((gear != null) ? gear.Info : null) == (Object)null) { return; } if (PlayerData.Instance == null) { if (log != null) { log.LogDebug((object)"[GrenadeRegistration] PlayerData.Instance null — defer GearData inject."); } return; } try { GearData val = null; try { val = PlayerData.GetGearData(gear); } catch { val = null; } if (val != null) { val.Gear = gear; if (autoUnlock) { val.Unlock(); } if (log != null) { log.LogInfo((object)"[GrenadeRegistration] Updated existing GearData entry."); } } else if (typeof(PlayerData).GetField("collectedGear", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(PlayerData.Instance) is IDictionary dictionary) { GearData val2 = new GearData(gear, (UnlockState)(autoUnlock ? 2 : 0)); dictionary[gear.Info.ID] = val2; if (autoUnlock) { val2.Unlock(); } if (log != null) { log.LogInfo((object)"[GrenadeRegistration] Added GearData to collectedGear."); } } else if (log != null) { log.LogDebug((object)"[GrenadeRegistration] collectedGear not ready — will retry after OnAwake."); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("[GrenadeRegistration] InjectIntoPlayerData deferred: " + ex.Message)); } } } private static void TryRegisterNetworkPrefab(GameObject prefab, ManualLogSource log) { try { NetworkManager val = NetworkManager.Singleton; if ((Object)(object)val == (Object)null && (Object)(object)Global.Instance != (Object)null) { val = Global.Instance.NetworkManager; } if ((Object)(object)val == (Object)null) { if (log != null) { log.LogDebug((object)"[GrenadeRegistration] NetworkManager not ready — skip AddNetworkPrefab (ok at boot)."); } return; } MethodInfo method = ((object)val).GetType().GetMethod("AddNetworkPrefab", new Type[1] { typeof(GameObject) }); if (method != null) { method.Invoke(val, new object[1] { prefab }); if (log != null) { log.LogInfo((object)"[GrenadeRegistration] AddNetworkPrefab succeeded."); } return; } object obj = ((object)val).GetType().GetProperty("NetworkConfig")?.GetValue(val); if (obj != null) { object obj2 = obj.GetType().GetProperty("Prefabs")?.GetValue(obj); MethodInfo methodInfo = obj2?.GetType().GetMethod("Add", new Type[1] { typeof(GameObject) }) ?? obj2?.GetType().GetMethod("Add", new Type[1] { typeof(NetworkPrefab) }); if (methodInfo != null) { if (methodInfo.GetParameters()[0].ParameterType == typeof(GameObject)) { methodInfo.Invoke(obj2, new object[1] { prefab }); } else { object obj3 = Activator.CreateInstance(methodInfo.GetParameters()[0].ParameterType); TrySetMember(obj3, "Prefab", prefab); methodInfo.Invoke(obj2, new object[1] { obj3 }); } if (log != null) { log.LogInfo((object)"[GrenadeRegistration] NetworkConfig.Prefabs add succeeded."); } return; } } if (log != null) { log.LogDebug((object)"[GrenadeRegistration] No network prefab API found — multiplayer may need a real AssetBundle prefab."); } } catch (Exception ex) { if (log != null) { log.LogWarning((object)("[GrenadeRegistration] Network prefab registration failed: " + ex.Message)); } } } private static bool TrySetMember(object target, string name, object value) { if (target == null) { return false; } Type type = target.GetType(); PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(target, value); return true; } FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(target, value); return true; } Type baseType = type.BaseType; while (baseType != null) { field = baseType.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(target, value); return true; } property = baseType.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(target, value); return true; } baseType = baseType.BaseType; } return false; } private static object GetMember(object target, string name) { if (target == null) { return null; } Type type = target.GetType(); object obj = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target); if (obj == null) { PropertyInfo? property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)property == null) { return null; } obj = property.GetValue(target); } return obj; } private static void TryAppendObjectArray(object host, string fieldName, Object item) { FieldInfo field = host.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field == null) && field.GetValue(host) is Array array) { Type elementType = array.GetType().GetElementType(); if (!(elementType == null) && elementType.IsInstanceOfType(item)) { Array array2 = Array.CreateInstance(elementType, array.Length + 1); Array.Copy(array, array2, array.Length); array2.SetValue(item, array.Length); field.SetValue(host, array2); } } } } public static class ModelImportHooks { public static void ApplyPlaceholderHooks(GameObject gearRoot, ManualLogSource log) { if (log != null) { log.LogDebug((object)"[ModelImportHooks] Placeholder only — using vanilla Incendiary visuals (Friend in a Box)."); } } public static bool ApplyMesh(GameObject root, Mesh mesh, Material material = null) { if ((Object)(object)root == (Object)null || (Object)(object)mesh == (Object)null) { return false; } MeshFilter componentInChildren = root.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { return false; } componentInChildren.sharedMesh = mesh; MeshRenderer val = default(MeshRenderer); if ((Object)(object)material != (Object)null && ((Component)componentInChildren).TryGetComponent(ref val)) { ((Renderer)val).sharedMaterial = material; } return true; } public static bool TrySetBulletPrefab(Throwable throwable, GameObject bulletPrefab) { if ((Object)(object)throwable == (Object)null || (Object)(object)bulletPrefab == (Object)null) { return false; } FieldInfo fieldInfo = typeof(Throwable).GetField("_bulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(Throwable).GetField("bulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (fieldInfo == null) { return false; } fieldInfo.SetValue(throwable, bulletPrefab); return true; } } [HarmonyPatch(typeof(PlayerData), "OnAwake")] internal static class PlayerDataOnAwakeFix { [HarmonyTranspiler] private static IEnumerable Transpiler(IEnumerable instructions) { List list = new List(instructions); AccessTools.Method(typeof(List<>).MakeGenericType(AccessTools.Inner(typeof(PlayerData), "UpgradeInfoBackup") ?? FindUpgradeInfoBackupType() ?? typeof(object)), "RemoveAt", new Type[1] { typeof(int) }, (Type[])null); int num = 0; int num2 = -1; FieldInfo fieldInfo = AccessTools.Field(typeof(PlayerData), "invalidUpgrades"); for (int i = 0; i < list.Count; i++) { if (fieldInfo != null && i + 3 < list.Count && CodeInstructionExtensions.LoadsField(list[i], fieldInfo, false) && list[i + 1].opcode == OpCodes.Callvirt && list[i + 1].operand is MethodInfo { Name: "get_Count" } && IsLdloc(list[i + 2], out var localIndex)) { num2 = localIndex; } if (!(list[i].opcode == OpCodes.Callvirt) || !(list[i].operand is MethodInfo { Name: "RemoveAt" } methodInfo2) || methodInfo2.GetParameters().Length != 1 || i < 1 || !IsLdloc(list[i - 1], out var localIndex2) || num2 < 0 || localIndex2 == num2 || i < 2) { continue; } bool flag = false; for (int num3 = i - 2; num3 >= Math.Max(0, i - 6); num3--) { if (CodeInstructionExtensions.LoadsField(list[num3], fieldInfo, false)) { flag = true; break; } } if (flag) { list[i - 1] = Ldloc(num2); num++; ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogInfo((object)$"[FriendinaBox] OnAwake fix: RemoveAt local {localIndex2} → {num2}."); } } } if (num == 0) { ManualLogSource logger2 = FriendinaBoxPlugin.Logger; if (logger2 != null) { logger2.LogWarning((object)"[FriendinaBox] OnAwake RemoveAt transpiler found 0 sites — relying on finalizer guard."); } } else { ManualLogSource logger3 = FriendinaBoxPlugin.Logger; if (logger3 != null) { logger3.LogInfo((object)$"[FriendinaBox] OnAwake RemoveAt transpiler patched {num} site(s)."); } } return list; } [HarmonyFinalizer] private static Exception Finalizer(Exception __exception) { if ((__exception is ArgumentOutOfRangeException || __exception is NullReferenceException) ? true : false) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogError((object)("[FriendinaBox] Swallowed PlayerData.OnAwake exception so boot can continue:\n" + __exception)); } return null; } return __exception; } private static Type FindUpgradeInfoBackupType() { Type[] nestedTypes = typeof(PlayerData).GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic); foreach (Type type in nestedTypes) { if (type.Name.Contains("UpgradeInfoBackup")) { return type; } } return null; } private static bool IsLdloc(CodeInstruction ins, out int localIndex) { localIndex = -1; if (ins.opcode == OpCodes.Ldloc_0) { localIndex = 0; return true; } if (ins.opcode == OpCodes.Ldloc_1) { localIndex = 1; return true; } if (ins.opcode == OpCodes.Ldloc_2) { localIndex = 2; return true; } if (ins.opcode == OpCodes.Ldloc_3) { localIndex = 3; return true; } if (ins.opcode == OpCodes.Ldloc_S || ins.opcode == OpCodes.Ldloc) { if (ins.operand is LocalBuilder localBuilder) { localIndex = localBuilder.LocalIndex; return true; } if (ins.operand is byte b) { localIndex = b; return true; } if (ins.operand is int num) { localIndex = num; return true; } } return false; } private static CodeInstruction Ldloc(int localIndex) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown switch (localIndex) { case 0: return new CodeInstruction(OpCodes.Ldloc_0, (object)null); case 1: return new CodeInstruction(OpCodes.Ldloc_1, (object)null); case 2: return new CodeInstruction(OpCodes.Ldloc_2, (object)null); case 3: return new CodeInstruction(OpCodes.Ldloc_3, (object)null); default: if (localIndex > 255) { return new CodeInstruction(OpCodes.Ldloc, (object)localIndex); } return new CodeInstruction(OpCodes.Ldloc_S, (object)(byte)localIndex); } } } [BepInPlugin("sparroh.friendinabox", "FriendinaBox", "1.0.0")] [MycoMod(/*Could not decode attribute arguments.*/)] public class FriendinaBoxPlugin : BaseUnityPlugin { public const string PluginGUID = "sparroh.friendinabox"; public const string PluginName = "FriendinaBox"; public const string PluginVersion = "1.0.0"; public const int GearId = 92100; public const string GearApiName = "friend_in_a_box"; public const string GearDisplayName = "Friend in a Box"; public const string GearDescription = "Deployable ally grenade. Lands as a proximity mine. While equipped, enables multiplayer-only upgrades in Ouroboros."; public const int UpgradeWiderNetId = 92101; public const int UpgradeLongWatchId = 92102; public const int UpgradeQuickDeployId = 92103; public const int UpgradeLingeringGiftId = 92104; public const int UpgradePartingBoostId = 92105; public const int UpgradeTurretModeId = 92106; public const int UpgradeMortarModeId = 92107; public const int UpgradeDroneModeId = 92108; public const int UpgradeSquadDropId = 92109; public const int UpgradeSympatheticLinkId = 92110; public const int UpgradeFieldRechargeId = 92111; public const int UpgradeOvertimeId = 92112; public const int UpgradePaintedTargetsId = 92113; public const int UpgradeScuttleChargeId = 92114; public const int UpgradeReactiveShellId = 92115; public const int UpgradeCalibratedLinkId = 92116; public const int UpgradeDesignatedTargetId = 92117; public const int UpgradeHiveKinId = 92118; internal static ManualLogSource Logger; internal static FriendinaBoxPlugin Instance; public static IUpgradable CustomGrenadePrefab; private Harmony _harmony; private bool _gearRegistered; private bool _upgradesRegistered; private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Instance = this; Logger = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("sparroh.friendinabox"); _harmony.PatchAll(typeof(PlayerDataOnAwakePrefix)); _harmony.PatchAll(typeof(PlayerDataOnAwakeFix)); _harmony.PatchAll(typeof(GlobalLoadHook)); _harmony.PatchAll(typeof(FriendGrenadeHooks)); _harmony.PatchAll(typeof(CoopUnlockHook)); _harmony.PatchAll(typeof(GearSelectionWindowHooks)); _harmony.PatchAll(typeof(GearSlotUpdateHook)); _harmony.PatchAll(typeof(FriendPlayerSpawnHook)); _harmony.PatchAll(typeof(SwarmFriendFireHooks)); SpawnGearHooks.Apply(_harmony); PlayerData.AddRegisterUpgradesCallback((Action)RegisterUpgrades); TryRegisterGear("Awake"); Logger.LogInfo((object)"FriendinaBox v1.0.0 loaded."); } private void OnDestroy() { FriendDeployTracker.Clear(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; Instance = null; } internal void OnPlayerDataReady() { try { GrenadeRegistration.EnsurePlayerDataEntry(autoUnlock: true, Logger); } catch (Exception ex) { Logger.LogWarning((object)("[FriendinaBox] EnsurePlayerDataEntry: " + ex.Message)); } if (!_gearRegistered) { TryRegisterGear("PlayerData ready"); } else if (CustomGrenadePrefab == null) { CustomGrenadePrefab = GrenadeRegistration.CatalogGear; } RegisterUpgrades(); } internal void TryRegisterGear(string reason) { if (_gearRegistered) { if (!_upgradesRegistered && (CustomGrenadePrefab != null || GrenadeRegistration.CatalogGear != null)) { RegisterUpgrades(); } return; } if ((Object)(object)Global.Instance == (Object)null || Global.Instance.AllGear == null || Global.Instance.AllGear.Length == 0) { Logger.LogDebug((object)("[FriendinaBox] Global.AllGear not ready yet (" + reason + ").")); return; } try { if (GrenadeRegistration.TryCreateAndRegister("sparroh.friendinabox", 92100, "friend_in_a_box", "Friend in a Box", "Deployable ally grenade. Lands as a proximity mine. While equipped, enables multiplayer-only upgrades in Ouroboros.", "IncendiaryGrenade", autoUnlock: true, Logger, out CustomGrenadePrefab)) { _gearRegistered = true; Logger.LogInfo((object)("[FriendinaBox] Registered gear 'Friend in a Box' " + string.Format("(api={0}, id={1}) via {2}.", "friend_in_a_box", 92100, reason))); try { FriendBulletCache.EnsureCached(); } catch (Exception ex) { Logger.LogWarning((object)("[FriendinaBox] Bullet cache failed (non-fatal): " + ex.Message)); } RegisterUpgrades(); } } catch (Exception arg) { Logger.LogError((object)$"[FriendinaBox] Gear registration failed: {arg}"); } } private void RegisterUpgrades() { if (_upgradesRegistered) { return; } try { IUpgradable val = CustomGrenadePrefab ?? GrenadeRegistration.CatalogGear; if (val == null) { Logger.LogDebug((object)"[FriendinaBox] Deferring upgrades until gear is registered."); return; } if (PlayerData.Instance == null) { Logger.LogDebug((object)"[FriendinaBox] Deferring upgrades until PlayerData.Instance exists."); return; } if ((1u & (CreateUpgrade(val, 92101, "Wider Net", "Increases mine detection radius and explosion radius.", (Rarity)0, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendWiderNetProperty() }, UpgradeRegistration.CreateRadiusPattern(), (UpgradeFlags)8) ? 1u : 0u) & (CreateUpgrade(val, 92102, "Long Watch", "Mine lasts longer, but throw cooldown is slower.", (Rarity)0, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendLongWatchProperty() }, UpgradeRegistration.CreateBarPattern(), (UpgradeFlags)8) ? 1u : 0u) & (CreateUpgrade(val, 92103, "Quick Deploy", "Throw cooldown is faster, but mine duration is shorter.", (Rarity)0, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendQuickDeployProperty() }, UpgradeRegistration.CreateBarPattern(), (UpgradeFlags)8) ? 1u : 0u) & (CreateUpgrade(val, 92104, "Lingering Gift", "When the mine expires without detonating, leave an acid puddle.", (Rarity)1, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendLingeringGiftProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)0) ? 1u : 0u) & (CreateUpgrade(val, 92105, "Parting Boost", "When the mine expires without detonating, gain a burst of move speed.", (Rarity)1, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendPartingBoostProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)0) ? 1u : 0u) & (CreateUpgrade(val, 92106, "Sentry Kit", "Deploy as a stationary auto-turret instead of a mine. (Green box)", (Rarity)3, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendTurretModeProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)0) ? 1u : 0u) & (CreateUpgrade(val, 92107, "Lobber Kit", "Deploy as a mortar that lobs AoE at range. (Orange box)", (Rarity)3, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendMortarModeProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)0) ? 1u : 0u) & (CreateUpgrade(val, 92108, "Buddy Protocol", "Deploy as a drone that hovers near you. Suicide dives by default; combines with Sentry/Lobber. (Cyan box)", (Rarity)3, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendDroneModeProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)0) ? 1u : 0u) & (CreateUpgrade(val, 92109, "Squad Drop", "Each throw deploys multiple Friends in a spread. Stackable.", (Rarity)1, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendSquadDropProperty() }, UpgradeRegistration.CreateBarPattern(), (UpgradeFlags)8) ? 1u : 0u) & (CreateUpgrade(val, 92110, "Sympathetic Link", "A portion of Friend damage returns as blue overhealth (does not fill base HP first).", (Rarity)0, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendSympatheticLinkProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)8) ? 1u : 0u) & (CreateUpgrade(val, 92111, "Field Recharge", "Friend kills refund grenade charge.", (Rarity)0, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendFieldRechargeProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)8) ? 1u : 0u) & (CreateUpgrade(val, 92112, "Overtime", "Friend kills extend the active deploy duration.", (Rarity)0, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendOvertimeProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)8) ? 1u : 0u) & (CreateUpgrade(val, 92113, "Painted Targets", "Enemies hit by Friend are marked and take bonus damage briefly.", (Rarity)1, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendPaintedTargetsProperty() }, UpgradeRegistration.CreateRadiusPattern(), (UpgradeFlags)8) ? 1u : 0u) & (CreateUpgrade(val, 92114, "Scuttle Charge", "Shoot your deployable to detonate it early. Power scales with remaining duration.", (Rarity)3, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendScuttleChargeProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)0) ? 1u : 0u) & (CreateUpgrade(val, 92115, "Reactive Shell", "When you take damage, gain blue overhealth. Does not top up base HP first.", (Rarity)1, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendReactiveShellProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)8) ? 1u : 0u) & (CreateUpgrade(val, 92116, "Calibrated Link", "Turret/mortar/drone fire blends toward your primary weapon's damage, effect, fire rate, and bullets per shot.", (Rarity)3, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendCalibratedLinkProperty() }, UpgradeRegistration.CreateBarPattern(), (UpgradeFlags)8) ? 1u : 0u) & (CreateUpgrade(val, 92117, "Designated Target", "After you damage an enemy, Friend focuses that target while it stays in range.", (Rarity)1, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendDesignatedTargetProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)0) ? 1u : 0u) & (CreateUpgrade(val, 92118, "Hive Kin", "Your Friend deployables (mine, turret, mortar, drone) count as Swarm Launcher allies for Breeding Season. Extra pellets spawn from them while firing.", (Rarity)3, (UpgradeProperty[])(object)new UpgradeProperty[1] { new FriendHiveKinProperty() }, UpgradeRegistration.CreateSinglePattern(), (UpgradeFlags)0) ? 1u : 0u)) == 0) { Logger.LogWarning((object)"[FriendinaBox] One or more upgrades failed to register."); return; } _upgradesRegistered = true; Logger.LogInfo((object)("[FriendinaBox] Registered 18 upgrades on 'friend_in_a_box' " + $"(ids={92101}-{92118}).")); } catch (Exception arg) { Logger.LogError((object)$"[FriendinaBox] Upgrade registration failed: {arg}"); } } private bool CreateUpgrade(IUpgradable gear, int id, string name, string description, Rarity rarity, UpgradeProperty[] properties, HexMap pattern, UpgradeFlags flags) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Upgrade upgrade; return UpgradeRegistration.TryCreateGunUpgrade("sparroh.friendinabox", gear, id, name, description, rarity, properties, pattern, flags, null, Logger, out upgrade, 0, (CollectionSource)0); } } [HarmonyPatch(typeof(PlayerData), "OnAwake")] internal static class PlayerDataOnAwakePrefix { [HarmonyPrefix] private static void Prefix() { FriendinaBoxPlugin.Instance?.TryRegisterGear("PlayerData.OnAwake prefix"); } [HarmonyPostfix] private static void Postfix() { try { FriendinaBoxPlugin.Instance?.OnPlayerDataReady(); } catch (Exception ex) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)("[FriendinaBox] OnAwake postfix: " + ex.Message)); } } } } [HarmonyPatch(typeof(Global), "LoadInstance")] internal static class GlobalLoadHook { [HarmonyPostfix] private static void Postfix() { FriendinaBoxPlugin.Instance?.TryRegisterGear("Global.LoadInstance"); } } internal static class SpawnGearHooks { [ThreadStatic] private static bool RemappingCustomSpawn; [ThreadStatic] private static int RemappedSlot; public static void Apply(Harmony harmony) { //IL_016a: 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_018c: Expected O, but got Unknown //IL_018c: Expected O, but got Unknown Type typeFromHandle = typeof(Player); MethodInfo methodInfo = AccessTools.Method(typeFromHandle, "SpawnGear_Server", new Type[6] { typeof(int), typeof(int), typeof(bool), typeof(bool), typeof(int), typeof(int) }, (Type[])null); if (methodInfo == null) { methodInfo = AccessTools.Method(typeFromHandle, "SpawnGear_Server", new Type[4] { typeof(int), typeof(int), typeof(bool), typeof(bool) }, (Type[])null); } if (methodInfo == null) { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeFromHandle)) { if (declaredMethod.Name == "SpawnGear_Server") { methodInfo = declaredMethod; break; } } } if (methodInfo == null) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogError((object)"[FriendinaBox] Could not find Player.SpawnGear_Server to patch."); } return; } MethodInfo methodInfo2 = AccessTools.Method(typeof(SpawnGearHooks), "SpawnGearServerPrefix_6", (Type[])null, (Type[])null); ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 4) { methodInfo2 = AccessTools.Method(typeof(SpawnGearHooks), "SpawnGearServerPrefix_4", (Type[])null, (Type[])null); } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), new HarmonyMethod(typeof(SpawnGearHooks), "SpawnGearServerPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ManualLogSource logger2 = FriendinaBoxPlugin.Logger; if (logger2 != null) { logger2.LogInfo((object)("[FriendinaBox] Patched " + methodInfo.DeclaringType.FullName + "." + methodInfo.Name + " " + $"({parameters.Length} params) with {methodInfo2.Name}.")); } } private static void SpawnGearServerPrefix_6(Player __instance, int slot, ref int allGearIndex, bool equip, bool despawn, int skinID, int skinSeed) { TryRemap(slot, ref allGearIndex); } private static void SpawnGearServerPrefix_4(Player __instance, int slot, ref int allGearIndex, bool equip, bool despawn) { TryRemap(slot, ref allGearIndex); } private static void TryRemap(int slot, ref int allGearIndex) { RemappingCustomSpawn = false; if (Global.Instance?.AllGear == null || allGearIndex < 0 || allGearIndex >= Global.Instance.AllGear.Length) { return; } IUpgradable val = Global.Instance.AllGear[allGearIndex]; if (!IsOurCatalogGear(val)) { return; } int num = ResolveBaseIndex(); if (num < 0) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogError((object)"[FriendinaBox] Cannot remap spawn — base Incendiary AllGear index unknown. Aborting custom equip to avoid NRE."); } return; } ManualLogSource logger2 = FriendinaBoxPlugin.Logger; if (logger2 != null) { string text = $"[FriendinaBox] Remap SpawnGear slot={slot} index {allGearIndex} → base {num} "; GearInfo info = val.Info; logger2.LogInfo((object)(text + "(api=" + ((info != null) ? info.APIName : null) + ").")); } allGearIndex = num; RemappingCustomSpawn = true; RemappedSlot = slot; } private static int ResolveBaseIndex() { if (Global.Instance?.AllGear == null) { return -1; } int baseAllGearIndex = GrenadeRegistration.BaseAllGearIndex; if (baseAllGearIndex >= 0 && baseAllGearIndex < Global.Instance.AllGear.Length) { IUpgradable val = Global.Instance.AllGear[baseAllGearIndex]; if (val != null && !IsOurCatalogGear(val)) { return baseAllGearIndex; } } if ((Object)(object)GrenadeRegistration.BaseGrenadePrefab != (Object)null) { int num = Array.IndexOf(Global.Instance.AllGear, (IUpgradable)(object)GrenadeRegistration.BaseGrenadePrefab); if (num >= 0) { GrenadeRegistration.SetBaseAllGearIndex(num); return num; } } if ((Object)(object)GrenadeRegistration.BaseNetworkPrefab != (Object)null) { for (int i = 0; i < Global.Instance.AllGear.Length; i++) { IUpgradable obj = Global.Instance.AllGear[i]; Component val2 = (Component)(object)((obj is Component) ? obj : null); if (val2 != null && (Object)(object)val2.gameObject == (Object)(object)GrenadeRegistration.BaseNetworkPrefab && !IsOurCatalogGear(Global.Instance.AllGear[i])) { GrenadeRegistration.SetBaseAllGearIndex(i); return i; } } } for (int j = 0; j < Global.Instance.AllGear.Length; j++) { if (Global.Instance.AllGear[j] is GrenadeGear && !IsOurCatalogGear(Global.Instance.AllGear[j])) { GrenadeRegistration.SetBaseAllGearIndex(j); return j; } } return -1; } private static void SpawnGearServerPostfix(Player __instance) { if (!RemappingCustomSpawn) { return; } RemappingCustomSpawn = false; int remappedSlot = RemappedSlot; try { if (!TryStampCustomIdentityOnSlot(__instance, remappedSlot)) { FriendinaBoxPlugin instance = FriendinaBoxPlugin.Instance; if ((Object)(object)instance != (Object)null) { ((MonoBehaviour)instance).StartCoroutine(StampWhenReady(__instance, remappedSlot)); } } } catch (Exception arg) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogError((object)$"[FriendinaBox] Post-spawn stamp failed: {arg}"); } } } private static IEnumerator StampWhenReady(Player player, int slot) { for (int attempt = 0; attempt < 30; attempt++) { yield return null; if ((Object)(object)player == (Object)null) { yield break; } try { if (!TryStampCustomIdentityOnSlot(player, slot)) { continue; } yield break; } catch (Exception ex) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)$"[FriendinaBox] Deferred stamp attempt {attempt}: {ex.Message}"); } } } ManualLogSource logger2 = FriendinaBoxPlugin.Logger; if (logger2 != null) { logger2.LogWarning((object)$"[FriendinaBox] Deferred stamp gave up on Gear[{slot}] after retries."); } } private static bool TryStampCustomIdentityOnSlot(Player player, int slot) { if (((player != null) ? player.Gear : null) == null || slot < 0 || slot >= player.Gear.Length) { return false; } IGear val = player.Gear[slot]; if (val == null) { return false; } bool flag = (Object)(object)((IUpgradable)val).Info != (Object)null && ((IUpgradable)val).Info.APIName == "friend_in_a_box" && (Object)(object)val.gameObject != (Object)null && (Object)(object)val.gameObject.GetComponent() != (Object)null; IUpgradable val2 = FriendinaBoxPlugin.CustomGrenadePrefab ?? GrenadeRegistration.CatalogGear ?? FindCatalogInAllGear(); if (val2 == null) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)"[FriendinaBox] Catalog gear missing during stamp."); } return false; } if (!flag) { ((IUpgradable)val).Prefab = val2; if ((Object)(object)val2.Info != (Object)null) { TryAssignInfo(val, val2.Info); } if ((Object)(object)val.gameObject != (Object)null) { FriendinaBoxBehaviour friendinaBoxBehaviour = val.gameObject.GetComponent(); if ((Object)(object)friendinaBoxBehaviour == (Object)null) { friendinaBoxBehaviour = val.gameObject.AddComponent(); } FriendinaBoxBehaviour friendinaBoxBehaviour2 = null; Component val3 = (Component)(object)((val2 is Component) ? val2 : null); if (val3 != null) { friendinaBoxBehaviour2 = val3.GetComponent(); } friendinaBoxBehaviour.InitializeAsPrefab(((Object)(object)friendinaBoxBehaviour2 != (Object)null) ? friendinaBoxBehaviour2.Description : "Deployable ally grenade. Lands as a proximity mine. While equipped, enables multiplayer-only upgrades in Ouroboros."); if ((Object)(object)friendinaBoxBehaviour2 != (Object)null) { friendinaBoxBehaviour.CopySnapshotFrom(friendinaBoxBehaviour2); } } } else { ((IUpgradable)val).Prefab = val2; } try { val.ApplyUpgrades(); if (FriendinaBoxBehaviour.TryGet(val, out var behaviour)) { FriendCombatHooks.EnsureBound(val, behaviour.GrenadeData); } } catch (Exception ex) { ManualLogSource logger2 = FriendinaBoxPlugin.Logger; if (logger2 != null) { logger2.LogWarning((object)$"[FriendinaBox] Post-stamp ApplyUpgrades failed on Gear[{slot}]: {ex.Message}"); } } ManualLogSource logger3 = FriendinaBoxPlugin.Logger; if (logger3 != null) { logger3.LogInfo((object)string.Format("[FriendinaBox] Stamped custom identity on Gear[{0}] ({1}) + reapplied upgrades.", slot, "friend_in_a_box")); } return true; } private static IUpgradable FindCatalogInAllGear() { if (Global.Instance?.AllGear == null) { return null; } for (int i = 0; i < Global.Instance.AllGear.Length; i++) { IUpgradable val = Global.Instance.AllGear[i]; if (IsOurCatalogGear(val)) { return val; } } return null; } internal static bool IsOurCatalogGear(IUpgradable gear) { if (gear == null) { return false; } if (gear == FriendinaBoxPlugin.CustomGrenadePrefab || gear == GrenadeRegistration.CatalogGear) { return true; } if ((Object)(object)gear.Info != (Object)null && gear.Info.APIName == "friend_in_a_box") { return true; } if ((Object)(object)gear.Info != (Object)null && gear.Info.ID == 92100) { return true; } return false; } private static void TryAssignInfo(IGear live, GearInfo info) { Type type = ((object)live).GetType(); while (type != null) { FieldInfo fieldInfo = type.GetField("k__BackingField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("Info", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (fieldInfo != null) { fieldInfo.SetValue(live, info); break; } PropertyInfo property = type.GetProperty("Info", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(live, info); break; } type = type.BaseType; } } } [HarmonyPatch(typeof(GearSlot), "Update")] internal static class GearSlotUpdateHook { [HarmonyFinalizer] private static Exception Finalizer(Exception __exception) { if (__exception is NullReferenceException) { return null; } return __exception; } } [HarmonyPatch(typeof(GearSelectionWindow))] internal static class GearSelectionWindowHooks { [HarmonyPatch("OnOpen")] [HarmonyFinalizer] private static Exception OnOpenFinalizer(Exception __exception) { if (__exception is NullReferenceException) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogError((object)("[FriendinaBox] GearSelectionWindow.OnOpen NRE (null Gear slot after failed spawn).\n" + __exception)); } return null; } return __exception; } [HarmonyPatch("OnCloseCallback")] [HarmonyPrefix] private static void OnCloseCallbackPrefix() { if (GrenadeRegistration.BaseAllGearIndex >= 0 || Global.Instance?.AllGear == null) { return; } for (int i = 0; i < Global.Instance.AllGear.Length; i++) { if (Global.Instance.AllGear[i] is GrenadeGear && !SpawnGearHooks.IsOurCatalogGear(Global.Instance.AllGear[i])) { GrenadeRegistration.SetBaseAllGearIndex(i); break; } } } [HarmonyPatch("OnCloseCallback")] [HarmonyFinalizer] private static Exception OnCloseFinalizer(Exception __exception) { if (__exception is NullReferenceException) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogError((object)("[FriendinaBox] GearSelectionWindow.OnCloseCallback NRE (null Gear[i] after failed spawn).\n" + __exception)); } return null; } return __exception; } } [HarmonyPatch(typeof(SwarmGun))] internal static class SwarmFriendFireHooks { private static FieldInfo _friendBulletCounterField; private static FieldInfo _fireDataField; private static MethodInfo _startFireCustom; private static MethodInfo _finishFireCustom; private static FieldInfo _bulletRotationField; private static bool _resolved; private static bool _resolveFailed; private static readonly List DroneBuffer = new List(8); private static void EnsureResolved() { if (_resolved || _resolveFailed) { return; } try { _friendBulletCounterField = AccessTools.Field(typeof(SwarmGun), "friendBulletCounter"); _fireDataField = AccessTools.Field(typeof(Gun), "fireData"); _startFireCustom = AccessTools.Method(typeof(SwarmGun), "StartFireCustomSwarmBullet", new Type[4] { typeof(int), typeof(Vector3), typeof(Vector3), typeof(BulletData).MakeByRefType() }, (Type[])null); _finishFireCustom = AccessTools.Method(typeof(SwarmGun), "FinishFireCustomSwarmBullet", new Type[3] { typeof(int), typeof(BulletData).MakeByRefType(), typeof(GearUpgradeFlags) }, (Type[])null); if (_fireDataField != null) { _bulletRotationField = AccessTools.Field(_fireDataField.FieldType, "bulletRotation"); } if (_friendBulletCounterField == null || _startFireCustom == null || _finishFireCustom == null) { if ((object)_startFireCustom == null) { _startFireCustom = AccessTools.Method(typeof(SwarmGun), "StartFireCustomSwarmBullet", (Type[])null, (Type[])null); } if ((object)_finishFireCustom == null) { _finishFireCustom = AccessTools.Method(typeof(SwarmGun), "FinishFireCustomSwarmBullet", (Type[])null, (Type[])null); } } if (_friendBulletCounterField == null || _startFireCustom == null || _finishFireCustom == null) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)("[FriendinaBox] Hive Kin: could not resolve SwarmGun FriendFire internals " + $"(counter={_friendBulletCounterField != null}, start={_startFireCustom != null}, finish={_finishFireCustom != null}).")); } _resolveFailed = true; } else { _resolved = true; ManualLogSource logger2 = FriendinaBoxPlugin.Logger; if (logger2 != null) { logger2.LogInfo((object)"[FriendinaBox] Hive Kin SwarmGun hooks resolved."); } } } catch (Exception ex) { ManualLogSource logger3 = FriendinaBoxPlugin.Logger; if (logger3 != null) { logger3.LogWarning((object)("[FriendinaBox] Hive Kin resolve failed: " + ex.Message)); } _resolveFailed = true; } } [HarmonyPatch("OnFire")] [HarmonyPrefix] private static void OnFirePrefix(SwarmGun __instance, out bool __state) { __state = false; if ((Object)(object)__instance == (Object)null) { return; } try { ref Data swarmData = ref __instance.SwarmData; if (!(swarmData.friendFireRadius <= 0f) && HiveKinActive()) { EnsureResolved(); if (_resolved) { int num = (int)_friendBulletCounterField.GetValue(__instance); int num2 = Mathf.Max(1, swarmData.friendFireInterval); __state = num + 1 >= num2; } } } catch { __state = false; } } [HarmonyPatch("OnFire")] [HarmonyPostfix] private static void OnFirePostfix(SwarmGun __instance, int numBullets, bool __state) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) if (!__state || (Object)(object)__instance == (Object)null || !((NetworkBehaviour)__instance).IsOwner) { return; } try { if (!HiveKinActive()) { return; } EnsureResolved(); if (!_resolved) { return; } ref Data swarmData = ref __instance.SwarmData; float friendFireRadius = swarmData.friendFireRadius; if (friendFireRadius <= 0f) { return; } Player player = ((Gun)__instance).Player; if ((Object)(object)player == (Object)null) { return; } FriendDeployTracker.GetAlliesInRadius(player.InterpolatedPosition, friendFireRadius, DroneBuffer); if (DroneBuffer.Count == 0) { return; } Vector3 fireEuler = GetFireEuler(__instance, player); GearUpgradeFlags upgradeFlags = ((Gun)__instance).UpgradeFlags; for (int i = 0; i < DroneBuffer.Count; i++) { FriendDeployable friendDeployable = DroneBuffer[i]; if (!((Object)(object)friendDeployable == (Object)null)) { Vector3 position = ((Component)friendDeployable).transform.position; bool flag = (friendDeployable.Mode & FriendDeployMode.Drone) != 0; position.y += (flag ? 0.35f : 1.5f); int num = numBullets + 100 + i; BulletData val = default(BulletData); object[] array = new object[4] { num, position, fireEuler, val }; _startFireCustom.Invoke(__instance, array); val = (BulletData)array[3]; if (swarmData.friendFireDamageMult > 0f) { val.damage *= swarmData.friendFireDamageMult; } object[] parameters = new object[3] { num, val, upgradeFlags }; _finishFireCustom.Invoke(__instance, parameters); } } } catch (Exception ex) { ManualLogSource logger = FriendinaBoxPlugin.Logger; if (logger != null) { logger.LogWarning((object)("[FriendinaBox] Hive Kin OnFire failed: " + ex.Message)); } } } [HarmonyPatch("OnActiveUpdate")] [HarmonyPostfix] private static void OnActiveUpdatePostfix(SwarmGun __instance) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || !((NetworkBehaviour)__instance).IsOwner) { return; } try { ref Data swarmData = ref __instance.SwarmData; if (swarmData.friendFireRadius <= 0f || !HiveKinActive()) { return; } Player player = ((Gun)__instance).Player; if ((Object)(object)player == (Object)null) { return; } FriendDeployTracker.GetAlliesInRadius(player.InterpolatedPosition, swarmData.friendFireRadius, DroneBuffer); int count = DroneBuffer.Count; if (count <= 0) { return; } int num = 0; float num2 = swarmData.friendFireRadius * swarmData.friendFireRadius; Vector3 interpolatedPosition = player.InterpolatedPosition; for (int i = 0; i < GameManager.players.Count; i++) { Player val = GameManager.players[i]; if (!((Object)(object)val == (Object)null) && !((NetworkBehaviour)val).IsLocalPlayer) { Vector3 val2 = val.InterpolatedPosition - interpolatedPosition; if (!(((Vector3)(ref val2)).sqrMagnitude >= num2) && val.Gear != null && val.Gear.Length > 1 && (val.Gear[0] is SwarmGun || val.Gear[1] is SwarmGun)) { num++; } } } int num3 = num + count; if (num3 > 0) { Sprite icon = UpgradeProperty_SwarmGun_FriendFire.Icon; if ((Object)(object)icon != (Object)null) { player.UpdateStackDisplay((object)typeof(UpgradeProperty_SwarmGun_FriendFire), TextBlocks.GetString("FriendFire", 0), icon, num3, 0.1f); } } } catch { } } private static bool HiveKinActive() { if (!FriendinaBoxBehaviour.TryGetEquipped(out var behaviour, out var _)) { return false; } return behaviour.GrenadeData.countsAsSwarmAlly; } private static Vector3 GetFireEuler(SwarmGun gun, Player owner) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) try { if (_fireDataField != null && _bulletRotationField != null) { object value = _fireDataField.GetValue(gun); if (value != null && _bulletRotationField.GetValue(value) is Quaternion val) { return ((Quaternion)(ref val)).eulerAngles; } } } catch { } if ((Object)(object)owner != (Object)null) { return ((Component)owner).transform.eulerAngles; } return Vector3.zero; } } public static class UpgradeRegistration { public static bool TryCreateGunUpgrade(string modGuid, string gearApiName, int upgradeId, string name, string description, Rarity rarity, UpgradeProperty[] properties, HexMap pattern, UpgradeFlags flags, Sprite icon, ManualLogSource log, out Upgrade upgrade, int priority = 0, CollectionSource collectionSource = (CollectionSource)0) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) upgrade = null; IUpgradable val = null; try { val = PlayerData.FindGear(gearApiName); } catch (Exception ex) { if (log != null) { log.LogWarning((object)("[UpgradeRegistration] FindGear(\"" + gearApiName + "\") threw: " + ex.Message)); } return false; } if (val == null) { if (log != null) { log.LogError((object)("[UpgradeRegistration] FindGear(\"" + gearApiName + "\") returned null. Register gear first.")); } return false; } return TryCreateGunUpgrade(modGuid, val, upgradeId, name, description, rarity, properties, pattern, flags, icon, log, out upgrade, priority, collectionSource); } public static bool TryCreateGunUpgrade(string modGuid, IUpgradable gear, int upgradeId, string name, string description, Rarity rarity, UpgradeProperty[] properties, HexMap pattern, UpgradeFlags flags, Sprite icon, ManualLogSource log, out Upgrade upgrade, int priority = 0, CollectionSource collectionSource = (CollectionSource)0) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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) //IL_0099: Unknown result type (might be due to invalid IL or missing references) upgrade = null; if (string.IsNullOrEmpty(modGuid)) { if (log != null) { log.LogError((object)"[UpgradeRegistration] modGuid is null/empty."); } return false; } if (gear == null) { if (log != null) { log.LogError((object)"[UpgradeRegistration] gear is null."); } return false; } if (properties == null || properties.Length == 0) { if (log != null) { log.LogError((object)"[UpgradeRegistration] properties array is null/empty."); } return false; } CustomUpgradeParams val = CustomUpgradeParams.Create(gear, upgradeId, name, description, rarity, icon); val.flags = flags; val.priority = priority; val.collectionSource = collectionSource; val.upgradeType = (Type)3; val.useDefaultUnlockCost = true; if (pattern != null) { val.pattern = pattern; } try { upgrade = PlayerData.CreateUpgrade(modGuid, val, properties); } catch (Exception arg) { if (log != null) { log.LogError((object)$"[UpgradeRegistration] CreateUpgrade threw: {arg}"); } upgrade = null; return false; } if ((Object)(object)upgrade == (Object)null) { if (log != null) { log.LogError((object)"[UpgradeRegistration] CreateUpgrade returned null."); } return false; } if (pattern != null) { upgrade.SetPattern(pattern); } if (log != null) { object arg2 = upgradeId; GearInfo info = gear.Info; log.LogInfo((object)string.Format("[UpgradeRegistration] Created '{0}' id={1} on {2}.", name, arg2, ((info != null) ? info.APIName : null) ?? "?")); } return true; } public static HexMap CreateRadiusPattern() { //IL_0002: 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown HexMap val = new HexMap(3, 3); val[0, 0].enabled = true; val[1, 0].enabled = true; val[1, 0].connections = (Direction)36; val[2, 1].enabled = true; return val; } public static HexMap CreateBarPattern() { //IL_0002: 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown HexMap val = new HexMap(2, 2); val[0, 0].enabled = true; val[1, 0].enabled = true; val[0, 0].connections = (Direction)4; val[1, 0].connections = (Direction)32; return val; } public static HexMap CreateSinglePattern() { //IL_0002: 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_0016: Expected O, but got Unknown HexMap val = new HexMap(1, 1); val[0, 0].enabled = true; return val; } public static UpgradeInstance GrantTestInstance(IUpgradable gear, Upgrade upgrade, bool unlock = true, bool quietUnlock = true, ManualLogSource log = null) { if (gear == null || (Object)(object)upgrade == (Object)null) { if (log != null) { log.LogError((object)"[UpgradeRegistration] GrantTestInstance: gear or upgrade is null."); } return null; } UpgradeInstance val = PlayerData.CollectInstance(gear, upgrade, (UnlockFlags)1); if (val == null) { if (log != null) { log.LogError((object)"[UpgradeRegistration] CollectInstance returned null."); } return null; } if (unlock) { val.Unlock(quietUnlock); } if (log != null) { log.LogInfo((object)$"[UpgradeRegistration] Granted test instance of '{upgrade.Name}' (id={val.InstanceID})."); } return val; } } namespace FriendinaBox { public static class MyPluginInfo { public const string PLUGIN_GUID = "FriendinaBox"; public const string PLUGIN_NAME = "FriendinaBox"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }