using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib;
using BoneLib.BoneMenu;
using BulletCleanup;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Data;
using Il2CppSLZ.Marrow.Pool;
using Il2CppSLZ.Marrow.Warehouse;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(TrackingProbe), "BulletCleanup", "0.1.0", "Geozek (with Codex)", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0")]
[assembly: AssemblyVersion("0.0.0.0")]
[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;
}
}
}
namespace BulletCleanup
{
public sealed class TrackingProbe : MelonMod
{
private readonly Stopwatch clock = new Stopwatch();
private MelonPreferences_Category prefs;
private MelonPreferences_Entry<bool> enabled;
private MelonPreferences_Entry<bool> magazines;
private MelonPreferences_Entry<int> lifetime;
private MelonPreferences_Entry<int> limit;
private long nextLog;
private long frames;
private bool menu;
private bool ready;
private bool fault;
private float nextMenu;
private float nextScan;
private ITracker tracker;
private int magazineProbeShots;
private bool grabSubscribed;
private readonly Queue<GameObject> grabbed = new Queue<GameObject>();
public override void OnInitializeMelon()
{
clock.Start();
prefs = MelonPreferences.CreateCategory("BrassCleanup");
enabled = prefs.CreateEntry<bool>("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
magazines = prefs.CreateEntry<bool>("DeleteDroppedMagazines", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
lifetime = prefs.CreateEntry<int>("CasingLifetimeSeconds", 5, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
limit = prefs.CreateEntry<int>("MaximumCasings", 100, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
Hooking.OnPreFireGun += OnFire;
Hooking.OnLevelLoaded += Loaded;
Hooking.OnLevelLoading += Loading;
((MelonBase)this).LoggerInstance.Msg("LOOSE ITEM CLEANUP: casings, unfired rounds and optional dropped magazines share timer and cap. Cleanup follows the saved enabled setting; magazines also require their own toggle.");
}
private void Loaded(LevelInfo info)
{
ready = true;
if (!grabSubscribed)
{
Hooking.OnGrabObject += OnGrab;
grabSubscribed = true;
}
((MelonBase)this).LoggerInstance.Msg("Level-loaded callback received.");
}
private void Loading(LevelInfo info)
{
StopGrabDiscovery();
MagazineTypeProbe.Reset();
ready = false;
tracker = null;
fault = false;
magazineProbeShots = 0;
((MelonBase)this).LoggerInstance.Msg("Tracking cleared for level change.");
}
private void StopGrabDiscovery()
{
if (grabSubscribed)
{
Hooking.OnGrabObject -= OnGrab;
grabSubscribed = false;
}
grabbed.Clear();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void OnGrab(GameObject obj, Hand hand)
{
if (ready && !fault && enabled.Value && grabbed.Count < 32)
{
grabbed.Enqueue(obj);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void ProcessGrabs()
{
int num = 4;
while (grabbed.Count > 0 && num-- > 0)
{
GameObject val = grabbed.Dequeue();
if (Object.op_Implicit((Object)(object)val))
{
if (tracker == null)
{
((MelonBase)this).LoggerInstance.Msg("Creating cleanup worker after grab.");
tracker = CreateTracker();
}
tracker.LearnObject(val, magazines.Value);
}
}
}
private void OnFire(Gun gun)
{
if (!ready || !enabled.Value || fault)
{
return;
}
try
{
if (tracker == null)
{
((MelonBase)this).LoggerInstance.Msg("Creating casing worker after gunfire.");
tracker = CreateTracker();
((MelonBase)this).LoggerInstance.Msg("Casing worker created.");
}
tracker.LearnGun(gun, magazines.Value);
if (magazines.Value && magazineProbeShots < 3)
{
magazineProbeShots++;
((MelonBase)this).LoggerInstance.Msg("Magazine type probe BEGIN shot=" + magazineProbeShots);
string text = MagazineTypeProbe.Read(gun);
((MelonBase)this).LoggerInstance.Msg("Magazine type probe END barcode=" + (text ?? "<no default magazine>"));
}
}
catch (Exception e)
{
Fail(e);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private ITracker CreateTracker()
{
return new CasingTracker(delegate(string s)
{
((MelonBase)this).LoggerInstance.Msg(s);
});
}
private void Fail(Exception e)
{
fault = true;
((MelonBase)this).LoggerInstance.Error("Tracking stopped after exception: " + e);
}
private void Save()
{
prefs.SaveToFile(false);
}
private void Menu()
{
//IL_0029: 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_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
//IL_0139: Unknown result type (might be due to invalid IL or missing references)
((MelonBase)this).LoggerInstance.Msg("BoneMenu setup begin.");
Page obj = Page.Root.CreatePage("BulletCleanup", new Color(0.85f, 0.65f, 0.25f), 8, true);
obj.CreateBool("Cleanup enabled", Color.white, enabled.Value, (Action<bool>)delegate(bool v)
{
enabled.Value = v;
if (!v)
{
tracker = null;
grabbed.Clear();
}
Save();
});
obj.CreateInt("Loose item lifetime (seconds)", Color.white, Math.Clamp(lifetime.Value, 1, 300), 1, 1, 300, (Action<int>)delegate(int v)
{
lifetime.Value = v;
Save();
});
obj.CreateInt("Maximum loose items", Color.white, Math.Clamp(limit.Value, 0, 500), 10, 0, 500, (Action<int>)delegate(int v)
{
limit.Value = v;
Save();
});
obj.CreateBool("Delete dropped magazines", Color.white, magazines.Value, (Action<bool>)delegate(bool v)
{
magazines.Value = v;
Save();
((MelonBase)this).LoggerInstance.Msg("Magazine cleanup toggle saved.");
});
obj.CreateFunction("Clear tracked loose items now", Color.white, (Action)delegate
{
if (!enabled.Value)
{
((MelonBase)this).LoggerInstance.Msg("Enable cleanup before clearing loose items.");
return;
}
try
{
if (ready && !fault && tracker != null)
{
tracker.Tick(delete: true, all: true, 0, 60, magazines.Value);
}
}
catch (Exception e)
{
Fail(e);
}
});
obj.CreateFunction("Scan magazines (read only)", Color.yellow, (Action)delegate
{
if (!ready || fault || !enabled.Value || !magazines.Value)
{
((MelonBase)this).LoggerInstance.Msg("Enable cleanup and magazine detection in a loaded level first.");
return;
}
try
{
((MelonBase)this).LoggerInstance.Msg("Magazine scan BEGIN");
MagazineTypeProbe.Scan(delegate(string s)
{
((MelonBase)this).LoggerInstance.Msg(s);
});
((MelonBase)this).LoggerInstance.Msg("Magazine scan END");
}
catch (Exception e)
{
Fail(e);
}
});
obj.CreateFunction("Log cleanup status", Color.white, (Action)delegate
{
((MelonBase)this).LoggerInstance.Msg($"ready={ready}, fault={fault}, cleanup={enabled.Value}; " + (tracker?.Status ?? "waiting for gunfire or a grabbed gun/magazine"));
});
((MelonBase)this).LoggerInstance.Msg("BoneMenu setup complete.");
}
public override void OnUpdate()
{
frames++;
if (clock.ElapsedMilliseconds >= nextLog)
{
nextLog = clock.ElapsedMilliseconds + 10000;
((MelonBase)this).LoggerInstance.Msg($"Tracking probe heartbeat: updates={frames}, ready={ready}, worker={tracker != null}, cleanup={enabled.Value}; " + (tracker?.Status ?? "waiting for gunfire or a grabbed gun/magazine"));
}
try
{
if (!menu && Time.unscaledTime >= nextMenu)
{
nextMenu = Time.unscaledTime + 2f;
if (Page.Root != null)
{
Menu();
menu = true;
}
}
if (ready && !fault && enabled.Value && !(Time.unscaledTime < nextScan))
{
nextScan = Time.unscaledTime + 0.25f;
ProcessGrabs();
if (tracker != null)
{
tracker.Tick(delete: true, all: false, Math.Clamp(limit.Value, 0, 500), Math.Clamp(lifetime.Value, 1, 300), magazines.Value);
}
}
}
catch (Exception e)
{
Fail(e);
}
}
public override void OnDeinitializeMelon()
{
StopGrabDiscovery();
Hooking.OnPreFireGun -= OnFire;
Hooking.OnLevelLoaded -= Loaded;
Hooking.OnLevelLoading -= Loading;
tracker = null;
}
}
internal static class MagazineTypeProbe
{
private static readonly Action<string> Silent = delegate
{
};
private static Spawnable lastSpawn;
[MethodImpl(MethodImplOptions.NoInlining)]
internal static bool IsLoose(Poolee pool)
{
return Classify(pool, Silent) == 1;
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal static Spawnable GetSpawn(Gun gun)
{
if (!Object.op_Implicit((Object)(object)gun))
{
return null;
}
MagazineData defaultMagazine = gun.defaultMagazine;
if (!Object.op_Implicit((Object)(object)defaultMagazine))
{
return null;
}
return defaultMagazine.spawnable;
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void Reset()
{
lastSpawn = null;
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal static string Read(Gun gun)
{
if (!Object.op_Implicit((Object)(object)gun))
{
return null;
}
MagazineData defaultMagazine = gun.defaultMagazine;
Spawnable obj = (lastSpawn = (Object.op_Implicit((Object)(object)defaultMagazine) ? defaultMagazine.spawnable : null));
if (obj == null)
{
return null;
}
SpawnableCrateReference crateRef = obj.crateRef;
if (crateRef == null)
{
return null;
}
Barcode barcode = ((ScannableReference)crateRef).Barcode;
if (barcode == null)
{
return null;
}
return barcode.ID;
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void Scan(Action<string> log)
{
Spawnable obj = lastSpawn;
object obj2;
if (obj == null)
{
obj2 = null;
}
else
{
SpawnableCrateReference crateRef = obj.crateRef;
obj2 = ((crateRef != null) ? ((ScannableReference)crateRef).Barcode : null);
}
Barcode val = (Barcode)obj2;
if (val == (Barcode)null)
{
log("No magazine type learned; fire a magazine-fed gun first.");
return;
}
log("Magazine pool lookup BEGIN: " + val.ID);
if (!AssetSpawner._hasInstance)
{
log("No spawner.");
return;
}
AssetSpawner instance = AssetSpawner._instance;
if (!Object.op_Implicit((Object)(object)instance))
{
return;
}
Dictionary<Barcode, Pool> barcodeToPool = instance._barcodeToPool;
Pool val2 = default(Pool);
if (barcodeToPool == null || !barcodeToPool.TryGetValue(val, ref val2) || val2 == null)
{
log("Magazine pool not found.");
return;
}
List<Poolee> spawned = val2._spawned;
int num = spawned?.Count ?? 0;
log("Magazine pool lookup END: spawned=" + num);
int num2 = 0;
int num3 = 0;
int num4 = 0;
int num5 = 0;
for (int i = 0; i < Math.Min(num, 64); i++)
{
Poolee val3 = spawned[i];
if (!Object.op_Implicit((Object)(object)val3) || !((Component)val3).gameObject.activeInHierarchy)
{
num5++;
continue;
}
log("Magazine eligibility BEGIN ID=" + val3.ID);
int num6 = Classify(val3, log);
switch (num6)
{
case 1:
num2++;
break;
case 0:
num3++;
break;
default:
num4++;
break;
}
log("Magazine eligibility END ID=" + val3.ID + " state=" + num6);
}
log($"Magazine scan: loose={num2}, protected={num3}, missing={num4}, inactive={num5}, examined={Math.Min(num, 64)}/{num}. No magazines deleted.");
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static int Classify(Poolee pool, Action<string> log)
{
log("Magazine component lookup BEGIN");
Magazine componentInChildren = ((Component)pool).GetComponentInChildren<Magazine>();
log("Magazine component lookup END");
if (!Object.op_Implicit((Object)(object)componentInChildren) || (Object)(object)((Component)componentInChildren).GetComponentInParent<Poolee>() != (Object)(object)pool)
{
return -1;
}
log("Magazine insertion/parent checks BEGIN");
if (componentInChildren.isMagazineInserted || Object.op_Implicit((Object)(object)((Component)componentInChildren).GetComponentInParent<Gun>()) || Object.op_Implicit((Object)(object)((Component)componentInChildren).GetComponentInParent<RigManager>()))
{
return 0;
}
log("Magazine host checks BEGIN");
InteractableHost interactableHost = componentInChildren.interactableHost;
bool num = Object.op_Implicit((Object)(object)interactableHost) && interactableHost.HandCount() == 0 && !interactableHost.IsAttached && !interactableHost.IsPulling;
log("Magazine host checks END");
return num ? 1 : 0;
}
}
internal interface ITracker
{
string Status { get; }
void LearnGun(Gun gun, bool includeMagazines);
void LearnObject(GameObject obj, bool includeMagazines);
void Tick(bool delete, bool all, int maximum, int lifetime, bool includeMagazines);
}
internal sealed class CasingTracker : ITracker
{
private sealed class Entry
{
internal Poolee Pool;
internal ulong ID;
internal float Born;
}
private readonly Action<string> log;
private readonly Dictionary<string, Barcode> cases = new Dictionary<string, Barcode>();
private readonly HashSet<string> rounds = new HashSet<string>();
private readonly List<Gun> knownGuns = new List<Gun>();
private readonly HashSet<string> magazineTypes = new HashSet<string>();
private bool allowMagazines;
private readonly Dictionary<ulong, Entry> active = new Dictionary<ulong, Entry>();
private readonly List<Entry> ordered = new List<Entry>();
private int removed;
private int scanned;
private int passes;
public string Status => $"tracked={active.Count}, removed={removed}, scanned={scanned}, ammo types={cases.Count}, unfired types={rounds.Count}, magazine types={magazineTypes.Count}, passes={passes}";
internal CasingTracker(Action<string> log)
{
this.log = log;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public void LearnGun(Gun gun, bool includeMagazines)
{
if (Object.op_Implicit((Object)(object)gun))
{
if (!knownGuns.Contains(gun) && knownGuns.Count < 128)
{
knownGuns.Add(gun);
}
Learn(gun.chamberedCartridge);
Learn(gun.defaultCartridge);
if (includeMagazines)
{
LearnMagazine(gun);
}
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void LearnMagazine(Gun gun)
{
Spawnable spawn = MagazineTypeProbe.GetSpawn(gun);
object obj;
if (spawn == null)
{
obj = null;
}
else
{
SpawnableCrateReference crateRef = spawn.crateRef;
if (crateRef == null)
{
obj = null;
}
else
{
Barcode barcode = ((ScannableReference)crateRef).Barcode;
obj = ((barcode != null) ? barcode.ID : null);
}
}
string text = (string)obj;
if (!string.IsNullOrEmpty(text) && cases.Count < 128 && !rounds.Contains(text) && (!cases.ContainsKey(text) || magazineTypes.Contains(text)))
{
magazineTypes.Add(text);
if (!cases.ContainsKey(text))
{
cases.Add(text, ((ScannableReference)spawn.crateRef).Barcode);
log("Tracking magazine pool: " + text);
}
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public void LearnObject(GameObject obj, bool includeMagazines)
{
if (!Object.op_Implicit((Object)(object)obj))
{
return;
}
Gun val = obj.GetComponentInParent<Gun>();
if (!Object.op_Implicit((Object)(object)val))
{
val = obj.GetComponentInChildren<Gun>();
}
if (Object.op_Implicit((Object)(object)val))
{
LearnGun(val, includeMagazines);
}
if (!includeMagazines)
{
return;
}
Magazine val2 = obj.GetComponentInParent<Magazine>();
if (!Object.op_Implicit((Object)(object)val2))
{
val2 = obj.GetComponentInChildren<Magazine>();
}
if (!Object.op_Implicit((Object)(object)val2))
{
return;
}
Poolee componentInParent = ((Component)val2).GetComponentInParent<Poolee>();
if (!Object.op_Implicit((Object)(object)componentInParent))
{
return;
}
SpawnableCrate spawnableCrate = componentInParent.SpawnableCrate;
Barcode val3 = (Object.op_Implicit((Object)(object)spawnableCrate) ? ((Scannable)spawnableCrate).Barcode : null);
string text = ((val3 != null) ? val3.ID : null);
if (!string.IsNullOrEmpty(text) && !rounds.Contains(text) && cases.Count < 128 && (!cases.ContainsKey(text) || magazineTypes.Contains(text)))
{
magazineTypes.Add(text);
if (!cases.ContainsKey(text))
{
cases.Add(text, val3);
log("Tracking grabbed magazine pool: " + text);
}
}
}
private void Learn(CartridgeData data)
{
if (!Object.op_Implicit((Object)(object)data))
{
return;
}
Spawnable cartridgeSpawnable = data.cartridgeSpawnable;
object obj;
if (cartridgeSpawnable == null)
{
obj = null;
}
else
{
SpawnableCrateReference crateRef = cartridgeSpawnable.crateRef;
if (crateRef == null)
{
obj = null;
}
else
{
Barcode barcode = ((ScannableReference)crateRef).Barcode;
obj = ((barcode != null) ? barcode.ID : null);
}
}
string text = (string)obj;
Spawnable cartridgeCaseSpawnable = data.cartridgeCaseSpawnable;
object obj2;
if (cartridgeCaseSpawnable == null)
{
obj2 = null;
}
else
{
SpawnableCrateReference crateRef2 = cartridgeCaseSpawnable.crateRef;
if (crateRef2 == null)
{
obj2 = null;
}
else
{
Barcode barcode2 = ((ScannableReference)crateRef2).Barcode;
obj2 = ((barcode2 != null) ? barcode2.ID : null);
}
}
string text2 = (string)obj2;
if (!string.IsNullOrEmpty(text) && cases.Count < 128)
{
rounds.Add(text);
if (!cases.ContainsKey(text))
{
cases.Add(text, ((ScannableReference)data.cartridgeSpawnable.crateRef).Barcode);
log("Tracking unfired round type: " + text);
}
}
if (!string.IsNullOrEmpty(text2) && cases.Count < 128 && !cases.ContainsKey(text2))
{
cases.Add(text2, ((ScannableReference)cartridgeCaseSpawnable.crateRef).Barcode);
log("Tracking spent casing type: " + text2);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private bool LooseRound(Poolee pool)
{
if (Object.op_Implicit((Object)(object)((Component)pool).GetComponentInParent<Gun>()) || Object.op_Implicit((Object)(object)((Component)pool).GetComponentInParent<RigManager>()))
{
return false;
}
FirearmCartridge componentInChildren = ((Component)pool).GetComponentInChildren<FirearmCartridge>();
if (!Object.op_Implicit((Object)(object)componentInChildren) || (Object)(object)((Component)componentInChildren).GetComponentInParent<Poolee>() != (Object)(object)pool)
{
return false;
}
for (int num = knownGuns.Count - 1; num >= 0; num--)
{
Gun val = knownGuns[num];
if (!Object.op_Implicit((Object)(object)val))
{
knownGuns.RemoveAt(num);
}
else if ((Object)(object)val._chamberedCartridgeArt == (Object)(object)componentInChildren || (Object)(object)val._nextCartridgeArt == (Object)(object)componentInChildren)
{
return false;
}
}
foreach (InteractableHost componentsInChild in ((Component)pool).GetComponentsInChildren<InteractableHost>())
{
if (Object.op_Implicit((Object)(object)componentsInChild) && (componentsInChild.HandCount() > 0 || componentsInChild.IsAttached || componentsInChild.IsPulling))
{
return false;
}
}
InteractableHost componentInParent = ((Component)pool).GetComponentInParent<InteractableHost>();
if (Object.op_Implicit((Object)(object)componentInParent) && (componentInParent.HandCount() > 0 || componentInParent.IsAttached || componentInParent.IsPulling))
{
return false;
}
return true;
}
private bool Valid(Entry e)
{
Poolee pool = e.Pool;
if (!Object.op_Implicit((Object)(object)pool) || pool.ID != e.ID || !((Component)pool).gameObject.activeInHierarchy)
{
return false;
}
SpawnableCrate spawnableCrate = pool.SpawnableCrate;
object obj;
if (!Object.op_Implicit((Object)(object)spawnableCrate))
{
obj = null;
}
else
{
Barcode barcode = ((Scannable)spawnableCrate).Barcode;
obj = ((barcode != null) ? barcode.ID : null);
}
string text = (string)obj;
if (text != null && cases.ContainsKey(text) && !Object.op_Implicit((Object)(object)((Component)pool).GetComponentInParent<Gun>()) && (!rounds.Contains(text) || LooseRound(pool)))
{
if (magazineTypes.Contains(text))
{
if (allowMagazines)
{
return MagazineTypeProbe.IsLoose(pool);
}
return false;
}
return true;
}
return false;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void Discover()
{
if (cases.Count == 0 || !AssetSpawner._hasInstance)
{
return;
}
AssetSpawner instance = AssetSpawner._instance;
if (!Object.op_Implicit((Object)(object)instance))
{
return;
}
Dictionary<Barcode, Pool> barcodeToPool = instance._barcodeToPool;
if (barcodeToPool == null)
{
return;
}
Pool val = default(Pool);
foreach (KeyValuePair<string, Barcode> @case in cases)
{
if (magazineTypes.Contains(@case.Key) && !allowMagazines)
{
continue;
}
Barcode value = @case.Value;
if (value == (Barcode)null || !barcodeToPool.TryGetValue(value, ref val) || val == null)
{
continue;
}
List<Poolee> spawned = val._spawned;
if (spawned == null)
{
continue;
}
for (int i = 0; i < spawned.Count; i++)
{
Poolee val2 = spawned[i];
scanned++;
if (!Object.op_Implicit((Object)(object)val2) || !((Component)val2).gameObject.activeInHierarchy)
{
continue;
}
ulong iD = val2.ID;
if (active.ContainsKey(iD) || Object.op_Implicit((Object)(object)((Component)val2).GetComponentInParent<Gun>()))
{
continue;
}
if (magazineTypes.Contains(@case.Key))
{
if (!MagazineTypeProbe.IsLoose(val2))
{
continue;
}
}
else if (!Object.op_Implicit((Object)(object)((Component)val2).GetComponentInChildren<FirearmCartridge>()))
{
continue;
}
if (!rounds.Contains(@case.Key) || LooseRound(val2))
{
active.Add(iD, new Entry
{
Pool = val2,
ID = iD,
Born = Time.time
});
}
}
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public void Tick(bool delete, bool all, int maximum, int lifetime, bool includeMagazines)
{
allowMagazines = includeMagazines;
bool flag = passes < 3;
if (flag)
{
log("Casing scan begin.");
}
ordered.Clear();
foreach (Entry value in active.Values)
{
ordered.Add(value);
}
foreach (Entry item in ordered)
{
if (!Valid(item))
{
active.Remove(item.ID);
}
}
Discover();
passes++;
if (flag)
{
log("Casing scan complete: " + Status);
}
if (!delete)
{
ordered.Clear();
}
else
{
DeleteEligible(all, maximum, lifetime);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void DeleteEligible(bool all, int maximum, int lifetime)
{
ordered.Clear();
foreach (Entry value in active.Values)
{
ordered.Add(value);
}
ordered.Sort(delegate(Entry a, Entry b)
{
int num2 = a.Born.CompareTo(b.Born);
return (num2 == 0) ? a.ID.CompareTo(b.ID) : num2;
});
int num = Math.Max(0, ordered.Count - maximum);
float time = Time.time;
foreach (Entry item in ordered)
{
bool flag = num > 0;
if (flag)
{
num--;
}
if (!all && !flag && time - item.Born < (float)lifetime)
{
continue;
}
if (Valid(item))
{
if (removed < 8 || removed % 100 == 0)
{
log("Despawning loose item ID=" + item.ID);
}
item.Pool.Despawn();
removed++;
}
active.Remove(item.ID);
}
ordered.Clear();
}
}
}