using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using Animancer; using BITFALL; using BITFALL.Bullet; using BITKit; using BITKit.Entities; using Cysharp.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Net.BITKit.Quadtree; using Net.Project.B.Faction; using Net.Project.B.Health; using Net.Project.B.Melee; using Project.B.CharacterController; using Unity.Mathematics; using UnityEngine; using UnityEngine.AI; using Random = UnityEngine.Random; namespace Net.Project.B.AI { public class AIZombieService:IDisposable { private readonly IHealthService _healthService; private readonly IMeleeService _meleeService; private readonly ILogger _logger; private readonly IEntitiesService _entitiesService; private readonly ITicker _ticker; private readonly ConcurrentDictionary _attackIntervals = new(); private readonly ScriptableAIAnimations _animations; private readonly MeleeData _meleeData = new() { Damage = 10, IgnoreTags = { "zombie" }, MaxTargetCount = 1, Radius = 1.2f, }; public AIZombieService(ITicker ticker, IEntitiesService entitiesService, ILogger logger, IMeleeService meleeService, IHealthService healthService) { _ticker = ticker; _entitiesService = entitiesService; _logger = logger; _meleeService = meleeService; _healthService = healthService; _ticker.Add(OnTick); _animations = _entitiesService.QueryComponents() is {Length:>0} s ? s[0] :null; } private void OnTick(float obj) { foreach (var (blackBoard,characterController, entity, zombie, navMeshAgent, animancerComponent) in _entitiesService .QueryComponents()) { if (!navMeshAgent.isOnNavMesh) continue; if (characterController.AllowMovement.Allow is false) continue; if(blackBoard is not {Target:not null , AlertValue: 1})continue; blackBoard.Target.ServiceProvider.QueryComponents(out ICharacterController humanCc); var playerPos = humanCc.ViewPosition; var zombiePos = characterController.Position; navMeshAgent.SetDestination(playerPos); navMeshAgent.radius = Mathf.Clamp(Vector3.Distance(playerPos, zombiePos) * 0.1f, 0.64f, 1.6f); if(_attackIntervals.GetOrCreate(entity.Id).AllowUpdate)continue; var distance = math.distance(playerPos.xz, zombiePos.xz); if (distance < navMeshAgent.stoppingDistance*2) { Attack(); _meleeData.Initiator = entity.Id; _meleeData.StartPosition = characterController.ViewPosition; _meleeService.Add(_meleeData); } continue; async void Attack() { using var _ = characterController.AllowMovement.GetDisableHandle(); var state = animancerComponent.Layers[1].Play(_animations.AttachClip, 0.2f); state.Time = 0; await state; if (animancerComponent) state.Stop(); } } } public void Dispose() { _ticker.Remove(OnTick); } } }