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

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
UTF-8
import { getAccountForRequest, getBuildLabel } from "../../services/loginService.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { logger } from "../../utils/logger.ts";
import type { RequestHandler } from "express";
import { getRecipe } from "../../services/itemDataService.ts";
import {
    addItem,
    addKubrowPet,
    addMiscItem,
    freeUpSlot,
    getInventory,
    updateCurrency
} from "../../services/inventoryService.ts";
import { unixTimesInMs } from "../../constants/timeConstants.ts";
import { Types } from "mongoose";
import type { ISpectreLoadout } from "../../types/inventoryTypes/inventoryTypes.ts";
import { eInventorySlot } from "../../types/inventoryTypes/inventoryTypes.ts";
import { fromOid, toOid, U5ToModernRecipes, version_compare } from "../../helpers/inventoryHelpers.ts";
import { ExportWeapons } from "warframe-public-export-plus";
import { getRandomElement } from "../../services/rngService.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import gameToBuildVersion from "../../constants/gameToBuildVersion.ts";

interface IStartRecipeRequest {
    RecipeName: string;
    Ids: string[];
}

export const startRecipeController: RequestHandler = async (req, res) => {
    const startRecipeRequest = getJSONfromString<IStartRecipeRequest>(String(req.body));
    logger.debug("StartRecipe Request", { startRecipeRequest });

    const account = await getAccountForRequest(req);
    const buildLabel = getBuildLabel(req, account);

    let recipeName = startRecipeRequest.RecipeName;
    if (req.query.recipeName) recipeName = String(req.query.recipeName); // U8
    if (version_compare(buildLabel, gameToBuildVersion["7.3.0"]) < 0) {
        const modernItemType = U5ToModernRecipes[recipeName];
        if (modernItemType) recipeName = modernItemType;
    }
    const recipe = getRecipe(recipeName, buildLabel);

    if (!recipe) {
        throw new Error(`unknown recipe ${recipeName}`);
    }

    const inventory = await getInventory(account._id, undefined);
    try {
        updateCurrency(inventory, recipe.buildPrice, false);
    } catch (e) {
        logger.error((e as Error).message);
        res.status(400).json(1); // "Insufficient credits"
        return;
    }

    const pr =
        inventory.PendingRecipes[
            inventory.PendingRecipes.push({
                ItemType: recipeName,
                CompletionDate: new Date(Date.now() + recipe.buildTime * unixTimesInMs.second),
                _id: new Types.ObjectId()
            }) - 1
        ];

    for (let i = 0; i != recipe.ingredients.length; ++i) {
        if (startRecipeRequest.Ids[i] && startRecipeRequest.Ids[i][0] != "/") {
            if (
                recipe.ingredients[i].ItemType == "/Lotus/Types/Game/KubrowPet/Eggs/KubrowEgg" ||
                recipe.ingredients[i].ItemType == "/Lotus/Types/Game/KubrowPet/Eggs/KubrowPetEggItem"
            ) {
                addMiscItem(inventory, "/Lotus/Types/Game/KubrowPet/Eggs/KubrowEgg", -1);
            } else if (recipe.ingredients[i].ItemType.startsWith("/Lotus/Upgrades/")) {
                const index = inventory.Upgrades.findIndex(x => x._id.equals(startRecipeRequest.Ids[i]));
                if (index != -1) {
                    inventory.Upgrades.splice(index, 1);
                }
            } else {
                const category = ExportWeapons[recipe.ingredients[i].ItemType].productCategory;
                if (category != "LongGuns" && category != "Pistols" && category != "Melee") {
                    throw new Error(`unexpected equipment ingredient type: ${category}`);
                }
                const equipmentIndex = inventory[category].findIndex(x => x._id.equals(startRecipeRequest.Ids[i]));
                if (equipmentIndex == -1) {
                    throw new Error(`could not find equipment item to use for recipe`);
                }
                pr[category] ??= [];
                pr[category].push(inventory[category][equipmentIndex]);
                inventory[category].splice(equipmentIndex, 1);
                freeUpSlot(inventory, eInventorySlot.WEAPONS);
            }
        } else {
            const itemType = recipe.ingredients[i].ItemType;
            const itemCount = recipe.ingredients[i].ItemCount;
            await addItem(inventory, itemType, itemCount * -1, undefined, undefined, undefined, true);
        }
    }

    let inventoryChanges: IInventoryChanges | undefined;
    if (recipe.secretIngredientAction == "SIA_CREATE_KUBROW") {
        const infestedSuitId = startRecipeRequest.Ids[startRecipeRequest.Ids.length - 1];
        const infestedSuit = infestedSuitId.length == 24 ? inventory.Suits.id(infestedSuitId) : undefined;
        const resultSuitType = infestedSuit
            ? "/Lotus/Types/Game/KubrowPet/ChargerKubrowPetPowerSuit"
            : getRandomElement(recipe.secretIngredients!)!.ItemType;
        if (infestedSuit) {
            infestedSuit.InfestationDate = new Date();
        }
        inventoryChanges = addKubrowPet(inventory, resultSuitType, undefined, false, {}, buildLabel);
        pr.KubrowPet = new Types.ObjectId(fromOid(inventoryChanges.KubrowPets![0].ItemId));
    } else if (recipe.secretIngredientAction == "SIA_DISTILL_PRINT") {
        pr.KubrowPet = new Types.ObjectId(startRecipeRequest.Ids[recipe.ingredients.length]);
        const pet = inventory.KubrowPets.id(pr.KubrowPet)!;
        pet.Details.PrintsRemaining -= 1;
    } else if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") {
        const spectreLoadout: ISpectreLoadout = {
            ItemType: recipe.resultType,
            Suits: "",
            LongGuns: "",
            Pistols: "",
            Melee: ""
        };
        for (
            let secretIngredientsIndex = 0;
            secretIngredientsIndex != recipe.secretIngredients!.length;
            ++secretIngredientsIndex
        ) {
            const type = recipe.secretIngredients![secretIngredientsIndex].ItemType;
            const oid = startRecipeRequest.Ids[recipe.ingredients.length + secretIngredientsIndex];
            if (oid == "ffffffffffffffffffffffff") {
                // user chose to preserve the active loadout
                break;
            }
            if (type == "/Lotus/Types/Game/PowerSuits/PlayerPowerSuit") {
                const item = inventory.Suits.id(oid)!;
                spectreLoadout.Suits = item.ItemType;
            } else if (type == "/Lotus/Weapons/Tenno/Pistol/LotusPistol") {
                const item = inventory.Pistols.id(oid)!;
                spectreLoadout.Pistols = item.ItemType;
                spectreLoadout.PistolsModularParts = item.ModularParts;
            } else if (type == "/Lotus/Weapons/Tenno/LotusLongGun") {
                const item = inventory.LongGuns.id(oid)!;
                spectreLoadout.LongGuns = item.ItemType;
                spectreLoadout.LongGunsModularParts = item.ModularParts;
            } else {
                console.assert(type == "/Lotus/Types/Game/LotusMeleeWeapon");
                const item = inventory.Melee.id(oid)!;
                spectreLoadout.Melee = item.ItemType;
                spectreLoadout.MeleeModularParts = item.ModularParts;
            }
        }
        if (
            spectreLoadout.Suits != "" &&
            spectreLoadout.LongGuns != "" &&
            spectreLoadout.Pistols != "" &&
            spectreLoadout.Melee != ""
        ) {
            inventory.PendingSpectreLoadouts ??= [];
            const existingIndex = inventory.PendingSpectreLoadouts.findIndex(x => x.ItemType == recipe.resultType);
            if (existingIndex != -1) {
                inventory.PendingSpectreLoadouts.splice(existingIndex, 1);
            }
            inventory.PendingSpectreLoadouts.push(spectreLoadout);
            logger.debug("pending spectre loadout", spectreLoadout);
        }
    } else if (recipe.secretIngredientAction == "SIA_UNBRAND") {
        pr.SuitToUnbrand = new Types.ObjectId(startRecipeRequest.Ids[recipe.ingredients.length + 0]);
    }

    await inventory.save();

    res.json({ RecipeId: toOid(pr._id), InventoryChanges: inventoryChanges });
};