返回提交历史
Modified
src/controllers/api/guildTechController.ts
+58
-28
Modified
src/models/guildModel.ts
+1
-0
Modified
src/services/guildService.ts
+15
-0
Modified
src/types/guildTypes.ts
+2
-0
XFEstudio/SpaceNinjaServer
feat: clan polychrome research (#1177)
Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1177
ab11f67f
代码差异
4 个文件
+76
-28
@@ -4,6 +4,7 @@ import {
4
4
getGuildVault,
5
5
hasAccessToDojo,
6
6
hasGuildPermission,
7
removePigmentsFromGuildMembers,
7
8
scaleRequiredCount
8
9
} from "@/src/services/guildService";
9
10
import { ExportDojoRecipes, IDojoResearch } from "warframe-public-export-plus";
@@ -28,8 +29,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
28
29
const inventory = await getInventory(accountId);
29
30
const guild = await getGuildForRequestEx(req, inventory);
30
31
const data = JSON.parse(String(req.body)) as TGuildTechRequest;
31
const action = data.Action.split(",")[0];
32
if (action == "Sync") {
32
if (data.Action == "Sync") {
33
33
let needSave = false;
34
34
const techProjects: ITechProjectClient[] = [];
35
35
if (guild.TechProjects) {
@@ -53,18 +53,18 @@ export const guildTechController: RequestHandler = async (req, res) => {
53
53
await guild.save();
54
54
}
55
55
res.json({ TechProjects: techProjects });
56
} else if (action == "Start") {
57
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
56
} else if (data.Action == "Start") {
57
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
58
58
res.status(400).send("-1").end();
59
59
return;
60
60
}
61
const recipe = ExportDojoRecipes.research[data.RecipeType!];
61
const recipe = ExportDojoRecipes.research[data.RecipeType];
62
62
guild.TechProjects ??= [];
63
63
if (!guild.TechProjects.find(x => x.ItemType == data.RecipeType)) {
64
64
const techProject =
65
65
guild.TechProjects[
66
66
guild.TechProjects.push({
67
ItemType: data.RecipeType!,
67
ItemType: data.RecipeType,
68
68
ReqCredits: config.noDojoResearchCosts ? 0 : scaleRequiredCount(recipe.price),
69
69
ReqItems: recipe.ingredients.map(x => ({
70
70
ItemType: x.ItemType,
@@ -76,16 +76,20 @@ export const guildTechController: RequestHandler = async (req, res) => {
76
76
setTechLogState(guild, techProject.ItemType, 5);
77
77
if (config.noDojoResearchCosts) {
78
78
processFundedProject(guild, techProject, recipe);
79
} else {
80
if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") {
81
guild.ActiveDojoColorResearch = data.RecipeType;
82
}
79
83
}
80
84
}
81
85
await guild.save();
82
86
res.end();
83
} else if (action == "Contribute") {
87
} else if (data.Action == "Contribute") {
84
88
if (!hasAccessToDojo(inventory)) {
85
89
res.status(400).send("-1").end();
86
90
return;
87
91
}
88
const contributions = data as IGuildTechContributeFields;
92
const contributions = data;
89
93
const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!;
90
94
91
95
if (contributions.VaultCredits) {
@@ -136,8 +140,12 @@ export const guildTechController: RequestHandler = async (req, res) => {
136
140
137
141
if (techProject.ReqCredits == 0 && !techProject.ReqItems.find(x => x.ItemCount > 0)) {
138
142
// This research is now fully funded.
139
const recipe = ExportDojoRecipes.research[data.RecipeType!];
143
const recipe = ExportDojoRecipes.research[data.RecipeType];
140
144
processFundedProject(guild, techProject, recipe);
145
if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") {
146
guild.ActiveDojoColorResearch = "";
147
await removePigmentsFromGuildMembers(guild._id);
148
}
141
149
}
142
150
143
151
await guild.save();
@@ -146,12 +154,12 @@ export const guildTechController: RequestHandler = async (req, res) => {
146
154
InventoryChanges: inventoryChanges,
147
155
Vault: getGuildVault(guild)
148
156
});
149
} else if (action == "Buy") {
157
} else if (data.Action.split(",")[0] == "Buy") {
150
158
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
151
159
res.status(400).send("-1").end();
152
160
return;
153
161
}
154
const purchase = data as IGuildTechBuyFields;
162
const purchase = data as IGuildTechBuyRequest;
155
163
const quantity = parseInt(data.Action.split(",")[1]);
156
164
const recipeChanges = [
157
165
{
@@ -173,13 +181,12 @@ export const guildTechController: RequestHandler = async (req, res) => {
173
181
Recipes: recipeChanges
174
182
}
175
183
});
176
} else if (action == "Fabricate") {
184
} else if (data.Action == "Fabricate") {
177
185
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
178
186
res.status(400).send("-1").end();
179
187
return;
180
188
}
181
const payload = data as IGuildTechFabricateRequest;
182
const recipe = ExportDojoRecipes.fabrications[payload.RecipeType];
189
const recipe = ExportDojoRecipes.fabrications[data.RecipeType];
183
190
const inventoryChanges: IInventoryChanges = updateCurrency(inventory, recipe.price, false);
184
191
inventoryChanges.MiscItems = recipe.ingredients.map(x => ({
185
192
ItemType: x.ItemType,
@@ -190,6 +197,31 @@ export const guildTechController: RequestHandler = async (req, res) => {
190
197
await inventory.save();
191
198
// Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`.
192
199
res.json({ inventoryChanges: inventoryChanges });
200
} else if (data.Action == "Pause") {
201
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
202
res.status(400).send("-1").end();
203
return;
204
}
205
const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!;
206
project.State = -2;
207
guild.ActiveDojoColorResearch = "";
208
await guild.save();
209
await removePigmentsFromGuildMembers(guild._id);
210
res.end();
211
} else if (data.Action == "Unpause") {
212
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
213
res.status(400).send("-1").end();
214
return;
215
}
216
const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!;
217
project.State = 0;
218
guild.ActiveDojoColorResearch = data.RecipeType;
219
const entry = guild.TechChanges?.find(x => x.details == data.RecipeType);
220
if (entry) {
221
entry.dateTime = new Date();
222
}
223
await guild.save();
224
res.end();
193
225
} else {
194
226
throw new Error(`unknown guildTech action: ${data.Action}`);
195
227
}
@@ -233,20 +265,24 @@ const setTechLogState = (
233
265
};
234
266
235
267
type TGuildTechRequest =
236
| ({
237
Action: string;
238
} & Partial<IGuildTechStartFields> &
239
Partial<IGuildTechContributeFields>)
240
| IGuildTechFabricateRequest;
268
| { Action: "Sync" | "SomethingElseThatWeMightNotKnowAbout" }
269
| IGuildTechBasicRequest
270
| IGuildTechContributeRequest;
241
271
242
interface IGuildTechStartFields {
272
interface IGuildTechBasicRequest {
273
Action: "Start" | "Fabricate" | "Pause" | "Unpause";
243
274
Mode: "Guild";
244
275
RecipeType: string;
245
276
}
246
277
247
type IGuildTechBuyFields = IGuildTechStartFields;
278
interface IGuildTechBuyRequest {
279
Action: string;
280
Mode: "Guild";
281
RecipeType: string;
282
}
248
283
249
interface IGuildTechContributeFields {
284
interface IGuildTechContributeRequest {
285
Action: "Contribute";
250
286
ResearchId: "";
251
287
RecipeType: string;
252
288
RegularCredits: number;
@@ -254,9 +290,3 @@ interface IGuildTechContributeFields {
254
290
VaultCredits: number;
255
291
VaultMiscItems: IMiscItem[];
256
292
}
257
258
interface IGuildTechFabricateRequest {
259
Action: "Fabricate";
260
Mode: "Guild";
261
RecipeType: string;
262
}
@@ -152,6 +152,7 @@ const guildSchema = new Schema<IGuildDatabase>(
152
152
VaultShipDecorations: { type: [typeCountSchema], default: undefined },
153
153
VaultFusionTreasures: { type: [fusionTreasuresSchema], default: undefined },
154
154
TechProjects: { type: [techProjectSchema], default: undefined },
155
ActiveDojoColorResearch: { type: String, default: "" },
155
156
Class: { type: Number, default: 0 },
156
157
XP: { type: Number, default: 0 },
157
158
ClaimedXP: { type: [String], default: undefined },
@@ -93,6 +93,7 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
93
93
TradeTax: guild.TradeTax,
94
94
Tier: 1,
95
95
Vault: getGuildVault(guild),
96
ActiveDojoColorResearch: guild.ActiveDojoColorResearch,
96
97
Class: guild.Class,
97
98
XP: guild.XP,
98
99
IsContributor: !!guild.CeremonyContributors?.find(x => x.equals(accountId)),
@@ -350,3 +351,17 @@ export const hasGuildPermissionEx = (
350
351
const rank = guild.Ranks[member.rank];
351
352
return (rank.Permissions & perm) != 0;
352
353
};
354
355
export const removePigmentsFromGuildMembers = async (guildId: string | Types.ObjectId): Promise<void> => {
356
const members = await GuildMember.find({ guildId, status: 0 }, "accountId");
357
for (const member of members) {
358
const inventory = await getInventory(member.accountId.toString(), "MiscItems");
359
const index = inventory.MiscItems.findIndex(
360
x => x.ItemType == "/Lotus/Types/Items/Research/DojoColors/GenericDojoColorPigment"
361
);
362
if (index != -1) {
363
inventory.MiscItems.splice(index, 1);
364
await inventory.save();
365
}
366
}
367
};
@@ -12,6 +12,7 @@ export interface IGuildClient {
12
12
TradeTax: number;
13
13
Tier: number;
14
14
Vault: IGuildVault;
15
ActiveDojoColorResearch: string;
15
16
Class: number;
16
17
XP: number;
17
18
IsContributor: boolean;
@@ -38,6 +39,7 @@ export interface IGuildDatabase {
38
39
VaultFusionTreasures?: IFusionTreasure[];
39
40
40
41
TechProjects?: ITechProjectDatabase[];
42
ActiveDojoColorResearch: string;
41
43
42
44
Class: number;
43
45
XP: number;