using UnityEngine;
using UnityEngine.Assertions;
namespace Unity.Multiplayer.Samples.BossRoom.Server
{
///
/// Causes the attacker to teleport near a target spot, then perform a melee attack. The client
/// visualization moves the character locally beforehand, making the character appear to dash to the
/// destination spot.
///
/// After the ExecTime has elapsed, the character is immune to damage until the action ends.
///
/// Since the "Range" field means "range when we can teleport to our target", we need another
/// field to mean "range of our melee attack after dashing". We'll use the "Radius" field of the
/// ActionDescription for that.
///
///
/// See MeleeAction for relevant discussion about targeting; we use the same concept here: preferring
/// the chosen target, but using whatever is actually within striking distance at time of attack.
///
public class DashAttackAction : Action
{
private Vector3 m_TargetSpot;
public DashAttackAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
{
Assert.IsTrue(Description.Radius > 0, $"ActionDescription for {Description.ActionTypeEnum} needs a Radius assigned!");
}
public override bool Start()
{
// remember the exact spot we'll stop.
m_TargetSpot = ActionUtils.GetDashDestination(m_Parent.physicsWrapper.Transform, Data.Position, true, Description.Range, Description.Range);
// snap to face our destination. This ensures the client visualization faces the right way while "pretending" to dash
m_Parent.physicsWrapper.Transform.LookAt(m_TargetSpot);
m_Parent.serverAnimationHandler.NetworkAnimator.SetTrigger(Description.Anim);
// tell clients to visualize this action
m_Parent.NetState.RecvDoActionClientRPC(Data);
return ActionConclusion.Continue;
}
public override bool Update()
{
return ActionConclusion.Continue;
}
public override void End()
{
// Anim2 contains the name of the end-loop-sequence trigger
if (!string.IsNullOrEmpty(Description.Anim2))
{
m_Parent.serverAnimationHandler.NetworkAnimator.SetTrigger(Description.Anim2);
}
// we're done, time to teleport!
m_Parent.Movement.Teleport(m_TargetSpot);
// and then swing!
PerformMeleeAttack();
}
public override void Cancel()
{
// OtherAnimatorVariable contains the name of the cancellation trigger
if (!string.IsNullOrEmpty(Description.OtherAnimatorVariable))
{
m_Parent.serverAnimationHandler.NetworkAnimator.SetTrigger(Description.OtherAnimatorVariable);
}
// because the client-side visualization of the action moves the character visualization around,
// we need to explicitly end the client-side visuals when we abort
m_Parent.NetState.RecvCancelActionsByTypeClientRpc(Description.ActionTypeEnum);
}
public override void BuffValue(BuffableValue buffType, ref float buffedValue)
{
if (TimeRunning >= Description.ExecTimeSeconds && buffType == BuffableValue.PercentDamageReceived)
{
// we suffer no damage during the "dash" (client-side pretend movement)
buffedValue = 0;
}
}
private void PerformMeleeAttack()
{
// perform a typical melee-hit. But note that we are using the Radius field for range, not the Range field!
IDamageable foe = MeleeAction.GetIdealMeleeFoe(Description.IsFriendly ^ m_Parent.IsNpc,
m_Parent.physicsWrapper.DamageCollider,
Description.Radius,
(Data.TargetIds != null && Data.TargetIds.Length > 0 ? Data.TargetIds[0] : 0));
if (foe != null)
{
foe.ReceiveHP(m_Parent, -Description.Amount);
}
}
}
}