diff --git a/Nitrox.Test/Patcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchFront_PatchTest.cs b/Nitrox.Test/Patcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchFront_PatchTest.cs new file mode 100644 index 0000000000..9b8b66d6d1 --- /dev/null +++ b/Nitrox.Test/Patcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchFront_PatchTest.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using HarmonyLib; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NitroxTest.Patcher; + +namespace NitroxPatcher.Patches.Dynamic; + +[TestClass] +public class SeaDragonMeleeAttack_OnTouchFront_PatchTest +{ + [TestMethod] + public void Sanity() + { + IEnumerable originalIl = PatchTestHelper.GetInstructionsFromMethod(SeaDragonMeleeAttack_OnTouchFront_Patch.TARGET_METHOD); + IEnumerable transformedIl = SeaDragonMeleeAttack_OnTouchFront_Patch.Transpiler(originalIl); + transformedIl.Count().Should().Be(originalIl.Count() + 9); + } +} diff --git a/Nitrox.Test/Patcher/Patches/Dynamic/SeaDragonMeleeAttack_SwatAttack_PatchTest.cs b/Nitrox.Test/Patcher/Patches/Dynamic/SeaDragonMeleeAttack_SwatAttack_PatchTest.cs new file mode 100644 index 0000000000..025cc4e41e --- /dev/null +++ b/Nitrox.Test/Patcher/Patches/Dynamic/SeaDragonMeleeAttack_SwatAttack_PatchTest.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using HarmonyLib; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NitroxTest.Patcher; + +namespace NitroxPatcher.Patches.Dynamic; + +[TestClass] +public class SeaDragonMeleeAttack_SwatAttack_PatchTest +{ + [TestMethod] + public void Sanity() + { + IEnumerable originalIl = PatchTestHelper.GetInstructionsFromMethod(SeaDragonMeleeAttack_SwatAttack_Patch.TARGET_METHOD); + IEnumerable transformedIl = SeaDragonMeleeAttack_SwatAttack_Patch.Transpiler(originalIl); + transformedIl.Count().Should().Be(originalIl.Count() + 4); + } +} diff --git a/NitroxClient/Communication/Packets/Processors/RangedAttackLastTargetUpdateProcessor.cs b/NitroxClient/Communication/Packets/Processors/RangedAttackLastTargetUpdateProcessor.cs new file mode 100644 index 0000000000..c7d7716c2c --- /dev/null +++ b/NitroxClient/Communication/Packets/Processors/RangedAttackLastTargetUpdateProcessor.cs @@ -0,0 +1,13 @@ +using NitroxClient.Communication.Packets.Processors.Abstract; +using NitroxClient.GameLogic; +using NitroxModel.Packets; + +namespace NitroxClient.Communication.Packets.Processors; + +public class RangedAttackLastTargetUpdateProcessor : ClientPacketProcessor +{ + public override void Process(RangedAttackLastTargetUpdate packet) + { + AI.RangedAttackLastTargetUpdate(packet.CreatureId, packet.TargetId, packet.AttackTypeIndex, packet.State); + } +} diff --git a/NitroxClient/Communication/Packets/Processors/SeaDragonAttackTargetProcessor.cs b/NitroxClient/Communication/Packets/Processors/SeaDragonAttackTargetProcessor.cs new file mode 100644 index 0000000000..cf449b12f0 --- /dev/null +++ b/NitroxClient/Communication/Packets/Processors/SeaDragonAttackTargetProcessor.cs @@ -0,0 +1,53 @@ +using NitroxClient.Communication.Packets.Processors.Abstract; +using NitroxClient.GameLogic.PlayerLogic; +using NitroxClient.MonoBehaviours; +using NitroxModel.Packets; +using UnityEngine; + +namespace NitroxClient.Communication.Packets.Processors; + +public class SeaDragonAttackTargetProcessor : ClientPacketProcessor +{ + public override void Process(SeaDragonAttackTarget packet) + { + if (!NitroxEntity.TryGetComponentFrom(packet.SeaDragonId, out SeaDragonMeleeAttack seaDragonMeleeAttack) || + !NitroxEntity.TryGetObjectFrom(packet.TargetId, out GameObject target)) + { + return; + } + + seaDragonMeleeAttack.seaDragon.Aggression.Value = packet.Aggression; + if (target.GetComponent()) + { + // SeaDragonMeleeAttack.OnTouchFront's useful part about Cyclops attack + seaDragonMeleeAttack.animator.SetTrigger("shove"); + seaDragonMeleeAttack.SendMessage("OnMeleeAttack", target, SendMessageOptions.DontRequireReceiver); + seaDragonMeleeAttack.timeLastBite = Time.time; + return; + } + + + // SeaDragonMeleeAttack.OnTouchFront's useful part about local player attack + Collider collider; + if (target.TryGetComponent(out RemotePlayerIdentifier remotePlayerIdentifier)) + { + collider = remotePlayerIdentifier.RemotePlayer.Collider; + } + else if (target.GetComponent()) + { + collider = Player.mainCollider; + } + else + { + return; + } + + seaDragonMeleeAttack.timeLastBite = Time.time; + if (seaDragonMeleeAttack.attackSound) + { + // TODO: Adapt this code when #1780 is merged + Utils.PlayEnvSound(seaDragonMeleeAttack.attackSound, collider.transform.position, 20f); + } + seaDragonMeleeAttack.OnTouch(collider); + } +} diff --git a/NitroxClient/Communication/Packets/Processors/SeaDragonGrabExosuitProcessor.cs b/NitroxClient/Communication/Packets/Processors/SeaDragonGrabExosuitProcessor.cs new file mode 100644 index 0000000000..4c12bb58dd --- /dev/null +++ b/NitroxClient/Communication/Packets/Processors/SeaDragonGrabExosuitProcessor.cs @@ -0,0 +1,23 @@ +using NitroxClient.Communication.Packets.Processors.Abstract; +using NitroxClient.MonoBehaviours; +using NitroxModel.Packets; + +namespace NitroxClient.Communication.Packets.Processors; + +public class SeaDragonGrabExosuitProcessor : ClientPacketProcessor +{ + public override void Process(SeaDragonGrabExosuit packet) + { + if (!NitroxEntity.TryGetComponentFrom(packet.SeaDragonId, out SeaDragon seaDragon) || + !NitroxEntity.TryGetComponentFrom(packet.TargetId, out Exosuit exosuit)) + { + return; + } + + using (PacketSuppressor.Suppress()) + { + seaDragon.GrabExosuit(exosuit); + seaDragon.CancelInvoke(nameof(SeaDragon.DamageExosuit)); + } + } +} diff --git a/NitroxClient/Communication/Packets/Processors/SeaDragonSwatAttackProcessor.cs b/NitroxClient/Communication/Packets/Processors/SeaDragonSwatAttackProcessor.cs new file mode 100644 index 0000000000..13a01fd61a --- /dev/null +++ b/NitroxClient/Communication/Packets/Processors/SeaDragonSwatAttackProcessor.cs @@ -0,0 +1,24 @@ +using NitroxClient.Communication.Packets.Processors.Abstract; +using NitroxClient.MonoBehaviours; +using NitroxModel.Packets; +using UnityEngine; + +namespace NitroxClient.Communication.Packets.Processors; + +public class SeaDragonSwatAttackProcessor : ClientPacketProcessor +{ + public override void Process(SeaDragonSwatAttack packet) + { + if (!NitroxEntity.TryGetComponentFrom(packet.SeaDragonId, out SeaDragonMeleeAttack seaDragonMeleeAttack) || + !NitroxEntity.TryGetObjectFrom(packet.TargetId, out GameObject targetObject)) + { + return; + } + + using (PacketSuppressor.Suppress()) + { + seaDragonMeleeAttack.seaDragon.Aggression.Value = packet.Aggression; + seaDragonMeleeAttack.SwatAttack(targetObject, packet.IsRightHand); + } + } +} diff --git a/NitroxClient/GameLogic/AI.cs b/NitroxClient/GameLogic/AI.cs index df6e259115..1674d1c259 100644 --- a/NitroxClient/GameLogic/AI.cs +++ b/NitroxClient/GameLogic/AI.cs @@ -7,6 +7,7 @@ using NitroxModel.DataStructures; using NitroxModel.Packets; using UnityEngine; +using static NitroxModel.Packets.RangedAttackLastTargetUpdate; namespace NitroxClient.GameLogic; @@ -22,7 +23,7 @@ public class AI /// private readonly HashSet creatureActionWhitelist = [ - typeof(AttackLastTarget), typeof(AttackCyclops) + typeof(AttackLastTarget), typeof(RangedAttackLastTarget), typeof(AttackCyclops) ]; /// @@ -50,7 +51,7 @@ public void BroadcastNewAction(NitroxId creatureId, Creature creature, CreatureA return; } - ErrorMessage.AddMessage($"[SEND] reaper action: {newAction.GetType().FullName}"); + ErrorMessage.AddMessage($"[SEND] synced action: {newAction.GetType().FullName}"); packetSender.Send(new CreatureActionChanged(creatureId, newAction.GetType().FullName)); } @@ -117,6 +118,31 @@ public static void AttackCyclopsTargetChanged(NitroxId creatureId, NitroxId targ attackCyclops.SetCurrentTarget(targetObject, targetObject.GetComponent()); } + public static void RangedAttackLastTargetUpdate(NitroxId creatureId, NitroxId targetId, int attackTypeIndex, ActionState state) + { + if (!NitroxEntity.TryGetComponentFrom(creatureId, out RangedAttackLastTarget rangedAttackLastTarget) || + !NitroxEntity.TryGetObjectFrom(targetId, out GameObject targetObject)) + { + return; + } + + RangedAttackLastTarget.RangedAttackType attackType = rangedAttackLastTarget.attackTypes[attackTypeIndex]; + rangedAttackLastTarget.currentAttack = attackType; + rangedAttackLastTarget.currentTarget = targetObject; + + switch (state) + { + case ActionState.CHARGING: + rangedAttackLastTarget.StartCharging(attackType); + ErrorMessage.AddMessage($"[GET] {rangedAttackLastTarget.name} charges against {targetObject.name}"); + break; + case ActionState.CASTING: + rangedAttackLastTarget.StartCasting(attackType); + ErrorMessage.AddMessage($"[GET] {rangedAttackLastTarget.name} casts against {targetObject.name}"); + break; + } + } + public bool TryGetActionForCreature(Creature creature, out CreatureAction action) { // TODO: Fix ondeath cinematic being played for all players when getting bitten by a reaper diff --git a/NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs b/NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs index 0025a377e0..36d3103cf8 100644 --- a/NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs +++ b/NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs @@ -41,6 +41,9 @@ private void SetPlayerPermissions(Perms permissions) private void SetPlayerGameObjectId(NitroxId id) { + EcoTarget playerEcoTarget = Player.mainObject.AddComponent(); + playerEcoTarget.SetTargetType(RemotePlayer.PLAYER_ECO_TARGET_TYPE); + NitroxEntity.SetNewId(Player.mainObject, id); Log.Info($"Received initial sync player GameObject Id: {id}"); } diff --git a/NitroxClient/GameLogic/RemotePlayer.cs b/NitroxClient/GameLogic/RemotePlayer.cs index 7b544f7f9c..bbc1826889 100644 --- a/NitroxClient/GameLogic/RemotePlayer.cs +++ b/NitroxClient/GameLogic/RemotePlayer.cs @@ -17,6 +17,10 @@ namespace NitroxClient.GameLogic { public class RemotePlayer : INitroxPlayer { + /// + /// Marks and every so they can be precisely queried (e.g. by sea dragons). + /// + public const EcoTargetType PLAYER_ECO_TARGET_TYPE = (EcoTargetType)5050; private static readonly int animatorPlayerIn = Animator.StringToHash("player_in"); private readonly PlayerModelManager playerModelManager; @@ -311,8 +315,14 @@ public void UpdateEquipmentVisibility(List equippedItems) private void SetupBody() { // set as a target for reapers - EcoTarget ecoTarget = Body.AddComponent(); - ecoTarget.SetTargetType(EcoTargetType.Shark); + EcoTarget sharkEcoTarget = Body.AddComponent(); + sharkEcoTarget.SetTargetType(EcoTargetType.Shark); + + EcoTarget playerEcoTarget = Body.AddComponent(); + playerEcoTarget.SetTargetType(PLAYER_ECO_TARGET_TYPE); + + TechTag techTag = Body.AddComponent(); + techTag.type = TechType.Player; RemotePlayerIdentifier identifier = Body.AddComponent(); identifier.RemotePlayer = this; @@ -397,5 +407,14 @@ private void RefreshVitalsVisibility() vitals.SetStatsVisible(visible); } } + + /// + /// Adaptation of for remote players. + /// NB: This doesn't check for other player's use of 'invisible' command + /// + public bool CanBeAttacked() + { + return !SubRoot && !EscapePod && PlayerContext.GameMode != NitroxGameMode.CREATIVE; + } } } diff --git a/NitroxModel/Packets/RangedAttackLastTargetUpdate.cs b/NitroxModel/Packets/RangedAttackLastTargetUpdate.cs new file mode 100644 index 0000000000..92555a5a5c --- /dev/null +++ b/NitroxModel/Packets/RangedAttackLastTargetUpdate.cs @@ -0,0 +1,27 @@ +using System; +using NitroxModel.DataStructures; + +namespace NitroxModel.Packets; + +[Serializable] +public class RangedAttackLastTargetUpdate : Packet +{ + public NitroxId CreatureId { get; } + public NitroxId TargetId { get; } + public int AttackTypeIndex { get; } + public ActionState State { get; } + + public RangedAttackLastTargetUpdate(NitroxId creatureId, NitroxId targetId, int attackTypeIndex, ActionState state) + { + CreatureId = creatureId; + TargetId = targetId; + AttackTypeIndex = attackTypeIndex; + State = state; + } + + public enum ActionState + { + CHARGING, + CASTING + } +} diff --git a/NitroxModel/Packets/SeaDragonAttackTarget.cs b/NitroxModel/Packets/SeaDragonAttackTarget.cs new file mode 100644 index 0000000000..45b23f404e --- /dev/null +++ b/NitroxModel/Packets/SeaDragonAttackTarget.cs @@ -0,0 +1,19 @@ +using System; +using NitroxModel.DataStructures; + +namespace NitroxModel.Packets; + +[Serializable] +public class SeaDragonAttackTarget : Packet +{ + public NitroxId SeaDragonId { get; } + public NitroxId TargetId { get; } + public float Aggression { get; } + + public SeaDragonAttackTarget(NitroxId seaDragonId, NitroxId targetId, float aggression) + { + SeaDragonId = seaDragonId; + TargetId = targetId; + Aggression = aggression; + } +} diff --git a/NitroxModel/Packets/SeaDragonGrabExosuit.cs b/NitroxModel/Packets/SeaDragonGrabExosuit.cs new file mode 100644 index 0000000000..7b9893da5b --- /dev/null +++ b/NitroxModel/Packets/SeaDragonGrabExosuit.cs @@ -0,0 +1,17 @@ +using System; +using NitroxModel.DataStructures; + +namespace NitroxModel.Packets; + +[Serializable] +public class SeaDragonGrabExosuit : Packet +{ + public NitroxId SeaDragonId { get; } + public NitroxId TargetId { get; } + + public SeaDragonGrabExosuit(NitroxId seaDragonId, NitroxId targetId) + { + SeaDragonId = seaDragonId; + TargetId = targetId; + } +} diff --git a/NitroxModel/Packets/SeaDragonSwatAttack.cs b/NitroxModel/Packets/SeaDragonSwatAttack.cs new file mode 100644 index 0000000000..53c171382d --- /dev/null +++ b/NitroxModel/Packets/SeaDragonSwatAttack.cs @@ -0,0 +1,21 @@ +using System; +using NitroxModel.DataStructures; + +namespace NitroxModel.Packets; + +[Serializable] +public class SeaDragonSwatAttack : Packet +{ + public NitroxId SeaDragonId { get; } + public NitroxId TargetId { get; } + public bool IsRightHand { get; } + public float Aggression { get; } + + public SeaDragonSwatAttack(NitroxId seaDragonId, NitroxId targetId, bool isRightHand, float aggression) + { + SeaDragonId = seaDragonId; + TargetId = targetId; + IsRightHand = isRightHand; + Aggression = aggression; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/AggressiveWhenSeeTarget_IsTargetValid_Patch.cs b/NitroxPatcher/Patches/Dynamic/AggressiveWhenSeeTarget_IsTargetValid_Patch.cs index ec3471b4cf..5253a4f349 100644 --- a/NitroxPatcher/Patches/Dynamic/AggressiveWhenSeeTarget_IsTargetValid_Patch.cs +++ b/NitroxPatcher/Patches/Dynamic/AggressiveWhenSeeTarget_IsTargetValid_Patch.cs @@ -1,4 +1,5 @@ using System.Reflection; +using NitroxClient.GameLogic; using NitroxClient.GameLogic.PlayerLogic; using NitroxModel.Helper; using UnityEngine; @@ -6,8 +7,7 @@ namespace NitroxPatcher.Patches.Dynamic; /// -/// Allow creatures to chose remote players as targets only if they're not in creative mode. -/// NB: This doesn't check for other player's use of 'invisible' command +/// Allow creatures to chose remote players as targets only if they can be attacked () /// public sealed partial class AggressiveWhenSeeTarget_IsTargetValid_Patch : NitroxPatch, IDynamicPatch { @@ -19,9 +19,9 @@ public static bool Prefix(GameObject target, ref bool __result) { return false; } - // We only want to cancel if the remote player is a target but + // We only want to cancel if the target is a remote player which can't attacked if (target.TryGetComponent(out RemotePlayerIdentifier remotePlayerIdentifier) && - remotePlayerIdentifier.RemotePlayer.PlayerContext.GameMode == NitroxModel.Server.NitroxGameMode.CREATIVE) + !remotePlayerIdentifier.RemotePlayer.CanBeAttacked()) { __result = false; return false; diff --git a/NitroxPatcher/Patches/Dynamic/AttackLastTarget_CanAttackTarget_Patch.cs b/NitroxPatcher/Patches/Dynamic/AttackLastTarget_CanAttackTarget_Patch.cs new file mode 100644 index 0000000000..12b0c80d7d --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/AttackLastTarget_CanAttackTarget_Patch.cs @@ -0,0 +1,31 @@ +using System.Reflection; +using NitroxClient.GameLogic; +using NitroxClient.GameLogic.PlayerLogic; +using NitroxModel.Helper; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Allows creatures to chose remote players as targets only if they can be attacked () +/// +public sealed partial class AttackLastTarget_CanAttackTarget_Patch : NitroxPatch, IDynamicPatch +{ + public static readonly MethodInfo TARGET_METHOD = Reflect.Method((AttackLastTarget t) => t.CanAttackTarget(default)); + + public static bool Prefix(GameObject target, ref bool __result) + { + if (!target) + { + return false; + } + // We only want to cancel if the target is a remote player which can't attacked + if (target.TryGetComponent(out RemotePlayerIdentifier remotePlayerIdentifier) && + !remotePlayerIdentifier.RemotePlayer.CanBeAttacked()) + { + __result = false; + return false; + } + return true; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/Creature_ChooseBestAction_Patch.cs b/NitroxPatcher/Patches/Dynamic/Creature_ChooseBestAction_Patch.cs index 8c131b0e03..46c8737198 100644 --- a/NitroxPatcher/Patches/Dynamic/Creature_ChooseBestAction_Patch.cs +++ b/NitroxPatcher/Patches/Dynamic/Creature_ChooseBestAction_Patch.cs @@ -1,22 +1,21 @@ using System.Reflection; using NitroxClient.GameLogic; -using NitroxClient.Unity.Helper; using NitroxModel.DataStructures; using NitroxModel.Helper; namespace NitroxPatcher.Patches.Dynamic; +/// +/// For players without lock: apply remote creature actions and prevent the original call. +/// For players with lock: broadcast new creature actions +/// public sealed partial class Creature_ChooseBestAction_Patch : NitroxPatch, IDynamicPatch { public static readonly MethodInfo TARGET_METHOD = Reflect.Method((Creature t) => t.ChooseBestAction(default)); public static bool Prefix(Creature __instance, out NitroxId __state, ref CreatureAction __result) { - if (!__instance.TryGetIdOrWarn(out __state, true)) - { - return true; - } - if (Resolve().HasAnyLockType(__state)) + if (!__instance.TryGetIdOrWarn(out __state, true) || Resolve().HasAnyLockType(__state)) { return true; } diff --git a/NitroxPatcher/Patches/Dynamic/RangedAttackLastTarget_StartCasting_Patch.cs b/NitroxPatcher/Patches/Dynamic/RangedAttackLastTarget_StartCasting_Patch.cs new file mode 100644 index 0000000000..1cbae4a3c7 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/RangedAttackLastTarget_StartCasting_Patch.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using NitroxClient.Communication.Abstract; +using NitroxClient.GameLogic; +using NitroxModel.DataStructures; +using NitroxModel.Helper; +using NitroxModel.Packets; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Prevents non simulating players from running locally . +/// Broadcasts this event on the simulating player. +/// +public sealed partial class RangedAttackLastTarget_StartCasting_Patch : NitroxPatch, IDynamicPatch +{ + internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((RangedAttackLastTarget t) => t.StartCasting(default)); + + public static void Prefix(RangedAttackLastTarget __instance) + { + if (!Resolve().IsCreatureWhitelisted(__instance.creature) || + !__instance.TryGetNitroxId(out NitroxId creatureId) || + !Resolve().HasAnyLockType(creatureId) || + !__instance.currentTarget || !__instance.currentTarget.TryGetNitroxId(out NitroxId targetId)) + { + return; + } + + int attackTypeIndex = __instance.attackTypes.GetIndex(__instance.currentAttack); + + Resolve().Send(new RangedAttackLastTargetUpdate(creatureId, targetId, attackTypeIndex, RangedAttackLastTargetUpdate.ActionState.CASTING)); + ErrorMessage.AddMessage($"[SEND] {__instance.name} casts against {__instance.currentTarget.name}"); + } +} + diff --git a/NitroxPatcher/Patches/Dynamic/RangedAttackLastTarget_StartCharging_Patch.cs b/NitroxPatcher/Patches/Dynamic/RangedAttackLastTarget_StartCharging_Patch.cs new file mode 100644 index 0000000000..c093333c25 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/RangedAttackLastTarget_StartCharging_Patch.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using NitroxClient.Communication.Abstract; +using NitroxClient.GameLogic; +using NitroxModel.DataStructures; +using NitroxModel.Helper; +using NitroxModel.Packets; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Prevents non simulating players from running locally . +/// Broadcasts this event on the simulating player. +/// +public sealed partial class RangedAttackLastTarget_StartCharging_Patch : NitroxPatch, IDynamicPatch +{ + internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((RangedAttackLastTarget t) => t.StartCharging(default)); + + public static void Prefix(RangedAttackLastTarget __instance) + { + if (!Resolve().IsCreatureWhitelisted(__instance.creature) || + !__instance.TryGetNitroxId(out NitroxId creatureId) || + !Resolve().HasAnyLockType(creatureId) || + !__instance.currentTarget || !__instance.currentTarget.TryGetNitroxId(out NitroxId targetId)) + { + return; + } + + int attackTypeIndex = __instance.attackTypes.GetIndex(__instance.currentAttack); + + Resolve().Send(new RangedAttackLastTargetUpdate(creatureId, targetId, attackTypeIndex, RangedAttackLastTargetUpdate.ActionState.CHARGING)); + ErrorMessage.AddMessage($"[SEND] {__instance.name} charges against {__instance.currentTarget.name}"); + } +} + diff --git a/NitroxPatcher/Patches/Dynamic/SeaDragonAggressiveTowardsSharks_GetAggressionTarget_Patch.cs b/NitroxPatcher/Patches/Dynamic/SeaDragonAggressiveTowardsSharks_GetAggressionTarget_Patch.cs new file mode 100644 index 0000000000..db37d2df2a --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/SeaDragonAggressiveTowardsSharks_GetAggressionTarget_Patch.cs @@ -0,0 +1,40 @@ +using System.Reflection; +using NitroxClient.GameLogic; +using NitroxModel.Helper; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Allow sea dragons to chose remote players as targets +/// +public sealed partial class SeaDragonAggressiveTowardsSharks_GetAggressionTarget_Patch : NitroxPatch, IDynamicPatch +{ + private static readonly MethodInfo TARGET_METHOD = Reflect.Method((SeaDragonAggressiveTowardsSharks t) => t.GetAggressionTarget()); + + public static bool Prefix(SeaDragonAggressiveTowardsSharks __instance, ref GameObject __result) + { + if (Time.time <= __instance.timeLastPlayerAttack + __instance.playerAttackInterval) + { + // Will call base.GetAggressionTarget() + return true; + } + + // In this method, players are priority targets. This will also account for the local player case + IEcoTarget ecoTarget = EcoRegionManager.main.FindNearestTarget(RemotePlayer.PLAYER_ECO_TARGET_TYPE, __instance.transform.position, __instance.isTargetValidFilter, __instance.maxSearchRings); + if (ecoTarget != null && __instance.IsTargetValid(ecoTarget.GetGameObject())) + { + __result = ecoTarget.GetGameObject(); + return false; + } + + // To redirect the call to base.GetAggressionTarget(), we ensure the if is skipped in the original method + float timeLastPlayerAttack = __instance.timeLastPlayerAttack; + __instance.timeLastPlayerAttack = Time.time; + + __result = __instance.GetAggressionTarget(); + + __instance.timeLastPlayerAttack = timeLastPlayerAttack; + return false; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/SeaDragonAggressiveTowardsSharks_OnMeleeAttack_Patch.cs b/NitroxPatcher/Patches/Dynamic/SeaDragonAggressiveTowardsSharks_OnMeleeAttack_Patch.cs new file mode 100644 index 0000000000..cd31120d4d --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/SeaDragonAggressiveTowardsSharks_OnMeleeAttack_Patch.cs @@ -0,0 +1,24 @@ +using System.Reflection; +using NitroxClient.GameLogic.PlayerLogic; +using NitroxModel.Helper; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Registers sea dragons' attack on remote players as attacks on a player +/// +public sealed partial class SeaDragonAggressiveTowardsSharks_OnMeleeAttack_Patch : NitroxPatch, IDynamicPatch +{ + private static readonly MethodInfo TARGET_METHOD = Reflect.Method((SeaDragonAggressiveTowardsSharks t) => t.OnMeleeAttack(default)); + + public static bool Prefix(SeaDragonAggressiveTowardsSharks __instance, GameObject target) + { + if (target.GetComponent()) + { + __instance.timeLastPlayerAttack = Time.time; + return false; + } + return true; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnThrowExosuit_Patch.cs b/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnThrowExosuit_Patch.cs new file mode 100644 index 0000000000..449b33148a --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnThrowExosuit_Patch.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using NitroxClient.GameLogic; +using NitroxModel.DataStructures; +using NitroxModel.Helper; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Prevents non simulating players from running locally . +/// +public sealed partial class SeaDragonMeleeAttack_OnThrowExosuit_Patch : NitroxPatch, IDynamicPatch +{ + internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((SeaDragonMeleeAttack t) => t.OnThrowExosuit(default)); + + public static bool Prefix(SeaDragonMeleeAttack __instance) + { + if (!__instance.TryGetNitroxId(out NitroxId creatureId) || + Resolve().HasAnyLockType(creatureId)) + { + return true; + } + + return false; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchFront_Patch.cs b/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchFront_Patch.cs new file mode 100644 index 0000000000..b712731c36 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchFront_Patch.cs @@ -0,0 +1,117 @@ +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using NitroxClient.Communication.Abstract; +using NitroxClient.GameLogic; +using NitroxClient.GameLogic.PlayerLogic; +using NitroxModel.DataStructures; +using NitroxModel.Helper; +using NitroxModel.Packets; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Prevents non simulating players from running locally . +/// Broadcasts for the simulating player: +/// - attack on cyclops effects +/// - attack on local player effects +/// - attack on remote players effects +/// +public sealed partial class SeaDragonMeleeAttack_OnTouchFront_Patch : NitroxPatch, IDynamicPatch +{ + internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((SeaDragonMeleeAttack t) => t.OnTouchFront(default)); + + public static bool Prefix(SeaDragonMeleeAttack __instance) + { + if (!__instance.TryGetNitroxId(out NitroxId creatureId) || + Resolve().HasAnyLockType(creatureId)) + { + return true; + } + + return false; + } + + /* + * 1st injection: + * base.gameObject.SendMessage("OnMeleeAttack", target, SendMessageOptions.DontRequireReceiver); + * this.timeLastBite = Time.time; + * BroadcastSeaDragonAttackTarget(this, target); <---- INSERTED LINE + * + * 2nd injection: + * global::Utils.PlayEnvSound(this.attackSound, collider.transform.position, 20f); + * } + * component3.gameObject.GetComponent().TakeDamage(this.biteDamage, default(Vector3), DamageType.Normal, base.gameObject); + * BroadcastSeaDragonAttackTarget(this, target); <---- INSERTED LINE + * + * 3rd injection: + * Exosuit component4 = target.GetComponent(); + * BroadcastSeaDragonAttackRemotePlayer(this, target); <---- INSERTED LINE + * if (component4 != null) + * { + */ + public static IEnumerable Transpiler(IEnumerable instructions) + { + // 1st injection + return new CodeMatcher(instructions).MatchEndForward([ + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Call, Reflect.Property(() => Time.time).GetGetMethod()), + new CodeMatch(OpCodes.Stfld, Reflect.Field((MeleeAttack t) => t.timeLastBite)), + new CodeMatch(OpCodes.Ret) + ]) + .InsertAndAdvance([ + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldloc_0), + new CodeInstruction(OpCodes.Call, Reflect.Method(() => BroadcastSeaDragonAttackTarget(default, default))), + ]) + // 2nd injection + .MatchEndForward([ + new CodeMatch(OpCodes.Call, Reflect.Property((Component t) => t.gameObject).GetGetMethod()), + new CodeMatch(OpCodes.Callvirt, Reflect.Method((LiveMixin t) => t.TakeDamage(default, default, default, default))), + new CodeMatch(OpCodes.Pop), + new CodeMatch(OpCodes.Br) + ]) + .InsertAndAdvance([ + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldloc_0), + new CodeInstruction(OpCodes.Call, Reflect.Method(() => BroadcastSeaDragonAttackTarget(default, default))), + ]) + // 3rd injection + .MatchEndForward([ + new CodeMatch(OpCodes.Ldloc_0), + new CodeMatch(OpCodes.Callvirt, Reflect.Method((GameObject t) => t.GetComponent())), + new CodeMatch(OpCodes.Stloc_S), + new CodeMatch(OpCodes.Ldloc_S), + ]) + .InsertAndAdvance([ + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldloc_0), + new CodeInstruction(OpCodes.Call, Reflect.Method(() => BroadcastSeaDragonAttackRemotePlayer(default, default))), + ]) + .InstructionEnumeration(); + } + + public static void BroadcastSeaDragonAttackTarget(SeaDragonMeleeAttack seaDragonMeleeAttack, GameObject target) + { + if (seaDragonMeleeAttack.TryGetNitroxId(out NitroxId seaDragonId) && + target.TryGetNitroxId(out NitroxId targetId)) + { + Resolve().Send(new SeaDragonAttackTarget(seaDragonId, targetId, seaDragonMeleeAttack.seaDragon.Aggression.Value)); + } + } + + public static void BroadcastSeaDragonAttackRemotePlayer(SeaDragonMeleeAttack seaDragonMeleeAttack, GameObject target) + { + if (target.GetComponent()) + { + return; + } + if (seaDragonMeleeAttack.TryGetNitroxId(out NitroxId seaDragonId) && + target.TryGetComponent(out RemotePlayerIdentifier remotePlayerIdentifier)) + { + Resolve().Send(new SeaDragonAttackTarget(seaDragonId, remotePlayerIdentifier.RemotePlayer.PlayerContext.PlayerNitroxId, seaDragonMeleeAttack.seaDragon.Aggression.Value)); + } + } +} diff --git a/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchLeft_Patch.cs b/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchLeft_Patch.cs new file mode 100644 index 0000000000..11ab86871a --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchLeft_Patch.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using NitroxClient.GameLogic; +using NitroxModel.DataStructures; +using NitroxModel.Helper; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Prevents non simulating players from running locally . +/// +public sealed partial class SeaDragonMeleeAttack_OnTouchLeft_Patch : NitroxPatch, IDynamicPatch +{ + internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((SeaDragonMeleeAttack t) => t.OnTouchLeft(default)); + + public static bool Prefix(SeaDragonMeleeAttack __instance) + { + if (!__instance.TryGetNitroxId(out NitroxId creatureId) || + Resolve().HasAnyLockType(creatureId)) + { + return true; + } + + return false; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchRight_Patch.cs b/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchRight_Patch.cs new file mode 100644 index 0000000000..a6815e2d76 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_OnTouchRight_Patch.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using NitroxClient.GameLogic; +using NitroxModel.DataStructures; +using NitroxModel.Helper; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Prevents non simulating players from running locally . +/// +public sealed partial class SeaDragonMeleeAttack_OnTouchRight_Patch : NitroxPatch, IDynamicPatch +{ + internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((SeaDragonMeleeAttack t) => t.OnTouchRight(default)); + + public static bool Prefix(SeaDragonMeleeAttack __instance) + { + if (!__instance.TryGetNitroxId(out NitroxId creatureId) || + Resolve().HasAnyLockType(creatureId)) + { + return true; + } + + return false; + } +} diff --git a/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_SwatAttack_Patch.cs b/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_SwatAttack_Patch.cs new file mode 100644 index 0000000000..8bb32a1507 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/SeaDragonMeleeAttack_SwatAttack_Patch.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using NitroxClient.Communication.Abstract; +using NitroxModel.DataStructures; +using NitroxModel.Helper; +using NitroxModel.Packets; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Broadcasts Sea Dragons swat attacks on the simulating player. +/// +public sealed partial class SeaDragonMeleeAttack_SwatAttack_Patch : NitroxPatch, IDynamicPatch +{ + internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((SeaDragonMeleeAttack t) => t.SwatAttack(default, default)); + + // All calls sources are either from a simulating player or from a packet processor + + /* + * MODIFIED: + * global::Utils.PlayFMODAsset(this.swatAttackSound, target.transform, 20f); + * } + * BroadcastSwatAttack(this, target, isRightHand); <---- INSERTED LINE + */ + public static IEnumerable Transpiler(IEnumerable instructions) + { + // 1st injection + return new CodeMatcher(instructions).End() + .InsertAndAdvance([ + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldarg_1), + new CodeInstruction(OpCodes.Ldarg_2), + new CodeInstruction(OpCodes.Call, Reflect.Method(() => BroadcastSwatAttack(default, default, default))) + ]) + .InstructionEnumeration(); + } + + public static void BroadcastSwatAttack(SeaDragonMeleeAttack seaDragonMeleeAttack, GameObject target, bool isRightHand) + { + if (seaDragonMeleeAttack.TryGetNitroxId(out NitroxId seaDragonId) && + target.TryGetNitroxId(out NitroxId targetId)) + { + Resolve().Send(new SeaDragonSwatAttack(seaDragonId, targetId, isRightHand, seaDragonMeleeAttack.seaDragon.Aggression.Value)); + } + } +} diff --git a/NitroxPatcher/Patches/Dynamic/SeaDragon_GrabExosuit_Patch.cs b/NitroxPatcher/Patches/Dynamic/SeaDragon_GrabExosuit_Patch.cs new file mode 100644 index 0000000000..764e7b8be9 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/SeaDragon_GrabExosuit_Patch.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using NitroxClient.Communication.Abstract; +using NitroxClient.MonoBehaviours; +using NitroxModel.DataStructures; +using NitroxModel.Helper; +using NitroxModel.Packets; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Broadcasts the exosuit grab by Sea Dragons and temporarily disables exosuit's position sync while they're grabbed. +/// +public sealed partial class SeaDragon_GrabExosuit_Patch : NitroxPatch, IDynamicPatch +{ + public static readonly MethodInfo TARGET_METHOD = Reflect.Method((SeaDragon t) => t.GrabExosuit(default)); + + public static void Prefix(SeaDragon __instance, Exosuit exosuit) + { + if (exosuit.TryGetComponent(out RemotelyControlled remotelyControlled)) + { + remotelyControlled.enabled = false; + } + + if (__instance.TryGetNitroxId(out NitroxId seaDragonId) && exosuit.TryGetNitroxId(out NitroxId targetId)) + { + Resolve().Send(new SeaDragonGrabExosuit(seaDragonId, targetId)); + } + } +} diff --git a/NitroxPatcher/Patches/Dynamic/SeaDrargon_ReleaseExosuit_Patch.cs b/NitroxPatcher/Patches/Dynamic/SeaDrargon_ReleaseExosuit_Patch.cs new file mode 100644 index 0000000000..14ec2adbbc --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/SeaDrargon_ReleaseExosuit_Patch.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using NitroxClient.MonoBehaviours; +using NitroxModel.Helper; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Enables exosuits's position sync when they're released from sea dragons +/// +public sealed partial class SeaDrargon_ReleaseExosuit_Patch : NitroxPatch, IDynamicPatch +{ + public static readonly MethodInfo TARGET_METHOD = Reflect.Method((SeaDragon t) => t.ReleaseExosuit()); + + public static void Prefix(SeaDragon __instance) + { + if (__instance.holdingExosuit && + __instance.holdingExosuit.TryGetComponent(out RemotelyControlled remotelyControlled)) + { + remotelyControlled.enabled = true; + } + } +} diff --git a/NitroxServer/Communication/Packets/Processors/Abstract/TransmitIfCanSeePacketProcessor.cs b/NitroxServer/Communication/Packets/Processors/Abstract/TransmitIfCanSeePacketProcessor.cs new file mode 100644 index 0000000000..ce99585169 --- /dev/null +++ b/NitroxServer/Communication/Packets/Processors/Abstract/TransmitIfCanSeePacketProcessor.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using NitroxModel.DataStructures; +using NitroxModel.DataStructures.GameLogic; +using NitroxModel.Packets; +using NitroxServer.GameLogic; +using NitroxServer.GameLogic.Entities; + +namespace NitroxServer.Communication.Packets.Processors.Abstract; + +public abstract class TransmitIfCanSeePacketProcessor : AuthenticatedPacketProcessor where T : Packet +{ + private readonly PlayerManager playerManager; + private readonly EntityRegistry entityRegistry; + + public TransmitIfCanSeePacketProcessor(PlayerManager playerManager, EntityRegistry entityRegistry) + { + this.playerManager = playerManager; + this.entityRegistry = entityRegistry; + } + + /// + /// Transmits the provided to all other players (excluding ) + /// who can see () entities corresponding to the provided only if all those entities are registered. + /// + public void TransmitIfCanSeeEntities(Packet packet, Player senderPlayer, List entityIds) + { + List entities = []; + foreach (NitroxId entityId in entityIds) + { + if (entityRegistry.TryGetEntityById(entityId, out Entity entity)) + { + entities.Add(entity); + } + else + { + return; + } + } + bool canPlayerSeeAllEntities(Player player) + { + foreach (Entity entity in entities) + { + if (!player.CanSee(entity)) + { + return false; + } + } + return true; + } + + foreach (Player player in playerManager.GetConnectedPlayersExcept(senderPlayer)) + { + if (canPlayerSeeAllEntities(player)) + { + player.SendPacket(packet); + } + } + } +} diff --git a/NitroxServer/Communication/Packets/Processors/AggressiveWhenSeeTargetChangedProcessor.cs b/NitroxServer/Communication/Packets/Processors/AggressiveWhenSeeTargetChangedProcessor.cs new file mode 100644 index 0000000000..62176beb42 --- /dev/null +++ b/NitroxServer/Communication/Packets/Processors/AggressiveWhenSeeTargetChangedProcessor.cs @@ -0,0 +1,13 @@ +using NitroxModel.Packets; +using NitroxServer.Communication.Packets.Processors.Abstract; +using NitroxServer.GameLogic; +using NitroxServer.GameLogic.Entities; + +namespace NitroxServer.Communication.Packets.Processors; + +public class AggressiveWhenSeeTargetChangedProcessor : TransmitIfCanSeePacketProcessor +{ + public AggressiveWhenSeeTargetChangedProcessor(PlayerManager playerManager, EntityRegistry entityRegistry) : base(playerManager, entityRegistry) { } + + public override void Process(AggressiveWhenSeeTargetChanged packet, Player sender) => TransmitIfCanSeeEntities(packet, sender, [packet.CreatureId, packet.TargetId]); +} diff --git a/NitroxServer/Communication/Packets/Processors/AttackCyclopsTargetChangedProcessor.cs b/NitroxServer/Communication/Packets/Processors/AttackCyclopsTargetChangedProcessor.cs new file mode 100644 index 0000000000..0f621d329c --- /dev/null +++ b/NitroxServer/Communication/Packets/Processors/AttackCyclopsTargetChangedProcessor.cs @@ -0,0 +1,13 @@ +using NitroxModel.Packets; +using NitroxServer.Communication.Packets.Processors.Abstract; +using NitroxServer.GameLogic; +using NitroxServer.GameLogic.Entities; + +namespace NitroxServer.Communication.Packets.Processors; + +public class AttackCyclopsTargetChangedProcessor : TransmitIfCanSeePacketProcessor +{ + public AttackCyclopsTargetChangedProcessor(PlayerManager playerManager, EntityRegistry entityRegistry) : base(playerManager, entityRegistry) { } + + public override void Process(AttackCyclopsTargetChanged packet, Player sender) => TransmitIfCanSeeEntities(packet, sender, [packet.CreatureId, packet.TargetId]); +} diff --git a/NitroxServer/Communication/Packets/Processors/CreatureActionChangedProcessor.cs b/NitroxServer/Communication/Packets/Processors/CreatureActionChangedProcessor.cs new file mode 100644 index 0000000000..a7c8529f9e --- /dev/null +++ b/NitroxServer/Communication/Packets/Processors/CreatureActionChangedProcessor.cs @@ -0,0 +1,13 @@ +using NitroxModel.Packets; +using NitroxServer.Communication.Packets.Processors.Abstract; +using NitroxServer.GameLogic; +using NitroxServer.GameLogic.Entities; + +namespace NitroxServer.Communication.Packets.Processors; + +public class CreatureActionChangedProcessor : TransmitIfCanSeePacketProcessor +{ + public CreatureActionChangedProcessor(PlayerManager playerManager, EntityRegistry entityRegistry) : base(playerManager, entityRegistry) { } + + public override void Process(CreatureActionChanged packet, Player sender) => TransmitIfCanSeeEntities(packet, sender, [packet.CreatureId]); +} diff --git a/NitroxServer/Communication/Packets/Processors/DefaultServerPacketProcessor.cs b/NitroxServer/Communication/Packets/Processors/DefaultServerPacketProcessor.cs index 0f85c37b15..52831c5b94 100644 --- a/NitroxServer/Communication/Packets/Processors/DefaultServerPacketProcessor.cs +++ b/NitroxServer/Communication/Packets/Processors/DefaultServerPacketProcessor.cs @@ -23,9 +23,6 @@ public class DefaultServerPacketProcessor : AuthenticatedPacketProcessor typeof(PlayFMODCustomLoopingEmitter), typeof(PlayFMODStudioEmitter), typeof(PlayerCinematicControllerCall), - typeof(CreatureActionChanged), - typeof(AggressiveWhenSeeTargetChanged), - typeof(AttackCyclopsTargetChanged), typeof(TorpedoShot), typeof(TorpedoHit), typeof(TorpedoTargetAcquired), diff --git a/NitroxServer/Communication/Packets/Processors/RangedAttackLastTargetUpdateProcessor.cs b/NitroxServer/Communication/Packets/Processors/RangedAttackLastTargetUpdateProcessor.cs new file mode 100644 index 0000000000..fec65a04f6 --- /dev/null +++ b/NitroxServer/Communication/Packets/Processors/RangedAttackLastTargetUpdateProcessor.cs @@ -0,0 +1,13 @@ +using NitroxModel.Packets; +using NitroxServer.Communication.Packets.Processors.Abstract; +using NitroxServer.GameLogic; +using NitroxServer.GameLogic.Entities; + +namespace NitroxServer.Communication.Packets.Processors; + +public class RangedAttackLastTargetUpdateProcessor : TransmitIfCanSeePacketProcessor +{ + public RangedAttackLastTargetUpdateProcessor(PlayerManager playerManager, EntityRegistry entityRegistry) : base(playerManager, entityRegistry) { } + + public override void Process(RangedAttackLastTargetUpdate packet, Player sender) => TransmitIfCanSeeEntities(packet, sender, [packet.CreatureId, packet.TargetId]); +} diff --git a/NitroxServer/Communication/Packets/Processors/SeaDragonAttackTargetProcessor.cs b/NitroxServer/Communication/Packets/Processors/SeaDragonAttackTargetProcessor.cs new file mode 100644 index 0000000000..d81bc37e49 --- /dev/null +++ b/NitroxServer/Communication/Packets/Processors/SeaDragonAttackTargetProcessor.cs @@ -0,0 +1,13 @@ +using NitroxModel.Packets; +using NitroxServer.Communication.Packets.Processors.Abstract; +using NitroxServer.GameLogic; +using NitroxServer.GameLogic.Entities; + +namespace NitroxServer.Communication.Packets.Processors; + +public class SeaDragonAttackTargetProcessor : TransmitIfCanSeePacketProcessor +{ + public SeaDragonAttackTargetProcessor(PlayerManager playerManager, EntityRegistry entityRegistry) : base(playerManager, entityRegistry) { } + + public override void Process(SeaDragonAttackTarget packet, Player sender) => TransmitIfCanSeeEntities(packet, sender, [packet.SeaDragonId, packet.TargetId]); +} diff --git a/NitroxServer/Communication/Packets/Processors/SeaDragonGrabExosuitProcessor.cs b/NitroxServer/Communication/Packets/Processors/SeaDragonGrabExosuitProcessor.cs new file mode 100644 index 0000000000..056127cee9 --- /dev/null +++ b/NitroxServer/Communication/Packets/Processors/SeaDragonGrabExosuitProcessor.cs @@ -0,0 +1,13 @@ +using NitroxModel.Packets; +using NitroxServer.Communication.Packets.Processors.Abstract; +using NitroxServer.GameLogic; +using NitroxServer.GameLogic.Entities; + +namespace NitroxServer.Communication.Packets.Processors; + +public class SeaDragonGrabExosuitProcessor : TransmitIfCanSeePacketProcessor +{ + public SeaDragonGrabExosuitProcessor(PlayerManager playerManager, EntityRegistry entityRegistry) : base(playerManager, entityRegistry) { } + + public override void Process(SeaDragonGrabExosuit packet, Player sender) => TransmitIfCanSeeEntities(packet, sender, [packet.SeaDragonId, packet.TargetId]); +} diff --git a/NitroxServer/Communication/Packets/Processors/SeaDragonSwatAttackProcessor.cs b/NitroxServer/Communication/Packets/Processors/SeaDragonSwatAttackProcessor.cs new file mode 100644 index 0000000000..f955c6afb7 --- /dev/null +++ b/NitroxServer/Communication/Packets/Processors/SeaDragonSwatAttackProcessor.cs @@ -0,0 +1,13 @@ +using NitroxModel.Packets; +using NitroxServer.Communication.Packets.Processors.Abstract; +using NitroxServer.GameLogic; +using NitroxServer.GameLogic.Entities; + +namespace NitroxServer.Communication.Packets.Processors; + +public class SeaDragonSwatAttackProcessor : TransmitIfCanSeePacketProcessor +{ + public SeaDragonSwatAttackProcessor(PlayerManager playerManager, EntityRegistry entityRegistry) : base(playerManager, entityRegistry) { } + + public override void Process(SeaDragonSwatAttack packet, Player sender) => TransmitIfCanSeeEntities(packet, sender, [packet.SeaDragonId, packet.TargetId]); +}