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 UltimateLethal CrewMorale v2.2.0
BepInEx/plugins/UltimateLethal.CrewMorale.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("UltimateLethal.CrewMorale")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("UltimateLethal.CrewMorale")] [assembly: AssemblyTitle("UltimateLethal.CrewMorale")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace UltimateLethal.CrewMorale { [BepInPlugin("ultimatelethal.crewmorale", "Ultimate Lethal Crew Morale", "2.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class CrewMoralePlugin : BaseUnityPlugin { private sealed class HudEvent { internal string Title; internal string Detail; internal float Duration; internal bool RevealOutsideShip; internal bool ShipOnly; internal int Priority; } private enum ThreatTier { Minor, Standard, Dangerous, Extreme } public const string Guid = "ultimatelethal.crewmorale"; public const string Name = "Ultimate Lethal Crew Morale"; public const string Version = "2.2.0"; internal static CrewMoralePlugin Instance; internal static ConfigEntry<float> StartingMorale; internal static ConfigEntry<float> MinimumMorale; internal static ConfigEntry<float> MaximumMorale; internal static ConfigEntry<float> DecorationPriceScaleCredits; internal static ConfigEntry<float> DecorationPriceExponent; internal static ConfigEntry<float> DecorationMaxMoraleEach; internal static ConfigEntry<float> DecorationBonusCap; internal static ConfigEntry<float> DeathPenalty; internal static ConfigEntry<float> ReunionReward; internal static ConfigEntry<float> SurvivorReward; internal static ConfigEntry<float> QuotaFulfilledReward; internal static ConfigEntry<float> FullCrewShipRecoveryPerSecond; internal static ConfigEntry<float> OrbitRecoveryPerSecond; internal static ConfigEntry<float> EmptyShipDecayPerSecond; internal static ConfigEntry<float> CriticalInjuryPenalty; internal static ConfigEntry<int> CriticalInjuryHealthThreshold; internal static ConfigEntry<int> CriticalInjuryResetHealth; internal static ConfigEntry<float> ZeroScrapExpeditionPenalty; internal static ConfigEntry<float> LeftBehindPenalty; internal static ConfigEntry<float> MonsterKillReward; internal static ConfigEntry<bool> PlayerCreditRewardsEnabled; internal static ConfigEntry<int> MonsterKillPlayerCredits; internal static ConfigEntry<float> MinorThreatMorale; internal static ConfigEntry<float> StandardThreatMorale; internal static ConfigEntry<float> DangerousThreatMorale; internal static ConfigEntry<float> ExtremeThreatMorale; internal static ConfigEntry<int> MinorThreatPc; internal static ConfigEntry<int> StandardThreatPc; internal static ConfigEntry<int> DangerousThreatPc; internal static ConfigEntry<int> ExtremeThreatPc; internal static ConfigEntry<string> MinorThreatEnemies; internal static ConfigEntry<string> DangerousThreatEnemies; internal static ConfigEntry<string> ExtremeThreatEnemies; internal static ConfigEntry<int> SuccessfulHaulTier1Credits; internal static ConfigEntry<int> SuccessfulHaulTier2Credits; internal static ConfigEntry<int> SuccessfulHaulTier3Credits; internal static ConfigEntry<int> SuccessfulHaulTier4Credits; internal static ConfigEntry<int> ApparatusRecoveryCredits; internal static ConfigEntry<int> FullCrewExtractionCredits; internal static ConfigEntry<int> AgainstOddsCredits; internal static ConfigEntry<int> AgainstOddsHealthThreshold; internal static ConfigEntry<int> DangerousMoonTier1Credits; internal static ConfigEntry<int> DangerousMoonTier2Credits; internal static ConfigEntry<int> DangerousMoonTier3Credits; internal static ConfigEntry<bool> HallucinationsEnabled; internal static ConfigEntry<float> HallucinationMinimumInsanity; internal static ConfigEntry<float> HallucinationCheckSeconds; internal static ConfigEntry<float> HallucinationBaseChance; internal static ConfigEntry<float> HallucinationGlobalCooldown; internal static ConfigEntry<bool> HallucinationAudioEnabled; internal static ConfigEntry<bool> HallucinationVisualEnabled; internal static ConfigEntry<bool> HallucinationEnvironmentEnabled; internal static ConfigEntry<float> HallucinationAudioThreshold; internal static ConfigEntry<float> HallucinationVisualThreshold; internal static ConfigEntry<float> HallucinationEnvironmentThreshold; internal static ConfigEntry<float> HighMoraleSanityGainMultiplier; internal static ConfigEntry<float> LowMoraleSanityGainMultiplier; internal static ConfigEntry<float> HighMoraleRecoveryMultiplier; internal static ConfigEntry<float> LowMoraleRecoveryMultiplier; internal static ConfigEntry<float> TZPRecoveryMultiplier; internal static ConfigEntry<bool> SanityDebuffsEnabled; internal static ConfigEntry<float> SanityDebuffTier1Threshold; internal static ConfigEntry<float> SanityDebuffTier2Threshold; internal static ConfigEntry<float> SanityDebuffTier3Threshold; internal static ConfigEntry<float> SanityDebuffTier1Percent; internal static ConfigEntry<float> SanityDebuffTier2Percent; internal static ConfigEntry<float> SanityDebuffTier3Percent; internal static ConfigEntry<bool> SanityVisualEffectsEnabled; internal static ConfigEntry<float> SanityVisualMinimumInsanity; internal static ConfigEntry<float> SanityVisualMinimumInterval; internal static ConfigEntry<float> SanityVisualMaximumInterval; internal static ConfigEntry<float> SanityVisualMaximumDuration; internal static ConfigEntry<float> SanityVisualIntensity; internal static ConfigEntry<float> SanityVisualHudJitterPixels; internal static ConfigEntry<bool> SanityVisualDebugForceEffect; internal static ConfigEntry<float> ConfidentLguBonus; internal static ConfigEntry<float> ThrivingLguBonus; internal static ConfigEntry<float> PeakLguBonus; internal static ConfigEntry<float> BrokenInteractionPercent; internal static ConfigEntry<float> ShakenInteractionPercent; internal static ConfigEntry<float> UneasyInteractionPercent; internal static ConfigEntry<float> StableInteractionPercent; internal static ConfigEntry<float> ConfidentInteractionPercent; internal static ConfigEntry<float> ThrivingInteractionPercent; internal static ConfigEntry<float> PeakInteractionPercent; internal static ConfigEntry<float> BrokenStaminaPercent; internal static ConfigEntry<float> ShakenStaminaPercent; internal static ConfigEntry<float> UneasyStaminaPercent; internal static ConfigEntry<float> StableStaminaPercent; internal static ConfigEntry<float> ConfidentStaminaPercent; internal static ConfigEntry<float> ThrivingStaminaPercent; internal static ConfigEntry<float> PeakStaminaPercent; internal static ConfigEntry<int> HallucinationAudioWeight; internal static ConfigEntry<int> HallucinationVisualWeight; internal static ConfigEntry<int> HallucinationEnvironmentWeight; internal static ConfigEntry<int> HallucinationDebugForceType; internal static ConfigEntry<bool> LastStandEnabled; internal static ConfigEntry<float> LastStandTriggerMorale; internal static ConfigEntry<float> LastStandAfterMorale; internal static ConfigEntry<float> LastStandProtectionSeconds; internal static ConfigEntry<int> LastStandMinimumHealth; internal static ConfigEntry<float> EventHudSeconds; internal static ConfigEntry<bool> DebugV2Logging; internal static ConfigEntry<bool> ShowHud; internal static ConfigEntry<float> HudX; internal static ConfigEntry<float> HudY; internal static ConfigEntry<float> HudWidth; internal static ConfigEntry<float> HudHeight; internal static ConfigEntry<float> PlayerCreditsOffsetX; internal static ConfigEntry<float> PlayerCreditsOffsetY; internal static ConfigEntry<float> SanityHudRaisePixels; internal static ConfigEntry<bool> UseStatusColors; internal static ConfigEntry<bool> ShowTierDividers; internal static ConfigEntry<bool> FakeInsanityHudEnabled; internal static ConfigEntry<float> FakeInsanityStartThreshold; internal static ConfigEntry<float> FakeInsanityMaxOffset; internal static ConfigEntry<float> FakeInsanityUpdateInterval; internal static ConfigEntry<float> FakeInsanitySpikeChance; internal static ConfigEntry<float> FakeInsanityMaxDuration; internal static ConfigEntry<float> FakeInsanityReturnSpeed; private Harmony _harmony; private float _nextStateTick; private bool _wasFullLivingCrewInShip; private bool _wasLanded; private bool _someoneLeftShipThisExpedition; private bool _expeditionHadDeath; private bool _reunionAwardedThisExpedition; private int _expeditionStartingCrewCount; private readonly HashSet<PlayerControllerB> _expeditionRoster = new HashSet<PlayerControllerB>(); private readonly HashSet<PlayerControllerB> _countedDeadPlayers = new HashSet<PlayerControllerB>(); private readonly HashSet<PlayerControllerB> _criticalInjuryActive = new HashSet<PlayerControllerB>(); private readonly HashSet<int> _shipScrapAtLanding = new HashSet<int>(); private bool _recoveredScrapThisExpedition; private readonly HashSet<int> _creditedLocalScrap = new HashSet<int>(); private int _localRecoveredScrapValue; private bool _localTookFieldDamage; private bool _apparatusRecoveredByLocal; private float _nextHallucinationCheck; private float _hallucinationCooldownUntil; private GameObject _hallucinationVisual; private float _hallucinationVisualUntil; private readonly Dictionary<Light, float> _hallucinationLightIntensity = new Dictionary<Light, float>(); private float _hallucinationLightsUntil; private readonly List<Object> _hallucinationClips = new List<Object>(); private float _fakeInsanityDisplayed; private float _nextFakeInsanityUpdate; private float _fakeInsanityUntil; private bool _fakeInsanityInitialized; private bool _insanityHudPresentationLocated; private readonly Random _sanityVisualRandom = new Random(); private float _nextSanityVisualEffect; private float _sanityVisualEffectStarted; private float _sanityVisualEffectUntil; private float _sanityVisualSeverity; private int _sanityVisualSeed; private bool _quotaStateInitialized; private bool _quotaWasFulfilled; private float _previousDecorationBonus = -1f; private float _eventUntil; private string _eventTitle = ""; private string _eventDetail = ""; private bool _eventRevealOutsideShip; private int _eventPriority; private readonly List<HudEvent> _eventQueue = new List<HudEvent>(); private bool _wasQuotaFailureSequence; private readonly Dictionary<PlayerControllerB, float> _lastStandProtectedUntil = new Dictionary<PlayerControllerB, float>(); private GUIStyle _labelStyle; private GUIStyle _smallStyle; private GUIStyle _pcStyle; private GUIStyle _buffStyle; private GUIStyle _eventTitleStyle; private GUIStyle _eventDetailStyle; private Texture2D _pixel; internal static ManualLogSource PluginLogger { get { CrewMoralePlugin instance = Instance; if (instance == null) { return null; } return ((BaseUnityPlugin)instance).Logger; } } internal static float Morale { get; private set; } = 55f; internal static float DecorationBaseline { get; private set; } = 0f; internal static int DecorationCount { get; private set; } = 0; private void Awake() { //IL_107c: Unknown result type (might be due to invalid IL or missing references) //IL_1086: Expected O, but got Unknown Instance = this; StartingMorale = ((BaseUnityPlugin)this).Config.Bind<float>("Morale", "StartingMorale", 55f, "Morale at the beginning of a session."); MinimumMorale = ((BaseUnityPlugin)this).Config.Bind<float>("Morale", "MinimumMorale", 0f, "Minimum morale."); MaximumMorale = ((BaseUnityPlugin)this).Config.Bind<float>("Morale", "MaximumMorale", 100f, "Maximum morale."); DecorationPriceScaleCredits = ((BaseUnityPlugin)this).Config.Bind<float>("Decorations", "PriceScaleCredits", 200f, "Price divisor used by the decoration morale curve."); DecorationPriceExponent = ((BaseUnityPlugin)this).Config.Bind<float>("Decorations", "PriceExponent", 1.15f, "Exponent used by the decoration morale curve. Values above 1 make expensive decorations more efficient."); DecorationMaxMoraleEach = ((BaseUnityPlugin)this).Config.Bind<float>("Decorations", "MaxMoralePerDecoration", 10f, "Maximum morale a single decoration can contribute regardless of price."); DecorationBonusCap = ((BaseUnityPlugin)this).Config.Bind<float>("Decorations", "DecorationBonusCap", 20f, "Maximum total baseline morale supplied by all decorations."); DeathPenalty = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "DeathPenalty", 20f, "Immediate morale loss per crew death."); ReunionReward = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "PerfectReunionReward", 10f, "Morale gained when everyone who started the expedition returns to the ship alive after someone has left it."); SurvivorReward = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "SurvivorRewardPerPlayer", 2f, "Morale gained per surviving member of the expedition starting roster when the expedition ends."); QuotaFulfilledReward = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "QuotaFulfilledReward", 15f, "Morale gained once when the current quota becomes fulfilled."); FullCrewShipRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "FullCrewShipRecoveryPerSecond", 0.004166667f, "Passive morale recovery while the full living crew is together inside the ship (+0.25 morale/minute before decor scaling)."); OrbitRecoveryPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "OrbitRecoveryPerSecond", 0.004166667f, "Additional passive morale recovery while the ship is safely in orbit (+0.25 morale/minute before decor scaling)."); EmptyShipDecayPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "EmptyShipDecayPerSecond", 0f, "Legacy setting retained for config compatibility. TEST 2 disables passive empty-ship morale decay."); CriticalInjuryPenalty = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "CriticalInjuryPenalty", 5f, "Morale lost when a living crew member first drops into critical condition."); CriticalInjuryHealthThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("Events", "CriticalInjuryHealthThreshold", 25, "Health at or below which a living player enters the critical-injury morale state."); CriticalInjuryResetHealth = ((BaseUnityPlugin)this).Config.Bind<int>("Events", "CriticalInjuryResetHealth", 50, "Health above which that player can trigger a future critical-injury morale event again."); ZeroScrapExpeditionPenalty = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "ZeroScrapExpeditionPenalty", 10f, "Morale lost when an expedition ends with survivors but no newly recovered scrap brought aboard."); LeftBehindPenalty = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "LeftBehindPenalty", 15f, "Morale lost per living crew member left outside when the ship departs."); MonsterKillReward = ((BaseUnityPlugin)this).Config.Bind<float>("Events", "MonsterKillReward", 10f, "Morale gained when a player directly kills an enemy, awarded once per enemy instance."); PlayerCreditRewardsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("V2 - LGU Player Credits", "Enabled", true, "Award LGU Player Credits from Crew Morale gameplay events."); MonsterKillPlayerCredits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "MonsterKillCredits", 5, "Legacy flat monster-kill PC value retained for config compatibility; threat tiers are authoritative."); MinorThreatMorale = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Threat Rewards", "MinorMorale", 2f, "Morale for a Minor threat kill."); MinorThreatPc = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - Threat Rewards", "MinorPC", 1, "Personal PC for a Minor threat kill."); StandardThreatMorale = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Threat Rewards", "StandardMorale", 5f, "Morale for a Standard/unclassified threat kill."); StandardThreatPc = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - Threat Rewards", "StandardPC", 2, "Personal PC for a Standard/unclassified threat kill."); DangerousThreatMorale = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Threat Rewards", "DangerousMorale", 8f, "Morale for a Dangerous threat kill."); DangerousThreatPc = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - Threat Rewards", "DangerousPC", 4, "Personal PC for a Dangerous threat kill."); ExtremeThreatMorale = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Threat Rewards", "ExtremeMorale", 12f, "Morale for an Extreme threat kill."); ExtremeThreatPc = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - Threat Rewards", "ExtremePC", 6, "Personal PC for an Extreme threat kill."); MinorThreatEnemies = ((BaseUnityPlugin)this).Config.Bind<string>("V2 - Threat Rewards", "MinorEnemies", "Hoarding bug,Centipede,Snare flea,Spore lizard,Manticoil,Roaming locust", "Comma-separated Minor enemy names."); DangerousThreatEnemies = ((BaseUnityPlugin)this).Config.Bind<string>("V2 - Threat Rewards", "DangerousEnemies", "Flowerman,Bracken,Nutcracker,Butler,Masked,Thumper,Crawler,Baboon hawk", "Comma-separated Dangerous enemy names."); ExtremeThreatEnemies = ((BaseUnityPlugin)this).Config.Bind<string>("V2 - Threat Rewards", "ExtremeEnemies", "Old Bird,ForestGiant,Forest Keeper,Earth Leviathan,Jester", "Comma-separated Extreme enemy names. Unlisted enemies are Standard."); SuccessfulHaulTier1Credits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "SuccessfulHaul100Credits", 1, "PC for personally recovering at least 100 credits of unique scrap."); SuccessfulHaulTier2Credits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "SuccessfulHaul200Credits", 2, "PC for personally recovering at least 200 credits of unique scrap."); SuccessfulHaulTier3Credits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "SuccessfulHaul350Credits", 3, "PC for personally recovering at least 350 credits of unique scrap."); SuccessfulHaulTier4Credits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "SuccessfulHaul500Credits", 5, "PC for personally recovering at least 500 credits of unique scrap."); ApparatusRecoveryCredits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "ApparatusRecoveryCredits", 3, "PC for personally recovering the apparatus to the ship."); FullCrewExtractionCredits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "FullCrewExtractionCredits", 2, "PC awarded to each surviving player when the full starting crew extracts alive."); AgainstOddsCredits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "AgainstTheOddsCredits", 3, "PC for extracting at critical health after taking damage away from the ship."); AgainstOddsHealthThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "AgainstTheOddsHealthThreshold", 25, "Maximum extraction health for Against the Odds."); DangerousMoonTier1Credits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "DangerousMoonTier1Credits", 1, "PC for surviving a moderately dangerous moon (risk C/B)."); DangerousMoonTier2Credits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "DangerousMoonTier2Credits", 2, "PC for surviving a high-danger moon (risk A/S)."); DangerousMoonTier3Credits = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - LGU Player Credits", "DangerousMoonTier3Credits", 3, "PC for surviving an extreme moon (risk S+/X or equivalent)."); HallucinationsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("V2 - Hallucinations", "Enabled", true, "Enable client-side psychological hallucination events driven by InsanityMod."); HallucinationMinimumInsanity = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Hallucinations", "MinimumInsanity", 45f, "Insanity below this value cannot trigger custom hallucinations."); HallucinationCheckSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Hallucinations", "CheckSeconds", 5f, "Minimum seconds between custom hallucination opportunities. Opportunities are driven by InsanityMod insanity updates, not a separate Crew Morale scheduler."); HallucinationBaseChance = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Hallucinations", "BaseChance", 0.12f, "Base chance per InsanityMod-driven opportunity at maximum insanity before morale scaling."); HallucinationGlobalCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Hallucinations", "GlobalCooldownSeconds", 18f, "Minimum time between custom hallucinations."); HallucinationAudioEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("V2 - Hallucinations", "AudioEnabled", true, "Allow local phantom spatial audio events."); HallucinationVisualEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("V2 - Hallucinations", "VisualEnabled", true, "Allow local peripheral/doorway shadow events."); HallucinationEnvironmentEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("V2 - Hallucinations", "EnvironmentEnabled", true, "Allow local camera/environment disturbance events."); HallucinationAudioThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Hallucinations", "AudioMinimumInsanity", 45f, "Minimum insanity for phantom audio."); HallucinationVisualThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Hallucinations", "VisualMinimumInsanity", 58f, "Minimum insanity for shadow/silhouette events."); HallucinationEnvironmentThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Hallucinations", "EnvironmentMinimumInsanity", 72f, "Minimum insanity for environmental disturbances."); HallucinationAudioWeight = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - Hallucinations", "AudioWeight", 2, "Relative selection weight for phantom-audio events when eligible."); HallucinationVisualWeight = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - Hallucinations", "VisualWeight", 1, "Relative selection weight for shadow/silhouette events when eligible."); HallucinationEnvironmentWeight = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - Hallucinations", "EnvironmentWeight", 1, "Relative selection weight for environmental events when eligible."); HallucinationDebugForceType = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - Hallucinations", "DebugForceEventType", 0, "Testing only: 0=random, 1=audio, 2=visual, 3=environment. Use with BaseChance=1 and a short cooldown to verify an event category."); MigrateLegacyBalanceValues(); HighMoraleSanityGainMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Sanity", "HighMoraleSanityGainMultiplier", 0.15f, "Legacy endpoint retained for compatibility. v2.0.5 uses status-driven sanity gain; Peak is x0.15."); LowMoraleSanityGainMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Sanity", "LowMoraleSanityGainMultiplier", 3f, "Legacy endpoint retained for compatibility. v2.0.5 uses status-driven sanity gain; Broken is x3.00."); HighMoraleRecoveryMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Sanity", "HighMoraleRecoveryMultiplier", 1.6f, "At 100 morale, negative insanity change (recovery) is multiplied by this."); LowMoraleRecoveryMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Sanity", "LowMoraleRecoveryMultiplier", 0.65f, "At 0 morale, negative insanity change (recovery) is multiplied by this."); TZPRecoveryMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Sanity", "TZPRecoveryMultiplier", 1.765f, "Multiplier applied specifically to InsanityMod TZP recovery. The default changes a standard full effect from roughly 17 to 30 raw insanity points before morale scaling."); SanityDebuffsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("V2.2 - Sanity Debuffs", "Enabled", true, "Add progressive insanity penalties to interaction speed and stamina efficiency. These stack directly with morale and are shown in the existing INT/STAM HUD values."); SanityDebuffTier1Threshold = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Debuffs", "Tier1Threshold", 60f, "Insanity at which the first sanity penalty begins."); SanityDebuffTier2Threshold = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Debuffs", "Tier2Threshold", 75f, "Insanity at which the second sanity penalty begins."); SanityDebuffTier3Threshold = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Debuffs", "Tier3Threshold", 90f, "Insanity at which the critical sanity penalty begins."); SanityDebuffTier1Percent = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Debuffs", "Tier1Percent", -10f, "Interaction and stamina percentage added at Tier 1 insanity."); SanityDebuffTier2Percent = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Debuffs", "Tier2Percent", -25f, "Interaction and stamina percentage added at Tier 2 insanity."); SanityDebuffTier3Percent = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Debuffs", "Tier3Percent", -50f, "Interaction and stamina percentage added at critical insanity. With default Broken morale, the natural combined total is -150%."); SanityVisualEffectsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("V2.2 - Sanity Visuals", "Enabled", true, "Enable short local-only screen/HUD interference pulses driven by real InsanityMod insanity. Disable this for a clean HUD."); SanityVisualMinimumInsanity = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Visuals", "MinimumInsanity", 60f, "Real insanity at which intermittent screen/HUD interference can begin."); SanityVisualMinimumInterval = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Visuals", "MinimumIntervalSeconds", 5f, "Shortest gap between interference pulses at maximum insanity."); SanityVisualMaximumInterval = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Visuals", "MaximumIntervalSeconds", 24f, "Longest gap between interference pulses near the starting threshold."); SanityVisualMaximumDuration = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Visuals", "MaximumDurationSeconds", 0.9f, "Maximum duration of a high-insanity interference pulse."); SanityVisualIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Visuals", "Intensity", 1f, "Overall screen/HUD interference strength. Use 0 for no visible effect, 0.5 for reduced intensity or 1 for the intended default."); SanityVisualHudJitterPixels = ((BaseUnityPlugin)this).Config.Bind<float>("V2.2 - Sanity Visuals", "MoraleHudJitterPixels", 6f, "Maximum brief movement of the Crew Morale HUD during severe interference pulses."); SanityVisualDebugForceEffect = ((BaseUnityPlugin)this).Config.Bind<bool>("V2.2 - Sanity Visuals", "DebugForceEffect", false, "Testing only: continuously trigger the strongest interference pulse regardless of current insanity."); ConfidentLguBonus = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - LGU", "ConfidentEffectivenessBonus", 0.05f, "Extra effectiveness applied to supported active LGU effects at 70-84 morale."); ThrivingLguBonus = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - LGU", "ThrivingEffectivenessBonus", 0.1f, "Extra effectiveness applied to supported active LGU effects at 85-99 morale."); PeakLguBonus = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - LGU", "PeakEffectivenessBonus", 0.15f, "Extra effectiveness applied to supported active LGU effects at 100 morale."); BrokenInteractionPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Interaction Speed", "BrokenPercent", -100f, "Interaction contribution at Broken morale. Negative totals use hold-duration scaling, so values below -100 remain meaningful without reaching zero speed."); ShakenInteractionPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Interaction Speed", "ShakenPercent", -50f, "Interaction speed change at Shaken morale."); UneasyInteractionPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Interaction Speed", "UneasyPercent", -25f, "Interaction speed change at Uneasy morale."); StableInteractionPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Interaction Speed", "StablePercent", 0f, "Interaction speed change at Stable morale."); ConfidentInteractionPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Interaction Speed", "ConfidentPercent", 25f, "Interaction speed change at Confident morale."); ThrivingInteractionPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Interaction Speed", "ThrivingPercent", 50f, "Interaction speed change at Thriving morale."); PeakInteractionPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Interaction Speed", "PeakPercent", 100f, "Interaction speed change at Peak morale."); BrokenStaminaPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Stamina Efficiency", "BrokenPercent", -100f, "Stamina efficiency change at Broken morale. Negative values increase drain and slow recovery."); ShakenStaminaPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Stamina Efficiency", "ShakenPercent", -50f, "Stamina efficiency change at Shaken morale."); UneasyStaminaPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Stamina Efficiency", "UneasyPercent", -25f, "Stamina efficiency change at Uneasy morale."); StableStaminaPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Stamina Efficiency", "StablePercent", 0f, "Stamina efficiency change at Stable morale."); ConfidentStaminaPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Stamina Efficiency", "ConfidentPercent", 25f, "Stamina efficiency change at Confident morale."); ThrivingStaminaPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Stamina Efficiency", "ThrivingPercent", 50f, "Stamina efficiency change at Thriving morale."); PeakStaminaPercent = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Stamina Efficiency", "PeakPercent", 100f, "Stamina efficiency change at Peak morale. Positive values reduce drain and improve recovery."); MigrateV22EffectDefaults(); LastStandEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("V2 - Last Stand", "Enabled", true, "At peak morale, prevent one lethal event and consume peak morale."); LastStandTriggerMorale = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Last Stand", "TriggerMorale", 100f, "Morale required for Last Stand to be ready."); LastStandAfterMorale = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Last Stand", "MoraleAfterActivation", 70f, "Morale immediately after Last Stand activates."); LastStandProtectionSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - Last Stand", "ProtectionSeconds", 3f, "Invulnerability grace period after Last Stand activates."); LastStandMinimumHealth = ((BaseUnityPlugin)this).Config.Bind<int>("V2 - Last Stand", "MinimumHealthAfterSave", 20, "Minimum health restored when Last Stand prevents death."); EventHudSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("V2 - HUD", "EventPopupSeconds", 5f, "How long contextual morale event HUD remains visible outside the ship."); DebugV2Logging = ((BaseUnityPlugin)this).Config.Bind<bool>("V2 - Debug", "VerboseLogging", true, "Log LGU hooks, Last Stand triggers/protection, and v2 integration state."); ShowHud = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowMoraleBar", true, "Show Crew Morale while the local player is in the ship."); HudX = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "X", 0.5f, "Horizontal screen anchor (0 to 1)."); HudY = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Y", 0.76f, "Vertical screen anchor (0 to 1)."); HudWidth = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Width", 260f, "Morale bar width in pixels."); HudHeight = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Height", 14f, "Morale bar height in pixels."); PlayerCreditsOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "PCOffsetX", -180f, "Fine horizontal offset for PC on the far-right side of the Decor/Sanity information row."); PlayerCreditsOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "PCOffsetY", 0f, "Fine vertical offset for PC on the Decor/Sanity information row."); SanityHudRaisePixels = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "SanityHudRaisePixels", 360f, "Moves InsanityMod's bottom-right sanity ring upward by this many pixels."); UseStatusColors = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "UseStatusColors", true, "Color the morale status text and current bar fill by morale tier."); ShowTierDividers = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowTierDividers", true, "Draw status-threshold dividers on the morale bar at 20, 35, 50, 70 and 85."); FakeInsanityHudEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Insanity HUD Fake Values", "Enabled", true, "At high insanity, make InsanityMod's displayed HUD value drift or lie without changing the real insanity value."); FakeInsanityStartThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Insanity HUD Fake Values", "StartThreshold", 60f, "Real insanity where HUD deception begins."); FakeInsanityMaxOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Insanity HUD Fake Values", "MaxFakeOffset", 20f, "Maximum displayed-value deviation near 100 insanity."); FakeInsanityUpdateInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Insanity HUD Fake Values", "UpdateInterval", 0.35f, "Seconds between fake-value target changes."); FakeInsanitySpikeChance = ((BaseUnityPlugin)this).Config.Bind<float>("Insanity HUD Fake Values", "SpikeChance", 15f, "Percent chance per update for a stronger false spike at high insanity."); FakeInsanityMaxDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Insanity HUD Fake Values", "MaxFakeDuration", 1.5f, "Maximum time a fake target is held before drifting back."); FakeInsanityReturnSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Insanity HUD Fake Values", "ReturnSpeed", 18f, "Displayed insanity points per second used when drifting back toward the real value."); Morale = Clamp(StartingMorale.Value); _harmony = new Harmony("ultimatelethal.crewmorale"); _harmony.PatchAll(); TryPatchInsanityMod(); InsanityEventThresholdIntegration.TryPatch(_harmony, ((BaseUnityPlugin)this).Logger); LGUIntegration.TryPatch(_harmony, ((BaseUnityPlugin)this).Logger); InteractionSpeedIntegration.TryPatch(_harmony, ((BaseUnityPlugin)this).Logger); StaminaEfficiencyIntegration.TryPatch(_harmony, ((BaseUnityPlugin)this).Logger); MoraleResetIntegration.TryPatch(_harmony, ((BaseUnityPlugin)this).Logger); LGUCurrencyIntegration.Initialize(((BaseUnityPlugin)this).Logger); EnemyKillIntegration.TryPatch(_harmony, ((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Starting morale: {2:0}", "Ultimate Lethal Crew Morale", "2.2.0", Morale)); } private void MigrateLegacyBalanceValues() { if (Mathf.Approximately(DeathPenalty.Value, 12f)) { DeathPenalty.Value = 18f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated legacy DeathPenalty 12 -> 18."); } if (Mathf.Approximately(FullCrewShipRecoveryPerSecond.Value, 0.2f) || Mathf.Approximately(FullCrewShipRecoveryPerSecond.Value, 0.07f) || Mathf.Approximately(FullCrewShipRecoveryPerSecond.Value, 0.03f)) { FullCrewShipRecoveryPerSecond.Value = 0.00833333f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated legacy full-crew passive recovery -> +0.5 morale/minute."); } if (Mathf.Approximately(OrbitRecoveryPerSecond.Value, 0.35f) || Mathf.Approximately(OrbitRecoveryPerSecond.Value, 0.12f) || Mathf.Approximately(OrbitRecoveryPerSecond.Value, 0.05f)) { OrbitRecoveryPerSecond.Value = 0.0166667f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated legacy orbit passive recovery -> +1 morale/minute."); } if (Mathf.Approximately(DeathPenalty.Value, 18f)) { DeathPenalty.Value = 20f; } if (Mathf.Approximately(FullCrewShipRecoveryPerSecond.Value, 0.00833333f)) { FullCrewShipRecoveryPerSecond.Value = 0.004166667f; } if (Mathf.Approximately(OrbitRecoveryPerSecond.Value, 0.0166667f)) { OrbitRecoveryPerSecond.Value = 0.004166667f; } if (EmptyShipDecayPerSecond.Value > 0f) { EmptyShipDecayPerSecond.Value = 0f; } } private void MigrateV22EffectDefaults() { if (Mathf.Approximately(BrokenInteractionPercent.Value, -15f) && Mathf.Approximately(ShakenInteractionPercent.Value, -10f) && Mathf.Approximately(UneasyInteractionPercent.Value, -5f) && Mathf.Approximately(StableInteractionPercent.Value, 0f) && Mathf.Approximately(ConfidentInteractionPercent.Value, 5f) && Mathf.Approximately(ThrivingInteractionPercent.Value, 10f) && Mathf.Approximately(PeakInteractionPercent.Value, 15f)) { BrokenInteractionPercent.Value = -100f; ShakenInteractionPercent.Value = -50f; UneasyInteractionPercent.Value = -25f; StableInteractionPercent.Value = 0f; ConfidentInteractionPercent.Value = 25f; ThrivingInteractionPercent.Value = 50f; PeakInteractionPercent.Value = 100f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated v2.1 interaction defaults to -100/-50/-25/0/+25/+50/+100."); } if (Mathf.Approximately(BrokenStaminaPercent.Value, -15f) && Mathf.Approximately(ShakenStaminaPercent.Value, -10f) && Mathf.Approximately(UneasyStaminaPercent.Value, -5f) && Mathf.Approximately(StableStaminaPercent.Value, 0f) && Mathf.Approximately(ConfidentStaminaPercent.Value, 5f) && Mathf.Approximately(ThrivingStaminaPercent.Value, 10f) && Mathf.Approximately(PeakStaminaPercent.Value, 15f)) { BrokenStaminaPercent.Value = -100f; ShakenStaminaPercent.Value = -50f; UneasyStaminaPercent.Value = -25f; StableStaminaPercent.Value = 0f; ConfidentStaminaPercent.Value = 25f; ThrivingStaminaPercent.Value = 50f; PeakStaminaPercent.Value = 100f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated v2.1 stamina defaults to -100/-50/-25/0/+25/+50/+100."); } } private void Start() { _expeditionRoster.Clear(); _countedDeadPlayers.Clear(); } private void Update() { if (!(Time.unscaledTime < _nextStateTick)) { float num = 0.25f; _nextStateTick = Time.unscaledTime + num; AdjustInsanityHudPosition(); CleanupExpiredLastStandProtection(); SaveScopedPersistence.Tick(); if (!((Object)(object)StartOfRound.Instance == (Object)null)) { RefreshDecorationBaseline(); ApplyWorldState(num); UpdateQuotaFailureReset(); ProcessHudEventQueue(); } } } private void LateUpdate() { UpdateInsanityHudDeception(); UpdateSanityVisualEffects(); } private void UpdateSanityVisualEffects() { ConfigEntry<bool> sanityVisualEffectsEnabled = SanityVisualEffectsEnabled; if (sanityVisualEffectsEnabled == null || !sanityVisualEffectsEnabled.Value) { _sanityVisualEffectUntil = 0f; _nextSanityVisualEffect = 0f; return; } PlayerControllerB val = null; try { val = GameNetworkManager.Instance?.localPlayerController; } catch { } if ((Object)(object)val == (Object)null || val.isPlayerDead) { _sanityVisualEffectUntil = 0f; return; } try { if ((Object)(object)val.quickMenuManager != (Object)null && val.quickMenuManager.isMenuOpen) { _sanityVisualEffectUntil = 0f; return; } } catch { } bool flag = SanityVisualDebugForceEffect?.Value ?? false; if (!InsanityManagerBridge.TryReadInsanity(out var value)) { if (!flag) { return; } value = 100f; } float num = Mathf.Clamp(SanityVisualMinimumInsanity?.Value ?? 60f, 0f, 99f); if (!flag && value < num) { _sanityVisualEffectUntil = 0f; _nextSanityVisualEffect = 0f; return; } float num2 = (flag ? 1f : Mathf.InverseLerp(num, 100f, value)); float unscaledTime = Time.unscaledTime; if (flag && unscaledTime >= _sanityVisualEffectUntil) { BeginSanityVisualEffect(unscaledTime, 1f, 0.75f); } else if (!flag && unscaledTime >= _sanityVisualEffectUntil && unscaledTime >= _nextSanityVisualEffect) { float num3 = Mathf.Max(1f, SanityVisualMinimumInterval?.Value ?? 5f); float num4 = Mathf.Lerp(Mathf.Max(num3, SanityVisualMaximumInterval?.Value ?? 24f), num3, num2) * RandomRange(0.82f, 1.18f); float num5 = Mathf.Max(0.15f, SanityVisualMaximumDuration?.Value ?? 0.9f); float num6 = Mathf.Lerp(0.2f, num5, num2) * RandomRange(0.82f, 1.08f); BeginSanityVisualEffect(unscaledTime, Mathf.Lerp(0.28f, 1f, num2), num6); _nextSanityVisualEffect = unscaledTime + num6 + num4; } } private void BeginSanityVisualEffect(float now, float severity, float duration) { _sanityVisualEffectStarted = now; _sanityVisualEffectUntil = now + Mathf.Max(0.12f, duration); _sanityVisualSeverity = Mathf.Clamp01(severity); _sanityVisualSeed = _sanityVisualRandom.Next(1, int.MaxValue); ConfigEntry<bool> debugV2Logging = DebugV2Logging; if (debugV2Logging != null && debugV2Logging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"SANITY VISUAL: strength {_sanityVisualSeverity:0.00} | duration {_sanityVisualEffectUntil - now:0.00}s"); } } private float RandomRange(float minimum, float maximum) { return Mathf.Lerp(minimum, maximum, (float)_sanityVisualRandom.NextDouble()); } private float CurrentSanityVisualStrength() { float unscaledTime = Time.unscaledTime; if (unscaledTime >= _sanityVisualEffectUntil) { return 0f; } float num = Mathf.Max(0.01f, _sanityVisualEffectUntil - _sanityVisualEffectStarted); float num2 = Mathf.Sin(Mathf.Clamp01((unscaledTime - _sanityVisualEffectStarted) / num) * MathF.PI); float num3 = Mathf.Max(0f, SanityVisualIntensity?.Value ?? 1f); return Mathf.Clamp(_sanityVisualSeverity * num2 * num3, 0f, 1.5f); } private float SanityVisualNoise(int index) { float num = Mathf.Sin((float)(_sanityVisualSeed + index * 92821) * 0.0001234f) * 43758.547f; return num - Mathf.Floor(num); } private void DrawSanityScreenEffect() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_03a5: Unknown result type (might be due to invalid IL or missing references) float num = CurrentSanityVisualStrength(); if (num <= 0.001f) { return; } EnsureGui(); Color color = GUI.color; int depth = GUI.depth; GUI.depth = -1000; float num2 = Screen.width; float num3 = Screen.height; float num4 = Mathf.Lerp(18f, 82f, Mathf.Clamp01(num)); GUI.color = new Color(0.04f, 0.2f, 0.22f, 0.035f * num); GUI.DrawTexture(new Rect(0f, 0f, num2, num3), (Texture)(object)_pixel); GUI.color = new Color(0f, 0f, 0f, 0.16f * num); GUI.DrawTexture(new Rect(0f, 0f, num2, num4), (Texture)(object)_pixel); GUI.DrawTexture(new Rect(0f, num3 - num4, num2, num4), (Texture)(object)_pixel); GUI.DrawTexture(new Rect(0f, num4, num4 * 0.65f, num3 - num4 * 2f), (Texture)(object)_pixel); GUI.DrawTexture(new Rect(num2 - num4 * 0.65f, num4, num4 * 0.65f, num3 - num4 * 2f), (Texture)(object)_pixel); int num5 = 3 + Mathf.RoundToInt(7f * Mathf.Clamp01(num)); for (int i = 0; i < num5; i++) { float num6 = SanityVisualNoise(i) * num3; float num7 = Mathf.Lerp(1f, 10f, SanityVisualNoise(i + 31)) * Mathf.Lerp(0.5f, 1f, num); float num8 = SanityVisualNoise(i + 63) * num2 * 0.18f; float num9 = Mathf.Max(20f, num2 - num8 * 2f); float num10 = Mathf.Lerp(2f, 18f, SanityVisualNoise(i + 97)) * num; GUI.color = new Color(0f, 0f, 0f, (0.08f + SanityVisualNoise(i + 121) * 0.1f) * num); GUI.DrawTexture(new Rect(num8 + num10, num6, num9, num7), (Texture)(object)_pixel); GUI.color = new Color(0.15f, 0.95f, 1f, 0.065f * num); GUI.DrawTexture(new Rect(num8 - num10, num6 - 1f, num9 * 0.72f, 1f + num7 * 0.35f), (Texture)(object)_pixel); GUI.color = new Color(1f, 0.12f, 0.28f, 0.052f * num); GUI.DrawTexture(new Rect(num8 + num10 * 1.4f, num6 + num7, num9 * 0.58f, 1f + num7 * 0.25f), (Texture)(object)_pixel); } if (num >= 0.55f) { int num11 = 2 + Mathf.RoundToInt(4f * Mathf.Clamp01(num)); for (int j = 0; j < num11; j++) { float num12 = Mathf.Lerp(45f, num2 * 0.3f, SanityVisualNoise(j + 211)); float num13 = Mathf.Lerp(2f, 14f, SanityVisualNoise(j + 243)); float num14 = SanityVisualNoise(j + 277) * Mathf.Max(1f, num2 - num12); float num15 = SanityVisualNoise(j + 309) * num3; GUI.color = new Color(0f, 0f, 0f, 0.18f * num); GUI.DrawTexture(new Rect(num14, num15, num12, num13), (Texture)(object)_pixel); } } GUI.color = color; GUI.depth = depth; } private void UpdateInsanityHudDeception() { ConfigEntry<bool> fakeInsanityHudEnabled = FakeInsanityHudEnabled; if (fakeInsanityHudEnabled == null || !fakeInsanityHudEnabled.Value || !InsanityManagerBridge.TryReadInsanity(out var value)) { return; } GameObject val = GameObject.Find("InsanityHud_Root"); if ((Object)(object)val == (Object)null) { return; } float num = Mathf.Clamp(FakeInsanityStartThreshold?.Value ?? 60f, 0f, 99f); if (value < num) { _fakeInsanityInitialized = false; ApplyInsanityHudPresentation(val, value); return; } float num2 = Mathf.InverseLerp(num, 100f, value); if (!_fakeInsanityInitialized) { _fakeInsanityDisplayed = value; _fakeInsanityInitialized = true; _nextFakeInsanityUpdate = 0f; _fakeInsanityUntil = 0f; } if (Time.unscaledTime >= _nextFakeInsanityUpdate) { float num3 = Mathf.Lerp(3f, Mathf.Max(3f, FakeInsanityMaxOffset?.Value ?? 20f), num2); float num4 = Random.Range(0f - num3, num3); float num5 = Mathf.Clamp01((FakeInsanitySpikeChance?.Value ?? 15f) / 100f) * num2; if (value >= 75f && Random.value < num5) { num4 = ((Random.value < 0.35f) ? (-1f) : 1f) * Random.Range(num3 * 0.65f, num3); } float num6 = ((value >= 90f) ? 70f : ((value >= 75f) ? 55f : Mathf.Max(0f, num - 8f))); _fakeInsanityDisplayed = Mathf.Clamp(value + num4, num6, 100f); float num7 = Mathf.Max(0.08f, FakeInsanityUpdateInterval?.Value ?? 0.35f); _nextFakeInsanityUpdate = Time.unscaledTime + num7; float num8 = Mathf.Max(0.25f, FakeInsanityMaxDuration?.Value ?? 1.5f); _fakeInsanityUntil = Time.unscaledTime + Random.Range(0.2f, num8); } if (Time.unscaledTime >= _fakeInsanityUntil) { float num9 = Mathf.Max(1f, FakeInsanityReturnSpeed?.Value ?? 18f); _fakeInsanityDisplayed = Mathf.MoveTowards(_fakeInsanityDisplayed, value, num9 * Time.unscaledDeltaTime); } ApplyInsanityHudPresentation(val, _fakeInsanityDisplayed); } private void ApplyInsanityHudPresentation(GameObject root, float displayed) { bool flag = false; try { Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } Type type = ((object)val).GetType(); PropertyInfo property = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanRead && property.CanWrite && property.PropertyType == typeof(string)) { string text = property.GetValue(val) as string; if (LooksLikeNumericHudValue(text)) { string text2 = text.Trim(); bool flag2 = text2.EndsWith("%", StringComparison.Ordinal); bool flag3 = (flag2 ? text2.Substring(0, text2.Length - 1).Trim() : text2).Contains("."); string value = displayed.ToString(flag3 ? "0.0" : "0") + (flag2 ? "%" : ""); property.SetValue(val, value); flag = true; } } PropertyInfo property2 = type.GetProperty("fillAmount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property2 != null && property2.CanWrite && property2.PropertyType == typeof(float)) { string text3 = ((((Object)val).name != null) ? ((Object)val).name.ToLowerInvariant() : ""); if (text3.Contains("fill") || text3.Contains("progress")) { property2.SetValue(val, Mathf.Clamp01(displayed / 100f)); flag = true; } } } } catch (Exception ex) { ConfigEntry<bool> debugV2Logging = DebugV2Logging; if (debugV2Logging != null && debugV2Logging.Value) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("INSANITY HUD deception note: " + ex.Message)); } } if (flag && !_insanityHudPresentationLocated) { _insanityHudPresentationLocated = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Insanity HUD deception located a presentation value/fill. Real InsanityMod insanity remains untouched."); } } private static bool LooksLikeNumericHudValue(string value) { if (string.IsNullOrWhiteSpace(value)) { return false; } string text = value.Trim(); if (text.EndsWith("%", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - 1).Trim(); } if (text.Length == 0 || text.Length > 7) { return false; } bool flag = false; string text2 = text; foreach (char c in text2) { if (char.IsDigit(c)) { flag = true; } else if (c != '.' && c != '-' && c != '+') { return false; } } if (!flag) { return false; } if (float.TryParse(text, out var result) && result >= -1f) { return result <= 101f; } return false; } private static void AdjustInsanityHudPosition() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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_006f: Unknown result type (might be due to invalid IL or missing references) GameObject val = GameObject.Find("InsanityHud_Root"); if ((Object)(object)val == (Object)null) { return; } RectTransform component = val.GetComponent<RectTransform>(); if (!((Object)(object)component == (Object)null)) { float num = 130f + Mathf.Max(0f, SanityHudRaisePixels?.Value ?? 360f); Vector2 anchoredPosition = component.anchoredPosition; if (Mathf.Abs(anchoredPosition.y - num) > 0.1f) { component.anchoredPosition = new Vector2(anchoredPosition.x, num); } } } private void ApplyWorldState(float dt) { StartOfRound instance = StartOfRound.Instance; bool flag = (Object)(object)instance != (Object)null && instance.shipHasLanded; if (flag && !_wasLanded) { CaptureExpeditionRoster(); _expeditionStartingCrewCount = _expeditionRoster.Count; _someoneLeftShipThisExpedition = false; _expeditionHadDeath = false; _reunionAwardedThisExpedition = false; _recoveredScrapThisExpedition = false; _creditedLocalScrap.Clear(); _localRecoveredScrapValue = 0; _localTookFieldDamage = false; _apparatusRecoveredByLocal = false; _criticalInjuryActive.Clear(); CaptureShipScrapAtLanding(); } if (!flag && _wasLanded) { HandleExpeditionDeparture(); } int num = DetectNewExpeditionDeaths(); if (num > 0) { _expeditionHadDeath = true; float num2 = DeathPenalty.Value * (float)num; if (ChangeMorale(0f - num2, "crew death")) { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val != (Object)null && IsPlayerInShip(val)) { ShowEvent("CREW MEMBER LOST", $"-{num2:0.#} MORALE", -1f, revealOutsideShip: false, 10); } else { QueueEvent("CREW MEMBER LOST", $"-{num2:0.#} MORALE", revealOutsideShip: false, shipOnly: true, 10); } } } UpdateCriticalInjuries(); if (flag) { UpdateRecoveredScrap(); UpdateLocalPersonalRecoveryTracking(); } int num3 = CountLivingExpeditionPlayers(); int num4 = CountLivingExpeditionPlayersInShip(); bool flag2 = num3 > 0 && num4 == num3; if (flag && num3 > 0 && num4 < num3) { _someoneLeftShipThisExpedition = true; } if (flag && _someoneLeftShipThisExpedition && !_expeditionHadDeath && !_reunionAwardedThisExpedition && _expeditionStartingCrewCount > 1 && num3 == _expeditionStartingCrewCount && flag2) { ChangeMorale(ReunionReward.Value, "perfect crew reunion"); ShowEvent("CREW REUNITED", $"+{ReunionReward.Value:0.#} MORALE", -1f, revealOutsideShip: true); _reunionAwardedThisExpedition = true; } UpdateQuotaReward(); bool num5 = IsInOrbit(); float num6 = 0f; if (flag2) { num6 += FullCrewShipRecoveryPerSecond.Value; } if (num5) { num6 += OrbitRecoveryPerSecond.Value; } if (num6 > 0f) { float num7 = Mathf.Max(0f, DecorationBaseline - StartingMorale.Value); float num8 = Mathf.Max(0.01f, DecorationBonusCap.Value); float num9 = 1f + Mathf.Clamp01(num7 / num8); MoveTowardMorale(DecorationBaseline, num6 * num9 * dt); } _wasFullLivingCrewInShip = flag2; _wasLanded = flag; } private void UpdateCriticalInjuries() { foreach (PlayerControllerB item in _expeditionRoster) { if ((Object)(object)item == (Object)null || item.isPlayerDead) { continue; } int num = ReadPlayerHealth(item); if (num < 0) { continue; } if (num <= CriticalInjuryHealthThreshold.Value && !_criticalInjuryActive.Contains(item)) { _criticalInjuryActive.Add(item); float morale = Morale; bool flag = ChangeMorale(0f - CriticalInjuryPenalty.Value, "critical injury"); bool revealOutsideShip = false; try { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; revealOutsideShip = (Object)(object)val != (Object)null && ((Object)(object)val == (Object)(object)item || val.playerClientId == item.playerClientId); } catch { } if (flag) { ShowEvent("CRITICAL INJURY", $"-{CriticalInjuryPenalty.Value:0.#} MORALE", -1f, revealOutsideShip); } ConfigEntry<bool> debugV2Logging = DebugV2Logging; if (debugV2Logging != null && debugV2Logging.Value) { string arg = (flag ? $"morale {morale:0.#} -> {Morale:0.#}" : $"blocked at morale {morale:0.#}"); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"CRITICAL INJURY: player {item.playerClientId} | HP {num} | {arg}"); } } else if (num > CriticalInjuryResetHealth.Value && _criticalInjuryActive.Contains(item)) { _criticalInjuryActive.Remove(item); ConfigEntry<bool> debugV2Logging2 = DebugV2Logging; if (debugV2Logging2 != null && debugV2Logging2.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"CRITICAL INJURY RESET: player {item.playerClientId} | HP {num}"); } } } } private static int ReadPlayerHealth(PlayerControllerB player) { try { FieldInfo fieldInfo = AccessTools.Field(typeof(PlayerControllerB), "health"); if (fieldInfo != null) { return Convert.ToInt32(fieldInfo.GetValue(player)); } } catch { } return -1; } private void HandleExpeditionDeparture() { if (IsCompanyMoon()) { return; } int num = CountLivingExpeditionPlayers(); if (num <= 0) { return; } AwardPersonalExpeditionCredits(num); float num2 = SurvivorReward.Value * (float)num; if (num2 > 0f) { ChangeMorale(num2, "expedition survivors"); ShowEvent((num == 1) ? "1 SURVIVOR RETURNED" : $"{num} SURVIVORS RETURNED", $"+{num2:0.#} MORALE", -1f, revealOutsideShip: true); } int num3 = 0; foreach (PlayerControllerB item in _expeditionRoster) { if (!((Object)(object)item == (Object)null) && !item.isPlayerDead && !IsPlayerInShip(item)) { num3++; } } if (num3 > 0) { float num4 = LeftBehindPenalty.Value * (float)num3; if (ChangeMorale(0f - num4, "crew left behind")) { ShowEvent((num3 == 1) ? "CREW MEMBER LEFT BEHIND" : "CREW LEFT BEHIND", $"-{num4:0.#} MORALE", -1f, revealOutsideShip: true); } } if (_someoneLeftShipThisExpedition && !_recoveredScrapThisExpedition && ChangeMorale(0f - ZeroScrapExpeditionPenalty.Value, "zero-scrap expedition")) { ShowEvent("EXPEDITION CAME BACK EMPTY", $"-{ZeroScrapExpeditionPenalty.Value:0.#} MORALE", -1f, revealOutsideShip: true); } } private void UpdateLocalPersonalRecoveryTracking() { if (IsCompanyMoon()) { return; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null || val.isPlayerDead) { return; } if (!IsPlayerInShip(val)) { int num = ReadPlayerHealth(val); if (num >= 0 && num < 100) { _localTookFieldDamage = true; } } try { Type type = AccessTools.TypeByName("GrabbableObject"); if (type == null) { return; } Object[] array = Object.FindObjectsOfType(type); foreach (Object val2 in array) { if (val2 == (Object)null) { continue; } FieldInfo fieldInfo = AccessTools.Field(((object)val2).GetType(), "isInShipRoom"); if (fieldInfo == null || !Convert.ToBoolean(fieldInfo.GetValue(val2))) { continue; } int instanceID = val2.GetInstanceID(); if (_shipScrapAtLanding.Contains(instanceID) || _creditedLocalScrap.Contains(instanceID)) { continue; } object? obj = AccessTools.Field(((object)val2).GetType(), "playerHeldBy")?.GetValue(val2) ?? AccessTools.Field(((object)val2).GetType(), "heldByPlayerOnServer")?.GetValue(val2); PlayerControllerB val3 = (PlayerControllerB)((obj is PlayerControllerB) ? obj : null); if (val3 != null && (Object)(object)val3 != (Object)(object)val) { continue; } object obj2 = AccessTools.Field(((object)val2).GetType(), "itemProperties")?.GetValue(val2); if (obj2 != null && Convert.ToBoolean(AccessTools.Field(obj2.GetType(), "isScrap")?.GetValue(obj2) ?? ((object)false))) { int num2 = 0; FieldInfo fieldInfo2 = AccessTools.Field(((object)val2).GetType(), "scrapValue"); if (fieldInfo2 != null) { num2 = Math.Max(0, Convert.ToInt32(fieldInfo2.GetValue(val2))); } _creditedLocalScrap.Add(instanceID); _localRecoveredScrapValue += num2; _recoveredScrapThisExpedition = true; if ((AccessTools.Field(obj2.GetType(), "itemName")?.GetValue(obj2)?.ToString() ?? "").IndexOf("apparatus", StringComparison.OrdinalIgnoreCase) >= 0) { _apparatusRecoveredByLocal = true; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("Personal recovery tracking compatibility note: " + ex.Message)); } } private int SuccessfulHaulCreditsForValue(int value) { if (value >= 500) { return SuccessfulHaulTier4Credits.Value; } if (value >= 350) { return SuccessfulHaulTier3Credits.Value; } if (value >= 200) { return SuccessfulHaulTier2Credits.Value; } if (value >= 100) { return SuccessfulHaulTier1Credits.Value; } return 0; } private void AwardPersonalExpeditionCredits(int living) { ConfigEntry<bool> playerCreditRewardsEnabled = PlayerCreditRewardsEnabled; if (playerCreditRewardsEnabled == null || !playerCreditRewardsEnabled.Value || IsCompanyMoon()) { return; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if (!((Object)(object)val == (Object)null) && !val.isPlayerDead && _expeditionRoster.Contains(val)) { int num = 0; List<string> list = new List<string>(); int num2 = SuccessfulHaulCreditsForValue(_localRecoveredScrapValue); if (num2 > 0) { num += num2; list.Add($"HAUL +{num2}"); } int num3 = DangerousMoonCredits(); if (num3 > 0) { num += num3; list.Add($"DANGEROUS MOON +{num3}"); } if (_apparatusRecoveredByLocal && ApparatusRecoveryCredits.Value > 0) { num += ApparatusRecoveryCredits.Value; list.Add($"APPARATUS +{ApparatusRecoveryCredits.Value}"); } if (living == _expeditionStartingCrewCount && FullCrewExtractionCredits.Value > 0) { num += FullCrewExtractionCredits.Value; list.Add($"FULL CREW +{FullCrewExtractionCredits.Value}"); } int num4 = ReadPlayerHealth(val); if (_localTookFieldDamage && num4 >= 0 && num4 <= AgainstOddsHealthThreshold.Value && AgainstOddsCredits.Value > 0) { num += AgainstOddsCredits.Value; list.Add($"AGAINST ODDS +{AgainstOddsCredits.Value}"); } if (num > 0 && LGUCurrencyIntegration.TryAwardLocal(num, "expedition performance")) { ShowEvent("EXPEDITION PERFORMANCE", string.Join(" • ", list) + $" = +{num} PC", -1f, revealOutsideShip: true, 70); } } } private int DangerousMoonCredits() { if (IsCompanyMoon()) { return 0; } try { object obj = StartOfRound.Instance?.currentLevel; string text = (AccessTools.Field(obj?.GetType(), "riskLevel")?.GetValue(obj)?.ToString() ?? AccessTools.Property(obj?.GetType(), "riskLevel")?.GetValue(obj)?.ToString() ?? "").Trim().ToUpperInvariant(); if (text.StartsWith("X") || text.StartsWith("S+") || text.Contains("S++")) { return DangerousMoonTier3Credits.Value; } if (text.StartsWith("S") || text.StartsWith("A")) { return DangerousMoonTier2Credits.Value; } if (text.StartsWith("B") || text.StartsWith("C")) { return DangerousMoonTier1Credits.Value; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("Dangerous moon compatibility note: " + ex.Message)); } return 0; } internal void TryTriggerAddedHallucination() { ConfigEntry<bool> hallucinationsEnabled = HallucinationsEnabled; if (hallucinationsEnabled == null || !hallucinationsEnabled.Value) { return; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null || val.isPlayerDead || (Object)(object)StartOfRound.Instance == (Object)null || !StartOfRound.Instance.shipHasLanded || IsCompanyMoon()) { return; } if ((Object)(object)_hallucinationVisual != (Object)null && Time.unscaledTime >= _hallucinationVisualUntil) { Object.Destroy((Object)(object)_hallucinationVisual); _hallucinationVisual = null; } RestoreHallucinationLightsIfNeeded(); if (Time.unscaledTime < _nextHallucinationCheck || Time.unscaledTime < _hallucinationCooldownUntil) { return; } _nextHallucinationCheck = Time.unscaledTime + Mathf.Max(1f, HallucinationCheckSeconds.Value); if (!InsanityManagerBridge.TryReadInsanity(out var value) || value < HallucinationMinimumInsanity.Value) { return; } float num = Mathf.InverseLerp(HallucinationMinimumInsanity.Value, 100f, value); float num2 = InsanityIntegration.EventFrequencyMultiplier(); float num3 = Mathf.Clamp01(HallucinationBaseChance.Value * num * num2); float value2 = Random.value; ConfigEntry<bool> debugV2Logging = DebugV2Logging; if (debugV2Logging != null && debugV2Logging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"HALLUCINATION ROLL: insanity {value:0.#} | morale {Morale:0.#} | freq x{num2:0.00} | chance {num3 * 100f:0.#}% | roll {value2 * 100f:0.#}%"); } if (!(value2 > num3)) { TriggerHallucination(val, value); _hallucinationCooldownUntil = Time.unscaledTime + Mathf.Max(3f, HallucinationGlobalCooldown.Value); ConfigEntry<bool> debugV2Logging2 = DebugV2Logging; if (debugV2Logging2 != null && debugV2Logging2.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"HALLUCINATION TRIGGERED: insanity {value:0.#} | morale {Morale:0.#}"); } } } private void TriggerHallucination(PlayerControllerB local, float insanity) { List<int> list = new List<int>(); int num = Mathf.Clamp(HallucinationDebugForceType?.Value ?? 0, 0, 3); if (num == 1 && HallucinationAudioEnabled.Value && insanity >= HallucinationAudioThreshold.Value) { list.Add(0); } else if (num == 2 && HallucinationVisualEnabled.Value && insanity >= HallucinationVisualThreshold.Value) { list.Add(1); } else if (num == 3 && HallucinationEnvironmentEnabled.Value && insanity >= HallucinationEnvironmentThreshold.Value) { list.Add(2); } else { if (HallucinationAudioEnabled.Value && insanity >= HallucinationAudioThreshold.Value) { for (int i = 0; i < Mathf.Max(0, HallucinationAudioWeight.Value); i++) { list.Add(0); } } if (HallucinationVisualEnabled.Value && insanity >= HallucinationVisualThreshold.Value) { for (int j = 0; j < Mathf.Max(0, HallucinationVisualWeight.Value); j++) { list.Add(1); } } if (HallucinationEnvironmentEnabled.Value && insanity >= HallucinationEnvironmentThreshold.Value) { for (int k = 0; k < Mathf.Max(0, HallucinationEnvironmentWeight.Value); k++) { list.Add(2); } } } if (list.Count != 0) { switch (list[Random.Range(0, list.Count)]) { case 0: TriggerPhantomAudio(local, insanity); break; case 1: TriggerShadowFigure(local, insanity); break; default: TriggerCameraDisturbance(local, insanity); break; } } } private void TriggerPhantomAudio(PlayerControllerB local, float insanity) { //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Expected O, but got Unknown //IL_0256: Unknown result type (might be due to invalid IL or missing references) Camera gameplayCamera = local.gameplayCamera; if ((Object)(object)gameplayCamera == (Object)null) { return; } Type type = AccessTools.TypeByName("UnityEngine.AudioClip"); Type type2 = AccessTools.TypeByName("UnityEngine.AudioSource"); if (type == null || type2 == null) { TriggerShadowFigure(local, insanity); return; } if (_hallucinationClips.Count == 0) { try { Object[] array = Resources.FindObjectsOfTypeAll(type); foreach (Object val in array) { if (!(val == (Object)null) && !string.IsNullOrEmpty(val.name)) { string text = val.name.ToLowerInvariant(); if (text.Contains("footstep") || text.Contains("step") || text.Contains("run") || text.Contains("door") || text.Contains("metal") || text.Contains("creak") || text.Contains("bang") || text.Contains("vent") || text.Contains("radio") || text.Contains("static") || text.Contains("walkie") || text.Contains("growl") || text.Contains("roar") || text.Contains("breath") || text.Contains("scream") || text.Contains("snarl")) { _hallucinationClips.Add(val); } if (_hallucinationClips.Count >= 48) { break; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("Hallucination audio discovery note: " + ex.Message)); } } if (_hallucinationClips.Count == 0) { TriggerShadowFigure(local, insanity); return; } Vector3 val2 = ((Component)gameplayCamera).transform.right * Random.Range(-8f, 8f); Vector3 position = ((Component)gameplayCamera).transform.position + ((Component)gameplayCamera).transform.forward * Random.Range(-3f, 10f) + val2; GameObject val3 = new GameObject("UltimateLethal_PhantomAudio"); val3.transform.position = position; Component obj = val3.AddComponent(type2); AccessTools.Property(type2, "spatialBlend")?.SetValue(obj, 1f); AccessTools.Property(type2, "minDistance")?.SetValue(obj, 1.5f); AccessTools.Property(type2, "maxDistance")?.SetValue(obj, 24f); AccessTools.Property(type2, "volume")?.SetValue(obj, Mathf.Lerp(0.28f, 0.62f, Mathf.InverseLerp(45f, 100f, insanity))); List<Object> list = _hallucinationClips; if (insanity >= 72f) { List<Object> list2 = _hallucinationClips.Where(delegate(Object c) { string text2 = ((c != (Object)null) ? c.name.ToLowerInvariant() : ""); return text2.Contains("footstep") || text2.Contains("step") || text2.Contains("run") || text2.Contains("door") || text2.Contains("vent") || text2.Contains("radio") || text2.Contains("static") || text2.Contains("walkie") || text2.Contains("growl") || text2.Contains("roar") || text2.Contains("breath") || text2.Contains("scream") || text2.Contains("snarl"); }).ToList(); if (list2.Count > 0) { list = list2; } } Object val4 = list[Random.Range(0, list.Count)]; MethodInfo methodInfo = AccessTools.Method(type2, "PlayOneShot", new Type[1] { type }, (Type[])null); if (methodInfo == null) { Object.Destroy((Object)(object)val3); TriggerShadowFigure(local, insanity); return; } methodInfo.Invoke(obj, new object[1] { val4 }); float num = 2f; try { object obj2 = AccessTools.Property(type, "length")?.GetValue(val4); if (obj2 != null) { num = Convert.ToSingle(obj2); } } catch { } Object.Destroy((Object)(object)val3, Mathf.Max(2f, num + 0.25f)); ((BaseUnityPlugin)this).Logger.LogDebug((object)$"Hallucination: phantom audio '{val4.name}' at insanity {insanity:0.#}."); } private void TriggerShadowFigure(PlayerControllerB local, float insanity) { //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_03e3: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_043c: Unknown result type (might be due to invalid IL or missing references) //IL_0443: Expected O, but got Unknown //IL_0459: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) Camera gameplayCamera = local.gameplayCamera; if ((Object)(object)gameplayCamera == (Object)null) { return; } if ((Object)(object)_hallucinationVisual != (Object)null) { Object.Destroy((Object)(object)_hallucinationVisual); _hallucinationVisual = null; } float num = Mathf.InverseLerp(58f, 100f, insanity); bool flag = Random.value < Mathf.Lerp(0.45f, 0.75f, num); float num2 = ((Random.value < 0.5f) ? (-1f) : 1f); float num3 = Random.Range(Mathf.Lerp(3.4f, 2.1f, num), Mathf.Lerp(5.2f, 3.8f, num)); float num4 = (flag ? Random.Range(-0.65f, 0.65f) : (num2 * Random.Range(1.2f, 2.7f))); Vector3 val = ((Component)gameplayCamera).transform.position + ((Component)gameplayCamera).transform.forward * num3 + ((Component)gameplayCamera).transform.right * num4; try { RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(val + Vector3.up * 3f, Vector3.down, ref val2, 8f)) { val = ((RaycastHit)(ref val2)).point + Vector3.up * 1.28f; } else { val.y = ((Component)local).transform.position.y + 1.28f; } } catch { val.y = ((Component)local).transform.position.y + 1.28f; } GameObject val3 = new GameObject("UltimateLethal_LocalHallucination"); val3.transform.position = val; Transform transform = val3.transform; Vector3 val4 = Vector3.ProjectOnPlane(((Component)gameplayCamera).transform.position - val, Vector3.up); transform.rotation = Quaternion.LookRotation(((Vector3)(ref val4)).normalized, Vector3.up); float num5 = Mathf.Lerp(1.05f, 1.38f, num); GameObject obj2 = GameObject.CreatePrimitive((PrimitiveType)1); ((Object)obj2).name = "HallucinationBody"; obj2.transform.SetParent(val3.transform, false); obj2.transform.localScale = new Vector3(0.62f * num5, 1.05f * num5, 0.4f * num5); Collider component = obj2.GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } GameObject obj3 = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)obj3).name = "HallucinationHead"; obj3.transform.SetParent(val3.transform, false); obj3.transform.localPosition = new Vector3(0f, 1.38f * num5, 0f); obj3.transform.localScale = new Vector3(0.58f * num5, 0.62f * num5, 0.52f * num5); Collider component2 = obj3.GetComponent<Collider>(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } GameObject obj4 = GameObject.CreatePrimitive((PrimitiveType)1); ((Object)obj4).name = "HallucinationArmL"; obj4.transform.SetParent(val3.transform, false); obj4.transform.localPosition = new Vector3(-0.46f * num5, 0.25f * num5, 0f); obj4.transform.localScale = new Vector3(0.18f * num5, 0.86f * num5, 0.18f * num5); obj4.transform.localRotation = Quaternion.Euler(0f, 0f, -8f); Collider component3 = obj4.GetComponent<Collider>(); if ((Object)(object)component3 != (Object)null) { Object.Destroy((Object)(object)component3); } GameObject obj5 = GameObject.CreatePrimitive((PrimitiveType)1); ((Object)obj5).name = "HallucinationArmR"; obj5.transform.SetParent(val3.transform, false); obj5.transform.localPosition = new Vector3(0.46f * num5, 0.25f * num5, 0f); obj5.transform.localScale = new Vector3(0.18f * num5, 0.86f * num5, 0.18f * num5); obj5.transform.localRotation = Quaternion.Euler(0f, 0f, 8f); Collider component4 = obj5.GetComponent<Collider>(); if ((Object)(object)component4 != (Object)null) { Object.Destroy((Object)(object)component4); } Shader val5 = Shader.Find("Unlit/Color"); if ((Object)(object)val5 != (Object)null) { Material val6 = new Material(val5); val6.color = new Color(0.001f, 0.001f, 0.001f, 1f); Renderer[] componentsInChildren = val3.GetComponentsInChildren<Renderer>(true); foreach (Renderer val7 in componentsInChildren) { if ((Object)(object)val7 != (Object)null) { val7.material = val6; } } } _hallucinationVisual = val3; _hallucinationVisualUntil = Time.unscaledTime + Mathf.Lerp(0.55f, 1.15f, num); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("HALLUCINATION VISUAL: obvious figure | insanity {0:0.#} | distance {1:0.0}m | {2}", insanity, num3, flag ? "center" : "peripheral")); } private void TriggerCameraDisturbance(PlayerControllerB local, float insanity) { Camera cam = local.gameplayCamera; if ((Object)(object)cam == (Object)null) { return; } RestoreHallucinationLightsNow(); try { List<Light> list = (from l in Object.FindObjectsOfType<Light>() where (Object)(object)l != (Object)null && ((Behaviour)l).enabled && l.intensity > 0.01f && Vector3.Distance(((Component)l).transform.position, ((Component)cam).transform.position) <= 24f orderby Vector3.Distance(((Component)l).transform.position, ((Component)cam).transform.position) select l).Take(5).ToList(); foreach (Light item in list) { _hallucinationLightIntensity[item] = item.intensity; item.intensity *= Random.Range(0.02f, 0.18f); } if (list.Count == 0) { TriggerShadowFigure(local, insanity); return; } _hallucinationLightsUntil = Time.unscaledTime + Mathf.Lerp(0.16f, 0.42f, Mathf.InverseLerp(72f, 100f, insanity)); ((BaseUnityPlugin)this).Logger.LogDebug((object)$"Hallucination: local light flicker ({list.Count} lights) at insanity {insanity:0.#}."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("Hallucination environment fallback: " + ex.Message)); TriggerShadowFigure(local, insanity); } } private void RestoreHallucinationLightsIfNeeded() { if (_hallucinationLightIntensity.Count != 0 && !(Time.unscaledTime < _hallucinationLightsUntil)) { RestoreHallucinationLightsNow(); } } private void RestoreHallucinationLightsNow() { if (_hallucinationLightIntensity.Count == 0) { return; } foreach (KeyValuePair<Light, float> item in _hallucinationLightIntensity.ToList()) { if ((Object)(object)item.Key != (Object)null) { try { item.Key.intensity = item.Value; } catch { } } } _hallucinationLightIntensity.Clear(); _hallucinationLightsUntil = 0f; } private static bool IsCompanyMoon() { try { object obj = StartOfRound.Instance?.currentLevel; if (obj == null) { return false; } string text = AccessTools.Field(obj.GetType(), "PlanetName")?.GetValue(obj)?.ToString() ?? AccessTools.Property(obj.GetType(), "PlanetName")?.GetValue(obj)?.ToString() ?? ""; string text2 = AccessTools.Field(obj.GetType(), "sceneName")?.GetValue(obj)?.ToString() ?? ""; return text.IndexOf("Gordion", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Company", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("Company", StringComparison.OrdinalIgnoreCase) >= 0; } catch { return false; } } private void CaptureShipScrapAtLanding() { _shipScrapAtLanding.Clear(); foreach (int currentShipScrapInstanceId in GetCurrentShipScrapInstanceIds()) { _shipScrapAtLanding.Add(currentShipScrapInstanceId); } } private void UpdateRecoveredScrap() { if (_recoveredScrapThisExpedition) { return; } foreach (int currentShipScrapInstanceId in GetCurrentShipScrapInstanceIds()) { if (!_shipScrapAtLanding.Contains(currentShipScrapInstanceId)) { _recoveredScrapThisExpedition = true; break; } } } private static IEnumerable<int> GetCurrentShipScrapInstanceIds() { //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) List<int> list = new List<int>(); try { Type type = AccessTools.TypeByName("GrabbableObject"); if (type == null) { return list; } Object[] array = Object.FindObjectsOfType(type); foreach (Object val in array) { if (val == (Object)null) { continue; } object obj = AccessTools.Field(((object)val).GetType(), "itemProperties")?.GetValue(val); if (obj == null) { continue; } FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), "isScrap"); if (fieldInfo == null || !Convert.ToBoolean(fieldInfo.GetValue(obj))) { continue; } bool flag = false; FieldInfo fieldInfo2 = AccessTools.Field(((object)val).GetType(), "isInShipRoom"); if (fieldInfo2 != null) { flag = Convert.ToBoolean(fieldInfo2.GetValue(val)); } else { Component val2 = (Component)(object)((val is Component) ? val : null); if (val2 != null) { int num; if ((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.shipInnerRoomBounds != (Object)null) { Bounds bounds = StartOfRound.Instance.shipInnerRoomBounds.bounds; num = (((Bounds)(ref bounds)).Contains(val2.transform.position) ? 1 : 0); } else { num = 0; } flag = (byte)num != 0; } } if (flag) { list.Add(val.GetInstanceID()); } } } catch (Exception ex) { CrewMoralePlugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogDebug((object)("Ship scrap scan compatibility note: " + ex.Message)); } } return list; } private static string GetEnemyRewardName(object enemy) { try { Type type = enemy.GetType(); object obj = AccessTools.Field(type, "enemyType")?.GetValue(enemy) ?? AccessTools.Property(type, "enemyType")?.GetValue(enemy); if (obj != null) { string[] array = new string[2] { "enemyName", "name" }; foreach (string text in array) { string text2 = AccessTools.Field(obj.GetType(), text)?.GetValue(obj)?.ToString(); if (!string.IsNullOrWhiteSpace(text2)) { return text2; } text2 = AccessTools.Property(obj.GetType(), text)?.GetValue(obj)?.ToString(); if (!string.IsNullOrWhiteSpace(text2)) { return text2; } } } Component val = (Component)((enemy is Component) ? enemy : null); if (val != null && (Object)(object)val.gameObject != (Object)null) { return ((Object)val.gameObject).name; } return type.Name; } catch { return enemy?.GetType().Name ?? "Unknown"; } } private static bool ThreatListContains(string csv, string name) { if (string.IsNullOrWhiteSpace(csv) || string.IsNullOrWhiteSpace(name)) { return false; } string text = name.Replace("(Clone)", "").Replace("Enemy", "").Trim(); string[] array = csv.Split(','); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length > 0 && (text.Equals(text2, StringComparison.OrdinalIgnoreCase) || text.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)) { return true; } } return false; } private static ThreatTier ClassifyThreat(object enemy, out string name) { name = GetEnemyRewardName(enemy); if (ThreatListContains(ExtremeThreatEnemies?.Value, name)) { return ThreatTier.Extreme; } if (ThreatListContains(DangerousThreatEnemies?.Value, name)) { return ThreatTier.Dangerous; } if (ThreatListContains(MinorThreatEnemies?.Value, name)) { return ThreatTier.Minor; } return ThreatTier.Standard; } internal static void AwardMonsterKill(object enemyInstance, PlayerControllerB killer) { if ((Object)(object)Instance == (Object)null || enemyInstance == null || (Object)(object)killer == (Object)null) { return; } int instanceId; try { Object val = (Object)((enemyInstance is Object) ? enemyInstance : null); instanceId = ((val != null) ? val.GetInstanceID() : enemyInstance.GetHashCode()); } catch { return; } if (!EnemyKillIntegration.TryMarkRewarded(instanceId)) { return; } string name; ThreatTier threatTier = ClassifyThreat(enemyInstance, out name); float num = threatTier switch { ThreatTier.Extreme => ExtremeThreatMorale.Value, ThreatTier.Dangerous => DangerousThreatMorale.Value, ThreatTier.Minor => MinorThreatMorale.Value, _ => StandardThreatMorale.Value, }; int num2 = threatTier switch { ThreatTier.Extreme => ExtremeThreatPc.Value, ThreatTier.Dangerous => DangerousThreatPc.Value, ThreatTier.Minor => MinorThreatPc.Value, _ => StandardThreatPc.Value, }; bool flag = num > 0f && ChangeMorale(num, $"{threatTier} threat {name} killed by player {killer.playerClientId}"); ConfigEntry<bool> playerCreditRewardsEnabled = PlayerCreditRewardsEnabled; bool flag2 = playerCreditRewardsEnabled != null && playerCreditRewardsEnabled.Value && num2 > 0 && LGUCurrencyIntegration.TryAwardToLocalPlayer(killer, num2, $"{threatTier} threat kill ({name})"); if (flag || flag2) { string arg = threatTier.ToString().ToUpperInvariant() + " THREAT"; string detail = ((flag && flag2) ? $"{arg} • +{num:0.#} MORALE • +{num2} PC" : (flag ? $"{arg} • +{num:0.#} MORALE" : $"{arg} • +{num2} PC")); Instance.ShowEvent("THREAT ELIMINATED", detail, -1f, revealOutsideShip: true, 80); } ConfigEntry<bool> debugV2Logging = DebugV2Logging; if (debugV2Logging != null && debugV2Logging.Value) { ManualLogSource pluginLogger = PluginLogger; if (pluginLogger != null) { pluginLogger.LogInfo((object)$"THREAT REWARD: {name} -> {threatTier} | morale +{num:0.#} | PC +{num2}"); } } } private void UpdateQuotaReward() { try { TimeOfDay val = Object.FindObjectOfType<TimeOfDay>(); if ((Object)(object)val == (Object)null) { return; } FieldInfo fieldInfo = AccessTools.Field(typeof(TimeOfDay), "quotaFulfilled"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(TimeOfDay), "profitQuota"); if (fieldInfo == null || fieldInfo2 == null) { return; } int num = Convert.ToInt32(fieldInfo.GetValue(val)); int num2 = Mathf.Max(1, Convert.ToInt32(fieldInfo2.GetValue(val))); bool flag = num >= num2; if (!_quotaStateInitialized) { _quotaStateInitialized = true; _quotaWasFulfilled = flag; return; } if (flag && !_quotaWasFulfilled) { ChangeMorale(QuotaFulfilledReward.Value, "quota fulfilled"); ShowEvent("QUOTA FULFILLED", $"+{QuotaFulfilledReward.Value:0.#} MORALE", -1f, revealOutsideShip: true); } _quotaWasFulfilled = flag; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("Quota morale check failed: " + ex.Message)); } } private void ShowEvent(string title, string detail, float seconds = -1f, bool revealOutsideShip = false, int priority = 50) { float num = ((seconds > 0f) ? seconds : (EventHudSeconds?.Value ?? 5f)); float unscaledTime = Time.unscaledTime; if (unscaledTime < _eventUntil) { if (priority <= _eventPriority) { QueueEvent(title, detail, revealOutsideShip, shipOnly: false, priority, num); return; } float seconds2 = Mathf.Max(0.5f, _eventUntil - unscaledTime); QueueEvent(_eventTitle, _eventDetail, _eventRevealOutsideShip, shipOnly: false, _eventPriority, seconds2); } _eventTitle = title ?? ""; _eventDetail = detail ?? ""; _eventRevealOutsideShip = revealOutsideShip; _eventPriority = priority; _eventUntil = unscaledTime + num; } private void QueueEvent(string title, string detail, bool revealOutsideShip = false, bool shipOnly = false, int priority = 20, float seconds = -1f) { _eventQueue.Add(new HudEvent { Title = (title ?? ""), Detail = (detail ?? ""), Duration = ((seconds > 0f) ? seconds : (EventHudSeconds?.Value ?? 5f)), RevealOutsideShip = revealOutsideShip, ShipOnly = shipOnly, Priority = priority }); } private void ProcessHudEventQueue() { if (Time.unscaledTime < _eventUntil || _eventQueue.Count == 0) { return; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; bool flag = (Object)(object)val != (Object)null && IsPlayerInShip(val); int num = -1; int num2 = int.MinValue; for (int i = 0; i < _eventQueue.Count; i++) { HudEvent hudEvent = _eventQueue[i]; if (hudEvent != null && (!hudEvent.ShipOnly || flag) && hudEvent.Priority > num2) { num = i; num2 = hudEvent.Priority; } } if (num >= 0) { HudEvent hudEvent2 = _eventQueue[num]; _eventQueue.RemoveAt(num); _eventTitle = hudEvent2.Title; _eventDetail = hudEvent2.Detail; _eventRevealOutsideShip = hudEvent2.RevealOutsideShip; _eventPriority = hudEvent2.Priority; _eventUntil = Time.unscaledTime + hudEvent2.Duration; } } private void UpdateQuotaFailureReset() { bool flag = false; try { object instance = StartOfRound.Instance; if (instance != null) { string[] array = new string[3] { "firingPlayersCutsceneRunning", "firingPlayersCutscene", "playersBeingFired" }; foreach (string text in array) { FieldInfo fieldInfo = AccessTools.Field(instance.GetType(), text); if (fieldInfo != null && fieldInfo.FieldType == typeof(bool) && Convert.ToBoolean(fieldInfo.GetValue(instance))) { flag = true; break; } PropertyInfo propertyInfo = AccessTools.Property(instance.GetType(), text); if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool) && Convert.ToBoolean(propertyInfo.GetValue(instance))) { flag = true; break; } } } } catch { } if (flag && !_wasQuotaFailureSequence) { ResetMoraleToStarting("quota failure"); } _wasQuotaFailureSequence = flag; } internal static void ResetMoraleToStarting(string reason) { float morale = Morale; Morale = Clamp(StartingMorale?.Value ?? 55f); SaveScopedPersistence.MarkMoraleDirty(immediate: true); if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)$"MORALE RESET: {morale:0.#} -> {Morale:0.#} ({reason})"); } } internal static void RestoreMoraleFromSave(float value) { Morale = Clamp(value); } internal static float CurrentLguBonus() { if (Morale >= 99.999f) { return Mathf.Max(0f, PeakLguBonus?.Value ?? 0.15f); } if (Morale >= 85f) { return Mathf.Max(0f, ThrivingLguBonus?.Value ?? 0.1f); } if (Morale >= 70f) { return Mathf.Max(0f, ConfidentLguBonus?.Value ?? 0.05f); } return 0f; } internal static bool IsLastStandProtected(PlayerControllerB player) { if ((Object)(object)Instance == (Object)null || (Object)(object)player == (Object)null) { return false; } if (!Instance._lastStandProtectedUntil.TryGetValue(player, out var value)) { return false; } return Time.unscaledTime < value; } internal static float LastStandProtectionRemaining(PlayerControllerB player) { if ((Object)(object)Instance == (Object)null || (Object)(object)player == (Object)null) { return 0f; } if (!Instance._lastStandProtectedUntil.TryGetValue(player, out var value)) { return 0f; } return Mathf.Max(0f, value - Time.unscaledTime); } private void CleanupExpiredLastStandProtection() { if (_lastStandProtectedUntil.Count == 0) { return; } float now = Time.unscaledTime; foreach (PlayerControllerB item in (from kv in _lastStandProtectedUntil where (Object)(object)kv.Key == (Object)null || now >= kv.Value select kv.Key).ToList()) { if ((Object)(object)item != (Object)null && DebugV2Logging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Last Stand protection expired."); } _lastStandProtectedUntil.Remove(item); } } internal static bool TryTriggerLastStand(PlayerControllerB player) { if ((Object)(object)Instance == (Object)null || (Object)(object)player == (Object)null || !LastStandEnabled.Value) { return false; } if (IsLastStandProtected(player)) { return true; } if (Morale + 0.001f < LastStandTriggerMorale.Value) { return false; } float morale = Morale; ChangeMorale(LastStandAfterMorale.Value - Morale, "Last Stand", allowPeakBreak: true); float num = Mathf.Max(0.25f, LastStandProtectionSeconds.Value); Instance._lastStandProtectedUntil[player] = Time.unscaledTime + num; try { FieldInfo fieldInfo = AccessTools.Field(typeof(PlayerControllerB), "health"); if (fieldInfo != null) { int num2 = Convert.ToInt32(fieldInfo.GetValue(player)); fieldInfo.SetValue(player, Mathf.Max(num2, LastStandMinimumHealth.Value)); } FieldInfo fieldInfo2 = AccessTools.Field(typeof(PlayerControllerB), "criticallyInjured"); if (fieldInfo2 != null && fieldInfo2.FieldType == typeof(bool)) { fieldInfo2.SetValue(player, false); } } catch (Exception ex) { ((BaseUnityPlugin)Instance).Logger.LogDebug((object)("Last Stand health recovery compatibility note: " + ex.Message)); } Instance.ShowEvent("★ LAST STAND ACTIVATED ★", $"CREW SAVED • MORALE {morale:0} → {Morale:0} • PROTECTED {num:0.0}s", -1f, revealOutsideShip: true, 100); if (DebugV2Logging.Value) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)$"LAST STAND: prevented death; morale {morale:0.0}->{Morale:0.0}; protection {num:0.0}s."); } return true; } internal static bool ChangeMorale(float amount, string reason = null, bool allowPeakBreak = false) { float morale = Morale; float num = MaximumMorale?.Value ?? 100f; if (amount < 0f && morale >= num - 0.001f && !allowPeakBreak) { if ((Object)(object)Instance != (Object)null && reason != null) { ((BaseUnityPlugin)Instance).Logger.LogDebug((object)$"Peak morale blocked loss of {0f - amount:0.0} ({reason})"); } return false; } Morale = Clamp(Morale + amount); bool flag = Mathf.Abs(Morale - morale) >= 0.01f; if (flag) { SaveScopedPersistence.MarkMoraleDirty(); } if ((Object)(object)Instance != (Object)null && flag && reason != null) { ((BaseUnityPlugin)Instance).Logger.LogDebug((object)$"Morale {morale:0.0} -> {Morale:0.0} ({reason})"); } return flag; } private static void MoveTowardMorale(float target, float maxDelta) { target = Clamp(target); if (!(target <= Morale)) { float morale = Morale; Morale = Mathf.MoveTowards(Morale, target, Mathf.Max(0f, maxDelta)); if (Mathf.Abs(Morale - morale) >= 0.01f) { SaveScopedPersistence.MarkMoraleDirty(); } } } private static float Clamp(float value) { return Mathf.Clamp(value, MinimumMorale?.Value ?? 0f, MaximumMorale?.Value ?? 100f); } private void RefreshDecorationBaseline() { try { StartOfRound instance = StartOfRound.Instance; if (instance?.unlockablesList?.unlockables == null) { return; } int num = 0; float num2 = 0f; foreach (UnlockableItem unlockable in instance.unlockablesList.unlockables) { if (unlockable != null && unlockable.unlockableType == 1 && !unlockable.alreadyUnlocked && unlockable.hasBeenUnlockedByPlayer && !unlockable.inStorage) { int num3 = 0; if ((Object)(object)unlockable.shopSelectionNode != (Object)null) { num3 = Mathf.Max(0, unlockable.shopSelectionNode.itemCost); } float num4 = Mathf.Max(1f, DecorationPriceScaleCredits.Value); float num5 = Mathf.Max(0.01f, DecorationPriceExponent.Value); float num6 = Mathf.Pow(Mathf.Max(0f, (float)num3) / num4, num5); num6 = Mathf.Clamp(num6, 0f, DecorationMaxMoraleEach.Value); num2 += num6; num++; } } DecorationCount = num; float num7 = Mathf.Min(DecorationBonusCap.Value, num2); DecorationBaseline = Clamp(StartingMorale.Value + num7); if (_previousDecorationBonus >= 0f && Mathf.Abs(num7 - _previousDecorationBonus) >= 0.1f) { float num8 = num7 - _previousDecorationBonus; if (num8 > 0f) { ShowEvent("SHIP DECOR IMPROVED", $"+{num8:0.##} MORALE • DECOR {num7:0.##}/{DecorationBonusCap.Value:0}"); } else { ShowEvent("DECORATION REMOVED", $"{num8:0.##} MORALE • DECOR {num7:0.##}/{DecorationBonusCap.Value:0}"); } } _previousDecorationBonus = num7; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("Decoration scan failed: " + ex.Message)); } } private void CaptureExpeditionRoster() { _expeditionRoster.Clear(); _countedDeadPlayers.Clear(); PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts; if (array == null) { return; } PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead) { _expeditionRoster.Add(val); } } ((BaseUnit