using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Windows.Forms.VisualStyles; using BITKit; using BITKit.Entities; using BITKit.OpenWorld; using kcp2k; using Quadtree; using Unity.Mathematics; using UnityEngine; using UnityEngine.AI; using YooAsset; using Random = UnityEngine.Random; namespace BITFALL.GameMode { public class UnityAISpawner : MonoBehaviour { [SerializeReference,SubclassSelector] private ITicker ticker; [SerializeReference, SubclassSelector] private new IReference name; [SerializeReference,SubclassSelector] private IReference entityPath; [SerializeField, ReadOnly] private int spawnedCount; [SerializeField] private Transform spawnPoint; [SerializeField] private ChunkBehaviour chunkBehaviour; [SerializeField] private int minCapacity; [SerializeField] private int maxCapacity; private readonly HashSet _spawnedEntities = new(); private int _capacity; public IEntity Entity { get { var task = YooAssets.LoadAssetAsync(entityPath.Value); task.WaitForAsyncComplete(); return task.AssetObject.As().GetComponent(); } } private readonly ConcurrentQueue _removeQueue = new(); private readonly ValidHandle allowTick = new(); private void OnEnable() { _capacity = Random.Range(minCapacity, maxCapacity); } private void Start() { allowTick.AddListener(x => { if(x) ticker.Add(OnTick); else ticker.Remove(OnTick); }); chunkBehaviour.OnLodChangedEvent += OnLodChanged; destroyCancellationToken.Register(Dispose); } private void Dispose() { allowTick.SetDisableElements(this); } private void OnLodChanged(int arg1, int lod) { switch (lod) { case 0: allowTick.AddElement(this); break; case 1: case 2: allowTick.RemoveElement(this); break; default: allowTick.RemoveElement(this); break; } } private void OnTick(float obj) { while (_removeQueue.TryDequeue(out var _x)) { if (_spawnedEntities.Contains(_x)) _spawnedEntities.Remove(_x); } spawnedCount = _spawnedEntities.Count; if(chunkBehaviour.Lod is not 0) return; if (_spawnedEntities.Count >= _capacity) return; var position = spawnPoint ? spawnPoint.position : transform.position; if (NavMesh.SamplePosition(position, out var navMeshHit, 100, NavMesh.AllAreas)) { } var entity = Entity.As(); var _transform = entity.transform; for (var i = 0; i < _capacity - _spawnedEntities.Count; i++) { if (PoolService.TryGet(_transform, out var instance) is false) continue; if(Physics.Raycast(position+Vector3.up,Vector3.down,out var hit,100)) position = hit.point; var rotation = Quaternion.Euler( 0, Random.Range(0, 360), 0); instance.position = position; instance.rotation = rotation; var newEntity = instance.GetComponent(); _spawnedEntities.Add(newEntity.Id); newEntity.TryGetComponent(out var heal); heal.HealthPoint = heal.MaxHealthPoint; heal.OnSetAlive += OnSetAlive; continue; void OnSetAlive(bool alive) { if (alive) return; PoolService.Release(_transform.name, instance); _removeQueue.Enqueue(newEntity.Id); heal.OnSetAlive -= OnSetAlive; } } } } }