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.2
plugins/BalrondSecondChance.dll
Decompiled 6 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.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using JetBrains.Annotations; using LitJson2; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; 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: AssemblyTitle("BalrondReviveSoul")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BalrondReviveSoul")] [assembly: AssemblyCopyright("Copyright © 2022")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("cde312a0-cf19-4264-8616-e1c74774beed")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.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; } } 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) { 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", "Sit or Bed."); 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); } } 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 bool _registered; private void Awake() { _player = ((Component)this).GetComponent<Player>(); _nview = ((Component)this).GetComponent<ZNetView>(); TryRegisterRpc(); } private void OnEnable() { TryRegisterRpc(); } private void Update() { if (!((Object)(object)_player == (Object)null) && !((Object)(object)_nview == (Object)null) && _nview.IsValid() && _player.IsDowned() && ((Character)_player).IsOwner() && !(Time.time < _nextOwnerTick)) { _nextOwnerTick = Time.time + 0.1f; MaintainDownedOwnerState(); if (_player.ReleaseRequested()) { PerformRealDeath(); } else if (BRSConfig.AutoReleaseWhenTimerEnds.Value && Time.time >= _player.GetDownedUntil()) { PerformRealDeath(); } } } public string GetHoverName() { return ((Object)(object)_player != (Object)null) ? _player.GetPlayerName() : string.Empty; } 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() && BRSReviveRules.CanSelfRevive(_player, out var _) && (!BRSConfig.RequireItemForSelfRevive.Value || BRSItemUtils.ConsumeResurrectionPotion(_player))) { _nview.InvokeRPC("BRS_SelfReviveRequest", Array.Empty<object>()); } } private bool TryReviveFromActor(Player reviver, ItemData directItem, bool fromUseItem) { if (!BRSReviveRules.CanFriendRevive(reviver, _player, out var reason)) { ((Character)reviver).Message((MessageType)2, reason, 0, (Sprite)null); 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); 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()) { 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); } BRSPlayerPresentation.SetDownedPresentation(_player, downed: true); } 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); } } } 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; 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; } private IEnumerator SetupPopupWhenReady() { _isBinding = true; while (!_isSetup && !TrySetupPopup()) { yield return (object)new WaitForSeconds(0.5f); } _isBinding = false; } private bool TrySetupPopup() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) Transform val = FindUnifiedPopupTransform(); if ((Object)(object)val == (Object)null) { return false; } Transform val2 = val.Find("PopupBlockingBackground"); if ((Object)(object)val2 == (Object)null) { return false; } 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"; } if (!CachePopupRefs(_downedPopupRoot.transform)) { return false; } PrepareCloneForDownedUsage(); WireButtons(); _isSetup = true; HidePopup(); return true; } private Transform FindUnifiedPopupTransform() { Transform[] array = Resources.FindObjectsOfTypeAll<Transform>(); foreach (Transform val in array) { if (!((Object)(object)val == (Object)null) && !(((Object)val).name != "UnifiedPopup")) { string hierarchyPath = GetHierarchyPath(val); if (hierarchyPath.Contains("_GameMain/LoadingGUI/PixelFix/IngameGui/UnifiedPopup")) { return val; } } } return null; } 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"); Transform val6 = val2.Find("ButtonYes"); Transform val7 = val2.Find("ButtonOk"); Transform val8 = val2.Find("ButtonNo"); if ((Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null || (Object)(object)val6 == (Object)null || (Object)(object)val7 == (Object)null || (Object)(object)val8 == (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); _buttonYes = ((Component)val6).GetComponent<Button>(); _buttonOk = ((Component)val7).GetComponent<Button>(); _buttonNo = ((Component)val8).GetComponent<Button>(); _buttonYesText = GetTextComponent(val6.Find("Text")); _buttonOkText = GetTextComponent(val7.Find("Text")); _buttonNoText = GetTextComponent(val8.Find("Text")); 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 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 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 void PrepareCloneForDownedUsage() { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) 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; SetText(_headerText, "$tag_brs_popup_header_bal"); SetText(_bodyText, "$tag_brs_popup_wait_for_revive_bal"); SetText(_buttonYesText, "$tag_brs_button_resurrect_self_bal"); SetText(_buttonNoText, "$tag_brs_button_release_soul_bal"); SetText(_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; ((Component)_buttonOk).gameObject.SetActive(false); } } private void WireButtons() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown if ((Object)(object)_buttonYes != (Object)null) { ((UnityEventBase)_buttonYes.onClick).RemoveAllListeners(); ((UnityEvent)_buttonYes.onClick).AddListener(new UnityAction(OnSelfReviveClicked)); } if ((Object)(object)_buttonNo != (Object)null) { ((UnityEventBase)_buttonNo.onClick).RemoveAllListeners(); ((UnityEvent)_buttonNo.onClick).AddListener(new UnityAction(OnReleaseSoulClicked)); } if ((Object)(object)_buttonOk != (Object)null) { ((UnityEventBase)_buttonOk.onClick).RemoveAllListeners(); ((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; } } SetText(_headerText, "$tag_brs_popup_header_bal"); SetText(_bodyText, text); SetText(_buttonYesText, "$tag_brs_button_resurrect_self_bal"); SetText(_buttonNoText, "$tag_brs_button_release_soul_bal"); SetText(_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); ((Selectable)_buttonNo).interactable = true; } if ((Object)(object)_buttonOk != (Object)null) { ((Component)_buttonOk).gameObject.SetActive(!flag3); ((Selectable)_buttonOk).interactable = true; } _downedPopupRoot.transform.SetAsLastSibling(); if (!((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); PlayerController component = ((Component)localPlayer).GetComponent<PlayerController>(); if ((Object)(object)component != (Object)null && ((Behaviour)component).enabled) { ((Behaviour)component).enabled = false; } } 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()) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { PlayerController component = ((Component)localPlayer).GetComponent<PlayerController>(); if ((Object)(object)component != (Object)null && !((Behaviour)component).enabled) { ((Behaviour)component).enabled = true; } } 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()) { 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 (!((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 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 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); 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) { 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); } } 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); internal static void SetDownedPresentation(Player player, bool downed) { if (!((Object)(object)player == (Object)null)) { BRSDownedVisualState orCreateState = GetOrCreateState(player); if (downed) { EnsureAnchorAtPlayer(player, orCreateState); ApplyDownedPhysics(player); AttachPlayer(player, orCreateState); } else { DetachPlayer(player, orCreateState); DestroyAnchor(orCreateState); } } } 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) { if ((Object)(object)state.AttachPoint == (Object)null) { return; } string configuredAnimation = GetConfiguredAnimation(); bool sitMode = configuredAnimation == "emote_sit"; if (!state.Attached || state.AnimationName != configuredAnimation) { if (state.Attached) { DetachPlayer(player, state); } SnapPlayerToAnchor(player, state.AttachPoint, sitMode); bool flag = TryAttach(player, state.AttachPoint, configuredAnimation); if (!flag && configuredAnimation != "emote_sit") { flag = TryAttach(player, state.AttachPoint, "emote_sit"); } if (flag) { SnapPlayerToAnchor(player, state.AttachPoint, sitMode); state.Attached = true; state.AnimationName = configuredAnimation; } } else { SnapPlayerToAnchor(player, state.AttachPoint, sitMode); } } private static string GetConfiguredAnimation() { string text = ((BRSConfig.DownedPresentationMode != null) ? BRSConfig.DownedPresentationMode.Value : "Sit"); if (!string.IsNullOrEmpty(text) && text.ToLowerInvariant() == "bed") { return "attach_bed"; } 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, bool sitMode) { //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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) Quaternion rotation = attachPoint.rotation; Vector3 position = attachPoint.position; if (sitMode) { rotation = attachPoint.rotation * Quaternion.Euler(0f, 0f, 0f); } 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 DetachPlayer(Player player, BRSDownedVisualState state) { if (state.Attached) { try { ((Character)player).AttachStop(); } catch { } 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; private void OnDestroy() { if ((Object)(object)AnchorRoot != (Object)null) { Object.Destroy((Object)(object)AnchorRoot); } } } internal static class BRSReviveRules { 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 (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 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); 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.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.2")] 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.2"; 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.2", MinimumRequiredVersion = "1.0.2" }; 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; 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 LitJson2 { internal enum JsonType { None, Object, Array, String, Int, Long, Double, Boolean } internal interface IJsonWrapper : IList, IOrderedDictionary, IDictionary, ICollection, IEnumerable { bool IsArray { get; } bool IsBoolean { get; } bool IsDouble { get; } bool IsInt { get; } bool IsLong { get; } bool IsObject { get; } bool IsString { get; } bool GetBoolean(); double GetDouble(); int GetInt(); JsonType GetJsonType(); long GetLong(); string GetString(); void SetBoolean(bool val); void SetDouble(double val); void SetInt(int val); void SetJsonType(JsonType type); void SetLong(long val); void SetString(string val); string ToJson(); void ToJson(JsonWriter writer); } internal class JsonData : IJsonWrapper, IList, IOrderedDictionary, IDictionary, ICollection, IEnumerable, IEquatable<JsonData> { private IList<JsonData> inst_array; private bool inst_boolean; private double inst_double; private int inst_int; private long inst_long; private IDictionary<string, JsonData> inst_object; private string inst_string; private string json; private JsonType type; private IList<KeyValuePair<string, JsonData>> object_list; public int Count => EnsureCollection().Count; public bool IsArray => type == JsonType.Array; public bool IsBoolean => type == JsonType.Boolean; public bool IsDouble => type == JsonType.Double; public bool IsInt => type == JsonType.Int; public bool IsLong => type == JsonType.Long; public bool IsObject => type == JsonType.Object; public bool IsString => type == JsonType.String; public ICollection<string> Keys { get { EnsureDictionary(); return inst_object.Keys; } } int ICollection.Count => Count; bool ICollection.IsSynchronized => EnsureCollection().IsSynchronized; object ICollection.SyncRoot => EnsureCollection().SyncRoot; bool IDictionary.IsFixedSize => EnsureDictionary().IsFixedSize; bool IDictionary.IsReadOnly => EnsureDictionary().IsReadOnly; ICollection IDictionary.Keys { get { EnsureDictionary(); IList<string> list = new List<string>(); foreach (KeyValuePair<string, JsonData> item in object_list) { list.Add(item.Key); } return (ICollection)list; } } ICollection IDictionary.Values { get { EnsureDictionary(); IList<JsonData> list = new List<JsonData>(); foreach (KeyValuePair<string, JsonData> item in object_list) { list.Add(item.Value); } return (ICollection)list; } } bool IJsonWrapper.IsArray => IsArray; bool IJsonWrapper.IsBoolean => IsBoolean; bool IJsonWrapper.IsDouble => IsDouble; bool IJsonWrapper.IsInt => IsInt; bool IJsonWrapper.IsLong => IsLong; bool IJsonWrapper.IsObject => IsObject; bool IJsonWrapper.IsString => IsString; bool IList.IsFixedSize => EnsureList().IsFixedSize; bool IList.IsReadOnly => EnsureList().IsReadOnly; object IDictionary.this[object key] { get { return EnsureDictionary()[key]; } set { if (!(key is string)) { throw new ArgumentException("The key has to be a string"); } JsonData value2 = ToJsonData(value); this[(string)key] = value2; } } object IOrderedDictionary.this[int idx] { get { EnsureDictionary(); return object_list[idx].Value; } set { EnsureDictionary(); JsonData value2 = ToJsonData(value); KeyValuePair<string, JsonData> keyValuePair = object_list[idx]; inst_object[keyValuePair.Key] = value2; KeyValuePair<string, JsonData> value3 = new KeyValuePair<string, JsonData>(keyValuePair.Key, value2); object_list[idx] = value3; } } object IList.this[int index] { get { return EnsureList()[index]; } set { EnsureList(); JsonData value2 = ToJsonData(value); this[index] = value2; } } public JsonData this[string prop_name] { get { EnsureDictionary(); return inst_object[prop_name]; } set { EnsureDictionary(); KeyValuePair<string, JsonData> keyValuePair = new KeyValuePair<string, JsonData>(prop_name, value); if (inst_object.ContainsKey(prop_name)) { for (int i = 0; i < object_list.Count; i++) { if (object_list[i].Key == prop_name) { object_list[i] = keyValuePair; break; } } } else { object_list.Add(keyValuePair); } inst_object[prop_name] = value; json = null; } } public JsonData this[int index] { get { EnsureCollection(); if (type == JsonType.Array) { return inst_array[index]; } return object_list[index].Value; } set { EnsureCollection(); if (type == JsonType.Array) { inst_array[index] = value; } else { KeyValuePair<string, JsonData> keyValuePair = object_list[index]; KeyValuePair<string, JsonData> value2 = new KeyValuePair<string, JsonData>(keyValuePair.Key, value); object_list[index] = value2; inst_object[keyValuePair.Key] = value; } json = null; } } public JsonData() { } public JsonData(bool boolean) { type = JsonType.Boolean; inst_boolean = boolean; } public JsonData(double number) { type = JsonType.Double; inst_double = number; } public JsonData(int number) { type = JsonType.Int; inst_int = number; } public JsonData(long number) { type = JsonType.Long; inst_long = number; } public JsonData(object obj) { if (obj is bool) { type = JsonType.Boolean; inst_boolean = (bool)obj; return; } if (obj is double) { type = JsonType.Double; inst_double = (double)obj; return; } if (obj is int) { type = JsonType.Int; inst_int = (int)obj; return; } if (obj is long) { type = JsonType.Long; inst_long = (long)obj; return; } if (obj is string) { type = JsonType.String; inst_string = (string)obj; return; } throw new ArgumentException("Unable to wrap the given object with JsonData"); } public JsonData(string str) { type = JsonType.String; inst_string = str; } public static implicit operator JsonData(bool data) { return new JsonData(data); } public static implicit operator JsonData(double data) { return new JsonData(data); } public static implicit operator JsonData(int data) { return new JsonData(data); } public static implicit operator JsonData(long data) { return new JsonData(data); } public static implicit operator JsonData(string data) { return new JsonData(data); } public static explicit operator bool(JsonData data) { if (data.type != JsonType.Boolean) { throw new InvalidCastException("Instance of JsonData doesn't hold a double"); } return data.inst_boolean; } public static explicit operator double(JsonData data) { if (data.type != JsonType.Double) { throw new InvalidCastException("Instance of JsonData doesn't hold a double"); } return data.inst_double; } public static explicit operator int(JsonData data) { if (data.type != JsonType.Int) { throw new InvalidCastException("Instance of JsonData doesn't hold an int"); } return data.inst_int; } public static explicit operator long(JsonData data) { if (data.type != JsonType.Long) { throw new InvalidCastException("Instance of JsonData doesn't hold an int"); } return data.inst_long; } public static explicit operator string(JsonData data) { if (data.type != JsonType.String) { throw new InvalidCastException("Instance of JsonData doesn't hold a string"); } return data.inst_string; } void ICollection.CopyTo(Array array, int index) { EnsureCollection().CopyTo(array, index); } void IDictionary.Add(object key, object value) { JsonData value2 = ToJsonData(value); EnsureDictionary().Add(key, value2); KeyValuePair<string, JsonData> item = new KeyValuePair<string, JsonData>((string)key, value2); object_list.Add(item); json = null; } void IDictionary.Clear() { EnsureDictionary().Clear(); object_list.Clear(); json = null; } bool IDictionary.Contains(object key) { return EnsureDictionary().Contains(key); } IDictionaryEnumerator IDictionary.GetEnumerator() { return ((IOrderedDictionary)this).GetEnumerator(); } void IDictionary.Remove(object key) { EnsureDictionary().Remove(key); for (int i = 0; i < object_list.Count; i++) { if (object_list[i].Key == (string)key) { object_list.RemoveAt(i); break; } } json = null; } IEnumerator IEnumerable.GetEnumerator() { return EnsureCollection().GetEnumerator(); } bool IJsonWrapper.GetBoolean() { if (type != JsonType.Boolean) { throw new InvalidOperationException("JsonData instance doesn't hold a boolean"); } return inst_boolean; } double IJsonWrapper.GetDouble() { if (type != JsonType.Double) { throw new InvalidOperationException("JsonData instance doesn't hold a double"); } return inst_double; } int IJsonWrapper.GetInt() { if (type != JsonType.Int) { throw new InvalidOperationException("JsonData instance doesn't hold an int"); } return inst_int; } long IJsonWrapper.GetLong() { if (type != JsonType.Long) { throw new InvalidOperationException("JsonData instance doesn't hold a long"); } return inst_long; } string IJsonWrapper.GetString() { if (type != JsonType.String) { throw new InvalidOperationException("JsonData instance doesn't hold a string"); } return inst_string; } void IJsonWrapper.SetBoolean(bool val) { type = JsonType.Boolean; inst_boolean = val; json = null; } void IJsonWrapper.SetDouble(double val) { type = JsonType.Double; inst_double = val; json = null; } void IJsonWrapper.SetInt(int val) { type = JsonType.Int; inst_int = val; json = null; } void IJsonWrapper.SetLong(long val) { type = JsonType.Long; inst_long = val; json = null; } void IJsonWrapper.SetString(string val) { type = JsonType.String; inst_string = val; json = null; } string IJsonWrapper.ToJson() { return ToJson(); } void IJsonWrapper.ToJson(JsonWriter writer) { ToJson(writer); } int IList.Add(object value) { return Add(value); } void IList.Clear() { EnsureList().Clear(); json = null; } bool IList.Contains(object value) { return EnsureList().Contains(value); } int IList.IndexOf(object value) { return EnsureList().IndexOf(value); } void IList.Insert(int index, object value) { EnsureList().Insert(index, value); json = null; } void IList.Remove(object value) { EnsureList().Remove(value); json = null; } void IList.RemoveAt(int index) { EnsureList().RemoveAt(index); json = null; } IDictionaryEnumerator IOrderedDictionary.GetEnumerator() { EnsureDictionary(); return new OrderedDictionaryEnumerator(object_list.GetEnumerator()); } void IOrderedDictionary.Insert(int idx, object key, object value) { string text = (string)key; JsonData value2 = (this[text] = ToJsonData(value)); KeyValuePair<string, JsonData> item = new KeyValuePair<string, JsonData>(text, value2); object_list.Insert(idx, item); } void IOrderedDictionary.RemoveAt(int idx) { EnsureDictionary(); inst_object.Remove(object_list[idx].Key); object_list.RemoveAt(idx); } private ICollection EnsureCollection() { if (type == JsonType.Array) { return (ICollection)inst_array; } if (type == JsonType.Object) { return (ICollection)inst_object; } throw new InvalidOperationException("The JsonData instance has to be initialized first"); } private IDictionary EnsureDictionary() { if (type == JsonType.Object) { return (IDictionary)inst_object; } if (type != 0) { throw new InvalidOperationException("Instance of JsonData is not a dictionary"); } type = JsonType.Object; inst_object = new Dictionary<string, JsonData>(); object_list = new List<KeyValuePair<string, JsonData>>(); return (IDictionary)inst_object; } private IList EnsureList() { if (type == JsonType.Array) { return (IList)inst_array; } if (type != 0) { throw new InvalidOperationException("Instance of JsonData is not a list"); } type = JsonType.Array; inst_array = new List<JsonData>(); return (IList)inst_array; } private JsonData ToJsonData(object obj) { if (obj == null) { return null; } if (obj is JsonData) { return (JsonData)obj; } return new JsonData(obj); } private static void WriteJson(IJsonWrapper obj, JsonWriter writer) { if (obj == null) { writer.Write(null); } else if (obj.IsString) { writer.Write(obj.GetString()); } else if (obj.IsBoolean) { writer.Write(obj.GetBoolean()); } else if (obj.IsDouble) { writer.Write(obj.GetDouble()); } else if (obj.IsInt) { writer.Write(obj.GetInt()); } else if (obj.IsLong) { writer.Write(obj.GetLong()); } else if (obj.IsArray) { writer.WriteArrayStart(); foreach (object item in (IEnumerable)obj) { WriteJson((JsonData)item, writer); } writer.WriteArrayEnd(); } else { if (!obj.IsObject) { return; } writer.WriteObjectStart(); foreach (DictionaryEntry item2 in (IDictionary)obj) { writer.WritePropertyName((string)item2.Key); WriteJson((JsonData)item2.Value, writer); } writer.WriteObjectEnd(); } } public int Add(object value) { JsonData value2 = ToJsonData(value); json = null; return EnsureList().Add(value2); } public void Clear() { if (IsObject) { ((IDictionary)this).Clear(); } else if (IsArray) { ((IList)this).Clear(); } } public bool Equals(JsonData x) { if (x == null) { return false; } if (x.type != type) { return false; } return type switch { JsonType.None => true, JsonType.Object => inst_object.Equals(x.inst_object), JsonType.Array => inst_array.Equals(x.inst_array), JsonType.String => inst_string.Equals(x.inst_string), JsonType.Int => inst_int.Equals(x.inst_int), JsonType.Long => inst_long.Equals(x.inst_long), JsonType.Double => inst_double.Equals(x.inst_double), JsonType.Boolean => inst_boolean.Equals(x.inst_boolean), _ => false, }; } public JsonType GetJsonType() { return type; } public void SetJsonType(JsonType type) { if (this.type != type) { switch (type) { case JsonType.Object: inst_object = new Dictionary<string, JsonData>(); object_list = new List<KeyValuePair<string, JsonData>>(); break; case JsonType.Array: inst_array = new List<JsonData>(); break; case JsonType.String: inst_string = null; break; case JsonType.Int: inst_int = 0; break; case JsonType.Long: inst_long = 0L; break; case JsonType.Double: inst_double = 0.0; break; case JsonType.Boolean: inst_boolean = false; break; } this.type = type; } } public string ToJson() { if (json != null) { return json; } StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.Validate = false; WriteJson(this, jsonWriter); json = stringWriter.ToString(); return json; } public void ToJson(JsonWriter writer) { bool validate = writer.Validate; writer.Validate = false; WriteJson(this, writer); writer.Validate = validate; } public override string ToString() { return type switch { JsonType.Array => "JsonData array", JsonType.Boolean => inst_boolean.ToString(), JsonType.Double => inst_double.ToString(), JsonType.Int => inst_int.ToString(), JsonType.Long => inst_long.ToString(), JsonType.Object => "JsonData object", JsonType.String => inst_string, _ => "Uninitialized JsonData", }; } } internal class OrderedDictionaryEnumerator : IDictionaryEnumerator, IEnumerator { private IEnumerator<KeyValuePair<string, JsonData>> list_enumerator; public object Current => Entry; public DictionaryEntry Entry { get { KeyValuePair<string, JsonData> current = list_enumerator.Current; return new DictionaryEntry(current.Key, current.Value); } } public object Key => list_enumerator.Current.Key; public object Value => list_enumerator.Current.Value; public OrderedDictionaryEnumerator(IEnumerator<KeyValuePair<string, JsonData>> enumerator) { list_enumerator = enumerator; } public bool MoveNext() { return list_enumerator.MoveNext(); } public void Reset() { list_enumerator.Reset(); } } internal class JsonException : ApplicationException { public JsonException() { } internal JsonException(ParserToken token) : base($"Invalid token '{token}' in input string") { } internal JsonException(ParserToken token, Exception inner_exception) : base($"Invalid token '{token}' in input string", inner_exception) { } internal JsonException(int c) : base($"Invalid character '{(char)c}' in input string") { } internal JsonException(int c, Exception inner_exception) : base($"Invalid character '{(char)c}' in input string", inner_exception) { } public JsonException(string message) : base(message) { } public JsonException(string message, Exception inner_exception) : base(message, inner_exception) { } } internal struct PropertyMetadata { public MemberInfo Info; public bool IsField; public Type Type; } internal struct ArrayMetadata { private Type element_type; private bool is_array; private bool is_list; public Type ElementType { get { if (element_type == null) { return typeof(JsonData); } return element_type; } set { element_type = value; } } public bool IsArray { get { return is_array; } set { is_array = value; } } public bool IsList { get { return is_list; } set { is_list = value; } } } internal struct ObjectMetadata { private Type element_type; private bool is_dictionary; private IDictionary<string, PropertyMetadata> properties; public Type ElementType { get { if (element_type == null) { return typeof(JsonData); } return element_type; } set { element_type = value; } } public bool IsDictionary { get { return is_dictionary; } set { is_dictionary = value; } } public IDictionary<string, PropertyMetadata> Properties { get { return properties; } set { properties = value; } } } internal delegate void ExporterFunc(object obj, JsonWriter writer); internal delegate void ExporterFunc<T>(T obj, JsonWriter writer); internal delegate object ImporterFunc(object input); internal delegate TValue ImporterFunc<TJson, TValue>(TJson input); internal delegate IJsonWrapper WrapperFactory(); internal class JsonMapper { private static int max_nesting_depth; private static IFormatProvider datetime_format; private static IDictionary<Type, ExporterFunc> base_exporters_table; private static IDictionary<Type, ExporterFunc> custom_exporters_table; private static IDictionary<Type, IDictionary<Type, ImporterFunc>> base_importers_table; private static IDictionary<Type, IDictionary<Type, ImporterFunc>> custom_importers_table; private static IDictionary<Type, ArrayMetadata> array_metadata; private static readonly object array_metadata_lock; private static IDictionary<Type, IDictionary<Type, MethodInfo>> conv_ops; private static readonly object conv_ops_lock; private static IDictionary<Type, ObjectMetadata> object_metadata; private static readonly object object_metadata_lock; private static IDictionary<Type, IList<PropertyMetadata>> type_properties; private static readonly object type_properties_lock; private static JsonWriter static_writer; private static readonly object static_writer_lock; static JsonMapper() { array_metadata_lock = new object(); conv_ops_lock = new object(); object_metadata_lock = new object(); type_properties_lock = new object(); static_writer_lock = new object(); max_nesting_depth = 100; array_metadata = new Dictionary<Type, ArrayMetadata>(); conv_ops = new Dictionary<Type, IDictionary<Type, MethodInfo>>(); object_metadata = new Dictionary<Type, ObjectMetadata>(); type_properties = new Dictionary<Type, IList<PropertyMetadata>>(); static_writer = new JsonWriter(); datetime_format = DateTimeFormatInfo.InvariantInfo; base_exporters_table = new Dictionary<Type, ExporterFunc>(); custom_exporters_table = new Dictionary<Type, ExporterFunc>(); base_importers_table = new Dictionary<Type, IDictionary<Type, ImporterFunc>>(); custom_importers_table = new Dictionary<Type, IDictionary<Type, ImporterFunc>>(); RegisterBaseExporters(); RegisterBaseImporters(); } private static void AddArrayMetadata(Type type) { if (array_metadata.ContainsKey(type)) { return; } ArrayMetadata value = default(ArrayMetadata); value.IsArray = type.IsArray; if (type.GetInterface("System.Collections.IList") != null) { value.IsList = true; } PropertyInfo[] properties = type.GetProperties(); foreach (PropertyInfo propertyInfo in properties) { if (!(propertyInfo.Name != "Item")) { ParameterInfo[] indexParameters = propertyInfo.GetIndexParameters(); if (indexParameters.Length == 1 && indexParameters[0].ParameterType == typeof(int)) { value.ElementType = propertyInfo.PropertyType; } } } lock (array_metadata_lock) { try { array_metadata.Add(type, value); } catch (ArgumentException) { } } } private static void AddObjectMetadata(Type type) { if (object_metadata.ContainsKey(type)) { return; } ObjectMetadata value = default(ObjectMetadata); if (type.GetInterface("System.Collections.IDictionary") != null) { value.IsDictionary = true; } value.Properties = new Dictionary<string, PropertyMetadata>(); PropertyInfo[] properties = type.GetProperties(); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.Name == "Item") { ParameterInfo[] indexParameters = propertyInfo.GetIndexParameters(); if (indexParameters.Length == 1 && indexParameters[0].ParameterType == typeof(string)) { value.ElementType = propertyInfo.PropertyType; } } else { PropertyMetadata value2 = default(PropertyMetadata); value2.Info = propertyInfo; value2.Type = propertyInfo.PropertyType; value.Properties.Add(propertyInfo.Name, value2); } } FieldInfo[] fields = type.GetFields(); foreach (FieldInfo fieldInfo in fields) { PropertyMetadata value3 = default(PropertyMetadata); value3.Info = fieldInfo; value3.IsField = true; value3.Type = fieldInfo.FieldType; value.Properties.Add(fieldInfo.Name, value3); } lock (object_metadata_lock) { try { object_metadata.Add(type, value); } catch (ArgumentException) { } } } private static void AddTypeProperties(Type type) { if (type_properties.ContainsKey(type)) { return; } IList<PropertyMetadata> list = new List<PropertyMetadata>(); PropertyInfo[] properties = type.GetProperties(); foreach (PropertyInfo propertyInfo in properties) { if (!(propertyInfo.Name == "Item")) { PropertyMetadata item = default(PropertyMetadata); item.Info = propertyInfo; item.IsField = false; list.Add(item); } } FieldInfo[] fields = type.GetFields(); foreach (FieldInfo info in fields) { PropertyMetadata item2 = default(PropertyMetadata); item2.Info = info; item2.IsField = true; list.Add(item2); } lock (type_properties_lock) { try { type_properties.Add(type, list); } catch (ArgumentException) { } } } private static MethodInfo GetConvOp(Type t1, Type t2) { lock (conv_ops_lock) { if (!conv_ops.ContainsKey(t1)) { conv_ops.Add(t1, new Dictionary<Type, MethodInfo>()); } } if (conv_ops[t1].ContainsKey(t2)) { return conv_ops[t1][t2]; } MethodInfo method = t1.GetMethod("op_Implicit", new Type[1] { t2 }); lock (conv_ops_lock) { try { conv_ops[t1].Add(t2, method); return method; } catch (ArgumentException) { return conv_ops[t1][t2]; } } } private static object ReadValue(Type inst_type, JsonReader reader) { reader.Read(); if (reader.Token == JsonToken.ArrayEnd) { return null; } Type underlyingType = Nullable.GetUnderlyingType(inst_type); Type type = underlyingType ?? inst_type; if (reader.Token == JsonToken.Null) { if (inst_type.IsClass || underlyingType != null) { return null; } throw new JsonException($"Can't assign null to an instance of type {inst_type}"); } if (reader.Token == JsonToken.Double || reader.Token == JsonToken.Int || reader.Token == JsonToken.Long || reader.Token == JsonToken.String || reader.Token == JsonToken.Boolean) { Type type2 = reader.Value.GetType(); if (type.IsAssignableFrom(type2)) { return reader.Value; } if (custom_importers_table.ContainsKey(type2) && custom_importers_table[type2].ContainsKey(type)) { ImporterFunc importerFunc = custom_importers_table[type2][type]; return importerFunc(reader.Value); } if (base_importers_table.ContainsKey(type2) && base_importers_table[type2].ContainsKey(type)) { ImporterFunc importerFunc2 = base_importers_table[type2][type]; return importerFunc2(reader.Value); } if (type.IsEnum) { return Enum.ToObject(type, reader.Value); } MethodInfo convOp = GetConvOp(type, type2); if (convOp != null) { return convOp.Invoke(null, new object[1] { reader.Value }); } throw new JsonException($"Can't assign value '{reader.Value}' (type {type2}) to type {inst_type}"); } object obj = null; if (reader.Token == JsonToken.ArrayStart) { AddArrayMetadata(inst_type); ArrayMetadata arrayMetadata = array_metadata[inst_type]; if (!arrayMetadata.IsArray && !arrayMetadata.IsList) { throw new JsonException($"Type {inst_type} can't act as an array"); } IList list; Type elementType; if (!arrayMetadata.IsArray) { list = (IList)Activator.CreateInstance(inst_type); elementType = arrayMetadata.ElementType; } else { list = new ArrayList(); elementType = inst_type.GetElementType(); } while (true) { object obj2 = ReadValue(elementType, reader); if (obj2 == null && reader.Token == JsonToken.ArrayEnd) { break; } list.Add(obj2); } if (arrayMetadata.IsArray) { int count = list.Count; obj = Array.CreateInstance(elementType, count); for (int i = 0; i < count; i++) { ((Array)obj).SetValue(list[i], i); } } else { obj = list; } } else if (reader.Token == JsonToken.ObjectStart) { AddObjectMetadata(type); ObjectMetadata objectMetadata = object_metadata[type]; obj = Activator.CreateInstance(type); while (true) { reader.Read(); if (reader.Token == JsonToken.ObjectEnd) { break; } string text = (string)reader.Value; if (objectMetadata.Properties.ContainsKey(text)) { PropertyMetadata propertyMetadata = objectMetadata.Properties[text]; if (propertyMetadata.IsField) { ((FieldInfo)propertyMetadata.Info).SetValue(obj, ReadValue(propertyMetadata.Type, reader)); continue; } PropertyInfo propertyInfo = (PropertyInfo)propertyMetadata.Info; if (propertyInfo.CanWrite) { propertyInfo.SetValue(obj, ReadValue(propertyMetadata.Type, reader), null); } else { ReadValue(propertyMetadata.Type, reader); } } else if (!objectMetadata.IsDictionary) { if (!reader.SkipNonMembers) { throw new JsonException($"The type {inst_type} doesn't have the property '{text}'"); } ReadSkip(reader); } else { ((IDictionary)obj).Add(text, ReadValue(objectMetadata.ElementType, reader)); } } } return obj; } private static IJsonWrapper ReadValue(WrapperFactory factory, JsonReader reader) { reader.Read(); if (reader.Token == JsonToken.ArrayEnd || reader.Token == JsonToken.Null) { return null; } IJsonWrapper jsonWrapper = factory(); if (reader.Token == JsonToken.String) { jsonWrapper.SetString((string)reader.Value); return jsonWrapper; } if (reader.Token == JsonToken.Double) { jsonWrapper.SetDouble((double)reader.Value); return jsonWrapper; } if (reader.Token == JsonToken.Int) { jsonWrapper.SetInt((int)reader.Value); return jsonWrapper; } if (reader.Token == JsonToken.Long) { jsonWrapper.SetLong((long)reader.Value); return jsonWrapper; } if (reader.Token == JsonToken.Boolean) { jsonWrapper.SetBoolean((bool)reader.Value); return jsonWrapper; } if (reader.Token == JsonToken.ArrayStart) { jsonWrapper.SetJsonType(JsonType.Array); while (true) { IJsonWrapper jsonWrapper2 = ReadValue(factory, reader); if (jsonWrapper2 == null && reader.Token == JsonToken.ArrayEnd) { break; } jsonWrapper.Add(jsonWrapper2); } } else if (reader.Token == JsonToken.ObjectStart) { jsonWrapper.SetJsonType(JsonType.Object); while (true) { reader.Read(); if (reader.Token == JsonToken.ObjectEnd) { break; } string key = (string)reader.Value; jsonWrapper[key] = ReadValue(factory, reader); } } return jsonWrapper; } private static void ReadSkip(JsonReader reader) { ToWrapper(() => new JsonMockWrapper(), reader); } private static void RegisterBaseExporters() { base_exporters_table[typeof(byte)] = delegate(object obj, JsonWriter writer) { writer.Write(Convert.ToInt32((byte)obj)); }; base_exporters_table[typeof(char)] = delegate(object obj, JsonWriter writer) { writer.Write(Convert.ToString((char)obj)); }; base_exporters_table[typeof(DateTime)] = delegate(object obj, JsonWriter writer) { writer.Write(Convert.ToString((DateTime)obj, datetime_format)); }; base_exporters_table[typeof(decimal)] = delegate(object obj, JsonWriter writer) { writer.Write((decimal)obj); }; base_exporters_table[typeof(sbyte)] = delegate(object obj, JsonWriter writer) { writer.Write(Convert.ToInt32((sbyte)obj)); }; base_exporters_table[typeof(short)] = delegate(object obj, JsonWriter writer) { writer.Write(Convert.ToInt32((short)obj)); }; base_exporters_table[typeof(ushort)] = delegate(object obj, JsonWriter writer) { writer.Write(Convert.ToInt32((ushort)obj)); }; base_exporters_table[typeof(uint)] = delegate(object obj, JsonWriter writer) { writer.Write(Convert.ToUInt64((uint)obj)); }; base_exporters_table[typeof(ulong)] = delegate(object obj, JsonWriter writer) { writer.Write((ulong)obj); }; } private static void RegisterBaseImporters() { ImporterFunc importer = (object input) => Convert.ToByte((int)input); RegisterImporter(base_importers_table, typeof(int), typeof(byte), importer); importer = (object input) => Convert.ToUInt64((int)input); RegisterImporter(base_importers_table, typeof(int), typeof(ulong), importer); importer = (object input) => Convert.ToSByte((int)input); RegisterImporter(base_importers_table, typeof(int), typeof(sbyte), importer); importer = (object input) => Convert.ToInt16((int)input); RegisterImporter(base_importers_table, typeof(int), typeof(short), importer); importer = (object input) => Convert.ToUInt16((int)input); RegisterImporter(base_importers_table, typeof(int), typeof(ushort), importer); importer = (object input) => Convert.ToUInt32((int)input); RegisterImporter(base_importers_table, typeof(int), typeof(uint), importer); importer = (object input) => Convert.ToSingle((int)input); RegisterImporter(base_importers_table, typeof(int), typeof(float), importer); importer = (object input) => Convert.ToDouble((int)input); RegisterImporter(base_importers_table, typeof(int), typeof(double), importer); importer = (object input) => Convert.ToDecimal((double)input); RegisterImporter(base_importers_table, typeof(double), typeof(decimal), importer); importer = (object input) => Convert.ToUInt32((long)input); RegisterImporter(base_importers_table, typeof(long), typeof(uint), importer); importer = (object input) => Convert.ToChar((string)input); RegisterImporter(base_importers_table, typeof(string), typeof(char), importer); importer = (object input) => Convert.ToDateTime((string)input, datetime_format); RegisterImporter(base_importers_table, typeof(string), typeof(DateTime), importer); } private static void RegisterImporter(IDictionary<Type, IDictionary<Type, ImporterFunc>> table, Type json_type, Type value_type, ImporterFunc importer) { if (!table.ContainsKey(json_type)) { table.Add(json_type, new Dictionary<Type, ImporterFunc>()); } table[json_type][value_type] = importer; } private static void WriteValue(object obj, JsonWriter writer, bool writer_is_private, int depth) { if (depth > max_nesting_depth) { throw new JsonException($"Max allowed object depth reached while trying to export from type {obj.GetType()}"); } if (obj == null) { writer.Write(null); return; } if (obj is IJsonWrapper) { if (writer_is_private) { writer.TextWriter.Write(((IJsonWrapper)obj).ToJson()); } else { ((IJsonWrapper)obj).ToJson(writer); } return; } if (obj is string) { writer.Write((string)obj); return; } if (obj is double) { writer.Write((double)obj); return; } if (obj is int) { writer.Write((int)obj); return; } if (obj is bool) { writer.Write((bool)obj); return; } if (obj is long) { writer.Write((long)obj); return; } if (obj is Array) { writer.WriteArrayStart(); foreach (object item in (Array)obj) { WriteValue(item, writer, writer_is_private, depth + 1); } writer.WriteArrayEnd(); return; } if (obj is IList) { writer.WriteArrayStart(); foreach (object item2 in (IList)obj) { WriteValue(item2, writer, writer_is_private, depth + 1); } writer.WriteArrayEnd(); return; } if (obj is IDictionary) { writer.WriteObjectStart(); foreach (DictionaryEntry item3 in (IDictionary)obj) { writer.WritePropertyName((string)item3.Key); WriteValue(item3.Value, writer, writer_is_private, depth + 1); } writer.WriteObjectEnd(); return; } Type type = obj.GetType(); if (custom_exporters_table.ContainsKey(type)) { ExporterFunc exporterFunc = custom_exporters_table[type]; exporterFunc(obj, writer); return; } if (base_exporters_table.ContainsKey(type)) { ExporterFunc exporterFunc2 = base_exporters_table[type]; exporterFunc2(obj, writer); return; } if (obj is Enum) { Type underlyingType = Enum.GetUnderlyingType(type); if (underlyingType == typeof(long) || underlyingType == typeof(uint) || underlyingType == typeof(ulong)) { writer.Write((ulong)obj); } else { writer.Write((int)obj); } return; } AddTypeProperties(type); IList<PropertyMetadata> list = type_properties[type]; writer.WriteObjectStart(); foreach (PropertyMetadata item4 in list) { if (item4.IsField) { writer.WritePropertyName(item4.Info.Name); WriteValue(((FieldInfo)item4.Info).GetValue(obj), writer, writer_is_private, depth + 1); continue; } PropertyInfo propertyInfo = (PropertyInfo)item4.Info; if (propertyInfo.CanRead) { writer.WritePropertyName(item4.Info.Name); WriteValue(propertyInfo.GetValue(obj, null), writer, writer_is_private, depth + 1); } } writer.WriteObjectEnd(); } public static string ToJson(object obj) { lock (static_writer_lock) { static_writer.Reset(); WriteValue(obj, static_writer, writer_is_private: true, 0); return static_writer.ToString(); } } public static void ToJson(object obj, JsonWriter writer) { WriteValue(obj, writer, writer_is_private: false, 0); } public static JsonData ToObject(JsonReader reader) { return (JsonData)ToWrapper(() => new JsonData(), reader); } public static JsonData ToObject(TextReader reader) { JsonReader reader2 = new JsonReader(reader); return (JsonData)ToWrapper(() => new JsonData(), reader2); } public static JsonData ToObject(string json) { return (JsonData)ToWrapper(() => new JsonData(), json); } public static T ToObject<T>(JsonReader reader) { return (T)ReadValue(typeof(T), reader); } public static T ToObject<T>(TextReader reader) { JsonReader reader2 = new JsonReader(reader); return (T)ReadValue(typeof(T), reader2); } public static T ToObject<T>(string json) { JsonReader reader = new JsonReader(json); return (T)ReadValue(typeof(T), reader); } public static IJsonWrapper ToWrapper(WrapperFactory factory, JsonReader reader) { return ReadValue(factory, reader); } public static IJsonWrapper ToWrapper(WrapperFactory factory, string json) { JsonReader reader = new JsonReader(json); return ReadValue(factory, reader); } public static void RegisterExporter<T>(ExporterFunc<T> exporter) { ExporterFunc value = delegate(object obj, JsonWriter writer) { exporter((T)obj, writer); }; custom_exporters_table[typeof(T)] = value; } public static void RegisterImporter<TJson, TValue>(ImporterFunc<TJson, TValue> importer) { ImporterFunc importer2 = (object input) => importer((TJson)input); RegisterImporter(custom_importers_table, typeof(TJson), typeof(TValue), importer2); } public static void UnregisterExporters() { custom_exporters_table.Clear(); } public static void UnregisterImporters() { custom_importers_table.Clear(); } } internal class JsonMockWrapper : IJsonWrapper, IList, IOrderedDictionary, IDic