feat: dojo component "collecting materials" stage (#1071)
Closes #1051 Reviewed-on: OpenWF/SpaceNinjaServer#1071 Co-authored-by: Sainan <sainan@calamity.inc> Co-committed-by: Sainan <sainan@calamity.inc>
This commit is contained in:
parent
77cadc732c
commit
67a275a009
@ -32,6 +32,7 @@
|
||||
"unlockArcanesEverywhere": true,
|
||||
"noDailyStandingLimits": true,
|
||||
"instantResourceExtractorDrones": false,
|
||||
"noDojoRoomBuildStage": true,
|
||||
"noDojoResearchCosts": true,
|
||||
"noDojoResearchTime": true,
|
||||
"spoofMasteryRank": -1
|
||||
|
19
src/controllers/api/abortDojoComponentController.ts
Normal file
19
src/controllers/api/abortDojoComponentController.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { getDojoClient, getGuildForRequestEx } from "@/src/services/guildService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const abortDojoComponentController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
const request = JSON.parse(String(req.body)) as IAbortDojoComponentRequest;
|
||||
// TODO: Move already-contributed credits & items to the clan vault
|
||||
guild.DojoComponents.pull({ _id: request.ComponentId });
|
||||
await guild.save();
|
||||
res.json(getDojoClient(guild, 0));
|
||||
};
|
||||
|
||||
export interface IAbortDojoComponentRequest {
|
||||
ComponentId: string;
|
||||
}
|
82
src/controllers/api/contributeToDojoComponentController.ts
Normal file
82
src/controllers/api/contributeToDojoComponentController.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { getDojoClient, getGuildForRequestEx, scaleRequiredCount } from "@/src/services/guildService";
|
||||
import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
|
||||
export const contributeToDojoComponentController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
const guild = await getGuildForRequestEx(req, inventory);
|
||||
const request = JSON.parse(String(req.body)) as IContributeToDojoComponentRequest;
|
||||
const component = guild.DojoComponents.id(request.ComponentId)!;
|
||||
const componentMeta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!;
|
||||
|
||||
component.RegularCredits ??= 0;
|
||||
if (component.RegularCredits + request.RegularCredits > scaleRequiredCount(componentMeta.price)) {
|
||||
request.RegularCredits = scaleRequiredCount(componentMeta.price) - component.RegularCredits;
|
||||
}
|
||||
component.RegularCredits += request.RegularCredits;
|
||||
const inventoryChanges: IInventoryChanges = updateCurrency(inventory, request.RegularCredits, false);
|
||||
|
||||
component.MiscItems ??= [];
|
||||
const miscItemChanges: IMiscItem[] = [];
|
||||
for (const ingredientContribution of request.IngredientContributions) {
|
||||
const componentMiscItem = component.MiscItems.find(x => x.ItemType == ingredientContribution.ItemType);
|
||||
if (componentMiscItem) {
|
||||
const ingredientMeta = componentMeta.ingredients.find(x => x.ItemType == ingredientContribution.ItemType)!;
|
||||
if (
|
||||
componentMiscItem.ItemCount + ingredientContribution.ItemCount >
|
||||
scaleRequiredCount(ingredientMeta.ItemCount)
|
||||
) {
|
||||
ingredientContribution.ItemCount =
|
||||
scaleRequiredCount(ingredientMeta.ItemCount) - componentMiscItem.ItemCount;
|
||||
}
|
||||
componentMiscItem.ItemCount += ingredientContribution.ItemCount;
|
||||
} else {
|
||||
component.MiscItems.push(ingredientContribution);
|
||||
}
|
||||
miscItemChanges.push({
|
||||
ItemType: ingredientContribution.ItemType,
|
||||
ItemCount: ingredientContribution.ItemCount * -1
|
||||
});
|
||||
}
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
inventoryChanges.MiscItems = miscItemChanges;
|
||||
|
||||
if (component.RegularCredits >= scaleRequiredCount(componentMeta.price)) {
|
||||
let fullyFunded = true;
|
||||
for (const ingredient of componentMeta.ingredients) {
|
||||
const componentMiscItem = component.MiscItems.find(x => x.ItemType == ingredient.ItemType);
|
||||
if (!componentMiscItem || componentMiscItem.ItemCount < scaleRequiredCount(ingredient.ItemCount)) {
|
||||
fullyFunded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fullyFunded) {
|
||||
component.RegularCredits = undefined;
|
||||
component.MiscItems = undefined;
|
||||
component.CompletionTime = new Date(Date.now() + componentMeta.time * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
await guild.save();
|
||||
await inventory.save();
|
||||
res.json({
|
||||
...getDojoClient(guild, 0, component._id),
|
||||
InventoryChanges: inventoryChanges
|
||||
});
|
||||
};
|
||||
|
||||
export interface IContributeToDojoComponentRequest {
|
||||
ComponentId: string;
|
||||
IngredientContributions: {
|
||||
ItemType: string;
|
||||
ItemCount: number;
|
||||
}[];
|
||||
RegularCredits: number;
|
||||
VaultIngredientContributions: [];
|
||||
VaultCredits: number;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getGuildForRequestEx } from "@/src/services/guildService";
|
||||
import { getGuildForRequestEx, scaleRequiredCount } from "@/src/services/guildService";
|
||||
import { ExportDojoRecipes, IDojoResearch } from "warframe-public-export-plus";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||
@ -130,8 +130,3 @@ interface IGuildTechContributeFields {
|
||||
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));
|
||||
};
|
||||
|
@ -12,7 +12,7 @@ export const setDojoComponentMessageController: RequestHandler = async (req, res
|
||||
component.Message = payload.Message;
|
||||
}
|
||||
await guild.save();
|
||||
res.json(getDojoClient(guild, 1));
|
||||
res.json(getDojoClient(guild, 0, component._id));
|
||||
};
|
||||
|
||||
type SetDojoComponentMessageRequest = { Name: string } | { Message: string };
|
||||
|
@ -3,6 +3,7 @@ import { IDojoComponentClient } from "@/src/types/guildTypes";
|
||||
import { getDojoClient, getGuildForRequest } from "@/src/services/guildService";
|
||||
import { Types } from "mongoose";
|
||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
import { config } from "@/src/services/configService";
|
||||
|
||||
interface IStartDojoRecipeRequest {
|
||||
PlacedComponent: IDojoComponentClient;
|
||||
@ -20,15 +21,20 @@ export const startDojoRecipeController: RequestHandler = async (req, res) => {
|
||||
guild.DojoEnergy += room.energy;
|
||||
}
|
||||
|
||||
const component =
|
||||
guild.DojoComponents[
|
||||
guild.DojoComponents.push({
|
||||
_id: new Types.ObjectId(),
|
||||
pf: request.PlacedComponent.pf,
|
||||
ppf: request.PlacedComponent.ppf,
|
||||
pi: new Types.ObjectId(request.PlacedComponent.pi!.$oid),
|
||||
op: request.PlacedComponent.op,
|
||||
pp: request.PlacedComponent.pp,
|
||||
CompletionTime: new Date(Date.now()) // TOOD: Omit this field & handle the "Collecting Materials" state.
|
||||
});
|
||||
pp: request.PlacedComponent.pp
|
||||
}) - 1
|
||||
];
|
||||
if (config.noDojoRoomBuildStage) {
|
||||
component.CompletionTime = new Date(Date.now());
|
||||
}
|
||||
await guild.save();
|
||||
res.json(getDojoClient(guild, 0));
|
||||
};
|
||||
|
@ -16,6 +16,8 @@ const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
|
||||
pp: String,
|
||||
Name: String,
|
||||
Message: String,
|
||||
RegularCredits: Number,
|
||||
MiscItems: { type: [typeCountSchema], default: undefined },
|
||||
CompletionTime: Date
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import express from "express";
|
||||
import { abandonLibraryDailyTaskController } from "@/src/controllers/api/abandonLibraryDailyTaskController";
|
||||
import { abortDojoComponentController } from "@/src/controllers/api/abortDojoComponentController";
|
||||
import { activateRandomModController } from "@/src/controllers/api/activateRandomModController";
|
||||
import { addFriendImageController } from "@/src/controllers/api/addFriendImageController";
|
||||
import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController";
|
||||
@ -11,6 +12,7 @@ import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompl
|
||||
import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController";
|
||||
import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
|
||||
import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
|
||||
import { contributeToDojoComponentController } from "@/src/controllers/api/contributeToDojoComponentController";
|
||||
import { createGuildController } from "@/src/controllers/api/createGuildController";
|
||||
import { creditsController } from "@/src/controllers/api/creditsController";
|
||||
import { deleteSessionController } from "@/src/controllers/api/deleteSessionController";
|
||||
@ -135,6 +137,7 @@ apiRouter.get("/surveys.php", surveysController);
|
||||
apiRouter.get("/updateSession.php", updateSessionGetController);
|
||||
|
||||
// post
|
||||
apiRouter.post("/abortDojoComponent.php", abortDojoComponentController);
|
||||
apiRouter.post("/activateRandomMod.php", activateRandomModController);
|
||||
apiRouter.post("/addFriendImage.php", addFriendImageController);
|
||||
apiRouter.post("/arcaneCommon.php", arcaneCommonController);
|
||||
@ -144,6 +147,7 @@ apiRouter.post("/changeDojoRoot.php", changeDojoRootController);
|
||||
apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
|
||||
apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
|
||||
apiRouter.post("/completeRandomModChallenge.php", completeRandomModChallengeController);
|
||||
apiRouter.post("/contributeToDojoComponent.php", contributeToDojoComponentController);
|
||||
apiRouter.post("/createGuild.php", createGuildController);
|
||||
apiRouter.post("/drones.php", dronesController);
|
||||
apiRouter.post("/endlessXp.php", endlessXpController);
|
||||
|
@ -58,6 +58,7 @@ interface IConfig {
|
||||
unlockArcanesEverywhere?: boolean;
|
||||
noDailyStandingLimits?: boolean;
|
||||
instantResourceExtractorDrones?: boolean;
|
||||
noDojoRoomBuildStage?: boolean;
|
||||
noDojoResearchCosts?: boolean;
|
||||
noDojoResearchTime?: boolean;
|
||||
spoofMasteryRank?: number;
|
||||
|
@ -5,6 +5,7 @@ import { Guild, TGuildDatabaseDocument } from "@/src/models/guildModel";
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { IDojoClient, IDojoComponentClient } from "@/src/types/guildTypes";
|
||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -27,7 +28,11 @@ export const getGuildForRequestEx = async (
|
||||
return guild;
|
||||
};
|
||||
|
||||
export const getDojoClient = (guild: TGuildDatabaseDocument, status: number): IDojoClient => {
|
||||
export const getDojoClient = (
|
||||
guild: TGuildDatabaseDocument,
|
||||
status: number,
|
||||
componentId: Types.ObjectId | undefined = undefined
|
||||
): IDojoClient => {
|
||||
const dojo: IDojoClient = {
|
||||
_id: { $oid: guild._id.toString() },
|
||||
Name: guild.Name,
|
||||
@ -41,6 +46,7 @@ export const getDojoClient = (guild: TGuildDatabaseDocument, status: number): ID
|
||||
DojoComponents: []
|
||||
};
|
||||
guild.DojoComponents.forEach(dojoComponent => {
|
||||
if (!componentId || componentId == dojoComponent._id) {
|
||||
const clientComponent: IDojoComponentClient = {
|
||||
id: toOid(dojoComponent._id),
|
||||
pf: dojoComponent.pf,
|
||||
@ -56,8 +62,17 @@ export const getDojoClient = (guild: TGuildDatabaseDocument, status: number): ID
|
||||
}
|
||||
if (dojoComponent.CompletionTime) {
|
||||
clientComponent.CompletionTime = toMongoDate(dojoComponent.CompletionTime);
|
||||
} else {
|
||||
clientComponent.RegularCredits = dojoComponent.RegularCredits;
|
||||
clientComponent.MiscItems = dojoComponent.MiscItems;
|
||||
}
|
||||
dojo.DojoComponents.push(clientComponent);
|
||||
}
|
||||
});
|
||||
return dojo;
|
||||
};
|
||||
|
||||
export 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));
|
||||
};
|
||||
|
@ -521,6 +521,10 @@
|
||||
<input class="form-check-input" type="checkbox" id="instantResourceExtractorDrones" />
|
||||
<label class="form-check-label" for="instantResourceExtractorDrones" data-loc="cheats_instantResourceExtractorDrones"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="noDojoRoomBuildStage" />
|
||||
<label class="form-check-label" for="noDojoRoomBuildStage" data-loc="cheats_noDojoRoomBuildStage"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="noDojoResearchCosts" />
|
||||
<label class="form-check-label" for="noDojoResearchCosts" data-loc="cheats_noDojoResearchCosts"></label>
|
||||
|
@ -112,6 +112,7 @@ dict = {
|
||||
cheats_unlockArcanesEverywhere: `Arcane Adapters Everywhere`,
|
||||
cheats_noDailyStandingLimits: `No Daily Standing Limits`,
|
||||
cheats_instantResourceExtractorDrones: `Instant Resource Extractor Drones`,
|
||||
cheats_noDojoRoomBuildStage: `No Dojo Room Build Stage`,
|
||||
cheats_noDojoResearchCosts: `No Dojo Research Costs`,
|
||||
cheats_noDojoResearchTime: `No Dojo Research Time`,
|
||||
cheats_spoofMasteryRank: `Spoofed Mastery Rank (-1 to disable)`,
|
||||
|
@ -113,6 +113,7 @@ dict = {
|
||||
cheats_unlockArcanesEverywhere: `Адаптеры для мистификаторов везде`,
|
||||
cheats_noDailyStandingLimits: `Без ежедневных ограничений репутации`,
|
||||
cheats_instantResourceExtractorDrones: `[UNTRANSLATED] Instant Resource Extractor Drones`,
|
||||
cheats_noDojoRoomBuildStage: `[UNTRANSLATED] No Dojo Room Build Stage`,
|
||||
cheats_noDojoResearchCosts: `[UNTRANSLATED] No Dojo Research Costs`,
|
||||
cheats_noDojoResearchTime: `[UNTRANSLATED] No Dojo Research Time`,
|
||||
cheats_spoofMasteryRank: `Подделанный ранг мастерства (-1 для отключения)`,
|
||||
|
Loading…
x
Reference in New Issue
Block a user