XmdsInstancePet.cs 19 KB

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