| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 | using System;using System.Collections;using UnityEngine;using Random = UnityEngine.Random;using UnityEngine.Events;#if UNITY_EDITORusing UnityEditor;#endifnamespace HQFPSWeapons{	[RequireComponent(typeof(Collider))]	[RequireComponent(typeof(Rigidbody))]	public class ShaftedProjectile : Projectile	{		public struct ImpactInfo		{			public Hitbox Hitbox;		}		public Message<ImpactInfo> Impact = new Message<ImpactInfo>();		public Message Launched = new Message();		[BHeader("General", true)]		[SerializeField]		private LayerMask m_Mask = new LayerMask();		[SerializeField]		private float m_MaxDistance = 2f;		[SerializeField]		private TrailRenderer m_Trail = null;		[SerializeField]		private bool m_AllowSticking = false;		[BHeader("Damage")]		[SerializeField]		[Range(0f, 200f)]		private float m_MaxDamageSpeed = 50f;		[SerializeField]		[Range(0f, 200f)]		private float m_MaxDamage = 100f;		[SerializeField]		private AnimationCurve m_DamageCurve = new AnimationCurve(			new Keyframe(0f, 1f),			new Keyframe(0.8f, 0.5f),			new Keyframe(1f, 0f));		[SerializeField]		private float m_ImpactForce = 15f;		[BHeader("Penetration")]		[SerializeField]		private float m_PenetrationOffset = 0.2f;		[SerializeField]		private Vector2 m_RandomRotation = Vector2.zero;		[SerializeField]		private bool m_PlayTwangAnimation = false;		[SerializeField]		private UnityEvent m_OnImpact = null;		[SerializeField]		[ShowIf("m_PlayTwangAnimation", true)]		private TwangSettings m_TwangSettings = null;		[BHeader("Audio")]		[SerializeField]		private AudioSource m_AudioSource = null;		[SerializeField]		private SurfaceEffects m_PenetrationEffect = new SurfaceEffects();		[BHeader("Pickup")]		[SerializeField]		private bool m_EnablePickupOnImpact = true;		[SerializeField]		private ItemPickup m_Pickup = null;		private LivingEntity m_Launcher;		private Collider m_Collider;		private Rigidbody m_Rigidbody;		private bool m_Done;		private bool m_Launched;		private Transform m_Pivot;		public override void Launch(LivingEntity launcher)		{			if (m_Launcher != null)			{				Debug.LogWarningFormat(this, "Already launched this projectile!", name);				return;			}			m_Launcher = launcher;			m_Launched = true;			OnLaunched();			Launched.Send();		}		public void CheckForSurfaces(Vector3 position, Vector3 direction)		{			RaycastHit hitInfo;			Ray ray = new Ray(position, direction);			if (Physics.Raycast(ray, out hitInfo, m_MaxDistance, m_Mask, QueryTriggerInteraction.Ignore))			{				SurfaceManager.SpawnEffect(hitInfo, SurfaceEffects.Stab, 1f);				float currentSpeed = m_Rigidbody.velocity.magnitude;				float impulse = m_ImpactForce;				var damageable = hitInfo.collider.GetComponent<IDamageable>();				// If the object is damageable...				if (damageable != null)				{					float damageMod = m_DamageCurve.Evaluate(1f - currentSpeed / m_MaxDamageSpeed);					float damage = m_MaxDamage * damageMod;					var damageData = new HealthEventData(-damage, DamageType.Stab, hitInfo.point, ray.direction, impulse, hitInfo.normal, m_Launcher);					damageable.TakeDamage(damageData);				}				if (hitInfo.rigidbody != null)				{					// If the object is a rigidbody, apply an impact force					hitInfo.rigidbody.AddForceAtPosition(transform.forward * impulse, hitInfo.point, ForceMode.Impulse);				}				SurfaceManager.SpawnEffect(hitInfo, m_PenetrationEffect, 1f);				// Stick the projectile in the object				transform.position = hitInfo.point + transform.forward * m_PenetrationOffset;				var hitbox = hitInfo.collider.GetComponent<Hitbox>();				m_OnImpact.Invoke();				Impact.Send(new ImpactInfo() { Hitbox = hitbox });				m_Collider.enabled = true;				if (m_AllowSticking && m_Pickup != null)				{					transform.SetParent(hitInfo.transform);					OnSurfacePenetrated();					Physics.IgnoreCollision(m_Collider, hitInfo.collider);				}				m_Done = true;			}		}		public void OnLaunched()		{			if (m_Pickup != null)				m_Pickup.EnablePickup(false);			m_Collider.enabled = false;			if(m_Trail != null)				m_Trail.enabled = true;		}		public void OnSurfacePenetrated()		{			m_Rigidbody.isKinematic = true;			if (m_PlayTwangAnimation)				StartCoroutine(C_DoTwang());			if (m_Pickup != null && m_EnablePickupOnImpact)				m_Pickup.EnablePickup(true);		}		private void Awake()		{			m_Collider = GetComponent<Collider>();			m_Rigidbody = GetComponent<Rigidbody>();			if(m_Trail != null)				m_Trail.enabled = false;		}		private void FixedUpdate()		{			if (m_Launched && !m_Done)				CheckForSurfaces(transform.position, transform.forward);		}		#if UNITY_EDITOR		private void OnDrawGizmosSelected()		{			if (m_PlayTwangAnimation)			{				Vector3 twangPivotPosition = transform.position + transform.TransformVector(m_TwangSettings.MovementPivot);				Gizmos.color = new Color(1f, 0f, 0f, 0.85f);				Gizmos.DrawSphere(twangPivotPosition, 0.03f);				Vector3 sceneCamPosition = SceneView.currentDrawingSceneView.camera.transform.position;				Vector3 sceneCamForward = SceneView.currentDrawingSceneView.camera.transform.forward;				// Make sure we don't draw the label when not looking at it				if (Vector3.Dot(sceneCamForward, twangPivotPosition - sceneCamPosition) >= 0f)					Handles.Label(twangPivotPosition, "Twang Pivot");			}		}		#endif		private IEnumerator C_DoTwang()		{			m_Pivot = new GameObject("Shafted Projectile Pivot").transform;			m_Pivot.position = transform.position + Vector3Utils.LocalToWorld(m_TwangSettings.MovementPivot, transform);			m_Pivot.rotation = transform.rotation;			float stopTime = Time.time + m_TwangSettings.Duration;			float range = m_TwangSettings.Range;			float currentVelocity = 0f;			m_TwangSettings.Audio.Play(ItemSelection.Method.RandomExcludeLast, m_AudioSource);			Quaternion localRotation = m_Pivot.localRotation;			Quaternion randomRotation = Quaternion.Euler(new Vector2(				Random.Range(-m_RandomRotation.x, m_RandomRotation.x),				Random.Range(-m_RandomRotation.y, m_RandomRotation.y)));			while (Time.time < stopTime)			{				m_Pivot.localRotation = localRotation * randomRotation * Quaternion.Euler(Random.Range(-range, range), Random.Range(-range, range), 0f);				range = Mathf.SmoothDamp(range, 0f, ref currentVelocity, stopTime - Time.time);				yield return null;			}		}		private void OnDestroy()		{			if (m_Pivot != null)				Destroy(m_Pivot.gameObject);		}        #region Internal        [Serializable]		public class TwangSettings		{			public Vector3 MovementPivot;			public float Duration = 1f;			public float Range = 18f;			public SoundPlayer Audio;		}        #endregion    }}
 |