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 type { RequestHandler } from "express";
import { fromOid, version_compare } from "../../helpers/inventoryHelpers.ts";
import type { IUpgradesRequest, IUpgradesRequestLegacy } from "../../types/requestTypes.ts";
import type { TArtifactPolarity, IAbilityOverride } from "../../types/inventoryTypes/commonInventoryTypes.ts";
import type { IInventoryClient, IMiscItem } from "../../types/inventoryTypes/inventoryTypes.ts";
import { getAccountForRequest, getBuildLabel } from "../../services/loginService.ts";
import {
    addMiscItems,
    addMods,
    addRecipes,
    getInventory2,
    updateCredits,
    updatePlatinum
} from "../../services/inventoryService.ts";
import type { IInventoryChanges } from "../../types/purchaseTypes.ts";
import { addInfestedFoundryXP, applyCheatsToInfestedFoundry } from "../../services/infestedFoundryService.ts";
import { sendWsBroadcastTo, sendWsBroadcastToWebui } from "../../services/wsService.ts";
import type { IEquipmentDatabase } from "../../types/equipmentTypes.ts";
import { eEquipmentFeatures } from "../../types/equipmentTypes.ts";
import { Types } from "mongoose";
import gameToBuildVersion from "../../constants/gameToBuildVersion.ts";
import { ExportRecipes } from "warframe-public-export-plus";

export const upgradesController: RequestHandler = async (req, res) => {
    const account = await getAccountForRequest(req);
    const buildLabel = getBuildLabel(req, account);
    const accountId = account._id.toString();
    const inventoryChanges: IInventoryChanges = {};

    if (version_compare(buildLabel, gameToBuildVersion["24.4.0"]) < 0) {
        // Builds before U24.4.0 have a different request format
        const payload = JSON.parse(String(req.body)) as IUpgradesRequestLegacy;
        const itemId = fromOid(payload.Weapon.ItemId);
        if (itemId) {
            const inventory = await getInventory2(
                accountId,
                payload.Category,
                "MiscItems",
                "RawUpgrades",
                "Upgrades",
                "infiniteCredits",
                "RegularCredits",
                "infinitePlatinum",
                "PremiumCredits",
                "PremiumCreditsFree"
            );

            if (payload.IsSwappingOperation === true) {
                const item = inventory[payload.Category].id(itemId)!;
                for (let i = 0; i != payload.PolarityRemap.length; ++i) {
                    // Can't really be selective here like the newer format, it pushes everything in a way that the comparison fails against...
                    setSlotPolarity(item, i, payload.PolarityRemap[i].Value);
                }
            } else {
                if (payload.PolarizeReq) {
                    switch (payload.PolarizeReq) {
                        case "/Lotus/Types/Items/MiscItems/Forma":
                        case "/Lotus/Types/Items/MiscItems/FormaUmbra": {
                            const item = inventory[payload.Category].id(itemId)!;
                            item.XP = 0;
                            setSlotPolarity(item, payload.PolarizeSlot, payload.PolarizeValue);
                            item.Polarized ??= 0;
                            item.Polarized += 1;
                            sendWsBroadcastTo(accountId, { update_inventory: true });
                            break;
                        }
                        default:
                            throw new Error("Unsupported polarize item: " + payload.PolarizeReq);
                    }
                    addMiscItems(inventory, [
                        {
                            ItemType: payload.PolarizeReq,
                            ItemCount: -1
                        } satisfies IMiscItem
                    ]);
                }
                if (payload.UtilityReq) {
                    switch (payload.UtilityReq) {
                        case "/Lotus/Types/Items/MiscItems/UtilityUnlocker": {
                            const item = inventory[payload.Category].id(itemId)!;
                            item.Features ??= 0;
                            item.Features |= eEquipmentFeatures.UTILITY_SLOT;
                            break;
                        }
                        default:
                            throw new Error("Unsupported utility item: " + payload.UtilityReq);
                    }
                    addMiscItems(inventory, [
                        {
                            ItemType: payload.UtilityReq,
                            ItemCount: -1
                        } satisfies IMiscItem
                    ]);
                }
                if (payload.UpgradeReq) {
                    switch (payload.UpgradeReq) {
                        case "/Lotus/Types/Items/MiscItems/OrokinReactor":
                        case "/Lotus/Types/Items/MiscItems/OrokinCatalyst": {
                            const item = inventory[payload.Category].id(itemId)!;
                            item.Features ??= 0;
                            item.Features |= eEquipmentFeatures.DOUBLE_CAPACITY;
                            break;
                        }
                        default:
                            throw new Error("Unsupported upgrade: " + payload.UpgradeReq);
                    }
                    addMiscItems(inventory, [
                        {
                            ItemType: payload.UpgradeReq,
                            ItemCount: -1
                        } satisfies IMiscItem
                    ]);
                }
            }

            // Handle attaching/detaching mods in U7-U8
            if (payload.UpgradesToAttach && payload.UpgradesToAttach.length > 0) {
                const item = inventory[payload.Category].id(itemId)!;
                if (!item.Configs[0]) {
                    item.Configs.push({ Upgrades: ["", "", "", "", "", "", "", "", "", "", ""] });
                }
                if (item.Configs[0].Upgrades && item.Configs[0].Upgrades.length < 11) {
                    item.Configs[0].Upgrades.length = 11;
                }
                payload.UpgradesToAttach.forEach(upgrade => {
                    if (item.Configs[0].Upgrades && upgrade.ItemId.$id && upgrade.Slot) {
                        const arr = item.Configs[0].Upgrades;
                        if (arr.indexOf(upgrade.ItemId.$id) != -1) {
                            // Handle swapping mod to a different slot
                            arr[arr.indexOf(upgrade.ItemId.$id)] = "";
                        }
                        // We need to convert RawUpgrade into Upgrade once it's attached
                        const rawUpgrade = inventory.RawUpgrades.id(upgrade.ItemId.$id);
                        if (rawUpgrade) {
                            const newId = new Types.ObjectId().toString();
                            arr[upgrade.Slot - 1] = newId;
                            addMods(inventory, [
                                {
                                    ItemType: upgrade.ItemType,
                                    ItemCount: -1
                                }
                            ]);
                            inventory.Upgrades.push({
                                UpgradeFingerprint: `{"lvl":0}`,
                                ItemType: upgrade.ItemType,
                                _id: newId
                            });
                        } else {
                            arr[upgrade.Slot - 1] = upgrade.ItemId.$id;
                        }
                    }
                });
            }
            if (payload.UpgradesToDetach && payload.UpgradesToDetach.length > 0) {
                const item = inventory[payload.Category].id(itemId)!;
                if (item.Configs[0]) {
                    if (item.Configs[0].Upgrades && item.Configs[0].Upgrades.length < 11) {
                        item.Configs[0].Upgrades.length = 11;
                    }
                    payload.UpgradesToDetach.forEach(upgrade => {
                        if (item.Configs[0].Upgrades && upgrade.ItemId.$id) {
                            const arr = item.Configs[0].Upgrades;
                            arr[arr.indexOf(upgrade.ItemId.$id)] = "";
                        }
                    });
                }
            }

            if (payload.Weapon.UnlockLevel && payload.Weapon.UnlockLevel > 0) {
                const item = inventory[payload.Category].id(itemId)!;
                item.Features ??= 0;
                item.Features |= eEquipmentFeatures.DOUBLE_CAPACITY;
                updatePlatinum(inventory, 20);
            }
            if (payload.Weapon.UpgradeNodes != undefined) {
                const item = inventory[payload.Category].id(itemId)!;
                item.UpgradeNodes = payload.Weapon.UpgradeNodes;
            }
            if (payload.Cost) {
                updateCredits(inventory, payload.Cost);
            }

            await inventory.save();
        }
    } else {
        const payload = JSON.parse(String(req.body)) as IUpgradesRequest;
        const bayonetOtherCategory = payload.ItemCategory == "Melee" ? "LongGuns" : "Melee";
        const inventory = await getInventory2(
            accountId,
            payload.ItemCategory,
            bayonetOtherCategory,
            "MiscItems",
            "infinitePlatinum",
            "PremiumCredits",
            "PremiumCreditsFree",
            "infiniteHelminthMaterials",
            "InfestedFoundry",
            "Recipes"
        );
        for (const operation of payload.Operations) {
            if (
                operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/ModSlotUnlocker" ||
                operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker"
            ) {
                updatePlatinum(inventory, 10);
            } else if (
                operation.OperationType != "UOT_SWAP_POLARITY" &&
                operation.OperationType != "UOT_ABILITY_OVERRIDE"
            ) {
                if (!operation.UpgradeRequirement) {
                    throw new Error(`${operation.OperationType} operation should be free?`);
                }
                addMiscItems(inventory, [
                    {
                        ItemType: operation.UpgradeRequirement,
                        ItemCount: -1
                    } satisfies IMiscItem
                ]);
            }

            if (operation.OperationType == "UOT_ABILITY_OVERRIDE") {
                console.assert(payload.ItemCategory == "Suits");
                const suit = inventory.Suits.id(payload.ItemId.$oid)!;

                let newAbilityOverride: IAbilityOverride | undefined;
                let totalPercentagePointsConsumed = 0;
                if (operation.UpgradeRequirement != "") {
                    const abilityName = operation.UpgradeRequirement.split("/").pop();
                    const recipe = Object.values(ExportRecipes).find(x => x.resultType.endsWith(`/${abilityName}`));
                    if (!recipe) {
                        throw new Error(`could not find recipe for ${operation.UpgradeRequirement}`);
                    }
                    for (const ingredient of recipe.ingredients) {
                        totalPercentagePointsConsumed += ingredient.ItemCount / 10;
                        if (!inventory.infiniteHelminthMaterials) {
                            inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType)!.Count -=
                                ingredient.ItemCount;
                        }
                    }
                    newAbilityOverride = {
                        Ability: recipe.resultType,
                        Index: operation.PolarizeSlot
                    };
                }

                for (const entry of operation.PolarityRemap) {
                    suit.Configs[entry.Slot] ??= {};
                    suit.Configs[entry.Slot].AbilityOverride = newAbilityOverride;
                }

                const recipeChanges = addInfestedFoundryXP(
                    inventory.InfestedFoundry!,
                    totalPercentagePointsConsumed * 8
                );
                addRecipes(inventory, recipeChanges);

                inventoryChanges.Recipes = recipeChanges;
                inventoryChanges.InfestedFoundry = inventory.toJSON<IInventoryClient>().InfestedFoundry;
                applyCheatsToInfestedFoundry(inventory, inventoryChanges.InfestedFoundry!);
            } else
                switch (operation.UpgradeRequirement) {
                    case "/Lotus/Types/Items/MiscItems/OrokinReactor":
                    case "/Lotus/Types/Items/MiscItems/OrokinCatalyst": {
                        const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
                        item.Features ??= 0;
                        item.Features |= eEquipmentFeatures.DOUBLE_CAPACITY;
                        if (item.AltWeaponModeId) {
                            const otherItem = inventory[bayonetOtherCategory].id(item.AltWeaponModeId)!;
                            otherItem.Features ??= 0;
                            otherItem.Features |= eEquipmentFeatures.DOUBLE_CAPACITY;
                        }
                        break;
                    }
                    case "/Lotus/Types/Items/MiscItems/UtilityUnlocker":
                    case "/Lotus/Types/Items/MiscItems/WeaponUtilityUnlocker": {
                        const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
                        item.Features ??= 0;
                        item.Features |= eEquipmentFeatures.UTILITY_SLOT;
                        if (item.AltWeaponModeId) {
                            const otherItem = inventory[bayonetOtherCategory].id(item.AltWeaponModeId)!;
                            otherItem.Features ??= 0;
                            otherItem.Features |= eEquipmentFeatures.UTILITY_SLOT;
                        }
                        break;
                    }
                    case "/Lotus/Types/Items/MiscItems/HeavyWeaponCatalyst": {
                        console.assert(payload.ItemCategory == "SpaceGuns");
                        const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
                        item.Features ??= 0;
                        item.Features |= eEquipmentFeatures.GRAVIMAG_INSTALLED;
                        break;
                    }
                    case "/Lotus/Types/Items/MiscItems/WeaponPrimaryArcaneUnlocker":
                    case "/Lotus/Types/Items/MiscItems/WeaponSecondaryArcaneUnlocker":
                    case "/Lotus/Types/Items/MiscItems/WeaponMeleeArcaneUnlocker":
                    case "/Lotus/Types/Items/MiscItems/WeaponAmpArcaneUnlocker":
                    case "/Lotus/Types/Items/MiscItems/WeaponArchGunArcaneUnlocker": {
                        const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
                        item.Features ??= 0;
                        if (operation.OperationType == "UOT_ARCANE_UNLOCK_1") {
                            item.Features |= eEquipmentFeatures.SECOND_ARCANE_SLOT;
                        } else {
                            item.Features |= eEquipmentFeatures.ARCANE_SLOT;
                        }
                        break;
                    }
                    case "/Lotus/Types/Items/MiscItems/ValenceAdapter": {
                        const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
                        item.Features ??= 0;
                        item.Features |= eEquipmentFeatures.VALENCE_SWAP;
                        break;
                    }
                    case "/Lotus/Types/Items/MiscItems/Forma":
                    case "/Lotus/Types/Items/MiscItems/FormaUmbra":
                    case "/Lotus/Types/Items/MiscItems/FormaAura":
                    case "/Lotus/Types/Items/MiscItems/FormaStance": {
                        const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
                        item.XP = 0;
                        setSlotPolarity(item, operation.PolarizeSlot, operation.PolarizeValue);
                        item.Polarized ??= 0;
                        item.Polarized += 1;
                        if (item.AltWeaponModeId) {
                            const otherItem = inventory[bayonetOtherCategory].id(item.AltWeaponModeId)!;
                            otherItem.XP = 0;
                            setSlotPolarity(otherItem, operation.PolarizeSlot, operation.PolarizeValue);
                            otherItem.Polarized ??= 0;
                            otherItem.Polarized += 1;
                        }
                        sendWsBroadcastTo(accountId, { update_inventory: true }); // webui may need to to re-add "max rank" button
                        break;
                    }
                    case "/Lotus/Types/Items/MiscItems/ModSlotUnlocker": {
                        const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
                        item.ModSlotPurchases ??= 0;
                        item.ModSlotPurchases += 1;
                        break;
                    }
                    case "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker": {
                        const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
                        item.CustomizationSlotPurchases ??= 0;
                        item.CustomizationSlotPurchases += 1;
                        break;
                    }
                    case "": {
                        console.assert(operation.OperationType == "UOT_SWAP_POLARITY");
                        const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!;
                        for (let i = 0; i != operation.PolarityRemap.length; ++i) {
                            if (operation.PolarityRemap[i].Slot != i) {
                                setSlotPolarity(item, i, operation.PolarityRemap[i].Value);
                            }
                        }
                        break;
                    }
                    default:
                        throw new Error("Unsupported upgrade: " + operation.UpgradeRequirement);
                }
        }
        await inventory.save();
    }
    res.json({ InventoryChanges: inventoryChanges });
    sendWsBroadcastToWebui({ update_inventory: true }, accountId);
};

const setSlotPolarity = (item: IEquipmentDatabase, slot: number, polarity: TArtifactPolarity): void => {
    item.Polarity ??= [];
    const entry = item.Polarity.find(entry => entry.Slot == slot);
    if (entry) {
        entry.Value = polarity;
    } else {
        item.Polarity.push({ Slot: slot, Value: polarity });
    }
};