Decompiled source of FriendinaBox v1.0.0
BepInEx/plugins/FriendinaBox.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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<Player>), "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<CodeInstruction> list = new List<CodeInstruction>(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<Player> 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<RailBullet>() != (Object)null || prefab.GetComponent<IBullet>() != 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<RailBullet>() != (Object)null) { _railPrefab = prefab2; return; } } } RailBullet[] array = Resources.FindObjectsOfTypeAll<RailBullet>(); 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<MortarBullet>(); 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<RocketSalvoBullet>(); foreach (RocketSalvoBullet val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { _ = val2 is MortarBullet; } } if (Global.Instance?.AllGear == null) { return; } ProjectileGunArmTip[] array3 = Resources.FindObjectsOfTypeAll<ProjectileGunArmTip>(); for (int k = 0; k < array3.Length; k++) { if (TryReadArmTipBulletPrefab(array3[k], out var prefab) && (Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<MortarBullet>() != (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<RailBullet>(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<DamageData, IDamageSource> <3>__OnPlayerTookDamage; } private static bool _playerHooksBound; private static DamageCallback _onGearDamage; private static KillCallback _onGearKill; private static MutableDamageCallback _onPlayerBeforeDamage; private static RefAction<DamageData, IDamageSource> _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<Camera>(); } 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<FriendCombatRunner>(); } } 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<int, Material> MaterialsByColor = new Dictionary<int, Material>(); 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<Collider>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Renderer component2 = obj.GetComponent<Renderer>(); 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>(); 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<SphereCollider>(); ((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<IBullet>(); 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<IBullet> 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<IBullet>(); 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<IBullet> 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<FriendDeployable> Active = new List<FriendDeployable>(); 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<FriendDeployable> 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<FriendDeployable> 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<float> 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<FriendExpireSpeedBuff>(); 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<FriendExpireSpeedBuff>().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<FriendinaBoxBehaviour>(); 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<FriendinaBoxBehaviour>(); } 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<FriendinaBoxBehaviour>(); 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<FriendinaBoxBehaviour>() != (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<FriendMarkedTarget> Active = new List<FriendMarkedTarget>(); 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<FriendMarkedTarget>(); 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>(); 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<float> radiusBonus = new Range<float>(0.2f, 0.35f); public override IEnumerator<StatData> 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<float>?)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<float> durationBonus = new Range<float>(0.35f, 0.5f); public Range<float> cooldownPenalty = new Range<float>(0.2f, 0.3f); public override IEnumerator<StatData> 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<float>?)null); yield return StatData.Create("Cooldown:", cooldownPenalty, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)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<float> cooldownBonus = new Range<float>(0.2f, 0.3f); public Range<float> durationPenalty = new Range<float>(0.15f, 0.25f); public override IEnumerator<StatData> 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<float>?)null); yield return StatData.Create("Mine duration:", durationPenalty, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)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<float> puddleDuration = new Range<float>(4f, 6f); public override IEnumerator<StatData> 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<float>?)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<float> speedBonus = new Range<float>(0.2f, 0.3f); public Range<float> buffDuration = new Range<float>(4f, 6f); public override IEnumerator<StatData> 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<float>?)null); yield return StatData.Create("Boost duration:", buffDuration, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range<float>?)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<StatData> 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<int>(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<StatData> 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<int>(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<StatData> 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<int>(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<int> extraDeploys = new Range<int>(1, 1); public override IEnumerator<StatData> 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<float> lifesteal = new Range<float>(0.08f, 0.15f); public override IEnumerator<StatData> 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<float>?)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 : UpgradeProper