Decompiled source of SafehouseProgressionModeCode Beta v0.1.10
NGA.SHPlibs.dll
Decompiled a month 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.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using Atlas; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FistVR; using HarmonyLib; using Microsoft.CodeAnalysis; using NGA.EventSys; using NGA.ItemShop; using Sodalite.Api; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyCompany("NGA")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Persistent player progression! Raid, stash loot, and deploy with seemless scene/loadout saving.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("NGA.SHPlibs")] [assembly: AssemblyTitle("BepInEx Plugin Title")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [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; } } } public class BaseViewModel { public event Action Changed; protected void NotifyListeners() { this.Changed?.Invoke(); } public virtual void Initialize() { } public virtual void Dispose() { } } public class ViewModelRegistry : MonoBehaviour { private static ViewModelRegistry _instance; private readonly Dictionary<Type, object> _map = new Dictionary<Type, object>(); private void Awake() { if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } _instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private static void EnsureInstance() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("ViewModelRegistry"); _instance = val.AddComponent<ViewModelRegistry>(); Object.DontDestroyOnLoad((Object)(object)val); } } public static T Get<T>() where T : BaseViewModel, new() { EnsureInstance(); Type typeFromHandle = typeof(T); if (!_instance._map.TryGetValue(typeFromHandle, out var value)) { T val = new T(); val.Initialize(); _instance._map[typeFromHandle] = val; value = val; } return (T)value; } public static void Prewarm<T>() where T : BaseViewModel, new() { Get<T>(); } public static void Ensure() { EnsureInstance(); } public static void Register<T>(T instance) where T : BaseViewModel { EnsureInstance(); _instance._map[typeof(T)] = instance; } public static void DisposeAll() { EnsureInstance(); foreach (KeyValuePair<Type, object> item in _instance._map) { ((BaseViewModel)item.Value).Dispose(); } _instance._map.Clear(); } } public class UIElementAnimator { public static IEnumerator AnimateRoutine(Transform rect, Transform targetTransform, float duration) { Vector3 startPos = rect.position; Quaternion startRot = rect.rotation; Vector3 targetPos = targetTransform.position; Quaternion targetRot = targetTransform.rotation; float t = 0f; while (t < duration) { t += Time.deltaTime; float lerpT = Mathf.Clamp01(t / duration); rect.position = Vector3.Lerp(startPos, targetPos, lerpT); rect.rotation = Quaternion.Lerp(startRot, targetRot, lerpT); yield return null; } rect.position = targetPos; rect.rotation = targetRot; } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string id = null, string name = null, string version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string id = null, string name = null, string version = null) { } } } namespace NGA { public sealed class BankEventsBridge { private readonly BankService _svc; private string _subRankTierReachedId; public BankEventsBridge(BankService svc) { _svc = svc; } public void Start() { if (!((Object)(object)SHGM.Instance == (Object)null) && SHGM.Instance.eventBus != null) { EventSubscriptionSpec eventSubscriptionSpec = new EventSubscriptionSpec { Channel = "Player" }; eventSubscriptionSpec.TypesExact.Add("rank_tier_reached"); _subRankTierReachedId = SHGM.Instance.eventBus.Subscribe(eventSubscriptionSpec, (Action<EventView>)OnRankTierReachedEvent); } } public void Stop() { SHGM.Instance.eventBus.Unsubscribe(_subRankTierReachedId); _subRankTierReachedId = null; } private void OnRankTierReachedEvent(EventView ev) { Debug.LogWarning((object)("||=> Recieved OnRankTierEvent bank: " + ev)); if (ev == null) { Debug.LogError((object)"OnRankTierEvent was null"); return; } RankTierReachedEvent rankTierReachedEvent = null; if (ev.Payload != null && ev.Payload.TryGetValue("event", out var value)) { rankTierReachedEvent = value as RankTierReachedEvent; } if (rankTierReachedEvent == null || rankTierReachedEvent.rewards == null) { Debug.LogError((object)("Either payload " + rankTierReachedEvent?.ToString() + " or rewards are null " + rankTierReachedEvent?.rewards)); return; } for (int i = 0; i < rankTierReachedEvent.rewards.Count; i++) { RewardDef rewardDef = rankTierReachedEvent.rewards[i]; if (rewardDef.rewardType != 1) { continue; } CurrencyReward currencyReward = rewardDef?.currency; if (currencyReward == null || currencyReward.amount == 0 || string.IsNullOrEmpty(currencyReward.currencyId)) { Debug.LogWarning((object)("Award was null " + currencyReward?.ToString() + " Amount was zero " + currencyReward?.amount + " or currency id null or empty: " + currencyReward?.currencyId)); continue; } string currencyId = currencyReward.currencyId; string text = currencyId; if (!(text == "bitties")) { if (text == "spades") { _svc.AddSpades(currencyReward.amount); } } else { _svc.ProcessTransaction(new ShBank.TransactionRecord(currencyReward.amount, "Level-reached award")); } } } } public sealed class BankService : ShService { private BankEventsBridge _eventBridge; public override void Initialize(ShMetaState meta, ShSessionManager session) { base.Initialize(meta, session); StartEventBridge(); } public override void BindToProfile(ShProfileState profile) { StopEventBridge(); base.BindToProfile(profile); StartEventBridge(); } public override void Dispose() { base.Dispose(); } private void EnsureProfileBankState() { if (BoundProfile.bank == null) { BoundProfile.bank = new ShBank(); } BoundProfile.bank.EnsureInit(); } private ShBank GetBank() { if (BoundProfile == null) { return null; } EnsureProfileBankState(); return BoundProfile.bank; } public int GetBalance() { return GetBank()?.playerBalance ?? 0; } public int GetSpades() { return GetBank()?.playerSpades ?? 0; } public void AddSpades(int amount) { if (amount == 0) { return; } ShBank bank = GetBank(); if (bank == null) { Debug.LogWarning((object)"Spades: No current profile found"); return; } bank.AddSpades(amount); if ((Object)(object)SHGM.Instance != (Object)null && SHGM.Instance.eventBus != null) { EventPublishSpec eventPublishSpec = new EventPublishSpec(); eventPublishSpec.Type = "spades_awarded"; eventPublishSpec.Channels.Add("Player"); eventPublishSpec.Payload["event"] = new SpadesAwardedEvent(amount, null); SHGM.Instance.eventBus.Publish(eventPublishSpec); } } public bool TrySpendSpades(int amount) { if (amount <= 0) { return true; } ShBank bank = GetBank(); if (bank == null) { Debug.LogWarning((object)"Spades: No current profile found"); return false; } if (bank.playerSpades < amount) { return false; } bank.SubtractSpades(amount); return true; } public void ForceSpendSpades(int amount) { if (amount != 0) { ShBank bank = GetBank(); if (bank == null) { Debug.LogWarning((object)"Spades: No current profile found"); } else { bank.SubtractSpades(amount); } } } public void AddBalance(int amount, string description = null) { if (amount != 0) { ShBank bank = GetBank(); if (bank == null) { Debug.LogWarning((object)"Bitties: No current profile found"); return; } ShBank.TransactionRecord transaction = new ShBank.TransactionRecord(Math.Abs(amount), description); bank.AddBalance(Math.Abs(amount)); bank.AddTransaction(transaction); } } public bool TrySpendBalance(int amount, string description = null) { if (amount <= 0) { return true; } ShBank bank = GetBank(); if (bank == null) { Debug.LogWarning((object)"Bitties: No current profile found"); return false; } if (bank.playerBalance < amount) { return false; } ShBank.TransactionRecord transaction = new ShBank.TransactionRecord(-amount, description); bank.SubtractBalance(amount); bank.AddTransaction(transaction); return true; } public void ForceSpendBalance(int amount, string description = null) { if (amount != 0) { ShBank bank = GetBank(); if (bank == null) { Debug.LogWarning((object)"Bitties: No current profile found"); return; } ShBank.TransactionRecord transaction = new ShBank.TransactionRecord(-Math.Abs(amount), description); bank.SubtractBalance(Math.Abs(amount)); bank.AddTransaction(transaction); } } public bool ProcessTransaction(ShBank.TransactionRecord transaction, bool forceDecrement = false) { ShBank bank = GetBank(); if (bank == null) { Debug.LogWarning((object)"Try buy: No current profile found"); return false; } if (transaction == null) { return false; } int amount = transaction.amount; if (amount < 0) { int num = -amount; if (!forceDecrement && bank.playerBalance < num) { return false; } bank.SubtractBalance(num); } else if (amount > 0) { bank.AddBalance(amount); } bank.AddTransaction(transaction); return true; } private void StartEventBridge() { if (_eventBridge == null) { _eventBridge = new BankEventsBridge(this); } _eventBridge.Start(); } private void StopEventBridge() { if (_eventBridge != null) { _eventBridge.Stop(); } } } [Serializable] public sealed class SpadesAwardedEvent { public int deltaAmount; public string sourceTag; public SpadesAwardedEvent(int deltaAmount, string sourceTag) { this.deltaAmount = deltaAmount; this.sourceTag = sourceTag; } } [Serializable] public sealed class ShBank { [Serializable] public sealed class TransactionRecord { public int amount; public string description; public TransactionRecord(int amount, string description) { this.amount = amount; this.description = description; } } public int playerBalance; public List<TransactionRecord> transactions; public int playerSpades; public ShBank() { playerBalance = 0; transactions = new List<TransactionRecord>(); playerSpades = 0; } public void EnsureInit() { if (transactions == null) { transactions = new List<TransactionRecord>(); } } public void AddBalance(int amount) { playerBalance += amount; } public void SubtractBalance(int amount) { playerBalance -= amount; } public void AddSpades(int amount) { playerSpades += amount; } public void SubtractSpades(int amount) { playerSpades -= amount; } public void AddTransaction(TransactionRecord transaction) { EnsureInit(); transactions.Insert(0, transaction); } public void ClearTransactions() { EnsureInit(); transactions.Clear(); } } public static class BankUtils { public const string BITTIES_SYMBOL = "₿"; public const string SPADES_SYMBOL = "♠"; public const string BITTIES_COLOR = "yellow"; public const string SPADES_COLOR = "lightblue"; public static string BITTIES_SYMBOL_COLORED = "<color=yellow>₿</color>"; public static string SPADES_SYMBOL_COLORED = "<color=lightblue>♠</color>"; private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture; public static string FormatAmount(int amount) { return amount.ToString("N0", Invariant); } public static string FormatFromId(string id, int amount, bool bold = true) { if (!(id == "bitties")) { if (id == "spades") { return FormatSpades(amount, bold); } return FormatAmount(amount); } return FormatBitties(amount, bold); } public static string FormatBitties(int amount, bool bold = true) { string text = FormatAmount(amount); string text2 = "<color=yellow>₿</color>" + text; return bold ? ("<b>" + text2 + "</b>") : text2; } public static string FormatBitties(float amount, bool bold = true) { string text = amount.ToString("N2", Invariant).TrimEnd(new char[1] { '0' }).TrimEnd(new char[1] { Invariant.NumberFormat.NumberDecimalSeparator[0] }); string text2 = "<color=yellow>₿</color>" + text; return bold ? ("<b>" + text2 + "</b>") : text2; } public static string FormatSpades(int amount, bool bold = true) { string text = FormatAmount(amount); string text2 = "<color=lightblue>♠</color>" + text; return bold ? ("<b>" + text2 + "</b>") : text2; } public static string FormatBoth(int bitties, int spades) { return FormatBitties(bitties) + " " + FormatSpades(spades); } } public sealed class BankViewModel : BaseViewModel { private BankService _service; public static BankViewModel Instance => ViewModelRegistry.Get<BankViewModel>(); public override void Initialize() { _service = SHGM.Instance?.sessionManager?.GetService<BankService>(); if (_service != null) { _service.Changed += HandleServiceChanged; } NotifyListeners(); } public override void Dispose() { if (_service != null) { _service.Changed -= HandleServiceChanged; } _service = null; } private void HandleServiceChanged() { NotifyListeners(); } public bool ProcessTransaction(ShBank.TransactionRecord record) { if (_service == null) { return false; } return _service.ProcessTransaction(record); } public int GetSpades() { if (_service == null) { return 0; } return _service.GetSpades(); } public void AddSpades(int amount) { if (_service != null) { _service.AddSpades(amount); } } public bool TrySpendSpades(int amount) { if (_service == null) { return false; } return _service.TrySpendSpades(amount); } public void ForceSpendSpades(int amount) { if (_service != null) { _service.ForceSpendSpades(amount); } } public int GetBalance() { if (_service == null) { return 0; } return _service.GetBalance(); } public void AddBalance(int amount, string description = null) { if (_service != null) { _service.AddBalance(amount, description); } } public bool TrySpendBalance(int amount, string description = null) { if (_service == null) { return false; } return _service.TrySpendBalance(amount, description); } public void ForceSpendBalance(int amount, string description = null) { if (_service != null) { _service.ForceSpendBalance(amount, description); } } } public class GiftMate : MonoBehaviour { public int refreshRate = 30; public float timeSinceRefresh = 0f; public int selectedIx = 0; private ShProfileState CurrentProfile => SHGM.Instance.sessionManager.ActiveProfile; public void Start() { drawGifts(); } public void Update() { timeSinceRefresh += Time.deltaTime; if (timeSinceRefresh >= (float)refreshRate) { timeSinceRefresh = 0f; drawGifts(); } } private void drawGifts() { CurrentProfile.giftsList.giftsQueue.ForEach(delegate { }); } public void BTN_SpawnSelected() { ShGiftsList giftsList = CurrentProfile.giftsList; if (selectedIx >= 0 && selectedIx < giftsList.giftsQueue.Count) { giftsList.giftsQueue[selectedIx].idsToSpawn.ForEach(delegate { }); giftsList.RemoveGift(giftsList.giftsQueue[selectedIx]); drawGifts(); } } } [Serializable] public class ShGift { public string tinyMessage; public List<string> idsToSpawn; public ShGift(string tinyMessage, List<string> idsToSpawn) { this.tinyMessage = tinyMessage; this.idsToSpawn = idsToSpawn; } } [Serializable] public class ShGiftsList { public List<ShGift> giftsQueue; public ShGiftsList() { giftsQueue = new List<ShGift>(); } public void AddGift(ShGift g) { giftsQueue.Add(g); } public void RemoveGift(ShGift g) { giftsQueue.Remove(g); } } public interface IShService { void Initialize(ShMetaState meta, ShSessionManager session); void BindToProfile(ShProfileState profile); } public abstract class ShService : IShService { protected ShMetaState Meta; protected ShSessionManager Session; protected ShProfileState BoundProfile; public event Action Changed; public virtual void Initialize(ShMetaState meta, ShSessionManager session) { Meta = meta; Session = session; } public virtual void BindToProfile(ShProfileState profile) { BoundProfile = profile; NotifyListeners(); } protected void NotifyListeners() { this.Changed?.Invoke(); } public virtual void Dispose() { Meta = null; Session = null; BoundProfile = null; } } internal static class ItemFeatureExtractor { internal static void TryEnrichFromFvrObject(ItemIndexRecord rec) { if (rec != null && !string.IsNullOrEmpty(rec.ItemID)) { if (!IM.OD.TryGetValue(rec.ItemID, out var value) || (Object)(object)value == (Object)null) { rec.FvrObject = null; return; } rec.FvrObject = value; Extract(rec, value); } } private static void Extract(ItemIndexRecord rec, FVRObject fvr) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //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_0081: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0105: 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_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: 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_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0286: 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_02b4: 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_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_03c8: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Unknown result type (might be due to invalid IL or missing references) rec.StringTags.Clear(); rec.TagTokenSet.Clear(); ItemExtractedFeatures itemExtractedFeatures = new ItemExtractedFeatures(); itemExtractedFeatures.Family = fvr.Family; itemExtractedFeatures.Category = fvr.Category; itemExtractedFeatures.IsModContent = fvr.IsModContent; itemExtractedFeatures.Mass = fvr.Mass; itemExtractedFeatures.MagazineCapacity = fvr.MagazineCapacity; itemExtractedFeatures.RequiresPicatinnySight = fvr.RequiresPicatinnySight; itemExtractedFeatures.TagEra = fvr.TagEra; itemExtractedFeatures.TagSet = fvr.TagSet; itemExtractedFeatures.TagFirearmSize = fvr.TagFirearmSize; itemExtractedFeatures.TagFirearmAction = fvr.TagFirearmAction; itemExtractedFeatures.TagFirearmRoundPower = fvr.TagFirearmRoundPower; itemExtractedFeatures.TagFirearmCountryOfOrigin = fvr.TagFirearmCountryOfOrigin; itemExtractedFeatures.TagFirearmFirstYear = fvr.TagFirearmFirstYear; itemExtractedFeatures.TagAttachmentMount = fvr.TagAttachmentMount; itemExtractedFeatures.TagAttachmentFeature = fvr.TagAttachmentFeature; itemExtractedFeatures.TagMeleeStyle = fvr.TagMeleeStyle; itemExtractedFeatures.TagMeleeHandedness = fvr.TagMeleeHandedness; itemExtractedFeatures.TagPowerupType = fvr.TagPowerupType; itemExtractedFeatures.TagThrownType = fvr.TagThrownType; itemExtractedFeatures.TagThrownDamageType = fvr.TagThrownDamageType; itemExtractedFeatures.TagFirearmFiringModes = ((fvr.TagFirearmFiringModes != null) ? new List<OTagFirearmFiringMode>(fvr.TagFirearmFiringModes) : null); itemExtractedFeatures.TagFirearmFeedOption = ((fvr.TagFirearmFeedOption != null) ? new List<OTagFirearmFeedOption>(fvr.TagFirearmFeedOption) : null); itemExtractedFeatures.TagFirearmMounts = ((fvr.TagFirearmMounts != null) ? new List<OTagFirearmMount>(fvr.TagFirearmMounts) : null); itemExtractedFeatures.MagazineType = fvr.MagazineType; itemExtractedFeatures.ClipType = fvr.ClipType; itemExtractedFeatures.UsesRoundTypeFlag = fvr.UsesRoundTypeFlag; itemExtractedFeatures.RoundType = fvr.RoundType; itemExtractedFeatures.CreditCost = fvr.CreditCost; itemExtractedFeatures.CompatibleMagazines = ExtractIds(fvr.CompatibleMagazines); itemExtractedFeatures.CompatibleClips = ExtractIds(fvr.CompatibleClips); itemExtractedFeatures.CompatibleSpeedLoaders = ExtractIds(fvr.CompatibleSpeedLoaders); itemExtractedFeatures.CompatibleSingleRounds = ExtractIds(fvr.CompatibleSingleRounds); itemExtractedFeatures.BespokeAttachments = ExtractIds(fvr.BespokeAttachments); itemExtractedFeatures.RequiredSecondaryPieces = ExtractIds(fvr.RequiredSecondaryPieces); rec.Features = itemExtractedFeatures; AddEnumTag(rec, "Era", itemExtractedFeatures.TagEra); AddEnumTag(rec, "Set", itemExtractedFeatures.TagSet); AddEnumTag(rec, "FirearmSize", itemExtractedFeatures.TagFirearmSize); AddEnumTag(rec, "FirearmAction", itemExtractedFeatures.TagFirearmAction); AddEnumTag(rec, "FirearmRoundPower", itemExtractedFeatures.TagFirearmRoundPower); AddEnumTag(rec, "Country", itemExtractedFeatures.TagFirearmCountryOfOrigin); AddEnumTag(rec, "AttachmentMount", itemExtractedFeatures.TagAttachmentMount); AddEnumTag(rec, "AttachmentFeature", itemExtractedFeatures.TagAttachmentFeature); AddEnumTag(rec, "MeleeStyle", itemExtractedFeatures.TagMeleeStyle); AddEnumTag(rec, "MeleeHandedness", itemExtractedFeatures.TagMeleeHandedness); AddEnumTag(rec, "PowerupType", itemExtractedFeatures.TagPowerupType); AddEnumTag(rec, "ThrownType", itemExtractedFeatures.TagThrownType); AddEnumTag(rec, "ThrownDamage", itemExtractedFeatures.TagThrownDamageType); if (itemExtractedFeatures.TagFirearmFirstYear > 0) { AddStringTag(rec, "Year_" + itemExtractedFeatures.TagFirearmFirstYear); } if (itemExtractedFeatures.TagFirearmFiringModes != null) { for (int i = 0; i < itemExtractedFeatures.TagFirearmFiringModes.Count; i++) { AddEnumTag(rec, "FiringMode", itemExtractedFeatures.TagFirearmFiringModes[i]); } } if (itemExtractedFeatures.TagFirearmFeedOption != null) { for (int j = 0; j < itemExtractedFeatures.TagFirearmFeedOption.Count; j++) { AddEnumTag(rec, "FeedOption", itemExtractedFeatures.TagFirearmFeedOption[j]); } } if (itemExtractedFeatures.TagFirearmMounts != null) { for (int k = 0; k < itemExtractedFeatures.TagFirearmMounts.Count; k++) { AddEnumTag(rec, "FirearmMount", itemExtractedFeatures.TagFirearmMounts[k]); } } AddStringTag(rec, "RoundType_" + ((object)Unsafe.As<FireArmRoundType, FireArmRoundType>(ref itemExtractedFeatures.RoundType)/*cast due to .constrained prefix*/).ToString()); AddStringTag(rec, "MagazineType_" + ((object)Unsafe.As<FireArmMagazineType, FireArmMagazineType>(ref itemExtractedFeatures.MagazineType)/*cast due to .constrained prefix*/).ToString()); AddStringTag(rec, "ClipType_" + ((object)Unsafe.As<FireArmClipType, FireArmClipType>(ref itemExtractedFeatures.ClipType)/*cast due to .constrained prefix*/).ToString()); AddStringTag(rec, "AttachmentMount_" + ((object)Unsafe.As<OTagFirearmMount, OTagFirearmMount>(ref itemExtractedFeatures.TagAttachmentMount)/*cast due to .constrained prefix*/).ToString()); if (rec.Data != null) { rec.Data.Tags.Clear(); rec.Data.Tags.AddRange(rec.StringTags); } } private static List<string> ExtractIds(List<FVRObject> objs) { if (objs == null || objs.Count == 0) { return null; } List<string> list = new List<string>(objs.Count); for (int i = 0; i < objs.Count; i++) { FVRObject val = objs[i]; if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(val.ItemID)) { list.Add(val.ItemID); } } return list; } private static void AddEnumTag(ItemIndexRecord rec, string prefix, object enumValue) { if (enumValue != null) { string text = enumValue.ToString(); if (!string.Equals(text, "None", StringComparison.OrdinalIgnoreCase)) { AddStringTag(rec, prefix + "_" + text); } } } private static void AddStringTag(ItemIndexRecord rec, string tag) { if (!string.IsNullOrEmpty(tag)) { string text = tag.Trim(); if (text.Length != 0) { rec.StringTags.Add(text); rec.TagTokenSet.Add(ItemToken.StableToken(text)); } } } } internal static class ItemQueryEngine { internal static List<BaseItemData> Query(ItemIndex.FilterRequest request) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (request == null) { return new List<BaseItemData>(0); } HashSet<string> hashSet = null; if (request.Category.HasValue) { hashSet = ItemIndexStore.IntersectCandidates(hashSet, ItemIndexStore.GetByCategory(request.Category.Value)); } if (!string.IsNullOrEmpty(request.Family)) { hashSet = ItemIndexStore.IntersectCandidates(hashSet, ItemIndexStore.GetByFamily(request.Family)); } if (request.RequireAllStringTags != null && request.RequireAllStringTags.Count > 0) { for (int i = 0; i < request.RequireAllStringTags.Count; i++) { int tok = ItemToken.StableToken(request.RequireAllStringTags[i]); hashSet = ItemIndexStore.IntersectCandidates(hashSet, ItemIndexStore.GetByTagToken(tok)); if (hashSet != null && hashSet.Count == 0) { break; } } } if (hashSet == null) { hashSet = ItemIndexStore.GetAllIds(); } List<BaseItemData> list = new List<BaseItemData>(Math.Max(0, request.MaxResults)); foreach (string item in hashSet) { if (ItemIndexStore.TryGet(item, out var rec) && Matches(request, rec)) { if (rec.Data != null) { list.Add(rec.Data); } if (list.Count >= request.MaxResults) { break; } } } return list; } private static bool Matches(ItemIndex.FilterRequest req, ItemIndexRecord rec) { //IL_007b: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0147: 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_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: 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_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Unknown result type (might be due to invalid IL or missing references) //IL_043e: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_0471: Unknown result type (might be due to invalid IL or missing references) //IL_049e: Unknown result type (might be due to invalid IL or missing references) //IL_04a4: Unknown result type (might be due to invalid IL or missing references) ItemExtractedFeatures features = rec.Features; if (!string.IsNullOrEmpty(req.ItemID) && !string.Equals(req.ItemID, rec.ItemID, StringComparison.OrdinalIgnoreCase)) { return false; } if (!string.IsNullOrEmpty(req.Family) && !string.Equals(req.Family, features.Family, StringComparison.OrdinalIgnoreCase)) { return false; } if (req.Category.HasValue && req.Category.Value != features.Category) { return false; } if (req.IsModContent.HasValue && req.IsModContent.Value != features.IsModContent) { return false; } if (req.TagEra.HasValue && req.TagEra.Value != features.TagEra) { return false; } if (req.TagSet.HasValue && req.TagSet.Value != features.TagSet) { return false; } if (req.TagFirearmSize.HasValue && req.TagFirearmSize.Value != features.TagFirearmSize) { return false; } if (req.TagFirearmAction.HasValue && req.TagFirearmAction.Value != features.TagFirearmAction) { return false; } if (req.TagFirearmRoundPower.HasValue && req.TagFirearmRoundPower.Value != features.TagFirearmRoundPower) { return false; } if (req.TagFirearmCountryOfOrigin.HasValue && req.TagFirearmCountryOfOrigin.Value != features.TagFirearmCountryOfOrigin) { return false; } if (req.TagAttachmentMount.HasValue && req.TagAttachmentMount.Value != features.TagAttachmentMount) { return false; } if (req.TagAttachmentFeature.HasValue && req.TagAttachmentFeature.Value != features.TagAttachmentFeature) { return false; } if (req.TagMeleeStyle.HasValue && req.TagMeleeStyle.Value != features.TagMeleeStyle) { return false; } if (req.TagMeleeHandedness.HasValue && req.TagMeleeHandedness.Value != features.TagMeleeHandedness) { return false; } if (req.TagPowerupType.HasValue && req.TagPowerupType.Value != features.TagPowerupType) { return false; } if (req.TagThrownType.HasValue && req.TagThrownType.Value != features.TagThrownType) { return false; } if (req.TagThrownDamageType.HasValue && req.TagThrownDamageType.Value != features.TagThrownDamageType) { return false; } if (req.FirearmFirstYearMin.HasValue && features.TagFirearmFirstYear < req.FirearmFirstYearMin.Value) { return false; } if (req.FirearmFirstYearMax.HasValue && features.TagFirearmFirstYear > req.FirearmFirstYearMax.Value) { return false; } if (req.MassMin.HasValue && features.Mass < req.MassMin.Value) { return false; } if (req.MassMax.HasValue && features.Mass > req.MassMax.Value) { return false; } if (req.RoundType.HasValue && req.RoundType.Value != features.RoundType) { return false; } if (req.MagazineType.HasValue && req.MagazineType.Value != features.MagazineType) { return false; } if (req.ClipType.HasValue && req.ClipType.Value != features.ClipType) { return false; } if (req.RequireAnyFiringModes != null && req.RequireAnyFiringModes.Count > 0) { if (features.TagFirearmFiringModes == null || features.TagFirearmFiringModes.Count == 0) { return false; } if (!OverlapsAny(req.RequireAnyFiringModes, features.TagFirearmFiringModes)) { return false; } } if (req.RequireAnyFeedOptions != null && req.RequireAnyFeedOptions.Count > 0) { if (features.TagFirearmFeedOption == null || features.TagFirearmFeedOption.Count == 0) { return false; } if (!OverlapsAny(req.RequireAnyFeedOptions, features.TagFirearmFeedOption)) { return false; } } if (req.RequireAnyFirearmMounts != null && req.RequireAnyFirearmMounts.Count > 0) { if (features.TagFirearmMounts == null || features.TagFirearmMounts.Count == 0) { return false; } if (!OverlapsAny(req.RequireAnyFirearmMounts, features.TagFirearmMounts)) { return false; } } if (req.RequireAnyStringTags != null && req.RequireAnyStringTags.Count > 0) { bool flag = false; for (int i = 0; i < req.RequireAnyStringTags.Count; i++) { int item = ItemToken.StableToken(req.RequireAnyStringTags[i]); if (rec.TagTokenSet.Contains(item)) { flag = true; break; } } if (!flag) { return false; } } if (req.RequireAllStringTags != null && req.RequireAllStringTags.Count > 0) { for (int j = 0; j < req.RequireAllStringTags.Count; j++) { int item2 = ItemToken.StableToken(req.RequireAllStringTags[j]); if (!rec.TagTokenSet.Contains(item2)) { return false; } } } return true; } private static bool OverlapsAny<T>(List<T> requiredAny, List<T> actual) { for (int i = 0; i < requiredAny.Count; i++) { T x = requiredAny[i]; for (int j = 0; j < actual.Count; j++) { if (EqualityComparer<T>.Default.Equals(x, actual[j])) { return true; } } } return false; } } internal static class ItemSimilarityEngine { internal static List<ItemIndex.SimilarityResult> FindNearestNeighbors(string seedItemId, int k) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(seedItemId)) { return new List<ItemIndex.SimilarityResult>(0); } if (!ItemIndexStore.TryGet(seedItemId, out var rec)) { ItemIndex.EnsureIndexed(seedItemId); if (!ItemIndexStore.TryGet(seedItemId, out rec)) { return new List<ItemIndex.SimilarityResult>(0); } } ItemSimilarityProfile profile = GetProfile(rec); HashSet<string> hashSet = null; if (profile.RequireSameCategory) { hashSet = ItemIndexStore.IntersectCandidates(hashSet, ItemIndexStore.GetByCategory(rec.Features.Category)); } if ((Object)(object)rec.FvrObject != (Object)null) { if (profile.RequireSameRoundTypeForFirearms) { hashSet = ItemIndexStore.IntersectCandidates(hashSet, ItemIndexStore.GetByTagToken(ItemToken.StableToken("RoundType_" + ((object)Unsafe.As<FireArmRoundType, FireArmRoundType>(ref rec.Features.RoundType)/*cast due to .constrained prefix*/).ToString()))); } if (profile.RequireSameAttachmentMountForAttachments) { hashSet = ItemIndexStore.IntersectCandidates(hashSet, ItemIndexStore.GetByTagToken(ItemToken.StableToken("AttachmentMount_" + ((object)Unsafe.As<OTagFirearmMount, OTagFirearmMount>(ref rec.Features.TagAttachmentMount)/*cast due to .constrained prefix*/).ToString()))); } } if (hashSet == null) { hashSet = ItemIndexStore.GetAllIds(); } List<ItemIndex.SimilarityResult> list = new List<ItemIndex.SimilarityResult>(Mathf.Min(Mathf.Max(8, k * 8), hashSet.Count)); foreach (string item in hashSet) { if (!string.Equals(item, seedItemId, StringComparison.OrdinalIgnoreCase) && ItemIndexStore.TryGet(item, out var rec2)) { float num = ScoreSimilarity(rec, rec2, profile); if (!(num <= 0f)) { list.Add(new ItemIndex.SimilarityResult { ItemID = item, Score = num, Data = rec2.Data }); } } } list.Sort((ItemIndex.SimilarityResult a, ItemIndex.SimilarityResult b) => b.Score.CompareTo(a.Score)); if (k < 1) { k = 1; } if (list.Count > k) { list.RemoveRange(k, list.Count - k); } return list; } internal static float PredictPrice(string seedItemId, int k = 3) { List<ItemIndex.SimilarityResult> list = FindNearestNeighbors(seedItemId, Mathf.Max(1, k * 3)); float num = 0f; int num2 = 0; for (int i = 0; i < list.Count; i++) { BaseItemData data = list[i].Data; if (data != null && !(data.Price <= 0f)) { num += data.Price; num2++; if (num2 >= k) { break; } } } return (num2 == 0) ? 0f : (num / (float)num2); } private static ItemSimilarityProfile GetProfile(ItemIndexRecord seed) { return ItemSimilarityPolicy.Default; } private static float ScoreSimilarity(ItemIndexRecord a, ItemIndexRecord b, ItemSimilarityProfile p) { //IL_000d: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_011a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) float num = 0f; if (a.Features.Category == b.Features.Category) { num += p.WeightCategory; } if (!string.IsNullOrEmpty(a.Features.Family) && string.Equals(a.Features.Family, b.Features.Family, StringComparison.OrdinalIgnoreCase)) { num += p.WeightFamily; } ref OTagEra tagEra = ref a.Features.TagEra; object obj = b.Features.TagEra; if (((object)Unsafe.As<OTagEra, OTagEra>(ref tagEra)/*cast due to .constrained prefix*/).Equals(obj)) { num += p.WeightEra; } ref OTagSet tagSet = ref a.Features.TagSet; object obj2 = b.Features.TagSet; if (((object)Unsafe.As<OTagSet, OTagSet>(ref tagSet)/*cast due to .constrained prefix*/).Equals(obj2)) { num += p.WeightSet; } ref OTagFirearmAction tagFirearmAction = ref a.Features.TagFirearmAction; object obj3 = b.Features.TagFirearmAction; if (((object)Unsafe.As<OTagFirearmAction, OTagFirearmAction>(ref tagFirearmAction)/*cast due to .constrained prefix*/).Equals(obj3)) { num += p.WeightAction; } ref OTagFirearmSize tagFirearmSize = ref a.Features.TagFirearmSize; object obj4 = b.Features.TagFirearmSize; if (((object)Unsafe.As<OTagFirearmSize, OTagFirearmSize>(ref tagFirearmSize)/*cast due to .constrained prefix*/).Equals(obj4)) { num += p.WeightSize; } ref OTagFirearmRoundPower tagFirearmRoundPower = ref a.Features.TagFirearmRoundPower; object obj5 = b.Features.TagFirearmRoundPower; if (((object)Unsafe.As<OTagFirearmRoundPower, OTagFirearmRoundPower>(ref tagFirearmRoundPower)/*cast due to .constrained prefix*/).Equals(obj5)) { num += p.WeightRoundPower; } ref OTagFirearmCountryOfOrigin tagFirearmCountryOfOrigin = ref a.Features.TagFirearmCountryOfOrigin; object obj6 = b.Features.TagFirearmCountryOfOrigin; if (((object)Unsafe.As<OTagFirearmCountryOfOrigin, OTagFirearmCountryOfOrigin>(ref tagFirearmCountryOfOrigin)/*cast due to .constrained prefix*/).Equals(obj6)) { num += p.WeightCountry; } ref OTagFirearmMount tagAttachmentMount = ref a.Features.TagAttachmentMount; object obj7 = b.Features.TagAttachmentMount; if (((object)Unsafe.As<OTagFirearmMount, OTagFirearmMount>(ref tagAttachmentMount)/*cast due to .constrained prefix*/).Equals(obj7)) { num += p.WeightAttachmentMount; } ref FireArmRoundType roundType = ref a.Features.RoundType; object obj8 = b.Features.RoundType; if (((object)Unsafe.As<FireArmRoundType, FireArmRoundType>(ref roundType)/*cast due to .constrained prefix*/).Equals(obj8)) { num += p.WeightRoundType; } if (a.Features.TagFirearmFirstYear > 0 && b.Features.TagFirearmFirstYear > 0) { int num2 = Mathf.Abs(a.Features.TagFirearmFirstYear - b.Features.TagFirearmFirstYear); float num3 = 1f / (1f + (float)num2 / Mathf.Max(1f, p.FirstYearScale)); num += num3 * p.WeightFirstYear; } num += OverlapScore(a.Features.TagFirearmFiringModes, b.Features.TagFirearmFiringModes) * p.WeightFiringModes; num += OverlapScore(a.Features.TagFirearmFeedOption, b.Features.TagFirearmFeedOption) * p.WeightFeedOptions; num += OverlapScore(a.Features.TagFirearmMounts, b.Features.TagFirearmMounts) * p.WeightMounts; num += CompatibilityOverlapScore(a.Features, b.Features) * p.WeightCompatibility; if (a.TagTokenSet.Count > 0 && b.TagTokenSet.Count > 0) { int num4 = 0; if (a.TagTokenSet.Count <= b.TagTokenSet.Count) { foreach (int item in a.TagTokenSet) { if (b.TagTokenSet.Contains(item)) { num4++; } } } else { foreach (int item2 in b.TagTokenSet) { if (a.TagTokenSet.Contains(item2)) { num4++; } } } int num5 = Mathf.Max(1, Mathf.Min(a.TagTokenSet.Count, b.TagTokenSet.Count)); float num6 = (float)num4 / (float)num5; num += num6 * p.WeightStringTags; } return num; } private static float OverlapScore<T>(List<T> a, List<T> b) { if (a == null || b == null) { return 0f; } if (a.Count == 0 || b.Count == 0) { return 0f; } int num = 0; for (int i = 0; i < a.Count; i++) { T x = a[i]; for (int j = 0; j < b.Count; j++) { if (EqualityComparer<T>.Default.Equals(x, b[j])) { num++; break; } } } int num2 = Mathf.Max(1, Mathf.Min(a.Count, b.Count)); return (float)num / (float)num2; } private static float CompatibilityOverlapScore(ItemExtractedFeatures a, ItemExtractedFeatures b) { int num = 0; int denom = 0; num += OverlapCount(a.CompatibleMagazines, b.CompatibleMagazines, ref denom); num += OverlapCount(a.CompatibleClips, b.CompatibleClips, ref denom); num += OverlapCount(a.CompatibleSpeedLoaders, b.CompatibleSpeedLoaders, ref denom); num += OverlapCount(a.CompatibleSingleRounds, b.CompatibleSingleRounds, ref denom); num += OverlapCount(a.BespokeAttachments, b.BespokeAttachments, ref denom); return (denom <= 0) ? 0f : ((float)num / (float)denom); } private static int OverlapCount(List<string> a, List<string> b, ref int denom) { if (a == null || b == null) { return 0; } if (a.Count == 0 || b.Count == 0) { return 0; } denom += Mathf.Max(1, Mathf.Min(a.Count, b.Count)); int num = 0; for (int i = 0; i < a.Count; i++) { string a2 = a[i]; for (int j = 0; j < b.Count; j++) { if (string.Equals(a2, b[j], StringComparison.OrdinalIgnoreCase)) { num++; break; } } } return num; } } public static class ItemIndex { [Serializable] public sealed class FilterRequest { public string ItemID; public string Family; public ObjectCategory? Category; public OTagEra? TagEra; public OTagSet? TagSet; public OTagFirearmSize? TagFirearmSize; public OTagFirearmAction? TagFirearmAction; public OTagFirearmRoundPower? TagFirearmRoundPower; public OTagFirearmCountryOfOrigin? TagFirearmCountryOfOrigin; public OTagFirearmMount? TagAttachmentMount; public OTagAttachmentFeature? TagAttachmentFeature; public OTagMeleeStyle? TagMeleeStyle; public OTagMeleeHandedness? TagMeleeHandedness; public OTagPowerupType? TagPowerupType; public OTagThrownType? TagThrownType; public OTagThrownDamageType? TagThrownDamageType; public int? FirearmFirstYearMin; public int? FirearmFirstYearMax; public float? MassMin; public float? MassMax; public FireArmRoundType? RoundType; public FireArmMagazineType? MagazineType; public FireArmClipType? ClipType; public List<OTagFirearmFiringMode> RequireAnyFiringModes; public List<OTagFirearmFeedOption> RequireAnyFeedOptions; public List<OTagFirearmMount> RequireAnyFirearmMounts; public List<string> RequireAllStringTags; public List<string> RequireAnyStringTags; public bool? IsModContent; public int MaxResults = 100; } public struct SimilarityResult { public string ItemID; public float Score; public BaseItemData Data; } public static void Clear() { ItemIndexStore.Clear(); } public static void AddOrUpdateItem(BaseItemData item) { if (item != null && !string.IsNullOrEmpty(item.ObjectID)) { ItemIndexRecord orCreate = ItemIndexStore.GetOrCreate(item.ObjectID); orCreate.Data = item; ItemFeatureExtractor.TryEnrichFromFvrObject(orCreate); ItemIndexStore.Commit(orCreate); } } public static void AddOrUpdateItem(string itemId) { if (!string.IsNullOrEmpty(itemId)) { ItemIndexRecord orCreate = ItemIndexStore.GetOrCreate(itemId); if (orCreate.Data == null) { orCreate.Data = new BaseItemData(itemId, 0f); } ItemFeatureExtractor.TryEnrichFromFvrObject(orCreate); ItemIndexStore.Commit(orCreate); } } public static void AddItems(List<string> objectIds) { if (objectIds != null) { for (int i = 0; i < objectIds.Count; i++) { AddOrUpdateItem(objectIds[i]); } } } public static void AddItems(List<BaseItemData> items) { if (items != null) { for (int i = 0; i < items.Count; i++) { AddOrUpdateItem(items[i]); } } } public static void EnsureIndexed(string itemId) { if (!string.IsNullOrEmpty(itemId) && !ItemIndexStore.Contains(itemId)) { AddOrUpdateItem(new BaseItemData(itemId, 0f)); } } public static ItemExtractedFeatures GetItemFeatures(string itemId) { if (string.IsNullOrEmpty(itemId)) { return null; } ItemIndexStore.TryGet(itemId, out var rec); return rec?.Features; } public static List<BaseItemData> Query(FilterRequest request) { return ItemQueryEngine.Query(request); } public static List<SimilarityResult> FindNearestNeighbors(string seedItemId, int k = 10) { return ItemSimilarityEngine.FindNearestNeighbors(seedItemId, k); } public static float PredictPrice(string seedItemId, int k = 5) { return ItemSimilarityEngine.PredictPrice(seedItemId, k); } public static List<BaseItemData> FindByItemId(string itemId) { if (string.IsNullOrEmpty(itemId)) { return new List<BaseItemData>(0); } return Query(new FilterRequest { ItemID = itemId, MaxResults = 1 }); } public static List<BaseItemData> FindByCategory(ObjectCategory category, int maxResults = 100) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return Query(new FilterRequest { Category = category, MaxResults = maxResults }); } public static List<BaseItemData> FindByFamily(string family, int maxResults = 100) { return Query(new FilterRequest { Family = family, MaxResults = maxResults }); } public static List<BaseItemData> FindByEra(OTagEra era, int maxResults = 100) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return Query(new FilterRequest { TagEra = era, MaxResults = maxResults }); } public static List<BaseItemData> FindBySet(OTagSet set, int maxResults = 100) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return Query(new FilterRequest { TagSet = set, MaxResults = maxResults }); } public static List<BaseItemData> FindByRoundType(FireArmRoundType roundType, int maxResults = 100) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return Query(new FilterRequest { RoundType = roundType, MaxResults = maxResults }); } public static List<BaseItemData> FindByMagazineType(FireArmMagazineType magazineType, int maxResults = 100) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return Query(new FilterRequest { MagazineType = magazineType, MaxResults = maxResults }); } public static List<BaseItemData> FindByClipType(FireArmClipType clipType, int maxResults = 100) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return Query(new FilterRequest { ClipType = clipType, MaxResults = maxResults }); } public static List<BaseItemData> FindByFirstYearRange(int minInclusive, int maxInclusive, int maxResults = 100) { return Query(new FilterRequest { FirearmFirstYearMin = minInclusive, FirearmFirstYearMax = maxInclusive, MaxResults = maxResults }); } public static List<BaseItemData> FindByTagsAll(int maxResults, params string[] tags) { FilterRequest filterRequest = new FilterRequest { MaxResults = maxResults, RequireAllStringTags = new List<string>() }; if (tags != null) { for (int i = 0; i < tags.Length; i++) { if (!string.IsNullOrEmpty(tags[i])) { filterRequest.RequireAllStringTags.Add(tags[i]); } } } if (filterRequest.RequireAllStringTags.Count == 0) { filterRequest.RequireAllStringTags = null; } return Query(filterRequest); } public static List<BaseItemData> FindByTagsAny(int maxResults, params string[] tags) { FilterRequest filterRequest = new FilterRequest { MaxResults = maxResults, RequireAnyStringTags = new List<string>() }; if (tags != null) { for (int i = 0; i < tags.Length; i++) { if (!string.IsNullOrEmpty(tags[i])) { filterRequest.RequireAnyStringTags.Add(tags[i]); } } } if (filterRequest.RequireAnyStringTags.Count == 0) { filterRequest.RequireAnyStringTags = null; } return Query(filterRequest); } public static List<BaseItemData> FindByAnyFiringMode(int maxResults, params OTagFirearmFiringMode[] modes) { FilterRequest filterRequest = new FilterRequest { MaxResults = maxResults, RequireAnyFiringModes = new List<OTagFirearmFiringMode>() }; if (modes != null) { for (int i = 0; i < modes.Length; i++) { filterRequest.RequireAnyFiringModes.Add(modes[i]); } } if (filterRequest.RequireAnyFiringModes.Count == 0) { filterRequest.RequireAnyFiringModes = null; } return Query(filterRequest); } public static HashSet<string> GetIdsByCategoryCopy(ObjectCategory category) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) HashSet<string> byCategory = ItemIndexStore.GetByCategory(category); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (string item in byCategory) { hashSet.Add(item); } return hashSet; } public static HashSet<string> GetIdsByFamilyCopy(string family) { HashSet<string> byFamily = ItemIndexStore.GetByFamily(family); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (string item in byFamily) { hashSet.Add(item); } return hashSet; } public static HashSet<string> GetIdsByStringTagCopy(string tag) { int tok = ItemToken.StableToken(tag); HashSet<string> byTagToken = ItemIndexStore.GetByTagToken(tok); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (string item in byTagToken) { hashSet.Add(item); } return hashSet; } public static bool TryGetIndexedPrice(string itemId, out float price) { price = 0f; if (string.IsNullOrEmpty(itemId)) { return false; } if (!ItemIndexStore.TryGet(itemId, out var rec)) { return false; } if (rec.Data == null) { return false; } price = rec.Data.Price; return true; } public static void OverrideIndexedPrice(string itemId, float newPrice) { if (!string.IsNullOrEmpty(itemId)) { ItemIndexRecord orCreate = ItemIndexStore.GetOrCreate(itemId); if (orCreate.Data == null) { orCreate.Data = new BaseItemData(itemId, newPrice); ItemIndexStore.Commit(orCreate); } else { orCreate.Data.Price = newPrice; ItemIndexStore.Commit(orCreate); } } } } [Serializable] public class BaseItemData { public float Price; public List<string> Tags = new List<string>(); public string ObjectID { get; protected set; } public BaseItemData(string objectId, float price) { ObjectID = objectId; Price = price; } public override string ToString() { return string.Format("[Base Item Data] ID: {0}, Price: {1:C}, Tags: {2}", ObjectID, Price, string.Join(", ", Tags.ToArray())); } } public class ItemExtractedFeatures { public string Family; public ObjectCategory Category; public bool IsModContent; public float Mass; public int MagazineCapacity; public bool RequiresPicatinnySight; public OTagEra TagEra; public OTagSet TagSet; public OTagFirearmSize TagFirearmSize; public OTagFirearmAction TagFirearmAction; public OTagFirearmRoundPower TagFirearmRoundPower; public OTagFirearmCountryOfOrigin TagFirearmCountryOfOrigin; public int TagFirearmFirstYear; public OTagFirearmMount TagAttachmentMount; public OTagAttachmentFeature TagAttachmentFeature; public OTagMeleeStyle TagMeleeStyle; public OTagMeleeHandedness TagMeleeHandedness; public OTagPowerupType TagPowerupType; public OTagThrownType TagThrownType; public OTagThrownDamageType TagThrownDamageType; public List<OTagFirearmFiringMode> TagFirearmFiringModes; public List<OTagFirearmFeedOption> TagFirearmFeedOption; public List<OTagFirearmMount> TagFirearmMounts; public FireArmMagazineType MagazineType; public FireArmClipType ClipType; public bool UsesRoundTypeFlag; public FireArmRoundType RoundType; public List<string> CompatibleMagazines; public List<string> CompatibleClips; public List<string> CompatibleSpeedLoaders; public List<string> CompatibleSingleRounds; public List<string> BespokeAttachments; public List<string> RequiredSecondaryPieces; public int CreditCost; } internal sealed class ItemIndexRecord { public string ItemID; public BaseItemData Data; public FVRObject FvrObject; public ItemExtractedFeatures Features; public readonly List<string> StringTags = new List<string>(32); public readonly HashSet<int> TagTokenSet = new HashSet<int>(); } internal sealed class ItemSimilarityProfile { public bool RequireSameCategory = true; public bool RequireSameRoundTypeForFirearms = false; public bool RequireSameAttachmentMountForAttachments = false; public float WeightCategory = 5f; public float WeightFamily = 4f; public float WeightSet = 4f; public float WeightRoundPower = 3f; public float WeightRoundType = 3f; public float WeightFirstYear = 2f; public float FirstYearScale = 25f; public float WeightEra = 2f; public float WeightStringTags = 2f; public float WeightAction = 2f; public float WeightFiringModes = 2f; public float WeightCountry = 1f; public float WeightFeedOptions = 1f; public float WeightMounts = 1f; public float WeightCompatibility = 0f; public float WeightAttachmentMount = 0f; public float WeightSize = 0f; } internal static class ItemSimilarityPolicy { internal static readonly ItemSimilarityProfile Default = new ItemSimilarityProfile(); } internal static class ItemIndexStore { private static readonly Dictionary<string, ItemIndexRecord> _recordsById = new Dictionary<string, ItemIndexRecord>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<ObjectCategory, HashSet<string>> _byCategory = new Dictionary<ObjectCategory, HashSet<string>>(); private static readonly Dictionary<string, HashSet<string>> _byFamily = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<int, HashSet<string>> _byTagToken = new Dictionary<int, HashSet<string>>(); internal static void Clear() { _recordsById.Clear(); _byCategory.Clear(); _byFamily.Clear(); _byTagToken.Clear(); } internal static bool Contains(string itemId) { return _recordsById.ContainsKey(itemId); } internal static bool TryGet(string itemId, out ItemIndexRecord rec) { return _recordsById.TryGetValue(itemId, out rec); } internal static ItemIndexRecord GetOrCreate(string itemId) { if (_recordsById.TryGetValue(itemId, out var value)) { return value; } value = new ItemIndexRecord { ItemID = itemId }; _recordsById[itemId] = value; return value; } internal static void Commit(ItemIndexRecord rec) { _recordsById[rec.ItemID] = rec; ReindexAppendOnly(rec); } internal static IEnumerable<KeyValuePair<string, ItemIndexRecord>> EnumerateAll() { return _recordsById; } internal static HashSet<string> GetAllIds() { HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, ItemIndexRecord> item in _recordsById) { hashSet.Add(item.Key); } return hashSet; } internal static HashSet<string> GetByCategory(ObjectCategory cat) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) HashSet<string> value; return _byCategory.TryGetValue(cat, out value) ? value : new HashSet<string>(StringComparer.OrdinalIgnoreCase); } internal static HashSet<string> GetByFamily(string family) { if (string.IsNullOrEmpty(family)) { return new HashSet<string>(StringComparer.OrdinalIgnoreCase); } HashSet<string> value; return _byFamily.TryGetValue(family, out value) ? value : new HashSet<string>(StringComparer.OrdinalIgnoreCase); } internal static HashSet<string> GetByTagToken(int tok) { HashSet<string> value; return _byTagToken.TryGetValue(tok, out value) ? value : new HashSet<string>(StringComparer.OrdinalIgnoreCase); } internal static HashSet<string> IntersectCandidates(HashSet<string> current, HashSet<string> next) { if (current == null) { HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (string item in next) { hashSet.Add(item); } return hashSet; } if (current.Count == 0) { return current; } List<string> list = new List<string>(); foreach (string item2 in current) { if (!next.Contains(item2)) { list.Add(item2); } } for (int i = 0; i < list.Count; i++) { current.Remove(list[i]); } return current; } private static void ReindexAppendOnly(ItemIndexRecord rec) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) if (rec == null) { return; } ItemExtractedFeatures features = rec.Features; string itemID = rec.ItemID; if (string.IsNullOrEmpty(itemID)) { return; } if (features != null) { ObjectCategory category = features.Category; if (!_byCategory.TryGetValue(category, out var value)) { value = new HashSet<string>(StringComparer.OrdinalIgnoreCase); _byCategory[category] = value; } value.Add(itemID); } string text = features?.Family; if (!string.IsNullOrEmpty(text)) { if (!_byFamily.TryGetValue(text, out var value2)) { value2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase); _byFamily[text] = value2; } value2.Add(itemID); } HashSet<int> tagTokenSet = rec.TagTokenSet; if (tagTokenSet == null) { return; } foreach (int item in tagTokenSet) { if (!_byTagToken.TryGetValue(item, out var value3)) { value3 = new HashSet<string>(StringComparer.OrdinalIgnoreCase); _byTagToken[item] = value3; } value3.Add(itemID); } } } internal static class ItemToken { internal static int StableToken(string s) { if (s == null) { return 0; } int num = 23; for (int i = 0; i < s.Length; i++) { char c = char.ToLowerInvariant(s[i]); num = num * 31 + c; } return num; } } public static class CsvItemPriceLoader { public const string itemPriceFilePrefix = "shItmPr_"; public static Dictionary<string, float> LoadAllItemPrices() { Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); List<string> list = ShFileIoHandler.LoadAllModCsvPaths("shItmPr_"); if (list == null || list.Count == 0) { return dictionary; } list.Sort(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < list.Count; i++) { TryMergeFromFile(dictionary, list[i]); } return dictionary; } private static void TryMergeFromFile(Dictionary<string, float> prices, string fullPath) { if (string.IsNullOrEmpty(fullPath) || !File.Exists(fullPath)) { return; } int num = 0; string fileName = Path.GetFileName(fullPath); try { string[] array = File.ReadAllLines(fullPath); string[] array2 = array; foreach (string text in array2) { num++; string text2 = text?.Trim() ?? string.Empty; if (string.IsNullOrEmpty(text2) || text2.StartsWith("#") || text2.StartsWith("//")) { continue; } string[] array3 = text2.Split(new char[1] { ',' }); if (array3.Length < 2) { continue; } string text3 = array3[0].Trim(); if (!string.IsNullOrEmpty(text3)) { if (!float.TryParse(array3[1].Trim(), NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) { Debug.LogError((object)$"[{fileName}] Invalid price on line {num}: '{array3[1]}'"); } else { prices[text3] = result; } } } } catch (Exception ex) { Debug.LogError((object)$"[{fileName}] CSV load failure on line {num}: {ex.Message}"); } } public static List<BaseItemData> LoadBaseItems(string relativePath) { List<BaseItemData> list = new List<BaseItemData>(); string path = Path.Combine(ShFileIoHandler.GetShAssetsModFolder(), relativePath); if (!File.Exists(path)) { return list; } int num = 0; try { string[] array = File.ReadAllLines(path); string[] array2 = array; foreach (string text in array2) { num++; string text2 = text.Trim(); if (string.IsNullOrEmpty(text2)) { continue; } string[] array3 = text2.Split(new char[1] { ',' }); if (array3.Length >= 2) { string text3 = array3[0].Trim(); if (!string.IsNullOrEmpty(text3) && float.TryParse(array3[1].Trim(), NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) { list.Add(new BaseItemData(text3, result)); } } } } catch { list.Clear(); } return list; } } public static class ItemPricesManager { private static readonly Dictionary<string, float> _priceCache = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); public static float CHEAPEND_SELL_PRICE = 0.1f; public static float SELL_DISCOUNT = 0.95f; public static List<ObjectCategory> sellCheapenedCategories = new List<ObjectCategory> { (ObjectCategory)2, (ObjectCategory)4, (ObjectCategory)3, (ObjectCategory)8, (ObjectCategory)64 }; public static void LoadAndApplyCsvPrices() { _priceCache.Clear(); Dictionary<string, float> dictionary = CsvItemPriceLoader.LoadAllItemPrices(); if (dictionary == null || dictionary.Count == 0) { return; } foreach (KeyValuePair<string, float> item in dictionary) { string key = item.Key; float value = item.Value; if (!string.IsNullOrEmpty(key)) { ItemIndex.EnsureIndexed(key); ItemIndex.OverrideIndexedPrice(key, value); _priceCache[key] = value; } } } public static float GetPrice(string objectId, int k = 3) { if (string.IsNullOrEmpty(objectId)) { Debug.LogError((object)"GetPrice called with null or empty objectId."); return 0f; } if (_priceCache.TryGetValue(objectId, out var value)) { return value; } if (ItemIndex.TryGetIndexedPrice(objectId, out var price) && price > 0f) { _priceCache[objectId] = price; return price; } float num = ItemIndex.PredictPrice(objectId, Mathf.Max(1, k)); _priceCache[objectId] = num; return num; } public static float GetSellPrice(string objectId) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(objectId)) { Debug.LogError((object)"GetSellPrice called with null or empty objectId."); return 0f; } ItemExtractedFeatures itemFeatures = ItemIndex.GetItemFeatures(objectId); if (itemFeatures != null && sellCheapenedCategories.Contains(itemFeatures.Category)) { return CHEAPEND_SELL_PRICE; } float price = GetPrice(objectId); return price * SELL_DISCOUNT; } public static void ClearCache() { _priceCache.Clear(); } } public sealed class LoadoutsService : ShService { private ShLoadoutRack CurrentRack => (BoundProfile != null) ? BoundProfile.loadoutRack : null; public override void Initialize(ShMetaState meta, ShSessionManager session) { base.Initialize(meta, session); } public override void BindToProfile(ShProfileState profile) { base.BindToProfile(profile); } public bool IsAnyProfileActive() { return BoundProfile != null; } private void EnsureRack() { if (BoundProfile.loadoutRack == null) { BoundProfile.loadoutRack = new ShLoadoutRack(); } BoundProfile.loadoutRack.Init(); } public ShLoadout CreateEmptyLoadout() { if (!IsAnyProfileActive()) { return null; } EnsureRack(); return ShLoadoutRackRules.CreateEmptyLoadout(CurrentRack); } public ShLoadout EnsureExtractionLoadout() { if (!IsAnyProfileActive()) { return null; } EnsureRack(); return ShLoadoutRackRules.EnsureExtractionLoadout(CurrentRack); } public List<ShLoadout> listLoadysSorted() { if (!IsAnyProfileActive()) { return new List<ShLoadout>(); } EnsureRack(); return new List<ShLoadout>(ShLoadoutRackRules.ListLoadoutsSortedByUpdateTime(CurrentRack)); } public List<ShLoadout> listLoadys() { if (!IsAnyProfileActive()) { return new List<ShLoadout>(); } EnsureRack(); return new List<ShLoadout>(ShLoadoutRackRules.ListLoadoutsUnsorted(CurrentRack)); } public void DeleteLoadout(string uid) { if (IsAnyProfileActive()) { EnsureRack(); ShLoadout loadout = CurrentRack.GetLoadout(uid); if (loadout != null) { ShLoadoutsPersistence.TryDelete(BoundProfile, loadout, uid, out var _); CurrentRack.ConsumeLoadout(uid); } } } public bool EquipLoadout(string loadyUid) { string text; if (!IsAnyProfileActive()) { text = "No profile selected."; return false; } EnsureRack(); ShLoadout loadout = CurrentRack.GetLoadout(loadyUid); bool flag = ShLoadoutsPersistence.TryLoad(BoundProfile, loadout, loadyUid, out text); if (flag && SafehouseProgressionMPatchy.ConsumeLoadoutsOnSpawn.Value) { DeleteLoadout(loadyUid); } return flag; } public bool SaveWornToLoadout(string loadyUid) { if (!IsAnyProfileActive()) { return false; } EnsureRack(); if (!ShLoadoutsPersistence.TrySaveCustom(BoundProfile, loadyUid, out var _, out var error)) { Debug.LogError((object)("SaveWornToLoadout: Error while trying to save custom loady: " + error)); return false; } CurrentRack.GetLoadout(loadyUid)?.UpdateLastSavedTime(); GM.CurrentPlayerBody.WipeQuickbeltContents(); return true; } public bool SaveWornToExtractionLoady() { string text; if (!IsAnyProfileActive()) { text = "No profile is active, can't save!"; return false; } if (!ShLoadoutsPersistence.TrySaveExtraction(BoundProfile, out text)) { Debug.LogError((object)("SaveWornToExtractionLoady: Error while trying to save custom loady: " + text)); return false; } GM.CurrentPlayerBody.WipeQuickbeltContents(); return true; } public bool RenameLoadout(string loadyUid, string newName) { if (!IsAnyProfileActive()) { return false; } EnsureRack(); ShLoadout loadout = CurrentRack.GetLoadout(loadyUid); if (loadout == null) { Debug.LogError((object)("RenameLoadout: ShLoadout for uid " + loadyUid + " is null!")); return false; } loadout.ChangeDisplayName(newName); return true; } public ShLoadout GetLoadoutForSelection(string loadyUid) { if (!IsAnyProfileActive()) { return null; } EnsureRack(); ShLoadout loadout = CurrentRack.GetLoadout(loadyUid); if (loadout == null) { Debug.LogError((object)("SelectLoadout: ShLoadout for uid " + loadyUid + " is null!")); return null; } return loadout; } public ShLoadout GetExtractionLoadoutForSelection() { if (!IsAnyProfileActive()) { return null; } EnsureRack(); ShLoadout shLoadout = EnsureExtractionLoadout(); if (shLoadout == null) { Debug.LogError((object)"SelectLoadout: ShLoadout for uid EXTRACTIOn is null!"); return null; } return shLoadout; } public VaultFile GetLoadoutVaultFile(string loadyUid) { if (!IsAnyProfileActive()) { return null; } EnsureRack(); ShLoadout loadout = CurrentRack.GetLoadout(loadyUid); if (loadout == null) { Debug.LogError((object)("GetLoadoutVaultFile: Bad loady UID not found: " + loadyUid)); return null; } string error; VaultFile val = ShLoadoutsPersistence.TryGetVaultFile(BoundProfile, loadout, loadyUid, out error); if (val == null && !loadout.IsOfficialExtract) { Debug.LogError((object)("GetLoadoutVaultFile: Could not get VaultFile for given loadyUid: " + loadyUid)); } return val; } public VaultFile GetExtractionLoadoutVaultFile() { if (!IsAnyProfileActive()) { return null; } string error; VaultFile val = ShLoadoutsPersistence.TryGetExtractionVaultFile(BoundProfile, out error); if (val == null) { return null; } return val; } } [Serializable] public class ShLoadout { public string displayName; public string uid; public string fullFileName; public string lastUsedorSaved; public bool IsOfficialExtract; private ShLoadout(string displayName, bool isOfficialExtract) { uid = Guid.NewGuid().ToString(); this.displayName = displayName; fullFileName = ""; lastUsedorSaved = ShTimeCalculator.dateTimeToString(DateTime.UtcNow); IsOfficialExtract = isOfficialExtract; } internal static ShLoadout Create(string displayName, bool isOfficialExtract) { return new ShLoadout(displayName, isOfficialExtract); } public void UpdateLastSavedTime() { lastUsedorSaved = ShTimeCalculator.dateTimeToString(DateTime.UtcNow); } public void ChangeDisplayName(string newname) { displayName = newname; } } public static class ShLoadoutFactory { private const string DEFAULT_NAME = "Unnamed Loadout"; private const string DEFAULT_EXTRACT_NAME = "Extracted Loadout"; public static ShLoadout CreateDefault() { return ShLoadout.Create("Unnamed Loadout", isOfficialExtract: false); } public static ShLoadout CreateNamed(string name) { if (string.IsNullOrEmpty(name)) { name = "Unnamed Loadout"; } return ShLoadout.Create(name, isOfficialExtract: false); } public static ShLoadout CreateOfficialExtract() { return ShLoadout.Create("Extracted Loadout", isOfficialExtract: true); } } [Serializable] public class ShLoadoutRack { public List<ShLoadout> loadouts; public ShLoadoutRack() { Init(); } public void Init() { if (loadouts == null) { loadouts = new List<ShLoadout>(); } } public ShLoadout GetLoadout(string uid) { if (loadouts == null) { return null; } foreach (ShLoadout loadout in loadouts) { if (loadout.uid == uid) { return loadout; } } return null; } public void ConsumeLoadout(string uid) { if (loadouts == null) { return; } for (int num = loadouts.Count - 1; num >= 0; num--) { ShLoadout shLoadout = loadouts[num]; if (shLoadout != null && shLoadout.uid == uid) { loadouts.RemoveAt(num); break; } } } } public static class ShLoadoutsPersistence { public static bool TryDelete(ShProfileState profile, ShLoadout loadout, string loadyUid, out string error) { if (loadout != null && loadout.IsOfficialExtract) { return ShLoadoutsVault.TryDeleteExtractionLoady(profile.uid, out error); } return ShLoadoutsVault.TryDeleteCustomLoady(profile.uid, loadyUid, out error); } public static bool TryLoad(ShProfileState profile, ShLoadout loadout, string loadyUid, out string error) { if (loadout != null && loadout.IsOfficialExtract) { return ShLoadoutsVault.TryLoadExtractionLoady(profile.uid, out error); } return ShLoadoutsVault.TryLoadCustomLoady(profile.uid, loadyUid, out error); } public static bool TrySaveCustom(ShProfileState profile, string loadyUid, out string fullFileName, out string error) { return ShLoadoutsVault.TrySaveCustomLoady(profile.uid, loadyUid, out fullFileName, out error); } public static bool TrySaveExtraction(ShProfileState profile, out string error) { return ShLoadoutsVault.TrySaveExtractionLoady(profile.uid, out error); } public static VaultFile TryGetVaultFile(ShProfileState profile, ShLoadout loadout, string loadyUid, out string error) { if (loadout != null && loadout.IsOfficialExtract) { return ShLoadoutsVault.TryGetExtractionLoadyVault(profile.uid, out error); } return ShLoadoutsVault.TryGetCustomLoadyFile(profile.uid, loadyUid, out error); } public static VaultFile TryGetExtractionVaultFile(ShProfileState profile, out string error) { return ShLoadoutsVault.TryGetExtractionLoadyVault(profile.uid, out error); } } public static class ShLoadoutRackRules { public static ShLoadout CreateEmptyLoadout(ShLoadoutRack rack) { if (rack.loadouts == null) { rack.loadouts = new List<ShLoadout>(); } int num = 0; foreach (ShLoadout loadout in rack.loadouts) { if (loadout != null && !loadout.IsOfficialExtract) { num++; } } int num2 = num + 1; string name = $"Loadout {num2}"; ShLoadout shLoadout = ShLoadoutFactory.CreateNamed(name); rack.loadouts.Add(shLoadout); return shLoadout; } public static ShLoadout EnsureExtractionLoadout(ShLoadoutRack rack) { if (rack.loadouts == null) { rack.loadouts = new List<ShLoadout>(); } List<ShLoadout> list = new List<ShLoadout>(); foreach (ShLoadout loadout in rack.loadouts) { if (loadout != null && loadout.IsOfficialExtract) { list.Add(loadout); } } if (list.Count == 0) { ShLoadout shLoadout = ShLoadoutFactory.CreateOfficialExtract(); rack.loadouts.Add(shLoadout); return shLoadout; } if (list.Count == 1) { return list[0]; } ShLoadout shLoadout2 = list[0]; DateTime dateTime = ShTimeCalculator.stringToDateTime(shLoadout2.lastUsedorSaved); for (int i = 1; i < list.Count; i++) { DateTime dateTime2 = ShTimeCalculator.stringToDateTime(list[i].lastUsedorSaved); if (dateTime2 > dateTime) { shLoadout2 = list[i]; dateTime = dateTime2; } } for (int num = rack.loadouts.Count - 1; num >= 0; num--) { ShLoadout shLoadout3 = rack.loadouts[num]; if (shLoadout3 != null && shLoadout3.IsOfficialExtract && shLoadout3 != shLoadout2) { rack.loadouts.RemoveAt(num); } } return shLoadout2; } public static List<ShLoadout> ListLoadoutsSortedByUpdateTime(ShLoadoutRack rack) { ShLoadout item = EnsureExtractionLoadout(rack); rack.loadouts.Sort(delegate(ShLoadout a, ShLoadout b) { if (a == null && b == null) { return 0; } if (a == null) { return 1; } if (b == null) { return -1; } DateTime value = ShTimeCalculator.stringToDateTime(a.lastUsedorSaved); return ShTimeCalculator.stringToDateTime(b.lastUsedorSaved).CompareTo(value); }); for (int num = rack.loadouts.Count - 1; num >= 0; num--) { ShLoadout shLoadout = rack.loadouts[num]; if (shLoadout != null && shLoadout.IsOfficialExtract) { rack.loadouts.RemoveAt(num); } } rack.loadouts.Insert(0, item); return rack.loadouts; } public static List<ShLoadout> ListLoadoutsUnsorted(ShLoadoutRack rack) { ShLoadout item = EnsureExtractionLoadout(rack); for (int num = rack.loadouts.Count - 1; num >= 0; num--) { ShLoadout shLoadout = rack.loadouts[num]; if (shLoadout != null && shLoadout.IsOfficialExtract) { rack.loadouts.RemoveAt(num); } } rack.loadouts.Insert(0, item); return rack.loadouts; } } public static class ShLoadoutsVault { private const string LOADOUTS_FOLDER = "Loadouts"; private const string EXTR_LOADY_FILE = "ExtractionLoady.json"; private const string LOADY_EXT = "_.json"; public static string GetVaultProfileRoot(string profileUid) { return Path.Combine(Path.Combine(ShFileIoHandler.GetH3SaveFolder(), ShFileIoHandler.rootSaveFolder), profileUid); } public static string GetLoadoutsFolder(string profileUid) { return Path.Combine(GetVaultProfileRoot(profileUid), "Loadouts"); } public static string GetExtractionLoadyFile(string profileUid) { return Path.Combine(GetLoadoutsFolder(profileUid), "ExtractionLoady.json"); } public static string GetLoadyFileFromUid(string profileUid, string loadyUid) { return Path.Combine(GetLoadoutsFolder(profileUid), loadyUid + "_.json"); } public static bool TrySaveExtractionLoady(string profileUid, out string error) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown error = null; VaultFile vf = new VaultFile(); if (!TryScanCurrentLoadout(out vf, out error)) { error = "[TrySaveExtractionLoady] Scanning quickbelt failed."; return false; } string extractionLoadyFile = GetExtractionLoadyFile(profileUid); ShFileIoHandler.EnsureFile(extractionLoadyFile); if (!ShFileIoHandler.TrySaveJson(vf, extractionLoadyFile, out error)) { error = "[TrySaveExtractionLoady] Failed to save w error: " + error; return false; } return true; } public static VaultFile TryGetExtractionLoadyVault(string profileUid, out string error) { string extractionLoadyFile = GetExtractionLoadyFile(profileUid); ShFileIoHandler.EnsureFile(extractionLoadyFile); if (!ShFileIoHandler.TryLoadJson<VaultFile>(extractionLoadyFile, out VaultFile obj, out error)) { if (error == "File was empty") { return null; } error = "[TryGetExtractionLoady]: Failed to parse json w: " + error + " on file " + extractionLoadyFile; return null; } return obj; } public static bool TryLoadExtractionLoady(string profileUid, out string error) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) string extractionLoadyFile = GetExtractionLoadyFile(profileUid); ShFileIoHandler.EnsureDirectory(Path.GetDirectoryName(extractionLoadyFile)); if (!ShFileIoHandler.TryLoadJson<VaultFile>(extractionLoadyFile, out VaultFile obj, out error)) { error = "[TryLoadExtractionLoady]: Failed to parse json w: " + error + " on file " + extractionLoadyFile; return false; } Transform transform = ((Component)GM.CurrentPlayerBody).transform; return VaultSystem.SpawnObjects((VaultFileType)2, obj, ref error, transform, Vector3.zero); } public static bool TryClearExtractionLoadyFile(string profileUid, out string error) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown VaultFile obj = new VaultFile(); string extractionLoadyFile = GetExtractionLoadyFile(profileUid); ShFileIoHandler.EnsureDirectory(Path.GetDirectoryName(extractionLoadyFile)); if (!ShFileIoHandler.TrySaveJson(obj, extractionLoadyFile, out error)) { Debug.LogError((object)("[TrySaveExtractionLoady] Failed to save w error: " + error)); return false; } return true; } public static bool TrySaveCustomLoady(string profileUid, string loadyUid, out string fullFileName, out string error) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown error = null; fullFileName = null; VaultFile vf = new VaultFile(); if (!TryScanCurrentLoadout(out vf, out error)) { Debug.LogError((object)"[TrySaveCustomLoady] Scanning quickbelt failed."); return false; } string loadyFileFromUid = GetLoadyFileFromUid(profileUid, loadyUid); ShFileIoHandler.EnsureDirectory(Path.GetDirectoryName(loadyFileFromUid)); if (!ShFileIoHandler.TrySaveJson(vf, loadyFileFromUid, out error)) { Debug.LogError((object)("[TrySaveCustomLoady] Failed to save w error: " + error)); return false; } fullFileName = loadyFileFromUid; return true; } public static VaultFile TryGetCustomLoadyFile(string profileUid, string loadyUid, out string error) { string loadyFileFromUid = GetLoadyFileFromUid(profileUid, loadyUid); ShFileIoHandler.EnsureDirectory(Path.GetDirectoryName(loadyFileFromUid)); if (!ShFileIoHandler.TryLoadJson<VaultFile>(loadyFileFromUid, out VaultFile obj, out error)) { error = "[TryGetCustomLoadyFile]: Failed to parse json w: " + error + " on file " + loadyFileFromUid; return null; } return obj; } public static bool TryLoadCustomLoady(string profileUid, string loadyUid, out string error) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) string loadyFileFromUid = GetLoadyFileFromUid(profileUid, loadyUid); ShFileIoHandler.EnsureDirectory(Path.GetDirectoryName(loadyFileFromUid)); if (!ShFileIoHandler.TryLoadJson<VaultFile>(loadyFileFromUid, out VaultFile obj, out error)) { error = "[TryLoadCustomLoady]: Failed to parse json w: " + error + " on file " + loadyFileFromUid; return false; } Transform transform = ((Component)GM.CurrentPlayerBody).transform; return VaultSystem.SpawnObjects((VaultFileType)2, obj, ref error, transform, Vector3.zero); } public static bool TryDeleteCustomLoady(string profileUid, string loadyUid, out string error) { error = null; string loadyFileFromUid = GetLoadyFileFromUid(profileUid, loadyUid); ShFileIoHandler.EnsureDirectory(Path.GetDirectoryName(loadyFileFromUid)); try { if (File.Exists(loadyFileFromUid)) { File.Delete(loadyFileFromUid); } return true; } catch (Exception ex) { error = ex.Message; Debug.LogError((object)("[TryDeleteCustomLoady]: Failed to delete w: " + error + " on file " + loadyFileFromUid)); return false; } } public static bool TryDeleteExtractionLoady(string profileUid, out string error) { error = null; string extractionLoadyFile = GetExtractionLoadyFile(profileUid); try { if (File.Exists(extractionLoadyFile)) { File.Delete(extractionLoadyFile); } return true; } catch (Exception ex) { error = ex.Message; Debug.LogError((object)("[TryDeleteExtractionLoady]: Failed to delete w: " + error + " on file " + extractionLoadyFile)); return false; } } private static bool TryScanCurrentLoadout(out VaultFile vf, out string error) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown vf = new VaultFile(); error = null; if (!VaultSystem.FindAndScanObjectsInQuickbelt(vf)) { error = "Loady scan failed."; return false; } return true; } } public class LoadoutsViewModel : BaseViewModel { public ShLoadout uiSelectedLoadout; private LoadoutsService _service; public static LoadoutsViewModel Instance => ViewModelRegistry.Get<LoadoutsViewModel>(); public override void Initialize() { _service = SHGM.Instance?.sessionManager?.GetService<LoadoutsService>(); if (_service != null) { _service.Changed += HandleServiceChanged; } NotifyListeners(); } public override void Dispose() { if (_service != null) { _service.Changed -= HandleServiceChanged; } _service = null; } private void HandleServiceChanged() { uiSelectedLoadout = null; NotifyListeners(); } public bool IsAnyProfileActive() { return _service != null && _service.IsAnyProfileActive(); } public string GetCurrentLoadyUid() { if (!IsAnyProfileActive()) { return ""; } if (uiSelectedLoadout == null) { return ""; } return uiSelectedLoadout.uid; } public List<ShLoadout> listLoadysSorted() { if (!IsAnyProfileActive()) { return new List<ShLoadout>(); } return _service.listLoadysSorted(); } public List<ShLoadout> listLoadys() { if (!IsAnyProfileActive()) { return new List<ShLoadout>(); } return _service.listLoadys(); } public void DeleteLoadout(string uid) { if (IsAnyProfileActive()) { _service.DeleteLoadout(uid); if (uiSelectedLoadout != null && uiSelectedLoadout.uid == uid) { uiSelectedLoadout = null; } NotifyListeners(); } } public void AddNewCustomLoadout() { if (IsAnyProfileActive()) { ShLoadout shLoadout = _service.CreateEmptyLoadout(); uiSelectedLoadout = shLoadout; NotifyListeners(); } } public bool EquipLoadout(string loadyUid) { if (!IsAnyProfileActive()) { return false; } bool flag = _service.EquipLoadout(loadyUid); if (flag) { if (uiSelectedLoadout != null && uiSelectedLoadout.uid == loadyUid) { uiSelectedLoadout = null; } NotifyListeners(); } return flag; } public bool SaveWornToLoadout(string loadyUid) { if (!IsAnyProfileActive()) { return false; } bool flag = _service.SaveWornToLoadout(loadyUid); if (flag) { NotifyListeners(); } return flag; } public bool SaveWornToExtractionLoady() { if (!IsAnyProfileActive()) { return false; } bool flag = _service.SaveWornToExtractionLoady(); if (flag) { NotifyListeners(); } return flag; } public bool RenameLoadout(string loadyUid, string newName) { if (!IsAnyProfileActive()) { return false; } bool flag = _service.RenameLoadout(loadyUid, newName); if (flag) { NotifyListeners(); } return flag; } public bool SelectLoadout(string loadyUid) { if (!IsAnyProfileActive()) { return false; } ShLoadout loadoutForSelection = _service.GetLoadoutForSelection(loadyUid); if (loadoutForSelection == null) { return false; } uiSelectedLoadout = loadoutForSelection; NotifyListeners(); return true; } public bool SelectExtractionLoadout() { if (!IsAnyProfileActive()) { return false; } ShLoadout extractionLoadoutForSelection = _service.GetExtractionLoadoutForSelection(); if (extractionLoadoutForSelection == null) { return false; } uiSelectedLoadout = extractionLoadoutForSelection; NotifyListeners(); return true; } public VaultFile GetLoadoutVaultFile(string loadyUid) { if (!IsAnyProfileActive()) { return null; } return _service.GetLoadoutVaultFile(loadyUid); } public VaultFile GetExtractionLoadoutVaultFile() { if (!IsAnyProfileActive()) { return null; } return _service.GetExtractionLoadoutVaultFile(); } } public class LootCalculator { public static List<string> GetOrderedItemsIDs(List<ObjectCategory> orderedCategories) { int count = orderedCategories?.Count ?? 0; List<string> result = NewBlankResultList(count); Dictionary<int, string> firearmAtIndex = new Dictionary<int, string>(); string lastFirearmId = string.Empty; PlaceFirearms(orderedCategories, result, firearmAtIndex, ref lastFirearmId); FillNonFirearms(orderedCategories, result, firearmAtIndex, ref lastFirearmId); return result; } private static List<string> NewBlankResultList(int count) { List<string> list = new List<string>(count); for (int i = 0; i < count; i++) { list.Add(string.Empty); } return list; } private static void PlaceFirearms(List<ObjectCategory> cats, List<string> result, Dictionary<int, string> firearmAtIndex, ref string lastFirearmId) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 for (int i = 0; i < cats.Count; i++) { ObjectCategory val = cats[i]; if (!IsAnyCategory(val) && (int)val == 1) { FVRObject obj = PickRandomFromCategory((ObjectCategory)1); string text = (result[i] = ToItemId(obj)); if (!string.IsNullOrEmpty(text)) { firearmAtIndex[i] = text; lastFirearmId = text; } } } } private static void FillNonFirearms(List<ObjectCategory> cats, List<string> result, Dictionary<int, string> firearmAtIndex, ref string lastFirearmId) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 //IL_009c: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < cats.Count; i++) { if (!string.IsNullOrEmpty(result[i])) { continue; } ObjectCategory cat = cats[i]; cat = ResolveAnyCategory(cat); if (IsAmmoCategory(cat)) { result[i] = GetAmmoItemId(i, cats.Count, firearmAtIndex, ref lastFirearmId); } else if ((int)cat == 1) { FVRObject obj = PickRandomFromCategory((ObjectCategory)1); result[i] = ToItemId(obj); if (!string.IsNullOrEmpty(result[i])) { lastFirearmId = result[i]; } } else { result[i] = ToItemId(PickRandomFromCategory(cat)); } } } private static bool IsAnyCategory(ObjectCategory cat) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 return (int)cat == 0; } private static ObjectCategory ResolveAnyCategory(ObjectCategory cat) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //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) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (!IsAnyCategory(cat)) { return cat; } Dictionary<ObjectCategory, List<FVRObject>> odicTagCategory = ManagerSingleton<IM>.Instance.odicTagCategory; if (odicTagCategory == null || odicTagCategory.Count == 0) { return (ObjectCategory)0; } List<ObjectCategory> list = new List<ObjectCategory>(odicTagCategory.Keys); list.Remove((ObjectCategory)0); return list[Random.Range(0, list.Count)]; } private static bool IsAmmoCategory(ObjectCategory cat) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 return (int)cat == 2 || (int)cat == 6 || (int)cat == 3 || (int)cat == 4; } private static string GetAmmoItemId(int index, int totalCount, Dictionary<int, string> firearmAtIndex, ref string lastFirearmId) { string text = ((!string.IsNullOrEmpty(lastFirearmId)) ? lastFirearmId : FindWeaponAhead(index, totalCount, firearmAtIndex)); if (!string.IsNullOrEmpty(text)) { FVRObject val = IM.OD[text]; FVRObject randomAmmoObject = FVRObject.GetRandomAmmoObject(val, (List<OTagEra>)null, -1, -1, (List<OTagSet>)null, (List<FireArmRoundClass>)null); string text2 = ToItemId(randomAmmoObject); if (!string.IsNullOrEmpty(text2)) { return text2; } } return ToItemId(PickRandomFromCategory((ObjectCategory)2)); } private static string FindWeaponAhead(int idx, int totalCount, Dictionary<int, string> firearmAtIndex) { for (int i = idx + 1; i < totalCount; i++) { if (firearmAtIndex.TryGetValue(i, out var value)) { return value; } } return string.Empty; } private unsafe static FVRObject PickRandomFromCategory(ObjectCategory cat) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) Dictionary<ObjectCategory, List<FVRObject>> odicTagCategory = ManagerSingleton<IM>.Instance.odicTagCategory; if (odicTagCategory == null) { Debug.LogError((object)"PickRandomFromCategory: failed get odicTagCategory at all"); return null; } if (!odicTagCategory.TryGetValue(cat, out var value) || value == null || value.Count == 0) { Debug.LogError((object)("PickRandomFromCategory: failed get category " + ((object)(*(ObjectCategory*)(&cat))/*cast due to .constrained prefix*/).ToString())); return null; } return value[Random.Range(0, value.Count)]; } private static string ToItemId(FVRObject obj) { if ((Object)(object)obj == (Object)null) { Debug.LogError((object)"ToItemId: obj in put is null"); return string.Empty; } try { if (!string.IsNullOrEmpty(obj.ItemID)) { return obj.ItemID; } } catch { Debug.LogError((object)"ToItemId: ItemID is not present in the object"); return string.Empty; } return string.Empty; } } public class ShowLootUiOnInteract : MonoBehaviour { private bool ready = false; private SosigLink slink; private SLinkLootController controller; public void Init(SosigLink Slink, SLinkLootController Controller) { ready = true; slink = Slink; controller = Controller; } private void Update() { if (ready) { controlle