744 lines
32 KiB
C#
744 lines
32 KiB
C#
/********************************************************************************
|
||
* 文件名: AI_PlayCombat.cs
|
||
* 全路径: \Script\Player\AI\AI_PlayCombat.cs
|
||
* 创建时间:2013-12-4
|
||
*
|
||
* 功能说明:主角战斗AI
|
||
* 修改记录:
|
||
*********************************************************************************/
|
||
|
||
using System.Collections.Generic;
|
||
using Games.GlobeDefine;
|
||
using Games.ImpactModle;
|
||
using Games.LogicObj;
|
||
using Games.Mission;
|
||
using Games.SkillModle;
|
||
using GCGame.Table;
|
||
using Module.Log;
|
||
using UnityEngine;
|
||
|
||
namespace Games.AI_Logic
|
||
{
|
||
public class AI_PlayerCombat : AI_BaseCombat
|
||
{
|
||
// 挂机技能延迟时间,防止操作过快
|
||
private const float _autoSkillDelay = 0.05f;
|
||
|
||
// 挂机最小接近距离
|
||
public const float minCloseInDistance = 0.5f;
|
||
|
||
// 跟随NPC开始距离
|
||
private const float _escortDistTolerance = 2f;
|
||
|
||
// 进入地图锁定时间
|
||
private const float _startLockTime = 2f;
|
||
|
||
// 无法索敌发送协议时间
|
||
private const float _requestEnemyInterval = 0.3f;
|
||
|
||
// 最小设置移动到目标时间间隔
|
||
private const float _minMoveResetTime = 1f;
|
||
|
||
// 使用技能失败后的锁定时间
|
||
private const float _skillFailLockTime = 0.5f;
|
||
|
||
//记录杀怪任务有哪些怪可杀
|
||
private readonly List<int> _killMonsters = new List<int>();
|
||
|
||
// 记录哪些技能出错后,需要锁定一段时间
|
||
private readonly List<MyTuple<int, float>> _skillFailList = new List<MyTuple<int, float>>();
|
||
|
||
// 上次释放的技能 - 防止单个技能出错,锁死自动战斗流程
|
||
private Tab_SkillBase _lastSkillBase;
|
||
|
||
// 下次设置移动目标的时间
|
||
private float _nextMoveToTargetTime;
|
||
|
||
// 下次发送需求敌人位置协议的时间
|
||
private float _nextRequestEnemyTime;
|
||
|
||
// 下次设置普攻的时间
|
||
private float _nextSimpleAttackTime;
|
||
|
||
// 下次设置技能的时间
|
||
private float _nextSkillTime;
|
||
|
||
// 主角引用
|
||
private Obj_MainPlayer _player;
|
||
|
||
private float _unlockTime = float.PositiveInfinity;
|
||
|
||
private void Start()
|
||
{
|
||
//装载AI到AIController,进行统一管理
|
||
LoadAI();
|
||
_player = gameObject.GetComponent<Obj_MainPlayer>();
|
||
if (_player != null && _player.Controller != null)
|
||
{
|
||
//如果已经设定了自动挂机 则开启自动挂机
|
||
if (_player.isAutoCombat)
|
||
{
|
||
if (GameManager.gameManager.ActiveScene.IsCopyScene() == false)
|
||
_player.BreakAutoCombatState();
|
||
_player.Controller.EnterCombat();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
var errorType = _player == null ? "Obj_MainPlayer" : "AIController";
|
||
LogModule.ErrorLog(string.Format("AI_PlayerCombat初始化失败,没有找到{0},也无法自修复!", errorType));
|
||
}
|
||
}
|
||
|
||
public void LockAction()
|
||
{
|
||
_unlockTime = Time.unscaledTime + _startLockTime;
|
||
}
|
||
|
||
//激活AI
|
||
public override void OnActive()
|
||
{
|
||
base.OnActive();
|
||
RefreshKillMonsterId();
|
||
UpdateAI();
|
||
}
|
||
|
||
public override void OnDeactive()
|
||
{
|
||
base.OnDeactive();
|
||
_killMonsters.Clear();
|
||
}
|
||
|
||
// 基本构想 - 每次释放技能后,AI等待一个最小可接技能的时间,然后再试图扫描新的状态;
|
||
public override void UpdateAI()
|
||
{
|
||
base.UpdateAI();
|
||
if (IsCanAuto())
|
||
if (_player.IsDisableState(DISABLESTATE.Disable_UseSkill) ||
|
||
_player.IsDisableState(DISABLESTATE.Disable_Select))
|
||
{
|
||
}
|
||
// 如果正在使用自动连续技能,暂时不修改当前状态
|
||
else if (_player.IsPredictSkill &&
|
||
_player.PredictSkillBase.SkillClass.ContainFlag((int) SKILLCLASS.AUTOREPEAT) ||
|
||
_player.IsUsingSkill &&
|
||
_player.SkillCore.CurrentSkillBase.SkillClass.ContainFlag((int) SKILLCLASS.AUTOREPEAT))
|
||
{
|
||
}
|
||
// 如果预测处于自动战斗技能使用状态,则跳过这一帧
|
||
else if (Time.time < _nextSkillTime)
|
||
{
|
||
}
|
||
else
|
||
{
|
||
// 2019.01.31 自动战斗流程改成行为树流程,放弃之前不易维护的运算量优化
|
||
// 每一帧对可用技能执行排序,然后依次对目标监测当前技能
|
||
// 每一帧对可用目标执行排序,然后依次监测最适合目标
|
||
// 获得可用技能列表
|
||
var skillList = new List<AutoCombatSkill>();
|
||
var skillBar = SkillBarLogic.Instance();
|
||
if (skillBar != null)
|
||
for (var i = 0; i < skillBar.SkillButtons.Length; i++)
|
||
{
|
||
var skillButton = skillBar.SkillButtons[i];
|
||
if (skillButton.SkillIndex >= 0 &&
|
||
skillButton.SkillIndex < _player.OwnSkillInfo.Length)
|
||
{
|
||
var ownSkill = _player.OwnSkillInfo[skillButton.SkillIndex];
|
||
if (ValidOwnSkill(ownSkill) &&
|
||
// 不允许连续使用同一个技能 - 防止自动战斗因为单个技能故障锁死
|
||
ownSkill.ComboBaseTable != _lastSkillBase)
|
||
{
|
||
var actionMask = GetSkillActionPriority(ownSkill);
|
||
skillList.Add(new AutoCombatSkill(ownSkill, actionMask));
|
||
}
|
||
}
|
||
}
|
||
skillList.Sort(SkillComparison);
|
||
// 获得可能目标列表
|
||
var resurrectTargets = new List<AutoCombatTarget>();
|
||
var healTargets = new List<AutoCombatTarget>();
|
||
var attackTargets = new List<AutoCombatTarget>();
|
||
foreach (var keyValue in ObjManager.Instance.ObjPools)
|
||
{
|
||
var character = keyValue.Value as Obj_Character;
|
||
if (character)
|
||
{
|
||
var actionMask = GetTargetActionMask(character);
|
||
if (actionMask > 0)
|
||
{
|
||
var distance = _player.DistanceToAnotherEdge(character);
|
||
if (actionMask.ContainFlag(AutoActionPriority.resurrect))
|
||
{
|
||
var combatTarget = new AutoCombatTarget(character, actionMask, distance);
|
||
resurrectTargets.Add(combatTarget);
|
||
}
|
||
else if (actionMask.ContainFlag(AutoActionPriority.heal))
|
||
{
|
||
var combatTarget = new AutoCombatTarget(character, actionMask, distance);
|
||
healTargets.Add(combatTarget);
|
||
}
|
||
else
|
||
{
|
||
var priority = GetAttackPriority(character);
|
||
if (priority > 0)
|
||
{
|
||
var combatTarget = new AutoCombatTarget(character, actionMask, distance) {priority = priority};
|
||
// 额外优先位,暂时只有攻击类需要这个
|
||
attackTargets.Add(combatTarget);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
OwnSkillData skill = null;
|
||
Obj_Character target = null;
|
||
for (var i = 0; i < skillList.Count; i++)
|
||
{
|
||
var checkSkill = skillList[i];
|
||
List<AutoCombatTarget> targetList = null;
|
||
var actionPriority = checkSkill.actionPriority;
|
||
switch (actionPriority)
|
||
{
|
||
case AutoActionPriority.resurrect:
|
||
targetList = resurrectTargets;
|
||
break;
|
||
case AutoActionPriority.heal:
|
||
targetList = healTargets;
|
||
break;
|
||
case AutoActionPriority.attack:
|
||
targetList = attackTargets;
|
||
break;
|
||
default:
|
||
LogModule.ErrorLog("Cannot solve action mask for " + checkSkill.actionPriority +
|
||
" of skillEx " + checkSkill.ownSkill.ComboExTableFinal.SkillExID);
|
||
break;
|
||
}
|
||
// 在拥有当前敌对目标时,仅仅使用敌对目标
|
||
if (actionPriority == AutoActionPriority.attack && _player.enemyTarget != null)
|
||
{
|
||
target = _player.enemyTarget;
|
||
if (ValidateSkillTarget(checkSkill, target))
|
||
{
|
||
skill = checkSkill.ownSkill;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (targetList != null && targetList.Count > 0)
|
||
{
|
||
target = GetSkillTarget(targetList, checkSkill);
|
||
if (target != null)
|
||
skill = checkSkill.ownSkill;
|
||
}
|
||
}
|
||
if (skill != null)
|
||
break;
|
||
}
|
||
// 无法使用技能时,监视普攻技能
|
||
if (skill == null)
|
||
{
|
||
var nextAttack = _player.GetNextSimpleAttack();
|
||
if (nextAttack != null)
|
||
{
|
||
var actionMask = GetSkillActionPriority(nextAttack);
|
||
var checkSkill = new AutoCombatSkill(nextAttack, actionMask);
|
||
if (_player.enemyTarget != null)
|
||
target = _player.enemyTarget;
|
||
else
|
||
target = GetSkillTarget(attackTargets, checkSkill);
|
||
if (target != null)
|
||
skill = checkSkill.ownSkill;
|
||
}
|
||
}
|
||
// 根据选择情况重置当前目标和技能
|
||
if (skill == null)
|
||
{
|
||
// 如果确实没有可以使用的技能,获得最近的敌人作为目标
|
||
if (attackTargets.Count > 0)
|
||
target = _player.enemyTarget ?? attackTargets[0].target;
|
||
}
|
||
else
|
||
{
|
||
// 如果不能释放,则取消技能选择,但保留目标
|
||
if (!_player.ValidSkillRangeOnTarget(skill.ComboExTableFinal,
|
||
skill.ComboBaseTable, target))
|
||
skill = null;
|
||
}
|
||
// 无目标时,试图获得新的目标
|
||
if (target == null)
|
||
{
|
||
if (_player.MovementState == MoveState.Static &&
|
||
IsAutoSearchNpc())
|
||
{
|
||
// 如果是护送副本,则试图获得护送NPC位置
|
||
var fubenData =
|
||
TableManager.GetFubenByID(
|
||
GameManager.gameManager.PlayerDataPool.EnterSceneCache.EnterCopySceneID, 0);
|
||
var isEscortFuben = fubenData != null && fubenData.IsEscort > 0;
|
||
if (isEscortFuben)
|
||
{
|
||
var escortNpc = SelectEscortNpc();
|
||
if (escortNpc == null)
|
||
{
|
||
if (Time.time > _nextRequestEnemyTime)
|
||
{
|
||
_nextRequestEnemyTime = Time.time + _requestEnemyInterval;
|
||
// 护送任务,试图搜索护送NPC
|
||
var send = (CG_REQ_NEED_FOLLOW_NPC_POS) PacketDistributed.CreatePacket(
|
||
MessageID
|
||
.PACKET_CG_REQ_NEED_FOLLOW_NPC_POS);
|
||
send.SetNilparam(0);
|
||
send.SendPacket();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MoveToEscortNpc(_player, escortNpc);
|
||
}
|
||
}
|
||
else if (Time.time > _nextRequestEnemyTime)
|
||
{
|
||
_nextRequestEnemyTime = Time.time + _requestEnemyInterval;
|
||
// 非护送任务,试图搜索敌对NPC
|
||
var send = (CG_REQ_NEAREST_NPC_POS) PacketDistributed.CreatePacket(MessageID
|
||
.PACKET_CG_REQ_NEAREST_NPC_POS);
|
||
send.SetNilparam(0);
|
||
send.SendPacket();
|
||
}
|
||
}
|
||
}
|
||
else if (skill == null)
|
||
{
|
||
WalkToTarget(target);
|
||
}
|
||
else
|
||
{
|
||
CastAutoCombatSkill(skill, target);
|
||
}
|
||
}
|
||
}
|
||
|
||
private bool IsAutoSearchNpc()
|
||
{
|
||
Tab_Fuben fuben = TableManager.GetFubenByID(GameManager.gameManager.PlayerDataPool.EnterSceneCache.EnterCopySceneID, 0);
|
||
if (fuben != null && (fuben.Type == 2 || fuben.AutoSearchNPC == 1))
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
private Obj_Character GetSkillTarget(List<AutoCombatTarget> targetList, AutoCombatSkill skill)
|
||
{
|
||
Obj_Character result = null;
|
||
if (targetList != null && targetList.Count > 0)
|
||
{
|
||
for (var i = 0; i < targetList.Count; i++)
|
||
targetList[i].UpdateWeightage(_player, skill);
|
||
targetList.Sort(TargetComparison);
|
||
for (var i = 0; i < targetList.Count; i++)
|
||
{
|
||
var checkTarget = targetList[i];
|
||
if (ValidateSkillTarget(skill, checkTarget.target))
|
||
{
|
||
result = checkTarget.target;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
private bool ValidateSkillTarget(AutoCombatSkill skill, Obj_Character target)
|
||
{
|
||
var result = true;
|
||
// 治疗技能额外监视目标生命
|
||
if (skill.actionPriority == AutoActionPriority.heal && skill.ownSkill.ComboBaseTable.HealthAutoFight > 0)
|
||
{
|
||
var baseAttr = target.BaseAttr;
|
||
if (baseAttr == null)
|
||
result = false;
|
||
else if (baseAttr.HP * 100 / baseAttr.MaxHP > skill.ownSkill.ComboBaseTable.HealthAutoFight)
|
||
result = false;
|
||
}
|
||
if (result)
|
||
if (!Obj_MainPlayer.ValidTargetTypeBySkillBase(target,
|
||
skill.ownSkill.ComboBaseTable.PriorityTarget))
|
||
result = false;
|
||
// 特殊处理冲锋类技能
|
||
if (result &&
|
||
skill.actionPriority == AutoActionPriority.attack)
|
||
{
|
||
var moveImpact = skill.ownSkill.ComboExTableFinal.GetMoveSkillImpact();
|
||
if (moveImpact != null && !_player.CanDashToTarget(moveImpact, target))
|
||
result = false;
|
||
}
|
||
return result;
|
||
}
|
||
|
||
private int SkillComparison(AutoCombatSkill a, AutoCombatSkill b)
|
||
{
|
||
var result = a.actionPriority.CompareTo(b.actionPriority);
|
||
if (result == 0)
|
||
result = a.ownSkill.PriorityAutoCombat.CompareTo(b.ownSkill.PriorityAutoCombat);
|
||
// 高释放距离的技能优先
|
||
if (result == 0)
|
||
result = a.ownSkill.ComboExTableFinal.Radius.CompareTo(b.ownSkill.ComboExTableFinal.Radius);
|
||
return -result;
|
||
}
|
||
|
||
private int TargetComparison(AutoCombatTarget a, AutoCombatTarget b)
|
||
{
|
||
var result = a.priority.CompareTo(b.priority);
|
||
if (result == 0)
|
||
result = a.threat.CompareTo(b.threat);
|
||
// 注:weightage 和 distance 均为越小越优先
|
||
if (result == 0)
|
||
result = -a.weightage.CompareTo(b.weightage);
|
||
if (result == 0)
|
||
result = a.inRange.CompareTo(b.inRange);
|
||
if (result == 0)
|
||
result = -a.distance.CompareTo(b.distance);
|
||
return -result;
|
||
}
|
||
|
||
private void CastAutoCombatSkill(OwnSkillData ownSkill, Obj_Character target)
|
||
{
|
||
var attackPoint = (target.Position - _player.Position).RemoveY();
|
||
var castRange = ownSkill.ComboExTableFinal.Radius;
|
||
// 校正射程 - 由于距离计算时会用目标边缘,目标中心位置可能超过最大距离
|
||
if (attackPoint.sqrMagnitude > castRange.ToSquare())
|
||
attackPoint = attackPoint.normalized * castRange;
|
||
// 对地技能不上传目标
|
||
var skillTarget = ownSkill.ComboExTableFinal.IsTargetGround() ? null : target;
|
||
// 如果是对地面释放的技能,推送敌人目标位置到ActiveScene
|
||
if (_player.TryStartTargetSkill(ownSkill, attackPoint, skillTarget, false) ==
|
||
SkillCastableCheckResult.Success)
|
||
{
|
||
_nextSimpleAttackTime =
|
||
Time.time + _autoSkillDelay +
|
||
Obj_Character.SkillToSimpleDelay(ownSkill.ComboExTableFinal);
|
||
_nextSkillTime = Time.time + _autoSkillDelay +
|
||
Obj_Character.SkillToSkillDelay(ownSkill.ComboExTableFinal);
|
||
}
|
||
var skillBase = ownSkill.ComboBaseTable;
|
||
_lastSkillBase = skillBase.SkillClass.ContainFlag((int) SKILLCLASS.SIMPLEATTACK) ? null : skillBase;
|
||
}
|
||
|
||
private void WalkToTarget(Obj_Character target)
|
||
{
|
||
if (Time.time > _nextSimpleAttackTime + _autoSkillDelay &&
|
||
_player.MovementState == MoveState.Static)
|
||
if (Time.time > _nextMoveToTargetTime)
|
||
{
|
||
_nextMoveToTargetTime = Time.time + _minMoveResetTime;
|
||
// 注:自动战斗移动不作为指令发出,但是需要自行判断移动条件
|
||
if (_player.IsCanOperate_Move(false) &&
|
||
(_player.Position - target.Position).RemoveY().sqrMagnitude > minCloseInDistance.ToSquare())
|
||
_player.MainPlayMoveToTarget(target,
|
||
new Obj_MainPlayer.MoveToTargetTillDistance(minCloseInDistance), isMoveOrder: false);
|
||
}
|
||
}
|
||
|
||
public void AddSkillFailRecord(int skillBaseId, bool canBeUsed)
|
||
{
|
||
_nextSkillTime = 0f;
|
||
_nextSimpleAttackTime = 0f;
|
||
if (!canBeUsed)
|
||
{
|
||
MyTuple<int, float> fail = null;
|
||
for (var i = 0; i < _skillFailList.Count; i++)
|
||
if (_skillFailList[i].first == skillBaseId)
|
||
{
|
||
fail = _skillFailList[i];
|
||
break;
|
||
}
|
||
|
||
if (fail == null)
|
||
{
|
||
fail = new MyTuple<int, float>(skillBaseId, 0f);
|
||
_skillFailList.Add(fail);
|
||
}
|
||
|
||
fail.second = Time.unscaledTime + _skillFailLockTime;
|
||
}
|
||
}
|
||
|
||
private bool IsCanAuto()
|
||
{
|
||
return Time.unscaledTime > _unlockTime
|
||
&& _player != null
|
||
&& _player.Controller != null
|
||
&& !_player.IsDie()
|
||
// 副本需要灵敏地远程寻路到目标,因此干掉这个标识
|
||
// && _player.Controller.CombatFlag
|
||
&& _player.isAutoCombat
|
||
&& !SceneMovieManager.Instance._PlayingMovie
|
||
// 注:同IsDisableControl不同
|
||
&& _player.FollowState != TeamFollowState.followMove
|
||
&& !_player.IsHaveChaosBuff()
|
||
// 只能能够使用技能或者能够使用普攻,不然就不折腾了
|
||
&& (GameManager.gameManager.PlayerDataPool.AllowSimpleInput ||
|
||
GameManager.gameManager.PlayerDataPool.AllowSkillInput);
|
||
}
|
||
|
||
public static void MoveToEscortNpc(Obj_MainPlayer mainPlayer, Obj_Character escortNpc)
|
||
{
|
||
var roleBase = TableManager.GetRoleBaseAttrByID(escortNpc.BaseAttr.RoleBaseID, 0);
|
||
var minDist = roleBase == null ? 0 : roleBase.PlayerFollow;
|
||
var maxDist = minDist + _escortDistTolerance;
|
||
if ((mainPlayer.Position - escortNpc.Position).sqrMagnitude > maxDist.ToSquare())
|
||
mainPlayer.MainPlayMoveToTarget(escortNpc,
|
||
new Obj_MainPlayer.MoveToTargetTillDistance(minDist));
|
||
}
|
||
|
||
private bool ValidOwnSkill(OwnSkillData ownSkill)
|
||
{
|
||
return ownSkill.IsValid()
|
||
&& !ownSkill.ComboBaseTable.SkillClass.ContainFlag((int) SKILLCLASS.SIMPLEATTACK)
|
||
// 可以使用复活类绝技,过滤其他类型
|
||
&& !ownSkill.ComboBaseTable.SkillClass.ContainFlag((int) SKILLCLASS.XP)
|
||
&& ownSkill.PriorityAutoCombat > 0
|
||
&& !IsInFailLock(ownSkill.SkillBaseTable.Id)
|
||
&& GetSkillActionPriority(ownSkill) > 0
|
||
&& _player.CheckSkillCanBeCasted(ownSkill.SkillBaseTable,ownSkill.SkillExTable,ownSkill.IsCooldownFinish()) == SkillCastableCheckResult.Success;
|
||
}
|
||
|
||
private int GetSkillActionPriority(OwnSkillData ownSkill)
|
||
{
|
||
int autoActionMask;
|
||
var targetType = ownSkill.ComboBaseTable.PriorityTarget;
|
||
if (targetType.ContainFlag(CharacterDefine.SkillBaseTargetType
|
||
.friend) || targetType.ContainFlag(CharacterDefine.SkillBaseTargetType.self))
|
||
autoActionMask = targetType.ContainFlag(CharacterDefine.SkillBaseTargetType.die)
|
||
? AutoActionPriority.resurrect
|
||
: AutoActionPriority.heal;
|
||
else if (targetType.ContainFlag(CharacterDefine.SkillBaseTargetType.enemy))
|
||
autoActionMask = AutoActionPriority.attack;
|
||
else
|
||
autoActionMask = 0;
|
||
return autoActionMask;
|
||
}
|
||
|
||
private bool IsInFailLock(int skillBaseId)
|
||
{
|
||
var result = false;
|
||
for (var i = 0; i < _skillFailList.Count; i++)
|
||
if (_skillFailList[i].first == skillBaseId)
|
||
result = _skillFailList[i].second > Time.unscaledTime;
|
||
return result;
|
||
}
|
||
|
||
private int GetTargetActionMask(Obj_Character character)
|
||
{
|
||
var result = 0;
|
||
if (!character.IsDisableState(DISABLESTATE.Disable_BeSelect))
|
||
{
|
||
// 注:之后使用otherPlayer != null 判定是否允许执行友好行为
|
||
var otherPlayer = character as Obj_OtherPlayer;
|
||
if (otherPlayer != null)
|
||
if (otherPlayer != _player &&
|
||
!GameManager.gameManager.PlayerDataPool.IsTeamMem(otherPlayer.GUID))
|
||
otherPlayer = null;
|
||
|
||
if (otherPlayer != null)
|
||
if (otherPlayer.IsDie())
|
||
{
|
||
if (otherPlayer != _player)
|
||
result = AutoActionPriority.resurrect;
|
||
}
|
||
else
|
||
{
|
||
result = AutoActionPriority.heal;
|
||
}
|
||
else if (!character.IsDie())
|
||
result = AutoActionPriority.attack;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
private void RefreshKillMonsterId()
|
||
{
|
||
_killMonsters.Clear();
|
||
foreach (var oldPair in GameManager.gameManager.MissionManager.MissionList.m_aMission)
|
||
if (oldPair.Value != null && oldPair.Value.m_yStatus == (int) MissionState.Mission_Accepted)
|
||
{
|
||
var missInfo =
|
||
CommonUtility.TryGetTable(oldPair.Key, a => TableManager.GetMissionBaseByID(a, 0));
|
||
if (missInfo != null)
|
||
{
|
||
var missionLogic =
|
||
CommonUtility.TryGetTable(missInfo.LogicID,
|
||
a => TableManager.GetMissionLogicByID(a, 0));
|
||
if (missionLogic != null)
|
||
{
|
||
var curMissionIndex =
|
||
GameManager.gameManager.MissionManager.getCurMissionIndex(oldPair.Key);
|
||
if (missionLogic.GetLogicTypebyIndex(curMissionIndex) ==
|
||
(int) TableType.Table_KillMonster)
|
||
{
|
||
var killMonster =
|
||
TableManager.GetMissionKillMonsterByID(
|
||
missionLogic.GetLogicIDbyIndex(curMissionIndex),
|
||
0);
|
||
if (killMonster != null && !_killMonsters.Contains(killMonster.MonsterDataID))
|
||
_killMonsters.Add(killMonster.MonsterDataID);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获得护送副本NPC
|
||
/// </summary>
|
||
private Obj_Character SelectEscortNpc()
|
||
{
|
||
Obj_Character result = null;
|
||
var targets = Singleton<ObjManager>.GetInstance().ObjPools.Values;
|
||
foreach (var obj in targets)
|
||
{
|
||
var npc = obj as Obj_NPC;
|
||
if (npc != null)
|
||
{
|
||
var roleBase = TableManager.GetRoleBaseAttrByID(npc.BaseAttr.RoleBaseID, 0);
|
||
if (roleBase != null && roleBase.PlayerFollow > 0)
|
||
{
|
||
result = npc;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获得目标相对于主角的选取优先度
|
||
/// </summary>
|
||
public int GetAttackPriority(Obj_Character obj)
|
||
{
|
||
var priority = 0;
|
||
if (obj.IsDie() || obj.ObjType == GameDefine_Globe.OBJ_TYPE.OBJ_MAIN_PLAYER)
|
||
{
|
||
}
|
||
else
|
||
{
|
||
// 声望已经包含Pk模式判断,不需要额外检查
|
||
var reputation = Reputation.GetObjReputionType(obj);
|
||
if (Reputation.CanAttack(reputation) && _player.ValidPkProtection(obj, false))
|
||
{
|
||
// 可以攻击该目标
|
||
priority += TargetPriority.canAttack.ToFlag();
|
||
// 然后选择任务目标
|
||
if (_killMonsters.Contains(obj.BaseAttr.RoleBaseID))
|
||
priority += TargetPriority.mission.ToFlag();
|
||
if (obj is Obj_OtherPlayer)
|
||
{
|
||
priority += TargetPriority.otherPlayer.ToFlag();
|
||
}
|
||
else
|
||
{
|
||
// 然后判断NPC类型
|
||
var npc = obj as Obj_NPC;
|
||
if (npc != null)
|
||
{
|
||
var attrData = TableManager.GetRoleBaseAttrByID(npc.RoleBaseID, 0);
|
||
if (attrData != null)
|
||
if (attrData.NpcType == (int) GameDefine_Globe.NPC_TYPE.ELITE ||
|
||
attrData.NpcType == (int) GameDefine_Globe.NPC_TYPE.TOWER)
|
||
priority += TargetPriority.eliteNpc.ToFlag();
|
||
else if (attrData.NpcType == (int) GameDefine_Globe.NPC_TYPE.BOSS)
|
||
priority += TargetPriority.boss.ToFlag();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果可以攻击,则敌对目标最为优先
|
||
if (priority > 0 && reputation == CharacterDefine.REPUTATION_TYPE.REPUTATION_HOSTILE)
|
||
priority += TargetPriority.hostile.ToFlag();
|
||
}
|
||
|
||
return priority;
|
||
}
|
||
|
||
private static class TargetPriority
|
||
{
|
||
// 可以进行攻击
|
||
public const int canAttack = 0;
|
||
|
||
// 精英怪物
|
||
public const int eliteNpc = 1;
|
||
|
||
// 任务目标怪物
|
||
public const int mission = 2;
|
||
|
||
// 可攻击其他玩家
|
||
public const int otherPlayer = 3;
|
||
|
||
// Boss 类型,比其他玩家优先度高
|
||
public const int boss = 4;
|
||
|
||
// 敌对类型目标
|
||
public const int hostile = 5;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动战斗行为类型
|
||
/// </summary>
|
||
private static class AutoActionPriority
|
||
{
|
||
// 复活目标
|
||
public const int resurrect = 1 << 2;
|
||
|
||
// 治疗目标
|
||
public const int heal = 1 << 1;
|
||
|
||
// 攻击目标
|
||
public const int attack = 1 << 0;
|
||
}
|
||
|
||
private struct AutoCombatSkill
|
||
{
|
||
public readonly int actionPriority;
|
||
public readonly OwnSkillData ownSkill;
|
||
|
||
public AutoCombatSkill(OwnSkillData ownSkillData, int actionPriority)
|
||
{
|
||
ownSkill = ownSkillData;
|
||
this.actionPriority = actionPriority;
|
||
}
|
||
}
|
||
|
||
private struct AutoCombatTarget
|
||
{
|
||
public readonly int actionMask;
|
||
public readonly Obj_Character target;
|
||
public bool inRange;
|
||
public float weightage;
|
||
public readonly float distance;
|
||
public int priority;
|
||
public int threat;
|
||
|
||
public AutoCombatTarget(Obj_Character target, int actionMask, float distance)
|
||
{
|
||
this.target = target;
|
||
this.actionMask = actionMask;
|
||
this.distance = distance;
|
||
inRange = default(bool);
|
||
weightage = default(int);
|
||
priority = default(int);
|
||
threat = default(int);
|
||
}
|
||
|
||
public void UpdateWeightage(Obj_MainPlayer mainPlayer, AutoCombatSkill skillData)
|
||
{
|
||
var skillEx = skillData.ownSkill.ComboExTableFinal;
|
||
inRange = distance < skillEx.Radius;
|
||
var targetOrder = Obj_MainPlayer.GetTargetSelectOrder(skillEx);
|
||
weightage = mainPlayer.GetWeightByOrder(target, targetOrder);
|
||
threat = mainPlayer.Controller.GetThreatValue(target);
|
||
}
|
||
}
|
||
}
|
||
} |