Bullet.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using QFSW.MOP2;
  5. using Sirenix.OdinInspector;
  6. using KairoEngine.Core;
  7. using KairoEngine.Inventory;
  8. using KairoEngine.SFX;
  9. using KairoEngine.Utilities.Statistics;
  10. namespace KairoEngine.CharacterSystem
  11. {
  12. public class Bullet : MonoBehaviour
  13. {
  14. public string muzzlePrefabPool = "";
  15. public string hitPrefabPool = "";
  16. public List<GameObject> trails;
  17. [ReadOnly, ShowInInspector] public ItemFirearmRef firearmRef;
  18. [ReadOnly] public CharacterController character;
  19. public bool bounce = false;
  20. public bool alwaysSpawnHitPrefab = false;
  21. public float bounceForce = 10;
  22. [FoldoutGroup("Inpact Sound Effects")] public SFXClip concreteSFX;
  23. [FoldoutGroup("Inpact Sound Effects")] public SFXClip dirtSFX;
  24. [FoldoutGroup("Inpact Sound Effects")] public SFXClip fleshSFX;
  25. [FoldoutGroup("Inpact Sound Effects")] public SFXClip grassSFX;
  26. [FoldoutGroup("Inpact Sound Effects")] public SFXClip metalSFX;
  27. [FoldoutGroup("Inpact Sound Effects")] public SFXClip mudSFX;
  28. [FoldoutGroup("Inpact Sound Effects")] public SFXClip sandSFX;
  29. [FoldoutGroup("Inpact Sound Effects")] public SFXClip stoneSFX;
  30. [FoldoutGroup("Inpact Sound Effects")] public SFXClip woodSFX;
  31. private float counter = 0f;
  32. public Vector3 startPos;
  33. private float speedRandomness;
  34. private Vector3 offset;
  35. private bool collided;
  36. private Rigidbody rb;
  37. private GameObject target;
  38. private ItemBaseFirearm itemBaseFirearm;
  39. private float speed;
  40. private bool hitEnemy = false;
  41. public string bulletPrefabPool = "";
  42. public void Initialize(Transform source, ItemFirearmRef firearmRef, CharacterController character, string bulletPrefabPool = "")
  43. {
  44. this.firearmRef = firearmRef;
  45. this.character = character;
  46. this.bulletPrefabPool = bulletPrefabPool;
  47. startPos = source.position;
  48. this.gameObject.transform.position = source.position;
  49. this.transform.rotation = source.rotation;
  50. if(rb == null) rb = GetComponent<Rigidbody>();
  51. rb.isKinematic = false;
  52. itemBaseFirearm = (ItemBaseFirearm)firearmRef.item;
  53. counter = 0;
  54. hitEnemy = false;
  55. target = null;
  56. collided = false;
  57. CalculateAccuracy();
  58. MuzzleFX();
  59. }
  60. void FixedUpdate()
  61. {
  62. if (speed != 0 && rb != null)
  63. {
  64. rb.position += (transform.forward + offset) * (speed * Time.deltaTime);
  65. }
  66. counter += Time.deltaTime;
  67. if (counter > itemBaseFirearm.ammoType.lifetime)
  68. {
  69. RemoveBullet();
  70. }
  71. }
  72. void OnCollisionEnter(Collision co)
  73. {
  74. if(co.gameObject.GetComponent<DamageEmitter>() != null) return;
  75. if (!bounce)
  76. {
  77. if (co.gameObject.tag != "Bullet" && !collided)
  78. {
  79. collided = true;
  80. if (trails.Count > 0)
  81. {
  82. for (int i = 0; i < trails.Count; i++)
  83. {
  84. trails[i].transform.parent = null;
  85. var ps = trails[i].GetComponent<ParticleSystem>();
  86. if (ps != null)
  87. {
  88. ps.Stop();
  89. Destroy(ps.gameObject, ps.main.duration + ps.main.startLifetime.constantMax);
  90. }
  91. }
  92. }
  93. PlayInpactSound(co);
  94. //speed = 0;
  95. rb.isKinematic = true;
  96. ContactPoint contact = co.contacts[0];
  97. Quaternion rot = Quaternion.FromToRotation(Vector3.up, contact.normal);
  98. Vector3 pos = contact.point;
  99. WorldObject worldObject = co.gameObject.transform.root.GetComponent<WorldObject>();
  100. DamageController damageController = null;
  101. DamagePoint damagePoint = co.gameObject.GetComponent<DamagePoint>();
  102. if(worldObject != null)
  103. {
  104. if(worldObject.IsCharacter()) damageController = worldObject.character.damageController;
  105. }
  106. if(damageController == null && damagePoint == null)
  107. {
  108. HitFX(pos, rot);
  109. RemoveBullet();
  110. }
  111. else if(damagePoint != null)
  112. {
  113. damagePoint.damageController.Damage(this, pos, damagePoint );
  114. hitEnemy = true;
  115. }
  116. else if(damageController != null)
  117. {
  118. damageController.Damage(this, pos);
  119. hitEnemy = true;
  120. }
  121. // if(damageController != null)
  122. // {
  123. // Rigidbody rigidbody = damageController.GetComponentInChildren<Rigidbody>();
  124. // if(rigidbody != null)
  125. // {
  126. // rigidbody.AddForce(transform.forward * 2, ForceMode.Impulse);
  127. // }
  128. // }
  129. }
  130. }
  131. else
  132. {
  133. rb.useGravity = true;
  134. rb.drag = 0.5f;
  135. ContactPoint contact = co.contacts[0];
  136. rb.AddForce(Vector3.Reflect((contact.point - startPos).normalized, contact.normal) * bounceForce, ForceMode.Impulse);
  137. RemoveBullet();
  138. }
  139. }
  140. public void RemoveBullet()
  141. {
  142. ProcessStatistics();
  143. if(bulletPrefabPool == "") Destroy(this.gameObject);
  144. else MasterObjectPooler.Instance.GetPool(bulletPrefabPool).Release(this.gameObject);
  145. }
  146. private void MuzzleFX()
  147. {
  148. GameObject muzzleVFX = null;
  149. if(muzzlePrefabPool != "")
  150. {
  151. muzzleVFX = MasterObjectPooler.Instance.GetPool(muzzlePrefabPool).GetObject();
  152. muzzleVFX.transform.position = startPos;
  153. muzzleVFX.transform.forward = gameObject.transform.forward + offset;
  154. }
  155. }
  156. private void HitFX(Vector3 pos, Quaternion rot)
  157. {
  158. GameObject hitVFX = null;
  159. if(hitPrefabPool != "")
  160. {
  161. hitVFX = MasterObjectPooler.Instance.GetPool(hitPrefabPool).GetObject();
  162. hitVFX.transform.position = pos;
  163. hitVFX.transform.rotation = rot;
  164. }
  165. }
  166. private void CalculateAccuracy()
  167. {
  168. float accuracy = itemBaseFirearm.Accuracy(firearmRef) + firearmRef.accuracyModifier;
  169. if(accuracy > 100) accuracy = 100;
  170. if(accuracy < 50) accuracy = 50;
  171. speed = itemBaseFirearm.ammoType.bulletSpeed;
  172. //used to create a radius for the accuracy and have a very unique randomness
  173. if (accuracy != 100)
  174. {
  175. accuracy = 1 - (accuracy / 100) ;
  176. for (int i = 0; i < 2; i++)
  177. {
  178. var val = 1 * Random.Range(-accuracy, accuracy);
  179. var index = Random.Range(0, 2);
  180. if (i == 0)
  181. {
  182. if (index == 0)
  183. offset = new Vector3(0, -val, 0);
  184. else
  185. offset = new Vector3(0, val, 0);
  186. }
  187. else
  188. {
  189. if (index == 0)
  190. offset = new Vector3(0, offset.y, -val);
  191. else
  192. offset = new Vector3(0, offset.y, val);
  193. }
  194. }
  195. }
  196. }
  197. private void ProcessStatistics()
  198. {
  199. if(character.unique_name == "player-character_")
  200. {
  201. Statistics.GetData("ShotsFired").AddInteger(1);
  202. if(hitEnemy == true)
  203. {
  204. Statistics.GetData("ShotsHit").AddInteger(1);
  205. }
  206. }
  207. }
  208. void PlayInpactSound(Collision co)
  209. {
  210. //Debug.Log(co.collider.material.name);
  211. SFXClip clip = null;
  212. string materialName = co.collider.material.name;
  213. materialName = materialName.Replace(" (Instance)", "");
  214. switch (materialName)
  215. {
  216. case "Concrete":
  217. clip = concreteSFX;
  218. break;
  219. case "Dirt":
  220. clip = dirtSFX;
  221. break;
  222. case "Flesh":
  223. clip = fleshSFX;
  224. break;
  225. case "Grass":
  226. clip = grassSFX;
  227. break;
  228. case "Metal":
  229. clip = metalSFX;
  230. break;
  231. case "Mud":
  232. clip = mudSFX;
  233. break;
  234. case "Sand":
  235. clip = sandSFX;
  236. break;
  237. case "Stone":
  238. clip = stoneSFX;
  239. break;
  240. case "Wood":
  241. clip = woodSFX;
  242. break;
  243. default:
  244. break;
  245. }
  246. if(clip == null)
  247. {
  248. Debug.LogWarning($"Missing inpact SFX clip for material \'{materialName}\' ({co.gameObject.name})", co.gameObject);
  249. return;
  250. }
  251. else SoundController.EmmitSound(clip, co.contacts[0].point);
  252. }
  253. }
  254. }