using CommonAI.Data;
using CommonAI.Zone;
using CommonAI.Zone.Helper;
using CommonAI.Zone.Instance;
using CommonLang;
using CommonLang.Geometry;
using CommonLang.Vector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XmdsCommon.Message;
using XmdsCommon.Plugin;
using XmdsCommonServer.Plugin.XmdsSkillTemplate.DamageCalculator;
using static CommonAI.Zone.SkillTemplate;
namespace XmdsCommonServer.Plugin.Units
{
public class XmdsInstancePet : InstancePet
{
private XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode mCurFollowMode = XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode.ActiveAtk;
//主人当前战斗状态.
private BattleStatus mMasterCombatStatus;
private InstanceUnit mCurAtkUnit = null;
//跟随最大距离的平方
private float mMaxFollowDistance = 10;
public XmdsInstancePet(InstanceZone zone, UnitInfo info, string name, int force, int level)
: base(zone, info, name, force, level)
{
this.mMaxFollowDistance = (float)Math.Pow(Templates.CFG.PET_FOLLOW_DISTANCE_MAX, 2);
}
public override bool IsActive
{
get { return (this.Enable) && (this.CurrentHP > 0) && (MasterIsReady() && !IsMasterDead()); }
}
public override bool IsAttackable { get { return (this.Enable && MasterIsReady() && !IsMasterDead()); } }
public override bool IsVisible
{
get
{
if (mMaster != null && mMaster.IsDead() == true)
{
return false;
}
return base.IsVisible;
}
}
public bool MasterIsReady()
{
if (mMaster == null) { return true; }
else
{
if (mMaster is XmdsInstancePlayer)
{
return (mMaster as XmdsInstancePlayer).IsPlayerReady();
}
}
return true;
}
private bool IsMasterDead()
{
if (mMaster == null) { return false; }
return mMaster.IsDead();
}
protected virtual bool CheckFollowMaster() { return true; }
protected override void OnPetUpdate()
{
if (mMaster != null && !IsMasterDead() && MasterIsReady())
{
if (this.CheckFollowMaster() && !CMath.includeRoundPoint(X, Y, Templates.CFG.PET_FOLLOW_DISTANCE_LIMIT, mMaster.X, mMaster.Y))
{
mCurAtkUnit = null;
this.transportToMaster();
}
else
{
//if ((this.Virtual as XmdsVirtual).CombatState != BattleStatus.None)
//{
// float d = 0;
// //非战斗状态.
// d = Templates.CFG.PET_FOLLOW_DISTANCE_MAX;
// if (!CMath.includeRoundPoint(X, Y, d, mMaster.X, mMaster.Y))
// {
// mCurAtkUnit = null;
// this.followMaster();
// }
//}
}
//2021.11.1 主人脱战了,并且没有伤害,宠物强行脱战(宠物战斗会导致玩家无法脱战)
//2020.9.22修改,宠物可以和怪物撕逼
if (this.checkOutOffBattle())
{
mCurAtkUnit = null;
//if ((this.Virtual as XmdsVirtual).CombatState == BattleStatus.None || (this.Virtual as XmdsVirtual).CombatState == BattleStatus.ReadyBattle)
if(this.CheckFollowMaster() && this.Virtual.GetBattleStatus() <= BattleStatus.ReadyBattle)
{
float d = 0;
//战斗状态.
d = Math.Max(XmdsConfig.Instance.PET_IDLE_FOLLOW_MAX_DISTANCE, Master.BodyBlockSize + this.BodyBlockSize);
if (!CMath.includeRoundPoint(X, Y, d, mMaster.X, mMaster.Y))
{
this.followMaster();
}
}
DoSomethingByMode();
}
else if (CurrentState is StateFollowAndAttack)
{
mCurAtkUnit = (CurrentState as StateFollowAndAttack).TargetUnit;
}
}
else if(this.Info.SumType == SummonType.attack && CurrentState is StateIdle)
{
DoSomethingByMode();
}
}
public virtual bool checkOutOffBattle()
{
if (CurrentState is StateIdle || (this.Master.mLastHitOtherTime + XmdsConfig.Instance.OUTOF_BATTLE_PLAYER < CommonLang.CUtils.localTimeMS
&& this.Master.mLastDamageTime + XmdsConfig.Instance.OUTOF_BATTLE_PLAYER < CommonLang.CUtils.localTimeMS))
{
return true;
}
return false;
}
public void SetFollowMode(XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode mode)
{
if (mCurFollowMode != mode)
{
mCurFollowMode = mode;
}
}
// 获取队伍的天命属性加成
public override int GetTeamFateValue(UnitFateType fateType)
{
if(this.Master == null)
{
return 0;
}
return this.Master.GetTeamFateValue(fateType);
}
public void DoSomethingByMode()
{
if (IsNoneSkill) { return; }
//当前模式.
switch (mCurFollowMode)
{
case XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode.ActiveAtk:
if (!IsPetCanAttack() && this.Master.CurrentActionStatus == UnitActionStatus.Move && !(this.CurrentState is StateFollowMaster))
{
this.Virtual.SetCombatState(BattleStatus.None);
this.followMaster();
break;
}
else if (mMasterCombatStatus == BattleStatus.PVP)
{
//优先寻找与主人有PK关联的怪物(IsPlayer).
if (TryAtkMasterEnemy(FindEnemy()))
{
return;
}
else
{
TryAtkSelfEnemy();
return;
}
}
else
{
if (TryAtkSelfEnemy())
{
return;
}
else
{
TryAtkMasterEnemy(FindEnemy());
return;
}
}
case XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode.PassiveAtk:
if (!IsPetCanAttack() && this.Master.CurrentActionStatus == UnitActionStatus.Move && !(this.CurrentState is StateFollowMaster))
{
this.Virtual.SetCombatState(BattleStatus.None);
this.followMaster();
break;
}
else
if (mMasterCombatStatus == BattleStatus.PVP)
{
//优先寻找与主人有PK关联的怪物(IsPlayer).
if (TryAtkMasterEnemy(FindEnemy()))
{
return;
}
}
else if (mMasterCombatStatus == BattleStatus.PVE)
{
if (TryAtkMasterEnemy(FindEnemy()))
{
return;
}
}
//如果没有找到敌人,且自己战斗状态下,攻击自己的敌人
if(this.Virtual.GetBattleStatus() > BattleStatus.ReadyBattle)
{
//找不到再寻找自己的敌人.
if (TryAtkSelfEnemy())
{
return;
}
}
break;
case XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode.FollowMaster:
//跟随宿主.什么都不做.
break;
default:
break;
}
}
public void OnMasterChangeCombatStatus(BattleStatus status)
{
mMasterCombatStatus = status;
//如果变更为PVP模式,宠物立即切换攻击目标.
if (mMasterCombatStatus == BattleStatus.PVP)
{
if (this.CurrentState is StateSkill)
{
var ss = CurrentState as StateSkill;
ss.block();
}
DoSomethingByMode();
}
}
public override bool IsPet { get { return true; } }
public override bool IsPlayerUnit { get { return true; } }
///
/// PVP模式下找出与主人有PVP关联且离自己最近的单位.
/// 非PVP模式下找出与主人有战斗关联且离自己最近的单位.
///
///
///
private List FindEnemy()
{
var master = this.Master;
if (master == null)
{
return null;
}
var masterVirtual = (master.Virtual as XmdsVirtual);
if (masterVirtual == null)
{
return null;
}
var hateSystem = masterVirtual.GetHateSystem();
//bool pa = false;
//bool pb = false;
List list = new List();
hateSystem.GetHateList(list);
if (list != null && list.Count > 0)
{
list.Sort((a, b) =>
{
//选距离最近的单位.
float da = CMath.getDistanceSquare(a.Unit.X, a.Unit.Y, this.X, this.Y);
float db = CMath.getDistanceSquare(b.Unit.X, b.Unit.Y, this.X, this.Y);
return (int)(da - db);
});
return list;
}
return null;
}
/** 是否可攻击 */
//2021.1.6修改,放弃宠物自行攻击的逻辑,总是被人物束缚,没法攻击自己的敌人
protected virtual bool IsPetCanAttack()
{
var master = this.Master;
if (master != null)
{
if (master.mLastHitOtherTime + XmdsConfig.Instance.OUTOF_BATTLE_PLAYER > CommonLang.CUtils.localTimeMS ||
master.mLastDamageTime + XmdsConfig.Instance.OUTOF_BATTLE_PLAYER > CommonLang.CUtils.localTimeMS ||
this.mLastDamageTime + XmdsConfig.Instance.OUTOF_BATTLE_PLAYER > CommonLang.CUtils.localTimeMS)
{
return true;
}
}
return false;
}
///
/// 攻击主人的敌人(优先PLAYER距离最近).
///
///
///
private bool TryAtkMasterEnemy(List list)
{
bool ret = false;
if (list == null || list.Count == 0) { return ret; }
foreach (SkillState skill in SkillStatus)
{
if (skill.LaunchSkill.AutoLaunch && skill.TryLaunch())
{
if(skill.Data.ExpectTarget == CastTarget.PetForMaster)
{
//是否在守卫范围内.
if (CheckInAtkRange(this.Master, skill.Data.AttackRange))
{
if (tryAutoLaunch(skill, this.Master))
{
return true;
}
}
}
else
{
foreach (HateSystem.HateInfo hi in list)
{
InstanceUnit u = hi.Unit;
if (this.IsCanAttack(hi.Unit) && Parent.IsAttackable(this, u, skill.Data.ExpectTarget, AttackReason.Attack, skill.Data))
{
//是否在守卫范围内.
if (CheckInAtkRange(u, skill.Data.AttackRange))
{
//检测是否有可释放技能//
if (tryAutoLaunch(skill, u))
{
return true;
}
}
}
}
}
}
}
return ret;
}
protected virtual bool IsCanAttack(InstanceUnit unit)
{
return unit.IsMonster;
}
protected override bool tryAutoLaunch(SkillState st, InstanceUnit target)
{
if(st == null)
{
return false;
}
//var v = (this.Virtual as XmdsVirtual);
// if (v != null && v.AllowAutoLaunch(st.LaunchSkill.SkillID))
if(st.LaunchSkill.AutoLaunch && st.TryLaunch())
{
changeState(new StateFollowAndAttack(this, target, st.Data.ExpectTarget));
return true;
}
return false;
}
///
/// 攻击自身敌人(距离最近).
///
///
protected virtual bool TryAtkSelfEnemy()
{
bool ret = false;
//using (var list = ListObjectPool.AllocAutoRelease())
//{
// //找到离自己最近的单位攻击.
// Parent.getObjectsRoundRange(Collider.Object_BlockBody_TouchRound, X, Y, Info.GuardRange, list , AoiStatus);
// DoAndRemoveCollection.UpdateAndRemove(list, (InstanceUnit u) =>
// {
// return !u.IsActive;
// });
// if (list != null && list.Count > 0)
// {
// list.Sort((a, b) =>
// {
// //选距离最近的单位.
// float da = CMath.getDistanceSquare(a.X, a.Y, this.X, this.Y);
// float db = CMath.getDistanceSquare(b.X, b.Y, this.X, this.Y);
// return (int)(da - db);
// });
// foreach (SkillState skill in SkillStatus)
// {
// if (skill.LaunchSkill.AutoLaunch && skill.TryLaunch())
// {
// foreach (InstanceUnit u in list)
// {
// if (this.IsCanAttack(u) && Parent.IsAttackable(this, u, skill.Data.ExpectTarget, AttackReason.Attack, skill.Data))
// {
// //是否在守卫范围内.
// if (CheckInAtkRange(u, this.Info.GuardRange + skill.Data.AttackRange))
// {
// //检测是否有可释放技能//
// if (tryAutoLaunch(skill, u))
// {
// return true;
// }
// }
// }
// }
// }
// }
// }
//}
return ret;
}
public override void InitSkills(LaunchSkill baseSkill, params LaunchSkill[] skills)
{
if (this.Virtual == null ||
(this.Virtual as XmdsVirtual).IsFinishSkillInit() == false)
{
return;
}
base.InitSkills(baseSkill, skills);
}
///
/// 判断该单位是否处在可攻击范围内.
///
///
///
///
protected bool CheckInAtkRange(InstanceUnit target, float skillrange)
{
bool ret = false;
//两圆相切.
var atkr = this.GetSkillAttackRange(skillrange);
if (Collider.Object_HitBody_TouchRound(target, this.X, this.Y, atkr))
{
return true;
}
else if (this.SummonerUnit != null && target != null)
{
////宠物可以离开宿主的最大范围.
//float da = (float)Math.Pow(Templates.CFG.PET_FOLLOW_DISTANCE_MAX, 2);
//CommonLang.Geometry.Vector2 v1 = new CommonLang.Geometry.Vector2(this.X, this.Y);
//CommonLang.Geometry.Vector2 v2 = new CommonLang.Geometry.Vector2(target.X, target.Y);
////方向.
//CommonLang.Geometry.Vector2 dir = (v2 - v1);
//dir.Normalize();
//var pos = v2 - dir * (atkr * target.BodyHitSize);
//float rd = CMath.getDistanceSquare(pos.X, pos.Y, this.SummonerUnit.X, this.SummonerUnit.Y);
////在范围内.
//if (rd < da)
//{
// ret = true;
//}
ret = CMath.getDistanceSquare(target.X, target.Y, this.SummonerUnit.X, this.SummonerUnit.Y) < this.mMaxFollowDistance;
}
return ret;
}
public override bool tryLaunchRandomSkillAndCancelCurrentSkill(InstanceUnit target, bool autoFocusNearTarget = false)
{
//自动战斗不允许中断当前技能施放.
return false;
}
/** 计算并获得伤害 */
public override void PetShareDamage(int baseDmgValue, InstanceUnit sender)
{
XmdsVirtual petVirtual = this.Virtual as XmdsVirtual;
int finalDmg = XmdsDamageCalculator.GetPetDamage(baseDmgValue, petVirtual);
//分发事件
finalDmg = petVirtual.DispatchShareMasterDmgEvent(finalDmg, sender);
this.AddHP(-finalDmg, sender);
}
public override void doSomething()
{
if (CurrentState is StateSkill)
{
var target = mCurAtkUnit;
if (target != null)
{
if (tryMoveScatterTarget(target)) { return; }
}
}
base.doSomething();
}
///
/// 攻击间歇,尝试换个位置,避免怪物堆在一个点
///
protected virtual bool tryMoveScatterTarget(InstanceUnit target)
{
//只有单位为非碰撞时,才有这个需求//
if (!this.IntersectObj)
{
if (CommonLang.CUtils.localTimeMS > mCheckAttackSpread && CUtils.RandomPercent(Parent.RandomN, Templates.CFG.AI_NPC_ATTACK_IDLE_SCATTER_PCT))
{
mCheckAttackSpread = CommonLang.CUtils.localTimeMS + Templates.CFG.AI_NPC_ATTACK_IDLE_INTERVAL;
InstanceUnit block = null;
Parent.ForEachNearObjects(X, Y, (InstanceZoneObject o, ref bool cancel) =>
{
if ((o != this) && (o is InstanceUnit) && Parent.TouchObject2(this, o))
{
block = o as InstanceUnit;
cancel = true;
}
});
if (block != null)
{
float degree = MathVector.getDegree(X, Y, target.X, target.Y);
float distance = this.BodyBlockSize + block.BodyBlockSize;
CommonLang.Vector.Vector2 turnL = new CommonLang.Vector.Vector2(X, Y);
CommonLang.Vector.Vector2 turnR = new CommonLang.Vector.Vector2(X, Y);
MathVector.movePolar(turnL, degree + CMath.PI_DIV_2, distance);
MathVector.movePolar(turnR, degree - CMath.PI_DIV_2, distance);
float dl = MathVector.getDistanceSquare(turnL.X, turnL.Y, target.X, target.Y);
float dr = MathVector.getDistanceSquare(turnR.X, turnR.Y, target.X, target.Y);
if (dl < dr)
{
this.startMoveTo(turnL.X, turnL.Y);
}
else
{
this.startMoveTo(turnR.X, turnR.Y);
}
return true;
}
}
}
return false;
}
protected override void Disposing()
{
mCurAtkUnit = null;
base.Disposing();
}
}
}