XmdsInstancePet.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. using CommonAI.Data;
  2. using CommonAI.Zone;
  3. using CommonAI.Zone.Helper;
  4. using CommonAI.Zone.Instance;
  5. using CommonLang;
  6. using CommonLang.Geometry;
  7. using CommonLang.Vector;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using System.Text;
  12. using XmdsCommon.Message;
  13. using XmdsCommon.Plugin;
  14. using XmdsCommonServer.Plugin.XmdsSkillTemplate.DamageCalculator;
  15. using static CommonAI.Zone.SkillTemplate;
  16. namespace XmdsCommonServer.Plugin.Units
  17. {
  18. public class XmdsInstancePet : InstancePet
  19. {
  20. private XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode mCurFollowMode = XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode.ActiveAtk;
  21. //主人当前战斗状态.
  22. private BattleStatus mMasterCombatStatus;
  23. private InstanceUnit mCurAtkUnit = null;
  24. //跟随最大距离的平方
  25. private float mMaxFollowDistance = 10;
  26. public XmdsInstancePet(InstanceZone zone, UnitInfo info, string name, int force, int level)
  27. : base(zone, info, name, force, level)
  28. {
  29. this.mMaxFollowDistance = (float)Math.Pow(Templates.CFG.PET_FOLLOW_DISTANCE_MAX, 2);
  30. }
  31. public override bool IsActive
  32. {
  33. get { return (this.Enable) && (this.CurrentHP > 0) && (MasterIsReady() && !IsMasterDead()); }
  34. }
  35. public override bool IsAttackable { get { return (this.Enable && MasterIsReady() && !IsMasterDead()); } }
  36. public override bool IsVisible
  37. {
  38. get
  39. {
  40. if (mMaster != null && mMaster.IsDead() == true)
  41. {
  42. return false;
  43. }
  44. return base.IsVisible;
  45. }
  46. }
  47. public bool MasterIsReady()
  48. {
  49. if (mMaster == null) { return true; }
  50. else
  51. {
  52. if (mMaster is XmdsInstancePlayer)
  53. {
  54. return (mMaster as XmdsInstancePlayer).IsPlayerReady();
  55. }
  56. }
  57. return true;
  58. }
  59. private bool IsMasterDead()
  60. {
  61. if (mMaster == null) { return false; }
  62. return mMaster.IsDead();
  63. }
  64. protected virtual bool CheckFollowMaster() { return true; }
  65. protected override void OnPetUpdate()
  66. {
  67. if (mMaster != null && !IsMasterDead() && MasterIsReady())
  68. {
  69. if (this.CheckFollowMaster() && !CMath.includeRoundPoint(X, Y, Templates.CFG.PET_FOLLOW_DISTANCE_LIMIT, mMaster.X, mMaster.Y))
  70. {
  71. mCurAtkUnit = null;
  72. this.transportToMaster();
  73. }
  74. else
  75. {
  76. //if ((this.Virtual as XmdsVirtual).CombatState != BattleStatus.None)
  77. //{
  78. // float d = 0;
  79. // //非战斗状态.
  80. // d = Templates.CFG.PET_FOLLOW_DISTANCE_MAX;
  81. // if (!CMath.includeRoundPoint(X, Y, d, mMaster.X, mMaster.Y))
  82. // {
  83. // mCurAtkUnit = null;
  84. // this.followMaster();
  85. // }
  86. //}
  87. }
  88. //2021.11.1 主人脱战了,并且没有伤害,宠物强行脱战(宠物战斗会导致玩家无法脱战)
  89. //2020.9.22修改,宠物可以和怪物撕逼
  90. if (this.checkOutOffBattle())
  91. {
  92. mCurAtkUnit = null;
  93. //if ((this.Virtual as XmdsVirtual).CombatState == BattleStatus.None || (this.Virtual as XmdsVirtual).CombatState == BattleStatus.ReadyBattle)
  94. if(this.CheckFollowMaster() && this.Virtual.GetBattleStatus() <= BattleStatus.ReadyBattle)
  95. {
  96. float d = 0;
  97. //战斗状态.
  98. d = Math.Max(XmdsConfig.Instance.PET_IDLE_FOLLOW_MAX_DISTANCE, Master.BodyBlockSize + this.BodyBlockSize);
  99. if (!CMath.includeRoundPoint(X, Y, d, mMaster.X, mMaster.Y))
  100. {
  101. this.followMaster();
  102. }
  103. }
  104. DoSomethingByMode();
  105. }
  106. else if (CurrentState is StateFollowAndAttack)
  107. {
  108. mCurAtkUnit = (CurrentState as StateFollowAndAttack).TargetUnit;
  109. }
  110. }
  111. else if(this.Info.SumType == SummonType.attack && CurrentState is StateIdle)
  112. {
  113. DoSomethingByMode();
  114. }
  115. }
  116. public virtual bool checkOutOffBattle()
  117. {
  118. if (CurrentState is StateIdle || (this.Master.mLastHitOtherTime + XmdsConfig.Instance.OUTOF_BATTLE_PLAYER < CommonLang.CUtils.localTimeMS
  119. && this.Master.mLastDamageTime + XmdsConfig.Instance.OUTOF_BATTLE_PLAYER < CommonLang.CUtils.localTimeMS))
  120. {
  121. return true;
  122. }
  123. return false;
  124. }
  125. public void SetFollowMode(XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode mode)
  126. {
  127. if (mCurFollowMode != mode)
  128. {
  129. mCurFollowMode = mode;
  130. }
  131. }
  132. // 获取队伍的天命属性加成
  133. public override int GetTeamFateValue(UnitFateType fateType)
  134. {
  135. if(this.Master == null)
  136. {
  137. return 0;
  138. }
  139. return this.Master.GetTeamFateValue(fateType);
  140. }
  141. public void DoSomethingByMode()
  142. {
  143. if (IsNoneSkill) { return; }
  144. //当前模式.
  145. switch (mCurFollowMode)
  146. {
  147. case XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode.ActiveAtk:
  148. if (!IsPetCanAttack() && this.Master.CurrentActionStatus == UnitActionStatus.Move && !(this.CurrentState is StateFollowMaster))
  149. {
  150. this.Virtual.SetCombatState(BattleStatus.None);
  151. this.followMaster();
  152. break;
  153. }
  154. else if (mMasterCombatStatus == BattleStatus.PVP)
  155. {
  156. //优先寻找与主人有PK关联的怪物(IsPlayer).
  157. if (TryAtkMasterEnemy(FindEnemy()))
  158. {
  159. return;
  160. }
  161. else
  162. {
  163. TryAtkSelfEnemy();
  164. return;
  165. }
  166. }
  167. else
  168. {
  169. if (TryAtkSelfEnemy())
  170. {
  171. return;
  172. }
  173. else
  174. {
  175. TryAtkMasterEnemy(FindEnemy());
  176. return;
  177. }
  178. }
  179. case XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode.PassiveAtk:
  180. if (!IsPetCanAttack() && this.Master.CurrentActionStatus == UnitActionStatus.Move && !(this.CurrentState is StateFollowMaster))
  181. {
  182. this.Virtual.SetCombatState(BattleStatus.None);
  183. this.followMaster();
  184. break;
  185. }
  186. else
  187. if (mMasterCombatStatus == BattleStatus.PVP)
  188. {
  189. //优先寻找与主人有PK关联的怪物(IsPlayer).
  190. if (TryAtkMasterEnemy(FindEnemy()))
  191. {
  192. return;
  193. }
  194. }
  195. else if (mMasterCombatStatus == BattleStatus.PVE)
  196. {
  197. if (TryAtkMasterEnemy(FindEnemy()))
  198. {
  199. return;
  200. }
  201. }
  202. //如果没有找到敌人,且自己战斗状态下,攻击自己的敌人
  203. if(this.Virtual.GetBattleStatus() > BattleStatus.ReadyBattle)
  204. {
  205. //找不到再寻找自己的敌人.
  206. if (TryAtkSelfEnemy())
  207. {
  208. return;
  209. }
  210. }
  211. break;
  212. case XmdsCommon.Plugin.XmdsPetConifg.XmdsPetFollowMode.FollowMaster:
  213. //跟随宿主.什么都不做.
  214. break;
  215. default:
  216. break;
  217. }
  218. }
  219. public void OnMasterChangeCombatStatus(BattleStatus status)
  220. {
  221. mMasterCombatStatus = status;
  222. //如果变更为PVP模式,宠物立即切换攻击目标.
  223. if (mMasterCombatStatus == BattleStatus.PVP)
  224. {
  225. if (this.CurrentState is StateSkill)
  226. {
  227. var ss = CurrentState as StateSkill;
  228. ss.block();
  229. }
  230. DoSomethingByMode();
  231. }
  232. }
  233. public override bool IsPet { get { return true; } }
  234. public override bool IsPlayerUnit { get { return true; } }
  235. /// <summary>
  236. /// PVP模式下找出与主人有PVP关联且离自己最近的单位.
  237. /// 非PVP模式下找出与主人有战斗关联且离自己最近的单位.
  238. /// </summary>
  239. /// <param name="ispvp"></param>
  240. /// <returns></returns>
  241. private List<HateSystem.HateInfo> FindEnemy()
  242. {
  243. var master = this.Master;
  244. if (master == null)
  245. {
  246. return null;
  247. }
  248. var masterVirtual = (master.Virtual as XmdsVirtual);
  249. if (masterVirtual == null)
  250. {
  251. return null;
  252. }
  253. var hateSystem = masterVirtual.GetHateSystem();
  254. //bool pa = false;
  255. //bool pb = false;
  256. List<HateSystem.HateInfo> list = new List<HateSystem.HateInfo>();
  257. hateSystem.GetHateList(list);
  258. if (list != null && list.Count > 0)
  259. {
  260. list.Sort((a, b) =>
  261. {
  262. //选距离最近的单位.
  263. float da = CMath.getDistanceSquare(a.Unit.X, a.Unit.Y, this.X, this.Y);
  264. float db = CMath.getDistanceSquare(b.Unit.X, b.Unit.Y, this.X, this.Y);
  265. return (int)(da - db);
  266. });
  267. return list;
  268. }
  269. return null;
  270. }
  271. /** 是否可攻击 */
  272. //2021.1.6修改,放弃宠物自行攻击的逻辑,总是被人物束缚,没法攻击自己的敌人
  273. protected virtual bool IsPetCanAttack()
  274. {
  275. var master = this.Master;
  276. if (master != null)
  277. {
  278. if (master.mLastHitOtherTime + XmdsConfig.Instance.OUTOF_BATTLE_PLAYER > CommonLang.CUtils.localTimeMS ||
  279. master.mLastDamageTime + XmdsConfig.Instance.OUTOF_BATTLE_PLAYER > CommonLang.CUtils.localTimeMS ||
  280. this.mLastDamageTime + XmdsConfig.Instance.OUTOF_BATTLE_PLAYER > CommonLang.CUtils.localTimeMS)
  281. {
  282. return true;
  283. }
  284. }
  285. return false;
  286. }
  287. /// <summary>
  288. /// 攻击主人的敌人(优先PLAYER距离最近).
  289. /// </summary>
  290. /// <param name="list"></param>
  291. /// <returns></returns>
  292. private bool TryAtkMasterEnemy(List<HateSystem.HateInfo> list)
  293. {
  294. bool ret = false;
  295. if (list == null || list.Count == 0) { return ret; }
  296. foreach (SkillState skill in SkillStatus)
  297. {
  298. if (skill.LaunchSkill.AutoLaunch && skill.TryLaunch())
  299. {
  300. if(skill.Data.ExpectTarget == CastTarget.PetForMaster)
  301. {
  302. //是否在守卫范围内.
  303. if (CheckInAtkRange(this.Master, skill.Data.AttackRange))
  304. {
  305. if (tryAutoLaunch(skill, this.Master))
  306. {
  307. return true;
  308. }
  309. }
  310. }
  311. else
  312. {
  313. foreach (HateSystem.HateInfo hi in list)
  314. {
  315. InstanceUnit u = hi.Unit;
  316. if (this.IsCanAttack(hi.Unit) && Parent.IsAttackable(this, u, skill.Data.ExpectTarget, AttackReason.Attack, skill.Data))
  317. {
  318. //是否在守卫范围内.
  319. if (CheckInAtkRange(u, skill.Data.AttackRange))
  320. {
  321. //检测是否有可释放技能//
  322. if (tryAutoLaunch(skill, u))
  323. {
  324. return true;
  325. }
  326. }
  327. }
  328. }
  329. }
  330. }
  331. }
  332. return ret;
  333. }
  334. protected virtual bool IsCanAttack(InstanceUnit unit)
  335. {
  336. return unit.IsMonster;
  337. }
  338. protected override bool tryAutoLaunch(SkillState st, InstanceUnit target)
  339. {
  340. if(st == null)
  341. {
  342. return false;
  343. }
  344. //var v = (this.Virtual as XmdsVirtual);
  345. // if (v != null && v.AllowAutoLaunch(st.LaunchSkill.SkillID))
  346. if(st.LaunchSkill.AutoLaunch && st.TryLaunch())
  347. {
  348. changeState(new StateFollowAndAttack(this, target, st.Data.ExpectTarget));
  349. return true;
  350. }
  351. return false;
  352. }
  353. /// <summary>
  354. /// 攻击自身敌人(距离最近).
  355. /// </summary>
  356. /// <returns></returns>
  357. protected virtual bool TryAtkSelfEnemy()
  358. {
  359. bool ret = false;
  360. //using (var list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
  361. //{
  362. // //找到离自己最近的单位攻击.
  363. // Parent.getObjectsRoundRange<InstanceUnit>(Collider.Object_BlockBody_TouchRound, X, Y, Info.GuardRange, list , AoiStatus);
  364. // DoAndRemoveCollection.UpdateAndRemove<InstanceUnit>(list, (InstanceUnit u) =>
  365. // {
  366. // return !u.IsActive;
  367. // });
  368. // if (list != null && list.Count > 0)
  369. // {
  370. // list.Sort((a, b) =>
  371. // {
  372. // //选距离最近的单位.
  373. // float da = CMath.getDistanceSquare(a.X, a.Y, this.X, this.Y);
  374. // float db = CMath.getDistanceSquare(b.X, b.Y, this.X, this.Y);
  375. // return (int)(da - db);
  376. // });
  377. // foreach (SkillState skill in SkillStatus)
  378. // {
  379. // if (skill.LaunchSkill.AutoLaunch && skill.TryLaunch())
  380. // {
  381. // foreach (InstanceUnit u in list)
  382. // {
  383. // if (this.IsCanAttack(u) && Parent.IsAttackable(this, u, skill.Data.ExpectTarget, AttackReason.Attack, skill.Data))
  384. // {
  385. // //是否在守卫范围内.
  386. // if (CheckInAtkRange(u, this.Info.GuardRange + skill.Data.AttackRange))
  387. // {
  388. // //检测是否有可释放技能//
  389. // if (tryAutoLaunch(skill, u))
  390. // {
  391. // return true;
  392. // }
  393. // }
  394. // }
  395. // }
  396. // }
  397. // }
  398. // }
  399. //}
  400. return ret;
  401. }
  402. public override void InitSkills(LaunchSkill baseSkill, params LaunchSkill[] skills)
  403. {
  404. if (this.Virtual == null ||
  405. (this.Virtual as XmdsVirtual).IsFinishSkillInit() == false)
  406. {
  407. return;
  408. }
  409. base.InitSkills(baseSkill, skills);
  410. }
  411. /// <summary>
  412. /// 判断该单位是否处在可攻击范围内.
  413. /// </summary>
  414. /// <param name="target"></param>
  415. /// <param name="skillrange"></param>
  416. /// <returns></returns>
  417. protected bool CheckInAtkRange(InstanceUnit target, float skillrange)
  418. {
  419. bool ret = false;
  420. //两圆相切.
  421. var atkr = this.GetSkillAttackRange(skillrange);
  422. if (Collider.Object_HitBody_TouchRound(target, this.X, this.Y, atkr))
  423. {
  424. return true;
  425. }
  426. else if (this.SummonerUnit != null && target != null)
  427. {
  428. ////宠物可以离开宿主的最大范围.
  429. //float da = (float)Math.Pow(Templates.CFG.PET_FOLLOW_DISTANCE_MAX, 2);
  430. //CommonLang.Geometry.Vector2 v1 = new CommonLang.Geometry.Vector2(this.X, this.Y);
  431. //CommonLang.Geometry.Vector2 v2 = new CommonLang.Geometry.Vector2(target.X, target.Y);
  432. ////方向.
  433. //CommonLang.Geometry.Vector2 dir = (v2 - v1);
  434. //dir.Normalize();
  435. //var pos = v2 - dir * (atkr * target.BodyHitSize);
  436. //float rd = CMath.getDistanceSquare(pos.X, pos.Y, this.SummonerUnit.X, this.SummonerUnit.Y);
  437. ////在范围内.
  438. //if (rd < da)
  439. //{
  440. // ret = true;
  441. //}
  442. ret = CMath.getDistanceSquare(target.X, target.Y, this.SummonerUnit.X, this.SummonerUnit.Y) < this.mMaxFollowDistance;
  443. }
  444. return ret;
  445. }
  446. public override bool tryLaunchRandomSkillAndCancelCurrentSkill(InstanceUnit target, bool autoFocusNearTarget = false)
  447. {
  448. //自动战斗不允许中断当前技能施放.
  449. return false;
  450. }
  451. /** 计算并获得伤害 */
  452. public override void PetShareDamage(int baseDmgValue, InstanceUnit sender)
  453. {
  454. XmdsVirtual petVirtual = this.Virtual as XmdsVirtual;
  455. int finalDmg = XmdsDamageCalculator.GetPetDamage(baseDmgValue, petVirtual);
  456. //分发事件
  457. finalDmg = petVirtual.DispatchShareMasterDmgEvent(finalDmg, sender);
  458. this.AddHP(-finalDmg, sender);
  459. }
  460. public override void doSomething()
  461. {
  462. if (CurrentState is StateSkill)
  463. {
  464. var target = mCurAtkUnit;
  465. if (target != null)
  466. {
  467. if (tryMoveScatterTarget(target)) { return; }
  468. }
  469. }
  470. base.doSomething();
  471. }
  472. /// <summary>
  473. /// 攻击间歇,尝试换个位置,避免怪物堆在一个点
  474. /// </summary>
  475. protected virtual bool tryMoveScatterTarget(InstanceUnit target)
  476. {
  477. //只有单位为非碰撞时,才有这个需求//
  478. if (!this.IntersectObj)
  479. {
  480. if (CommonLang.CUtils.localTimeMS > mCheckAttackSpread && CUtils.RandomPercent(Parent.RandomN, Templates.CFG.AI_NPC_ATTACK_IDLE_SCATTER_PCT))
  481. {
  482. mCheckAttackSpread = CommonLang.CUtils.localTimeMS + Templates.CFG.AI_NPC_ATTACK_IDLE_INTERVAL;
  483. InstanceUnit block = null;
  484. Parent.ForEachNearObjects(X, Y, (InstanceZoneObject o, ref bool cancel) =>
  485. {
  486. if ((o != this) && (o is InstanceUnit) && Parent.TouchObject2(this, o))
  487. {
  488. block = o as InstanceUnit;
  489. cancel = true;
  490. }
  491. });
  492. if (block != null)
  493. {
  494. float degree = MathVector.getDegree(X, Y, target.X, target.Y);
  495. float distance = this.BodyBlockSize + block.BodyBlockSize;
  496. CommonLang.Vector.Vector2 turnL = new CommonLang.Vector.Vector2(X, Y);
  497. CommonLang.Vector.Vector2 turnR = new CommonLang.Vector.Vector2(X, Y);
  498. MathVector.movePolar(turnL, degree + CMath.PI_DIV_2, distance);
  499. MathVector.movePolar(turnR, degree - CMath.PI_DIV_2, distance);
  500. float dl = MathVector.getDistanceSquare(turnL.X, turnL.Y, target.X, target.Y);
  501. float dr = MathVector.getDistanceSquare(turnR.X, turnR.Y, target.X, target.Y);
  502. if (dl < dr)
  503. {
  504. this.startMoveTo(turnL.X, turnL.Y);
  505. }
  506. else
  507. {
  508. this.startMoveTo(turnR.X, turnR.Y);
  509. }
  510. return true;
  511. }
  512. }
  513. }
  514. return false;
  515. }
  516. protected override void Disposing()
  517. {
  518. mCurAtkUnit = null;
  519. base.Disposing();
  520. }
  521. }
  522. }