Decompiled source of HostReviveMod v1.2.0

com.sakurakite.revivemod.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.sakurakite.revivemod")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("com.sakurakite.revivemod")]
[assembly: AssemblyTitle("HostReviveMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace HostReviveMod
{
	[BepInPlugin("com.sakurakites.hostrevivemod", "HostReviveMod", "1.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private enum UiLanguage
		{
			ZhCn,
			EnUs
		}

		private enum ReviveAllPositionMode
		{
			HostPosition,
			OriginalPosition
		}

		public static Plugin? instance;

		internal static ManualLogSource? logger;

		private ConfigEntry<UiLanguage>? uiLanguage;

		private ConfigEntry<KeyCode>? holdReviveKey;

		private ConfigEntry<KeyCode>? reviveAllKey;

		private ConfigEntry<float>? holdDurationSeconds;

		private ConfigEntry<float>? interactDistance;

		private ConfigEntry<bool>? includeHostInReviveAll;

		private ConfigEntry<ReviveAllPositionMode>? reviveAllPositionMode;

		private ConfigEntry<bool>? enableNotifications;

		private Character? currentHoldTarget;

		private float holdStartTime;

		private bool holdTriggered;

		private bool localWasDead;

		private string overlayText = string.Empty;

		private float overlayHideTime;

		private static readonly Dictionary<int, string> PendingReviverByViewId = new Dictionary<int, string>();

		private void Awake()
		{
			instance = this;
			logger = ((BaseUnityPlugin)this).Logger;
			uiLanguage = ((BaseUnityPlugin)this).Config.Bind<UiLanguage>("界面 / UI", "语言 / Language", UiLanguage.ZhCn, "切换提示文本语言(中文/英文)。Switch runtime notification language (Chinese/English).");
			holdReviveKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("输入 / Input", "长按复活按键 / Hold Revive Key", (KeyCode)103, "对准倒地队友骨头/身体时,长按此键可复活。Hold to revive a downed teammate while aiming at their body/bones.");
			reviveAllKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("输入 / Input", "全体复活按键 / Revive All Key", (KeyCode)121, "房主按下此键复活全部倒地队友(位置由下方选项决定)。Host revives all downed teammates (spawn position depends on option below).");
			holdDurationSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("复活 / Revive", "长按时长秒数 / Hold Duration (Seconds)", 5f, "房主对准倒地队友后,长按复活键达到该秒数才会生效。Seconds required to hold before revive triggers.");
			interactDistance = ((BaseUnityPlugin)this).Config.Bind<float>("复活 / Revive", "复活检测距离 / Revive Distance", 6f, "对准倒地队友进行长按复活时的最大检测距离。Maximum aiming distance for hold-revive.");
			includeHostInReviveAll = ((BaseUnityPlugin)this).Config.Bind<bool>("复活 / Revive", "全体复活包含房主 / Include Host In Revive All", true, "开启后,房主倒地时按全体复活也会把自己复活(单人测试推荐开启)。If enabled, Revive All can also revive the host.");
			reviveAllPositionMode = ((BaseUnityPlugin)this).Config.Bind<ReviveAllPositionMode>("复活 / Revive", "全体复活位置 / Revive All Position", ReviveAllPositionMode.HostPosition, "HostPosition=复活到房主位置;OriginalPosition=原地复活。HostPosition revives at host location; OriginalPosition revives at each target position.");
			enableNotifications = ((BaseUnityPlugin)this).Config.Bind<bool>("界面 / UI", "启用复活提示 / Enable Notifications", true, "显示复活提示(本地提示,且会尽力发送到游戏内消息)。Show local and best-effort in-game notifications.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[HostReviveMod] " + L("加载成功:房主长按复活与全体复活已启用", "Loaded: host hold-revive and revive-all are enabled")));
		}

		private void Update()
		{
			TrackLocalReviveNotification();
			if (!CanHostUseReviveFeatures())
			{
				ResetHoldTarget();
				return;
			}
			HandleHoldRevive();
			HandleReviveAll();
		}

		private static bool CanHostUseReviveFeatures()
		{
			return PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient;
		}

		private void HandleHoldRevive()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			if (holdReviveKey == null || holdDurationSeconds == null || interactDistance == null)
			{
				return;
			}
			if (!Input.GetKey(holdReviveKey.Value))
			{
				ResetHoldTarget();
				return;
			}
			Character val = FindDeadTeammateInSight(interactDistance.Value);
			if ((Object)(object)val == (Object)null)
			{
				ResetHoldTarget();
			}
			else if ((Object)(object)val != (Object)(object)currentHoldTarget)
			{
				currentHoldTarget = val;
				holdStartTime = Time.time;
				holdTriggered = false;
			}
			else if (!holdTriggered && !(Time.time - holdStartTime < Mathf.Max(0.1f, holdDurationSeconds.Value)))
			{
				string localPlayerName = GetLocalPlayerName();
				if (TryReviveCharacter(val, ((Component)val).transform.position, localPlayerName))
				{
					holdTriggered = true;
					string characterDisplayName = GetCharacterDisplayName(val);
					NotifyReviveEvent(L("你复活了 " + characterDisplayName, "You revived " + characterDisplayName), L(localPlayerName + " 复活了 " + characterDisplayName, localPlayerName + " revived " + characterDisplayName));
				}
			}
		}

		private void HandleReviveAll()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			if (reviveAllKey == null || !Input.GetKeyDown(reviveAllKey.Value))
			{
				return;
			}
			Vector3 hostPosition = GetHostPosition();
			int num = 0;
			string localPlayerName = GetLocalPlayerName();
			bool flag = reviveAllPositionMode == null || reviveAllPositionMode.Value == ReviveAllPositionMode.HostPosition;
			foreach (Character allCharacter in Character.AllCharacters)
			{
				if (!IsDeadTeammate(allCharacter))
				{
					ConfigEntry<bool>? obj = includeHostInReviveAll;
					if (obj == null || !obj.Value || !IsDeadLocalCharacter(allCharacter))
					{
						continue;
					}
				}
				Vector3 position = (flag ? hostPosition : ((Component)allCharacter).transform.position);
				if (TryReviveCharacter(allCharacter, position, localPlayerName))
				{
					num++;
				}
			}
			ManualLogSource? obj2 = logger;
			if (obj2 != null)
			{
				obj2.LogInfo((object)string.Format("[{0}] {1}: {2}", "HostReviveMod", L("全体复活触发", "Revive-all triggered"), num));
			}
			if (num > 0)
			{
				string localMessage = L($"你触发了全体复活({num}人)", $"You triggered Revive All ({num} players)");
				string arg = (flag ? L("复活到房主位置", "revived at host position") : L("原地复活", "revived at original positions"));
				string broadcastMessage = L($"房主 {localPlayerName} 复活了所有人({num}人,{arg})", $"Host {localPlayerName} revived everyone ({num}, {arg})");
				NotifyReviveEvent(localMessage, broadcastMessage);
			}
		}

		private static Character? FindDeadTeammateInSight(float maxDistance)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				return null;
			}
			Ray val = default(Ray);
			((Ray)(ref val))..ctor(((Component)main).transform.position, ((Component)main).transform.forward);
			RaycastHit val2 = default(RaycastHit);
			if (Physics.Raycast(val, ref val2, Mathf.Max(0.5f, maxDistance), -5, (QueryTriggerInteraction)2))
			{
				Character componentInParent = ((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent<Character>();
				if (IsDeadTeammate(componentInParent))
				{
					return componentInParent;
				}
			}
			return null;
		}

		private static bool IsDeadTeammate(Character? character)
		{
			if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null)
			{
				return false;
			}
			Character localCharacter = GetLocalCharacter();
			if ((Object)(object)localCharacter != (Object)null && (Object)(object)character == (Object)(object)localCharacter)
			{
				return false;
			}
			return character.data.dead || character.data.fullyPassedOut;
		}

		private static bool IsDeadLocalCharacter(Character? character)
		{
			if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null)
			{
				return false;
			}
			Character localCharacter = GetLocalCharacter();
			if ((Object)(object)localCharacter == (Object)null || (Object)(object)character != (Object)(object)localCharacter)
			{
				return false;
			}
			return character.data.dead || character.data.fullyPassedOut;
		}

		private static Character? GetLocalCharacter()
		{
			foreach (Character allCharacter in Character.AllCharacters)
			{
				if ((Object)(object)allCharacter == (Object)null || (Object)(object)((MonoBehaviourPun)allCharacter).photonView == (Object)null || !((MonoBehaviourPun)allCharacter).photonView.IsMine)
				{
					continue;
				}
				return allCharacter;
			}
			return null;
		}

		private static bool TryReviveCharacter(Character character, Vector3 position, string reviverName)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)((MonoBehaviourPun)character).photonView == (Object)null)
			{
				return false;
			}
			((MonoBehaviourPun)character).photonView.RPC("RPCA_ReviveAtPosition", (RpcTarget)0, new object[2] { position, false });
			CharacterRefs refs = character.refs;
			if (refs != null)
			{
				CharacterAfflictions afflictions = refs.afflictions;
				if (afflictions != null)
				{
					afflictions.ClearAllStatus(true);
				}
			}
			PendingReviverByViewId[((MonoBehaviourPun)character).photonView.ViewID] = reviverName;
			return true;
		}

		private static string GetLocalPlayerName()
		{
			if (!string.IsNullOrWhiteSpace(PhotonNetwork.NickName))
			{
				return PhotonNetwork.NickName;
			}
			return "Host";
		}

		private static string GetCharacterDisplayName(Character character)
		{
			PhotonView photonView = ((MonoBehaviourPun)character).photonView;
			if (((photonView != null) ? photonView.Owner : null) != null && !string.IsNullOrWhiteSpace(((MonoBehaviourPun)character).photonView.Owner.NickName))
			{
				return ((MonoBehaviourPun)character).photonView.Owner.NickName;
			}
			return ((Object)character).name;
		}

		private static Vector3 GetHostPosition()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Character localCharacter = GetLocalCharacter();
			if ((Object)(object)localCharacter != (Object)null)
			{
				return ((Component)localCharacter).transform.position;
			}
			return Vector3.zero;
		}

		private void ResetHoldTarget()
		{
			currentHoldTarget = null;
			holdStartTime = 0f;
			holdTriggered = false;
		}

		private void NotifyReviveEvent(string localMessage, string broadcastMessage)
		{
			ConfigEntry<bool>? obj = enableNotifications;
			if (obj == null || !obj.Value)
			{
				return;
			}
			ShowLocalOverlay(localMessage);
			bool flag = TryPostSystemMessage(broadcastMessage);
			ManualLogSource? obj2 = logger;
			if (obj2 != null)
			{
				obj2.LogInfo((object)("[HostReviveMod] " + broadcastMessage));
			}
			if (!flag)
			{
				ManualLogSource? obj3 = logger;
				if (obj3 != null)
				{
					obj3.LogDebug((object)("[HostReviveMod] " + L("未找到游戏内消息接口,已使用本地提示+日志回退", "No in-game message API found; using local overlay + log fallback")));
				}
			}
		}

		private void TrackLocalReviveNotification()
		{
			ConfigEntry<bool>? obj = enableNotifications;
			if (obj == null || !obj.Value)
			{
				return;
			}
			Character localCharacter = GetLocalCharacter();
			if (!((Object)(object)localCharacter == (Object)null) && !((Object)(object)((MonoBehaviourPun)localCharacter).photonView == (Object)null))
			{
				bool flag = (Object)(object)localCharacter.data != (Object)null && (localCharacter.data.dead || localCharacter.data.fullyPassedOut);
				if (localWasDead && !flag && PendingReviverByViewId.TryGetValue(((MonoBehaviourPun)localCharacter).photonView.ViewID, out string value))
				{
					ShowLocalOverlay(L(value + " 复活了你", value + " revived you"));
					PendingReviverByViewId.Remove(((MonoBehaviourPun)localCharacter).photonView.ViewID);
				}
				localWasDead = flag;
			}
		}

		private void ShowLocalOverlay(string message)
		{
			overlayText = message;
			overlayHideTime = Time.time + 3f;
		}

		private void OnGUI()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrEmpty(overlayText) && !(Time.time > overlayHideTime))
			{
				GUIStyle val = new GUIStyle(GUI.skin.box)
				{
					fontSize = 20,
					alignment = (TextAnchor)4,
					wordWrap = true
				};
				float num = Mathf.Min(700f, (float)Screen.width * 0.85f);
				Rect val2 = default(Rect);
				((Rect)(ref val2))..ctor(((float)Screen.width - num) * 0.5f, (float)Screen.height * 0.08f, num, 56f);
				GUI.Box(val2, overlayText, val);
			}
		}

		private static bool TryPostSystemMessage(string message)
		{
			Assembly assembly = typeof(Character).Assembly;
			string[] array = new string[5] { "ChatManager", "ChatUI", "UI_Chat", "NotificationManager", "GameUI" };
			string[] singletonNames = new string[4] { "Instance", "instance", "Singleton", "m_instance" };
			string[] array2 = new string[7] { "AddSystemMessage", "AddMessage", "PostMessage", "ShowNotification", "ShowMessage", "DisplayMessage", "Notify" };
			string[] array3 = array;
			foreach (string name in array3)
			{
				Type type = assembly.GetType(name);
				if (type == null)
				{
					continue;
				}
				object singletonInstance = GetSingletonInstance(type, singletonNames);
				if (singletonInstance == null)
				{
					continue;
				}
				string[] array4 = array2;
				foreach (string name2 in array4)
				{
					MethodInfo method = type.GetMethod(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null);
					if (!(method == null))
					{
						method.Invoke(singletonInstance, new object[1] { message });
						return true;
					}
				}
			}
			return false;
		}

		private static object? GetSingletonInstance(Type type, string[] singletonNames)
		{
			foreach (string name in singletonNames)
			{
				PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					object value = property.GetValue(null);
					if (value != null)
					{
						return value;
					}
				}
				FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					object value2 = field.GetValue(null);
					if (value2 != null)
					{
						return value2;
					}
				}
			}
			return null;
		}

		private string L(string zh, string en)
		{
			return (uiLanguage != null && uiLanguage.Value == UiLanguage.EnUs) ? en : zh;
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.sakurakites.hostrevivemod";

		public const string PLUGIN_NAME = "HostReviveMod";

		public const string PLUGIN_VERSION = "1.2.0";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}