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

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

feat: recipes that consume weapons (#1032)

Closes #720 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1032

caec5a6c
Sainan <sainan@calamity.inc>
提交于

代码差异

4 个文件 +106 -56
Modified src/controllers/api/claimCompletedRecipeController.ts +30 -7
@@ -8,6 +8,9 @@ import { IOid } from "@/src/types/commonTypes";
8 8 import { getJSONfromString } from "@/src/helpers/stringHelpers";
9 9 import { getAccountIdForRequest } from "@/src/services/loginService";
10 10 import { getInventory, updateCurrency, addItem, addMiscItems, addRecipes } from "@/src/services/inventoryService";
11 import { IInventoryChanges } from "@/src/types/purchaseTypes";
12 import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
13 import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
11 14
12 15 export interface IClaimCompletedRecipeRequest {
13 16 RecipeIds: IOid[];
@@ -37,15 +40,35 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
37 40 }
38 41
39 42 if (req.query.cancel) {
40 const currencyChanges = updateCurrency(inventory, recipe.buildPrice * -1, false);
41 addMiscItems(inventory, recipe.ingredients);
43 const inventoryChanges: IInventoryChanges = {
44 ...updateCurrency(inventory, recipe.buildPrice * -1, false)
45 };
42 46
43 await inventory.save();
44 // Not a bug: In the specific case of cancelling a recipe, InventoryChanges are expected to be the root.
45 res.json({
46 ...currencyChanges,
47 MiscItems: recipe.ingredients
47 const nonMiscItemIngredients = new Set();
48 for (const category of ["LongGuns", "Pistols", "Melee"] as const) {
49 if (pendingRecipe[category]) {
50 pendingRecipe[category].forEach(item => {
51 const index = inventory[category].push(item) - 1;
52 inventoryChanges[category] ??= [];
53 inventoryChanges[category].push(inventory[category][index].toJSON<IEquipmentClient>());
54 nonMiscItemIngredients.add(item.ItemType);
55
56 inventoryChanges.WeaponBin ??= { Slots: 0 };
57 inventoryChanges.WeaponBin.Slots -= 1;
58 });
59 }
60 }
61 const miscItemChanges: IMiscItem[] = [];
62 recipe.ingredients.forEach(ingredient => {
63 if (!nonMiscItemIngredients.has(ingredient.ItemType)) {
64 miscItemChanges.push(ingredient);
65 }
48 66 });
67 addMiscItems(inventory, miscItemChanges);
68 inventoryChanges.MiscItems = miscItemChanges;
69
70 await inventory.save();
71 res.json(inventoryChanges); // Not a bug: In the specific case of cancelling a recipe, InventoryChanges are expected to be the root.
49 72 } else {
50 73 logger.debug("Claiming Recipe", { recipe, pendingRecipe });
51 74
Modified src/controllers/api/startRecipeController.ts +34 -17
@@ -7,6 +7,8 @@ import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inven
7 7 import { unixTimesInMs } from "@/src/constants/timeConstants";
8 8 import { Types } from "mongoose";
9 9 import { ISpectreLoadout } from "@/src/types/inventoryTypes/inventoryTypes";
10 import { toOid } from "@/src/helpers/inventoryHelpers";
11 import { ExportWeapons } from "warframe-public-export-plus";
10 12
11 13 interface IStartRecipeRequest {
12 14 RecipeName: string;
@@ -26,23 +28,40 @@ export const startRecipeController: RequestHandler = async (req, res) => {
26 28 throw new Error(`unknown recipe ${recipeName}`);
27 29 }
28 30
29 const ingredientsInverse = recipe.ingredients.map(component => ({
30 ItemType: component.ItemType,
31 ItemCount: component.ItemCount * -1
32 }));
33
34 31 const inventory = await getInventory(accountId);
35 32 updateCurrency(inventory, recipe.buildPrice, false);
36 addMiscItems(inventory, ingredientsInverse);
37 33
38 //buildtime is in seconds
39 const completionDate = new Date(Date.now() + recipe.buildTime * unixTimesInMs.second);
34 const pr =
35 inventory.PendingRecipes[
36 inventory.PendingRecipes.push({
37 ItemType: recipeName,
38 CompletionDate: new Date(Date.now() + recipe.buildTime * unixTimesInMs.second),
39 _id: new Types.ObjectId()
40 }) - 1
41 ];
40 42
41 inventory.PendingRecipes.push({
42 ItemType: recipeName,
43 CompletionDate: completionDate,
44 _id: new Types.ObjectId()
45 });
43 for (let i = 0; i != recipe.ingredients.length; ++i) {
44 if (startRecipeRequest.Ids[i]) {
45 const category = ExportWeapons[recipe.ingredients[i].ItemType].productCategory;
46 if (category != "LongGuns" && category != "Pistols" && category != "Melee") {
47 throw new Error(`unexpected equipment ingredient type: ${category}`);
48 }
49 const equipmentIndex = inventory[category].findIndex(x => x._id.equals(startRecipeRequest.Ids[i]));
50 if (equipmentIndex == -1) {
51 throw new Error(`could not find equipment item to use for recipe`);
52 }
53 pr[category] ??= [];
54 pr[category].push(inventory[category][equipmentIndex]);
55 inventory[category].splice(equipmentIndex, 1);
56 } else {
57 addMiscItems(inventory, [
58 {
59 ItemType: recipe.ingredients[i].ItemType,
60 ItemCount: recipe.ingredients[i].ItemCount * -1
61 }
62 ]);
63 }
64 }
46 65
47 66 if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") {
48 67 const spectreLoadout: ISpectreLoadout = {
@@ -98,9 +117,7 @@ export const startRecipeController: RequestHandler = async (req, res) => {
98 117 }
99 118 }
100 119
101 const newInventory = await inventory.save();
120 await inventory.save();
102 121
103 res.json({
104 RecipeId: { $oid: newInventory.PendingRecipes[newInventory.PendingRecipes.length - 1]._id.toString() }
105 });
122 res.json({ RecipeId: toOid(pr._id) });
106 123 };
Modified src/models/inventoryModels/inventoryModel.ts +31 -25
@@ -9,8 +9,8 @@ import {
9 9 ISlots,
10 10 IMailboxDatabase,
11 11 IDuviriInfo,
12 IPendingRecipe as IPendingRecipeDatabase,
13 IPendingRecipeResponse,
12 IPendingRecipeDatabase,
13 IPendingRecipeClient,
14 14 ITypeCount,
15 15 IFocusXP,
16 16 IFocusUpgrade,
@@ -108,29 +108,6 @@ const focusUpgradeSchema = new Schema<IFocusUpgrade>(
108 108 { _id: false }
109 109 );
110 110
111 const pendingRecipeSchema = new Schema<IPendingRecipeDatabase>(
112 {
113 ItemType: String,
114 CompletionDate: Date
115 },
116 { id: false }
117 );
118
119 pendingRecipeSchema.virtual("ItemId").get(function () {
120 return { $oid: this._id.toString() };
121 });
122
123 pendingRecipeSchema.set("toJSON", {
124 virtuals: true,
125 transform(_document, returnedObject) {
126 delete returnedObject._id;
127 delete returnedObject.__v;
128 (returnedObject as IPendingRecipeResponse).CompletionDate = {
129 $date: { $numberLong: (returnedObject as IPendingRecipeDatabase).CompletionDate.getTime().toString() }
130 };
131 }
132 });
133
134 111 const polaritySchema = new Schema<IPolarity>(
135 112 {
136 113 Slot: Number,
@@ -865,6 +842,35 @@ equipmentKeys.forEach(key => {
865 842 equipmentFields[key] = { type: [EquipmentSchema] };
866 843 });
867 844
845 const pendingRecipeSchema = new Schema<IPendingRecipeDatabase>(
846 {
847 ItemType: String,
848 CompletionDate: Date,
849 LongGuns: { type: [EquipmentSchema], default: undefined },
850 Pistols: { type: [EquipmentSchema], default: undefined },
851 Melee: { type: [EquipmentSchema], default: undefined }
852 },
853 { id: false }
854 );
855
856 pendingRecipeSchema.virtual("ItemId").get(function () {
857 return { $oid: this._id.toString() };
858 });
859
860 pendingRecipeSchema.set("toJSON", {
861 virtuals: true,
862 transform(_document, returnedObject) {
863 delete returnedObject._id;
864 delete returnedObject.__v;
865 delete returnedObject.LongGuns;
866 delete returnedObject.Pistols;
867 delete returnedObject.Melees;
868 (returnedObject as IPendingRecipeClient).CompletionDate = {
869 $date: { $numberLong: (returnedObject as IPendingRecipeDatabase).CompletionDate.getTime().toString() }
870 };
871 }
872 });
873
868 874 const infestedFoundrySchema = new Schema<IInfestedFoundryDatabase>(
869 875 {
870 876 Name: String,
Modified src/types/inventoryTypes/inventoryTypes.ts +11 -7
@@ -49,7 +49,7 @@ export interface IInventoryDatabase
49 49 LoadOutPresets: Types.ObjectId; // LoadOutPresets changed from ILoadOutPresets to Types.ObjectId for population
50 50 Mailbox?: IMailboxDatabase;
51 51 GuildId?: Types.ObjectId;
52 PendingRecipes: IPendingRecipe[];
52 PendingRecipes: IPendingRecipeDatabase[];
53 53 QuestKeys: IQuestKeyDatabase[];
54 54 BlessingCooldown?: Date;
55 55 Ships: Types.ObjectId[];
@@ -143,10 +143,6 @@ export type TSolarMapRegion =
143 143
144 144 //TODO: perhaps split response and database into their own files
145 145
146 export interface IPendingRecipeResponse extends Omit<IPendingRecipe, "CompletionDate"> {
147 CompletionDate: IMongoDate;
148 }
149
150 146 export interface IDailyAffiliations {
151 147 DailyAffiliation: number;
152 148 DailyAffiliationPvp: number;
@@ -217,7 +213,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
217 213 XPInfo: ITypeXPItem[];
218 214 Recipes: ITypeCount[];
219 215 WeaponSkins: IWeaponSkinClient[];
220 PendingRecipes: IPendingRecipeResponse[];
216 PendingRecipes: IPendingRecipeClient[];
221 217 TrainingDate: IMongoDate;
222 218 PlayerLevel: number;
223 219 Staff?: boolean;
@@ -783,12 +779,20 @@ export interface IPendingCouponClient {
783 779 Discount: number;
784 780 }
785 781
786 export interface IPendingRecipe {
782 export interface IPendingRecipeDatabase {
787 783 ItemType: string;
788 784 CompletionDate: Date;
789 785 ItemId: IOid;
790 786 TargetItemId?: string; // likely related to liches
791 787 TargetFingerprint?: string; // likely related to liches
788 LongGuns?: IEquipmentDatabase[];
789 Pistols?: IEquipmentDatabase[];
790 Melee?: IEquipmentDatabase[];
791 }
792
793 export interface IPendingRecipeClient
794 extends Omit<IPendingRecipeDatabase, "CompletionDate" | "LongGuns" | "Pistols" | "Melee"> {
795 CompletionDate: IMongoDate;
792 796 }
793 797
794 798 export interface IPendingTrade {