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.
 
 
 

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