Files
beyond/Assets/ThirdParty/Invector-3rdPersonController/Shooter/Scripts/Generic/vExplosive.cs
2024-11-20 15:21:28 +01:00

207 lines
7.1 KiB
C#

using System.Collections;
using UnityEngine;
namespace Invector
{
using System.Collections.Generic;
using vEventSystems;
[vClassHeader("Explosive", openClose = false)]
public class vExplosive : vMonoBehaviour
{
[System.Serializable]
protected class OnUpdateTime : UnityEngine.Events.UnityEvent<float> { }
public vDamage damage;
public float explosionForce;
public float minExplosionRadius;
public float maxExplosionRadius;
public float upwardsModifier = 1;
public ForceMode forceMode;
public ExplosiveMethod method;
public LayerMask applyDamageLayer, applyForceLayer;
public float timeToExplode = 10f;
[Tooltip("conver to progress 0 to 1")]
public bool normalizeTime;
public bool showGizmos;
public UnityEngine.Events.UnityEvent onInitTimer;
[SerializeField] protected OnUpdateTime onUpdateTimer;
public UnityEngine.Events.UnityEvent onExplode;
private bool inTimer;
private List<GameObject> collidersReached;
void OnDrawGizmosSelected()
{
if (!showGizmos) return;
Gizmos.color = new Color(1, 0, 0, 0.2f);
Gizmos.DrawSphere(transform.position, minExplosionRadius);
Gizmos.color = new Color(0, 1, 0, 0.2f);
Gizmos.DrawSphere(transform.position, maxExplosionRadius);
}
public void SetDamage(vDamage damage)
{
this.damage = damage;
}
public enum ExplosiveMethod
{
collisionEnter,
collisionEnterTimer,
remote,
timer,
remoteTimer
}
protected virtual void Start()
{
collidersReached = new List<GameObject>();
if (method == ExplosiveMethod.timer)
{
StartCoroutine(StartTimer());
}
}
protected virtual IEnumerator StartTimer()
{
if (!inTimer)
{
onInitTimer.Invoke();
inTimer = true;
var startTime = Time.time;
var time = 0f;
while (time < timeToExplode)
{
yield return new WaitForEndOfFrame();
time = Time.time - startTime;
onUpdateTimer.Invoke(normalizeTime ? (time / timeToExplode) : time);
}
if (gameObject)
{
onUpdateTimer.Invoke(normalizeTime ? (1f) : timeToExplode);
Explode();
}
}
}
protected virtual IEnumerator DestroyBomb()
{
yield return new WaitForSeconds(0.1f);
Destroy(gameObject);
}
protected virtual void OnCollisionEnter(Collision collision)
{
if (method == ExplosiveMethod.collisionEnter)
Explode();
else if (method == ExplosiveMethod.collisionEnterTimer)
StartCoroutine(StartTimer());
}
protected virtual void Explode()
{
onExplode.Invoke();
var colliders = Physics.OverlapSphere(transform.position, maxExplosionRadius, applyDamageLayer);
if(collidersReached == null)
{
collidersReached = new List<GameObject>();
}
for (int i = 0; i < colliders.Length; ++i)
{
if (colliders[i] != null && colliders[i].gameObject != null && !collidersReached.Contains(colliders[i].gameObject))
{
collidersReached.Add(colliders[i].gameObject);
var _damage = new vDamage(damage);
if(!_damage.sender) _damage.sender = transform;
_damage.hitPosition = colliders[i].ClosestPointOnBounds(transform.position);
_damage.receiver = colliders[i].transform;
var distance = Vector3.Distance(transform.position, _damage.hitPosition);
var damageValue = distance <= minExplosionRadius ? damage.damageValue : GetPercentageForce(distance, damage.damageValue);
_damage.activeRagdoll = distance > maxExplosionRadius * 0.5f ? false : _damage.activeRagdoll;
_damage.damageValue = (int)damageValue;
colliders[i].gameObject.ApplyDamage(_damage, null);
}
}
StartCoroutine(ApplyExplosionForce());
StartCoroutine(DestroyBomb());
}
protected virtual IEnumerator ApplyExplosionForce()
{
yield return new WaitForSeconds(0.1f);
var colliders = Physics.OverlapSphere(transform.position, maxExplosionRadius, applyForceLayer);
for (int i = 0; i < colliders.Length; i++)
{
var _rigdbody = colliders[i].GetComponent<Rigidbody>();
var distance = Vector3.Distance(transform.position, colliders[i].ClosestPointOnBounds(transform.position));
var force = distance <= minExplosionRadius ? explosionForce : GetPercentageForce(distance, explosionForce);
if (_rigdbody)
{
_rigdbody.AddExplosionForce(force, transform.position, maxExplosionRadius, upwardsModifier, forceMode);
}
}
}
private float GetPercentageForce(float distance, float value)
{
if (distance > maxExplosionRadius) distance = maxExplosionRadius;
var distanceLimit = maxExplosionRadius - minExplosionRadius;
var distanceCalc = Mathf.Clamp(distance - minExplosionRadius, 0, distanceLimit);
var distanceResult = Mathf.Clamp(distanceLimit - (distanceCalc), 0, distanceLimit);
var multiple = ((distanceResult / distanceLimit) * 100f) * 0.01f;
return value * multiple;
}
public virtual void SetCollisionEnterMethod()
{
method = ExplosiveMethod.collisionEnter;
}
public virtual void SetCollisionEnterTimerMethod(int timer)
{
method = ExplosiveMethod.collisionEnterTimer;
this.timeToExplode = timer;
}
public virtual void SetRemoveMethod()
{
method = ExplosiveMethod.remote;
}
public virtual void SetRemoveTimerMethod(int timer)
{
method = ExplosiveMethod.remoteTimer;
this.timeToExplode = timer;
}
public virtual void SetTimerMethod(int timer)
{
method = ExplosiveMethod.timer;
this.timeToExplode = timer;
}
public virtual void ActiveExplosion()
{
if (method == ExplosiveMethod.remote)
Explode();
else if (method == ExplosiveMethod.remoteTimer)
{
StartCoroutine(StartTimer());
}
}
public void RemoveParent()
{
transform.parent = null;
}
public void RemoveParentOfOther(Transform other)
{
other.parent = null;
}
}
}