Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of UniversalSurvivorUnlocks v0.3.2
BepInEx/plugins/UniversalSurvivorUnlocks/UniversalSurvivorUnlocks.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.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