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.Sensors; 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 HashSet ignoreNoises = new(); protected readonly CacheList detectedEnemies = new(); protected readonly CacheList hearEnemies = new(); public bool IsFriend(IEntity target) { return _friends.GetOrAdd(target.Id, IsFriendInternal); } public bool IsEnemy(IEntity target) { return _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) { foreach (var _collider in entity.As().GetComponents()) { Service.RangeSensor.Ignores.Add(_collider.GetInstanceID()); ; } continue; } detectedEnemies.Add(entity); } hearEnemies.Clear(); foreach (var x in Service.AudioSensor.Get()) { if (x.TryGetComponent(out var entity) is false) continue; if (IsEnemy(entity) is false) { foreach (var _collider in entity.As().GetComponents()) { Service.AudioSensor.Ignores.Add(_collider.GetInstanceID()); } continue; } hearEnemies.Add(entity); } } private bool IsEnemyInternal(int 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(int 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); } 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(); return; } foreach (var entity in hearEnemies) { Service.CombatTarget = entity; Service.TransitionState(); return; } foreach (var x in Service.AudioSensor.Noises) { if(ignoreNoises.Add(x.Id) is false)continue; var distance = Vector3.Distance(Service.transform.position, x.Position); //Debug.Log($"{x.Radius}:{distance}"); if (distance <= x.Radius && NavMesh.SamplePosition(x.Position,out var hit,1,NavMesh.AllAreas)) { Service.Agent.SetDestination(hit.position); } return; } } } [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) { foreach (var entity in hearEnemies) { Service.Agent.SetDestination(entity.As().transform.position); return; } detectedWeight -= deltaTime; } else { foreach (var entity in detectedEnemies) { var distance= Vector3.Distance(Service.transform.position, entity.As().transform.position); entity.TryGetComponent(out var sensorTarget); if(distance<=immediatelyDetectDistance) { Service.CombatTarget = entity; Service.TransitionState(); sensorTarget?.Detected(1,Service.RangeSensor,Service.transform); return; } detectedWeight += deltaTime; sensorTarget?.Detected(detectedWeight,Service.RangeSensor,Service.transform); 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); Service.RangeSensor.DetectedHeightWeight = 1; 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.DetectedHeightWeight = 0.5f; Service.RangeSensor.transform.localRotation = Quaternion.identity; } public override void OnStateUpdate(float deltaTime) { base.OnStateUpdate(deltaTime); var position = Service.CombatTarget.As().transform.position; var direction = position - Service.transform.position; direction = Vector3.ProjectOnPlane(direction, Vector3.up); if (direction.magnitude > 0.01) { Service.RangeSensor.transform.rotation = Quaternion.LookRotation(direction); } if (NavMesh.SamplePosition(position, out var hit, 1, NavMesh.AllAreas)) { Service.Agent.SetDestination(hit.position); } foreach (var entity in detectedEnemies) { currentLostTargetTime = lostTargetTime; if (reTargetInterval.AllowUpdate) { Service.CombatTarget = entity; Service.CombatTarget.TryGetComponent(out _currentTarget); } return; } foreach (var entity in hearEnemies) { currentLostTargetTime = lostTargetTime; } currentLostTargetTime -= deltaTime; if (currentLostTargetTime <= 0) { Service.TransitionState(); } } } }