Decompiled source of UniversalSurvivorUnlocks v0.3.2

BepInEx/plugins/UniversalSurvivorUnlocks/UniversalSurvivorUnlocks.dll

Decompiled a day ago
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.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using EntityStates;
using EntityStates.Bandit2.Weapon;
using EntityStates.Railgunner.Weapon;
using HG;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using On.EntityStates.Bandit2.Weapon;
using On.EntityStates.Railgunner.Weapon;
using On.RoR2;
using On.RoR2.Projectile;
using On.RoR2.UI;
using R2API;
using R2API.ContentManagement;
using R2API.Networking;
using R2API.Networking.Interfaces;
using RoR2;
using RoR2.Achievements;
using RoR2.ContentManagement;
using RoR2.EntitlementManagement;
using RoR2.ExpansionManagement;
using RoR2.Projectile;
using RoR2.Skills;
using RoR2.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
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: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("UniversalSurvivorUnlocks")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.3.1.0")]
[assembly: AssemblyInformationalVersion("0.3.1+0e248b4f30582d47a87a5917b7c27ed092339ab4")]
[assembly: AssemblyProduct("UniversalSurvivorUnlocks")]
[assembly: AssemblyTitle("UniversalSurvivorUnlocks")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace UniversalSurvivorUnlocks
{
	[BepInPlugin("com.danileo.UniversalSurvivorUnlocks", "Universal Survivor Unlocks", "0.3.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency("com.rune580.riskofoptions", "2.8.6")]
	[BepInDependency("com.KingEnderBrine.ScrollableLobbyUI", "1.10.5")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.danileo.UniversalSurvivorUnlocks";

		public const string PluginName = "Universal Survivor Unlocks";

		public const string PluginVersion = "0.3.1";

		public static List<SurvivorInfo> Survivors { get; private set; }

		private void Awake()
		{
			UsuLog.Initialize(((BaseUnityPlugin)this).Config);
			TemporaryGeneratedTestOptions.Initialize(((BaseUnityPlugin)this).Config);
			NetworkingAPI.RegisterMessageType<HunkBanditShotResultMessage>();
			NetworkingAPI.RegisterMessageType<SessionUnlockResultMessage>();
			NetworkingAPI.RegisterMessageType<HunkRailgunnerShotResultMessage>();
			NetworkingAPI.RegisterMessageType<SessionUnlockGrantMessage>();
			NetworkingAPI.RegisterMessageType<SessionMissionSyncMessage>();
			NetworkingAPI.RegisterMessageType<SessionMissionLobbyRequestMessage>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Universal Survivor Unlocks 0.3.1 cargado.");
			SurvivorJsonManager.LoadForStartup(((BaseUnityPlugin)this).Logger);
			SurvivorUnlockManager.RegisterConfiguredUnlockables(((BaseUnityPlugin)this).Logger);
			UniversalContentPackProvider.Initialize(((BaseUnityPlugin)this).Logger);
			SurvivorLockedPortraitManager.Initialize(((BaseUnityPlugin)this).Logger);
			RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(OnGameLoaded));
		}

		private void OnGameLoaded()
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Risk of Rain 2 terminó de cargar.");
			GameplayRuntimeBootstrap.Initialize(((BaseUnityPlugin)this).Logger);
			MissionLibraryUIManager.Initialize(((BaseUnityPlugin)this).Logger);
			ModdedSurvivorRegistry.ReconcileCatalog(((BaseUnityPlugin)this).Logger);
			Survivors = SurvivorDetector.DetectSurvivors(((BaseUnityPlugin)this).Logger);
			CurrentGameCompatibilityService.Audit(Survivors, ((BaseUnityPlugin)this).Logger);
			VanillaUnlockMethodCatalogService.Audit(Survivors, ((BaseUnityPlugin)this).Logger);
			VanillaUnlockCoverageService.Audit(Survivors, ((BaseUnityPlugin)this).Logger);
			VanillaUnlockSemanticInspectionService.Audit(Survivors, ((BaseUnityPlugin)this).Logger);
			SurvivorDefinitionRegistry.Initialize(Survivors, ((BaseUnityPlugin)this).Logger);
			ModdedSurvivorLocalization.RegisterInstalledTranslations(Survivors, ((BaseUnityPlugin)this).Logger);
			ContentCatalogService.Initialize(((BaseUnityPlugin)this).Logger);
			SurvivorJsonManager.Sync(Survivors, ((BaseUnityPlugin)this).Logger);
			PersistenceMigrationAuditService.Audit(((BaseUnityPlugin)this).Logger);
			MissionAssignmentService.RefreshAssignedPresetSnapshots(((BaseUnityPlugin)this).Logger);
			SurvivorUnlockManager.ApplyConfiguredUnlockables(Survivors, ((BaseUnityPlugin)this).Logger);
			SurvivorContentProfileService.Initialize(Survivors, ((BaseUnityPlugin)this).Logger);
			MissionProfileService.Initialize(SurvivorContentProfileService.GetAll(), ((BaseUnityPlugin)this).Logger);
			LocalPowerAssessmentService.Initialize(MissionProfileService.GetAll(), ((BaseUnityPlugin)this).Logger);
			ChallengeTierService.Initialize(LocalPowerAssessmentService.GetAll(), ((BaseUnityPlugin)this).Logger);
			MissionTemplateCompatibilityService.Initialize(MissionProfileService.GetAll(), ChallengeTierService.GetAll(), ((BaseUnityPlugin)this).Logger);
			DeterministicMissionGeneratorService.Initialize(Survivors, MissionTemplateCompatibilityService.GetAll(), ((BaseUnityPlugin)this).Logger);
			GeneratedMissionRuntimeMaterializer.Initialize(Survivors, MissionProfileService.GetAll(), ((BaseUnityPlugin)this).Logger);
			GeneratedMissionRuntimeMaterializer.PersistGeneratedMissions();
			OfficialContentAuthorityInvariantService.Audit(Survivors, ((BaseUnityPlugin)this).Logger);
			UsuLog.Diagnostic(((BaseUnityPlugin)this).Logger, "[CURRENT GAME COMPAT] C2 runtime validado | Dependencias actuales + DLC4 + métodos vanilla + Mission V2 extraction + authority guard.");
			ProviderCoexistenceAuditService.Audit(((BaseUnityPlugin)this).Logger);
			UsuLog.Verbose(((BaseUnityPlugin)this).Logger, "[PROVIDER COEXISTENCE] C4 código completo | inventario de slots + restore dry-run + Generated snapshot + reroll readiness + provider/source invariant | cambios persistidos por auditoría: no.");
			SurvivorUnlockManager.InitializeEffectiveMissionPresentation(((BaseUnityPlugin)this).Logger);
			if (UsuLog.VerboseEnabled || UsuLog.ExtendedAuditEnabled)
			{
				LocalizationCoverageDiagnostics.Log(SurvivorContentProfileService.GetAll(), ((BaseUnityPlugin)this).Logger);
			}
			RoR2Application.onLoad = (Action)Delegate.Remove(RoR2Application.onLoad, new Action(OnGameLoaded));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Universal Survivor Unlocks terminó su inicialización.");
		}
	}
	public static class CurrentGameCompatibilityService
	{
		private sealed class OfficialSurvivorExpectation
		{
			public string BodyName = "";

			public string Label = "";
		}

		private static readonly OfficialSurvivorExpectation[] Dlc4Survivors = new OfficialSurvivorExpectation[2]
		{
			new OfficialSurvivorExpectation
			{
				BodyName = "Enforcer2Body",
				Label = "Enforcer"
			},
			new OfficialSurvivorExpectation
			{
				BodyName = "AcolyteBody",
				Label = "Acolyte"
			}
		};

		public static void Audit(IReadOnlyList<SurvivorInfo> survivors, ManualLogSource logger)
		{
			if (survivors == null)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			OfficialSurvivorExpectation[] dlc4Survivors = Dlc4Survivors;
			foreach (OfficialSurvivorExpectation officialSurvivorExpectation in dlc4Survivors)
			{
				SurvivorInfo survivorInfo = FindByBodyName(survivors, officialSurvivorExpectation.BodyName);
				if (survivorInfo == null)
				{
					UsuLog.Verbose(logger, "[CURRENT GAME COMPAT] DLC4 " + officialSurvivorExpectation.Label + " no detectado | Body esperado: " + officialSurvivorExpectation.BodyName);
					continue;
				}
				num++;
				if (survivorInfo.HasOriginalUnlock)
				{
					num2++;
				}
				if (survivorInfo.IsModded)
				{
					num3++;
					if (logger != null)
					{
						logger.LogWarning((object)("[CURRENT GAME COMPAT] " + officialSurvivorExpectation.BodyName + " fue clasificado como modded. USU conservará la autoridad existente y no debe generar un fallback hasta corregir la procedencia."));
					}
				}
				UsuLog.Verbose(logger, "[CURRENT GAME COMPAT] DLC4 " + officialSurvivorExpectation.Label + " | Body: " + survivorInfo.BodyName + " | Original: " + (survivorInfo.HasOriginalUnlock ? "SI" : "NO") + " | Modded: " + (survivorInfo.IsModded ? "SI" : "NO") + " | " + $"Estado: {survivorInfo.Status} | " + "Expansion: " + survivorInfo.ExpansionName);
			}
			UsuLog.Diagnostic(logger, "[CURRENT GAME COMPAT] RoR2 1.5/DLC4 auditado | " + $"Survivors oficiales esperados: {Dlc4Survivors.Length} | " + $"Detectados: {num} | Unlock Original: {num2} | " + $"Clasificación incorrecta como modded: {num3} | " + "Autoridad modificada: no");
		}

		private static SurvivorInfo FindByBodyName(IReadOnlyList<SurvivorInfo> survivors, string bodyName)
		{
			for (int i = 0; i < survivors.Count; i++)
			{
				SurvivorInfo survivorInfo = survivors[i];
				if (survivorInfo != null && string.Equals(survivorInfo.BodyName, bodyName, StringComparison.OrdinalIgnoreCase))
				{
					return survivorInfo;
				}
			}
			return null;
		}
	}
	public static class OfficialContentAuthorityInvariantService
	{
		public static void Audit(IReadOnlyList<SurvivorInfo> survivors, ManualLogSource logger)
		{
			if (survivors == null)
			{
				return;
			}
			IReadOnlyList<GeneratedMissionCandidate> all = DeterministicMissionGeneratorService.GetAll();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			for (int i = 0; i < survivors.Count; i++)
			{
				SurvivorInfo survivorInfo = survivors[i];
				if (survivorInfo == null || (Object)(object)survivorInfo.SurvivorDef == (Object)null || survivorInfo.IsModded)
				{
					continue;
				}
				num++;
				if (survivorInfo.HasOriginalUnlock)
				{
					num4++;
				}
				if (HasGeneratedCandidate(all, survivorInfo.BodyName))
				{
					num2++;
					if (logger != null)
					{
						logger.LogError((object)("[OFFICIAL AUTHORITY GUARD] VIOLATION | " + survivorInfo.BodyName + " es contenido oficial pero recibió candidato Generated."));
					}
				}
				string a = SurvivorJsonManager.GetEntryAnywhere(survivorInfo.BodyName)?.Challenge?.MissionConfig?.Source ?? "";
				if (string.Equals(a, "Generated", StringComparison.OrdinalIgnoreCase))
				{
					num3++;
					if (logger != null)
					{
						logger.LogError((object)("[OFFICIAL AUTHORITY GUARD] VIOLATION | " + survivorInfo.BodyName + " es contenido oficial pero Survivors.json conserva Source=Generated."));
					}
				}
			}
			bool flag = num2 == 0 && num3 == 0;
			UsuLog.Diagnostic(logger, "[OFFICIAL AUTHORITY GUARD] C2.6 " + (flag ? "OK" : "FAILED") + " | Oficiales: " + num + " | Con Original preservado: " + num4 + " | Generated candidates indebidos: " + num2 + " | Generated persistidos indebidos: " + num3 + " | Corrección automática: no");
		}

		private static bool HasGeneratedCandidate(IReadOnlyList<GeneratedMissionCandidate> candidates, string bodyName)
		{
			if (candidates == null || string.IsNullOrWhiteSpace(bodyName))
			{
				return false;
			}
			for (int i = 0; i < candidates.Count; i++)
			{
				GeneratedMissionCandidate generatedMissionCandidate = candidates[i];
				if (generatedMissionCandidate != null && string.Equals(generatedMissionCandidate.BodyName, bodyName, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}
	}
	public static class ProviderCoexistenceAuditService
	{
		private sealed class AuditTotals
		{
			public int Entries;

			public int WithOriginal;

			public int WithUsu;

			public int WithCustom;

			public int WithGenerated;

			public int MultiSlot;

			public int GeneratedRestorable;

			public int CustomRestorable;

			public int GeneratedSnapshotMismatch;

			public int InvalidActiveSource;

			public int InvalidProviderSource;

			public int RerollReady;

			public int RerollBlocked;
		}

		public static void Audit(ManualLogSource logger)
		{
			SurvivorJsonFile currentConfig = SurvivorJsonManager.CurrentConfig;
			if (currentConfig == null)
			{
				UsuLog.Diagnostic(logger, "[PROVIDER COEXISTENCE] C4 sin Survivors.json cargado; auditoría omitida.");
				return;
			}
			List<SurvivorJsonEntry> list = (from entry in EnumerateEntries(currentConfig)
				where entry != null
				select entry).ToList();
			AuditTotals auditTotals = new AuditTotals();
			foreach (SurvivorJsonEntry item in list)
			{
				AuditEntry(item, auditTotals, logger);
			}
			UsuLog.Diagnostic(logger, "[PROVIDER COEXISTENCE] C4 auditado | " + $"Entradas: {auditTotals.Entries} | " + $"Original: {auditTotals.WithOriginal} | " + $"USU: {auditTotals.WithUsu} | " + $"Custom: {auditTotals.WithCustom} | " + $"Generated: {auditTotals.WithGenerated} | " + $"Multi-slot: {auditTotals.MultiSlot} | " + $"Generated restaurable: {auditTotals.GeneratedRestorable} | " + $"Custom restaurable: {auditTotals.CustomRestorable} | " + $"Reroll ready/bloqueado: {auditTotals.RerollReady}/{auditTotals.RerollBlocked} | " + $"Mismatch Generated: {auditTotals.GeneratedSnapshotMismatch} | " + $"Source inválido: {auditTotals.InvalidActiveSource} | " + $"Provider/Source inválido: {auditTotals.InvalidProviderSource} | " + "Persistencia modificada: no");
		}

		private static void AuditEntry(SurvivorJsonEntry entry, AuditTotals totals, ManualLogSource logger)
		{
			totals.Entries++;
			SurvivorChallengeJson challenge = entry.Challenge;
			MissionConfiguration missionConfiguration = challenge?.MissionConfig;
			bool flag = UnlockProviderPolicy.HasStoredOriginalUnlock(entry);
			bool flag2 = MissionProviderSlotStore.HasCustomMission(challenge);
			bool flag3 = MissionProviderSlotStore.HasGeneratedMission(challenge);
			MissionProviderSelectionState missionProviderSelectionState = MissionProviderSelectionPolicy.ResolveFromStoredEntry(entry);
			bool flag4 = missionProviderSelectionState.UsuTargetAvailable || missionProviderSelectionState.DirectUsuPresetAvailable || (missionConfiguration != null && string.Equals(missionConfiguration.Source, "Preset", StringComparison.OrdinalIgnoreCase));
			if (flag)
			{
				totals.WithOriginal++;
			}
			if (flag4)
			{
				totals.WithUsu++;
			}
			if (flag2)
			{
				totals.WithCustom++;
			}
			if (flag3)
			{
				totals.WithGenerated++;
			}
			int num = (flag ? 1 : 0) + (flag4 ? 1 : 0) + (flag2 ? 1 : 0) + (flag3 ? 1 : 0);
			if (num >= 2)
			{
				totals.MultiSlot++;
			}
			bool flag5 = TestGeneratedRestore(challenge);
			bool flag6 = TestCustomRestore(challenge);
			if (flag5)
			{
				totals.GeneratedRestorable++;
			}
			if (flag6)
			{
				totals.CustomRestorable++;
			}
			if (HasGeneratedSnapshotMismatch(challenge))
			{
				totals.GeneratedSnapshotMismatch++;
			}
			string text = missionConfiguration?.Source?.Trim() ?? "";
			if (!string.IsNullOrWhiteSpace(text) && !string.Equals(text, "Preset", StringComparison.OrdinalIgnoreCase) && !string.Equals(text, "Generated", StringComparison.OrdinalIgnoreCase) && !string.Equals(text, "Custom", StringComparison.OrdinalIgnoreCase))
			{
				totals.InvalidActiveSource++;
			}
			if (!IsProviderSourceConsistent(missionConfiguration))
			{
				totals.InvalidProviderSource++;
			}
			bool flag7 = IsGeneratedRerollReady(challenge);
			if (flag3)
			{
				if (flag7)
				{
					totals.RerollReady++;
				}
				else
				{
					totals.RerollBlocked++;
				}
			}
			if (num >= 2 || flag3 || flag2 || missionProviderSelectionState.DirectUsuAlternativeDeclared)
			{
				UsuLog.Verbose(logger, "[PROVIDER COEXISTENCE] " + entry.BodyName + " | " + $"Activo: {missionProviderSelectionState.ActiveProvider}/{text} | " + "Slots O/U/C/G: " + Bool(flag) + "/" + Bool(flag4) + "/" + Bool(flag2) + "/" + Bool(flag3) + " | USU preset: " + missionProviderSelectionState.UsuTargetPresetId + " | Generated restore: " + Bool(flag5) + " | Custom restore: " + Bool(flag6) + " | Reroll: " + ((!flag3) ? "N/A" : (flag7 ? "READY" : "BLOCKED")) + " | Nonce: " + ResolveGenerationNonce(challenge));
			}
		}

		private static IEnumerable<SurvivorJsonEntry> EnumerateEntries(SurvivorJsonFile config)
		{
			if (config?.AvailableSurvivors != null)
			{
				foreach (SurvivorJsonEntry value in config.AvailableSurvivors.Values)
				{
					yield return value;
				}
			}
			if (config?.UnavailableSurvivors == null)
			{
				yield break;
			}
			foreach (SurvivorJsonEntry value2 in config.UnavailableSurvivors.Values)
			{
				yield return value2;
			}
		}

		private static bool TestGeneratedRestore(SurvivorChallengeJson challenge)
		{
			if (!MissionProviderSlotStore.HasGeneratedMission(challenge))
			{
				return false;
			}
			SurvivorChallengeJson survivorChallengeJson = CloneChallenge(challenge);
			if (survivorChallengeJson == null)
			{
				return false;
			}
			MissionProviderSlotStore.CaptureActiveSlots(survivorChallengeJson);
			return MissionProviderSlotStore.RestoreGenerated(survivorChallengeJson) && string.Equals(survivorChallengeJson.MissionConfig?.Source, "Generated", StringComparison.OrdinalIgnoreCase) && survivorChallengeJson.Mission != null;
		}

		private static bool TestCustomRestore(SurvivorChallengeJson challenge)
		{
			if (!MissionProviderSlotStore.HasCustomMission(challenge))
			{
				return false;
			}
			SurvivorChallengeJson survivorChallengeJson = CloneChallenge(challenge);
			if (survivorChallengeJson == null)
			{
				return false;
			}
			MissionProviderSlotStore.CaptureActiveSlots(survivorChallengeJson);
			return MissionProviderSlotStore.RestoreCustom(survivorChallengeJson) && string.Equals(survivorChallengeJson.MissionConfig?.Source, "Custom", StringComparison.OrdinalIgnoreCase) && survivorChallengeJson.Mission != null;
		}

		private static bool HasGeneratedSnapshotMismatch(SurvivorChallengeJson challenge)
		{
			MissionConfiguration missionConfiguration = challenge?.MissionConfig;
			if (missionConfiguration == null || !string.Equals(missionConfiguration.Source, "Generated", StringComparison.OrdinalIgnoreCase) || challenge.Mission == null || missionConfiguration.GeneratedMission?.Mission == null)
			{
				return false;
			}
			string a = JsonConvert.SerializeObject((object)challenge.Mission, (Formatting)0);
			string b = JsonConvert.SerializeObject((object)missionConfiguration.GeneratedMission.Mission, (Formatting)0);
			return !string.Equals(a, b, StringComparison.Ordinal);
		}

		private static bool IsProviderSourceConsistent(MissionConfiguration config)
		{
			if (config == null)
			{
				return true;
			}
			string a = config.Source?.Trim() ?? "";
			if (string.Equals(a, "Custom", StringComparison.OrdinalIgnoreCase))
			{
				return !config.Provider.HasValue || config.Provider.Value == UnlockProviderKind.Custom;
			}
			if (string.Equals(a, "Generated", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "Preset", StringComparison.OrdinalIgnoreCase))
			{
				return !config.Provider.HasValue || UnlockProviderPolicy.IsUsuFamily(config.Provider.Value);
			}
			return true;
		}

		private static bool IsGeneratedRerollReady(SurvivorChallengeJson challenge)
		{
			if (!MissionProviderSlotStore.HasGeneratedMission(challenge))
			{
				return false;
			}
			string value = ResolveGenerationNonce(challenge);
			return !string.IsNullOrWhiteSpace(value);
		}

		private static string ResolveGenerationNonce(SurvivorChallengeJson challenge)
		{
			object obj;
			if (challenge == null)
			{
				obj = null;
			}
			else
			{
				JObject parameters = challenge.Parameters;
				obj = ((parameters == null) ? null : ((JToken)parameters).Value<string>((object)"generationNonce")?.Trim());
			}
			if (obj == null)
			{
				obj = "";
			}
			string text = (string)obj;
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			object obj2;
			if (challenge == null)
			{
				obj2 = null;
			}
			else
			{
				MissionConfiguration missionConfig = challenge.MissionConfig;
				if (missionConfig == null)
				{
					obj2 = null;
				}
				else
				{
					GeneratedMissionSnapshot generatedMission = missionConfig.GeneratedMission;
					if (generatedMission == null)
					{
						obj2 = null;
					}
					else
					{
						JObject parameters2 = generatedMission.Parameters;
						obj2 = ((parameters2 == null) ? null : ((JToken)parameters2).Value<string>((object)"generationNonce")?.Trim());
					}
				}
			}
			if (obj2 == null)
			{
				obj2 = "";
			}
			return (string)obj2;
		}

		private static SurvivorChallengeJson CloneChallenge(SurvivorChallengeJson challenge)
		{
			if (challenge == null)
			{
				return null;
			}
			try
			{
				string text = JsonConvert.SerializeObject((object)challenge, (Formatting)0);
				return JsonConvert.DeserializeObject<SurvivorChallengeJson>(text);
			}
			catch
			{
				return null;
			}
		}

		private static string Bool(bool value)
		{
			return value ? "SI" : "NO";
		}
	}
	public static class VanillaUnlockCoverageService
	{
		private sealed class CoverageResult
		{
			public string Status = "Unmapped";

			public string Candidate = "<none>";

			public string Evidence = "none";
		}

		public static void Audit(IReadOnlyList<SurvivorInfo> survivors, ManualLogSource logger)
		{
			if (survivors == null)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			for (int i = 0; i < survivors.Count; i++)
			{
				SurvivorInfo survivorInfo = survivors[i];
				if (survivorInfo == null || (Object)(object)survivorInfo.SurvivorDef == (Object)null || survivorInfo.IsModded || (Object)(object)survivorInfo.SurvivorDef.unlockableDef == (Object)null)
				{
					continue;
				}
				AchievementDef val = FindAchievementForUnlockable(survivorInfo.SurvivorDef.unlockableDef);
				if (val != null)
				{
					num++;
					string text = TypeName(val.type);
					string text2 = TypeName(val.serverTrackerType);
					CoverageResult coverageResult = Classify(val.identifier, text, text2);
					if (coverageResult.Status == "Covered")
					{
						num2++;
					}
					else if (coverageResult.Status == "LikelyCovered")
					{
						num3++;
					}
					else
					{
						num4++;
					}
					UsuLog.Verbose(logger, "[VANILLA UNLOCK COVERAGE] " + SafeBodyName(survivorInfo) + " | Achievement: " + val.identifier + " | Coverage: " + coverageResult.Status + " | Candidate: " + coverageResult.Candidate + " | Evidence: " + coverageResult.Evidence + " | Type: " + text + " | ServerTracker: " + text2);
				}
			}
			UsuLog.Diagnostic(logger, "[VANILLA UNLOCK COVERAGE] C2.4 auditado | Achievement methods: " + num + " | Covered: " + num2 + " | LikelyCovered: " + num3 + " | Unmapped: " + num4 + " | Nuevos objetivos creados: 0 | Autoridad modificada: no");
		}

		private static CoverageResult Classify(string identifier, string achievementType, string trackerType)
		{
			string text = (identifier + " " + achievementType + " " + trackerType).ToLowerInvariant();
			if (ContainsAny(text, "teleporter"))
			{
				return Covered("base.objective.complete_teleporter", "teleporter");
			}
			if (ContainsAny(text, "heal", "healing"))
			{
				return Covered("base.objective.heal_health", "heal/healing");
			}
			if (ContainsAny(text, "level"))
			{
				return Covered("base.objective.reach_level", "level");
			}
			if (ContainsAny(text, "minion", "drone", "turret"))
			{
				return Covered("base.objective.recruit_minions", "minion/drone/turret");
			}
			if (ContainsAny(text, "umbra", "vengeance"))
			{
				return Covered("base.objective.defeat_umbra_waves", "umbra/vengeance");
			}
			if (ContainsAny(text, "obliterate", "ending"))
			{
				return Covered("base.objective.complete_ending", "obliterate/ending");
			}
			if (ContainsAny(text, "interact", "shrine", "altar"))
			{
				return Likely("base.objective.interact_with_target", "interaction keyword");
			}
			if (ContainsAny(text, "acolyte"))
			{
				return Likely("base.objective.consume_item_on_interaction", "Acolyte unlock family; exact item rule must be verified at runtime");
			}
			if (ContainsAny(text, "skill", "ability"))
			{
				return Likely("base.objective.use_skill", "skill/ability");
			}
			if (ContainsAny(text, "equipment"))
			{
				return Likely("base.objective.carry_equipment", "equipment");
			}
			if (ContainsAny(text, "item", "inventory"))
			{
				return Likely("base.objective.hold_item_stack", "item/inventory");
			}
			if (ContainsAny(text, "boss"))
			{
				return Likely("base.objective.kill_boss", "boss");
			}
			if (ContainsAny(text, "kill", "death", "defeat"))
			{
				return Likely("base.objective.kill_enemies", "kill/death/defeat");
			}
			if (ContainsAny(text, "stage", "scene"))
			{
				return Likely("base.objective.complete_stage", "stage/scene");
			}
			if (ContainsAny(text, "run", "game"))
			{
				return Likely("base.objective.complete_run", "run/game");
			}
			return new CoverageResult
			{
				Status = "Unmapped",
				Candidate = "<none>",
				Evidence = "no safe keyword mapping"
			};
		}

		private static CoverageResult Covered(string candidate, string evidence)
		{
			return new CoverageResult
			{
				Status = "Covered",
				Candidate = (candidate ?? "<none>"),
				Evidence = (evidence ?? "")
			};
		}

		private static CoverageResult Likely(string candidate, string evidence)
		{
			return new CoverageResult
			{
				Status = "LikelyCovered",
				Candidate = (candidate ?? "<none>"),
				Evidence = (evidence ?? "")
			};
		}

		private static bool ContainsAny(string text, params string[] terms)
		{
			if (string.IsNullOrEmpty(text) || terms == null)
			{
				return false;
			}
			foreach (string value in terms)
			{
				if (!string.IsNullOrEmpty(value) && text.IndexOf(value, StringComparison.Ordinal) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static AchievementDef FindAchievementForUnlockable(UnlockableDef unlockable)
		{
			if ((Object)(object)unlockable == (Object)null || string.IsNullOrWhiteSpace(unlockable.cachedName))
			{
				return null;
			}
			try
			{
				IEnumerable achievementDefs = AchievementManager.achievementDefs;
				if (achievementDefs == null)
				{
					return null;
				}
				foreach (object item in achievementDefs)
				{
					AchievementDef val = (AchievementDef)((item is AchievementDef) ? item : null);
					if (val == null || !string.Equals(val.unlockableRewardIdentifier, unlockable.cachedName, StringComparison.Ordinal))
					{
						continue;
					}
					return val;
				}
			}
			catch
			{
			}
			return null;
		}

		private static string SafeBodyName(SurvivorInfo survivor)
		{
			if (survivor != null && !string.IsNullOrWhiteSpace(survivor.BodyName))
			{
				return survivor.BodyName;
			}
			return "<unknown>";
		}

		private static string TypeName(Type type)
		{
			return (type != null) ? (type.FullName ?? type.Name ?? "") : "<none>";
		}
	}
	public static class VanillaUnlockMethodCatalogService
	{
		public static void Audit(IReadOnlyList<SurvivorInfo> survivors, ManualLogSource logger)
		{
			if (survivors == null)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			for (int i = 0; i < survivors.Count; i++)
			{
				SurvivorInfo survivorInfo = survivors[i];
				if (survivorInfo == null || (Object)(object)survivorInfo.SurvivorDef == (Object)null || survivorInfo.IsModded)
				{
					continue;
				}
				num++;
				UnlockableDef unlockableDef = survivorInfo.SurvivorDef.unlockableDef;
				if ((Object)(object)unlockableDef == (Object)null)
				{
					continue;
				}
				num2++;
				AchievementDef val = FindAchievementForUnlockable(unlockableDef);
				if (val == null)
				{
					num6++;
					UsuLog.Verbose(logger, "[VANILLA UNLOCK METHOD] " + SafeBodyName(survivorInfo) + " | Unlockable: " + unlockableDef.cachedName + " | Achievement: NO RESUELTO");
					continue;
				}
				num3++;
				string text = TypeName(val.type);
				string text2 = TypeName(val.serverTrackerType);
				if (val.serverTrackerType != null)
				{
					num4++;
				}
				else
				{
					num5++;
				}
				UsuLog.Verbose(logger, "[VANILLA UNLOCK METHOD] " + SafeBodyName(survivorInfo) + " | Achievement: " + val.identifier + " | Type: " + text + " | ServerTracker: " + text2 + " | Prerequisite: " + val.prerequisiteAchievementIdentifier + " | Reward: " + val.unlockableRewardIdentifier);
			}
			UsuLog.Diagnostic(logger, "[VANILLA UNLOCK METHOD] C2.3 catálogo construido | Survivors oficiales: " + num + " | Con unlock Original: " + num2 + " | Achievement resuelto: " + num3 + " | Server-tracked: " + num4 + " | Client-only: " + num5 + " | Sin AchievementDef: " + num6 + " | Autoridad modificada: no");
		}

		private static AchievementDef FindAchievementForUnlockable(UnlockableDef unlockable)
		{
			if ((Object)(object)unlockable == (Object)null || string.IsNullOrWhiteSpace(unlockable.cachedName))
			{
				return null;
			}
			try
			{
				IEnumerable achievementDefs = AchievementManager.achievementDefs;
				if (achievementDefs == null)
				{
					return null;
				}
				foreach (object item in achievementDefs)
				{
					AchievementDef val = (AchievementDef)((item is AchievementDef) ? item : null);
					if (val == null || !string.Equals(val.unlockableRewardIdentifier, unlockable.cachedName, StringComparison.Ordinal))
					{
						continue;
					}
					return val;
				}
			}
			catch
			{
			}
			return null;
		}

		private static string SafeBodyName(SurvivorInfo survivor)
		{
			if (survivor != null && !string.IsNullOrWhiteSpace(survivor.BodyName))
			{
				return survivor.BodyName;
			}
			return "<unknown>";
		}

		private static string TypeName(Type type)
		{
			return (type != null) ? (type.FullName ?? type.Name ?? "") : "<none>";
		}
	}
	public static class VanillaUnlockSemanticInspectionService
	{
		private static readonly string[] SemanticTerms = new string[49]
		{
			"kill", "death", "defeat", "boss", "teleporter", "stage", "scene", "run", "time", "difficulty",
			"item", "inventory", "equipment", "interact", "shrine", "altar", "heal", "health", "damage", "skill",
			"ability", "minion", "drone", "turret", "level", "jump", "air", "fall", "speed", "distance",
			"gold", "money", "loop", "ending", "obliterate", "portal", "artifact", "buff", "debuff", "dot",
			"elite", "champion", "acolyte", "page", "ritual", "anima", "harvester", "revive", "consume"
		};

		public static void Audit(IReadOnlyList<SurvivorInfo> survivors, ManualLogSource logger)
		{
			if (survivors == null)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			for (int i = 0; i < survivors.Count; i++)
			{
				SurvivorInfo survivorInfo = survivors[i];
				if (survivorInfo == null || (Object)(object)survivorInfo.SurvivorDef == (Object)null || survivorInfo.IsModded || (Object)(object)survivorInfo.SurvivorDef.unlockableDef == (Object)null)
				{
					continue;
				}
				AchievementDef val = FindAchievementForUnlockable(survivorInfo.SurvivorDef.unlockableDef);
				if (val != null)
				{
					num++;
					if (val.serverTrackerType != null)
					{
						num2++;
					}
					List<string> list = new List<string>();
					CollectSemanticMembers(val.type, "Achievement", list);
					CollectSemanticMembers(val.serverTrackerType, "Tracker", list);
					num3 += list.Count;
					UsuLog.Verbose(logger, "[VANILLA UNLOCK SEMANTICS] " + SafeBodyName(survivorInfo) + " | Achievement: " + val.identifier + " | AchievementType: " + TypeName(val.type) + " | TrackerType: " + TypeName(val.serverTrackerType) + " | Signature: " + ((list.Count > 0) ? string.Join(",", list.ToArray()) : "<none>"));
				}
			}
			UsuLog.Diagnostic(logger, "[VANILLA UNLOCK SEMANTICS] C2.5 inspección estructural completada | Achievements: " + num + " | Con server tracker: " + num2 + " | Miembros semánticos: " + num3 + " | Runtime mutation: no | Autoridad modificada: no");
		}

		private static void CollectSemanticMembers(Type type, string prefix, List<string> output)
		{
			if (type == null || output == null)
			{
				return;
			}
			HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			Type type2 = type;
			int num = 0;
			while (type2 != null && num < 4)
			{
				BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
				try
				{
					FieldInfo[] fields = type2.GetFields(bindingAttr);
					for (int i = 0; i < fields.Length; i++)
					{
						AddIfSemantic(prefix + ".F:" + fields[i].Name, seen, output);
					}
					PropertyInfo[] properties = type2.GetProperties(bindingAttr);
					for (int j = 0; j < properties.Length; j++)
					{
						AddIfSemantic(prefix + ".P:" + properties[j].Name, seen, output);
					}
					MethodInfo[] methods = type2.GetMethods(bindingAttr);
					for (int k = 0; k < methods.Length; k++)
					{
						if (!methods[k].IsSpecialName)
						{
							AddIfSemantic(prefix + ".M:" + methods[k].Name, seen, output);
						}
					}
				}
				catch
				{
				}
				type2 = type2.BaseType;
				num++;
			}
		}

		private static void AddIfSemantic(string value, HashSet<string> seen, List<string> output)
		{
			if (string.IsNullOrWhiteSpace(value) || output.Count >= 24)
			{
				return;
			}
			string text = value.ToLowerInvariant();
			bool flag = false;
			for (int i = 0; i < SemanticTerms.Length; i++)
			{
				if (text.IndexOf(SemanticTerms[i], StringComparison.Ordinal) >= 0)
				{
					flag = true;
					break;
				}
			}
			if (flag && seen.Add(value))
			{
				output.Add(value);
			}
		}

		private static AchievementDef FindAchievementForUnlockable(UnlockableDef unlockable)
		{
			if ((Object)(object)unlockable == (Object)null || string.IsNullOrWhiteSpace(unlockable.cachedName))
			{
				return null;
			}
			try
			{
				IEnumerable achievementDefs = AchievementManager.achievementDefs;
				if (achievementDefs == null)
				{
					return null;
				}
				foreach (object item in achievementDefs)
				{
					AchievementDef val = (AchievementDef)((item is AchievementDef) ? item : null);
					if (val == null || !string.Equals(val.unlockableRewardIdentifier, unlockable.cachedName, StringComparison.Ordinal))
					{
						continue;
					}
					return val;
				}
			}
			catch
			{
			}
			return null;
		}

		private static string SafeBodyName(SurvivorInfo survivor)
		{
			return (survivor != null && !string.IsNullOrWhiteSpace(survivor.BodyName)) ? survivor.BodyName : "<unknown>";
		}

		private static string TypeName(Type type)
		{
			return (type != null) ? (type.FullName ?? type.Name ?? "") : "<none>";
		}
	}
	public enum ContentCatalogBodyCategory
	{
		NotApplicable = 0,
		Enemy = 1,
		Champion = 2,
		Drone = 3,
		Summon = 4,
		Decoy = 5,
		Special = 6,
		Internal = 100
	}
	public static class ContentCatalogDiscoveryService
	{
		public static void Refresh(IReadOnlyList<ContentCatalogEntry> entries, ManualLogSource logger)
		{
			if (entries == null)
			{
				return;
			}
			UserProfile val = TryGetLocalProfile();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			for (int i = 0; i < entries.Count; i++)
			{
				ContentCatalogEntry contentCatalogEntry = entries[i];
				if (contentCatalogEntry != null)
				{
					contentCatalogEntry.Discovery = ResolveDiscovery(contentCatalogEntry, val);
					switch (contentCatalogEntry.Discovery)
					{
					case ContentCatalogDiscoveryState.Discovered:
						num++;
						break;
					case ContentCatalogDiscoveryState.Undiscovered:
						num2++;
						break;
					case ContentCatalogDiscoveryState.NotApplicable:
						num3++;
						break;
					default:
						num4++;
						break;
					}
				}
			}
			UsuLog.Verbose(logger, "[CONTENT DISCOVERY] Refrescado | Perfil: " + ((val != null) ? "Sí" : "No") + " | Descubiertos: " + num + " | No descubiertos: " + num2 + " | No aplica: " + num3 + " | Desconocidos: " + num4);
		}

		private static ContentCatalogDiscoveryState ResolveDiscovery(ContentCatalogEntry entry, UserProfile profile)
		{
			if (entry == null)
			{
				return ContentCatalogDiscoveryState.Unknown;
			}
			if (profile == null)
			{
				return ContentCatalogDiscoveryState.Unknown;
			}
			try
			{
				switch (entry.Kind)
				{
				case ContentCatalogKind.Survivor:
					return ResolveSurvivorDiscovery(entry, profile);
				case ContentCatalogKind.Item:
					return ResolveItemDiscovery(entry, profile);
				case ContentCatalogKind.Equipment:
					return ResolveEquipmentDiscovery(entry, profile);
				case ContentCatalogKind.Enemy:
				case ContentCatalogKind.Boss:
				case ContentCatalogKind.Stage:
					return ResolveUnlockableDiscovery(entry.DiscoveryUnlockable, profile);
				case ContentCatalogKind.Skill:
					return ContentCatalogDiscoveryState.NotApplicable;
				default:
					return ContentCatalogDiscoveryState.NotApplicable;
				}
			}
			catch
			{
				return ContentCatalogDiscoveryState.Unknown;
			}
		}

		private static ContentCatalogDiscoveryState ResolveSurvivorDiscovery(ContentCatalogEntry entry, UserProfile profile)
		{
			if ((Object)(object)entry.DiscoveryUnlockable == (Object)null)
			{
				return ContentCatalogDiscoveryState.Discovered;
			}
			return profile.HasUnlockable(entry.DiscoveryUnlockable) ? ContentCatalogDiscoveryState.Discovered : ContentCatalogDiscoveryState.Undiscovered;
		}

		private static ContentCatalogDiscoveryState ResolveItemDiscovery(ContentCatalogEntry entry, UserProfile profile)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Invalid comparison between Unknown and I4
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			Object asset = entry.Asset;
			ItemDef val = (ItemDef)(object)((asset is ItemDef) ? asset : null);
			if ((Object)(object)val == (Object)null)
			{
				return ContentCatalogDiscoveryState.Unknown;
			}
			if ((Object)(object)entry.DiscoveryUnlockable != (Object)null && !profile.HasUnlockable(entry.DiscoveryUnlockable))
			{
				return ContentCatalogDiscoveryState.Undiscovered;
			}
			if ((int)val.itemIndex < 0)
			{
				return ContentCatalogDiscoveryState.NotApplicable;
			}
			PickupIndex val2 = PickupCatalog.FindPickupIndex(val.itemIndex);
			if (val2 == PickupIndex.none)
			{
				return ContentCatalogDiscoveryState.NotApplicable;
			}
			return profile.HasDiscoveredPickup(val2) ? ContentCatalogDiscoveryState.Discovered : ContentCatalogDiscoveryState.Undiscovered;
		}

		private static ContentCatalogDiscoveryState ResolveEquipmentDiscovery(ContentCatalogEntry entry, UserProfile profile)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Invalid comparison between Unknown and I4
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			Object asset = entry.Asset;
			EquipmentDef val = (EquipmentDef)(object)((asset is EquipmentDef) ? asset : null);
			if ((Object)(object)val == (Object)null)
			{
				return ContentCatalogDiscoveryState.Unknown;
			}
			if ((Object)(object)entry.DiscoveryUnlockable != (Object)null && !profile.HasUnlockable(entry.DiscoveryUnlockable))
			{
				return ContentCatalogDiscoveryState.Undiscovered;
			}
			if ((int)val.equipmentIndex < 0)
			{
				return ContentCatalogDiscoveryState.NotApplicable;
			}
			PickupIndex val2 = PickupCatalog.FindPickupIndex(val.equipmentIndex);
			if (val2 == PickupIndex.none)
			{
				return ContentCatalogDiscoveryState.NotApplicable;
			}
			return profile.HasDiscoveredPickup(val2) ? ContentCatalogDiscoveryState.Discovered : ContentCatalogDiscoveryState.Undiscovered;
		}

		private static ContentCatalogDiscoveryState ResolveUnlockableDiscovery(UnlockableDef unlockable, UserProfile profile)
		{
			if ((Object)(object)unlockable == (Object)null)
			{
				return ContentCatalogDiscoveryState.NotApplicable;
			}
			return profile.HasUnlockable(unlockable) ? ContentCatalogDiscoveryState.Discovered : ContentCatalogDiscoveryState.Undiscovered;
		}

		private static UserProfile TryGetLocalProfile()
		{
			try
			{
				if (LocalUserManager.readOnlyLocalUsersList != null && LocalUserManager.readOnlyLocalUsersList.Count > 0)
				{
					LocalUser val = LocalUserManager.readOnlyLocalUsersList[0];
					if (val != null)
					{
						return val.userProfile;
					}
				}
			}
			catch
			{
			}
			return null;
		}
	}
	public enum ContentCatalogDiscoveryState
	{
		Unknown,
		Discovered,
		Undiscovered,
		NotApplicable
	}
	public sealed class ContentCatalogEntry
	{
		public ContentCatalogKind Kind { get; set; }

		public string StableId { get; set; } = "";

		public string InternalName { get; set; } = "";

		public string DisplayName { get; set; } = "";

		public string Description { get; set; } = "";

		public string NameToken { get; set; } = "";

		public ContentCatalogSourceInfo Source { get; set; }

		public ContentCatalogDiscoveryState Discovery { get; set; } = ContentCatalogDiscoveryState.Unknown;

		public ContentCatalogBodyCategory BodyCategory { get; set; } = ContentCatalogBodyCategory.NotApplicable;

		public bool IsChampion { get; set; }

		public UnlockableDef DiscoveryUnlockable { get; set; }

		public bool HasLogBookUnlock { get; set; }

		public bool HideIdentityWhenUndiscovered { get; set; }

		public bool RevealUnlockRequirementWhenUndiscovered { get; set; } = true;

		public bool Hidden { get; set; }

		public bool Selectable { get; set; } = true;

		public Object Asset { get; set; }

		public Sprite IconSprite { get; set; }

		public Texture IconTexture { get; set; }

		public GameObject ModelPrefab { get; set; }

		public bool ShouldHideIdentity => HideIdentityWhenUndiscovered && Discovery == ContentCatalogDiscoveryState.Undiscovered;

		public override string ToString()
		{
			return Kind.ToString() + " | " + DisplayName + " | " + StableId;
		}
	}
	public enum ContentCatalogKind
	{
		Unknown = 0,
		Survivor = 1,
		Skill = 2,
		Item = 3,
		Equipment = 4,
		Enemy = 5,
		Boss = 6,
		Stage = 7,
		Interactable = 8,
		Skin = 9,
		Weapon = 10,
		Other = 100
	}
	internal static class ContentCatalogReflection
	{
		public static string ReadString(object target, params string[] memberNames)
		{
			if (target == null || memberNames == null)
			{
				return "";
			}
			Type type = target.GetType();
			foreach (string text in memberNames)
			{
				if (string.IsNullOrWhiteSpace(text))
				{
					continue;
				}
				try
				{
					PropertyInfo property = type.GetProperty(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null && property.CanRead && property.PropertyType == typeof(string))
					{
						return (property.GetValue(target, null) as string) ?? "";
					}
					FieldInfo field = type.GetField(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null && field.FieldType == typeof(string))
					{
						return (field.GetValue(target) as string) ?? "";
					}
				}
				catch
				{
				}
			}
			return "";
		}

		public static T ReadUnityObject<T>(object target, params string[] memberNames) where T : Object
		{
			if (target == null || memberNames == null)
			{
				return default(T);
			}
			Type type = target.GetType();
			foreach (string text in memberNames)
			{
				if (string.IsNullOrWhiteSpace(text))
				{
					continue;
				}
				try
				{
					PropertyInfo property = type.GetProperty(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null && property.CanRead && typeof(T).IsAssignableFrom(property.PropertyType))
					{
						object? value = property.GetValue(target, null);
						return (T)((value is T) ? value : null);
					}
					FieldInfo field = type.GetField(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null && typeof(T).IsAssignableFrom(field.FieldType))
					{
						object? value2 = field.GetValue(target);
						return (T)((value2 is T) ? value2 : null);
					}
				}
				catch
				{
				}
			}
			return default(T);
		}

		public static bool ReadBool(object target, bool fallback, params string[] memberNames)
		{
			if (target == null || memberNames == null)
			{
				return fallback;
			}
			Type type = target.GetType();
			foreach (string name in memberNames)
			{
				try
				{
					PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null && property.CanRead && property.PropertyType == typeof(bool))
					{
						return (bool)property.GetValue(target, null);
					}
					FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null && field.FieldType == typeof(bool))
					{
						return (bool)field.GetValue(target);
					}
				}
				catch
				{
				}
			}
			return fallback;
		}

		public static string ResolveDisplayName(Object asset, params string[] tokenMembers)
		{
			if (asset == (Object)null)
			{
				return "";
			}
			string text = ReadString(asset, tokenMembers);
			if (!string.IsNullOrWhiteSpace(text))
			{
				string text2 = Language.GetString(text);
				if (!string.IsNullOrWhiteSpace(text2) && !string.Equals(text2, text, StringComparison.Ordinal))
				{
					return text2;
				}
			}
			return string.IsNullOrWhiteSpace(asset.name) ? "Unknown" : asset.name;
		}

		public static string ResolveDescription(Object asset, params string[] tokenMembers)
		{
			if (asset == (Object)null)
			{
				return "";
			}
			string text = ReadString(asset, tokenMembers);
			if (string.IsNullOrWhiteSpace(text))
			{
				return "";
			}
			string text2 = Language.GetString(text);
			if (string.IsNullOrWhiteSpace(text2) || string.Equals(text2, text, StringComparison.Ordinal))
			{
				return "";
			}
			return text2;
		}
	}
	public static class ContentCatalogService
	{
		private static readonly List<ContentCatalogEntry> Entries = new List<ContentCatalogEntry>();

		private static readonly Dictionary<ContentCatalogKind, List<ContentCatalogEntry>> EntriesByKind = new Dictionary<ContentCatalogKind, List<ContentCatalogEntry>>();

		private static readonly HashSet<string> StableIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static ManualLogSource logger;

		private static bool initialized;

		public static int Count => Entries.Count;

		public static void Initialize(ManualLogSource activeLogger)
		{
			logger = activeLogger;
			if (initialized)
			{
				Rebuild();
				return;
			}
			initialized = true;
			Rebuild();
			Language.onCurrentLanguageChanged += OnLanguageChanged;
		}

		private static void OnLanguageChanged()
		{
			Rebuild();
		}

		public static void Rebuild()
		{
			Entries.Clear();
			EntriesByKind.Clear();
			StableIds.Clear();
			ScanSurvivors();
			ScanItems();
			ScanEquipment();
			ScanSkills();
			ScanBodies();
			ScanStages();
			ContentCatalogDiscoveryService.Refresh(Entries, logger);
			SortAll();
			LogSummary();
		}

		public static IReadOnlyList<ContentCatalogEntry> GetAll()
		{
			return Entries;
		}

		public static void RefreshDiscovery()
		{
			ContentCatalogDiscoveryService.Refresh(Entries, logger);
		}

		public static IReadOnlyList<ContentCatalogEntry> GetEntries(ContentCatalogKind kind)
		{
			if (EntriesByKind.TryGetValue(kind, out var value))
			{
				return value;
			}
			return Array.Empty<ContentCatalogEntry>();
		}

		public static bool TryGet(string stableId, out ContentCatalogEntry entry)
		{
			entry = null;
			if (string.IsNullOrWhiteSpace(stableId))
			{
				return false;
			}
			for (int i = 0; i < Entries.Count; i++)
			{
				ContentCatalogEntry contentCatalogEntry = Entries[i];
				if (contentCatalogEntry != null && string.Equals(contentCatalogEntry.StableId, stableId, StringComparison.OrdinalIgnoreCase))
				{
					entry = contentCatalogEntry;
					return true;
				}
			}
			return false;
		}

		private static void ScanSurvivors()
		{
			SurvivorDef[] survivorDefs = SurvivorCatalog.survivorDefs;
			if (survivorDefs == null)
			{
				return;
			}
			foreach (SurvivorDef val in survivorDefs)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string text = (((Object)(object)val.bodyPrefab != (Object)null) ? ((Object)val.bodyPrefab).name : val.cachedName);
					ContentCatalogEntry contentCatalogEntry = CreateBaseEntry(ContentCatalogKind.Survivor, (Object)(object)val, "survivor:" + text, val.cachedName, new string[1] { "displayNameToken" }, new string[1] { "descriptionToken" });
					contentCatalogEntry.Hidden = val.hidden;
					contentCatalogEntry.Selectable = !val.hidden && (Object)(object)val.bodyPrefab != (Object)null;
					contentCatalogEntry.ModelPrefab = val.bodyPrefab;
					contentCatalogEntry.IconTexture = ContentCatalogReflection.ReadUnityObject<Texture>((object)val, new string[1] { "portraitIcon" });
					contentCatalogEntry.DiscoveryUnlockable = val.unlockableDef;
					contentCatalogEntry.HideIdentityWhenUndiscovered = (Object)(object)val.unlockableDef != (Object)null;
					Add(contentCatalogEntry);
				}
			}
		}

		private static void ScanItems()
		{
			ItemDef[] array = Resources.FindObjectsOfTypeAll<ItemDef>();
			if (array == null)
			{
				return;
			}
			foreach (ItemDef val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string text = (string.IsNullOrWhiteSpace(((Object)val).name) ? "UnknownItem" : ((Object)val).name);
					ContentCatalogEntry contentCatalogEntry = CreateBaseEntry(ContentCatalogKind.Item, (Object)(object)val, "item:" + text, text, new string[1] { "nameToken" }, new string[2] { "descriptionToken", "pickupToken" });
					contentCatalogEntry.IconSprite = ContentCatalogReflection.ReadUnityObject<Sprite>((object)val, new string[1] { "pickupIconSprite" });
					contentCatalogEntry.ModelPrefab = ContentCatalogReflection.ReadUnityObject<GameObject>((object)val, new string[1] { "pickupModelPrefab" });
					contentCatalogEntry.DiscoveryUnlockable = val.unlockableDef;
					contentCatalogEntry.HideIdentityWhenUndiscovered = true;
					Add(contentCatalogEntry);
				}
			}
		}

		private static void ScanEquipment()
		{
			EquipmentDef[] array = Resources.FindObjectsOfTypeAll<EquipmentDef>();
			if (array == null)
			{
				return;
			}
			foreach (EquipmentDef val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string text = (string.IsNullOrWhiteSpace(((Object)val).name) ? "UnknownEquipment" : ((Object)val).name);
					ContentCatalogEntry contentCatalogEntry = CreateBaseEntry(ContentCatalogKind.Equipment, (Object)(object)val, "equipment:" + text, text, new string[1] { "nameToken" }, new string[2] { "descriptionToken", "pickupToken" });
					contentCatalogEntry.IconSprite = ContentCatalogReflection.ReadUnityObject<Sprite>((object)val, new string[1] { "pickupIconSprite" });
					contentCatalogEntry.ModelPrefab = ContentCatalogReflection.ReadUnityObject<GameObject>((object)val, new string[1] { "pickupModelPrefab" });
					contentCatalogEntry.DiscoveryUnlockable = val.unlockableDef;
					contentCatalogEntry.HideIdentityWhenUndiscovered = true;
					Add(contentCatalogEntry);
				}
			}
		}

		private static void ScanSkills()
		{
			SkillDef[] array = Resources.FindObjectsOfTypeAll<SkillDef>();
			if (array == null)
			{
				return;
			}
			foreach (SkillDef val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string text = SkillCatalog.GetSkillName(val.skillIndex);
					if (string.IsNullOrWhiteSpace(text))
					{
						text = val.skillName;
					}
					if (string.IsNullOrWhiteSpace(text))
					{
						text = val.skillNameToken;
					}
					if (!string.IsNullOrWhiteSpace(text))
					{
						ContentCatalogEntry contentCatalogEntry = CreateBaseEntry(ContentCatalogKind.Skill, (Object)(object)val, "skill:" + text, text, new string[1] { "skillNameToken" }, new string[1] { "skillDescriptionToken" });
						contentCatalogEntry.IconSprite = ContentCatalogReflection.ReadUnityObject<Sprite>((object)val, new string[1] { "icon" });
						Add(contentCatalogEntry);
					}
				}
			}
		}

		private static void ScanBodies()
		{
			GameObject[] bodyPrefabs = BodyCatalog.bodyPrefabs;
			if (bodyPrefabs == null)
			{
				return;
			}
			HashSet<GameObject> hashSet = new HashSet<GameObject>();
			SurvivorDef[] survivorDefs = SurvivorCatalog.survivorDefs;
			if (survivorDefs != null)
			{
				foreach (SurvivorDef val in survivorDefs)
				{
					if ((Object)(object)val != (Object)null && (Object)(object)val.bodyPrefab != (Object)null)
					{
						hashSet.Add(val.bodyPrefab);
					}
				}
			}
			foreach (GameObject val2 in bodyPrefabs)
			{
				if ((Object)(object)val2 == (Object)null || hashSet.Contains(val2))
				{
					continue;
				}
				CharacterBody component = val2.GetComponent<CharacterBody>();
				if (!((Object)(object)component == (Object)null))
				{
					bool isChampion = component.isChampion;
					ContentCatalogKind kind = (isChampion ? ContentCatalogKind.Boss : ContentCatalogKind.Enemy);
					string name = ((Object)val2).name;
					DeathRewards component2 = val2.GetComponent<DeathRewards>();
					UnlockableDef val3 = ContentCatalogReflection.ReadUnityObject<UnlockableDef>((object)component2, new string[1] { "logUnlockableDef" });
					ContentCatalogBodyCategory contentCatalogBodyCategory = ClassifyBody(val2, component2, isChampion);
					ContentCatalogEntry contentCatalogEntry = CreateBaseEntry(kind, (Object)(object)val2, (isChampion ? "boss:" : "enemy:") + name, name, new string[1] { "baseNameToken" }, Array.Empty<string>(), component);
					contentCatalogEntry.ModelPrefab = val2;
					contentCatalogEntry.IconTexture = ContentCatalogReflection.ReadUnityObject<Texture>((object)component, new string[1] { "portraitIcon" });
					contentCatalogEntry.BodyCategory = contentCatalogBodyCategory;
					contentCatalogEntry.IsChampion = isChampion;
					contentCatalogEntry.DiscoveryUnlockable = val3;
					contentCatalogEntry.HasLogBookUnlock = (Object)(object)val3 != (Object)null;
					contentCatalogEntry.HideIdentityWhenUndiscovered = (Object)(object)val3 != (Object)null;
					if (contentCatalogBodyCategory == ContentCatalogBodyCategory.Internal)
					{
						contentCatalogEntry.Hidden = true;
						contentCatalogEntry.Selectable = false;
					}
					Add(contentCatalogEntry);
				}
			}
		}

		private static ContentCatalogBodyCategory ClassifyBody(GameObject bodyPrefab, DeathRewards deathRewards, bool isChampion)
		{
			//IL_00ab: 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_00b6: Invalid comparison between Unknown and I4
			if ((Object)(object)bodyPrefab == (Object)null)
			{
				return ContentCatalogBodyCategory.Internal;
			}
			if (isChampion)
			{
				return ContentCatalogBodyCategory.Champion;
			}
			string text = ((Object)bodyPrefab).name ?? "";
			string text2 = text.ToLowerInvariant();
			if (text2.Contains("dummy") || text2.Contains("testbody") || text2.Contains("placeholder"))
			{
				return ContentCatalogBodyCategory.Internal;
			}
			if (text2.Contains("decoy"))
			{
				return ContentCatalogBodyCategory.Decoy;
			}
			bool flag = false;
			try
			{
				CharacterBody component = bodyPrefab.GetComponent<CharacterBody>();
				if ((Object)(object)component != (Object)null)
				{
					flag = (int)DroneCatalog.GetDroneIndexFromBodyIndex(component.bodyIndex) >= 0;
				}
			}
			catch
			{
				flag = false;
			}
			if (flag || text2.Contains("drone"))
			{
				return ContentCatalogBodyCategory.Drone;
			}
			if (text2.Contains("turret") || text2.Contains("minion"))
			{
				return ContentCatalogBodyCategory.Summon;
			}
			if ((Object)(object)deathRewards != (Object)null)
			{
				return ContentCatalogBodyCategory.Enemy;
			}
			return ContentCatalogBodyCategory.Special;
		}

		private static void ScanStages()
		{
			SceneDef[] array = Resources.FindObjectsOfTypeAll<SceneDef>();
			if (array == null)
			{
				return;
			}
			foreach (SceneDef val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string text = (string.IsNullOrWhiteSpace(val.cachedName) ? "UnknownScene" : val.cachedName);
					ContentCatalogEntry contentCatalogEntry = CreateBaseEntry(ContentCatalogKind.Stage, (Object)(object)val, "stage:" + text, text, new string[1] { "nameToken" }, new string[1] { "subtitleToken" });
					contentCatalogEntry.IconTexture = ContentCatalogReflection.ReadUnityObject<Texture>((object)val, new string[1] { "previewTexture" });
					contentCatalogEntry.DiscoveryUnlockable = ContentCatalogReflection.ReadUnityObject<UnlockableDef>((object)val, new string[2] { "unlockableDef", "logUnlockableDef" });
					contentCatalogEntry.HasLogBookUnlock = (Object)(object)contentCatalogEntry.DiscoveryUnlockable != (Object)null;
					contentCatalogEntry.HideIdentityWhenUndiscovered = (Object)(object)contentCatalogEntry.DiscoveryUnlockable != (Object)null;
					Add(contentCatalogEntry);
				}
			}
		}

		private static ContentCatalogEntry CreateBaseEntry(ContentCatalogKind kind, Object asset, string stableId, string internalName, string[] nameTokenMembers, string[] descriptionTokenMembers, object tokenSource = null)
		{
			object obj = tokenSource ?? asset;
			string text = ContentCatalogReflection.ReadString(obj, nameTokenMembers);
			string text2 = ResolveLocalizedText(obj, nameTokenMembers);
			if (string.IsNullOrWhiteSpace(text2))
			{
				text2 = internalName;
			}
			string text3 = ResolveLocalizedText(obj, descriptionTokenMembers);
			ContentSourceRegistry.TryGetSource(asset, out var source);
			return new ContentCatalogEntry
			{
				Kind = kind,
				StableId = stableId,
				InternalName = (internalName ?? ""),
				DisplayName = (text2 ?? ""),
				Description = (text3 ?? ""),
				NameToken = (text ?? ""),
				Source = source,
				Discovery = ContentCatalogDiscoveryState.Unknown,
				Asset = asset
			};
		}

		private static string ResolveLocalizedText(object source, params string[] tokenMembers)
		{
			if (source == null || tokenMembers == null || tokenMembers.Length == 0)
			{
				return "";
			}
			string text = ContentCatalogReflection.ReadString(source, tokenMembers);
			if (string.IsNullOrWhiteSpace(text))
			{
				return "";
			}
			string text2 = Language.GetString(text);
			if (string.IsNullOrWhiteSpace(text2) || string.Equals(text2, text, StringComparison.Ordinal))
			{
				return "";
			}
			return text2;
		}

		private static void Add(ContentCatalogEntry entry)
		{
			if (entry != null && !string.IsNullOrWhiteSpace(entry.StableId) && StableIds.Add(entry.StableId))
			{
				Entries.Add(entry);
				if (!EntriesByKind.TryGetValue(entry.Kind, out var value))
				{
					value = new List<ContentCatalogEntry>();
					EntriesByKind.Add(entry.Kind, value);
				}
				value.Add(entry);
			}
		}

		private static void SortAll()
		{
			Comparison<ContentCatalogEntry> comparison = (ContentCatalogEntry a, ContentCatalogEntry b) => string.Compare(a?.DisplayName, b?.DisplayName, StringComparison.CurrentCultureIgnoreCase);
			Entries.Sort(comparison);
			foreach (KeyValuePair<ContentCatalogKind, List<ContentCatalogEntry>> item in EntriesByKind)
			{
				item.Value.Sort(comparison);
			}
		}

		private static void LogSummary()
		{
			int count = GetCount(ContentCatalogKind.Survivor);
			int count2 = GetCount(ContentCatalogKind.Skill);
			int count3 = GetCount(ContentCatalogKind.Item);
			int count4 = GetCount(ContentCatalogKind.Equipment);
			int count5 = GetCount(ContentCatalogKind.Enemy);
			int count6 = GetCount(ContentCatalogKind.Boss);
			int count7 = GetCount(ContentCatalogKind.Stage);
			int bodyCategoryCount = GetBodyCategoryCount(ContentCatalogBodyCategory.Internal);
			int bodyCategoryCount2 = GetBodyCategoryCount(ContentCatalogBodyCategory.Summon);
			int bodyCategoryCount3 = GetBodyCategoryCount(ContentCatalogBodyCategory.Drone);
			int bodyCategoryCount4 = GetBodyCategoryCount(ContentCatalogBodyCategory.Decoy);
			int discoveryCount = GetDiscoveryCount(ContentCatalogDiscoveryState.Discovered);
			int discoveryCount2 = GetDiscoveryCount(ContentCatalogDiscoveryState.Undiscovered);
			int discoveryCount3 = GetDiscoveryCount(ContentCatalogDiscoveryState.Unknown);
			UsuLog.Diagnostic(logger, "[CONTENT CATALOG] Catálogo 5G.2D-B construido | Total: " + Entries.Count + " | Survivors: " + count + " | Skills(raw): " + count2 + " | Items: " + count3 + " | Equipment: " + count4 + " | Enemies: " + count5 + " | Bosses/Champions: " + count6 + " | Stages: " + count7 + " | Summons: " + bodyCategoryCount2 + " | Drones: " + bodyCategoryCount3 + " | Decoys: " + bodyCategoryCount4 + " | Internal ocultos: " + bodyCategoryCount + " | Descubiertos: " + discoveryCount + " | No descubiertos: " + discoveryCount2 + " | Discovery desconocido: " + discoveryCount3 + " | Assets con procedencia: " + ContentSourceRegistry.Count);
		}

		private static int GetBodyCategoryCount(ContentCatalogBodyCategory category)
		{
			int num = 0;
			for (int i = 0; i < Entries.Count; i++)
			{
				ContentCatalogEntry contentCatalogEntry = Entries[i];
				if (contentCatalogEntry != null && contentCatalogEntry.BodyCategory == category)
				{
					num++;
				}
			}
			return num;
		}

		private static int GetDiscoveryCount(ContentCatalogDiscoveryState state)
		{
			int num = 0;
			for (int i = 0; i < Entries.Count; i++)
			{
				ContentCatalogEntry contentCatalogEntry = Entries[i];
				if (contentCatalogEntry != null && contentCatalogEntry.Discovery == state)
				{
					num++;
				}
			}
			return num;
		}

		private static int GetCount(ContentCatalogKind kind)
		{
			if (EntriesByKind.TryGetValue(kind, out var value))
			{
				return value.Count;
			}
			return 0;
		}
	}
	public sealed class ContentCatalogSourceInfo
	{
		public string ContentPackIdentifier { get; set; } = "";

		public string AssemblyName { get; set; } = "";

		public ContentCatalogSourceKind Kind { get; set; } = ContentCatalogSourceKind.Unknown;

		public string DisplayLabel
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(AssemblyName))
				{
					return AssemblyName;
				}
				if (!string.IsNullOrWhiteSpace(ContentPackIdentifier))
				{
					return ContentPackIdentifier;
				}
				return Kind.ToString();
			}
		}
	}
	public enum ContentCatalogSourceKind
	{
		Unknown,
		Vanilla,
		Dlc,
		Mod,
		Usu
	}
	public static class ContentRuntimeContextTracker
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_OnMemberAddedServer <0>__BossGroup_OnMemberAddedServer;
		}

		private static readonly HashSet<CharacterMaster> TeleporterBossMasters = new HashSet<CharacterMaster>();

		private static readonly HashSet<string> SurvivorBodyNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static ManualLogSource logger;

		private static bool initialized;

		private static BossGroup activeTeleporterBossGroup;

		public static void Initialize(ManualLogSource activeLogger)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			logger = activeLogger;
			if (!initialized)
			{
				initialized = true;
				BuildSurvivorBodyNameCache();
				object obj = <>O.<0>__BossGroup_OnMemberAddedServer;
				if (obj == null)
				{
					hook_OnMemberAddedServer val = BossGroup_OnMemberAddedServer;
					<>O.<0>__BossGroup_OnMemberAddedServer = val;
					obj = (object)val;
				}
				BossGroup.OnMemberAddedServer += (hook_OnMemberAddedServer)obj;
				UsuLog.Verbose(logger, "[CONTENT CONTEXT] Runtime context tracker inicializado | TeleporterBoss / Elite / Umbra / Summon.");
			}
		}

		public static ContentRuntimeTargetFlags GetFlags(CharacterBody body)
		{
			if ((Object)(object)body == (Object)null)
			{
				return ContentRuntimeTargetFlags.None;
			}
			ContentRuntimeTargetFlags contentRuntimeTargetFlags = ContentRuntimeTargetFlags.None;
			if (IsTeleporterBoss(body))
			{
				contentRuntimeTargetFlags |= ContentRuntimeTargetFlags.TeleporterBoss;
			}
			if (IsElite(body))
			{
				contentRuntimeTargetFlags |= ContentRuntimeTargetFlags.Elite;
			}
			if (IsUmbra(body))
			{
				contentRuntimeTargetFlags |= ContentRuntimeTargetFlags.Umbra;
			}
			if (IsSummon(body))
			{
				contentRuntimeTargetFlags |= ContentRuntimeTargetFlags.Summon;
			}
			return contentRuntimeTargetFlags;
		}

		public static bool IsTeleporterBoss(CharacterBody body)
		{
			if ((Object)(object)body == (Object)null || (Object)(object)body.master == (Object)null)
			{
				return false;
			}
			CleanupDestroyedMasters();
			return TeleporterBossMasters.Contains(body.master);
		}

		public static bool IsElite(CharacterBody body)
		{
			return ContentCatalogReflection.ReadBool(body, false, "isElite");
		}

		public static bool IsSummon(CharacterBody body)
		{
			if ((Object)(object)body == (Object)null || (Object)(object)body.master == (Object)null)
			{
				return false;
			}
			try
			{
				MinionOwnership minionOwnership = body.master.minionOwnership;
				return (Object)(object)minionOwnership != (Object)null && (Object)(object)minionOwnership.ownerMaster != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		public static bool IsUmbra(CharacterBody body)
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Invalid comparison between Unknown and I4
			if ((Object)(object)body == (Object)null)
			{
				return false;
			}
			if (SurvivorBodyNames.Count == 0)
			{
				BuildSurvivorBodyNameCache();
			}
			string text = NormalizeRuntimeBodyName(((Object)(object)((Component)body).gameObject != (Object)null) ? ((Object)((Component)body).gameObject).name : "");
			if (string.IsNullOrWhiteSpace(text) || !SurvivorBodyNames.Contains(text))
			{
				return false;
			}
			if (ContentCatalogReflection.ReadBool(body, false, "isPlayerControlled"))
			{
				return false;
			}
			TeamComponent component = ((Component)body).GetComponent<TeamComponent>();
			return (Object)(object)component != (Object)null && (int)component.teamIndex == 2;
		}

		private static void BossGroup_OnMemberAddedServer(orig_OnMemberAddedServer orig, BossGroup self, CharacterMaster memberMaster)
		{
			orig.Invoke(self, memberMaster);
			if (!((Object)(object)self == (Object)null) && !((Object)(object)memberMaster == (Object)null) && IsTeleporterBossGroup(self))
			{
				if ((Object)(object)activeTeleporterBossGroup != (Object)(object)self)
				{
					activeTeleporterBossGroup = self;
					TeleporterBossMasters.Clear();
				}
				TeleporterBossMasters.Add(memberMaster);
				UsuLog.Verbose(logger, "[CONTENT CONTEXT] TeleporterBoss registrado | Master: " + ((Object)memberMaster).name + " | Total grupo: " + TeleporterBossMasters.Count);
			}
		}

		private static bool IsTeleporterBossGroup(BossGroup bossGroup)
		{
			if ((Object)(object)bossGroup == (Object)null)
			{
				return false;
			}
			TeleporterInteraction instance = TeleporterInteraction.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)bossGroup).gameObject == (Object)(object)((Component)instance).gameObject)
			{
				return true;
			}
			TeleporterInteraction component = ((Component)bossGroup).GetComponent<TeleporterInteraction>();
			return (Object)(object)component != (Object)null && (Object)(object)component == (Object)(object)instance;
		}

		private static void CleanupDestroyedMasters()
		{
			if (TeleporterBossMasters.Count != 0)
			{
				TeleporterBossMasters.RemoveWhere((CharacterMaster master) => (Object)(object)master == (Object)null);
			}
		}

		private static void BuildSurvivorBodyNameCache()
		{
			SurvivorBodyNames.Clear();
			SurvivorDef[] survivorDefs = SurvivorCatalog.survivorDefs;
			if (survivorDefs == null)
			{
				return;
			}
			foreach (SurvivorDef val in survivorDefs)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.bodyPrefab == (Object)null))
				{
					string text = NormalizeRuntimeBodyName(((Object)val.bodyPrefab).name);
					if (!string.IsNullOrWhiteSpace(text))
					{
						SurvivorBodyNames.Add(text);
					}
				}
			}
		}

		private static string NormalizeRuntimeBodyName(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return "";
			}
			string text = value.Trim();
			if (text.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - "(Clone)".Length).Trim();
			}
			return text;
		}
	}
	[Flags]
	public enum ContentRuntimeTargetFlags
	{
		None = 0,
		TeleporterBoss = 1,
		Elite = 2,
		Umbra = 4,
		Summon = 8
	}
	public static class ContentSourceRegistry
	{
		private const string UsuPluginGuid = "com.danileo.UniversalSurvivorUnlocks";

		private static readonly Dictionary<Object, ContentCatalogSourceInfo> Sources = new Dictionary<Object, ContentCatalogSourceInfo>();

		private static readonly Dictionary<string, ContentCatalogSourceInfo> SourcesByAssetName = new Dictionary<string, ContentCatalogSourceInfo>(StringComparer.OrdinalIgnoreCase);

		public static int Count => Sources.Count;

		public static void Reset(ManualLogSource logger)
		{
			Sources.Clear();
			SourcesByAssetName.Clear();
			UsuLog.Verbose(logger, "[CONTENT CATALOG] Registro de procedencia reiniciado.");
		}

		public static void RegisterContentPack(ReadOnlyContentPack contentPack, ManualLogSource logger)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			string text = (string.IsNullOrWhiteSpace(((ReadOnlyContentPack)(ref contentPack)).identifier) ? "UnknownContentPack" : ((ReadOnlyContentPack)(ref contentPack)).identifier);
			ContentCatalogSourceInfo contentCatalogSourceInfo = new ContentCatalogSourceInfo
			{
				ContentPackIdentifier = text,
				AssemblyName = ResolveAssemblyName(text),
				Kind = ResolveSourceKind(text)
			};
			int count = Sources.Count;
			try
			{
				PropertyInfo[] properties = typeof(ReadOnlyContentPack).GetProperties(BindingFlags.Instance | BindingFlags.Public);
				foreach (PropertyInfo propertyInfo in properties)
				{
					if (propertyInfo == null || !propertyInfo.CanRead)
					{
						continue;
					}
					object value;
					try
					{
						value = propertyInfo.GetValue(contentPack, null);
					}
					catch
					{
						continue;
					}
					if (value == null || value is string || !(value is IEnumerable enumerable))
					{
						continue;
					}
					foreach (object item in enumerable)
					{
						Object val = (Object)((item is Object) ? item : null);
						if (val != null && val != (Object)null)
						{
							RegisterAsset(val, contentCatalogSourceInfo);
						}
					}
				}
			}
			catch (Exception ex)
			{
				if (logger != null)
				{
					logger.LogWarning((object)("[CONTENT CATALOG] No se pudo recorrer completamente el ContentPack '" + text + "': " + ex.GetType().Name + " - " + ex.Message));
				}
			}
			int num = Sources.Count - count;
			UsuLog.ExtendedAudit(logger, "[CONTENT CATALOG] ContentPack registrado | Pack: " + text + " | Fuente: " + contentCatalogSourceInfo.Kind.ToString() + " | Assets nuevos: " + num);
		}

		private static void RegisterAsset(Object asset, ContentCatalogSourceInfo source)
		{
			if (!(asset == (Object)null) && source != null)
			{
				if (!Sources.ContainsKey(asset))
				{
					Sources.Add(asset, source);
				}
				if (!string.IsNullOrWhiteSpace(asset.name) && !SourcesByAssetName.ContainsKey(asset.name))
				{
					SourcesByAssetName.Add(asset.name, source);
				}
			}
		}

		public static bool TryGetSource(Object asset, out ContentCatalogSourceInfo source)
		{
			source = null;
			if (asset == (Object)null)
			{
				return false;
			}
			if (Sources.TryGetValue(asset, out source))
			{
				return true;
			}
			if (!string.IsNullOrWhiteSpace(asset.name) && SourcesByAssetName.TryGetValue(asset.name, out source))
			{
				return true;
			}
			return false;
		}

		private static ContentCatalogSourceKind ResolveSourceKind(string identifier)
		{
			if (string.IsNullOrWhiteSpace(identifier))
			{
				return ContentCatalogSourceKind.Unknown;
			}
			if (identifier.StartsWith("com.danileo.UniversalSurvivorUnlocks", StringComparison.OrdinalIgnoreCase))
			{
				return ContentCatalogSourceKind.Usu;
			}
			if (identifier.StartsWith("RoR2.DLC", StringComparison.OrdinalIgnoreCase))
			{
				return ContentCatalogSourceKind.Dlc;
			}
			if (identifier.StartsWith("RoR2.", StringComparison.OrdinalIgnoreCase))
			{
				return ContentCatalogSourceKind.Vanilla;
			}
			return ContentCatalogSourceKind.Mod;
		}

		private static string ResolveAssemblyName(string identifier)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Enumerator<ManagedReadOnlyContentPack> enumerator = R2APIContentManager.ManagedContentPacks.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						ManagedReadOnlyContentPack current = enumerator.Current;
						if (!string.Equals(((ManagedReadOnlyContentPack)(ref current)).Identifier, identifier, StringComparison.Ordinal) || !(((ManagedReadOnlyContentPack)(ref current)).TiedAssembly != null))
						{
							continue;
						}
						return ((ManagedReadOnlyContentPack)(ref current)).TiedAssembly.GetName().Name;
					}
				}
				finally
				{
					((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch
			{
			}
			return identifier;
		}
	}
	public sealed class SurvivorContentProfile
	{
		public SurvivorInfo SurvivorInfo { get; set; }

		public SurvivorDef SurvivorDef { get; set; }

		public string BodyName { get; set; } = "";

		public string InternalName { get; set; } = "";

		public string DisplayName { get; set; } = "";

		public string Subtitle { get; set; } = "";

		public string Description { get; set; } = "";

		public string Lore { get; set; } = "";

		public string SourceIdentifier { get; set; } = "";

		public string SourceAssembly { get; set; } = "";

		public Texture Portrait { get; set; }

		public GameObject BodyPrefab { get; set; }

		public ContentCatalogDiscoveryState Discovery { get; set; } = ContentCatalogDiscoveryState.Unknown;

		public bool HideIdentityWhenUndiscovered { get; set; }

		public SurvivorContentUnlockInfo SurvivorUnlock { get; set; }

		public List<SurvivorContentSkillGroup> SkillGroups { get; } = new List<SurvivorContentSkillGroup>();

		public List<SurvivorContentSkinEntry> Skins { get; } = new List<SurvivorContentSkinEntry>();

		public bool ShouldHideIdentity => HideIdentityWhenUndiscovered && Discovery == ContentCatalogDiscoveryState.Undiscovered;
	}
	public static class SurvivorContentProfileService
	{
		private static readonly List<SurvivorContentProfile> Profiles = new List<SurvivorContentProfile>();

		private static readonly Dictionary<string, SurvivorContentProfile> ProfilesByBody = new Dictionary<string, SurvivorContentProfile>(StringComparer.OrdinalIgnoreCase);

		private static readonly List<SurvivorInfo> CachedSurvivors = new List<SurvivorInfo>();

		private static ManualLogSource logger;

		private static bool initialized;

		public static int Count => Profiles.Count;

		public static void Initialize(IReadOnlyList<SurvivorInfo> survivors, ManualLogSource activeLogger)
		{
			logger = activeLogger;
			CacheSurvivors(survivors);
			if (!initialized)
			{
				initialized = true;
				Language.onCurrentLanguageChanged += OnLanguageChanged;
			}
			Rebuild();
		}

		public static void Rebuild()
		{
			Profiles.Clear();
			ProfilesByBody.Clear();
			UserProfile userProfile = TryGetLocalProfile();
			for (int i = 0; i < CachedSurvivors.Count; i++)
			{
				SurvivorInfo survivorInfo = CachedSurvivors[i];
				SurvivorContentProfile survivorContentProfile = BuildProfile(survivorInfo, userProfile);
				if (survivorContentProfile != null)
				{
					Profiles.Add(survivorContentProfile);
					if (!string.IsNullOrWhiteSpace(survivorContentProfile.BodyName) && !ProfilesByBody.ContainsKey(survivorContentProfile.BodyName))
					{
						ProfilesByBody.Add(survivorContentProfile.BodyName, survivorContentProfile);
					}
				}
			}
			Profiles.Sort((SurvivorContentProfile a, SurvivorContentProfile b) => string.Compare(a?.DisplayName, b?.DisplayName, StringComparison.CurrentCultureIgnoreCase));
			LogSummary();
		}

		public static void RefreshDiscovery()
		{
			Rebuild();
		}

		public static bool RefreshDiscoveryIfChanged()
		{
			UserProfile val = TryGetLocalProfile();
			if (val == null)
			{
				return false;
			}
			for (int i = 0; i < Profiles.Count; i++)
			{
				SurvivorContentProfile survivorContentProfile = Profiles[i];
				if (survivorContentProfile != null && !((Object)(object)survivorContentProfile.SurvivorDef == (Object)null))
				{
					UnlockableDef unlockableDef = survivorContentProfile.SurvivorDef.unlockableDef;
					ContentCatalogDiscoveryState contentCatalogDiscoveryState = ResolveUnlockableDiscovery(unlockableDef, val, unlockedWhenNull: true);
					bool flag = (Object)(object)unlockableDef == (Object)null || contentCatalogDiscoveryState == ContentCatalogDiscoveryState.Discovered;
					bool flag2 = ((survivorContentProfile.SurvivorUnlock == null) ? ((Object)(object)unlockableDef != (Object)null) : (survivorContentProfile.SurvivorUnlock.UnlockableDef != unlockableDef));
					bool flag3 = survivorContentProfile.SurvivorUnlock != null && survivorContentProfile.SurvivorUnlock.IsUnlocked != flag;
					if (survivorContentProfile.Discovery != contentCatalogDiscoveryState || flag2 || flag3)
					{
						Rebuild();
						return true;
					}
				}
			}
			return false;
		}

		public static IReadOnlyList<SurvivorContentProfile> GetAll()
		{
			return Profiles;
		}

		public static bool TryGetByBodyName(string bodyName, out SurvivorContentProfile profile)
		{
			profile = null;
			if (string.IsNullOrWhiteSpace(bodyName))
			{
				return false;
			}
			return ProfilesByBody.TryGetValue(bodyName, out profile);
		}

		private static void OnLanguageChanged()
		{
			Rebuild();
		}

		private static void CacheSurvivors(IReadOnlyList<SurvivorInfo> survivors)
		{
			CachedSurvivors.Clear();
			if (survivors == null)
			{
				return;
			}
			for (int i = 0; i < survivors.Count; i++)
			{
				SurvivorInfo survivorInfo = survivors[i];
				if (survivorInfo != null)
				{
					CachedSurvivors.Add(survivorInfo);
				}
			}
		}

		private static SurvivorContentProfile BuildProfile(SurvivorInfo survivorInfo, UserProfile userProfile)
		{
			if (survivorInfo == null || (Object)(object)survivorInfo.SurvivorDef == (Object)null)
			{
				return null;
			}
			SurvivorDef survivorDef = survivorInfo.SurvivorDef;
			GameObject bodyPrefab = survivorDef.bodyPrefab;
			if ((Object)(object)bodyPrefab == (Object)null)
			{
				return null;
			}
			CharacterBody component = bodyPrefab.GetComponent<CharacterBody>();
			string text = ((!string.IsNullOrWhiteSpace(survivorInfo.BodyName)) ? survivorInfo.BodyName : ((Object)bodyPrefab).name);
			string text2 = ResolveLocalizedToken(survivorDef, "displayNameToken");
			if (string.IsNullOrWhiteSpace(text2))
			{
				text2 = survivorInfo.DisplayName;
			}
			string text3 = ResolveLocalizedToken(component, "subtitleNameToken", "subtitleToken");
			string text4 = ResolveLocalizedToken(survivorDef, "descriptionToken");
			string text5 = ResolveLore(survivorDef, component);
			Texture val = (((Object)(object)component != (Object)null) ? component.portraitIcon : null);
			if ((Object)(object)val == (Object)null && ContentCatalogService.TryGet("survivor:" + text, out var entry))
			{
				val = entry.IconTexture;
			}
			UnlockableDef unlockableDef = survivorDef.unlockableDef;
			ContentCatalogDiscoveryState discovery = ResolveUnlockableDiscovery(unlockableDef, userProfile, unlockedWhenNull: true);
			SurvivorContentProfile survivorContentProfile = new SurvivorContentProfile
			{
				SurvivorInfo = survivorInfo,
				SurvivorDef = survivorDef,
				BodyName = (text ?? ""),
				InternalName = (survivorInfo.InternalName ?? ""),
				DisplayName = (text2 ?? ""),
				Subtitle = (text3 ?? ""),
				Description = (text4 ?? ""),
				Lore = (text5 ?? ""),
				SourceIdentifier = ResolveSourceIdentifier(survivorInfo, survivorDef),
				SourceAssembly = (survivorInfo.SourceAssembly ?? ""),
				Portrait = val,
				BodyPrefab = bodyPrefab,
				Discovery = discovery,
				HideIdentityWhenUndiscovered = ((Object)(object)unlockableDef != (Object)null),
				SurvivorUnlock = BuildUnlockInfo(unlockableDef, userProfile)
			};
			BuildSkillGroups(survivorContentProfile, bodyPrefab, userProfile);
			BuildSkins(survivorContentProfile, bodyPrefab, component, userProfile);
			UsuLog.ExtendedAudit(logger, "[SURVIVOR PROFILE] " + survivorContentProfile.DisplayName + " | Body: " + survivorContentProfile.BodyName + " | Skills: " + CountSkillVariants(survivorContentProfile) + " | Grupos: " + survivorContentProfile.SkillGroups.Count + " | Skins: " + survivorContentProfile.Skins.Count + " | Lore: " + ((!string.IsNullOrWhiteSpace(survivorContentProfile.Lore)) ? "Sí" : "No") + " | Unlock: " + survivorContentProfile.SurvivorUnlock.Source);
			return survivorContentProfile;
		}

		private static void BuildSkillGroups(SurvivorContentProfile profile, GameObject bodyPrefab, UserProfile userProfile)
		{
			if (profile == null || (Object)(object)bodyPrefab == (Object)null)
			{
				return;
			}
			SkillLocator component = bodyPrefab.GetComponent<SkillLocator>();
			BuildPassive(profile, component);
			GenericSkill[] components = bodyPrefab.GetComponents<GenericSkill>();
			if (components == null)
			{
				return;
			}
			int num = 0;
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (GenericSkill val in components)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				SkillFamily val2 = ContentCatalogReflection.ReadUnityObject<SkillFamily>((object)val, new string[1] { "skillFamily" });
				SurvivorContentSkillSlot survivorContentSkillSlot = ResolveSkillSlot(val, component);
				int num2 = 0;
				if (survivorContentSkillSlot == SurvivorContentSkillSlot.Extra)
				{
					num++;
					num2 = num;
				}
				SurvivorContentSkillGroup survivorContentSkillGroup = new SurvivorContentSkillGroup
				{
					StableId = BuildGroupStableId(profile.BodyName, survivorContentSkillSlot, num2),
					Slot = survivorContentSkillSlot,
					SlotLabel = ResolveSlotLabel(survivorContentSkillSlot, num2),
					ExtraSlotIndex = num2,
					GenericSkill = val,
					SkillFamily = val2
				};
				if ((Object)(object)val2 != (Object)null)
				{
					BuildFamilyVariants(profile, survivorContentSkillGroup, val2, userProfile, hashSet);
				}
				else
				{
					SkillDef val3 = ContentCatalogReflection.ReadUnityObject<SkillDef>((object)val, new string[1] { "skillDef" });
					if ((Object)(object)val3 != (Object)null)
					{
						SurvivorContentSkillVariant survivorContentSkillVariant = BuildSkillVariant(profile, survivorContentSkillGroup, val3, null, 0, isDefault: true, userProfile);
						if (survivorContentSkillVariant != null && hashSet.Add(survivorContentSkillVariant.StableId))
						{
							survivorContentSkillGroup.Variants.Add(survivorContentSkillVariant);
						}
					}
				}
				if (survivorContentSkillGroup.Variants.Count > 0)
				{
					profile.SkillGroups.Add(survivorContentSkillGroup);
				}
			}
			profile.SkillGroups.Sort(delegate(SurvivorContentSkillGroup a, SurvivorContentSkillGroup b)
			{
				int num3 = ((int)a.Slot).CompareTo((int)b.Slot);
				return (num3 != 0) ? num3 : a.ExtraSlotIndex.CompareTo(b.ExtraSlotIndex);
			});
		}

		private static void BuildFamilyVariants(SurvivorContentProfile profile, SurvivorContentSkillGroup group, SkillFamily family, UserProfile userProfile, HashSet<string> stableIds)
		{
			Array array = ReadArrayMember(family, "variants");
			if (array == null)
			{
				return;
			}
			int num = ReadIntegerMember(family, 0, "defaultVariantIndex");
			for (int i = 0; i < array.Length; i++)
			{
				object value = array.GetValue(i);
				if (value == null)
				{
					continue;
				}
				SkillDef val = ContentCatalogReflection.ReadUnityObject<SkillDef>(value, new string[1] { "skillDef" });
				if (!((Object)(object)val == (Object)null))
				{
					UnlockableDef unlockable = ContentCatalogReflection.ReadUnityObject<UnlockableDef>(value, new string[1] { "unlockableDef" });
					SurvivorContentSkillVariant survivorContentSkillVariant = BuildSkillVariant(profile, group, val, unlockable, i, i == num, userProfile);
					if (survivorContentSkillVariant != null && stableIds.Add(survivorContentSkillVariant.StableId))
					{
						group.Variants.Add(survivorContentSkillVariant);
					}
				}
			}
		}

		private static SurvivorContentSkillVariant BuildSkillVariant(SurvivorContentProfile profile, SurvivorContentSkillGroup group, SkillDef skillDef, UnlockableDef unlockable, int variantIndex, bool isDefault, UserProfile userProfile)
		{
			if (profile == null || group == null || (Object)(object)skillDef == (Object)null)
			{
				return null;
			}
			string text = SkillCatalog.GetSkillName(skillDef.skillIndex);
			if (string.IsNullOrWhiteSpace(text))
			{
				text = skillDef.skillName;
			}
			if (string.IsNullOrWhiteSpace(text))
			{
				text = skillDef.skillNameToken;
			}
			if (string.IsNullOrWhiteSpace(text))
			{
				text = group.Slot.ToString() + "_Variant_" + variantIndex;
			}
			string text2 = ResolveToken(skillDef.skillNameToken);
			if (string.IsNullOrWhiteSpace(text2))
			{
				text2 = text;
			}
			string description = ResolveToken(skillDef.skillDescriptionToken);
			ContentSourceRegistry.TryGetSource((Object)(object)skillDef, out var source);
			if (source == null)
			{
				ContentSourceRegistry.TryGetSource((Object)(object)profile.SurvivorDef, out source);
			}
			SurvivorContentSkillVariant survivorContentSkillVariant = new SurvivorContentSkillVariant();
			survivorContentSkillVariant.StableId = "survivorSkill:" + profile.BodyName + ":" + group.Slot.ToString() + ":" + text;
			survivorContentSkillVariant.VariantIndex = variantIndex;
			survivorContentSkillVariant.IsDefaultVariant = isDefault;
			survivorContentSkillVariant.InternalName = text;
			survivorContentSkillVariant.DisplayName = text2;
			survivorContentSkillVariant.Description = description;
			survivorContentSkillVariant.NameToken = skillDef.skillNameToken ?? "";
			survivorContentSkillVariant.SkillDef = skillDef;
			survivorContentSkillVariant.Icon = skillDef.icon;
			survivorContentSkillVariant.Source = source;
			survivorContentSkillVariant.Discovery = ResolveUnlockableDiscovery(unlockable, userProfile, unlockedWhenNull: true);
			survivorContentSkillVariant.Unlock = BuildUnlockInfo(unlockable, userProfile);
			survivorContentSkillVariant.HideIdentityWhenUndiscovered = (Object)(object)unlockable != (Object)null;
			return survivorContentSkillVariant;
		}

		private static void BuildPassive(SurvivorContentProfile profile, SkillLocator locator)
		{
			if (profile == null || (Object)(object)locator == (Object)null)
			{
				return;
			}
			object obj = ReadMemberObject(locator, "passiveSkill");
			if (obj != null && ContentCatalogReflection.ReadBool(obj, true, "enabled"))
			{
				string text = ContentCatalogReflection.ReadString(obj, "skillNameToken", "nameToken");
				string token = ContentCatalogReflection.ReadString(obj, "skillDescriptionToken", "descriptionToken");
				string text2 = ResolveToken(text);
				string text3 = ResolveToken(token);
				Sprite val = ContentCatalogReflection.ReadUnityObject<Sprite>(obj, new string[1] { "icon" });
				if (!string.IsNullOrWhiteSpace(text2) || !string.IsNullOrWhiteSpace(text3) || !((Object)(object)val == (Object)null))
				{
					SurvivorContentSkillGroup survivorContentSkillGroup = new SurvivorContentSkillGroup
					{
						StableId = "survivorSkillGroup:" + profile.BodyName + ":Passive",
						Slot = SurvivorContentSkillSlot.Passive,
						SlotLabel = "Passive"
					};
					survivorContentSkillGroup.Variants.Add(new SurvivorContentSkillVariant
					{
						StableId = "survivorSkill:" + profile.BodyName + ":Passive",
						VariantIndex = 0,
						IsDefaultVariant = true,
						InternalName = (string.IsNullOrWhiteSpace(text) ? "Passive" : text),
						DisplayName = (string.IsNullOrWhiteSpace(text2) ? "Passive" : text2),
						Description = text3,
						NameToken = (text ?? ""),
						Icon = val,
						Discovery = ContentCatalogDiscoveryState.Discovered,
						Unlock = BuildUnlockInfo(null, TryGetLocalProfile()),
						HideIdentityWhenUndiscovered = false
					});
					profile.SkillGroups.Add(survivorContentSkillGroup);
				}
			}
		}

		private static SurvivorContentSkillSlot ResolveSkillSlot(GenericSkill skill, SkillLocator locator)
		{
			if ((Object)(object)skill == (Object)null || (Object)(object)locator == (Object)null)
			{
				return SurvivorContentSkillSlot.Extra;
			}
			if (skill == locator.primary)
			{
				return SurvivorContentSkillSlot.Primary;
			}
			if (skill == locator.secondary)
			{
				return SurvivorContentSkillSlot.Secondary;
			}
			if (skill == locator.utility)
			{
				return SurvivorContentSkillSlot.Utility;
			}
			if (skill == locator.special)
			{
				return SurvivorContentSkillSlot.Special;
			}
			return SurvivorContentSkillSlot.Extra;
		}

		private static void BuildSkins(SurvivorContentProfile profile, GameObject bodyPrefab, CharacterBody body, UserProfile userProfile)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (profile == null || (Object)(object)bodyPrefab == (Object)null || (Object)(object)body == (Object)null)
			{
				return;
			}
			List<SkinDef> bodySkins = GetBodySkins(bodyPrefab, body.bodyIndex);
			HashSet<SkinDef> hashSet = new HashSet<SkinDef>();
			for (int i = 0; i < bodySkins.Count; i++)
			{
				SkinDef val = bodySkins[i];
				if (!((Object)(object)val == (Object)null) && hashSet.Add(val))
				{
					string text = ContentCatalogReflection.ReadString(val, "nameToken");
					string text2 = ResolveToken(text);
					string text3 = ((!string.IsNullOrWhiteSpace(((Object)val).name)) ? ((Object)val).name : ((!string.IsNullOrWhiteSpace(text)) ? text : ("Skin_" + i)));
					if (string.IsNullOrWhiteSpace(text2))
					{
						text2 = text3;
					}
					UnlockableDef val2 = ContentCatalogReflection.ReadUnityObject<UnlockableDef>((object)val, new string[1] { "unlockableDef" });
					Sprite icon = ContentCatalogReflection.ReadUnityObject<Sprite>((object)val, new string[1] { "icon" });
					ContentSourceRegistry.TryGetSource((Object)(object)val, out var source);
					if (source == null)
					{
						ContentSourceRegistry.TryGetSource((Object)(object)profile.SurvivorDef, out source);
					}
					profile.Skins.Add(new SurvivorContentSkinEntry
					{
						StableId = "survivorSkin:" + profile.BodyName + ":" + text3,
						SkinIndex = i,
						InternalName = text3,
						DisplayName = text2,
						NameToken = (text ?? ""),
						SkinDef = val,
						Icon = icon,
						Source = source,
						Discovery = ResolveUnlockableDiscovery(val2, userProfile, unlockedWhenNull: true),
						Unlock = BuildUnlockInfo(val2, userProfile),
						HideIdentityWhenUndiscovered = ((Object)(object)val2 != (Object)null)
					});
				}
			}
		}

		private static List<SkinDef> GetBodySkins(GameObject bodyPrefab, BodyIndex bodyIndex)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			List<SkinDef> list = new List<SkinDef>();
			try
			{
				MethodInfo[] methods = typeof(SkinCatalog).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (methodInfo == null || !string.Equals(methodInfo.Name, "GetBodySkins", StringComparison.Ordinal))
					{
						continue;
					}
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 1 && !(parameters[0].ParameterType != typeof(BodyIndex)))
					{
						object value = methodInfo.Invoke(null, new object[1] { bodyIndex });
						AddSkinEnumerable(value, list);
						if (list.Count > 0)
						{
							break;
						}
					}
				}
			}
			catch
			{
			}
			try
			{
				ModelLocator val = (((Object)(object)bodyPrefab != (Object)null) ? bodyPrefab.GetComponent<ModelLocator>() : null);
				if ((Object)(object)val != (Object)null && (Object)(object)val.modelTransform != (Object)null)
				{
					ModelSkinController component = ((Component)val.modelTransform).GetComponent<ModelSkinController>();
					if ((Object)(object)component != (Object)null)
					{
						object value2 = ReadMemberObject(component, "skins");
						AddSkinEnumerable(value2, list);
					}
				}
			}
			catch
			{
			}
			return list;
		}

		private static void AddSkinEnumerable(object value, List<SkinDef> destination)
		{
			if (value == null || destination == null || !(value is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item in enumerable)
			{
				SkinDef val = (SkinDef)((item is SkinDef) ? item : null);
				if (val != null && (Object)(object)val != (Object)null && !destination.Contains(val))
				{
					destination.Add(val);
				}
			}
		}

		private static SurvivorContentUnlockInfo BuildUnlockInfo(UnlockableDef unlockable, UserProfile userProfile)
		{
			SurvivorContentUnlockInfo survivorContentUnlockInfo = new SurvivorContentUnlockInfo
			{
				UnlockableDef = unlockable,
				HasRequirement = ((Object)(object)unlockable != (Object)null),
				IsUnlocked = ((Object)(object)unlockable == (Object)null || (userProfile != null && userProfile.HasUnlockable(unlockable))),
				UnlockableIdentifier = (((Object)(object)unlockable != (Object)null) ? (unlockable.cachedName ?? "") : ""),
				Source = ResolveUnlockSource(unlockable)
			};
			if ((Object)(object)unlockable == (Object)null)
			{
				return survivorContentUnlockInfo;
			}
			AchievementDef val = FindAchievementForUnlockable(unlockable);
			if (val == null)
			{
				return survivorContentUnlockInfo;
			}
			survivorContentUnlockInfo.AchievementIdentifier = val.identifier ?? "";
			survivorContentUnlockInfo.MissionName = ResolveToken(val.nameToken);
			survivorContentUnlockInfo.MissionDescription = ResolveToken(val.descriptionToken);
			survivorContentUnlockInfo.AchievementIcon = val.achievedIcon;
			return survivorContentUnlockInfo;
		}

		private static AchievementDef FindAchievementForUnlockable(UnlockableDef unlockable)
		{
			if ((Object)(object)unlockable == (Object)null || string.IsNullOrWhiteSpace(unlockable.cachedName))
			{
				return null;
			}
			try
			{
				object obj = ReadStaticMemberObject(typeof(AchievementManager), "achievementDefs");
				if (!(obj is IEnumerable enumerable))
				{
					return null;
				}
				foreach (object item in enumerable)
				{
					AchievementDef val = (AchievementDef)((item is AchievementDef) ? item : null);
					if (val == null || !string.Equals(val.unlockableRewardIdentifier, unlockable.cachedName, StringComparison.Ordinal))
					{
						continue;
					}
					return val;
				}
			}
			catch
			{
			}
			return null;
		}

		private static SurvivorContentUnlockSource ResolveUnlockSource(UnlockableDef unlockable)
		{
			if ((Object)(object)unlockable == (Object)null)
			{
				return SurvivorContentUnlockSource.UnlockedByDefault;
			}
			if (SurvivorUnlockManager.IsCustomUnlock(unlockable))
			{
				return SurvivorContentUnlockSource.UsuManaged;
			}
			return SurvivorContentUnlockSource.Original;
		}

		private static ContentCatalogDiscoveryState ResolveUnlockableDiscovery(UnlockableDef unlockable, UserProfile userProfile, bool unlockedWhenNull)
		{
			if ((Object)(object)unlockable == (Object)null)
			{
				return unlockedWhenNull ? ContentCatalogDiscoveryState.Discovered : ContentCatalogDiscoveryState.NotApplicable;
			}
			if (userProfile == null)
			{
				return ContentCatalogDiscoveryState.Unknown;
			}
			try
			{
				return userProfile.HasUnlockable(unlockable) ? ContentCatalogDiscoveryState.Discovered : ContentCatalogDiscoveryState.Undiscovered;
			}
			catch
			{
				return ContentCatalogDiscoveryState.Unknown;
			}
		}

		private static string ResolveLore(SurvivorDef survivor, CharacterBody body)
		{
			List<string> list = new List<string>();
			string nameToken = ContentCatalogReflection.ReadString(survivor, "displayNameToken");
			string nameToken2 = ContentCatalogReflection.ReadString(body, "baseNameToken");
			AddLoreCandidate(list, nameToken);
			AddLoreCandidate(list, nameToken2);
			for (int i = 0; i < list.Count; i++)
			{
				string text = ResolveToken(list[i]);
				if (!string.IsNullOrWhiteSpace(text))
				{
					return text;
				}
			}
			return "";
		}

		private static void AddLoreCandidate(List<string> candidates, string nameToken)
		{
			if (candidates != null && !string.IsNullOrWhiteSpace(nameToken))
			{
				string text = nameToken.Trim();
				if (text.EndsWith("_NAME", StringComparison.OrdinalIgnoreCase))
				{
					candidates.Add(text.Substring(0, text.Length - "_NAME".Length) + "_LORE");
				}
				if (text.EndsWith("_DISPLAYNAME", StringComparison.OrdinalIgnoreCase))
				{
					candidates.Add(text.Substring(0, text.Length - "_DISPLAYNAME".Length) + "_LORE");
				}
			}
		}

		private static string ResolveLocalizedToken(object source, params string[] memberNames)
		{
			string token = ContentCatalogReflection.ReadString(source, memberNames);
			return ResolveToken(token);
		}

		private static string ResolveToken(string token)
		{
			if (string.IsNullOrWhiteSpace(token))
			{
				return "";
			}
			try
			{
				string text = Language.GetString(token);
				if (string.IsNullOrWhiteSpace(text) || string.Equals(text, token, StringComparison.Ordinal))
				{
					return "";
				}
				return text;
			}
			catch
			{
				return "";
			}
		}

		private static string ResolveSourceIdentifier(SurvivorInfo survivorInfo, SurvivorDef survivorDef)
		{
			if (survivorInfo != null && !string.IsNullOrWhiteSpace(survivorInfo.ContentPackIdentifier))
			{
				return survivorInfo.ContentPackIdentifier;
			}
			if ((Object)(object)survivorDef != (Object)null && ContentSourceRegistry.TryGetSource((Object)(object)survivorDef, out var source) && source != null)
			{
				return source.ContentPackIdentifier ?? "";
			}
			return survivorInfo?.ExpansionName ?? "";
		}

		private static object ReadMemberObject(object target, params string[] memberNames)
		{
			if (target == null || memberNames == null)
			{
				return null;
			}
			Type type = target.GetType();
			foreach (string text in memberNames)
			{
				if (string.IsNullOrWhiteSpace(text))
				{
					continue;
				}
				try
				{
					PropertyInfo property = type.GetProperty(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null && property.CanRead)
					{
						return property.GetValue(target, null);
					}
					FieldInfo field = type.GetField(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						return field.GetValue(target);
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private static object ReadStaticMemberObject(Type type, params string[] memberNames)
		{
			if (type == null || memberNames == null)
			{
				return null;
			}
			foreach (string name in memberNames)
			{
				try
				{
					PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null && property.CanRead)
					{
						return property.GetValue(null, null);
					}
					FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						return field.GetValue(null);
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private static Array ReadArrayMember(object target, params string[] memberNames)
		{
			object obj = ReadMemberObject(target, memberNames);
			return obj as Array;
		}

		private static int ReadIntegerMember(object target, int fallback, params string[] memberNames)
		{
			object obj = ReadMemberObjec