using System; using Framework; using Gameplay.Area; using Gameplay.Unit; using Gameplay.Level; using Gameplay.Unit.Data; using System.Collections.Generic; using static Gameplay.Level.LevelData; namespace Gameplay.AI { public static class AIUtils { private static List inSightEnemies = new List(16); private static Dictionary inSightDistanceList = new Dictionary(16); private static List enemies = new List(16); private static List inAttackEnemies = new List(16); private static List allFriends = new List(16); private static List friendsInSight = new List(16); private static Dictionary patrolDistanceList = new Dictionary(64); /// /// 寻找锁敌范围内最近的敌人 /// public static GameUnit FindNearestEnemyInSightDistance(GameUnit owner) { try { if (owner == null) return null; var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return null; inSightEnemies.Clear(); inSightDistanceList.Clear(); GameUnitManager.instance.characterManager.SearchEnemies(owner, inSightEnemies); var ownerCellIndex = owner.transData?.cellIndex; for (var i = 0; i < inSightEnemies.Count; i++) { var enemy = inSightEnemies[i]; if (enemy == null || enemy.statusData.isDead) { continue; } var enemyCellIndex = enemy.transData?.cellIndex; if (!ownerCellIndex.HasValue || !enemyCellIndex.HasValue) { continue; } var distance = MapUtils.Distance(map, ownerCellIndex.Value, enemyCellIndex.Value); if (distance <= owner.fightData.findEnemyRange && owner.statusData.CheckCanBeTarget(enemy, distance)) { inSightDistanceList.Add(enemy, distance); } } if (inSightDistanceList.Count <= 0) { return null; } else { var minDistance = float.MaxValue; GameUnit nearestEnemy = null; foreach (var pair in inSightDistanceList) { if (minDistance > pair.Value) { minDistance = pair.Value; nearestEnemy = pair.Key; } } return nearestEnemy; } } catch (Exception e) { DebugUtil.LogError("AIUtils.FindNearestEnemyInSightDistance error: {0}", e); return null; } } /// /// 寻找攻击范围内最近的敌人 /// public static GameUnit FindNearestEnemyInShootRange(GameUnit owner) { try { if (owner == null) return null; var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return null; inSightEnemies.Clear(); inSightDistanceList.Clear(); GameUnitManager.instance.characterManager.SearchEnemies(owner, inSightEnemies); var ownerCellIndex = owner.transData?.cellIndex; for (var i = 0; i < inSightEnemies.Count; i++) { var enemy = inSightEnemies[i]; if (enemy == null) { continue; } var enemyCellIndex = enemy.transData?.cellIndex; if (!ownerCellIndex.HasValue || !enemyCellIndex.HasValue) { continue; } var distance = MapUtils.Distance(map, ownerCellIndex.Value, enemyCellIndex.Value); if (distance <= owner.fightData.normalAttackDistance) { inSightDistanceList.Add(enemy, distance); } } if (inSightDistanceList.Count <= 0) { return null; } else { owner.statusData.isAttackActive = true; var minDistance = float.MaxValue; GameUnit nearestEnemy = null; foreach (var pair in inSightDistanceList) { if (minDistance > pair.Value) { minDistance = pair.Value; nearestEnemy = pair.Key; } } return nearestEnemy; } } catch (Exception e) { DebugUtil.LogError("AIUtils.FindNearestEnemyInShootRange error: {0}", e); return null; } } /// /// 寻找锁敌范围内的友军 /// public static List FindFriendsInSightDistance(GameUnit owner) { try { if (owner == null) return null; var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return null; allFriends.Clear(); friendsInSight.Clear(); GameUnitManager.instance.characterManager.SearchFriends(owner, allFriends); var ownerCellIndex = owner.transData?.cellIndex; for (var i = 0; i < allFriends.Count; i++) { var friend = allFriends[i]; if (friend == null || friend.GetID() == owner.GetID()) { continue; } var friendCellIndex = friend.transData?.cellIndex; if (!ownerCellIndex.HasValue || !friendCellIndex.HasValue) { continue; } var distance = MapUtils.Distance(map, ownerCellIndex.Value, friendCellIndex.Value); if (distance <= owner.fightData.findEnemyRange) { friendsInSight.Add(friend); } } return friendsInSight; } catch (Exception e) { DebugUtil.LogError("AIUtils.FindFriendsInSightDistance error: {0}", e); return null; } } /// /// 寻找锁敌范围内的敌方npc /// private static Dictionary FindEnemyNpcInSight(GameUnit owner) { try { var enemyNpc = new Dictionary(); var level = LevelManager.Instance.CurrentLevel; if (level == null) return enemyNpc; var map = LevelManager.Instance.CurrentLevel.Map; if (map == null || level.SelfNpcLocalID == null || level.SelfNpcLocalID.Count <= 0) return enemyNpc; var enemyList = new List(); GameUnitManager.instance.characterManager.SearchEnemies(owner, enemyList); foreach (var enemy in enemyList) { if (enemy.unitLevelData == null) continue; if (level.SelfNpcLocalID.Contains(enemy.unitLevelData.LocalId)) { var distance = MapUtils.Distance(map, enemy.transData.cellIndex, owner.transData.cellIndex); if (distance <= owner.fightData.findEnemyRange) enemyNpc.Add(enemy, distance); } } return enemyNpc; } catch (Exception e) { DebugUtil.LogError("AIUtils.FindEnemyNpcInSight error: {0}", e); return null; } } /// /// 寻找范围内最近的敌方npc /// public static GameUnit FindNearestEnemyNpcInShootRange(GameUnit owner) { try { var enemyNpc = FindEnemyNpcInSight(owner); if (enemyNpc == null || enemyNpc.Count <= 0) return null; GameUnit nearestEnemy = null; var minDistance = float.MaxValue; foreach (var pair in enemyNpc) { if (minDistance > pair.Value) { minDistance = pair.Value; nearestEnemy = pair.Key; } } return nearestEnemy; } catch (Exception e) { DebugUtil.LogError("AIUtils.FindNearestEnemyNpcInShootRange error: {0}", e); return null; } } /// /// 获取距离自己最近的单位 /// public static GameUnit GetNearestGameUnit(GameUnit owner, GameUnit newUnit, GameUnit unit) { try { if (owner == null || newUnit == null || unit == null) return null; var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return null; var ownerCellIndex = owner.transData?.cellIndex; var unitCellIndex = unit.transData?.cellIndex; var newUnitCellIndex = newUnit.transData?.cellIndex; if (!ownerCellIndex.HasValue || !unitCellIndex.HasValue || !newUnitCellIndex.HasValue) return null; var distance = MapUtils.Distance(map, ownerCellIndex.Value, unitCellIndex.Value); var newDistance = MapUtils.Distance(map, ownerCellIndex.Value, newUnitCellIndex.Value); return distance <= newDistance ? unit : newUnit; } catch (Exception e) { DebugUtil.LogError("AIUtils.GetNearestGameUnit error: {0}", e); // 异常返回默认单位 return unit; } } /// /// 寻找最近的巡逻点位 /// public static int FindNearestPointInPatrolInfo(GameUnit owner, List patrolInfos) { try { if (owner == null) return Constants.INVALID_ID; var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return Constants.INVALID_ID; patrolDistanceList.Clear(); var ownerCellIndex = owner.transData?.cellIndex; for (var i = 0; i < patrolInfos.Count; i++) { var point = map.BlockPosition2TGSCellIndex(patrolInfos[i].patrolPoint); if (!ownerCellIndex.HasValue || point == Constants.INVALID_ID) { continue; } patrolDistanceList.Add(i, MapUtils.Distance(map, ownerCellIndex.Value, point)); } var minDistance = int.MaxValue; int nearestPoint = 0; foreach (var pair in patrolDistanceList) { if (minDistance > pair.Value) { minDistance = pair.Value; nearestPoint = pair.Key; } } return nearestPoint; } catch (Exception e) { DebugUtil.LogError("AIUtils.FindNearestPointInPatrolInfo error: {0}", e); // 异常返回第一个索引点 return 0; } } /// /// 巡逻点位转换为格子索引 /// public static int GetPatrolPointIndexFromVector2Int(Map map, int patrolIndex, List patrolInfos) { try { if (map == null || patrolIndex >= patrolInfos.Count) return -1; var patrolInfo = patrolInfos[patrolIndex]; if (patrolInfo == null) return -1; return map.BlockPosition2TGSCellIndex(patrolInfo.patrolPoint); } catch (Exception e) { DebugUtil.LogError("AIUtils.GetPatrolPointIndexFromVector2Int error: {0}", e); // 异常返回无效格子索引 return -1; } } public static bool IsNeedStop(GameUnit owner) { return owner.statusData.isOnVehicle || owner.statusData.isChaos || owner.statusData.isStun || owner.statusData.isLockPos || owner.statusData.isRetreat || owner.statusData.isDead; } public static bool AINeedStopUpdate(Level.Level level) { if (level == null) return false; return level.IsPreparing() || level.IsEnd() || Math.Abs(level.GetSpeed() - 0f) < 0.01f; } /// /// 激活AI攻击 /// public static void ActiveAIAttack(GameUnit owner) { try { if (owner == null) return; if (owner.statusData.isAttackActive) return; var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return; if (owner.controlData.targetEnemy != null) { owner.statusData.isAttackActive = true; } else { var target = FindNearestEnemyInShootRange(owner); if (target != null) { owner.controlData.targetEnemy = target; owner.statusData.isAttackActive = true; } } } catch (Exception e) { DebugUtil.LogError("AIUtils.ActiveAIAttack error: {0}", e); } } /// /// 单位移动 /// public static bool MoveTo(GameUnit owner, int cellIndex, bool cancelFollow = true) { try { if (owner == null || cellIndex == -1) return false; var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return false; var mapData = map.MapData; if (mapData == null) return false; var blockData = mapData.GetBlockData(cellIndex); if (blockData == null) return false; var typeProperty = blockData.GetTerrainTypeProperty(); if (typeProperty == null || !typeProperty.selectable) return false; owner.MoveTo(cellIndex, cancelFollow); return true; } catch (Exception e) { DebugUtil.LogError("AIUtils.MoveTo error: {0}", e); return false; } } /// /// 单位撤退 /// /// public static bool MoveAway(GameUnit owner, GameUnit enemyUnit, bool cancelFollow = true) { try { if (enemyUnit == null) return false; var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return false; var mapData = map.MapData; if (mapData == null) return false; map.WorldPosition2BlockPosition(enemyUnit.Node.GetPosition(), out var targetEnemy2IntPos); var self2IntPos = map.TGSCellIndex2BlockPosition(owner.transData.cellIndex); var newSelf2IntPos = self2IntPos; newSelf2IntPos.x = Math.Clamp( (self2IntPos.x - targetEnemy2IntPos.x) > 0 ? self2IntPos.x + 1 : self2IntPos.x - 1, 0, map.MapData.Height - 1); newSelf2IntPos.y = Math.Clamp( (self2IntPos.y - targetEnemy2IntPos.y) > 0 ? self2IntPos.y + 1 : self2IntPos.y - 1, 0, map.MapData.Width - 1); if (self2IntPos == newSelf2IntPos) return false; var cellIndex = map.BlockPosition2TGSCellIndex(newSelf2IntPos); if (PathFinder.CanPass(cellIndex, owner)) { owner.MoveTo(cellIndex, cancelFollow); return true; } return false; } catch (Exception e) { DebugUtil.LogError("AIUtils.MoveAway error: {0}", e); return false; } } /// /// AI 移动向敌人并检查 /// public static void CheckAndMove(GameUnit owner, int cellIndex, bool cancelFollow = true) { try { if (!MoveTo(owner, cellIndex, cancelFollow)) return; if (owner.gameunitType != EGameUnitType.Character && owner.gameunitType != EGameUnitType.Vehicle) return; var units = AreaManager.instance.GetUnitsByCellIndex(cellIndex); for (int i = 0; i < units.Count; i++) { var unit = units[i]; if (unit.commonData.troopId == owner.commonData.troopId) { /*if (owner.gameunitType == EGameUnitType.Vehicle) { // 载具不能上载具 return; } // 点击的己方单位, 这里必然是载具 var vehicle = unit as NormalVehicle; if (vehicle == null) { DebugUtil.LogError("不是载具"); return; } if (vehicle.CheckCanUp(owner)) { owner.controlData.markVehicle = vehicle; } return;*/ } else { // 如果是敌人, 则设置敌人目标 if (unit.statusData.CheckCanBeTarget(owner, -1)) { owner.controlData.markEnemy = unit; owner.controlData.forceUpdateTarget = true; return; } } } // 走到这里,说明没有目标,清空标记 owner.controlData.markVehicle = null; owner.controlData.markEnemy = null; } catch (Exception e) { DebugUtil.LogError("AIUtils.CheckAndMove error: {0}", e); } } /// /// 普通撤退AI 特殊处理 避让角色 /// public static void AvoidEnemyTarget(GameUnit owner, EventDataStartMove data) { try { if (!owner.statusData.isMoveing || data == null) return; if (data.sender == null || data.sender.commonData.troopId == owner.commonData.troopId) return; if (data.targetCellIndex == owner.controlData.targetCellIndex) { DebugUtil.Log($"AI[{owner.GetID()}] 需要再次逃离"); AvoidEnemy(owner, data.targetCellIndex); } } catch (Exception e) { DebugUtil.LogError("AIUtils.AvoidEnemyTarget error: {0}", e); } } /// /// 判断是否有敌人重合 /// public static void CheckAreaIsOtherEnemyUnit(GameUnit owner) { if (owner.statusData.isMoveing) return; var cellIndex = owner.transData.cellIndex; var units = AreaManager.instance.GetUnitsByCellIndex(cellIndex); foreach (var unit in units) { if (unit.GetID() != owner.GetID()) AvoidEnemy(owner, owner.transData.cellIndex); } } private static void AvoidEnemy(GameUnit owner, int cellIndex) { try { var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return; var height = map.MapData.Height; var width = map.MapData.Width; var target = cellIndex; var newArea = new List() { -1, width, width + 1, 1, 1 - width, -width }; foreach (var value in newArea) { var sum = target + value; /*var blockData = map.GetBlockData(sum); if (blockData == null) continue; var typeProperty = blockData.GetTerrainTypeProperty(); if (typeProperty == null) continue;*/ if (sum >= 0 && sum < height * width /*&& typeProperty.selectable*/ && PathFinder.CanPass(sum, owner)) { DebugUtil.Log($"AI[{owner.GetID()}] 向周围移动"); owner.MoveTo(sum, true); return; } } // 无处可去 DebugUtil.Log($"AI[{owner.GetID()}] 需要回到出生点"); owner.MoveTo(owner.transData.bornCellIndex, true); } catch (Exception e) { DebugUtil.LogError("AIUtils.AvoidEnemy error: {0}", e); } } /// /// 计算当前位置与出生点位置是否超过防守距离 /// public static bool CheckDistanceIsOver(GameUnit owner) { try { if (owner == null) return true; var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return true; var curPos = owner.transData.cellIndex; var bornPos = map.BlockPosition2TGSCellIndex(owner.unitLevelData.BornPosition); var distance = MapUtils.Distance(map, curPos, bornPos); return owner.unitLevelData.DefensiveDistance < distance; } catch (Exception e) { DebugUtil.LogError("AIUtils.CheckDistanceIsOver error: {0}", e); // 默认超出 return true; } } /// /// 顺时针获取周围能够到达的一圈格子 /// private static List GetOneCycleCellIndex(Map map, GameUnit owner, int cellIndex) { if (map == null) return null; var height = map.MapData.Height; var width = map.MapData.Width; var target = cellIndex; var newArea = new List() { -1, width, width + 1, 1, 1 - width, -width }; var cellIndexList = new List(); foreach (var value in newArea) { var sum = target + value; if (sum >= height * width) continue; var blockData = map.GetBlockData(sum); if (blockData == null) continue; var typeProperty = blockData.GetTerrainTypeProperty(); if (typeProperty == null) continue; if (sum >= 0 && sum < height * width && typeProperty.selectable && PathFinder.CanPass(sum, owner)) { cellIndexList.Add(sum); } } return cellIndexList; } /// /// 检查该点是否能到达,不能则返回周围一圈能到达的点 /// public static int CheckAndGetCanPassOneCycleCellIndex(Map map, GameUnit owner, int cellIndex) { var canPass = MapUtils.GetTypeProperty(map, cellIndex) != null && MapUtils.GetTypeProperty(map, cellIndex).selectable; if (canPass && PathFinder.CanPass(cellIndex, owner)) { return cellIndex; } var cells = GetOneCycleCellIndex(map, owner, cellIndex); if (cells == null || cells.Count <= 0) return -1; return cells[0]; } } }