144 lines
4.7 KiB
C#
144 lines
4.7 KiB
C#
using MegaKoop.Game.Combat;
|
|
using MegaKoop.Game.Networking;
|
|
using MegaKoop.Game.Vfx;
|
|
using UnityEngine;
|
|
|
|
namespace MegaKoop.Game.WeaponSystem
|
|
{
|
|
public class Projectile : MonoBehaviour
|
|
{
|
|
[SerializeField] private float hitRadius = 0.05f;
|
|
[SerializeField] private LayerMask hitMask = Physics.DefaultRaycastLayers;
|
|
[SerializeField] private bool alignToVelocity = true;
|
|
|
|
private Vector3 direction;
|
|
private float speed;
|
|
private float damage;
|
|
private float lifetime;
|
|
private float timeAlive;
|
|
private Team sourceTeam;
|
|
private GameObject owner;
|
|
private Collider[] projectileColliders;
|
|
private bool isAuthoritative = true;
|
|
|
|
public void Initialize(Vector3 shotDirection, float projectileSpeed, float damageAmount, float projectileLifetime, GameObject projectileOwner, Team ownerTeam, Collider[] ownerColliders, LayerMask mask, bool authoritative = true)
|
|
{
|
|
direction = shotDirection.sqrMagnitude > 0f ? shotDirection.normalized : transform.forward;
|
|
speed = Mathf.Max(0f, projectileSpeed);
|
|
damage = Mathf.Max(0f, damageAmount);
|
|
lifetime = Mathf.Max(0.01f, projectileLifetime);
|
|
owner = projectileOwner;
|
|
sourceTeam = ownerTeam;
|
|
hitMask = mask;
|
|
timeAlive = 0f;
|
|
isAuthoritative = authoritative;
|
|
|
|
projectileColliders ??= GetComponentsInChildren<Collider>();
|
|
if (ownerColliders != null && projectileColliders != null)
|
|
{
|
|
foreach (Collider ownerCollider in ownerColliders)
|
|
{
|
|
if (ownerCollider == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (Collider projectileCollider in projectileColliders)
|
|
{
|
|
if (projectileCollider == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Physics.IgnoreCollision(projectileCollider, ownerCollider, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
float deltaTime = Time.deltaTime;
|
|
Vector3 displacement = direction * speed * deltaTime;
|
|
float distance = displacement.magnitude;
|
|
|
|
if (distance > 0f)
|
|
{
|
|
if (Physics.SphereCast(transform.position, hitRadius, direction, out RaycastHit hitInfo, distance, hitMask, QueryTriggerInteraction.Ignore))
|
|
{
|
|
TryHandleHit(hitInfo.collider, hitInfo.point, hitInfo.normal);
|
|
return;
|
|
}
|
|
|
|
transform.position += displacement;
|
|
}
|
|
|
|
if (alignToVelocity && distance > 0f)
|
|
{
|
|
transform.forward = direction;
|
|
}
|
|
|
|
timeAlive += deltaTime;
|
|
if (timeAlive >= lifetime)
|
|
{
|
|
Destroy(gameObject);
|
|
}
|
|
}
|
|
|
|
private void OnTriggerEnter(Collider other)
|
|
{
|
|
TryHandleHit(other, transform.position, -direction);
|
|
}
|
|
|
|
private void TryHandleHit(Collider other, Vector3 hitPoint, Vector3 hitNormal)
|
|
{
|
|
if (other == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (owner != null)
|
|
{
|
|
if (other.transform.IsChildOf(owner.transform))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
IDamageable damageable = other.GetComponentInParent<IDamageable>();
|
|
ProjectileImpactKind? impactKind = null;
|
|
|
|
if (damageable != null && damageable.IsAlive)
|
|
{
|
|
bool isFriendly = damageable.Team == sourceTeam && damageable.Team != Team.Neutral;
|
|
if (isFriendly)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (damageable.Team == Team.Heroes)
|
|
{
|
|
impactKind = ProjectileImpactKind.Hero;
|
|
}
|
|
else if (damageable.Team == Team.Enemies)
|
|
{
|
|
impactKind = ProjectileImpactKind.Enemy;
|
|
}
|
|
|
|
if (isAuthoritative && damage > 0f)
|
|
{
|
|
var payload = new DamagePayload(damage, hitPoint, hitNormal, owner, sourceTeam, other.gameObject);
|
|
damageable.ApplyDamage(payload);
|
|
}
|
|
}
|
|
|
|
if (isAuthoritative && impactKind.HasValue)
|
|
{
|
|
ProjectileImpactVfxService.ReportImpact(impactKind.Value, hitPoint, hitNormal);
|
|
}
|
|
|
|
Destroy(gameObject);
|
|
}
|
|
}
|
|
}
|