purchase fix

This commit is contained in:
AMelonInsideLemon 2024-12-01 08:21:39 +01:00
parent 38e7d3d078
commit 46f39c5211
12 changed files with 387 additions and 378 deletions

View File

@ -103,7 +103,7 @@ export const focusController: RequestHandler = async (req, res) => {
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingChassis",
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel"
];
const result = await addEquipment("OperatorAmps", request.StartingWeaponType, accountId, parts);
const result = await addEquipment("OperatorAmps", request.StartingWeaponType, accountId, parts, true);
res.json(result);
break;
}

View File

@ -2,16 +2,8 @@ import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService";
import { WeaponTypeInternal } from "@/src/services/itemDataService";
import { ArtifactPolarity, EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes";
const modularWeaponCategory: (WeaponTypeInternal | "Hoverboards")[] = [
"LongGuns",
"Pistols",
"Melee",
"OperatorAmps",
"Hoverboards" // Not sure about hoverboards just coppied from modual crafting
];
import { equipmentKeys, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
interface IGildWeaponRequest {
ItemName: string;
@ -19,7 +11,7 @@ interface IGildWeaponRequest {
PolarizeSlot?: number;
PolarizeValue?: ArtifactPolarity;
ItemId: string;
Category: WeaponTypeInternal | "Hoverboards";
Category: TEquipmentKey;
}
// In export there no recipes for gild action, so reputation and ressources only consumed visually
@ -29,10 +21,10 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const data: IGildWeaponRequest = getJSONfromString(String(req.body));
data.ItemId = String(req.query.ItemId);
if (!modularWeaponCategory.includes(req.query.Category as WeaponTypeInternal | "Hoverboards")) {
if (!equipmentKeys.includes(req.query.Category as TEquipmentKey)) {
throw new Error(`Unknown modular weapon Category: ${req.query.Category}`);
}
data.Category = req.query.Category as WeaponTypeInternal | "Hoverboards";
data.Category = req.query.Category as TEquipmentKey;
const inventory = await getInventory(accountId);
if (!inventory[data.Category]) {

View File

@ -2,7 +2,7 @@ import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { WeaponTypeInternal } from "@/src/services/itemDataService";
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
// eslint-disable-next-line @typescript-eslint/no-misused-promises
export const setWeaponSkillTreeController: RequestHandler = async (req, res) => {
@ -10,7 +10,7 @@ export const setWeaponSkillTreeController: RequestHandler = async (req, res) =>
const inventory = await getInventory(accountId);
const payload = getJSONfromString(String(req.body)) as ISetWeaponSkillTreeRequest;
const item = inventory[req.query.Category as WeaponTypeInternal].find(
const item = inventory[req.query.Category as TEquipmentKey].find(
item => item._id.toString() == (req.query.ItemId as string)
)!;
item.SkillTree = payload.SkillTree;

View File

@ -1,28 +1,15 @@
import { getAccountIdForRequest } from "@/src/services/loginService";
import { ItemType, toAddItemRequest } from "@/src/helpers/customHelpers/addItemHelpers";
import { getWeaponType } from "@/src/services/itemDataService";
import { addPowerSuit, addEquipment } from "@/src/services/inventoryService";
import { toAddItemRequest } from "@/src/helpers/customHelpers/addItemHelpers";
import { addItem } from "@/src/services/inventoryService";
import { RequestHandler } from "express";
// eslint-disable-next-line @typescript-eslint/no-misused-promises
const addItemController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const request = toAddItemRequest(req.body);
switch (request.type) {
case ItemType.Powersuit:
const powersuit = await addPowerSuit(request.InternalName, accountId);
res.json(powersuit);
return;
case ItemType.Weapon:
const weaponType = getWeaponType(request.InternalName);
const weapon = await addEquipment(weaponType, request.InternalName, accountId);
res.json(weapon);
break;
default:
res.status(400).json({ error: "something went wrong" });
break;
}
const response = await addItem(request.InternalName, accountId, 1, true);
res.json(response);
return;
};
export { addItemController };

View File

@ -36,7 +36,9 @@ import {
IPeriodicMissionCompletionDatabase,
IPeriodicMissionCompletionResponse,
ILoreFragmentScan,
IEvolutionProgress
IEvolutionProgress,
IKubrowPetEgg as IKubrowPetEggDatabase,
IKubrowPetEggResponse
} from "../../types/inventoryTypes/inventoryTypes";
import { IOid } from "../../types/commonTypes";
import {
@ -578,6 +580,26 @@ const evolutionProgressSchema = new Schema<IEvolutionProgress>(
{ _id: false }
);
const kubrowPetEggSchema = new Schema<IKubrowPetEggDatabase>({
ItemType: String,
ExpirationDate: Date
});
kubrowPetEggSchema.virtual("ItemId").get(function () {
return { $oid: this._id.toString() };
});
kubrowPetEggSchema.set("toJSON", {
virtuals: true,
transform(_document, returnedObject) {
delete returnedObject._id;
delete returnedObject.__v;
(returnedObject as IKubrowPetEggResponse).ExpirationDate = {
$date: { $numberLong: (returnedObject as IKubrowPetEggDatabase).ExpirationDate.getTime().toString() }
};
}
});
const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
{
accountOwnerId: Schema.Types.ObjectId,
@ -676,7 +698,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//Modular Pets
MoaPets: [EquipmentSchema],
KubrowPetEggs: [Schema.Types.Mixed],
KubrowPetEggs: [kubrowPetEggSchema],
//Like PowerSuit Cat\Kubrow or etc Pets
KubrowPets: [EquipmentSchema],
//Prints Cat(3 Prints)\Kubrow(2 Prints) Pets
@ -726,7 +748,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
CrewShipRawSalvage: [Schema.Types.Mixed],
//Default RailJack
CrewShips: [Schema.Types.Mixed],
CrewShips: [EquipmentSchema],
CrewShipAmmo: [typeCountSchema],
CrewShipWeapons: [Schema.Types.Mixed],
CrewShipWeaponSkins: [Schema.Types.Mixed],
@ -984,7 +1006,12 @@ type InventoryDocumentProps = {
SentinelWeapons: Types.DocumentArray<IEquipmentDatabase>;
Hoverboards: Types.DocumentArray<IEquipmentDatabase>;
MoaPets: Types.DocumentArray<IEquipmentDatabase>;
KubrowPets: Types.DocumentArray<IEquipmentDatabase>;
CrewShips: Types.DocumentArray<IEquipmentDatabase>;
CrewShipHarnesses: Types.DocumentArray<IEquipmentDatabase>;
KubrowPetEggs: Types.DocumentArray<IKubrowPetEggDatabase>;
WeaponSkins: Types.DocumentArray<IWeaponSkinDatabase>;
QuestKeys: Types.DocumentArray<IQuestKeyDatabase>;
};
// eslint-disable-next-line @typescript-eslint/ban-types

View File

@ -13,31 +13,38 @@ import {
IRawUpgrade,
ISeasonChallenge,
ITypeCount,
InventorySlot,
IWeaponSkinClient,
TEquipmentKey,
equipmentKeys,
IFusionTreasure
IFusionTreasure,
IKubrowPetEgg,
IQuestKeyResponse
} from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate } from "../types/genericUpdate";
import { IGenericUpdate } from "@/src/types/genericUpdate";
import {
IArtifactsRequest,
IMissionInventoryUpdateRequest,
IThemeUpdateRequest,
IUpdateChallengeProgressRequest
} from "../types/requestTypes";
} from "@/src/types/requestTypes";
import { logger } from "@/src/utils/logger";
import { getWeaponType, getExalted } from "@/src/services/itemDataService";
import { getDefaultGear, getBinKey } from "@/src/services/itemDataService";
import { getRandomWeightedReward } from "@/src/services/rngService";
import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndicateTypes";
import { IEquipmentClient } from "../types/inventoryTypes/commonInventoryTypes";
import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "@/src/types/syndicateTypes";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
import {
ExportBoosterPacks,
ExportCustoms,
ExportFlavour,
ExportKeys,
ExportRecipes,
ExportResources
ExportResources,
ExportSentinels,
ExportUpgrades,
ExportWarframes,
ExportWeapons
} from "warframe-public-export-plus";
import { creditBundles, fusionBundles } from "@/src/services/missionInventoryUpdateService";
export const createInventory = async (
accountOwnerId: Types.ObjectId,
@ -105,8 +112,9 @@ export const getInventory = async (accountOwnerId: string) => {
export const addItem = async (
accountId: string,
typeName: string,
quantity: number = 1
): Promise<{ InventoryChanges: IInventoryChanges }> => {
quantity: number = 1,
isStorePurchase: boolean = false
): Promise<{ InventoryChanges: any }> => {
// Strict typing
if (typeName in ExportRecipes) {
const inventory = await getInventory(accountId);
@ -124,22 +132,105 @@ export const addItem = async (
}
};
}
if (typeName in ExportResources) {
const inventory = await getInventory(accountId);
const miscItemChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
MiscItems: miscItemChanges
switch (ExportResources[typeName].productCategory) {
case "ShipDecorations": {
const inventory = await getInventory(accountId);
const changes = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addShipDecorations(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
ShipDecorations: changes
}
};
}
};
case "MiscItems": {
const inventory = await getInventory(accountId);
const miscItemChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
MiscItems: miscItemChanges
}
};
}
case "CrewShips": {
const inventory = await getInventory(accountId);
const miscItemChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
const crewShipChanges = await addEquipment(
ExportResources[typeName].productCategory,
typeName,
accountId,
undefined,
isStorePurchase
);
const crewShipHarnessChanges = await addEquipment(
"CrewShipHarnesses",
"/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness",
accountId,
undefined,
isStorePurchase
);
await inventory.save();
return {
InventoryChanges: {
...crewShipChanges.InventoryChanges,
...crewShipHarnessChanges.InventoryChanges,
MiscItems: miscItemChanges
}
};
}
case "KubrowPetEggs": {
const inventory = await getInventory(accountId);
const changes = {
ItemType: typeName,
ExpirationDate: new Date()
} satisfies IKubrowPetEgg;
return {
InventoryChanges: {
kubrowPetEggs: [await addKubrowEgg(inventory, changes)]
}
};
}
case "FusionTreasures": {
const inventory = await getInventory(accountId);
const changes = [
{
ItemType: typeName,
ItemCount: quantity,
Sockets: 0
} satisfies IFusionTreasure
];
addFusionTreasures(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
FusionTreasures: changes
}
};
}
}
}
if (typeName in ExportCustoms) {
return {
InventoryChanges: {
@ -147,6 +238,7 @@ export const addItem = async (
}
};
}
if (typeName in ExportFlavour) {
return {
InventoryChanges: {
@ -154,6 +246,7 @@ export const addItem = async (
}
};
}
if (typeName in ExportBoosterPacks) {
const pack = ExportBoosterPacks[typeName];
const InventoryChanges = {};
@ -163,248 +256,130 @@ export const addItem = async (
logger.debug(`booster pack rolled`, result);
combineInventoryChanges(
InventoryChanges,
(await addItem(accountId, result.type, result.itemCount)).InventoryChanges
(await addItem(accountId, result.type, result.itemCount, isStorePurchase)).InventoryChanges
);
}
}
return { InventoryChanges };
}
// Path-based duck typing
switch (typeName.substr(1).split("/")[1]) {
case "Powersuits":
switch (typeName.substr(1).split("/")[2]) {
default: {
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 "Archwing": {
const spaceSuit = await addSpaceSuit(typeName, accountId);
await updateSlots(accountId, InventorySlot.SPACESUITS, 0, 1);
return {
InventoryChanges: {
SpaceSuitBin: {
count: 1,
platinum: 0,
Slots: -1
},
SpaceSuits: [spaceSuit]
}
};
}
case "EntratiMech": {
const mechSuit = await addMechSuit(typeName, accountId);
await updateSlots(accountId, InventorySlot.MECHSUITS, 0, 1);
return {
InventoryChanges: {
MechBin: {
count: 1,
platinum: 0,
Slots: -1
},
MechSuits: [mechSuit]
}
};
}
if (typeName in ExportUpgrades) {
const inventory = await getInventory(accountId);
const changes = [
{
ItemType: typeName,
ItemCount: quantity
}
break;
case "Weapons":
const weaponType = getWeaponType(typeName);
const weapon = await addEquipment(weaponType, typeName, accountId);
await updateSlots(accountId, InventorySlot.WEAPONS, 0, 1);
return {
InventoryChanges: {
WeaponBin: { count: 1, platinum: 0, Slots: -1 },
[weaponType]: [weapon]
}
};
case "Upgrades": {
const inventory = await getInventory(accountId);
const changes = [
{
ItemType: typeName,
ItemCount: quantity
}
];
addMods(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
RawUpgrades: changes
}
};
];
addMods(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
RawUpgrades: changes
}
};
}
for (const exportCategory of [ExportWarframes, ExportWeapons, ExportSentinels]) {
if (typeName in exportCategory) {
const categoryData = exportCategory[typeName];
const productCategory = categoryData.productCategory;
const changes = await addEquipment(productCategory, typeName, accountId, undefined, isStorePurchase);
const binKey = getBinKey(productCategory);
const inventoryChanges = { ...changes.InventoryChanges };
if (binKey && !isStorePurchase) {
await updateSlots(accountId, binKey, 0, 1);
inventoryChanges[binKey] = inventoryChanges[binKey]
? {
...inventoryChanges[binKey],
count: inventoryChanges[binKey].count + 1,
Slots: inventoryChanges[binKey].Slots - 1
}
: { count: 1, platinum: 0, Slots: -1 };
}
return { InventoryChanges: inventoryChanges };
}
case "Objects": {
// /Lotus/Objects/Tenno/Props/TnoLisetTextProjector (Note Beacon)
const inventory = await getInventory(accountId);
const changes = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addShipDecorations(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
ShipDecorations: changes
}
};
}
case "Types":
switch (typeName.substr(1).split("/")[2]) {
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": {
switch (typeName.substr(1).split("/")[3]) {
case "ShipDecos": {
const inventory = await getInventory(accountId);
const changes = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addShipDecorations(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
ShipDecorations: changes
}
};
}
default: {
const inventory = await getInventory(accountId);
const miscItemChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
MiscItems: miscItemChanges
}
};
}
}
}
case "Game":
if (typeName.substr(1).split("/")[3] == "Projections") {
// Void Relics, e.g. /Lotus/Types/Game/Projections/T2VoidProjectionGaussPrimeDBronze
const inventory = await getInventory(accountId);
const miscItemChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
MiscItems: miscItemChanges
}
};
}
break;
case "Restoratives": // Codex Scanner, Remote Observer, Starburst
}
if (typeName in ExportKeys) {
const changes = {
ItemType: typeName
};
addKey(typeName, accountId);
return {
InventoryChanges: {
QuestKeys: [changes]
}
};
}
// Path-based duck typing
const pathParts = typeName.slice(1).split("/");
if (pathParts[1] === "Types") {
switch (pathParts[2]) {
case "Game":
if (pathParts[3] === "Projections") {
// Void Relics, e.g. /Lotus/Types/Game/Projections/T2VoidProjectionGaussPrimeDBronze
const inventory = await getInventory(accountId);
const consumablesChanges = [
const miscItemChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies IConsumable
} satisfies IMiscItem
];
addConsumables(inventory, consumablesChanges);
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
Consumables: consumablesChanges
MiscItems: miscItemChanges
}
};
}
break;
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
}
};
case "StoreItems":
if (pathParts[3] === "CreditBundles") {
const currencyChanges = await updateCurrency(creditBundles[typeName] * -quantity, false, accountId);
return {
InventoryChanges: {
...currencyChanges
}
};
}
break;
}
} else if (pathParts[2] === "Mods" && pathParts[3] === "FusionBundles") {
const inventory = await getInventory(accountId);
const fusionPoints = fusionBundles[typeName] * quantity;
inventory.FusionPoints += fusionPoints;
await inventory.save();
return {
InventoryChanges: {
FusionPoints: fusionPoints
}
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);
const sentinelIndex = inventory.Sentinels.push({ ItemType: sentinelName, Configs: [], XP: 0 });
const changedInventory = await inventory.save();
return changedInventory.Sentinels[sentinelIndex - 1].toJSON();
};
export const addPowerSuit = async (powersuitName: string, accountId: string): Promise<IEquipmentClient> => {
const specialItems = getExalted(powersuitName);
if (specialItems != false) {
for await (const specialItem of specialItems) {
await addSpecialItem(specialItem, accountId);
}
}
const inventory = await getInventory(accountId);
const suitIndex = inventory.Suits.push({ ItemType: powersuitName, Configs: [], UpgradeVer: 101, XP: 0 });
const changedInventory = await inventory.save();
return changedInventory.Suits[suitIndex - 1].toJSON();
};
export const addMechSuit = async (mechsuitName: string, accountId: string) => {
const specialItems = getExalted(mechsuitName);
if (specialItems != false) {
for await (const specialItem of specialItems) {
await addSpecialItem(specialItem, accountId);
}
}
const inventory = await getInventory(accountId);
const suitIndex = inventory.MechSuits.push({ ItemType: mechsuitName, Configs: [], UpgradeVer: 101, XP: 0 });
const changedInventory = await inventory.save();
return changedInventory.MechSuits[suitIndex - 1].toJSON();
};
export const addSpecialItem = async (itemName: string, accountId: string) => {
const inventory = await getInventory(accountId);
const specialItemIndex = inventory.SpecialItems.push({
ItemType: itemName,
Configs: [],
Features: 1,
UpgradeVer: 101,
XP: 0
});
const changedInventory = await inventory.save();
return changedInventory.SpecialItems[specialItemIndex - 1].toJSON();
};
export const addSpaceSuit = async (spacesuitName: string, accountId: string) => {
const inventory = await getInventory(accountId);
const suitIndex = inventory.SpaceSuits.push({ ItemType: spacesuitName, Configs: [], UpgradeVer: 101, XP: 0 });
const changedInventory = await inventory.save();
return changedInventory.SpaceSuits[suitIndex - 1].toJSON();
};
export const updateSlots = async (accountId: string, slotName: SlotNames, slotAmount: number, extraAmount: number) => {
const inventory = await getInventory(accountId);
@ -515,8 +490,23 @@ export const addEquipment = async (
category: TEquipmentKey,
type: string,
accountId: string,
modularParts: string[] | undefined = undefined
): Promise<IEquipmentClient> => {
modularParts: string[] | undefined = undefined,
isStorePurchase: boolean = false
): Promise<{ InventoryChanges: any }> => {
const defaultGear = getDefaultGear(type);
let InventoryChanges: any = {};
if (defaultGear != false) {
for await (const item of defaultGear) {
logger.debug(`defaultGear ${item}`);
const result = await addItem(accountId, item, 1, isStorePurchase);
InventoryChanges = {
...InventoryChanges,
...result.InventoryChanges
};
}
}
const inventory = await getInventory(accountId);
const index = inventory[category].push({
@ -527,7 +517,12 @@ export const addEquipment = async (
});
const changedInventory = await inventory.save();
return changedInventory[category][index - 1].toJSON();
InventoryChanges[category] = [changedInventory[category][index - 1].toJSON()];
return {
InventoryChanges
};
};
export const addCustomization = async (customizatonName: string, accountId: string): Promise<IFlavourItem> => {
@ -544,6 +539,23 @@ export const addSkin = async (typeName: string, accountId: string): Promise<IWea
return changedInventory.WeaponSkins[index].toJSON();
};
export const addKubrowEgg = async (
inventory: IInventoryDatabaseDocument,
kubrowEgg: IKubrowPetEgg
): Promise<IKubrowPetEgg> => {
const index =
inventory.KubrowPetEggs.push({ ItemType: kubrowEgg.ItemType, ExpirationDate: kubrowEgg.ExpirationDate }) - 1;
const changedInventory = await inventory.save();
return changedInventory.KubrowPetEggs[index - 1];
};
export const addKey = async (typeName: string, accountId: string): Promise<IQuestKeyResponse> => {
const inventory = await getInventory(accountId);
const index = inventory.QuestKeys.push({ ItemType: typeName }) - 1;
const changedInventory = await inventory.save();
return changedInventory.QuestKeys[index].toJSON();
};
const addGearExpByCategory = (
inventory: IInventoryDatabaseDocument,
gearArray: IEquipmentClient[] | undefined,

View File

@ -1,80 +1,55 @@
import { getIndexAfter } from "@/src/helpers/stringHelpers";
import { logger } from "@/src/utils/logger";
import {
dict_en,
ExportRecipes,
ExportWarframes,
ExportWeapons,
IPowersuit,
IRecipe
} from "warframe-public-export-plus";
export type WeaponTypeInternal =
| "LongGuns"
| "Pistols"
| "Melee"
| "SpaceMelee"
| "SpaceGuns"
| "SentinelWeapons"
| "OperatorAmps"
| "SpecialItems";
export const getWeaponType = (weaponName: string): WeaponTypeInternal => {
const weaponInfo = ExportWeapons[weaponName];
if (!weaponInfo) {
throw new Error(`unknown weapon ${weaponName}`);
}
// Many non-weapon items are "Pistols" in Public Export, so some duck typing is needed.
if (weaponInfo.totalDamage == 0) {
throw new Error(`${weaponName} doesn't quack like a weapon`);
}
const weaponType = weaponInfo.productCategory;
if (!weaponType) {
logger.error(`unknown weapon category for item ${weaponName}`);
throw new Error(`unknown weapon category for item ${weaponName}`);
}
return weaponType;
};
import { dict_en, ExportRecipes, ExportSentinels, ExportWarframes, IRecipe } from "warframe-public-export-plus";
import { InventorySlot, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
export const getRecipe = (uniqueName: string): IRecipe | undefined => {
return ExportRecipes[uniqueName];
};
export const getExalted = (uniqueName: string) => {
const suit = getSuitByUniqueName(uniqueName);
if (suit?.exalted !== undefined) {
return suit.exalted;
} else {
return false;
}
};
export const getItemCategoryByUniqueName = (uniqueName: string) => {
//Lotus/Types/Items/MiscItems/PolymerBundle
let splitWord = "Items/";
if (!uniqueName.includes("/Items/")) {
splitWord = "/Types/";
export const getDefaultGear = (itemType: string) => {
if (itemType in ExportWarframes) {
return ExportWarframes[itemType]?.exalted ?? false;
}
const index = getIndexAfter(uniqueName, splitWord);
if (index === -1) {
logger.error(`error parsing item category ${uniqueName}`);
throw new Error(`error parsing item category ${uniqueName}`);
if (itemType in ExportSentinels) {
const { defaultUpgrades = [], defaultWeapon } = ExportSentinels[itemType] || {};
const defaultGear = [
...defaultUpgrades.map(upgrade => upgrade.ItemType),
...(defaultWeapon ? [defaultWeapon] : [])
];
return defaultGear.length ? defaultGear : false;
}
const category = uniqueName.substring(index).split("/")[0];
return category;
};
export const getSuitByUniqueName = (uniqueName: string): IPowersuit | undefined => {
return ExportWarframes[uniqueName];
return false;
};
export const getEnglishString = (key: string): string => {
return dict_en[key] ?? key;
};
export const getBinKey = (equipmentType: TEquipmentKey): InventorySlot | null => {
switch (equipmentType) {
case "Suits":
return InventorySlot.SUITS;
case "MechSuits":
return InventorySlot.MECHSUITS;
case "LongGuns":
case "Pistols":
case "Melee":
return InventorySlot.WEAPONS;
case "Sentinels":
case "SentinelWeapons":
case "MoaPets":
case "KubrowPets":
return InventorySlot.SENTINELS;
case "SpaceSuits":
case "Hoverboards":
return InventorySlot.SPACESUITS;
case "SpaceGuns":
case "SpaceMelee":
return InventorySlot.SPACEWEAPON;
case "OperatorAmps":
return InventorySlot.OPERATORAMP;
default:
return null;
}
};

View File

@ -123,7 +123,10 @@ const getRandomRewardByChance = (pool: IReward[]): IRngResult | undefined => {
return getRandomReward(pool as IRngResult[]);
};
const creditBundles: Record<string, number> = {
export const creditBundles: Record<string, number> = {
"/Lotus/Types/StoreItems/CreditBundles/CreditBundleA": 50000,
"/Lotus/Types/StoreItems/CreditBundles/CreditBundleB": 25000,
"/Lotus/Types/StoreItems/CreditBundles/CreditBundleC": 175000,
"/Lotus/StoreItems/Types/PickUps/Credits/1500Credits": 1500,
"/Lotus/StoreItems/Types/PickUps/Credits/2000Credits": 2000,
"/Lotus/StoreItems/Types/PickUps/Credits/2500Credits": 2500,
@ -139,7 +142,10 @@ const creditBundles: Record<string, number> = {
"/Lotus/StoreItems/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardThreeHard": 250000
};
const fusionBundles: Record<string, number> = {
export const fusionBundles: Record<string, number> = {
"/Lotus/Upgrades/Mods/FusionBundles/MarketTier1FusionBundle": 100,
"/Lotus/Upgrades/Mods/FusionBundles/MarketTier2FusionBundle": 400,
"/Lotus/Upgrades/Mods/FusionBundles/MarketTier3FusionBundle": 1000,
"/Lotus/Upgrades/Mods/FusionBundles/CommonFusionBundle": 15,
"/Lotus/Upgrades/Mods/FusionBundles/UncommonFusionBundle": 50,
"/Lotus/Upgrades/Mods/FusionBundles/RareFusionBundle": 80

View File

@ -91,10 +91,10 @@ const handleStoreItemAcquisition = async (
}
switch (storeCategory) {
default:
purchaseResponse = await addItem(accountId, internalName, quantity);
purchaseResponse = await addItem(accountId, internalName, quantity, true);
break;
case "Types":
purchaseResponse = await handleTypesPurchase(internalName, accountId, quantity);
purchaseResponse = await handleTypesPurchase(internalName, accountId, quantity, true);
break;
case "Boosters":
purchaseResponse = await handleBoostersPurchase(internalName, accountId, durability);
@ -154,13 +154,14 @@ const handleSlotPurchase = async (
const handleTypesPurchase = async (
typesName: string,
accountId: string,
quantity: number
quantity: number,
isStorePurchase: boolean = false
): Promise<{ InventoryChanges: IInventoryChanges }> => {
const typeCategory = getStoreItemTypesCategory(typesName);
logger.debug(`type category ${typeCategory}`);
switch (typeCategory) {
default:
return await addItem(accountId, typesName, quantity);
return await addItem(accountId, typesName, quantity, isStorePurchase);
case "SlotItems":
return await handleSlotPurchase(typesName, accountId);
}

View File

@ -1,5 +1,6 @@
import { IMongoDate, IOid } from "@/src/types/commonTypes";
import { Types } from "mongoose";
import { ICrewMembers, ICrewshipWeapon, ICustomization, IFlavourItem } from "@/src/types/inventoryTypes/inventoryTypes";
export interface IPolarity {
Slot: number;
@ -113,6 +114,10 @@ export interface IEquipmentDatabase {
Expiry?: IMongoDate;
SkillTree?: string;
ArchonCrystalUpgrades?: IArchonCrystalUpgrade[];
Weapon?: ICrewshipWeapon;
Customization?: ICustomization;
RailjackImage?: IFlavourItem;
CrewMembers?: ICrewMembers;
_id: Types.ObjectId;
}

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Document, Types } from "mongoose";
import { IOid, IMongoDate } from "../commonTypes";
import { IOid, IMongoDate } from "@/src/types/commonTypes";
import {
ArtifactPolarity,
IColor,
@ -25,6 +25,7 @@ export interface IInventoryDatabase
| "BlessingCooldown"
| "Ships"
| "WeaponSkins"
| "KubrowPetEggs"
> {
accountOwnerId: Types.ObjectId;
Created: Date;
@ -37,6 +38,7 @@ export interface IInventoryDatabase
BlessingCooldown: Date;
Ships: Types.ObjectId[];
WeaponSkins: IWeaponSkinDatabase[];
KubrowPetEggs: IKubrowPetEgg[];
}
export interface IInventoryResponseDocument extends IInventoryResponse, Document {}
@ -63,6 +65,7 @@ export interface ITypeCount {
export const equipmentKeys = [
"Suits",
"MechSuits",
"LongGuns",
"Pistols",
"Melee",
@ -74,7 +77,10 @@ export const equipmentKeys = [
"SpaceMelee",
"Hoverboards",
"OperatorAmps",
"MoaPets"
"MoaPets",
"KubrowPets",
"CrewShips",
"CrewShipHarnesses"
] as const;
export type TEquipmentKey = (typeof equipmentKeys)[number];
@ -111,6 +117,10 @@ export type TSolarMapRegion =
export interface IPendingRecipeResponse extends Omit<IPendingRecipe, "CompletionDate"> {
CompletionDate: IMongoDate;
}
export interface IKubrowPetEggResponse extends Omit<IKubrowPetEgg, "ExpirationDate"> {
ExpirationDate: IMongoDate;
}
export interface IInventoryResponse {
Horses: IEquipmentDatabase[];
DrifterMelee: IEquipmentDatabase[];
@ -181,7 +191,7 @@ export interface IInventoryResponse {
TauntHistory?: ITaunt[];
StoryModeChoice: string;
PeriodicMissionCompletions: IPeriodicMissionCompletionDatabase[];
KubrowPetEggs: IKubrowPetEgg[];
KubrowPetEggs: IKubrowPetEggResponse[];
LoreFragmentScans: ILoreFragmentScan[];
EquippedEmotes: string[];
PendingTrades: IPendingTrade[];
@ -249,7 +259,7 @@ export interface IInventoryResponse {
LastNemesisAllySpawnTime?: IMongoDate;
Settings: ISettings;
PersonalTechProjects: IPersonalTechProject[];
CrewShips: ICrewShip[];
CrewShips: IEquipmentDatabase[];
PlayerSkills: IPlayerSkills;
CrewShipAmmo: IConsumable[];
CrewShipSalvagedWeaponSkins: ICrewShipSalvagedWeaponSkin[];
@ -391,9 +401,13 @@ export enum InventorySlot {
SUITS = "SuitBin",
WEAPONS = "WeaponBin",
SPACESUITS = "SpaceSuitBin",
SPACEWEAPON = "SpaceWeaponBin",
MECHSUITS = "MechBin",
PVE_LOADOUTS = "PveBonusLoadoutBin",
SENTINELS = "SentinelBin"
SENTINELS = "SentinelBin",
OPERATORAMP = "OperatorAmpBin",
CREWMEMBERS = "CrewMemberBin",
CREWSHIPSALVAGE = "CrewShipSalvageBin"
}
export interface ISlots {
@ -417,18 +431,6 @@ export interface ICrewShipWeapon {
ItemId: IOid;
}
export interface ICrewShip {
ItemType: string;
Configs: IItemConfig[];
Weapon: ICrewshipWeapon;
Customization: ICustomization;
ItemName: string;
RailjackImage: IFlavourItem;
CrewMembers: ICrewMembers;
ItemId: IOid;
_id: Types.ObjectId;
}
export interface ICrewMembers {
SLOT_A: ISlot;
SLOT_B: ISlot;
@ -536,13 +538,9 @@ export interface IInvasionChainProgress {
}
export interface IKubrowPetEgg {
ItemType: KubrowPetEggItemType;
ExpirationDate: IMongoDate;
ItemId: IOid;
}
export enum KubrowPetEggItemType {
LotusTypesGameKubrowPetEggsKubrowEgg = "/Lotus/Types/Game/KubrowPet/Eggs/KubrowEgg"
ItemType: string;
ExpirationDate: Date;
ItemId?: IOid;
}
export interface IKubrowPetPrint {

View File

@ -1,4 +1,4 @@
import { IOid } from "./commonTypes";
import { IOid } from "@/src/types/commonTypes";
import { ArtifactPolarity, IPolarity, IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
import {
IBooster,
@ -13,8 +13,9 @@ import {
ISeasonChallenge,
TSolarMapRegion,
TEquipmentKey,
IFusionTreasure
} from "./inventoryTypes/inventoryTypes";
IFusionTreasure,
IKubrowPetEggResponse
} from "@/src/types/inventoryTypes/inventoryTypes";
export interface IArtifactsRequest {
Upgrade: ICrewShipSalvagedWeaponSkin;
@ -58,6 +59,11 @@ export interface IMissionInventoryUpdateRequest {
Hoverboards?: IEquipmentClient[];
OperatorAmps?: IEquipmentClient[];
MoaPets?: IEquipmentClient[];
CrewShips?: IEquipmentClient[];
CrewShipHarnesses?: IEquipmentClient[];
MechSuits?: IEquipmentClient[];
KubrowPets?: IEquipmentClient[];
KubrowPetsEggs?: IKubrowPetEggResponse[];
FusionBundles?: ITypeCount[];
RawUpgrades?: IRawUpgrade[];
MiscItems?: IMiscItem[];