using System.Reflection; using NitroxClient.Communication.Abstract; using NitroxClient.GameLogic; using NitroxClient.GameLogic.Spawning.Metadata; using NitroxClient.MonoBehaviours; using NitroxClient.Unity.Helper; using NitroxModel.DataStructures; using NitroxModel.DataStructures.GameLogic.Entities.Metadata; using NitroxModel.DataStructures.Util; using NitroxModel.Helper; using NitroxModel.Packets; using UnityEngine; namespace NitroxPatcher.Patches.Dynamic; public sealed partial class LiveMixin_AddHealth_Patch : NitroxPatch, IDynamicPatch { private static readonly MethodInfo TARGET_METHOD = Reflect.Method((LiveMixin t) => t.AddHealth(default)); /// /// We can broadcast packet update when we aren't processing remote health change /// private static bool CanBroadcast => !Resolve().IsRemoteHealthChanging; public static bool Prefix(out float __state, LiveMixin __instance) { // Persist the previous health value __state = __instance.health; if (!Resolve().IsWhitelistedUpdateType(__instance)) { return true; // everyone should process this locally } return Resolve().ShouldApplyNextHealthUpdate(__instance); } public static void Postfix(float __state, LiveMixin __instance, bool __runOriginal) { if (!__runOriginal || __state == __instance.health) { return; } // This foreach will detect the presence of some components which need to be handled specifically foreach (MonoBehaviour monoBehaviour in __instance.GetComponents()) { switch (monoBehaviour) { case RadiationLeak radiationLeak: HandleRadiationLeakRepair(radiationLeak); return; case BaseCell baseCell: HandleBaseLeakRepair(baseCell, __instance); return; } } // If the foreach ends, it'll mean that none of those components will have been detected so we just execute the generic case HandleGenericEntity(__instance); } private static void HandleRadiationLeakRepair(RadiationLeak radiationLeak) { if (!CanBroadcast || !radiationLeak.TryGetNitroxId(out NitroxId leakId)) { return; } Optional metadata = Resolve().Extract(radiationLeak); if (metadata.HasValue) { Resolve().BroadcastMetadataUpdate(leakId, metadata.Value); } } private static void HandleBaseLeakRepair(BaseCell baseCell, LiveMixin liveMixin) { if (liveMixin.IsFullHealth()) { if (liveMixin.TryGetComponentInParent(out BaseLeakManager baseLeakManager, true)) { LeakRepaired leakRepaired = baseLeakManager.RemoveLeakByAbsoluteCell(baseCell.cell); if (CanBroadcast && leakRepaired != null) { Resolve().Send(leakRepaired); } } } else if (liveMixin.TryGetComponentInParent(out BaseHullStrength baseHullStrength, true)) { BaseHullStrength_CrushDamageUpdate_Patch.BroadcastChange(baseHullStrength, liveMixin); } } private static void HandleGenericEntity(LiveMixin victim) { // Let others know if we have a lock on this entity if (!CanBroadcast || !victim.TryGetIdOrWarn(out NitroxId id) || !Resolve().HasAnyLockType(id)) { return; } Optional metadata = Resolve().Extract(victim.gameObject); if (metadata.HasValue) { Resolve().BroadcastMetadataUpdate(id, metadata.Value); } } }