feat: all server-side metamorphosis levels #716

Merged
Sainan merged 4 commits from helminth-xp into main 2025-01-04 22:16:48 -08:00
4 changed files with 95 additions and 16 deletions

View File

@ -71,14 +71,14 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
const snack = ExportMisc.helminthSnacks[contribution.ItemType]; const snack = ExportMisc.helminthSnacks[contribution.ItemType];
// Note: Currently ignoring loss of apetite // Note: Currently ignoring loss of apetite
totalPercentagePointsGained += snack.gain / 0.01; totalPercentagePointsGained += snack.gain * 100; // 30% would be gain=0.3, so percentage points is equal to gain * 100.
const resource = inventory.InfestedFoundry.Resources.find(x => x.ItemType == snack.type); const resource = inventory.InfestedFoundry.Resources.find(x => x.ItemType == snack.type);
if (resource) { if (resource) {
resource.Count += Math.trunc(snack.gain * 1000); resource.Count += Math.trunc(snack.gain * 1000);
} else { } else {
inventory.InfestedFoundry.Resources.push({ inventory.InfestedFoundry.Resources.push({
ItemType: snack.type, ItemType: snack.type,
Count: Math.trunc(snack.gain * 1000) Count: Math.trunc(snack.gain * 1000) // 30% would be gain=0.3 or Count=300, so Count=gain*1000.
}); });
} }
@ -91,19 +91,21 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
} }
} }
addInfestedFoundryXP(inventory.InfestedFoundry, 666 * totalPercentagePointsGained); const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry, 666 * totalPercentagePointsGained);
addRecipes(inventory, recipeChanges);
addMiscItems(inventory, miscItemChanges); addMiscItems(inventory, miscItemChanges);
await inventory.save(); await inventory.save();
res.json({ res.json({
InventoryChanges: { InventoryChanges: {
Recipes: recipeChanges,
InfestedFoundry: { InfestedFoundry: {
XP: inventory.InfestedFoundry.XP, XP: inventory.InfestedFoundry.XP,
Resources: inventory.InfestedFoundry.Resources, Resources: inventory.InfestedFoundry.Resources,
Slots: inventory.InfestedFoundry.Slots Slots: inventory.InfestedFoundry.Slots
}
}, },
MiscItems: miscItemChanges MiscItems: miscItemChanges
}
}); });
break; break;
} }
@ -144,18 +146,21 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
if (suit.Configs && suit.Configs[0] && suit.Configs[0].pricol) { if (suit.Configs && suit.Configs[0] && suit.Configs[0].pricol) {
consumedSuit.c = suit.Configs[0].pricol; consumedSuit.c = suit.Configs[0].pricol;
} }
if ((inventory.InfestedFoundry!.XP ?? 0) < 73125_00) {
inventory.InfestedFoundry!.Slots!--; inventory.InfestedFoundry!.Slots!--;
}
inventory.InfestedFoundry!.ConsumedSuits ??= []; inventory.InfestedFoundry!.ConsumedSuits ??= [];
inventory.InfestedFoundry!.ConsumedSuits?.push(consumedSuit); inventory.InfestedFoundry!.ConsumedSuits?.push(consumedSuit);
inventory.InfestedFoundry!.LastConsumedSuit = suit; inventory.InfestedFoundry!.LastConsumedSuit = suit;
inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = new Date( inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = new Date(
new Date().getTime() + 24 * 60 * 60 * 1000 new Date().getTime() + 24 * 60 * 60 * 1000
); );
addInfestedFoundryXP(inventory.InfestedFoundry!, 1600_00); const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, 1600_00);
addRecipes(inventory, recipeChanges);
await inventory.save(); await inventory.save();
console.log(inventory.toJSON().InfestedFoundry);
res.json({ res.json({
InventoryChanges: { InventoryChanges: {
Recipes: recipeChanges,
RemovedIdItems: [ RemovedIdItems: [
{ {
ItemId: request.SuitId ItemId: request.SuitId
@ -196,7 +201,8 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
suit.OffensiveUpgrade = request.OffensiveUpgradeType; suit.OffensiveUpgrade = request.OffensiveUpgradeType;
suit.DefensiveUpgrade = request.DefensiveUpgradeType; suit.DefensiveUpgrade = request.DefensiveUpgradeType;
suit.UpgradesExpiry = upgradesExpiry; suit.UpgradesExpiry = upgradesExpiry;
addInfestedFoundryXP(inventory.InfestedFoundry!, 4800_00); const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, 4800_00);
addRecipes(inventory, recipeChanges);
for (let i = 0; i != request.ResourceTypes.length; ++i) { for (let i = 0; i != request.ResourceTypes.length; ++i) {
inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == request.ResourceTypes[i])!.Count -= inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == request.ResourceTypes[i])!.Count -=
request.ResourceCosts[i]; request.ResourceCosts[i];
@ -210,6 +216,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
DefensiveUpgrade: request.DefensiveUpgradeType, DefensiveUpgrade: request.DefensiveUpgradeType,
UpgradesExpiry: toMongoDate(upgradesExpiry), UpgradesExpiry: toMongoDate(upgradesExpiry),
InventoryChanges: { InventoryChanges: {
Recipes: recipeChanges,
InfestedFoundry: inventory.toJSON().InfestedFoundry InfestedFoundry: inventory.toJSON().InfestedFoundry
} }
}); });
@ -254,7 +261,8 @@ const colorToShard: Record<string, string> = {
ACC_PURPLE_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalVioletMythic" ACC_PURPLE_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalVioletMythic"
}; };
const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number): void => { export const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number): ITypeCount[] => {
const recipeChanges: ITypeCount[] = [];
infestedFoundry.XP ??= 0; infestedFoundry.XP ??= 0;
const prevXP = infestedFoundry.XP; const prevXP = infestedFoundry.XP;
infestedFoundry.XP += delta; infestedFoundry.XP += delta;
@ -262,14 +270,69 @@ const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number):
infestedFoundry.Slots ??= 0; infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 3; infestedFoundry.Slots += 3;
} }
if (prevXP < 5625_00 && infestedFoundry.XP >= 5625_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldsBlueprint",
ItemCount: 1
});
}
if (prevXP < 10125_00 && infestedFoundry.XP >= 10125_00) {
recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthHackBlueprint", ItemCount: 1 });
}
if (prevXP < 15750_00 && infestedFoundry.XP >= 15750_00) { if (prevXP < 15750_00 && infestedFoundry.XP >= 15750_00) {
infestedFoundry.Slots ??= 0; infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 10; infestedFoundry.Slots += 10;
} }
if (prevXP < 22500_00 && infestedFoundry.XP >= 22500_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthAmmoEfficiencyBlueprint",
ItemCount: 1
});
}
if (prevXP < 30375_00 && infestedFoundry.XP >= 30375_00) {
recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStunBlueprint", ItemCount: 1 });
}
if (prevXP < 39375_00 && infestedFoundry.XP >= 39375_00) { if (prevXP < 39375_00 && infestedFoundry.XP >= 39375_00) {
infestedFoundry.Slots ??= 0; infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 20; infestedFoundry.Slots += 20;
} }
if (prevXP < 60750_00 && infestedFoundry.XP >= 60750_00) {
recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStatusBlueprint", ItemCount: 1 });
}
if (prevXP < 73125_00 && infestedFoundry.XP >= 73125_00) {
infestedFoundry.Slots = 1;
}
if (prevXP < 86625_00 && infestedFoundry.XP >= 86625_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldArmorBlueprint",
ItemCount: 1
});
}
if (prevXP < 101250_00 && infestedFoundry.XP >= 101250_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthProcBlockBlueprint",
ItemCount: 1
});
}
if (prevXP < 117000_00 && infestedFoundry.XP >= 117000_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthEnergyShareBlueprint",
ItemCount: 1
});
}
if (prevXP < 133875_00 && infestedFoundry.XP >= 133875_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthMaxStatusBlueprint",
ItemCount: 1
});
}
if (prevXP < 151875_00 && infestedFoundry.XP >= 151875_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthTreasureBlueprint",
ItemCount: 1
});
}
return recipeChanges;
}; };
interface IHelminthSubsumeRequest { interface IHelminthSubsumeRequest {

View File

@ -8,13 +8,16 @@ import {
} from "@/src/types/inventoryTypes/commonInventoryTypes"; } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService"; import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { getRecipeByResult } from "@/src/services/itemDataService"; import { getRecipeByResult } from "@/src/services/itemDataService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { addInfestedFoundryXP } from "./infestedFoundryController";
export const upgradesController: RequestHandler = async (req, res) => { export const upgradesController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const payload = JSON.parse(String(req.body)) as IUpgradesRequest; const payload = JSON.parse(String(req.body)) as IUpgradesRequest;
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
const inventoryChanges: IInventoryChanges = {};
for (const operation of payload.Operations) { for (const operation of payload.Operations) {
if ( if (
operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/ModSlotUnlocker" || operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/ModSlotUnlocker" ||
@ -35,6 +38,7 @@ export const upgradesController: RequestHandler = async (req, res) => {
const suit = inventory.Suits.find(x => x._id.toString() == payload.ItemId.$oid)!; const suit = inventory.Suits.find(x => x._id.toString() == payload.ItemId.$oid)!;
let newAbilityOverride: IAbilityOverride | undefined; let newAbilityOverride: IAbilityOverride | undefined;
let totalPercentagePointsConsumed = 0;
if (operation.UpgradeRequirement != "") { if (operation.UpgradeRequirement != "") {
newAbilityOverride = { newAbilityOverride = {
Ability: operation.UpgradeRequirement, Ability: operation.UpgradeRequirement,
@ -43,6 +47,7 @@ export const upgradesController: RequestHandler = async (req, res) => {
const recipe = getRecipeByResult(operation.UpgradeRequirement)!; const recipe = getRecipeByResult(operation.UpgradeRequirement)!;
for (const ingredient of recipe.ingredients) { for (const ingredient of recipe.ingredients) {
totalPercentagePointsConsumed += ingredient.ItemCount / 10;
inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType)!.Count -= inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType)!.Count -=
ingredient.ItemCount; ingredient.ItemCount;
} }
@ -52,6 +57,12 @@ export const upgradesController: RequestHandler = async (req, res) => {
suit.Configs[entry.Slot] ??= {}; suit.Configs[entry.Slot] ??= {};
suit.Configs[entry.Slot].AbilityOverride = newAbilityOverride; suit.Configs[entry.Slot].AbilityOverride = newAbilityOverride;
} }
const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, totalPercentagePointsConsumed * 8);
addRecipes(inventory, recipeChanges);
inventoryChanges.Recipes = recipeChanges;
inventoryChanges.InfestedFoundry = inventory.toJSON().InfestedFoundry;
} else } else
switch (operation.UpgradeRequirement) { switch (operation.UpgradeRequirement) {
case "/Lotus/Types/Items/MiscItems/OrokinReactor": case "/Lotus/Types/Items/MiscItems/OrokinReactor":
@ -146,7 +157,7 @@ export const upgradesController: RequestHandler = async (req, res) => {
} }
} }
await inventory.save(); await inventory.save();
res.json({ InventoryChanges: {} }); res.json({ InventoryChanges: inventoryChanges });
}; };
const setSlotPolarity = (item: IEquipmentDatabase, slot: number, polarity: ArtifactPolarity): void => { const setSlotPolarity = (item: IEquipmentDatabase, slot: number, polarity: ArtifactPolarity): void => {

View File

@ -79,8 +79,9 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
} }
} else if (typeof delta[key] == "object") { } else if (typeof delta[key] == "object") {
console.assert(key.substring(-3) == "Bin"); console.assert(key.substring(-3) == "Bin");
console.assert(key != "InfestedFoundry");
const left = InventoryChanges[key] as IBinChanges; const left = InventoryChanges[key] as IBinChanges;
const right: IBinChanges = delta[key]; const right = delta[key] as IBinChanges;
left.count += right.count; left.count += right.count;
left.platinum += right.platinum; left.platinum += right.platinum;
left.Slots += right.Slots; left.Slots += right.Slots;

View File

@ -1,3 +1,5 @@
import { IInfestedFoundry } from "./inventoryTypes/inventoryTypes";
export interface IPurchaseRequest { export interface IPurchaseRequest {
PurchaseParams: IPurchaseParams; PurchaseParams: IPurchaseParams;
buildLabel: string; buildLabel: string;
@ -25,8 +27,10 @@ export interface ICurrencyChanges {
export type IInventoryChanges = { export type IInventoryChanges = {
[_ in SlotNames]?: IBinChanges; [_ in SlotNames]?: IBinChanges;
} & ICurrencyChanges & } & ICurrencyChanges & { InfestedFoundry?: IInfestedFoundry } & Record<
Record<string, IBinChanges | number | object[]>; string,
IBinChanges | number | object[] | IInfestedFoundry
>;
export interface IPurchaseResponse { export interface IPurchaseResponse {
InventoryChanges: IInventoryChanges; InventoryChanges: IInventoryChanges;