feat: personal decos in dojo & move dojo decos #1451

Merged
Sainan merged 5 commits from personal-decos into main 2025-04-04 06:02:41 -07:00
10 changed files with 140 additions and 43 deletions

8
package-lock.json generated
View File

@ -18,7 +18,7 @@
"morgan": "^1.10.0", "morgan": "^1.10.0",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"typescript": ">=5.5 <5.6.0", "typescript": ">=5.5 <5.6.0",
"warframe-public-export-plus": "^0.5.49", "warframe-public-export-plus": "^0.5.50",
"warframe-riven-info": "^0.1.2", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0" "winston-daily-rotate-file": "^5.0.0"
@ -3789,9 +3789,9 @@
} }
}, },
"node_modules/warframe-public-export-plus": { "node_modules/warframe-public-export-plus": {
"version": "0.5.49", "version": "0.5.50",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.49.tgz", "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.50.tgz",
"integrity": "sha512-11HA8qEMhFfl12W2qIjjk7fhas+/5G2yXbrOEb8FRZby6tWka0CyUnB6tLT+PCqBEIoU+kwhz0g7CLh3Zmy7Pw==" "integrity": "sha512-KlhdY/Q5sRAIn/RhmdviKBoX3gk+Jtuen0cWnFB2zqK7eKYMDtd79bKOtTPtnK9zCNzh6gFug2wEeDVam3Bwlw=="
}, },
"node_modules/warframe-riven-info": { "node_modules/warframe-riven-info": {
"version": "0.1.2", "version": "0.1.2",

View File

@ -24,7 +24,7 @@
"morgan": "^1.10.0", "morgan": "^1.10.0",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"typescript": ">=5.5 <5.6.0", "typescript": ">=5.5 <5.6.0",
"warframe-public-export-plus": "^0.5.49", "warframe-public-export-plus": "^0.5.50",
"warframe-riven-info": "^0.1.2", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0" "winston-daily-rotate-file": "^5.0.0"

View File

@ -1,7 +1,10 @@
import { Alliance, GuildMember } from "@/src/models/guildModel"; import { Alliance, GuildMember } from "@/src/models/guildModel";
import { import {
addGuildMemberMiscItemContribution, addGuildMemberMiscItemContribution,
addGuildMemberShipDecoContribution,
addVaultFusionTreasures,
addVaultMiscItems, addVaultMiscItems,
addVaultShipDecos,
getGuildForRequestEx getGuildForRequestEx
} from "@/src/services/guildService"; } from "@/src/services/guildService";
import { import {
@ -51,26 +54,21 @@ export const contributeToVaultController: RequestHandler = async (req, res) => {
} }
if (request.MiscItems.length) { if (request.MiscItems.length) {
addVaultMiscItems(guild, request.MiscItems); addVaultMiscItems(guild, request.MiscItems);
for (const item of request.MiscItems) { for (const item of request.MiscItems) {
addGuildMemberMiscItemContribution(guildMember, item); addGuildMemberMiscItemContribution(guildMember, item);
addMiscItems(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]); addMiscItems(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
} }
} }
if (request.ShipDecorations.length) { if (request.ShipDecorations.length) {
guild.VaultShipDecorations ??= []; addVaultShipDecos(guild, request.ShipDecorations);
guildMember.ShipDecorationsContributed ??= [];
for (const item of request.ShipDecorations) { for (const item of request.ShipDecorations) {
guild.VaultShipDecorations.push(item); addGuildMemberShipDecoContribution(guildMember, item);
guildMember.ShipDecorationsContributed.push(item);
addShipDecorations(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]); addShipDecorations(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
} }
} }
if (request.FusionTreasures.length) { if (request.FusionTreasures.length) {
guild.VaultFusionTreasures ??= []; addVaultFusionTreasures(guild, request.FusionTreasures);
for (const item of request.FusionTreasures) { for (const item of request.FusionTreasures) {
guild.VaultFusionTreasures.push(item);
addFusionTreasures(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]); addFusionTreasures(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
} }
} }

View File

@ -4,7 +4,7 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
import { GuildPermission } from "@/src/types/guildTypes"; import { GuildPermission } from "@/src/types/guildTypes";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { Types } from "mongoose"; import { Types } from "mongoose";
import { ExportDojoRecipes } from "warframe-public-export-plus"; import { ExportDojoRecipes, ExportResources } from "warframe-public-export-plus";
export const placeDecoInComponentController: RequestHandler = async (req, res) => { export const placeDecoInComponentController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
@ -24,23 +24,37 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
} }
component.Decos ??= []; component.Decos ??= [];
const deco = if (request.MoveId) {
component.Decos[ const deco = component.Decos.find(x => x._id.equals(request.MoveId))!;
component.Decos.push({ deco.Pos = request.Pos;
_id: new Types.ObjectId(), deco.Rot = request.Rot;
Type: request.Type, } else {
Pos: request.Pos, const deco =
Rot: request.Rot, component.Decos[
Name: request.Name component.Decos.push({
}) - 1 _id: new Types.ObjectId(),
]; Type: request.Type,
Pos: request.Pos,
const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == request.Type); Rot: request.Rot,
if (meta) { Name: request.Name,
if (meta.capacityCost) { Sockets: request.Sockets
component.DecoCapacity -= meta.capacityCost; }) - 1
];
const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == request.Type);
if (meta) {
if (meta.capacityCost) {
component.DecoCapacity -= meta.capacityCost;
}
} else {
const itemType = Object.entries(ExportResources).find(arr => arr[1].deco == deco.Type)![0];
if (deco.Sockets !== undefined) {
guild.VaultFusionTreasures!.find(x => x.ItemType == itemType && x.Sockets == deco.Sockets)!.ItemCount -=
1;
} else {
guild.VaultShipDecorations!.find(x => x.ItemType == itemType)!.ItemCount -= 1;
}
} }
if (meta.price == 0 && meta.ingredients.length == 0) { if (!meta || (meta.price == 0 && meta.ingredients.length == 0)) {
deco.CompletionTime = new Date(); deco.CompletionTime = new Date();
} }
} }
@ -56,4 +70,9 @@ interface IPlaceDecoInComponentRequest {
Pos: number[]; Pos: number[];
Rot: number[]; Rot: number[];
Name?: string; Name?: string;
Sockets?: number;
Scale?: number; // only provided alongside MoveId and seems to always be 1
MoveId?: string;
ShipDeco?: boolean;
VaultDeco?: boolean;
} }

View File

@ -17,16 +17,19 @@ import {
} from "@/src/types/guildTypes"; } from "@/src/types/guildTypes";
import { Document, Model, model, Schema, Types } from "mongoose"; import { Document, Model, model, Schema, Types } from "mongoose";
import { fusionTreasuresSchema, typeCountSchema } from "./inventoryModels/inventoryModel"; import { fusionTreasuresSchema, typeCountSchema } from "./inventoryModels/inventoryModel";
import { pictureFrameInfoSchema } from "./personalRoomsModel";
const dojoDecoSchema = new Schema<IDojoDecoDatabase>({ const dojoDecoSchema = new Schema<IDojoDecoDatabase>({
Type: String, Type: String,
Pos: [Number], Pos: [Number],
Rot: [Number], Rot: [Number],
Name: String, Name: String,
Sockets: Number,
RegularCredits: Number, RegularCredits: Number,
MiscItems: { type: [typeCountSchema], default: undefined }, MiscItems: { type: [typeCountSchema], default: undefined },
CompletionTime: Date, CompletionTime: Date,
RushPlatinum: Number RushPlatinum: Number,
PictureFrameInfo: pictureFrameInfoSchema
}); });
const dojoLeaderboardEntrySchema = new Schema<IDojoLeaderboardEntry>( const dojoLeaderboardEntrySchema = new Schema<IDojoLeaderboardEntry>(

View File

@ -12,7 +12,7 @@ import {
} from "@/src/types/shipTypes"; } from "@/src/types/shipTypes";
import { Schema, model } from "mongoose"; import { Schema, model } from "mongoose";
const pictureFrameInfoSchema = new Schema<IPictureFrameInfo>( export const pictureFrameInfoSchema = new Schema<IPictureFrameInfo>(
{ {
Image: String, Image: String,
Filter: String, Filter: String,

View File

@ -21,13 +21,13 @@ import {
} from "@/src/types/guildTypes"; } from "@/src/types/guildTypes";
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers"; import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
import { Types } from "mongoose"; import { Types } from "mongoose";
import { ExportDojoRecipes, IDojoBuild, IDojoResearch } from "warframe-public-export-plus"; import { ExportDojoRecipes, ExportResources, IDojoBuild, IDojoResearch } from "warframe-public-export-plus";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { config } from "./configService"; import { config } from "./configService";
import { Account } from "../models/loginModel"; import { Account } from "../models/loginModel";
import { getRandomInt } from "./rngService"; import { getRandomInt } from "./rngService";
import { Inbox } from "../models/inboxModel"; import { Inbox } from "../models/inboxModel";
import { ITypeCount } from "../types/inventoryTypes/inventoryTypes"; import { IFusionTreasure, ITypeCount } from "../types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "../types/purchaseTypes"; import { IInventoryChanges } from "../types/purchaseTypes";
export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => { export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => {
@ -202,7 +202,9 @@ export const getDojoClient = async (
Type: deco.Type, Type: deco.Type,
Pos: deco.Pos, Pos: deco.Pos,
Rot: deco.Rot, Rot: deco.Rot,
Name: deco.Name Name: deco.Name,
Sockets: deco.Sockets,
PictureFrameInfo: deco.PictureFrameInfo
}; };
if (deco.CompletionTime) { if (deco.CompletionTime) {
clientDeco.CompletionTime = toMongoDate(deco.CompletionTime); clientDeco.CompletionTime = toMongoDate(deco.CompletionTime);
@ -285,8 +287,28 @@ export const removeDojoDeco = (
1 1
)[0]; )[0];
const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == deco.Type); const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == deco.Type);
if (meta && meta.capacityCost) { if (meta) {
component.DecoCapacity! += meta.capacityCost; if (meta.capacityCost) {
component.DecoCapacity! += meta.capacityCost;
}
} else {
const itemType = Object.entries(ExportResources).find(arr => arr[1].deco == deco.Type)![0];
if (deco.Sockets !== undefined) {
addVaultFusionTreasures(guild, [
{
ItemType: itemType,
ItemCount: 1,
Sockets: deco.Sockets
}
]);
} else {
addVaultShipDecos(guild, [
{
ItemType: itemType,
ItemCount: 1
}
]);
}
} }
moveResourcesToVault(guild, deco); moveResourcesToVault(guild, deco);
}; };
@ -311,12 +333,38 @@ export const getVaultMiscItemCount = (guild: TGuildDatabaseDocument, itemType: s
export const addVaultMiscItems = (guild: TGuildDatabaseDocument, miscItems: ITypeCount[]): void => { export const addVaultMiscItems = (guild: TGuildDatabaseDocument, miscItems: ITypeCount[]): void => {
guild.VaultMiscItems ??= []; guild.VaultMiscItems ??= [];
for (const miscItem of miscItems) { for (const item of miscItems) {
const vaultMiscItem = guild.VaultMiscItems.find(x => x.ItemType == miscItem.ItemType); const vaultItem = guild.VaultMiscItems.find(x => x.ItemType == item.ItemType);
if (vaultMiscItem) { if (vaultItem) {
vaultMiscItem.ItemCount += miscItem.ItemCount; vaultItem.ItemCount += item.ItemCount;
} else { } else {
guild.VaultMiscItems.push(miscItem); guild.VaultMiscItems.push(item);
}
}
};
export const addVaultShipDecos = (guild: TGuildDatabaseDocument, shipDecos: ITypeCount[]): void => {
guild.VaultShipDecorations ??= [];
for (const item of shipDecos) {
const vaultItem = guild.VaultShipDecorations.find(x => x.ItemType == item.ItemType);
if (vaultItem) {
vaultItem.ItemCount += item.ItemCount;
} else {
guild.VaultShipDecorations.push(item);
}
}
};
export const addVaultFusionTreasures = (guild: TGuildDatabaseDocument, fusionTreasures: IFusionTreasure[]): void => {
guild.VaultFusionTreasures ??= [];
for (const item of fusionTreasures) {
const vaultItem = guild.VaultFusionTreasures.find(
x => x.ItemType == item.ItemType && x.Sockets == item.Sockets
);
if (vaultItem) {
vaultItem.ItemCount += item.ItemCount;
} else {
guild.VaultFusionTreasures.push(item);
} }
} }
}; };
@ -331,6 +379,16 @@ export const addGuildMemberMiscItemContribution = (guildMember: IGuildMemberData
} }
}; };
export const addGuildMemberShipDecoContribution = (guildMember: IGuildMemberDatabase, item: ITypeCount): void => {
guildMember.ShipDecorationsContributed ??= [];
const shipDecoContribution = guildMember.ShipDecorationsContributed.find(x => x.ItemType == item.ItemType);
if (shipDecoContribution) {
shipDecoContribution.ItemCount += item.ItemCount;
} else {
guildMember.ShipDecorationsContributed.push(item);
}
};
export const processDojoBuildMaterialsGathered = (guild: TGuildDatabaseDocument, build: IDojoBuild): void => { export const processDojoBuildMaterialsGathered = (guild: TGuildDatabaseDocument, build: IDojoBuild): void => {
if (build.guildXpValue) { if (build.guildXpValue) {
guild.ClaimedXP ??= []; guild.ClaimedXP ??= [];

View File

@ -10,6 +10,9 @@ import { logger } from "@/src/utils/logger";
import { Types } from "mongoose"; import { Types } from "mongoose";
import { addShipDecorations, getInventory } from "./inventoryService"; import { addShipDecorations, getInventory } from "./inventoryService";
import { config } from "./configService"; import { config } from "./configService";
import { Guild } from "../models/guildModel";
import { hasGuildPermission } from "./guildService";
import { GuildPermission } from "../types/guildTypes";
export const setShipCustomizations = async ( export const setShipCustomizations = async (
accountId: string, accountId: string,
@ -154,6 +157,17 @@ export const handleSetShipDecorations = async (
}; };
export const handleSetPlacedDecoInfo = async (accountId: string, req: ISetPlacedDecoInfoRequest): Promise<void> => { export const handleSetPlacedDecoInfo = async (accountId: string, req: ISetPlacedDecoInfoRequest): Promise<void> => {
if (req.GuildId && req.ComponentId) {
const guild = (await Guild.findById(req.GuildId))!;
if (await hasGuildPermission(guild, accountId, GuildPermission.Decorator)) {
const component = guild.DojoComponents.id(req.ComponentId)!;
const deco = component.Decos!.find(x => x._id.equals(req.DecoId))!;
deco.PictureFrameInfo = req.PictureFrameInfo;
await guild.save();
}
return;
}
const personalRooms = await getPersonalRooms(accountId); const personalRooms = await getPersonalRooms(accountId);
const room = personalRooms.Ship.Rooms.find(room => room.Name === req.Room); const room = personalRooms.Ship.Rooms.find(room => room.Name === req.Room);

View File

@ -1,6 +1,7 @@
import { Types } from "mongoose"; import { Types } from "mongoose";
import { IOid, IMongoDate } from "@/src/types/commonTypes"; import { IOid, IMongoDate } from "@/src/types/commonTypes";
import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes"; import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
import { IPictureFrameInfo } from "./shipTypes";
export interface IGuildClient { export interface IGuildClient {
_id: IOid; _id: IOid;
@ -190,10 +191,12 @@ export interface IDojoDecoClient {
Pos: number[]; Pos: number[];
Rot: number[]; Rot: number[];
Name?: string; // for teleporters Name?: string; // for teleporters
Sockets?: number;
RegularCredits?: number; RegularCredits?: number;
MiscItems?: IMiscItem[]; MiscItems?: IMiscItem[];
CompletionTime?: IMongoDate; CompletionTime?: IMongoDate;
RushPlatinum?: number; RushPlatinum?: number;
PictureFrameInfo?: IPictureFrameInfo;
} }
export interface IDojoDecoDatabase extends Omit<IDojoDecoClient, "id" | "CompletionTime"> { export interface IDojoDecoDatabase extends Omit<IDojoDecoClient, "id" | "CompletionTime"> {

View File

@ -127,7 +127,9 @@ export interface ISetPlacedDecoInfoRequest {
DecoId: string; DecoId: string;
Room: string; Room: string;
PictureFrameInfo: IPictureFrameInfo; PictureFrameInfo: IPictureFrameInfo;
BootLocation: string; BootLocation?: string;
ComponentId?: string;
GuildId?: string;
} }
export interface IPictureFrameInfo { export interface IPictureFrameInfo {