forked from OpenWF/SpaceNinjaServer
		
	feat: initial support for multiple nightwave seasons (#2096)
Reviewed-on: OpenWF/SpaceNinjaServer#2096 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									92d34fd69e
								
							
						
					
					
						commit
						ba6cd47432
					
				
							
								
								
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -18,7 +18,7 @@
 | 
				
			|||||||
        "morgan": "^1.10.0",
 | 
					        "morgan": "^1.10.0",
 | 
				
			||||||
        "ncp": "^2.0.0",
 | 
					        "ncp": "^2.0.0",
 | 
				
			||||||
        "typescript": "^5.5",
 | 
					        "typescript": "^5.5",
 | 
				
			||||||
        "warframe-public-export-plus": "^0.5.62",
 | 
					        "warframe-public-export-plus": "^0.5.64",
 | 
				
			||||||
        "warframe-riven-info": "^0.1.2",
 | 
					        "warframe-riven-info": "^0.1.2",
 | 
				
			||||||
        "winston": "^3.17.0",
 | 
					        "winston": "^3.17.0",
 | 
				
			||||||
        "winston-daily-rotate-file": "^5.0.0"
 | 
					        "winston-daily-rotate-file": "^5.0.0"
 | 
				
			||||||
@ -3703,9 +3703,9 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/warframe-public-export-plus": {
 | 
					    "node_modules/warframe-public-export-plus": {
 | 
				
			||||||
      "version": "0.5.62",
 | 
					      "version": "0.5.64",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.62.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.64.tgz",
 | 
				
			||||||
      "integrity": "sha512-D8ZzjkU9rrK/59VqCfpMoV31HVmwHZV1dNZxPO85AOlcjg/G81Fu3kgITQTaw9sdNagLPLQnFaiXY58pxxRwgA=="
 | 
					      "integrity": "sha512-JyHRtYumfwQ1Iog2unzlBWfQHJlZER+iUISquyFFv0Qqtv2QsNzFv2AbV7sCaqgDcE8tw6e5/YqGgfI0m403/g=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/warframe-riven-info": {
 | 
					    "node_modules/warframe-riven-info": {
 | 
				
			||||||
      "version": "0.1.2",
 | 
					      "version": "0.1.2",
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@
 | 
				
			|||||||
    "morgan": "^1.10.0",
 | 
					    "morgan": "^1.10.0",
 | 
				
			||||||
    "ncp": "^2.0.0",
 | 
					    "ncp": "^2.0.0",
 | 
				
			||||||
    "typescript": "^5.5",
 | 
					    "typescript": "^5.5",
 | 
				
			||||||
    "warframe-public-export-plus": "^0.5.62",
 | 
					    "warframe-public-export-plus": "^0.5.64",
 | 
				
			||||||
    "warframe-riven-info": "^0.1.2",
 | 
					    "warframe-riven-info": "^0.1.2",
 | 
				
			||||||
    "winston": "^3.17.0",
 | 
					    "winston": "^3.17.0",
 | 
				
			||||||
    "winston-daily-rotate-file": "^5.0.0"
 | 
					    "winston-daily-rotate-file": "^5.0.0"
 | 
				
			||||||
 | 
				
			|||||||
@ -57,7 +57,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
 | 
				
			|||||||
    const firstCompletion = missionReport.SortieId
 | 
					    const firstCompletion = missionReport.SortieId
 | 
				
			||||||
        ? inventory.CompletedSorties.indexOf(missionReport.SortieId) == -1
 | 
					        ? inventory.CompletedSorties.indexOf(missionReport.SortieId) == -1
 | 
				
			||||||
        : false;
 | 
					        : false;
 | 
				
			||||||
    const inventoryUpdates = await addMissionInventoryUpdates(inventory, missionReport);
 | 
					    const inventoryUpdates = await addMissionInventoryUpdates(account, inventory, missionReport);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (
 | 
					    if (
 | 
				
			||||||
        missionReport.MissionStatus !== "GS_SUCCESS" &&
 | 
					        missionReport.MissionStatus !== "GS_SUCCESS" &&
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,13 @@
 | 
				
			|||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
					import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
				
			||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { ExportNightwave, ExportSyndicates, ISyndicateSacrifice } from "warframe-public-export-plus";
 | 
					import { ExportSyndicates, ISyndicateSacrifice } from "warframe-public-export-plus";
 | 
				
			||||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
 | 
					import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
 | 
				
			||||||
import { addMiscItems, combineInventoryChanges, getInventory, updateCurrency } from "@/src/services/inventoryService";
 | 
					import { addMiscItems, combineInventoryChanges, getInventory, updateCurrency } from "@/src/services/inventoryService";
 | 
				
			||||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
					import { IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
				
			||||||
import { isStoreItem, toStoreItem } from "@/src/services/itemDataService";
 | 
					import { toStoreItem } from "@/src/services/itemDataService";
 | 
				
			||||||
import { logger } from "@/src/utils/logger";
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const nightwaveCredsItemType = ExportNightwave.rewards[ExportNightwave.rewards.length - 1].uniqueName;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const syndicateSacrificeController: RequestHandler = async (request, response) => {
 | 
					export const syndicateSacrificeController: RequestHandler = async (request, response) => {
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(request);
 | 
					    const accountId = await getAccountIdForRequest(request);
 | 
				
			||||||
    const inventory = await getInventory(accountId);
 | 
					    const inventory = await getInventory(accountId);
 | 
				
			||||||
@ -54,13 +52,6 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
 | 
				
			|||||||
    syndicate.Title ??= 0;
 | 
					    syndicate.Title ??= 0;
 | 
				
			||||||
    syndicate.Title += 1;
 | 
					    syndicate.Title += 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (syndicate.Title > 0 && manifest.favours.find(x => x.rankUpReward && x.requiredLevel == syndicate.Title)) {
 | 
					 | 
				
			||||||
        syndicate.FreeFavorsEarned ??= [];
 | 
					 | 
				
			||||||
        if (!syndicate.FreeFavorsEarned.includes(syndicate.Title)) {
 | 
					 | 
				
			||||||
            syndicate.FreeFavorsEarned.push(syndicate.Title);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (reward) {
 | 
					    if (reward) {
 | 
				
			||||||
        combineInventoryChanges(
 | 
					        combineInventoryChanges(
 | 
				
			||||||
            res.InventoryChanges,
 | 
					            res.InventoryChanges,
 | 
				
			||||||
@ -68,24 +59,37 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (data.AffiliationTag == ExportNightwave.affiliationTag) {
 | 
					    // Quacks like a nightwave syndicate?
 | 
				
			||||||
        const index = syndicate.Title - 1;
 | 
					    if (manifest.dailyChallenges) {
 | 
				
			||||||
        if (index < ExportNightwave.rewards.length) {
 | 
					        const title = manifest.titles!.find(x => x.level == syndicate.Title);
 | 
				
			||||||
 | 
					        if (title) {
 | 
				
			||||||
            res.NewEpisodeReward = true;
 | 
					            res.NewEpisodeReward = true;
 | 
				
			||||||
            const reward = ExportNightwave.rewards[index];
 | 
					            let rewardType: string;
 | 
				
			||||||
            let rewardType = reward.uniqueName;
 | 
					            let rewardCount: number;
 | 
				
			||||||
            if (!isStoreItem(rewardType)) {
 | 
					            if (title.storeItemReward) {
 | 
				
			||||||
                rewardType = toStoreItem(rewardType);
 | 
					                rewardType = title.storeItemReward;
 | 
				
			||||||
 | 
					                rewardCount = 1;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                rewardType = toStoreItem(title.reward!.ItemType);
 | 
				
			||||||
 | 
					                rewardCount = title.reward!.ItemCount;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            const rewardInventoryChanges = (await handleStoreItemAcquisition(rewardType, inventory, reward.itemCount))
 | 
					            const rewardInventoryChanges = (await handleStoreItemAcquisition(rewardType, inventory, rewardCount))
 | 
				
			||||||
                .InventoryChanges;
 | 
					                .InventoryChanges;
 | 
				
			||||||
            if (Object.keys(rewardInventoryChanges).length == 0) {
 | 
					            if (Object.keys(rewardInventoryChanges).length == 0) {
 | 
				
			||||||
                logger.debug(`nightwave rank up reward did not seem to get added, giving 50 creds instead`);
 | 
					                logger.debug(`nightwave rank up reward did not seem to get added, giving 50 creds instead`);
 | 
				
			||||||
 | 
					                const nightwaveCredsItemType = manifest.titles![0].reward!.ItemType;
 | 
				
			||||||
                rewardInventoryChanges.MiscItems = [{ ItemType: nightwaveCredsItemType, ItemCount: 50 }];
 | 
					                rewardInventoryChanges.MiscItems = [{ ItemType: nightwaveCredsItemType, ItemCount: 50 }];
 | 
				
			||||||
                addMiscItems(inventory, rewardInventoryChanges.MiscItems);
 | 
					                addMiscItems(inventory, rewardInventoryChanges.MiscItems);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            combineInventoryChanges(res.InventoryChanges, rewardInventoryChanges);
 | 
					            combineInventoryChanges(res.InventoryChanges, rewardInventoryChanges);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (syndicate.Title > 0 && manifest.favours.find(x => x.rankUpReward && x.requiredLevel == syndicate.Title)) {
 | 
				
			||||||
 | 
					            syndicate.FreeFavorsEarned ??= [];
 | 
				
			||||||
 | 
					            if (!syndicate.FreeFavorsEarned.includes(syndicate.Title)) {
 | 
				
			||||||
 | 
					                syndicate.FreeFavorsEarned.push(syndicate.Title);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await inventory.save();
 | 
					    await inventory.save();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,18 +1,26 @@
 | 
				
			|||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
					import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
				
			||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
					import { getAccountForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { addChallenges, getInventory } from "@/src/services/inventoryService";
 | 
					import { addChallenges, getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
import { IChallengeProgress, ISeasonChallenge } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					import { IChallengeProgress, ISeasonChallenge } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import { IAffiliationMods } from "@/src/types/purchaseTypes";
 | 
					import { IAffiliationMods } from "@/src/types/purchaseTypes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const updateChallengeProgressController: RequestHandler = async (req, res) => {
 | 
					export const updateChallengeProgressController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const challenges = getJSONfromString<IUpdateChallengeProgressRequest>(String(req.body));
 | 
					    const challenges = getJSONfromString<IUpdateChallengeProgressRequest>(String(req.body));
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					    const account = await getAccountForRequest(req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const inventory = await getInventory(accountId, "ChallengeProgress SeasonChallengeHistory Affiliations");
 | 
					    const inventory = await getInventory(
 | 
				
			||||||
 | 
					        account._id.toString(),
 | 
				
			||||||
 | 
					        "ChallengeProgress SeasonChallengeHistory Affiliations"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    let affiliationMods: IAffiliationMods[] = [];
 | 
					    let affiliationMods: IAffiliationMods[] = [];
 | 
				
			||||||
    if (challenges.ChallengeProgress) {
 | 
					    if (challenges.ChallengeProgress) {
 | 
				
			||||||
        affiliationMods = addChallenges(inventory, challenges.ChallengeProgress, challenges.SeasonChallengeCompletions);
 | 
					        affiliationMods = addChallenges(
 | 
				
			||||||
 | 
					            account,
 | 
				
			||||||
 | 
					            inventory,
 | 
				
			||||||
 | 
					            challenges.ChallengeProgress,
 | 
				
			||||||
 | 
					            challenges.SeasonChallengeCompletions
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (challenges.SeasonChallengeHistory) {
 | 
					    if (challenges.SeasonChallengeHistory) {
 | 
				
			||||||
        challenges.SeasonChallengeHistory.forEach(({ challenge, id }) => {
 | 
					        challenges.SeasonChallengeHistory.forEach(({ challenge, id }) => {
 | 
				
			||||||
 | 
				
			|||||||
@ -44,6 +44,7 @@ import {
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
    ExportArcanes,
 | 
					    ExportArcanes,
 | 
				
			||||||
    ExportBundles,
 | 
					    ExportBundles,
 | 
				
			||||||
 | 
					    ExportChallenges,
 | 
				
			||||||
    ExportCustoms,
 | 
					    ExportCustoms,
 | 
				
			||||||
    ExportDrones,
 | 
					    ExportDrones,
 | 
				
			||||||
    ExportEmailItems,
 | 
					    ExportEmailItems,
 | 
				
			||||||
@ -53,7 +54,6 @@ import {
 | 
				
			|||||||
    ExportGear,
 | 
					    ExportGear,
 | 
				
			||||||
    ExportKeys,
 | 
					    ExportKeys,
 | 
				
			||||||
    ExportMisc,
 | 
					    ExportMisc,
 | 
				
			||||||
    ExportNightwave,
 | 
					 | 
				
			||||||
    ExportRailjackWeapons,
 | 
					    ExportRailjackWeapons,
 | 
				
			||||||
    ExportRecipes,
 | 
					    ExportRecipes,
 | 
				
			||||||
    ExportResources,
 | 
					    ExportResources,
 | 
				
			||||||
@ -83,8 +83,9 @@ import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
 | 
				
			|||||||
import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService";
 | 
					import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService";
 | 
				
			||||||
import { createMessage } from "./inboxService";
 | 
					import { createMessage } from "./inboxService";
 | 
				
			||||||
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
 | 
					import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
 | 
				
			||||||
import { getWorldState } from "./worldStateService";
 | 
					import { getNightwaveSyndicateTag, getWorldState } from "./worldStateService";
 | 
				
			||||||
import { generateNemesisProfile, INemesisProfile } from "../helpers/nemesisHelpers";
 | 
					import { generateNemesisProfile, INemesisProfile } from "../helpers/nemesisHelpers";
 | 
				
			||||||
 | 
					import { TAccountDocument } from "./loginService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const createInventory = async (
 | 
					export const createInventory = async (
 | 
				
			||||||
    accountOwnerId: Types.ObjectId,
 | 
					    accountOwnerId: Types.ObjectId,
 | 
				
			||||||
@ -1716,6 +1717,7 @@ export const addLoreFragmentScans = (inventory: TInventoryDatabaseDocument, arr:
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const addChallenges = (
 | 
					export const addChallenges = (
 | 
				
			||||||
 | 
					    account: TAccountDocument,
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
    ChallengeProgress: IChallengeProgress[],
 | 
					    ChallengeProgress: IChallengeProgress[],
 | 
				
			||||||
    SeasonChallengeCompletions: ISeasonChallenge[] | undefined
 | 
					    SeasonChallengeCompletions: ISeasonChallenge[] | undefined
 | 
				
			||||||
@ -1738,26 +1740,32 @@ export const addChallenges = (
 | 
				
			|||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const meta = ExportNightwave.challenges[challenge.challenge];
 | 
					            const meta = ExportChallenges[challenge.challenge];
 | 
				
			||||||
            logger.debug("Completed challenge", meta);
 | 
					            const nightwaveSyndicateTag = getNightwaveSyndicateTag(account.BuildLabel);
 | 
				
			||||||
 | 
					            logger.debug("Completed season challenge", {
 | 
				
			||||||
            let affiliation = inventory.Affiliations.find(x => x.Tag == ExportNightwave.affiliationTag);
 | 
					                uniqueName: challenge.challenge,
 | 
				
			||||||
 | 
					                syndicateTag: nightwaveSyndicateTag,
 | 
				
			||||||
 | 
					                ...meta
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            if (nightwaveSyndicateTag) {
 | 
				
			||||||
 | 
					                let affiliation = inventory.Affiliations.find(x => x.Tag == nightwaveSyndicateTag);
 | 
				
			||||||
                if (!affiliation) {
 | 
					                if (!affiliation) {
 | 
				
			||||||
                    affiliation =
 | 
					                    affiliation =
 | 
				
			||||||
                        inventory.Affiliations[
 | 
					                        inventory.Affiliations[
 | 
				
			||||||
                            inventory.Affiliations.push({
 | 
					                            inventory.Affiliations.push({
 | 
				
			||||||
                            Tag: ExportNightwave.affiliationTag,
 | 
					                                Tag: nightwaveSyndicateTag,
 | 
				
			||||||
                                Standing: 0
 | 
					                                Standing: 0
 | 
				
			||||||
                            }) - 1
 | 
					                            }) - 1
 | 
				
			||||||
                        ];
 | 
					                        ];
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            affiliation.Standing += meta.standing;
 | 
					                affiliation.Standing += meta.standing!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (affiliationMods.length == 0) {
 | 
					                if (affiliationMods.length == 0) {
 | 
				
			||||||
                affiliationMods.push({ Tag: ExportNightwave.affiliationTag });
 | 
					                    affiliationMods.push({ Tag: nightwaveSyndicateTag });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                affiliationMods[0].Standing ??= 0;
 | 
					                affiliationMods[0].Standing ??= 0;
 | 
				
			||||||
            affiliationMods[0].Standing += meta.standing;
 | 
					                affiliationMods[0].Standing += meta.standing!;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return affiliationMods;
 | 
					    return affiliationMods;
 | 
				
			||||||
 | 
				
			|||||||
@ -79,6 +79,7 @@ import { config } from "./configService";
 | 
				
			|||||||
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
 | 
					import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
 | 
				
			||||||
import { ISyndicateMissionInfo } from "../types/worldStateTypes";
 | 
					import { ISyndicateMissionInfo } from "../types/worldStateTypes";
 | 
				
			||||||
import { fromOid } from "../helpers/inventoryHelpers";
 | 
					import { fromOid } from "../helpers/inventoryHelpers";
 | 
				
			||||||
 | 
					import { TAccountDocument } from "./loginService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
 | 
					const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
 | 
				
			||||||
    // For Spy missions, e.g. 3 vaults cracked = A, B, C
 | 
					    // For Spy missions, e.g. 3 vaults cracked = A, B, C
 | 
				
			||||||
@ -135,6 +136,7 @@ const getRandomRewardByChance = (pool: IReward[], rng?: SRng): IRngResult | unde
 | 
				
			|||||||
//const knownUnhandledKeys: readonly string[] = ["test"] as const; // for unimplemented but important keys
 | 
					//const knownUnhandledKeys: readonly string[] = ["test"] as const; // for unimplemented but important keys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const addMissionInventoryUpdates = async (
 | 
					export const addMissionInventoryUpdates = async (
 | 
				
			||||||
 | 
					    account: TAccountDocument,
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
    inventoryUpdates: IMissionInventoryUpdateRequest
 | 
					    inventoryUpdates: IMissionInventoryUpdateRequest
 | 
				
			||||||
): Promise<IInventoryChanges> => {
 | 
					): Promise<IInventoryChanges> => {
 | 
				
			||||||
@ -287,7 +289,7 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                addRecipes(inventory, value);
 | 
					                addRecipes(inventory, value);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case "ChallengeProgress":
 | 
					            case "ChallengeProgress":
 | 
				
			||||||
                addChallenges(inventory, value, inventoryUpdates.SeasonChallengeCompletions);
 | 
					                addChallenges(account, inventory, value, inventoryUpdates.SeasonChallengeCompletions);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case "FusionTreasures":
 | 
					            case "FusionTreasures":
 | 
				
			||||||
                addFusionTreasures(inventory, value);
 | 
					                addFusionTreasures(inventory, value);
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,6 @@ import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSal
 | 
				
			|||||||
import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo/Nova1999ConquestShopManifest.json";
 | 
					import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo/Nova1999ConquestShopManifest.json";
 | 
				
			||||||
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
 | 
					import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
 | 
				
			||||||
import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
 | 
					import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
 | 
				
			||||||
import RadioLegionIntermission12VendorManifest from "@/static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json";
 | 
					 | 
				
			||||||
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
 | 
					import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
 | 
				
			||||||
import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json";
 | 
					import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json";
 | 
				
			||||||
import Temple1999VendorManifest from "@/static/fixed_responses/getVendorInfo/Temple1999VendorManifest.json";
 | 
					import Temple1999VendorManifest from "@/static/fixed_responses/getVendorInfo/Temple1999VendorManifest.json";
 | 
				
			||||||
@ -54,7 +53,6 @@ const rawVendorManifests: IVendorManifest[] = [
 | 
				
			|||||||
    Nova1999ConquestShopManifest,
 | 
					    Nova1999ConquestShopManifest,
 | 
				
			||||||
    OstronPetVendorManifest,
 | 
					    OstronPetVendorManifest,
 | 
				
			||||||
    OstronProspectorVendorManifest,
 | 
					    OstronProspectorVendorManifest,
 | 
				
			||||||
    RadioLegionIntermission12VendorManifest,
 | 
					 | 
				
			||||||
    SolarisDebtTokenVendorRepossessionsManifest,
 | 
					    SolarisDebtTokenVendorRepossessionsManifest,
 | 
				
			||||||
    SolarisProspectorVendorManifest,
 | 
					    SolarisProspectorVendorManifest,
 | 
				
			||||||
    Temple1999VendorManifest,
 | 
					    Temple1999VendorManifest,
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import { buildConfig } from "@/src/services/buildConfigService";
 | 
				
			|||||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
					import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
				
			||||||
import { config } from "@/src/services/configService";
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
import { SRng } from "@/src/services/rngService";
 | 
					import { SRng } from "@/src/services/rngService";
 | 
				
			||||||
import { ExportNightwave, ExportRegions, IRegion } from "warframe-public-export-plus";
 | 
					import { ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ICalendarDay,
 | 
					    ICalendarDay,
 | 
				
			||||||
    ICalendarEvent,
 | 
					    ICalendarEvent,
 | 
				
			||||||
@ -344,11 +344,26 @@ export const getSortie = (day: number): ISortie => {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dailyChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
 | 
					interface IRotatingSeasonChallengePools {
 | 
				
			||||||
    x.startsWith("/Lotus/Types/Challenges/Seasons/Daily/")
 | 
					    daily: string[];
 | 
				
			||||||
);
 | 
					    weekly: string[];
 | 
				
			||||||
 | 
					    hardWeekly: string[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getSeasonDailyChallenge = (day: number): ISeasonChallenge => {
 | 
					const getSeasonChallengePools = (syndicateTag: string): IRotatingSeasonChallengePools => {
 | 
				
			||||||
 | 
					    const syndicate = ExportSyndicates[syndicateTag];
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        daily: syndicate.dailyChallenges!,
 | 
				
			||||||
 | 
					        weekly: syndicate.weeklyChallenges!.filter(
 | 
				
			||||||
 | 
					            x =>
 | 
				
			||||||
 | 
					                x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/") &&
 | 
				
			||||||
 | 
					                !x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanent")
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        hardWeekly: syndicate.weeklyChallenges!.filter(x => x.startsWith("/Lotus/Types/Challenges/Seasons/WeeklyHard/"))
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getSeasonDailyChallenge = (pools: IRotatingSeasonChallengePools, day: number): ISeasonChallenge => {
 | 
				
			||||||
    const dayStart = EPOCH + day * 86400000;
 | 
					    const dayStart = EPOCH + day * 86400000;
 | 
				
			||||||
    const dayEnd = EPOCH + (day + 3) * 86400000;
 | 
					    const dayEnd = EPOCH + (day + 3) * 86400000;
 | 
				
			||||||
    const rng = new SRng(new SRng(day).randomInt(0, 100_000));
 | 
					    const rng = new SRng(new SRng(day).randomInt(0, 100_000));
 | 
				
			||||||
@ -357,17 +372,11 @@ const getSeasonDailyChallenge = (day: number): ISeasonChallenge => {
 | 
				
			|||||||
        Daily: true,
 | 
					        Daily: true,
 | 
				
			||||||
        Activation: { $date: { $numberLong: dayStart.toString() } },
 | 
					        Activation: { $date: { $numberLong: dayStart.toString() } },
 | 
				
			||||||
        Expiry: { $date: { $numberLong: dayEnd.toString() } },
 | 
					        Expiry: { $date: { $numberLong: dayEnd.toString() } },
 | 
				
			||||||
        Challenge: rng.randomElement(dailyChallenges)!
 | 
					        Challenge: rng.randomElement(pools.daily)!
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const weeklyChallenges = Object.keys(ExportNightwave.challenges).filter(
 | 
					const getSeasonWeeklyChallenge = (pools: IRotatingSeasonChallengePools, week: number, id: number): ISeasonChallenge => {
 | 
				
			||||||
    x =>
 | 
					 | 
				
			||||||
        x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/") &&
 | 
					 | 
				
			||||||
        !x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanent")
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const getSeasonWeeklyChallenge = (week: number, id: number): ISeasonChallenge => {
 | 
					 | 
				
			||||||
    const weekStart = EPOCH + week * 604800000;
 | 
					    const weekStart = EPOCH + week * 604800000;
 | 
				
			||||||
    const weekEnd = weekStart + 604800000;
 | 
					    const weekEnd = weekStart + 604800000;
 | 
				
			||||||
    const challengeId = week * 7 + id;
 | 
					    const challengeId = week * 7 + id;
 | 
				
			||||||
@ -376,15 +385,15 @@ const getSeasonWeeklyChallenge = (week: number, id: number): ISeasonChallenge =>
 | 
				
			|||||||
        _id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
 | 
					        _id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
 | 
				
			||||||
        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
					        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
				
			||||||
        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
					        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
				
			||||||
        Challenge: rng.randomElement(weeklyChallenges)!
 | 
					        Challenge: rng.randomElement(pools.weekly)!
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const weeklyHardChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
 | 
					const getSeasonWeeklyHardChallenge = (
 | 
				
			||||||
    x.startsWith("/Lotus/Types/Challenges/Seasons/WeeklyHard/")
 | 
					    pools: IRotatingSeasonChallengePools,
 | 
				
			||||||
);
 | 
					    week: number,
 | 
				
			||||||
 | 
					    id: number
 | 
				
			||||||
const getSeasonWeeklyHardChallenge = (week: number, id: number): ISeasonChallenge => {
 | 
					): ISeasonChallenge => {
 | 
				
			||||||
    const weekStart = EPOCH + week * 604800000;
 | 
					    const weekStart = EPOCH + week * 604800000;
 | 
				
			||||||
    const weekEnd = weekStart + 604800000;
 | 
					    const weekEnd = weekStart + 604800000;
 | 
				
			||||||
    const challengeId = week * 7 + id;
 | 
					    const challengeId = week * 7 + id;
 | 
				
			||||||
@ -393,35 +402,39 @@ const getSeasonWeeklyHardChallenge = (week: number, id: number): ISeasonChalleng
 | 
				
			|||||||
        _id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
 | 
					        _id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
 | 
				
			||||||
        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
					        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
				
			||||||
        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
					        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
				
			||||||
        Challenge: rng.randomElement(weeklyHardChallenges)!
 | 
					        Challenge: rng.randomElement(pools.hardWeekly)!
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const pushWeeklyActs = (worldState: IWorldState, week: number): void => {
 | 
					const pushWeeklyActs = (
 | 
				
			||||||
 | 
					    activeChallenges: ISeasonChallenge[],
 | 
				
			||||||
 | 
					    pools: IRotatingSeasonChallengePools,
 | 
				
			||||||
 | 
					    week: number
 | 
				
			||||||
 | 
					): void => {
 | 
				
			||||||
    const weekStart = EPOCH + week * 604800000;
 | 
					    const weekStart = EPOCH + week * 604800000;
 | 
				
			||||||
    const weekEnd = weekStart + 604800000;
 | 
					    const weekEnd = weekStart + 604800000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 0));
 | 
					    activeChallenges.push(getSeasonWeeklyChallenge(pools, week, 0));
 | 
				
			||||||
    worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyChallenge(week, 1));
 | 
					    activeChallenges.push(getSeasonWeeklyChallenge(pools, week, 1));
 | 
				
			||||||
    worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 2));
 | 
					    activeChallenges.push(getSeasonWeeklyHardChallenge(pools, week, 2));
 | 
				
			||||||
    worldState.SeasonInfo.ActiveChallenges.push(getSeasonWeeklyHardChallenge(week, 3));
 | 
					    activeChallenges.push(getSeasonWeeklyHardChallenge(pools, week, 3));
 | 
				
			||||||
    worldState.SeasonInfo.ActiveChallenges.push({
 | 
					    activeChallenges.push({
 | 
				
			||||||
        _id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 0).toString().padStart(8, "0") },
 | 
					        _id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 0).toString().padStart(8, "0") },
 | 
				
			||||||
        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
					        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
				
			||||||
        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
					        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
				
			||||||
        Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentCompleteMissions" + (week - 12)
 | 
					        Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentCompleteMissions"
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    worldState.SeasonInfo.ActiveChallenges.push({
 | 
					    activeChallenges.push({
 | 
				
			||||||
        _id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 1).toString().padStart(8, "0") },
 | 
					        _id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 1).toString().padStart(8, "0") },
 | 
				
			||||||
        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
					        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
				
			||||||
        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
					        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
				
			||||||
        Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEximus" + (week - 12)
 | 
					        Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEximus"
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    worldState.SeasonInfo.ActiveChallenges.push({
 | 
					    activeChallenges.push({
 | 
				
			||||||
        _id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 2).toString().padStart(8, "0") },
 | 
					        _id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 2).toString().padStart(8, "0") },
 | 
				
			||||||
        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
					        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
				
			||||||
        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
					        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
				
			||||||
        Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEnemies" + (week - 12)
 | 
					        Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEnemies"
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -926,15 +939,6 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        LiteSorties: [],
 | 
					        LiteSorties: [],
 | 
				
			||||||
        GlobalUpgrades: [],
 | 
					        GlobalUpgrades: [],
 | 
				
			||||||
        EndlessXpChoices: [],
 | 
					        EndlessXpChoices: [],
 | 
				
			||||||
        SeasonInfo: {
 | 
					 | 
				
			||||||
            Activation: { $date: { $numberLong: "1715796000000" } },
 | 
					 | 
				
			||||||
            Expiry: { $date: { $numberLong: "2000000000000" } },
 | 
					 | 
				
			||||||
            AffiliationTag: "RadioLegionIntermission12Syndicate",
 | 
					 | 
				
			||||||
            Season: 14,
 | 
					 | 
				
			||||||
            Phase: 0,
 | 
					 | 
				
			||||||
            Params: "",
 | 
					 | 
				
			||||||
            ActiveChallenges: []
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        KnownCalendarSeasons: [],
 | 
					        KnownCalendarSeasons: [],
 | 
				
			||||||
        ...staticWorldState,
 | 
					        ...staticWorldState,
 | 
				
			||||||
        SyndicateMissions: [...staticWorldState.SyndicateMissions]
 | 
					        SyndicateMissions: [...staticWorldState.SyndicateMissions]
 | 
				
			||||||
@ -967,17 +971,27 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Nightwave Challenges
 | 
					    // Nightwave Challenges
 | 
				
			||||||
    // 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.
 | 
					    const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
 | 
				
			||||||
    if (!buildLabel || version_compare(buildLabel, "2025.02.05.11.19") >= 0) {
 | 
					    if (nightwaveSyndicateTag) {
 | 
				
			||||||
        worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day - 2));
 | 
					        worldState.SeasonInfo = {
 | 
				
			||||||
        worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day - 1));
 | 
					            Activation: { $date: { $numberLong: "1715796000000" } },
 | 
				
			||||||
        worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day - 0));
 | 
					            Expiry: { $date: { $numberLong: "2000000000000" } },
 | 
				
			||||||
 | 
					            AffiliationTag: nightwaveSyndicateTag,
 | 
				
			||||||
 | 
					            Season: nightwaveTagToSeason[nightwaveSyndicateTag],
 | 
				
			||||||
 | 
					            Phase: 0,
 | 
				
			||||||
 | 
					            Params: "",
 | 
				
			||||||
 | 
					            ActiveChallenges: []
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        const pools = getSeasonChallengePools(nightwaveSyndicateTag);
 | 
				
			||||||
 | 
					        worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(pools, day - 2));
 | 
				
			||||||
 | 
					        worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(pools, day - 1));
 | 
				
			||||||
 | 
					        worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(pools, day - 0));
 | 
				
			||||||
        if (isBeforeNextExpectedWorldStateRefresh(EPOCH + (day + 1) * 86400000)) {
 | 
					        if (isBeforeNextExpectedWorldStateRefresh(EPOCH + (day + 1) * 86400000)) {
 | 
				
			||||||
            worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(day + 1));
 | 
					            worldState.SeasonInfo.ActiveChallenges.push(getSeasonDailyChallenge(pools, day + 1));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        pushWeeklyActs(worldState, week);
 | 
					        pushWeeklyActs(worldState.SeasonInfo.ActiveChallenges, pools, week);
 | 
				
			||||||
        if (isBeforeNextExpectedWorldStateRefresh(weekEnd)) {
 | 
					        if (isBeforeNextExpectedWorldStateRefresh(weekEnd)) {
 | 
				
			||||||
            pushWeeklyActs(worldState, week + 1);
 | 
					            pushWeeklyActs(worldState.SeasonInfo.ActiveChallenges, pools, week + 1);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1242,3 +1256,18 @@ export const isArchwingMission = (node: IRegion): boolean => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getNightwaveSyndicateTag = (buildLabel: string | undefined): string | undefined => {
 | 
				
			||||||
 | 
					    if (!buildLabel || version_compare(buildLabel, "2025.05.20.10.18") >= 0) {
 | 
				
			||||||
 | 
					        return "RadioLegionIntermission13Syndicate";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (version_compare(buildLabel, "2025.02.05.11.19") >= 0) {
 | 
				
			||||||
 | 
					        return "RadioLegionIntermission12Syndicate";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return undefined;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const nightwaveTagToSeason: Record<string, number> = {
 | 
				
			||||||
 | 
					    RadioLegionIntermission13Syndicate: 15,
 | 
				
			||||||
 | 
					    RadioLegionIntermission12Syndicate: 14
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@ export interface IWorldState {
 | 
				
			|||||||
    NodeOverrides: INodeOverride[];
 | 
					    NodeOverrides: INodeOverride[];
 | 
				
			||||||
    PVPChallengeInstances: IPVPChallengeInstance[];
 | 
					    PVPChallengeInstances: IPVPChallengeInstance[];
 | 
				
			||||||
    EndlessXpChoices: IEndlessXpChoice[];
 | 
					    EndlessXpChoices: IEndlessXpChoice[];
 | 
				
			||||||
    SeasonInfo: {
 | 
					    SeasonInfo?: {
 | 
				
			||||||
        Activation: IMongoDate;
 | 
					        Activation: IMongoDate;
 | 
				
			||||||
        Expiry: IMongoDate;
 | 
					        Expiry: IMongoDate;
 | 
				
			||||||
        AffiliationTag: string;
 | 
					        AffiliationTag: string;
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user