2024-01-25 14:49:45 +01:00
|
|
|
import { parseSlotPurchaseName } from "@/src/helpers/purchaseHelpers";
|
|
|
|
import { getWeaponType } from "@/src/services/itemDataService";
|
2023-06-14 02:26:19 +02:00
|
|
|
import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
|
2023-12-14 17:34:15 +01:00
|
|
|
import {
|
|
|
|
addBooster,
|
2024-05-09 16:04:31 +02:00
|
|
|
addConsumables,
|
2023-12-14 17:34:15 +01:00
|
|
|
addCustomization,
|
|
|
|
addMechSuit,
|
2024-05-09 01:27:32 +02:00
|
|
|
addMiscItems,
|
2023-12-14 17:34:15 +01:00
|
|
|
addPowerSuit,
|
2024-05-09 16:04:31 +02:00
|
|
|
addRecipes,
|
2023-12-14 17:34:15 +01:00
|
|
|
addSentinel,
|
|
|
|
addWeapon,
|
2024-05-09 01:27:32 +02:00
|
|
|
getInventory,
|
2023-12-28 16:24:52 +01:00
|
|
|
updateCurrency,
|
2023-12-14 17:34:15 +01:00
|
|
|
updateSlots
|
|
|
|
} from "@/src/services/inventoryService";
|
2024-05-09 16:04:31 +02:00
|
|
|
import { IConsumable, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
2023-12-28 16:24:52 +01:00
|
|
|
import { IPurchaseRequest, IPurchaseResponse, SlotNameToInventoryName, SlotPurchase } from "@/src/types/purchaseTypes";
|
2024-01-06 16:26:58 +01:00
|
|
|
import { logger } from "@/src/utils/logger";
|
2023-06-14 02:26:19 +02:00
|
|
|
|
|
|
|
export const getStoreItemCategory = (storeItem: string) => {
|
|
|
|
const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
|
|
|
|
const storeItemElements = storeItemString.split("/");
|
|
|
|
return storeItemElements[1];
|
|
|
|
};
|
|
|
|
|
|
|
|
export const getStoreItemTypesCategory = (typesItem: string) => {
|
|
|
|
const typesString = getSubstringFromKeyword(typesItem, "Types");
|
|
|
|
const typeElements = typesString.split("/");
|
|
|
|
if (typesItem.includes("StoreItems")) {
|
|
|
|
return typeElements[2];
|
|
|
|
}
|
|
|
|
return typeElements[1];
|
|
|
|
};
|
|
|
|
|
|
|
|
export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountId: string) => {
|
2024-01-06 16:26:58 +01:00
|
|
|
logger.debug("purchase request", purchaseRequest);
|
2023-06-14 02:26:19 +02:00
|
|
|
const storeCategory = getStoreItemCategory(purchaseRequest.PurchaseParams.StoreItem);
|
|
|
|
const internalName = purchaseRequest.PurchaseParams.StoreItem.replace("/StoreItems", "");
|
2024-01-06 16:26:58 +01:00
|
|
|
logger.debug(`store category ${storeCategory}`);
|
2023-06-14 02:26:19 +02:00
|
|
|
|
2023-12-28 16:24:52 +01:00
|
|
|
let inventoryChanges;
|
2023-06-14 02:26:19 +02:00
|
|
|
switch (storeCategory) {
|
|
|
|
case "Powersuits":
|
2023-12-28 16:24:52 +01:00
|
|
|
inventoryChanges = await handlePowersuitPurchase(internalName, accountId);
|
2023-06-14 02:26:19 +02:00
|
|
|
break;
|
|
|
|
case "Weapons":
|
2023-12-28 16:24:52 +01:00
|
|
|
inventoryChanges = await handleWeaponsPurchase(internalName, accountId);
|
2023-06-14 02:26:19 +02:00
|
|
|
break;
|
|
|
|
case "Types":
|
2024-05-09 01:27:32 +02:00
|
|
|
inventoryChanges = await handleTypesPurchase(
|
|
|
|
internalName,
|
|
|
|
accountId,
|
|
|
|
purchaseRequest.PurchaseParams.Quantity
|
|
|
|
);
|
2023-06-14 02:26:19 +02:00
|
|
|
break;
|
2023-08-31 14:29:09 +04:00
|
|
|
case "Boosters":
|
2023-12-28 16:24:52 +01:00
|
|
|
inventoryChanges = await handleBoostersPurchase(internalName, accountId);
|
2023-08-31 14:29:09 +04:00
|
|
|
break;
|
2024-05-09 01:27:32 +02:00
|
|
|
case "Interface":
|
|
|
|
inventoryChanges = await handleCustomizationPurchase(internalName, accountId);
|
|
|
|
break;
|
2023-06-14 02:26:19 +02:00
|
|
|
default:
|
2024-01-06 16:26:58 +01:00
|
|
|
const errorMessage = `unknown store category: ${storeCategory} not implemented or new`;
|
|
|
|
logger.error(errorMessage);
|
|
|
|
throw new Error(errorMessage);
|
2023-06-14 02:26:19 +02:00
|
|
|
}
|
|
|
|
|
2023-12-28 16:24:52 +01:00
|
|
|
if (!inventoryChanges) throw new Error("purchase response was undefined");
|
2023-06-14 02:26:19 +02:00
|
|
|
|
2023-12-28 16:24:52 +01:00
|
|
|
const currencyChanges = await updateCurrency(
|
|
|
|
purchaseRequest.PurchaseParams.ExpectedPrice,
|
|
|
|
purchaseRequest.PurchaseParams.UsePremium,
|
|
|
|
accountId
|
|
|
|
);
|
2023-06-14 02:26:19 +02:00
|
|
|
|
2023-12-28 16:24:52 +01:00
|
|
|
inventoryChanges.InventoryChanges = {
|
|
|
|
...currencyChanges,
|
|
|
|
...inventoryChanges.InventoryChanges
|
|
|
|
};
|
|
|
|
|
|
|
|
return inventoryChanges;
|
|
|
|
};
|
|
|
|
|
|
|
|
export const slotPurchaseNameToSlotName: SlotPurchase = {
|
|
|
|
SuitSlotItem: { name: "SuitBin", slotsPerPurchase: 1 },
|
|
|
|
TwoSentinelSlotItem: { name: "SentinelBin", slotsPerPurchase: 2 },
|
|
|
|
TwoWeaponSlotItem: { name: "WeaponBin", slotsPerPurchase: 2 },
|
|
|
|
SpaceSuitSlotItem: { name: "SpaceSuitBin", slotsPerPurchase: 1 },
|
|
|
|
TwoSpaceWeaponSlotItem: { name: "SpaceWeaponBin", slotsPerPurchase: 2 },
|
|
|
|
MechSlotItem: { name: "MechBin", slotsPerPurchase: 1 },
|
|
|
|
TwoOperatorWeaponSlotItem: { name: "OperatorAmpBin", slotsPerPurchase: 2 },
|
|
|
|
RandomModSlotItem: { name: "RandomModBin", slotsPerPurchase: 3 },
|
|
|
|
TwoCrewShipSalvageSlotItem: { name: "CrewShipSalvageBin", slotsPerPurchase: 2 },
|
|
|
|
CrewMemberSlotItem: { name: "CrewMemberBin", slotsPerPurchase: 1 }
|
|
|
|
};
|
|
|
|
|
|
|
|
// // extra = everything above the base +2 slots (depending on slot type)
|
|
|
|
// // new slot above base = extra + 1 and slots +1
|
|
|
|
// // new frame = slots -1
|
|
|
|
// // number of frames = extra - slots + 2
|
|
|
|
const handleSlotPurchase = async (slotPurchaseNameFull: string, accountId: string) => {
|
2024-01-06 16:26:58 +01:00
|
|
|
logger.debug(`slot name ${slotPurchaseNameFull}`);
|
2023-12-28 16:24:52 +01:00
|
|
|
const slotPurchaseName = parseSlotPurchaseName(
|
|
|
|
slotPurchaseNameFull.substring(slotPurchaseNameFull.lastIndexOf("/") + 1)
|
|
|
|
);
|
2024-01-06 16:26:58 +01:00
|
|
|
logger.debug(`slot purchase name ${slotPurchaseName}`);
|
2023-12-28 16:24:52 +01:00
|
|
|
|
2024-01-06 16:26:58 +01:00
|
|
|
const slotName = slotPurchaseNameToSlotName[slotPurchaseName].name;
|
|
|
|
const slotsPerPurchase = slotPurchaseNameToSlotName[slotPurchaseName].slotsPerPurchase;
|
2023-12-28 16:24:52 +01:00
|
|
|
|
2024-01-06 16:26:58 +01:00
|
|
|
await updateSlots(accountId, slotName, slotsPerPurchase, slotsPerPurchase);
|
|
|
|
|
|
|
|
logger.debug(`added ${slotsPerPurchase} slot ${slotName}`);
|
2023-12-28 16:24:52 +01:00
|
|
|
|
|
|
|
return {
|
|
|
|
InventoryChanges: {
|
2024-01-06 16:26:58 +01:00
|
|
|
[slotName]: {
|
2023-12-28 16:24:52 +01:00
|
|
|
count: 0,
|
|
|
|
platinum: 1,
|
2024-01-06 16:26:58 +01:00
|
|
|
Slots: slotsPerPurchase,
|
|
|
|
Extra: slotsPerPurchase
|
2023-12-28 16:24:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2023-06-14 02:26:19 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleWeaponsPurchase = async (weaponName: string, accountId: string) => {
|
|
|
|
const weaponType = getWeaponType(weaponName);
|
|
|
|
const addedWeapon = await addWeapon(weaponType, weaponName, accountId);
|
|
|
|
|
2023-12-28 16:24:52 +01:00
|
|
|
await updateSlots(accountId, SlotNameToInventoryName.WEAPON, 0, 1);
|
2023-06-14 02:26:19 +02:00
|
|
|
|
|
|
|
return {
|
|
|
|
InventoryChanges: {
|
|
|
|
WeaponBin: { count: 1, platinum: 0, Slots: -1 },
|
|
|
|
[weaponType]: [addedWeapon]
|
|
|
|
}
|
2023-12-28 16:24:52 +01:00
|
|
|
} as IPurchaseResponse;
|
2023-06-14 02:26:19 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const handlePowersuitPurchase = async (powersuitName: string, accountId: string) => {
|
2023-12-14 17:34:15 +01:00
|
|
|
if (powersuitName.includes("EntratiMech")) {
|
|
|
|
const mechSuit = await addMechSuit(powersuitName, accountId);
|
2023-12-28 16:24:52 +01:00
|
|
|
|
|
|
|
await updateSlots(accountId, SlotNameToInventoryName.MECHSUIT, 0, 1);
|
2024-01-06 16:26:58 +01:00
|
|
|
logger.debug("mech suit", mechSuit);
|
2023-12-14 17:34:15 +01:00
|
|
|
|
|
|
|
return {
|
|
|
|
InventoryChanges: {
|
|
|
|
MechBin: {
|
|
|
|
count: 1,
|
|
|
|
platinum: 0,
|
|
|
|
Slots: -1
|
|
|
|
},
|
|
|
|
MechSuits: [mechSuit]
|
|
|
|
}
|
2023-12-28 16:24:52 +01:00
|
|
|
} as IPurchaseResponse;
|
2023-12-14 17:34:15 +01:00
|
|
|
}
|
|
|
|
|
2023-06-14 02:26:19 +02:00
|
|
|
const suit = await addPowerSuit(powersuitName, accountId);
|
2023-12-28 16:24:52 +01:00
|
|
|
await updateSlots(accountId, SlotNameToInventoryName.SUIT, 0, 1);
|
2023-06-14 02:26:19 +02:00
|
|
|
|
|
|
|
return {
|
|
|
|
InventoryChanges: {
|
|
|
|
SuitBin: {
|
|
|
|
count: 1,
|
|
|
|
platinum: 0,
|
|
|
|
Slots: -1
|
|
|
|
},
|
|
|
|
Suits: [suit]
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-12-28 16:24:52 +01:00
|
|
|
//TODO: change to getInventory, apply changes then save at the end
|
2024-05-09 01:27:32 +02:00
|
|
|
const handleTypesPurchase = async (typesName: string, accountId: string, quantity: number) => {
|
2023-06-14 02:26:19 +02:00
|
|
|
const typeCategory = getStoreItemTypesCategory(typesName);
|
2024-01-06 16:26:58 +01:00
|
|
|
logger.debug(`type category ${typeCategory}`);
|
2023-06-14 02:26:19 +02:00
|
|
|
switch (typeCategory) {
|
2024-05-09 01:27:32 +02:00
|
|
|
case "AvatarImages":
|
2023-06-14 02:26:19 +02:00
|
|
|
case "SuitCustomizations":
|
2024-05-09 01:27:32 +02:00
|
|
|
return await handleCustomizationPurchase(typesName, accountId);
|
2023-12-14 17:34:15 +01:00
|
|
|
case "Sentinels":
|
|
|
|
return await handleSentinelPurchase(typesName, accountId);
|
2023-12-28 16:24:52 +01:00
|
|
|
case "SlotItems":
|
|
|
|
return await handleSlotPurchase(typesName, accountId);
|
2024-05-09 01:27:32 +02:00
|
|
|
case "Items":
|
|
|
|
return await handleMiscItemPurchase(typesName, accountId, quantity);
|
2024-05-09 16:04:31 +02:00
|
|
|
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;
|
2023-06-14 02:26:19 +02:00
|
|
|
default:
|
|
|
|
throw new Error(`unknown Types category: ${typeCategory} not implemented or new`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-12-14 17:34:15 +01:00
|
|
|
const handleSentinelPurchase = async (sentinelName: string, accountId: string) => {
|
|
|
|
const sentinel = await addSentinel(sentinelName, accountId);
|
|
|
|
|
2023-12-28 16:24:52 +01:00
|
|
|
await updateSlots(accountId, SlotNameToInventoryName.SENTINEL, 0, 1);
|
|
|
|
|
2023-12-14 17:34:15 +01:00
|
|
|
return {
|
|
|
|
InventoryChanges: {
|
|
|
|
SentinelBin: { count: 1, platinum: 0, Slots: -1 },
|
|
|
|
Sentinels: [sentinel]
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2024-05-09 01:27:32 +02:00
|
|
|
const handleCustomizationPurchase = async (customizationName: string, accountId: string) => {
|
2023-06-14 02:26:19 +02:00
|
|
|
const customization = await addCustomization(customizationName, accountId);
|
|
|
|
|
|
|
|
return {
|
|
|
|
InventoryChanges: {
|
|
|
|
FlavourItems: [customization]
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
2023-08-31 14:29:09 +04:00
|
|
|
|
|
|
|
const boosterCollection = [
|
|
|
|
"/Lotus/Types/Boosters/ResourceAmountBooster",
|
|
|
|
"/Lotus/Types/Boosters/AffinityBooster",
|
|
|
|
"/Lotus/Types/Boosters/ResourceDropChanceBooster",
|
|
|
|
"/Lotus/Types/Boosters/CreditBooster"
|
|
|
|
];
|
|
|
|
|
|
|
|
const handleBoostersPurchase = async (boosterStoreName: string, accountId: string) => {
|
|
|
|
const match = boosterStoreName.match(/(\d+)Day/);
|
|
|
|
if (!match) return;
|
|
|
|
|
|
|
|
const extractedDigit = Number(match[1]);
|
|
|
|
const ItemType = boosterCollection.find(i =>
|
|
|
|
boosterStoreName.includes(i.split("/").pop()!.replace("Booster", ""))
|
|
|
|
)!;
|
|
|
|
const ExpiryDate = extractedDigit * 86400;
|
|
|
|
|
|
|
|
await addBooster(ItemType, ExpiryDate, accountId);
|
|
|
|
|
|
|
|
return {
|
|
|
|
InventoryChanges: {
|
|
|
|
Boosters: [{ ItemType, ExpiryDate }]
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
2024-05-09 01:27:32 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
2024-05-09 16:04:31 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|