返回提交历史
Modified
src/controllers/api/createGuildController.ts
+4
-3
Modified
src/controllers/api/guildTechController.ts
+97
-2
Modified
src/models/guildModel.ts
+32
-2
Modified
src/models/inventoryModels/inventoryModel.ts
+1
-1
Modified
src/services/guildService.ts
+6
-1
Modified
src/types/guildTypes.ts
+13
-4
Modified
src/types/inventoryTypes/inventoryTypes.ts
+1
-4
XFEstudio/XFESpaceNinjaServer
feat: dojo research (#689)
e0ff240d
代码差异
7 个文件
+154
-17
@@ -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
}
@@ -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
};
@@ -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
);
@@ -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
{
@@ -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
};
@@ -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
}
@@ -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;