選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

523 行
11 KiB

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