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 DamageShare v0.1.3
plugins/DamageShare/DamageShare.dll
Decompiled 6 days agousing 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, }; } } }