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.
 
 
 

528 lines
11 KiB

  1. //System Imports
  2. const fs = require('fs');
  3. //Imports
  4. const phaseTemplate = require('./phases/phaseTemplate');
  5. const { mapList } = require('../world/mapManager');
  6. //Helpers
  7. const applyVariablesToDescription = (desc, variables) => {
  8. if (!variables)
  9. return desc;
  10. Object.entries(variables).forEach(e => {
  11. const [key, value] = e;
  12. desc = desc.split(`$${key}$`).join(value);
  13. });
  14. return desc;
  15. };
  16. //Exports
  17. module.exports = {
  18. configs: [],
  19. nextId: 0,
  20. init: function (instance) {
  21. this.instance = instance;
  22. const zoneName = this.instance.map.name;
  23. const zonePath = mapList.find(z => z.name === zoneName).path;
  24. const zoneEventPath = zonePath + '/' + zoneName + '/events';
  25. const paths = ['config/globalEvents', zoneEventPath];
  26. const files = [];
  27. paths.forEach(p => {
  28. if (!fs.existsSync(p))
  29. return;
  30. files.push(...fs.readdirSync(p).map(f => ('../' + p + '/' + f)));
  31. });
  32. this.instance.eventEmitter.emit('onBeforeGetEventList', zoneName, files);
  33. files.forEach(f => {
  34. if (!f.includes('.js'))
  35. return;
  36. const e = require(f);
  37. if (!e.disabled)
  38. this.configs.push(extend({}, e));
  39. }, this);
  40. },
  41. getEvent: function (name) {
  42. return this.configs.find(c => (c.name === name)).event.config;
  43. },
  44. setEventDescription: function (name, desc) {
  45. let config = this.getEvent(name);
  46. let event = config.event;
  47. if (!event)
  48. return;
  49. if (!config.oldDescription)
  50. config.oldDescription = config.description;
  51. if ((config.events) && (config.events.beforeSetDescription))
  52. config.events.beforeSetDescription(this);
  53. if (desc) {
  54. desc = applyVariablesToDescription(desc, event.variables);
  55. config.description = desc;
  56. }
  57. event.participators.forEach(p => p.events.syncList());
  58. },
  59. setEventRewards: function (name, rewards) {
  60. let config = this.getEvent(name);
  61. let event = config.event;
  62. if (!event)
  63. return;
  64. event.rewards = rewards;
  65. event.age = event.config.duration - 2;
  66. },
  67. setParticipantRewards: function (eventName, participantName, newRewards) {
  68. const { event: { rewards } } = this.getEvent(eventName);
  69. rewards[participantName] = newRewards;
  70. },
  71. addParticipantRewards: function (eventName, participantName, addRewards) {
  72. const { event: { rewards } } = this.getEvent(eventName);
  73. let pRewards = rewards[participantName];
  74. if (!pRewards) {
  75. pRewards = [];
  76. rewards[participantName] = pRewards;
  77. }
  78. if (!addRewards.push)
  79. addRewards = [ addRewards ];
  80. addRewards.forEach(r => {
  81. const { name, quantity = 1 } = r;
  82. const exists = pRewards.find(f => f.name === name);
  83. if (exists)
  84. exists.quantity = (exists.quantity || 1) + quantity;
  85. else
  86. pRewards.push(r);
  87. });
  88. },
  89. setWinText: function (name, text) {
  90. let config = this.getEvent(name);
  91. let event = config.event;
  92. if (!event)
  93. return;
  94. event.winText = text;
  95. },
  96. setEventVariable: function (eventName, variableName, value) {
  97. let config = this.getEvent(eventName);
  98. let event = config.event;
  99. if (!event)
  100. return;
  101. event.variables[variableName] = value;
  102. },
  103. incrementEventVariable: function (eventName, variableName, delta) {
  104. let config = this.getEvent(eventName);
  105. let event = config.event;
  106. if (!event)
  107. return;
  108. const currentValue = event.variables[variableName] || 0;
  109. event.variables[variableName] = currentValue + delta;
  110. },
  111. update: function () {
  112. let configs = this.configs;
  113. if (!configs)
  114. return;
  115. let scheduler = this.instance.scheduler;
  116. let cLen = configs.length;
  117. for (let i = 0; i < cLen; i++) {
  118. let c = configs[i];
  119. if (c.event) {
  120. this.updateEvent(c.event);
  121. const shouldStop = (
  122. c.event.done ||
  123. (
  124. c.cron &&
  125. c.durationEvent &&
  126. !scheduler.isActive(c)
  127. )
  128. );
  129. if (shouldStop)
  130. this.stopEvent(c);
  131. continue;
  132. } else if ((c.ttl) && (c.ttl > 0)) {
  133. c.ttl--;
  134. continue;
  135. } else if (c.cron) {
  136. if (c.durationEvent && !scheduler.isActive(c))
  137. continue;
  138. else if (!c.durationEvent && !scheduler.shouldRun(c))
  139. continue;
  140. } else if (c.manualTrigger)
  141. continue;
  142. c.event = this.startEvent(c);
  143. this.updateEvent(c.event);
  144. }
  145. },
  146. startEvent: function (config) {
  147. if (config.oldDescription)
  148. config.description = config.oldDescription;
  149. const event = {
  150. id: this.nextId++,
  151. config: extend({}, config),
  152. eventManager: this,
  153. variables: {},
  154. rewards: {},
  155. phases: [],
  156. participators: [],
  157. objects: [],
  158. nextPhase: 0,
  159. age: 0
  160. };
  161. event.config.event = event;
  162. const onStart = _.getDeepProperty(event, ['config', 'events', 'onStart']);
  163. if (onStart)
  164. onStart(this, event);
  165. return event;
  166. },
  167. startEventByCode: function (eventCode) {
  168. const config = this.configs.find(c => c.code === eventCode);
  169. if (!config || config.event)
  170. return;
  171. config.event = this.startEvent(config);
  172. this.updateEvent(config.event);
  173. this.instance.syncer.queue('onGetMessages', {
  174. messages: {
  175. class: 'color-pinkA',
  176. message: `The ${config.name} event has begun!`
  177. }
  178. }, -1);
  179. },
  180. stopEventByCode: function (eventCode) {
  181. const config = this.configs.find(c => c.code === eventCode);
  182. if (!config || !config.event)
  183. return;
  184. this.stopEvent(config);
  185. this.instance.syncer.queue('onGetMessages', {
  186. messages: {
  187. class: 'color-pinkA',
  188. message: `The ${config.name} event has come to an end!`
  189. }
  190. }, -1);
  191. },
  192. giveRewards: function (config) {
  193. const { event: { rewards = {} } } = config;
  194. const subject = `${config.name} Rewards`;
  195. const senderName = config.rewardSenderName;
  196. Object.entries(rewards).forEach(e => {
  197. const [ name, rList ] = e;
  198. if (!rList || !rList.length)
  199. return;
  200. //Hack: Mail is a mod. As such, events should be a mod that depends on mail
  201. if (global.mailManager) {
  202. global.mailManager.sendSystemMail({
  203. to: name,
  204. from: senderName,
  205. subject,
  206. msg: '',
  207. items: rList,
  208. notify: true
  209. });
  210. }
  211. });
  212. if ((config.events) && (config.events.afterGiveRewards))
  213. config.events.afterGiveRewards(this, config);
  214. },
  215. stopAll: function () {
  216. this.configs.forEach(c => {
  217. if (c.event)
  218. c.event.done = true;
  219. });
  220. },
  221. stopEvent: function (config) {
  222. let event = config.event;
  223. config.event.participators.forEach(function (p) {
  224. p.events.unregisterEvent(event);
  225. }, this);
  226. config.event.objects.forEach(function (o) {
  227. o.destroyed = true;
  228. this.instance.syncer.queue('onGetObject', {
  229. x: o.x,
  230. y: o.y,
  231. components: [{
  232. type: 'attackAnimation',
  233. row: 0,
  234. col: 4
  235. }]
  236. }, -1);
  237. }, this);
  238. if (event.winText) {
  239. this.instance.syncer.queue('serverModule', {
  240. module: 'cons',
  241. method: 'emit',
  242. msg: [
  243. 'event',
  244. {
  245. event: 'onGetMessages',
  246. data: {
  247. messages: {
  248. class: 'color-pinkA',
  249. message: event.winText
  250. }
  251. }
  252. }
  253. ]
  254. }, ['server']);
  255. }
  256. event.phases.forEach(function (p) {
  257. if ((p.destroy) && (!p.destroyed)) {
  258. p.destroyed = true;
  259. p.destroy();
  260. }
  261. });
  262. const onStop = _.getDeepProperty(event, ['config', 'events', 'onStop']);
  263. if (onStop)
  264. onStop(this, event);
  265. delete config.event;
  266. },
  267. handleNotification: function (event, { msg, desc, event: triggerEvent }) {
  268. if (msg) {
  269. this.instance.syncer.queue('serverModule', {
  270. module: 'cons',
  271. method: 'emit',
  272. msg: [
  273. 'event',
  274. {
  275. event: 'onGetMessages',
  276. data: {
  277. messages: {
  278. class: 'color-pinkA',
  279. message: msg
  280. }
  281. }
  282. }
  283. ]
  284. }, ['server']);
  285. }
  286. if (desc) {
  287. event.config.descTimer = desc;
  288. this.setEventDescription(event.config.name);
  289. }
  290. if (triggerEvent && event.config.events[triggerEvent])
  291. event.config.events[triggerEvent](this, event);
  292. },
  293. updateEvent: function (event) {
  294. const onTick = _.getDeepProperty(event, ['config', 'events', 'onTick']);
  295. if (onTick)
  296. onTick(this, event);
  297. let objects = event.objects;
  298. let oLen = objects.length;
  299. for (let i = 0; i < oLen; i++) {
  300. if (objects[i].destroyed) {
  301. objects.splice(i, 1);
  302. i--;
  303. oLen--;
  304. }
  305. }
  306. let currentPhases = event.phases;
  307. let cLen = currentPhases.length;
  308. let stillBusy = false;
  309. for (let i = 0; i < cLen; i++) {
  310. let phase = currentPhases[i];
  311. if (!phase.destroyed) {
  312. if (phase.end || (phase.endMark !== -1 && phase.endMark <= event.age)) {
  313. if ((phase.destroy) && (!phase.destroyed))
  314. phase.destroy();
  315. phase.destroyed = true;
  316. continue;
  317. } else {
  318. if (phase.has('ttl')) {
  319. if (phase.ttl === 0) {
  320. phase.end = true;
  321. continue;
  322. }
  323. phase.ttl--;
  324. stillBusy = true;
  325. } else if (!phase.auto)
  326. stillBusy = true;
  327. phase.update(event);
  328. }
  329. }
  330. }
  331. const notifications = event.config.notifications || [];
  332. notifications.forEach(n => {
  333. if (n.mark === event.age)
  334. this.handleNotification(event, n);
  335. });
  336. event.age++;
  337. if (event.age === event.config.duration)
  338. event.done = true;
  339. else if ((event.config.prizeTime) && (event.age === event.config.prizeTime))
  340. this.giveRewards(event.config);
  341. if (stillBusy)
  342. return;
  343. let config = event.config;
  344. let phases = config.phases;
  345. let pLen = phases.length;
  346. for (let i = event.nextPhase; i < pLen; i++) {
  347. let p = phases[i];
  348. let phase = event.phases[i];
  349. if (!phase) {
  350. let phaseFile = 'phase' + p.type[0].toUpperCase() + p.type.substr(1);
  351. let typeTemplate = require('./phases/' + phaseFile);
  352. phase = extend({
  353. instance: this.instance,
  354. event: event
  355. }, phaseTemplate, typeTemplate, p);
  356. event.phases.push(phase);
  357. event.currentPhase = phase;
  358. }
  359. event.nextPhase = i + 1;
  360. phase.init(event);
  361. if (!p.auto) {
  362. stillBusy = true;
  363. break;
  364. }
  365. }
  366. if ((event.nextPhase >= pLen) && (!stillBusy))
  367. event.done = true;
  368. let oList = this.instance.objects.objects;
  369. oLen = oList.length;
  370. for (let i = 0; i < oLen; i++) {
  371. let o = oList[i];
  372. if (!o.player)
  373. continue;
  374. o.events.events.afterMove.call(o.events);
  375. }
  376. },
  377. getCloseEvents: function (obj) {
  378. let x = obj.x;
  379. let y = obj.y;
  380. let configs = this.configs;
  381. if (!configs)
  382. return;
  383. let cLen = configs.length;
  384. let result = [];
  385. for (let i = 0; i < cLen; i++) {
  386. let event = configs[i].event;
  387. if (!event)
  388. continue;
  389. let exists = event.participators.find(p => (p.name === obj.name));
  390. if (exists) {
  391. event.participators.spliceWhere(p => (p === exists));
  392. event.participators.push(obj);
  393. result.push(event);
  394. continue;
  395. }
  396. let distance = event.config.distance;
  397. if (distance === -1) {
  398. event.participators.push(obj);
  399. result.push(event);
  400. if (event.config.events && event.config.events.onParticipantJoin)
  401. event.config.events.onParticipantJoin(this, obj);
  402. continue;
  403. }
  404. let objects = event.objects;
  405. let oLen = objects.length;
  406. for (let j = 0; j < oLen; j++) {
  407. let o = objects[j];
  408. if (
  409. (distance === -1) ||
  410. (!distance) ||
  411. (
  412. (Math.abs(x - o.x) < distance) &&
  413. (Math.abs(y - o.y) < distance)
  414. )
  415. ) {
  416. event.participators.push(obj);
  417. result.push(event);
  418. if (event.config.events && event.config.events.onParticipantJoin)
  419. event.config.events.onParticipantJoin(this, obj);
  420. break;
  421. }
  422. }
  423. }
  424. return result;
  425. }
  426. };