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.
141 lines
3.3 KiB
C#
141 lines
3.3 KiB
C#
using Fusion;
|
|
using UnityEngine;
|
|
|
|
namespace Projectiles
|
|
{
|
|
/// <summary>
|
|
/// Deals damage to all IHitTargets within specified radius right after spawning.
|
|
/// </summary>
|
|
public class Explosion : ContextBehaviour
|
|
{
|
|
// PRIVATE MEMBERS
|
|
|
|
[SerializeField]
|
|
private LayerMask _targetMask;
|
|
[SerializeField]
|
|
private LayerMask _blockingMask;
|
|
[SerializeField]
|
|
private EHitType _hitType = EHitType.Explosion;
|
|
|
|
[SerializeField, Tooltip("It is usually better to check from point slightly above explosion to filter out terrain discrepancies")]
|
|
private Vector3 _explosionCheckOffset = new(0f, 0.5f, 0f);
|
|
[SerializeField]
|
|
private float _innerRadius = 1f;
|
|
[SerializeField]
|
|
private float _outerRadius = 6f;
|
|
|
|
[SerializeField]
|
|
private float _innerDamage = 100f;
|
|
[SerializeField]
|
|
private float _outerDamage = 10f;
|
|
[SerializeField]
|
|
private bool _canDamageOwner = true;
|
|
|
|
[SerializeField]
|
|
private float _despawnDelay = 3f;
|
|
|
|
[SerializeField]
|
|
private Transform _effectRoot;
|
|
|
|
private TickTimer _despawnTimer;
|
|
|
|
// NetworkBehaviour INTERFACE
|
|
|
|
public override void Spawned()
|
|
{
|
|
ShowEffect();
|
|
|
|
if (HasStateAuthority == true)
|
|
{
|
|
Explode();
|
|
}
|
|
|
|
_despawnTimer = TickTimer.CreateFromSeconds(Runner, _despawnDelay);
|
|
}
|
|
|
|
public override void FixedUpdateNetwork()
|
|
{
|
|
if (HasStateAuthority == false)
|
|
return;
|
|
if (_despawnTimer.Expired(Runner) == false)
|
|
return;
|
|
|
|
Runner.Despawn(Object);
|
|
}
|
|
|
|
// PRIVATE METHODS
|
|
|
|
private void Explode()
|
|
{
|
|
var hits = ListPool.Get<LagCompensatedHit>(16);
|
|
var hitRoots = ListPool.Get<int>(16);
|
|
|
|
var position = transform.position + _explosionCheckOffset;
|
|
|
|
var hitOptions = HitOptions.IncludePhysX;
|
|
if (_canDamageOwner == false)
|
|
{
|
|
hitOptions |= HitOptions.IgnoreInputAuthority;
|
|
}
|
|
|
|
int count = Runner.LagCompensation.OverlapSphere(position, _outerRadius, Object.InputAuthority, hits, _targetMask, hitOptions);
|
|
|
|
bool damageFalloff = _innerRadius < _outerRadius && _innerDamage != _outerDamage;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var hit = hits[i];
|
|
|
|
var hitTarget = HitUtility.GetHitTarget(hit.Hitbox, hit.Collider);
|
|
|
|
if (hitTarget == null)
|
|
continue;
|
|
|
|
int hitRootID = hit.Hitbox != null ? hit.Hitbox.Root.GetInstanceID() : 0;
|
|
if (hitRoots.Contains(hitRootID) == true)
|
|
continue; // Same object was hit multiple times
|
|
|
|
var direction = hit.GameObject.transform.position - position;
|
|
float distance = direction.magnitude;
|
|
direction /= distance; // Normalize
|
|
|
|
// Check if direction to the hitbox is not obstructed
|
|
if (Runner.GetPhysicsScene().Raycast(position, direction, distance, _blockingMask) == true)
|
|
continue;
|
|
|
|
if (hitRootID != 0)
|
|
{
|
|
hitRoots.Add(hitRootID);
|
|
}
|
|
|
|
float damage = _innerDamage;
|
|
|
|
if (damageFalloff == true && distance > _innerRadius)
|
|
{
|
|
damage = MathUtility.Map(_innerRadius, _outerRadius, _innerDamage, _outerDamage, distance);
|
|
}
|
|
|
|
hit.Point = hit.GameObject.transform.position;
|
|
hit.Normal = -direction;
|
|
|
|
HitUtility.ProcessHit(Object.InputAuthority, direction, hit, damage, _hitType);
|
|
}
|
|
|
|
ListPool.Return(hitRoots);
|
|
ListPool.Return(hits);
|
|
}
|
|
|
|
private void ShowEffect()
|
|
{
|
|
if (Runner.Mode == SimulationModes.Server)
|
|
return;
|
|
|
|
if (_effectRoot != null)
|
|
{
|
|
_effectRoot.SetActive(true);
|
|
_effectRoot.localScale = Vector3.one * _outerRadius * 2f;
|
|
}
|
|
}
|
|
}
|
|
}
|