XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

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
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

11 个文件 +150 -1293
Modified package-lock.json +4 -4
@@ -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",
Modified package.json +1 -1
@@ -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"
Modified src/controllers/api/missionInventoryUpdateController.ts +1 -1
@@ -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" &&
Modified src/controllers/api/syndicateSacrificeController.ts +23 -19
@@ -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();
Modified src/controllers/api/updateChallengeProgressController.ts +12 -4
@@ -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 }) => {
Modified src/services/inventoryService.ts +28 -20
@@ -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;
Modified src/services/missionInventoryUpdateService.ts +3 -1
@@ -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);
Modified src/services/serversideVendorsService.ts +0 -2
@@ -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,
Modified src/services/worldStateService.ts +77 -48
@@ -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 };
Modified src/types/worldStateTypes.ts +1 -1
@@ -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;