SpaceNinjaServer/src/services/purchaseService.ts

251 lines
9.1 KiB
TypeScript
Raw Normal View History

2024-01-25 14:49:45 +01:00
import { parseSlotPurchaseName } from "@/src/helpers/purchaseHelpers";
import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
import {
addBooster,
addItem,
addMiscItems,
combineInventoryChanges,
getInventory,
updateCurrency,
updateSlots
} from "@/src/services/inventoryService";
import { getVendorManifestByOid } from "@/src/services/serversideVendorsService";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { IPurchaseRequest, SlotPurchase, IInventoryChanges } from "@/src/types/purchaseTypes";
2024-01-06 16:26:58 +01:00
import { logger } from "@/src/utils/logger";
import { ExportBundles, ExportGear, ExportVendors, TRarity } from "warframe-public-export-plus";
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);
if (purchaseRequest.PurchaseParams.Source == 7) {
const manifest = getVendorManifestByOid(purchaseRequest.PurchaseParams.SourceId!);
if (manifest) {
const offer = manifest.VendorInfo.ItemManifest.find(
x => x.StoreItem == purchaseRequest.PurchaseParams.StoreItem
);
if (offer) {
purchaseRequest.PurchaseParams.Quantity *= offer.QuantityMultiplier;
}
}
}
2024-06-15 22:12:57 +02:00
const purchaseResponse = await handleStoreItemAcquisition(
purchaseRequest.PurchaseParams.StoreItem,
accountId,
purchaseRequest.PurchaseParams.Quantity,
"COMMON"
2024-06-15 22:12:57 +02:00
);
if (!purchaseResponse) throw new Error("purchase response was undefined");
const currencyChanges = await updateCurrency(
purchaseRequest.PurchaseParams.ExpectedPrice,
purchaseRequest.PurchaseParams.UsePremium,
accountId
);
purchaseResponse.InventoryChanges = {
...currencyChanges,
...purchaseResponse.InventoryChanges
};
switch (purchaseRequest.PurchaseParams.Source) {
case 7:
if (purchaseRequest.PurchaseParams.SourceId! in ExportVendors) {
const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
const offer = vendor.items.find(x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem);
if (offer) {
const inventory = await getInventory(accountId);
for (const item of offer.itemPrices) {
const invItem: IMiscItem = {
ItemType: item.ItemType,
2024-12-22 22:14:08 +01:00
ItemCount: item.ItemCount * purchaseRequest.PurchaseParams.Quantity * -1
};
addMiscItems(inventory, [invItem]);
purchaseResponse.InventoryChanges.MiscItems ??= [];
const change = (purchaseResponse.InventoryChanges.MiscItems as IMiscItem[]).find(
x => x.ItemType == item.ItemType
);
if (change) {
2024-12-22 22:14:08 +01:00
change.ItemCount += invItem.ItemCount;
} else {
(purchaseResponse.InventoryChanges.MiscItems as IMiscItem[]).push(invItem);
}
}
await inventory.save();
}
}
break;
}
return purchaseResponse;
};
2024-06-15 22:12:57 +02:00
const handleStoreItemAcquisition = async (
storeItemName: string,
accountId: string,
quantity: number,
durability: TRarity,
ignorePurchaseQuantity: boolean = false
): Promise<{ InventoryChanges: IInventoryChanges }> => {
2024-06-15 22:12:57 +02:00
let purchaseResponse = {
InventoryChanges: {}
};
logger.debug(`handling acquision of ${storeItemName}`);
if (storeItemName in ExportBundles) {
const bundle = ExportBundles[storeItemName];
logger.debug("acquiring bundle", bundle);
for (const component of bundle.components) {
combineInventoryChanges(
purchaseResponse.InventoryChanges,
(
await handleStoreItemAcquisition(
component.typeName,
accountId,
component.purchaseQuantity * quantity,
component.durability,
true
)
).InventoryChanges
);
2024-06-15 22:12:57 +02:00
}
} else {
const storeCategory = getStoreItemCategory(storeItemName);
const internalName = storeItemName.replace("/StoreItems", "");
logger.debug(`store category ${storeCategory}`);
if (!ignorePurchaseQuantity) {
if (internalName in ExportGear) {
quantity *= ExportGear[internalName].purchaseQuantity || 1;
}
}
2024-06-15 22:12:57 +02:00
switch (storeCategory) {
default:
purchaseResponse = await addItem(accountId, internalName, quantity);
2024-06-15 22:12:57 +02:00
break;
case "Types":
purchaseResponse = await handleTypesPurchase(internalName, accountId, quantity);
break;
case "Boosters":
purchaseResponse = await handleBoostersPurchase(internalName, accountId, durability);
2024-06-15 22:12:57 +02:00
break;
}
}
return purchaseResponse;
};
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
): Promise<{ InventoryChanges: IInventoryChanges }> => {
2024-01-06 16:26:58 +01:00
logger.debug(`slot name ${slotPurchaseNameFull}`);
const slotPurchaseName = parseSlotPurchaseName(
slotPurchaseNameFull.substring(slotPurchaseNameFull.lastIndexOf("/") + 1)
);
2024-01-06 16:26:58 +01:00
logger.debug(`slot purchase name ${slotPurchaseName}`);
2024-01-06 16:26:58 +01:00
const slotName = slotPurchaseNameToSlotName[slotPurchaseName].name;
const slotsPerPurchase = slotPurchaseNameToSlotName[slotPurchaseName].slotsPerPurchase;
2024-01-06 16:26:58 +01:00
await updateSlots(accountId, slotName, slotsPerPurchase, slotsPerPurchase);
logger.debug(`added ${slotsPerPurchase} slot ${slotName}`);
return {
InventoryChanges: {
2024-01-06 16:26:58 +01:00
[slotName]: {
count: 0,
platinum: 1,
2024-01-06 16:26:58 +01:00
Slots: slotsPerPurchase,
Extra: slotsPerPurchase
}
}
};
};
//TODO: change to getInventory, apply changes then save at the end
const handleTypesPurchase = async (
typesName: string,
accountId: string,
quantity: number
): Promise<{ InventoryChanges: IInventoryChanges }> => {
const typeCategory = getStoreItemTypesCategory(typesName);
2024-01-06 16:26:58 +01:00
logger.debug(`type category ${typeCategory}`);
switch (typeCategory) {
default:
return await addItem(accountId, typesName, quantity);
case "SlotItems":
return await handleSlotPurchase(typesName, accountId);
}
};
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 boosterDuration: Record<TRarity, number> = {
COMMON: 3 * 86400,
UNCOMMON: 7 * 86400,
RARE: 30 * 86400,
LEGENDARY: 90 * 86400
};
const handleBoostersPurchase = async (
boosterStoreName: string,
accountId: string,
durability: TRarity
): Promise<{ InventoryChanges: IInventoryChanges }> => {
const ItemType = boosterStoreName.replace("StoreItem", "");
if (!boosterCollection.find(x => x == ItemType)) {
logger.error(`unknown booster type: ${ItemType}`);
2024-06-15 22:12:57 +02:00
return { InventoryChanges: {} };
}
2023-08-31 14:29:09 +04:00
const ExpiryDate = boosterDuration[durability];
2023-08-31 14:29:09 +04:00
await addBooster(ItemType, ExpiryDate, accountId);
return {
InventoryChanges: {
Boosters: [{ ItemType, ExpiryDate }]
}
};
};