feat: clan polychrome research #1177
@ -4,6 +4,7 @@ import {
 | 
			
		||||
    getGuildVault,
 | 
			
		||||
    hasAccessToDojo,
 | 
			
		||||
    hasGuildPermission,
 | 
			
		||||
    removePigmentsFromGuildMembers,
 | 
			
		||||
    scaleRequiredCount
 | 
			
		||||
} from "@/src/services/guildService";
 | 
			
		||||
import { ExportDojoRecipes, IDojoResearch } from "warframe-public-export-plus";
 | 
			
		||||
@ -28,8 +29,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const inventory = await getInventory(accountId);
 | 
			
		||||
    const guild = await getGuildForRequestEx(req, inventory);
 | 
			
		||||
    const data = JSON.parse(String(req.body)) as TGuildTechRequest;
 | 
			
		||||
    const action = data.Action.split(",")[0];
 | 
			
		||||
    if (action == "Sync") {
 | 
			
		||||
    if (data.Action == "Sync") {
 | 
			
		||||
        let needSave = false;
 | 
			
		||||
        const techProjects: ITechProjectClient[] = [];
 | 
			
		||||
        if (guild.TechProjects) {
 | 
			
		||||
@ -53,18 +53,18 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
			
		||||
            await guild.save();
 | 
			
		||||
        }
 | 
			
		||||
        res.json({ TechProjects: techProjects });
 | 
			
		||||
    } else if (action == "Start") {
 | 
			
		||||
        if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
 | 
			
		||||
    } else if (data.Action == "Start") {
 | 
			
		||||
        if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
 | 
			
		||||
            res.status(400).send("-1").end();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        const recipe = ExportDojoRecipes.research[data.RecipeType!];
 | 
			
		||||
        const recipe = ExportDojoRecipes.research[data.RecipeType];
 | 
			
		||||
        guild.TechProjects ??= [];
 | 
			
		||||
        if (!guild.TechProjects.find(x => x.ItemType == data.RecipeType)) {
 | 
			
		||||
            const techProject =
 | 
			
		||||
                guild.TechProjects[
 | 
			
		||||
                    guild.TechProjects.push({
 | 
			
		||||
                        ItemType: data.RecipeType!,
 | 
			
		||||
                        ItemType: data.RecipeType,
 | 
			
		||||
                        ReqCredits: config.noDojoResearchCosts ? 0 : scaleRequiredCount(recipe.price),
 | 
			
		||||
                        ReqItems: recipe.ingredients.map(x => ({
 | 
			
		||||
                            ItemType: x.ItemType,
 | 
			
		||||
@ -76,16 +76,20 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
			
		||||
            setTechLogState(guild, techProject.ItemType, 5);
 | 
			
		||||
            if (config.noDojoResearchCosts) {
 | 
			
		||||
                processFundedProject(guild, techProject, recipe);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") {
 | 
			
		||||
                    guild.ActiveDojoColorResearch = data.RecipeType;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        await guild.save();
 | 
			
		||||
        res.end();
 | 
			
		||||
    } else if (action == "Contribute") {
 | 
			
		||||
    } else if (data.Action == "Contribute") {
 | 
			
		||||
        if (!hasAccessToDojo(inventory)) {
 | 
			
		||||
            res.status(400).send("-1").end();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        const contributions = data as IGuildTechContributeFields;
 | 
			
		||||
        const contributions = data;
 | 
			
		||||
        const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!;
 | 
			
		||||
 | 
			
		||||
        if (contributions.VaultCredits) {
 | 
			
		||||
@ -136,8 +140,12 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
			
		||||
 | 
			
		||||
        if (techProject.ReqCredits == 0 && !techProject.ReqItems.find(x => x.ItemCount > 0)) {
 | 
			
		||||
            // This research is now fully funded.
 | 
			
		||||
            const recipe = ExportDojoRecipes.research[data.RecipeType!];
 | 
			
		||||
            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();
 | 
			
		||||
@ -146,12 +154,12 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
			
		||||
            InventoryChanges: inventoryChanges,
 | 
			
		||||
            Vault: getGuildVault(guild)
 | 
			
		||||
        });
 | 
			
		||||
    } else if (action == "Buy") {
 | 
			
		||||
    } else if (data.Action.split(",")[0] == "Buy") {
 | 
			
		||||
        if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
 | 
			
		||||
            res.status(400).send("-1").end();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        const purchase = data as IGuildTechBuyFields;
 | 
			
		||||
        const purchase = data as IGuildTechBuyRequest;
 | 
			
		||||
        const quantity = parseInt(data.Action.split(",")[1]);
 | 
			
		||||
        const recipeChanges = [
 | 
			
		||||
            {
 | 
			
		||||
@ -173,13 +181,12 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
			
		||||
                Recipes: recipeChanges
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    } else if (action == "Fabricate") {
 | 
			
		||||
    } else if (data.Action == "Fabricate") {
 | 
			
		||||
        if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
 | 
			
		||||
            res.status(400).send("-1").end();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        const payload = data as IGuildTechFabricateRequest;
 | 
			
		||||
        const recipe = ExportDojoRecipes.fabrications[payload.RecipeType];
 | 
			
		||||
        const recipe = ExportDojoRecipes.fabrications[data.RecipeType];
 | 
			
		||||
        const inventoryChanges: IInventoryChanges = updateCurrency(inventory, recipe.price, false);
 | 
			
		||||
        inventoryChanges.MiscItems = recipe.ingredients.map(x => ({
 | 
			
		||||
            ItemType: x.ItemType,
 | 
			
		||||
@ -190,6 +197,31 @@ export const guildTechController: RequestHandler = async (req, res) => {
 | 
			
		||||
        await inventory.save();
 | 
			
		||||
        // Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`.
 | 
			
		||||
        res.json({ inventoryChanges: inventoryChanges });
 | 
			
		||||
    } else if (data.Action == "Pause") {
 | 
			
		||||
        if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
 | 
			
		||||
            res.status(400).send("-1").end();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!;
 | 
			
		||||
        project.State = -2;
 | 
			
		||||
        guild.ActiveDojoColorResearch = "";
 | 
			
		||||
        await guild.save();
 | 
			
		||||
        await removePigmentsFromGuildMembers(guild._id);
 | 
			
		||||
        res.end();
 | 
			
		||||
    } else if (data.Action == "Unpause") {
 | 
			
		||||
        if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
 | 
			
		||||
            res.status(400).send("-1").end();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!;
 | 
			
		||||
        project.State = 0;
 | 
			
		||||
        guild.ActiveDojoColorResearch = data.RecipeType;
 | 
			
		||||
        const entry = guild.TechChanges?.find(x => x.details == data.RecipeType);
 | 
			
		||||
        if (entry) {
 | 
			
		||||
            entry.dateTime = new Date();
 | 
			
		||||
        }
 | 
			
		||||
        await guild.save();
 | 
			
		||||
        res.end();
 | 
			
		||||
    } else {
 | 
			
		||||
        throw new Error(`unknown guildTech action: ${data.Action}`);
 | 
			
		||||
    }
 | 
			
		||||
@ -233,20 +265,24 @@ const setTechLogState = (
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type TGuildTechRequest =
 | 
			
		||||
    | ({
 | 
			
		||||
          Action: string;
 | 
			
		||||
      } & Partial<IGuildTechStartFields> &
 | 
			
		||||
          Partial<IGuildTechContributeFields>)
 | 
			
		||||
    | IGuildTechFabricateRequest;
 | 
			
		||||
    | { Action: "Sync" | "SomethingElseThatWeMightNotKnowAbout" }
 | 
			
		||||
    | IGuildTechBasicRequest
 | 
			
		||||
    | IGuildTechContributeRequest;
 | 
			
		||||
 | 
			
		||||
interface IGuildTechStartFields {
 | 
			
		||||
interface IGuildTechBasicRequest {
 | 
			
		||||
    Action: "Start" | "Fabricate" | "Pause" | "Unpause";
 | 
			
		||||
    Mode: "Guild";
 | 
			
		||||
    RecipeType: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type IGuildTechBuyFields = IGuildTechStartFields;
 | 
			
		||||
interface IGuildTechBuyRequest {
 | 
			
		||||
    Action: string;
 | 
			
		||||
    Mode: "Guild";
 | 
			
		||||
    RecipeType: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IGuildTechContributeFields {
 | 
			
		||||
interface IGuildTechContributeRequest {
 | 
			
		||||
    Action: "Contribute";
 | 
			
		||||
    ResearchId: "";
 | 
			
		||||
    RecipeType: string;
 | 
			
		||||
    RegularCredits: number;
 | 
			
		||||
@ -254,9 +290,3 @@ interface IGuildTechContributeFields {
 | 
			
		||||
    VaultCredits: number;
 | 
			
		||||
    VaultMiscItems: IMiscItem[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IGuildTechFabricateRequest {
 | 
			
		||||
    Action: "Fabricate";
 | 
			
		||||
    Mode: "Guild";
 | 
			
		||||
    RecipeType: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -151,6 +151,7 @@ const guildSchema = new Schema<IGuildDatabase>(
 | 
			
		||||
        VaultShipDecorations: { type: [typeCountSchema], default: undefined },
 | 
			
		||||
        VaultFusionTreasures: { type: [fusionTreasuresSchema], default: undefined },
 | 
			
		||||
        TechProjects: { type: [techProjectSchema], default: undefined },
 | 
			
		||||
        ActiveDojoColorResearch: { type: String, default: "" },
 | 
			
		||||
        Class: { type: Number, default: 0 },
 | 
			
		||||
        XP: { type: Number, default: 0 },
 | 
			
		||||
        ClaimedXP: { type: [String], default: undefined },
 | 
			
		||||
 | 
			
		||||
@ -92,6 +92,7 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
 | 
			
		||||
        Ranks: guild.Ranks,
 | 
			
		||||
        Tier: 1,
 | 
			
		||||
        Vault: getGuildVault(guild),
 | 
			
		||||
        ActiveDojoColorResearch: guild.ActiveDojoColorResearch,
 | 
			
		||||
        Class: guild.Class,
 | 
			
		||||
        XP: guild.XP,
 | 
			
		||||
        IsContributor: !!guild.CeremonyContributors?.find(x => x.equals(accountId)),
 | 
			
		||||
@ -349,3 +350,17 @@ export const hasGuildPermissionEx = (
 | 
			
		||||
    const rank = guild.Ranks[member.rank];
 | 
			
		||||
    return (rank.Permissions & perm) != 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const removePigmentsFromGuildMembers = async (guildId: string | Types.ObjectId): Promise<void> => {
 | 
			
		||||
    const members = await GuildMember.find({ guildId, status: 0 }, "accountId");
 | 
			
		||||
    for (const member of members) {
 | 
			
		||||
        const inventory = await getInventory(member.accountId.toString(), "MiscItems");
 | 
			
		||||
        const index = inventory.MiscItems.findIndex(
 | 
			
		||||
            x => x.ItemType == "/Lotus/Types/Items/Research/DojoColors/GenericDojoColorPigment"
 | 
			
		||||
        );
 | 
			
		||||
        if (index != -1) {
 | 
			
		||||
            inventory.MiscItems.splice(index, 1);
 | 
			
		||||
            await inventory.save();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ export interface IGuildClient {
 | 
			
		||||
    }[];
 | 
			
		||||
    Tier: number;
 | 
			
		||||
    Vault: IGuildVault;
 | 
			
		||||
    ActiveDojoColorResearch: string;
 | 
			
		||||
    Class: number;
 | 
			
		||||
    XP: number;
 | 
			
		||||
    IsContributor: boolean;
 | 
			
		||||
@ -39,6 +40,7 @@ export interface IGuildDatabase {
 | 
			
		||||
    VaultFusionTreasures?: IFusionTreasure[];
 | 
			
		||||
 | 
			
		||||
    TechProjects?: ITechProjectDatabase[];
 | 
			
		||||
    ActiveDojoColorResearch: string;
 | 
			
		||||
 | 
			
		||||
    Class: number;
 | 
			
		||||
    XP: number;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user