using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 OtherPlayerStatusBars { internal sealed class CadaverGrowthInfectionProvider { private Type? cadaverGrowthType; private FieldInfo? playerInfectionsField; private FieldInfo? infectionMeterField; private Object? cachedCadaverGrowthInstance; private float nextFindTime; public bool TryGetNormalizedInfection(PlayerControllerB player, out float infectionMeter) { infectionMeter = 0f; if ((Object)(object)player == (Object)null) { return false; } if (!TryResolveCadaverGrowthInstance(out object instance)) { return false; } if (!(playerInfectionsField?.GetValue(instance) is Array array)) { return false; } int num = (int)player.playerClientId; if (num < 0 || num >= array.Length) { return false; } object value = array.GetValue(num); if (value == null || infectionMeterField == null) { return false; } if (!(infectionMeterField.GetValue(value) is float num2)) { return false; } infectionMeter = Mathf.Clamp01(num2); return true; } private bool TryResolveCadaverGrowthInstance(out object instance) { instance = null; if (!TryResolveCadaverGrowthMetadata()) { return false; } if (cachedCadaverGrowthInstance == (Object)null && Time.unscaledTime >= nextFindTime) { nextFindTime = Time.unscaledTime + 1f; cachedCadaverGrowthInstance = Object.FindObjectOfType(cadaverGrowthType); } if (cachedCadaverGrowthInstance == (Object)null) { return false; } instance = cachedCadaverGrowthInstance; return true; } private bool TryResolveCadaverGrowthMetadata() { if (cadaverGrowthType != null && playerInfectionsField != null && infectionMeterField != null) { return true; } cadaverGrowthType = (from assembly in AppDomain.CurrentDomain.GetAssemblies() select assembly.GetType("CadaverGrowthAI", throwOnError: false)).FirstOrDefault((Type type) => type != null); if (cadaverGrowthType == null) { return false; } playerInfectionsField = cadaverGrowthType.GetField("playerInfections", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (playerInfectionsField == null) { return false; } Type type2 = cadaverGrowthType.GetNestedType("PlayerInfection", BindingFlags.Public | BindingFlags.NonPublic); if (type2 == null) { type2 = playerInfectionsField.FieldType.GetElementType(); } if (type2 == null) { return false; } infectionMeterField = type2.GetField("infectionMeter", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return infectionMeterField != null; } } internal static class MyPluginInfo { public const string PluginGuid = "Codex.OtherPlayerStatusBars"; public const string PluginName = "OtherPlayerStatusBars"; public const string PluginVersion = "0.1.1"; } internal sealed class PlayerStatusBarManager : MonoBehaviour { private readonly Dictionary trackedBars = new Dictionary(); private readonly HashSet seenPlayerIds = new HashSet(); private readonly List staleIds = new List(); private float nextRefreshTime; private void Update() { if (!Plugin.Settings.Enabled) { ClearBars(); } else if (!(Time.unscaledTime < nextRefreshTime)) { nextRefreshTime = Time.unscaledTime + 0.5f; RefreshBars(); } } private void RefreshBars() { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown StartOfRound instance = StartOfRound.Instance; PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : instance?.localPlayerController); if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null || instance.allPlayerScripts == null) { ClearBars(); return; } if (ShouldSkipBarsInOrbit(instance)) { ClearBars(); return; } seenPlayerIds.Clear(); PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val2 in allPlayerScripts) { if (!ShouldTrackPlayer(val2, val, allPlayerScripts)) { continue; } int num = (int)val2.playerClientId; seenPlayerIds.Add(num); if (!trackedBars.ContainsKey(num)) { GameObject val3 = new GameObject($"OtherPlayerStatusBar_{num}"); val3.transform.SetParent(((Component)this).transform, false); PlayerStatusBarView playerStatusBarView = val3.AddComponent(); if (!playerStatusBarView.Initialize(val2)) { Object.Destroy((Object)(object)val3); Plugin.Log.LogWarning((object)$"Failed to create player status bar for playerId={num}."); } else { trackedBars[num] = playerStatusBarView; } } } staleIds.Clear(); foreach (KeyValuePair trackedBar in trackedBars) { if (!seenPlayerIds.Contains(trackedBar.Key) || (Object)(object)trackedBar.Value == (Object)null || !trackedBar.Value.IsStillValid(val)) { staleIds.Add(trackedBar.Key); } } for (int j = 0; j < staleIds.Count; j++) { RemoveBar(staleIds[j]); } } private static bool ShouldTrackPlayer(PlayerControllerB player, PlayerControllerB localPlayer, PlayerControllerB[] allPlayers) { if ((Object)(object)player == (Object)null || (Object)(object)localPlayer == (Object)null) { return false; } if ((Object)(object)player == (Object)(object)localPlayer) { return false; } if (!player.isPlayerControlled || player.isPlayerDead) { return false; } if (player.health <= 0) { return false; } int num = (int)player.playerClientId; if (num < 0 || num >= allPlayers.Length || (Object)(object)allPlayers[num] != (Object)(object)player) { return false; } return ((Component)player).gameObject.activeInHierarchy; } private static bool ShouldSkipBarsInOrbit(StartOfRound startOfRound) { if (Plugin.Settings.HideInOrbit && startOfRound.inShipPhase) { return !startOfRound.shipHasLanded; } return false; } private void RemoveBar(int playerId) { if (trackedBars.TryGetValue(playerId, out PlayerStatusBarView value)) { trackedBars.Remove(playerId); if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } } } private void ClearBars() { foreach (KeyValuePair trackedBar in trackedBars) { if ((Object)(object)trackedBar.Value != (Object)null) { Object.Destroy((Object)(object)((Component)trackedBar.Value).gameObject); } } trackedBars.Clear(); } private void OnDestroy() { ClearBars(); } private void OnEnable() { Plugin.Settings.SettingsChanged += HandleSettingsChanged; } private void OnDisable() { Plugin.Settings.SettingsChanged -= HandleSettingsChanged; } private void HandleSettingsChanged() { nextRefreshTime = 0f; } } internal sealed class PlayerStatusBarStrip : MonoBehaviour { internal enum StripType { Health, Infection } private const float Width = 172f; private const float Height = 20f; private bool visible = true; private string label = string.Empty; private float currentValue; private float maxValue = 1f; private bool renderAsPercent; private bool labelOnly; private StripType stripType; private RectTransform rectTransform; private Image backgroundImage; private Image fillImage; private TextMeshProUGUI text; private Outline border; private RectTransform fillRect; private bool dirty = true; private bool lastAppliedVisible = true; private float lastAppliedNormalized = -1f; private string lastAppliedText = string.Empty; private bool lastAppliedTextEnabled = true; private Color lastAppliedBackgroundColor = Color.clear; private Color lastAppliedFillColor = Color.clear; private Color lastAppliedBorderColor = Color.clear; private int lastAppliedSettingsRevision = -1; private bool hasFillColorOverride; private Color fillColorOverride; public void Initialize(StripType type) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Expected O, but got Unknown //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: 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_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Expected O, but got Unknown //IL_0236: 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_0256: Unknown result type (might be due to invalid IL or missing references) //IL_026a: 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) stripType = type; rectTransform = ((Component)this).gameObject.GetComponent(); if ((Object)(object)rectTransform == (Object)null) { rectTransform = ((Component)this).gameObject.AddComponent(); } rectTransform.sizeDelta = new Vector2(172f, 20f); rectTransform.anchorMin = new Vector2(0.5f, 0.5f); rectTransform.anchorMax = new Vector2(0.5f, 0.5f); rectTransform.pivot = new Vector2(0.5f, 0.5f); GameObject val = new GameObject("Background", new Type[3] { typeof(RectTransform), typeof(Image), typeof(Outline) }); val.transform.SetParent(((Component)this).transform, false); RectTransform component = val.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; backgroundImage = val.GetComponent(); border = val.GetComponent(); GameObject val2 = new GameObject("Fill", new Type[2] { typeof(RectTransform), typeof(Image) }); val2.transform.SetParent(val.transform, false); RectTransform component2 = val2.GetComponent(); component2.anchorMin = new Vector2(0f, 0f); component2.anchorMax = new Vector2(0f, 1f); component2.pivot = new Vector2(0f, 0.5f); component2.offsetMin = new Vector2(1f, 1f); component2.offsetMax = new Vector2(-1f, -1f); fillImage = val2.GetComponent(); fillRect = component2; GameObject val3 = new GameObject("Text", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val3.transform.SetParent(val.transform, false); RectTransform component3 = val3.GetComponent(); component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.offsetMin = new Vector2(6f, 1f); component3.offsetMax = new Vector2(-6f, -1f); text = val3.GetComponent(); ((TMP_Text)text).alignment = (TextAlignmentOptions)514; ((TMP_Text)text).fontSize = 12f; ((TMP_Text)text).enableWordWrapping = false; ((Graphic)text).color = Color.white; } public void SetDisplay(string displayLabel, float value, float max, bool showPercent) { float num = Mathf.Clamp(value, 0f, (max <= 0f) ? 1f : max); float num2 = Mathf.Max(max, 0.0001f); if (label == displayLabel && Mathf.Approximately(currentValue, num) && Mathf.Approximately(maxValue, num2) && renderAsPercent == showPercent && !labelOnly) { visible = true; return; } label = displayLabel; currentValue = num; maxValue = num2; renderAsPercent = showPercent; labelOnly = false; visible = true; dirty = true; } public void SetLabelOnly(string displayLabel, float fillValue, float max) { float num = Mathf.Clamp(fillValue, 0f, (max <= 0f) ? 1f : max); float num2 = Mathf.Max(max, 0.0001f); if (label == displayLabel && Mathf.Approximately(currentValue, num) && Mathf.Approximately(maxValue, num2) && labelOnly) { visible = true; return; } label = displayLabel; currentValue = num; maxValue = num2; renderAsPercent = false; labelOnly = true; visible = true; dirty = true; } public void SetVisible(bool isVisible) { if (visible != isVisible || ((Component)this).gameObject.activeSelf != isVisible) { visible = isVisible; ((Component)this).gameObject.SetActive(isVisible); dirty = true; } } public void SetFillColorOverride(bool enabled, Color color) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (hasFillColorOverride != enabled || (enabled && !(fillColorOverride == color))) { hasFillColorOverride = enabled; fillColorOverride = color; dirty = true; } } private void LateUpdate() { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0120: 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_012e: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)backgroundImage == (Object)null || (Object)(object)fillImage == (Object)null || (Object)(object)this.text == (Object)null || !visible) { return; } StatusBarConfig settings = Plugin.Settings; if (!dirty && settings.Revision == lastAppliedSettingsRevision) { return; } float num = Mathf.Clamp01(currentValue / maxValue); Color val = ((stripType == StripType.Health) ? settings.GetHealthFillColor() : settings.GetInfectionFillColor()); if (hasFillColorOverride) { val = fillColorOverride; } Color backgroundColor = settings.GetBackgroundColor(); Color borderColor = settings.GetBorderColor(); bool flag = ((stripType == StripType.Health) ? settings.ShowHealthText : settings.ShowInfectionText); string text = (labelOnly ? label : (renderAsPercent ? $"{label} {Mathf.RoundToInt(num * 100f)}%" : $"{label} {Mathf.RoundToInt(currentValue)}/{Mathf.RoundToInt(maxValue)}")); bool flag2 = backgroundColor != lastAppliedBackgroundColor || val != lastAppliedFillColor || borderColor != lastAppliedBorderColor; bool flag3 = !Mathf.Approximately(num, lastAppliedNormalized); bool flag4 = flag != lastAppliedTextEnabled; bool flag5 = text != lastAppliedText; bool flag6 = visible != lastAppliedVisible; if (dirty || flag2 || flag3 || flag4 || flag5 || flag6) { if (flag2) { ((Graphic)backgroundImage).color = backgroundColor; ((Shadow)border).effectColor = borderColor; ((Shadow)border).effectDistance = new Vector2(1f, -1f); ((Graphic)fillImage).color = val; lastAppliedBackgroundColor = backgroundColor; lastAppliedFillColor = val; lastAppliedBorderColor = borderColor; } if (dirty || flag3) { fillRect.SetSizeWithCurrentAnchors((Axis)0, Mathf.Lerp(0f, 170f, num)); lastAppliedNormalized = num; } if (dirty || flag4) { ((Behaviour)this.text).enabled = flag; lastAppliedTextEnabled = flag; } if (flag && (dirty || flag5)) { ((TMP_Text)this.text).text = text; lastAppliedText = text; } lastAppliedVisible = visible; lastAppliedSettingsRevision = settings.Revision; dirty = false; } } } internal sealed class PlayerStatusBarView : MonoBehaviour { private static readonly Color CriticalHealthBarColor = new Color(0.88f, 0.15f, 0.12f, 0.95f); private const float LowHealthRecoveryDisplayDelay = 15f; private const float LowHealthConfirmDelay = 0.5f; private const float InitialStateStabilizationDelay = 3f; private readonly CadaverGrowthInfectionProvider infectionProvider = new CadaverGrowthInfectionProvider(); private PlayerControllerB targetPlayer; private PlayerControllerB? localPlayer; private Transform? anchor; private PlayerStatusBarStrip healthStrip; private PlayerStatusBarStrip infectionStrip; private RectTransform healthStripRect; private RectTransform infectionStripRect; private Canvas worldCanvas; private RectTransform canvasRect; private int lastDisplayedHealth = int.MinValue; private int lastDisplayedInfectionPercent = int.MinValue; private bool wasHealthVisible; private bool wasInfectionVisible; private bool isLowHealthRecoveryTiming; private float lowHealthRecoveryTimer; private bool isLowHealthConfirming; private float lowHealthConfirmTimer; private string lastDisplayedHealthLabel = string.Empty; private bool lastCanvasEnabled = true; private float lastAppliedUiScale = -1f; private float lastAppliedHealthBarYOffset = float.NaN; private float lastAppliedInfectionBarYOffset = float.NaN; private float initializedAtTime; private bool suppressInitialStaleCriticalState; private bool suppressInitialStaleHealthState; private bool hasCompletedInitialStateStabilization; private bool hasObservedStableNonCriticalState; public int PlayerId { get { if (!((Object)(object)targetPlayer != (Object)null)) { return -1; } return (int)targetPlayer.playerClientId; } } public bool Initialize(PlayerControllerB player) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) targetPlayer = player; anchor = ResolveAnchor(player); initializedAtTime = Time.unscaledTime; ((Component)this).gameObject.AddComponent(); worldCanvas = ((Component)this).gameObject.AddComponent(); worldCanvas.renderMode = (RenderMode)2; worldCanvas.sortingOrder = 50; ((Component)this).gameObject.AddComponent(); canvasRect = ((Component)worldCanvas).GetComponent(); canvasRect.sizeDelta = new Vector2(220f, 84f); if (!TryCreateStrip("HealthStrip", out healthStrip)) { return false; } healthStripRect = ((Component)healthStrip).GetComponent(); if (!TryCreateStrip("InfectionStrip", out infectionStrip)) { return false; } infectionStripRect = ((Component)infectionStrip).GetComponent(); ((Object)this).name = $"PlayerStatusBar_{player.playerClientId}"; return true; } public bool IsStillValid(PlayerControllerB localPlayer) { if ((Object)(object)targetPlayer == (Object)null || (Object)(object)localPlayer == (Object)null) { return false; } if ((Object)(object)targetPlayer == (Object)(object)localPlayer || !targetPlayer.isPlayerControlled || targetPlayer.isPlayerDead) { return false; } return ((Component)targetPlayer).gameObject.activeInHierarchy; } private void LateUpdate() { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)targetPlayer == (Object)null) { return; } localPlayer = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : StartOfRound.Instance?.localPlayerController); bool flag = ShouldShowGroup(); if (anchor == null) { anchor = ResolveAnchor(targetPlayer); } float barSpacing = Plugin.Settings.BarSpacing; Vector3 val = (((Object)(object)anchor != (Object)null) ? anchor.position : ((Component)targetPlayer).transform.position); ((Component)this).transform.position = val + Vector3.up * (Plugin.Settings.HeadOffset + barSpacing * Plugin.Settings.UiScale); ApplyCanvasScale(Plugin.Settings.UiScale); SetCanvasEnabled(flag); if (!flag || !IsTargetReadyForDisplay()) { healthStrip.SetVisible(isVisible: false); infectionStrip.SetVisible(isVisible: false); wasHealthVisible = false; wasInfectionVisible = false; ResetLowHealthFallbackState(); ResetInitialStateStabilization(); return; } bool flag2 = ShouldShowForDistance(); ApplyStripLayoutOffsets(); int num = Mathf.Clamp(targetPlayer.health, 0, 100); bool criticallyInjured = targetPlayer.criticallyInjured; bool bleedingHeavily = targetPlayer.bleedingHeavily; bool flag3 = criticallyInjured || bleedingHeavily; UpdateInitialStateStabilization(flag3, num); bool flag4 = flag3 && !suppressInitialStaleCriticalState; int num2 = ((suppressInitialStaleHealthState && num == 20 && !flag4) ? 100 : num); bool flag5 = false; if (!flag4 && num < 20 && !targetPlayer.isPlayerDead) { if (!isLowHealthRecoveryTiming) { if (!isLowHealthConfirming) { isLowHealthConfirming = true; lowHealthConfirmTimer = 0f; } else { lowHealthConfirmTimer += Time.unscaledDeltaTime; } if (lowHealthConfirmTimer >= 0.5f) { isLowHealthRecoveryTiming = true; lowHealthRecoveryTimer = 0f; isLowHealthConfirming = false; lowHealthConfirmTimer = 0f; } } else { lowHealthRecoveryTimer += Time.unscaledDeltaTime; } if (isLowHealthRecoveryTiming && lowHealthRecoveryTimer < 15f) { flag5 = true; } else if (isLowHealthRecoveryTiming) { num2 = 20; } } else if (isLowHealthRecoveryTiming || lowHealthRecoveryTimer > 0f || isLowHealthConfirming || lowHealthConfirmTimer > 0f) { ResetLowHealthFallbackState(); } bool flag6 = flag2; bool flag7 = flag4 || flag5; string text = (flag7 ? GetCriticalHealthLabel() : "HP"); healthStrip.SetFillColorOverride(flag7, CriticalHealthBarColor); if (flag6 && (!wasHealthVisible || num2 != lastDisplayedHealth || text != lastDisplayedHealthLabel)) { if (flag7) { healthStrip.SetLabelOnly(text, 100f, 100f); } else { healthStrip.SetDisplay(text, num2, 100f, showPercent: false); } lastDisplayedHealth = num2; lastDisplayedHealthLabel = text; } healthStrip.SetVisible(flag6); wasHealthVisible = flag6; float infectionMeter = 0f; bool flag8 = false; if (flag2) { flag8 = infectionProvider.TryGetNormalizedInfection(targetPlayer, out infectionMeter); } bool num3; if (Plugin.Settings.InfectionDisplayMode != 0) { if (!(flag2 && flag8)) { goto IL_03d6; } num3 = infectionMeter > 0f; } else { num3 = flag2; } if (num3) { int num4 = Mathf.Clamp(Mathf.RoundToInt(infectionMeter * 100f), 0, 100); if (!wasInfectionVisible || num4 != lastDisplayedInfectionPercent) { infectionStrip.SetDisplay("INF", num4, 100f, showPercent: true); lastDisplayedInfectionPercent = num4; } infectionStrip.SetVisible(isVisible: true); wasInfectionVisible = true; return; } goto IL_03d6; IL_03d6: infectionStrip.SetVisible(isVisible: false); wasInfectionVisible = false; } private bool IsTargetReadyForDisplay() { if ((Object)(object)targetPlayer != (Object)null && targetPlayer.isPlayerControlled && !targetPlayer.isPlayerDead) { return targetPlayer.health > 0; } return false; } private void ResetLowHealthFallbackState() { isLowHealthRecoveryTiming = false; lowHealthRecoveryTimer = 0f; isLowHealthConfirming = false; lowHealthConfirmTimer = 0f; } private static string GetCriticalHealthLabel() { if (!Plugin.UseChineseCriticalLabel) { return "CRITICAL"; } return "重伤"; } private void UpdateInitialStateStabilization(bool rawCriticalState, int rawHealth) { if (!rawCriticalState && rawHealth > 20) { hasObservedStableNonCriticalState = true; } if (!hasCompletedInitialStateStabilization) { float num = Time.unscaledTime - initializedAtTime; if (num <= 3f && !hasObservedStableNonCriticalState) { if (rawHealth == 20) { suppressInitialStaleHealthState = true; } if (rawCriticalState && rawHealth >= 20) { suppressInitialStaleCriticalState = true; suppressInitialStaleHealthState = true; } } else if (num > 3f) { hasCompletedInitialStateStabilization = true; } } if ((suppressInitialStaleCriticalState || suppressInitialStaleHealthState) && (targetPlayer.isPlayerDead || rawHealth < 20 || (!rawCriticalState && rawHealth > 20))) { suppressInitialStaleCriticalState = false; suppressInitialStaleHealthState = false; } } private void ResetInitialStateStabilization() { initializedAtTime = Time.unscaledTime; suppressInitialStaleCriticalState = false; suppressInitialStaleHealthState = false; hasCompletedInitialStateStabilization = false; hasObservedStableNonCriticalState = false; } private void ApplyCanvasScale(float uiScale) { //IL_0015: 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) if (!Mathf.Approximately(lastAppliedUiScale, uiScale)) { ((Transform)canvasRect).localScale = Vector3.one * uiScale; lastAppliedUiScale = uiScale; } } private void ApplyStripLayoutOffsets() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) float healthBarYOffset = Plugin.Settings.HealthBarYOffset; if (!Mathf.Approximately(lastAppliedHealthBarYOffset, healthBarYOffset)) { healthStripRect.anchoredPosition = new Vector2(0f, healthBarYOffset); lastAppliedHealthBarYOffset = healthBarYOffset; } float infectionBarYOffset = Plugin.Settings.InfectionBarYOffset; if (!Mathf.Approximately(lastAppliedInfectionBarYOffset, infectionBarYOffset)) { infectionStripRect.anchoredPosition = new Vector2(0f, infectionBarYOffset); lastAppliedInfectionBarYOffset = infectionBarYOffset; } } private void SetCanvasEnabled(bool enabled) { if (lastCanvasEnabled != enabled || ((Behaviour)worldCanvas).enabled != enabled) { ((Behaviour)worldCanvas).enabled = enabled; lastCanvasEnabled = enabled; } } private bool TryCreateStrip(string stripName, out PlayerStatusBarStrip strip) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) strip = null; GameObject val = new GameObject(stripName, new Type[1] { typeof(RectTransform) }); val.transform.SetParent(((Component)worldCanvas).transform, false); ((Object)val).name = stripName; val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; strip = val.AddComponent(); strip.Initialize((!(stripName == "HealthStrip")) ? PlayerStatusBarStrip.StripType.Infection : PlayerStatusBarStrip.StripType.Health); return true; } private bool ShouldShowForDistance() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)localPlayer == (Object)null) { return true; } float maxDistance = Plugin.Settings.MaxDistance; float num = maxDistance * maxDistance; Vector3 val = ((Component)localPlayer).transform.position - ((Component)targetPlayer).transform.position; return ((Vector3)(ref val)).sqrMagnitude <= num; } private bool ShouldShowGroup() { if (!Plugin.Settings.HideInOrbit) { return true; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return true; } if (instance.inShipPhase) { return instance.shipHasLanded; } return true; } private static Transform? ResolveAnchor(PlayerControllerB player) { if ((Object)(object)player.playerGlobalHead != (Object)null) { return player.playerGlobalHead; } if (player.bodyParts != null && player.bodyParts.Length != 0 && (Object)(object)player.bodyParts[0] != (Object)null) { return player.bodyParts[0]; } return ((Component)player).transform; } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("Codex.OtherPlayerStatusBars", "OtherPlayerStatusBars", "0.1.1")] public sealed class Plugin : BaseUnityPlugin { internal static ManualLogSource Log { get; private set; } internal static StatusBarConfig Settings { get; private set; } internal static bool UseChineseCriticalLabel { get; private set; } private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Settings = StatusBarConfig.Create(((BaseUnityPlugin)this).Config); UseChineseCriticalLabel = DetectChineseLocalizationMod(); TryRegisterLethalConfig(); ((Component)this).gameObject.AddComponent(); ((BaseUnityPlugin)this).Config.Save(); Log.LogInfo((object)"OtherPlayerStatusBars loaded."); } private static bool DetectChineseLocalizationMod() { foreach (PluginInfo value4 in Chainloader.PluginInfos.Values) { string value = value4.Metadata.GUID ?? string.Empty; string value2 = value4.Metadata.Name ?? string.Empty; string value3 = value4.Location ?? string.Empty; if (LooksLikeChineseLocalization(value) || LooksLikeChineseLocalization(value2) || LooksLikeChineseLocalization(value3)) { return true; } } return false; } private static bool LooksLikeChineseLocalization(string value) { if (!Contains(value, "chinese") && !Contains(value, "simplified_chinese") && !Contains(value, "zh_cn") && !Contains(value, "zh-cn") && !Contains(value, "zh_hans") && !Contains(value, "zh-hans") && !Contains(value, "中文") && !Contains(value, "汉化")) { return Contains(value, "FixGameTranslate"); } return true; } private static bool Contains(string value, string term) { return value.IndexOf(term, StringComparison.OrdinalIgnoreCase) >= 0; } private static void TryRegisterLethalConfig() { if (Type.GetType("LethalConfig.LethalConfigManager, LethalConfig", throwOnError: false) == null) { return; } try { StatusBarConfig.LethalConfigIntegration.Register(Settings); } catch (Exception arg) { Log.LogWarning((object)$"Failed to register LethalConfig items: {arg}"); } } } internal sealed class StatusBarBillboard : MonoBehaviour { private void LateUpdate() { //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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) Camera val = ResolveViewCamera(); if (!((Object)(object)val == (Object)null)) { Vector3 forward = ((Component)val).transform.forward; Vector3 up = ((Component)val).transform.up; if (!(forward == Vector3.zero) && !(up == Vector3.zero)) { ((Component)this).transform.rotation = Quaternion.LookRotation(forward, up); } } } private static Camera? ResolveViewCamera() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return Camera.current; } PlayerControllerB localPlayerController = instance.localPlayerController; if ((Object)(object)localPlayerController != (Object)null && !localPlayerController.isPlayerDead) { return localPlayerController.gameplayCamera; } if (!((Object)(object)instance.spectateCamera != (Object)null)) { return Camera.current; } return instance.spectateCamera; } } internal sealed class StatusBarConfig { public enum InfectionBarDisplayMode { AlwaysVisible, ShowOnlyWhenInfected } public enum ColorPreset { Green, Red, Orange, Yellow, Blue, Cyan, White, Black, Slate } public static class LethalConfigIntegration { public static void Register(StatusBarConfig config) { StatusBarConfig config2 = config; AddBool(config2.enabled); AddBool(config2.hideInOrbit); AddFloat(config2.maxDistance, 2f, 80f); AddFloat(config2.headOffset, 0f, 2f); AddFloat(config2.barSpacing, 6f, 48f); AddFloat(config2.healthBarYOffset, -64f, 64f); AddFloat(config2.infectionBarYOffset, -64f, 64f); AddFloat(config2.uiScale, 0.003f, 0.05f); AddBool(config2.showHealthText); AddBool(config2.showInfectionText); AddEnum(config2.healthColor); AddEnum(config2.infectionColor); AddEnum(config2.backgroundColor); AddEnum(config2.infectionDisplayMode); AddConfigItem(CreateItem("LethalConfig.ConfigItems.GenericButtonConfigItem", "General", "Refresh all player bars", "Forces all visible player bars to refresh on the next update.", "Refresh", (Action)delegate { config2.NotifySettingsChanged(); })); } private static void AddBool(ConfigEntry entry) { AddConfigItem(CreateItem("LethalConfig.ConfigItems.BoolCheckBoxConfigItem", entry, CreateOptions("LethalConfig.ConfigItems.Options.BoolCheckBoxOptions", ("RequiresRestart", false)))); } private static void AddFloat(ConfigEntry entry, float min, float max) { AddConfigItem(CreateItem("LethalConfig.ConfigItems.FloatSliderConfigItem", entry, CreateOptions("LethalConfig.ConfigItems.Options.FloatSliderOptions", ("Min", min), ("Max", max), ("RequiresRestart", false)))); } private static void AddEnum(ConfigEntry entry) { AddConfigItem(CreateItem(RequireType("LethalConfig.ConfigItems.EnumDropDownConfigItem`1").MakeGenericType(typeof(T)), entry, false)); } private static object CreateOptions(string typeName, params (string Name, object Value)[] values) { object obj = Activator.CreateInstance(RequireType(typeName)) ?? throw new InvalidOperationException("Failed to create " + typeName + "."); Type type = obj.GetType(); for (int i = 0; i < values.Length; i++) { (string Name, object Value) tuple = values[i]; string item = tuple.Name; object item2 = tuple.Value; PropertyInfo property = type.GetProperty(item, BindingFlags.Instance | BindingFlags.Public); if (property == null || !property.CanWrite) { throw new MissingMemberException(type.FullName, item); } property.SetValue(obj, item2); } return obj; } private static object CreateItem(string typeName, params object[] args) { return CreateItem(RequireType(typeName), args); } private static object CreateItem(Type type, params object[] args) { return Activator.CreateInstance(type, args) ?? throw new InvalidOperationException("Failed to create " + type.FullName + "."); } private static void AddConfigItem(object configItem) { Type type = RequireType("LethalConfig.LethalConfigManager"); MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (methodInfo.Name == "AddConfigItem" && parameters.Length == 2 && parameters[0].ParameterType.IsInstanceOfType(configItem) && parameters[1].ParameterType == typeof(Assembly)) { methodInfo.Invoke(null, new object[2] { configItem, Assembly.GetExecutingAssembly() }); return; } } methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { ParameterInfo[] parameters2 = methodInfo2.GetParameters(); if (methodInfo2.Name == "AddConfigItem" && parameters2.Length == 1 && parameters2[0].ParameterType.IsInstanceOfType(configItem)) { methodInfo2.Invoke(null, new object[1] { configItem }); return; } } throw new MissingMethodException(type.FullName, "AddConfigItem"); } private static Type RequireType(string typeName) { return Type.GetType(typeName + ", LethalConfig", throwOnError: false) ?? throw new TypeLoadException("Could not find optional LethalConfig type '" + typeName + "'."); } } private readonly ConfigEntry enabled; private readonly ConfigEntry hideInOrbit; private readonly ConfigEntry maxDistance; private readonly ConfigEntry headOffset; private readonly ConfigEntry barSpacing; private readonly ConfigEntry healthBarYOffset; private readonly ConfigEntry infectionBarYOffset; private readonly ConfigEntry uiScale; private readonly ConfigEntry showHealthText; private readonly ConfigEntry showInfectionText; private readonly ConfigEntry healthColor; private readonly ConfigEntry infectionColor; private readonly ConfigEntry backgroundColor; private readonly ConfigEntry infectionDisplayMode; public bool Enabled => enabled.Value; public float MaxDistance => maxDistance.Value; public float HeadOffset => headOffset.Value; public float BarSpacing => barSpacing.Value; public float HealthBarYOffset => healthBarYOffset.Value; public float InfectionBarYOffset => infectionBarYOffset.Value; public float UiScale => uiScale.Value; public bool HideInOrbit => hideInOrbit.Value; public bool ShowHealthText => showHealthText.Value; public bool ShowInfectionText => showInfectionText.Value; public InfectionBarDisplayMode InfectionDisplayMode => infectionDisplayMode.Value; public int Revision { get; private set; } public event Action? SettingsChanged; public static StatusBarConfig Create(ConfigFile configFile) { return new StatusBarConfig(configFile); } private StatusBarConfig(ConfigFile configFile) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown enabled = configFile.Bind("General", "Enabled", true, "Enable other player status bars."); hideInOrbit = configFile.Bind("General", "Hide In Orbit", true, "Hide player status bars while the ship is still in orbit."); maxDistance = configFile.Bind("General", "Max Display Distance", 18f, new ConfigDescription("Hide other player status bars beyond this distance.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 80f), Array.Empty())); headOffset = configFile.Bind("Layout", "Head Offset", 0.65f, new ConfigDescription("Vertical world-space offset above the player head anchor.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 2f), Array.Empty())); barSpacing = configFile.Bind("Layout", "Bar Spacing", 14f, new ConfigDescription("Vertical spacing between the health bar and infection bar.", (AcceptableValueBase)(object)new AcceptableValueRange(6f, 48f), Array.Empty())); healthBarYOffset = configFile.Bind("Layout", "Health Bar Y Offset", 0f, new ConfigDescription("Local UI Y offset for the health bar.", (AcceptableValueBase)(object)new AcceptableValueRange(-64f, 64f), Array.Empty())); infectionBarYOffset = configFile.Bind("Layout", "Infection Bar Y Offset", 25f, new ConfigDescription("Local UI Y offset for the infection bar.", (AcceptableValueBase)(object)new AcceptableValueRange(-64f, 64f), Array.Empty())); uiScale = configFile.Bind("Layout", "UI Scale", 0.0085f, new ConfigDescription("World-space scale of the player status bar canvas.", (AcceptableValueBase)(object)new AcceptableValueRange(0.003f, 0.05f), Array.Empty())); showHealthText = configFile.Bind("Text", "Show Health Text", true, "Show health numbers on the health bar."); showInfectionText = configFile.Bind("Text", "Show Infection Text", true, "Show infection percentage on the infection bar."); healthColor = configFile.Bind("Colors", "Health Bar Color", ColorPreset.Green, "Fill color preset for the health bar."); infectionColor = configFile.Bind("Colors", "Infection Bar Color", ColorPreset.Orange, "Fill color preset for the infection bar."); backgroundColor = configFile.Bind("Colors", "Background Color", ColorPreset.Slate, "Background color preset for both bars."); infectionDisplayMode = configFile.Bind("General", "Infection Bar Display Mode", InfectionBarDisplayMode.ShowOnlyWhenInfected, "Always show the infection bar, or only show it when infection is above zero."); Subscribe(enabled); Subscribe(hideInOrbit); Subscribe(maxDistance); Subscribe(headOffset); Subscribe(barSpacing); Subscribe(healthBarYOffset); Subscribe(infectionBarYOffset); Subscribe(uiScale); Subscribe(showHealthText); Subscribe(showInfectionText); Subscribe(healthColor); Subscribe(infectionColor); Subscribe(backgroundColor); Subscribe(infectionDisplayMode); } public Color GetHealthFillColor() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return ResolveColor(healthColor.Value, 0.95f); } public Color GetInfectionFillColor() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return ResolveColor(infectionColor.Value, 0.95f); } public Color GetBackgroundColor() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return ResolveColor(backgroundColor.Value, 0.78f); } public Color GetBorderColor() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Color val = GetBackgroundColor(); val.a = 1f; return Color.Lerp(val, Color.white, 0.35f); } private void Subscribe(ConfigEntry entry) { entry.SettingChanged += delegate { NotifySettingsChanged(); }; } private void NotifySettingsChanged() { Revision++; this.SettingsChanged?.Invoke(); } private static Color ResolveColor(ColorPreset preset, float alpha) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) Color result = (Color)(preset switch { ColorPreset.Green => new Color(0.18f, 0.86f, 0.32f, alpha), ColorPreset.Red => new Color(0.88f, 0.25f, 0.22f, alpha), ColorPreset.Orange => new Color(0.93f, 0.47f, 0.16f, alpha), ColorPreset.Yellow => new Color(0.95f, 0.82f, 0.18f, alpha), ColorPreset.Blue => new Color(0.28f, 0.58f, 0.92f, alpha), ColorPreset.Cyan => new Color(0.18f, 0.83f, 0.88f, alpha), ColorPreset.White => new Color(0.95f, 0.95f, 0.95f, alpha), ColorPreset.Black => new Color(0.08f, 0.08f, 0.08f, alpha), _ => new Color(0.12f, 0.15f, 0.2f, alpha), }); result.a = alpha; return result; } } }