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: dojo research (#689)

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

代码差异

7 个文件 +154 -17
Modified src/controllers/api/createGuildController.ts +4 -3
@@ -3,9 +3,8 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
3 3 import { getJSONfromString } from "@/src/helpers/stringHelpers";
4 4 import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
5 5 import { Guild } from "@/src/models/guildModel";
6 import { ICreateGuildRequest } from "@/src/types/guildTypes";
7 6
8 const createGuildController: RequestHandler = async (req, res) => {
7 export const createGuildController: RequestHandler = async (req, res) => {
9 8 const accountId = await getAccountIdForRequest(req);
10 9 const payload = getJSONfromString(String(req.body)) as ICreateGuildRequest;
11 10
@@ -34,4 +33,6 @@ const createGuildController: RequestHandler = async (req, res) => {
34 33 res.json(guild);
35 34 };
36 35
37 export { createGuildController };
36 interface ICreateGuildRequest {
37 guildName: string;
38 }
Modified src/controllers/api/guildTechController.ts +97 -2
@@ -1,5 +1,100 @@
1 1 import { RequestHandler } from "express";
2 import { getGuildForRequestEx } from "@/src/services/guildService";
3 import { ExportDojoRecipes } from "warframe-public-export-plus";
4 import { getAccountIdForRequest } from "@/src/services/loginService";
5 import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService";
6 import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
7 import { IInventoryChanges } from "@/src/types/purchaseTypes";
2 8
3 export const guildTechController: RequestHandler = (_req, res) => {
4 res.status(500).end(); // This is what I got for a fresh clan.
9 export const guildTechController: RequestHandler = async (req, res) => {
10 const accountId = await getAccountIdForRequest(req);
11 const inventory = await getInventory(accountId);
12 const guild = await getGuildForRequestEx(req, inventory);
13 const data = JSON.parse(String(req.body)) as TGuildTechRequest;
14 if (data.Action == "Sync") {
15 res.json({
16 TechProjects: guild.toJSON().TechProjects
17 });
18 } else if (data.Action == "Start") {
19 const recipe = ExportDojoRecipes.research[data.RecipeType!];
20 guild.TechProjects ??= [];
21 if (!guild.TechProjects.find(x => x.ItemType == data.RecipeType)) {
22 guild.TechProjects.push({
23 ItemType: data.RecipeType!,
24 ReqCredits: scaleRequiredCount(recipe.price),
25 ReqItems: recipe.ingredients.map(x => ({
26 ItemType: x.ItemType,
27 ItemCount: scaleRequiredCount(x.ItemCount)
28 })),
29 State: 0
30 });
31 }
32 await guild.save();
33 res.end();
34 } else if (data.Action == "Contribute") {
35 const contributions = data as IGuildTechContributeFields;
36 const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!;
37 if (contributions.RegularCredits > techProject.ReqCredits) {
38 contributions.RegularCredits = techProject.ReqCredits;
39 }
40 techProject.ReqCredits -= contributions.RegularCredits;
41 const miscItemChanges = [];
42 for (const miscItem of contributions.MiscItems) {
43 const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
44 if (reqItem) {
45 if (miscItem.ItemCount > reqItem.ItemCount) {
46 miscItem.ItemCount = reqItem.ItemCount;
47 }
48 reqItem.ItemCount -= miscItem.ItemCount;
49 miscItemChanges.push({
50 ItemType: miscItem.ItemType,
51 ItemCount: miscItem.ItemCount * -1
52 });
53 }
54 }
55 addMiscItems(inventory, miscItemChanges);
56 const inventoryChanges: IInventoryChanges = {
57 ...updateCurrency(inventory, contributions.RegularCredits, false),
58 MiscItems: miscItemChanges
59 };
60
61 if (techProject.ReqCredits == 0 && !techProject.ReqItems.find(x => x.ItemCount > 0)) {
62 // This research is now fully funded.
63 techProject.State = 1;
64 const recipe = ExportDojoRecipes.research[data.RecipeType!];
65 techProject.CompletionDate = new Date(new Date().getTime() + recipe.time * 1000);
66 }
67
68 await guild.save();
69 await inventory.save();
70 res.json({
71 InventoryChanges: inventoryChanges
72 });
73 } else {
74 throw new Error(`unknown guildTech action: ${data.Action}`);
75 }
76 };
77
78 type TGuildTechRequest = {
79 Action: string;
80 } & Partial<IGuildTechStartFields> &
81 Partial<IGuildTechContributeFields>;
82
83 interface IGuildTechStartFields {
84 Mode: "Guild";
85 RecipeType: string;
86 }
87
88 interface IGuildTechContributeFields {
89 ResearchId: "";
90 RecipeType: string;
91 RegularCredits: number;
92 MiscItems: IMiscItem[];
93 VaultCredits: number;
94 VaultMiscItems: IMiscItem[];
95 }
96
97 const scaleRequiredCount = (count: number): number => {
98 // The recipes in the export are for Moon clans. For now we'll just assume we only have Ghost clans.
99 return Math.max(1, Math.trunc(count / 100));
5 100 };
Modified src/models/guildModel.ts +32 -2
@@ -1,5 +1,12 @@
1 import { IGuildDatabase, IDojoComponentDatabase } from "@/src/types/guildTypes";
1 import {
2 IGuildDatabase,
3 IDojoComponentDatabase,
4 ITechProjectDatabase,
5 ITechProjectClient
6 } from "@/src/types/guildTypes";
2 7 import { model, Schema } from "mongoose";
8 import { typeCountSchema } from "./inventoryModels/inventoryModel";
9 import { toMongoDate } from "../helpers/inventoryHelpers";
3 10
4 11 const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
5 12 pf: { type: String, required: true },
@@ -10,12 +17,35 @@ const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
10 17 CompletionTime: Date
11 18 });
12 19
20 const techProjectSchema = new Schema<ITechProjectDatabase>(
21 {
22 ItemType: String,
23 ReqCredits: Number,
24 ReqItems: [typeCountSchema],
25 State: Number,
26 CompletionDate: Date
27 },
28 { _id: false }
29 );
30
31 techProjectSchema.set("toJSON", {
32 virtuals: true,
33 transform(_doc, obj) {
34 const db = obj as ITechProjectDatabase;
35 const client = obj as ITechProjectClient;
36 if (db.CompletionDate) {
37 client.CompletionDate = toMongoDate(db.CompletionDate);
38 }
39 }
40 });
41
13 42 const guildSchema = new Schema<IGuildDatabase>(
14 43 {
15 44 Name: { type: String, required: true },
16 45 DojoComponents: [dojoComponentSchema],
17 46 DojoCapacity: { type: Number, default: 100 },
18 DojoEnergy: { type: Number, default: 5 }
47 DojoEnergy: { type: Number, default: 5 },
48 TechProjects: { type: [techProjectSchema], default: undefined }
19 49 },
20 50 { id: false }
21 51 );
Modified src/models/inventoryModels/inventoryModel.ts +1 -1
@@ -61,7 +61,7 @@ import {
61 61 import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
62 62 import { EquipmentSelectionSchema } from "./loadoutModel";
63 63
64 const typeCountSchema = new Schema<ITypeCount>({ ItemType: String, ItemCount: Number }, { _id: false });
64 export const typeCountSchema = new Schema<ITypeCount>({ ItemType: String, ItemCount: Number }, { _id: false });
65 65
66 66 const focusXPSchema = new Schema<IFocusXP>(
67 67 {
Modified src/services/guildService.ts +6 -1
@@ -2,17 +2,22 @@ import { Request } from "express";
2 2 import { getAccountIdForRequest } from "@/src/services/loginService";
3 3 import { getInventory } from "@/src/services/inventoryService";
4 4 import { Guild } from "@/src/models/guildModel";
5 import { IInventoryDatabaseDocument } from "../types/inventoryTypes/inventoryTypes";
5 6
6 7 export const getGuildForRequest = async (req: Request) => {
7 8 const accountId = await getAccountIdForRequest(req);
8 9 const inventory = await getInventory(accountId);
10 return await getGuildForRequestEx(req, inventory);
11 };
12
13 export const getGuildForRequestEx = async (req: Request, inventory: IInventoryDatabaseDocument) => {
9 14 const guildId = req.query.guildId as string;
10 15 if (!inventory.GuildId || inventory.GuildId.toString() != guildId) {
11 16 throw new Error("Account is not in the guild that it has sent a request for");
12 17 }
13 18 const guild = await Guild.findOne({ _id: guildId });
14 19 if (!guild) {
15 throw new Error("Account thinks it is a in guild that doesn't exist");
20 throw new Error("Account thinks it is in a guild that doesn't exist");
16 21 }
17 22 return guild;
18 23 };
Modified src/types/guildTypes.ts +13 -4
@@ -11,10 +11,7 @@ export interface IGuildDatabase extends IGuild {
11 11 DojoComponents?: IDojoComponentDatabase[];
12 12 DojoCapacity: number;
13 13 DojoEnergy: number;
14 }
15
16 export interface ICreateGuildRequest {
17 guildName: string;
14 TechProjects?: ITechProjectDatabase[];
18 15 }
19 16
20 17 export interface IDojoClient {
@@ -49,3 +46,15 @@ export interface IDojoComponentDatabase
49 46 pi?: Types.ObjectId;
50 47 CompletionTime?: Date;
51 48 }
49
50 export interface ITechProjectClient {
51 ItemType: string;
52 ReqCredits: number;
53 ReqItems: IMiscItem[];
54 State: number; // 0 = pending, 1 = complete
55 CompletionDate?: IMongoDate;
56 }
57
58 export interface ITechProjectDatabase extends Omit<ITechProjectClient, "CompletionDate"> {
59 CompletionDate?: Date;
60 }
Modified src/types/inventoryTypes/inventoryTypes.ts +1 -4
@@ -460,10 +460,7 @@ export interface IFlavourItem {
460 460 ItemType: string;
461 461 }
462 462
463 export interface IMiscItem {
464 ItemCount: number;
465 ItemType: string;
466 }
463 export type IMiscItem = ITypeCount;
467 464
468 465 export interface ICrewShipWeapon {
469 466 PILOT: ICrewShipPilotWeapon;