using System; using System.Collections; using System.Collections.Generic; using System.Linq; using BITFALL.Combat; using BITKit; using BITKit.Entities; using BITKit.Entities.Melee; using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks.Triggers; using Unity.Collections; using UnityEngine; using UnityEngine.VFX; namespace BITFALL.Combat { [Serializable] public class MeleeServiceSingleton : IMeleeService { public void Melee(MeleeCommand command) => MeleeService.Melee(command); } public class MeleeService : MonoBehaviour,IMeleeService { internal static MeleeService Singleton; [RuntimeInitializeOnLoadMethod] private static void Initialize() { Queue.Clear(); } private static readonly Queue Queue = new(); public static void Melee(MeleeCommand command)=>Queue.Enqueue(command); [SerializeField] private LayerMask detectLayer; [SerializeReference, SubclassSelector] private IDamageService damageService; private void Awake() { Singleton = this; } private void FixedUpdate() { if (Queue.TryDequeue(out var command) is false) return; var colliders = Physics.OverlapSphere(command.Position, command.Range,detectLayer); var _damaged = new Dictionary(); var hits = colliders .Where(x => x.GetComponent() is not null) .Where(x => MathV.IsForward(command.Position, command.Forward, x.transform.position)) .Select(x => x.GetComponent()) .Where(damageable =>damageable.UnityEntity?.Id != command.PlayerId) .Where(damageable => { if (command.IgnoreTags is null || !(command.IgnoreTags?.Length > 0) || damageable.UnityEntity is null || !damageable.UnityEntity.TryGetComponent(out var iTags)) return true; return !MathE.Contains(iTags.GetTags(), command.IgnoreTags); }) .Where(x => x.UnityEntity is null || _damaged.TryAdd(x.UnityEntity.Id, x.UnityEntity)) .OrderBy(ByDistance) ; var damageCount = 0; if (command.Limit is 0) { command.Limit = 1; } foreach (var x in hits) { try { var damageable = x; if (command.ForceHitStun) { if (damageable is not null && damageable.UnityEntity is not null && damageable.UnityEntity.TryGetComponent(out var combat)) { combat.HitStun(); } } var damageMsg = new DamageMessage() { Initiator = UnityEntitiesService.Get(command.PlayerId) as IUnityEntity, DamageType = new MeleeDamageMessage { }, Target = damageable.UnityEntity, Damage = command.Damage is 0 ? 64 : command.Damage, Hit = damageable, Position = command.Position, Rotation = Quaternion.LookRotation(command.Forward) }; if (command.PlayerId !=default) { damageMsg.Initiator = UnityEntitiesService.Get(command.PlayerId) as IUnityEntity; } if (damageable.Health is not null && damageable.Health.IsAlive && damageCount < command.Limit) { damageService.Execute(damageMsg); damageCount++; } damageable.Rigidbody.AddForceAtPositionAsync(command.Force, command.Position, ForceMode.Impulse) .Forget(); if ((x as MonoBehaviour)!.TryGetComponent(out var _tag)) { DI.Get().Spawn(new Location { position =x.Rigidbody? x.Rigidbody.position:(x as MonoBehaviour).transform.position, //rotation = Quaternion.identity, }, _tag.GetTags()); } } catch (UnassignedReferenceException e) { Debug.LogWarning(x); Debug.LogException(e); } } return; float ByDistance(IDamagable arg) { if (arg is Component component) { return Vector3.Distance(component.transform.position, command.Position); } return 0; } } void IMeleeService.Melee(MeleeCommand command) => Melee(command); } }