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.
 
 
 

433 lines
8.2 KiB

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