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 balrond secondchance v1.0.4
plugins/BalrondSecondChance.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using Balrond.Shared; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using JetBrains.Annotations; using LitJson2; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.Audio; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("BalrondSecondChance")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.9.0")] [assembly: AssemblyInformationalVersion("1.0.9")] [assembly: AssemblyProduct("BalrondSecondChance")] [assembly: AssemblyTitle("BalrondSecondChance")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.9.0")] [module: UnverifiableCode] namespace BalrondReviveSoul { public class BalrondTranslator { public static Dictionary<string, Dictionary<string, string>> translations = new Dictionary<string, Dictionary<string, string>>(); public static Dictionary<string, string> getLanguage(string language) { if (string.IsNullOrEmpty(language)) { return null; } if (translations.TryGetValue(language, out var value)) { return value; } return null; } } [HarmonyPatch(typeof(AudioMan), "Awake")] internal static class BRSAudioManAwakePatch { private static void Postfix(AudioMan __instance) { AudioRoutingService.OnAudioManAwake(__instance); } } internal static class BRSConfig { internal static ConfigEntry<bool> LockConfiguration; internal static ConfigEntry<float> DownedSeconds; internal static ConfigEntry<bool> AutoReleaseWhenTimerEnds; internal static ConfigEntry<bool> AllowSelfPotionRevive; internal static ConfigEntry<bool> RequireItemForSelfRevive; internal static ConfigEntry<bool> RequirePotionForFriendRevive; internal static ConfigEntry<string> ResurrectionPotionPrefabName; internal static ConfigEntry<int> ResurrectionItemAmount; internal static ConfigEntry<float> ResurrectionCooldownMinutes; internal static ConfigEntry<float> ReviveHealthPercent; internal static ConfigEntry<float> ReviveDistance; internal static ConfigEntry<string> DownedPresentationMode; internal static void Init(Launch plugin) { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown LockConfiguration = plugin.config("0 General", "Lock Configuration", value: true, "If on, server values are enforced."); DownedSeconds = plugin.config("0 General", "Downed Seconds", 120f, "How long player stays downed."); AutoReleaseWhenTimerEnds = plugin.config("0 General", "Auto Release When Timer Ends", value: true, "Auto death after timer."); ResurrectionCooldownMinutes = plugin.config("0 General", "Cooldown Minutes", 10f, "Cooldown after revive."); ReviveHealthPercent = plugin.config("0 General", "Revive Health Percent", 0.25f, "Health after revive."); ReviveDistance = plugin.config("0 General", "Revive Distance", 3f, "Max revive distance."); DownedPresentationMode = plugin.config("0 General", "Downed Presentation Mode", "Sit", new ConfigDescription("Server-synchronized pose/animation used for every player while downed.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[4] { "Sit", "Bed", "Dance", "Headbang" }), Array.Empty<object>())); AllowSelfPotionRevive = plugin.config("1 Self Revive", "Allow Self Revive", value: true, "Allow self revive."); RequireItemForSelfRevive = plugin.config("1 Self Revive", "Require Item For Self Revive", value: true, "Require item for self revive."); RequirePotionForFriendRevive = plugin.config("2 Friend Revive", "Require Item For Friend Revive", value: true, "Require item for reviving others."); ResurrectionPotionPrefabName = plugin.config("3 Items", "Resurrection Item Prefab", "MeadHealthMinor", "Item prefab name."); ResurrectionItemAmount = plugin.config("3 Items", "Resurrection Item Amount", 1, "Amount required."); } } [HarmonyPatch(typeof(Player), "Awake")] internal static class BRSPlayerAwakePatch { private static void Postfix(Player __instance) { if (!((Object)(object)__instance == (Object)null) && (Object)(object)((Component)__instance).GetComponent<BRSDownedBehaviour>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<BRSDownedBehaviour>(); } } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class BRSPlayerOnDeathPatch { private static bool Prefix(Player __instance) { if ((Object)(object)__instance == (Object)null) { return true; } if (Launch.AllowOriginalDeath) { return true; } if ((Object)(object)((Character)__instance).m_nview == (Object)null || !((Character)__instance).m_nview.IsValid()) { return true; } if (!((Character)__instance).IsOwner()) { return true; } if (__instance.IsDowned()) { return false; } if (BRSReviveRules.IsPotionReviveOnCooldown(__instance, out var _)) { return true; } float num = Mathf.Max(1f, BRSConfig.DownedSeconds.Value); float until = Time.time + num; float health = Mathf.Max(1f, ((Character)__instance).GetMaxHealth() * 0.01f); __instance.SetDowned(value: true, until); __instance.ClearReleaseRequest(); ((Character)__instance).SetHealth(health); BRSPlayerPresentation.SetDownedPresentation(__instance, downed: true); if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "$tag_brs_death_wait_message_bal", 0, (Sprite)null, false, true); } } return false; } } [HarmonyPatch(typeof(Game), "UpdateRespawn")] internal static class BRSGameUpdateRespawnPatch { private static bool Prefix() { Player localPlayer = Player.m_localPlayer; return (Object)(object)localPlayer == (Object)null || !localPlayer.IsDowned(); } } [HarmonyPatch(typeof(Player), "CreateTombStone")] internal static class BRSCreateTombStonePatch { private static bool Prefix(Player __instance) { return (Object)(object)__instance == (Object)null || !__instance.IsDowned(); } } internal static class BRSAIIgnoreDownedPlayers { internal static bool IsDownedPlayer(Character character) { Player val = (Player)(object)((character is Player) ? character : null); return (Object)(object)val != (Object)null && val.IsDowned(); } internal static void ClearTargetIfDowned(MonsterAI ai) { if (!((Object)(object)ai == (Object)null) && IsDownedPlayer(((BaseAI)ai).GetTargetCreature())) { ai.SetTarget((Character)null); ((BaseAI)ai).SetAlerted(false); } } } [HarmonyPatch(typeof(MonsterAI), "SetTarget")] internal static class BRSMonsterAISetTargetPatch { private static bool Prefix(MonsterAI __instance, Character __0) { if (!BRSAIIgnoreDownedPlayers.IsDownedPlayer(__0)) { return true; } if ((Object)(object)__instance != (Object)null) { ((BaseAI)__instance).SetAlerted(false); } return false; } } [HarmonyPatch(typeof(MonsterAI), "UpdateAI")] internal static class BRSMonsterAIUpdateAIPatch { private static void Prefix(MonsterAI __instance) { BRSAIIgnoreDownedPlayers.ClearTargetIfDowned(__instance); } private static void Postfix(MonsterAI __instance) { BRSAIIgnoreDownedPlayers.ClearTargetIfDowned(__instance); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class BRSDownedPlayerDamagePatch { private static bool Prefix(Character __instance) { return !BRSAIIgnoreDownedPlayers.IsDownedPlayer(__instance); } } [HarmonyPatch] internal static class BRSBaseAIIsEnemyCharacterPatch { private static IEnumerable<MethodBase> TargetMethods() { MethodInfo method = AccessTools.Method(typeof(BaseAI), "IsEnemy", new Type[2] { typeof(Character), typeof(Character) }, (Type[])null); if (method != null) { yield return method; } } private static void Postfix(Character __0, Character __1, ref bool __result) { if (__result && (BRSAIIgnoreDownedPlayers.IsDownedPlayer(__0) || BRSAIIgnoreDownedPlayers.IsDownedPlayer(__1))) { __result = false; } } } internal sealed class BRSDownedBehaviour : MonoBehaviour, Hoverable, Interactable { private const string RpcReviveRequest = "BRS_ReviveRequest"; private const string RpcSelfReviveRequest = "BRS_SelfReviveRequest"; private Player _player; private ZNetView _nview; private float _nextOwnerTick; private float _nextPresentationTick; private bool _registered; private void Awake() { _player = ((Component)this).GetComponent<Player>(); _nview = ((Component)this).GetComponent<ZNetView>(); TryRegisterRpc(); } private void OnEnable() { TryRegisterRpc(); } private void Update() { TryRegisterRpc(); if ((Object)(object)_player == (Object)null || (Object)(object)_nview == (Object)null || !_nview.IsValid()) { return; } if (!_player.IsDowned()) { BRSPlayerPresentation.SetDownedPresentation(_player, downed: false); return; } if (Time.time >= _nextPresentationTick) { _nextPresentationTick = Time.time + 0.1f; BRSPlayerPresentation.SetDownedPresentation(_player, downed: true); } if (((Character)_player).IsOwner() && !(Time.time < _nextOwnerTick)) { _nextOwnerTick = Time.time + 0.1f; MaintainDownedOwnerState(); if (_player.ReleaseRequested()) { PerformRealDeath(); } else if (BRSConfig.AutoReleaseWhenTimerEnds.Value && _player.GetRemainingDownedSeconds() <= 0f) { PerformRealDeath(); } } } public string GetHoverName() { return ((Object)(object)_player != (Object)null) ? _player.GetPlayerName() : string.Empty; } public float GetHoverOffset() { return ((Object)(object)_player != (Object)null) ? ((Character)_player).GetHoverOffset() : 0f; } public string GetHoverText() { if ((Object)(object)_player == (Object)null || !_player.IsDowned()) { return string.Empty; } Player localPlayer = Player.m_localPlayer; float remainingDownedSeconds = _player.GetRemainingDownedSeconds(); string playerName = _player.GetPlayerName(); string text = "$tag_brs_release_in_bal <color=orange>" + remainingDownedSeconds.ToString("0") + "s</color>"; string reason; bool flag = BRSReviveRules.CanFriendRevive(localPlayer, _player, out reason); if (BRSConfig.RequirePotionForFriendRevive.Value) { string value = BRSConfig.ResurrectionPotionPrefabName.Value; int num = Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value); if (flag) { return Localization.instance.Localize(playerName + "\n$tag_brs_hover_use_revive_bal\n" + num + "x " + value + "\n" + text); } return Localization.instance.Localize(playerName + "\n" + text + "\n" + reason); } if (flag) { return Localization.instance.Localize(playerName + "\n$tag_brs_hover_use_revive_bal\n" + text); } return Localization.instance.Localize(playerName + "\n" + text + "\n" + reason); } public bool Interact(Humanoid user, bool repeat, bool alt) { if (repeat || alt) { return false; } Player val = (Player)(object)((user is Player) ? user : null); if ((Object)(object)val == (Object)null || (Object)(object)_player == (Object)null || !_player.IsDowned()) { return false; } return TryReviveFromActor(val, null, fromUseItem: false); } public bool UseItem(Humanoid user, ItemData item) { Player val = (Player)(object)((user is Player) ? user : null); if ((Object)(object)val == (Object)null || item == null || (Object)(object)_player == (Object)null || !_player.IsDowned()) { return false; } if (!IsResurrectionPotion(item)) { return false; } return TryReviveFromActor(val, item, fromUseItem: true); } internal void TrySelfReviveLocal() { if ((Object)(object)_player == (Object)null || !((Character)_player).IsOwner() || !_player.IsDowned()) { Debug.LogWarning((object)"[BalrondSecondChance] Self-revive click rejected before rules: player/owner/downed state invalid."); return; } TryRegisterRpc(); if ((Object)(object)_nview == (Object)null || !_nview.IsValid()) { Debug.LogWarning((object)"[BalrondSecondChance] Self-revive click rejected: ZNetView is not valid."); return; } if (!BRSReviveRules.CanSelfRevive(_player, out var reason)) { Debug.LogWarning((object)("[BalrondSecondChance] Self-revive click rejected by rules: " + reason)); return; } if (BRSConfig.RequireItemForSelfRevive.Value && !BRSItemUtils.ConsumeResurrectionPotion(_player)) { Debug.LogWarning((object)"[BalrondSecondChance] Self-revive click passed rules but potion consumption failed."); return; } Debug.Log((object)"[BalrondSecondChance] Self-revive validated; invoking BRS_SelfReviveRequest."); _nview.InvokeRPC("BRS_SelfReviveRequest", Array.Empty<object>()); } private bool TryReviveFromActor(Player reviver, ItemData directItem, bool fromUseItem) { if (!BRSReviveRules.IsWithinReviveDistance(reviver, _player)) { ((Character)reviver).Message((MessageType)2, "$msg_toofar", 0, (Sprite)null, false); return true; } if (!BRSReviveRules.CanFriendRevive(reviver, _player, out var reason)) { ((Character)reviver).Message((MessageType)2, reason, 0, (Sprite)null, false); return true; } if (BRSConfig.RequirePotionForFriendRevive.Value && !(fromUseItem ? BRSItemUtils.ConsumeSpecificItem(reviver, directItem, Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value)) : BRSItemUtils.ConsumeResurrectionPotion(reviver))) { string text = "$tag_brs_need_item_bal " + Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value) + "x " + BRSConfig.ResurrectionPotionPrefabName.Value + "."; ((Character)reviver).Message((MessageType)2, text, 0, (Sprite)null, false); return true; } _nview.InvokeRPC("BRS_ReviveRequest", Array.Empty<object>()); return true; } private bool IsResurrectionPotion(ItemData item) { if (item == null) { return false; } string value = BRSConfig.ResurrectionPotionPrefabName.Value; if ((Object)(object)item.m_dropPrefab != (Object)null && string.Equals(((Object)item.m_dropPrefab).name, value, StringComparison.OrdinalIgnoreCase)) { return true; } if (item.m_shared != null && !string.IsNullOrEmpty(item.m_shared.m_name) && string.Equals(item.m_shared.m_name, value, StringComparison.OrdinalIgnoreCase)) { return true; } return false; } private void TryRegisterRpc() { if (!_registered && !((Object)(object)_nview == (Object)null) && _nview.IsValid()) { _registered = true; _nview.Register("BRS_ReviveRequest", (Action<long>)RPC_ReviveRequest); _nview.Register("BRS_SelfReviveRequest", (Action<long>)RPC_SelfReviveRequest); } } private void RPC_ReviveRequest(long sender) { if (!((Object)(object)_player == (Object)null) && ((Character)_player).IsOwner() && _player.IsDowned()) { ReviveHere(usedPotion: true, selfRevive: false); } } private void RPC_SelfReviveRequest(long sender) { if (!((Object)(object)_player == (Object)null) && ((Character)_player).IsOwner() && _player.IsDowned()) { Debug.Log((object)"[BalrondSecondChance] BRS_SelfReviveRequest received by owner; reviving player."); ReviveHere(usedPotion: true, selfRevive: true); } } private void MaintainDownedOwnerState() { float num = Mathf.Max(1f, ((Character)_player).GetMaxHealth() * 0.01f); if (((Character)_player).GetHealth() > num) { ((Character)_player).SetHealth(num); } } private void ReviveHere(bool usedPotion, bool selfRevive) { float health = Mathf.Max(1f, ((Character)_player).GetMaxHealth() * Mathf.Clamp01(BRSConfig.ReviveHealthPercent.Value)); _player.SetDowned(value: false, 0f); _player.ClearReleaseRequest(); ((Character)_player).SetHealth(health); BRSPlayerPresentation.SetDownedPresentation(_player, downed: false); if (usedPotion) { BRSReviveRules.MarkPotionReviveUsed(_player); } if ((Object)(object)_player == (Object)(object)Player.m_localPlayer) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, selfRevive ? "$tag_brs_self_revived_bal" : "$tag_brs_revived_by_player_bal", 0, (Sprite)null, false, true); } } } private void PerformRealDeath() { _player.SetDowned(value: false, 0f); _player.ClearReleaseRequest(); BRSPlayerPresentation.SetDownedPresentation(_player, downed: false); Launch.AllowOriginalDeath = true; try { ((Character)_player).SetHealth(0f); ((Character)_player).OnDeath(); } finally { Launch.AllowOriginalDeath = false; } } } internal sealed class BRSHudController : MonoBehaviour { private const string UnifiedPopupPath = "_GameMain/LoadingGUI/PixelFix/IngameGui/UnifiedPopup"; private static BRSHudController _instance; private static string _cachedItemDisplayName; private GameObject _downedPopupRoot; private bool _isSetup; private bool _isBinding; private float _nextRefreshTime; private bool _suppressReleaseUntilEscapeUp; private bool _wantPopupCursor; private GameObject _popupBackground; private GameObject _popupPanel; private GameObject _popupBkg; private Component _headerText; private Component _bodyText; private Button _buttonYes; private Button _buttonOk; private Button _buttonNo; private Component _buttonYesText; private Component _buttonOkText; private Component _buttonNoText; private readonly List<Button> _extraPopupButtons = new List<Button>(); private string _sourceLeftButtonPath; private string _sourceCenterButtonPath; private string _sourceRightButtonPath; private string _sourceConfirmButtonPath; private string _sourceLeftTextPath; private string _sourceCenterTextPath; private string _sourceRightTextPath; internal static BRSHudController Instance => EnsureInstance(); internal static bool HasInstance => (Object)(object)_instance != (Object)null; internal static BRSHudController EnsureInstance() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("BRS_HudController"); Object.DontDestroyOnLoad((Object)(object)val); _instance = val.AddComponent<BRSHudController>(); return _instance; } internal static void ClearCachedItemDisplayName() { _cachedItemDisplayName = null; } internal bool ShouldForceMouseActive() { return _wantPopupCursor; } internal void BindHud(Hud hud) { if (!((Object)(object)hud == (Object)null) && !_isBinding && !_isSetup) { ((MonoBehaviour)this).StartCoroutine(SetupPopupWhenReady()); } } internal void UnbindHud(Hud hud) { _isBinding = false; _isSetup = false; ReleaseInputCapture(); if ((Object)(object)_downedPopupRoot != (Object)null) { Object.Destroy((Object)(object)_downedPopupRoot); _downedPopupRoot = null; } _popupBackground = null; _popupPanel = null; _popupBkg = null; _headerText = null; _bodyText = null; _buttonYes = null; _buttonOk = null; _buttonNo = null; _buttonYesText = null; _buttonOkText = null; _buttonNoText = null; _extraPopupButtons.Clear(); ClearSourcePopupSemanticPaths(); } private IEnumerator SetupPopupWhenReady() { _isBinding = true; while (!_isSetup && !TrySetupPopup()) { yield return (object)new WaitForSeconds(0.5f); } _isBinding = false; } private bool TrySetupPopup() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_009c: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) Transform val = FindUnifiedPopupTransform(); if ((Object)(object)val == (Object)null) { return false; } Transform val2 = val.Find("PopupBlockingBackground"); if ((Object)(object)val2 == (Object)null) { return false; } CaptureSourcePopupSemanticPaths(val, val2); if ((Object)(object)_downedPopupRoot == (Object)null) { _downedPopupRoot = new GameObject("BRS_DownedUnifiedPopup"); _downedPopupRoot.transform.SetParent(val, false); _downedPopupRoot.transform.SetAsLastSibling(); RectTransform val3 = _downedPopupRoot.AddComponent<RectTransform>(); val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val3.offsetMin = Vector2.zero; val3.offsetMax = Vector2.zero; ((Transform)val3).localScale = Vector3.one; GameObject val4 = Object.Instantiate<GameObject>(((Component)val2).gameObject, _downedPopupRoot.transform, false); ((Object)val4).name = "PopupBlockingBackground"; AudioRoutingService.Process(_downedPopupRoot); } if (!CachePopupRefs(_downedPopupRoot.transform)) { return false; } HideClonedPopupInputFields(_downedPopupRoot.transform); PrepareCloneForDownedUsage(); WireButtons(); _isSetup = true; HidePopup(); return true; } private Transform FindUnifiedPopupTransform() { Transform[] array = Resources.FindObjectsOfTypeAll<Transform>(); Transform val = null; foreach (Transform val2 in array) { if ((Object)(object)val2 == (Object)null || ((Object)val2).name != "UnifiedPopup") { continue; } Transform val3 = val2.Find("PopupBlockingBackground"); if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3.Find("Popup") == (Object)null)) { string hierarchyPath = GetHierarchyPath(val2); if (hierarchyPath.Contains("_GameMain/LoadingGUI/PixelFix/IngameGui/UnifiedPopup")) { return val2; } if ((Object)(object)val == (Object)null && hierarchyPath.Contains("/IngameGui/")) { val = val2; } } } return val; } private static string GetHierarchyPath(Transform t) { if ((Object)(object)t == (Object)null) { return string.Empty; } string text = ((Object)t).name; Transform parent = t.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } private bool CachePopupRefs(Transform root) { Transform val = root.Find("PopupBlockingBackground"); if ((Object)(object)val == (Object)null) { return false; } Transform val2 = val.Find("Popup"); if ((Object)(object)val2 == (Object)null) { return false; } Transform val3 = val2.Find("bkg"); Transform val4 = val2.Find("HeaderText"); Transform val5 = val2.Find("BodyText"); if ((Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null) { return false; } _popupBackground = ((Component)val).gameObject; _popupPanel = ((Component)val2).gameObject; _popupBkg = (((Object)(object)val3 != (Object)null) ? ((Component)val3).gameObject : null); _headerText = GetTextComponent(val4); _bodyText = GetTextComponent(val5); if (!ResolveActionButtons(val)) { return false; } _buttonNoText = ResolveTextByRelativePath(val, _sourceLeftTextPath) ?? GetButtonTextComponent(_buttonNo); _buttonOkText = ResolveTextByRelativePath(val, _sourceCenterTextPath) ?? GetButtonTextComponent(_buttonOk); _buttonYesText = ResolveTextByRelativePath(val, _sourceRightTextPath) ?? GetButtonTextComponent(_buttonYes); LogResolvedPopupButtons(); return (Object)(object)_popupBackground != (Object)null && (Object)(object)_popupPanel != (Object)null && (Object)(object)_headerText != (Object)null && (Object)(object)_bodyText != (Object)null && (Object)(object)_buttonYes != (Object)null && (Object)(object)_buttonNo != (Object)null && (Object)(object)_buttonOk != (Object)null; } private bool ResolveActionButtons(Transform clonedBackgroundRoot) { _buttonYes = null; _buttonOk = null; _buttonNo = null; _extraPopupButtons.Clear(); Button[] componentsInChildren = ((Component)clonedBackgroundRoot).GetComponentsInChildren<Button>(true); List<Button> list = new List<Button>(); foreach (Button val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<TMP_InputField>() != (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<InputField>() != (Object)null)) { list.Add(val); } } if (list.Count < 2) { return false; } _buttonNo = ResolveButtonByRelativePath(clonedBackgroundRoot, _sourceLeftButtonPath); _buttonOk = ResolveButtonByRelativePath(clonedBackgroundRoot, _sourceCenterButtonPath); _buttonYes = ResolveButtonByRelativePath(clonedBackgroundRoot, _sourceRightButtonPath); Button val2 = ResolveButtonByRelativePath(clonedBackgroundRoot, _sourceConfirmButtonPath); if ((Object)(object)_buttonNo == (Object)null) { _buttonNo = FindButtonByAliases(list, "buttonLeft", "ButtonLeft", "ButtonNo", "buttonNo"); } if ((Object)(object)_buttonOk == (Object)null) { _buttonOk = FindButtonByAliases(list, "buttonCenter", "ButtonCenter", "ButtonOk", "buttonOk"); } if ((Object)(object)_buttonYes == (Object)null) { _buttonYes = FindButtonByAliases(list, "buttonRight", "ButtonRight", "ButtonYes", "buttonYes"); } List<Button> list2 = new List<Button>(list); list2.Sort((Button a, Button b) => GetButtonScreenX(a).CompareTo(GetButtonScreenX(b))); if ((Object)(object)_buttonNo == (Object)null && list2.Count > 0) { _buttonNo = list2[0]; } if ((Object)(object)_buttonYes == (Object)null && list2.Count > 1) { for (int num = list2.Count - 1; num >= 0; num--) { if ((Object)(object)list2[num] != (Object)(object)_buttonNo) { _buttonYes = list2[num]; break; } } } if ((Object)(object)_buttonOk == (Object)null) { float num2 = (((Object)(object)_buttonNo != (Object)null) ? GetButtonScreenX(_buttonNo) : 0f); float num3 = (((Object)(object)_buttonYes != (Object)null) ? GetButtonScreenX(_buttonYes) : 0f); float num4 = (num2 + num3) * 0.5f; float num5 = float.MaxValue; for (int num6 = 0; num6 < list2.Count; num6++) { Button val3 = list2[num6]; if (!((Object)(object)val3 == (Object)(object)_buttonNo) && !((Object)(object)val3 == (Object)(object)_buttonYes)) { float num7 = Mathf.Abs(GetButtonScreenX(val3) - num4); if (num7 < num5) { num5 = num7; _buttonOk = val3; } } } } if ((Object)(object)_buttonOk == (Object)null) { _buttonOk = _buttonNo; } for (int num8 = 0; num8 < list.Count; num8++) { Button val4 = list[num8]; if ((Object)(object)val4 != (Object)(object)_buttonNo && (Object)(object)val4 != (Object)(object)_buttonYes && (Object)(object)val4 != (Object)(object)_buttonOk && !_extraPopupButtons.Contains(val4)) { _extraPopupButtons.Add(val4); } } if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)_buttonNo && (Object)(object)val2 != (Object)(object)_buttonYes && (Object)(object)val2 != (Object)(object)_buttonOk && !_extraPopupButtons.Contains(val2)) { _extraPopupButtons.Add(val2); } return (Object)(object)_buttonNo != (Object)null && (Object)(object)_buttonYes != (Object)null && (Object)(object)_buttonOk != (Object)null; } private void CaptureSourcePopupSemanticPaths(Transform unifiedPopup, Transform sourceBackground) { ClearSourcePopupSemanticPaths(); if (!((Object)(object)unifiedPopup == (Object)null) && !((Object)(object)sourceBackground == (Object)null)) { UnifiedPopup component = ((Component)unifiedPopup).GetComponent<UnifiedPopup>(); if (!((Object)(object)component == (Object)null)) { Button privateFieldValue = GetPrivateFieldValue<Button>(component, "buttonLeft"); Button privateFieldValue2 = GetPrivateFieldValue<Button>(component, "buttonCenter"); Button privateFieldValue3 = GetPrivateFieldValue<Button>(component, "buttonRight"); Button privateFieldValue4 = GetPrivateFieldValue<Button>(component, "buttonConfirm"); Component privateFieldValue5 = GetPrivateFieldValue<Component>(component, "buttonLeftText"); Component privateFieldValue6 = GetPrivateFieldValue<Component>(component, "buttonCenterText"); Component privateFieldValue7 = GetPrivateFieldValue<Component>(component, "buttonRightText"); _sourceLeftButtonPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue != (Object)null) ? ((Component)privateFieldValue).transform : null); _sourceCenterButtonPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue2 != (Object)null) ? ((Component)privateFieldValue2).transform : null); _sourceRightButtonPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue3 != (Object)null) ? ((Component)privateFieldValue3).transform : null); _sourceConfirmButtonPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue4 != (Object)null) ? ((Component)privateFieldValue4).transform : null); _sourceLeftTextPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue5 != (Object)null) ? privateFieldValue5.transform : null); _sourceCenterTextPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue6 != (Object)null) ? privateFieldValue6.transform : null); _sourceRightTextPath = GetRelativePath(sourceBackground, ((Object)(object)privateFieldValue7 != (Object)null) ? privateFieldValue7.transform : null); } } } private void ClearSourcePopupSemanticPaths() { _sourceLeftButtonPath = null; _sourceCenterButtonPath = null; _sourceRightButtonPath = null; _sourceConfirmButtonPath = null; _sourceLeftTextPath = null; _sourceCenterTextPath = null; _sourceRightTextPath = null; } private static T GetPrivateFieldValue<T>(object instance, string fieldName) where T : class { if (instance == null || string.IsNullOrEmpty(fieldName)) { return null; } FieldInfo field = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return (field != null) ? (field.GetValue(instance) as T) : null; } private static string GetRelativePath(Transform root, Transform target) { if ((Object)(object)root == (Object)null || (Object)(object)target == (Object)null) { return null; } if ((Object)(object)target == (Object)(object)root) { return string.Empty; } List<string> list = new List<string>(); Transform val = target; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Add(((Object)val).name); val = val.parent; } if ((Object)(object)val != (Object)(object)root) { return null; } list.Reverse(); return string.Join("/", list.ToArray()); } private static Button ResolveButtonByRelativePath(Transform root, string path) { if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(path)) { return null; } Transform val = root.Find(path); return ((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Button>() : null; } private static Component ResolveTextByRelativePath(Transform root, string path) { if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(path)) { return null; } Transform t = root.Find(path); return GetTextComponent(t); } private static Button FindButtonByAliases(List<Button> buttons, params string[] aliases) { for (int i = 0; i < buttons.Count; i++) { Button val = buttons[i]; if ((Object)(object)val == (Object)null) { continue; } string name = ((Object)((Component)val).gameObject).name; for (int j = 0; j < aliases.Length; j++) { if (string.Equals(name, aliases[j], StringComparison.OrdinalIgnoreCase)) { return val; } } } return null; } private static float GetButtonScreenX(Button button) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) return ((Object)(object)button != (Object)null) ? ((Component)button).transform.position.x : 0f; } private void DisableExtraPopupButtons() { for (int i = 0; i < _extraPopupButtons.Count; i++) { Button val = _extraPopupButtons[i]; if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(false); } } } private void LogResolvedPopupButtons() { Debug.Log((object)("[BalrondSecondChance] UnifiedPopup buttons resolved: left/release=" + GetButtonDebugName(_buttonNo) + ", center/single=" + GetButtonDebugName(_buttonOk) + ", right/self-revive=" + GetButtonDebugName(_buttonYes) + ", confirm-source-path=" + (_sourceConfirmButtonPath ?? "<none>") + ", disabled-extra-buttons=" + _extraPopupButtons.Count + ".")); } private static string GetButtonDebugName(Button button) { return ((Object)(object)button != (Object)null) ? GetHierarchyPath(((Component)button).transform) : "<null>"; } private static Component GetTextComponent(Transform t) { if ((Object)(object)t == (Object)null) { return null; } TMP_Text component = ((Component)t).GetComponent<TMP_Text>(); if ((Object)(object)component != (Object)null) { return (Component)(object)component; } Text component2 = ((Component)t).GetComponent<Text>(); if ((Object)(object)component2 != (Object)null) { return (Component)(object)component2; } return null; } private static Component GetButtonTextComponent(Button button) { if ((Object)(object)button == (Object)null) { return null; } Component result = null; int num = int.MinValue; TMP_Text[] componentsInChildren = ((Component)button).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<TMP_InputField>() != (Object)null)) { int num2 = ScoreButtonLabel(val.transform, ((Component)val).gameObject.activeSelf, val.rectTransform); if (num2 > num) { num = num2; result = (Component)(object)val; } } } Text[] componentsInChildren2 = ((Component)button).GetComponentsInChildren<Text>(true); foreach (Text val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).GetComponentInParent<InputField>() != (Object)null)) { int num3 = ScoreButtonLabel(((Component)val2).transform, ((Component)val2).gameObject.activeSelf, ((Graphic)val2).rectTransform); if (num3 > num) { num = num3; result = (Component)(object)val2; } } } return result; } private static int ScoreButtonLabel(Transform textTransform, bool activeSelf, RectTransform rect) { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_00d7: Unknown result type (might be due to invalid IL or missing references) int num = 0; string text = (((Object)(object)textTransform != (Object)null) ? ((Object)textTransform).name : string.Empty); if (string.Equals(text, "Text", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "Label", StringComparison.OrdinalIgnoreCase)) { num += 200; } else if (text.IndexOf("text", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("label", StringComparison.OrdinalIgnoreCase) >= 0) { num += 100; } if (activeSelf) { num += 25; } if ((Object)(object)textTransform != (Object)null && (Object)(object)textTransform.parent != (Object)null && (Object)(object)((Component)textTransform.parent).GetComponent<Button>() != (Object)null) { num += 50; } if ((Object)(object)rect != (Object)null) { Rect rect2 = rect.rect; float width = ((Rect)(ref rect2)).width; rect2 = rect.rect; float num2 = Mathf.Abs(width * ((Rect)(ref rect2)).height); if (num2 > 1000f) { num += 30; } else if (num2 > 100f) { num += 10; } } return num; } private static void HideClonedPopupInputFields(Transform root) { if ((Object)(object)root == (Object)null) { return; } TMP_InputField[] componentsInChildren = ((Component)root).GetComponentsInChildren<TMP_InputField>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null) { ((Component)componentsInChildren[i]).gameObject.SetActive(false); } } InputField[] componentsInChildren2 = ((Component)root).GetComponentsInChildren<InputField>(true); for (int j = 0; j < componentsInChildren2.Length; j++) { if ((Object)(object)componentsInChildren2[j] != (Object)null) { ((Component)componentsInChildren2[j]).gameObject.SetActive(false); } } } private static void SetText(Component textComponent, string value) { if ((Object)(object)textComponent == (Object)null) { return; } if (Localization.instance != null) { value = Localization.instance.Localize(value); } TMP_Text val = (TMP_Text)(object)((textComponent is TMP_Text) ? textComponent : null); if ((Object)(object)val != (Object)null) { val.text = value; return; } Text val2 = (Text)(object)((textComponent is Text) ? textComponent : null); if ((Object)(object)val2 != (Object)null) { val2.text = value; } } private static void SetButtonLabel(Button button, Component preferredText, string value) { if (!((Object)(object)button == (Object)null)) { Component textComponent = preferredText ?? GetButtonTextComponent(button); SetText(textComponent, value); } } private void PrepareCloneForDownedUsage() { //IL_00cd: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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_0111: 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) //IL_0126: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_downedPopupRoot == (Object)null)) { CanvasGroup val = _popupBackground.GetComponent<CanvasGroup>(); if ((Object)(object)val == (Object)null) { val = _popupBackground.AddComponent<CanvasGroup>(); } val.alpha = 1f; val.blocksRaycasts = true; val.interactable = true; DisableExtraPopupButtons(); SetText(_headerText, "$tag_brs_popup_header_bal"); SetText(_bodyText, "$tag_brs_popup_wait_for_revive_bal"); SetButtonLabel(_buttonYes, _buttonYesText, "$tag_brs_button_resurrect_self_bal"); SetButtonLabel(_buttonNo, _buttonNoText, "$tag_brs_button_release_soul_bal"); SetButtonLabel(_buttonOk, _buttonOkText, "$tag_brs_button_release_soul_bal"); Navigation navigation = ((Selectable)_buttonYes).navigation; ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)_buttonYes).navigation = navigation; Navigation navigation2 = ((Selectable)_buttonNo).navigation; ((Navigation)(ref navigation2)).mode = (Mode)0; ((Selectable)_buttonNo).navigation = navigation2; Navigation navigation3 = ((Selectable)_buttonOk).navigation; ((Navigation)(ref navigation3)).mode = (Mode)0; ((Selectable)_buttonOk).navigation = navigation3; if ((Object)(object)_buttonOk != (Object)(object)_buttonNo && (Object)(object)_buttonOk != (Object)(object)_buttonYes) { ((Component)_buttonOk).gameObject.SetActive(false); } } } private void WireButtons() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown if ((Object)(object)_buttonYes != (Object)null) { _buttonYes.onClick = new ButtonClickedEvent(); ((UnityEvent)_buttonYes.onClick).AddListener(new UnityAction(OnSelfReviveClicked)); } if ((Object)(object)_buttonNo != (Object)null) { _buttonNo.onClick = new ButtonClickedEvent(); ((UnityEvent)_buttonNo.onClick).AddListener(new UnityAction(OnReleaseSoulClicked)); } if ((Object)(object)_buttonOk != (Object)null && (Object)(object)_buttonOk != (Object)(object)_buttonNo && (Object)(object)_buttonOk != (Object)(object)_buttonYes) { _buttonOk.onClick = new ButtonClickedEvent(); ((UnityEvent)_buttonOk.onClick).AddListener(new UnityAction(OnReleaseSoulClicked)); } } private void Update() { if (!_isSetup || (Object)(object)_downedPopupRoot == (Object)null || Time.time < _nextRefreshTime) { return; } _nextRefreshTime = Time.time + 0.05f; if (_suppressReleaseUntilEscapeUp && !Input.GetKey((KeyCode)27)) { _suppressReleaseUntilEscapeUp = false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !localPlayer.IsDowned()) { HidePopup(); } else if (Menu.IsVisible()) { HidePopup(); if ((Object)(object)EventSystem.current != (Object)null) { EventSystem.current.SetSelectedGameObject((GameObject)null); } } else { RefreshPopup(localPlayer); } } private void RefreshPopup(Player player) { ShowPopup(); int num = Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value); string resurrectionItemDisplayName = GetResurrectionItemDisplayName(); bool value = BRSConfig.AllowSelfPotionRevive.Value; bool value2 = BRSConfig.RequireItemForSelfRevive.Value; ItemData foundItem; bool flag = BRSItemUtils.TryFindResurrectionPotion(player, out foundItem); bool flag2 = false; string reason = string.Empty; if (value) { flag2 = BRSReviveRules.CanSelfRevive(player, out reason); } else { reason = "$tag_brs_reason_self_revive_disabled_bal"; } string text = "$tag_brs_popup_wait_for_revive_bal\n\n$tag_brs_release_in_bal " + player.GetRemainingDownedSeconds().ToString("0") + "s"; if (value) { if (value2) { if (flag && flag2) { text = text + "\n\n$tag_brs_popup_self_revive_use_item_bal " + num + "x " + resurrectionItemDisplayName + " $tag_brs_popup_self_revive_suffix_bal"; } else if (!string.IsNullOrEmpty(reason)) { text = text + "\n\n" + reason; } } else if (flag2) { text += "\n\n$tag_brs_popup_self_revive_free_bal"; } else if (!string.IsNullOrEmpty(reason)) { text = text + "\n\n" + reason; } } DisableExtraPopupButtons(); SetText(_headerText, "$tag_brs_popup_header_bal"); SetText(_bodyText, text); SetButtonLabel(_buttonYes, _buttonYesText, "$tag_brs_button_resurrect_self_bal"); SetButtonLabel(_buttonNo, _buttonNoText, "$tag_brs_button_release_soul_bal"); SetButtonLabel(_buttonOk, _buttonOkText, "$tag_brs_button_release_soul_bal"); bool flag3 = value; if ((Object)(object)_buttonYes != (Object)null) { ((Component)_buttonYes).gameObject.SetActive(flag3); ((Selectable)_buttonYes).interactable = flag2; } if ((Object)(object)_buttonNo != (Object)null) { ((Component)_buttonNo).gameObject.SetActive(flag3 || (Object)(object)_buttonOk == (Object)(object)_buttonNo); ((Selectable)_buttonNo).interactable = true; } if ((Object)(object)_buttonOk != (Object)null && (Object)(object)_buttonOk != (Object)(object)_buttonNo && (Object)(object)_buttonOk != (Object)(object)_buttonYes) { ((Component)_buttonOk).gameObject.SetActive(!flag3); ((Selectable)_buttonOk).interactable = true; } bool flag4 = (Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus(); if (!flag4) { _downedPopupRoot.transform.SetAsLastSibling(); } if (flag4 || !((Object)(object)EventSystem.current != (Object)null)) { return; } GameObject currentSelectedGameObject = EventSystem.current.currentSelectedGameObject; if ((Object)(object)currentSelectedGameObject == (Object)null || !currentSelectedGameObject.activeInHierarchy || !currentSelectedGameObject.transform.IsChildOf(_downedPopupRoot.transform)) { if (flag3) { EventSystem.current.SetSelectedGameObject((GameObject)null); } else if ((Object)(object)_buttonOk != (Object)null && ((Component)_buttonOk).gameObject.activeInHierarchy) { EventSystem.current.SetSelectedGameObject(((Component)_buttonOk).gameObject); } } } private static string GetResurrectionItemDisplayName() { if (!string.IsNullOrEmpty(_cachedItemDisplayName)) { return _cachedItemDisplayName; } string value = BRSConfig.ResurrectionPotionPrefabName.Value; if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_items == null) { return value; } for (int i = 0; i < ObjectDB.instance.m_items.Count; i++) { GameObject val = ObjectDB.instance.m_items[i]; if (!((Object)(object)val == (Object)null) && string.Equals(((Object)val).name, value, StringComparison.OrdinalIgnoreCase)) { ItemDrop component = val.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { return value; } string name = component.m_itemData.m_shared.m_name; if (string.IsNullOrEmpty(name)) { return value; } _cachedItemDisplayName = ((Localization.instance != null) ? Localization.instance.Localize(name) : name); return _cachedItemDisplayName; } } return value; } private void ShowPopup() { CaptureInput(); if ((Object)(object)_downedPopupRoot != (Object)null && !_downedPopupRoot.activeSelf) { _downedPopupRoot.SetActive(true); } if ((Object)(object)_popupBackground != (Object)null && !_popupBackground.activeSelf) { _popupBackground.SetActive(true); } if ((Object)(object)_popupPanel != (Object)null && !_popupPanel.activeSelf) { _popupPanel.SetActive(true); } if ((Object)(object)_popupBkg != (Object)null && !_popupBkg.activeSelf) { _popupBkg.SetActive(true); } } private void HidePopup() { ReleaseInputCapture(); if ((Object)(object)_popupBackground != (Object)null) { _popupBackground.SetActive(false); } if ((Object)(object)_downedPopupRoot != (Object)null) { _downedPopupRoot.SetActive(false); } } private void CaptureInput() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) _wantPopupCursor = true; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { localPlayer.SetMouseLook(Vector2.zero); } if ((Object)(object)GameCamera.instance != (Object)null && ((Behaviour)GameCamera.instance).enabled) { ((Behaviour)GameCamera.instance).enabled = false; } Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } private void ReleaseInputCapture() { _wantPopupCursor = false; if (!Menu.IsVisible()) { if ((Object)(object)GameCamera.instance != (Object)null && !((Behaviour)GameCamera.instance).enabled) { ((Behaviour)GameCamera.instance).enabled = true; } Cursor.lockState = (CursorLockMode)1; Cursor.visible = false; } } private void OnSelfReviveClicked() { try { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && localPlayer.IsDowned()) { Debug.Log((object)"[BalrondSecondChance] Self-revive UI button clicked."); BRSDownedBehaviour component = ((Component)localPlayer).GetComponent<BRSDownedBehaviour>(); if (!((Object)(object)component == (Object)null)) { component.TrySelfReviveLocal(); } } } catch (Exception ex) { Debug.LogError((object)("[BRSHudController] Exception in OnSelfReviveClicked: " + ex)); } } private void OnReleaseSoulClicked() { try { if (!_suppressReleaseUntilEscapeUp) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && localPlayer.IsDowned()) { localPlayer.SetReleaseRequested(value: true); } } } catch (Exception ex) { Debug.LogError((object)("[BRSHudController] Exception in OnReleaseSoulClicked: " + ex)); } } private void LateUpdate() { if (_isSetup && Input.GetKeyDown((KeyCode)27)) { _suppressReleaseUntilEscapeUp = true; if ((Object)(object)EventSystem.current != (Object)null) { EventSystem.current.SetSelectedGameObject((GameObject)null); } } } } [HarmonyPatch(typeof(Hud), "Awake")] internal static class BRSHudAwakePatch { private static void Postfix(Hud __instance) { BRSHudController.EnsureInstance(); BRSHudController.Instance.BindHud(__instance); } } [HarmonyPatch(typeof(Hud), "OnDestroy")] internal static class BRSHudOnDestroyPatch { private static void Prefix(Hud __instance) { if (BRSHudController.HasInstance) { BRSHudController.Instance.UnbindHud(__instance); } } } [HarmonyPatch(typeof(PlayerController), "TakeInput")] internal static class BRSPlayerControllerTakeInputPatch { private static bool Prefix(ref bool __result) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !localPlayer.IsDowned()) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Player), "Update")] internal static class BRSPlayerUpdatePatch { private static bool Prefix(Player __instance) { if ((Object)(object)__instance == (Object)null) { return true; } if ((Object)(object)((Character)__instance).m_nview == (Object)null || !((Character)__instance).m_nview.IsValid() || !((Character)__instance).m_nview.IsOwner()) { return true; } if (!__instance.IsDowned()) { return true; } return false; } } [HarmonyPatch(typeof(Player), "UseHotbarItem")] internal static class BRSUseHotbarItemPatch { private static bool Prefix(Player __instance) { return (Object)(object)__instance == (Object)null || !__instance.IsDowned(); } } [HarmonyPatch(typeof(Player), "HandleRadialInput")] internal static class BRSHandleRadialInputPatch { private static bool Prefix(Player __instance) { return (Object)(object)__instance == (Object)null || !__instance.IsDowned(); } } [HarmonyPatch(typeof(Player), "UpdatePlacement")] internal static class BRSUpdatePlacementPatch { private static bool Prefix(Player __instance) { return (Object)(object)__instance == (Object)null || !__instance.IsDowned(); } } [HarmonyPatch(typeof(ZInput), "IsMouseActive")] internal static class BRSZInputIsMouseActivePatch { private static void Postfix(ref bool __result) { if (BRSHudController.HasInstance && BRSHudController.Instance.ShouldForceMouseActive()) { __result = true; } } } internal static class BRSItemUtils { internal static bool TryFindResurrectionPotion(Player player, out ItemData foundItem) { foundItem = null; if ((Object)(object)player == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } string value = BRSConfig.ResurrectionPotionPrefabName.Value; int num = Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null) { bool flag = (Object)(object)allItem.m_dropPrefab != (Object)null && string.Equals(((Object)allItem.m_dropPrefab).name, value, StringComparison.OrdinalIgnoreCase); bool flag2 = allItem.m_shared != null && !string.IsNullOrEmpty(allItem.m_shared.m_name) && string.Equals(allItem.m_shared.m_name, value, StringComparison.OrdinalIgnoreCase); if ((flag || flag2) && allItem.m_stack >= num) { foundItem = allItem; return true; } } } return false; } internal static bool ConsumeResurrectionPotion(Player player) { if ((Object)(object)player == (Object)null) { return false; } if (!TryFindResurrectionPotion(player, out var foundItem)) { return false; } return ConsumeSpecificItem(player, foundItem, Mathf.Max(1, BRSConfig.ResurrectionItemAmount.Value)); } internal static bool ConsumeSpecificItem(Player player, ItemData item, int amount) { if ((Object)(object)player == (Object)null || item == null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } amount = Mathf.Max(1, amount); if (item.m_stack < amount) { return false; } inventory.RemoveItem(item, amount); player.OnInventoryChanged(); return true; } } internal static class BRSKeys { public const string Downed = "brs_downed"; public const string DownedUntil = "brs_downed_until"; public const string DownedUntilNetworkTicks = "brs_downed_until_net_ticks"; public const string ReleaseRequested = "brs_release_requested"; public const string LastPotionReviveTime = "brs_last_potion_revive_time"; } internal static class BRSPlayerExtensions { private static ZDO GetValidZdo(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)((Character)player).m_nview == (Object)null || !((Character)player).m_nview.IsValid()) { return null; } return ((Character)player).m_nview.GetZDO(); } public static bool IsDowned(this Player player) { ZDO validZdo = GetValidZdo(player); return validZdo != null && validZdo.GetBool("brs_downed", false); } public static float GetDownedUntil(this Player player) { ZDO validZdo = GetValidZdo(player); return (validZdo != null) ? validZdo.GetFloat("brs_downed_until", 0f) : 0f; } public static long GetDownedUntilNetworkTicks(this Player player) { ZDO validZdo = GetValidZdo(player); return (validZdo != null) ? validZdo.GetLong("brs_downed_until_net_ticks", 0L) : 0; } public static void SetDowned(this Player player, bool value, float until) { ZDO validZdo = GetValidZdo(player); if (validZdo != null) { validZdo.Set("brs_downed", value); validZdo.Set("brs_downed_until", value ? until : 0f); long num = 0L; if (value && (Object)(object)ZNet.instance != (Object)null) { float num2 = Mathf.Max(0f, until - Time.time); num = ZNet.instance.GetTime().AddSeconds(num2).Ticks; } validZdo.Set("brs_downed_until_net_ticks", num); validZdo.Set("brs_release_requested", false); } } public static bool ReleaseRequested(this Player player) { ZDO validZdo = GetValidZdo(player); return validZdo != null && validZdo.GetBool("brs_release_requested", false); } public static void SetReleaseRequested(this Player player, bool value) { ZDO validZdo = GetValidZdo(player); if (validZdo != null) { validZdo.Set("brs_release_requested", value); } } public static void RequestRelease(this Player player) { player.SetReleaseRequested(value: true); } public static void ClearReleaseRequest(this Player player) { player.SetReleaseRequested(value: false); } public static long GetLastPotionReviveTicks(this Player player) { ZDO validZdo = GetValidZdo(player); return (validZdo != null) ? validZdo.GetLong("brs_last_potion_revive_time", 0L) : 0; } public static void SetLastPotionReviveTicks(this Player player, long ticks) { ZDO validZdo = GetValidZdo(player); if (validZdo != null) { validZdo.Set("brs_last_potion_revive_time", ticks); } } public static float GetRemainingDownedSeconds(this Player player) { long downedUntilNetworkTicks = player.GetDownedUntilNetworkTicks(); if (downedUntilNetworkTicks > 0 && (Object)(object)ZNet.instance != (Object)null) { try { DateTime dateTime = new DateTime(downedUntilNetworkTicks); return Mathf.Max(0f, (float)(dateTime - ZNet.instance.GetTime()).TotalSeconds); } catch { } } return Mathf.Max(0f, player.GetDownedUntil() - Time.time); } public static bool HasActivePotionReviveCooldown(this Player player) { return player.GetRemainingPotionReviveCooldownMinutes() > 0f; } public static float GetRemainingPotionReviveCooldownMinutes(this Player player) { long lastPotionReviveTicks = player.GetLastPotionReviveTicks(); if (lastPotionReviveTicks <= 0) { return 0f; } float value = BRSConfig.ResurrectionCooldownMinutes.Value; if (value <= 0f) { return 0f; } DateTime dateTime; try { dateTime = new DateTime(lastPotionReviveTicks, DateTimeKind.Utc); } catch { return 0f; } double totalMinutes = (DateTime.UtcNow - dateTime).TotalMinutes; return Mathf.Max(0f, value - (float)totalMinutes); } } [HarmonyPatch(typeof(Player), "GetHoverText")] internal static class BRSPlayerHoverTextPatch { private static bool Prefix(Player __instance, ref string __result) { if ((Object)(object)__instance == (Object)null || !__instance.IsDowned()) { return true; } BRSDownedBehaviour component = ((Component)__instance).GetComponent<BRSDownedBehaviour>(); if ((Object)(object)component == (Object)null) { return true; } __result = component.GetHoverText(); return false; } } internal static class BRSPlayerPresentation { private const string AttachRootName = "BRS_DownedAttachRoot"; private const string AttachPointName = "attachpoint"; private const string BedAnimation = "attach_bed"; private const string SitAnimation = "emote_sit"; private static readonly Vector3 DetachOffset = new Vector3(0f, 0.5f, 0f); private static readonly Dictionary<string, string> DownedAnimations = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Sit", "emote_sit" }, { "Bed", "attach_bed" }, { "Dance", "emote_dance" }, { "Headbang", "emote_headbang" } }; internal static void SetDownedPresentation(Player player, bool downed) { if ((Object)(object)player == (Object)null) { return; } bool flag = (Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid() && ((Character)player).IsOwner(); if (!downed) { BRSDownedVisualState component = ((Component)player).GetComponent<BRSDownedVisualState>(); if (!((Object)(object)component == (Object)null)) { ClearObserverAnimation(player, component); if (flag) { DetachPlayer(player, component); } else { ClearLocalAttachBookkeeping(component); } DestroyAnchor(component); } } else { BRSDownedVisualState orCreateState = GetOrCreateState(player); string configuredAnimation = GetConfiguredAnimation(); if (flag) { ClearObserverAnimation(player, orCreateState); EnsureAnchorAtPlayer(player, orCreateState); ApplyDownedPhysics(player); AttachPlayer(player, orCreateState, configuredAnimation); } else { ApplyObserverAnimation(player, orCreateState, configuredAnimation); } } } private static BRSDownedVisualState GetOrCreateState(Player player) { BRSDownedVisualState bRSDownedVisualState = ((Component)player).GetComponent<BRSDownedVisualState>(); if ((Object)(object)bRSDownedVisualState == (Object)null) { bRSDownedVisualState = ((Component)player).gameObject.AddComponent<BRSDownedVisualState>(); } return bRSDownedVisualState; } private static void EnsureAnchorAtPlayer(Player player, BRSDownedVisualState state) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown if ((Object)(object)state.AnchorRoot == (Object)null) { state.AnchorRoot = new GameObject("BRS_DownedAttachRoot"); ((Object)state.AnchorRoot).hideFlags = (HideFlags)61; state.AnchorRoot.layer = ((Component)player).gameObject.layer; } state.AnchorRoot.transform.position = ((Component)player).transform.position; state.AnchorRoot.transform.rotation = ((Component)player).transform.rotation; if ((Object)(object)state.AttachPoint == (Object)null) { GameObject val = new GameObject("attachpoint"); ((Object)val).hideFlags = (HideFlags)61; val.transform.SetParent(state.AnchorRoot.transform, false); state.AttachPoint = val.transform; } state.AttachPoint.localPosition = Vector3.zero; state.AttachPoint.localRotation = Quaternion.identity; } private static void ApplyDownedPhysics(Player player) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)((Character)player).m_body != (Object)null) { ((Character)player).m_body.linearVelocity = Vector3.zero; ((Character)player).m_body.angularVelocity = Vector3.zero; } } catch { } } private static void AttachPlayer(Player player, BRSDownedVisualState state, string wantedAnimation) { if ((Object)(object)state.AttachPoint == (Object)null) { return; } if (!state.Attached || !string.Equals(state.AnimationName, wantedAnimation, StringComparison.Ordinal)) { if (state.Attached) { DetachPlayer(player, state); } string text = wantedAnimation; bool flag = TryAttach(player, state.AttachPoint, text); if (!flag && !string.Equals(text, "emote_sit", StringComparison.Ordinal)) { text = "emote_sit"; flag = TryAttach(player, state.AttachPoint, text); } if (flag) { SnapPlayerToAnchor(player, state.AttachPoint); state.Attached = true; state.AnimationName = text; } } else { SnapPlayerToAnchor(player, state.AttachPoint); } } private static string GetConfiguredAnimation() { string text = ((BRSConfig.DownedPresentationMode != null) ? BRSConfig.DownedPresentationMode.Value : "Sit"); if (string.IsNullOrWhiteSpace(text)) { return "emote_sit"; } if (DownedAnimations.TryGetValue(text.Trim(), out var value)) { return value; } return "emote_sit"; } private static bool TryAttach(Player player, Transform attachPoint, string animationName) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) try { ((Character)player).AttachStart(attachPoint, (GameObject)null, false, false, false, animationName, DetachOffset, (Transform)null); return true; } catch { return false; } } private static void SnapPlayerToAnchor(Player player, Transform attachPoint) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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) Quaternion rotation = attachPoint.rotation; Vector3 position = attachPoint.position; try { ((Component)player).transform.position = position; ((Component)player).transform.rotation = rotation; } catch { } try { if ((Object)(object)((Character)player).m_body != (Object)null) { ((Character)player).m_body.position = position; ((Character)player).m_body.rotation = rotation; ((Character)player).m_body.linearVelocity = Vector3.zero; ((Character)player).m_body.angularVelocity = Vector3.zero; } } catch { } } private static void ApplyObserverAnimation(Player player, BRSDownedVisualState state, string wantedAnimation) { ZSyncAnimation val = (((Object)(object)player != (Object)null) ? ((Character)player).m_zanim : null); if ((Object)(object)val == (Object)null) { return; } string text = (HasBoolParameter(val, wantedAnimation) ? wantedAnimation : "emote_sit"); if (HasBoolParameter(val, text)) { if (!string.IsNullOrEmpty(state.ObserverAnimationName) && !string.Equals(state.ObserverAnimationName, text, StringComparison.Ordinal)) { SetSyncedAnimatorBoolLocal(val, state.ObserverAnimationName, value: false); state.ObserverAnimationName = string.Empty; } SetSyncedAnimatorBoolLocal(val, text, value: true); state.ObserverAnimationName = text; } } private static void ClearObserverAnimation(Player player, BRSDownedVisualState state) { if (!string.IsNullOrEmpty(state.ObserverAnimationName)) { ZSyncAnimation val = (((Object)(object)player != (Object)null) ? ((Character)player).m_zanim : null); if ((Object)(object)val != (Object)null) { SetSyncedAnimatorBoolLocal(val, state.ObserverAnimationName, value: false); } state.ObserverAnimationName = string.Empty; } } private static bool HasBoolParameter(ZSyncAnimation syncAnimation, string parameterName) { if ((Object)(object)syncAnimation == (Object)null || string.IsNullOrEmpty(parameterName)) { return false; } try { return syncAnimation.HasParameter(parameterName, (AnimatorControllerParameterType)4); } catch { return false; } } private static void SetSyncedAnimatorBoolLocal(ZSyncAnimation syncAnimation, string parameterName, bool value) { if ((Object)(object)syncAnimation == (Object)null || string.IsNullOrEmpty(parameterName)) { return; } try { if (HasBoolParameter(syncAnimation, parameterName)) { syncAnimation.SetBool(parameterName, value); } } catch { } } private static void DetachPlayer(Player player, BRSDownedVisualState state) { if (state.Attached) { try { ((Character)player).AttachStop(); } catch { } state.Attached = false; state.AnimationName = string.Empty; } } private static void ClearLocalAttachBookkeeping(BRSDownedVisualState state) { state.Attached = false; state.AnimationName = string.Empty; } private static void DestroyAnchor(BRSDownedVisualState state) { state.AttachPoint = null; if ((Object)(object)state.AnchorRoot != (Object)null) { Object.Destroy((Object)(object)state.AnchorRoot); state.AnchorRoot = null; } } } internal sealed class BRSDownedVisualState : MonoBehaviour { internal GameObject AnchorRoot; internal Transform AttachPoint; internal bool Attached; internal string AnimationName = string.Empty; internal string ObserverAnimationName = string.Empty; private void OnDestroy() { if ((Object)(object)AnchorRoot != (Object)null) { Object.Destroy((Object)(object)AnchorRoot); } } } internal static class BRSReviveRules { internal static bool IsWithinReviveDistance(Player reviver, Player target) { //IL_0037: 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)reviver == (Object)null || (Object)(object)target == (Object)null) { return false; } float num = Mathf.Max(0f, BRSConfig.ReviveDistance.Value); return Vector3.Distance(((Component)reviver).transform.position, ((Component)target).transform.position) <= num; } internal static bool IsPotionReviveOnCooldown(Player target, out float remainingMinutes) { remainingMinutes = 0f; if ((Object)(object)target == (Object)null) { return false; } float num = Mathf.Max(0f, BRSConfig.ResurrectionCooldownMinutes.Value); if (num <= 0f) { return false; } long lastPotionReviveTicks = target.GetLastPotionReviveTicks(); if (lastPotionReviveTicks <= 0) { return false; } DateTime dateTime; try { dateTime = new DateTime(lastPotionReviveTicks, DateTimeKind.Utc); } catch { return false; } double totalMinutes = (DateTime.UtcNow - dateTime).TotalMinutes; double num2 = (double)num - totalMinutes; if (num2 <= 0.0) { return false; } remainingMinutes = (float)num2; return true; } internal static bool CanFriendRevive(Player reviver, Player target, out string reason) { reason = string.Empty; if ((Object)(object)reviver == (Object)null) { reason = "$tag_brs_reason_no_reviver_bal"; return false; } if ((Object)(object)target == (Object)null) { reason = "$tag_brs_reason_invalid_revive_target_bal"; return false; } if ((Object)(object)reviver == (Object)(object)target) { reason = "$tag_brs_reason_use_self_revive_instead_bal"; return false; } if (!target.IsDowned()) { reason = "$tag_brs_reason_target_not_downed_bal"; return false; } if (!IsWithinReviveDistance(reviver, target)) { reason = "$msg_toofar"; return false; } if (IsPotionReviveOnCooldown(target, out var remainingMinutes)) { reason = BuildCooldownReason(remainingMinutes); return false; } if (BRSConfig.RequirePotionForFriendRevive.Value && !BRSItemUtils.TryFindResurrectionPotion(reviver, out var _)) { reason = "$tag_brs_need_item_bal " + BRSConfig.ResurrectionPotionPrefabName.Value + "."; return false; } return true; } internal static bool CanSelfRevive(Player target, out string reason) { reason = string.Empty; if ((Object)(object)target == (Object)null) { reason = "$tag_brs_reason_invalid_player_bal"; return false; } if (!target.IsDowned()) { reason = "$tag_brs_reason_you_are_not_downed_bal"; return false; } if (!BRSConfig.AllowSelfPotionRevive.Value) { reason = "$tag_brs_reason_self_revive_disabled_bal"; return false; } if (IsPotionReviveOnCooldown(target, out var remainingMinutes)) { reason = BuildCooldownReason(remainingMinutes); return false; } if (BRSConfig.RequireItemForSelfRevive.Value && !BRSItemUtils.TryFindResurrectionPotion(target, out var _)) { reason = "$tag_brs_need_item_bal " + BRSConfig.ResurrectionPotionPrefabName.Value + " $tag_brs_in_your_inventory_bal"; return false; } return true; } internal static void MarkPotionReviveUsed(Player target) { if (!((Object)(object)target == (Object)null)) { target.SetLastPotionReviveTicks(DateTime.UtcNow.Ticks); BRSStatusEffectManager.ApplyCooldownVisual(target); } } internal static string BuildCooldownReason(float remainingMinutes) { return "$tag_brs_reason_soul_unstable_bal " + Mathf.Max(0f, remainingMinutes).ToString("0.0") + " $tag_brs_reason_cooldown_left_bal"; } } internal static class BRSStatusEffectManager { private const string SourceEffectName = "SoftDeath"; private const string CooldownEffectObjectName = "RessurectCooldown_bal"; private const string CooldownDisplayName = "$tag_brs_ressurection_sickness_bal"; private const string CooldownTooltip = "$tag_brs_ressurection_sickness_tooltip_bal"; private static StatusEffect _cachedEffect; internal static void ResetCachedEffect() { _cachedEffect = null; } internal static StatusEffect EnsureCooldownEffect() { if ((Object)(object)_cachedEffect != (Object)null) { return _cachedEffect; } if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } StatusEffect val = FindEffectByObjectName("RessurectCooldown_bal"); if ((Object)(object)val != (Object)null) { _cachedEffect = val; return _cachedEffect; } StatusEffect val2 = FindEffectByObjectName("SoftDeath"); if ((Object)(object)val2 == (Object)null) { return null; } StatusEffect val3 = Object.Instantiate<StatusEffect>(val2); ((Object)val3).name = "RessurectCooldown_bal"; val3.m_name = "$tag_brs_ressurection_sickness_bal"; val3.m_tooltip = "$tag_brs_ressurection_sickness_tooltip_bal"; val3.m_ttl = Mathf.Max(1f, BRSConfig.ResurrectionCooldownMinutes.Value * 60f); if (ObjectDB.instance.m_StatusEffects == null) { ObjectDB.instance.m_StatusEffects = new List<StatusEffect>(); } ObjectDB.instance.m_StatusEffects.Add(val3); _cachedEffect = val3; return _cachedEffect; } internal static void ApplyCooldownVisual(Player player) { if ((Object)(object)player == (Object)null) { return; } EnsureCooldownEffect(); SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan == null) { return; } int stableHashCode = StringExtensionMethods.GetStableHashCode("RessurectCooldown_bal"); StatusEffect statusEffect = sEMan.GetStatusEffect(stableHashCode); if ((Object)(object)statusEffect != (Object)null) { sEMan.RemoveStatusEffect(stableHashCode, true); } sEMan.AddStatusEffect(stableHashCode, true, 0, 0f, (short)(-1)); StatusEffect statusEffect2 = sEMan.GetStatusEffect(stableHashCode); if ((Object)(object)statusEffect2 != (Object)null) { float num = player.GetRemainingPotionReviveCooldownMinutes(); if (num <= 0f) { num = Mathf.Max(0f, BRSConfig.ResurrectionCooldownMinutes.Value); } statusEffect2.m_ttl = Mathf.Max(1f, num * 60f); } } private static StatusEffect FindEffectByObjectName(string objectName) { if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_StatusEffects == null) { return null; } for (int i = 0; i < ObjectDB.instance.m_StatusEffects.Count; i++) { StatusEffect val = ObjectDB.instance.m_StatusEffects[i]; if (!((Object)(object)val == (Object)null) && ((Object)val).name == objectName) { return val; } } return null; } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class BRSObjectDBAwakeStatusEffectPatch { private static void Postfix() { BRSStatusEffectManager.ResetCachedEffect(); BRSStatusEffectManager.EnsureCooldownEffect(); BRSHudController.ClearCachedItemDisplayName(); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class BRSObjectDBCopyOtherDBStatusEffectPatch { private static void Postfix() { BRSStatusEffectManager.ResetCachedEffect(); BRSStatusEffectManager.EnsureCooldownEffect(); BRSHudController.ClearCachedItemDisplayName(); } } public class JsonLoader { public string defaultPath = string.Empty; public void loadJson() { LoadTranslations(); justDefaultPath(); } public void justDefaultPath() { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "BalrondSecondChance-translation/"); defaultPath = text; } public void createDefaultPath() { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "BalrondSecondChance-translation/"); if (!Directory.Exists(text)) { CreateFolder(text); } else { Debug.Log((object)("BalrondSecondChance: Folder already exists: " + text)); } defaultPath = text; } private string[] jsonFilePath(string folderName, string extension) { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "BalrondSecondChance-translation/"); if (!Directory.Exists(text)) { CreateFolder(text); } else { Debug.Log((object)("BalrondSecondChance: Folder already exists: " + text)); } string[] files = Directory.GetFiles(text, extension); Debug.Log((object)("BalrondSecondChance:" + folderName + " Json Files Found: " + files.Length)); return files; } private static void CreateFolder(string path) { try { Directory.CreateDirectory(path); Debug.Log((object)"BalrondSecondChance: Folder created successfully."); } catch (Exception ex) { Debug.Log((object)("BalrondSecondChance: Error creating folder: " + ex.Message)); } } private void LoadTranslations() { int num = 0; string[] array = jsonFilePath("Translation", "*.json"); foreach (string text in array) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); string json = File.ReadAllText(text); JsonData jsonData = JsonMapper.ToObject(json); Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (string key in jsonData.Keys) { dictionary[key] = jsonData[key].ToString(); } if (dictionary != null) { BalrondTranslator.translations.Add(fileNameWithoutExtension, dictionary); Debug.Log((object)("BalrondSecondChance: Json Files Language: " + fileNameWithoutExtension)); num++; } else { Debug.LogError((object)("BalrondSecondChance: Loading FAILED file: " + text)); } } Debug.Log((object)("BalrondSecondChance: Translation JsonFiles Loaded: " + num)); } } [BepInPlugin("balrond.astafaraios.BalrondSecondChance", "BalrondSecondChance", "1.0.4")] public class Launch : BaseUnityPlugin { private readonly Harmony harmony = new Harmony("balrond.astafaraios.BalrondSecondChance"); public const string PluginGUID = "balrond.astafaraios.BalrondSecondChance"; public const string PluginName = "BalrondSecondChance"; public const string PluginVersion = "1.0.4"; public static JsonLoader jsonLoader = new JsonLoader(); internal static Launch Instance; internal static Harmony Harmony; internal static bool AllowOriginalDeath; internal static readonly ConfigSync configSync = new ConfigSync("balrond.astafaraios.BalrondSecondChance") { DisplayName = "BalrondSecondChance", CurrentVersion = "1.0.4", MinimumRequiredVersion = "1.0.4" }; internal static Launch _self; internal ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, description); SyncedConfigEntry<T> syncedConfigEntry = configSync.AddConfigEntry<T>(val); syncedConfigEntry.SynchronizedConfig = synchronizedSetting; return val; } internal ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } private void Awake() { Instance = this; _self = this; AudioRoutingService.ConfigureLogging(delegate(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); }, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogError((object)message); }); AudioRoutingService.DebugLogging = false; jsonLoader.loadJson(); BRSConfig.Init(this); if (BRSConfig.LockConfiguration != null) { configSync.AddLockingConfigEntry<bool>(BRSConfig.LockConfiguration); } Harmony = harmony; harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"BalrondSecondChance loaded."); } private void OnDestroy() { Harmony obj = Harmony; if (obj != null) { obj.UnpatchSelf(); } } } [HarmonyPatch] internal static class TranslationPatches { [HarmonyPatch(typeof(FejdStartup), "SetupGui")] private class FejdStartup_SetupGUI { private static void Postfix() { string selectedLanguage = Localization.instance.GetSelectedLanguage(); Dictionary<string, string> translations = GetTranslations(selectedLanguage); AddTranslations(translations); } } [HarmonyPriority(800)] [HarmonyPatch(typeof(Localization), "SetupLanguage")] private class Translation_SetupLanguage { private static void Prefix(Localization __instance, string language) { Dictionary<string, string> translations = GetTranslations(language); AddTranslations(translations, __instance); } } [HarmonyPriority(800)] [HarmonyPatch(typeof(Localization), "LoadCSV")] private class Translation_LoadCSV { private static void Prefix(Localization __instance, string language) { Dictionary<string, string> translations = GetTranslations(language); AddTranslations(translations, __instance); } } private static Dictionary<string, string> GetTranslations(string language) { Dictionary<string, string> result = BalrondTranslator.getLanguage("English"); if (!string.Equals(language, "English", StringComparison.OrdinalIgnoreCase)) { Dictionary<string, string> language2 = BalrondTranslator.getLanguage(language); if (language2 != null) { result = language2; } else { Debug.Log((object)("BalrondSecondChance: Did not find translation file for '" + language + "', loading English")); } } return result; } private static void AddTranslations(Dictionary<string, string> translations, Localization localizationInstance = null) { if (translations == null) { Debug.LogWarning((object)"BalrondSecondChance: No translation file found!"); return; } if (localizationInstance != null) { foreach (KeyValuePair<string, string> translation in translations) { localizationInstance.AddWord(translation.Key, translation.Value); } return; } foreach (KeyValuePair<string, string> translation2 in translations) { Localization.instance.AddWord(translation2.Key, translation2.Value); } } } } namespace Balrond.Shared { public static class AudioRoutingService { public enum AudioRoute { SFX, Ambient, Music, Master } private enum TargetKind { Route, NamedRuntimeGroup } private sealed class AudioTarget { public TargetKind Kind; public AudioRoute Route; public string RuntimeGroupName; public string Reason; } private sealed class AudioBinding { public AudioSource Source; public AudioTarget Target; } private sealed class ProcessedNode { public GameObject Root; public readonly List<AudioBinding> AudioBindings = new List<AudioBinding>(); public readonly List<GameObject> ReferencedRoots = new List<GameObject>(); public readonly HashSet<GameObject> ReferencedRootSet = new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance); } private sealed class ExplicitOverride { public bool UsesNamedGroup; public AudioRoute Route; public string RuntimeGroupName; } private sealed class RuntimeMixerCatalog { private readonly Dictionary<string, AudioMixerGroup> _groupsByName = new Dictionary<string, AudioMixerGroup>(StringComparer.OrdinalIgnoreCase); private AudioMixer _mixer; private AudioMixerGroup _master; private AudioMixerGroup _ambient; private AudioMixerGroup _music; private AudioMixerGroup _sfx; public bool IsReady => (Object)(object)_master != (Object)null || (Object)(object)_ambient != (Object)null || (Object)(object)_music != (Object)null || (Object)(object)_sfx != (Object)null; public void Rebuild(AudioMan audioMan) { _groupsByName.Clear(); _mixer = null; _master = null; _ambient = null; _music = null; _sfx = null; if ((Object)(object)audioMan == (Object)null) { return; } _master = ReadAudioManGroup(audioMan, "m_masterMixer"); _ambient = ReadAudioManGroup(audioMan, "m_ambientMixer"); _music = ReadAudioManGroup(audioMan, "m_musicMixer"); _mixer = ReadAudioManMixer(audioMan, "m_masterMixer") ?? GetMixer(_master) ?? GetMixer(_ambient) ?? GetMixer(_music); AddGroup(_master); AddGroup(_ambient); AddGroup(_music); if ((Object)(object)_mixer != (Object)null) { try { AudioMixerGroup[] array = _mixer.FindMatchingGroups(string.Empty); if (array != null) { for (int i = 0; i < array.Length; i++) { AddGroup(array[i]); } } } catch (Exception ex) { Warn("Could not enumerate runtime AudioMixer groups: " + ex.Message); } } if ((Object)(object)_master == (Object)null) { _master = FindNamedGroup("Master") ?? QueryExactGroup("Master"); } if ((Object)(object)_ambient == (Object)null) { _ambient = FindNamedGroup("Ambient") ?? QueryExactGroup("Ambient"); } if ((Object)(object)_music == (Object)null) { _music = FindNamedGroup("Music") ?? QueryExactGroup("Music"); } _sfx = FindNamedGroup("SFX"); if ((Object)(object)_sfx == (Object)null) { _sfx = QueryExactGroup("SFX"); } if ((Object)(object)_sfx == (Object)null) { AudioMixerGroup val = FindNamedGroup("Effects"); if ((Object)(object)val == (Object)null) { val = QueryExactGroup("Effects"); } _sfx = val; } if ((Object)(object)_sfx == (Object)null) { _sfx = _master; WarnOnce("sfx-group-fallback", "Runtime SFX mixer group was not found. Falling back to the AudioMan master output group."); } } public AudioMixerGroup Resolve(AudioTarget target) { if (target == null) { return ResolveRoute(AudioRoute.SFX); } if (target.Kind == TargetKind.NamedRuntimeGroup) { AudioMixerGroup val = FindNamedGroup(target.RuntimeGroupName); if ((Object)(object)val == (Object)null) { val = QueryExactGroup(target.RuntimeGroupName); } if ((Object)(object)val != (Object)null) { return val; } return ResolveRoute(AudioRoute.SFX); } return ResolveRoute(target.Route); } public bool HasNamedGroup(string groupName) { if (string.IsNullOrEmpty(groupName)) { return false; } return (Object)(object)FindNamedGroup(groupName) != (Object)null || (Object)(object)QueryExactGroup(groupName) != (Object)null; } private AudioMixerGroup ResolveRoute(AudioRoute route) { return (AudioMixerGroup)(route switch { AudioRoute.Ambient => _ambient ?? _sfx ?? _master, AudioRoute.Music => _music ?? _sfx ?? _master, AudioRoute.Master => _master ?? _sfx ?? _ambient ?? _music, _ => _sfx ?? _master ?? _ambient ?? _music, }); } private AudioMixerGroup FindNamedGroup(string groupName) { if (!string.IsNullOrEmpty(groupName) && _groupsByName.TryGetValue(groupName, out var value)) { return value; } return null; } private AudioMixerGroup QueryExactGroup(string groupName) { if ((Object)(object)_mixer == (Object)null || string.IsNullOrEmpty(groupName)) { return null; } try { AudioMixerGroup[] array = _mixer.FindMatchingGroups(groupName); if (array == null) { return null; } foreach (AudioMixerGroup val in array) { AddGroup(val); if ((Object)(object)val != (Object)null && string.Equals(((Object)val).name, groupName, StringComparison.OrdinalIgnoreCase)) { return val; } } } catch (Exception ex) { WarnOnce("query-group-" + groupName, "Could not query runtime AudioMixer group '" + groupName + "': " + ex.Message); } return null; } private void AddGroup(AudioMixerGroup group) { if (!((Object)(object)group == (Object)null) && !string.IsNullOrEmpty(((Object)group).name) && !_groupsByName.ContainsKey(((Object)group).name)) { _groupsByName.Add(((Object)group).name, group); } } private static AudioMixer GetMixer(AudioMixerGroup group) { try { return ((Object)(object)group != (Object)null) ? group.audioMixer : null; } catch { return null; } } private static AudioMixer ReadAudioManMixer(AudioMan audioMan, string fieldName) { try { FieldInfo field = typeof(AudioMan).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return null; } object value = field.GetValue(audioMan); AudioMixer val = (AudioMixer)((value is AudioMixer) ? value : null); if ((Object)(object)val != (Object)null) { return val; } AudioMixerGroup val2 = (AudioMixerGroup)((value is AudioMixerGroup) ? value : null); if ((Object)(object)val2 != (Object)null) { return GetMixer(val2); } AudioSource val3 = (AudioSource)((value is AudioSource) ? value : null); if ((Object)(object)val3 != (Object)null) { return GetMixer(val3.outputAudioMixerGroup); } } catch (Exception ex) { WarnOnce("audioman-mixer-" + fieldName, "Could not read runtime AudioMixer from AudioMan." + fieldName + ": " + ex.Message); } return null; } private static AudioMixerGroup ReadAudioManGroup(AudioMan audioMan, string fieldName) { try { FieldInfo field = typeof(AudioMan).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return null; } object value = field.GetValue(audioMan); AudioMixerGroup val = (AudioMixerGroup)((value is AudioMixerGroup) ? value : null); if ((Object)(object)val != (Object)null) { return val; } AudioSource val2 = (AudioSource)((value is AudioSource) ? value : null); if ((Object)(object)val2 != (Object)null) { return val2.outputAudioMixerGroup; } } catch (Exception ex) { WarnOnce("audioman-field-" + fieldName, "Could not read AudioMan." + fieldName + ": " + ex.Message); } return null; } } private sealed class ReferenceComparer<T> : IEqualityComparer<T> where T : class { public static readonly ReferenceComparer<T> Instance = new ReferenceComparer<T>(); public bool Equals(T x, T y) { return x == y; } public int GetHashCode(T obj) { return RuntimeHelpers.GetHashCode(obj); } } private const int MaxManagedDepth = 24; private const int MaxCollectionEntries = 4096; private static readonly RuntimeMixerCatalog MixerCatalog = new RuntimeMixerCatalog(); private static readonly Dictionary<string, ExplicitOverride> Overrides = new Dictionary<string, ExplicitOverride>(StringComparer.Ordinal); private static readonly Dictionary<GameObject, ProcessedNode> ProcessedNodes = new Dictionary<GameObject, ProcessedNode>(ReferenceComparer<GameObject>.Instance); private static readonly Dictionary<AudioSource, AudioBinding> SourceBindings = new Dictionary<AudioSource, AudioBinding>(ReferenceComparer<AudioSource>.Instance); private static readonly Dictionary<Type, FieldInfo[]> TraversalFields = new Dictionary<Type, FieldInfo[]>(); private static readonly HashSet<GameObject> RegisteredRootSet = new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance); private static readonly List<GameObject> RegisteredRoots = new List<GameObject>(); private static readonly HashSet<GameObject> ActiveProcessing = new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance); private static readonly HashSet<string> WarnedKeys = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> ErrorKeys = new HashSet<string>(StringComparer.Ordinal); private static Action<string> _infoLogger; private static Action<string> _warningLogger; private static Action<string> _errorLogger; public static bool DebugLogging { get; set; } public static void ConfigureLogging(Action<string> infoLogger, Action<string> warningLogger, Action<string> errorLogger) { _infoLogger = infoLogger; _warningLogger = warningLogger; _errorLogger = errorLogger; } public static void SetOverride(string gameObjectName, AudioRoute route) { if (!string.IsNullOrWhiteSpace(gameObjectName)) { Overrides[gameObjectName] = new ExplicitOverride { UsesNamedGroup = false, Route = route, RuntimeGroupName = null }; InvalidateClassificationCaches(); } } public static void SetOverrides(AudioRoute route, params string[] gameObjectNames) { if (gameObjectNames == null) { return; } bool flag = false; foreach (string text in gameObjectNames) { if (!string.IsNullOrWhiteSpace(text)) { Overrides[text] = new ExplicitOverride { UsesNamedGroup = false, Route = route, RuntimeGroupName = null }; flag = true; } } if (flag) { InvalidateClassificationCaches(); } } public static void SetMixerGroupOverride(string gameObjectName, string runtimeMixerGroupName) { if (!string.IsNullOrWhiteSpace(gameObjectName) && !string.IsNullOrWhiteSpace(runtimeMixerGroupName)) { Overrides[gameObjectName] = new ExplicitOverride { UsesNamedGroup = true, Route = AudioRoute.SFX, RuntimeGroupName = runtimeMixerGroupName }; InvalidateClassificationCaches(); } } public static void RemoveOverride(string gameObjectName) { if (!string.IsNullOrWhiteSpace(gameObjectName) && Overrides.Remove(gameObjectName)) { InvalidateClassificationCaches(); } } public static void Process(GameObject root) { if (IsUsable((Object)(object)root)) { RegisterRoot(root); if (MixerCatalog.IsReady) { ProcessRoot(root); } } } public static void Process(IEnumerable<GameObject> roots) { if (roots == null) { return; } foreach (GameObject root in roots) { if (IsUsable((Object)(object)root)) { RegisterRoot(root); } } if (!MixerCatalog.IsReady) { return; } for (int i = 0; i < RegisteredRoots.Count; i++) { GameObject val = RegisteredRoots[i]; if (IsUsable((Object)(object)val)) { ProcessRoot(val); } } } public static void OnAudioManAwake(AudioMan audioMan) { if ((Object)(object)audioMan == (Object)null) { return; } MixerCatalog.Rebuild(audioMan); if (!MixerCatalog.IsReady) { ErrorOnce("runtime-mixer-unresolved", "AudioMan was available, but no usable runtime mixer group could be resolved."); return; } PruneRegisteredRoots(); ProcessedNodes.Clear(); SourceBindings.Clear(); ActiveProcessing.Clear(); for (int i = 0; i < RegisteredRoots.Count; i++) { GameObject val = RegisteredRoots[i]; if (IsUsable((Object)(object)val)) { ProcessRoot(val); } } } private static void RegisterRoot(GameObject root) { if (RegisteredRootSet.Add(root)) { RegisteredRoots.Add(root); } } private static void ProcessRoot(GameObject root) { if (!MixerCatalog.IsReady || !IsUsable((Object)(object)root)) { return; } if (ProcessedNodes.TryGetValue(root, out var value)) { ValidateNodeGraph(value, new HashSet<GameObject>(ReferenceComparer<GameObject>.Instance)); } else { if (!ActiveProcessing.Add(root)) { return; } ProcessedNode processedNode = new ProcessedNode { Root = root }; ProcessedNodes[root] = processedNode; try { ScanAudioSources(root, processedNode); } catch (Exception ex) { ErrorOnce("process-root-" + ObjectKey((Object)(object)root), "Failed to process audio sources for '" + SafeName((Object)(object)root) + "': " + ex); } finally { ActiveProcessing.Remove(root); } } } private static void ScanAudioSources(GameObject root, ProcessedNode node) { AudioSource[] componentsInChildren; try { componentsInChildren = root.GetComponentsInChildren<AudioSource>(true); } catch (Exception ex) { WarnOnce("enumerate-audiosources-" + ObjectKey((Object)(object)root), "Could not enumerate AudioSources under '" + SafeName((Object)(object)root) + "': " + ex.Message); return; } if (componentsInChildren == null) { return; } foreach (AudioSource val in componentsInChildren) { if (IsUsable((Object)(object)val)) { if (!SourceBindings.TryGetValue(val, out var value)) { value = new AudioBinding { Source = val, Target = ClassifySource(root, val) }; SourceBindings[val] = value; } if (!node.AudioBindings.Contains(value)) { node.AudioBindings.Add(value); } ApplyBinding(value, root); } } } private static void ScanBehaviourReferences(GameObject root, ProcessedNode node) { MonoBehaviour[] componentsInChildren; try { componentsInChildren = root.GetComponentsInChildren<MonoBehaviour>(true); } catch (Exception ex) { Warn("Could not enumerate MonoBehaviours under '" + SafeName((Object)(object)root) + "': " + ex.Message); return; } if (componentsInChildren == null) { return; } HashSet<object> managedVisited = new HashSet<object>(ReferenceComparer<object>.Instance); foreach (MonoBehaviour val in componentsInChildren) { if (IsUsable((Object)(object)val) && !(val is ZSFX)) { TraverseManagedObject(val, root, node, managedVisited, 0, isComponentRoot: true); } } } private static void TraverseManagedObject(object value, GameObject ownerRoot, ProcessedNode node, HashSet<object> managedVisited, int depth, bool isComponentRoot) { if (value == null || depth > 24) { return; } Type type = value.GetType(); if (!type.IsValueType && !managedVisited.Add(value)) { return; } FieldInfo[] traversalFields = GetTraversalFields(type, isComponentRoot); foreach (FieldInfo fieldInfo in traversalFields) { object value2; try { value2 = fieldInfo.GetValue(value); } catch { continue; } TraverseValue(value2, ownerRoot, node, managedVisited, depth + 1); } } private static void TraverseValue(object value, GameObject ownerRoot, ProcessedNode node, HashSet<object> managedVisited, int depth) { if (value == null || depth > 24) { return; } EffectList val = (EffectList)((value is EffectList) ? value : null); if (val != null) { TraverseEffectList(val, ownerRoot, node); return; } GameObject val2 = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val2 != (Object)null) { HandleReferencedGameObject(val2, ownerRoot, node); return; } Component val3 = (Component)((value is Component) ? value : null); if ((Object)(object)val3 != (Object)null) { if (IsUsable((Object)(object)val3)) { HandleReferencedGameObject(val3.gameObject