返回提交历史
Modified
package-lock.json
+4
-4
Modified
package.json
+1
-1
Modified
src/controllers/api/missionInventoryUpdateController.ts
+1
-1
Modified
src/controllers/api/syndicateSacrificeController.ts
+23
-19
Modified
src/controllers/api/updateChallengeProgressController.ts
+12
-4
Modified
src/services/inventoryService.ts
+28
-20
Modified
src/services/missionInventoryUpdateService.ts
+3
-1
Modified
src/services/serversideVendorsService.ts
+0
-2
Modified
src/services/worldStateService.ts
+77
-48
Modified
src/types/worldStateTypes.ts
+1
-1
Deleted
static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json
+0
-1192
XFEstudio/SpaceNinjaServer
feat: initial support for multiple nightwave seasons (#2096)
Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2096 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
ba6cd474
代码差异
11 个文件
+150
-1293
@@ -18,7 +18,7 @@
18
18
"morgan": "^1.10.0",
19
19
"ncp": "^2.0.0",
20
20
"typescript": "^5.5",
21
"warframe-public-export-plus": "^0.5.62",
21
"warframe-public-export-plus": "^0.5.64",
22
22
"warframe-riven-info": "^0.1.2",
23
23
"winston": "^3.17.0",
24
24
"winston-daily-rotate-file": "^5.0.0"
@@ -3703,9 +3703,9 @@
3703
3703
}
3704
3704
},
3705
3705
"node_modules/warframe-public-export-plus": {
3706
"version": "0.5.62",
3707
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.62.tgz",
3708
"integrity": "sha512-D8ZzjkU9rrK/59VqCfpMoV31HVmwHZV1dNZxPO85AOlcjg/G81Fu3kgITQTaw9sdNagLPLQnFaiXY58pxxRwgA=="
3706
"version": "0.5.64",
3707
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.64.tgz",
3708
"integrity": "sha512-JyHRtYumfwQ1Iog2unzlBWfQHJlZER+iUISquyFFv0Qqtv2QsNzFv2AbV7sCaqgDcE8tw6e5/YqGgfI0m403/g=="
3709
3709
},
3710
3710
"node_modules/warframe-riven-info": {
3711
3711
"version": "0.1.2",
@@ -25,7 +25,7 @@
25
25
"morgan": "^1.10.0",
26
26
"ncp": "^2.0.0",
27
27
"typescript": "^5.5",
28
"warframe-public-export-plus": "^0.5.62",
28
"warframe-public-export-plus": "^0.5.64",
29
29
"warframe-riven-info": "^0.1.2",
30
30
"winston": "^3.17.0",
31
31
"winston-daily-rotate-file": "^5.0.0"
@@ -57,7 +57,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
57
57
const firstCompletion = missionReport.SortieId
58
58
? inventory.CompletedSorties.indexOf(missionReport.SortieId) == -1
59
59
: false;
60
const inventoryUpdates = await addMissionInventoryUpdates(inventory, missionReport);
60
const inventoryUpdates = await addMissionInventoryUpdates(account, inventory, missionReport);
61
61
62
62
if (
63
63
missionReport.MissionStatus !== "GS_SUCCESS" &&
@@ -1,15 +1,13 @@
1
1
import { getJSONfromString } from "@/src/helpers/stringHelpers";
2
2
import { RequestHandler } from "express";
3
3
import { getAccountIdForRequest } from "@/src/services/loginService";
4
import { ExportNightwave, ExportSyndicates, ISyndicateSacrifice } from "warframe-public-export-plus";
4
import { ExportSyndicates, ISyndicateSacrifice } from "warframe-public-export-plus";
5
5
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
6
6
import { addMiscItems, combineInventoryChanges, getInventory, updateCurrency } from "@/src/services/inventoryService";
7
7
import { IInventoryChanges } from "@/src/types/purchaseTypes";
8
import { isStoreItem, toStoreItem } from "@/src/services/itemDataService";
8
import { toStoreItem } from "@/src/services/itemDataService";
9
9
import { logger } from "@/src/utils/logger";
10
10
11
const nightwaveCredsItemType = ExportNightwave.rewards[ExportNightwave.rewards.length - 1].uniqueName;
12
13
11
export const syndicateSacrificeController: RequestHandler = async (request, response) => {
14
12
const accountId = await getAccountIdForRequest(request);
15
13
const inventory = await getInventory(accountId);
@@ -54,13 +52,6 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
54
52
syndicate.Title ??= 0;
55
53
syndicate.Title += 1;
56
54
57
if (syndicate.Title > 0 && manifest.favours.find(x => x.rankUpReward && x.requiredLevel == syndicate.Title)) {
58
syndicate.FreeFavorsEarned ??= [];
59
if (!syndicate.FreeFavorsEarned.includes(syndicate.Title)) {
60
syndicate.FreeFavorsEarned.push(syndicate.Title);
61
}
62
}
63
64
55
if (reward) {
65
56
combineInventoryChanges(
66
57
res.InventoryChanges,
@@ -68,24 +59,37 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
68
59
);
69
60
}
70
61
71
if (data.AffiliationTag == ExportNightwave.affiliationTag) {
72
const index = syndicate.Title - 1;
73
if (index < ExportNightwave.rewards.length) {
62
// Quacks like a nightwave syndicate?
63
if (manifest.dailyChallenges) {
64
const title = manifest.titles!.find(x => x.level == syndicate.Title);
65
if (title) {
74
66
res.NewEpisodeReward = true;
75
const reward = ExportNightwave.rewards[index];
76
let rewardType = reward.uniqueName;
77
if (!isStoreItem(rewardType)) {
78
rewardType = toStoreItem(rewardType);
67
let rewardType: string;
68
let rewardCount: number;
69
if (title.storeItemReward) {
70
rewardType = title.storeItemReward;
71
rewardCount = 1;
72
} else {
73
rewardType = toStoreItem(title.reward!.ItemType);
74
rewardCount = title.reward!.ItemCount;
79
75
}
80
const rewardInventoryChanges = (await handleStoreItemAcquisition(rewardType, inventory, reward.itemCount))
76
const rewardInventoryChanges = (await handleStoreItemAcquisition(rewardType, inventory, rewardCount))
81
77
.InventoryChanges;
82
78
if (Object.keys(rewardInventoryChanges).length == 0) {
83
79
logger.debug(`nightwave rank up reward did not seem to get added, giving 50 creds instead`);
80
const nightwaveCredsItemType = manifest.titles![0].reward!.ItemType;
84
81
rewardInventoryChanges.MiscItems = [{ ItemType: nightwaveCredsItemType, ItemCount: 50 }];
85
82
addMiscItems(inventory, rewardInventoryChanges.MiscItems);
86
83
}
87
84
combineInventoryChanges(res.InventoryChanges, rewardInventoryChanges);
88
85
}
86
} else {
87
if (syndicate.Title > 0 && manifest.favours.find(x => x.rankUpReward && x.requiredLevel == syndicate.Title)) {
88
syndicate.FreeFavorsEarned ??= [];
89
if (!syndicate.FreeFavorsEarned.includes(syndicate.Title)) {
90
syndicate.FreeFavorsEarned.push(syndicate.Title);
91
}
92
}
89
93
}
90
94
91
95
await inventory.save();
@@ -1,18 +1,26 @@
1
1
import { RequestHandler } from "express";
2
2
import { getJSONfromString } from "@/src/helpers/stringHelpers";
3
import { getAccountIdForRequest } from "@/src/services/loginService";
3
import { getAccountForRequest } from "@/src/services/loginService";
4
4
import { addChallenges, getInventory } from "@/src/services/inventoryService";
5
5
import { IChallengeProgress, ISeasonChallenge } from "@/src/types/inventoryTypes/inventoryTypes";
6
6
import { IAffiliationMods } from "@/src/types/purchaseTypes";
7
7
8
8
export const updateChallengeProgressController: RequestHandler = async (req, res) => {
9
9
const challenges = getJSONfromString<IUpdateChallengeProgressRequest>(String(req.body));
10
const accountId = await getAccountIdForRequest(req);
10
const account = await getAccountForRequest(req);
11
11
12
const inventory = await getInventory(accountId, "ChallengeProgress SeasonChallengeHistory Affiliations");
12
const inventory = await getInventory(
13
account._id.toString(),
14
"ChallengeProgress SeasonChallengeHistory Affiliations"
15
);
13
16
let affiliationMods: IAffiliationMods[] = [];
14
17
if (challenges.ChallengeProgress) {
15
affiliationMods = addChallenges(inventory, challenges.ChallengeProgress, challenges.SeasonChallengeCompletions);
18
affiliationMods = addChallenges(
19
account,
20
inventory,
21
challenges.ChallengeProgress,
22
challenges.SeasonChallengeCompletions
23
);
16
24
}
17
25
if (challenges.SeasonChallengeHistory) {
18
26
challenges.SeasonChallengeHistory.forEach(({ challenge, id }) => {
@@ -44,6 +44,7 @@ import {
44
44
import {
45
45
ExportArcanes,
46
46
ExportBundles,
47
ExportChallenges,
47
48
ExportCustoms,
48
49
ExportDrones,
49
50
ExportEmailItems,
@@ -53,7 +54,6 @@ import {
53
54
ExportGear,
54
55
ExportKeys,
55
56
ExportMisc,
56
ExportNightwave,
57
57
ExportRailjackWeapons,
58
58
ExportRecipes,
59
59
ExportResources,
@@ -83,8 +83,9 @@ import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
83
83
import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService";
84
84
import { createMessage } from "./inboxService";
85
85
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
86
import { getWorldState } from "./worldStateService";
86
import { getNightwaveSyndicateTag, getWorldState } from "./worldStateService";
87
87
import { generateNemesisProfile, INemesisProfile } from "../helpers/nemesisHelpers";
88
import { TAccountDocument } from "./loginService";
88
89
89
90
export const createInventory = async (
90
91
accountOwnerId: Types.ObjectId,
@@ -1716,6 +1717,7 @@ export const addLoreFragmentScans = (inventory: TInventoryDatabaseDocument, arr:
1716
1717
};
1717
1718
1718
1719
export const addChallenges = (
1720
account: TAccountDocument,
1719
1721
inventory: TInventoryDatabaseDocument,
1720
1722
ChallengeProgress: IChallengeProgress[],
1721
1723
SeasonChallengeCompletions: ISeasonChallenge[] | undefined
@@ -1738,26 +1740,32 @@ export const addChallenges = (
1738
1740
continue;
1739
1741
}
1740
1742
1741
const meta = ExportNightwave.challenges[challenge.challenge];
1742
logger.debug("Completed challenge", meta);
1743
1744
let affiliation = inventory.Affiliations.find(x => x.Tag == ExportNightwave.affiliationTag);
1745
if (!affiliation) {
1746
affiliation =
1747
inventory.Affiliations[
1748
inventory.Affiliations.push({
1749
Tag: ExportNightwave.affiliationTag,
1750
Standing: 0
1751
}) - 1
1752
];
1753
}
1754
affiliation.Standing += meta.standing;
1743
const meta = ExportChallenges[challenge.challenge];
1744
const nightwaveSyndicateTag = getNightwaveSyndicateTag(account.BuildLabel);
1745
logger.debug("Completed season challenge", {
1746
uniqueName: challenge.challenge,
1747
syndicateTag: nightwaveSyndicateTag,
1748
...meta
1749
});
1750
if (nightwaveSyndicateTag) {
1751
let affiliation = inventory.Affiliations.find(x => x.Tag == nightwaveSyndicateTag);
1752
if (!affiliation) {
1753
affiliation =
1754
inventory.Affiliations[
1755
inventory.Affiliations.push({
1756
Tag: nightwaveSyndicateTag,
1757
Standing: 0
1758
}) - 1
1759
];
1760
}
1761
affiliation.Standing += meta.standing!;
1755
1762
1756
if (affiliationMods.length == 0) {
1757
affiliationMods.push({ Tag: ExportNightwave.affiliationTag });
1763
if (affiliationMods.length == 0) {
1764
affiliationMods.push({ Tag: nightwaveSyndicateTag });
1765
}
1766
affiliationMods[0].Standing ??= 0;
1767
affiliationMods[0].Standing += meta.standing!;
1758
1768
}
1759
affiliationMods[0].Standing ??= 0;
1760
affiliationMods[0].Standing += meta.standing;
1761
1769
}
1762
1770
}
1763
1771
return affiliationMods;
@@ -79,6 +79,7 @@ import { config } from "./configService";
79
79
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
80
80
import { ISyndicateMissionInfo } from "../types/worldStateTypes";
81
81
import { fromOid } from "../helpers/inventoryHelpers";
82
import { TAccountDocument } from "./loginService";
82
83
83
84
const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
84
85
// For Spy missions, e.g. 3 vaults cracked = A, B, C
@@ -135,6 +136,7 @@ const getRandomRewardByChance = (pool: IReward[], rng?: SRng): IRngResult | unde
135
136
//const knownUnhandledKeys: readonly string[] = ["test"] as const; // for unimplemented but important keys
136
137
137
138
export const addMissionInventoryUpdates = async (
139
account: TAccountDocument,
138
140
inventory: TInventoryDatabaseDocument,
139
141
inventoryUpdates: IMissionInventoryUpdateRequest
140
142
): Promise<IInventoryChanges> => {
@@ -287,7 +289,7 @@ export const addMissionInventoryUpdates = async (
287
289
addRecipes(inventory, value);
288
290
break;
289
291
case "ChallengeProgress":
290
addChallenges(inventory, value, inventoryUpdates.SeasonChallengeCompletions);
292
addChallenges(account, inventory, value, inventoryUpdates.SeasonChallengeCompletions);
291
293
break;
292
294
case "FusionTreasures":
293
295
addFusionTreasures(inventory, value);
@@ -27,7 +27,6 @@ import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSal
27
27
import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo/Nova1999ConquestShopManifest.json";
28
28
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
29
29
import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
30
import RadioLegionIntermission12VendorManifest from "@/static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json";
31
30
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
32
31
import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json";
33
32
import Temple1999VendorManifest from "@/static/fixed_responses/getVendorInfo/Temple1999VendorManifest.json";
@@ -54,7 +53,6 @@ const rawVendorManifests: IVendorManifest[] = [
54
53
Nova1999ConquestShopManifest,
55
54
OstronPetVendorManifest,
56
55
OstronProspectorVendorManifest,
57
RadioLegionIntermission12VendorManifest,
58
56
SolarisDebtTokenVendorRepossessionsManifest,
59
57
SolarisProspectorVendorManifest,
60
58
Temple1999VendorManifest,
@@ -6,7 +6,7 @@ import { buildConfig } from "@/src/services/buildConfigService";
6
6
import { unixTimesInMs } from "@/src/constants/timeConstants";
7
7
import { config } from "@/src/services/configService";
8
8
import { SRng } from "@/src/services/rngService";
9
import { ExportNightwave, ExportRegions, IRegion } from "warframe-public-export-plus";
9
import { ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
10
10
import {
11
11
ICalendarDay,
12
12
ICalendarEvent,
@@ -344,11 +344,26 @@ export const getSortie = (day: number): ISortie => {
344
344
};
345
345
};
346
346
347
const dailyChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
348
x.startsWith("/Lotus/Types/Challenges/Seasons/Daily/")
349
);
347
interface IRotatingSeasonChallengePools {
348
daily: string[];
349
weekly: string[];
350
hardWeekly: string[];
351
}
350
352
351
const getSeasonDailyChallenge = (day: number): ISeasonChallenge => {
353
const getSeasonChallengePools = (syndicateTag: string): IRotatingSeasonChallengePools => {
354
const syndicate = ExportSyndicates[syndicateTag];
355
return {
356
daily: syndicate.dailyChallenges!,
357
weekly: syndicate.weeklyChallenges!.filter(
358
x =>
359
x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/") &&
360
!x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanent")
361
),
362
hardWeekly: syndicate.weeklyChallenges!.filter(x => x.startsWith("/Lotus/Types/Challenges/Seasons/WeeklyHard/"))
363
};
364
};
365
366
const getSeasonDailyChallenge = (pools: IRotatingSeasonChallengePools, day: number): ISeasonChallenge => {
352
367
const dayStart = EPOCH + day * 86400000;
353
368
const dayEnd = EPOCH + (day + 3) * 86400000;
354
369
const rng = new SRng(new SRng(day).randomInt(0, 100_000));
@@ -357,17 +372,11 @@ const getSeasonDailyChallenge = (day: number): ISeasonChallenge => {
357
372
Daily: true,
358
373
Activation: { $date: { $numberLong: dayStart.toString() } },
359
374
Expiry: { $date: { $numberLong: dayEnd.toString() } },
360
Challenge: rng.randomElement(dailyChallenges)!
375
Challenge: rng.randomElement(pools.daily)!
361
376
};
362
377
};
363
378
364
const weeklyChallenges = Object.keys(ExportNightwave.challenges).filter(
365
x =>
366
x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/") &&
367
!x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanent")
368
);
369
370
const getSeasonWeeklyChallenge = (week: number, id: number): ISeasonChallenge => {
379
const getSeasonWeeklyChallenge = (pools: IRotatingSeasonChallengePools, week: number, id: number): ISeasonChallenge => {
371
380
const weekStart = EPOCH + week * 604800000;
372
381
const weekEnd = weekStart + 604800000;
373
382
const challengeId = week * 7 + id;
@@ -376,15 +385,15 @@ const getSeasonWeeklyChallenge = (week: number, id: number): ISeasonChallenge =>
376
385
_id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
377
386
Activation: { $date: { $numberLong: weekStart.toString() } },
378
387
Expiry: { $date: { $numberLong: weekEnd.toString() } },
379
Challenge: rng.randomElement(weeklyChallenges)!
388
Challenge: rng.randomElement(pools.weekly)!
380
389
};
381
390
};
382
391
383
const weeklyHardChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
384
x.startsWith("/Lotus/Types/Challenges/Seasons/WeeklyHard/")
385
);
386
387
const getSeasonWeeklyHardChallenge = (week: number, id: number): ISeasonChallenge => {
392
const getSeasonWeeklyHardChallenge = (
393
pools: IRotatingSeasonChallengePools,
394
week: number,
395
id: number
396
): ISeasonChallenge => {
388
397
const weekStart = EPOCH + week * 604800000;
389
398
const weekEnd = weekStart + 604800000;
390
399
const challengeId = week * 7 + id;
@@ -393,35 +402,39 @@ const getSeasonWeeklyHardChallenge = (week: number, id: number): ISeasonChalleng
393
402
_id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
394
403
Activation: { $date: { $numberLong: weekStart.toString() } },
395
404
Expiry: { $date: { $numberLong: weekEnd.toString() } },
396
Challenge: rng.randomElement(weeklyHardChallenges)!
405
Challenge: rng.randomElement(pools.hardWeekly)!
397
406
};
398
407
};
399
408
400
const pushWeeklyActs = (worldState: IWorldState, week: number): void => {
409
const pushWeeklyActs = (
410
activeChallenges: ISeasonChallenge[],
411
pools: IRotatingSeasonChallengePools,
412
week: number
413
): void => {
401
414
const weekStart = EPOCH + week * 604800000;
402
415
const weekEnd = weekStart + 604800000;
403
416
404
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 0));
405
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 1));
406
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 2));
407
worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 3));
408
worldState.SeasonInfo.ActiveChallenges.push({
417
activeChallenges.push(getSeasonWeeklyChallenge(pools, week, 0));
418
activeChallenges.push(getSeasonWeeklyChallenge(pools, week, 1));
419
activeChallenges.push(getSeasonWeeklyHardChallenge(pools, week, 2));
420
activeChallenges.push(getSeasonWeeklyHardChallenge(pools, week, 3));
421
activeChallenges.push({
409
422
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 0).toString().padStart(8, "0") },
410
423
Activation: { $date: { $numberLong: weekStart.toString() } },
411
424
Expiry: { $date: { $numberLong: weekEnd.toString() } },
412
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentCompleteMissions" + (week - 12)
425
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentCompleteMissions"
413
426
});
414
worldState.SeasonInfo.ActiveChallenges.push({
427
activeChallenges.push({
415
428
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 1).toString().padStart(8, "0") },
416
429
Activation: { $date: { $numberLong: weekStart.toString() } },
417
430
Expiry: { $date: { $numberLong: weekEnd.toString() } },
418
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEximus" + (week - 12)
431
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEximus"
419
432
});
420
worldState.SeasonInfo.ActiveChallenges.push({
433
activeChallenges.push({
421
434
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 2).toString().padStart(8, "0") },
422
435
Activation: { $date: { $numberLong: weekStart.toString() } },
423
436
Expiry: { $date: { $numberLong: weekEnd.toString() } },
424
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEnemies" + (week - 12)
437
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEnemies"
425
438
});
426
439
};
427
440
@@ -926,15 +939,6 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
926
939
LiteSorties: [],
927
940
GlobalUpgrades: [],
928
941
EndlessXpChoices: [],
929
SeasonInfo: {
930
Activation: { $date: { $numberLong: "1715796000000" } },
931
Expiry: { $date: { $numberLong: "2000000000000" } },
932
AffiliationTag: "RadioLegionIntermission12Syndicate",
933
Season: 14,
934
Phase: 0,
935
Params: "",
936
ActiveChallenges: []
937
},
938
942
KnownCalendarSeasons: [],
939
943
...staticWorldState,
940
944
SyndicateMissions: [...staticWorldState.SyndicateMissions]
@@ -967,17 +971,27 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
967
971
}
968
972
969
973
// Nightwave Challenges
970
// Current nightwave season was introduced in 38.0.8 so omitting challenges before that to avoid UI bugs and even crashes on really old versions.
971
if (!buildLabel || version_compare(buildLabel, "2025.02.05.11.19") >= 0) {
972
worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day - 2));
973
worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day - 1));
974
worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day - 0));
974
const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
975
if (nightwaveSyndicateTag) {
976
worldState.SeasonInfo = {
977
Activation: { $date: { $numberLong: "1715796000000" } },
978
Expiry: { $date: { $numberLong: "2000000000000" } },
979
AffiliationTag: nightwaveSyndicateTag,
980
Season: nightwaveTagToSeason[nightwaveSyndicateTag],
981
Phase: 0,
982
Params: "",
983
ActiveChallenges: []
984
};
985
const pools = getSeasonChallengePools(nightwaveSyndicateTag);
986
worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(pools, day - 2));
987
worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(pools, day - 1));
988
worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(pools, day - 0));
975
989
if (isBeforeNextExpectedWorldStateRefresh(EPOCH + (day + 1) * 86400000)) {
976
worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day + 1));
990
worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(pools, day + 1));
977
991
}
978
pushWeeklyActs(worldState, week);
992
pushWeeklyActs(worldState.SeasonInfo.ActiveChallenges, pools, week);
979
993
if (isBeforeNextExpectedWorldStateRefresh(weekEnd)) {
980
pushWeeklyActs(worldState, week + 1);
994
pushWeeklyActs(worldState.SeasonInfo.ActiveChallenges, pools, week + 1);
981
995
}
982
996
}
983
997
@@ -1242,3 +1256,18 @@ export const isArchwingMission = (node: IRegion): boolean => {
1242
1256
}
1243
1257
return false;
1244
1258
};
1259
1260
export const getNightwaveSyndicateTag = (buildLabel: string | undefined): string | undefined => {
1261
if (!buildLabel || version_compare(buildLabel, "2025.05.20.10.18") >= 0) {
1262
return "RadioLegionIntermission13Syndicate";
1263
}
1264
if (version_compare(buildLabel, "2025.02.05.11.19") >= 0) {
1265
return "RadioLegionIntermission12Syndicate";
1266
}
1267
return undefined;
1268
};
1269
1270
const nightwaveTagToSeason: Record<string, number> = {
1271
RadioLegionIntermission13Syndicate: 15,
1272
RadioLegionIntermission12Syndicate: 14
1273
};
@@ -14,7 +14,7 @@ export interface IWorldState {
14
14
NodeOverrides: INodeOverride[];
15
15
PVPChallengeInstances: IPVPChallengeInstance[];
16
16
EndlessXpChoices: IEndlessXpChoice[];
17
SeasonInfo: {
17
SeasonInfo?: {
18
18
Activation: IMongoDate;
19
19
Expiry: IMongoDate;
20
20
AffiliationTag: string;