feat: clan tiers (#1378)
All checks were successful
Build / build (20) (push) Successful in 39s
Build / build (18) (push) Successful in 1m19s
Build / build (22) (push) Successful in 1m15s
Build Docker image / docker (push) Successful in 39s

Reviewed-on: #1378
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
Sainan 2025-03-30 09:58:51 -07:00 committed by Sainan
parent fccdbf4a8e
commit 516f822e43
6 changed files with 205 additions and 90 deletions

View File

@ -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();

View File

@ -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;
} }

View File

@ -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);
if (miscItemContribution) {
miscItemContribution.ItemCount += item.ItemCount;
} else {
guildMember.MiscItemsContributed.push(item); guildMember.MiscItemsContributed.push(item);
}
addMiscItems(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]); addMiscItems(inventory, [{ ...item, ItemCount: item.ItemCount * -1 }]);
} }
} }

View File

@ -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(

View File

@ -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

View File

@ -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 });