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#

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;
}
}
}
}