using System.Collections; using System.Collections.Generic; using UnityEngine; using QFSW.MOP2; using Sirenix.OdinInspector; using KairoEngine.Core; using KairoEngine.Inventory; using KairoEngine.SFX; using KairoEngine.Utilities.Statistics; namespace KairoEngine.CharacterSystem { public class Bullet : MonoBehaviour { public string muzzlePrefabPool = ""; public string hitPrefabPool = ""; public List trails; [ReadOnly, ShowInInspector] public ItemFirearmRef firearmRef; [ReadOnly] public CharacterController character; public bool bounce = false; public bool alwaysSpawnHitPrefab = false; public float bounceForce = 10; [FoldoutGroup("Inpact Sound Effects")] public SFXClip concreteSFX; [FoldoutGroup("Inpact Sound Effects")] public SFXClip dirtSFX; [FoldoutGroup("Inpact Sound Effects")] public SFXClip fleshSFX; [FoldoutGroup("Inpact Sound Effects")] public SFXClip grassSFX; [FoldoutGroup("Inpact Sound Effects")] public SFXClip metalSFX; [FoldoutGroup("Inpact Sound Effects")] public SFXClip mudSFX; [FoldoutGroup("Inpact Sound Effects")] public SFXClip sandSFX; [FoldoutGroup("Inpact Sound Effects")] public SFXClip stoneSFX; [FoldoutGroup("Inpact Sound Effects")] public SFXClip woodSFX; private float counter = 0f; public Vector3 startPos; private float speedRandomness; private Vector3 offset; private bool collided; private Rigidbody rb; private GameObject target; private ItemBaseFirearm itemBaseFirearm; private float speed; private bool hitEnemy = false; public string bulletPrefabPool = ""; public void Initialize(Transform source, ItemFirearmRef firearmRef, CharacterController character, string bulletPrefabPool = "") { this.firearmRef = firearmRef; this.character = character; this.bulletPrefabPool = bulletPrefabPool; startPos = source.position; this.gameObject.transform.position = source.position; this.transform.rotation = source.rotation; if(rb == null) rb = GetComponent(); rb.isKinematic = false; itemBaseFirearm = (ItemBaseFirearm)firearmRef.item; counter = 0; hitEnemy = false; target = null; collided = false; CalculateAccuracy(); MuzzleFX(); } void FixedUpdate() { if (speed != 0 && rb != null) { rb.position += (transform.forward + offset) * (speed * Time.deltaTime); } counter += Time.deltaTime; if (counter > itemBaseFirearm.ammoType.lifetime) { RemoveBullet(); } } void OnCollisionEnter(Collision co) { if(co.gameObject.GetComponent() != null) return; if (!bounce) { if (co.gameObject.tag != "Bullet" && !collided) { collided = true; if (trails.Count > 0) { for (int i = 0; i < trails.Count; i++) { trails[i].transform.parent = null; var ps = trails[i].GetComponent(); if (ps != null) { ps.Stop(); Destroy(ps.gameObject, ps.main.duration + ps.main.startLifetime.constantMax); } } } PlayInpactSound(co); //speed = 0; rb.isKinematic = true; ContactPoint contact = co.contacts[0]; Quaternion rot = Quaternion.FromToRotation(Vector3.up, contact.normal); Vector3 pos = contact.point; WorldObject worldObject = co.gameObject.transform.root.GetComponent(); DamageController damageController = null; DamagePoint damagePoint = co.gameObject.GetComponent(); if(worldObject != null) { if(worldObject.IsCharacter()) damageController = worldObject.character.damageController; } if(damageController == null && damagePoint == null) { HitFX(pos, rot); RemoveBullet(); } else if(damagePoint != null) { damagePoint.damageController.Damage(this, pos, damagePoint ); hitEnemy = true; } else if(damageController != null) { damageController.Damage(this, pos); hitEnemy = true; } // if(damageController != null) // { // Rigidbody rigidbody = damageController.GetComponentInChildren(); // if(rigidbody != null) // { // rigidbody.AddForce(transform.forward * 2, ForceMode.Impulse); // } // } } } else { rb.useGravity = true; rb.drag = 0.5f; ContactPoint contact = co.contacts[0]; rb.AddForce(Vector3.Reflect((contact.point - startPos).normalized, contact.normal) * bounceForce, ForceMode.Impulse); RemoveBullet(); } } public void RemoveBullet() { ProcessStatistics(); if(bulletPrefabPool == "") Destroy(this.gameObject); else MasterObjectPooler.Instance.GetPool(bulletPrefabPool).Release(this.gameObject); } private void MuzzleFX() { GameObject muzzleVFX = null; if(muzzlePrefabPool != "") { muzzleVFX = MasterObjectPooler.Instance.GetPool(muzzlePrefabPool).GetObject(); muzzleVFX.transform.position = startPos; muzzleVFX.transform.forward = gameObject.transform.forward + offset; } } private void HitFX(Vector3 pos, Quaternion rot) { GameObject hitVFX = null; if(hitPrefabPool != "") { hitVFX = MasterObjectPooler.Instance.GetPool(hitPrefabPool).GetObject(); hitVFX.transform.position = pos; hitVFX.transform.rotation = rot; } } private void CalculateAccuracy() { float accuracy = itemBaseFirearm.Accuracy(firearmRef) + firearmRef.accuracyModifier; if(accuracy > 100) accuracy = 100; if(accuracy < 50) accuracy = 50; speed = itemBaseFirearm.ammoType.bulletSpeed; //used to create a radius for the accuracy and have a very unique randomness if (accuracy != 100) { accuracy = 1 - (accuracy / 100) ; for (int i = 0; i < 2; i++) { var val = 1 * Random.Range(-accuracy, accuracy); var index = Random.Range(0, 2); if (i == 0) { if (index == 0) offset = new Vector3(0, -val, 0); else offset = new Vector3(0, val, 0); } else { if (index == 0) offset = new Vector3(0, offset.y, -val); else offset = new Vector3(0, offset.y, val); } } } } private void ProcessStatistics() { if(character.unique_name == "player-character_") { Statistics.GetData("ShotsFired").AddInteger(1); if(hitEnemy == true) { Statistics.GetData("ShotsHit").AddInteger(1); } } } void PlayInpactSound(Collision co) { //Debug.Log(co.collider.material.name); SFXClip clip = null; string materialName = co.collider.material.name; materialName = materialName.Replace(" (Instance)", ""); switch (materialName) { case "Concrete": clip = concreteSFX; break; case "Dirt": clip = dirtSFX; break; case "Flesh": clip = fleshSFX; break; case "Grass": clip = grassSFX; break; case "Metal": clip = metalSFX; break; case "Mud": clip = mudSFX; break; case "Sand": clip = sandSFX; break; case "Stone": clip = stoneSFX; break; case "Wood": clip = woodSFX; break; default: break; } if(clip == null) { Debug.LogWarning($"Missing inpact SFX clip for material \'{materialName}\' ({co.gameObject.name})", co.gameObject); return; } else SoundController.EmmitSound(clip, co.contacts[0].point); } } }