feat: clan tiers #1378
@ -32,7 +32,7 @@ export const abortDojoComponentController: RequestHandler = async (req, res) =>
 | 
				
			|||||||
    if (request.DecoId) {
 | 
					    if (request.DecoId) {
 | 
				
			||||||
        removeDojoDeco(guild, request.ComponentId, request.DecoId);
 | 
					        removeDojoDeco(guild, request.ComponentId, request.DecoId);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        removeDojoRoom(guild, request.ComponentId);
 | 
					        await removeDojoRoom(guild, request.ComponentId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await guild.save();
 | 
					    await guild.save();
 | 
				
			||||||
 | 
				
			|||||||
@ -94,10 +94,10 @@ const processContribution = (
 | 
				
			|||||||
        component.RegularCredits += request.VaultCredits;
 | 
					        component.RegularCredits += request.VaultCredits;
 | 
				
			||||||
        guild.VaultRegularCredits! -= request.VaultCredits;
 | 
					        guild.VaultRegularCredits! -= request.VaultCredits;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (component.RegularCredits > scaleRequiredCount(meta.price)) {
 | 
					    if (component.RegularCredits > scaleRequiredCount(guild.Tier, meta.price)) {
 | 
				
			||||||
        guild.VaultRegularCredits ??= 0;
 | 
					        guild.VaultRegularCredits ??= 0;
 | 
				
			||||||
        guild.VaultRegularCredits += component.RegularCredits - scaleRequiredCount(meta.price);
 | 
					        guild.VaultRegularCredits += component.RegularCredits - scaleRequiredCount(guild.Tier, meta.price);
 | 
				
			||||||
        component.RegularCredits = scaleRequiredCount(meta.price);
 | 
					        component.RegularCredits = scaleRequiredCount(guild.Tier, meta.price);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    component.MiscItems ??= [];
 | 
					    component.MiscItems ??= [];
 | 
				
			||||||
@ -108,10 +108,10 @@ const processContribution = (
 | 
				
			|||||||
                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(guild.Tier, ingredientMeta.ItemCount)
 | 
				
			||||||
                ) {
 | 
					                ) {
 | 
				
			||||||
                    ingredientContribution.ItemCount =
 | 
					                    ingredientContribution.ItemCount =
 | 
				
			||||||
                        scaleRequiredCount(ingredientMeta.ItemCount) - componentMiscItem.ItemCount;
 | 
					                        scaleRequiredCount(guild.Tier, ingredientMeta.ItemCount) - componentMiscItem.ItemCount;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                componentMiscItem.ItemCount += ingredientContribution.ItemCount;
 | 
					                componentMiscItem.ItemCount += ingredientContribution.ItemCount;
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
@ -129,10 +129,10 @@ const processContribution = (
 | 
				
			|||||||
                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(guild.Tier, ingredientMeta.ItemCount)
 | 
				
			||||||
                ) {
 | 
					                ) {
 | 
				
			||||||
                    ingredientContribution.ItemCount =
 | 
					                    ingredientContribution.ItemCount =
 | 
				
			||||||
                        scaleRequiredCount(ingredientMeta.ItemCount) - componentMiscItem.ItemCount;
 | 
					                        scaleRequiredCount(guild.Tier, ingredientMeta.ItemCount) - componentMiscItem.ItemCount;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                componentMiscItem.ItemCount += ingredientContribution.ItemCount;
 | 
					                componentMiscItem.ItemCount += ingredientContribution.ItemCount;
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
@ -150,11 +150,14 @@ const processContribution = (
 | 
				
			|||||||
        inventoryChanges.MiscItems = miscItemChanges;
 | 
					        inventoryChanges.MiscItems = miscItemChanges;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (component.RegularCredits >= scaleRequiredCount(meta.price)) {
 | 
					    if (component.RegularCredits >= scaleRequiredCount(guild.Tier, meta.price)) {
 | 
				
			||||||
        let fullyFunded = true;
 | 
					        let fullyFunded = true;
 | 
				
			||||||
        for (const ingredient of meta.ingredients) {
 | 
					        for (const ingredient of meta.ingredients) {
 | 
				
			||||||
            const componentMiscItem = component.MiscItems.find(x => x.ItemType == ingredient.ItemType);
 | 
					            const componentMiscItem = component.MiscItems.find(x => x.ItemType == ingredient.ItemType);
 | 
				
			||||||
            if (!componentMiscItem || componentMiscItem.ItemCount < scaleRequiredCount(ingredient.ItemCount)) {
 | 
					            if (
 | 
				
			||||||
 | 
					                !componentMiscItem ||
 | 
				
			||||||
 | 
					                componentMiscItem.ItemCount < scaleRequiredCount(guild.Tier, ingredient.ItemCount)
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
                fullyFunded = false;
 | 
					                fullyFunded = false;
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import { GuildMember } from "@/src/models/guildModel";
 | 
					import { GuildMember } from "@/src/models/guildModel";
 | 
				
			||||||
import { getGuildForRequestEx } from "@/src/services/guildService";
 | 
					import { addVaultMiscItems, getGuildForRequestEx } from "@/src/services/guildService";
 | 
				
			||||||
import { addFusionTreasures, addMiscItems, addShipDecorations, getInventory } from "@/src/services/inventoryService";
 | 
					import { addFusionTreasures, addMiscItems, addShipDecorations, getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
@ -23,11 +23,17 @@ export const contributeToVaultController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        guildMember.RegularCreditsContributed += request.RegularCredits;
 | 
					        guildMember.RegularCreditsContributed += request.RegularCredits;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (request.MiscItems.length) {
 | 
					    if (request.MiscItems.length) {
 | 
				
			||||||
        guild.VaultMiscItems ??= [];
 | 
					        addVaultMiscItems(guild, request.MiscItems);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        guildMember.MiscItemsContributed ??= [];
 | 
					        guildMember.MiscItemsContributed ??= [];
 | 
				
			||||||
        for (const item of request.MiscItems) {
 | 
					        for (const item of request.MiscItems) {
 | 
				
			||||||
            guild.VaultMiscItems.push(item);
 | 
					            const miscItemContribution = guildMember.MiscItemsContributed.find(x => x.ItemType == item.ItemType);
 | 
				
			||||||
            guildMember.MiscItemsContributed.push(item);
 | 
					            if (miscItemContribution) {
 | 
				
			||||||
 | 
					                miscItemContribution.ItemCount += item.ItemCount;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                guildMember.MiscItemsContributed.push(item);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            addMiscItems(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
 | 
					            addMiscItems(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { GuildMember } from "@/src/models/guildModel";
 | 
					import { GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
 | 
				
			||||||
import { getDojoClient, getGuildForRequestEx, hasAccessToDojo, scaleRequiredCount } from "@/src/services/guildService";
 | 
					import { getDojoClient, getGuildForRequestEx, hasAccessToDojo, scaleRequiredCount } from "@/src/services/guildService";
 | 
				
			||||||
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
 | 
					import { getInventory, updateCurrency } from "@/src/services/inventoryService";
 | 
				
			||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
@ -36,10 +36,10 @@ export const dojoComponentRushController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    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)!;
 | 
				
			||||||
        processContribution(deco, meta, platinumDonated);
 | 
					        processContribution(guild, deco, meta, platinumDonated);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        const meta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!;
 | 
					        const meta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!;
 | 
				
			||||||
        processContribution(component, meta, platinumDonated);
 | 
					        processContribution(guild, component, meta, platinumDonated);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const entry = guild.RoomChanges?.find(x => x.componentId.equals(component._id));
 | 
					        const entry = guild.RoomChanges?.find(x => x.componentId.equals(component._id));
 | 
				
			||||||
        if (entry) {
 | 
					        if (entry) {
 | 
				
			||||||
@ -61,8 +61,13 @@ export const dojoComponentRushController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const processContribution = (component: IDojoContributable, meta: IDojoBuild, platinumDonated: number): void => {
 | 
					const processContribution = (
 | 
				
			||||||
    const fullPlatinumCost = scaleRequiredCount(meta.skipTimePrice);
 | 
					    guild: TGuildDatabaseDocument,
 | 
				
			||||||
 | 
					    component: IDojoContributable,
 | 
				
			||||||
 | 
					    meta: IDojoBuild,
 | 
				
			||||||
 | 
					    platinumDonated: number
 | 
				
			||||||
 | 
					): void => {
 | 
				
			||||||
 | 
					    const fullPlatinumCost = scaleRequiredCount(guild.Tier, meta.skipTimePrice);
 | 
				
			||||||
    const fullDurationSeconds = meta.time;
 | 
					    const fullDurationSeconds = meta.time;
 | 
				
			||||||
    const secondsPerPlatinum = fullDurationSeconds / fullPlatinumCost;
 | 
					    const secondsPerPlatinum = fullDurationSeconds / fullPlatinumCost;
 | 
				
			||||||
    component.CompletionTime = new Date(
 | 
					    component.CompletionTime = new Date(
 | 
				
			||||||
 | 
				
			|||||||
@ -4,10 +4,13 @@ import {
 | 
				
			|||||||
    getGuildVault,
 | 
					    getGuildVault,
 | 
				
			||||||
    hasAccessToDojo,
 | 
					    hasAccessToDojo,
 | 
				
			||||||
    hasGuildPermission,
 | 
					    hasGuildPermission,
 | 
				
			||||||
 | 
					    processFundedGuildTechProject,
 | 
				
			||||||
 | 
					    processGuildTechProjectContributionsUpdate,
 | 
				
			||||||
    removePigmentsFromGuildMembers,
 | 
					    removePigmentsFromGuildMembers,
 | 
				
			||||||
    scaleRequiredCount
 | 
					    scaleRequiredCount,
 | 
				
			||||||
 | 
					    setGuildTechLogState
 | 
				
			||||||
} from "@/src/services/guildService";
 | 
					} from "@/src/services/guildService";
 | 
				
			||||||
import { ExportDojoRecipes, IDojoResearch } from "warframe-public-export-plus";
 | 
					import { ExportDojoRecipes } from "warframe-public-export-plus";
 | 
				
			||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    addItem,
 | 
					    addItem,
 | 
				
			||||||
@ -20,8 +23,8 @@ import {
 | 
				
			|||||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
					import { IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
				
			||||||
import { config } from "@/src/services/configService";
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
import { GuildPermission, ITechProjectClient, ITechProjectDatabase } from "@/src/types/guildTypes";
 | 
					import { GuildPermission, ITechProjectClient } from "@/src/types/guildTypes";
 | 
				
			||||||
import { GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
 | 
					import { GuildMember } from "@/src/models/guildModel";
 | 
				
			||||||
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
 | 
					import { toMongoDate } from "@/src/helpers/inventoryHelpers";
 | 
				
			||||||
import { logger } from "@/src/utils/logger";
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -44,7 +47,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
                if (project.CompletionDate) {
 | 
					                if (project.CompletionDate) {
 | 
				
			||||||
                    techProject.CompletionDate = toMongoDate(project.CompletionDate);
 | 
					                    techProject.CompletionDate = toMongoDate(project.CompletionDate);
 | 
				
			||||||
                    if (Date.now() >= project.CompletionDate.getTime()) {
 | 
					                    if (Date.now() >= project.CompletionDate.getTime()) {
 | 
				
			||||||
                        needSave ||= setTechLogState(guild, project.ItemType, 4, project.CompletionDate);
 | 
					                        needSave ||= setGuildTechLogState(guild, project.ItemType, 4, project.CompletionDate);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                techProjects.push(techProject);
 | 
					                techProjects.push(techProject);
 | 
				
			||||||
@ -66,17 +69,17 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
                guild.TechProjects[
 | 
					                guild.TechProjects[
 | 
				
			||||||
                    guild.TechProjects.push({
 | 
					                    guild.TechProjects.push({
 | 
				
			||||||
                        ItemType: data.RecipeType,
 | 
					                        ItemType: data.RecipeType,
 | 
				
			||||||
                        ReqCredits: config.noDojoResearchCosts ? 0 : scaleRequiredCount(recipe.price),
 | 
					                        ReqCredits: config.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, recipe.price),
 | 
				
			||||||
                        ReqItems: recipe.ingredients.map(x => ({
 | 
					                        ReqItems: recipe.ingredients.map(x => ({
 | 
				
			||||||
                            ItemType: x.ItemType,
 | 
					                            ItemType: x.ItemType,
 | 
				
			||||||
                            ItemCount: config.noDojoResearchCosts ? 0 : scaleRequiredCount(x.ItemCount)
 | 
					                            ItemCount: config.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, x.ItemCount)
 | 
				
			||||||
                        })),
 | 
					                        })),
 | 
				
			||||||
                        State: 0
 | 
					                        State: 0
 | 
				
			||||||
                    }) - 1
 | 
					                    }) - 1
 | 
				
			||||||
                ];
 | 
					                ];
 | 
				
			||||||
            setTechLogState(guild, techProject.ItemType, 5);
 | 
					            setGuildTechLogState(guild, techProject.ItemType, 5);
 | 
				
			||||||
            if (config.noDojoResearchCosts) {
 | 
					            if (config.noDojoResearchCosts) {
 | 
				
			||||||
                processFundedProject(guild, techProject, recipe);
 | 
					                processFundedGuildTechProject(guild, techProject, recipe);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") {
 | 
					                if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") {
 | 
				
			||||||
                    guild.ActiveDojoColorResearch = data.RecipeType;
 | 
					                    guild.ActiveDojoColorResearch = data.RecipeType;
 | 
				
			||||||
@ -151,15 +154,8 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        const inventoryChanges: IInventoryChanges = updateCurrency(inventory, contributions.RegularCredits, false);
 | 
					        const inventoryChanges: IInventoryChanges = updateCurrency(inventory, contributions.RegularCredits, false);
 | 
				
			||||||
        inventoryChanges.MiscItems = miscItemChanges;
 | 
					        inventoryChanges.MiscItems = miscItemChanges;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (techProject.ReqCredits == 0 && !techProject.ReqItems.find(x => x.ItemCount > 0)) {
 | 
					        // Check if research is fully funded now.
 | 
				
			||||||
            // This research is now fully funded.
 | 
					        await processGuildTechProjectContributionsUpdate(guild, techProject);
 | 
				
			||||||
            const recipe = ExportDojoRecipes.research[data.RecipeType];
 | 
					 | 
				
			||||||
            processFundedProject(guild, techProject, recipe);
 | 
					 | 
				
			||||||
            if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") {
 | 
					 | 
				
			||||||
                guild.ActiveDojoColorResearch = "";
 | 
					 | 
				
			||||||
                await removePigmentsFromGuildMembers(guild._id);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await guild.save();
 | 
					        await guild.save();
 | 
				
			||||||
        await inventory.save();
 | 
					        await inventory.save();
 | 
				
			||||||
@ -238,43 +234,6 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const processFundedProject = (
 | 
					 | 
				
			||||||
    guild: TGuildDatabaseDocument,
 | 
					 | 
				
			||||||
    techProject: ITechProjectDatabase,
 | 
					 | 
				
			||||||
    recipe: IDojoResearch
 | 
					 | 
				
			||||||
): void => {
 | 
					 | 
				
			||||||
    techProject.State = 1;
 | 
					 | 
				
			||||||
    techProject.CompletionDate = new Date(Date.now() + (config.noDojoResearchTime ? 0 : recipe.time) * 1000);
 | 
					 | 
				
			||||||
    if (recipe.guildXpValue) {
 | 
					 | 
				
			||||||
        guild.XP += recipe.guildXpValue;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    setTechLogState(guild, techProject.ItemType, config.noDojoResearchTime ? 4 : 3, techProject.CompletionDate);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const setTechLogState = (
 | 
					 | 
				
			||||||
    guild: TGuildDatabaseDocument,
 | 
					 | 
				
			||||||
    type: string,
 | 
					 | 
				
			||||||
    state: number,
 | 
					 | 
				
			||||||
    dateTime: Date | undefined = undefined
 | 
					 | 
				
			||||||
): boolean => {
 | 
					 | 
				
			||||||
    guild.TechChanges ??= [];
 | 
					 | 
				
			||||||
    const entry = guild.TechChanges.find(x => x.details == type);
 | 
					 | 
				
			||||||
    if (entry) {
 | 
					 | 
				
			||||||
        if (entry.entryType == state) {
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        entry.dateTime = dateTime;
 | 
					 | 
				
			||||||
        entry.entryType = state;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        guild.TechChanges.push({
 | 
					 | 
				
			||||||
            dateTime: dateTime,
 | 
					 | 
				
			||||||
            entryType: state,
 | 
					 | 
				
			||||||
            details: type
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type TGuildTechRequest =
 | 
					type TGuildTechRequest =
 | 
				
			||||||
    | { Action: "Sync" | "SomethingElseThatWeMightNotKnowAbout" }
 | 
					    | { Action: "Sync" | "SomethingElseThatWeMightNotKnowAbout" }
 | 
				
			||||||
    | IGuildTechBasicRequest
 | 
					    | IGuildTechBasicRequest
 | 
				
			||||||
 | 
				
			|||||||
@ -13,16 +13,18 @@ import {
 | 
				
			|||||||
    IGuildClient,
 | 
					    IGuildClient,
 | 
				
			||||||
    IGuildMemberClient,
 | 
					    IGuildMemberClient,
 | 
				
			||||||
    IGuildMemberDatabase,
 | 
					    IGuildMemberDatabase,
 | 
				
			||||||
    IGuildVault
 | 
					    IGuildVault,
 | 
				
			||||||
 | 
					    ITechProjectDatabase
 | 
				
			||||||
} 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 } from "warframe-public-export-plus";
 | 
					import { ExportDojoRecipes, 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";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => {
 | 
					export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => {
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
@ -152,6 +154,27 @@ export const getDojoClient = async (
 | 
				
			|||||||
                        entry.entryType = 1;
 | 
					                        entry.entryType = 1;
 | 
				
			||||||
                        needSave = true;
 | 
					                        needSave = true;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let newTier: number | undefined;
 | 
				
			||||||
 | 
					                    switch (dojoComponent.pf) {
 | 
				
			||||||
 | 
					                        case "/Lotus/Levels/ClanDojo/ClanDojoBarracksShadow.level":
 | 
				
			||||||
 | 
					                            newTier = 2;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        case "/Lotus/Levels/ClanDojo/ClanDojoBarracksStorm.level":
 | 
				
			||||||
 | 
					                            newTier = 3;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        case "/Lotus/Levels/ClanDojo/ClanDojoBarracksMountain.level":
 | 
				
			||||||
 | 
					                            newTier = 4;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        case "/Lotus/Levels/ClanDojo/ClanDojoBarracksMoon.level":
 | 
				
			||||||
 | 
					                            newTier = 5;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if (newTier) {
 | 
				
			||||||
 | 
					                        logger.debug(`clan finished building barracks, updating to tier ${newTier}`);
 | 
				
			||||||
 | 
					                        await setGuildTier(guild, newTier);
 | 
				
			||||||
 | 
					                        needSave = true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (dojoComponent.DestructionTime) {
 | 
					                if (dojoComponent.DestructionTime) {
 | 
				
			||||||
                    if (Date.now() >= dojoComponent.DestructionTime.getTime()) {
 | 
					                    if (Date.now() >= dojoComponent.DestructionTime.getTime()) {
 | 
				
			||||||
@ -189,22 +212,26 @@ export const getDojoClient = async (
 | 
				
			|||||||
    if (roomsToRemove.length) {
 | 
					    if (roomsToRemove.length) {
 | 
				
			||||||
        logger.debug(`removing now-destroyed rooms`, roomsToRemove);
 | 
					        logger.debug(`removing now-destroyed rooms`, roomsToRemove);
 | 
				
			||||||
        for (const id of roomsToRemove) {
 | 
					        for (const id of roomsToRemove) {
 | 
				
			||||||
            removeDojoRoom(guild, id);
 | 
					            await removeDojoRoom(guild, id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        needSave = true;
 | 
					        needSave = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (needSave) {
 | 
					    if (needSave) {
 | 
				
			||||||
        await guild.save();
 | 
					        await guild.save();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    dojo.Tier = guild.Tier;
 | 
				
			||||||
    return dojo;
 | 
					    return dojo;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const scaleRequiredCount = (count: number): number => {
 | 
					const guildTierScalingFactors = [0.01, 0.03, 0.1, 0.3, 1];
 | 
				
			||||||
    // The recipes in the export are for Moon clans. For now we'll just assume we only have Ghost clans.
 | 
					export const scaleRequiredCount = (tier: number, count: number): number => {
 | 
				
			||||||
    return Math.max(1, Math.trunc(count / 100));
 | 
					    return Math.max(1, Math.trunc(count * guildTierScalingFactors[tier - 1]));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const removeDojoRoom = (guild: TGuildDatabaseDocument, componentId: Types.ObjectId | string): void => {
 | 
					export const removeDojoRoom = async (
 | 
				
			||||||
 | 
					    guild: TGuildDatabaseDocument,
 | 
				
			||||||
 | 
					    componentId: Types.ObjectId | string
 | 
				
			||||||
 | 
					): Promise<void> => {
 | 
				
			||||||
    const component = guild.DojoComponents.splice(
 | 
					    const component = guild.DojoComponents.splice(
 | 
				
			||||||
        guild.DojoComponents.findIndex(x => x._id.equals(componentId)),
 | 
					        guild.DojoComponents.findIndex(x => x._id.equals(componentId)),
 | 
				
			||||||
        1
 | 
					        1
 | 
				
			||||||
@ -223,6 +250,21 @@ export const removeDojoRoom = (guild: TGuildDatabaseDocument, componentId: Types
 | 
				
			|||||||
            guild.RoomChanges.splice(index, 1);
 | 
					            guild.RoomChanges.splice(index, 1);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (component.pf) {
 | 
				
			||||||
 | 
					        case "/Lotus/Levels/ClanDojo/ClanDojoBarracksShadow.level":
 | 
				
			||||||
 | 
					            await setGuildTier(guild, 1);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "/Lotus/Levels/ClanDojo/ClanDojoBarracksStorm.level":
 | 
				
			||||||
 | 
					            await setGuildTier(guild, 2);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "/Lotus/Levels/ClanDojo/ClanDojoBarracksMountain.level":
 | 
				
			||||||
 | 
					            await setGuildTier(guild, 3);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case "/Lotus/Levels/ClanDojo/ClanDojoBarracksMoon.level":
 | 
				
			||||||
 | 
					            await setGuildTier(guild, 4);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const removeDojoDeco = (
 | 
					export const removeDojoDeco = (
 | 
				
			||||||
@ -248,15 +290,7 @@ const moveResourcesToVault = (guild: TGuildDatabaseDocument, component: IDojoCon
 | 
				
			|||||||
        guild.VaultRegularCredits += component.RegularCredits;
 | 
					        guild.VaultRegularCredits += component.RegularCredits;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (component.MiscItems) {
 | 
					    if (component.MiscItems) {
 | 
				
			||||||
        guild.VaultMiscItems ??= [];
 | 
					        addVaultMiscItems(guild, component.MiscItems);
 | 
				
			||||||
        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) {
 | 
					    if (component.RushPlatinum) {
 | 
				
			||||||
        guild.VaultPremiumCredits ??= 0;
 | 
					        guild.VaultPremiumCredits ??= 0;
 | 
				
			||||||
@ -264,6 +298,18 @@ const moveResourcesToVault = (guild: TGuildDatabaseDocument, component: IDojoCon
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const addVaultMiscItems = (guild: TGuildDatabaseDocument, miscItems: ITypeCount[]): void => {
 | 
				
			||||||
 | 
					    guild.VaultMiscItems ??= [];
 | 
				
			||||||
 | 
					    for (const miscItem of miscItems) {
 | 
				
			||||||
 | 
					        const vaultMiscItem = guild.VaultMiscItems.find(x => x.ItemType == miscItem.ItemType);
 | 
				
			||||||
 | 
					        if (vaultMiscItem) {
 | 
				
			||||||
 | 
					            vaultMiscItem.ItemCount += miscItem.ItemCount;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            guild.VaultMiscItems.push(miscItem);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 ??= [];
 | 
				
			||||||
@ -362,6 +408,102 @@ export const removePigmentsFromGuildMembers = async (guildId: string | Types.Obj
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const processGuildTechProjectContributionsUpdate = async (
 | 
				
			||||||
 | 
					    guild: TGuildDatabaseDocument,
 | 
				
			||||||
 | 
					    techProject: ITechProjectDatabase
 | 
				
			||||||
 | 
					): Promise<void> => {
 | 
				
			||||||
 | 
					    if (techProject.ReqCredits == 0 && !techProject.ReqItems.find(x => x.ItemCount > 0)) {
 | 
				
			||||||
 | 
					        // This research is now fully funded.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            techProject.State == 0 &&
 | 
				
			||||||
 | 
					            techProject.ItemType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/"
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            guild.ActiveDojoColorResearch = "";
 | 
				
			||||||
 | 
					            await removePigmentsFromGuildMembers(guild._id);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const recipe = ExportDojoRecipes.research[techProject.ItemType];
 | 
				
			||||||
 | 
					        processFundedGuildTechProject(guild, techProject, recipe);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const processFundedGuildTechProject = (
 | 
				
			||||||
 | 
					    guild: TGuildDatabaseDocument,
 | 
				
			||||||
 | 
					    techProject: ITechProjectDatabase,
 | 
				
			||||||
 | 
					    recipe: IDojoResearch
 | 
				
			||||||
 | 
					): void => {
 | 
				
			||||||
 | 
					    techProject.State = 1;
 | 
				
			||||||
 | 
					    techProject.CompletionDate = new Date(Date.now() + (config.noDojoResearchTime ? 0 : recipe.time) * 1000);
 | 
				
			||||||
 | 
					    if (recipe.guildXpValue) {
 | 
				
			||||||
 | 
					        guild.XP += recipe.guildXpValue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setGuildTechLogState(guild, techProject.ItemType, config.noDojoResearchTime ? 4 : 3, techProject.CompletionDate);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const setGuildTechLogState = (
 | 
				
			||||||
 | 
					    guild: TGuildDatabaseDocument,
 | 
				
			||||||
 | 
					    type: string,
 | 
				
			||||||
 | 
					    state: number,
 | 
				
			||||||
 | 
					    dateTime: Date | undefined = undefined
 | 
				
			||||||
 | 
					): boolean => {
 | 
				
			||||||
 | 
					    guild.TechChanges ??= [];
 | 
				
			||||||
 | 
					    const entry = guild.TechChanges.find(x => x.details == type);
 | 
				
			||||||
 | 
					    if (entry) {
 | 
				
			||||||
 | 
					        if (entry.entryType == state) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        entry.dateTime = dateTime;
 | 
				
			||||||
 | 
					        entry.entryType = state;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        guild.TechChanges.push({
 | 
				
			||||||
 | 
					            dateTime: dateTime,
 | 
				
			||||||
 | 
					            entryType: state,
 | 
				
			||||||
 | 
					            details: type
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const setGuildTier = async (guild: TGuildDatabaseDocument, newTier: number): Promise<void> => {
 | 
				
			||||||
 | 
					    const oldTier = guild.Tier;
 | 
				
			||||||
 | 
					    guild.Tier = newTier;
 | 
				
			||||||
 | 
					    if (guild.TechProjects) {
 | 
				
			||||||
 | 
					        for (const project of guild.TechProjects) {
 | 
				
			||||||
 | 
					            if (project.State == 1) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const meta = ExportDojoRecipes.research[project.ItemType];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                const numContributed = scaleRequiredCount(oldTier, meta.price) - project.ReqCredits;
 | 
				
			||||||
 | 
					                project.ReqCredits = scaleRequiredCount(newTier, meta.price) - numContributed;
 | 
				
			||||||
 | 
					                if (project.ReqCredits < 0) {
 | 
				
			||||||
 | 
					                    guild.VaultRegularCredits ??= 0;
 | 
				
			||||||
 | 
					                    guild.VaultRegularCredits += project.ReqCredits * -1;
 | 
				
			||||||
 | 
					                    project.ReqCredits = 0;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (let i = 0; i != project.ReqItems.length; ++i) {
 | 
				
			||||||
 | 
					                const numContributed =
 | 
				
			||||||
 | 
					                    scaleRequiredCount(oldTier, meta.ingredients[i].ItemCount) - project.ReqItems[i].ItemCount;
 | 
				
			||||||
 | 
					                project.ReqItems[i].ItemCount =
 | 
				
			||||||
 | 
					                    scaleRequiredCount(newTier, meta.ingredients[i].ItemCount) - numContributed;
 | 
				
			||||||
 | 
					                if (project.ReqItems[i].ItemCount < 0) {
 | 
				
			||||||
 | 
					                    project.ReqItems[i].ItemCount *= -1;
 | 
				
			||||||
 | 
					                    addVaultMiscItems(guild, [project.ReqItems[i]]);
 | 
				
			||||||
 | 
					                    project.ReqItems[i].ItemCount = 0;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Check if research is fully funded now due to lowered requirements.
 | 
				
			||||||
 | 
					            await processGuildTechProjectContributionsUpdate(guild, project);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const deleteGuild = async (guildId: Types.ObjectId): Promise<void> => {
 | 
					export const deleteGuild = async (guildId: Types.ObjectId): Promise<void> => {
 | 
				
			||||||
    await Guild.deleteOne({ _id: guildId });
 | 
					    await Guild.deleteOne({ _id: guildId });
 | 
				
			||||||
    await GuildMember.deleteMany({ guildId });
 | 
					    await GuildMember.deleteMany({ guildId });
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user