XmdsInstancePet.cs 20 KB

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