forked from OpenWF/SpaceNinjaServer
		
	feat: the circuit (#2039)
Closes #1965 Closes #2041 Reviewed-on: OpenWF/SpaceNinjaServer#2039 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									c83e732b88
								
							
						
					
					
						commit
						9d4bce852e
					
				@ -1,60 +1,529 @@
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { getInventory } from "@/src/services/inventoryService";
 | 
			
		||||
import { combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { TEndlessXpCategory } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { IEndlessXpReward, IInventoryClient, TEndlessXpCategory } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { logger } from "@/src/utils/logger";
 | 
			
		||||
import { ExportRewards, ICountedStoreItem } from "warframe-public-export-plus";
 | 
			
		||||
import { getRandomElement } from "@/src/services/rngService";
 | 
			
		||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
 | 
			
		||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
			
		||||
 | 
			
		||||
export const endlessXpController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const inventory = await getInventory(accountId);
 | 
			
		||||
    const payload = getJSONfromString<IEndlessXpRequest>(String(req.body));
 | 
			
		||||
 | 
			
		||||
    if (payload.Mode == "r") {
 | 
			
		||||
        const inventory = await getInventory(accountId, "EndlessXP");
 | 
			
		||||
        inventory.EndlessXP ??= [];
 | 
			
		||||
    const entry = inventory.EndlessXP.find(x => x.Category == payload.Category);
 | 
			
		||||
    if (entry) {
 | 
			
		||||
        entry.Choices = payload.Choices;
 | 
			
		||||
    } else {
 | 
			
		||||
        inventory.EndlessXP.push({
 | 
			
		||||
            Category: payload.Category,
 | 
			
		||||
            Choices: payload.Choices
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    await inventory.save();
 | 
			
		||||
 | 
			
		||||
    res.json({
 | 
			
		||||
        NewProgress: {
 | 
			
		||||
        let entry = inventory.EndlessXP.find(x => x.Category == payload.Category);
 | 
			
		||||
        if (!entry) {
 | 
			
		||||
            entry = {
 | 
			
		||||
                Category: payload.Category,
 | 
			
		||||
                Earn: 0,
 | 
			
		||||
                Claim: 0,
 | 
			
		||||
            BonusAvailable: {
 | 
			
		||||
                $date: {
 | 
			
		||||
                    $numberLong: "9999999999999"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            Expiry: {
 | 
			
		||||
                $date: {
 | 
			
		||||
                    $numberLong: "9999999999999"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
                Choices: payload.Choices,
 | 
			
		||||
            PendingRewards: [
 | 
			
		||||
                PendingRewards: []
 | 
			
		||||
            };
 | 
			
		||||
            inventory.EndlessXP.push(entry);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const weekStart = 1734307200_000 + Math.trunc((Date.now() - 1734307200_000) / 604800000) * 604800000;
 | 
			
		||||
        const weekEnd = weekStart + 604800000;
 | 
			
		||||
 | 
			
		||||
        entry.Earn = 0;
 | 
			
		||||
        entry.Claim = 0;
 | 
			
		||||
        entry.BonusAvailable = new Date(weekStart);
 | 
			
		||||
        entry.Expiry = new Date(weekEnd);
 | 
			
		||||
        entry.Choices = payload.Choices;
 | 
			
		||||
        entry.PendingRewards =
 | 
			
		||||
            payload.Category == "EXC_HARD"
 | 
			
		||||
                ? generateHardModeRewards(payload.Choices)
 | 
			
		||||
                : generateNormalModeRewards(payload.Choices);
 | 
			
		||||
 | 
			
		||||
        await inventory.save();
 | 
			
		||||
        res.json({
 | 
			
		||||
            NewProgress: inventory.toJSON<IInventoryClient>().EndlessXP!.find(x => x.Category == payload.Category)!
 | 
			
		||||
        });
 | 
			
		||||
    } else if (payload.Mode == "c") {
 | 
			
		||||
        const inventory = await getInventory(accountId);
 | 
			
		||||
        const entry = inventory.EndlessXP!.find(x => x.Category == payload.Category)!;
 | 
			
		||||
        const inventoryChanges: IInventoryChanges = {};
 | 
			
		||||
        for (const reward of entry.PendingRewards) {
 | 
			
		||||
            if (entry.Claim < reward.RequiredTotalXp && reward.RequiredTotalXp <= entry.Earn) {
 | 
			
		||||
                combineInventoryChanges(
 | 
			
		||||
                    inventoryChanges,
 | 
			
		||||
                    (
 | 
			
		||||
                        await handleStoreItemAcquisition(
 | 
			
		||||
                            reward.Rewards[0].StoreItem,
 | 
			
		||||
                            inventory,
 | 
			
		||||
                            reward.Rewards[0].ItemCount
 | 
			
		||||
                        )
 | 
			
		||||
                    ).InventoryChanges
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        entry.Claim = entry.Earn;
 | 
			
		||||
        await inventory.save();
 | 
			
		||||
        res.json({
 | 
			
		||||
            InventoryChanges: inventoryChanges,
 | 
			
		||||
            ClaimedXp: entry.Claim
 | 
			
		||||
        });
 | 
			
		||||
    } else {
 | 
			
		||||
        logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
 | 
			
		||||
        throw new Error(`unexpected endlessXp mode: ${payload.Mode}`);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type IEndlessXpRequest =
 | 
			
		||||
    | {
 | 
			
		||||
          Mode: "r";
 | 
			
		||||
          Category: TEndlessXpCategory;
 | 
			
		||||
          Choices: string[];
 | 
			
		||||
      }
 | 
			
		||||
    | {
 | 
			
		||||
          Mode: "c" | "something else";
 | 
			
		||||
          Category: TEndlessXpCategory;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
const generateRandomRewards = (deckName: string): ICountedStoreItem[] => {
 | 
			
		||||
    const reward = getRandomElement(ExportRewards[deckName][0])!;
 | 
			
		||||
    return [
 | 
			
		||||
        {
 | 
			
		||||
            StoreItem: reward.type,
 | 
			
		||||
            ItemCount: reward.itemCount
 | 
			
		||||
        }
 | 
			
		||||
    ];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const normalModeChosenRewards: Record<string, string[]> = {
 | 
			
		||||
    Excalibur: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/ExcaliburHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/ExcaliburChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Excalibur/RadialJavelinAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/ExcaliburSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/ExcaliburBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Trinity: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TrinityHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TrinityChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Trinity/EnergyVampireAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TrinitySystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TrinityBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Ember: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/EmberHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/EmberChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Ember/WorldOnFireAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/EmberSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/EmberBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Loki: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/LOKIHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/LOKIChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Loki/InvisibilityAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/LOKISystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/LOKIBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Mag: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MagHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MagChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Mag/CrushAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MagSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MagBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Rhino: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RhinoHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RhinoChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Rhino/RhinoChargeAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RhinoSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RhinoBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Ash: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/AshHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/AshChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Ninja/GlaiveAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/AshSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/AshBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Frost: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/FrostHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/FrostChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Frost/IceShieldAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/FrostSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/FrostBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Nyx: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NyxHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NyxChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Jade/SelfBulletAttractorAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NyxSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NyxBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Saryn: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/SarynHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/SarynChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Saryn/PoisonAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/SarynSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/SarynBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Vauban: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TrapperHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TrapperChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Trapper/LevTrapAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TrapperSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TrapperBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Nova: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NovaHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NovaChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/AntiMatter/MolecularPrimeAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NovaSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NovaBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Nekros: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NecroHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NecroChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Necro/CloneTheDeadAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NecroSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NecroBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Valkyr: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/BerserkerHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/BerserkerChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Berserker/IntimidateAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/BerserkerSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/BerserkerBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Oberon: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PaladinHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PaladinChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Paladin/RegenerationAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PaladinSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PaladinBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Hydroid: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/HydroidHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/HydroidChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Pirate/CannonBarrageAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/HydroidSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/HydroidBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Mirage: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/HarlequinHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/HarlequinChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Harlequin/LightAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/HarlequinSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/HarlequinBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Limbo: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MagicianHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MagicianChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Magician/TearInSpaceAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MagicianSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MagicianBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Mesa: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GunslingerHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GunslingerChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Cowgirl/GunFuPvPAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GunslingerSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GunslingerBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Chroma: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/ChromaHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/ChromaChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Dragon/DragonLuckAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/ChromaSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/ChromaBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Atlas: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/BrawlerHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/BrawlerChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Brawler/BrawlerPassiveAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/BrawlerSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/BrawlerBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Ivara: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RangerHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RangerChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Ranger/RangerStealAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RangerSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RangerBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Inaros: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MummyHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MummyChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Sandman/SandmanSwarmAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MummySystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/MummyBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Titania: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/FairyHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/FairyChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Fairy/FairyFlightAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/FairySystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/FairyBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Nidus: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NidusHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NidusChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Infestation/InfestPodsAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NidusSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/NidusBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Octavia: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/OctaviaHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/OctaviaChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Bard/BardCharmAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/OctaviaSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/OctaviaBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Harrow: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PriestHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PriestChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Priest/PriestPactAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PriestSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PriestBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Gara: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GlassHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GlassChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Glass/GlassFragmentAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GlassSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GlassBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Khora: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/KhoraHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/KhoraChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Khora/KhoraCrackAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/KhoraSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/KhoraBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Revenant: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RevenantHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RevenantChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Revenant/RevenantMarkAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RevenantSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/RevenantBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Garuda: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GarudaHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GarudaChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Garuda/GarudaUnstoppableAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GarudaSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/GarudaBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Baruuk: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PacifistHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PacifistChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/Pacifist/PacifistFistAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PacifistSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/PacifistBlueprint"
 | 
			
		||||
    ],
 | 
			
		||||
    Hildryn: [
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/IronframeHelmetBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/IronframeChassisBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Powersuits/IronFrame/IronFrameStripAugmentCard",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/IronframeSystemsBlueprint",
 | 
			
		||||
        "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/IronframeBlueprint"
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const generateNormalModeRewards = (choices: string[]): IEndlessXpReward[] => {
 | 
			
		||||
    const choiceRewards = normalModeChosenRewards[choices[0]];
 | 
			
		||||
    return [
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 190,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessNormalSilverRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 400,
 | 
			
		||||
            Rewards: [
 | 
			
		||||
                {
 | 
			
		||||
                            StoreItem: "/Lotus/StoreItems/Upgrades/Mods/Aura/PlayerHealthAuraMod",
 | 
			
		||||
                    StoreItem: choiceRewards[0],
 | 
			
		||||
                    ItemCount: 1
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 630,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessNormalSilverRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 890,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessNormalMODRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 1190,
 | 
			
		||||
            Rewards: [
 | 
			
		||||
                {
 | 
			
		||||
                    StoreItem: choiceRewards[1],
 | 
			
		||||
                    ItemCount: 1
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 1540,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessNormalGoldRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 1950,
 | 
			
		||||
            Rewards: [
 | 
			
		||||
                {
 | 
			
		||||
                    StoreItem: choiceRewards[2],
 | 
			
		||||
                    ItemCount: 1
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 2430,
 | 
			
		||||
            Rewards: [
 | 
			
		||||
                {
 | 
			
		||||
                    StoreItem: choiceRewards[3],
 | 
			
		||||
                    ItemCount: 1
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 2990,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessNormalArcaneRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 3640,
 | 
			
		||||
            Rewards: [
 | 
			
		||||
                {
 | 
			
		||||
                    StoreItem: choiceRewards[4],
 | 
			
		||||
                    ItemCount: 1
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
                // ...
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    ];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface IEndlessXpRequest {
 | 
			
		||||
    Mode: string; // "r"
 | 
			
		||||
    Category: TEndlessXpCategory;
 | 
			
		||||
    Choices: string[];
 | 
			
		||||
}
 | 
			
		||||
const hardModeChosenRewards: Record<string, string> = {
 | 
			
		||||
    Braton: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/BratonIncarnonUnlocker",
 | 
			
		||||
    Lato: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/LatoIncarnonUnlocker",
 | 
			
		||||
    Skana: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/SkanaIncarnonUnlocker",
 | 
			
		||||
    Paris: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/ParisIncarnonUnlocker",
 | 
			
		||||
    Kunai: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/KunaiIncarnonUnlocker",
 | 
			
		||||
    Boar: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/BoarIncarnonUnlocker",
 | 
			
		||||
    Gammacor: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/GammacorIncarnonUnlocker",
 | 
			
		||||
    Anku: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/AnkuIncarnonUnlocker",
 | 
			
		||||
    Gorgon: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/GorgonIncarnonUnlocker",
 | 
			
		||||
    Angstrum: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/AngstrumIncarnonUnlocker",
 | 
			
		||||
    Bo: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/BoIncarnonUnlocker",
 | 
			
		||||
    Latron: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/LatronIncarnonUnlocker",
 | 
			
		||||
    Furis: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/FurisIncarnonUnlocker",
 | 
			
		||||
    Furax: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/FuraxIncarnonUnlocker",
 | 
			
		||||
    Strun: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/StrunIncarnonUnlocker",
 | 
			
		||||
    Lex: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/LexIncarnonUnlocker",
 | 
			
		||||
    Magistar: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/MagistarIncarnonUnlocker",
 | 
			
		||||
    Boltor: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/BoltorIncarnonUnlocker",
 | 
			
		||||
    Bronco: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/BroncoIncarnonUnlocker",
 | 
			
		||||
    CeramicDagger: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/CeramicDaggerIncarnonUnlocker",
 | 
			
		||||
    Torid: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/ToridIncarnonUnlocker",
 | 
			
		||||
    DualToxocyst: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/DualToxocystIncarnonUnlocker",
 | 
			
		||||
    DualIchor: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/DualIchorIncarnonUnlocker",
 | 
			
		||||
    Miter: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/MiterIncarnonUnlocker",
 | 
			
		||||
    Atomos: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/AtomosIncarnonUnlocker",
 | 
			
		||||
    AckAndBrunt: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/AckAndBruntIncarnonUnlocker",
 | 
			
		||||
    Soma: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/SomaIncarnonUnlocker",
 | 
			
		||||
    Vasto: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/VastoIncarnonUnlocker",
 | 
			
		||||
    NamiSolo: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/NamiSoloIncarnonUnlocker",
 | 
			
		||||
    Burston: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/BurstonIncarnonUnlocker",
 | 
			
		||||
    Zylok: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/ZylokIncarnonUnlocker",
 | 
			
		||||
    Sibear: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/SibearIncarnonUnlocker",
 | 
			
		||||
    Dread: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/DreadIncarnonUnlocker",
 | 
			
		||||
    Despair: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/DespairIncarnonUnlocker",
 | 
			
		||||
    Hate: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/HateIncarnonUnlocker",
 | 
			
		||||
    Dera: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/DeraIncarnonUnlocker",
 | 
			
		||||
    Cestra: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/CestraIncarnonUnlocker",
 | 
			
		||||
    Okina: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Melee/OkinaIncarnonUnlocker",
 | 
			
		||||
    Sybaris: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Primary/SybarisIncarnonUnlocker",
 | 
			
		||||
    Sicarus: "/Lotus/StoreItems/Types/Items/MiscItems/IncarnonAdapters/Secondary/SicarusIncarnonUnlocker",
 | 
			
		||||
    RivenPrimary: "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawRifleRandomMod",
 | 
			
		||||
    RivenSecondary: "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawPistolRandomMod",
 | 
			
		||||
    RivenMelee: "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawMeleeRandomMod",
 | 
			
		||||
    Kuva: "/Lotus/Types/Game/DuviriEndless/CircuitSteelPathBIGKuvaReward"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const generateHardModeRewards = (choices: string[]): IEndlessXpReward[] => {
 | 
			
		||||
    return [
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 285,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessSteelPathSilverRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 600,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessSteelPathArcaneRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 945,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessSteelPathSilverRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 1335,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessSteelPathSilverRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 1785,
 | 
			
		||||
            Rewards: [
 | 
			
		||||
                {
 | 
			
		||||
                    StoreItem: hardModeChosenRewards[choices[0]],
 | 
			
		||||
                    ItemCount: 1
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 2310,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessSteelPathGoldRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 2925,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessSteelPathGoldRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 3645,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessSteelPathArcaneRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 4485,
 | 
			
		||||
            Rewards: generateRandomRewards(
 | 
			
		||||
                "/Lotus/Types/Game/MissionDecks/DuviriEndlessCircuitRewards/DuviriEndlessSteelPathSteelEssenceRewards"
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            RequiredTotalXp: 5460,
 | 
			
		||||
            Rewards: [
 | 
			
		||||
                {
 | 
			
		||||
                    StoreItem: hardModeChosenRewards[choices[1]],
 | 
			
		||||
                    ItemCount: 1
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,8 @@ import {
 | 
			
		||||
    IPeriodicMissionCompletionResponse,
 | 
			
		||||
    ILoreFragmentScan,
 | 
			
		||||
    IEvolutionProgress,
 | 
			
		||||
    IEndlessXpProgress,
 | 
			
		||||
    IEndlessXpProgressDatabase,
 | 
			
		||||
    IEndlessXpProgressClient,
 | 
			
		||||
    ICrewShipCustomization,
 | 
			
		||||
    ICrewShipWeapon,
 | 
			
		||||
    ICrewShipWeaponEmplacements,
 | 
			
		||||
@ -97,7 +98,8 @@ import {
 | 
			
		||||
    IInvasionProgressClient,
 | 
			
		||||
    IAccolades,
 | 
			
		||||
    IHubNpcCustomization,
 | 
			
		||||
    ILotusCustomization
 | 
			
		||||
    ILotusCustomization,
 | 
			
		||||
    IEndlessXpReward
 | 
			
		||||
} from "../../types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { IOid } from "../../types/commonTypes";
 | 
			
		||||
import {
 | 
			
		||||
@ -112,6 +114,7 @@ import {
 | 
			
		||||
} from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
			
		||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
 | 
			
		||||
import { EquipmentSelectionSchema, oidSchema } from "./loadoutModel";
 | 
			
		||||
import { ICountedStoreItem } from "warframe-public-export-plus";
 | 
			
		||||
 | 
			
		||||
export const typeCountSchema = new Schema<ITypeCount>({ ItemType: String, ItemCount: Number }, { _id: false });
 | 
			
		||||
 | 
			
		||||
@ -810,14 +813,48 @@ const evolutionProgressSchema = new Schema<IEvolutionProgress>(
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const endlessXpProgressSchema = new Schema<IEndlessXpProgress>(
 | 
			
		||||
const countedStoreItemSchema = new Schema<ICountedStoreItem>(
 | 
			
		||||
    {
 | 
			
		||||
        Category: String,
 | 
			
		||||
        Choices: [String]
 | 
			
		||||
        StoreItem: String,
 | 
			
		||||
        ItemCount: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const endlessXpRewardSchema = new Schema<IEndlessXpReward>(
 | 
			
		||||
    {
 | 
			
		||||
        RequiredTotalXp: Number,
 | 
			
		||||
        Rewards: [countedStoreItemSchema]
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const endlessXpProgressSchema = new Schema<IEndlessXpProgressDatabase>(
 | 
			
		||||
    {
 | 
			
		||||
        Category: { type: String, required: true },
 | 
			
		||||
        Earn: { type: Number, default: 0 },
 | 
			
		||||
        Claim: { type: Number, default: 0 },
 | 
			
		||||
        BonusAvailable: Date,
 | 
			
		||||
        Expiry: Date,
 | 
			
		||||
        Choices: { type: [String], required: true },
 | 
			
		||||
        PendingRewards: { type: [endlessXpRewardSchema], default: [] }
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
endlessXpProgressSchema.set("toJSON", {
 | 
			
		||||
    transform(_doc, ret) {
 | 
			
		||||
        const db = ret as IEndlessXpProgressDatabase;
 | 
			
		||||
        const client = ret as IEndlessXpProgressClient;
 | 
			
		||||
 | 
			
		||||
        if (db.BonusAvailable) {
 | 
			
		||||
            client.BonusAvailable = toMongoDate(db.BonusAvailable);
 | 
			
		||||
        }
 | 
			
		||||
        if (db.Expiry) {
 | 
			
		||||
            client.Expiry = toMongoDate(db.Expiry);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
const crewShipWeaponEmplacementsSchema = new Schema<ICrewShipWeaponEmplacements>(
 | 
			
		||||
    {
 | 
			
		||||
        PRIMARY_A: EquipmentSelectionSchema,
 | 
			
		||||
 | 
			
		||||
@ -377,9 +377,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
 | 
			
		||||
            db[key] = client[key];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (client.EndlessXP !== undefined) {
 | 
			
		||||
        db.EndlessXP = client.EndlessXP;
 | 
			
		||||
    }
 | 
			
		||||
    if (client.SongChallenges !== undefined) {
 | 
			
		||||
        db.SongChallenges = client.SongChallenges;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ import {
 | 
			
		||||
import { IMissionInventoryUpdateRequest, IRewardInfo } from "../types/requestTypes";
 | 
			
		||||
import { logger } from "@/src/utils/logger";
 | 
			
		||||
import { IRngResult, SRng, getRandomElement, getRandomReward } from "@/src/services/rngService";
 | 
			
		||||
import { equipmentKeys, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { equipmentKeys, IMission, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import {
 | 
			
		||||
    addBooster,
 | 
			
		||||
    addChallenges,
 | 
			
		||||
@ -841,7 +841,13 @@ export const addMissionRewards = async (
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //TODO: check double reward merging
 | 
			
		||||
    const MissionRewards: IMissionReward[] = getRandomMissionDrops(inventory, rewardInfo, wagerTier, firstCompletion);
 | 
			
		||||
    const MissionRewards: IMissionReward[] = getRandomMissionDrops(
 | 
			
		||||
        inventory,
 | 
			
		||||
        rewardInfo,
 | 
			
		||||
        missions,
 | 
			
		||||
        wagerTier,
 | 
			
		||||
        firstCompletion
 | 
			
		||||
    );
 | 
			
		||||
    logger.debug("random mission drops:", MissionRewards);
 | 
			
		||||
    const inventoryChanges: IInventoryChanges = {};
 | 
			
		||||
    const AffiliationMods: IAffiliationMods[] = [];
 | 
			
		||||
@ -1290,6 +1296,7 @@ function getLevelCreditRewards(node: IRegion): number {
 | 
			
		||||
function getRandomMissionDrops(
 | 
			
		||||
    inventory: TInventoryDatabaseDocument,
 | 
			
		||||
    RewardInfo: IRewardInfo,
 | 
			
		||||
    mission: IMission | undefined,
 | 
			
		||||
    tierOverride: number | undefined,
 | 
			
		||||
    firstCompletion: boolean
 | 
			
		||||
): IMissionReward[] {
 | 
			
		||||
@ -1531,6 +1538,35 @@ function getRandomMissionDrops(
 | 
			
		||||
                logger.error(`Unknown syndicate or tier: ${RewardInfo.challengeMissionId}`);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (RewardInfo.node == "SolNode238") {
 | 
			
		||||
                // The Circuit
 | 
			
		||||
                const category = mission?.Tier == 1 ? "EXC_HARD" : "EXC_NORMAL";
 | 
			
		||||
                const progress = inventory.EndlessXP?.find(x => x.Category == category);
 | 
			
		||||
                if (progress) {
 | 
			
		||||
                    // https://wiki.warframe.com/w/The%20Circuit#Tiers_and_Weekly_Rewards
 | 
			
		||||
                    const roundsCompleted = RewardInfo.rewardQualifications?.length || 0;
 | 
			
		||||
                    if (roundsCompleted >= 1) {
 | 
			
		||||
                        progress.Earn += 100;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (roundsCompleted >= 2) {
 | 
			
		||||
                        progress.Earn += 110;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (roundsCompleted >= 3) {
 | 
			
		||||
                        progress.Earn += 125;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (roundsCompleted >= 4) {
 | 
			
		||||
                        progress.Earn += 145;
 | 
			
		||||
                        if (progress.BonusAvailable && progress.BonusAvailable.getTime() <= Date.now()) {
 | 
			
		||||
                            progress.Earn += 50;
 | 
			
		||||
                            progress.BonusAvailable = new Date(Date.now() + 24 * 3600_000); // TOVERIFY
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (roundsCompleted >= 5) {
 | 
			
		||||
                        progress.Earn += (roundsCompleted - 4) * 170;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                tierOverride = 0;
 | 
			
		||||
            }
 | 
			
		||||
            rotations = getRotations(RewardInfo, tierOverride);
 | 
			
		||||
        }
 | 
			
		||||
        if (rewardManifests.length != 0) {
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ import {
 | 
			
		||||
} from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
			
		||||
import { IFingerprintStat, RivenFingerprint } from "@/src/helpers/rivenHelper";
 | 
			
		||||
import { IOrbiter } from "../personalRoomsTypes";
 | 
			
		||||
import { ICountedStoreItem } from "warframe-public-export-plus";
 | 
			
		||||
 | 
			
		||||
export type InventoryDatabaseEquipment = {
 | 
			
		||||
    [_ in TEquipmentKey]: IEquipmentDatabase[];
 | 
			
		||||
@ -54,6 +55,7 @@ export interface IInventoryDatabase
 | 
			
		||||
            | "CrewMembers"
 | 
			
		||||
            | "QualifyingInvasions"
 | 
			
		||||
            | "LastInventorySync"
 | 
			
		||||
            | "EndlessXP"
 | 
			
		||||
            | TEquipmentKey
 | 
			
		||||
        >,
 | 
			
		||||
        InventoryDatabaseEquipment {
 | 
			
		||||
@ -92,6 +94,7 @@ export interface IInventoryDatabase
 | 
			
		||||
    CrewMembers: ICrewMemberDatabase[];
 | 
			
		||||
    QualifyingInvasions: IInvasionProgressDatabase[];
 | 
			
		||||
    LastInventorySync?: Types.ObjectId;
 | 
			
		||||
    EndlessXP?: IEndlessXpProgressDatabase[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IQuestKeyDatabase {
 | 
			
		||||
@ -356,7 +359,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
			
		||||
    PendingCoupon?: IPendingCouponClient;
 | 
			
		||||
    Harvestable: boolean;
 | 
			
		||||
    DeathSquadable: boolean;
 | 
			
		||||
    EndlessXP?: IEndlessXpProgress[];
 | 
			
		||||
    EndlessXP?: IEndlessXpProgressClient[];
 | 
			
		||||
    DialogueHistory?: IDialogueHistoryClient;
 | 
			
		||||
    CalendarProgress?: ICalendarProgress;
 | 
			
		||||
    SongChallenges?: ISongChallenge[];
 | 
			
		||||
@ -1143,9 +1146,24 @@ export interface IEvolutionProgress {
 | 
			
		||||
 | 
			
		||||
export type TEndlessXpCategory = "EXC_NORMAL" | "EXC_HARD";
 | 
			
		||||
 | 
			
		||||
export interface IEndlessXpProgress {
 | 
			
		||||
export interface IEndlessXpProgressDatabase {
 | 
			
		||||
    Category: TEndlessXpCategory;
 | 
			
		||||
    Earn: number;
 | 
			
		||||
    Claim: number;
 | 
			
		||||
    BonusAvailable?: Date;
 | 
			
		||||
    Expiry?: Date;
 | 
			
		||||
    Choices: string[];
 | 
			
		||||
    PendingRewards: IEndlessXpReward[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IEndlessXpProgressClient extends Omit<IEndlessXpProgressDatabase, "BonusAvailable" | "Expiry"> {
 | 
			
		||||
    BonusAvailable?: IMongoDate;
 | 
			
		||||
    Expiry?: IMongoDate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IEndlessXpReward {
 | 
			
		||||
    RequiredTotalXp: number;
 | 
			
		||||
    Rewards: ICountedStoreItem[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IDialogueHistoryClient {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user