using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
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("Medic!")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+068df2e1f8d23c6cfdc691f2f04f8fb680a31ff3")]
[assembly: AssemblyProduct("Medic!")]
[assembly: AssemblyTitle("Medic!")]
[assembly: AssemblyVersion("1.0.0.0")]
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;
}
}
}
namespace Medic_
{
public sealed class AvatarReviveRelay : MonoBehaviourPun
{
[CompilerGenerated]
private sealed class <NotifyRevivedDelayed>d__4 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public int actorNumber;
public AvatarReviveRelay <>4__this;
private PlayerAvatar <local>5__1;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <NotifyRevivedDelayed>d__4(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<local>5__1 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = null;
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
<>2__current = null;
<>1__state = 2;
return true;
case 2:
<>1__state = -1;
<>2__current = (object)new WaitForSeconds(0.2f);
<>1__state = 3;
return true;
case 3:
<>1__state = -1;
<local>5__1 = MedicHeadReviver.GetLocalAvatar();
if ((Object)(object)<local>5__1 != (Object)null && <local>5__1.photonView.IsMine && <local>5__1.photonView.OwnerActorNr == actorNumber)
{
MedicHeadReviver.SendRevivedChatMessage(<local>5__1);
}
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[PunRPC]
public void SyncHealth(int actorNumber, float health)
{
PlayerAvatar avatarByActorNumber = MedicHeadReviver.GetAvatarByActorNumber(actorNumber);
if ((Object)(object)avatarByActorNumber != (Object)null)
{
MedicHeadReviver.SetHealth(avatarByActorNumber, health);
}
}
[PunRPC]
public void SyncRevivedHealth(int actorNumber, float health)
{
SyncHealth(actorNumber, health);
}
[PunRPC]
public void SyncReviverHealth(int actorNumber, float health)
{
SyncHealth(actorNumber, health);
}
[PunRPC]
public void NotifyRevived(int actorNumber)
{
((MonoBehaviour)this).StartCoroutine(NotifyRevivedDelayed(actorNumber));
}
[IteratorStateMachine(typeof(<NotifyRevivedDelayed>d__4))]
private IEnumerator NotifyRevivedDelayed(int actorNumber)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <NotifyRevivedDelayed>d__4(0)
{
<>4__this = this,
actorNumber = actorNumber
};
}
}
[BepInPlugin("com.callmehill.medic", "Medic Mod", "1.1.4")]
public class MedicPlugin : BaseUnityPlugin
{
[CompilerGenerated]
private sealed class <BindDeniedSound>d__26 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public MedicPlugin <>4__this;
private Sound <sound>5__1;
object? IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object? IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <BindDeniedSound>d__26(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<sound>5__1 = null;
<>1__state = -2;
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
break;
case 1:
<>1__state = -1;
break;
}
if ((Object)(object)MenuManager.instance == (Object)null)
{
<>2__current = null;
<>1__state = 1;
return true;
}
<sound>5__1 = MenuManager.instance.soundDeny;
if (<sound>5__1 != null && <sound>5__1.Sounds != null && <sound>5__1.Sounds.Length != 0)
{
DeniedSoundClip = <sound>5__1.Sounds[0];
}
if ((Object)(object)DeniedSoundClip == (Object)null && DeniedSoundNativeName != null)
{
DeniedSoundClip = ((IEnumerable<AudioClip>)Resources.FindObjectsOfTypeAll<AudioClip>()).FirstOrDefault((Func<AudioClip, bool>)((AudioClip c) => ((Object)c).name == DeniedSoundNativeName.Value));
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
internal static bool IsHoldingPlayerDeathHead;
internal static string? PlayerDeathHeadOwner;
internal static PlayerDeathHead? HeldPlayerDeathHead;
internal static ConfigEntry<float>? HealthTransferAmount;
internal static ConfigEntry<bool>? EnableGratitude;
internal static ConfigEntry<string>? DeniedSoundNativeName;
internal static AudioClip? DeniedSoundClip;
internal static ConfigEntry<float>? DenyActionCooldown;
private static float _lastDenySoundTime = -999f;
private static AudioSource? _localAudioSource;
private static float _lastAudioTime = -999f;
internal static ConfigEntry<bool>? EnableFailedReviveMessage;
internal static ConfigEntry<string>? FailedReviveMessage;
internal static readonly List<string> FailedRevivePhrases = new List<string>();
private static readonly string[] DefaultFailedPhrases = new string[5] { "I don't have enough health to revive you!", "Need more health to revive!", "Can't revive - low on health!", "Not enough HP to help you up!", "Too weak to revive you right now!" };
internal static ConfigEntry<float>? ChatCooldownDuration;
internal static float _lastFailedReviveMessageTime = -999f;
internal static ConfigEntry<string>? PhrasesFileName;
internal static ConfigEntry<string>? FallbackPhrases;
internal static readonly List<string> GratitudePhrases = new List<string>();
private static readonly string[] DefaultPhrases = new string[5] { "Wow! Thank you, friend!", "You saved me!", "Back in the fight!", "Clutch revive!", "I owe you one!" };
private static readonly Random s_rng = new Random();
internal static string? PluginDirectory;
private static bool _initialized;
private Harmony? _harmony;
private void Awake()
{
//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
//IL_01b4: Expected O, but got Unknown
if (_initialized)
{
Object.Destroy((Object)(object)this);
return;
}
_initialized = true;
PluginDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Medic! mod activated.");
HealthTransferAmount = ((BaseUnityPlugin)this).Config.Bind<float>("General", "HealthTransferAmount", 20f, "Amount of health transferred from reviver to revivee.");
EnableGratitude = ((BaseUnityPlugin)this).Config.Bind<bool>("Chat", "EnableGratitude", true, "Enable or disable the revive gratitude chat message.");
PhrasesFileName = ((BaseUnityPlugin)this).Config.Bind<string>("Chat", "PhrasesFile", "gratitude_phrases.txt", "Text file name to load phrases from (one phrase per line). Searched in the plugin directory and BepInEx/config.");
FallbackPhrases = ((BaseUnityPlugin)this).Config.Bind<string>("Chat", "FallbackPhrases", "Wow! Thank you, friend!|You saved me!|I owe you one!|Back in the fight!|That was clutch!", "Fallback phrases separated by |, used if the phrases file is missing or empty.");
DeniedSoundNativeName = ((BaseUnityPlugin)this).Config.Bind<string>("Sound", "DeniedSoundNativeName", "soundDeny", "Native AudioClip name played when chat input exceeds the character limit.");
EnableFailedReviveMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("Chat", "EnableFailedReviveMessage", true, "Enable or disable the failed revive chat message.");
FailedReviveMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Chat", "FailedReviveMessage", "I don't have enough health to revive you!|Need more health to revive!|Can't revive - low on health!|Not enough HP to help you up!|Too weak to revive you right now!", "Failed revive messages separated by |, randomly selected when a revive fails due to low health.");
ChatCooldownDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Chat", "ChatCooldownDuration", 1f, "Cooldown in seconds between failed revive chat messages to prevent spam.");
DenyActionCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "DenyActionCooldown", 0.5f, "Cooldown in seconds between deny actions when health is insufficient.");
LoadFailedRevivePhrases();
((MonoBehaviour)this).StartCoroutine(BindDeniedSound());
LoadPhrases();
Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
_harmony = new Harmony("com.callmehill.medic");
_harmony.PatchAll();
}
[IteratorStateMachine(typeof(<BindDeniedSound>d__26))]
private IEnumerator BindDeniedSound()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <BindDeniedSound>d__26(0)
{
<>4__this = this
};
}
private void OnDestroy()
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Expected O, but got Unknown
Harmony? harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
_initialized = false;
GameObject val = new GameObject("MedicPlugin");
val.AddComponent<MedicPlugin>();
}
internal static void PlayDenySound(Vector3 position)
{
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Expected O, but got Unknown
if ((Object)(object)DeniedSoundClip == (Object)null)
{
return;
}
float num = 0.3f;
if (!(Time.time - _lastAudioTime < num))
{
_lastAudioTime = Time.time;
if ((Object)(object)_localAudioSource == (Object)null)
{
GameObject val = new GameObject("MedicAudio");
_localAudioSource = val.AddComponent<AudioSource>();
Object.DontDestroyOnLoad((Object)(object)val);
}
((Component)_localAudioSource).transform.position = position;
_localAudioSource.PlayOneShot(DeniedSoundClip);
}
}
private void Update()
{
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
if (Input.GetKeyDown((KeyCode)104))
{
if ((Object)(object)HeldPlayerDeathHead == (Object)null)
{
return;
}
try
{
PlayerAvatar localAvatar = MedicHeadReviver.GetLocalAvatar();
if ((Object)(object)localAvatar == (Object)null)
{
return;
}
if (!MedicHeadReviver.HasRequiredHealth(localAvatar))
{
float num = DenyActionCooldown?.Value ?? 0.5f;
if (!(Time.time - _lastDenySoundTime < num))
{
_lastDenySoundTime = Time.time;
Vector3 position = ((Component)localAvatar).transform.position;
PlayDenySound(position);
MedicHeadReviver.SendFailedReviveMessage(localAvatar, HeldPlayerDeathHead);
}
return;
}
if (PhotonNetwork.IsMasterClient)
{
MedicHeadReviver.ReviveHead(HeldPlayerDeathHead, localAvatar);
}
else
{
MedicHeadReviver component = ((Component)HeldPlayerDeathHead).GetComponent<MedicHeadReviver>();
if (component != null)
{
((MonoBehaviourPun)component).photonView.RPC("RequestRevive", (RpcTarget)2, Array.Empty<object>());
}
}
}
catch (Exception)
{
}
}
if (Input.GetKey((KeyCode)306) && Input.GetKeyDown((KeyCode)112))
{
LoadPhrases();
}
}
internal static void LoadPhrases()
{
try
{
GratitudePhrases.Clear();
string text = PhrasesFileName?.Value ?? "gratitude_phrases.txt";
List<string> list = new List<string>();
if (!string.IsNullOrEmpty(PluginDirectory))
{
list.Add(Path.Combine(PluginDirectory, text));
}
list.Add(Path.Combine(Paths.ConfigPath, text));
if (File.Exists(text))
{
list.Add(text);
}
foreach (string item2 in list)
{
if (!File.Exists(item2))
{
continue;
}
string[] array = File.ReadAllLines(item2);
foreach (string text2 in array)
{
string text3 = (text2 ?? string.Empty).Trim();
if (text3.Length != 0 && !text3.StartsWith("#"))
{
string item = ((text3.Length > 160) ? text3.Substring(0, 160).TrimEnd() : text3);
GratitudePhrases.Add(item);
}
}
if (GratitudePhrases.Count > 0)
{
return;
}
}
string text4 = FallbackPhrases?.Value ?? string.Empty;
string[] array2 = text4.Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string text5 in array2)
{
string text6 = text5.Trim();
if (text6.Length > 0)
{
GratitudePhrases.Add(text6);
}
}
if (GratitudePhrases.Count == 0)
{
GratitudePhrases.AddRange(DefaultPhrases);
}
}
catch (Exception)
{
if (GratitudePhrases.Count == 0)
{
GratitudePhrases.AddRange(DefaultPhrases);
}
}
}
internal static string GetRandomGratitude()
{
if (GratitudePhrases.Count == 0)
{
LoadPhrases();
}
int index = s_rng.Next(GratitudePhrases.Count);
return GratitudePhrases[index];
}
internal static void LoadFailedRevivePhrases()
{
try
{
FailedRevivePhrases.Clear();
string text = FailedReviveMessage?.Value ?? string.Empty;
string[] array = text.Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string text2 in array)
{
string text3 = text2.Trim();
if (text3.Length > 0)
{
FailedRevivePhrases.Add(text3);
}
}
if (FailedRevivePhrases.Count == 0)
{
FailedRevivePhrases.AddRange(DefaultFailedPhrases);
}
}
catch (Exception)
{
if (FailedRevivePhrases.Count == 0)
{
FailedRevivePhrases.AddRange(DefaultFailedPhrases);
}
}
}
internal static string GetRandomFailedReviveMessage()
{
if (FailedRevivePhrases.Count == 0)
{
LoadFailedRevivePhrases();
}
int index = s_rng.Next(FailedRevivePhrases.Count);
return FailedRevivePhrases[index];
}
}
[HarmonyPatch(typeof(PhysGrabber), "PhysGrabStarted")]
internal class PhysGrabberStartedPatch
{
private static readonly FieldInfo GrabbedPhysGrabObjectField = AccessTools.Field(typeof(PhysGrabber), "grabbedPhysGrabObject");
private static readonly FieldInfo PlayerAvatarField = AccessTools.Field(typeof(PlayerDeathHead), "playerAvatar");
private static readonly FieldInfo PlayerNameField = AccessTools.Field(typeof(PlayerAvatar), "playerName");
private static void Postfix(PhysGrabber __instance)
{
object? value = GrabbedPhysGrabObjectField.GetValue(__instance);
PhysGrabObject val = (PhysGrabObject)((value is PhysGrabObject) ? value : null);
MedicPlugin.IsHoldingPlayerDeathHead = false;
MedicPlugin.PlayerDeathHeadOwner = null;
MedicPlugin.HeldPlayerDeathHead = null;
PlayerDeathHead val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<PlayerDeathHead>() : null);
if ((Object)(object)val2 != (Object)null)
{
MedicPlugin.IsHoldingPlayerDeathHead = true;
MedicPlugin.HeldPlayerDeathHead = val2;
object? value2 = PlayerAvatarField.GetValue(val2);
PlayerAvatar val3 = (PlayerAvatar)((value2 is PlayerAvatar) ? value2 : null);
if ((Object)(object)val3 != (Object)null)
{
MedicPlugin.PlayerDeathHeadOwner = PlayerNameField.GetValue(val3) as string;
}
}
}
}
[HarmonyPatch(typeof(PhysGrabber), "PhysGrabEnded")]
internal class PhysGrabberEndedPatch
{
private static void Postfix()
{
MedicPlugin.IsHoldingPlayerDeathHead = false;
MedicPlugin.PlayerDeathHeadOwner = null;
MedicPlugin.HeldPlayerDeathHead = null;
}
}
[HarmonyPatch(typeof(PlayerDeathHead), "Start")]
internal class PlayerDeathHeadStartPatch
{
private static void Postfix(PlayerDeathHead __instance)
{
if ((Object)(object)((Component)__instance).GetComponent<MedicHeadReviver>() == (Object)null)
{
((Component)__instance).gameObject.AddComponent<MedicHeadReviver>();
}
}
}
[HarmonyPatch(typeof(PlayerAvatar), "Awake")]
internal static class PlayerAvatar_Awake_AddRelay
{
private static void Postfix(PlayerAvatar __instance)
{
if ((Object)(object)((Component)__instance).GetComponent<AvatarReviveRelay>() == (Object)null)
{
((Component)__instance).gameObject.AddComponent<AvatarReviveRelay>();
}
MedicHeadReviver.RegisterAvatar(__instance);
}
}
[HarmonyPatch(typeof(PlayerAvatar), "OnDestroy")]
internal static class PlayerAvatar_OnDestroy_RemoveFromCache
{
private static void Prefix(PlayerAvatar __instance)
{
MedicHeadReviver.UnregisterAvatar(__instance);
}
}
internal class MedicHeadReviver : MonoBehaviourPun
{
private struct CachedHealthEntry
{
public float Health;
public float Timestamp;
}
private static readonly FieldInfo PlayerAvatarField = AccessTools.Field(typeof(PlayerDeathHead), "playerAvatar");
private static readonly FieldInfo PlayerNameField = AccessTools.Field(typeof(PlayerAvatar), "playerName");
private static readonly Type? PlayerHealthType = AccessTools.TypeByName("PlayerHealth");
private static readonly FieldInfo? HealthField = AccessTools.Field(PlayerHealthType, "health");
private static readonly FieldInfo? HealthSetField = AccessTools.Field(PlayerHealthType, "healthSet");
private static readonly FieldInfo? MaxHealthField = AccessTools.Field(PlayerHealthType, "maxHealth");
private static PlayerAvatar? _cachedLocalAvatar;
private static readonly Dictionary<int, PlayerAvatar> _avatarsByActorNumber = new Dictionary<int, PlayerAvatar>();
private static readonly Dictionary<int, CachedHealthEntry> _healthCache = new Dictionary<int, CachedHealthEntry>();
private static MethodInfo? _playerChatMessageSendMethod;
internal static PlayerAvatar? GetLocalAvatar()
{
CleanupAvatarCache();
if (TryGetPhotonView(_cachedLocalAvatar, out PhotonView view) && view.IsMine)
{
return _cachedLocalAvatar;
}
_cachedLocalAvatar = null;
return ScanForLocalAvatar();
}
internal static PlayerAvatar? GetAvatarByActorNumber(int actorNumber)
{
if (actorNumber <= 0)
{
return null;
}
CleanupAvatarCache();
if (_avatarsByActorNumber.TryGetValue(actorNumber, out PlayerAvatar value))
{
if (TryGetPhotonView(value, out PhotonView view) && view.OwnerActorNr == actorNumber)
{
return value;
}
_avatarsByActorNumber.Remove(actorNumber);
}
return ScanForAvatarByActorNumber(actorNumber);
}
internal static void RegisterAvatar(PlayerAvatar avatar)
{
if (TryGetPhotonView(avatar, out PhotonView view))
{
if (view.OwnerActorNr > 0)
{
_avatarsByActorNumber[view.OwnerActorNr] = avatar;
}
if (view.IsMine)
{
_cachedLocalAvatar = avatar;
}
}
}
internal static void UnregisterAvatar(PlayerAvatar avatar)
{
if ((Object)(object)avatar == (Object)null)
{
return;
}
if (_cachedLocalAvatar == avatar)
{
_cachedLocalAvatar = null;
}
List<int> list = new List<int>();
foreach (KeyValuePair<int, PlayerAvatar> item in _avatarsByActorNumber)
{
if (!TryGetPhotonView(item.Value, out PhotonView _) || item.Value == avatar)
{
list.Add(item.Key);
}
}
for (int i = 0; i < list.Count; i++)
{
_avatarsByActorNumber.Remove(list[i]);
}
CleanupAvatarCache();
}
private static void CleanupAvatarCache()
{
if ((Object)(object)_cachedLocalAvatar != (Object)null && (!TryGetPhotonView(_cachedLocalAvatar, out PhotonView view) || !view.IsMine))
{
_cachedLocalAvatar = null;
}
if (_avatarsByActorNumber.Count == 0)
{
return;
}
List<int> list = new List<int>();
foreach (KeyValuePair<int, PlayerAvatar> item in _avatarsByActorNumber)
{
if (!TryGetPhotonView(item.Value, out PhotonView view2))
{
list.Add(item.Key);
}
else if (view2.OwnerActorNr <= 0 || view2.OwnerActorNr != item.Key)
{
list.Add(item.Key);
}
}
for (int i = 0; i < list.Count; i++)
{
_avatarsByActorNumber.Remove(list[i]);
}
}
private static PlayerAvatar? ScanForLocalAvatar()
{
PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>();
foreach (PlayerAvatar val in array)
{
RegisterAvatar(val);
if (TryGetPhotonView(val, out PhotonView view) && view.IsMine)
{
return val;
}
}
return null;
}
private static PlayerAvatar? ScanForAvatarByActorNumber(int actorNumber)
{
PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>();
foreach (PlayerAvatar val in array)
{
RegisterAvatar(val);
if (TryGetPhotonView(val, out PhotonView view) && view.OwnerActorNr == actorNumber)
{
return val;
}
}
return null;
}
private static bool TryGetPhotonView(PlayerAvatar? avatar, out PhotonView view)
{
if ((Object)(object)avatar != (Object)null)
{
PhotonView photonView = avatar.photonView;
if ((Object)(object)photonView != (Object)null)
{
view = photonView;
return true;
}
}
view = null;
return false;
}
private static float GetHealth(PlayerAvatar avatar)
{
Component val = ((PlayerHealthType != null) ? ((Component)avatar).GetComponent(PlayerHealthType) : null);
if ((Object)(object)val != (Object)null && HealthField != null)
{
object value = HealthField.GetValue(val);
if (value != null)
{
return Convert.ToSingle(value);
}
}
return 0f;
}
private static float GetCachedHealth(PlayerAvatar avatar)
{
int instanceID = ((Object)avatar).GetInstanceID();
if (_healthCache.TryGetValue(instanceID, out var value) && Time.time - value.Timestamp < 0.5f)
{
return value.Health;
}
float health = GetHealth(avatar);
_healthCache[instanceID] = new CachedHealthEntry
{
Health = health,
Timestamp = Time.time
};
if (_healthCache.Count > 100)
{
_healthCache.Clear();
}
return health;
}
internal static void SetHealth(PlayerAvatar avatar, float amount)
{
Component val = ((PlayerHealthType != null) ? ((Component)avatar).GetComponent(PlayerHealthType) : null);
if (!((Object)(object)val != (Object)null) || !(HealthField != null))
{
return;
}
try
{
object value = Convert.ChangeType(amount, HealthField.FieldType);
HealthField.SetValue(val, value);
HealthSetField?.SetValue(val, true);
if (_healthCache.Count >= 100)
{
_healthCache.Clear();
}
int instanceID = ((Object)avatar).GetInstanceID();
_healthCache[instanceID] = new CachedHealthEntry
{
Health = amount,
Timestamp = Time.time
};
}
catch (InvalidCastException)
{
}
}
private static float GetMaxHealth(PlayerAvatar avatar)
{
Component val = ((PlayerHealthType != null) ? ((Component)avatar).GetComponent(PlayerHealthType) : null);
if ((Object)(object)val != (Object)null && MaxHealthField != null)
{
object value = MaxHealthField.GetValue(val);
if (value != null)
{
return Convert.ToSingle(value);
}
}
return 100f;
}
private static void SendChatMessage(PlayerAvatar avatar, string message)
{
try
{
if ((object)_playerChatMessageSendMethod == null)
{
_playerChatMessageSendMethod = AccessTools.Method(typeof(PlayerAvatar), "ChatMessageSend", new Type[1] { typeof(string) }, (Type[])null);
}
if (!(_playerChatMessageSendMethod == null))
{
_playerChatMessageSendMethod.Invoke(avatar, new object[1] { message });
}
}
catch (Exception)
{
}
}
internal static void SendRevivedChatMessage(PlayerAvatar avatar)
{
if (MedicPlugin.EnableGratitude == null || MedicPlugin.EnableGratitude.Value)
{
string randomGratitude = MedicPlugin.GetRandomGratitude();
SendChatMessage(avatar, randomGratitude);
}
}
internal static void SendFailedReviveMessage(PlayerAvatar avatar, PlayerDeathHead? head)
{
if (MedicPlugin.EnableFailedReviveMessage == null || !MedicPlugin.EnableFailedReviveMessage.Value)
{
return;
}
float num = MedicPlugin.ChatCooldownDuration?.Value ?? 1f;
if (Time.time - MedicPlugin._lastFailedReviveMessageTime < num)
{
return;
}
string text = MedicPlugin.GetRandomFailedReviveMessage();
if ((Object)(object)head != (Object)null)
{
object? value = PlayerAvatarField.GetValue(head);
PlayerAvatar val = (PlayerAvatar)((value is PlayerAvatar) ? value : null);
if ((Object)(object)val != (Object)null && PlayerNameField != null)
{
string text2 = PlayerNameField.GetValue(val) as string;
if (!string.IsNullOrEmpty(text2))
{
text = text2 + ", " + text.ToLower();
}
}
}
MedicPlugin._lastFailedReviveMessageTime = Time.time;
SendChatMessage(avatar, text);
}
internal static bool HasRequiredHealth(PlayerAvatar avatar)
{
float num = MedicPlugin.HealthTransferAmount?.Value ?? 20f;
return GetCachedHealth(avatar) >= num + 1f;
}
internal static void ReviveHead(PlayerDeathHead? head, PlayerAvatar? reviver)
{
if ((Object)(object)head == (Object)null || (Object)(object)reviver == (Object)null)
{
return;
}
object? value = PlayerAvatarField.GetValue(head);
PlayerAvatar val = (PlayerAvatar)((value is PlayerAvatar) ? value : null);
if ((Object)(object)val == (Object)null)
{
return;
}
float num = MedicPlugin.HealthTransferAmount?.Value ?? 20f;
float health = GetHealth(reviver);
if (!(health < num + 1f))
{
float num2 = health - num;
SetHealth(reviver, num2);
if ((Object)(object)reviver.photonView != (Object)null)
{
reviver.photonView.RPC("SyncReviverHealth", (RpcTarget)0, new object[2]
{
reviver.photonView.OwnerActorNr,
num2
});
}
val.photonView.RPC("ReviveRPC", (RpcTarget)0, new object[1] { true });
float maxHealth = GetMaxHealth(val);
float num3 = Mathf.Min(num, maxHealth);
SetHealth(val, num3);
val.photonView.RPC("SyncRevivedHealth", (RpcTarget)0, new object[2]
{
val.photonView.OwnerActorNr,
num3
});
val.photonView.RPC("NotifyRevived", val.photonView.Owner, new object[1] { val.photonView.OwnerActorNr });
}
}
[PunRPC]
public void RequestRevive(PhotonMessageInfo info)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
PlayerAvatar avatarByActorNumber = GetAvatarByActorNumber(info.Sender.ActorNumber);
ReviveHead(((Component)this).GetComponent<PlayerDeathHead>(), avatarByActorNumber);
}
}
}