feat: rushing recipes, refactor: addItem (#248)

This commit is contained in:
Sainan 2024-06-15 02:50:43 +02:00 committed by GitHub
parent ba8a3afce5
commit b08fff1906
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 174 additions and 209 deletions

View File

@ -3,11 +3,11 @@
import { RequestHandler } from "express";
import { logger } from "@/src/utils/logger";
import { getItemByBlueprint, getItemCategoryByUniqueName } from "@/src/services/itemDataService";
import { getItemByBlueprint } from "@/src/services/itemDataService";
import { IOid } from "@/src/types/commonTypes";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService";
import { getInventory, updateCurrency, addItem } from "@/src/services/inventoryService";
export interface IClaimCompletedRecipeRequest {
RecipeIds: IOid[];
@ -19,12 +19,10 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
const accountId = await getAccountIdForRequest(req);
if (!accountId) throw new Error("no account id");
console.log(claimCompletedRecipeRequest);
const inventory = await getInventory(accountId);
const pendingRecipe = inventory.PendingRecipes.find(
recipe => recipe._id?.toString() === claimCompletedRecipeRequest.RecipeIds[0].$oid
);
console.log(pendingRecipe);
if (!pendingRecipe) {
logger.error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`);
throw new Error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`);
@ -36,29 +34,29 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
// throw new Error(`recipe ${pendingRecipe._id} is not ready to be completed`);
// }
//get completed Items
const completedItemName = getItemByBlueprint(pendingRecipe.ItemType)?.uniqueName;
inventory.PendingRecipes.pull(pendingRecipe._id);
await inventory.save();
if (!completedItemName) {
const buildable = getItemByBlueprint(pendingRecipe.ItemType);
if (!buildable) {
logger.error(`no completed item found for recipe ${pendingRecipe._id}`);
throw new Error(`no completed item found for recipe ${pendingRecipe._id}`);
}
const itemCategory = getItemCategoryByUniqueName(completedItemName) as keyof typeof inventory;
console.log(itemCategory);
//TODO: remove all Schema.Mixed for inventory[itemCategory] not to be any
//add item
//inventory[itemCategory].
//add additional item components like mods or weapons for a sentinel.
//const additionalItemComponents = itemComponents[uniqueName]
//add these items to inventory
//return changes as InventoryChanges
//remove pending recipe
inventory.PendingRecipes.pull(pendingRecipe._id);
// await inventory.save();
logger.debug("Claiming Completed Recipe", { completedItemName });
res.json({ InventoryChanges: {} });
if (req.query.cancel) {
// TODO: Refund items
res.json({});
} else {
logger.debug("Claiming Recipe", { buildable, pendingRecipe });
let currencyChanges = {};
if (req.query.rush && buildable.skipBuildTimePrice) {
currencyChanges = await updateCurrency(buildable.skipBuildTimePrice, true, accountId);
}
res.json({
InventoryChanges: {
...currencyChanges,
...(await addItem(accountId, buildable.uniqueName, buildable.buildQuantity)).InventoryChanges
}
});
}
};

View File

@ -2,7 +2,7 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
import { updateCurrency } from "@/src/services/inventoryService";
import { RequestHandler } from "express";
import { updateSlots } from "@/src/services/inventoryService";
import { SlotNameToInventoryName } from "@/src/types/purchaseTypes";
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
/*
loadout slots are additionally purchased slots only
@ -28,7 +28,7 @@ export const inventorySlotsController: RequestHandler = async (req, res) => {
//TODO: check which slot was purchased because pvpBonus is also possible
const currencyChanges = await updateCurrency(20, true, accountId);
await updateSlots(accountId, SlotNameToInventoryName.LOADOUT, 1, 1);
await updateSlots(accountId, InventorySlot.PVE_LOADOUTS, 1, 1);
//console.log({ InventoryChanges: currencyChanges }, " added loadout changes:");

View File

@ -14,7 +14,8 @@ import {
IMission,
IRawUpgrade,
ISeasonChallengeHistory,
ITypeCount
ITypeCount,
InventorySlot
} from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate } from "../types/genericUpdate";
import {
@ -24,7 +25,7 @@ import {
IUpdateChallengeProgressRequest
} from "../types/requestTypes";
import { logger } from "@/src/utils/logger";
import { WeaponTypeInternal, getExalted } from "@/src/services/itemDataService";
import { WeaponTypeInternal, getWeaponType, getExalted } from "@/src/services/itemDataService";
import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndicateTypes";
export const createInventory = async (
@ -65,6 +66,132 @@ export const getInventory = async (accountOwnerId: string) => {
return inventory;
};
export const addItem = async (
accountId: string,
typeName: string,
quantity: number = 1
): Promise<{ InventoryChanges: object }> => {
switch (typeName.substr(1).split("/")[1]) {
case "Powersuits":
if (typeName.includes("EntratiMech")) {
const mechSuit = await addMechSuit(typeName, accountId);
await updateSlots(accountId, InventorySlot.MECHSUITS, 0, 1);
logger.debug("mech suit", mechSuit);
return {
InventoryChanges: {
MechBin: {
count: 1,
platinum: 0,
Slots: -1
},
MechSuits: [mechSuit]
}
};
}
const suit = await addPowerSuit(typeName, accountId);
await updateSlots(accountId, InventorySlot.SUITS, 0, 1);
return {
InventoryChanges: {
SuitBin: {
count: 1,
platinum: 0,
Slots: -1
},
Suits: [suit]
}
};
case "Weapons":
const weaponType = getWeaponType(typeName);
const weapon = await addWeapon(weaponType, typeName, accountId);
await updateSlots(accountId, InventorySlot.WEAPONS, 0, 1);
return {
InventoryChanges: {
WeaponBin: { count: 1, platinum: 0, Slots: -1 },
[weaponType]: [weapon]
}
};
case "Interface":
return {
InventoryChanges: {
FlavourItems: [await addCustomization(typeName, accountId)]
}
};
case "Types":
switch (typeName.substr(1).split("/")[2]) {
case "AvatarImages":
case "SuitCustomizations":
return {
InventoryChanges: {
FlavourItems: [await addCustomization(typeName, accountId)]
}
};
case "Sentinels":
// TOOD: Sentinels should also grant their DefaultUpgrades & SentinelWeapon.
const sentinel = await addSentinel(typeName, accountId);
await updateSlots(accountId, InventorySlot.SENTINELS, 0, 1);
return {
InventoryChanges: {
SentinelBin: { count: 1, platinum: 0, Slots: -1 },
Sentinels: [sentinel]
}
};
case "Items": {
const inventory = await getInventory(accountId);
const miscItemChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
MiscItems: miscItemChanges
}
};
}
case "Recipes":
case "Consumables": {
// Blueprints for Ciphers, Antitoxins
const inventory = await getInventory(accountId);
const recipeChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies ITypeCount
];
addRecipes(inventory, recipeChanges);
await inventory.save();
return {
InventoryChanges: {
Recipes: recipeChanges
}
};
}
case "Restoratives": // Codex Scanner, Remote Observer, Starburst
const inventory = await getInventory(accountId);
const consumablesChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IConsumable
];
addConsumables(inventory, consumablesChanges);
await inventory.save();
return {
InventoryChanges: {
Consumables: consumablesChanges
}
};
}
break;
}
const errorMessage = `unable to add item: ${typeName}`;
logger.error(errorMessage);
throw new Error(errorMessage);
};
//TODO: maybe genericMethod for all the add methods, they share a lot of logic
export const addSentinel = async (sentinelName: string, accountId: string) => {
const inventory = await getInventory(accountId);

View File

@ -1,22 +1,7 @@
import { parseSlotPurchaseName } from "@/src/helpers/purchaseHelpers";
import { getWeaponType } from "@/src/services/itemDataService";
import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
import {
addBooster,
addConsumables,
addCustomization,
addMechSuit,
addMiscItems,
addPowerSuit,
addRecipes,
addSentinel,
addWeapon,
getInventory,
updateCurrency,
updateSlots
} from "@/src/services/inventoryService";
import { IConsumable, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
import { IPurchaseRequest, IPurchaseResponse, SlotNameToInventoryName, SlotPurchase } from "@/src/types/purchaseTypes";
import { addItem, addBooster, updateCurrency, updateSlots } from "@/src/services/inventoryService";
import { IPurchaseRequest, SlotPurchase } from "@/src/types/purchaseTypes";
import { logger } from "@/src/utils/logger";
export const getStoreItemCategory = (storeItem: string) => {
@ -40,34 +25,24 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI
const internalName = purchaseRequest.PurchaseParams.StoreItem.replace("/StoreItems", "");
logger.debug(`store category ${storeCategory}`);
let inventoryChanges;
let purchaseResponse;
switch (storeCategory) {
case "Powersuits":
inventoryChanges = await handlePowersuitPurchase(internalName, accountId);
break;
case "Weapons":
inventoryChanges = await handleWeaponsPurchase(internalName, accountId);
default:
purchaseResponse = await addItem(accountId, internalName);
break;
case "Types":
inventoryChanges = await handleTypesPurchase(
purchaseResponse = await handleTypesPurchase(
internalName,
accountId,
purchaseRequest.PurchaseParams.Quantity
);
break;
case "Boosters":
inventoryChanges = await handleBoostersPurchase(internalName, accountId);
purchaseResponse = await handleBoostersPurchase(internalName, accountId);
break;
case "Interface":
inventoryChanges = await handleCustomizationPurchase(internalName, accountId);
break;
default:
const errorMessage = `unknown store category: ${storeCategory} not implemented or new`;
logger.error(errorMessage);
throw new Error(errorMessage);
}
if (!inventoryChanges) throw new Error("purchase response was undefined");
if (!purchaseResponse) throw new Error("purchase response was undefined");
const currencyChanges = await updateCurrency(
purchaseRequest.PurchaseParams.ExpectedPrice,
@ -75,12 +50,12 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI
accountId
);
inventoryChanges.InventoryChanges = {
purchaseResponse.InventoryChanges = {
...currencyChanges,
...inventoryChanges.InventoryChanges
...purchaseResponse.InventoryChanges
};
return inventoryChanges;
return purchaseResponse;
};
export const slotPurchaseNameToSlotName: SlotPurchase = {
@ -126,102 +101,18 @@ const handleSlotPurchase = async (slotPurchaseNameFull: string, accountId: strin
};
};
const handleWeaponsPurchase = async (weaponName: string, accountId: string) => {
const weaponType = getWeaponType(weaponName);
const addedWeapon = await addWeapon(weaponType, weaponName, accountId);
await updateSlots(accountId, SlotNameToInventoryName.WEAPON, 0, 1);
return {
InventoryChanges: {
WeaponBin: { count: 1, platinum: 0, Slots: -1 },
[weaponType]: [addedWeapon]
}
} as IPurchaseResponse;
};
const handlePowersuitPurchase = async (powersuitName: string, accountId: string) => {
if (powersuitName.includes("EntratiMech")) {
const mechSuit = await addMechSuit(powersuitName, accountId);
await updateSlots(accountId, SlotNameToInventoryName.MECHSUIT, 0, 1);
logger.debug("mech suit", mechSuit);
return {
InventoryChanges: {
MechBin: {
count: 1,
platinum: 0,
Slots: -1
},
MechSuits: [mechSuit]
}
} as IPurchaseResponse;
}
const suit = await addPowerSuit(powersuitName, accountId);
await updateSlots(accountId, SlotNameToInventoryName.SUIT, 0, 1);
return {
InventoryChanges: {
SuitBin: {
count: 1,
platinum: 0,
Slots: -1
},
Suits: [suit]
}
};
};
//TODO: change to getInventory, apply changes then save at the end
const handleTypesPurchase = async (typesName: string, accountId: string, quantity: number) => {
const typeCategory = getStoreItemTypesCategory(typesName);
logger.debug(`type category ${typeCategory}`);
switch (typeCategory) {
case "AvatarImages":
case "SuitCustomizations":
return await handleCustomizationPurchase(typesName, accountId);
case "Sentinels":
return await handleSentinelPurchase(typesName, accountId);
default:
return await addItem(accountId, typesName, quantity);
case "SlotItems":
return await handleSlotPurchase(typesName, accountId);
case "Items":
return await handleMiscItemPurchase(typesName, accountId, quantity);
case "Recipes":
case "Consumables": // Blueprints for Ciphers, Antitoxins
return await handleRecipesPurchase(typesName, accountId, quantity);
case "Restoratives": // Codex Scanner, Remote Observer, Starburst
return await handleRestorativesPurchase(typesName, accountId, quantity);
break;
default:
throw new Error(`unknown Types category: ${typeCategory} not implemented or new`);
}
};
const handleSentinelPurchase = async (sentinelName: string, accountId: string) => {
const sentinel = await addSentinel(sentinelName, accountId);
await updateSlots(accountId, SlotNameToInventoryName.SENTINEL, 0, 1);
return {
InventoryChanges: {
SentinelBin: { count: 1, platinum: 0, Slots: -1 },
Sentinels: [sentinel]
}
};
};
const handleCustomizationPurchase = async (customizationName: string, accountId: string) => {
const customization = await addCustomization(customizationName, accountId);
return {
InventoryChanges: {
FlavourItems: [customization]
}
};
};
const boosterCollection = [
"/Lotus/Types/Boosters/ResourceAmountBooster",
"/Lotus/Types/Boosters/AffinityBooster",
@ -247,54 +138,3 @@ const handleBoostersPurchase = async (boosterStoreName: string, accountId: strin
}
};
};
const handleMiscItemPurchase = async (uniqueName: string, accountId: string, quantity: number) => {
const inventory = await getInventory(accountId);
const miscItemChanges = [
{
ItemType: uniqueName,
ItemCount: quantity
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
MiscItems: miscItemChanges
}
};
};
const handleRecipesPurchase = async (uniqueName: string, accountId: string, quantity: number) => {
const inventory = await getInventory(accountId);
const recipeChanges = [
{
ItemType: uniqueName,
ItemCount: quantity
} satisfies ITypeCount
];
addRecipes(inventory, recipeChanges);
await inventory.save();
return {
InventoryChanges: {
Recipes: recipeChanges
}
};
};
const handleRestorativesPurchase = async (uniqueName: string, accountId: string, quantity: number) => {
const inventory = await getInventory(accountId);
const consumablesChanges = [
{
ItemType: uniqueName,
ItemCount: quantity
} satisfies IConsumable
];
addConsumables(inventory, consumablesChanges);
await inventory.save();
return {
InventoryChanges: {
Consumables: consumablesChanges
}
};
};

View File

@ -419,6 +419,14 @@ export interface ICrewShipHarnessConfig {
Upgrades?: string[];
}
export enum InventorySlot {
SUITS = "SuitBin",
WEAPONS = "WeaponBin",
MECHSUITS = "MechBin",
PVE_LOADOUTS = "PveBonusLoadoutBin",
SENTINELS = "SentinelBin"
}
export interface ISlots {
Extra: number; // can be undefined, but not if used via mongoose
Slots: number;

View File

@ -42,14 +42,6 @@ export type IBinChanges = {
Extra?: number;
};
export enum SlotNameToInventoryName {
SUIT = "SuitBin",
WEAPON = "WeaponBin",
MECHSUIT = "MechBin",
LOADOUT = "PveBonusLoadoutBin",
SENTINEL = "SentinelBin"
}
export type SlotPurchaseName =
| "SuitSlotItem"
| "TwoSentinelSlotItem"