From 38dfe14776710592a2a9fd3b6c41dad8460d42fe Mon Sep 17 00:00:00 2001 From: Sainan Date: Tue, 11 Mar 2025 07:56:18 -0700 Subject: [PATCH] feat: fabricate research (#1150) Closes #910 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1150 --- package-lock.json | 8 ++-- package.json | 2 +- src/controllers/api/guildTechController.ts | 39 ++++++++++++++++--- .../custom/getItemListsController.ts | 7 ++++ src/models/inventoryModels/inventoryModel.ts | 25 +++++++++++- src/services/inventoryService.ts | 29 +++++++++++++- src/types/inventoryTypes/inventoryTypes.ts | 21 ++++++++-- 7 files changed, 115 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index d7524f05..486ac1a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "mongoose": "^8.11.0", "morgan": "^1.10.0", "typescript": ">=4.7.4 <5.6.0", - "warframe-public-export-plus": "^0.5.42", + "warframe-public-export-plus": "^0.5.43", "warframe-riven-info": "^0.1.2", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" @@ -4073,9 +4073,9 @@ } }, "node_modules/warframe-public-export-plus": { - "version": "0.5.42", - "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.42.tgz", - "integrity": "sha512-up3P5bLKD42Xkr3o7TX9WUwvpJzK88aQTLZ2bB6QWUHdsJxl/Z3TBn+HSd3eouIDTMVUzbTDeDPosSw7TcLegA==" + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.43.tgz", + "integrity": "sha512-LeF7HmsjOPsJDtgr66x3iMEIAQgcxKNM54VG895FTemgHLLo34UGDyeS1yIfY67WxxbTUgW3MkHQLlCEJXD14w==" }, "node_modules/warframe-riven-info": { "version": "0.1.2", diff --git a/package.json b/package.json index 1a824fd1..eb4ceaa7 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "mongoose": "^8.11.0", "morgan": "^1.10.0", "typescript": ">=4.7.4 <5.6.0", - "warframe-public-export-plus": "^0.5.42", + "warframe-public-export-plus": "^0.5.43", "warframe-riven-info": "^0.1.2", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index 35dffe2d..90714492 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -2,7 +2,14 @@ import { RequestHandler } from "express"; import { getGuildForRequestEx, getGuildVault, scaleRequiredCount } from "@/src/services/guildService"; import { ExportDojoRecipes, IDojoResearch } from "warframe-public-export-plus"; import { getAccountIdForRequest } from "@/src/services/loginService"; -import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService"; +import { + addItem, + addMiscItems, + addRecipes, + combineInventoryChanges, + getInventory, + updateCurrency +} from "@/src/services/inventoryService"; import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { config } from "@/src/services/configService"; @@ -127,6 +134,20 @@ export const guildTechController: RequestHandler = async (req, res) => { Recipes: recipeChanges } }); + } else if (action == "Fabricate") { + const payload = data as IGuildTechFabricateRequest; + const recipe = ExportDojoRecipes.fabrications[payload.RecipeType]; + const inventory = await getInventory(accountId); + const inventoryChanges: IInventoryChanges = updateCurrency(inventory, recipe.price, false); + inventoryChanges.MiscItems = recipe.ingredients.map(x => ({ + ItemType: x.ItemType, + ItemCount: x.ItemCount * -1 + })); + addMiscItems(inventory, inventoryChanges.MiscItems); + combineInventoryChanges(inventoryChanges, (await addItem(inventory, recipe.resultType)).InventoryChanges); + await inventory.save(); + // Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`. + res.json({ inventoryChanges: inventoryChanges }); } else { throw new Error(`unknown guildTech action: ${data.Action}`); } @@ -144,10 +165,12 @@ const processFundedProject = ( } }; -type TGuildTechRequest = { - Action: string; -} & Partial & - Partial; +type TGuildTechRequest = + | ({ + Action: string; + } & Partial & + Partial) + | IGuildTechFabricateRequest; interface IGuildTechStartFields { Mode: "Guild"; @@ -164,3 +187,9 @@ interface IGuildTechContributeFields { VaultCredits: number; VaultMiscItems: IMiscItem[]; } + +interface IGuildTechFabricateRequest { + Action: "Fabricate"; + Mode: "Guild"; + RecipeType: string; +} diff --git a/src/controllers/custom/getItemListsController.ts b/src/controllers/custom/getItemListsController.ts index 8e2b6dc1..cfdde03d 100644 --- a/src/controllers/custom/getItemListsController.ts +++ b/src/controllers/custom/getItemListsController.ts @@ -6,6 +6,7 @@ import { ExportDrones, ExportGear, ExportMisc, + ExportRailjackWeapons, ExportRecipes, ExportRelics, ExportResources, @@ -160,6 +161,12 @@ const getItemListsController: RequestHandler = (req, response) => { name: getString(item.name, lang) }); } + for (const [uniqueName, item] of Object.entries(ExportRailjackWeapons)) { + res.miscitems.push({ + uniqueName: uniqueName, + name: getString(item.name, lang) + }); + } res.mods = []; for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) { diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index f8cf68f2..880dd09e 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -75,7 +75,8 @@ import { ICollectibleEntry, IIncentiveState, ISongChallenge, - ILibraryPersonalProgress + ILibraryPersonalProgress, + ICrewShipWeaponDatabase } from "../../types/inventoryTypes/inventoryTypes"; import { IOid } from "../../types/commonTypes"; import { @@ -1037,6 +1038,25 @@ const alignmentSchema = new Schema( { _id: false } ); +const crewShipWeaponSchema2 = new Schema( + { + ItemType: String + }, + { id: false } +); + +crewShipWeaponSchema2.virtual("ItemId").get(function () { + return { $oid: this._id.toString() } satisfies IOid; +}); + +crewShipWeaponSchema2.set("toJSON", { + virtuals: true, + transform(_document, returnedObject) { + delete returnedObject._id; + delete returnedObject.__v; + } +}); + const inventorySchema = new Schema( { accountOwnerId: Schema.Types.ObjectId, @@ -1157,7 +1177,7 @@ const inventorySchema = new Schema( //Default RailJack CrewShipAmmo: [typeCountSchema], - CrewShipWeapons: [Schema.Types.Mixed], + CrewShipWeapons: [crewShipWeaponSchema2], CrewShipWeaponSkins: [upgradeSchema], //NPC Crew and weapon @@ -1404,6 +1424,7 @@ export type InventoryDocumentProps = { WeaponSkins: Types.DocumentArray; QuestKeys: Types.DocumentArray; Drones: Types.DocumentArray; + CrewShipWeapons: Types.DocumentArray; CrewShipWeaponSkins: Types.DocumentArray; } & { [K in TEquipmentKey]: Types.DocumentArray }; diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index b8e01e53..59da757a 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -26,7 +26,8 @@ import { ILibraryDailyTaskInfo, ICalendarProgress, IDroneClient, - IUpgradeClient + IUpgradeClient, + ICrewShipWeaponClient } from "@/src/types/inventoryTypes/inventoryTypes"; import { IGenericUpdate } from "../types/genericUpdate"; import { @@ -54,6 +55,7 @@ import { ExportGear, ExportKeys, ExportMisc, + ExportRailjackWeapons, ExportRecipes, ExportResources, ExportSentinels, @@ -386,6 +388,14 @@ export const addItem = async ( }; } } + if (typeName in ExportRailjackWeapons) { + return { + InventoryChanges: { + ...addCrewShipWeapon(inventory, typeName), + ...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, premiumPurchase) + } + }; + } if (typeName in ExportMisc.creditBundles) { const creditsTotal = ExportMisc.creditBundles[typeName] * quantity; inventory.RegularCredits += creditsTotal; @@ -859,6 +869,7 @@ export const addCustomization = ( inventoryChanges: IInventoryChanges = {} ): IInventoryChanges => { const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition inventoryChanges.FlavourItems ??= []; (inventoryChanges.FlavourItems as IFlavourItem[]).push( inventory.FlavourItems[flavourItemIndex].toJSON() @@ -872,6 +883,7 @@ export const addSkin = ( inventoryChanges: IInventoryChanges = {} ): IInventoryChanges => { const index = inventory.WeaponSkins.push({ ItemType: typeName }) - 1; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition inventoryChanges.WeaponSkins ??= []; (inventoryChanges.WeaponSkins as IWeaponSkinClient[]).push( inventory.WeaponSkins[index].toJSON() @@ -879,12 +891,27 @@ export const addSkin = ( return inventoryChanges; }; +const addCrewShipWeapon = ( + inventory: TInventoryDatabaseDocument, + typeName: string, + inventoryChanges: IInventoryChanges = {} +): IInventoryChanges => { + const index = inventory.CrewShipWeapons.push({ ItemType: typeName, _id: new Types.ObjectId() }) - 1; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + inventoryChanges.CrewShipWeapons ??= []; + (inventoryChanges.CrewShipWeapons as ICrewShipWeaponClient[]).push( + inventory.CrewShipWeapons[index].toJSON() + ); + return inventoryChanges; +}; + const addCrewShipWeaponSkin = ( inventory: TInventoryDatabaseDocument, typeName: string, inventoryChanges: IInventoryChanges = {} ): IInventoryChanges => { const index = inventory.CrewShipWeaponSkins.push({ ItemType: typeName }) - 1; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition inventoryChanges.CrewShipWeaponSkins ??= []; (inventoryChanges.CrewShipWeaponSkins as IUpgradeClient[]).push( inventory.CrewShipWeaponSkins[index].toJSON() diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 22f80b99..af029a34 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -31,6 +31,7 @@ export interface IInventoryDatabase | "WeaponSkins" | "Upgrades" | "CrewShipSalvagedWeaponSkins" + | "CrewShipWeapons" | "CrewShipWeaponSkins" | "AdultOperatorLoadOuts" | "OperatorLoadOuts" @@ -56,6 +57,7 @@ export interface IInventoryDatabase WeaponSkins: IWeaponSkinDatabase[]; Upgrades: IUpgradeDatabase[]; CrewShipSalvagedWeaponSkins: IUpgradeDatabase[]; + CrewShipWeapons: ICrewShipWeaponDatabase[]; CrewShipWeaponSkins: IUpgradeDatabase[]; AdultOperatorLoadOuts: IOperatorConfigDatabase[]; OperatorLoadOuts: IOperatorConfigDatabase[]; @@ -289,8 +291,8 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu PlayerSkills: IPlayerSkills; CrewShipAmmo: IConsumable[]; CrewShipSalvagedWeaponSkins: IUpgradeClient[]; - CrewShipWeapons: ICrewShipWeapon[]; - CrewShipSalvagedWeapons: ICrewShipWeapon[]; + CrewShipWeapons: ICrewShipWeaponClient[]; + CrewShipSalvagedWeapons: IEquipmentClient[]; CrewShipWeaponSkins: IUpgradeClient[]; TradeBannedUntil?: IMongoDate; PlayedParkourTutorial: boolean; @@ -428,7 +430,8 @@ export enum InventorySlot { SPACESUITS = "SpaceSuitBin", MECHSUITS = "MechBin", PVE_LOADOUTS = "PveBonusLoadoutBin", - SENTINELS = "SentinelBin" + SENTINELS = "SentinelBin", + RJ_COMPONENT_AND_ARMAMENTS = "CrewShipSalvageBin" } export interface ISlots { @@ -489,11 +492,23 @@ export interface IFlavourItem { export type IMiscItem = ITypeCount; +// inventory.CrewShips[0].Weapon export interface ICrewShipWeapon { PILOT: ICrewShipPilotWeapon; PORT_GUNS: ICrewShipPortGuns; } +// inventory.CrewShipWeapons +export interface ICrewShipWeaponClient { + ItemType: string; + ItemId: IOid; +} + +export interface ICrewShipWeaponDatabase { + ItemType: string; + _id: Types.ObjectId; +} + export interface ICrewShipPilotWeapon { PRIMARY_A: IEquipmentSelection; SECONDARY_A: IEquipmentSelection;