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(); } } }