You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

341 lines
6.9 KiB

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