forked from OpenWF/SpaceNinjaServer
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			main
			...
			clan-vault
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 214ade1948 | |||
| 512f3a6ae9 | |||
| f7ab620c80 | |||
| ff1083c835 | |||
| 9dd9478936 | 
@ -13,12 +13,9 @@ interface IContributeToDojoComponentRequest {
 | 
				
			|||||||
    ComponentId: string;
 | 
					    ComponentId: string;
 | 
				
			||||||
    DecoId?: string;
 | 
					    DecoId?: string;
 | 
				
			||||||
    DecoType?: string;
 | 
					    DecoType?: string;
 | 
				
			||||||
    IngredientContributions: {
 | 
					    IngredientContributions: IMiscItem[];
 | 
				
			||||||
        ItemType: string;
 | 
					 | 
				
			||||||
        ItemCount: number;
 | 
					 | 
				
			||||||
    }[];
 | 
					 | 
				
			||||||
    RegularCredits: number;
 | 
					    RegularCredits: number;
 | 
				
			||||||
    VaultIngredientContributions: [];
 | 
					    VaultIngredientContributions: IMiscItem[];
 | 
				
			||||||
    VaultCredits: number;
 | 
					    VaultCredits: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -37,13 +34,13 @@ export const contributeToDojoComponentController: RequestHandler = async (req, r
 | 
				
			|||||||
            throw new Error("attempt to contribute to a deco in an unfinished room?!");
 | 
					            throw new Error("attempt to contribute to a deco in an unfinished room?!");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const meta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!;
 | 
					        const meta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!;
 | 
				
			||||||
        await processContribution(guild, request, inventory, inventoryChanges, meta, component);
 | 
					        processContribution(guild, request, inventory, inventoryChanges, meta, component);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        // Room is past "Collecting Materials"
 | 
					        // Room is past "Collecting Materials"
 | 
				
			||||||
        if (request.DecoId) {
 | 
					        if (request.DecoId) {
 | 
				
			||||||
            const deco = component.Decos!.find(x => x._id.equals(request.DecoId))!;
 | 
					            const deco = component.Decos!.find(x => x._id.equals(request.DecoId))!;
 | 
				
			||||||
            const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == deco.Type)!;
 | 
					            const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == deco.Type)!;
 | 
				
			||||||
            await processContribution(guild, request, inventory, inventoryChanges, meta, deco);
 | 
					            processContribution(guild, request, inventory, inventoryChanges, meta, deco);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -55,46 +52,76 @@ export const contributeToDojoComponentController: RequestHandler = async (req, r
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const processContribution = async (
 | 
					const processContribution = (
 | 
				
			||||||
    guild: TGuildDatabaseDocument,
 | 
					    guild: TGuildDatabaseDocument,
 | 
				
			||||||
    request: IContributeToDojoComponentRequest,
 | 
					    request: IContributeToDojoComponentRequest,
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
    inventoryChanges: IInventoryChanges,
 | 
					    inventoryChanges: IInventoryChanges,
 | 
				
			||||||
    meta: IDojoRecipe,
 | 
					    meta: IDojoRecipe,
 | 
				
			||||||
    component: IDojoContributable
 | 
					    component: IDojoContributable
 | 
				
			||||||
): Promise<void> => {
 | 
					): void => {
 | 
				
			||||||
    component.RegularCredits ??= 0;
 | 
					    component.RegularCredits ??= 0;
 | 
				
			||||||
    if (component.RegularCredits + request.RegularCredits > scaleRequiredCount(meta.price)) {
 | 
					    if (request.RegularCredits) {
 | 
				
			||||||
        request.RegularCredits = scaleRequiredCount(meta.price) - component.RegularCredits;
 | 
					        component.RegularCredits += request.RegularCredits;
 | 
				
			||||||
 | 
					        inventoryChanges.RegularCredits = -request.RegularCredits;
 | 
				
			||||||
 | 
					        updateCurrency(inventory, request.RegularCredits, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (request.VaultCredits) {
 | 
				
			||||||
 | 
					        component.RegularCredits += request.VaultCredits;
 | 
				
			||||||
 | 
					        guild.VaultRegularCredits! -= request.VaultCredits;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (component.RegularCredits > scaleRequiredCount(meta.price)) {
 | 
				
			||||||
 | 
					        guild.VaultRegularCredits ??= 0;
 | 
				
			||||||
 | 
					        guild.VaultRegularCredits += component.RegularCredits - scaleRequiredCount(meta.price);
 | 
				
			||||||
 | 
					        component.RegularCredits = scaleRequiredCount(meta.price);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    component.RegularCredits += request.RegularCredits;
 | 
					 | 
				
			||||||
    inventoryChanges.RegularCredits = -request.RegularCredits;
 | 
					 | 
				
			||||||
    updateCurrency(inventory, request.RegularCredits, false);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    component.MiscItems ??= [];
 | 
					    component.MiscItems ??= [];
 | 
				
			||||||
    const miscItemChanges: IMiscItem[] = [];
 | 
					    if (request.VaultIngredientContributions.length) {
 | 
				
			||||||
    for (const ingredientContribution of request.IngredientContributions) {
 | 
					        for (const ingredientContribution of request.VaultIngredientContributions) {
 | 
				
			||||||
        const componentMiscItem = component.MiscItems.find(x => x.ItemType == ingredientContribution.ItemType);
 | 
					            const componentMiscItem = component.MiscItems.find(x => x.ItemType == ingredientContribution.ItemType);
 | 
				
			||||||
        if (componentMiscItem) {
 | 
					            if (componentMiscItem) {
 | 
				
			||||||
            const ingredientMeta = meta.ingredients.find(x => x.ItemType == ingredientContribution.ItemType)!;
 | 
					                const ingredientMeta = meta.ingredients.find(x => x.ItemType == ingredientContribution.ItemType)!;
 | 
				
			||||||
            if (
 | 
					                if (
 | 
				
			||||||
                componentMiscItem.ItemCount + ingredientContribution.ItemCount >
 | 
					                    componentMiscItem.ItemCount + ingredientContribution.ItemCount >
 | 
				
			||||||
                scaleRequiredCount(ingredientMeta.ItemCount)
 | 
					                    scaleRequiredCount(ingredientMeta.ItemCount)
 | 
				
			||||||
            ) {
 | 
					                ) {
 | 
				
			||||||
                ingredientContribution.ItemCount =
 | 
					                    ingredientContribution.ItemCount =
 | 
				
			||||||
                    scaleRequiredCount(ingredientMeta.ItemCount) - componentMiscItem.ItemCount;
 | 
					                        scaleRequiredCount(ingredientMeta.ItemCount) - componentMiscItem.ItemCount;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                componentMiscItem.ItemCount += ingredientContribution.ItemCount;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                component.MiscItems.push(ingredientContribution);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            componentMiscItem.ItemCount += ingredientContribution.ItemCount;
 | 
					            const vaultMiscItem = guild.VaultMiscItems!.find(x => x.ItemType == ingredientContribution.ItemType)!;
 | 
				
			||||||
        } else {
 | 
					            vaultMiscItem.ItemCount -= ingredientContribution.ItemCount;
 | 
				
			||||||
            component.MiscItems.push(ingredientContribution);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        miscItemChanges.push({
 | 
					 | 
				
			||||||
            ItemType: ingredientContribution.ItemType,
 | 
					 | 
				
			||||||
            ItemCount: ingredientContribution.ItemCount * -1
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    addMiscItems(inventory, miscItemChanges);
 | 
					    if (request.IngredientContributions.length) {
 | 
				
			||||||
    inventoryChanges.MiscItems = miscItemChanges;
 | 
					        const miscItemChanges: IMiscItem[] = [];
 | 
				
			||||||
 | 
					        for (const ingredientContribution of request.IngredientContributions) {
 | 
				
			||||||
 | 
					            const componentMiscItem = component.MiscItems.find(x => x.ItemType == ingredientContribution.ItemType);
 | 
				
			||||||
 | 
					            if (componentMiscItem) {
 | 
				
			||||||
 | 
					                const ingredientMeta = meta.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(meta.price)) {
 | 
					    if (component.RegularCredits >= scaleRequiredCount(meta.price)) {
 | 
				
			||||||
        let fullyFunded = true;
 | 
					        let fullyFunded = true;
 | 
				
			||||||
@ -106,12 +133,6 @@ const processContribution = async (
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (fullyFunded) {
 | 
					        if (fullyFunded) {
 | 
				
			||||||
            if (request.IngredientContributions.length) {
 | 
					 | 
				
			||||||
                // We've already updated subpaths of MiscItems, we need to allow MongoDB to save this before we remove MiscItems.
 | 
					 | 
				
			||||||
                await guild.save();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            component.RegularCredits = undefined;
 | 
					 | 
				
			||||||
            component.MiscItems = undefined;
 | 
					 | 
				
			||||||
            component.CompletionTime = new Date(Date.now() + meta.time * 1000);
 | 
					            component.CompletionTime = new Date(Date.now() + meta.time * 1000);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										49
									
								
								src/controllers/api/contributeToVaultController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/controllers/api/contributeToVaultController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					import { getGuildForRequestEx } from "@/src/services/guildService";
 | 
				
			||||||
 | 
					import { addFusionTreasures, addMiscItems, addShipDecorations, getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
 | 
					import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const contributeToVaultController: 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 IContributeToVaultRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (request.RegularCredits) {
 | 
				
			||||||
 | 
					        guild.VaultRegularCredits ??= 0;
 | 
				
			||||||
 | 
					        guild.VaultRegularCredits += request.RegularCredits;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (request.MiscItems.length) {
 | 
				
			||||||
 | 
					        guild.VaultMiscItems ??= [];
 | 
				
			||||||
 | 
					        for (const item of request.MiscItems) {
 | 
				
			||||||
 | 
					            guild.VaultMiscItems.push(item);
 | 
				
			||||||
 | 
					            addMiscItems(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (request.ShipDecorations.length) {
 | 
				
			||||||
 | 
					        guild.VaultShipDecorations ??= [];
 | 
				
			||||||
 | 
					        for (const item of request.ShipDecorations) {
 | 
				
			||||||
 | 
					            guild.VaultShipDecorations.push(item);
 | 
				
			||||||
 | 
					            addShipDecorations(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (request.FusionTreasures.length) {
 | 
				
			||||||
 | 
					        guild.VaultFusionTreasures ??= [];
 | 
				
			||||||
 | 
					        for (const item of request.FusionTreasures) {
 | 
				
			||||||
 | 
					            guild.VaultFusionTreasures.push(item);
 | 
				
			||||||
 | 
					            addFusionTreasures(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await guild.save();
 | 
				
			||||||
 | 
					    await inventory.save();
 | 
				
			||||||
 | 
					    res.end();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IContributeToVaultRequest {
 | 
				
			||||||
 | 
					    RegularCredits: number;
 | 
				
			||||||
 | 
					    MiscItems: IMiscItem[];
 | 
				
			||||||
 | 
					    ShipDecorations: ITypeCount[];
 | 
				
			||||||
 | 
					    FusionTreasures: IFusionTreasure[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -47,4 +47,6 @@ const processContribution = (component: IDojoContributable, meta: IDojoRecipe, p
 | 
				
			|||||||
    component.CompletionTime = new Date(
 | 
					    component.CompletionTime = new Date(
 | 
				
			||||||
        component.CompletionTime!.getTime() - secondsPerPlatinum * platinumDonated * 1000
 | 
					        component.CompletionTime!.getTime() - secondsPerPlatinum * platinumDonated * 1000
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					    component.RushPlatinum ??= 0;
 | 
				
			||||||
 | 
					    component.RushPlatinum += platinumDonated;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
 | 
				
			|||||||
import { Guild } from "@/src/models/guildModel";
 | 
					import { Guild } from "@/src/models/guildModel";
 | 
				
			||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { toOid } from "@/src/helpers/inventoryHelpers";
 | 
					import { toOid } from "@/src/helpers/inventoryHelpers";
 | 
				
			||||||
 | 
					import { getGuildVault } from "@/src/services/guildService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getGuildController: RequestHandler = async (req, res) => {
 | 
					const getGuildController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
@ -62,7 +63,8 @@ const getGuildController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
                        Permissions: 4096
 | 
					                        Permissions: 4096
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                Tier: 1
 | 
					                Tier: 1,
 | 
				
			||||||
 | 
					                Vault: getGuildVault(guild)
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
import { getGuildForRequestEx, scaleRequiredCount } from "@/src/services/guildService";
 | 
					import { getGuildForRequestEx, getGuildVault, scaleRequiredCount } from "@/src/services/guildService";
 | 
				
			||||||
import { ExportDojoRecipes, IDojoResearch } from "warframe-public-export-plus";
 | 
					import { ExportDojoRecipes, IDojoResearch } from "warframe-public-export-plus";
 | 
				
			||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService";
 | 
					import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService";
 | 
				
			||||||
@ -43,10 +43,35 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    } else if (action == "Contribute") {
 | 
					    } else if (action == "Contribute") {
 | 
				
			||||||
        const contributions = data as IGuildTechContributeFields;
 | 
					        const contributions = data as IGuildTechContributeFields;
 | 
				
			||||||
        const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!;
 | 
					        const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (contributions.VaultCredits) {
 | 
				
			||||||
 | 
					            if (contributions.VaultCredits > techProject.ReqCredits) {
 | 
				
			||||||
 | 
					                contributions.VaultCredits = techProject.ReqCredits;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            techProject.ReqCredits -= contributions.VaultCredits;
 | 
				
			||||||
 | 
					            guild.VaultRegularCredits! -= contributions.VaultCredits;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (contributions.RegularCredits > techProject.ReqCredits) {
 | 
					        if (contributions.RegularCredits > techProject.ReqCredits) {
 | 
				
			||||||
            contributions.RegularCredits = techProject.ReqCredits;
 | 
					            contributions.RegularCredits = techProject.ReqCredits;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        techProject.ReqCredits -= contributions.RegularCredits;
 | 
					        techProject.ReqCredits -= contributions.RegularCredits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (contributions.VaultMiscItems.length) {
 | 
				
			||||||
 | 
					            for (const miscItem of contributions.VaultMiscItems) {
 | 
				
			||||||
 | 
					                const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
 | 
				
			||||||
 | 
					                if (reqItem) {
 | 
				
			||||||
 | 
					                    if (miscItem.ItemCount > reqItem.ItemCount) {
 | 
				
			||||||
 | 
					                        miscItem.ItemCount = reqItem.ItemCount;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    reqItem.ItemCount -= miscItem.ItemCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    const vaultMiscItem = guild.VaultMiscItems!.find(x => x.ItemType == miscItem.ItemType)!;
 | 
				
			||||||
 | 
					                    vaultMiscItem.ItemCount -= miscItem.ItemCount;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const miscItemChanges = [];
 | 
					        const miscItemChanges = [];
 | 
				
			||||||
        for (const miscItem of contributions.MiscItems) {
 | 
					        for (const miscItem of contributions.MiscItems) {
 | 
				
			||||||
            const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
 | 
					            const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
 | 
				
			||||||
@ -74,7 +99,8 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        await guild.save();
 | 
					        await guild.save();
 | 
				
			||||||
        await inventory.save();
 | 
					        await inventory.save();
 | 
				
			||||||
        res.json({
 | 
					        res.json({
 | 
				
			||||||
            InventoryChanges: inventoryChanges
 | 
					            InventoryChanges: inventoryChanges,
 | 
				
			||||||
 | 
					            Vault: getGuildVault(guild)
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    } else if (action == "Buy") {
 | 
					    } else if (action == "Buy") {
 | 
				
			||||||
        const purchase = data as IGuildTechBuyFields;
 | 
					        const purchase = data as IGuildTechBuyFields;
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import {
 | 
				
			|||||||
    IDojoDecoDatabase
 | 
					    IDojoDecoDatabase
 | 
				
			||||||
} 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 { typeCountSchema } from "./inventoryModels/inventoryModel";
 | 
					import { fusionTreasuresSchema, typeCountSchema } from "./inventoryModels/inventoryModel";
 | 
				
			||||||
import { toMongoDate } from "../helpers/inventoryHelpers";
 | 
					import { toMongoDate } from "../helpers/inventoryHelpers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dojoDecoSchema = new Schema<IDojoDecoDatabase>({
 | 
					const dojoDecoSchema = new Schema<IDojoDecoDatabase>({
 | 
				
			||||||
@ -16,7 +16,8 @@ const dojoDecoSchema = new Schema<IDojoDecoDatabase>({
 | 
				
			|||||||
    Name: String,
 | 
					    Name: String,
 | 
				
			||||||
    RegularCredits: Number,
 | 
					    RegularCredits: Number,
 | 
				
			||||||
    MiscItems: { type: [typeCountSchema], default: undefined },
 | 
					    MiscItems: { type: [typeCountSchema], default: undefined },
 | 
				
			||||||
    CompletionTime: Date
 | 
					    CompletionTime: Date,
 | 
				
			||||||
 | 
					    RushPlatinum: Number
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
 | 
					const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
 | 
				
			||||||
@ -30,6 +31,7 @@ const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
 | 
				
			|||||||
    RegularCredits: Number,
 | 
					    RegularCredits: Number,
 | 
				
			||||||
    MiscItems: { type: [typeCountSchema], default: undefined },
 | 
					    MiscItems: { type: [typeCountSchema], default: undefined },
 | 
				
			||||||
    CompletionTime: Date,
 | 
					    CompletionTime: Date,
 | 
				
			||||||
 | 
					    RushPlatinum: Number,
 | 
				
			||||||
    DestructionTime: Date,
 | 
					    DestructionTime: Date,
 | 
				
			||||||
    Decos: [dojoDecoSchema],
 | 
					    Decos: [dojoDecoSchema],
 | 
				
			||||||
    DecoCapacity: Number
 | 
					    DecoCapacity: Number
 | 
				
			||||||
@ -63,6 +65,11 @@ const guildSchema = new Schema<IGuildDatabase>(
 | 
				
			|||||||
        DojoComponents: { type: [dojoComponentSchema], default: [] },
 | 
					        DojoComponents: { type: [dojoComponentSchema], default: [] },
 | 
				
			||||||
        DojoCapacity: { type: Number, default: 100 },
 | 
					        DojoCapacity: { type: Number, default: 100 },
 | 
				
			||||||
        DojoEnergy: { type: Number, default: 5 },
 | 
					        DojoEnergy: { type: Number, default: 5 },
 | 
				
			||||||
 | 
					        VaultRegularCredits: Number,
 | 
				
			||||||
 | 
					        VaultPremiumCredits: Number,
 | 
				
			||||||
 | 
					        VaultMiscItems: { type: [typeCountSchema], default: undefined },
 | 
				
			||||||
 | 
					        VaultShipDecorations: { type: [typeCountSchema], default: undefined },
 | 
				
			||||||
 | 
					        VaultFusionTreasures: { type: [fusionTreasuresSchema], default: undefined },
 | 
				
			||||||
        TechProjects: { type: [techProjectSchema], default: undefined }
 | 
					        TechProjects: { type: [techProjectSchema], default: undefined }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    { id: false }
 | 
					    { id: false }
 | 
				
			||||||
 | 
				
			|||||||
@ -552,7 +552,7 @@ questKeysSchema.set("toJSON", {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fusionTreasuresSchema = new Schema<IFusionTreasure>().add(typeCountSchema).add({ Sockets: Number });
 | 
					export const fusionTreasuresSchema = new Schema<IFusionTreasure>().add(typeCountSchema).add({ Sockets: Number });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const spectreLoadoutsSchema = new Schema<ISpectreLoadout>(
 | 
					const spectreLoadoutsSchema = new Schema<ISpectreLoadout>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,7 @@ import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/cla
 | 
				
			|||||||
import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
 | 
					import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
 | 
				
			||||||
import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
 | 
					import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
 | 
				
			||||||
import { contributeToDojoComponentController } from "@/src/controllers/api/contributeToDojoComponentController";
 | 
					import { contributeToDojoComponentController } from "@/src/controllers/api/contributeToDojoComponentController";
 | 
				
			||||||
 | 
					import { contributeToVaultController } from "@/src/controllers/api/contributeToVaultController";
 | 
				
			||||||
import { createGuildController } from "@/src/controllers/api/createGuildController";
 | 
					import { createGuildController } from "@/src/controllers/api/createGuildController";
 | 
				
			||||||
import { creditsController } from "@/src/controllers/api/creditsController";
 | 
					import { creditsController } from "@/src/controllers/api/creditsController";
 | 
				
			||||||
import { deleteSessionController } from "@/src/controllers/api/deleteSessionController";
 | 
					import { deleteSessionController } from "@/src/controllers/api/deleteSessionController";
 | 
				
			||||||
@ -153,6 +154,7 @@ apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
 | 
				
			|||||||
apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
 | 
					apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
 | 
				
			||||||
apiRouter.post("/completeRandomModChallenge.php", completeRandomModChallengeController);
 | 
					apiRouter.post("/completeRandomModChallenge.php", completeRandomModChallengeController);
 | 
				
			||||||
apiRouter.post("/contributeToDojoComponent.php", contributeToDojoComponentController);
 | 
					apiRouter.post("/contributeToDojoComponent.php", contributeToDojoComponentController);
 | 
				
			||||||
 | 
					apiRouter.post("/contributeToVault.php", contributeToVaultController);
 | 
				
			||||||
apiRouter.post("/createGuild.php", createGuildController);
 | 
					apiRouter.post("/createGuild.php", createGuildController);
 | 
				
			||||||
apiRouter.post("/destroyDojoDeco.php", destroyDojoDecoController);
 | 
					apiRouter.post("/destroyDojoDeco.php", destroyDojoDecoController);
 | 
				
			||||||
apiRouter.post("/dojoComponentRush.php", dojoComponentRushController);
 | 
					apiRouter.post("/dojoComponentRush.php", dojoComponentRushController);
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,13 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			|||||||
import { getInventory } from "@/src/services/inventoryService";
 | 
					import { getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
import { Guild, TGuildDatabaseDocument } from "@/src/models/guildModel";
 | 
					import { Guild, TGuildDatabaseDocument } from "@/src/models/guildModel";
 | 
				
			||||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
 | 
					import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
 | 
				
			||||||
import { IDojoClient, IDojoComponentClient } from "@/src/types/guildTypes";
 | 
					import {
 | 
				
			||||||
 | 
					    IDojoClient,
 | 
				
			||||||
 | 
					    IDojoComponentClient,
 | 
				
			||||||
 | 
					    IDojoContributable,
 | 
				
			||||||
 | 
					    IDojoDecoClient,
 | 
				
			||||||
 | 
					    IGuildVault
 | 
				
			||||||
 | 
					} 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 } from "warframe-public-export-plus";
 | 
					import { ExportDojoRecipes } from "warframe-public-export-plus";
 | 
				
			||||||
@ -30,6 +36,16 @@ export const getGuildForRequestEx = async (
 | 
				
			|||||||
    return guild;
 | 
					    return guild;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getGuildVault = (guild: TGuildDatabaseDocument): IGuildVault => {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        DojoRefundRegularCredits: guild.VaultRegularCredits,
 | 
				
			||||||
 | 
					        DojoRefundMiscItems: guild.VaultMiscItems,
 | 
				
			||||||
 | 
					        DojoRefundPremiumCredits: guild.VaultPremiumCredits,
 | 
				
			||||||
 | 
					        ShipDecorations: guild.VaultShipDecorations,
 | 
				
			||||||
 | 
					        FusionTreasures: guild.VaultFusionTreasures
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getDojoClient = async (
 | 
					export const getDojoClient = async (
 | 
				
			||||||
    guild: TGuildDatabaseDocument,
 | 
					    guild: TGuildDatabaseDocument,
 | 
				
			||||||
    status: number,
 | 
					    status: number,
 | 
				
			||||||
@ -41,6 +57,7 @@ export const getDojoClient = async (
 | 
				
			|||||||
        Tier: 1,
 | 
					        Tier: 1,
 | 
				
			||||||
        FixedContributions: true,
 | 
					        FixedContributions: true,
 | 
				
			||||||
        DojoRevision: 1,
 | 
					        DojoRevision: 1,
 | 
				
			||||||
 | 
					        Vault: getGuildVault(guild),
 | 
				
			||||||
        RevisionTime: Math.round(Date.now() / 1000),
 | 
					        RevisionTime: Math.round(Date.now() / 1000),
 | 
				
			||||||
        Energy: guild.DojoEnergy,
 | 
					        Energy: guild.DojoEnergy,
 | 
				
			||||||
        Capacity: guild.DojoCapacity,
 | 
					        Capacity: guild.DojoCapacity,
 | 
				
			||||||
@ -79,15 +96,20 @@ export const getDojoClient = async (
 | 
				
			|||||||
            if (dojoComponent.Decos) {
 | 
					            if (dojoComponent.Decos) {
 | 
				
			||||||
                clientComponent.Decos = [];
 | 
					                clientComponent.Decos = [];
 | 
				
			||||||
                for (const deco of dojoComponent.Decos) {
 | 
					                for (const deco of dojoComponent.Decos) {
 | 
				
			||||||
                    clientComponent.Decos.push({
 | 
					                    const clientDeco: IDojoDecoClient = {
 | 
				
			||||||
                        id: toOid(deco._id),
 | 
					                        id: toOid(deco._id),
 | 
				
			||||||
                        Type: deco.Type,
 | 
					                        Type: deco.Type,
 | 
				
			||||||
                        Pos: deco.Pos,
 | 
					                        Pos: deco.Pos,
 | 
				
			||||||
                        Rot: deco.Rot,
 | 
					                        Rot: deco.Rot,
 | 
				
			||||||
                        CompletionTime: deco.CompletionTime ? toMongoDate(deco.CompletionTime) : undefined,
 | 
					                        Name: deco.Name
 | 
				
			||||||
                        RegularCredits: deco.RegularCredits,
 | 
					                    };
 | 
				
			||||||
                        MiscItems: deco.MiscItems
 | 
					                    if (deco.CompletionTime) {
 | 
				
			||||||
                    });
 | 
					                        clientDeco.CompletionTime = toMongoDate(deco.CompletionTime);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        clientDeco.RegularCredits = deco.RegularCredits;
 | 
				
			||||||
 | 
					                        clientDeco.MiscItems = deco.MiscItems;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    clientComponent.Decos.push(clientDeco);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            dojo.DojoComponents.push(clientComponent);
 | 
					            dojo.DojoComponents.push(clientComponent);
 | 
				
			||||||
@ -118,7 +140,8 @@ export const removeDojoRoom = (guild: TGuildDatabaseDocument, componentId: Types
 | 
				
			|||||||
        guild.DojoCapacity -= meta.capacity;
 | 
					        guild.DojoCapacity -= meta.capacity;
 | 
				
			||||||
        guild.DojoEnergy -= meta.energy;
 | 
					        guild.DojoEnergy -= meta.energy;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // TODO: Add resources spent to the clan vault
 | 
					    moveResourcesToVault(guild, component);
 | 
				
			||||||
 | 
					    component.Decos?.forEach(deco => moveResourcesToVault(guild, deco));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const removeDojoDeco = (
 | 
					export const removeDojoDeco = (
 | 
				
			||||||
@ -135,5 +158,27 @@ export const removeDojoDeco = (
 | 
				
			|||||||
    if (meta && meta.capacityCost) {
 | 
					    if (meta && meta.capacityCost) {
 | 
				
			||||||
        component.DecoCapacity! += meta.capacityCost;
 | 
					        component.DecoCapacity! += meta.capacityCost;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // TODO: Add resources spent to the clan vault
 | 
					    moveResourcesToVault(guild, deco);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const moveResourcesToVault = (guild: TGuildDatabaseDocument, component: IDojoContributable): void => {
 | 
				
			||||||
 | 
					    if (component.RegularCredits) {
 | 
				
			||||||
 | 
					        guild.VaultRegularCredits ??= 0;
 | 
				
			||||||
 | 
					        guild.VaultRegularCredits += component.RegularCredits;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (component.MiscItems) {
 | 
				
			||||||
 | 
					        guild.VaultMiscItems ??= [];
 | 
				
			||||||
 | 
					        for (const componentMiscItem of component.MiscItems) {
 | 
				
			||||||
 | 
					            const vaultMiscItem = guild.VaultMiscItems.find(x => x.ItemType == componentMiscItem.ItemType);
 | 
				
			||||||
 | 
					            if (vaultMiscItem) {
 | 
				
			||||||
 | 
					                vaultMiscItem.ItemCount += componentMiscItem.ItemCount;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                guild.VaultMiscItems.push(componentMiscItem);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (component.RushPlatinum) {
 | 
				
			||||||
 | 
					        guild.VaultPremiumCredits ??= 0;
 | 
				
			||||||
 | 
					        guild.VaultPremiumCredits += component.RushPlatinum;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { Types } from "mongoose";
 | 
					import { Types } from "mongoose";
 | 
				
			||||||
import { IOid, IMongoDate } from "@/src/types/commonTypes";
 | 
					import { IOid, IMongoDate } from "@/src/types/commonTypes";
 | 
				
			||||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IGuild {
 | 
					export interface IGuild {
 | 
				
			||||||
    Name: string;
 | 
					    Name: string;
 | 
				
			||||||
@ -11,19 +11,38 @@ export interface IGuildDatabase extends IGuild {
 | 
				
			|||||||
    DojoComponents: IDojoComponentDatabase[];
 | 
					    DojoComponents: IDojoComponentDatabase[];
 | 
				
			||||||
    DojoCapacity: number;
 | 
					    DojoCapacity: number;
 | 
				
			||||||
    DojoEnergy: number;
 | 
					    DojoEnergy: number;
 | 
				
			||||||
 | 
					    VaultRegularCredits?: number;
 | 
				
			||||||
 | 
					    VaultPremiumCredits?: number;
 | 
				
			||||||
 | 
					    VaultMiscItems?: IMiscItem[];
 | 
				
			||||||
 | 
					    VaultShipDecorations?: ITypeCount[];
 | 
				
			||||||
 | 
					    VaultFusionTreasures?: IFusionTreasure[];
 | 
				
			||||||
    TechProjects?: ITechProjectDatabase[];
 | 
					    TechProjects?: ITechProjectDatabase[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IGuildVault {
 | 
				
			||||||
 | 
					    DojoRefundRegularCredits?: number;
 | 
				
			||||||
 | 
					    DojoRefundMiscItems?: IMiscItem[];
 | 
				
			||||||
 | 
					    DojoRefundPremiumCredits?: number;
 | 
				
			||||||
 | 
					    ShipDecorations?: ITypeCount[];
 | 
				
			||||||
 | 
					    FusionTreasures?: IFusionTreasure[];
 | 
				
			||||||
 | 
					    DecoRecipes?: ITypeCount[]; // Event Trophies
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IDojoClient {
 | 
					export interface IDojoClient {
 | 
				
			||||||
    _id: IOid; // ID of the guild
 | 
					    _id: IOid; // ID of the guild
 | 
				
			||||||
    Name: string;
 | 
					    Name: string;
 | 
				
			||||||
    Tier: number;
 | 
					    Tier: number;
 | 
				
			||||||
    FixedContributions: boolean;
 | 
					    FixedContributions: boolean;
 | 
				
			||||||
    DojoRevision: number;
 | 
					    DojoRevision: number;
 | 
				
			||||||
 | 
					    AllianceId?: IOid;
 | 
				
			||||||
 | 
					    Vault?: IGuildVault;
 | 
				
			||||||
 | 
					    Class?: number; // Level
 | 
				
			||||||
    RevisionTime: number;
 | 
					    RevisionTime: number;
 | 
				
			||||||
    Energy: number;
 | 
					    Energy: number;
 | 
				
			||||||
    Capacity: number;
 | 
					    Capacity: number;
 | 
				
			||||||
    DojoRequestStatus: number;
 | 
					    DojoRequestStatus: number;
 | 
				
			||||||
 | 
					    ContentURL?: string;
 | 
				
			||||||
 | 
					    GuildEmblem?: boolean;
 | 
				
			||||||
    DojoComponents: IDojoComponentClient[];
 | 
					    DojoComponents: IDojoComponentClient[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -46,7 +65,7 @@ export interface IDojoComponentClient {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IDojoComponentDatabase
 | 
					export interface IDojoComponentDatabase
 | 
				
			||||||
    extends Omit<IDojoComponentClient, "id" | "pi" | "CompletionTime" | "RushPlatinum" | "DestructionTime" | "Decos"> {
 | 
					    extends Omit<IDojoComponentClient, "id" | "pi" | "CompletionTime" | "DestructionTime" | "Decos"> {
 | 
				
			||||||
    _id: Types.ObjectId;
 | 
					    _id: Types.ObjectId;
 | 
				
			||||||
    pi?: Types.ObjectId;
 | 
					    pi?: Types.ObjectId;
 | 
				
			||||||
    CompletionTime?: Date;
 | 
					    CompletionTime?: Date;
 | 
				
			||||||
@ -63,6 +82,7 @@ export interface IDojoDecoClient {
 | 
				
			|||||||
    RegularCredits?: number;
 | 
					    RegularCredits?: number;
 | 
				
			||||||
    MiscItems?: IMiscItem[];
 | 
					    MiscItems?: IMiscItem[];
 | 
				
			||||||
    CompletionTime?: IMongoDate;
 | 
					    CompletionTime?: IMongoDate;
 | 
				
			||||||
 | 
					    RushPlatinum?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IDojoDecoDatabase extends Omit<IDojoDecoClient, "id" | "CompletionTime"> {
 | 
					export interface IDojoDecoDatabase extends Omit<IDojoDecoClient, "id" | "CompletionTime"> {
 | 
				
			||||||
@ -70,6 +90,14 @@ export interface IDojoDecoDatabase extends Omit<IDojoDecoClient, "id" | "Complet
 | 
				
			|||||||
    CompletionTime?: Date;
 | 
					    CompletionTime?: Date;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A common subset of the database representation of rooms & decos.
 | 
				
			||||||
 | 
					export interface IDojoContributable {
 | 
				
			||||||
 | 
					    RegularCredits?: number;
 | 
				
			||||||
 | 
					    MiscItems?: IMiscItem[];
 | 
				
			||||||
 | 
					    CompletionTime?: Date;
 | 
				
			||||||
 | 
					    RushPlatinum?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ITechProjectClient {
 | 
					export interface ITechProjectClient {
 | 
				
			||||||
    ItemType: string;
 | 
					    ItemType: string;
 | 
				
			||||||
    ReqCredits: number;
 | 
					    ReqCredits: number;
 | 
				
			||||||
@ -81,9 +109,3 @@ export interface ITechProjectClient {
 | 
				
			|||||||
export interface ITechProjectDatabase extends Omit<ITechProjectClient, "CompletionDate"> {
 | 
					export interface ITechProjectDatabase extends Omit<ITechProjectClient, "CompletionDate"> {
 | 
				
			||||||
    CompletionDate?: Date;
 | 
					    CompletionDate?: Date;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface IDojoContributable {
 | 
					 | 
				
			||||||
    RegularCredits?: number;
 | 
					 | 
				
			||||||
    MiscItems?: IMiscItem[];
 | 
					 | 
				
			||||||
    CompletionTime?: Date;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user