You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
6.4 KiB
C#
154 lines
6.4 KiB
C#
3 weeks ago
|
using System;
|
||
|
using Unity.BossRoom.Gameplay.GameplayObjects;
|
||
|
using Unity.BossRoom.Gameplay.GameplayObjects.Character;
|
||
|
using Unity.Netcode;
|
||
|
using UnityEngine;
|
||
|
using Object = UnityEngine.Object;
|
||
|
|
||
|
namespace Unity.BossRoom.Gameplay.Actions
|
||
|
{
|
||
|
public partial class FXProjectileTargetedAction
|
||
|
{
|
||
|
// have we actually played an impact?
|
||
|
private bool m_ImpactPlayed;
|
||
|
// the time the FX projectile spends in the air
|
||
|
private float m_ProjectileDuration;
|
||
|
// the currently-live projectile. (Note that the projectile will normally destroy itself! We only care in case someone calls Cancel() on us)
|
||
|
private FXProjectile m_Projectile;
|
||
|
// the enemy we're aiming at
|
||
|
private NetworkObject m_Target;
|
||
|
Transform m_TargetTransform;
|
||
|
|
||
|
public override bool OnStartClient(ClientCharacter clientCharacter)
|
||
|
{
|
||
|
base.OnStartClient(clientCharacter);
|
||
|
m_Target = GetTarget(clientCharacter);
|
||
|
|
||
|
if (m_Target && PhysicsWrapper.TryGetPhysicsWrapper(m_Target.NetworkObjectId, out var physicsWrapper))
|
||
|
{
|
||
|
m_TargetTransform = physicsWrapper.Transform;
|
||
|
}
|
||
|
|
||
|
if (Config.Projectiles.Length < 1 || Config.Projectiles[0].ProjectilePrefab == null)
|
||
|
throw new System.Exception($"Action {name} has no valid ProjectileInfo!");
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public override bool OnUpdateClient(ClientCharacter clientCharacter)
|
||
|
{
|
||
|
if (TimeRunning >= Config.ExecTimeSeconds && m_Projectile == null)
|
||
|
{
|
||
|
// figure out how long the pretend-projectile will be flying to the target
|
||
|
var targetPos = m_TargetTransform ? m_TargetTransform.position : Data.Position;
|
||
|
var initialDistance = Vector3.Distance(targetPos, clientCharacter.transform.position);
|
||
|
m_ProjectileDuration = initialDistance / Config.Projectiles[0].Speed_m_s;
|
||
|
|
||
|
// create the projectile. It will control itself from here on out
|
||
|
m_Projectile = SpawnAndInitializeProjectile(clientCharacter);
|
||
|
}
|
||
|
|
||
|
// we keep going until the projectile's duration ends
|
||
|
return TimeRunning <= m_ProjectileDuration + Config.ExecTimeSeconds;
|
||
|
}
|
||
|
|
||
|
public override void CancelClient(ClientCharacter clientCharacter)
|
||
|
{
|
||
|
if (m_Projectile)
|
||
|
{
|
||
|
// we aborted post-projectile-launch (somehow)! Tell the graphics! (It will destroy itself, possibly after playing some more FX)
|
||
|
m_Projectile.Cancel();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void EndClient(ClientCharacter clientCharacter)
|
||
|
{
|
||
|
PlayHitReact();
|
||
|
}
|
||
|
|
||
|
void PlayHitReact()
|
||
|
{
|
||
|
if (m_ImpactPlayed)
|
||
|
return;
|
||
|
m_ImpactPlayed = true;
|
||
|
|
||
|
if (NetworkManager.Singleton.IsServer)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (m_Target && m_Target.TryGetComponent(out ServerCharacter clientCharacter) && clientCharacter.clientCharacter != null)
|
||
|
{
|
||
|
var hitReact = !string.IsNullOrEmpty(Config.ReactAnim) ? Config.ReactAnim : k_DefaultHitReact;
|
||
|
clientCharacter.clientCharacter.OurAnimator.SetTrigger(hitReact);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NetworkObject GetTarget(ClientCharacter parent)
|
||
|
{
|
||
|
if (Data.TargetIds == null || Data.TargetIds.Length == 0)
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(Data.TargetIds[0], out NetworkObject targetObject) && targetObject != null)
|
||
|
{
|
||
|
// make sure this isn't a friend (or if it is, make sure this is a friendly-fire action)
|
||
|
var targetable = targetObject.GetComponent<ITargetable>();
|
||
|
if (targetable != null && targetable.IsNpc == (Config.IsFriendly ^ parent.serverCharacter.IsNpc))
|
||
|
{
|
||
|
// not a valid target
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return targetObject;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// target could have legitimately disappeared in the time it took to queue this action... but that's pretty unlikely, so we'll log about it to ease debugging
|
||
|
Debug.Log($"FXProjectileTargetedActionFX was targeted at ID {Data.TargetIds[0]}, but that target can't be found in spawned object list! (May have just been deleted?)");
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FXProjectile SpawnAndInitializeProjectile(ClientCharacter parent)
|
||
|
{
|
||
|
var projectileGO = Object.Instantiate(Config.Projectiles[0].ProjectilePrefab, parent.transform.position, parent.transform.rotation, null);
|
||
|
|
||
|
var projectile = projectileGO.GetComponent<FXProjectile>();
|
||
|
if (!projectile)
|
||
|
{
|
||
|
throw new System.Exception($"FXProjectileTargetedAction tried to spawn projectile {projectileGO.name}, as dictated for action {name}, but the object doesn't have a FXProjectile component!");
|
||
|
}
|
||
|
|
||
|
// now that we have our projectile, initialize it so it'll fly at the target appropriately
|
||
|
projectile.Initialize(parent.transform.position, m_TargetTransform, Data.Position, m_ProjectileDuration);
|
||
|
return projectile;
|
||
|
}
|
||
|
|
||
|
public override void AnticipateActionClient(ClientCharacter clientCharacter)
|
||
|
{
|
||
|
base.AnticipateActionClient(clientCharacter);
|
||
|
|
||
|
// see if this is going to be a "miss" because the player tried to click through a wall. If so,
|
||
|
// we change our data in the same way that the server will (changing our target point to the spot on the wall)
|
||
|
Vector3 targetSpot = Data.Position;
|
||
|
if (Data.TargetIds != null && Data.TargetIds.Length > 0)
|
||
|
{
|
||
|
var targetObj = NetworkManager.Singleton.SpawnManager.SpawnedObjects[Data.TargetIds[0]];
|
||
|
if (targetObj)
|
||
|
{
|
||
|
targetSpot = targetObj.transform.position;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!ActionUtils.HasLineOfSight(clientCharacter.transform.position, targetSpot, out Vector3 collidePos))
|
||
|
{
|
||
|
// we do not have line of sight to the target point. So our target instead becomes the obstruction point
|
||
|
Data.TargetIds = null;
|
||
|
Data.Position = collidePos;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|