using System; using System.Buffers; 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.Pool; 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); [SerializeReference, SubclassSelector] private ITicker ticker; [SerializeField] private LayerMask detectLayer; [SerializeField] private LayerMask blockLayer; [SerializeReference, SubclassSelector] private IDamageService damageService; private void Awake() { Singleton = this; } private void Start() { ticker.Add(OnTick); destroyCancellationToken.Register(Dispose); } private void Dispose() { ticker.Remove(OnTick); } private void OnTick(float obj) { if (Queue.TryDequeue(out var command) is false) return; Debug.DrawLine(command.Position, command.Position + command.Forward * command.Range, Color.red, 1f); 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)) .Where(x=>MathV.InFovRange(command.Position,command.Forward,x.transform.position,90)) .Select(x => x.GetComponent()) .OrderBy(ByDistance) .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)) .Where(InSight) ; var damageCount = 0; if (command.Limit is 0) { command.Limit = 1; } _damaged.Clear(); 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()!.TryGetComponent(out var _tag)) { var tags = _tag.GetTags(); var list = ArrayPool.Shared.Rent(tags.Length+1); list[^1] = "Melee"; //帮我Copy一下 Array.Copy(tags, 0, list, 0, tags.Length); DI.Get().Spawn(new Location { //position =x.Rigidbody? x.Rigidbody.position:x.As().transform.position, position = x.As().transform.position //rotation = Quaternion.identity, }, list); ArrayPool.Shared.Return(list); } } catch (UnassignedReferenceException e) { Debug.LogWarning(x); Debug.LogException(e); } } return; bool InSight(IDamagable arg) { if (arg is Component component) { var ray = new Ray(command.Position, (Vector3)component.transform.position - (Vector3)command.Position); if (Physics.Raycast(ray, out var hit, command.Range, blockLayer)) { return hit.collider == component.GetComponent(); } } return true; } 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); } }