using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data.Odbc; using System.Linq; using BITKit; using BITKit.AI.States; using BITKit.Entities; using BITKit.StateMachine; using UnityEngine; using UnityEngine.AI; namespace BITFALL.AI.States { public abstract class AIRuntimeState : AIState { [Inject,HideInInspector] public AIService Service; private readonly ConcurrentDictionary _friends = new(); private readonly ConcurrentDictionary _enemies = new(); protected readonly CacheList detectedEnemies = new(); public bool IsFriend(IEntity target) => _friends.GetOrAdd(target.Id, IsFriendInternal); public bool IsEnemy(IEntity target) => _enemies.GetOrAdd(target.Id, IsEnemyInternal); public override void OnStateUpdate(float deltaTime) { base.OnStateUpdate(deltaTime); detectedEnemies.Clear(); foreach (var x in Service.RangeSensor.Get()) { if (x.TryGetComponent(out var entity) is false) continue; if (IsEnemy(entity) is false)continue; detectedEnemies.Add(entity); } } private bool IsEnemyInternal(ulong id) { var target = UnityEntitiesService.Get(id); var tags = target.As().GetComponent().GetTags(); foreach (var x in Service.EnemyTag.GetTags()) { if (tags.Contains(x)) return true; } return false; } private bool IsFriendInternal(ulong id) { var target = UnityEntitiesService.Get(id); var tags = target.As().GetComponent().GetTags(); foreach (var x in Service.FriendTag.GetTags()) { if (tags.Contains(x)) return true; } return false; } } [Serializable] [CustomType(typeof(AI_Idle))] public sealed class Idle : AIRuntimeState, AI_Idle { public override void OnStateEntry(IState old) { base.OnStateEntry(old); Service.Agent.isStopped = true; } public override void OnStateUpdate(float deltaTime) { base.OnStateUpdate(deltaTime); //foreach (var x in Service.AudioSensor.Get().Union(Service.RangeSensor.Get())) foreach (var entity in detectedEnemies) { Service.CombatTarget = entity; Service.TransitionState(); } } } [Serializable] [CustomType(typeof(AI_Idle))] public sealed class Alert : AIRuntimeState, AI_Alert { public int AlertLevel { get; set; } public float RemainingAlertTime { get; set; } [SerializeField] private int initialAlertTime; [SerializeField] private int immediatelyDetectDistance = 5; [SerializeField,ReadOnly]private float detectedWeight; public override void OnStateEntry(IState old) { base.OnStateEntry(old); RemainingAlertTime = initialAlertTime; detectedWeight = old switch { AI_Combat=>0.5f, _ => 0 }; } public override void OnStateUpdate(float deltaTime) { base.OnStateUpdate(deltaTime); if (detectedEnemies.Count is 0) { detectedWeight -= deltaTime; } else { foreach (var entity in detectedEnemies) { var distance= Vector3.Distance(Service.transform.position, entity.As().transform.position); if(distance<=immediatelyDetectDistance) { Service.CombatTarget = entity; Service.TransitionState(); return; } detectedWeight += deltaTime; if (detectedWeight >= 1) { Service.CombatTarget = entity; Service.TransitionState(); return; } } } RemainingAlertTime -= deltaTime; if(RemainingAlertTime<=0) { Service.TransitionState(); return; } } } [Serializable] [CustomType(typeof(AI_Idle))] public sealed class Combat : AIRuntimeState, AI_Combat { public AITarget CurrentTarget => _currentTarget; private AITarget _currentTarget; [SerializeField] private float lostTargetTime; private readonly IntervalUpdate reTargetInterval = new(2); [SerializeField,ReadOnly]private float currentLostTargetTime; public override void OnStateEntry(IState old) { base.OnStateEntry(old); currentLostTargetTime = lostTargetTime; reTargetInterval.Reset(); Service.CombatTarget.TryGetComponent(out _currentTarget); Service.Agent.isStopped = false; } public override void OnStateExit(IState old, IState newState) { base.OnStateExit(old, newState); Service.RangeSensor.transform.localRotation = Quaternion.identity; } public override void OnStateUpdate(float deltaTime) { base.OnStateUpdate(deltaTime); Service.RangeSensor.transform.rotation = Service.transform.rotation; if (reTargetInterval.AllowUpdate) { Service.CombatTarget = null; } if (Service.CombatTarget is not null) { var position = Service.CombatTarget.As().transform.position; if (NavMesh.SamplePosition(position, out var hit, 1, NavMesh.AllAreas)) { Service.Agent.SetDestination(hit.position); } } foreach (var entity in detectedEnemies) { currentLostTargetTime = lostTargetTime; Service.CombatTarget = entity; Service.CombatTarget.TryGetComponent(out _currentTarget); return; } currentLostTargetTime-=deltaTime; if(currentLostTargetTime<=0) { Service.TransitionState(); } } } }