feat: Inbox #876
@ -1,6 +1,6 @@
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
import { getAccountForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
 | 
			
		||||
import { Inventory, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
 | 
			
		||||
import { config } from "@/src/services/configService";
 | 
			
		||||
import allDialogue from "@/static/fixed_responses/allDialogue.json";
 | 
			
		||||
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
 | 
			
		||||
@ -51,6 +51,13 @@ export const inventoryController: RequestHandler = async (request, response) =>
 | 
			
		||||
        await inventory.save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    response.json(await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getInventoryResponse = async (
 | 
			
		||||
    inventory: TInventoryDatabaseDocument,
 | 
			
		||||
    xpBasedLevelCapDisabled: boolean
 | 
			
		||||
): Promise<IInventoryClient> => {
 | 
			
		||||
    const inventoryWithLoadOutPresets = await inventory.populate<{ LoadOutPresets: ILoadoutDatabase }>(
 | 
			
		||||
        "LoadOutPresets"
 | 
			
		||||
    );
 | 
			
		||||
@ -175,7 +182,7 @@ export const inventoryController: RequestHandler = async (request, response) =>
 | 
			
		||||
 | 
			
		||||
    if (typeof config.spoofMasteryRank === "number" && config.spoofMasteryRank >= 0) {
 | 
			
		||||
        inventoryResponse.PlayerLevel = config.spoofMasteryRank;
 | 
			
		||||
        if (!("xpBasedLevelCapDisabled" in request.query)) {
 | 
			
		||||
        if (!xpBasedLevelCapDisabled) {
 | 
			
		||||
            // This client has not been patched to accept any mastery rank, need to fake the XP.
 | 
			
		||||
            inventoryResponse.XPInfo = [];
 | 
			
		||||
            let numFrames = getExpRequiredForMr(Math.min(config.spoofMasteryRank, 5030)) / 6000;
 | 
			
		||||
@ -252,7 +259,7 @@ export const inventoryController: RequestHandler = async (request, response) =>
 | 
			
		||||
 | 
			
		||||
    inventoryResponse.LastInventorySync = toOid(new Types.ObjectId());
 | 
			
		||||
 | 
			
		||||
    response.json(inventoryResponse);
 | 
			
		||||
    return inventoryResponse;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const addString = (arr: string[], str: string): void => {
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import {
 | 
			
		||||
    calculateFinalCredits
 | 
			
		||||
} from "@/src/services/missionInventoryUpdateService";
 | 
			
		||||
import { getInventory } from "@/src/services/inventoryService";
 | 
			
		||||
import { getInventoryResponse } from "./inventoryController";
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
**** INPUT ****
 | 
			
		||||
@ -58,9 +59,12 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
 | 
			
		||||
    const inventoryUpdates = addMissionInventoryUpdates(inventory, missionReport);
 | 
			
		||||
 | 
			
		||||
    if (missionReport.MissionStatus !== "GS_SUCCESS") {
 | 
			
		||||
        const InventoryJson = JSON.stringify((await inventory.save()).toJSON());
 | 
			
		||||
 | 
			
		||||
        res.json({ InventoryJson, MissionRewards: [] });
 | 
			
		||||
        await inventory.save();
 | 
			
		||||
        const inventoryResponse = await getInventoryResponse(inventory, true);
 | 
			
		||||
        res.json({
 | 
			
		||||
            InventoryJson: JSON.stringify(inventoryResponse),
 | 
			
		||||
            MissionRewards: []
 | 
			
		||||
        });
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const missionRewardsResults = await addMissionRewards(inventory, missionReport);
 | 
			
		||||
@ -76,11 +80,12 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
 | 
			
		||||
        rngRewardCredits: inventoryChanges.RegularCredits as number
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const InventoryJson = JSON.stringify((await inventory.save()).toJSON());
 | 
			
		||||
    await inventory.save();
 | 
			
		||||
    const inventoryResponse = await getInventoryResponse(inventory, true);
 | 
			
		||||
 | 
			
		||||
    //TODO: figure out when to send inventory. it is needed for many cases.
 | 
			
		||||
    res.json({
 | 
			
		||||
        InventoryJson,
 | 
			
		||||
        InventoryJson: JSON.stringify(inventoryResponse),
 | 
			
		||||
        InventoryChanges: inventoryChanges,
 | 
			
		||||
        MissionRewards,
 | 
			
		||||
        ...credits,
 | 
			
		||||
 | 
			
		||||
@ -51,10 +51,14 @@ import {
 | 
			
		||||
    ICompletedDialogue,
 | 
			
		||||
    IDialogueClient,
 | 
			
		||||
| 
					
	
	
	
	
	
	
	
	 | 
			||||
    IUpgradeDatabase,
 | 
			
		||||
    ICrewShipDatabase,
 | 
			
		||||
    ICrewShipMemberDatabase,
 | 
			
		||||
    ICrewShipMemberClient,
 | 
			
		||||
    IMailboxClient
 | 
			
		||||
    TEquipmentKey,
 | 
			
		||||
    equipmentKeys,
 | 
			
		||||
    IKubrowPetDetailsDatabase,
 | 
			
		||||
    ITraits,
 | 
			
		||||
    IKubrowPetDetailsClient
 | 
			
		||||
} from "../../types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { IOid } from "../../types/commonTypes";
 | 
			
		||||
import {
 | 
			
		||||
@ -224,55 +228,6 @@ ArchonCrystalUpgradeSchema.set("toJSON", {
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const EquipmentSchema = new Schema<IEquipmentDatabase>(
 | 
			
		||||
    {
 | 
			
		||||
        ItemType: String,
 | 
			
		||||
        Configs: [ItemConfigSchema],
 | 
			
		||||
        UpgradeVer: Number,
 | 
			
		||||
        XP: Number,
 | 
			
		||||
        Features: Number,
 | 
			
		||||
        Polarized: Number,
 | 
			
		||||
        Polarity: [polaritySchema],
 | 
			
		||||
        FocusLens: String,
 | 
			
		||||
        ModSlotPurchases: Number,
 | 
			
		||||
        CustomizationSlotPurchases: Number,
 | 
			
		||||
        UpgradeType: String,
 | 
			
		||||
        UpgradeFingerprint: String,
 | 
			
		||||
        ItemName: String,
 | 
			
		||||
        InfestationDate: Date,
 | 
			
		||||
        InfestationDays: Number,
 | 
			
		||||
        InfestationType: String,
 | 
			
		||||
        ModularParts: { type: [String], default: undefined },
 | 
			
		||||
        UnlockLevel: Number,
 | 
			
		||||
        Expiry: Date,
 | 
			
		||||
        SkillTree: String,
 | 
			
		||||
        OffensiveUpgrade: String,
 | 
			
		||||
        DefensiveUpgrade: String,
 | 
			
		||||
        UpgradesExpiry: Date,
 | 
			
		||||
        ArchonCrystalUpgrades: { type: [ArchonCrystalUpgradeSchema], default: undefined }
 | 
			
		||||
    },
 | 
			
		||||
    { id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
EquipmentSchema.virtual("ItemId").get(function () {
 | 
			
		||||
    return { $oid: this._id.toString() } satisfies IOid;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
EquipmentSchema.set("toJSON", {
 | 
			
		||||
    virtuals: true,
 | 
			
		||||
    transform(_document, returnedObject) {
 | 
			
		||||
        delete returnedObject._id;
 | 
			
		||||
        delete returnedObject.__v;
 | 
			
		||||
 | 
			
		||||
        const db = returnedObject as IEquipmentDatabase;
 | 
			
		||||
        const client = returnedObject as IEquipmentClient;
 | 
			
		||||
 | 
			
		||||
        if (db.InfestationDate) {
 | 
			
		||||
            client.InfestationDate = toMongoDate(db.InfestationDate);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const boosterSchema = new Schema<IBooster>(
 | 
			
		||||
    {
 | 
			
		||||
        ExpiryDate: Number,
 | 
			
		||||
@ -502,31 +457,6 @@ const helminthResourceSchema = new Schema<IHelminthResource>(
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const infestedFoundrySchema = new Schema<IInfestedFoundryDatabase>(
 | 
			
		||||
    {
 | 
			
		||||
        Name: String,
 | 
			
		||||
        Resources: { type: [helminthResourceSchema], default: undefined },
 | 
			
		||||
        Slots: Number,
 | 
			
		||||
        XP: Number,
 | 
			
		||||
        ConsumedSuits: { type: [consumedSchuitsSchema], default: undefined },
 | 
			
		||||
        InvigorationIndex: Number,
 | 
			
		||||
        InvigorationSuitOfferings: { type: [String], default: undefined },
 | 
			
		||||
        InvigorationsApplied: Number,
 | 
			
		||||
        LastConsumedSuit: { type: EquipmentSchema, default: undefined },
 | 
			
		||||
        AbilityOverrideUnlockCooldown: Date
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
infestedFoundrySchema.set("toJSON", {
 | 
			
		||||
    transform(_doc, ret, _options) {
 | 
			
		||||
        if (ret.AbilityOverrideUnlockCooldown) {
 | 
			
		||||
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
 | 
			
		||||
            ret.AbilityOverrideUnlockCooldown = toMongoDate(ret.AbilityOverrideUnlockCooldown);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const questProgressSchema = new Schema<IQuestStage>({
 | 
			
		||||
    c: Number,
 | 
			
		||||
    i: Boolean,
 | 
			
		||||
@ -712,26 +642,6 @@ const crewShipMembersSchema = new Schema<ICrewShipMembersDatabase>(
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const crewShipSchema = new Schema<ICrewShipDatabase>({
 | 
			
		||||
    ItemType: { type: String, required: true },
 | 
			
		||||
    Configs: { type: [ItemConfigSchema], default: [] },
 | 
			
		||||
    Weapon: { type: crewShipWeaponSchema, default: undefined },
 | 
			
		||||
    Customization: { type: crewShipCustomizationSchema, default: undefined },
 | 
			
		||||
    ItemName: { type: String, default: "" },
 | 
			
		||||
    RailjackImage: { type: FlavourItemSchema, default: undefined },
 | 
			
		||||
    CrewMembers: { type: crewShipMembersSchema, default: undefined }
 | 
			
		||||
});
 | 
			
		||||
crewShipSchema.virtual("ItemId").get(function () {
 | 
			
		||||
    return { $oid: this._id.toString() };
 | 
			
		||||
});
 | 
			
		||||
crewShipSchema.set("toJSON", {
 | 
			
		||||
    virtuals: true,
 | 
			
		||||
    transform(_doc, ret, _options) {
 | 
			
		||||
        delete ret._id;
 | 
			
		||||
        delete ret.__v;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const dialogueGiftSchema = new Schema<IDialogueGift>(
 | 
			
		||||
    {
 | 
			
		||||
        Item: String,
 | 
			
		||||
@ -786,6 +696,134 @@ const dialogueHistorySchema = new Schema<IDialogueHistoryDatabase>(
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const traitsSchema = new Schema<ITraits>(
 | 
			
		||||
    {
 | 
			
		||||
        BaseColor: String,
 | 
			
		||||
        SecondaryColor: String,
 | 
			
		||||
        TertiaryColor: String,
 | 
			
		||||
        AccentColor: String,
 | 
			
		||||
        EyeColor: String,
 | 
			
		||||
        FurPattern: String,
 | 
			
		||||
        Personality: String,
 | 
			
		||||
        BodyType: String,
 | 
			
		||||
        Head: { type: String, required: false },
 | 
			
		||||
        Tail: { type: String, required: false }
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const detailsSchema = new Schema<IKubrowPetDetailsDatabase>(
 | 
			
		||||
    {
 | 
			
		||||
        Name: String,
 | 
			
		||||
        IsPuppy: Boolean,
 | 
			
		||||
        HasCollar: Boolean,
 | 
			
		||||
        PrintsRemaining: Number,
 | 
			
		||||
        Status: String,
 | 
			
		||||
        HatchDate: Date,
 | 
			
		||||
        DominantTraits: traitsSchema,
 | 
			
		||||
        RecessiveTraits: traitsSchema,
 | 
			
		||||
        IsMale: Boolean,
 | 
			
		||||
        Size: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
detailsSchema.set("toJSON", {
 | 
			
		||||
    transform(_doc, returnedObject) {
 | 
			
		||||
        delete returnedObject.__v;
 | 
			
		||||
 | 
			
		||||
        const db = returnedObject as IKubrowPetDetailsDatabase;
 | 
			
		||||
        const client = returnedObject as IKubrowPetDetailsClient;
 | 
			
		||||
 | 
			
		||||
        client.HatchDate = toMongoDate(db.HatchDate);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const EquipmentSchema = new Schema<IEquipmentDatabase>(
 | 
			
		||||
    {
 | 
			
		||||
        ItemType: String,
 | 
			
		||||
        Configs: [ItemConfigSchema],
 | 
			
		||||
        UpgradeVer: { type: Number, default: 101 },
 | 
			
		||||
        XP: { type: Number, default: 0 },
 | 
			
		||||
        Features: Number,
 | 
			
		||||
        Polarized: Number,
 | 
			
		||||
        Polarity: [polaritySchema],
 | 
			
		||||
        FocusLens: String,
 | 
			
		||||
        ModSlotPurchases: Number,
 | 
			
		||||
        CustomizationSlotPurchases: Number,
 | 
			
		||||
        UpgradeType: String,
 | 
			
		||||
        UpgradeFingerprint: String,
 | 
			
		||||
        ItemName: String,
 | 
			
		||||
        InfestationDate: Date,
 | 
			
		||||
        InfestationDays: Number,
 | 
			
		||||
        InfestationType: String,
 | 
			
		||||
        ModularParts: { type: [String], default: undefined },
 | 
			
		||||
        UnlockLevel: Number,
 | 
			
		||||
        Expiry: Date,
 | 
			
		||||
        SkillTree: String,
 | 
			
		||||
        OffensiveUpgrade: String,
 | 
			
		||||
        DefensiveUpgrade: String,
 | 
			
		||||
        UpgradesExpiry: Date,
 | 
			
		||||
        ArchonCrystalUpgrades: { type: [ArchonCrystalUpgradeSchema], default: undefined },
 | 
			
		||||
        Weapon: crewShipWeaponSchema,
 | 
			
		||||
        Customization: crewShipCustomizationSchema,
 | 
			
		||||
        RailjackImage: FlavourItemSchema,
 | 
			
		||||
        CrewMembers: crewShipMembersSchema,
 | 
			
		||||
        Details: detailsSchema
 | 
			
		||||
    },
 | 
			
		||||
    { id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
EquipmentSchema.virtual("ItemId").get(function () {
 | 
			
		||||
    return { $oid: this._id.toString() } satisfies IOid;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
EquipmentSchema.set("toJSON", {
 | 
			
		||||
    virtuals: true,
 | 
			
		||||
    transform(_document, returnedObject) {
 | 
			
		||||
        delete returnedObject._id;
 | 
			
		||||
        delete returnedObject.__v;
 | 
			
		||||
 | 
			
		||||
        const db = returnedObject as IEquipmentDatabase;
 | 
			
		||||
        const client = returnedObject as IEquipmentClient;
 | 
			
		||||
 | 
			
		||||
        if (db.InfestationDate) {
 | 
			
		||||
            client.InfestationDate = toMongoDate(db.InfestationDate);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const equipmentFields: Record<string, { type: (typeof EquipmentSchema)[] }> = {};
 | 
			
		||||
 | 
			
		||||
equipmentKeys.forEach(key => {
 | 
			
		||||
    equipmentFields[key] = { type: [EquipmentSchema] };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const infestedFoundrySchema = new Schema<IInfestedFoundryDatabase>(
 | 
			
		||||
    {
 | 
			
		||||
        Name: String,
 | 
			
		||||
        Resources: { type: [helminthResourceSchema], default: undefined },
 | 
			
		||||
        Slots: Number,
 | 
			
		||||
        XP: Number,
 | 
			
		||||
        ConsumedSuits: { type: [consumedSchuitsSchema], default: undefined },
 | 
			
		||||
        InvigorationIndex: Number,
 | 
			
		||||
        InvigorationSuitOfferings: { type: [String], default: undefined },
 | 
			
		||||
        InvigorationsApplied: Number,
 | 
			
		||||
        LastConsumedSuit: { type: EquipmentSchema, default: undefined },
 | 
			
		||||
        AbilityOverrideUnlockCooldown: Date
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
infestedFoundrySchema.set("toJSON", {
 | 
			
		||||
    transform(_doc, ret, _options) {
 | 
			
		||||
        if (ret.AbilityOverrideUnlockCooldown) {
 | 
			
		||||
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
 | 
			
		||||
            ret.AbilityOverrideUnlockCooldown = toMongoDate(ret.AbilityOverrideUnlockCooldown);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
			
		||||
    {
 | 
			
		||||
        accountOwnerId: Schema.Types.ObjectId,
 | 
			
		||||
@ -818,6 +856,8 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
			
		||||
        MechBin: { type: slotsBinSchema, default: { Slots: 4 } },
 | 
			
		||||
        CrewMemberBin: { type: slotsBinSchema, default: { Slots: 3 } },
 | 
			
		||||
 | 
			
		||||
        ...equipmentFields,
 | 
			
		||||
 | 
			
		||||
        //How many trades do you have left
 | 
			
		||||
        TradesRemaining: { type: Number, default: 0 },
 | 
			
		||||
        //How many Gift do you have left*(gift spends the trade)
 | 
			
		||||
@ -867,29 +907,10 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
			
		||||
        //Upgrade Mods\Riven\Arcane Example:"UpgradeFingerprint"+"ItemType"+""
 | 
			
		||||
        Upgrades: [upgradeSchema],
 | 
			
		||||
 | 
			
		||||
        //Warframe
 | 
			
		||||
        Suits: [EquipmentSchema],
 | 
			
		||||
        //Primary    Weapon
 | 
			
		||||
        LongGuns: [EquipmentSchema],
 | 
			
		||||
        //Secondary  Weapon
 | 
			
		||||
        Pistols: [EquipmentSchema],
 | 
			
		||||
        //Melee      Weapon
 | 
			
		||||
        Melee: [EquipmentSchema],
 | 
			
		||||
        //Ability Weapon like Ultimate Mech\Excalibur\Ivara etc
 | 
			
		||||
        SpecialItems: [EquipmentSchema],
 | 
			
		||||
        //The Mandachord(Octavia) is a step sequencer
 | 
			
		||||
        StepSequencers: [StepSequencersSchema],
 | 
			
		||||
 | 
			
		||||
        //Sentinel(like Helios or modular)
 | 
			
		||||
        Sentinels: [EquipmentSchema],
 | 
			
		||||
        //Any /Sentinels/SentinelWeapons/ (like warframe weapon)
 | 
			
		||||
        SentinelWeapons: [EquipmentSchema],
 | 
			
		||||
        //Modular Pets
 | 
			
		||||
        MoaPets: [EquipmentSchema],
 | 
			
		||||
 | 
			
		||||
        KubrowPetEggs: [Schema.Types.Mixed],
 | 
			
		||||
        //Like PowerSuit Cat\Kubrow or etc Pets
 | 
			
		||||
        KubrowPets: [EquipmentSchema],
 | 
			
		||||
        //Prints   Cat(3 Prints)\Kubrow(2 Prints) Pets
 | 
			
		||||
        KubrowPetPrints: [Schema.Types.Mixed],
 | 
			
		||||
 | 
			
		||||
@ -902,42 +923,26 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
			
		||||
        EquippedInstrument: String,
 | 
			
		||||
        ReceivedStartingGear: Boolean,
 | 
			
		||||
 | 
			
		||||
        //to use add SummonItem to Consumables+EquippedGear
 | 
			
		||||
        //Archwing need Suits+Melee+Guns
 | 
			
		||||
        SpaceSuits: [EquipmentSchema],
 | 
			
		||||
        SpaceMelee: [EquipmentSchema],
 | 
			
		||||
        SpaceGuns: [EquipmentSchema],
 | 
			
		||||
        ArchwingEnabled: Boolean,
 | 
			
		||||
        //Mech need Suits+SpaceGuns+SpecialItem
 | 
			
		||||
        MechSuits: [EquipmentSchema],
 | 
			
		||||
        ///Restoratives/HoverboardSummon (like Suit)
 | 
			
		||||
        Hoverboards: [EquipmentSchema],
 | 
			
		||||
 | 
			
		||||
        //Use Operator\Drifter
 | 
			
		||||
        UseAdultOperatorLoadout: Boolean,
 | 
			
		||||
        //Operator\Drifter Weapon
 | 
			
		||||
        OperatorAmps: [EquipmentSchema],
 | 
			
		||||
        //Operator
 | 
			
		||||
        OperatorLoadOuts: [operatorConfigSchema],
 | 
			
		||||
        //Drifter
 | 
			
		||||
        AdultOperatorLoadOuts: [operatorConfigSchema],
 | 
			
		||||
        DrifterMelee: [EquipmentSchema],
 | 
			
		||||
        DrifterGuns: [EquipmentSchema],
 | 
			
		||||
        //ErsatzHorsePowerSuit
 | 
			
		||||
        Horses: [EquipmentSchema],
 | 
			
		||||
        // Kahl
 | 
			
		||||
        KahlLoadOuts: [operatorConfigSchema],
 | 
			
		||||
 | 
			
		||||
        //LandingCraft like Liset
 | 
			
		||||
        Ships: { type: [Schema.Types.ObjectId], ref: "Ships" },
 | 
			
		||||
        // /Lotus/Types/Items/ShipDecos/
 | 
			
		||||
        ShipDecorations: [typeCountSchema],
 | 
			
		||||
 | 
			
		||||
        //RailJack Setting(Mods,Skin,Weapon,etc)
 | 
			
		||||
        CrewShipHarnesses: [EquipmentSchema],
 | 
			
		||||
        //Railjack/Components(https://warframe.fandom.com/wiki/Railjack/Components)
 | 
			
		||||
        CrewShipRawSalvage: [Schema.Types.Mixed],
 | 
			
		||||
 | 
			
		||||
        //Default RailJack
 | 
			
		||||
        CrewShips: [crewShipSchema],
 | 
			
		||||
        CrewShipAmmo: [typeCountSchema],
 | 
			
		||||
        CrewShipWeapons: [Schema.Types.Mixed],
 | 
			
		||||
        CrewShipWeaponSkins: [Schema.Types.Mixed],
 | 
			
		||||
@ -959,9 +964,6 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
			
		||||
        //Cosmetics like profile glyphs\Kavasa Prime Kubrow Collar\Game Theme etc
 | 
			
		||||
        FlavourItems: [FlavourItemSchema],
 | 
			
		||||
 | 
			
		||||
        //Lunaro Weapon
 | 
			
		||||
        Scoops: [EquipmentSchema],
 | 
			
		||||
 | 
			
		||||
        //Mastery Rank*(Need item XPInfo to rank up)
 | 
			
		||||
        PlayerLevel: { type: Number, default: 0 },
 | 
			
		||||
        //Item Mastery Rank exp
 | 
			
		||||
@ -1072,11 +1074,6 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
			
		||||
        //https://warframe.fandom.com/wiki/Invasion
 | 
			
		||||
        InvasionChainProgress: [Schema.Types.Mixed],
 | 
			
		||||
 | 
			
		||||
        //https://warframe.fandom.com/wiki/Parazon
 | 
			
		||||
        DataKnives: [EquipmentSchema],
 | 
			
		||||
 | 
			
		||||
        Motorcycles: [EquipmentSchema],
 | 
			
		||||
 | 
			
		||||
        //CorpusLich or GrineerLich
 | 
			
		||||
        NemesisAbandonedRewards: [String],
 | 
			
		||||
        //CorpusLich\KuvaLich
 | 
			
		||||
@ -1113,7 +1110,6 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
			
		||||
        //Unknown and system
 | 
			
		||||
        DuviriInfo: DuviriInfoSchema,
 | 
			
		||||
        Mailbox: MailboxSchema,
 | 
			
		||||
        KahlLoadOuts: [Schema.Types.Mixed],
 | 
			
		||||
        HandlerPoints: Number,
 | 
			
		||||
        ChallengesFixVersion: Number,
 | 
			
		||||
        PlayedParkourTutorial: Boolean,
 | 
			
		||||
@ -1172,37 +1168,17 @@ inventorySchema.set("toJSON", {
 | 
			
		||||
 | 
			
		||||
// type overwrites for subdocuments/subdocument arrays
 | 
			
		||||
export type InventoryDocumentProps = {
 | 
			
		||||
    Suits: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    LongGuns: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    Pistols: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    Melee: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    OperatorAmps: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    FlavourItems: Types.DocumentArray<IFlavourItem>;
 | 
			
		||||
    RawUpgrades: Types.DocumentArray<IRawUpgrade>;
 | 
			
		||||
    Upgrades: Types.DocumentArray<IUpgradeDatabase>;
 | 
			
		||||
    MiscItems: Types.DocumentArray<IMiscItem>;
 | 
			
		||||
    Boosters: Types.DocumentArray<IBooster>;
 | 
			
		||||
    OperatorLoadOuts: Types.DocumentArray<IOperatorConfigDatabase>;
 | 
			
		||||
    SpecialItems: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    AdultOperatorLoadOuts: Types.DocumentArray<IOperatorConfigDatabase>;
 | 
			
		||||
    MechSuits: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    Scoops: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    DataKnives: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    Motorcycles: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    DrifterMelee: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    Sentinels: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    Horses: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    KahlLoadOuts: Types.DocumentArray<IOperatorConfigDatabase>;
 | 
			
		||||
    PendingRecipes: Types.DocumentArray<IPendingRecipeDatabase>;
 | 
			
		||||
    SpaceSuits: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    SpaceGuns: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    SpaceMelee: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    SentinelWeapons: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    Hoverboards: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    MoaPets: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
    WeaponSkins: Types.DocumentArray<IWeaponSkinDatabase>;
 | 
			
		||||
    CrewShips: Types.DocumentArray<ICrewShipDatabase>;
 | 
			
		||||
    CrewShipHarnesses: Types.DocumentArray<IEquipmentDatabase>;
 | 
			
		||||
};
 | 
			
		||||
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/ban-types
 | 
			
		||||
type InventoryModelType = Model<IInventoryDatabase, {}, InventoryDocumentProps>;
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,7 @@ import { getShipController } from "@/src/controllers/api/getShipController";
 | 
			
		||||
import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController";
 | 
			
		||||
import { getVoidProjectionRewardsController } from "@/src/controllers/api/getVoidProjectionRewardsController";
 | 
			
		||||
import { gildWeaponController } from "@/src/controllers/api/gildWeaponController";
 | 
			
		||||
import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
 | 
			
		||||
import { guildTechController } from "../controllers/api/guildTechController";
 | 
			
		||||
import { hostSessionController } from "@/src/controllers/api/hostSessionController";
 | 
			
		||||
import { hubController } from "@/src/controllers/api/hubController";
 | 
			
		||||
@ -74,13 +75,12 @@ import { syndicateSacrificeController } from "../controllers/api/syndicateSacrif
 | 
			
		||||
import { syndicateStandingBonusController } from "../controllers/api/syndicateStandingBonusController";
 | 
			
		||||
import { tauntHistoryController } from "@/src/controllers/api/tauntHistoryController";
 | 
			
		||||
import { trainingResultController } from "@/src/controllers/api/trainingResultController";
 | 
			
		||||
import { unlockShipFeatureController } from "@/src/controllers/api/unlockShipFeatureController";
 | 
			
		||||
import { updateChallengeProgressController } from "@/src/controllers/api/updateChallengeProgressController";
 | 
			
		||||
import { updateQuestController } from "@/src/controllers/api/updateQuestController";
 | 
			
		||||
import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController";
 | 
			
		||||
import { updateThemeController } from "../controllers/api/updateThemeController";
 | 
			
		||||
import { upgradesController } from "@/src/controllers/api/upgradesController";
 | 
			
		||||
import { updateQuestController } from "@/src/controllers/api/updateQuestController";
 | 
			
		||||
import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
 | 
			
		||||
import { unlockShipFeatureController } from "@/src/controllers/api/unlockShipFeatureController";
 | 
			
		||||
 | 
			
		||||
const apiRouter = express.Router();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,8 +8,6 @@ import {
 | 
			
		||||
import { IMongoDate } from "../types/commonTypes";
 | 
			
		||||
import {
 | 
			
		||||
    equipmentKeys,
 | 
			
		||||
    ICrewShipClient,
 | 
			
		||||
    ICrewShipDatabase,
 | 
			
		||||
    ICrewShipMemberClient,
 | 
			
		||||
    ICrewShipMemberDatabase,
 | 
			
		||||
    ICrewShipMembersClient,
 | 
			
		||||
@ -21,6 +19,8 @@ import {
 | 
			
		||||
    IInfestedFoundryClient,
 | 
			
		||||
    IInfestedFoundryDatabase,
 | 
			
		||||
    IInventoryClient,
 | 
			
		||||
    IKubrowPetDetailsClient,
 | 
			
		||||
    IKubrowPetDetailsDatabase,
 | 
			
		||||
    ILoadoutConfigClient,
 | 
			
		||||
    ILoadOutPresets,
 | 
			
		||||
    ISlots,
 | 
			
		||||
@ -47,7 +47,16 @@ const convertEquipment = (client: IEquipmentClient): IEquipmentDatabase => {
 | 
			
		||||
        _id: new Types.ObjectId(ItemId.$oid),
 | 
			
		||||
        InfestationDate: convertOptionalDate(client.InfestationDate),
 | 
			
		||||
        Expiry: convertOptionalDate(client.Expiry),
 | 
			
		||||
        UpgradesExpiry: convertOptionalDate(client.UpgradesExpiry)
 | 
			
		||||
        UpgradesExpiry: convertOptionalDate(client.UpgradesExpiry),
 | 
			
		||||
        CrewMembers: client.CrewMembers ? convertCrewShipMembers(client.CrewMembers) : undefined,
 | 
			
		||||
        Details: client.Details ? convertKubrowDetails(client.Details) : undefined,
 | 
			
		||||
        Configs: client.Configs
 | 
			
		||||
            ? client.Configs.map(obj =>
 | 
			
		||||
                  Object.fromEntries(
 | 
			
		||||
                      Object.entries(obj).filter(([_, value]) => !Array.isArray(value) || value.length > 0)
 | 
			
		||||
                  )
 | 
			
		||||
              )
 | 
			
		||||
            : []
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -102,15 +111,6 @@ const convertCrewShipMembers = (client: ICrewShipMembersClient): ICrewShipMember
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const convertCrewShip = (client: ICrewShipClient): ICrewShipDatabase => {
 | 
			
		||||
    const { ItemId, ...rest } = client;
 | 
			
		||||
    return {
 | 
			
		||||
        ...rest,
 | 
			
		||||
        _id: new Types.ObjectId(ItemId.$oid),
 | 
			
		||||
        CrewMembers: client.CrewMembers ? convertCrewShipMembers(client.CrewMembers) : undefined
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const convertInfestedFoundry = (client: IInfestedFoundryClient): IInfestedFoundryDatabase => {
 | 
			
		||||
    return {
 | 
			
		||||
        ...client,
 | 
			
		||||
@ -136,6 +136,13 @@ const convertDialogueHistory = (client: IDialogueHistoryClient): IDialogueHistor
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const convertKubrowDetails = (client: IKubrowPetDetailsClient): IKubrowPetDetailsDatabase => {
 | 
			
		||||
    return {
 | 
			
		||||
        ...client,
 | 
			
		||||
        HatchDate: convertDate(client.HatchDate)
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<IInventoryClient>): void => {
 | 
			
		||||
    for (const key of equipmentKeys) {
 | 
			
		||||
        if (client[key] !== undefined) {
 | 
			
		||||
@ -159,7 +166,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    for (const key of ["OperatorLoadOuts", "AdultOperatorLoadOuts"] as const) {
 | 
			
		||||
    for (const key of ["AdultOperatorLoadOuts", "OperatorLoadOuts", "KahlLoadOuts"] as const) {
 | 
			
		||||
        if (client[key] !== undefined) {
 | 
			
		||||
            replaceArray<IOperatorConfigDatabase>(db[key], client[key].map(convertOperatorConfig));
 | 
			
		||||
        }
 | 
			
		||||
@ -222,9 +229,6 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
 | 
			
		||||
    if (client.FocusUpgrades !== undefined) {
 | 
			
		||||
        db.FocusUpgrades = client.FocusUpgrades;
 | 
			
		||||
    }
 | 
			
		||||
    if (client.CrewShips !== undefined) {
 | 
			
		||||
        replaceArray<ICrewShipDatabase>(db.CrewShips, client.CrewShips.map(convertCrewShip));
 | 
			
		||||
    }
 | 
			
		||||
    if (client.InfestedFoundry !== undefined) {
 | 
			
		||||
        db.InfestedFoundry = convertInfestedFoundry(client.InfestedFoundry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ import {
 | 
			
		||||
    IUpdateChallengeProgressRequest
 | 
			
		||||
} from "../types/requestTypes";
 | 
			
		||||
import { logger } from "@/src/utils/logger";
 | 
			
		||||
import { getWeaponType, getExalted, getKeyChainItems } from "@/src/services/itemDataService";
 | 
			
		||||
import { getExalted, getKeyChainItems } from "@/src/services/itemDataService";
 | 
			
		||||
import { IEquipmentClient, IItemConfig } from "../types/inventoryTypes/commonInventoryTypes";
 | 
			
		||||
import {
 | 
			
		||||
    ExportArcanes,
 | 
			
		||||
@ -40,6 +40,7 @@ import {
 | 
			
		||||
    ExportResources,
 | 
			
		||||
    ExportSentinels,
 | 
			
		||||
    ExportUpgrades,
 | 
			
		||||
    ExportWeapons,
 | 
			
		||||
    TStandingLimitBin
 | 
			
		||||
} from "warframe-public-export-plus";
 | 
			
		||||
import { createShip } from "./shipService";
 | 
			
		||||
@ -288,6 +289,20 @@ export const addItem = async (
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    if (typeName in ExportWeapons) {
 | 
			
		||||
        const weapon = ExportWeapons[typeName];
 | 
			
		||||
        // Many non-weapon items are "Pistols" in Public Export, so some duck typing is needed.
 | 
			
		||||
        if (weapon.totalDamage != 0) {
 | 
			
		||||
            const inventoryChanges = addEquipment(inventory, weapon.productCategory, typeName);
 | 
			
		||||
            updateSlots(inventory, InventorySlot.WEAPONS, 0, 1);
 | 
			
		||||
            return {
 | 
			
		||||
                InventoryChanges: {
 | 
			
		||||
                    ...inventoryChanges,
 | 
			
		||||
                    WeaponBin: { count: 1, platinum: 0, Slots: -1 }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (typeName in creditBundles) {
 | 
			
		||||
        const creditsTotal = creditBundles[typeName] * quantity;
 | 
			
		||||
        inventory.RegularCredits += creditsTotal;
 | 
			
		||||
@ -355,17 +370,6 @@ export const addItem = async (
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case "Weapons": {
 | 
			
		||||
            const weaponType = getWeaponType(typeName);
 | 
			
		||||
            const inventoryChanges = addEquipment(inventory, weaponType, typeName);
 | 
			
		||||
            updateSlots(inventory, InventorySlot.WEAPONS, 0, 1);
 | 
			
		||||
            return {
 | 
			
		||||
                InventoryChanges: {
 | 
			
		||||
                    ...inventoryChanges,
 | 
			
		||||
                    WeaponBin: { count: 1, platinum: 0, Slots: -1 }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        case "Upgrades": {
 | 
			
		||||
            // Needed to add Traumatic Peculiar
 | 
			
		||||
            const changes = [
 | 
			
		||||
@ -917,6 +921,30 @@ export const addFusionTreasures = (
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const addFocusXpIncreases = (inventory: TInventoryDatabaseDocument, focusXpPlus: number[] | undefined): void => {
 | 
			
		||||
    enum FocusType {
 | 
			
		||||
        AP_UNIVERSAL,
 | 
			
		||||
        AP_ATTACK,
 | 
			
		||||
        AP_DEFENSE,
 | 
			
		||||
        AP_TACTIC,
 | 
			
		||||
        AP_POWER,
 | 
			
		||||
        AP_PRECEPT,
 | 
			
		||||
        AP_FUSION,
 | 
			
		||||
        AP_WARD,
 | 
			
		||||
        AP_UMBRA,
 | 
			
		||||
        AP_ANY
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (focusXpPlus) {
 | 
			
		||||
        inventory.FocusXP ??= { AP_ATTACK: 0, AP_DEFENSE: 0, AP_TACTIC: 0, AP_POWER: 0, AP_WARD: 0 };
 | 
			
		||||
        inventory.FocusXP.AP_ATTACK += focusXpPlus[FocusType.AP_ATTACK];
 | 
			
		||||
        inventory.FocusXP.AP_DEFENSE += focusXpPlus[FocusType.AP_DEFENSE];
 | 
			
		||||
        inventory.FocusXP.AP_TACTIC += focusXpPlus[FocusType.AP_TACTIC];
 | 
			
		||||
        inventory.FocusXP.AP_POWER += focusXpPlus[FocusType.AP_POWER];
 | 
			
		||||
        inventory.FocusXP.AP_WARD += focusXpPlus[FocusType.AP_WARD];
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const updateChallengeProgress = async (
 | 
			
		||||
    challenges: IUpdateChallengeProgressRequest,
 | 
			
		||||
    accountId: string
 | 
			
		||||
 | 
			
		||||
@ -44,21 +44,6 @@ export type WeaponTypeInternal =
 | 
			
		||||
    | "OperatorAmps"
 | 
			
		||||
    | "SpecialItems";
 | 
			
		||||
 | 
			
		||||
export const getWeaponType = (weaponName: string): WeaponTypeInternal => {
 | 
			
		||||
    const weaponInfo = ExportWeapons[weaponName];
 | 
			
		||||
 | 
			
		||||
    if (!weaponInfo) {
 | 
			
		||||
        throw new Error(`unknown weapon ${weaponName}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Many non-weapon items are "Pistols" in Public Export, so some duck typing is needed.
 | 
			
		||||
    if (weaponInfo.totalDamage == 0) {
 | 
			
		||||
        throw new Error(`${weaponName} doesn't quack like a weapon`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return weaponInfo.productCategory;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getRecipe = (uniqueName: string): IRecipe | undefined => {
 | 
			
		||||
    return ExportRecipes[uniqueName];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -2,10 +2,11 @@ import { ExportRegions, ExportRewards, IReward } from "warframe-public-export-pl
 | 
			
		||||
import { IMissionInventoryUpdateRequest, IRewardInfo } from "../types/requestTypes";
 | 
			
		||||
import { logger } from "@/src/utils/logger";
 | 
			
		||||
import { IRngResult, getRandomReward } from "@/src/services/rngService";
 | 
			
		||||
import { IInventoryDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import { equipmentKeys, IInventoryDatabase, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
import {
 | 
			
		||||
    addChallenges,
 | 
			
		||||
    addConsumables,
 | 
			
		||||
    addFocusXpIncreases,
 | 
			
		||||
    addFusionTreasures,
 | 
			
		||||
    addGearExpByCategory,
 | 
			
		||||
    addItem,
 | 
			
		||||
@ -22,6 +23,7 @@ import { IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
			
		||||
import { getLevelKeyRewards, getNode } from "@/src/services/itemDataService";
 | 
			
		||||
import { InventoryDocumentProps, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
 | 
			
		||||
import { getEntriesUnsafe } from "@/src/utils/ts-utils";
 | 
			
		||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
			
		||||
 | 
			
		||||
const getRotations = (rotationCount: number): number[] => {
 | 
			
		||||
    if (rotationCount === 0) return [0];
 | 
			
		||||
@ -144,23 +146,16 @@ export const addMissionInventoryUpdates = (
 | 
			
		||||
                inventoryChanges.FusionPoints = fusionPoints;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            // Equipment XP updates
 | 
			
		||||
            case "Suits":
 | 
			
		||||
            case "LongGuns":
 | 
			
		||||
            case "Pistols":
 | 
			
		||||
            case "Melee":
 | 
			
		||||
            case "SpecialItems":
 | 
			
		||||
            case "Sentinels":
 | 
			
		||||
            case "SentinelWeapons":
 | 
			
		||||
            case "SpaceSuits":
 | 
			
		||||
            case "SpaceGuns":
 | 
			
		||||
            case "SpaceMelee":
 | 
			
		||||
            case "Hoverboards":
 | 
			
		||||
            case "OperatorAmps":
 | 
			
		||||
            case "MoaPets":
 | 
			
		||||
                addGearExpByCategory(inventory, value, key);
 | 
			
		||||
            case "FocusXpIncreases": {
 | 
			
		||||
                addFocusXpIncreases(inventory, value);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            default:
 | 
			
		||||
                // Equipment XP updates
 | 
			
		||||
                if (equipmentKeys.includes(key as TEquipmentKey)) {
 | 
			
		||||
                    addGearExpByCategory(inventory, value as IEquipmentClient[], key as TEquipmentKey);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            // if (
 | 
			
		||||
            //     (ignoredInventoryUpdateKeys as readonly string[]).includes(key) ||
 | 
			
		||||
            //     knownUnhandledKeys.includes(key)
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ import { IOid } from "@/src/types/commonTypes";
 | 
			
		||||
import { Types } from "mongoose";
 | 
			
		||||
import { isEmptyObject } from "@/src/helpers/general";
 | 
			
		||||
import { logger } from "@/src/utils/logger";
 | 
			
		||||
import { equipmentKeys, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
 | 
			
		||||
//TODO: setup default items on account creation or like originally in giveStartingItems.php
 | 
			
		||||
 | 
			
		||||
@ -36,8 +37,9 @@ export const handleInventoryItemConfigChange = async (
 | 
			
		||||
        }
 | 
			
		||||
        // non-empty is a change in loadout(or suit...)
 | 
			
		||||
        switch (equipmentName) {
 | 
			
		||||
            case "AdultOperatorLoadOuts":
 | 
			
		||||
            case "OperatorLoadOuts":
 | 
			
		||||
            case "AdultOperatorLoadOuts": {
 | 
			
		||||
            case "KahlLoadOuts": {
 | 
			
		||||
                const operatorConfig = equipment as IOperatorConfigEntry;
 | 
			
		||||
                const operatorLoadout = inventory[equipmentName];
 | 
			
		||||
                logger.debug(`operator loadout received ${equipmentName} `, operatorConfig);
 | 
			
		||||
@ -124,45 +126,6 @@ export const handleInventoryItemConfigChange = async (
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "LongGuns":
 | 
			
		||||
            case "Pistols":
 | 
			
		||||
            case "Suits":
 | 
			
		||||
            case "Melee":
 | 
			
		||||
            case "Scoops":
 | 
			
		||||
            case "DataKnives":
 | 
			
		||||
            case "DrifterMelee":
 | 
			
		||||
            case "Sentinels":
 | 
			
		||||
            case "Horses":
 | 
			
		||||
            case "OperatorAmps":
 | 
			
		||||
            case "SentinelWeapons":
 | 
			
		||||
            case "KubrowPets":
 | 
			
		||||
            case "SpaceSuits":
 | 
			
		||||
            case "SpaceGuns":
 | 
			
		||||
            case "SpaceMelee":
 | 
			
		||||
            case "SpecialItems":
 | 
			
		||||
            case "MoaPets":
 | 
			
		||||
            case "Hoverboards":
 | 
			
		||||
            case "MechSuits":
 | 
			
		||||
            case "CrewShipHarnesses":
 | 
			
		||||
            case "CrewShips":
 | 
			
		||||
            case "Motorcycles": {
 | 
			
		||||
                logger.debug(`general Item config saved of type ${equipmentName}`, { config: equipment });
 | 
			
		||||
 | 
			
		||||
                const itemEntries = equipment as IItemEntry;
 | 
			
		||||
                for (const [itemId, itemConfigEntries] of Object.entries(itemEntries)) {
 | 
			
		||||
                    const inventoryItem = inventory[equipmentName].find(item => item._id?.toString() === itemId);
 | 
			
		||||
 | 
			
		||||
                    if (!inventoryItem) {
 | 
			
		||||
                        throw new Error(`inventory item ${equipmentName} not found with id ${itemId}`);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    //config ids are 0,1,2 can there be a 3?
 | 
			
		||||
                    for (const [configId, config] of Object.entries(itemConfigEntries)) {
 | 
			
		||||
                        inventoryItem.Configs[parseInt(configId)] = config;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case "CurrentLoadOutIds": {
 | 
			
		||||
                const loadoutIds = equipment as IOid[]; // TODO: Check for more than just an array of oids, I think i remember one instance
 | 
			
		||||
                inventory.CurrentLoadOutIds = loadoutIds;
 | 
			
		||||
@ -178,11 +141,30 @@ export const handleInventoryItemConfigChange = async (
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            default: {
 | 
			
		||||
                logger.warn(`loadout category not implemented, changes may be lost: ${equipmentName}`, {
 | 
			
		||||
                    config: equipment
 | 
			
		||||
                });
 | 
			
		||||
                if (equipmentKeys.includes(equipmentName as TEquipmentKey) && equipmentName != "ValidNewLoadoutId") {
 | 
			
		||||
                    logger.debug(`general Item config saved of type ${equipmentName}`, {
 | 
			
		||||
                        config: equipment
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    const itemEntries = equipment as IItemEntry;
 | 
			
		||||
                    for (const [itemId, itemConfigEntries] of Object.entries(itemEntries)) {
 | 
			
		||||
                        const inventoryItem = inventory[equipmentName].find(item => item._id?.toString() === itemId);
 | 
			
		||||
 | 
			
		||||
                        if (!inventoryItem) {
 | 
			
		||||
                            throw new Error(`inventory item ${equipmentName} not found with id ${itemId}`);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        for (const [configId, config] of Object.entries(itemConfigEntries)) {
 | 
			
		||||
                            inventoryItem.Configs[parseInt(configId)] = config;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                } else {
 | 
			
		||||
                    logger.warn(`loadout category not implemented, changes may be lost: ${equipmentName}`, {
 | 
			
		||||
                        config: equipment
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            //case "KahlLoadOuts": not sure yet how to handle kahl: it is not sent in inventory
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    await inventory.save();
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,14 @@
 | 
			
		||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
 | 
			
		||||
import { Types } from "mongoose";
 | 
			
		||||
import {
 | 
			
		||||
    ICrewShipCustomization,
 | 
			
		||||
    ICrewShipMembersClient,
 | 
			
		||||
    ICrewShipMembersDatabase,
 | 
			
		||||
    ICrewShipWeapon,
 | 
			
		||||
    IFlavourItem,
 | 
			
		||||
    IKubrowPetDetailsClient,
 | 
			
		||||
    IKubrowPetDetailsDatabase
 | 
			
		||||
} from "@/src/types/inventoryTypes/inventoryTypes";
 | 
			
		||||
 | 
			
		||||
export interface IPolarity {
 | 
			
		||||
    Slot: number;
 | 
			
		||||
@ -79,11 +88,16 @@ export interface IEquipmentSelection {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IEquipmentClient
 | 
			
		||||
    extends Omit<IEquipmentDatabase, "_id" | "InfestationDate" | "Expiry" | "UpgradesExpiry"> {
 | 
			
		||||
    extends Omit<
 | 
			
		||||
        IEquipmentDatabase,
 | 
			
		||||
        "_id" | "InfestationDate" | "Expiry" | "UpgradesExpiry" | "CrewMembers" | "Details"
 | 
			
		||||
    > {
 | 
			
		||||
    ItemId: IOid;
 | 
			
		||||
    InfestationDate?: IMongoDate;
 | 
			
		||||
    Expiry?: IMongoDate;
 | 
			
		||||
    UpgradesExpiry?: IMongoDate;
 | 
			
		||||
    CrewMembers?: ICrewShipMembersClient;
 | 
			
		||||
    Details?: IKubrowPetDetailsClient;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum EquipmentFeatures {
 | 
			
		||||
@ -120,6 +134,11 @@ export interface IEquipmentDatabase {
 | 
			
		||||
    DefensiveUpgrade?: string;
 | 
			
		||||
    UpgradesExpiry?: Date;
 | 
			
		||||
    ArchonCrystalUpgrades?: IArchonCrystalUpgrade[];
 | 
			
		||||
    Weapon?: ICrewShipWeapon;
 | 
			
		||||
    Customization?: ICrewShipCustomization;
 | 
			
		||||
    RailjackImage?: IFlavourItem;
 | 
			
		||||
    CrewMembers?: ICrewShipMembersDatabase;
 | 
			
		||||
    Details?: IKubrowPetDetailsDatabase;
 | 
			
		||||
    _id: Types.ObjectId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,9 +28,9 @@ export interface IInventoryDatabase
 | 
			
		||||
        | "Upgrades"
 | 
			
		||||
        | "CrewShipSalvagedWeaponSkins"
 | 
			
		||||
        | "CrewShipWeaponSkins"
 | 
			
		||||
        | "OperatorLoadOuts"
 | 
			
		||||
        | "AdultOperatorLoadOuts"
 | 
			
		||||
        | "CrewShips"
 | 
			
		||||
        | "OperatorLoadOuts"
 | 
			
		||||
        | "KahlLoadOuts"
 | 
			
		||||
        | "InfestedFoundry"
 | 
			
		||||
        | "DialogueHistory"
 | 
			
		||||
        | TEquipmentKey
 | 
			
		||||
@ -49,9 +49,9 @@ export interface IInventoryDatabase
 | 
			
		||||
    Upgrades: IUpgradeDatabase[];
 | 
			
		||||
    CrewShipSalvagedWeaponSkins: IUpgradeDatabase[];
 | 
			
		||||
    CrewShipWeaponSkins: IUpgradeDatabase[];
 | 
			
		||||
    OperatorLoadOuts: IOperatorConfigDatabase[];
 | 
			
		||||
    AdultOperatorLoadOuts: IOperatorConfigDatabase[];
 | 
			
		||||
    CrewShips: ICrewShipDatabase[];
 | 
			
		||||
    OperatorLoadOuts: IOperatorConfigDatabase[];
 | 
			
		||||
    KahlLoadOuts: IOperatorConfigDatabase[];
 | 
			
		||||
    InfestedFoundry?: IInfestedFoundryDatabase;
 | 
			
		||||
    DialogueHistory?: IDialogueHistoryDatabase;
 | 
			
		||||
 | 
			
		||||
@ -68,6 +68,16 @@ export interface IInventoryDatabase
 | 
			
		||||
    Hoverboards: IEquipmentDatabase[];
 | 
			
		||||
    OperatorAmps: IEquipmentDatabase[];
 | 
			
		||||
    MoaPets: IEquipmentDatabase[];
 | 
			
		||||
    Scoops: IEquipmentDatabase[];
 | 
			
		||||
    Horses: IEquipmentDatabase[];
 | 
			
		||||
    DrifterGuns: IEquipmentDatabase[];
 | 
			
		||||
    DrifterMelee: IEquipmentDatabase[];
 | 
			
		||||
    Motorcycles: IEquipmentDatabase[];
 | 
			
		||||
    CrewShips: IEquipmentDatabase[];
 | 
			
		||||
    DataKnives: IEquipmentDatabase[];
 | 
			
		||||
    MechSuits: IEquipmentDatabase[];
 | 
			
		||||
    CrewShipHarnesses: IEquipmentDatabase[];
 | 
			
		||||
    KubrowPets: IEquipmentDatabase[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IQuestKeyDatabase {
 | 
			
		||||
@ -97,7 +107,17 @@ export const equipmentKeys = [
 | 
			
		||||
    "SpaceMelee",
 | 
			
		||||
    "Hoverboards",
 | 
			
		||||
    "OperatorAmps",
 | 
			
		||||
    "MoaPets"
 | 
			
		||||
    "MoaPets",
 | 
			
		||||
    "Scoops",
 | 
			
		||||
    "Horses",
 | 
			
		||||
    "DrifterGuns",
 | 
			
		||||
    "DrifterMelee",
 | 
			
		||||
    "Motorcycles",
 | 
			
		||||
    "CrewShips",
 | 
			
		||||
    "DataKnives",
 | 
			
		||||
    "MechSuits",
 | 
			
		||||
    "CrewShipHarnesses",
 | 
			
		||||
    "KubrowPets"
 | 
			
		||||
] as const;
 | 
			
		||||
 | 
			
		||||
export type TEquipmentKey = (typeof equipmentKeys)[number];
 | 
			
		||||
@ -171,13 +191,22 @@ export interface IInventoryClient extends IDailyAffiliations {
 | 
			
		||||
    Hoverboards: IEquipmentClient[];
 | 
			
		||||
    OperatorAmps: IEquipmentClient[];
 | 
			
		||||
    MoaPets: IEquipmentClient[];
 | 
			
		||||
    Scoops: IEquipmentClient[];
 | 
			
		||||
    Horses: IEquipmentClient[];
 | 
			
		||||
    DrifterGuns: IEquipmentClient[];
 | 
			
		||||
    DrifterMelee: IEquipmentClient[];
 | 
			
		||||
    Motorcycles: IEquipmentClient[];
 | 
			
		||||
    CrewShips: IEquipmentClient[];
 | 
			
		||||
    DataKnives: IEquipmentClient[];
 | 
			
		||||
    MechSuits: IEquipmentClient[];
 | 
			
		||||
    CrewShipHarnesses: IEquipmentClient[];
 | 
			
		||||
    KubrowPets: IEquipmentClient[];
 | 
			
		||||
    AdultOperatorLoadOuts: IOperatorConfigClient[];
 | 
			
		||||
    OperatorLoadOuts: IOperatorConfigClient[];
 | 
			
		||||
    KahlLoadOuts: IOperatorConfigClient[];
 | 
			
		||||
 | 
			
		||||
    Horses: IEquipmentDatabase[];
 | 
			
		||||
    DrifterMelee: IEquipmentDatabase[];
 | 
			
		||||
    DrifterGuns: IEquipmentDatabase[];
 | 
			
		||||
    DuviriInfo: IDuviriInfo;
 | 
			
		||||
    Mailbox?: IMailboxClient;
 | 
			
		||||
    KahlLoadOuts: IEquipmentDatabase[];
 | 
			
		||||
    SubscribedToEmails: number;
 | 
			
		||||
    Created: IMongoDate;
 | 
			
		||||
    RewardSeed: number;
 | 
			
		||||
@ -212,7 +241,6 @@ export interface IInventoryClient extends IDailyAffiliations {
 | 
			
		||||
    QuestKeys: IQuestKeyClient[];
 | 
			
		||||
    ActiveQuest: string;
 | 
			
		||||
    FlavourItems: IFlavourItem[];
 | 
			
		||||
    Scoops: IEquipmentDatabase[];
 | 
			
		||||
    LoadOutPresets: ILoadOutPresets;
 | 
			
		||||
    CurrentLoadOutIds: IOid[]; //TODO: we store it in the database using this representation as well :/
 | 
			
		||||
    Missions: IMission[];
 | 
			
		||||
@ -267,7 +295,6 @@ export interface IInventoryClient extends IDailyAffiliations {
 | 
			
		||||
    Drones: IDrone[];
 | 
			
		||||
    StepSequencers: IStepSequencer[];
 | 
			
		||||
    ActiveAvatarImageType: string;
 | 
			
		||||
    KubrowPets: IEquipmentDatabase[];
 | 
			
		||||
    ShipDecorations: IConsumable[];
 | 
			
		||||
    DiscoveredMarkers: IDiscoveredMarker[];
 | 
			
		||||
    CompletedJobs: ICompletedJob[];
 | 
			
		||||
@ -284,7 +311,6 @@ export interface IInventoryClient extends IDailyAffiliations {
 | 
			
		||||
    BountyScore: number;
 | 
			
		||||
    ChallengeInstanceStates: IChallengeInstanceState[];
 | 
			
		||||
    LoginMilestoneRewards: string[];
 | 
			
		||||
    OperatorLoadOuts: IOperatorConfigClient[];
 | 
			
		||||
    RecentVendorPurchases: Array<number | string>;
 | 
			
		||||
    NodeIntrosCompleted: string[];
 | 
			
		||||
    GuildId?: IOid;
 | 
			
		||||
@ -292,13 +318,10 @@ export interface IInventoryClient extends IDailyAffiliations {
 | 
			
		||||
    SeasonChallengeHistory: ISeasonChallenge[];
 | 
			
		||||
    EquippedInstrument?: string;
 | 
			
		||||
    InvasionChainProgress: IInvasionChainProgress[];
 | 
			
		||||
    DataKnives: IEquipmentDatabase[];
 | 
			
		||||
    Motorcycles: IEquipmentDatabase[];
 | 
			
		||||
    NemesisHistory: INemesisHistory[];
 | 
			
		||||
    LastNemesisAllySpawnTime?: IMongoDate;
 | 
			
		||||
    Settings: ISettings;
 | 
			
		||||
    PersonalTechProjects: IPersonalTechProject[];
 | 
			
		||||
    CrewShips: ICrewShipClient[];
 | 
			
		||||
    PlayerSkills: IPlayerSkills;
 | 
			
		||||
    CrewShipAmmo: IConsumable[];
 | 
			
		||||
    CrewShipSalvagedWeaponSkins: IUpgradeClient[];
 | 
			
		||||
@ -308,13 +331,10 @@ export interface IInventoryClient extends IDailyAffiliations {
 | 
			
		||||
    TradeBannedUntil?: IMongoDate;
 | 
			
		||||
    PlayedParkourTutorial: boolean;
 | 
			
		||||
    SubscribedToEmailsPersonalized: number;
 | 
			
		||||
    MechSuits: IEquipmentDatabase[];
 | 
			
		||||
    InfestedFoundry?: IInfestedFoundryClient;
 | 
			
		||||
    BlessingCooldown: IMongoDate;
 | 
			
		||||
    CrewShipHarnesses: IEquipmentDatabase[];
 | 
			
		||||
    CrewShipRawSalvage: IConsumable[];
 | 
			
		||||
    CrewMembers: ICrewMember[];
 | 
			
		||||
    AdultOperatorLoadOuts: IOperatorConfigClient[];
 | 
			
		||||
    LotusCustomization: ILotusCustomization;
 | 
			
		||||
    UseAdultOperatorLoadout?: boolean;
 | 
			
		||||
    NemesisAbandonedRewards: string[];
 | 
			
		||||
@ -458,22 +478,6 @@ export interface IUpgradeDatabase extends Omit<IUpgradeClient, "ItemId"> {
 | 
			
		||||
    _id: Types.ObjectId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ICrewShipClient {
 | 
			
		||||
    ItemType: string;
 | 
			
		||||
    Configs: IItemConfig[];
 | 
			
		||||
    Weapon?: ICrewShipWeapon;
 | 
			
		||||
    Customization?: ICrewShipCustomization;
 | 
			
		||||
    ItemName: string;
 | 
			
		||||
    RailjackImage?: IFlavourItem;
 | 
			
		||||
    CrewMembers?: ICrewShipMembersClient;
 | 
			
		||||
    ItemId: IOid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ICrewShipDatabase extends Omit<ICrewShipClient, "CrewMembers" | "ItemId"> {
 | 
			
		||||
    CrewMembers?: ICrewShipMembersDatabase;
 | 
			
		||||
    _id: Types.ObjectId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ICrewShipMembersClient {
 | 
			
		||||
    SLOT_A?: ICrewShipMemberClient;
 | 
			
		||||
    SLOT_B?: ICrewShipMemberClient;
 | 
			
		||||
@ -647,19 +651,23 @@ export enum KubrowPetPrintItemType {
 | 
			
		||||
    LotusTypesGameKubrowPetImprintedTraitPrint = "/Lotus/Types/Game/KubrowPet/ImprintedTraitPrint"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IDetails {
 | 
			
		||||
export interface IKubrowPetDetailsDatabase {
 | 
			
		||||
    Name: string;
 | 
			
		||||
    IsPuppy: boolean;
 | 
			
		||||
    HasCollar: boolean;
 | 
			
		||||
    PrintsRemaining: number;
 | 
			
		||||
    Status: Status;
 | 
			
		||||
    HatchDate: IMongoDate;
 | 
			
		||||
    HatchDate: Date;
 | 
			
		||||
    DominantTraits: ITraits;
 | 
			
		||||
    RecessiveTraits: ITraits;
 | 
			
		||||
    IsMale: boolean;
 | 
			
		||||
    Size: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IKubrowPetDetailsClient extends Omit<IKubrowPetDetailsDatabase, "HatchDate"> {
 | 
			
		||||
    HatchDate: IMongoDate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum Status {
 | 
			
		||||
    StatusAvailable = "STATUS_AVAILABLE",
 | 
			
		||||
    StatusStasis = "STATUS_STASIS"
 | 
			
		||||
 | 
			
		||||
@ -39,19 +39,6 @@ export type IMissionInventoryUpdateRequest = {
 | 
			
		||||
    GoalTag: string;
 | 
			
		||||
    LevelKeyName: string;
 | 
			
		||||
    ActiveBoosters?: IBooster[];
 | 
			
		||||
    Suits?: IEquipmentClient[];
 | 
			
		||||
    LongGuns?: IEquipmentClient[];
 | 
			
		||||
    Pistols?: IEquipmentClient[];
 | 
			
		||||
    Melee?: IEquipmentClient[];
 | 
			
		||||
    SpecialItems?: IEquipmentClient[];
 | 
			
		||||
    Sentinels?: IEquipmentClient[];
 | 
			
		||||
    SentinelWeapons?: IEquipmentClient[];
 | 
			
		||||
    SpaceSuits?: IEquipmentClient[];
 | 
			
		||||
    SpaceGuns?: IEquipmentClient[];
 | 
			
		||||
    SpaceMelee?: IEquipmentClient[];
 | 
			
		||||
    Hoverboards?: IEquipmentClient[];
 | 
			
		||||
    OperatorAmps?: IEquipmentClient[];
 | 
			
		||||
    MoaPets?: IEquipmentClient[];
 | 
			
		||||
    FusionBundles?: ITypeCount[];
 | 
			
		||||
    RawUpgrades?: IRawUpgrade[];
 | 
			
		||||
    MiscItems?: ITypeCount[];
 | 
			
		||||
@ -85,6 +72,9 @@ export type IMissionInventoryUpdateRequest = {
 | 
			
		||||
    FpsMax: number;
 | 
			
		||||
    FpsSamples: number;
 | 
			
		||||
    EvolutionProgress?: IEvolutionProgress[];
 | 
			
		||||
    FocusXpIncreases?: number[];
 | 
			
		||||
} & {
 | 
			
		||||
    [K in TEquipmentKey]?: IEquipmentClient[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export interface IRewardInfo {
 | 
			
		||||
 | 
			
		||||
@ -27,9 +27,9 @@ export interface ISaveLoadoutRequest {
 | 
			
		||||
    Horses: IItemEntry;
 | 
			
		||||
    DrifterMelee: IItemEntry;
 | 
			
		||||
    UpgradeVer: number;
 | 
			
		||||
    OperatorLoadOuts: IOperatorConfigEntry;
 | 
			
		||||
    AdultOperatorLoadOuts: IOperatorConfigEntry;
 | 
			
		||||
    KahlLoadOuts: IItemEntry;
 | 
			
		||||
    OperatorLoadOuts: IOperatorConfigEntry;
 | 
			
		||||
    KahlLoadOuts: IOperatorConfigEntry;
 | 
			
		||||
    CrewShips: IItemEntry;
 | 
			
		||||
    CurrentLoadOutIds: IOid[];
 | 
			
		||||
    ValidNewLoadoutId: string;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	
⚠️ Potential issue
Fix syntax error in type imports.
There's a missing comma after
IMailboxClientin the type imports list.Apply this diff to fix the syntax error:
📝 Committable suggestion
🧰 Tools
🪛 Biome (1.9.4)
[error] 57-57: expected
,but instead foundTEquipmentKeyRemove TEquipmentKey
(parse)
🪛 GitHub Check: build (22)
[failure] 57-57:
',' expected.
🪛 GitHub Check: build (20)
[failure] 57-57:
',' expected.
🪛 GitHub Check: build (18)
[failure] 57-57:
',' expected.
🪛 GitHub Actions: Build
[error] 57-57: Syntax error: Missing comma in TypeScript code