using System; using System.Collections.Generic; using System.Linq; using System.Text; using BITFALL.Bullet; using UnityEngine; using BITKit; using BITKit.Core.Entites; using BITKit.Entities; using Cysharp.Threading.Tasks; using UnityEditor; using UnityEngine.Animations; using UnityEngine.UIElements; namespace BITFALL { [Serializable] public record InstanceBullet : SpawnBullet { public Vector3 currentPos; public float currentSpeed = 64; public BITBullet model; } [Serializable] public class BulletServiceSingleton : IBulletService { public void Spawn(SpawnBullet bullet)=>BulletService.Spawn?.Invoke(bullet); } public class BulletService : MonoBehaviour, IBulletService { public static Action Spawn; [Header(Constant.Header.Settings)] public LayerMask layerMask; [Header(Constant.Header.Providers)] [SerializeField, SerializeReference, SubclassSelector] private IEntitiesService entitiesService; [SerializeField, SerializeReference, SubclassSelector] private INetProvider netProvider; [SerializeField, SerializeReference, SubclassSelector] private IDamageService damageService; [Header(Constant.Header.Settings)] public UnityPool pool = new(); [Header(Constant.Header.InternalVariables)] public List instances = new(); private readonly RaycastHit[] _raycastHits = new RaycastHit[16]; private void Start() { Spawn += SpawnBullet; } private void OnDestroy() { Spawn -= SpawnBullet; } private void FixedUpdate() { instances ??= new List(); foreach (var bullet in instances.ToArray()) { var size = Physics.RaycastNonAlloc(bullet.currentPos, bullet.forward, _raycastHits, bullet.currentSpeed * Time.fixedDeltaTime, layerMask); var validHit = false; foreach (var raycastHit in _raycastHits.Take(size).OrderBy(x => Vector3.Distance(bullet.pos, x.point))) { if (!IsValidHit(raycastHit, bullet)) continue; validHit = true; break; } if (validHit ||bullet.currentSpeed <= 0) { instances.TryRemove(bullet); pool.Return(bullet.model); } else { bullet.currentSpeed -= bullet.startSpeed * Time.fixedDeltaTime; bullet.currentPos += (Vector3)bullet.forward * (bullet.currentSpeed * Time.fixedDeltaTime); bullet.model.transform.position = bullet.currentPos; } } } private async void RpcSpawnBullet(SpawnBullet x) { await UniTask.SwitchToMainThread(); var instance = pool.Get(); InstanceBullet bullet = new() { initiator = x.initiator, currentPos = x.pos, pos = x.pos, rot = x.rot, forward = x.forward, initialDamage = x.initialDamage, model = instance }; var instanceTransform = instance.transform; instanceTransform.SetPositionAndRotation(x.pos, x.rot); instanceTransform.forward = x.forward; instances.Add(bullet); } private void SpawnBullet(SpawnBullet x) { RpcSpawnBullet(x); } private bool IsValidHit(RaycastHit raycastHit, InstanceBullet bullet) { if (layerMask.Includes(raycastHit.collider.gameObject.layer) is false) return false; raycastHit.collider.TryGetComponentAny(out var physicsInfo); var force = (raycastHit.point - (Vector3)bullet.pos).normalized * (physicsInfo?.AddForceMultiple ?? 64); if (raycastHit.collider.TryGetComponent(out var damagable)) { if (damagable.Entity.Id == bullet.initiator) return false; if (layerMask.Includes(raycastHit.collider.gameObject.layer) is false) return false; var msg = new DamageMessage() { target = damagable.Entity, hit = damagable, damage = bullet.initialDamage, }; damageService.Execute(msg); } if (raycastHit.rigidbody is not null) { if(raycastHit.rigidbody.gameObject.layer is not 0) raycastHit.rigidbody.AddForceAtPositionAsync(force, raycastHit.point, ForceMode.Impulse).Forget(); } List tags = new() { "BulletHit", }; if (raycastHit.transform.TryGetComponent(out var _tag)) { tags.AddRange(_tag.GetTags()); } Location location = new() { position = raycastHit.point, forward = raycastHit.normal, }; var vfx = DI.Get().Spawn(location, tags.ToArray()); var constraint = vfx.gameObject.AddComponent(); var sourceTransform = raycastHit.transform; constraint.AddSource(new ConstraintSource() { sourceTransform = sourceTransform, weight = 1, }); var positionOffset = sourceTransform.InverseTransformPoint(raycastHit.point); var rotationOffset = Quaternion.Inverse(sourceTransform.rotation) * transform.rotation; constraint.SetTranslationOffset(0, positionOffset); //constraint.SetRotationOffset(0, rotationOffset.eulerAngles); constraint.weight = 1; constraint.constraintActive = true; return true; } void IBulletService.Spawn(SpawnBullet bullet) => SpawnBullet(bullet); } }