Decompiled source of DamageShare v0.1.3

plugins/DamageShare/DamageShare.dll

Decompiled 6 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("saya")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3")]
[assembly: AssemblyProduct("DamageShare")]
[assembly: AssemblyTitle("DamageShare")]
[assembly: AssemblyVersion("0.1.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 DamageShare
{
	internal sealed class DamageCapture
	{
		private int _depth;

		private int _before;

		private bool _eligible;

		private bool _failed;

		public void Begin(int health, bool eligible)
		{
			if (_depth++ == 0)
			{
				_before = Math.Max(0, health);
				_eligible = eligible;
				_failed = false;
			}
		}

		public int End(int health, bool succeeded)
		{
			if (_depth == 0)
			{
				throw new InvalidOperationException("No health operation to complete.");
			}
			_failed |= !succeeded;
			if (--_depth != 0 || !_eligible || _failed)
			{
				return 0;
			}
			return Math.Max(0, _before - Math.Max(0, health));
		}
	}
	[BepInPlugin("saya.DamageShare", "Damage Share", "0.1.3")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class HealingOperation
		{
			public readonly Operation Source;

			public readonly HealingReward Reward;

			public readonly int Amount;

			public readonly string Kind;

			public HealingOperation(Operation source, HealingReward reward, int amount, string kind)
			{
				Source = source;
				Reward = reward;
				Amount = amount;
				Kind = kind;
			}
		}

		[HarmonyPatch(typeof(ItemHealthPack), "Update")]
		private static class HealthPackHealingPatch
		{
			[HarmonyTranspiler]
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				return ReplaceHealingCalls(instructions, "HealFromPack", 1);
			}
		}

		[HarmonyPatch(typeof(EnemyTickHealAura), "PlayerHeal")]
		private static class AuraHealingPatch
		{
			[HarmonyTranspiler]
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				return ReplaceHealingCalls(instructions, "HealFromAura", 3);
			}
		}

		[HarmonyPatch(typeof(EnemySpinny), "Update")]
		private static class GambitHealingPatch
		{
			[HarmonyPrefix]
			private static void Prefix(EnemySpinny __instance, out HealingOperation? __state)
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Invalid comparison between Unknown and I4
				//IL_0051: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				//IL_0059: Invalid comparison between Unknown and I4
				//IL_006c: Unknown result type (might be due to invalid IL or missing references)
				//IL_006e: Invalid comparison between Unknown and I4
				//IL_005b: Unknown result type (might be due to invalid IL or missing references)
				//IL_005d: Invalid comparison between Unknown and I4
				//IL_0076: Unknown result type (might be due to invalid IL or missing references)
				//IL_0078: Invalid comparison between Unknown and I4
				__state = null;
				Plugin instance = _instance;
				if (instance == null || !instance._shareHealing.Value)
				{
					return;
				}
				try
				{
					if (instance.RefreshContext() && (int)__instance.currentState == 11 && !GambitEffectDone.Invoke(__instance) && Object.op_Implicit((Object)(object)__instance.playerTarget))
					{
						Colors currentColorbyAngle = __instance.GetCurrentColorbyAngle(__instance.targetAngleDegrees);
						if ((int)currentColorbyAngle == 2 || (int)currentColorbyAngle == 3)
						{
							__state = instance.BeginHealing(__instance.playerTarget.playerHealth, ((int)currentColorbyAngle == 3) ? HealingReward.Full : HealingReward.Fixed, 25, ((int)currentColorbyAngle == 3) ? "gambit-white" : "gambit-green");
						}
					}
				}
				catch (Exception exception)
				{
					instance.FailClosed(exception);
				}
			}

			[HarmonyFinalizer]
			private static Exception? Finalizer(EnemySpinny __instance, HealingOperation? __state, Exception? __exception)
			{
				if (__state == null)
				{
					return __exception;
				}
				Plugin plugin = __state.Source.Plugin;
				if (__exception != null)
				{
					plugin.FailClosed(__exception);
					return __exception;
				}
				try
				{
					if (GambitEffectDone.Invoke(__instance))
					{
						plugin.ShareHealing(__state);
					}
				}
				catch (Exception exception)
				{
					plugin.FailClosed(exception);
				}
				return __exception;
			}
		}

		private sealed class RemoteState
		{
			public readonly int Owner;

			public readonly int View;

			public readonly RemoteHealthLedger Ledger;

			public RemoteState(PlayerAvatar avatar, bool seed)
			{
				Owner = avatar.photonView.OwnerActorNr;
				View = avatar.photonView.ViewID;
				Ledger = new RemoteHealthLedger(Health.Invoke(avatar.playerHealth), seed && HealthSet.Invoke(avatar.playerHealth), Dead.Invoke(avatar));
			}
		}

		private sealed class Operation
		{
			public readonly Plugin Plugin;

			public readonly PlayerAvatar Avatar;

			public readonly int Epoch;

			public RemoteState? Remote;

			public bool OwnerMessage;

			public bool ExcludedDeath;

			public int Loss;

			public Operation(Plugin plugin, PlayerAvatar avatar)
			{
				Plugin = plugin;
				Avatar = avatar;
				Epoch = plugin._epoch;
			}
		}

		[HarmonyPatch(typeof(PlayerHealth), "Hurt")]
		private static class HurtPatch
		{
			[HarmonyPrefix]
			private static void Prefix(PlayerHealth __instance, bool hurtByHeal, int enemyIndex, out Operation? __state)
			{
				__state = _instance?.BeginLocal(__instance, hurtByHeal || MarkedDeath(enemyIndex));
			}

			[HarmonyFinalizer]
			private static Exception? Finalizer(PlayerHealth __instance, Operation? __state, Exception? __exception)
			{
				__state?.Plugin.EndLocal(__instance, __state, __exception);
				return __exception;
			}
		}

		[HarmonyPatch(typeof(PlayerHealth), "Death")]
		private static class DeathPatch
		{
			[HarmonyPrefix]
			private static void Prefix(PlayerHealth __instance, out Operation? __state)
			{
				__state = _instance?.BeginLocal(__instance, excluded: false);
			}

			[HarmonyFinalizer]
			private static Exception? Finalizer(PlayerHealth __instance, Operation? __state, Exception? __exception)
			{
				__state?.Plugin.EndLocal(__instance, __state, __exception);
				return __exception;
			}
		}

		[HarmonyPatch(typeof(PlayerHealth), "UpdateHealthRPC")]
		private static class HealthUpdatePatch
		{
			[HarmonyPrefix]
			private static void Prefix(PlayerHealth __instance, PhotonMessageInfo _info, out Operation? __state)
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				__state = _instance?.BeginRemote(((Component)__instance).GetComponent<PlayerAvatar>(), _info);
			}

			[HarmonyFinalizer]
			private static Exception? Finalizer(PlayerHealth __instance, bool effect, bool hurtByHeal, Operation? __state, Exception? __exception)
			{
				if (__state == null)
				{
					return __exception;
				}
				Plugin plugin = __state.Plugin;
				if (__exception != null)
				{
					plugin.FailClosed(__exception);
					return __exception;
				}
				try
				{
					if (!plugin.RefreshContext() || plugin._epoch != __state.Epoch)
					{
						return __exception;
					}
					int num = Health.Invoke(__instance);
					int num2 = __state.Remote.Ledger.Observe(num, effect, hurtByHeal || !__state.OwnerMessage);
					((BaseUnityPlugin)plugin).Logger.LogInfo((object)$"HP epoch={plugin._epoch} view={__state.Avatar.photonView.ViewID} hp={num} effect={effect} excluded={hurtByHeal} owner={__state.OwnerMessage} loss={num2}.");
					plugin.Share(__state, num2, "owner-update");
				}
				catch (Exception exception)
				{
					plugin.FailClosed(exception);
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")]
		private static class DeathRpcPatch
		{
			[HarmonyPrefix]
			private static void Prefix(PlayerAvatar __instance, int enemyIndex, PhotonMessageInfo _info, out Operation? __state)
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
				__state = null;
				Plugin instance = _instance;
				if (instance == null || !Object.op_Implicit((Object)(object)__instance.photonView) || _info.Sender == null)
				{
					return;
				}
				int actorNumber = _info.Sender.ActorNumber;
				if (actorNumber != __instance.photonView.OwnerActorNr)
				{
					Room currentRoom = PhotonNetwork.CurrentRoom;
					if (actorNumber != ((currentRoom != null) ? new int?(currentRoom.MasterClientId) : ((int?)null)))
					{
						return;
					}
				}
				if (__instance.photonView.IsMine)
				{
					__state = new Operation(instance, __instance)
					{
						ExcludedDeath = MarkedDeath(enemyIndex)
					};
					if (__state.ExcludedDeath)
					{
						instance._excludedDeathDepth++;
					}
				}
				else
				{
					__state = instance.BeginRemote(__instance, _info);
					if (__state != null)
					{
						__state.Loss = __state.Remote.Ledger.Die(MarkedDeath(enemyIndex) || !__state.OwnerMessage);
					}
				}
			}

			[HarmonyFinalizer]
			private static Exception? Finalizer(int enemyIndex, Operation? __state, Exception? __exception)
			{
				if (__state == null)
				{
					return __exception;
				}
				Plugin plugin = __state.Plugin;
				if (__state.ExcludedDeath)
				{
					plugin._excludedDeathDepth--;
				}
				if (__exception != null && plugin.RefreshContext())
				{
					plugin.FailClosed(__exception);
				}
				if (__state.Remote != null && __exception == null)
				{
					try
					{
						((BaseUnityPlugin)plugin).Logger.LogInfo((object)$"Death epoch={plugin._epoch} view={__state.Avatar.photonView.ViewID} marker={enemyIndex} loss={__state.Loss}.");
						plugin.Share(__state, __state.Loss, "owner-death");
					}
					catch (Exception exception)
					{
						plugin.FailClosed(exception);
					}
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(PlayerAvatar), "ReviveRPC")]
		private static class RevivePatch
		{
			[HarmonyFinalizer]
			private static Exception? Finalizer(PlayerAvatar __instance, PhotonMessageInfo _info, Exception? __exception)
			{
				//IL_0011: 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)
				Plugin instance = _instance;
				if (instance == null || !instance.RefreshContext() || _info.Sender == null || _info.Sender.ActorNumber != instance._master)
				{
					return __exception;
				}
				if (__exception != null)
				{
					instance.FailClosed(__exception);
					return __exception;
				}
				if (!Object.op_Implicit((Object)(object)__instance.playerHealth) || Dead.Invoke(__instance) || !Object.op_Implicit((Object)(object)__instance.photonView) || __instance.photonView.IsMine)
				{
					return __exception;
				}
				try
				{
					instance.GetRemote(__instance).Ledger.Revive();
				}
				catch (Exception exception)
				{
					instance.FailClosed(exception);
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(PlayerHealth), "HurtOther")]
		private static class HealthCostPatch
		{
			[HarmonyPrefix]
			private static void Prefix(bool hurtByHeal, ref int enemyIndex)
			{
				if (_instance != null && _instance.RefreshContext() && hurtByHeal && enemyIndex == -1)
				{
					enemyIndex = -2147483647;
				}
			}
		}

		private static readonly FieldRef<PlayerHealth, int> MaxHealth = AccessTools.FieldRefAccess<PlayerHealth, int>("maxHealth");

		private static readonly FieldRef<EnemySpinny, bool> GambitEffectDone = AccessTools.FieldRefAccess<EnemySpinny, bool>("effectDone");

		private ConfigEntry<bool> _shareHealing;

		public const string PluginGuid = "saya.DamageShare";

		public const string PluginName = "Damage Share";

		public const string PluginVersion = "0.1.3";

		private const int SharedDeath = int.MinValue;

		private const int HealthCostDeath = -2147483647;

		private static readonly FieldRef<PlayerHealth, int> Health = AccessTools.FieldRefAccess<PlayerHealth, int>("health");

		private static readonly FieldRef<PlayerHealth, bool> HealthSet = AccessTools.FieldRefAccess<PlayerHealth, bool>("healthSet");

		private static readonly FieldRef<PlayerAvatar, bool> Dead = AccessTools.FieldRefAccess<PlayerAvatar, bool>("deadSet");

		private static readonly FieldRef<PlayerAvatar, bool> Disabled = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isDisabled");

		private static Plugin? _instance;

		private readonly Harmony _harmony = new Harmony("saya.DamageShare");

		private readonly DamageCapture _capture = new DamageCapture();

		private readonly Dictionary<PlayerHealth, RemoteState> _remote = new Dictionary<PlayerHealth, RemoteState>();

		private object? _room;

		private LevelGenerator? _level;

		private int _master;

		private int _epoch;

		private int _excludedDeathDepth;

		private bool _active;

		private bool _failed;

		private long _event;

		private ConfigEntry<float> _damageMultiplier;

		private ConfigEntry<bool> _shareInShop;

		private ConfigEntry<bool> _shareInArena;

		private SharingArea _area;

		private static bool CanReceiveHealing(PlayerAvatar avatar)
		{
			if (Object.op_Implicit((Object)(object)avatar) && Object.op_Implicit((Object)(object)avatar.playerHealth) && !avatar.playerHealth.isMenuAvatar && Object.op_Implicit((Object)(object)avatar.photonView) && avatar.photonView.Owner != null && !avatar.photonView.Owner.IsInactive && !avatar.photonView.IsRoomView && !Disabled.Invoke(avatar) && !Dead.Invoke(avatar) && HealthSet.Invoke(avatar.playerHealth))
			{
				return Health.Invoke(avatar.playerHealth) > 0;
			}
			return false;
		}

		private HealingOperation? BeginHealing(PlayerHealth source, HealingReward reward, int amount, string kind)
		{
			if (!_shareHealing.Value || !RefreshContext() || !Object.op_Implicit((Object)(object)source))
			{
				return null;
			}
			PlayerAvatar component = ((Component)source).GetComponent<PlayerAvatar>();
			if (!CanReceiveHealing(component))
			{
				return null;
			}
			return new HealingOperation(new Operation(this, component), reward, amount, kind);
		}

		private void ShareHealing(HealingOperation operation)
		{
			Operation source = operation.Source;
			if (!_shareHealing.Value || !RefreshContext() || source.Epoch != _epoch || !CanReceiveHealing(source.Avatar))
			{
				return;
			}
			long num = ++_event;
			foreach (PlayerAvatar item in new List<PlayerAvatar>(SemiFunc.PlayerGetAll()))
			{
				if (!((Object)(object)item == (Object)(object)source.Avatar) && CanReceiveHealing(item))
				{
					if (!_shareHealing.Value || !RefreshContext() || source.Epoch != _epoch)
					{
						break;
					}
					int targetMaxHealth = MaxHealth.Invoke(item.playerHealth);
					int num2 = SharedHealing.Amount(operation.Reward, operation.Amount, targetMaxHealth);
					if (num2 > 0)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)$"Heal request event={num} epoch={_epoch} source={source.Avatar.photonView.ViewID} target={item.photonView.ViewID} kind={operation.Kind} amount={num2}; application is not acknowledged by this send.");
						item.playerHealth.HealOther(num2, true);
					}
				}
			}
		}

		private static void HealFromSource(PlayerHealth source, int amount, bool effect, string kind)
		{
			Plugin instance = _instance;
			HealingOperation healingOperation = null;
			try
			{
				healingOperation = instance?.BeginHealing(source, HealingReward.Fixed, amount, kind);
			}
			catch (Exception exception)
			{
				instance?.FailClosed(exception);
			}
			source.HealOther(amount, effect);
			if (healingOperation == null)
			{
				return;
			}
			try
			{
				instance.ShareHealing(healingOperation);
			}
			catch (Exception exception2)
			{
				instance.FailClosed(exception2);
			}
		}

		private static void HealFromPack(PlayerHealth source, int amount, bool effect)
		{
			HealFromSource(source, amount, effect, "health-pack");
		}

		private static void HealFromAura(PlayerHealth source, int amount, bool effect)
		{
			HealFromSource(source, amount, effect, "green-aura");
		}

		private static IEnumerable<CodeInstruction> ReplaceHealingCalls(IEnumerable<CodeInstruction> instructions, string replacementName, int expectedCalls)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo methodInfo = AccessTools.Method(typeof(PlayerHealth), "HealOther", new Type[2]
			{
				typeof(int),
				typeof(bool)
			}, (Type[])null);
			MethodInfo operand = AccessTools.Method(typeof(Plugin), replacementName, (Type[])null, (Type[])null);
			int num = 0;
			foreach (CodeInstruction item in list)
			{
				if (CodeInstructionExtensions.Calls(item, methodInfo))
				{
					item.opcode = OpCodes.Call;
					item.operand = operand;
					num++;
				}
			}
			if (num != expectedCalls)
			{
				throw new InvalidOperationException($"Healing patch {replacementName}: expected {expectedCalls} HealOther call sites, found {num}. Review game compatibility.");
			}
			return list;
		}

		private void Awake()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			((Component)this).transform.SetParent((Transform)null);
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			_damageMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "SharedDamageMultiplier", 1f, new ConfigDescription("Multiplier for damage sent to teammates, not the original victim. Range 0-3; fractional HP is rounded down per hit. Changes in REPOConfig apply to the next shared event. Only the host's setting is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			_shareInShop = ((BaseUnityPlugin)this).Config.Bind<bool>("Damage", "ShareDamageInShop", false, "Also share damage and enabled healing in the shop. Only the host's setting is used. Changes in REPOConfig apply immediately; excluded events are never backfilled.");
			_shareInArena = ((BaseUnityPlugin)this).Config.Bind<bool>("Damage", "ShareDamageInArena", false, "Also share damage and enabled healing in the arena. Only the host's setting is used. Changes in REPOConfig apply immediately. Shared deaths can affect arena results; vanilla ending and invincibility rules still apply.");
			_shareHealing = ((BaseUnityPlugin)this).Config.Bind<bool>("Healing", "ShareHealing", false, "Share health packs, green healing aura, and Gambit green/white rewards with living teammates. Excludes HP donation, truck healing, upgrades and revives. Uses the shop/arena switches above. Independent of damage multiplier. Host only; changes apply to the next source event without restart or backfill.");
			_instance = this;
			try
			{
				_harmony.PatchAll(typeof(HurtPatch));
				_harmony.PatchAll(typeof(DeathPatch));
				_harmony.PatchAll(typeof(HealthUpdatePatch));
				_harmony.PatchAll(typeof(DeathRpcPatch));
				_harmony.PatchAll(typeof(RevivePatch));
				_harmony.PatchAll(typeof(HealthCostPatch));
				_harmony.PatchAll(typeof(HealthPackHealingPatch));
				_harmony.PatchAll(typeof(AuraHealingPatch));
				_harmony.PatchAll(typeof(GambitHealingPatch));
			}
			catch
			{
				_failed = true;
				_instance = null;
				_harmony.UnpatchSelf();
				throw;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Damage Share v0.1.3: host-only sharing loaded. HP costs excluded; vanilla invincibility respected.");
		}

		private void Update()
		{
			RefreshContext();
		}

		private static SharingArea CurrentArea()
		{
			if (!Object.op_Implicit((Object)(object)RunManager.instance))
			{
				return SharingArea.None;
			}
			if (SemiFunc.RunIsShop())
			{
				return SharingArea.Shop;
			}
			if (SemiFunc.RunIsArena())
			{
				return SharingArea.Arena;
			}
			if (!SemiFunc.RunIsLevel() || SemiFunc.RunIsRecording())
			{
				return SharingArea.None;
			}
			return SharingArea.Level;
		}

		private bool RefreshContext()
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Invalid comparison between Unknown and I4
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Invalid comparison between Unknown and I4
			Room currentRoom = PhotonNetwork.CurrentRoom;
			LevelGenerator instance = LevelGenerator.Instance;
			int num = ((currentRoom != null) ? currentRoom.MasterClientId : 0);
			SharingArea sharingArea = ((Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMultiplayer()) ? CurrentArea() : SharingArea.None);
			bool flag = !_failed && Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMultiplayer() && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && SharingAreas.IsEnabled(sharingArea, _shareInShop.Value, _shareInArena.Value) && Object.op_Implicit((Object)(object)instance) && instance.Generated && Object.op_Implicit((Object)(object)GameDirector.instance) && ((int)GameDirector.instance.currentState == 2 || (int)GameDirector.instance.currentState == 7);
			if (_room != currentRoom || _level != instance || _master != num || _active != flag || _area != sharingArea)
			{
				_remote.Clear();
				_epoch++;
				_room = currentRoom;
				_level = instance;
				_master = num;
				_active = flag;
				_area = sharingArea;
				if (flag)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Host sharing active: epoch={_epoch}, master={num}, area={sharingArea}.");
				}
			}
			return flag;
		}

		private static bool MarkedDeath(int enemyIndex)
		{
			if (enemyIndex != int.MinValue)
			{
				return enemyIndex == -2147483647;
			}
			return true;
		}

		private static bool IsLocal(PlayerHealth health)
		{
			if (Object.op_Implicit((Object)(object)PlayerAvatar.instance) && (Object)(object)PlayerAvatar.instance.playerHealth == (Object)(object)health && !health.isMenuAvatar && Object.op_Implicit((Object)(object)PlayerAvatar.instance.photonView))
			{
				return PlayerAvatar.instance.photonView.IsMine;
			}
			return false;
		}

		private RemoteState GetRemote(PlayerAvatar avatar)
		{
			PlayerHealth playerHealth = avatar.playerHealth;
			RemoteState value;
			bool flag = _remote.TryGetValue(playerHealth, out value);
			if (!flag || value.Owner != avatar.photonView.OwnerActorNr || value.View != avatar.photonView.ViewID)
			{
				value = new RemoteState(avatar, !flag);
				_remote[playerHealth] = value;
			}
			return value;
		}

		private Operation? BeginLocal(PlayerHealth health, bool excluded)
		{
			if (!IsLocal(health))
			{
				return null;
			}
			bool flag = RefreshContext();
			PlayerAvatar instance = PlayerAvatar.instance;
			_capture.Begin(Health.Invoke(health), flag && HealthSet.Invoke(health) && !Dead.Invoke(instance) && !excluded && _excludedDeathDepth == 0);
			return new Operation(this, instance);
		}

		private void EndLocal(PlayerHealth health, Operation operation, Exception? error)
		{
			try
			{
				int loss = _capture.End(Health.Invoke(health), error == null);
				if (error == null)
				{
					Share(operation, loss, "local");
				}
			}
			catch (Exception exception)
			{
				FailClosed(exception);
			}
		}

		private Operation? BeginRemote(PlayerAvatar avatar, PhotonMessageInfo info)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			if (!RefreshContext() || !Object.op_Implicit((Object)(object)avatar) || !Object.op_Implicit((Object)(object)avatar.playerHealth) || !Object.op_Implicit((Object)(object)avatar.photonView) || avatar.playerHealth.isMenuAvatar || avatar.photonView.IsMine || info.Sender == null)
			{
				return null;
			}
			int actorNumber = info.Sender.ActorNumber;
			bool flag = actorNumber == avatar.photonView.OwnerActorNr;
			if (!flag && actorNumber != _master)
			{
				return null;
			}
			return new Operation(this, avatar)
			{
				Remote = GetRemote(avatar),
				OwnerMessage = flag
			};
		}

		private void Share(Operation operation, int loss, string reason)
		{
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			if (loss <= 0 || !RefreshContext() || operation.Epoch != _epoch)
			{
				return;
			}
			int num = SharedDamageAmount.Calculate(loss, _damageMultiplier.Value);
			if (num == 0)
			{
				return;
			}
			long num2 = ++_event;
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loss event={num2} epoch={_epoch} source={operation.Avatar.photonView.ViewID} amount={loss} shared={num} kind={reason}.");
			foreach (PlayerAvatar item in new List<PlayerAvatar>(SemiFunc.PlayerGetAll()))
			{
				if (Object.op_Implicit((Object)(object)item) && !((Object)(object)item == (Object)(object)operation.Avatar) && Object.op_Implicit((Object)(object)item.playerHealth) && Object.op_Implicit((Object)(object)item.photonView) && item.photonView.Owner != null && !item.photonView.Owner.IsInactive && !item.photonView.IsRoomView && !Disabled.Invoke(item) && !Dead.Invoke(item) && HealthSet.Invoke(item.playerHealth) && Health.Invoke(item.playerHealth) > 0)
				{
					if (!RefreshContext() || operation.Epoch != _epoch)
					{
						break;
					}
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Share request event={num2} target={item.photonView.ViewID} amount={num}; application is not acknowledged by this send.");
					item.playerHealth.HurtOther(num, Vector3.zero, false, int.MinValue, true);
				}
			}
		}

		private void FailClosed(Exception exception)
		{
			if (!_failed)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Damage and healing sharing disabled until game restart: {exception}");
			}
			_failed = true;
			RefreshContext();
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
			_remote.Clear();
			if ((Object)(object)_instance == (Object)(object)this)
			{
				_instance = null;
			}
		}
	}
	internal sealed class RemoteHealthLedger
	{
		public int Health { get; private set; }

		public bool Initialized { get; private set; }

		public bool Dead { get; private set; }

		public RemoteHealthLedger(int health, bool initialized, bool dead)
		{
			Health = Math.Max(0, health);
			Initialized = initialized;
			Dead = dead;
		}

		public int Observe(int health, bool effect, bool excluded)
		{
			health = Math.Max(0, health);
			if (Dead)
			{
				return 0;
			}
			if (!Initialized)
			{
				Health = health;
				Initialized = health > 0;
				return 0;
			}
			if (health == 0 && Health > 0 && effect)
			{
				throw new InvalidOperationException("HP=0 arrived before the death marker; sharing stopped to avoid a feedback loop.");
			}
			int result = ((effect && !excluded) ? Math.Max(0, Health - health) : 0);
			Health = health;
			return result;
		}

		public int Die(bool excluded)
		{
			int result = ((Initialized && !Dead && !excluded) ? Health : 0);
			Health = 0;
			Dead = true;
			return result;
		}

		public void Revive()
		{
			Health = 0;
			Initialized = false;
			Dead = false;
		}
	}
	internal static class SharedDamageAmount
	{
		public const float DefaultMultiplier = 1f;

		public const float MaxMultiplier = 3f;

		public static int Calculate(int actualLoss, float multiplier)
		{
			if (actualLoss <= 0)
			{
				return 0;
			}
			if (float.IsNaN(multiplier) || float.IsInfinity(multiplier))
			{
				multiplier = 1f;
			}
			multiplier = Math.Max(0f, Math.Min(3f, multiplier));
			decimal val = decimal.Floor((decimal)actualLoss * (decimal)multiplier);
			return (int)Math.Min(2147483647m, val);
		}
	}
	internal enum HealingReward
	{
		Fixed,
		Full
	}
	internal static class SharedHealing
	{
		public static int Amount(HealingReward reward, int sourceAmount, int targetMaxHealth)
		{
			if (targetMaxHealth <= 0 || targetMaxHealth > 1073741823)
			{
				return 0;
			}
			return reward switch
			{
				HealingReward.Fixed => Math.Max(0, Math.Min(sourceAmount, targetMaxHealth)), 
				HealingReward.Full => targetMaxHealth, 
				_ => 0, 
			};
		}
	}
	internal enum SharingArea
	{
		None,
		Level,
		Shop,
		Arena
	}
	internal static class SharingAreas
	{
		public static bool IsEnabled(SharingArea area, bool shop, bool arena)
		{
			return area switch
			{
				SharingArea.Level => true, 
				SharingArea.Shop => shop, 
				SharingArea.Arena => arena, 
				_ => false, 
			};
		}
	}
}