warframe-worldstate-parser 2.23.1 → 2.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/lib/Alert.js +8 -8
  2. package/lib/CambionCycle.js +1 -5
  3. package/lib/CetusCycle.js +3 -3
  4. package/lib/ChallengeInstance.js +6 -6
  5. package/lib/ConclaveChallenge.js +2 -5
  6. package/lib/ConstructionProgress.js +4 -2
  7. package/lib/DailyDeal.js +1 -3
  8. package/lib/DarkSector.js +11 -8
  9. package/lib/EarthCycle.js +4 -4
  10. package/lib/Fissure.js +3 -3
  11. package/lib/FlashSale.js +2 -7
  12. package/lib/GlobalUpgrade.js +5 -5
  13. package/lib/Invasion.js +11 -10
  14. package/lib/Kuva.js +16 -10
  15. package/lib/Mission.js +30 -20
  16. package/lib/News.js +13 -15
  17. package/lib/Nightwave.js +7 -4
  18. package/lib/NightwaveChallenge.js +2 -4
  19. package/lib/PersistentEnemy.js +1 -3
  20. package/lib/Reward.js +64 -60
  21. package/lib/SentientOutpost.js +6 -8
  22. package/lib/Simaris.js +5 -3
  23. package/lib/Sortie.js +12 -8
  24. package/lib/SortieVariant.js +13 -13
  25. package/lib/SyndicateJob.js +19 -11
  26. package/lib/SyndicateMission.js +6 -6
  27. package/lib/VallisCycle.js +2 -2
  28. package/lib/VoidTrader.js +9 -13
  29. package/lib/WeeklyChallenge.js +4 -3
  30. package/lib/WorldEvent.js +24 -30
  31. package/lib/WorldState.js +35 -24
  32. package/lib/WorldstateObject.js +3 -3
  33. package/lib/ZarimanCycle.js +141 -0
  34. package/lib/timeDate.js +2 -2
  35. package/lib/translation.js +22 -12
  36. package/package.json +3 -118
  37. package/types/lib/Alert.d.ts +1 -1
  38. package/types/lib/CambionCycle.d.ts +1 -0
  39. package/types/lib/ConclaveChallenge.d.ts +1 -1
  40. package/types/lib/DailyDeal.d.ts +1 -1
  41. package/types/lib/DarkSector.d.ts +1 -1
  42. package/types/lib/Fissure.d.ts +3 -3
  43. package/types/lib/FlashSale.d.ts +1 -1
  44. package/types/lib/GlobalUpgrade.d.ts +1 -1
  45. package/types/lib/Invasion.d.ts +4 -4
  46. package/types/lib/Kuva.d.ts +1 -6
  47. package/types/lib/Mission.d.ts +11 -4
  48. package/types/lib/News.d.ts +4 -4
  49. package/types/lib/Nightwave.d.ts +1 -1
  50. package/types/lib/NightwaveChallenge.d.ts +1 -1
  51. package/types/lib/PersistentEnemy.d.ts +1 -1
  52. package/types/lib/SentientOutpost.d.ts +1 -1
  53. package/types/lib/Sortie.d.ts +1 -1
  54. package/types/lib/SortieVariant.d.ts +10 -10
  55. package/types/lib/SyndicateMission.d.ts +1 -1
  56. package/types/lib/VoidTrader.d.ts +1 -1
  57. package/types/lib/WorldEvent.d.ts +3 -3
  58. package/types/lib/WorldState.d.ts +5 -0
  59. package/types/lib/ZarimanCycle.d.ts +55 -0
package/lib/VoidTrader.js CHANGED
@@ -17,9 +17,7 @@ class VoidTrader extends WorldstateObject {
17
17
  * @param {TimeDateFunctions} deps.timeDate The time and date functions
18
18
  * @param {string} deps.locale Locale to use for translations
19
19
  */
20
- constructor(data, {
21
- mdConfig, translator, timeDate, locale,
22
- }) {
20
+ constructor(data, { mdConfig, translator, timeDate, locale }) {
23
21
  super(data, { timeDate });
24
22
 
25
23
  /**
@@ -54,7 +52,7 @@ class VoidTrader extends WorldstateObject {
54
52
  * The void trader's name
55
53
  * @type {string}
56
54
  */
57
- this.character = data.Character ? data.Character.replace('Baro\'Ki Teel', 'Baro Ki\'Teer') : '';
55
+ this.character = data.Character ? data.Character.replace("Baro'Ki Teel", "Baro Ki'Teer") : '';
58
56
 
59
57
  /**
60
58
  * The node at which the Void Trader appears
@@ -66,9 +64,7 @@ class VoidTrader extends WorldstateObject {
66
64
  * The trader's inventory
67
65
  * @type {VoidTraderItem[]}
68
66
  */
69
- this.inventory = data.Manifest
70
- ? data.Manifest.map((i) => new VoidTraderItem(i, { translator, locale }))
71
- : [];
67
+ this.inventory = data.Manifest ? data.Manifest.map((i) => new VoidTraderItem(i, { translator, locale })) : [];
72
68
 
73
69
  /**
74
70
  * Pseudo Identifier for identifying changes in inventory
@@ -99,8 +95,7 @@ class VoidTrader extends WorldstateObject {
99
95
  this.initialStart = timeDate.parseDate(data.InitialStartDate);
100
96
  this.completed = data.Completed;
101
97
  this.schedule = data.ScheduleInfo
102
- ? data.ScheduleInfo
103
- .map((i) => new VoidTraderSchedule(i, { timeDate, translator, locale }))
98
+ ? data.ScheduleInfo.map((i) => new VoidTraderSchedule(i, { timeDate, translator, locale }))
104
99
  : [];
105
100
  }
106
101
 
@@ -109,8 +104,7 @@ class VoidTrader extends WorldstateObject {
109
104
  * @returns {boolean}
110
105
  */
111
106
  isActive() {
112
- return (this.timeDate.fromNow(this.activation) < 0)
113
- && (this.timeDate.fromNow(this.expiry) > 0);
107
+ return this.timeDate.fromNow(this.activation) < 0 && this.timeDate.fromNow(this.expiry) > 0;
114
108
  }
115
109
 
116
110
  /**
@@ -137,8 +131,10 @@ class VoidTrader extends WorldstateObject {
137
131
  if (!this.isActive()) {
138
132
  const timeDelta = this.timeDate.fromNow(this.activation);
139
133
  const nextArrivalTime = this.timeDate.timeDeltaToString(timeDelta);
140
- return `${this.mdConfig.codeMulti}${this.character} is not here yet, he will arrive in `
141
- + `${nextArrivalTime} at ${this.location}${this.mdConfig.blockEnd}`;
134
+ return (
135
+ `${this.mdConfig.codeMulti}${this.character} is not here yet, he will arrive in ` +
136
+ `${nextArrivalTime} at ${this.location}${this.mdConfig.blockEnd}`
137
+ );
142
138
  }
143
139
 
144
140
  const inventoryString = this.inventory
@@ -18,8 +18,7 @@ class WeeklyChallenge extends WorldstateObject {
18
18
  constructor(data, { timeDate, translator }) {
19
19
  super(data, { timeDate });
20
20
 
21
- this.challenges = data.Challenges
22
- .map((challengeData) => new ChallengeInstance(challengeData, { translator }));
21
+ this.challenges = data.Challenges.map((challengeData) => new ChallengeInstance(challengeData, { translator }));
23
22
  }
24
23
 
25
24
  /**
@@ -27,7 +26,9 @@ class WeeklyChallenge extends WorldstateObject {
27
26
  * @returns {string}
28
27
  */
29
28
  toString() {
30
- return `Starts: ${this.getStartString()}\nEnds: ${this.getEndString()}\nChallenges:\n${this.challenges.map((challenge) => `\t${challenge.toString()}`).join('\n')}`;
29
+ return `Starts: ${this.getStartString()}\nEnds: ${this.getEndString()}\nChallenges:\n${this.challenges
30
+ .map((challenge) => `\t${challenge.toString()}`)
31
+ .join('\n')}`;
31
32
  }
32
33
  }
33
34
 
package/lib/WorldEvent.js CHANGED
@@ -36,13 +36,15 @@ class WorldEvent extends WorldstateObject {
36
36
  * @param {Reward} deps.Reward The Reward parser
37
37
  * @param {string} deps.locale Locale to use for translations
38
38
  */
39
- constructor(data, {
40
- mdConfig, translator, timeDate, Reward, locale,
41
- }) {
39
+ constructor(data, { mdConfig, translator, timeDate, Reward, locale }) {
42
40
  super(data, { timeDate });
43
41
 
44
42
  const opts = {
45
- translator, mdConfig, Reward, timeDate, locale,
43
+ translator,
44
+ mdConfig,
45
+ Reward,
46
+ timeDate,
47
+ locale,
46
48
  };
47
49
 
48
50
  /**
@@ -118,9 +120,7 @@ class WorldEvent extends WorldstateObject {
118
120
  * The other nodes where the event takes place
119
121
  * @type {string[]}
120
122
  */
121
- this.concurrentNodes = data.ConcurrentNodes
122
- ? data.ConcurrentNodes.map((n) => translator.node(n), locale)
123
- : [];
123
+ this.concurrentNodes = data.ConcurrentNodes ? data.ConcurrentNodes.map((n) => translator.node(n), locale) : [];
124
124
 
125
125
  /**
126
126
  * The victim node
@@ -132,16 +132,14 @@ class WorldEvent extends WorldstateObject {
132
132
  * The score description
133
133
  * @type {?string}
134
134
  */
135
- this.scoreLocTag = data.Fomorian
136
- ? 'Fomorian Assault Score'
137
- : translator.languageString(data.ScoreLocTag, locale);
135
+ this.scoreLocTag = data.Fomorian ? 'Fomorian Assault Score' : translator.languageString(data.ScoreLocTag, locale);
138
136
 
139
137
  /**
140
138
  * The event's rewards
141
139
  * @type {Reward[]}
142
140
  */
143
141
  this.rewards = Object.keys(data)
144
- .filter((k) => (k.includes('Reward') || k.includes('reward')))
142
+ .filter((k) => k.includes('Reward') || k.includes('reward'))
145
143
  .map((k) => new Reward(data[k], opts));
146
144
 
147
145
  /**
@@ -154,16 +152,14 @@ class WorldEvent extends WorldstateObject {
154
152
  * Health remaining for the target
155
153
  * @type {Number}
156
154
  */
157
- this.health = typeof data.HealthPct !== 'undefined'
158
- ? Number.parseFloat(((data.HealthPct || 0.00) * 100).toFixed(2))
159
- : undefined;
155
+ this.health =
156
+ typeof data.HealthPct !== 'undefined' ? Number.parseFloat(((data.HealthPct || 0.0) * 100).toFixed(2)) : undefined;
160
157
 
161
158
  if (data.JobAffiliationTag) {
162
159
  this.affiliatedWith = translator.syndicate(data.JobAffiliationTag, locale);
163
160
  if (data.Jobs) {
164
161
  this.jobs = (data.Jobs || []).map((j) => new SyndicateJob(j, this.expiry, opts));
165
- this.previousJobs = (data.PreviousJobs || [])
166
- .map((j) => new SyndicateJob(j, this.expiry, opts));
162
+ this.previousJobs = (data.PreviousJobs || []).map((j) => new SyndicateJob(j, this.expiry, opts));
167
163
  }
168
164
  }
169
165
 
@@ -180,7 +176,7 @@ class WorldEvent extends WorldstateObject {
180
176
  this.interimSteps = [];
181
177
 
182
178
  (data.InterimRewards || []).forEach((reward, index) => {
183
- const msg = ((data.InterimRewardMessages || [])[index] || {});
179
+ const msg = (data.InterimRewardMessages || [])[index] || {};
184
180
  this.interimSteps[index] = {
185
181
  goal: Number.parseInt(data.InterimGoals[index], 10),
186
182
  reward: reward ? new Reward(reward, opts) : undefined,
@@ -191,13 +187,14 @@ class WorldEvent extends WorldstateObject {
191
187
  senderIcon: msg.senderIcon,
192
188
  attachments: msg.attachments,
193
189
  },
190
+ // eslint-disable-next-line no-underscore-dangle
194
191
  winnerCount: (data._interimWinnerCounts || [])[index],
195
192
  };
196
193
  });
197
194
 
198
195
  /**
199
196
  * Progress Steps, if any are present
200
- * @type {ProgessStep[]}
197
+ * @type {ProgressStep[]}
201
198
  */
202
199
  this.progressSteps = [];
203
200
 
@@ -213,8 +210,7 @@ class WorldEvent extends WorldstateObject {
213
210
  * Total of all MultiProgress
214
211
  * @type {Number}
215
212
  */
216
- this.progressTotal = Number.parseFloat(data.MultiProgress
217
- .reduce((accumulator, val) => accumulator + val));
213
+ this.progressTotal = Number.parseFloat(data.MultiProgress.reduce((accumulator, val) => accumulator + val));
218
214
  }
219
215
 
220
216
  /**
@@ -237,15 +233,13 @@ class WorldEvent extends WorldstateObject {
237
233
  * Affectors for this mission
238
234
  * @type {string[]}
239
235
  */
240
- this.regionDrops = (data.RegionDrops || [])
241
- .map((drop) => translator.languageString(drop, locale));
236
+ this.regionDrops = (data.RegionDrops || []).map((drop) => translator.languageString(drop, locale));
242
237
 
243
238
  /**
244
239
  * Archwing Drops in effect while this event is active
245
240
  * @type {string[]}
246
241
  */
247
- this.archwingDrops = (data.ArchwingDrops || [])
248
- .map((drop) => translator.languageString(drop, locale));
242
+ this.archwingDrops = (data.ArchwingDrops || []).map((drop) => translator.languageString(drop, locale));
249
243
 
250
244
  this.asString = this.toString();
251
245
 
@@ -285,9 +279,7 @@ class WorldEvent extends WorldstateObject {
285
279
  * @returns {string}
286
280
  */
287
281
  toString() {
288
- let lines = [
289
- `${this.description} : ${this.faction}`,
290
- ];
282
+ let lines = [`${this.description} : ${this.faction}`];
291
283
 
292
284
  if (this.scoreLocTag && this.maximumScore) {
293
285
  lines.push(`${this.scoreLocTag} : ${this.maximumScore}`);
@@ -307,9 +299,11 @@ class WorldEvent extends WorldstateObject {
307
299
  lines.push(`${this.health}% Remaining`);
308
300
  }
309
301
 
310
- if (this.affiliatedWith) {
311
- lines.push(`${this.affiliatedWith} will reward you for performing `
312
- + `${this.jobs.map((job) => job.type).join(', ')} job${this.jobs.length > 1 ? 's' : ''}`);
302
+ if (this.affiliatedWith && this.jobs) {
303
+ lines.push(
304
+ `${this.affiliatedWith} will reward you for performing ` +
305
+ `${this.jobs.map((job) => job.type).join(', ')} job${this.jobs.length > 1 ? 's' : ''}`
306
+ );
313
307
  }
314
308
 
315
309
  return lines.join(this.mdConfig.lineEnd);
package/lib/WorldState.js CHANGED
@@ -27,6 +27,7 @@ const EarthCycle = require('./EarthCycle');
27
27
  const CetusCycle = require('./CetusCycle');
28
28
  const ConstructionProgress = require('./ConstructionProgress');
29
29
  const VallisCycle = require('./VallisCycle');
30
+ const ZarimanCycle = require('./ZarimanCycle');
30
31
  const WeeklyChallenge = require('./WeeklyChallenge');
31
32
  const Nightwave = require('./Nightwave');
32
33
  const Kuva = require('./Kuva');
@@ -41,8 +42,8 @@ const ExternalMission = require('./supporting/ExternalMission');
41
42
  const MarkdownSettings = require('./supporting/MarkdownSettings');
42
43
  /* eslint-enable no-unused-vars */
43
44
 
44
- const safeArray = (arr) => (arr || []);
45
- const safeObj = (obj) => (obj || {});
45
+ const safeArray = (arr) => arr || [];
46
+ const safeObj = (obj) => obj || {};
46
47
 
47
48
  /**
48
49
  * Default Dependency object
@@ -81,7 +82,7 @@ function parseArray(ParserClass, dataArray, deps, uniqueField) {
81
82
  const arr = (dataArray || []).map((d) => new ParserClass(d, deps));
82
83
  if (uniqueField) {
83
84
  const utemp = {};
84
- arr.sort((a, b) => a.id.localeCompare((b.id)));
85
+ arr.sort((a, b) => a.id.localeCompare(b.id));
85
86
  arr.forEach((obj) => {
86
87
  utemp[obj[uniqueField]] = obj;
87
88
  });
@@ -126,9 +127,13 @@ module.exports = class WorldState {
126
127
  * The in-game news
127
128
  * @type {Array.<News>}
128
129
  */
129
- this.news = parseArray(deps.News, (data.Events
130
- ? data.Events.filter((e) => typeof e.Messages.find((msg) => msg.LanguageCode === deps.locale) !== 'undefined')
131
- : []), deps);
130
+ this.news = parseArray(
131
+ deps.News,
132
+ data.Events
133
+ ? data.Events.filter((e) => typeof e.Messages.find((msg) => msg.LanguageCode === deps.locale) !== 'undefined')
134
+ : [],
135
+ deps
136
+ );
132
137
 
133
138
  /**
134
139
  * The current events
@@ -158,8 +163,9 @@ module.exports = class WorldState {
158
163
  * The current fissures: 'ActiveMissions' & 'VoidStorms'
159
164
  * @type {Array.<News>}
160
165
  */
161
- this.fissures = parseArray(deps.Fissure, data.ActiveMissions, deps)
162
- .concat(parseArray(deps.Fissure, data.VoidStorms, deps));
166
+ this.fissures = parseArray(deps.Fissure, data.ActiveMissions, deps).concat(
167
+ parseArray(deps.Fissure, data.VoidStorms, deps)
168
+ );
163
169
 
164
170
  /**
165
171
  * The current global upgrades
@@ -221,10 +227,8 @@ module.exports = class WorldState {
221
227
  */
222
228
  this.earthCycle = new EarthCycle(deps);
223
229
 
224
- const cetusSynd = safeArray(data.SyndicateMissions)
225
- .filter((syndicate) => syndicate.Tag === 'CetusSyndicate');
226
- const cetusBountyEnd = timeDate
227
- .parseDate(cetusSynd.length > 0 ? cetusSynd[0].Expiry : { $date: 0 });
230
+ const cetusSynd = safeArray(data.SyndicateMissions).filter((syndicate) => syndicate.Tag === 'CetusSyndicate');
231
+ const cetusBountyEnd = timeDate.parseDate(cetusSynd.length > 0 ? cetusSynd[0].Expiry : { $date: 0 });
228
232
 
229
233
  /**
230
234
  * The current Cetus cycle
@@ -238,28 +242,35 @@ module.exports = class WorldState {
238
242
  */
239
243
  this.cambionCycle = new CambionCycle(this.cetusCycle, deps);
240
244
 
245
+ const zarimanSynd = safeArray(data.SyndicateMissions).filter((syndicate) => syndicate.Tag === 'ZarimanSyndicate');
246
+ const zarimanBountyEnd = timeDate.parseDate(zarimanSynd.length > 0 ? zarimanSynd[0].Expiry : { $date: 0 });
247
+
248
+ /**
249
+ * The current Zariman cycle based off current time
250
+ * @type {ZarimanCycle}
251
+ */
252
+ this.zarimanCycle = new ZarimanCycle(zarimanBountyEnd, deps);
253
+
241
254
  /**
242
255
  * Weekly challenges
243
256
  * @type {Array.<WeeklyChallenge>}
244
257
  */
245
- this.weeklyChallenges = data.WeeklyChallenges
246
- ? new deps.WeeklyChallenge(data.WeeklyChallenges, deps)
247
- : [];
258
+ this.weeklyChallenges = data.WeeklyChallenges ? new deps.WeeklyChallenge(data.WeeklyChallenges, deps) : [];
248
259
 
249
- const projectPCTwithOid = data.ProjectPct ? {
250
- ProjectPct: data.ProjectPct,
251
- _id: {
252
- $oid: `${Date.now()}${data.ProjectPct[0]}`,
253
- },
254
- } : undefined;
260
+ const projectPCTwithOid = data.ProjectPct
261
+ ? {
262
+ ProjectPct: data.ProjectPct,
263
+ _id: {
264
+ $oid: `${Date.now()}${data.ProjectPct[0]}`,
265
+ },
266
+ }
267
+ : undefined;
255
268
 
256
269
  /**
257
270
  * The Current construction progress for Fomorians/Razorback/etc.
258
271
  * @type {ConstructionProgress}
259
272
  */
260
- this.constructionProgress = projectPCTwithOid
261
- ? new ConstructionProgress(projectPCTwithOid, deps)
262
- : {};
273
+ this.constructionProgress = projectPCTwithOid ? new ConstructionProgress(projectPCTwithOid, deps) : {};
263
274
 
264
275
  /**
265
276
  * The current Orb Vallis cycle state
@@ -12,7 +12,8 @@ class WorldstateObject {
12
12
  * The object's id field
13
13
  * @type {string}
14
14
  */
15
- this.id = data._id ? (data._id.$oid || data._id.$id) : undefined;
15
+ // eslint-disable-next-line no-underscore-dangle
16
+ this.id = data._id ? data._id.$oid || data._id.$id : undefined;
16
17
 
17
18
  /**
18
19
  * The time and date functions
@@ -67,8 +68,7 @@ class WorldstateObject {
67
68
  * @returns {boolean}
68
69
  */
69
70
  isActive() {
70
- return (this.timeDate.fromNow(this.activation) < 0)
71
- && (this.timeDate.fromNow(this.expiry) > 0);
71
+ return this.timeDate.fromNow(this.activation) < 0 && this.timeDate.fromNow(this.expiry) > 0;
72
72
  }
73
73
 
74
74
  /**
@@ -0,0 +1,141 @@
1
+ 'use strict';
2
+
3
+ const WorldstateObject = require('./WorldstateObject');
4
+
5
+ // This is a confirmed starting time for Corpus (in millis)
6
+ // All faction operation should use this as a calculation point
7
+ // Unless there's a better logic
8
+ const corpusTimeMillis = 1655182800000;
9
+ const fullCycle = 18000000;
10
+ const stateMaximum = 9000000;
11
+
12
+ /**
13
+ * Represents the current Zariman Corpus/Grineer Cycle
14
+ * @extends {WorldstateObject}
15
+ */
16
+ module.exports = class ZarimanCycle extends WorldstateObject {
17
+ /**
18
+ * @param {Date} bountiesEndDate The current zariman cycle expiry
19
+ * @param {Object} deps The dependencies object
20
+ * @param {MarkdownSettings} deps.mdConfig The markdown settings
21
+ * @param {TimeDateFunctions} deps.timeDate The time and date functions
22
+ */
23
+ constructor(bountiesEndDate, { mdConfig, timeDate }) {
24
+ super({ _id: { $oid: 'zarimanCycle0' } }, { timeDate });
25
+
26
+ /**
27
+ * The markdown settings
28
+ * @type {MarkdownSettings}
29
+ * @private
30
+ */
31
+ this.mdConfig = mdConfig;
32
+ Object.defineProperty(this, 'mdConfig', { enumerable: false, configurable: false });
33
+
34
+ /**
35
+ * The end of the Zariman bounties timer, the faction changes exactly half way through
36
+ * @type {Date}
37
+ * @private
38
+ */
39
+ this.bountiesEndDate = bountiesEndDate;
40
+ Object.defineProperty(this, 'currentTime', { enumerable: false, configurable: false });
41
+
42
+ /**
43
+ * The time and date functions
44
+ * @type {TimeDateFunctions}
45
+ * @private
46
+ */
47
+ this.timeDate = timeDate;
48
+ Object.defineProperty(this, 'timeDate', { enumerable: false, configurable: false });
49
+
50
+ /**
51
+ * The current zariman cycle, for calculating the other fields
52
+ * @type {Object}
53
+ * @private
54
+ */
55
+ const ec = this.getCurrentZarimanCycle();
56
+ Object.defineProperty(this, 'ec', { enumerable: false, configurable: false });
57
+
58
+ /**
59
+ * The date and time at which the event ends
60
+ * @type {Date}
61
+ */
62
+ this.expiry = ec.expiry;
63
+
64
+ /**
65
+ * The date and time at which the event started
66
+ * @type {Date}
67
+ */
68
+ this.activation = new Date(ec.start);
69
+
70
+ /**
71
+ * Whether or not this it's corpus or grineer
72
+ * @type {boolean}
73
+ */
74
+ this.isCorpus = ec.isCorpus;
75
+
76
+ /**
77
+ * Current cycle state. One of `corpus`, `grineer`
78
+ * @type {string}
79
+ */
80
+ this.state = ec.state;
81
+
82
+ /**
83
+ * Time remaining string
84
+ * @type {string}
85
+ */
86
+ this.timeLeft = ec.timeLeft;
87
+
88
+ this.id = `zarimanCycle${this.expiry.getTime()}`;
89
+
90
+ this.shortString = `${this.timeLeft.replace(/\s\d*s/gi, '')} to ${this.isCorpus ? 'grineer' : 'corpus'}`;
91
+ }
92
+
93
+ /**
94
+ * Get whether or not the event has expired
95
+ * @returns {boolean}
96
+ */
97
+ getExpired() {
98
+ return this.expiry ? this.timeDate.fromNow(this.expiry) < 0 : true;
99
+ }
100
+
101
+ getCurrentZarimanCycle() {
102
+ const now = Date.now();
103
+ // determine if it is corpus cycle or grineer cycle based on bounty end time
104
+ // we subtract 5000 millis (5 seconds) to ensure the corpus/grineer calculation is correct
105
+ const bountiesClone = this.bountiesEndDate.getTime() - 5000;
106
+ const millisLeft = this.timeDate.fromNow(new Date(bountiesClone));
107
+ // the following line is a modulus operation
108
+ // this ensures that our calculation is correct if bountiesClone is before corpusTimeMillis
109
+ // if you really care, read https://torstencurdt.com/tech/posts/modulo-of-negative-numbers/
110
+ const cycleTimeElapsed = (((bountiesClone - corpusTimeMillis) % fullCycle) + fullCycle) % fullCycle;
111
+ const cycleTimeLeft = fullCycle - cycleTimeElapsed;
112
+ // if timeInCycle is more than 2.5 hours, it is corpus, otherwise it is grineer
113
+ const isCorpus = cycleTimeLeft > stateMaximum;
114
+
115
+ const minutesCoef = 1000 * 60;
116
+ const expiry = new Date(Math.round((now + millisLeft) / minutesCoef) * minutesCoef);
117
+ const state = isCorpus ? 'corpus' : 'grineer';
118
+
119
+ return {
120
+ isCorpus,
121
+ timeLeft: this.timeDate.timeDeltaToString(millisLeft),
122
+ expiry,
123
+ expiresIn: millisLeft,
124
+ state,
125
+ start: expiry.getTime() - stateMaximum,
126
+ };
127
+ }
128
+
129
+ /**
130
+ * The event's string representation
131
+ * @returns {string}
132
+ */
133
+ toString() {
134
+ const lines = [
135
+ `Operator, Zariman Ten Zero is currently occupied by ${this.state}`,
136
+ `Time remaining until ${this.isCorpus ? 'grineer' : 'corpus'} takeover: ${this.timeLeft}`,
137
+ ];
138
+
139
+ return lines.join(this.mdConfig.lineEnd);
140
+ }
141
+ };
package/lib/timeDate.js CHANGED
@@ -74,8 +74,8 @@ function toNow(d, now = Date.now) {
74
74
  * @returns {Date}
75
75
  */
76
76
  function parseDate(d) {
77
- const safeD = (d || epochZero);
78
- const dt = (safeD.$date || epochZero.$date);
77
+ const safeD = d || epochZero;
78
+ const dt = safeD.$date || epochZero.$date;
79
79
  return new Date(safeD.$date ? Number(dt.$numberLong) : 1000 * d.sec);
80
80
  }
81
81
 
@@ -32,7 +32,10 @@ function toTitleCase(str) {
32
32
  }
33
33
 
34
34
  function splitResourceName(str) {
35
- return str.split(/([A-Z]?[^A-Z]*)/g).filter((item) => item).join(' ');
35
+ return str
36
+ .split(/([A-Z]?[^A-Z]*)/g)
37
+ .filter((item) => item)
38
+ .join(' ');
36
39
  }
37
40
 
38
41
  const i18n = (locale = 'en') => data[locale] || data;
@@ -47,7 +50,8 @@ function faction(key, dataOverride) {
47
50
  function node(key, dataOverride) {
48
51
  if (key in i18n(dataOverride).solNodes) {
49
52
  return i18n(dataOverride).solNodes[key].value;
50
- } if (key) {
53
+ }
54
+ if (key) {
51
55
  return key.split('/').slice(-1)[0];
52
56
  }
53
57
  return key;
@@ -56,7 +60,8 @@ function node(key, dataOverride) {
56
60
  function nodeMissionType(key, dataOverride) {
57
61
  if (key in i18n(dataOverride).solNodes) {
58
62
  return i18n(dataOverride).solNodes[key].type;
59
- } if (key) {
63
+ }
64
+ if (key) {
60
65
  return key.split('/').slice(-1)[0];
61
66
  }
62
67
  return key;
@@ -65,7 +70,8 @@ function nodeMissionType(key, dataOverride) {
65
70
  function nodeEnemy(key, dataOverride) {
66
71
  if (key in i18n(dataOverride).solNodes) {
67
72
  return i18n(dataOverride).solNodes[key].enemy;
68
- } if (key) {
73
+ }
74
+ if (key) {
69
75
  return key.split('/').slice(-1)[0];
70
76
  }
71
77
  return key;
@@ -75,7 +81,8 @@ function languageString(key, dataOverride) {
75
81
  const lowerKey = String(key).toLowerCase();
76
82
  if (lowerKey in i18n(dataOverride).languages) {
77
83
  return i18n(dataOverride).languages[lowerKey].value;
78
- } if (key) {
84
+ }
85
+ if (key) {
79
86
  return toTitleCase(splitResourceName(String(key).split('/').slice(-1)[0]));
80
87
  }
81
88
  return key;
@@ -85,7 +92,8 @@ function languageDesc(key, dataOverride) {
85
92
  const lowerKey = String(key).toLowerCase();
86
93
  if (lowerKey in i18n(dataOverride).languages) {
87
94
  return i18n(dataOverride).languages[lowerKey].desc;
88
- } if (key) {
95
+ }
96
+ if (key) {
89
97
  return `[PH] ${toTitleCase(splitResourceName(String(key).split('/').slice(-1)[0]))} Desc`;
90
98
  }
91
99
  return key;
@@ -94,7 +102,8 @@ function languageDesc(key, dataOverride) {
94
102
  function missionType(key, dataOverride) {
95
103
  if (key in i18n(dataOverride).missionTypes) {
96
104
  return i18n(dataOverride).missionTypes[key].value;
97
- } if (key) {
105
+ }
106
+ if (key) {
98
107
  return toTitleCase(key.replace(/^MT_/, ''));
99
108
  }
100
109
  return key;
@@ -178,8 +187,7 @@ function sortieModifier(key, dataOverride) {
178
187
  }
179
188
 
180
189
  function sortieModDesc(key, dataOverride) {
181
- if (i18n(dataOverride).sortie.modifierDescriptions
182
- && key in i18n(dataOverride).sortie.modifierDescriptions) {
190
+ if (i18n(dataOverride).sortie.modifierDescriptions && key in i18n(dataOverride).sortie.modifierDescriptions) {
183
191
  return i18n(dataOverride).sortie.modifierDescriptions[key];
184
192
  }
185
193
  return key;
@@ -195,9 +203,11 @@ function region(key, dataOverride) {
195
203
  function conclaveChallenge(key, dataOverride) {
196
204
  const splitKey = String(key).split('/').slice(-1)[0];
197
205
 
198
- if (i18n(dataOverride).conclave
199
- && i18n(dataOverride).conclave.challenges
200
- && i18n(dataOverride).conclave.challenges[splitKey]) {
206
+ if (
207
+ i18n(dataOverride).conclave &&
208
+ i18n(dataOverride).conclave.challenges &&
209
+ i18n(dataOverride).conclave.challenges[splitKey]
210
+ ) {
201
211
  return i18n(dataOverride).conclave.challenges[splitKey];
202
212
  }
203
213
  return {