forked from OpenWF/SpaceNinjaServer
		
	Compare commits
	
		
			19 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9369e6cc5a | |||
| 49eb14f758 | |||
| 5e455d1e90 | |||
| 4d93dc80dc | |||
| 38326fc452 | |||
| 6e9c787c59 | |||
| 82fe598056 | |||
| bc271216ac | |||
| c5463166a8 | |||
| 8bce83d14c | |||
| cc5682760d | |||
| 00acaed62a | |||
| d794bd94ce | |||
| cecc65197b | |||
| b1c1b56de3 | |||
| 167da9c573 | |||
| 5e6955ae32 | |||
| f2145ed91b | |||
| 20d9a699b4 | 
@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					export const EPOCH = 1734307200_000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be the start of winter in 1999 iteration 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const millisecondsPerSecond = 1000;
 | 
					const millisecondsPerSecond = 1000;
 | 
				
			||||||
const secondsPerMinute = 60;
 | 
					const secondsPerMinute = 60;
 | 
				
			||||||
const minutesPerHour = 60;
 | 
					const minutesPerHour = 60;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,17 @@
 | 
				
			|||||||
import type { RequestHandler } from "express";
 | 
					import type { RequestHandler } from "express";
 | 
				
			||||||
import { deleteSession } from "../../managers/sessionManager.ts";
 | 
					import { deleteSession } from "../../managers/sessionManager.ts";
 | 
				
			||||||
 | 
					import { getAccountForRequest } from "../../services/loginService.ts";
 | 
				
			||||||
 | 
					import { version_compare } from "../../helpers/inventoryHelpers.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const deleteSessionController: RequestHandler = (_req, res) => {
 | 
					const deleteSessionController: RequestHandler = async (_req, res) => {
 | 
				
			||||||
 | 
					    const account = await getAccountForRequest(_req);
 | 
				
			||||||
    deleteSession(_req.query.sessionId as string);
 | 
					    deleteSession(_req.query.sessionId as string);
 | 
				
			||||||
    res.sendStatus(200);
 | 
					    if (account.BuildLabel && version_compare(account.BuildLabel, "2016.07.08.16.56") < 0) {
 | 
				
			||||||
 | 
					        // Pre-Specters of the Rail
 | 
				
			||||||
 | 
					        res.send(_req.query.sessionId as string); // Unsure if this is correct, but the client is chill with it
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        res.sendStatus(200);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { deleteSessionController };
 | 
					export { deleteSessionController };
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
import type { RequestHandler } from "express";
 | 
					import type { RequestHandler } from "express";
 | 
				
			||||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
 | 
					import { getAccountIdForRequest } from "../../services/loginService.ts";
 | 
				
			||||||
import { getInventory } from "../../services/inventoryService.ts";
 | 
					import { getInventory } from "../../services/inventoryService.ts";
 | 
				
			||||||
import { EPOCH, getSeasonChallengePools, getWorldState, pushWeeklyActs } from "../../services/worldStateService.ts";
 | 
					import { getSeasonChallengePools, getWorldState, pushWeeklyActs } from "../../services/worldStateService.ts";
 | 
				
			||||||
import { unixTimesInMs } from "../../constants/timeConstants.ts";
 | 
					import { EPOCH, unixTimesInMs } from "../../constants/timeConstants.ts";
 | 
				
			||||||
import type { ISeasonChallenge } from "../../types/worldStateTypes.ts";
 | 
					import type { ISeasonChallenge } from "../../types/worldStateTypes.ts";
 | 
				
			||||||
import { ExportChallenges } from "warframe-public-export-plus";
 | 
					import { ExportChallenges } from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -13,8 +13,8 @@ const hostSessionController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    const session = createNewSession(hostSessionRequest, account._id);
 | 
					    const session = createNewSession(hostSessionRequest, account._id);
 | 
				
			||||||
    logger.debug(`New Session Created`, { session });
 | 
					    logger.debug(`New Session Created`, { session });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (account.BuildLabel && version_compare(account.BuildLabel, "2015.03.21.08.17") < 0) {
 | 
					    if (account.BuildLabel && version_compare(account.BuildLabel, "2016.07.08.16.56") < 0) {
 | 
				
			||||||
        // U15 or below
 | 
					        // Pre-Specters of the Rail
 | 
				
			||||||
        res.send(session.sessionId.toString());
 | 
					        res.send(session.sessionId.toString());
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        res.json({ sessionId: toOid2(session.sessionId, account.BuildLabel), rewardSeed: 99999999 });
 | 
					        res.json({ sessionId: toOid2(session.sessionId, account.BuildLabel), rewardSeed: 99999999 });
 | 
				
			||||||
 | 
				
			|||||||
@ -310,12 +310,13 @@ export const getInventoryResponse = async (
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (inventory.skipAllDialogue) {
 | 
					    if (inventory.skipAllDialogue) {
 | 
				
			||||||
        inventoryResponse.TauntHistory = [
 | 
					        inventoryResponse.TauntHistory ??= [];
 | 
				
			||||||
            {
 | 
					        if (!inventoryResponse.TauntHistory.find(x => x.node == "TreasureTutorial")) {
 | 
				
			||||||
 | 
					            inventoryResponse.TauntHistory.push({
 | 
				
			||||||
                node: "TreasureTutorial",
 | 
					                node: "TreasureTutorial",
 | 
				
			||||||
                state: "TS_COMPLETED"
 | 
					                state: "TS_COMPLETED"
 | 
				
			||||||
            }
 | 
					            });
 | 
				
			||||||
        ];
 | 
					        }
 | 
				
			||||||
        for (const str of allDialogue) {
 | 
					        for (const str of allDialogue) {
 | 
				
			||||||
            addString(inventoryResponse.NodeIntrosCompleted, str);
 | 
					            addString(inventoryResponse.NodeIntrosCompleted, str);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -353,11 +354,11 @@ export const getInventoryResponse = async (
 | 
				
			|||||||
        if (!xpBasedLevelCapDisabled) {
 | 
					        if (!xpBasedLevelCapDisabled) {
 | 
				
			||||||
            // This client has not been patched to accept any mastery rank, need to fake the XP.
 | 
					            // This client has not been patched to accept any mastery rank, need to fake the XP.
 | 
				
			||||||
            inventoryResponse.XPInfo = [];
 | 
					            inventoryResponse.XPInfo = [];
 | 
				
			||||||
            let numFrames = getExpRequiredForMr(Math.min(inventory.spoofMasteryRank, 5030)) / 6000;
 | 
					            let numFrames = getExpRequiredForMr(Math.min(inventory.spoofMasteryRank, 5030)) / (30 * 200);
 | 
				
			||||||
            while (numFrames-- > 0) {
 | 
					            while (numFrames-- > 0) {
 | 
				
			||||||
                inventoryResponse.XPInfo.push({
 | 
					                inventoryResponse.XPInfo.push({
 | 
				
			||||||
                    ItemType: "/Lotus/Powersuits/Mag/Mag",
 | 
					                    ItemType: "/Lotus/Powersuits/Mag/Mag",
 | 
				
			||||||
                    XP: 1_600_000
 | 
					                    XP: 900_000 // Enough for rank 30 as per https://wiki.warframe.com/w/Affinity
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -109,12 +109,19 @@ const createLoginResponse = (
 | 
				
			|||||||
    const resp: ILoginResponse = {
 | 
					    const resp: ILoginResponse = {
 | 
				
			||||||
        id: account.id,
 | 
					        id: account.id,
 | 
				
			||||||
        DisplayName: account.DisplayName,
 | 
					        DisplayName: account.DisplayName,
 | 
				
			||||||
        CountryCode: account.CountryCode,
 | 
					 | 
				
			||||||
        AmazonAuthToken: account.AmazonAuthToken,
 | 
					        AmazonAuthToken: account.AmazonAuthToken,
 | 
				
			||||||
        AmazonRefreshToken: account.AmazonRefreshToken,
 | 
					        AmazonRefreshToken: account.AmazonRefreshToken,
 | 
				
			||||||
        Nonce: account.Nonce,
 | 
					        Nonce: account.Nonce,
 | 
				
			||||||
        BuildLabel: buildLabel
 | 
					        BuildLabel: buildLabel
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					    if (version_compare(buildLabel, "2014.10.24.08.24") >= 0) {
 | 
				
			||||||
 | 
					        // U15 and up
 | 
				
			||||||
 | 
					        resp.CountryCode = account.CountryCode;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // U8
 | 
				
			||||||
 | 
					        resp.NatHash = "0";
 | 
				
			||||||
 | 
					        resp.SteamId = "0";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (version_compare(buildLabel, "2015.02.13.10.41") >= 0) {
 | 
					    if (version_compare(buildLabel, "2015.02.13.10.41") >= 0) {
 | 
				
			||||||
        resp.NRS = [config.nrsAddress ?? myAddress];
 | 
					        resp.NRS = [config.nrsAddress ?? myAddress];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -36,6 +36,11 @@ export const completeAllMissionsController: RequestHandler = async (req, res) =>
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    addString(inventory.NodeIntrosCompleted, "TeshinHardModeUnlocked");
 | 
					    addString(inventory.NodeIntrosCompleted, "TeshinHardModeUnlocked");
 | 
				
			||||||
    addString(inventory.NodeIntrosCompleted, "CetusSyndicate_IntroJob");
 | 
					    addString(inventory.NodeIntrosCompleted, "CetusSyndicate_IntroJob");
 | 
				
			||||||
 | 
					    let syndicate = inventory.Affiliations.find(x => x.Tag == "CetusSyndicate");
 | 
				
			||||||
 | 
					    if (!syndicate) {
 | 
				
			||||||
 | 
					        syndicate =
 | 
				
			||||||
 | 
					            inventory.Affiliations[inventory.Affiliations.push({ Tag: "CetusSyndicate", Standing: 250, Title: 0 })]; // Non-zero standing avoids Konzu's "prove yourself" text. 250 is identical to newbie bounty + bonus
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    await inventory.save();
 | 
					    await inventory.save();
 | 
				
			||||||
    res.end();
 | 
					    res.end();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -37,7 +37,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    switch (operation) {
 | 
					    switch (operation) {
 | 
				
			||||||
        case "completeAll": {
 | 
					        case "completeAll": {
 | 
				
			||||||
            for (const questKey of inventory.QuestKeys) {
 | 
					            for (const questKey of inventory.QuestKeys) {
 | 
				
			||||||
                await completeQuest(inventory, questKey.ItemType);
 | 
					                await completeQuest(inventory, questKey.ItemType, undefined);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -61,6 +61,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            inventory.QuestKeys.pull({ ItemType: questItemType });
 | 
					            inventory.QuestKeys.pull({ ItemType: questItemType });
 | 
				
			||||||
 | 
					            if (inventory.ActiveQuest == questItemType) inventory.ActiveQuest = "";
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        case "completeKey": {
 | 
					        case "completeKey": {
 | 
				
			||||||
@ -71,7 +72,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await completeQuest(inventory, questItemType);
 | 
					                await completeQuest(inventory, questItemType, undefined);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -136,7 +137,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (currentStage + 1 == questManifest.chainStages?.length) {
 | 
					                if (currentStage + 1 == questManifest.chainStages?.length) {
 | 
				
			||||||
                    logger.debug(`Trying to complete last stage with nextStage, calling completeQuest instead`);
 | 
					                    logger.debug(`Trying to complete last stage with nextStage, calling completeQuest instead`);
 | 
				
			||||||
                    await completeQuest(inventory, questKey.ItemType, true);
 | 
					                    await completeQuest(inventory, questKey.ItemType, undefined, true);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    if (run > 0) {
 | 
					                    if (run > 0) {
 | 
				
			||||||
                        questKey.Progress[currentStage + 1].c = run;
 | 
					                        questKey.Progress[currentStage + 1].c = run;
 | 
				
			||||||
@ -150,10 +151,14 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (currentStage > 0) {
 | 
					                    if (currentStage > 0) {
 | 
				
			||||||
                        await giveKeyChainMissionReward(inventory, {
 | 
					                        await giveKeyChainMissionReward(
 | 
				
			||||||
                            KeyChain: questKey.ItemType,
 | 
					                            inventory,
 | 
				
			||||||
                            ChainStage: currentStage
 | 
					                            {
 | 
				
			||||||
                        });
 | 
					                                KeyChain: questKey.ItemType,
 | 
				
			||||||
 | 
					                                ChainStage: currentStage
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            undefined
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ import { addPendingFriendController } from "../controllers/api/addPendingFriendC
 | 
				
			|||||||
import { addToAllianceController } from "../controllers/api/addToAllianceController.ts";
 | 
					import { addToAllianceController } from "../controllers/api/addToAllianceController.ts";
 | 
				
			||||||
import { addToGuildController } from "../controllers/api/addToGuildController.ts";
 | 
					import { addToGuildController } from "../controllers/api/addToGuildController.ts";
 | 
				
			||||||
import { adoptPetController } from "../controllers/api/adoptPetController.ts";
 | 
					import { adoptPetController } from "../controllers/api/adoptPetController.ts";
 | 
				
			||||||
 | 
					import { aggregateSessionsController } from "../controllers/dynamic/aggregateSessionsController.ts";
 | 
				
			||||||
import { apartmentController } from "../controllers/api/apartmentController.ts";
 | 
					import { apartmentController } from "../controllers/api/apartmentController.ts";
 | 
				
			||||||
import { arcaneCommonController } from "../controllers/api/arcaneCommonController.ts";
 | 
					import { arcaneCommonController } from "../controllers/api/arcaneCommonController.ts";
 | 
				
			||||||
import { archonFusionController } from "../controllers/api/archonFusionController.ts";
 | 
					import { archonFusionController } from "../controllers/api/archonFusionController.ts";
 | 
				
			||||||
@ -170,6 +171,7 @@ import { upgradeOperatorController } from "../controllers/api/upgradeOperatorCon
 | 
				
			|||||||
import { upgradesController } from "../controllers/api/upgradesController.ts";
 | 
					import { upgradesController } from "../controllers/api/upgradesController.ts";
 | 
				
			||||||
import { valenceSwapController } from "../controllers/api/valenceSwapController.ts";
 | 
					import { valenceSwapController } from "../controllers/api/valenceSwapController.ts";
 | 
				
			||||||
import { wishlistController } from "../controllers/api/wishlistController.ts";
 | 
					import { wishlistController } from "../controllers/api/wishlistController.ts";
 | 
				
			||||||
 | 
					import { worldStateController } from "../controllers/dynamic/worldStateController.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const apiRouter = express.Router();
 | 
					const apiRouter = express.Router();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -235,6 +237,7 @@ apiRouter.get("/surveys.php", surveysController);
 | 
				
			|||||||
apiRouter.get("/trading.php", tradingController);
 | 
					apiRouter.get("/trading.php", tradingController);
 | 
				
			||||||
apiRouter.get("/updateSession.php", updateSessionGetController);
 | 
					apiRouter.get("/updateSession.php", updateSessionGetController);
 | 
				
			||||||
apiRouter.get("/upgradeOperator.php", upgradeOperatorController);
 | 
					apiRouter.get("/upgradeOperator.php", upgradeOperatorController);
 | 
				
			||||||
 | 
					apiRouter.get("/worldState.php", worldStateController); // U8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// post
 | 
					// post
 | 
				
			||||||
apiRouter.post("/abortDojoComponent.php", abortDojoComponentController);
 | 
					apiRouter.post("/abortDojoComponent.php", abortDojoComponentController);
 | 
				
			||||||
@ -246,6 +249,7 @@ apiRouter.post("/addPendingFriend.php", addPendingFriendController);
 | 
				
			|||||||
apiRouter.post("/addToAlliance.php", addToAllianceController);
 | 
					apiRouter.post("/addToAlliance.php", addToAllianceController);
 | 
				
			||||||
apiRouter.post("/addToGuild.php", addToGuildController);
 | 
					apiRouter.post("/addToGuild.php", addToGuildController);
 | 
				
			||||||
apiRouter.post("/adoptPet.php", adoptPetController);
 | 
					apiRouter.post("/adoptPet.php", adoptPetController);
 | 
				
			||||||
 | 
					apiRouter.post("/aggregateSessions.php", aggregateSessionsController); // Pre-Specters of the Rail builds
 | 
				
			||||||
apiRouter.post("/arcaneCommon.php", arcaneCommonController);
 | 
					apiRouter.post("/arcaneCommon.php", arcaneCommonController);
 | 
				
			||||||
apiRouter.post("/archonFusion.php", archonFusionController);
 | 
					apiRouter.post("/archonFusion.php", archonFusionController);
 | 
				
			||||||
apiRouter.post("/artifacts.php", artifactsController);
 | 
					apiRouter.post("/artifacts.php", artifactsController);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import express from "express";
 | 
					import express from "express";
 | 
				
			||||||
import path from "path";
 | 
					import path from "path";
 | 
				
			||||||
 | 
					import fs from "fs/promises";
 | 
				
			||||||
import { repoDir, rootDir } from "../helpers/pathHelper.ts";
 | 
					import { repoDir, rootDir } from "../helpers/pathHelper.ts";
 | 
				
			||||||
import { args } from "../helpers/commandLineArguments.ts";
 | 
					import { args } from "../helpers/commandLineArguments.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -21,50 +22,62 @@ webuiRouter.use("/webui", (req, res, next) => {
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serve virtual routes
 | 
					// Serve virtual routes
 | 
				
			||||||
webuiRouter.get("/webui/inventory", (_req, res) => {
 | 
					webuiRouter.get("/webui/inventory", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(baseDir, "static/webui/index.html"));
 | 
					    res.set("Content-Type", "text/html;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(baseDir, "static/webui/index.html")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
webuiRouter.get("/webui/detailedView", (_req, res) => {
 | 
					webuiRouter.get("/webui/detailedView", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(baseDir, "static/webui/index.html"));
 | 
					    res.set("Content-Type", "text/html;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(baseDir, "static/webui/index.html")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
webuiRouter.get("/webui/mods", (_req, res) => {
 | 
					webuiRouter.get("/webui/mods", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(baseDir, "static/webui/index.html"));
 | 
					    res.set("Content-Type", "text/html;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(baseDir, "static/webui/index.html")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
webuiRouter.get("/webui/settings", (_req, res) => {
 | 
					webuiRouter.get("/webui/settings", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(baseDir, "static/webui/index.html"));
 | 
					    res.set("Content-Type", "text/html;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(baseDir, "static/webui/index.html")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
webuiRouter.get("/webui/quests", (_req, res) => {
 | 
					webuiRouter.get("/webui/quests", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(baseDir, "static/webui/index.html"));
 | 
					    res.set("Content-Type", "text/html;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(baseDir, "static/webui/index.html")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
webuiRouter.get("/webui/cheats", (_req, res) => {
 | 
					webuiRouter.get("/webui/cheats", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(baseDir, "static/webui/index.html"));
 | 
					    res.set("Content-Type", "text/html;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(baseDir, "static/webui/index.html")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
webuiRouter.get("/webui/import", (_req, res) => {
 | 
					webuiRouter.get("/webui/import", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(baseDir, "static/webui/index.html"));
 | 
					    res.set("Content-Type", "text/html;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(baseDir, "static/webui/index.html")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
webuiRouter.get("/webui/guildView", (_req, res) => {
 | 
					webuiRouter.get("/webui/guildView", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(baseDir, "static/webui/index.html"));
 | 
					    res.set("Content-Type", "text/html;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(baseDir, "static/webui/index.html")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serve static files
 | 
					// Serve static files
 | 
				
			||||||
webuiRouter.use("/webui", express.static(path.join(baseDir, "static/webui")));
 | 
					webuiRouter.use("/webui", express.static(path.join(baseDir, "static/webui")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serve favicon
 | 
					// Serve favicon
 | 
				
			||||||
webuiRouter.get("/favicon.ico", (_req, res) => {
 | 
					webuiRouter.get("/favicon.ico", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(repoDir, "static/fixed_responses/favicon.ico"));
 | 
					    res.set("Content-Type", "image/vnd.microsoft.icon");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(repoDir, "static/fixed_responses/favicon.ico")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serve warframe-riven-info
 | 
					// Serve warframe-riven-info
 | 
				
			||||||
webuiRouter.get("/webui/riven-tool/", (_req, res) => {
 | 
					webuiRouter.get("/webui/riven-tool/", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(repoDir, "node_modules/warframe-riven-info/index.html"));
 | 
					    res.set("Content-Type", "text/html;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(repoDir, "node_modules/warframe-riven-info/index.html")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
webuiRouter.get("/webui/riven-tool/RivenParser.js", (_req, res) => {
 | 
					webuiRouter.get("/webui/riven-tool/RivenParser.js", async (_req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(repoDir, "node_modules/warframe-riven-info/RivenParser.js"));
 | 
					    res.set("Content-Type", "text/javascript;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(repoDir, "node_modules/warframe-riven-info/RivenParser.js")));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serve translations
 | 
					// Serve translations
 | 
				
			||||||
webuiRouter.get("/translations/:file", (req, res) => {
 | 
					webuiRouter.get("/translations/:file", async (req, res) => {
 | 
				
			||||||
    res.sendFile(path.join(baseDir, `static/webui/translations/${req.params.file}`));
 | 
					    res.set("Content-Type", "text/javascript;charset=utf8");
 | 
				
			||||||
 | 
					    res.send(await fs.readFile(path.join(baseDir, `static/webui/translations/${req.params.file}`)));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { webuiRouter };
 | 
					export { webuiRouter };
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										425
									
								
								src/services/conquestService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										425
									
								
								src/services/conquestService.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,425 @@
 | 
				
			|||||||
 | 
					import type { TFaction, TMissionType } from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					import type { CalendarSeasonType, IConquest, IConquestMission, TConquestType } from "../types/worldStateTypes.ts";
 | 
				
			||||||
 | 
					import { mixSeeds, SRng } from "./rngService.ts";
 | 
				
			||||||
 | 
					import { EPOCH } from "../constants/timeConstants.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const missionAndFactionTypes: Record<TConquestType, Partial<Record<TMissionType, TFaction[]>>> = {
 | 
				
			||||||
 | 
					    CT_LAB: {
 | 
				
			||||||
 | 
					        MT_EXTERMINATION: ["FC_MITW"],
 | 
				
			||||||
 | 
					        MT_SURVIVAL: ["FC_MITW"],
 | 
				
			||||||
 | 
					        MT_ALCHEMY: ["FC_MITW"],
 | 
				
			||||||
 | 
					        MT_DEFENSE: ["FC_MITW"],
 | 
				
			||||||
 | 
					        MT_ARTIFACT: ["FC_MITW"]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    CT_HEX: {
 | 
				
			||||||
 | 
					        MT_EXTERMINATION: ["FC_SCALDRA", "FC_TECHROT"],
 | 
				
			||||||
 | 
					        MT_SURVIVAL: ["FC_SCALDRA", "FC_TECHROT"],
 | 
				
			||||||
 | 
					        MT_DEFENSE: ["FC_SCALDRA"],
 | 
				
			||||||
 | 
					        MT_ENDLESS_CAPTURE: ["FC_TECHROT"]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const assassinationFactionOptions: Record<TConquestType, TFaction[]> = {
 | 
				
			||||||
 | 
					    CT_LAB: ["FC_MITW"],
 | 
				
			||||||
 | 
					    CT_HEX: ["FC_SCALDRA"]
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TConquestDifficulty = "CD_NORMAL" | "CD_HARD";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IConquestConditional {
 | 
				
			||||||
 | 
					    tag: string;
 | 
				
			||||||
 | 
					    missionType?: TMissionType;
 | 
				
			||||||
 | 
					    conquest?: TConquestType;
 | 
				
			||||||
 | 
					    difficulty?: TConquestDifficulty;
 | 
				
			||||||
 | 
					    season?: CalendarSeasonType;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const deviations: readonly IConquestConditional[] = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "AlchemicalShields",
 | 
				
			||||||
 | 
					        missionType: "MT_ALCHEMY"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "ContaminationZone",
 | 
				
			||||||
 | 
					        missionType: "MT_SURVIVAL",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "DoubleTrouble",
 | 
				
			||||||
 | 
					        missionType: "MT_ARTIFACT"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "EscalateImmediately",
 | 
				
			||||||
 | 
					        missionType: "MT_EXTERMINATION",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "EximusGrenadiers",
 | 
				
			||||||
 | 
					        missionType: "MT_ALCHEMY"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "FortifiedFoes",
 | 
				
			||||||
 | 
					        missionType: "MT_EXTERMINATION"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "FragileNodes",
 | 
				
			||||||
 | 
					        missionType: "MT_ARTIFACT"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "GrowingIncursion",
 | 
				
			||||||
 | 
					        missionType: "MT_EXTERMINATION",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "HarshWords",
 | 
				
			||||||
 | 
					        missionType: "MT_DEFENSE",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "HighScalingLegacyte",
 | 
				
			||||||
 | 
					        missionType: "MT_ENDLESS_CAPTURE",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "DoubleTroubleLegacyte",
 | 
				
			||||||
 | 
					        missionType: "MT_ENDLESS_CAPTURE",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "HostileSecurity",
 | 
				
			||||||
 | 
					        missionType: "MT_DEFENSE",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "InfiniteTide",
 | 
				
			||||||
 | 
					        missionType: "MT_ASSASSINATION",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "LostInTranslation",
 | 
				
			||||||
 | 
					        missionType: "MT_DEFENSE",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "MutatedEnemies",
 | 
				
			||||||
 | 
					        missionType: "MT_ENDLESS_CAPTURE",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "NecramechActivation",
 | 
				
			||||||
 | 
					        missionType: "MT_SURVIVAL",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "Reinforcements",
 | 
				
			||||||
 | 
					        missionType: "MT_ASSASSINATION",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "StickyFingers",
 | 
				
			||||||
 | 
					        missionType: "MT_ARTIFACT",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "TankStrongArmor",
 | 
				
			||||||
 | 
					        missionType: "MT_ASSASSINATION",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "TankReinforcements",
 | 
				
			||||||
 | 
					        missionType: "MT_ASSASSINATION",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "TankSuperToxic",
 | 
				
			||||||
 | 
					        missionType: "MT_ASSASSINATION",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "TechrotConjunction",
 | 
				
			||||||
 | 
					        missionType: "MT_SURVIVAL",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "UnpoweredCapsules",
 | 
				
			||||||
 | 
					        missionType: "MT_SURVIVAL",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "VolatileGrenades",
 | 
				
			||||||
 | 
					        missionType: "MT_ALCHEMY"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "GestatingTumors",
 | 
				
			||||||
 | 
					        missionType: "MT_SURVIVAL",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "ChemicalNoise",
 | 
				
			||||||
 | 
					        missionType: "MT_DEFENSE",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "ExplosiveEnergy",
 | 
				
			||||||
 | 
					        missionType: "MT_DEFENSE",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "DisruptiveSounds",
 | 
				
			||||||
 | 
					        missionType: "MT_DEFENSE",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const risks: readonly IConquestConditional[] = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "Voidburst"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "RegeneratingEnemies"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "VoidAberration"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "ShieldedFoes"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "PointBlank"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "Deflectors",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "AcceleratedEnemies"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "DrainingResiduals"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "Quicksand"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "AntiMaterialWeapons",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "ExplosiveCrawlers",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "EMPBlackHole",
 | 
				
			||||||
 | 
					        conquest: "CT_LAB"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "ArtilleryBeacons",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "InfectedTechrot",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "BalloonFest",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "MiasmiteHive",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "CompetitionSpillover",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "HostileOvergrowth",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "MurmurIncursion",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "FactionSwarm_Techrot",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX",
 | 
				
			||||||
 | 
					        difficulty: "CD_NORMAL"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "FactionSwarm_Scaldra",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX",
 | 
				
			||||||
 | 
					        difficulty: "CD_NORMAL"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "HeavyWarfare",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "ArcadeAutomata",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX",
 | 
				
			||||||
 | 
					        difficulty: "CD_NORMAL"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "EfervonFog",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "WinterFrost",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX",
 | 
				
			||||||
 | 
					        season: "CST_WINTER"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "JadeSpring",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX",
 | 
				
			||||||
 | 
					        season: "CST_SPRING"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "ExplosiveSummer",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX",
 | 
				
			||||||
 | 
					        season: "CST_SUMMER"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        tag: "FallFog",
 | 
				
			||||||
 | 
					        conquest: "CT_HEX",
 | 
				
			||||||
 | 
					        season: "CST_FALL"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const filterConditionals = (
 | 
				
			||||||
 | 
					    arr: readonly IConquestConditional[],
 | 
				
			||||||
 | 
					    missionType: TMissionType | null,
 | 
				
			||||||
 | 
					    conquest: TConquestType | null,
 | 
				
			||||||
 | 
					    difficulty: TConquestDifficulty | null,
 | 
				
			||||||
 | 
					    season: CalendarSeasonType | null
 | 
				
			||||||
 | 
					): string[] => {
 | 
				
			||||||
 | 
					    const applicable = [];
 | 
				
			||||||
 | 
					    for (const cond of arr) {
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            (!cond.missionType || cond.missionType == missionType) &&
 | 
				
			||||||
 | 
					            (!cond.conquest || cond.conquest == conquest) &&
 | 
				
			||||||
 | 
					            (!cond.difficulty || cond.difficulty == difficulty) &&
 | 
				
			||||||
 | 
					            (!cond.season || cond.season == season)
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            applicable.push(cond.tag);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return applicable;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const buildMission = (
 | 
				
			||||||
 | 
					    rng: SRng,
 | 
				
			||||||
 | 
					    conquest: TConquestType,
 | 
				
			||||||
 | 
					    missionType: TMissionType,
 | 
				
			||||||
 | 
					    faction: TFaction,
 | 
				
			||||||
 | 
					    season: CalendarSeasonType | null
 | 
				
			||||||
 | 
					): IConquestMission => {
 | 
				
			||||||
 | 
					    const deviation = rng.randomElement(filterConditionals(deviations, missionType, conquest, null, season))!;
 | 
				
			||||||
 | 
					    const easyRisk = rng.randomElement(filterConditionals(risks, missionType, conquest, "CD_NORMAL", season))!;
 | 
				
			||||||
 | 
					    const hardRiskOptions = filterConditionals(risks, missionType, conquest, "CD_HARD", season);
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const i = hardRiskOptions.indexOf(easyRisk);
 | 
				
			||||||
 | 
					        if (i != -1) {
 | 
				
			||||||
 | 
					            hardRiskOptions.splice(i, 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const hardRisk = rng.randomElement(hardRiskOptions)!;
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        faction,
 | 
				
			||||||
 | 
					        missionType,
 | 
				
			||||||
 | 
					        difficulties: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                type: "CD_NORMAL",
 | 
				
			||||||
 | 
					                deviation,
 | 
				
			||||||
 | 
					                risks: [easyRisk]
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                type: "CD_HARD",
 | 
				
			||||||
 | 
					                deviation,
 | 
				
			||||||
 | 
					                risks: [easyRisk, hardRisk]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const conquestStartingDay: Record<TConquestType, number> = {
 | 
				
			||||||
 | 
					    CT_LAB: 3703,
 | 
				
			||||||
 | 
					    CT_HEX: 4053
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This function produces identical results to clients pre-40.0.0.
 | 
				
			||||||
 | 
					const getFrameVariables = (conquestType: TConquestType, time: number): [string, string, string, string] => {
 | 
				
			||||||
 | 
					    const day = Math.floor((time - 1391990400_000) / 86400_000) - conquestStartingDay[conquestType];
 | 
				
			||||||
 | 
					    const week = Math.floor(day / 7) + 1;
 | 
				
			||||||
 | 
					    const frameVariables = [
 | 
				
			||||||
 | 
					        "Framecurse",
 | 
				
			||||||
 | 
					        "Knifestep",
 | 
				
			||||||
 | 
					        "Exhaustion",
 | 
				
			||||||
 | 
					        "Gearless",
 | 
				
			||||||
 | 
					        "TimeDilation",
 | 
				
			||||||
 | 
					        "Armorless",
 | 
				
			||||||
 | 
					        "Starvation",
 | 
				
			||||||
 | 
					        "ShieldDelay",
 | 
				
			||||||
 | 
					        "Withering",
 | 
				
			||||||
 | 
					        "ContactDamage",
 | 
				
			||||||
 | 
					        "AbilityLockout",
 | 
				
			||||||
 | 
					        "OperatorLockout",
 | 
				
			||||||
 | 
					        "EnergyStarved",
 | 
				
			||||||
 | 
					        "OverSensitive",
 | 
				
			||||||
 | 
					        "AntiGuard",
 | 
				
			||||||
 | 
					        "DecayingFlesh",
 | 
				
			||||||
 | 
					        "VoidEnergyOverload",
 | 
				
			||||||
 | 
					        "DullBlades",
 | 
				
			||||||
 | 
					        "Undersupplied"
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    const mag = Math.floor(frameVariables.length / 4);
 | 
				
			||||||
 | 
					    const rng = new SRng(conquestStartingDay[conquestType] + Math.floor(week / mag));
 | 
				
			||||||
 | 
					    rng.shuffleArray(frameVariables);
 | 
				
			||||||
 | 
					    const i = week % mag;
 | 
				
			||||||
 | 
					    return [frameVariables[i], frameVariables[i + 1], frameVariables[i + 2], frameVariables[i + 3]];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getConquest = (
 | 
				
			||||||
 | 
					    conquestType: TConquestType,
 | 
				
			||||||
 | 
					    week: number,
 | 
				
			||||||
 | 
					    season: CalendarSeasonType | null
 | 
				
			||||||
 | 
					): IConquest => {
 | 
				
			||||||
 | 
					    const rng = new SRng(mixSeeds(conquestStartingDay[conquestType], week));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const missions: IConquestMission[] = [];
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const missionOptions = Object.entries(missionAndFactionTypes[conquestType]);
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            const i = rng.randomInt(0, missionOptions.length - 1);
 | 
				
			||||||
 | 
					            const [missionType, factionOptions] = missionOptions.splice(i, 1)[0];
 | 
				
			||||||
 | 
					            missions.push(
 | 
				
			||||||
 | 
					                buildMission(rng, conquestType, missionType as TMissionType, rng.randomElement(factionOptions)!, season)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            const i = rng.randomInt(0, missionOptions.length - 1);
 | 
				
			||||||
 | 
					            const [missionType, factionOptions] = missionOptions.splice(i, 1)[0];
 | 
				
			||||||
 | 
					            missions.push(
 | 
				
			||||||
 | 
					                buildMission(rng, conquestType, missionType as TMissionType, rng.randomElement(factionOptions)!, season)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        missionOptions.push(["MT_ASSASSINATION", assassinationFactionOptions[conquestType]]);
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            const i = rng.randomInt(0, missionOptions.length - 1);
 | 
				
			||||||
 | 
					            const [missionType, factionOptions] = missionOptions.splice(i, 1)[0];
 | 
				
			||||||
 | 
					            missions.push(
 | 
				
			||||||
 | 
					                buildMission(rng, conquestType, missionType as TMissionType, rng.randomElement(factionOptions)!, season)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const weekStart = EPOCH + week * 604800000;
 | 
				
			||||||
 | 
					    const weekEnd = weekStart + 604800000;
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        Activation: { $date: { $numberLong: weekStart.toString() } },
 | 
				
			||||||
 | 
					        Expiry: { $date: { $numberLong: weekEnd.toString() } },
 | 
				
			||||||
 | 
					        Type: conquestType,
 | 
				
			||||||
 | 
					        Missions: missions,
 | 
				
			||||||
 | 
					        Variables: getFrameVariables(conquestType, weekStart),
 | 
				
			||||||
 | 
					        RandomSeed: rng.randomInt(0, 1_000_000)
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -116,8 +116,8 @@ export const createInventory = async (
 | 
				
			|||||||
        if (config.skipTutorial) {
 | 
					        if (config.skipTutorial) {
 | 
				
			||||||
            inventory.PlayedParkourTutorial = true;
 | 
					            inventory.PlayedParkourTutorial = true;
 | 
				
			||||||
            await addStartingGear(inventory);
 | 
					            await addStartingGear(inventory);
 | 
				
			||||||
            await completeQuest(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain");
 | 
					            await completeQuest(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain", undefined);
 | 
				
			||||||
            await completeQuest(inventory, "/Lotus/Types/Keys/ModQuest/ModQuestKeyChain");
 | 
					            await completeQuest(inventory, "/Lotus/Types/Keys/ModQuest/ModQuestKeyChain", undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const completedMissions = ["SolNode27", "SolNode89", "SolNode63", "SolNode85", "SolNode15", "SolNode79"];
 | 
					            const completedMissions = ["SolNode27", "SolNode89", "SolNode63", "SolNode85", "SolNode15", "SolNode79"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -473,7 +473,9 @@ export const addItem = async (
 | 
				
			|||||||
        return addCustomization(inventory, typeName);
 | 
					        return addCustomization(inventory, typeName);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (typeName in ExportUpgrades || typeName in ExportArcanes) {
 | 
					    if (typeName in ExportUpgrades || typeName in ExportArcanes) {
 | 
				
			||||||
        if (targetFingerprint) {
 | 
					        if (targetFingerprint && typeName.startsWith("/Lotus/Upgrades/Mods/Randomized/Raw")) {
 | 
				
			||||||
 | 
					            logger.debug(`ignoring fingerprint for raw riven mod`);
 | 
				
			||||||
 | 
					        } else if (targetFingerprint) {
 | 
				
			||||||
            if (quantity != 1) {
 | 
					            if (quantity != 1) {
 | 
				
			||||||
                logger.warn(`adding 1 of ${typeName} ${targetFingerprint} even tho quantity ${quantity} was requested`);
 | 
					                logger.warn(`adding 1 of ${typeName} ${targetFingerprint} even tho quantity ${quantity} was requested`);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -1569,6 +1571,7 @@ const addCrewShip = async (
 | 
				
			|||||||
            const questChanges = await completeQuest(
 | 
					            const questChanges = await completeQuest(
 | 
				
			||||||
                inventory,
 | 
					                inventory,
 | 
				
			||||||
                "/Lotus/Types/Keys/RailJackBuildQuest/RailjackBuildQuestKeyChain",
 | 
					                "/Lotus/Types/Keys/RailJackBuildQuest/RailjackBuildQuestKeyChain",
 | 
				
			||||||
 | 
					                undefined,
 | 
				
			||||||
                false
 | 
					                false
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            if (questChanges) {
 | 
					            if (questChanges) {
 | 
				
			||||||
 | 
				
			|||||||
@ -40,6 +40,8 @@ import {
 | 
				
			|||||||
} from "warframe-public-export-plus";
 | 
					} from "warframe-public-export-plus";
 | 
				
			||||||
import type { IMessage } from "../models/inboxModel.ts";
 | 
					import type { IMessage } from "../models/inboxModel.ts";
 | 
				
			||||||
import { logger } from "../utils/logger.ts";
 | 
					import { logger } from "../utils/logger.ts";
 | 
				
			||||||
 | 
					import { version_compare } from "../helpers/inventoryHelpers.ts";
 | 
				
			||||||
 | 
					import vorsPrizePreU40Rewards from "../../static/fixed_responses/vorsPrizePreU40Rewards.json" with { type: "json" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type WeaponTypeInternal =
 | 
					export type WeaponTypeInternal =
 | 
				
			||||||
    | "LongGuns"
 | 
					    | "LongGuns"
 | 
				
			||||||
@ -227,12 +229,13 @@ export const getKeyChainItems = ({ KeyChain, ChainStage }: IKeyChainRequest): st
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getLevelKeyRewards = (
 | 
					export const getLevelKeyRewards = (
 | 
				
			||||||
    levelKey: string
 | 
					    levelKey: string,
 | 
				
			||||||
 | 
					    buildLabel: string | undefined
 | 
				
			||||||
): { levelKeyRewards?: IMissionReward; levelKeyRewards2?: TReward[] } => {
 | 
					): { levelKeyRewards?: IMissionReward; levelKeyRewards2?: TReward[] } => {
 | 
				
			||||||
    const key = ExportKeys[levelKey] as IKey | undefined;
 | 
					    const key = ExportKeys[levelKey] as IKey | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const levelKeyRewards = key?.missionReward;
 | 
					    const levelKeyRewards = key?.missionReward;
 | 
				
			||||||
    const levelKeyRewards2 = key?.rewards;
 | 
					    let levelKeyRewards2 = key?.rewards;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!levelKeyRewards && !levelKeyRewards2) {
 | 
					    if (!levelKeyRewards && !levelKeyRewards2) {
 | 
				
			||||||
        logger.warn(
 | 
					        logger.warn(
 | 
				
			||||||
@ -240,6 +243,12 @@ export const getLevelKeyRewards = (
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (buildLabel && version_compare(buildLabel, "2025.10.14.16.10") < 0) {
 | 
				
			||||||
 | 
					        if (levelKey in vorsPrizePreU40Rewards) {
 | 
				
			||||||
 | 
					            levelKeyRewards2 = vorsPrizePreU40Rewards[levelKey as keyof typeof vorsPrizePreU40Rewards] as TReward[];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
        levelKeyRewards,
 | 
					        levelKeyRewards,
 | 
				
			||||||
        levelKeyRewards2
 | 
					        levelKeyRewards2
 | 
				
			||||||
 | 
				
			|||||||
@ -41,8 +41,8 @@ export const createAccount = async (accountData: IDatabaseAccountRequiredFields)
 | 
				
			|||||||
        await account.save();
 | 
					        await account.save();
 | 
				
			||||||
        const loadoutId = await createLoadout(account._id);
 | 
					        const loadoutId = await createLoadout(account._id);
 | 
				
			||||||
        const shipId = await createShip(account._id);
 | 
					        const shipId = await createShip(account._id);
 | 
				
			||||||
        await createInventory(account._id, { loadOutPresetId: loadoutId, ship: shipId });
 | 
					 | 
				
			||||||
        await createPersonalRooms(account._id, shipId);
 | 
					        await createPersonalRooms(account._id, shipId);
 | 
				
			||||||
 | 
					        await createInventory(account._id, { loadOutPresetId: loadoutId, ship: shipId });
 | 
				
			||||||
        await createStats(account._id.toString());
 | 
					        await createStats(account._id.toString());
 | 
				
			||||||
        return account.toJSON();
 | 
					        return account.toJSON();
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
@ -64,17 +64,6 @@ export const createPersonalRooms = async (accountId: Types.ObjectId, shipId: Typ
 | 
				
			|||||||
        personalRoomsOwnerId: accountId,
 | 
					        personalRoomsOwnerId: accountId,
 | 
				
			||||||
        activeShipId: shipId
 | 
					        activeShipId: shipId
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    if (config.skipTutorial) {
 | 
					 | 
				
			||||||
        // unlocked during Vor's Prize and The Teacher quests
 | 
					 | 
				
			||||||
        const defaultFeatures = [
 | 
					 | 
				
			||||||
            "/Lotus/Types/Items/ShipFeatureItems/MercuryNavigationFeatureItem",
 | 
					 | 
				
			||||||
            "/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem",
 | 
					 | 
				
			||||||
            "/Lotus/Types/Items/ShipFeatureItems/SocialMenuFeatureItem",
 | 
					 | 
				
			||||||
            "/Lotus/Types/Items/ShipFeatureItems/FoundryFeatureItem",
 | 
					 | 
				
			||||||
            "/Lotus/Types/Items/ShipFeatureItems/ModsFeatureItem"
 | 
					 | 
				
			||||||
        ];
 | 
					 | 
				
			||||||
        personalRooms.Ship.Features.push(...defaultFeatures);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    await personalRooms.save();
 | 
					    await personalRooms.save();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1133,7 +1133,8 @@ export const addMissionRewards = async (
 | 
				
			|||||||
        VoidTearParticipantsCurrWave: voidTearWave,
 | 
					        VoidTearParticipantsCurrWave: voidTearWave,
 | 
				
			||||||
        StrippedItems: strippedItems,
 | 
					        StrippedItems: strippedItems,
 | 
				
			||||||
        AffiliationChanges: AffiliationMods,
 | 
					        AffiliationChanges: AffiliationMods,
 | 
				
			||||||
        InvasionProgress: invasionProgress
 | 
					        InvasionProgress: invasionProgress,
 | 
				
			||||||
 | 
					        EndOfMatchUpload: endOfMatchUpload
 | 
				
			||||||
    }: IMissionInventoryUpdateRequest,
 | 
					    }: IMissionInventoryUpdateRequest,
 | 
				
			||||||
    firstCompletion: boolean
 | 
					    firstCompletion: boolean
 | 
				
			||||||
): Promise<AddMissionRewardsReturnType> => {
 | 
					): Promise<AddMissionRewardsReturnType> => {
 | 
				
			||||||
@ -1218,7 +1219,7 @@ export const addMissionRewards = async (
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (levelKeyName) {
 | 
					    if (levelKeyName) {
 | 
				
			||||||
        const fixedLevelRewards = getLevelKeyRewards(levelKeyName);
 | 
					        const fixedLevelRewards = getLevelKeyRewards(levelKeyName, account.BuildLabel);
 | 
				
			||||||
        //logger.debug(`fixedLevelRewards ${fixedLevelRewards}`);
 | 
					        //logger.debug(`fixedLevelRewards ${fixedLevelRewards}`);
 | 
				
			||||||
        if (fixedLevelRewards.levelKeyRewards) {
 | 
					        if (fixedLevelRewards.levelKeyRewards) {
 | 
				
			||||||
            missionCompletionCredits += addFixedLevelRewards(
 | 
					            missionCompletionCredits += addFixedLevelRewards(
 | 
				
			||||||
@ -1392,73 +1393,77 @@ export const addMissionRewards = async (
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (strippedItems) {
 | 
					    if (strippedItems) {
 | 
				
			||||||
        for (const si of strippedItems) {
 | 
					        if (endOfMatchUpload) {
 | 
				
			||||||
            if (si.DropTable in droptableAliases) {
 | 
					            for (const si of strippedItems) {
 | 
				
			||||||
                logger.debug(`rewriting ${si.DropTable} to ${droptableAliases[si.DropTable]}`);
 | 
					                if (si.DropTable in droptableAliases) {
 | 
				
			||||||
                si.DropTable = droptableAliases[si.DropTable];
 | 
					                    logger.debug(`rewriting ${si.DropTable} to ${droptableAliases[si.DropTable]}`);
 | 
				
			||||||
            }
 | 
					                    si.DropTable = droptableAliases[si.DropTable];
 | 
				
			||||||
            const droptables = ExportEnemies.droptables[si.DropTable] ?? [];
 | 
					 | 
				
			||||||
            if (si.DROP_MOD) {
 | 
					 | 
				
			||||||
                const modDroptable = droptables.find(x => x.type == "mod");
 | 
					 | 
				
			||||||
                if (modDroptable) {
 | 
					 | 
				
			||||||
                    for (let i = 0; i != si.DROP_MOD.length; ++i) {
 | 
					 | 
				
			||||||
                        const reward = getRandomReward(modDroptable.items)!;
 | 
					 | 
				
			||||||
                        logger.debug(`stripped droptable (mods pool) rolled`, reward);
 | 
					 | 
				
			||||||
                        await addItem(inventory, reward.type);
 | 
					 | 
				
			||||||
                        MissionRewards.push({
 | 
					 | 
				
			||||||
                            StoreItem: toStoreItem(reward.type),
 | 
					 | 
				
			||||||
                            ItemCount: 1,
 | 
					 | 
				
			||||||
                            FromEnemyCache: true // to show "identified"
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    logger.error(`unknown droptable ${si.DropTable} for DROP_MOD`);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					                const droptables = ExportEnemies.droptables[si.DropTable] ?? [];
 | 
				
			||||||
            if (si.DROP_BLUEPRINT) {
 | 
					                if (si.DROP_MOD) {
 | 
				
			||||||
                const blueprintDroptable = droptables.find(x => x.type == "blueprint");
 | 
					                    const modDroptable = droptables.find(x => x.type == "mod");
 | 
				
			||||||
                if (blueprintDroptable) {
 | 
					                    if (modDroptable) {
 | 
				
			||||||
                    for (let i = 0; i != si.DROP_BLUEPRINT.length; ++i) {
 | 
					                        for (let i = 0; i != si.DROP_MOD.length; ++i) {
 | 
				
			||||||
                        const reward = getRandomReward(blueprintDroptable.items)!;
 | 
					                            const reward = getRandomReward(modDroptable.items)!;
 | 
				
			||||||
                        logger.debug(`stripped droptable (blueprints pool) rolled`, reward);
 | 
					                            logger.debug(`stripped droptable (mods pool) rolled`, reward);
 | 
				
			||||||
                        await addItem(inventory, reward.type);
 | 
					                            await addItem(inventory, reward.type);
 | 
				
			||||||
                        MissionRewards.push({
 | 
					 | 
				
			||||||
                            StoreItem: toStoreItem(reward.type),
 | 
					 | 
				
			||||||
                            ItemCount: 1,
 | 
					 | 
				
			||||||
                            FromEnemyCache: true // to show "identified"
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    logger.error(`unknown droptable ${si.DropTable} for DROP_BLUEPRINT`);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            // e.g. H-09 Apex Turret Sumdali
 | 
					 | 
				
			||||||
            if (si.DROP_MISC_ITEM) {
 | 
					 | 
				
			||||||
                const resourceDroptable = droptables.find(x => x.type == "resource");
 | 
					 | 
				
			||||||
                if (resourceDroptable) {
 | 
					 | 
				
			||||||
                    for (let i = 0; i != si.DROP_MISC_ITEM.length; ++i) {
 | 
					 | 
				
			||||||
                        const reward = getRandomReward(resourceDroptable.items)!;
 | 
					 | 
				
			||||||
                        logger.debug(`stripped droptable (resources pool) rolled`, reward);
 | 
					 | 
				
			||||||
                        if (Object.keys(await addItem(inventory, reward.type)).length == 0) {
 | 
					 | 
				
			||||||
                            logger.debug(`item already owned, skipping`);
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            MissionRewards.push({
 | 
					                            MissionRewards.push({
 | 
				
			||||||
                                StoreItem: toStoreItem(reward.type),
 | 
					                                StoreItem: toStoreItem(reward.type),
 | 
				
			||||||
                                ItemCount: 1,
 | 
					                                ItemCount: 1,
 | 
				
			||||||
                                FromEnemyCache: true // to show "identified"
 | 
					                                FromEnemyCache: true // to show "identified"
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        logger.error(`unknown droptable ${si.DropTable} for DROP_MOD`);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                } else {
 | 
					                }
 | 
				
			||||||
                    logger.error(`unknown droptable ${si.DropTable} for DROP_MISC_ITEM`);
 | 
					                if (si.DROP_BLUEPRINT) {
 | 
				
			||||||
 | 
					                    const blueprintDroptable = droptables.find(x => x.type == "blueprint");
 | 
				
			||||||
 | 
					                    if (blueprintDroptable) {
 | 
				
			||||||
 | 
					                        for (let i = 0; i != si.DROP_BLUEPRINT.length; ++i) {
 | 
				
			||||||
 | 
					                            const reward = getRandomReward(blueprintDroptable.items)!;
 | 
				
			||||||
 | 
					                            logger.debug(`stripped droptable (blueprints pool) rolled`, reward);
 | 
				
			||||||
 | 
					                            await addItem(inventory, reward.type);
 | 
				
			||||||
 | 
					                            MissionRewards.push({
 | 
				
			||||||
 | 
					                                StoreItem: toStoreItem(reward.type),
 | 
				
			||||||
 | 
					                                ItemCount: 1,
 | 
				
			||||||
 | 
					                                FromEnemyCache: true // to show "identified"
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        logger.error(`unknown droptable ${si.DropTable} for DROP_BLUEPRINT`);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // e.g. H-09 Apex Turret Sumdali
 | 
				
			||||||
 | 
					                if (si.DROP_MISC_ITEM) {
 | 
				
			||||||
 | 
					                    const resourceDroptable = droptables.find(x => x.type == "resource");
 | 
				
			||||||
 | 
					                    if (resourceDroptable) {
 | 
				
			||||||
 | 
					                        for (let i = 0; i != si.DROP_MISC_ITEM.length; ++i) {
 | 
				
			||||||
 | 
					                            const reward = getRandomReward(resourceDroptable.items)!;
 | 
				
			||||||
 | 
					                            logger.debug(`stripped droptable (resources pool) rolled`, reward);
 | 
				
			||||||
 | 
					                            if (Object.keys(await addItem(inventory, reward.type)).length == 0) {
 | 
				
			||||||
 | 
					                                logger.debug(`item already owned, skipping`);
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                MissionRewards.push({
 | 
				
			||||||
 | 
					                                    StoreItem: toStoreItem(reward.type),
 | 
				
			||||||
 | 
					                                    ItemCount: 1,
 | 
				
			||||||
 | 
					                                    FromEnemyCache: true // to show "identified"
 | 
				
			||||||
 | 
					                                });
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        logger.error(`unknown droptable ${si.DropTable} for DROP_MISC_ITEM`);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (si.DropTable == "/Lotus/Types/DropTables/ContainerDropTables/VoidVaultMissionRewardsDropTable") {
 | 
				
			||||||
 | 
					                    // Consume netracells search pulse; only when the container reward was picked up. Discussed in https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2673
 | 
				
			||||||
 | 
					                    updateEntratiVault(inventory);
 | 
				
			||||||
 | 
					                    inventory.EntratiVaultCountLastPeriod! += 1;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
            if (si.DropTable == "/Lotus/Types/DropTables/ContainerDropTables/VoidVaultMissionRewardsDropTable") {
 | 
					            logger.debug(`ignoring StrippedItems in intermediate inventory update, deferring until extraction`);
 | 
				
			||||||
                // Consume netracells search pulse; only when the container reward was picked up. Discussed in https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2673
 | 
					 | 
				
			||||||
                updateEntratiVault(inventory);
 | 
					 | 
				
			||||||
                inventory.EntratiVaultCountLastPeriod! += 1;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,16 +2,25 @@ import type { IKeyChainRequest } from "../types/requestTypes.ts";
 | 
				
			|||||||
import { isEmptyObject } from "../helpers/general.ts";
 | 
					import { isEmptyObject } from "../helpers/general.ts";
 | 
				
			||||||
import type { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel.ts";
 | 
					import type { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel.ts";
 | 
				
			||||||
import { createMessage } from "./inboxService.ts";
 | 
					import { createMessage } from "./inboxService.ts";
 | 
				
			||||||
import { addItem, addItems, addKeyChainItems, setupKahlSyndicate } from "./inventoryService.ts";
 | 
					import {
 | 
				
			||||||
 | 
					    addEquipment,
 | 
				
			||||||
 | 
					    addItem,
 | 
				
			||||||
 | 
					    addItems,
 | 
				
			||||||
 | 
					    addKeyChainItems,
 | 
				
			||||||
 | 
					    addPowerSuit,
 | 
				
			||||||
 | 
					    setupKahlSyndicate
 | 
				
			||||||
 | 
					} from "./inventoryService.ts";
 | 
				
			||||||
import { fromStoreItem, getKeyChainMessage, getLevelKeyRewards } from "./itemDataService.ts";
 | 
					import { fromStoreItem, getKeyChainMessage, getLevelKeyRewards } from "./itemDataService.ts";
 | 
				
			||||||
import type { IQuestKeyClient, IQuestKeyDatabase, IQuestStage } from "../types/inventoryTypes/inventoryTypes.ts";
 | 
					import type { IQuestKeyClient, IQuestKeyDatabase, IQuestStage } from "../types/inventoryTypes/inventoryTypes.ts";
 | 
				
			||||||
import { logger } from "../utils/logger.ts";
 | 
					import { logger } from "../utils/logger.ts";
 | 
				
			||||||
import { ExportKeys } from "warframe-public-export-plus";
 | 
					import { ExportKeys, ExportRecipes } from "warframe-public-export-plus";
 | 
				
			||||||
import { addFixedLevelRewards } from "./missionInventoryUpdateService.ts";
 | 
					import { addFixedLevelRewards } from "./missionInventoryUpdateService.ts";
 | 
				
			||||||
import type { IInventoryChanges } from "../types/purchaseTypes.ts";
 | 
					import type { IInventoryChanges } from "../types/purchaseTypes.ts";
 | 
				
			||||||
import questCompletionItems from "../../static/fixed_responses/questCompletionRewards.json" with { type: "json" };
 | 
					import questCompletionItems from "../../static/fixed_responses/questCompletionRewards.json" with { type: "json" };
 | 
				
			||||||
import type { ITypeCount } from "../types/commonTypes.ts";
 | 
					import type { ITypeCount } from "../types/commonTypes.ts";
 | 
				
			||||||
import { addString } from "../helpers/stringHelpers.ts";
 | 
					import { addString } from "../helpers/stringHelpers.ts";
 | 
				
			||||||
 | 
					import { unlockShipFeature } from "./personalRoomsService.ts";
 | 
				
			||||||
 | 
					import { EquipmentFeatures } from "../types/equipmentTypes.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IUpdateQuestRequest {
 | 
					export interface IUpdateQuestRequest {
 | 
				
			||||||
    QuestKeys: IQuestKeyClient[];
 | 
					    QuestKeys: IQuestKeyClient[];
 | 
				
			||||||
@ -104,14 +113,10 @@ export const resetQuestKeyToStage = (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    quest.Progress ??= [];
 | 
					    quest.Progress ??= [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quest.Progress.splice(1 + ChainStage); // remove stages past the target
 | 
					    const run = quest.Progress[0]?.c ?? 0;
 | 
				
			||||||
 | 
					    if (run >= 0) {
 | 
				
			||||||
    const questStage = quest.Progress[ChainStage];
 | 
					        for (let i = ChainStage; i < quest.Progress.length; ++i) {
 | 
				
			||||||
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
					            quest.Progress[i].c = run - 1;
 | 
				
			||||||
    if (questStage) {
 | 
					 | 
				
			||||||
        const run = quest.Progress[0].c;
 | 
					 | 
				
			||||||
        if (run >= 0) {
 | 
					 | 
				
			||||||
            questStage.c = run - 1;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -144,6 +149,7 @@ export const addQuestKey = (
 | 
				
			|||||||
export const completeQuest = async (
 | 
					export const completeQuest = async (
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
    questKey: string,
 | 
					    questKey: string,
 | 
				
			||||||
 | 
					    buildLabel: string | undefined,
 | 
				
			||||||
    sendMessages: boolean = false
 | 
					    sendMessages: boolean = false
 | 
				
			||||||
): Promise<void | IQuestKeyClient> => {
 | 
					): Promise<void | IQuestKeyClient> => {
 | 
				
			||||||
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
					    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
				
			||||||
@ -192,7 +198,8 @@ export const completeQuest = async (
 | 
				
			|||||||
        if (stage.c <= run) {
 | 
					        if (stage.c <= run) {
 | 
				
			||||||
            stage.c = run;
 | 
					            stage.c = run;
 | 
				
			||||||
            await giveKeyChainStageTriggered(inventory, { KeyChain: questKey, ChainStage: i }, sendMessages);
 | 
					            await giveKeyChainStageTriggered(inventory, { KeyChain: questKey, ChainStage: i }, sendMessages);
 | 
				
			||||||
            await giveKeyChainMissionReward(inventory, { KeyChain: questKey, ChainStage: i });
 | 
					            await giveKeyChainMissionReward(inventory, { KeyChain: questKey, ChainStage: i }, buildLabel);
 | 
				
			||||||
 | 
					            await installShipFeatures(inventory, { KeyChain: questKey, ChainStage: i }, buildLabel);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -329,6 +336,9 @@ const handleQuestCompletion = async (
 | 
				
			|||||||
    logger.debug(`quest completion items`, questCompletionItems);
 | 
					    logger.debug(`quest completion items`, questCompletionItems);
 | 
				
			||||||
    if (questCompletionItems) {
 | 
					    if (questCompletionItems) {
 | 
				
			||||||
        await addItems(inventory, questCompletionItems, inventoryChanges);
 | 
					        await addItems(inventory, questCompletionItems, inventoryChanges);
 | 
				
			||||||
 | 
					        for (const item of questCompletionItems) {
 | 
				
			||||||
 | 
					            await removeRequiredItems(inventory, item.ItemType);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -378,7 +388,12 @@ export const giveKeyChainMessage = async (
 | 
				
			|||||||
        await createMessage(inventory.accountOwnerId, [keyChainMessage]);
 | 
					        await createMessage(inventory.accountOwnerId, [keyChainMessage]);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        if (keyChainMessage.countedAtt?.length) await addItems(inventory, keyChainMessage.countedAtt);
 | 
					        if (keyChainMessage.countedAtt?.length) await addItems(inventory, keyChainMessage.countedAtt);
 | 
				
			||||||
        if (keyChainMessage.att?.length) await addItems(inventory, keyChainMessage.att);
 | 
					        if (keyChainMessage.att?.length) {
 | 
				
			||||||
 | 
					            await addItems(inventory, keyChainMessage.att);
 | 
				
			||||||
 | 
					            for (const reward of keyChainMessage.att) {
 | 
				
			||||||
 | 
					                await removeRequiredItems(inventory, reward);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    updateQuestStage(inventory, keyChainInfo, { m: true });
 | 
					    updateQuestStage(inventory, keyChainInfo, { m: true });
 | 
				
			||||||
@ -386,7 +401,8 @@ export const giveKeyChainMessage = async (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const giveKeyChainMissionReward = async (
 | 
					export const giveKeyChainMissionReward = async (
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
    keyChainInfo: IKeyChainRequest
 | 
					    keyChainInfo: IKeyChainRequest,
 | 
				
			||||||
 | 
					    buildLabel: string | undefined
 | 
				
			||||||
): Promise<void> => {
 | 
					): Promise<void> => {
 | 
				
			||||||
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
					    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
				
			||||||
    const chainStages = ExportKeys[keyChainInfo.KeyChain]?.chainStages;
 | 
					    const chainStages = ExportKeys[keyChainInfo.KeyChain]?.chainStages;
 | 
				
			||||||
@ -395,7 +411,7 @@ export const giveKeyChainMissionReward = async (
 | 
				
			|||||||
        const missionName = chainStages[keyChainInfo.ChainStage].key;
 | 
					        const missionName = chainStages[keyChainInfo.ChainStage].key;
 | 
				
			||||||
        const questKey = inventory.QuestKeys.find(q => q.ItemType === keyChainInfo.KeyChain);
 | 
					        const questKey = inventory.QuestKeys.find(q => q.ItemType === keyChainInfo.KeyChain);
 | 
				
			||||||
        if (missionName && questKey) {
 | 
					        if (missionName && questKey) {
 | 
				
			||||||
            const fixedLevelRewards = getLevelKeyRewards(missionName);
 | 
					            const fixedLevelRewards = getLevelKeyRewards(missionName, buildLabel);
 | 
				
			||||||
            const run = questKey.Progress?.[0]?.c ?? 0;
 | 
					            const run = questKey.Progress?.[0]?.c ?? 0;
 | 
				
			||||||
            if (fixedLevelRewards.levelKeyRewards) {
 | 
					            if (fixedLevelRewards.levelKeyRewards) {
 | 
				
			||||||
                const missionRewards: { StoreItem: string; ItemCount: number }[] = [];
 | 
					                const missionRewards: { StoreItem: string; ItemCount: number }[] = [];
 | 
				
			||||||
@ -403,6 +419,7 @@ export const giveKeyChainMissionReward = async (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                for (const reward of missionRewards) {
 | 
					                for (const reward of missionRewards) {
 | 
				
			||||||
                    await addItem(inventory, fromStoreItem(reward.StoreItem), reward.ItemCount);
 | 
					                    await addItem(inventory, fromStoreItem(reward.StoreItem), reward.ItemCount);
 | 
				
			||||||
 | 
					                    await removeRequiredItems(inventory, fromStoreItem(reward.StoreItem));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                updateQuestStage(inventory, keyChainInfo, { c: run });
 | 
					                updateQuestStage(inventory, keyChainInfo, { c: run });
 | 
				
			||||||
@ -416,6 +433,7 @@ export const giveKeyChainMissionReward = async (
 | 
				
			|||||||
                        await addItem(inventory, fromStoreItem(reward.itemType), reward.amount);
 | 
					                        await addItem(inventory, fromStoreItem(reward.itemType), reward.amount);
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        await addItem(inventory, fromStoreItem(reward.itemType));
 | 
					                        await addItem(inventory, fromStoreItem(reward.itemType));
 | 
				
			||||||
 | 
					                        await removeRequiredItems(inventory, fromStoreItem(reward.itemType));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -444,3 +462,338 @@ export const giveKeyChainStageTriggered = async (
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const installShipFeatures = async (
 | 
				
			||||||
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
 | 
					    keyChainInfo: IKeyChainRequest,
 | 
				
			||||||
 | 
					    buildLabel: string | undefined
 | 
				
			||||||
 | 
					): Promise<void> => {
 | 
				
			||||||
 | 
					    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
				
			||||||
 | 
					    const chainStages = ExportKeys[keyChainInfo.KeyChain]?.chainStages;
 | 
				
			||||||
 | 
					    const questKey = inventory.QuestKeys.find(qk => qk.ItemType === keyChainInfo.KeyChain);
 | 
				
			||||||
 | 
					    if (chainStages && questKey) {
 | 
				
			||||||
 | 
					        if (keyChainInfo.ChainStage - 1 >= 0) {
 | 
				
			||||||
 | 
					            const prevStage = chainStages[keyChainInfo.ChainStage - 1];
 | 
				
			||||||
 | 
					            for (const item of prevStage.itemsToGiveWhenTriggered) {
 | 
				
			||||||
 | 
					                if (item.startsWith("/Lotus/StoreItems/Types/Items/ShipFeatureItems/")) {
 | 
				
			||||||
 | 
					                    logger.debug(`installing ship feature ${fromStoreItem(item)}`);
 | 
				
			||||||
 | 
					                    await unlockShipFeature(inventory, fromStoreItem(item));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (prevStage.key) {
 | 
				
			||||||
 | 
					                const fixedLevelRewards = getLevelKeyRewards(prevStage.key, buildLabel);
 | 
				
			||||||
 | 
					                if (fixedLevelRewards.levelKeyRewards?.items) {
 | 
				
			||||||
 | 
					                    for (const item of fixedLevelRewards.levelKeyRewards.items) {
 | 
				
			||||||
 | 
					                        if (item.startsWith("/Lotus/StoreItems/Types/Items/ShipFeatureItems/")) {
 | 
				
			||||||
 | 
					                            logger.debug(`installing ship feature ${fromStoreItem(item)}`);
 | 
				
			||||||
 | 
					                            await unlockShipFeature(inventory, fromStoreItem(item));
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (fixedLevelRewards.levelKeyRewards2) {
 | 
				
			||||||
 | 
					                    for (const item of fixedLevelRewards.levelKeyRewards2) {
 | 
				
			||||||
 | 
					                        if (
 | 
				
			||||||
 | 
					                            item.rewardType == "RT_STORE_ITEM" &&
 | 
				
			||||||
 | 
					                            item.itemType.startsWith("/Lotus/StoreItems/Types/Items/ShipFeatureItems/")
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
 | 
					                            logger.debug(`installing ship feature ${fromStoreItem(item.itemType)}`);
 | 
				
			||||||
 | 
					                            await unlockShipFeature(inventory, fromStoreItem(item.itemType));
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const removeRequiredItems = async (inventory: TInventoryDatabaseDocument, typeName: string): Promise<void> => {
 | 
				
			||||||
 | 
					    switch (typeName) {
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Recipes/WarframeRecipes/MagicianHelmetBlueprint": {
 | 
				
			||||||
 | 
					            const recipeItem = inventory.Recipes.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Keys/LimboQuest/LimboHelmetKeyBlueprint"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (recipeItem && recipeItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                const recipe = ExportRecipes[recipeItem.ItemType];
 | 
				
			||||||
 | 
					                if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
 | 
				
			||||||
 | 
					                    await addItem(inventory, recipe.resultType);
 | 
				
			||||||
 | 
					                    if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Recipes/WarframeRecipes/MagicianSystemsBlueprint": {
 | 
				
			||||||
 | 
					            const recipeItem = inventory.Recipes.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Keys/LimboQuest/LimboSystemsKeyBlueprint"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (recipeItem && recipeItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                const recipe = ExportRecipes[recipeItem.ItemType];
 | 
				
			||||||
 | 
					                if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
 | 
				
			||||||
 | 
					                    await addItem(inventory, recipe.resultType);
 | 
				
			||||||
 | 
					                    if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Recipes/WarframeRecipes/MagicianChassisBlueprint": {
 | 
				
			||||||
 | 
					            const recipeItem = inventory.Recipes.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Keys/LimboQuest/LimboChassisKeyBlueprint"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (recipeItem && recipeItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                const recipe = ExportRecipes[recipeItem.ItemType];
 | 
				
			||||||
 | 
					                if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
 | 
				
			||||||
 | 
					                    await addItem(inventory, recipe.resultType);
 | 
				
			||||||
 | 
					                    if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Recipes/WarframeRecipes/BrawlerBlueprint": {
 | 
				
			||||||
 | 
					            const recipeItem = inventory.Recipes.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Recipes/Components/InfestedIrradiatedBaitBallBlueprint"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (recipeItem && recipeItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                const recipe = ExportRecipes[recipeItem.ItemType];
 | 
				
			||||||
 | 
					                if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
 | 
				
			||||||
 | 
					                    await addItem(inventory, recipe.resultType);
 | 
				
			||||||
 | 
					                    await addItem(inventory, recipe.resultType, -1, false, undefined, undefined, true);
 | 
				
			||||||
 | 
					                    if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness": {
 | 
				
			||||||
 | 
					            const recipeItem = inventory.Recipes.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Recipes/ArchwingRecipes/StandardArchwing/StandardArchwingBlueprint"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (recipeItem && recipeItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                const recipe = ExportRecipes[recipeItem.ItemType];
 | 
				
			||||||
 | 
					                if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
 | 
				
			||||||
 | 
					                    await addItem(inventory, recipe.resultType);
 | 
				
			||||||
 | 
					                    if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, recipeItem.ItemCount * -1);
 | 
				
			||||||
 | 
					                    await addItems(inventory, [
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            ItemType:
 | 
				
			||||||
 | 
					                                "/Lotus/Types/Recipes/ArchwingRecipes/StandardArchwing/StandardArchwingWingsBlueprint",
 | 
				
			||||||
 | 
					                            ItemCount: -1
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            ItemType:
 | 
				
			||||||
 | 
					                                "/Lotus/Types/Recipes/ArchwingRecipes/StandardArchwing/StandardArchwingChassisBlueprint",
 | 
				
			||||||
 | 
					                            ItemCount: -1
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            ItemType:
 | 
				
			||||||
 | 
					                                "/Lotus/Types/Recipes/ArchwingRecipes/StandardArchwing/StandardArchwingSystemsBlueprint",
 | 
				
			||||||
 | 
					                            ItemCount: -1
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    ]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Keys/ModQuest/ModQuestKeyChain": {
 | 
				
			||||||
 | 
					            const recipeItem = inventory.Recipes.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Recipes/Components/VorBoltRemoverBlueprint"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (recipeItem && recipeItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                const recipe = ExportRecipes[recipeItem.ItemType];
 | 
				
			||||||
 | 
					                if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
 | 
				
			||||||
 | 
					                    await addItem(inventory, recipe.resultType);
 | 
				
			||||||
 | 
					                    if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Recipes/WarframeRecipes/ChromaBlueprint": {
 | 
				
			||||||
 | 
					            await addItems(inventory, [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ItemType: "/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconABlueprint",
 | 
				
			||||||
 | 
					                    ItemCount: -1
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ItemType: "/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconBBlueprint",
 | 
				
			||||||
 | 
					                    ItemCount: -1
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ItemType: "/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconCBlueprint",
 | 
				
			||||||
 | 
					                    ItemCount: -1
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Recipes/WarframeRecipes/OctaviaBlueprint": {
 | 
				
			||||||
 | 
					            const recipeItem = inventory.Recipes.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Keys/BardQuest/BardQuestSequencerBlueprint"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (recipeItem && recipeItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                const recipe = ExportRecipes[recipeItem.ItemType];
 | 
				
			||||||
 | 
					                if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
 | 
				
			||||||
 | 
					                    await addItem(inventory, recipe.resultType);
 | 
				
			||||||
 | 
					                    if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
 | 
				
			||||||
 | 
					                    await addItems(inventory, [
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            ItemType: "/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartA",
 | 
				
			||||||
 | 
					                            ItemCount: -1
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            ItemType: "/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartB",
 | 
				
			||||||
 | 
					                            ItemCount: -1
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            ItemType: "/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartC",
 | 
				
			||||||
 | 
					                            ItemCount: -1
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    ]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Game/CrewShip/Ships/RailJack": {
 | 
				
			||||||
 | 
					            const recipeItem = inventory.Recipes.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Recipes/Railjack/RailjackCephalonBlueprint"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (recipeItem && recipeItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                const recipe = ExportRecipes[recipeItem.ItemType];
 | 
				
			||||||
 | 
					                if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
 | 
				
			||||||
 | 
					                    await addItem(inventory, recipe.resultType);
 | 
				
			||||||
 | 
					                    if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, recipeItem.ItemCount * -1);
 | 
				
			||||||
 | 
					                    await unlockShipFeature(inventory, recipe.resultType);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Items/ShipDecos/MummyQuestVessel": {
 | 
				
			||||||
 | 
					            const gearItem = inventory.Consumables.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Keys/MummyQuest/MummyArtifact01GearItem"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (gearItem && gearItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                await addItem(inventory, gearItem.ItemType, gearItem.ItemCount * -1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case "/Lotus/Types/Recipes/WarframeRecipes/ConcreteFrameBlueprint": {
 | 
				
			||||||
 | 
					            const recipeItem = inventory.Recipes.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Gameplay/EntratiLab/Quest/GargoyleRecipeItem"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (recipeItem && recipeItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                const recipe = ExportRecipes[recipeItem.ItemType];
 | 
				
			||||||
 | 
					                if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
 | 
				
			||||||
 | 
					                    await addItem(inventory, recipe.resultType);
 | 
				
			||||||
 | 
					                    if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, recipeItem.ItemCount * -1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case "/Lotus/Upgrades/Skins/Umbra/UmbraAltHelmet": {
 | 
				
			||||||
 | 
					            const recipeItem = inventory.Recipes.find(
 | 
				
			||||||
 | 
					                i => i.ItemType == "/Lotus/Types/Recipes/WarframeRecipes/ExcaliburUmbraBlueprint"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (recipeItem && recipeItem.ItemCount > 0) {
 | 
				
			||||||
 | 
					                const recipe = ExportRecipes[recipeItem.ItemType];
 | 
				
			||||||
 | 
					                if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
 | 
				
			||||||
 | 
					                    const umbraModA = (
 | 
				
			||||||
 | 
					                        await addItem(
 | 
				
			||||||
 | 
					                            inventory,
 | 
				
			||||||
 | 
					                            "/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModA",
 | 
				
			||||||
 | 
					                            1,
 | 
				
			||||||
 | 
					                            false,
 | 
				
			||||||
 | 
					                            undefined,
 | 
				
			||||||
 | 
					                            `{"lvl":5}`
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    ).Upgrades![0];
 | 
				
			||||||
 | 
					                    const umbraModB = (
 | 
				
			||||||
 | 
					                        await addItem(
 | 
				
			||||||
 | 
					                            inventory,
 | 
				
			||||||
 | 
					                            "/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModB",
 | 
				
			||||||
 | 
					                            1,
 | 
				
			||||||
 | 
					                            false,
 | 
				
			||||||
 | 
					                            undefined,
 | 
				
			||||||
 | 
					                            `{"lvl":5}`
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    ).Upgrades![0];
 | 
				
			||||||
 | 
					                    const umbraModC = (
 | 
				
			||||||
 | 
					                        await addItem(
 | 
				
			||||||
 | 
					                            inventory,
 | 
				
			||||||
 | 
					                            "/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModC",
 | 
				
			||||||
 | 
					                            1,
 | 
				
			||||||
 | 
					                            false,
 | 
				
			||||||
 | 
					                            undefined,
 | 
				
			||||||
 | 
					                            `{"lvl":5}`
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    ).Upgrades![0];
 | 
				
			||||||
 | 
					                    const sacrificeModA = (
 | 
				
			||||||
 | 
					                        await addItem(
 | 
				
			||||||
 | 
					                            inventory,
 | 
				
			||||||
 | 
					                            "/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModA",
 | 
				
			||||||
 | 
					                            1,
 | 
				
			||||||
 | 
					                            false,
 | 
				
			||||||
 | 
					                            undefined,
 | 
				
			||||||
 | 
					                            `{"lvl":5}`
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    ).Upgrades![0];
 | 
				
			||||||
 | 
					                    const sacrificeModB = (
 | 
				
			||||||
 | 
					                        await addItem(
 | 
				
			||||||
 | 
					                            inventory,
 | 
				
			||||||
 | 
					                            "/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModB",
 | 
				
			||||||
 | 
					                            1,
 | 
				
			||||||
 | 
					                            false,
 | 
				
			||||||
 | 
					                            undefined,
 | 
				
			||||||
 | 
					                            `{"lvl":5}`
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    ).Upgrades![0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    await addPowerSuit(inventory, "/Lotus/Powersuits/Excalibur/ExcaliburUmbra", {
 | 
				
			||||||
 | 
					                        Configs: [
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                Upgrades: [
 | 
				
			||||||
 | 
					                                    "",
 | 
				
			||||||
 | 
					                                    "",
 | 
				
			||||||
 | 
					                                    "",
 | 
				
			||||||
 | 
					                                    "",
 | 
				
			||||||
 | 
					                                    "",
 | 
				
			||||||
 | 
					                                    umbraModA.ItemId.$oid,
 | 
				
			||||||
 | 
					                                    umbraModB.ItemId.$oid,
 | 
				
			||||||
 | 
					                                    umbraModC.ItemId.$oid
 | 
				
			||||||
 | 
					                                ]
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        ],
 | 
				
			||||||
 | 
					                        XP: 900_000,
 | 
				
			||||||
 | 
					                        Features: EquipmentFeatures.DOUBLE_CAPACITY
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    inventory.XPInfo.push({
 | 
				
			||||||
 | 
					                        ItemType: "/Lotus/Powersuits/Excalibur/ExcaliburUmbra",
 | 
				
			||||||
 | 
					                        XP: 900_000
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    addEquipment(inventory, "Melee", "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana", {
 | 
				
			||||||
 | 
					                        Configs: [
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                Upgrades: ["", "", "", "", "", "", sacrificeModA.ItemId.$oid, sacrificeModB.ItemId.$oid]
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        ],
 | 
				
			||||||
 | 
					                        XP: 450_000,
 | 
				
			||||||
 | 
					                        Features: EquipmentFeatures.DOUBLE_CAPACITY
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    inventory.XPInfo.push({
 | 
				
			||||||
 | 
					                        ItemType: "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
 | 
				
			||||||
 | 
					                        XP: 450_000
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, recipeItem.ItemCount * -1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -12,8 +12,8 @@ let httpServer: http.Server | undefined;
 | 
				
			|||||||
let httpsServer: https.Server | undefined;
 | 
					let httpsServer: https.Server | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const tlsOptions = {
 | 
					const tlsOptions = {
 | 
				
			||||||
    key: fs.readFileSync("static/certs/key.pem"),
 | 
					    key: fs.readFileSync("static/cert/key.pem"),
 | 
				
			||||||
    cert: fs.readFileSync("static/certs/cert.pem")
 | 
					    cert: fs.readFileSync("static/cert/cert.pem")
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const startWebServer = (): void => {
 | 
					export const startWebServer = (): void => {
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ import invasionNodes from "../../static/fixed_responses/worldState/invasionNodes
 | 
				
			|||||||
import invasionRewards from "../../static/fixed_responses/worldState/invasionRewards.json" with { type: "json" };
 | 
					import invasionRewards from "../../static/fixed_responses/worldState/invasionRewards.json" with { type: "json" };
 | 
				
			||||||
import pvpChallenges from "../../static/fixed_responses/worldState/pvpChallenges.json" with { type: "json" };
 | 
					import pvpChallenges from "../../static/fixed_responses/worldState/pvpChallenges.json" with { type: "json" };
 | 
				
			||||||
import { buildConfig } from "./buildConfigService.ts";
 | 
					import { buildConfig } from "./buildConfigService.ts";
 | 
				
			||||||
import { unixTimesInMs } from "../constants/timeConstants.ts";
 | 
					import { EPOCH, unixTimesInMs } from "../constants/timeConstants.ts";
 | 
				
			||||||
import { config } from "./configService.ts";
 | 
					import { config } from "./configService.ts";
 | 
				
			||||||
import { getRandomElement, getRandomInt, sequentiallyUniqueRandomElement, SRng } from "./rngService.ts";
 | 
					import { getRandomElement, getRandomInt, sequentiallyUniqueRandomElement, SRng } from "./rngService.ts";
 | 
				
			||||||
import type { IMissionReward, IRegion, TFaction } from "warframe-public-export-plus";
 | 
					import type { IMissionReward, IRegion, TFaction } from "warframe-public-export-plus";
 | 
				
			||||||
@ -41,6 +41,7 @@ import type {
 | 
				
			|||||||
import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers.ts";
 | 
					import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers.ts";
 | 
				
			||||||
import { logger } from "../utils/logger.ts";
 | 
					import { logger } from "../utils/logger.ts";
 | 
				
			||||||
import { DailyDeal, Fissure } from "../models/worldStateModel.ts";
 | 
					import { DailyDeal, Fissure } from "../models/worldStateModel.ts";
 | 
				
			||||||
 | 
					import { getConquest } from "./conquestService.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sortieBosses = [
 | 
					const sortieBosses = [
 | 
				
			||||||
    "SORTIE_BOSS_HYENA",
 | 
					    "SORTIE_BOSS_HYENA",
 | 
				
			||||||
@ -276,8 +277,6 @@ const microplanetEndlessJobs: readonly string[] = [
 | 
				
			|||||||
    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndlessPurifyBounty"
 | 
					    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndlessPurifyBounty"
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const EPOCH = 1734307200 * 1000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be winter in 1999 iteration 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const isBeforeNextExpectedWorldStateRefresh = (nowMs: number, thenMs: number): boolean => {
 | 
					const isBeforeNextExpectedWorldStateRefresh = (nowMs: number, thenMs: number): boolean => {
 | 
				
			||||||
    return nowMs + 300_000 > thenMs;
 | 
					    return nowMs + 300_000 > thenMs;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -3182,7 +3181,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
    // Nightwave Challenges
 | 
					    // Nightwave Challenges
 | 
				
			||||||
    const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
 | 
					    const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
 | 
				
			||||||
    if (nightwaveSyndicateTag) {
 | 
					    if (nightwaveSyndicateTag) {
 | 
				
			||||||
        const nightwaveStartTimestamp = 1747851300000;
 | 
					        const nightwaveStartTimestamp = nightwaveTagToActivation[nightwaveSyndicateTag] ?? 1747851300000;
 | 
				
			||||||
        const nightwaveSeason = nightwaveTagToSeason[nightwaveSyndicateTag];
 | 
					        const nightwaveSeason = nightwaveTagToSeason[nightwaveSyndicateTag];
 | 
				
			||||||
        worldState.SeasonInfo = {
 | 
					        worldState.SeasonInfo = {
 | 
				
			||||||
            Activation: { $date: { $numberLong: nightwaveStartTimestamp.toString() } },
 | 
					            Activation: { $date: { $numberLong: nightwaveStartTimestamp.toString() } },
 | 
				
			||||||
@ -3469,6 +3468,18 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Void Storms
 | 
				
			||||||
 | 
					    const hour = Math.trunc(timeMs / unixTimesInMs.hour);
 | 
				
			||||||
 | 
					    const overLastHourStormExpiry = hour * unixTimesInMs.hour + 10 * unixTimesInMs.minute;
 | 
				
			||||||
 | 
					    const thisHourStormActivation = hour * unixTimesInMs.hour + 40 * unixTimesInMs.minute;
 | 
				
			||||||
 | 
					    if (overLastHourStormExpiry > timeMs) {
 | 
				
			||||||
 | 
					        pushVoidStorms(worldState.VoidStorms, hour - 2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pushVoidStorms(worldState.VoidStorms, hour - 1);
 | 
				
			||||||
 | 
					    if (isBeforeNextExpectedWorldStateRefresh(timeMs, thisHourStormActivation)) {
 | 
				
			||||||
 | 
					        pushVoidStorms(worldState.VoidStorms, hour);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Sortie & syndicate missions cycling every day (at 16:00 or 17:00 UTC depending on if London, OT is observing DST)
 | 
					    // Sortie & syndicate missions cycling every day (at 16:00 or 17:00 UTC depending on if London, OT is observing DST)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        const rollover = getSortieTime(day);
 | 
					        const rollover = getSortieTime(day);
 | 
				
			||||||
@ -3551,16 +3562,18 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        worldState.KnownCalendarSeasons.push(getCalendarSeason(week + 1));
 | 
					        worldState.KnownCalendarSeasons.push(getCalendarSeason(week + 1));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Void Storms
 | 
					    if (!buildLabel || version_compare(buildLabel, "2025.10.14.16.10") >= 0) {
 | 
				
			||||||
    const hour = Math.trunc(timeMs / unixTimesInMs.hour);
 | 
					        worldState.Conquests = [];
 | 
				
			||||||
    const overLastHourStormExpiry = hour * unixTimesInMs.hour + 10 * unixTimesInMs.minute;
 | 
					        {
 | 
				
			||||||
    const thisHourStormActivation = hour * unixTimesInMs.hour + 40 * unixTimesInMs.minute;
 | 
					            const season = (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[week % 4];
 | 
				
			||||||
    if (overLastHourStormExpiry > timeMs) {
 | 
					            worldState.Conquests.push(getConquest("CT_LAB", week, null));
 | 
				
			||||||
        pushVoidStorms(worldState.VoidStorms, hour - 2);
 | 
					            worldState.Conquests.push(getConquest("CT_HEX", week, season));
 | 
				
			||||||
    }
 | 
					        }
 | 
				
			||||||
    pushVoidStorms(worldState.VoidStorms, hour - 1);
 | 
					        if (isBeforeNextExpectedWorldStateRefresh(timeMs, weekEnd)) {
 | 
				
			||||||
    if (isBeforeNextExpectedWorldStateRefresh(timeMs, thisHourStormActivation)) {
 | 
					            const season = (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[(week + 1) % 4];
 | 
				
			||||||
        pushVoidStorms(worldState.VoidStorms, hour);
 | 
					            worldState.Conquests.push(getConquest("CT_LAB", week, null));
 | 
				
			||||||
 | 
					            worldState.Conquests.push(getConquest("CT_HEX", week, season));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Sentient Anomaly + Xtra Cheese cycles
 | 
					    // Sentient Anomaly + Xtra Cheese cycles
 | 
				
			||||||
@ -3809,6 +3822,10 @@ const nightwaveTagToSeason: Record<string, number> = {
 | 
				
			|||||||
    RadioLegionSyndicate: 0 // The Wolf of Saturn Six
 | 
					    RadioLegionSyndicate: 0 // The Wolf of Saturn Six
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const nightwaveTagToActivation: Record<string, number> = {
 | 
				
			||||||
 | 
					    RadioLegionIntermission14Syndicate: 1761589199000
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const updateFissures = async (): Promise<void> => {
 | 
					const updateFissures = async (): Promise<void> => {
 | 
				
			||||||
    const fissures = await Fissure.find();
 | 
					    const fissures = await Fissure.find();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,7 @@ import type { IOrbiterClient } from "../personalRoomsTypes.ts";
 | 
				
			|||||||
import type { ICountedStoreItem } from "warframe-public-export-plus";
 | 
					import type { ICountedStoreItem } from "warframe-public-export-plus";
 | 
				
			||||||
import type { IEquipmentClient, IEquipmentDatabase, ITraits } from "../equipmentTypes.ts";
 | 
					import type { IEquipmentClient, IEquipmentDatabase, ITraits } from "../equipmentTypes.ts";
 | 
				
			||||||
import type { ILoadOutPresets } from "../saveLoadoutTypes.ts";
 | 
					import type { ILoadOutPresets } from "../saveLoadoutTypes.ts";
 | 
				
			||||||
 | 
					import type { CalendarSeasonType } from "../worldStateTypes.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type InventoryDatabaseEquipment = {
 | 
					export type InventoryDatabaseEquipment = {
 | 
				
			||||||
    [_ in TEquipmentKey]: IEquipmentDatabase[];
 | 
					    [_ in TEquipmentKey]: IEquipmentDatabase[];
 | 
				
			||||||
@ -1180,7 +1181,7 @@ export interface IMarker {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ISeasonProgress {
 | 
					export interface ISeasonProgress {
 | 
				
			||||||
    SeasonType: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
 | 
					    SeasonType: CalendarSeasonType;
 | 
				
			||||||
    LastCompletedDayIdx: number;
 | 
					    LastCompletedDayIdx: number;
 | 
				
			||||||
    LastCompletedChallengeDayIdx: number;
 | 
					    LastCompletedChallengeDayIdx: number;
 | 
				
			||||||
    ActivatedChallenges: string[];
 | 
					    ActivatedChallenges: string[];
 | 
				
			||||||
 | 
				
			|||||||
@ -55,6 +55,8 @@ export interface ILoginResponse extends IAccountAndLoginResponseCommons {
 | 
				
			|||||||
    DTLS?: number;
 | 
					    DTLS?: number;
 | 
				
			||||||
    IRC?: string[];
 | 
					    IRC?: string[];
 | 
				
			||||||
    HUB?: string;
 | 
					    HUB?: string;
 | 
				
			||||||
 | 
					    NatHash?: string;
 | 
				
			||||||
 | 
					    SteamId?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IGroup {
 | 
					export interface IGroup {
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,7 @@ export interface IWorldState {
 | 
				
			|||||||
        ActiveChallenges: ISeasonChallenge[];
 | 
					        ActiveChallenges: ISeasonChallenge[];
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    KnownCalendarSeasons: ICalendarSeason[];
 | 
					    KnownCalendarSeasons: ICalendarSeason[];
 | 
				
			||||||
 | 
					    Conquests?: IConquest[];
 | 
				
			||||||
    Tmp?: string;
 | 
					    Tmp?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -352,10 +353,11 @@ export interface ISeasonChallenge {
 | 
				
			|||||||
    Challenge: string;
 | 
					    Challenge: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CalendarSeasonType = "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
 | 
				
			||||||
export interface ICalendarSeason {
 | 
					export interface ICalendarSeason {
 | 
				
			||||||
    Activation: IMongoDate;
 | 
					    Activation: IMongoDate;
 | 
				
			||||||
    Expiry: IMongoDate;
 | 
					    Expiry: IMongoDate;
 | 
				
			||||||
    Season: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
 | 
					    Season: CalendarSeasonType;
 | 
				
			||||||
    Days: ICalendarDay[];
 | 
					    Days: ICalendarDay[];
 | 
				
			||||||
    YearIteration: number;
 | 
					    YearIteration: number;
 | 
				
			||||||
    Version: number;
 | 
					    Version: number;
 | 
				
			||||||
@ -416,6 +418,33 @@ export interface IGameMarketCategory {
 | 
				
			|||||||
    Items?: string[];
 | 
					    Items?: string[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// >= 40.0.0
 | 
				
			||||||
 | 
					export type TConquestType = "CT_LAB" | "CT_HEX";
 | 
				
			||||||
 | 
					export interface IConquest {
 | 
				
			||||||
 | 
					    Activation: IMongoDate;
 | 
				
			||||||
 | 
					    Expiry: IMongoDate;
 | 
				
			||||||
 | 
					    Type: TConquestType;
 | 
				
			||||||
 | 
					    Missions: IConquestMission[];
 | 
				
			||||||
 | 
					    Variables: [string, string, string, string];
 | 
				
			||||||
 | 
					    RandomSeed: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export interface IConquestMission {
 | 
				
			||||||
 | 
					    faction: TFaction;
 | 
				
			||||||
 | 
					    missionType: TMissionType;
 | 
				
			||||||
 | 
					    difficulties: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            type: "CD_NORMAL";
 | 
				
			||||||
 | 
					            deviation: string;
 | 
				
			||||||
 | 
					            risks: [string];
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            type: "CD_HARD";
 | 
				
			||||||
 | 
					            deviation: string;
 | 
				
			||||||
 | 
					            risks: [string, string];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ITmp {
 | 
					export interface ITmp {
 | 
				
			||||||
    cavabegin: string;
 | 
					    cavabegin: string;
 | 
				
			||||||
    PurchasePlatformLockEnabled: boolean; // Seems unused
 | 
					    PurchasePlatformLockEnabled: boolean; // Seems unused
 | 
				
			||||||
@ -423,6 +452,8 @@ export interface ITmp {
 | 
				
			|||||||
    ennnd?: boolean; // True if 1999 demo is available (no effect for >=38.6.0)
 | 
					    ennnd?: boolean; // True if 1999 demo is available (no effect for >=38.6.0)
 | 
				
			||||||
    mbrt?: boolean; // Related to mobile app rating request
 | 
					    mbrt?: boolean; // Related to mobile app rating request
 | 
				
			||||||
    fbst: IFbst;
 | 
					    fbst: IFbst;
 | 
				
			||||||
 | 
					    lqo?: IConquestOverride;
 | 
				
			||||||
 | 
					    hqo?: IConquestOverride;
 | 
				
			||||||
    sfn: number;
 | 
					    sfn: number;
 | 
				
			||||||
    edg?: TCircuitGameMode[]; // The Circuit game modes overwrite
 | 
					    edg?: TCircuitGameMode[]; // The Circuit game modes overwrite
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -451,3 +482,12 @@ interface IFbst {
 | 
				
			|||||||
    e: number;
 | 
					    e: number;
 | 
				
			||||||
    n: number;
 | 
					    n: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// < 40.0.0
 | 
				
			||||||
 | 
					interface IConquestOverride {
 | 
				
			||||||
 | 
					    mt?: string[]; // mission types but "Exterminate" instead of "MT_EXTERMINATION", etc. and "DualDefense" instead of "Defense" for hex conquest
 | 
				
			||||||
 | 
					    mv?: string[];
 | 
				
			||||||
 | 
					    mf?: number[]; // hex conquest only
 | 
				
			||||||
 | 
					    c?: [string, string][];
 | 
				
			||||||
 | 
					    fv?: string[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										70
									
								
								static/cert/cert.pem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								static/cert/cert.pem
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					-----BEGIN CERTIFICATE-----
 | 
				
			||||||
 | 
					MIIF/DCCBGSgAwIBAgIQH1nGumKdC869SnXDdjpRizANBgkqhkiG9w0BAQsFADBg
 | 
				
			||||||
 | 
					MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQD
 | 
				
			||||||
 | 
					Ey5TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gQ0EgRFYgUjM2
 | 
				
			||||||
 | 
					MB4XDTI1MTAzMTAwMDAwMFoXDTI2MDMwNjIzNTk1OVowGDEWMBQGA1UEAwwNKi5m
 | 
				
			||||||
 | 
					YWtldGxzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKZt+8pe
 | 
				
			||||||
 | 
					ZABBMSORWDlJBKykSbbNeEX2vPNrsQBqa3aBzQLdp6dm8qw099a5mn/vuQzdtFhc
 | 
				
			||||||
 | 
					QbaemxoVViszzM3dpTlOgLyl2r4cr8KMXJ/rRMqLeaYks+BVYJyxKAlVIduJCpP+
 | 
				
			||||||
 | 
					cxYUIiz+zNGMucuFdanzeNfCRhakDkzHH/LKZFYDHeqdR8vdUnOH8n9xuGR7sC6I
 | 
				
			||||||
 | 
					PbfML8SXCnazMpcPAeIFzJeZW3fbFIQ5R6B7X38k9+wUzKXrJo0+Q8U1mIAsV7fh
 | 
				
			||||||
 | 
					4gn+xgrFOJ7T2Vd8EtHgnr5nzNRDk4prE+ecTxBveL4QGuNoPaABtor6OLBR54AQ
 | 
				
			||||||
 | 
					8ycj29lQ094BrJcCAwEAAaOCAngwggJ0MB8GA1UdIwQYMBaAFGjAEhYYDq/O9oem
 | 
				
			||||||
 | 
					MlejRlFdywcnMB0GA1UdDgQWBBRqYi9dBjbiV9wE6GEtzyzUM6cQmjAOBgNVHQ8B
 | 
				
			||||||
 | 
					Af8EBAMCBaAwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDATBJBgNV
 | 
				
			||||||
 | 
					HSAEQjBAMDQGCysGAQQBsjEBAgIHMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2Vj
 | 
				
			||||||
 | 
					dGlnby5jb20vQ1BTMAgGBmeBDAECATCBhAYIKwYBBQUHAQEEeDB2ME8GCCsGAQUF
 | 
				
			||||||
 | 
					BzAChkNodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNTZXJ2ZXJB
 | 
				
			||||||
 | 
					dXRoZW50aWNhdGlvbkNBRFZSMzYuY3J0MCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz
 | 
				
			||||||
 | 
					cC5zZWN0aWdvLmNvbTAlBgNVHREEHjAcgg0qLmZha2V0bHMuY29tggtmYWtldGxz
 | 
				
			||||||
 | 
					LmNvbTCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AJaXZL9VWJet90OHaDcIQnfp
 | 
				
			||||||
 | 
					8DrV9qTzNm5GpD8PyqnGAAABmjqerq4AAAQDAEcwRQIgY1YdLRDkbq9U9qGLpX5M
 | 
				
			||||||
 | 
					IESNpCLFKVgoPLyNQQejtPUCIQCGlFB13fGO7GpVZLXyuOrXIWRZQUxnm1GqhvOP
 | 
				
			||||||
 | 
					BoovPgB2ANFuqaVoB35mNaA/N6XdvAOlPEESFNSIGPXpMbMjy5UEAAABmjqerwkA
 | 
				
			||||||
 | 
					AAQDAEcwRQIhAMxyLWnCpof354KMPYiWQmqd+2D+yyV5ZL7rmvIJw0ojAiAxxsid
 | 
				
			||||||
 | 
					se3hTmdo39MJLKyVqlZKUua5dmLckUYUImsogTANBgkqhkiG9w0BAQsFAAOCAYEA
 | 
				
			||||||
 | 
					ByJlO00LMIgL6d5xfouZHl1OL2w0DDiWHQa87BM9AqUFSgE1IsWyUAMTVoxNp3bM
 | 
				
			||||||
 | 
					c4fiBoJ9P32hDft2gJRCtJ+xOlU9ufQqCfpN9pBxe8eEldJdnHi7q9Y1dIVwRA+y
 | 
				
			||||||
 | 
					xY2r1xgTmjYgv1Uo5mQoOjgvrRdUNTyW0Rkp9+9zr6NFxi4orqypt4KKOFY2DlV9
 | 
				
			||||||
 | 
					wKmS4HPXSNR6QEylUR1+IsCP7iQzgTcZDYYLH2vAekJ1vFJkxZyB8a1vjuUxohPi
 | 
				
			||||||
 | 
					JoXnOqBu5T9JdE81/Rm01bHQ6XAiAHpQRCyZZ3TBUgpPd10DrV8K/gMuBduKOkgk
 | 
				
			||||||
 | 
					BnwNZjOCcCwgI66F5kmoeT95ovn8ApKO9aT3EfYXx3XM+xaWjEQRpXulB9AYjQvg
 | 
				
			||||||
 | 
					wxdW+cpHAa/ttPEnrb4y2Az4uYMM2016U5p1o6SP3/feGbWSsJ33OF4VvSaS1x0h
 | 
				
			||||||
 | 
					DgzfqIX4/8yeYsfNNsoSpcwV9UGBZ7Zs0Avy1Mm5ah0Z6CGGKVWb7Qqse1YR9MSa
 | 
				
			||||||
 | 
					-----END CERTIFICATE-----
 | 
				
			||||||
 | 
					-----BEGIN CERTIFICATE-----
 | 
				
			||||||
 | 
					MIIGTDCCBDSgAwIBAgIQOXpmzCdWNi4NqofKbqvjsTANBgkqhkiG9w0BAQwFADBf
 | 
				
			||||||
 | 
					MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD
 | 
				
			||||||
 | 
					Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw
 | 
				
			||||||
 | 
					HhcNMjEwMzIyMDAwMDAwWhcNMzYwMzIxMjM1OTU5WjBgMQswCQYDVQQGEwJHQjEY
 | 
				
			||||||
 | 
					MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFB1Ymxp
 | 
				
			||||||
 | 
					YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gQ0EgRFYgUjM2MIIBojANBgkqhkiG9w0B
 | 
				
			||||||
 | 
					AQEFAAOCAY8AMIIBigKCAYEAljZf2HIz7+SPUPQCQObZYcrxLTHYdf1ZtMRe7Yeq
 | 
				
			||||||
 | 
					RPSwygz16qJ9cAWtWNTcuICc++p8Dct7zNGxCpqmEtqifO7NvuB5dEVexXn9RFFH
 | 
				
			||||||
 | 
					12Hm+NtPRQgXIFjx6MSJcNWuVO3XGE57L1mHlcQYj+g4hny90aFh2SCZCDEVkAja
 | 
				
			||||||
 | 
					EMMfYPKuCjHuuF+bzHFb/9gV8P9+ekcHENF2nR1efGWSKwnfG5RawlkaQDpRtZTm
 | 
				
			||||||
 | 
					M64TIsv/r7cyFO4nSjs1jLdXYdz5q3a4L0NoabZfbdxVb+CUEHfB0bpulZQtH1Rv
 | 
				
			||||||
 | 
					38e/lIdP7OTTIlZh6OYL6NhxP8So0/sht/4J9mqIGxRFc0/pC8suja+wcIUna0HB
 | 
				
			||||||
 | 
					pXKfXTKpzgis+zmXDL06ASJf5E4A2/m+Hp6b84sfPAwQ766rI65mh50S0Di9E3Pn
 | 
				
			||||||
 | 
					2WcaJc+PILsBmYpgtmgWTR9eV9otfKRUBfzHUHcVgarub/XluEpRlTtZudU5xbFN
 | 
				
			||||||
 | 
					xx/DgMrXLUAPaI60fZ6wA+PTAgMBAAGjggGBMIIBfTAfBgNVHSMEGDAWgBRWc1hk
 | 
				
			||||||
 | 
					lfmSGrASKgRieaFAFYghSTAdBgNVHQ4EFgQUaMASFhgOr872h6YyV6NGUV3LBycw
 | 
				
			||||||
 | 
					DgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYI
 | 
				
			||||||
 | 
					KwYBBQUHAwEGCCsGAQUFBwMCMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBAgEw
 | 
				
			||||||
 | 
					VAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdv
 | 
				
			||||||
 | 
					UHVibGljU2VydmVyQXV0aGVudGljYXRpb25Sb290UjQ2LmNybDCBhAYIKwYBBQUH
 | 
				
			||||||
 | 
					AQEEeDB2ME8GCCsGAQUFBzAChkNodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3Rp
 | 
				
			||||||
 | 
					Z29QdWJsaWNTZXJ2ZXJBdXRoZW50aWNhdGlvblJvb3RSNDYucDdjMCMGCCsGAQUF
 | 
				
			||||||
 | 
					BzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEA
 | 
				
			||||||
 | 
					YtOC9Fy+TqECFw40IospI92kLGgoSZGPOSQXMBqmsGWZUQ7rux7cj1du6d9rD6C8
 | 
				
			||||||
 | 
					ze1B2eQjkrGkIL/OF1s7vSmgYVafsRoZd/IHUrkoQvX8FZwUsmPu7amgBfaY3g+d
 | 
				
			||||||
 | 
					q1x0jNGKb6I6Bzdl6LgMD9qxp+3i7GQOnd9J8LFSietY6Z4jUBzVoOoz8iAU84OF
 | 
				
			||||||
 | 
					h2HhAuiPw1ai0VnY38RTI+8kepGWVfGxfBWzwH9uIjeooIeaosVFvE8cmYUB4TSH
 | 
				
			||||||
 | 
					5dUyD0jHct2+8ceKEtIoFU/FfHq/mDaVnvcDCZXtIgitdMFQdMZaVehmObyhRdDD
 | 
				
			||||||
 | 
					4NQCs0gaI9AAgFj4L9QtkARzhQLNyRf87Kln+YU0lgCGr9HLg3rGO8q+Y4ppLsOd
 | 
				
			||||||
 | 
					unQZ6ZxPNGIfOApbPVf5hCe58EZwiWdHIMn9lPP6+F404y8NNugbQixBber+x536
 | 
				
			||||||
 | 
					WrZhFZLjEkhp7fFXf9r32rNPfb74X/U90Bdy4lzp3+X1ukh1BuMxA/EEhDoTOS3l
 | 
				
			||||||
 | 
					7ABvc7BYSQubQ2490OcdkIzUh3ZwDrakMVrbaTxUM2p24N6dB+ns2zptWCva6jzW
 | 
				
			||||||
 | 
					r8IWKIMxzxLPv5Kt3ePKcUdvkBU/smqujSczTzzSjIoR5QqQA6lN1ZRSnuHIWCvh
 | 
				
			||||||
 | 
					JEltkYnTAH41QJ6SAWO66GrrUESwN/cgZzL4JLEqz1Y=
 | 
				
			||||||
 | 
					-----END CERTIFICATE-----
 | 
				
			||||||
							
								
								
									
										28
									
								
								static/cert/key.pem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								static/cert/key.pem
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					-----BEGIN PRIVATE KEY-----
 | 
				
			||||||
 | 
					MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCmbfvKXmQAQTEj
 | 
				
			||||||
 | 
					kVg5SQSspEm2zXhF9rzza7EAamt2gc0C3aenZvKsNPfWuZp/77kM3bRYXEG2npsa
 | 
				
			||||||
 | 
					FVYrM8zN3aU5ToC8pdq+HK/CjFyf60TKi3mmJLPgVWCcsSgJVSHbiQqT/nMWFCIs
 | 
				
			||||||
 | 
					/szRjLnLhXWp83jXwkYWpA5Mxx/yymRWAx3qnUfL3VJzh/J/cbhke7AuiD23zC/E
 | 
				
			||||||
 | 
					lwp2szKXDwHiBcyXmVt32xSEOUege19/JPfsFMyl6yaNPkPFNZiALFe34eIJ/sYK
 | 
				
			||||||
 | 
					xTie09lXfBLR4J6+Z8zUQ5OKaxPnnE8Qb3i+EBrjaD2gAbaK+jiwUeeAEPMnI9vZ
 | 
				
			||||||
 | 
					UNPeAayXAgMBAAECggEAOYwMJVRwFZp1KExIij5STHPePURc0yxW94CESpWBpQ+K
 | 
				
			||||||
 | 
					2PPV1c+GF7+U9v1ki9pTTTyX8HmuCzxaezFngza9GW4LhH49i3153oTCzW2FVZKf
 | 
				
			||||||
 | 
					Tb3eiXFldStwZZ3oLxntxCBltPilyLubeZ19KvQTBmmWXvaeEVTOsWN2wluUE3oT
 | 
				
			||||||
 | 
					QEEFJmvWSj6Ow5HwA2xZP5dD4gv7nCY1swTePVLgWtJDC3AWDmKv3mB28brmtaDw
 | 
				
			||||||
 | 
					wP/Oq9LLlHJDK+AzJEuUs+VDFQi7j6yqXWglSjO3Jwkkfkqg/SSoZ9BKdd6i0DLs
 | 
				
			||||||
 | 
					UnUWHHATpv0Lwsa7w8csQBwfskUxsXECZpwzpCgsIQKBgQDYl2wvwjjG6qZtr060
 | 
				
			||||||
 | 
					rj4PDkFLxFbtlmBiyW9ShM6iqK6FWQJJ1FNr6MdM2zBqw+n4tldpU3DnuZQphgZK
 | 
				
			||||||
 | 
					57jejWFsHH5DYkS8k0k021AYT2IyCFHoEF43LlbRDXQguw1fADnoy6FpJBaRzuxx
 | 
				
			||||||
 | 
					jPEHdB/aYqngmTXglFYkizN2BwKBgQDEthR1FV5iPdQXKSR+Yu2irBzpxY1xhdd2
 | 
				
			||||||
 | 
					V5082etd2vBvAT7e4k8MroNMuYqL8miu2SeTJEyNkpCVoxQPF4uavandoRuEFIZl
 | 
				
			||||||
 | 
					8VPVvG5xqYIqXAJ4XlSImEgUww+jJfyLzHT0TZi6rrdHV/8zUXEimjc6OBZVafDg
 | 
				
			||||||
 | 
					FP6Lqo/w8QKBgQCxx3B8rv3tgDNFOqzur0qvDvNXnnv/nfvVeiPO5sW5S52cRJgV
 | 
				
			||||||
 | 
					Q5uJqlLUaeGO8Oo+RGTxRhUZjwDnKGRH3XWn7wI1PBoDc0iaRIbFRPK0UYx3Js8c
 | 
				
			||||||
 | 
					HTtILdgC1fko2IA8JzJhO6tsYrvHyMHY3mgExzNSDMQFX5ySjw86BawixwKBgBGc
 | 
				
			||||||
 | 
					J0qwBgoPdOw53618179HXzNCXz45eCd9AnOPIrX9Qqb9Wo6DfgYpnVGCDrgmlF6K
 | 
				
			||||||
 | 
					zDMs/bly1ITA26vaNMI+lnVj1d3GJJ39s76fpteAEEoQgJwb/b9YuqM5Ly4w2WH+
 | 
				
			||||||
 | 
					hL3WMIUN3RSC+TKz6MfrPGR23vD4kfrNhlgkhcxRAoGAEb6nXwyWe5hg9+qE2fcf
 | 
				
			||||||
 | 
					W27VApoalmnDOGFHtGhUitbBw7mHPb6wsSh4mx5Ucccfb5WUO3Cq5aW3qsDdkM+a
 | 
				
			||||||
 | 
					YrBDbvw7PPcAX0vokpyFQvHH35AsIXQuYlQyGzcCdZkpFPIB5gVbLvbgxpBvEbVZ
 | 
				
			||||||
 | 
					mGJpw7X/6Pg0ux0Ze11cr2c=
 | 
				
			||||||
 | 
					-----END PRIVATE KEY-----
 | 
				
			||||||
@ -1,71 +0,0 @@
 | 
				
			|||||||
-----BEGIN CERTIFICATE-----
 | 
					 | 
				
			||||||
MIIGMDCCBRigAwIBAgIQX4800cgswlDH/QexMSnnnjANBgkqhkiG9w0BAQsFADCB
 | 
					 | 
				
			||||||
jzELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
 | 
					 | 
				
			||||||
A1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQD
 | 
					 | 
				
			||||||
Ey5TZWN0aWdvIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB
 | 
					 | 
				
			||||||
MB4XDTI1MDMwNjAwMDAwMFoXDTI2MDMwNjIzNTk1OVowGDEWMBQGA1UEAwwNKi5m
 | 
					 | 
				
			||||||
YWtldGxzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMe42XWK
 | 
					 | 
				
			||||||
HJuR7doFTX79zrEKfTlD2hjRIif3dHKJNTJNvZa52mIoHelP7RVUuFOhp7aZCNLh
 | 
					 | 
				
			||||||
IEzDyZObl8vwO6L2PVu5tbBEEoNixbpfhc8ZICEBuVo2UAhnJFcMJtuvtrCq+7ye
 | 
					 | 
				
			||||||
oczM/k/nh8FBz2WnLzWs4CZt1sa5knZXFmBmsHJQtQIC6vx7QzVcKGOlAosIEHSK
 | 
					 | 
				
			||||||
X4nIz5fLgWSzor1Gay56j31PTk+qRvlPQM2aKiLWnlLfRED4zHJqLe94itu8llPX
 | 
					 | 
				
			||||||
b6g+cLxxRKUpMqtG/15cDdBZwv40Dja7bmNfe1u4w2QCVLjvHVaVpNXbcRay/Mhn
 | 
					 | 
				
			||||||
M1w5LzDZmV58b18CAwEAAaOCAvwwggL4MB8GA1UdIwQYMBaAFI2MXsRUrYrhd+mb
 | 
					 | 
				
			||||||
+ZsF4bgBjWHhMB0GA1UdDgQWBBS6/x/N38wMJrQq/cE1oIcRERMonTAOBgNVHQ8B
 | 
					 | 
				
			||||||
Af8EBAMCBaAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
 | 
					 | 
				
			||||||
BQUHAwIwSQYDVR0gBEIwQDA0BgsrBgEEAbIxAQICBzAlMCMGCCsGAQUFBwIBFhdo
 | 
					 | 
				
			||||||
dHRwczovL3NlY3RpZ28uY29tL0NQUzAIBgZngQwBAgEwgYQGCCsGAQUFBwEBBHgw
 | 
					 | 
				
			||||||
djBPBggrBgEFBQcwAoZDaHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUlNB
 | 
					 | 
				
			||||||
RG9tYWluVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNydDAjBggrBgEFBQcwAYYX
 | 
					 | 
				
			||||||
aHR0cDovL29jc3Auc2VjdGlnby5jb20wJQYDVR0RBB4wHIINKi5mYWtldGxzLmNv
 | 
					 | 
				
			||||||
bYILZmFrZXRscy5jb20wggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB2AJaXZL9V
 | 
					 | 
				
			||||||
WJet90OHaDcIQnfp8DrV9qTzNm5GpD8PyqnGAAABlWsz5fgAAAQDAEcwRQIgTN7Y
 | 
					 | 
				
			||||||
/mDqiD3RbGVLEOQK2wvXsboBolBRwGJFuFEsDScCIQCQ0qfb/0V8qqSxrkx/PiVS
 | 
					 | 
				
			||||||
1lSn5gBEnQUiQOkefcnW0gB2ABmG1Mcoqm/+ugNveCpNAZGqzi1yMQ+uzl1wQS0l
 | 
					 | 
				
			||||||
TMfUAAABlWsz5dAAAAQDAEcwRQIhAJnQJyrSCWWdi9Kyoa7XuMGyDKt183jJMY0E
 | 
					 | 
				
			||||||
71abTuBOAiBC+WnK1esG6xr8aVGHRcc+1U/I7LiaG3LCRMYtCKrTGwB2AMs49xWJ
 | 
					 | 
				
			||||||
fIShRF9bwd37yW7ymlnNRwppBYWwyxTDFFjnAAABlWsz5f4AAAQDAEcwRQIhAJUs
 | 
					 | 
				
			||||||
4PWDwyQJnCxCyEwFlFUY2uYQkGrQPA9f9Sw5Xk1fAiB63eQtZQGjvzvhOghy6z9a
 | 
					 | 
				
			||||||
8oGYbDfDQ/zfisMYO7rM6zANBgkqhkiG9w0BAQsFAAOCAQEAEHnSoeBbWiK3CS3a
 | 
					 | 
				
			||||||
px0BL+YXxRxdUcTMHgn5o+LlI9sWlpf+JLXmn7Z4QA6fAwT4k/Ue7xsmIq0OraDk
 | 
					 | 
				
			||||||
/pEVXWm1HO/9wUkGQg0DBi77BpfHircd7OWIMdt250Q8UAmZkOyhVgnwBcScqMwq
 | 
					 | 
				
			||||||
2T5CPaYvYGgYWx/qkIBv7JqhVbrP82rnF9b9ZUZ8GIE31chBmtMva9AsnAN5dmRw
 | 
					 | 
				
			||||||
81bVvPWXUfX30CYu5sxeWL06Zpy9nfJumxZri1SWXNTBjSvud2jsZ8tSCUAWLL/4
 | 
					 | 
				
			||||||
ui3Vien9m2oMOpaA8xbS88ZTk9Alm/o5febEKJZUPlytQzij8gQpiovFw2v+Cdei
 | 
					 | 
				
			||||||
+tFXKw==
 | 
					 | 
				
			||||||
-----END CERTIFICATE-----
 | 
					 | 
				
			||||||
-----BEGIN CERTIFICATE-----
 | 
					 | 
				
			||||||
MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB
 | 
					 | 
				
			||||||
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
 | 
					 | 
				
			||||||
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
 | 
					 | 
				
			||||||
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx
 | 
					 | 
				
			||||||
MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBjzELMAkGA1UEBhMCR0IxGzAZBgNV
 | 
					 | 
				
			||||||
BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE
 | 
					 | 
				
			||||||
ChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4g
 | 
					 | 
				
			||||||
VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
 | 
					 | 
				
			||||||
AQ8AMIIBCgKCAQEA1nMz1tc8INAA0hdFuNY+B6I/x0HuMjDJsGz99J/LEpgPLT+N
 | 
					 | 
				
			||||||
TQEMgg8Xf2Iu6bhIefsWg06t1zIlk7cHv7lQP6lMw0Aq6Tn/2YHKHxYyQdqAJrkj
 | 
					 | 
				
			||||||
eocgHuP/IJo8lURvh3UGkEC0MpMWCRAIIz7S3YcPb11RFGoKacVPAXJpz9OTTG0E
 | 
					 | 
				
			||||||
oKMbgn6xmrntxZ7FN3ifmgg0+1YuWMQJDgZkW7w33PGfKGioVrCSo1yfu4iYCBsk
 | 
					 | 
				
			||||||
Haswha6vsC6eep3BwEIc4gLw6uBK0u+QDrTBQBbwb4VCSmT3pDCg/r8uoydajotY
 | 
					 | 
				
			||||||
uK3DGReEY+1vVv2Dy2A0xHS+5p3b4eTlygxfFQIDAQABo4IBbjCCAWowHwYDVR0j
 | 
					 | 
				
			||||||
BBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFI2MXsRUrYrhd+mb
 | 
					 | 
				
			||||||
+ZsF4bgBjWHhMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G
 | 
					 | 
				
			||||||
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYGBFUdIAAw
 | 
					 | 
				
			||||||
CAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0
 | 
					 | 
				
			||||||
LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2Bggr
 | 
					 | 
				
			||||||
BgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNv
 | 
					 | 
				
			||||||
bS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDov
 | 
					 | 
				
			||||||
L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMr9hvQ5Iw0/H
 | 
					 | 
				
			||||||
ukdN+Jx4GQHcEx2Ab/zDcLRSmjEzmldS+zGea6TvVKqJjUAXaPgREHzSyrHxVYbH
 | 
					 | 
				
			||||||
7rM2kYb2OVG/Rr8PoLq0935JxCo2F57kaDl6r5ROVm+yezu/Coa9zcV3HAO4OLGi
 | 
					 | 
				
			||||||
H19+24rcRki2aArPsrW04jTkZ6k4Zgle0rj8nSg6F0AnwnJOKf0hPHzPE/uWLMUx
 | 
					 | 
				
			||||||
RP0T7dWbqWlod3zu4f+k+TY4CFM5ooQ0nBnzvg6s1SQ36yOoeNDT5++SR2RiOSLv
 | 
					 | 
				
			||||||
xvcRviKFxmZEJCaOEDKNyJOuB56DPi/Z+fVGjmO+wea03KbNIaiGCpXZLoUmGv38
 | 
					 | 
				
			||||||
sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL
 | 
					 | 
				
			||||||
l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq
 | 
					 | 
				
			||||||
6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY
 | 
					 | 
				
			||||||
LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5
 | 
					 | 
				
			||||||
yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K
 | 
					 | 
				
			||||||
00u/I5sUKUErmgQfky3xxzlIPK1aEn8=
 | 
					 | 
				
			||||||
-----END CERTIFICATE-----
 | 
					 | 
				
			||||||
@ -1,28 +0,0 @@
 | 
				
			|||||||
-----BEGIN PRIVATE KEY-----
 | 
					 | 
				
			||||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHuNl1ihybke3a
 | 
					 | 
				
			||||||
BU1+/c6xCn05Q9oY0SIn93RyiTUyTb2WudpiKB3pT+0VVLhToae2mQjS4SBMw8mT
 | 
					 | 
				
			||||||
m5fL8Dui9j1bubWwRBKDYsW6X4XPGSAhAblaNlAIZyRXDCbbr7awqvu8nqHMzP5P
 | 
					 | 
				
			||||||
54fBQc9lpy81rOAmbdbGuZJ2VxZgZrByULUCAur8e0M1XChjpQKLCBB0il+JyM+X
 | 
					 | 
				
			||||||
y4Fks6K9Rmsueo99T05Pqkb5T0DNmioi1p5S30RA+Mxyai3veIrbvJZT12+oPnC8
 | 
					 | 
				
			||||||
cUSlKTKrRv9eXA3QWcL+NA42u25jX3tbuMNkAlS47x1WlaTV23EWsvzIZzNcOS8w
 | 
					 | 
				
			||||||
2ZlefG9fAgMBAAECggEAT1Tti/LASks8300b60WFxG0WMJjzGMh5eMaiSpyVtNWM
 | 
					 | 
				
			||||||
aUKJrFOjDfnhgoeUcCPWKoG/L4Sc/+EFQMydDzTte120IasysEFZ2TZytAUdcZXZ
 | 
					 | 
				
			||||||
XUMCDQNl5vCRTsJU7Q5u0t4YAGRCgMcsfTDKi8lISGiQKBHzN1CJ74Xm13rgOInd
 | 
					 | 
				
			||||||
lAc0wd5S89sL6RYmRTj1LvuZ95EHXHqQGdv0fIFEyP3pF1iPwcoTuIVEeICqnEvW
 | 
					 | 
				
			||||||
vd8CVO68eH3HFIwioqjp4qW3pxPZMhVq4161805uAMkoQlE+7MtEVenmP++1u1gM
 | 
					 | 
				
			||||||
FjvAs3j9CZqOHZKcLlOtcGSwDlD++fCMMT4slLgLgQKBgQDy58E5nuYXdxlFQQk4
 | 
					 | 
				
			||||||
QccUKpyJ2aVXyp9xvTFBot/5Pik1SkuDzv2XU1OTxdxf3EongLy91nMJ2/6/39Je
 | 
					 | 
				
			||||||
lf0/2MjzCtJ/lSzZ/zpJAu86UkBkWBAA5loGIof6OKedbEIgqpJqtK59S+j3ExO9
 | 
					 | 
				
			||||||
eqa+uFrtt1UfaJG4A7TT+dIvIwKBgQDSfSOdSM5Dh3KsQHVnIWcIkzwTtlJlO+rG
 | 
					 | 
				
			||||||
6rDEADxw6Kp8VIL/dq4Foe8yW4VqLVrWUuZsU6jzC9GdnyYi6VaqZ/iSUtGkBMOT
 | 
					 | 
				
			||||||
WTTYhqXlURaQ13jhqdwCZJRbVI72JbXn2OGEv8DgXnk//QKED/8VdKqAzCSr1t1f
 | 
					 | 
				
			||||||
3yfwei0AlQKBgD19KU66yKg7/+umEP1quUiDmOjUbaSRqFcUe3mQD356m9ffnMob
 | 
					 | 
				
			||||||
BdrevxNzTNv/Wc4yKpUryic+x3gu4oQLF/annAbaQHsHejkdANYmpgRvedls6XAw
 | 
					 | 
				
			||||||
360Z5K4U1WlmVD8Mrs/QOTOCmdChxad7euZgqLPwat3ujKS2W3oljW1dAoGBAM4/
 | 
					 | 
				
			||||||
AB6lsDZLCfnuTxt2h1bHrh5CkAnR5AJ1BC+Ja6/WyvZ4eMOIroumWJKnStr3BgLr
 | 
					 | 
				
			||||||
yAxtDSbZddNUljGvIdRnfBEkRXbJlDlVN4rSpMtF4S6bcz7rCUDu/M9g05Qs70j2
 | 
					 | 
				
			||||||
IkPJAFzZNUWVzFlKs096uXbqkSQvrUq7ho8DqAThAoGBAL7Nrbr5LWcBgvwEhEla
 | 
					 | 
				
			||||||
VRfYb0FUrDwLIrVWntJjW566/pVQQ4BmatsblLjlQYWk9MCIYXWZbnB+2fRx9yjQ
 | 
					 | 
				
			||||||
Adggez7Dws/Mrh/wVudKgayHCy5Lgd8rYjNgC+VZf8XGrWX3QXMJ6UWAyQLTeoO7
 | 
					 | 
				
			||||||
hToW9o9CQMIhaR43G8di1kjF
 | 
					 | 
				
			||||||
-----END PRIVATE KEY-----
 | 
					 | 
				
			||||||
							
								
								
									
										160
									
								
								static/fixed_responses/vorsPrizePreU40Rewards.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								static/fixed_responses/vorsPrizePreU40Rewards.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,160 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "/Lotus/Types/Keys/VorsPrize/MissionOne": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Types/Items/ShipFeatureItems/SocialMenuFeatureItem"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/WeaponMeleeDamageMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/WeaponDamageAmountMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/WeaponDamageAmountMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_CREDITS",
 | 
				
			||||||
 | 
					      "amount": 2500
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "/Lotus/Types/Keys/VorsPrize/MissionTwo": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Types/Items/ShipFeatureItems/ModsFeatureItem"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/AvatarHealthMaxMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/AvatarShieldMaxMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/AvatarAbilityRangeMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/AvatarAbilityStrengthMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/AvatarAbilityDurationMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_CREDITS",
 | 
				
			||||||
 | 
					      "amount": 2500
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "/Lotus/Types/Keys/VorsPrize/MissionThree": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Types/Items/ShipFeatureItems/FoundryFeatureItem"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/WeaponFireRateMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/WeaponFactionDamageCorpus"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/WeaponFactionDamageGrineer"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/WeaponFireDamageMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/WeaponElectricityDamageMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_CREDITS",
 | 
				
			||||||
 | 
					      "amount": 2500
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "/Lotus/Types/Keys/VorsPrize/MissionFour": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/AvatarPickupBonusMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/AvatarPowerMaxMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/AvatarEnemyRadarMod"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_CREDITS",
 | 
				
			||||||
 | 
					      "amount": 2500
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "/Lotus/Types/Keys/VorsPrize/MissionFive": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Types/Items/ShipFeatureItems/MercuryNavigationFeatureItem"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_STORE_ITEM",
 | 
				
			||||||
 | 
					      "itemType": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/CommonFusionBundle"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "rewardType": "RT_CREDITS",
 | 
				
			||||||
 | 
					      "amount": 5000
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1152,7 +1152,7 @@
 | 
				
			|||||||
                                    <button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
 | 
					                                    <button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(addMissingHelminthRecipes);" data-loc="cheats_addMissingSubsumedAbilities"></button>
 | 
					                                    <button class="btn btn-primary" onclick="debounce(addMissingHelminthRecipes);" data-loc="cheats_addMissingSubsumedAbilities"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="doIntrinsicsUnlockAll();" data-loc="cheats_intrinsicsUnlockAll"></button>
 | 
					                                    <button class="btn btn-primary" onclick="doIntrinsicsUnlockAll();" data-loc="cheats_intrinsicsUnlockAll"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(doMaxPlexus);" data-loc="inventory_maxPlexus"></button>
 | 
					                                    <button class="btn btn-primary" onclick="debounce(doMaxPlexus);" data-loc="cheats_maxPlexus"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(unlockAllProfitTakerStages);" data-loc="cheats_unlockAllProfitTakerStages"></button>
 | 
					                                    <button class="btn btn-primary" onclick="debounce(unlockAllProfitTakerStages);" data-loc="cheats_unlockAllProfitTakerStages"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(unlockAllSimarisResearchEntries);" data-loc="cheats_unlockAllSimarisResearchEntries"></button>
 | 
					                                    <button class="btn btn-primary" onclick="debounce(unlockAllSimarisResearchEntries);" data-loc="cheats_unlockAllSimarisResearchEntries"></button>
 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -169,6 +169,7 @@ function renameAccount(taken_name) {
 | 
				
			|||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    $(".displayname").text(newname);
 | 
					                    $(".displayname").text(newname);
 | 
				
			||||||
                    updateLocElements();
 | 
					                    updateLocElements();
 | 
				
			||||||
 | 
					                    toast(loc("code_succRelog"));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@ -646,7 +647,7 @@ function fetchItemList() {
 | 
				
			|||||||
                        if ("badReason" in item) {
 | 
					                        if ("badReason" in item) {
 | 
				
			||||||
                            if (item.badReason == "starter") {
 | 
					                            if (item.badReason == "starter") {
 | 
				
			||||||
                                item.name = loc("code_starter").split("|MOD|").join(item.name);
 | 
					                                item.name = loc("code_starter").split("|MOD|").join(item.name);
 | 
				
			||||||
                            } else {
 | 
					                            } else if (item.badReason != "notraw") {
 | 
				
			||||||
                                item.name += " " + loc("code_badItem");
 | 
					                                item.name += " " + loc("code_badItem");
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
@ -1454,7 +1455,11 @@ function updateInventory() {
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    const td = document.createElement("td");
 | 
					                    const td = document.createElement("td");
 | 
				
			||||||
                    td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
 | 
					                    td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
 | 
				
			||||||
                    td.innerHTML += " <span title='" + loc("code_rank") + "'>★ " + rank + "/" + maxRank + "</span>";
 | 
					                    if (itemMap[item.ItemType]?.badReason == "notraw") {
 | 
				
			||||||
 | 
					                        // Assuming this is a riven with a pending challenge, so rank would be N/A, but otherwise it's fine.
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        td.innerHTML += " <span title='" + loc("code_rank") + "'>★ " + rank + "/" + maxRank + "</span>";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    tr.appendChild(td);
 | 
					                    tr.appendChild(td);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
				
			|||||||
@ -51,7 +51,7 @@ dict = {
 | 
				
			|||||||
    code_focusUnlocked: `|COUNT| neue Fokus-Schulen freigeschaltet! Ein Inventar-Update wird benötigt, damit die Änderungen im Spiel sichtbar werden.`,
 | 
					    code_focusUnlocked: `|COUNT| neue Fokus-Schulen freigeschaltet! Ein Inventar-Update wird benötigt, damit die Änderungen im Spiel sichtbar werden.`,
 | 
				
			||||||
    code_addModsConfirm: `Bist du sicher, dass du |COUNT| Mods zu deinem Account hinzufügen möchtest?`,
 | 
					    code_addModsConfirm: `Bist du sicher, dass du |COUNT| Mods zu deinem Account hinzufügen möchtest?`,
 | 
				
			||||||
    code_succImport: `Erfolgreich importiert.`,
 | 
					    code_succImport: `Erfolgreich importiert.`,
 | 
				
			||||||
    code_succRelog: `Fertig. Bitte beachte, dass du dich neu anmelden musst, um Änderungen im Spiel zu sehen.`,
 | 
					    code_succRelog: `Fertig. Bitte beachte, dass du dich neu anmelden musst, damit die Änderung im Spiel sichtbar wird.`,
 | 
				
			||||||
    code_nothingToDo: `Fertig. Es gab nichts zu tun.`,
 | 
					    code_nothingToDo: `Fertig. Es gab nichts zu tun.`,
 | 
				
			||||||
    code_gild: `Veredeln`,
 | 
					    code_gild: `Veredeln`,
 | 
				
			||||||
    code_moa: `Moa`,
 | 
					    code_moa: `Moa`,
 | 
				
			||||||
@ -134,7 +134,6 @@ dict = {
 | 
				
			|||||||
    inventory_bulkRankUpSentinels: `Alle Wächter auf Max. Rang`,
 | 
					    inventory_bulkRankUpSentinels: `Alle Wächter auf Max. Rang`,
 | 
				
			||||||
    inventory_bulkRankUpSentinelWeapons: `Alle Wächter-Waffen auf Max. Rang`,
 | 
					    inventory_bulkRankUpSentinelWeapons: `Alle Wächter-Waffen auf Max. Rang`,
 | 
				
			||||||
    inventory_bulkRankUpEvolutionProgress: `Alle Incarnon-Entwicklungsfortschritte auf Max. Rang`,
 | 
					    inventory_bulkRankUpEvolutionProgress: `Alle Incarnon-Entwicklungsfortschritte auf Max. Rang`,
 | 
				
			||||||
    inventory_maxPlexus: `Plexus auf Max. Rang`,
 | 
					 | 
				
			||||||
    inventory_removeIsNew: `Entferne Ausrufezeichen bei neuem Equipment`,
 | 
					    inventory_removeIsNew: `Entferne Ausrufezeichen bei neuem Equipment`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quests_list: `Quests`,
 | 
					    quests_list: `Quests`,
 | 
				
			||||||
@ -260,6 +259,7 @@ dict = {
 | 
				
			|||||||
    cheats_helminthUnlockAll: `Helminth vollständig aufleveln`,
 | 
					    cheats_helminthUnlockAll: `Helminth vollständig aufleveln`,
 | 
				
			||||||
    cheats_addMissingSubsumedAbilities: `Fehlende konsumierte Fähigkeiten hinzufügen`,
 | 
					    cheats_addMissingSubsumedAbilities: `Fehlende konsumierte Fähigkeiten hinzufügen`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Alle Inhärenzen auf Max. Rang`,
 | 
					    cheats_intrinsicsUnlockAll: `Alle Inhärenzen auf Max. Rang`,
 | 
				
			||||||
 | 
					    cheats_maxPlexus: `Plexus auf Max. Rang`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Unterstütztes Syndikat`,
 | 
					    cheats_changeSupportedSyndicate: `Unterstütztes Syndikat`,
 | 
				
			||||||
    cheats_changeButton: `Ändern`,
 | 
					    cheats_changeButton: `Ändern`,
 | 
				
			||||||
    cheats_markAllAsRead: `Posteingang als gelesen markieren`,
 | 
					    cheats_markAllAsRead: `Posteingang als gelesen markieren`,
 | 
				
			||||||
 | 
				
			|||||||
@ -133,7 +133,6 @@ dict = {
 | 
				
			|||||||
    inventory_bulkRankUpSentinels: `Max Rank All Sentinels`,
 | 
					    inventory_bulkRankUpSentinels: `Max Rank All Sentinels`,
 | 
				
			||||||
    inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`,
 | 
					    inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`,
 | 
				
			||||||
    inventory_bulkRankUpEvolutionProgress: `Max Rank All Incarnon Evolution Progress`,
 | 
					    inventory_bulkRankUpEvolutionProgress: `Max Rank All Incarnon Evolution Progress`,
 | 
				
			||||||
    inventory_maxPlexus: `Max Rank Plexus`,
 | 
					 | 
				
			||||||
    inventory_removeIsNew: `Remove New Equipment Exclamation Icon`,
 | 
					    inventory_removeIsNew: `Remove New Equipment Exclamation Icon`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quests_list: `Quests`,
 | 
					    quests_list: `Quests`,
 | 
				
			||||||
@ -259,6 +258,7 @@ dict = {
 | 
				
			|||||||
    cheats_helminthUnlockAll: `Fully Level Up Helminth`,
 | 
					    cheats_helminthUnlockAll: `Fully Level Up Helminth`,
 | 
				
			||||||
    cheats_addMissingSubsumedAbilities: `Add Missing Subsumed Abilities`,
 | 
					    cheats_addMissingSubsumedAbilities: `Add Missing Subsumed Abilities`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Max Rank All Intrinsics`,
 | 
					    cheats_intrinsicsUnlockAll: `Max Rank All Intrinsics`,
 | 
				
			||||||
 | 
					    cheats_maxPlexus: `Max Rank Plexus`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Supported syndicate`,
 | 
					    cheats_changeSupportedSyndicate: `Supported syndicate`,
 | 
				
			||||||
    cheats_changeButton: `Change`,
 | 
					    cheats_changeButton: `Change`,
 | 
				
			||||||
    cheats_markAllAsRead: `Mark Inbox As Read`,
 | 
					    cheats_markAllAsRead: `Mark Inbox As Read`,
 | 
				
			||||||
 | 
				
			|||||||
@ -134,7 +134,6 @@ dict = {
 | 
				
			|||||||
    inventory_bulkRankUpSentinels: `Maximizar rango de todos los centinelas`,
 | 
					    inventory_bulkRankUpSentinels: `Maximizar rango de todos los centinelas`,
 | 
				
			||||||
    inventory_bulkRankUpSentinelWeapons: `Maximizar rango de todas las armas de centinela`,
 | 
					    inventory_bulkRankUpSentinelWeapons: `Maximizar rango de todas las armas de centinela`,
 | 
				
			||||||
    inventory_bulkRankUpEvolutionProgress: `Maximizar todo el progreso de evolución Incarnon`,
 | 
					    inventory_bulkRankUpEvolutionProgress: `Maximizar todo el progreso de evolución Incarnon`,
 | 
				
			||||||
    inventory_maxPlexus: `Rango máximo de Plexus`,
 | 
					 | 
				
			||||||
    inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
 | 
					    inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quests_list: `Misiones`,
 | 
					    quests_list: `Misiones`,
 | 
				
			||||||
@ -260,6 +259,7 @@ dict = {
 | 
				
			|||||||
    cheats_helminthUnlockAll: `Subir al máximo el Helminto`,
 | 
					    cheats_helminthUnlockAll: `Subir al máximo el Helminto`,
 | 
				
			||||||
    cheats_addMissingSubsumedAbilities: `Agregar habilidades subsumidas faltantes`,
 | 
					    cheats_addMissingSubsumedAbilities: `Agregar habilidades subsumidas faltantes`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Maximizar todos los intrínsecos`,
 | 
					    cheats_intrinsicsUnlockAll: `Maximizar todos los intrínsecos`,
 | 
				
			||||||
 | 
					    cheats_maxPlexus: `Rango máximo de Plexus`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
 | 
					    cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
 | 
				
			||||||
    cheats_changeButton: `Cambiar`,
 | 
					    cheats_changeButton: `Cambiar`,
 | 
				
			||||||
    cheats_markAllAsRead: `Marcar bandeja de entrada como leída`,
 | 
					    cheats_markAllAsRead: `Marcar bandeja de entrada como leída`,
 | 
				
			||||||
 | 
				
			|||||||
@ -134,7 +134,6 @@ dict = {
 | 
				
			|||||||
    inventory_bulkRankUpSentinels: `Toutes les Sentinelles au rang max`,
 | 
					    inventory_bulkRankUpSentinels: `Toutes les Sentinelles au rang max`,
 | 
				
			||||||
    inventory_bulkRankUpSentinelWeapons: `Toutes les armes de Sentinelles au rang max`,
 | 
					    inventory_bulkRankUpSentinelWeapons: `Toutes les armes de Sentinelles au rang max`,
 | 
				
			||||||
    inventory_bulkRankUpEvolutionProgress: `Toutes les évolutions Incarnon au rang max`,
 | 
					    inventory_bulkRankUpEvolutionProgress: `Toutes les évolutions Incarnon au rang max`,
 | 
				
			||||||
    inventory_maxPlexus: `Plexus au rang max`,
 | 
					 | 
				
			||||||
    inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
 | 
					    inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quests_list: `Quêtes`,
 | 
					    quests_list: `Quêtes`,
 | 
				
			||||||
@ -260,6 +259,7 @@ dict = {
 | 
				
			|||||||
    cheats_helminthUnlockAll: `Helminth niveau max`,
 | 
					    cheats_helminthUnlockAll: `Helminth niveau max`,
 | 
				
			||||||
    cheats_addMissingSubsumedAbilities: `Ajouter les capacités subsumées manquantes`,
 | 
					    cheats_addMissingSubsumedAbilities: `Ajouter les capacités subsumées manquantes`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Inhérences niveau max`,
 | 
					    cheats_intrinsicsUnlockAll: `Inhérences niveau max`,
 | 
				
			||||||
 | 
					    cheats_maxPlexus: `Plexus au rang max`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Allégeance`,
 | 
					    cheats_changeSupportedSyndicate: `Allégeance`,
 | 
				
			||||||
    cheats_changeButton: `Changer`,
 | 
					    cheats_changeButton: `Changer`,
 | 
				
			||||||
    cheats_markAllAsRead: `Marquer la boîte de réception comme lue`,
 | 
					    cheats_markAllAsRead: `Marquer la boîte de réception comme lue`,
 | 
				
			||||||
 | 
				
			|||||||
@ -134,7 +134,6 @@ dict = {
 | 
				
			|||||||
    inventory_bulkRankUpSentinels: `Макс. ранг всех Стражей`,
 | 
					    inventory_bulkRankUpSentinels: `Макс. ранг всех Стражей`,
 | 
				
			||||||
    inventory_bulkRankUpSentinelWeapons: `Макс. ранг всего оружия Стражей`,
 | 
					    inventory_bulkRankUpSentinelWeapons: `Макс. ранг всего оружия Стражей`,
 | 
				
			||||||
    inventory_bulkRankUpEvolutionProgress: `Макс. ранг всех эволюций Инкарнонов`,
 | 
					    inventory_bulkRankUpEvolutionProgress: `Макс. ранг всех эволюций Инкарнонов`,
 | 
				
			||||||
    inventory_maxPlexus: `Макс. ранг Плексуса`,
 | 
					 | 
				
			||||||
    inventory_removeIsNew: `Удалить значок восклицательного знака нового снаряжения`,
 | 
					    inventory_removeIsNew: `Удалить значок восклицательного знака нового снаряжения`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quests_list: `Квесты`,
 | 
					    quests_list: `Квесты`,
 | 
				
			||||||
@ -260,6 +259,7 @@ dict = {
 | 
				
			|||||||
    cheats_helminthUnlockAll: `Полностью улучшить Гельминта`,
 | 
					    cheats_helminthUnlockAll: `Полностью улучшить Гельминта`,
 | 
				
			||||||
    cheats_addMissingSubsumedAbilities: `Добавить отсутствующие поглощённые способности`,
 | 
					    cheats_addMissingSubsumedAbilities: `Добавить отсутствующие поглощённые способности`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Полностью улучшить Модуляры`,
 | 
					    cheats_intrinsicsUnlockAll: `Полностью улучшить Модуляры`,
 | 
				
			||||||
 | 
					    cheats_maxPlexus: `Макс. ранг Плексуса`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
 | 
					    cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
 | 
				
			||||||
    cheats_changeButton: `Изменить`,
 | 
					    cheats_changeButton: `Изменить`,
 | 
				
			||||||
    cheats_markAllAsRead: `Пометить все входящие как прочитанные`,
 | 
					    cheats_markAllAsRead: `Пометить все входящие как прочитанные`,
 | 
				
			||||||
 | 
				
			|||||||
@ -134,7 +134,6 @@ dict = {
 | 
				
			|||||||
    inventory_bulkRankUpSentinels: `Макс. рівень всіх Вартових`,
 | 
					    inventory_bulkRankUpSentinels: `Макс. рівень всіх Вартових`,
 | 
				
			||||||
    inventory_bulkRankUpSentinelWeapons: `Макс. рівень всієї зброї Вартових`,
 | 
					    inventory_bulkRankUpSentinelWeapons: `Макс. рівень всієї зброї Вартових`,
 | 
				
			||||||
    inventory_bulkRankUpEvolutionProgress: `Макс. рівень всіх еволюцій Інкарнонів`,
 | 
					    inventory_bulkRankUpEvolutionProgress: `Макс. рівень всіх еволюцій Інкарнонів`,
 | 
				
			||||||
    inventory_maxPlexus: `Макс. рівень Плексу`,
 | 
					 | 
				
			||||||
    inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
 | 
					    inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quests_list: `Пригоди`,
 | 
					    quests_list: `Пригоди`,
 | 
				
			||||||
@ -260,6 +259,7 @@ dict = {
 | 
				
			|||||||
    cheats_helminthUnlockAll: `Повністю покращити Гельмінта`,
 | 
					    cheats_helminthUnlockAll: `Повністю покращити Гельмінта`,
 | 
				
			||||||
    cheats_addMissingSubsumedAbilities: `Додати відсутні поглинуті здібності`,
 | 
					    cheats_addMissingSubsumedAbilities: `Додати відсутні поглинуті здібності`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Повністю покращити Кваліфікації`,
 | 
					    cheats_intrinsicsUnlockAll: `Повністю покращити Кваліфікації`,
 | 
				
			||||||
 | 
					    cheats_maxPlexus: `Макс. рівень Плексу`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Підтримуваний синдикат`,
 | 
					    cheats_changeSupportedSyndicate: `Підтримуваний синдикат`,
 | 
				
			||||||
    cheats_changeButton: `Змінити`,
 | 
					    cheats_changeButton: `Змінити`,
 | 
				
			||||||
    cheats_markAllAsRead: `Помітити всі вхідні як прочитані`,
 | 
					    cheats_markAllAsRead: `Помітити всі вхідні як прочитані`,
 | 
				
			||||||
 | 
				
			|||||||
@ -134,7 +134,6 @@ dict = {
 | 
				
			|||||||
    inventory_bulkRankUpSentinels: `所有守护升满级`,
 | 
					    inventory_bulkRankUpSentinels: `所有守护升满级`,
 | 
				
			||||||
    inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`,
 | 
					    inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`,
 | 
				
			||||||
    inventory_bulkRankUpEvolutionProgress: `所有灵化之源进度最大等级`,
 | 
					    inventory_bulkRankUpEvolutionProgress: `所有灵化之源进度最大等级`,
 | 
				
			||||||
    inventory_maxPlexus: `最大深控等级`,
 | 
					 | 
				
			||||||
    inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
 | 
					    inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quests_list: `系列任务`,
 | 
					    quests_list: `系列任务`,
 | 
				
			||||||
@ -260,6 +259,7 @@ dict = {
 | 
				
			|||||||
    cheats_helminthUnlockAll: `完全升级Helminth`,
 | 
					    cheats_helminthUnlockAll: `完全升级Helminth`,
 | 
				
			||||||
    cheats_addMissingSubsumedAbilities: `添加Helminth未汲取的战甲技能`,
 | 
					    cheats_addMissingSubsumedAbilities: `添加Helminth未汲取的战甲技能`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `所有内源之力最大等级`,
 | 
					    cheats_intrinsicsUnlockAll: `所有内源之力最大等级`,
 | 
				
			||||||
 | 
					    cheats_maxPlexus: `最大深控等级`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `支持的集团`,
 | 
					    cheats_changeSupportedSyndicate: `支持的集团`,
 | 
				
			||||||
    cheats_changeButton: `更改`,
 | 
					    cheats_changeButton: `更改`,
 | 
				
			||||||
    cheats_markAllAsRead: `收件箱全部标记为已读`,
 | 
					    cheats_markAllAsRead: `收件箱全部标记为已读`,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user