Skip to content

Commit

Permalink
Sync Sea Dragons
Browse files Browse the repository at this point in the history
  • Loading branch information
tornac1234 committed Feb 19, 2024
1 parent 1a987e9 commit 862ae97
Show file tree
Hide file tree
Showing 36 changed files with 889 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
namespace NitroxPatcher.Patches.Dynamic;

[TestClass]
public class SpawnConsoleCommand_OnConsoleCommand_PatchTest
public class SeaDragonMeleeAttack_OnTouchFront_PatchTest
{
[TestMethod]
public void Sanity()
{
IEnumerable<CodeInstruction> originalIl = PatchTestHelper.GetInstructionsFromMethod(SpawnConsoleCommand_OnConsoleCommand_Patch.TARGET_METHOD);
IEnumerable<CodeInstruction> transformedIl = SpawnConsoleCommand_OnConsoleCommand_Patch.Transpiler(originalIl);
transformedIl.Count().Should().Be(originalIl.Count() + 2);
IEnumerable<CodeInstruction> originalIl = PatchTestHelper.GetInstructionsFromMethod(SeaDragonMeleeAttack_OnTouchFront_Patch.TARGET_METHOD);
IEnumerable<CodeInstruction> transformedIl = SeaDragonMeleeAttack_OnTouchFront_Patch.Transpiler(originalIl);
transformedIl.Count().Should().Be(originalIl.Count() + 9);
}
}
Original file line number Diff line number Diff line change
@@ -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<CodeInstruction> originalIl = PatchTestHelper.GetInstructionsFromMethod(SeaDragonMeleeAttack_SwatAttack_Patch.TARGET_METHOD);
IEnumerable<CodeInstruction> transformedIl = SeaDragonMeleeAttack_SwatAttack_Patch.Transpiler(originalIl);
transformedIl.Count().Should().Be(originalIl.Count() + 4);
}
}
Original file line number Diff line number Diff line change
@@ -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<RangedAttackLastTargetUpdate>
{
public override void Process(RangedAttackLastTargetUpdate packet)
{
AI.RangedAttackLastTargetUpdate(packet.CreatureId, packet.TargetId, packet.AttackTypeIndex, packet.State);
}
}
Original file line number Diff line number Diff line change
@@ -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<SeaDragonAttackTarget>
{
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<SubControl>())
{
// 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<Player>())
{
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);
}
}
Original file line number Diff line number Diff line change
@@ -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<SeaDragonGrabExosuit>
{
public override void Process(SeaDragonGrabExosuit packet)
{
if (!NitroxEntity.TryGetComponentFrom(packet.SeaDragonId, out SeaDragon seaDragon) ||
!NitroxEntity.TryGetComponentFrom(packet.TargetId, out Exosuit exosuit))
{
return;
}

using (PacketSuppressor<SeaDragonGrabExosuit>.Suppress())
{
seaDragon.GrabExosuit(exosuit);
seaDragon.CancelInvoke(nameof(SeaDragon.DamageExosuit));
}
}
}
Original file line number Diff line number Diff line change
@@ -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<SeaDragonSwatAttack>
{
public override void Process(SeaDragonSwatAttack packet)
{
if (!NitroxEntity.TryGetComponentFrom(packet.SeaDragonId, out SeaDragonMeleeAttack seaDragonMeleeAttack) ||
!NitroxEntity.TryGetObjectFrom(packet.TargetId, out GameObject targetObject))
{
return;
}

using (PacketSuppressor<SeaDragonSwatAttack>.Suppress())
{
seaDragonMeleeAttack.seaDragon.Aggression.Value = packet.Aggression;
seaDragonMeleeAttack.SwatAttack(targetObject, packet.IsRightHand);
}
}
}
30 changes: 28 additions & 2 deletions NitroxClient/GameLogic/AI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using NitroxModel.DataStructures;
using NitroxModel.Packets;
using UnityEngine;
using static NitroxModel.Packets.RangedAttackLastTargetUpdate;

namespace NitroxClient.GameLogic;

Expand All @@ -22,7 +23,7 @@ public class AI
/// </summary>
private readonly HashSet<Type> creatureActionWhitelist =
[
typeof(AttackLastTarget), typeof(AttackCyclops)
typeof(AttackLastTarget), typeof(RangedAttackLastTarget), typeof(AttackCyclops)
];

/// <summary>
Expand Down Expand Up @@ -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));
}

Expand Down Expand Up @@ -117,6 +118,31 @@ public static void AttackCyclopsTargetChanged(NitroxId creatureId, NitroxId targ
attackCyclops.SetCurrentTarget(targetObject, targetObject.GetComponent<CyclopsDecoy>());
}

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ private void SetPlayerPermissions(Perms permissions)

private void SetPlayerGameObjectId(NitroxId id)
{
EcoTarget playerEcoTarget = Player.mainObject.AddComponent<EcoTarget>();
playerEcoTarget.SetTargetType(RemotePlayer.PLAYER_ECO_TARGET_TYPE);

NitroxEntity.SetNewId(Player.mainObject, id);
Log.Info($"Received initial sync player GameObject Id: {id}");
}
Expand Down
25 changes: 23 additions & 2 deletions NitroxClient/GameLogic/RemotePlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ namespace NitroxClient.GameLogic;

public class RemotePlayer : INitroxPlayer
{
/// <summary>
/// Marks <see cref="Player.mainObject"/> and every <see cref="Body"/> so they can be precisely queried (e.g. by sea dragons).
/// The value (5050) is determined arbitrarily and should not be used already.
/// </summary>
public const EcoTargetType PLAYER_ECO_TARGET_TYPE = (EcoTargetType)5050;

private static readonly int animatorPlayerIn = Animator.StringToHash("player_in");

private readonly PlayerModelManager playerModelManager;
Expand Down Expand Up @@ -317,8 +323,14 @@ public void UpdateEquipmentVisibility(List<TechType> equippedItems)
private void SetupBody()
{
// set as a target for reapers
EcoTarget ecoTarget = Body.AddComponent<EcoTarget>();
ecoTarget.SetTargetType(EcoTargetType.Shark);
EcoTarget sharkEcoTarget = Body.AddComponent<EcoTarget>();
sharkEcoTarget.SetTargetType(EcoTargetType.Shark);

EcoTarget playerEcoTarget = Body.AddComponent<EcoTarget>();
playerEcoTarget.SetTargetType(PLAYER_ECO_TARGET_TYPE);

TechTag techTag = Body.AddComponent<TechTag>();
techTag.type = TechType.Player;

RemotePlayerIdentifier identifier = Body.AddComponent<RemotePlayerIdentifier>();
identifier.RemotePlayer = this;
Expand Down Expand Up @@ -464,4 +476,13 @@ private void RefreshVitalsVisibility()
vitals.SetStatsVisible(visible);
}
}

/// <summary>
/// Adaptation of <see cref="Player.CanBeAttacked"/> for remote players.
/// NB: This doesn't check for other player's use of 'invisible' command
/// </summary>
public bool CanBeAttacked()
{
return !SubRoot && !EscapePod && PlayerContext.GameMode != NitroxGameMode.CREATIVE;
}
}
27 changes: 27 additions & 0 deletions NitroxModel/Packets/RangedAttackLastTargetUpdate.cs
Original file line number Diff line number Diff line change
@@ -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
}
}
19 changes: 19 additions & 0 deletions NitroxModel/Packets/SeaDragonAttackTarget.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
17 changes: 17 additions & 0 deletions NitroxModel/Packets/SeaDragonGrabExosuit.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
21 changes: 21 additions & 0 deletions NitroxModel/Packets/SeaDragonSwatAttack.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using System.Reflection;
using NitroxClient.GameLogic;
using NitroxClient.GameLogic.PlayerLogic;
using NitroxModel.Helper;
using UnityEngine;

namespace NitroxPatcher.Patches.Dynamic;

/// <summary>
/// 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 (<see cref="RemotePlayer.CanBeAttacked"/>)
/// </summary>
public sealed partial class AggressiveWhenSeeTarget_IsTargetValid_Patch : NitroxPatch, IDynamicPatch
{
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 862ae97

Please sign in to comment.