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.
 
 
 

209 lines
4.6 KiB

  1. let itemGenerator = require('../items/generator');
  2. let abs = Math.abs.bind(Math);
  3. let rnd = Math.random.bind(Math);
  4. let max = Math.max.bind(Math);
  5. module.exports = {
  6. type: 'mob',
  7. target: null,
  8. physics: null,
  9. originX: 0,
  10. originY: 0,
  11. walkDistance: 1,
  12. maxChaseDistance: 25,
  13. goHome: false,
  14. init: function (blueprint) {
  15. this.physics = this.obj.instance.physics;
  16. this.originX = this.obj.x;
  17. this.originY = this.obj.y;
  18. },
  19. update: function () {
  20. let obj = this.obj;
  21. let target = null;
  22. if (obj.aggro)
  23. target = obj.aggro.getHighest();
  24. //Have we reached home?
  25. if (this.goHome) {
  26. let distanceFromHome = Math.max(Math.abs(this.originX - obj.x), Math.abs(this.originY - obj.y));
  27. if (distanceFromHome < this.walkDistance)
  28. this.goHome = false;
  29. }
  30. //Are we too far from home?
  31. if ((!this.goHome) && (!obj.follower) && (target)) {
  32. if (!this.canChase(target)) {
  33. obj.clearQueue();
  34. obj.aggro.unAggro(target);
  35. target = obj.aggro.getHighest();
  36. }
  37. }
  38. if (!this.goHome) {
  39. //Are we in fight mode?
  40. if ((target) && (target !== obj) && ((!obj.follower) || (obj.follower.master !== target))) {
  41. this.fight(target);
  42. return;
  43. }
  44. //Is fight mode over?
  45. else if ((!target) && (this.target)) {
  46. this.target = null;
  47. obj.clearQueue();
  48. this.goHome = true;
  49. }
  50. }
  51. //If we're already going somewhere, don't calculate a new path
  52. if (obj.actionQueue.length > 0)
  53. return;
  54. //Unless we're going home, don't always move
  55. if ((!this.goHome) && (rnd() < 0.85))
  56. return;
  57. //don't move around if we're not allowed to, unless we're going home
  58. let walkDistance = this.walkDistance;
  59. if ((!this.goHome) && (walkDistance <= 0))
  60. return;
  61. let toX = this.originX + ~~(rnd() * (walkDistance * 2)) - walkDistance;
  62. let toY = this.originY + ~~(rnd() * (walkDistance * 2)) - walkDistance;
  63. if (!this.physics.isCellOpen(toX, toY))
  64. return;
  65. let path = this.physics.getPath({
  66. x: obj.x,
  67. y: obj.y
  68. }, {
  69. x: toX,
  70. y: toY
  71. }, false);
  72. let pLen = path.length;
  73. for (let i = 0; i < pLen; i++) {
  74. let p = path[i];
  75. obj.queue({
  76. action: 'move',
  77. data: {
  78. x: p.x,
  79. y: p.y
  80. }
  81. });
  82. }
  83. //We use goHometo force followers to follow us around but they should never stay in that state
  84. // since it messes with combat
  85. if (obj.follower)
  86. this.goHome = false;
  87. },
  88. fight: function (target) {
  89. if (this.target !== target) {
  90. this.obj.clearQueue();
  91. this.target = target;
  92. }
  93. //If the target is true, it means we can't reach the target and should wait for a new one
  94. if (this.target === true)
  95. return;
  96. let obj = this.obj;
  97. let x = obj.x;
  98. let y = obj.y;
  99. let tx = ~~target.x;
  100. let ty = ~~target.y;
  101. let distance = max(abs(x - tx), abs(y - ty));
  102. let furthestRange = obj.spellbook.getFurthestRange();
  103. let doesCollide = null;
  104. let hasLos = null;
  105. if (distance <= furthestRange) {
  106. doesCollide = this.physics.mobsCollide(x, y, obj);
  107. if (!doesCollide) {
  108. hasLos = this.physics.hasLos(x, y, tx, ty);
  109. if (hasLos) {
  110. if (((obj.follower) && (obj.follower.master.player)) || (rnd() < 0.65)) {
  111. let spell = obj.spellbook.getRandomSpell(target);
  112. let success = obj.spellbook.cast({
  113. spell: spell,
  114. target: target
  115. });
  116. //null means we don't have LoS
  117. if (success !== null)
  118. return;
  119. hasLos = false;
  120. } else
  121. return;
  122. }
  123. }
  124. }
  125. let targetPos = this.physics.getClosestPos(x, y, tx, ty, target);
  126. if (!targetPos) {
  127. //Find a new target
  128. obj.aggro.ignore(target);
  129. //TODO: Don't skip a turn
  130. return;
  131. }
  132. let newDistance = max(abs(targetPos.x - tx), abs(targetPos.y - ty));
  133. if ((newDistance >= distance) && (newDistance > furthestRange)) {
  134. if (hasLos === null)
  135. hasLos = this.physics.hasLos(x, y, tx, ty);
  136. if (hasLos) {
  137. if (doesCollide === null)
  138. doesCollide = this.physics.mobsCollide(x, y, obj);
  139. if (!doesCollide) {
  140. obj.aggro.ignore(target);
  141. return;
  142. }
  143. } else {
  144. if (doesCollide === null)
  145. doesCollide = this.physics.mobsCollide(x, y, obj);
  146. if (!doesCollide) {
  147. obj.aggro.ignore(target);
  148. return;
  149. }
  150. }
  151. }
  152. let path = this.physics.getPath({
  153. x: x,
  154. y: y
  155. }, {
  156. x: targetPos.x,
  157. y: targetPos.y
  158. });
  159. if (path.length === 0) {
  160. obj.aggro.ignore(target);
  161. //TODO: Don't skip a turn
  162. return;
  163. }
  164. let p = path[0];
  165. obj.queue({
  166. action: 'move',
  167. data: {
  168. x: p.x,
  169. y: p.y
  170. }
  171. });
  172. },
  173. canChase: function (obj) {
  174. let distanceFromHome = Math.max(Math.abs(this.originX - obj.x), Math.abs(this.originY - obj.y));
  175. return ((!this.goHome) && (distanceFromHome <= this.maxChaseDistance));
  176. }
  177. };