feat: rushing recipes, refactor: addItem (#248)
This commit is contained in:
parent
ba8a3afce5
commit
b08fff1906
@ -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
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -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:");
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user