using System; using System.Buffers; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using BITKit; using BITKit.Entities; using BITKit.Pool; using Cysharp.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Net.BITKit.VFX; using Net.Project.B.Damage; using UnityEngine; namespace Net.Project.B.Melee { public class UnityMeleeService : IMeleeService,IDisposable { private readonly ITicker _ticker; private readonly IDamageService _damageService; private readonly IPoolService _poolService; private readonly VFXService _vfxService; private readonly IEntitiesService _entitiesService; private readonly ConcurrentQueue _melees=new(); private readonly LayerMask _detectedLayer = LayerMask.GetMask("Entities"); private readonly LayerMask _obstacleLayer = LayerMask.GetMask("Default"); private readonly Collider[] _colliders=new Collider[64]; public UnityMeleeService(ITicker ticker, IDamageService damageService, IEntitiesService entitiesService, IPoolService poolService, VFXService vfxService) { _ticker = ticker; _damageService = damageService; _entitiesService = entitiesService; _poolService = poolService; _vfxService = vfxService; _ticker.Add(OnTick); } private async void OnTick(float obj) { if (_melees.TryDequeue(out var melee) is false) return; var length = Physics.OverlapSphereNonAlloc(melee.StartPosition, melee.Radius, _colliders, _detectedLayer); var damagedCount = 0; for (var i = 0; i < length; i++) { var collider = _colliders[i]; if (Physics.Raycast(melee.StartPosition, collider.transform.position - (Vector3)melee.StartPosition, out var hit, melee.Radius, _obstacleLayer)) { if (hit.collider != collider) { // continue; } } if (_entitiesService.TryGetEntity(collider.gameObject.GetInstanceID(), out var entity) is false) continue; if (entity.Id == melee.Initiator) continue; if (entity.ServiceProvider.GetService() is not { } tags) continue; var targetTags = tags.Tags; if (melee.IgnoreTags.Count > 0) { if (melee.IgnoreTags.Intersect(targetTags).Any()) continue; } if (melee.Tags.Count > 0) { if (tags.Tags.Intersect(targetTags).Any() is false) continue; } var array = ArrayPool.Shared.Rent(targetTags.Count + 1); var index = 1; foreach (var x in targetTags) { array[index++] = x; } array[0] = "melee"; var vfx = _vfxService.GetPrefab(array).gameObject; vfx = await _poolService.Spawn(vfx.name, vfx); var closestPoint = collider.ClosestPoint(melee.StartPosition); vfx.transform.position = closestPoint; _damageService.CreateDamageAsync(melee.Initiator, entity.Id, melee.Damage, melee,melee.StartPosition).Forget(); ArrayPool.Shared.Return(array); if (damagedCount++ >= melee.MaxTargetCount) return; } } public void Add(IMeleeData meleeData) => _melees.Enqueue(meleeData); public void Dispose() { _ticker.Remove(OnTick); } } }