123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- 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; } }
- /// <summary>
- /// PVP模式下找出与主人有PVP关联且离自己最近的单位.
- /// 非PVP模式下找出与主人有战斗关联且离自己最近的单位.
- /// </summary>
- /// <param name="ispvp"></param>
- /// <returns></returns>
- private List<HateSystem.HateInfo> 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<HateSystem.HateInfo> list = new List<HateSystem.HateInfo>();
- 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;
- }
- /// <summary>
- /// 攻击主人的敌人(优先PLAYER距离最近).
- /// </summary>
- /// <param name="list"></param>
- /// <returns></returns>
- private bool TryAtkMasterEnemy(List<HateSystem.HateInfo> 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;
- }
- /// <summary>
- /// 攻击自身敌人(距离最近).
- /// </summary>
- /// <returns></returns>
- protected virtual bool TryAtkSelfEnemy()
- {
- bool ret = false;
- //using (var list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
- //{
- // //找到离自己最近的单位攻击.
- // Parent.getObjectsRoundRange<InstanceUnit>(Collider.Object_BlockBody_TouchRound, X, Y, Info.GuardRange, list , AoiStatus);
- // DoAndRemoveCollection.UpdateAndRemove<InstanceUnit>(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);
- }
- /// <summary>
- /// 判断该单位是否处在可攻击范围内.
- /// </summary>
- /// <param name="target"></param>
- /// <param name="skillrange"></param>
- /// <returns></returns>
- 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();
- }
- /// <summary>
- /// 攻击间歇,尝试换个位置,避免怪物堆在一个点
- /// </summary>
- 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();
- }
- }
- }
|