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 Quadtree; using Unity.Mathematics; using UnityEngine; using UnityEngine.AI; using YooAsset; using Random = UnityEngine.Random; namespace BITFALL.GameMode { public class UnityAISpawner : MonoBehaviour,IWorldChunkObject { [SerializeReference,SubclassSelector] private ITicker ticker; [SerializeReference, SubclassSelector] private new IReference name; [SerializeReference,SubclassSelector] private IReference entityPath; [SerializeReference,SubclassSelector] private IChunkService chunkService; [SerializeField, ReadOnly] private int lod; [SerializeField, ReadOnly] private int spawnedCount; [SerializeField] private int minCapacity; [SerializeField] private int maxCapacity; [SerializeField] private int cycleMaxCount; [SerializeField] private float4x4 bounds; private readonly HashSet _spawnedEntities = new(); public string Name => name.Value; public int MinCapacity => minCapacity; public int MaxCapacity => maxCapacity; private int _capacity; public IEntity Entity { get { var task = YooAssets.LoadAssetAsync(entityPath.Value); task.WaitForAsyncComplete(); return task.AssetObject.As().GetComponent(); } } public int CycleMaxCount => cycleMaxCount; public float4x4 Bounds => bounds; public IEntity[] spawnedEntities => _spawnedEntities.ToArray(); private readonly ConcurrentQueue _removeQueue = new(); private readonly ValidHandle allowTick = new(); private void OnEnable() { _capacity = Random.Range(minCapacity, maxCapacity); } private void Start() { lod = -1; allowTick.AddListener(x => { if(x) ticker.Add(OnTick); else ticker.Remove(OnTick); }); chunkService.Register(this); destroyCancellationToken.Register(Dispose); } private void Dispose() { chunkService.Unregister(this); } private void OnTick(float obj) { if(lod is not 0) return; while (_removeQueue.TryDequeue(out var entity)) { if (_spawnedEntities.Contains(entity)) _spawnedEntities.Remove(entity); } spawnedCount = _spawnedEntities.Count; if (spawnedEntities.Length >= _capacity) return; for (var i = 0; i < Math.Min(cycleMaxCount, _capacity - spawnedEntities.Length); i++) { var position =transform.position + new Vector3( Random.Range(-8, 8), Random.Range(0, 8), Random.Range(-8, 8) ); if(Physics.Raycast(position,Vector3.down,out var hit,100)) position = hit.point; var rotation = Quaternion.Euler( 0, Random.Range(0, 360), 0); var entity = Entity.As(); entity = Instantiate(entity, position, rotation); _spawnedEntities.Add(entity); entity.TryGetComponent(out var heal); heal.OnSetAlive += (x) => { if (x) return; _removeQueue.Enqueue(entity); }; } } public Bounds GetBounds() => new(transform.position, new Vector3(4, 4, 4)); public Node ParentNode { get; set; } public void QuadTree_Root_Initialized(IQuadtreeRoot> root) { } public int Id { get; set; } public int Lod { get=>lod; set { lod = value; switch (lod) { case 0: allowTick.AddElement(this); break; case 1: case 2: allowTick.RemoveElement(this); break; default: allowTick.RemoveElement(this); // foreach (var x in _spawnedEntities) // { // Destroy(x.As().gameObject); // } // _spawnedEntities.Clear(); // spawnedCount = 0; break; } } } } }