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.
 
 
 

431 lines
8.2 KiB

  1. let components = require('../components/components');
  2. module.exports = {
  3. components: [],
  4. actionQueue: [],
  5. eventListeners: [],
  6. addComponent: function (type, blueprint, isTransfer) {
  7. let cpn = this[type];
  8. if (!cpn) {
  9. let template = components.components[type];
  10. if (!template) {
  11. template = extend({
  12. type: type
  13. }, blueprint || {});
  14. }
  15. cpn = extend({}, template);
  16. cpn.obj = this;
  17. this.components.push(cpn);
  18. this[cpn.type] = cpn;
  19. }
  20. if (cpn.init && this.has('instance'))
  21. cpn.init(blueprint || {}, isTransfer);
  22. else {
  23. for (let p in blueprint)
  24. cpn[p] = blueprint[p];
  25. }
  26. return cpn;
  27. },
  28. addBuiltComponent: function (cpn) {
  29. this[cpn.type] = cpn;
  30. cpn.obj = this;
  31. this.components.push(cpn);
  32. return cpn;
  33. },
  34. removeComponent: function (type) {
  35. let cpn = this[type];
  36. if (!cpn)
  37. return;
  38. cpn.destroyed = true;
  39. },
  40. extendComponent: function (ext, type, blueprint) {
  41. let template = require('../components/extensions/' + type);
  42. let cpn = this[ext];
  43. extend(cpn, template);
  44. if (template.init)
  45. cpn.init(blueprint);
  46. return cpn;
  47. },
  48. update: function () {
  49. let usedTurn = false;
  50. let cpns = this.components;
  51. let len = cpns.length;
  52. for (let i = 0; i < len; i++) {
  53. let c = cpns[i];
  54. if (c.destroyed) {
  55. this.syncer.setSelfArray(false, 'removeComponents', c.type);
  56. cpns.spliceWhere(f => (f === c));
  57. delete this[c.type];
  58. len--;
  59. i--;
  60. } else if (c.update) {
  61. if (c.update())
  62. usedTurn = true;
  63. }
  64. }
  65. if (!usedTurn)
  66. this.performQueue();
  67. },
  68. getSimple: function (self, isSave, isTransfer) {
  69. let s = this.simplify(null, self, isSave, isTransfer);
  70. if (self && !isSave && this.syncer) {
  71. this.syncer.oSelf.components
  72. .forEach(c => {
  73. if (!this[c.type])
  74. s.components.push(c);
  75. });
  76. }
  77. return s;
  78. },
  79. simplify: function (o, self, isSave, isTransfer) {
  80. let result = {};
  81. if (!o) {
  82. result.components = [];
  83. o = this;
  84. }
  85. const syncTypes = ['portrait', 'area', 'filters'];
  86. const ignoreKeysWhenNotSelf = ['account'];
  87. for (let p in o) {
  88. let value = o[p];
  89. if (value === null)
  90. continue;
  91. let type = typeof (value);
  92. if (type === 'function')
  93. continue;
  94. else if (type !== 'object') {
  95. if (self || !ignoreKeysWhenNotSelf.includes(p))
  96. result[p] = value;
  97. } else if (type === 'undefined')
  98. continue;
  99. else {
  100. if (value.type) {
  101. if (!value.simplify) {
  102. if (self) {
  103. result.components.push({
  104. type: value.type
  105. });
  106. }
  107. } else {
  108. let component = null;
  109. if (isSave && value.save)
  110. component = value.save();
  111. else if (isTransfer && value.simplifyTransfer)
  112. component = value.simplifyTransfer();
  113. else
  114. component = value.simplify(self);
  115. if (value.destroyed) {
  116. if (!component) {
  117. component = {
  118. type: value.type
  119. };
  120. }
  121. component.destroyed = true;
  122. }
  123. if (component)
  124. result.components.push(component);
  125. }
  126. } else if (syncTypes.includes(p))
  127. result[p] = value;
  128. continue;
  129. }
  130. }
  131. return result;
  132. },
  133. sendEvent: function (event, data) {
  134. process.send({
  135. method: 'event',
  136. id: this.serverId,
  137. data: {
  138. event: event,
  139. data: data
  140. }
  141. });
  142. },
  143. queue: function (msg) {
  144. const { action, auto, data: { priority } } = msg;
  145. if (action === 'spell') {
  146. let spellbook = this.spellbook;
  147. const isCasting = spellbook.isCasting();
  148. if (isCasting && (!priority || !spellbook.canCast(msg))) {
  149. if (auto)
  150. spellbook.queueAuto(msg);
  151. return;
  152. }
  153. if (isCasting)
  154. spellbook.stopCasting();
  155. this.actionQueue.spliceWhere(a => a.priority);
  156. this.actionQueue.splice(0, 0, msg);
  157. } else {
  158. if (priority) {
  159. this.spellbook.stopCasting();
  160. this.actionQueue.splice(0, 0, msg);
  161. return;
  162. }
  163. this.actionQueue.push(msg);
  164. }
  165. },
  166. dequeue: function () {
  167. if (this.actionQueue.length === 0)
  168. return null;
  169. return this.actionQueue.splice(0, 1)[0];
  170. },
  171. clearQueue: function () {
  172. if (this.has('serverId')) {
  173. this.instance.syncer.queue('onClearQueue', {
  174. id: this.id
  175. }, [this.serverId]);
  176. }
  177. this.actionQueue = [];
  178. this.fireEvent('clearQueue');
  179. },
  180. performAction: function (action) {
  181. if (action.instanceModule)
  182. return;
  183. let cpn = this[action.cpn];
  184. if (!cpn)
  185. return;
  186. cpn[action.method](action.data);
  187. },
  188. performQueue: function () {
  189. let q = this.dequeue();
  190. if (!q)
  191. return;
  192. if (q.action === 'move') {
  193. let maxDistance = 1;
  194. if ((this.actionQueue[0]) && (this.actionQueue[0].action === 'move')) {
  195. let moveEvent = {
  196. sprintChance: this.stats.values.sprintChance || 0
  197. };
  198. this.fireEvent('onBeforeTryMove', moveEvent);
  199. let physics = this.instance.physics;
  200. let sprintChance = moveEvent.sprintChance;
  201. do {
  202. if ((~~(Math.random() * 100) < sprintChance) && (!physics.isTileBlocking(q.data.x, q.data.y))) {
  203. q = this.dequeue();
  204. maxDistance++;
  205. }
  206. sprintChance -= 100;
  207. } while (sprintChance > 0 && this.actionQueue.length > 0);
  208. }
  209. q.maxDistance = maxDistance;
  210. let success = this.performMove(q);
  211. if (!success)
  212. this.clearQueue();
  213. } else if (q.action === 'spell') {
  214. let success = this.spellbook.cast(q.data);
  215. if (!success)
  216. this.performQueue();
  217. }
  218. },
  219. performMove: function (action) {
  220. const { x: xOld, y: yOld, syncer, aggro, instance: { physics } } = this;
  221. const { maxDistance = 1, force, data } = action;
  222. const { x: xNew, y: yNew } = data;
  223. if (!force) {
  224. if (physics.isTileBlocking(data.x, data.y))
  225. return true;
  226. data.success = true;
  227. this.fireEvent('beforeMove', data);
  228. if (data.success === false) {
  229. action.priority = true;
  230. this.queue(action);
  231. return true;
  232. }
  233. let deltaX = Math.abs(xOld - xNew);
  234. let deltaY = Math.abs(yOld - yNew);
  235. if (
  236. (
  237. (deltaX > maxDistance) ||
  238. (deltaY > maxDistance)
  239. ) ||
  240. (
  241. (deltaX === 0) &&
  242. (deltaY === 0)
  243. )
  244. )
  245. return false;
  246. }
  247. this.x = xNew;
  248. this.y = yNew;
  249. if (physics.addObject(this, xNew, yNew, xOld, yOld))
  250. physics.removeObject(this, xOld, yOld, xNew, yNew);
  251. else {
  252. this.x = xOld;
  253. this.y = yOld;
  254. return false;
  255. }
  256. //We can't use xNew and yNew because addObject could have changed the position (like entering a building interior with stairs)
  257. syncer.o.x = this.x;
  258. syncer.o.y = this.y;
  259. if (aggro)
  260. aggro.move();
  261. this.fireEvent('afterMove');
  262. return true;
  263. },
  264. collisionEnter: function (obj) {
  265. let cpns = this.components;
  266. let cLen = cpns.length;
  267. for (let i = 0; i < cLen; i++) {
  268. let c = cpns[i];
  269. if (c.collisionEnter) {
  270. if (c.collisionEnter(obj))
  271. return true;
  272. }
  273. }
  274. },
  275. collisionExit: function (obj) {
  276. let cpns = this.components;
  277. let cLen = cpns.length;
  278. for (let i = 0; i < cLen; i++) {
  279. let c = cpns[i];
  280. if (c.collisionExit)
  281. c.collisionExit(obj);
  282. }
  283. },
  284. onEvent: function (eventName, callback) {
  285. const entry = {
  286. eventName,
  287. callback
  288. };
  289. this.eventListeners.push(entry);
  290. return this.offEvent.bind(this, entry);
  291. },
  292. offEvent: function (entry) {
  293. this.eventListeners.spliceWhere(e => e === entry);
  294. },
  295. fireEvent: function (event) {
  296. let args = [].slice.call(arguments, 1);
  297. let cpns = this.components;
  298. let cLen = cpns.length;
  299. for (let i = 0; i < cLen; i++) {
  300. let cpn = cpns[i];
  301. if (cpn.fireEvent)
  302. cpn.fireEvent(event, args);
  303. let events = cpn.events;
  304. if (!events)
  305. continue;
  306. let callback = events[event];
  307. if (!callback)
  308. continue;
  309. callback.apply(cpn, args);
  310. }
  311. this.eventListeners.forEach(l => {
  312. const { eventName, callback } = l;
  313. if (eventName !== event)
  314. return;
  315. callback.apply(null, args);
  316. });
  317. },
  318. destroy: function () {
  319. let cpns = this.components;
  320. let len = cpns.length;
  321. for (let i = 0; i < len; i++) {
  322. let c = cpns[i];
  323. if (c.destroy)
  324. c.destroy();
  325. }
  326. },
  327. toString: function () {
  328. let res = {};
  329. for (let p in this) {
  330. if (['components', 'syncer'].includes(p))
  331. continue;
  332. let val = this[p];
  333. let stringVal = (val && val.toString) ? val.toString() : val;
  334. const type = typeof(val);
  335. if (
  336. type !== 'function' &&
  337. (
  338. type !== 'object' ||
  339. val.type
  340. )
  341. )
  342. res[p] = stringVal;
  343. }
  344. return JSON.stringify(res, null, 4).split('"').join('') + '\r\n';
  345. }
  346. };