選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 

329 行
6.6 KiB

  1. module.exports = class Aggro {
  2. constructor () {
  3. this.type = 'aggro';
  4. this.range = 7;
  5. this.faction = null;
  6. this.physics = null;
  7. this.list = [];
  8. this.ignoreList = [];
  9. }
  10. init (blueprint) {
  11. this.physics = this.obj.instance.physics;
  12. blueprint = blueprint || {};
  13. if (blueprint.faction)
  14. this.faction = blueprint.faction;
  15. //TODO: Why don't we move if faction is null?
  16. if (!this.has('faction'))
  17. return;
  18. if (this.physics.width > 0)
  19. this.move();
  20. }
  21. simplify (self) {
  22. return {
  23. type: 'aggro',
  24. faction: this.faction
  25. };
  26. }
  27. move () {
  28. if (this.obj.dead)
  29. return;
  30. let result = {
  31. success: true
  32. };
  33. this.obj.fireEvent('beforeAggro', result);
  34. if (!result.success)
  35. return;
  36. let obj = this.obj;
  37. //If we're attacking something, don't try and look for more trouble. SAVE THE CPU!
  38. // this only counts for mobs, players can have multiple attackers
  39. let list = this.list;
  40. if (obj.isMob) {
  41. let lLen = list.length;
  42. for (let i = 0; i < lLen; i++) {
  43. let l = list[i];
  44. let lThreat = l.obj.aggro.getHighest();
  45. if (lThreat) {
  46. l.obj.aggro.list.forEach(function (a) {
  47. a.obj.aggro.unIgnore(lThreat);
  48. });
  49. }
  50. l.obj.aggro.unIgnore(obj);
  51. if (l.threat > 0)
  52. return;
  53. }
  54. } else {
  55. let lLen = list.length;
  56. for (let i = 0; i < lLen; i++) {
  57. let targetAggro = list[i].obj.aggro;
  58. //Maybe the aggro component has been removed?
  59. if (targetAggro)
  60. targetAggro.unIgnore(obj);
  61. }
  62. }
  63. let x = obj.x;
  64. let y = obj.y;
  65. //find mobs in range
  66. let range = this.range;
  67. let inRange = this.physics.getArea(x - range, y - range, x + range, y + range, (c => (((!c.player) || (!obj.player)) && (!obj.dead) && (c.aggro) && (c.aggro.willAutoAttack(obj)))));
  68. if (inRange.length === 0)
  69. return;
  70. let iLen = inRange.length;
  71. for (let i = 0; i < iLen; i++) {
  72. let enemy = inRange[i];
  73. //The length could change
  74. let lLen = list.length;
  75. for (let j = 0; j < lLen; j++) {
  76. //Set the enemy to null so we need we need to continue
  77. if (list[j].obj === enemy)
  78. enemy = null;
  79. }
  80. if (!enemy)
  81. continue;
  82. //Do we have LoS?
  83. if (!this.physics.hasLos(x, y, enemy.x, enemy.y))
  84. continue;
  85. if (enemy.aggro.tryEngage(obj))
  86. this.tryEngage(enemy, 0);
  87. }
  88. }
  89. canAttack (target) {
  90. let obj = this.obj;
  91. if (target === obj)
  92. return false;
  93. else if ((target.player) && (obj.player)) {
  94. let hasButcher = (obj.prophecies.hasProphecy('butcher')) && (target.prophecies.hasProphecy('butcher'));
  95. if ((!target.social.party) || (!obj.social.party))
  96. return hasButcher;
  97. else if (target.social.partyLeaderId !== obj.social.partyLeaderId)
  98. return hasButcher;
  99. return false;
  100. } else if ((target.follower) && (target.follower.master.player) && (obj.player))
  101. return false;
  102. else if (obj.player)
  103. return true;
  104. else if (target.aggro.faction !== obj.aggro.faction)
  105. return true;
  106. else if (!!target.player !== !!obj.player)
  107. return true;
  108. }
  109. willAutoAttack (target) {
  110. if (this.obj === target)
  111. return false;
  112. let faction = target.aggro.faction;
  113. if (!faction || !this.faction)
  114. return false;
  115. let rep = this.obj.reputation;
  116. if (!rep) {
  117. let targetRep = target.reputation;
  118. if (!targetRep)
  119. return false;
  120. return (targetRep.getTier(this.faction) < 3);
  121. }
  122. return (rep.getTier(faction) < 3);
  123. }
  124. ignore (obj) {
  125. this.ignoreList.spliceWhere(o => o === obj);
  126. this.ignoreList.push(obj);
  127. }
  128. unIgnore (obj) {
  129. this.ignoreList.spliceWhere(o => o === obj);
  130. }
  131. tryEngage (obj, amount, threatMult) {
  132. //Don't aggro yourself, stupid
  133. if (obj === this.obj)
  134. return;
  135. let result = {
  136. success: true
  137. };
  138. this.obj.fireEvent('beforeAggro', result);
  139. if (!result.success)
  140. return false;
  141. //Mobs shouldn't aggro players that are too far from their home
  142. let mob = this.obj.mob;
  143. if (!mob)
  144. mob = obj.mob;
  145. if (mob) {
  146. let notMob = (obj === mob) ? this.obj : obj;
  147. if (!mob.canChase(notMob))
  148. return false;
  149. }
  150. let oId = obj.id;
  151. let list = this.list;
  152. amount = amount || 0;
  153. threatMult = threatMult || 1;
  154. let exists = list.find(l => l.obj.id === oId);
  155. if (exists) {
  156. exists.damage += amount;
  157. exists.threat += amount * threatMult;
  158. } else {
  159. let l = {
  160. obj: obj,
  161. damage: amount,
  162. threat: amount * threatMult
  163. };
  164. list.push(l);
  165. }
  166. //this.sortThreat();
  167. return true;
  168. }
  169. getFirstAttacker () {
  170. let first = this.list.find(l => ((l.obj.player) && (l.damage > 0)));
  171. if (first)
  172. return first.obj;
  173. return null;
  174. }
  175. die () {
  176. let list = this.list;
  177. let lLen = list.length;
  178. for (let i = 0; i < lLen; i++) {
  179. let l = list[i];
  180. if (!l) {
  181. lLen--;
  182. continue;
  183. }
  184. //Maybe the aggro component was removed?
  185. let targetAggro = l.obj.aggro;
  186. if (targetAggro) {
  187. targetAggro.unAggro(this.obj);
  188. i--;
  189. lLen--;
  190. }
  191. }
  192. this.list = [];
  193. }
  194. unAggro (obj, amount) {
  195. let list = this.list;
  196. let lLen = list.length;
  197. for (let i = 0; i < lLen; i++) {
  198. let l = list[i];
  199. if (l.obj !== obj)
  200. continue;
  201. if (!amount) {
  202. list.splice(i, 1);
  203. obj.aggro.unAggro(this.obj);
  204. break;
  205. } else {
  206. l.threat -= amount;
  207. if (l.threat <= 0) {
  208. list.splice(i, 1);
  209. obj.aggro.unAggro(this.obj);
  210. break;
  211. }
  212. }
  213. }
  214. this.ignoreList.spliceWhere(o => o === obj);
  215. //Stuff like cocoons don't have spellbooks
  216. if (this.obj.spellbook)
  217. this.obj.spellbook.unregisterCallback(obj.id, true);
  218. if ((this.list.length === 0) && (this.obj.mob) && (!this.obj.follower))
  219. this.obj.stats.resetHp();
  220. }
  221. sortThreat () {
  222. this.list.sort(function (a, b) {
  223. return (b.threat - a.threat);
  224. });
  225. }
  226. getHighest () {
  227. if (this.list.length === 0)
  228. return null;
  229. let list = this.list;
  230. let lLen = list.length;
  231. let highest = null;
  232. let closest = 99999;
  233. let thisObj = this.obj;
  234. let x = thisObj.x;
  235. let y = thisObj.y;
  236. for (let i = 0; i < lLen; i++) {
  237. let l = list[i];
  238. let obj = l.obj;
  239. if (this.ignoreList.some(o => o === obj))
  240. continue;
  241. if (!highest || l.threat > highest.threat) {
  242. highest = l;
  243. closest = Math.max(Math.abs(x - obj.x), Math.abs(y - obj.y));
  244. } else if (l.threat === highest.threat) {
  245. let distance = Math.max(Math.abs(x - obj.x), Math.abs(y - obj.y));
  246. if (distance < closest) {
  247. highest = l;
  248. closest = distance;
  249. }
  250. }
  251. }
  252. if (highest)
  253. return highest.obj;
  254. //We have aggro but can't reach our target. Don't let the mob run away as if not in combat!
  255. return true;
  256. }
  257. update () {
  258. let list = this.list;
  259. let lLen = list.length;
  260. for (let i = 0; i < lLen; i++) {
  261. let l = list[i];
  262. if (l.obj.destroyed) {
  263. this.unAggro(l.obj);
  264. i--;
  265. lLen--;
  266. }
  267. }
  268. }
  269. };