warframe-worldstate-parser 2.23.0 → 2.24.0
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.
- package/lib/Alert.js +8 -8
- package/lib/CambionCycle.js +1 -5
- package/lib/CetusCycle.js +3 -3
- package/lib/ChallengeInstance.js +6 -6
- package/lib/ConclaveChallenge.js +2 -5
- package/lib/ConstructionProgress.js +4 -2
- package/lib/DailyDeal.js +1 -3
- package/lib/DarkSector.js +11 -8
- package/lib/EarthCycle.js +4 -4
- package/lib/Fissure.js +3 -3
- package/lib/FlashSale.js +2 -7
- package/lib/GlobalUpgrade.js +5 -5
- package/lib/Invasion.js +11 -10
- package/lib/Kuva.js +16 -10
- package/lib/Mission.js +30 -20
- package/lib/News.js +13 -15
- package/lib/Nightwave.js +7 -4
- package/lib/NightwaveChallenge.js +2 -4
- package/lib/PersistentEnemy.js +1 -3
- package/lib/Reward.js +64 -60
- package/lib/SentientOutpost.js +6 -8
- package/lib/Simaris.js +5 -3
- package/lib/Sortie.js +12 -8
- package/lib/SortieVariant.js +13 -13
- package/lib/SyndicateJob.js +49 -17
- package/lib/SyndicateMission.js +6 -6
- package/lib/VallisCycle.js +2 -2
- package/lib/VoidTrader.js +9 -13
- package/lib/WeeklyChallenge.js +4 -3
- package/lib/WorldEvent.js +24 -30
- package/lib/WorldState.js +32 -24
- package/lib/WorldstateObject.js +3 -3
- package/lib/ZarimanCycle.js +136 -0
- package/lib/timeDate.js +2 -2
- package/lib/translation.js +22 -12
- package/package.json +3 -118
- package/types/lib/Alert.d.ts +1 -1
- package/types/lib/CambionCycle.d.ts +1 -0
- package/types/lib/ConclaveChallenge.d.ts +1 -1
- package/types/lib/DailyDeal.d.ts +1 -1
- package/types/lib/DarkSector.d.ts +1 -1
- package/types/lib/Fissure.d.ts +3 -3
- package/types/lib/FlashSale.d.ts +1 -1
- package/types/lib/GlobalUpgrade.d.ts +1 -1
- package/types/lib/Invasion.d.ts +4 -4
- package/types/lib/Kuva.d.ts +1 -6
- package/types/lib/Mission.d.ts +11 -4
- package/types/lib/News.d.ts +4 -4
- package/types/lib/Nightwave.d.ts +1 -1
- package/types/lib/NightwaveChallenge.d.ts +1 -1
- package/types/lib/PersistentEnemy.d.ts +1 -1
- package/types/lib/SentientOutpost.d.ts +1 -1
- package/types/lib/Sortie.d.ts +1 -1
- package/types/lib/SortieVariant.d.ts +10 -10
- package/types/lib/SyndicateJob.d.ts +13 -9
- package/types/lib/SyndicateMission.d.ts +1 -1
- package/types/lib/VoidTrader.d.ts +1 -1
- package/types/lib/WorldEvent.d.ts +3 -3
- package/types/lib/WorldState.d.ts +5 -0
- package/types/lib/ZarimanCycle.d.ts +55 -0
package/lib/SyndicateJob.js
CHANGED
|
@@ -5,7 +5,7 @@ const fetch = require('node-fetch');
|
|
|
5
5
|
const WorldstateObject = require('./WorldstateObject');
|
|
6
6
|
|
|
7
7
|
const apiBase = process.env.API_BASE_URL || 'https://api.warframestat.us';
|
|
8
|
-
const bountyRewardRegex = /Tier([ABCDE])Table([ABC])Rewards/i;
|
|
8
|
+
const bountyRewardRegex = /(?:Tier([ABCDE])|Narmer)Table([ABC])Rewards/i;
|
|
9
9
|
const ghoulRewardRegex = /GhoulBountyTable([AB])Rewards/i;
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -63,7 +63,9 @@ const getBountyRewards = async (i18n, isVault, raw) => {
|
|
|
63
63
|
({ location, locationWRot } = determineLocation(i18n, isVault, raw));
|
|
64
64
|
}
|
|
65
65
|
const url = `${apiBase}/drops/search/${encodeURIComponent(location)}?grouped_by=location`;
|
|
66
|
-
const reply = await fetch(url)
|
|
66
|
+
const reply = await fetch(url)
|
|
67
|
+
.then((res) => res.json())
|
|
68
|
+
.catch(() => {}); // swallow errors
|
|
67
69
|
const pool = (reply || {})[locationWRot];
|
|
68
70
|
if (!pool) {
|
|
69
71
|
return ['Pattern Mismatch. Results inaccurate.'];
|
|
@@ -75,41 +77,50 @@ const getBountyRewards = async (i18n, isVault, raw) => {
|
|
|
75
77
|
return [];
|
|
76
78
|
};
|
|
77
79
|
|
|
80
|
+
const FIFTY_MINUTES = 3000000;
|
|
81
|
+
|
|
78
82
|
/**
|
|
79
83
|
* Represents a syndicate daily mission
|
|
80
84
|
* @extends {WorldstateObject}
|
|
81
85
|
*/
|
|
82
86
|
class SyndicateJob extends WorldstateObject {
|
|
83
87
|
/**
|
|
84
|
-
* @param
|
|
85
|
-
* @param
|
|
86
|
-
* @param
|
|
87
|
-
* @param
|
|
88
|
-
* @param
|
|
88
|
+
* @param {Object} data The syndicate mission data
|
|
89
|
+
* @param {Date} expiry The syndicate job expiration
|
|
90
|
+
* @param {Object} deps The dependencies object
|
|
91
|
+
* @param {Object} timeDate Time/Date functions
|
|
92
|
+
* @param {Translator} translator The string translator
|
|
93
|
+
* @param {string} locale Locale to use for translations
|
|
89
94
|
*/
|
|
90
95
|
constructor(data, expiry, { translator, timeDate, locale }) {
|
|
91
|
-
super(
|
|
96
|
+
super(
|
|
97
|
+
{
|
|
98
|
+
_id: {
|
|
99
|
+
$oid: data.JobCurrentVersion
|
|
100
|
+
? data.JobCurrentVersion.$oid
|
|
101
|
+
: `${(data.jobType || '').split('/').slice(-1)[0]}${expiry.getTime()}`,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{ timeDate }
|
|
105
|
+
);
|
|
92
106
|
|
|
93
107
|
/**
|
|
94
108
|
* Array of strings describing rewards
|
|
95
109
|
* @type {Array.<String>}
|
|
96
110
|
*/
|
|
97
111
|
this.rewardPool = [];
|
|
98
|
-
getBountyRewards(data.rewards, data.isVault, data)
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
});
|
|
112
|
+
getBountyRewards(data.rewards, data.isVault, data).then((rewards) => {
|
|
113
|
+
this.rewardPool = rewards;
|
|
114
|
+
});
|
|
102
115
|
|
|
103
|
-
const chamber = ((data.locationTag || '')
|
|
104
|
-
.match(/[A-Z]+(?![a-z])|[A-Z]?[a-z]+|\d+/g) || [])
|
|
105
|
-
.join(' ');
|
|
116
|
+
const chamber = ((data.locationTag || '').match(/[A-Z]+(?![a-z])|[A-Z]?[a-z]+|\d+/g) || []).join(' ');
|
|
106
117
|
|
|
107
118
|
/**
|
|
108
119
|
* The type of job this is
|
|
109
120
|
* @type {String}
|
|
110
121
|
*/
|
|
111
|
-
this.type =
|
|
112
|
-
|| `${data.isVault ? 'Isolation Vault' : ''} ${chamber}`;
|
|
122
|
+
this.type =
|
|
123
|
+
translator.languageString(data.jobType, locale) || `${data.isVault ? 'Isolation Vault' : ''} ${chamber}`;
|
|
113
124
|
|
|
114
125
|
/**
|
|
115
126
|
* Array of enemy levels
|
|
@@ -141,6 +152,27 @@ class SyndicateJob extends WorldstateObject {
|
|
|
141
152
|
* @type {string|null}
|
|
142
153
|
*/
|
|
143
154
|
this.locationTag = data.locationTag;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* End time for the syndicate mission.
|
|
158
|
+
* Should be inherited from the Syndicate, but some are timebound.
|
|
159
|
+
* @type {Date}
|
|
160
|
+
*/
|
|
161
|
+
this.expiry = expiry;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* What time phase this bounty is bound to
|
|
165
|
+
* @type {string}
|
|
166
|
+
*/
|
|
167
|
+
this.timeBound = undefined;
|
|
168
|
+
if (data.jobType && data.jobType.toLowerCase().includes('narmer')) {
|
|
169
|
+
if (data.jobType.toLowerCase().includes('eidolon')) {
|
|
170
|
+
this.timeBound = 'day';
|
|
171
|
+
this.expiry = new Date(this.expiry.getTime() - FIFTY_MINUTES);
|
|
172
|
+
} else {
|
|
173
|
+
this.timeBoound = 'night';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
144
176
|
}
|
|
145
177
|
}
|
|
146
178
|
|
package/lib/SyndicateMission.js
CHANGED
|
@@ -16,13 +16,14 @@ class SyndicateMission extends WorldstateObject {
|
|
|
16
16
|
* @param {TimeDateFunctions} deps.timeDate The time and date functions
|
|
17
17
|
* @param {string} deps.locale Locale to use for translations
|
|
18
18
|
*/
|
|
19
|
-
constructor(data, {
|
|
20
|
-
mdConfig, translator, timeDate, locale,
|
|
21
|
-
}) {
|
|
19
|
+
constructor(data, { mdConfig, translator, timeDate, locale }) {
|
|
22
20
|
super(data, { timeDate });
|
|
23
21
|
|
|
24
22
|
const deps = {
|
|
25
|
-
mdConfig,
|
|
23
|
+
mdConfig,
|
|
24
|
+
translator,
|
|
25
|
+
timeDate,
|
|
26
|
+
locale,
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
/**
|
|
@@ -113,8 +114,7 @@ class SyndicateMission extends WorldstateObject {
|
|
|
113
114
|
toString() {
|
|
114
115
|
if (this.nodes.length > 0) {
|
|
115
116
|
const missions = this.nodes.map((n) => ` \u2022${n.toString()}`).join(this.mdConfig.lineEnd);
|
|
116
|
-
return `[${this.getETAString()}] ${this.syndicate} currently has missions on
|
|
117
|
-
+ `${this.mdConfig.lineEnd}${missions}`;
|
|
117
|
+
return `[${this.getETAString()}] ${this.syndicate} currently has missions on:${this.mdConfig.lineEnd}${missions}`;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
return `No missions available for ${this.syndicate}`;
|
package/lib/VallisCycle.js
CHANGED
|
@@ -21,7 +21,7 @@ function getCurrentCycle() {
|
|
|
21
21
|
toNextMinor = toNextFull - coldTime;
|
|
22
22
|
}
|
|
23
23
|
const milliAtNext = Date.now() + toNextMinor;
|
|
24
|
-
const milliAtPrev =
|
|
24
|
+
const milliAtPrev = Date.now() + toNextFull - (state === 'warm' ? loopTime : coldTime);
|
|
25
25
|
const timeAtPrevious = new Date(milliAtPrev);
|
|
26
26
|
timeAtPrevious.setSeconds(0);
|
|
27
27
|
|
|
@@ -104,7 +104,7 @@ class VallisCycle extends WorldstateObject {
|
|
|
104
104
|
|
|
105
105
|
this.id = `vallisCycle${ec.timeAtPrevious.getTime()}`;
|
|
106
106
|
|
|
107
|
-
this.shortString = `${this.timeLeft.replace(/\s\d*s/
|
|
107
|
+
this.shortString = `${this.timeLeft.replace(/\s\d*s/gi, '')} to ${this.isWarm ? 'Cold' : 'Warm'}`;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/**
|
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(
|
|
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
|
|
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
|
|
141
|
-
|
|
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
|
package/lib/WeeklyChallenge.js
CHANGED
|
@@ -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
|
|
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,
|
|
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) =>
|
|
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 =
|
|
158
|
-
? Number.parseFloat(((data.HealthPct || 0.
|
|
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 = (
|
|
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 {
|
|
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(
|
|
312
|
-
|
|
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) =>
|
|
45
|
-
const safeObj = (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(
|
|
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(
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,32 @@ module.exports = class WorldState {
|
|
|
238
242
|
*/
|
|
239
243
|
this.cambionCycle = new CambionCycle(this.cetusCycle, deps);
|
|
240
244
|
|
|
245
|
+
/**
|
|
246
|
+
* The current Zariman cycle based off current time
|
|
247
|
+
* @type {ZarimanCycle}
|
|
248
|
+
*/
|
|
249
|
+
this.zarimanCycle = new ZarimanCycle(Date.now(), deps);
|
|
250
|
+
|
|
241
251
|
/**
|
|
242
252
|
* Weekly challenges
|
|
243
253
|
* @type {Array.<WeeklyChallenge>}
|
|
244
254
|
*/
|
|
245
|
-
this.weeklyChallenges = data.WeeklyChallenges
|
|
246
|
-
? new deps.WeeklyChallenge(data.WeeklyChallenges, deps)
|
|
247
|
-
: [];
|
|
255
|
+
this.weeklyChallenges = data.WeeklyChallenges ? new deps.WeeklyChallenge(data.WeeklyChallenges, deps) : [];
|
|
248
256
|
|
|
249
|
-
const projectPCTwithOid = data.ProjectPct
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
257
|
+
const projectPCTwithOid = data.ProjectPct
|
|
258
|
+
? {
|
|
259
|
+
ProjectPct: data.ProjectPct,
|
|
260
|
+
_id: {
|
|
261
|
+
$oid: `${Date.now()}${data.ProjectPct[0]}`,
|
|
262
|
+
},
|
|
263
|
+
}
|
|
264
|
+
: undefined;
|
|
255
265
|
|
|
256
266
|
/**
|
|
257
267
|
* The Current construction progress for Fomorians/Razorback/etc.
|
|
258
268
|
* @type {ConstructionProgress}
|
|
259
269
|
*/
|
|
260
|
-
this.constructionProgress = projectPCTwithOid
|
|
261
|
-
? new ConstructionProgress(projectPCTwithOid, deps)
|
|
262
|
-
: {};
|
|
270
|
+
this.constructionProgress = projectPCTwithOid ? new ConstructionProgress(projectPCTwithOid, deps) : {};
|
|
263
271
|
|
|
264
272
|
/**
|
|
265
273
|
* The current Orb Vallis cycle state
|
package/lib/WorldstateObject.js
CHANGED
|
@@ -12,7 +12,8 @@ class WorldstateObject {
|
|
|
12
12
|
* The object's id field
|
|
13
13
|
* @type {string}
|
|
14
14
|
*/
|
|
15
|
-
|
|
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
|
|
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,136 @@
|
|
|
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 = 1654725600000;
|
|
9
|
+
const fullCycle = 28800000;
|
|
10
|
+
const stateMaximum = 14400000;
|
|
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} currentTime The current time to calculate Zariman cycle for
|
|
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(currentTime, { 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.currentTime = currentTime;
|
|
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 daytime
|
|
72
|
+
* @type {boolean}
|
|
73
|
+
*/
|
|
74
|
+
this.isCorpus = ec.corpusTime;
|
|
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.timeDate.fromNow(this.expiry) < 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getCurrentZarimanCycle() {
|
|
102
|
+
// determine if it is corpus cycle or grineer cycle
|
|
103
|
+
const timeInCycle = (this.currentTime - corpusTimeMillis) % fullCycle;
|
|
104
|
+
// if timeInCycle is less than 4 hours, it is corpus, otherwise it is grineer
|
|
105
|
+
const corpusTime = timeInCycle <= stateMaximum;
|
|
106
|
+
|
|
107
|
+
// cycles are offset by 2 hours from bounties
|
|
108
|
+
const millisLeft = fullCycle - timeInCycle;
|
|
109
|
+
|
|
110
|
+
const minutesCoef = 1000 * 60;
|
|
111
|
+
const expiry = new Date(Math.round((this.currentTime + millisLeft) / minutesCoef) * minutesCoef);
|
|
112
|
+
const state = corpusTime ? 'corpus' : 'grineer';
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
corpusTime,
|
|
116
|
+
timeLeft: this.timeDate.timeDeltaToString(millisLeft),
|
|
117
|
+
expiry,
|
|
118
|
+
expiresIn: millisLeft,
|
|
119
|
+
state,
|
|
120
|
+
start: expiry.getTime() - stateMaximum,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The event's string representation
|
|
126
|
+
* @returns {string}
|
|
127
|
+
*/
|
|
128
|
+
toString() {
|
|
129
|
+
const lines = [
|
|
130
|
+
`Operator, Zariman Ten Zero is currently occupied by ${this.state}`,
|
|
131
|
+
`Time remaining until ${this.isCorpus ? 'grineer' : 'corpus'} takeover: ${this.timeLeft}`,
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
return lines.join(this.mdConfig.lineEnd);
|
|
135
|
+
}
|
|
136
|
+
};
|
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 =
|
|
78
|
-
const dt =
|
|
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
|
|