334 lines
11 KiB
C#
334 lines
11 KiB
C#
using System.Collections.Generic;
|
|
using cfg.AICfg;
|
|
using cfg.SkillCfg;
|
|
using Framework;
|
|
using Gameplay.Area;
|
|
using Gameplay.Character;
|
|
using Gameplay.Level;
|
|
using Gameplay.Skill;
|
|
using UnityEngine;
|
|
namespace Gameplay.Unit.AI
|
|
{
|
|
public class UnitAIManager
|
|
{
|
|
public GameUnit owner;
|
|
|
|
public DataAISkillControll rawConfig;
|
|
|
|
private SkillHandle _skillHandle;
|
|
private float _remainCheckTime;
|
|
private int _checkDistance;
|
|
|
|
private bool _requireSort;
|
|
|
|
private int _prelockCellIndex;
|
|
|
|
private List<GameUnit> _preSelectUnits;
|
|
|
|
public UnitAIManager(GameUnit owner)
|
|
{
|
|
this.owner = owner;
|
|
_preSelectUnits = new List<GameUnit>();
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
_Init();
|
|
}
|
|
|
|
private void _LoadRawConfig()
|
|
{
|
|
var skillUnitManager = SkillManager.instance.GetUnitSkillManager(owner);
|
|
if (skillUnitManager == null)
|
|
{
|
|
return;
|
|
}
|
|
var pSkill = skillUnitManager.positiveSkill;
|
|
if (pSkill == null)
|
|
{
|
|
return;
|
|
}
|
|
var skillConfig = pSkill.runtimeData.configData;
|
|
if (skillConfig == null)
|
|
{
|
|
return;
|
|
}
|
|
var aiControlId = skillConfig.AIControllId;
|
|
if (aiControlId == 0)
|
|
{
|
|
return;
|
|
}
|
|
rawConfig = TableManager.Instance.Tables.AISkillControlCfg.Get(aiControlId);
|
|
_skillHandle = pSkill;
|
|
}
|
|
|
|
private void _Init()
|
|
{
|
|
_LoadRawConfig();
|
|
if (rawConfig == null)
|
|
{
|
|
return;
|
|
}
|
|
_InitRuntimeData();
|
|
}
|
|
|
|
private void _InitRuntimeData()
|
|
{
|
|
_checkDistance = rawConfig.CheckDistance;
|
|
if (_checkDistance == -1)
|
|
{
|
|
_checkDistance = _skillHandle.runtimeData.configData.ReleaseArea;
|
|
}
|
|
_requireSort = rawConfig.HpCheck != HPCheckType.None;
|
|
}
|
|
|
|
public void LogicUpdate(float dt)
|
|
{
|
|
if (rawConfig == null)
|
|
{
|
|
return;
|
|
}
|
|
// 全局跳帧
|
|
if (SkillManager.instance.inSkillingUnitAI != null)
|
|
{
|
|
return;
|
|
}
|
|
if (_skillHandle.runtimeData.remainCDTime > 0) return;
|
|
// 逃跑过程中不释放技能
|
|
if (_skillHandle.owner.statusData.isRetreat) return;
|
|
// 死亡不释放技能
|
|
if (_skillHandle.owner.statusData.isDead) return;
|
|
// 检测是否在战斗中
|
|
var inFight = (LevelManager.Instance.CurrentLevel.LevelTime - owner.commonData.lastInFightSeconds) > 3;
|
|
if (rawConfig.OnlyReleaseInAttack && inFight) return;
|
|
_remainCheckTime -= dt;
|
|
if (_remainCheckTime > 0)
|
|
{
|
|
return;
|
|
}
|
|
_remainCheckTime = rawConfig.CheckTime;
|
|
var canRelease = _CheckLogic();
|
|
if (canRelease)
|
|
{
|
|
if (owner.commonData.troopId != ETroopsId.Self)
|
|
{
|
|
// 非己方单位,需要记录到AI释放列表中,防止多个AI一起释放技能
|
|
SkillManager.instance.inSkillingUnitAI = owner;
|
|
}
|
|
_UseSkill();
|
|
}
|
|
}
|
|
|
|
private void _UseSkill()
|
|
{
|
|
switch (rawConfig.AIReleasePos)
|
|
{
|
|
case EAIReleasePosType.Self:
|
|
// 对自己释放
|
|
_skillHandle.Use(owner.transData.cellIndex);
|
|
break;
|
|
case EAIReleasePosType.TargetCell:
|
|
// 对目标释放
|
|
_skillHandle.Use(_prelockCellIndex);
|
|
break;
|
|
case EAIReleasePosType.AroundTargetCellEmpty:
|
|
// 对目标周围空位释放
|
|
_ReleaseToAroundEmpty();
|
|
break;
|
|
default:
|
|
DebugUtil.LogError("暂未实现的AI释放位置类型:{0}", rawConfig.AIReleasePos);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void _ReleaseToAroundEmpty()
|
|
{
|
|
var aroundCellIndexs = AreaManager.instance.GetCellIndexsAround(_prelockCellIndex, 1);
|
|
for (int i = 0; i < aroundCellIndexs.Count; i++)
|
|
{
|
|
var cellIndex = aroundCellIndexs[i];
|
|
if (AreaManager.instance.IsCellEmpty(cellIndex))
|
|
{
|
|
_skillHandle.Use(cellIndex);
|
|
return;
|
|
}
|
|
}
|
|
DebugUtil.LogError("周围没有空位:" + _prelockCellIndex);
|
|
}
|
|
|
|
private bool _CheckLogic()
|
|
{
|
|
var checkTargetType = rawConfig.CheckTargetType;
|
|
switch (checkTargetType)
|
|
{
|
|
case ETargetType.AnyCell:
|
|
// 默认锁定自己
|
|
_prelockCellIndex = owner.transData.cellIndex;
|
|
return true;
|
|
case ETargetType.SelfSide:
|
|
return _CheckSelfSide();
|
|
case ETargetType.EnemySide:
|
|
return _CheckEnemySide();
|
|
}
|
|
DebugUtil.LogError("暂未实现的AI逻辑检测类型:{0}", checkTargetType);
|
|
return false;
|
|
}
|
|
|
|
private bool _CheckEnemySide()
|
|
{
|
|
_preSelectUnits.Clear();
|
|
var enemyTroopId = owner.commonData.troopId == ETroopsId.Self ? ETroopsId.Enemy1 : ETroopsId.Self;
|
|
var allEnemySideUnits = GameUnitManager.instance.infightUnits.GetListByTroop(enemyTroopId);
|
|
for (int i = 0; i < allEnemySideUnits.Count; i++)
|
|
{
|
|
var unit = allEnemySideUnits[i];
|
|
if (unit == owner) continue;
|
|
if (rawConfig.RequireTargetAlive && unit.statusData.isDead) continue;
|
|
var distance = AreaManager.instance.GetDistance(unit.transData.cellIndex, owner.transData.cellIndex);
|
|
if (distance < _checkDistance)
|
|
{
|
|
// 满足需求
|
|
if (_requireSort)
|
|
{
|
|
_preSelectUnits.Add(unit);
|
|
}
|
|
else
|
|
{
|
|
// 直接使用这个
|
|
_prelockCellIndex = unit.transData.cellIndex;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 第二次筛选
|
|
if (_preSelectUnits.Count > 0)
|
|
{
|
|
return _ReSelectUnits();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool _CheckSelfSide()
|
|
{
|
|
_preSelectUnits.Clear();
|
|
var selfTroopId = owner.commonData.troopId;
|
|
var allSelfSideUnits = GameUnitManager.instance.infightUnits.GetListByTroop(selfTroopId);
|
|
for (int i = 0; i < allSelfSideUnits.Count; i++)
|
|
{
|
|
var unit = allSelfSideUnits[i];
|
|
if (unit == owner) continue;
|
|
if (rawConfig.RequireTargetAlive && unit.statusData.isDead) continue;
|
|
var distance = AreaManager.instance.GetDistance(unit.transData.cellIndex, owner.transData.cellIndex);
|
|
if (distance < _checkDistance)
|
|
{
|
|
// 满足需求
|
|
if (_requireSort)
|
|
{
|
|
_preSelectUnits.Add(unit);
|
|
}
|
|
else
|
|
{
|
|
// 直接使用这个
|
|
_prelockCellIndex = unit.transData.cellIndex;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 第二次筛选
|
|
if (_preSelectUnits.Count > 0)
|
|
{
|
|
return _ReSelectUnits();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool _ReSelectUnits()
|
|
{
|
|
switch (rawConfig.HpCheck)
|
|
{
|
|
case HPCheckType.HpPercentMax:
|
|
_SelectHPPercentMax();
|
|
break;
|
|
case HPCheckType.HpPercentMin:
|
|
_SelectHPPercentMin();
|
|
break;
|
|
case HPCheckType.HpPercentLowerThan:
|
|
return _SelectMatchLowerThan();
|
|
default:
|
|
DebugUtil.LogError("暂未实现的AI血量检测类型:{0}", rawConfig.HpCheck);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool _SelectMatchLowerThan()
|
|
{
|
|
var lowerThan = _TryGetFloatParam(0);
|
|
for (int i = 0; i < _preSelectUnits.Count; i++)
|
|
{
|
|
var unit = _preSelectUnits[i];
|
|
var percent = unit.aliveData.currentHp / (unit.aliveData.maxHp * 1f);
|
|
if (percent < lowerThan)
|
|
{
|
|
_prelockCellIndex = unit.transData.cellIndex;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void _SelectHPPercentMin()
|
|
{
|
|
var minUnit = _preSelectUnits[0];
|
|
var minPercent = minUnit.aliveData.currentHp / (minUnit.aliveData.maxHp * 1f);
|
|
for (int i = 1; i < _preSelectUnits.Count; i++)
|
|
{
|
|
var unit = _preSelectUnits[i];
|
|
var percent = unit.aliveData.currentHp / (unit.aliveData.maxHp * 1f);
|
|
if (percent < minPercent)
|
|
{
|
|
minPercent = percent;
|
|
minUnit = unit;
|
|
}
|
|
}
|
|
_prelockCellIndex = minUnit.transData.cellIndex;
|
|
}
|
|
|
|
private void _SelectHPPercentMax()
|
|
{
|
|
var maxUnit = _preSelectUnits[0];
|
|
var maxPercent = maxUnit.aliveData.currentHp / (maxUnit.aliveData.maxHp * 1f);
|
|
for (int i = 1; i < _preSelectUnits.Count; i++)
|
|
{
|
|
var unit = _preSelectUnits[i];
|
|
var percent = unit.aliveData.currentHp / (unit.aliveData.maxHp * 1f);
|
|
if (percent > maxPercent)
|
|
{
|
|
maxPercent = percent;
|
|
maxUnit = unit;
|
|
}
|
|
}
|
|
_prelockCellIndex = maxUnit.transData.cellIndex;
|
|
}
|
|
|
|
private float _TryGetFloatParam(int index)
|
|
{
|
|
if (rawConfig.FloatParams.Count <= index)
|
|
{
|
|
DebugUtil.LogError("AI参数不足: " + rawConfig.Id + " " + index);
|
|
return 0;
|
|
}
|
|
return rawConfig.FloatParams[index];
|
|
}
|
|
|
|
private int _TryGetIntParam(int index)
|
|
{
|
|
var floatVal = _TryGetFloatParam(index);
|
|
return Mathf.RoundToInt(floatVal);
|
|
}
|
|
|
|
}
|
|
}
|