feat: fabricate research (#1150)
All checks were successful
Build / build (22) (push) Successful in 42s
Build / build (20) (push) Successful in 1m2s
Build / build (18) (push) Successful in 1m11s
Build Docker image / docker (push) Successful in 36s

Closes #910

Reviewed-on: #1150
This commit is contained in:
Sainan 2025-03-11 07:56:18 -07:00
parent ead7b67efc
commit 38dfe14776
7 changed files with 115 additions and 16 deletions

8
package-lock.json generated
View File

@ -17,7 +17,7 @@
"mongoose": "^8.11.0", "mongoose": "^8.11.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"typescript": ">=4.7.4 <5.6.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", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0" "winston-daily-rotate-file": "^5.0.0"
@ -4073,9 +4073,9 @@
} }
}, },
"node_modules/warframe-public-export-plus": { "node_modules/warframe-public-export-plus": {
"version": "0.5.42", "version": "0.5.43",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.42.tgz", "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.43.tgz",
"integrity": "sha512-up3P5bLKD42Xkr3o7TX9WUwvpJzK88aQTLZ2bB6QWUHdsJxl/Z3TBn+HSd3eouIDTMVUzbTDeDPosSw7TcLegA==" "integrity": "sha512-LeF7HmsjOPsJDtgr66x3iMEIAQgcxKNM54VG895FTemgHLLo34UGDyeS1yIfY67WxxbTUgW3MkHQLlCEJXD14w=="
}, },
"node_modules/warframe-riven-info": { "node_modules/warframe-riven-info": {
"version": "0.1.2", "version": "0.1.2",

View File

@ -22,7 +22,7 @@
"mongoose": "^8.11.0", "mongoose": "^8.11.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"typescript": ">=4.7.4 <5.6.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", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0" "winston-daily-rotate-file": "^5.0.0"

View File

@ -2,7 +2,14 @@ import { RequestHandler } from "express";
import { getGuildForRequestEx, getGuildVault, scaleRequiredCount } from "@/src/services/guildService"; import { getGuildForRequestEx, getGuildVault, scaleRequiredCount } from "@/src/services/guildService";
import { ExportDojoRecipes, IDojoResearch } from "warframe-public-export-plus"; import { ExportDojoRecipes, IDojoResearch } from "warframe-public-export-plus";
import { getAccountIdForRequest } from "@/src/services/loginService"; 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 { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { config } from "@/src/services/configService"; import { config } from "@/src/services/configService";
@ -127,6 +134,20 @@ export const guildTechController: RequestHandler = async (req, res) => {
Recipes: recipeChanges 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 { } else {
throw new Error(`unknown guildTech action: ${data.Action}`); throw new Error(`unknown guildTech action: ${data.Action}`);
} }
@ -144,10 +165,12 @@ const processFundedProject = (
} }
}; };
type TGuildTechRequest = { type TGuildTechRequest =
Action: string; | ({
} & Partial<IGuildTechStartFields> & Action: string;
Partial<IGuildTechContributeFields>; } & Partial<IGuildTechStartFields> &
Partial<IGuildTechContributeFields>)
| IGuildTechFabricateRequest;
interface IGuildTechStartFields { interface IGuildTechStartFields {
Mode: "Guild"; Mode: "Guild";
@ -164,3 +187,9 @@ interface IGuildTechContributeFields {
VaultCredits: number; VaultCredits: number;
VaultMiscItems: IMiscItem[]; VaultMiscItems: IMiscItem[];
} }
interface IGuildTechFabricateRequest {
Action: "Fabricate";
Mode: "Guild";
RecipeType: string;
}

View File

@ -6,6 +6,7 @@ import {
ExportDrones, ExportDrones,
ExportGear, ExportGear,
ExportMisc, ExportMisc,
ExportRailjackWeapons,
ExportRecipes, ExportRecipes,
ExportRelics, ExportRelics,
ExportResources, ExportResources,
@ -160,6 +161,12 @@ const getItemListsController: RequestHandler = (req, response) => {
name: getString(item.name, lang) 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 = []; res.mods = [];
for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) { for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) {

View File

@ -75,7 +75,8 @@ import {
ICollectibleEntry, ICollectibleEntry,
IIncentiveState, IIncentiveState,
ISongChallenge, ISongChallenge,
ILibraryPersonalProgress ILibraryPersonalProgress,
ICrewShipWeaponDatabase
} from "../../types/inventoryTypes/inventoryTypes"; } from "../../types/inventoryTypes/inventoryTypes";
import { IOid } from "../../types/commonTypes"; import { IOid } from "../../types/commonTypes";
import { import {
@ -1037,6 +1038,25 @@ const alignmentSchema = new Schema<IAlignment>(
{ _id: false } { _id: false }
); );
const crewShipWeaponSchema2 = new Schema<ICrewShipWeaponDatabase>(
{
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<IInventoryDatabase, InventoryDocumentProps>( const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
{ {
accountOwnerId: Schema.Types.ObjectId, accountOwnerId: Schema.Types.ObjectId,
@ -1157,7 +1177,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//Default RailJack //Default RailJack
CrewShipAmmo: [typeCountSchema], CrewShipAmmo: [typeCountSchema],
CrewShipWeapons: [Schema.Types.Mixed], CrewShipWeapons: [crewShipWeaponSchema2],
CrewShipWeaponSkins: [upgradeSchema], CrewShipWeaponSkins: [upgradeSchema],
//NPC Crew and weapon //NPC Crew and weapon
@ -1404,6 +1424,7 @@ export type InventoryDocumentProps = {
WeaponSkins: Types.DocumentArray<IWeaponSkinDatabase>; WeaponSkins: Types.DocumentArray<IWeaponSkinDatabase>;
QuestKeys: Types.DocumentArray<IQuestKeyDatabase>; QuestKeys: Types.DocumentArray<IQuestKeyDatabase>;
Drones: Types.DocumentArray<IDroneDatabase>; Drones: Types.DocumentArray<IDroneDatabase>;
CrewShipWeapons: Types.DocumentArray<ICrewShipWeaponDatabase>;
CrewShipWeaponSkins: Types.DocumentArray<IUpgradeDatabase>; CrewShipWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> }; } & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };

View File

@ -26,7 +26,8 @@ import {
ILibraryDailyTaskInfo, ILibraryDailyTaskInfo,
ICalendarProgress, ICalendarProgress,
IDroneClient, IDroneClient,
IUpgradeClient IUpgradeClient,
ICrewShipWeaponClient
} from "@/src/types/inventoryTypes/inventoryTypes"; } from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate } from "../types/genericUpdate"; import { IGenericUpdate } from "../types/genericUpdate";
import { import {
@ -54,6 +55,7 @@ import {
ExportGear, ExportGear,
ExportKeys, ExportKeys,
ExportMisc, ExportMisc,
ExportRailjackWeapons,
ExportRecipes, ExportRecipes,
ExportResources, ExportResources,
ExportSentinels, 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) { if (typeName in ExportMisc.creditBundles) {
const creditsTotal = ExportMisc.creditBundles[typeName] * quantity; const creditsTotal = ExportMisc.creditBundles[typeName] * quantity;
inventory.RegularCredits += creditsTotal; inventory.RegularCredits += creditsTotal;
@ -859,6 +869,7 @@ export const addCustomization = (
inventoryChanges: IInventoryChanges = {} inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => { ): IInventoryChanges => {
const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1; const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
inventoryChanges.FlavourItems ??= []; inventoryChanges.FlavourItems ??= [];
(inventoryChanges.FlavourItems as IFlavourItem[]).push( (inventoryChanges.FlavourItems as IFlavourItem[]).push(
inventory.FlavourItems[flavourItemIndex].toJSON<IFlavourItem>() inventory.FlavourItems[flavourItemIndex].toJSON<IFlavourItem>()
@ -872,6 +883,7 @@ export const addSkin = (
inventoryChanges: IInventoryChanges = {} inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => { ): IInventoryChanges => {
const index = inventory.WeaponSkins.push({ ItemType: typeName }) - 1; const index = inventory.WeaponSkins.push({ ItemType: typeName }) - 1;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
inventoryChanges.WeaponSkins ??= []; inventoryChanges.WeaponSkins ??= [];
(inventoryChanges.WeaponSkins as IWeaponSkinClient[]).push( (inventoryChanges.WeaponSkins as IWeaponSkinClient[]).push(
inventory.WeaponSkins[index].toJSON<IWeaponSkinClient>() inventory.WeaponSkins[index].toJSON<IWeaponSkinClient>()
@ -879,12 +891,27 @@ export const addSkin = (
return inventoryChanges; 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<ICrewShipWeaponClient>()
);
return inventoryChanges;
};
const addCrewShipWeaponSkin = ( const addCrewShipWeaponSkin = (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
typeName: string, typeName: string,
inventoryChanges: IInventoryChanges = {} inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => { ): IInventoryChanges => {
const index = inventory.CrewShipWeaponSkins.push({ ItemType: typeName }) - 1; const index = inventory.CrewShipWeaponSkins.push({ ItemType: typeName }) - 1;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
inventoryChanges.CrewShipWeaponSkins ??= []; inventoryChanges.CrewShipWeaponSkins ??= [];
(inventoryChanges.CrewShipWeaponSkins as IUpgradeClient[]).push( (inventoryChanges.CrewShipWeaponSkins as IUpgradeClient[]).push(
inventory.CrewShipWeaponSkins[index].toJSON<IUpgradeClient>() inventory.CrewShipWeaponSkins[index].toJSON<IUpgradeClient>()

View File

@ -31,6 +31,7 @@ export interface IInventoryDatabase
| "WeaponSkins" | "WeaponSkins"
| "Upgrades" | "Upgrades"
| "CrewShipSalvagedWeaponSkins" | "CrewShipSalvagedWeaponSkins"
| "CrewShipWeapons"
| "CrewShipWeaponSkins" | "CrewShipWeaponSkins"
| "AdultOperatorLoadOuts" | "AdultOperatorLoadOuts"
| "OperatorLoadOuts" | "OperatorLoadOuts"
@ -56,6 +57,7 @@ export interface IInventoryDatabase
WeaponSkins: IWeaponSkinDatabase[]; WeaponSkins: IWeaponSkinDatabase[];
Upgrades: IUpgradeDatabase[]; Upgrades: IUpgradeDatabase[];
CrewShipSalvagedWeaponSkins: IUpgradeDatabase[]; CrewShipSalvagedWeaponSkins: IUpgradeDatabase[];
CrewShipWeapons: ICrewShipWeaponDatabase[];
CrewShipWeaponSkins: IUpgradeDatabase[]; CrewShipWeaponSkins: IUpgradeDatabase[];
AdultOperatorLoadOuts: IOperatorConfigDatabase[]; AdultOperatorLoadOuts: IOperatorConfigDatabase[];
OperatorLoadOuts: IOperatorConfigDatabase[]; OperatorLoadOuts: IOperatorConfigDatabase[];
@ -289,8 +291,8 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
PlayerSkills: IPlayerSkills; PlayerSkills: IPlayerSkills;
CrewShipAmmo: IConsumable[]; CrewShipAmmo: IConsumable[];
CrewShipSalvagedWeaponSkins: IUpgradeClient[]; CrewShipSalvagedWeaponSkins: IUpgradeClient[];
CrewShipWeapons: ICrewShipWeapon[]; CrewShipWeapons: ICrewShipWeaponClient[];
CrewShipSalvagedWeapons: ICrewShipWeapon[]; CrewShipSalvagedWeapons: IEquipmentClient[];
CrewShipWeaponSkins: IUpgradeClient[]; CrewShipWeaponSkins: IUpgradeClient[];
TradeBannedUntil?: IMongoDate; TradeBannedUntil?: IMongoDate;
PlayedParkourTutorial: boolean; PlayedParkourTutorial: boolean;
@ -428,7 +430,8 @@ export enum InventorySlot {
SPACESUITS = "SpaceSuitBin", SPACESUITS = "SpaceSuitBin",
MECHSUITS = "MechBin", MECHSUITS = "MechBin",
PVE_LOADOUTS = "PveBonusLoadoutBin", PVE_LOADOUTS = "PveBonusLoadoutBin",
SENTINELS = "SentinelBin" SENTINELS = "SentinelBin",
RJ_COMPONENT_AND_ARMAMENTS = "CrewShipSalvageBin"
} }
export interface ISlots { export interface ISlots {
@ -489,11 +492,23 @@ export interface IFlavourItem {
export type IMiscItem = ITypeCount; export type IMiscItem = ITypeCount;
// inventory.CrewShips[0].Weapon
export interface ICrewShipWeapon { export interface ICrewShipWeapon {
PILOT: ICrewShipPilotWeapon; PILOT: ICrewShipPilotWeapon;
PORT_GUNS: ICrewShipPortGuns; PORT_GUNS: ICrewShipPortGuns;
} }
// inventory.CrewShipWeapons
export interface ICrewShipWeaponClient {
ItemType: string;
ItemId: IOid;
}
export interface ICrewShipWeaponDatabase {
ItemType: string;
_id: Types.ObjectId;
}
export interface ICrewShipPilotWeapon { export interface ICrewShipPilotWeapon {
PRIMARY_A: IEquipmentSelection; PRIMARY_A: IEquipmentSelection;
SECONDARY_A: IEquipmentSelection; SECONDARY_A: IEquipmentSelection;