feat: dojo research
This commit is contained in:
parent
be59a631db
commit
9042d8b265
@ -1,5 +1,92 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
import { getGuildForRequestEx } from "@/src/services/guildService";
|
||||||
|
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||||
|
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
|
|
||||||
export const guildTechController: RequestHandler = (_req, res) => {
|
export const guildTechController: RequestHandler = async (req, res) => {
|
||||||
res.status(500).end(); // This is what I got for a fresh clan.
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId);
|
||||||
|
const guild = await getGuildForRequestEx(req, inventory);
|
||||||
|
const data = JSON.parse(String(req.body)) as TGuildTechRequest;
|
||||||
|
if (data.Action == "Sync") {
|
||||||
|
res.json({
|
||||||
|
TechProjects: guild.toJSON().TechProjects
|
||||||
|
});
|
||||||
|
} else if (data.Action == "Start") {
|
||||||
|
const recipe = ExportDojoRecipes.research[data.RecipeType!];
|
||||||
|
guild.TechProjects ??= [];
|
||||||
|
guild.TechProjects.push({
|
||||||
|
ItemType: data.RecipeType!,
|
||||||
|
ReqCredits: scaleRequiredCount(recipe.price),
|
||||||
|
ReqItems: recipe.ingredients.map(x => ({
|
||||||
|
ItemType: x.ItemType,
|
||||||
|
ItemCount: scaleRequiredCount(x.ItemCount)
|
||||||
|
})),
|
||||||
|
State: 0
|
||||||
|
});
|
||||||
|
await guild.save();
|
||||||
|
res.end();
|
||||||
|
} else if (data.Action == "Contribute") {
|
||||||
|
const contributions = data as IGuildTechContributeFields;
|
||||||
|
const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!;
|
||||||
|
techProject.ReqCredits -= contributions.RegularCredits;
|
||||||
|
const miscItemChanges = [];
|
||||||
|
for (const miscItem of contributions.MiscItems) {
|
||||||
|
const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
|
||||||
|
if (reqItem) {
|
||||||
|
reqItem.ItemCount -= miscItem.ItemCount;
|
||||||
|
miscItemChanges.push({
|
||||||
|
ItemType: miscItem.ItemType,
|
||||||
|
ItemCount: miscItem.ItemCount * -1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addMiscItems(inventory, miscItemChanges);
|
||||||
|
const inventoryChanges: IInventoryChanges = {
|
||||||
|
...updateCurrency(inventory, contributions.RegularCredits, false),
|
||||||
|
MiscItems: miscItemChanges
|
||||||
|
};
|
||||||
|
|
||||||
|
if (techProject.ReqCredits == 0 && !techProject.ReqItems.find(x => x.ItemCount > 0)) {
|
||||||
|
// This research is now fully funded.
|
||||||
|
techProject.State = 1;
|
||||||
|
const recipe = ExportDojoRecipes.research[data.RecipeType!];
|
||||||
|
techProject.CompletionDate = new Date(new Date().getTime() + recipe.time * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
await guild.save();
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
InventoryChanges: inventoryChanges
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error(`unknown guildTech action: ${data.Action}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type TGuildTechRequest = {
|
||||||
|
Action: string;
|
||||||
|
} & Partial<IGuildTechStartFields> &
|
||||||
|
Partial<IGuildTechContributeFields>;
|
||||||
|
|
||||||
|
interface IGuildTechStartFields {
|
||||||
|
Mode: "Guild";
|
||||||
|
RecipeType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IGuildTechContributeFields {
|
||||||
|
ResearchId: "";
|
||||||
|
RecipeType: string;
|
||||||
|
RegularCredits: number;
|
||||||
|
MiscItems: IMiscItem[];
|
||||||
|
VaultCredits: number;
|
||||||
|
VaultMiscItems: IMiscItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const scaleRequiredCount = (count: number): number => {
|
||||||
|
// The recipes in the export are for Moon clans. For now we'll just assume we only have Ghost clans.
|
||||||
|
return Math.max(1, Math.trunc(count / 100));
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import { IGuildDatabase, IDojoComponentDatabase } from "@/src/types/guildTypes";
|
import {
|
||||||
|
IGuildDatabase,
|
||||||
|
IDojoComponentDatabase,
|
||||||
|
ITechProjectDatabase,
|
||||||
|
ITechProjectClient
|
||||||
|
} from "@/src/types/guildTypes";
|
||||||
import { model, Schema } from "mongoose";
|
import { model, Schema } from "mongoose";
|
||||||
|
import { typeCountSchema } from "./inventoryModels/inventoryModel";
|
||||||
|
import { toMongoDate } from "../helpers/inventoryHelpers";
|
||||||
|
|
||||||
const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
|
const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
|
||||||
pf: { type: String, required: true },
|
pf: { type: String, required: true },
|
||||||
@ -10,12 +17,35 @@ const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
|
|||||||
CompletionTime: Date
|
CompletionTime: Date
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const techProjectSchema = new Schema<ITechProjectDatabase>(
|
||||||
|
{
|
||||||
|
ItemType: String,
|
||||||
|
ReqCredits: Number,
|
||||||
|
ReqItems: [typeCountSchema],
|
||||||
|
State: Number,
|
||||||
|
CompletionDate: Date
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
techProjectSchema.set("toJSON", {
|
||||||
|
virtuals: true,
|
||||||
|
transform(_doc, obj) {
|
||||||
|
const db = obj as ITechProjectDatabase;
|
||||||
|
const client = obj as ITechProjectClient;
|
||||||
|
if (db.CompletionDate) {
|
||||||
|
client.CompletionDate = toMongoDate(db.CompletionDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const guildSchema = new Schema<IGuildDatabase>(
|
const guildSchema = new Schema<IGuildDatabase>(
|
||||||
{
|
{
|
||||||
Name: { type: String, required: true },
|
Name: { type: String, required: true },
|
||||||
DojoComponents: [dojoComponentSchema],
|
DojoComponents: [dojoComponentSchema],
|
||||||
DojoCapacity: { type: Number, default: 100 },
|
DojoCapacity: { type: Number, default: 100 },
|
||||||
DojoEnergy: { type: Number, default: 5 }
|
DojoEnergy: { type: Number, default: 5 },
|
||||||
|
TechProjects: { type: [techProjectSchema], default: undefined }
|
||||||
},
|
},
|
||||||
{ id: false }
|
{ id: false }
|
||||||
);
|
);
|
||||||
|
@ -61,7 +61,7 @@ import {
|
|||||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||||
import { EquipmentSelectionSchema } from "./loadoutModel";
|
import { EquipmentSelectionSchema } from "./loadoutModel";
|
||||||
|
|
||||||
const typeCountSchema = new Schema<ITypeCount>({ ItemType: String, ItemCount: Number }, { _id: false });
|
export const typeCountSchema = new Schema<ITypeCount>({ ItemType: String, ItemCount: Number }, { _id: false });
|
||||||
|
|
||||||
const focusXPSchema = new Schema<IFocusXP>(
|
const focusXPSchema = new Schema<IFocusXP>(
|
||||||
{
|
{
|
||||||
|
@ -2,10 +2,15 @@ import { Request } from "express";
|
|||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { getInventory } from "@/src/services/inventoryService";
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
import { Guild } from "@/src/models/guildModel";
|
import { Guild } from "@/src/models/guildModel";
|
||||||
|
import { IInventoryDatabaseDocument } from "../types/inventoryTypes/inventoryTypes";
|
||||||
|
|
||||||
export const getGuildForRequest = async (req: Request) => {
|
export const getGuildForRequest = async (req: Request) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
|
return await getGuildForRequestEx(req, inventory);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGuildForRequestEx = async (req: Request, inventory: IInventoryDatabaseDocument) => {
|
||||||
const guildId = req.query.guildId as string;
|
const guildId = req.query.guildId as string;
|
||||||
if (!inventory.GuildId || inventory.GuildId.toString() != guildId) {
|
if (!inventory.GuildId || inventory.GuildId.toString() != guildId) {
|
||||||
throw new Error("Account is not in the guild that it has sent a request for");
|
throw new Error("Account is not in the guild that it has sent a request for");
|
||||||
|
@ -11,6 +11,7 @@ export interface IGuildDatabase extends IGuild {
|
|||||||
DojoComponents?: IDojoComponentDatabase[];
|
DojoComponents?: IDojoComponentDatabase[];
|
||||||
DojoCapacity: number;
|
DojoCapacity: number;
|
||||||
DojoEnergy: number;
|
DojoEnergy: number;
|
||||||
|
TechProjects?: ITechProjectDatabase[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDojoClient {
|
export interface IDojoClient {
|
||||||
@ -45,3 +46,15 @@ export interface IDojoComponentDatabase
|
|||||||
pi?: Types.ObjectId;
|
pi?: Types.ObjectId;
|
||||||
CompletionTime?: Date;
|
CompletionTime?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ITechProjectClient {
|
||||||
|
ItemType: string;
|
||||||
|
ReqCredits: number;
|
||||||
|
ReqItems: IMiscItem[];
|
||||||
|
State: number; // 0 = pending, 1 = complete
|
||||||
|
CompletionDate?: IMongoDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITechProjectDatabase extends Omit<ITechProjectClient, "CompletionDate"> {
|
||||||
|
CompletionDate?: Date;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user