forked from OpenWF/SpaceNinjaServer
Purchase Loadouts and Inventory Slots (#105)
This commit is contained in:
parent
dd99e8782c
commit
1ab411e3cc
@ -7,5 +7,6 @@
|
|||||||
"skipStoryModeChoice": true,
|
"skipStoryModeChoice": true,
|
||||||
"skipTutorial": true,
|
"skipTutorial": true,
|
||||||
"testMission": true,
|
"testMission": true,
|
||||||
"testQuestKey": true
|
"testQuestKey": true,
|
||||||
|
"infinitePlatinum": false
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,27 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
import config from "@/config.json";
|
||||||
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { parseString } from "@/src/helpers/general";
|
||||||
|
|
||||||
const getCreditsController: RequestHandler = (_req, res) => {
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
res.json({ RegularCredits: 42069, TradesRemaining: 1, PremiumCreditsFree: 42069, PremiumCredits: 42069 });
|
export const getCreditsController: RequestHandler = async (req, res) => {
|
||||||
|
if (config.infinitePlatinum) {
|
||||||
|
res.json({
|
||||||
|
RegularCredits: 999999999,
|
||||||
|
TradesRemaining: 999999999,
|
||||||
|
PremiumCreditsFree: 999999999,
|
||||||
|
PremiumCredits: 999999999
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountId = parseString(req.query.accountId);
|
||||||
|
|
||||||
|
const inventory = await getInventory(accountId);
|
||||||
|
res.json({
|
||||||
|
RegularCredits: inventory.RegularCredits,
|
||||||
|
TradesRemaining: inventory.TradesRemaining,
|
||||||
|
PremiumCreditsFree: inventory.PremiumCreditsFree,
|
||||||
|
PremiumCredits: inventory.PremiumCredits
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export { getCreditsController };
|
|
||||||
|
36
src/controllers/api/inventorySlotsController.ts
Normal file
36
src/controllers/api/inventorySlotsController.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { parseString } from "@/src/helpers/general";
|
||||||
|
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
import { updateSlots } from "@/src/services/inventoryService";
|
||||||
|
import { SlotNameToInventoryName } from "@/src/types/purchaseTypes";
|
||||||
|
|
||||||
|
/*
|
||||||
|
loadout slots are additionally purchased slots only
|
||||||
|
1 slot per mastery rank is automatically given above mr10, without database needing to save the mastery slots
|
||||||
|
extra = everything above the base + 2 slots (e.g. for warframes)
|
||||||
|
new slot = extra + 1 and slots +1
|
||||||
|
using slot = slots -1, except for when purchased with platinum, then slots are included in price
|
||||||
|
|
||||||
|
e.g. number of frames:
|
||||||
|
19 slots, 71 extra
|
||||||
|
= 71 - 19 + 2 = 54
|
||||||
|
19 actually available slots in ingame inventory = 17 extra + 2 Base (base amount depends on slot) (+ 1 for every mastery rank above 10)
|
||||||
|
number of frames = extra - slots + 2
|
||||||
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
export const inventorySlotsController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = parseString(req.query.accountId);
|
||||||
|
//const body = JSON.parse(req.body as string) as IInventorySlotsRequest;
|
||||||
|
|
||||||
|
//console.log(body);
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
//console.log({ InventoryChanges: currencyChanges }, " added loadout changes:");
|
||||||
|
|
||||||
|
res.json({ InventoryChanges: currencyChanges });
|
||||||
|
};
|
@ -3,11 +3,9 @@ import { toPurchaseRequest } from "@/src/helpers/purchaseHelpers";
|
|||||||
import { handlePurchase } from "@/src/services/purchaseService";
|
import { handlePurchase } from "@/src/services/purchaseService";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
|
|
||||||
const purchaseController = async (req: Request, res: Response) => {
|
export const purchaseController = async (req: Request, res: Response) => {
|
||||||
const purchaseRequest = toPurchaseRequest(JSON.parse(String(req.body)));
|
const purchaseRequest = toPurchaseRequest(JSON.parse(String(req.body)));
|
||||||
const accountId = parseString(req.query.accountId);
|
const accountId = parseString(req.query.accountId);
|
||||||
const response = await handlePurchase(purchaseRequest, accountId);
|
const response = await handlePurchase(purchaseRequest, accountId);
|
||||||
res.json(response);
|
res.json(response);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { purchaseController };
|
|
||||||
|
@ -5,7 +5,7 @@ import { handleInventoryItemConfigChange } from "@/src/services/saveLoadoutServi
|
|||||||
import { parseString } from "@/src/helpers/general";
|
import { parseString } from "@/src/helpers/general";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
const saveLoadoutController: RequestHandler = async (req, res) => {
|
export const saveLoadoutController: RequestHandler = async (req, res) => {
|
||||||
//validate here
|
//validate here
|
||||||
const accountId = parseString(req.query.accountId);
|
const accountId = parseString(req.query.accountId);
|
||||||
|
|
||||||
@ -15,13 +15,19 @@ const saveLoadoutController: RequestHandler = async (req, res) => {
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { UpgradeVer, ...equipmentChanges } = body;
|
const { UpgradeVer, ...equipmentChanges } = body;
|
||||||
await handleInventoryItemConfigChange(equipmentChanges, accountId);
|
const newLoadoutId = await handleInventoryItemConfigChange(equipmentChanges, accountId);
|
||||||
|
|
||||||
|
//send back new loadout id, if new loadout was added
|
||||||
|
if (newLoadoutId) {
|
||||||
|
res.send(newLoadoutId);
|
||||||
|
}
|
||||||
res.status(200).end();
|
res.status(200).end();
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
|
console.log("error in saveLoadoutController", error.message);
|
||||||
res.status(400).json({ error: error.message });
|
res.status(400).json({ error: error.message });
|
||||||
|
} else {
|
||||||
|
res.status(400).json({ error: "unknown error" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { saveLoadoutController };
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
export const isEmptyObject = (obj: unknown): boolean => {
|
||||||
|
return Boolean(obj && Object.keys(obj).length === 0 && obj.constructor === Object);
|
||||||
|
};
|
||||||
|
|
||||||
const isString = (text: unknown): text is string => {
|
const isString = (text: unknown): text is string => {
|
||||||
return typeof text === "string" || text instanceof String;
|
return typeof text === "string" || text instanceof String;
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { parseBoolean, parseNumber, parseString } from "@/src/helpers/general";
|
import { parseBoolean, parseNumber, parseString } from "@/src/helpers/general";
|
||||||
import { WeaponTypeInternal } from "@/src/services/inventoryService";
|
import { WeaponTypeInternal } from "@/src/services/inventoryService";
|
||||||
import { IPurchaseRequest } from "@/src/types/purchaseTypes";
|
import { slotPurchaseNameToSlotName } from "@/src/services/purchaseService";
|
||||||
|
import { IPurchaseRequest, SlotPurchaseName } from "@/src/types/purchaseTypes";
|
||||||
import { weapons } from "@/static/data/items";
|
import { weapons } from "@/static/data/items";
|
||||||
|
|
||||||
const toPurchaseRequest = (purchaseRequest: unknown): IPurchaseRequest => {
|
export const toPurchaseRequest = (purchaseRequest: unknown): IPurchaseRequest => {
|
||||||
if (!purchaseRequest || typeof purchaseRequest !== "object") {
|
if (!purchaseRequest || typeof purchaseRequest !== "object") {
|
||||||
throw new Error("incorrect or missing purchase request data");
|
throw new Error("incorrect or missing purchase request data");
|
||||||
}
|
}
|
||||||
@ -40,7 +41,7 @@ const toPurchaseRequest = (purchaseRequest: unknown): IPurchaseRequest => {
|
|||||||
throw new Error("invalid purchaseRequest");
|
throw new Error("invalid purchaseRequest");
|
||||||
};
|
};
|
||||||
|
|
||||||
const getWeaponType = (weaponName: string) => {
|
export const getWeaponType = (weaponName: string) => {
|
||||||
const weaponInfo = weapons.find(i => i.uniqueName === weaponName);
|
const weaponInfo = weapons.find(i => i.uniqueName === weaponName);
|
||||||
|
|
||||||
if (!weaponInfo) {
|
if (!weaponInfo) {
|
||||||
@ -56,4 +57,11 @@ const getWeaponType = (weaponName: string) => {
|
|||||||
return weaponType;
|
return weaponType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { toPurchaseRequest, getWeaponType };
|
export const isSlotPurchaseName = (slotPurchaseName: string): slotPurchaseName is SlotPurchaseName => {
|
||||||
|
return slotPurchaseName in slotPurchaseNameToSlotName;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseSlotPurchaseName = (slotPurchaseName: string) => {
|
||||||
|
if (!isSlotPurchaseName(slotPurchaseName)) throw new Error(`invalid slot name ${slotPurchaseName}`);
|
||||||
|
return slotPurchaseName;
|
||||||
|
};
|
||||||
|
@ -34,6 +34,7 @@ import { artifactsController } from "../controllers/api/artifactsController";
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import { setBootLocationController } from "@/src/controllers/api/setBootLocationController";
|
import { setBootLocationController } from "@/src/controllers/api/setBootLocationController";
|
||||||
import { focusController } from "@/src/controllers/api/focusController";
|
import { focusController } from "@/src/controllers/api/focusController";
|
||||||
|
import { inventorySlotsController } from "@/src/controllers/api/inventorySlotsController";
|
||||||
|
|
||||||
const apiRouter = express.Router();
|
const apiRouter = express.Router();
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ apiRouter.get("/logout.php", logoutController);
|
|||||||
apiRouter.get("/setBootLocation.php", setBootLocationController);
|
apiRouter.get("/setBootLocation.php", setBootLocationController);
|
||||||
|
|
||||||
// post
|
// post
|
||||||
|
apiRouter.post("/inventorySlots.php", inventorySlotsController);
|
||||||
apiRouter.post("/focus.php", focusController);
|
apiRouter.post("/focus.php", focusController);
|
||||||
apiRouter.post("/artifacts.php", artifactsController);
|
apiRouter.post("/artifacts.php", artifactsController);
|
||||||
apiRouter.post("/findSessions.php", findSessionsController);
|
apiRouter.post("/findSessions.php", findSessionsController);
|
||||||
|
@ -2,9 +2,9 @@ import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
|
|||||||
import new_inventory from "@/static/fixed_responses/postTutorialInventory.json";
|
import new_inventory from "@/static/fixed_responses/postTutorialInventory.json";
|
||||||
import config from "@/config.json";
|
import config from "@/config.json";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { ISuitDatabase, ISuitClient } from "@/src/types/inventoryTypes/SuitTypes";
|
import { ISuitClient } from "@/src/types/inventoryTypes/SuitTypes";
|
||||||
import { SlotType } from "@/src/types/purchaseTypes";
|
import { SlotNames } from "@/src/types/purchaseTypes";
|
||||||
import { IWeaponDatabase, IWeaponClient } from "@/src/types/inventoryTypes/weaponTypes";
|
import { IWeaponClient } from "@/src/types/inventoryTypes/weaponTypes";
|
||||||
import {
|
import {
|
||||||
IChallengeProgress,
|
IChallengeProgress,
|
||||||
IConsumable,
|
IConsumable,
|
||||||
@ -41,8 +41,6 @@ export const createInventory = async (accountOwnerId: Types.ObjectId, loadOutPre
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//const updateInventory = async (accountOwnerId: Types.ObjectId, inventoryChanges: any) => {};
|
|
||||||
|
|
||||||
export const getInventory = async (accountOwnerId: string) => {
|
export const getInventory = async (accountOwnerId: string) => {
|
||||||
const inventory = await Inventory.findOne({ accountOwnerId: accountOwnerId });
|
const inventory = await Inventory.findOne({ accountOwnerId: accountOwnerId });
|
||||||
|
|
||||||
@ -53,7 +51,7 @@ export const getInventory = async (accountOwnerId: string) => {
|
|||||||
return inventory;
|
return inventory;
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO: genericMethod for all the add methods, they share a lot of logic
|
//TODO: maybe genericMethod for all the add methods, they share a lot of logic
|
||||||
export const addSentinel = async (sentinelName: string, accountId: string) => {
|
export const addSentinel = async (sentinelName: string, accountId: string) => {
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
const sentinelIndex = inventory.Sentinels.push({ ItemType: sentinelName, Configs: [], XP: 0 });
|
const sentinelIndex = inventory.Sentinels.push({ ItemType: sentinelName, Configs: [], XP: 0 });
|
||||||
@ -75,32 +73,54 @@ export const addMechSuit = async (mechsuitName: string, accountId: string) => {
|
|||||||
return changedInventory.MechSuits[suitIndex - 1].toJSON();
|
return changedInventory.MechSuits[suitIndex - 1].toJSON();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateSlots = async (slotType: SlotType, accountId: string, slots: number) => {
|
export const updateSlots = async (accountId: string, slotName: SlotNames, slotAmount: number, extraAmount: number) => {
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
|
|
||||||
switch (slotType) {
|
inventory[slotName].Slots += slotAmount;
|
||||||
case SlotType.SUIT:
|
if (inventory[slotName].Extra === undefined) {
|
||||||
inventory.SuitBin.Slots += slots;
|
inventory[slotName].Extra = extraAmount;
|
||||||
break;
|
} else {
|
||||||
case SlotType.WEAPON:
|
inventory[slotName].Extra += extraAmount;
|
||||||
inventory.WeaponBin.Slots += slots;
|
|
||||||
break;
|
|
||||||
case SlotType.MECHSUIT:
|
|
||||||
inventory.MechBin.Slots += slots;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("invalid slot type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateCurrency = async (price: number, usePremium: boolean, accountId: string) => {
|
export const updateCurrency = async (price: number, usePremium: boolean, accountId: string) => {
|
||||||
const currencyName = usePremium ? "PremiumCredits" : "RegularCredits";
|
|
||||||
|
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
inventory[currencyName] = inventory[currencyName] - price;
|
|
||||||
|
if (usePremium) {
|
||||||
|
if (inventory.PremiumCreditsFree > 0) {
|
||||||
|
inventory.PremiumCreditsFree += price;
|
||||||
|
}
|
||||||
|
inventory.PremiumCredits += price;
|
||||||
|
} else {
|
||||||
|
inventory.RegularCredits += price;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modifiedPaths = inventory.modifiedPaths();
|
||||||
|
|
||||||
|
type currencyKeys = "RegularCredits" | "PremiumCredits" | "PremiumCreditsFree";
|
||||||
|
|
||||||
|
const currencyChanges = {} as Record<currencyKeys, number>;
|
||||||
|
modifiedPaths.forEach(path => {
|
||||||
|
currencyChanges[path as currencyKeys] = -price;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(currencyChanges, "changes");
|
||||||
|
|
||||||
|
//let changes = {} as keyof currencyKeys;
|
||||||
|
|
||||||
|
// const obj2 = modifiedPaths.reduce(
|
||||||
|
// (obj, key) => {
|
||||||
|
// obj[key as keyof currencyKeys] = price;
|
||||||
|
// return obj;
|
||||||
|
// },
|
||||||
|
// {} as Record<keyof currencyKeys, number>
|
||||||
|
// );
|
||||||
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
return { [currencyName]: -price };
|
return currencyChanges;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: AffiliationMods support (Nightwave).
|
// TODO: AffiliationMods support (Nightwave).
|
||||||
@ -157,7 +177,7 @@ export const addCustomization = async (customizatonName: string, accountId: stri
|
|||||||
|
|
||||||
const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizatonName }) - 1;
|
const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizatonName }) - 1;
|
||||||
const changedInventory = await inventory.save();
|
const changedInventory = await inventory.save();
|
||||||
return changedInventory.FlavourItems[flavourItemIndex].toJSON(); //mongoose bug forces as FlavourItem
|
return changedInventory.FlavourItems[flavourItemIndex].toJSON();
|
||||||
};
|
};
|
||||||
|
|
||||||
const addGearExpByCategory = (
|
const addGearExpByCategory = (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getWeaponType } from "@/src/helpers/purchaseHelpers";
|
import { getWeaponType, parseSlotPurchaseName } from "@/src/helpers/purchaseHelpers";
|
||||||
import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
|
import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
|
||||||
import {
|
import {
|
||||||
addBooster,
|
addBooster,
|
||||||
@ -7,9 +7,10 @@ import {
|
|||||||
addPowerSuit,
|
addPowerSuit,
|
||||||
addSentinel,
|
addSentinel,
|
||||||
addWeapon,
|
addWeapon,
|
||||||
|
updateCurrency,
|
||||||
updateSlots
|
updateSlots
|
||||||
} from "@/src/services/inventoryService";
|
} from "@/src/services/inventoryService";
|
||||||
import { IPurchaseRequest, SlotType } from "@/src/types/purchaseTypes";
|
import { IPurchaseRequest, IPurchaseResponse, SlotNameToInventoryName, SlotPurchase } from "@/src/types/purchaseTypes";
|
||||||
|
|
||||||
export const getStoreItemCategory = (storeItem: string) => {
|
export const getStoreItemCategory = (storeItem: string) => {
|
||||||
const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
|
const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
|
||||||
@ -32,57 +33,109 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI
|
|||||||
const internalName = purchaseRequest.PurchaseParams.StoreItem.replace("/StoreItems", "");
|
const internalName = purchaseRequest.PurchaseParams.StoreItem.replace("/StoreItems", "");
|
||||||
console.log("Store category", storeCategory);
|
console.log("Store category", storeCategory);
|
||||||
|
|
||||||
let purchaseResponse;
|
let inventoryChanges;
|
||||||
switch (storeCategory) {
|
switch (storeCategory) {
|
||||||
case "Powersuits":
|
case "Powersuits":
|
||||||
purchaseResponse = await handlePowersuitPurchase(internalName, accountId);
|
inventoryChanges = await handlePowersuitPurchase(internalName, accountId);
|
||||||
break;
|
break;
|
||||||
case "Weapons":
|
case "Weapons":
|
||||||
purchaseResponse = await handleWeaponsPurchase(internalName, accountId);
|
inventoryChanges = await handleWeaponsPurchase(internalName, accountId);
|
||||||
break;
|
break;
|
||||||
case "Types":
|
case "Types":
|
||||||
purchaseResponse = await handleTypesPurchase(internalName, accountId);
|
inventoryChanges = await handleTypesPurchase(internalName, accountId);
|
||||||
break;
|
break;
|
||||||
case "Boosters":
|
case "Boosters":
|
||||||
purchaseResponse = await handleBoostersPurchase(internalName, accountId);
|
inventoryChanges = await handleBoostersPurchase(internalName, accountId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`unknown store category: ${storeCategory} not implemented or new`);
|
throw new Error(`unknown store category: ${storeCategory} not implemented or new`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// const currencyResponse = await updateCurrency(
|
if (!inventoryChanges) throw new Error("purchase response was undefined");
|
||||||
// purchaseRequest.PurchaseParams.ExpectedPrice,
|
|
||||||
// purchaseRequest.PurchaseParams.UsePremium,
|
|
||||||
// accountId
|
|
||||||
// );
|
|
||||||
|
|
||||||
// (purchaseResponse as IPurchaseResponse).InventoryChanges = {
|
const currencyChanges = await updateCurrency(
|
||||||
// ...purchaseResponse.InventoryChanges,
|
purchaseRequest.PurchaseParams.ExpectedPrice,
|
||||||
// ...currencyResponse
|
purchaseRequest.PurchaseParams.UsePremium,
|
||||||
// };
|
accountId
|
||||||
|
);
|
||||||
|
|
||||||
return purchaseResponse;
|
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) => {
|
||||||
|
console.log("slot name", slotPurchaseNameFull);
|
||||||
|
const slotPurchaseName = parseSlotPurchaseName(
|
||||||
|
slotPurchaseNameFull.substring(slotPurchaseNameFull.lastIndexOf("/") + 1)
|
||||||
|
);
|
||||||
|
console.log(slotPurchaseName, "slot purchase name");
|
||||||
|
|
||||||
|
await updateSlots(
|
||||||
|
accountId,
|
||||||
|
slotPurchaseNameToSlotName[slotPurchaseName].name,
|
||||||
|
slotPurchaseNameToSlotName[slotPurchaseName].slotsPerPurchase,
|
||||||
|
slotPurchaseNameToSlotName[slotPurchaseName].slotsPerPurchase
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
slotPurchaseNameToSlotName[slotPurchaseName].name,
|
||||||
|
slotPurchaseNameToSlotName[slotPurchaseName].slotsPerPurchase,
|
||||||
|
"slots added"
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
InventoryChanges: {
|
||||||
|
[slotPurchaseNameToSlotName[slotPurchaseName].name]: {
|
||||||
|
count: 0,
|
||||||
|
platinum: 1,
|
||||||
|
Slots: slotPurchaseNameToSlotName[slotPurchaseName].slotsPerPurchase,
|
||||||
|
Extra: slotPurchaseNameToSlotName[slotPurchaseName].slotsPerPurchase
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleWeaponsPurchase = async (weaponName: string, accountId: string) => {
|
const handleWeaponsPurchase = async (weaponName: string, accountId: string) => {
|
||||||
const weaponType = getWeaponType(weaponName);
|
const weaponType = getWeaponType(weaponName);
|
||||||
const addedWeapon = await addWeapon(weaponType, weaponName, accountId);
|
const addedWeapon = await addWeapon(weaponType, weaponName, accountId);
|
||||||
|
|
||||||
await updateSlots(SlotType.WEAPON, accountId, -1);
|
await updateSlots(accountId, SlotNameToInventoryName.WEAPON, 0, 1);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
InventoryChanges: {
|
InventoryChanges: {
|
||||||
WeaponBin: { count: 1, platinum: 0, Slots: -1 },
|
WeaponBin: { count: 1, platinum: 0, Slots: -1 },
|
||||||
[weaponType]: [addedWeapon]
|
[weaponType]: [addedWeapon]
|
||||||
}
|
}
|
||||||
};
|
} as IPurchaseResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePowersuitPurchase = async (powersuitName: string, accountId: string) => {
|
const handlePowersuitPurchase = async (powersuitName: string, accountId: string) => {
|
||||||
if (powersuitName.includes("EntratiMech")) {
|
if (powersuitName.includes("EntratiMech")) {
|
||||||
const mechSuit = await addMechSuit(powersuitName, accountId);
|
const mechSuit = await addMechSuit(powersuitName, accountId);
|
||||||
await updateSlots(SlotType.MECHSUIT, accountId, -1);
|
|
||||||
|
await updateSlots(accountId, SlotNameToInventoryName.MECHSUIT, 0, 1);
|
||||||
console.log("mech suit", mechSuit);
|
console.log("mech suit", mechSuit);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -94,11 +147,11 @@ const handlePowersuitPurchase = async (powersuitName: string, accountId: string)
|
|||||||
},
|
},
|
||||||
MechSuits: [mechSuit]
|
MechSuits: [mechSuit]
|
||||||
}
|
}
|
||||||
};
|
} as IPurchaseResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
const suit = await addPowerSuit(powersuitName, accountId);
|
const suit = await addPowerSuit(powersuitName, accountId);
|
||||||
await updateSlots(SlotType.SUIT, accountId, -1);
|
await updateSlots(accountId, SlotNameToInventoryName.SUIT, 0, 1);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
InventoryChanges: {
|
InventoryChanges: {
|
||||||
@ -112,6 +165,7 @@ const handlePowersuitPurchase = async (powersuitName: string, accountId: string)
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//TODO: change to getInventory, apply changes then save at the end
|
||||||
const handleTypesPurchase = async (typesName: string, accountId: string) => {
|
const handleTypesPurchase = async (typesName: string, accountId: string) => {
|
||||||
const typeCategory = getStoreItemTypesCategory(typesName);
|
const typeCategory = getStoreItemTypesCategory(typesName);
|
||||||
console.log("type category", typeCategory);
|
console.log("type category", typeCategory);
|
||||||
@ -122,6 +176,8 @@ const handleTypesPurchase = async (typesName: string, accountId: string) => {
|
|||||||
// break;
|
// break;
|
||||||
case "Sentinels":
|
case "Sentinels":
|
||||||
return await handleSentinelPurchase(typesName, accountId);
|
return await handleSentinelPurchase(typesName, accountId);
|
||||||
|
case "SlotItems":
|
||||||
|
return await handleSlotPurchase(typesName, accountId);
|
||||||
default:
|
default:
|
||||||
throw new Error(`unknown Types category: ${typeCategory} not implemented or new`);
|
throw new Error(`unknown Types category: ${typeCategory} not implemented or new`);
|
||||||
}
|
}
|
||||||
@ -130,6 +186,8 @@ const handleTypesPurchase = async (typesName: string, accountId: string) => {
|
|||||||
const handleSentinelPurchase = async (sentinelName: string, accountId: string) => {
|
const handleSentinelPurchase = async (sentinelName: string, accountId: string) => {
|
||||||
const sentinel = await addSentinel(sentinelName, accountId);
|
const sentinel = await addSentinel(sentinelName, accountId);
|
||||||
|
|
||||||
|
await updateSlots(accountId, SlotNameToInventoryName.SENTINEL, 0, 1);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
InventoryChanges: {
|
InventoryChanges: {
|
||||||
SentinelBin: { count: 1, platinum: 0, Slots: -1 },
|
SentinelBin: { count: 1, platinum: 0, Slots: -1 },
|
||||||
|
@ -8,10 +8,8 @@ import {
|
|||||||
import { LoadoutModel } from "@/src/models/inventoryModels/loadoutModel";
|
import { LoadoutModel } from "@/src/models/inventoryModels/loadoutModel";
|
||||||
import { getInventory } from "@/src/services/inventoryService";
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
import { IOid } from "@/src/types/commonTypes";
|
import { IOid } from "@/src/types/commonTypes";
|
||||||
|
import { Types } from "mongoose";
|
||||||
export const isEmptyObject = (obj: unknown): boolean => {
|
import { isEmptyObject } from "@/src/helpers/general";
|
||||||
return Boolean(obj && Object.keys(obj).length === 0 && obj.constructor === Object);
|
|
||||||
};
|
|
||||||
|
|
||||||
//TODO: setup default items on account creation or like originally in giveStartingItems.php
|
//TODO: setup default items on account creation or like originally in giveStartingItems.php
|
||||||
|
|
||||||
@ -24,7 +22,7 @@ itemconfig has multiple config ids
|
|||||||
export const handleInventoryItemConfigChange = async (
|
export const handleInventoryItemConfigChange = async (
|
||||||
equipmentChanges: ISaveLoadoutRequestNoUpgradeVer,
|
equipmentChanges: ISaveLoadoutRequestNoUpgradeVer,
|
||||||
accountId: string
|
accountId: string
|
||||||
) => {
|
): Promise<string | void> => {
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
|
|
||||||
for (const [_equipmentName, _equipment] of Object.entries(equipmentChanges)) {
|
for (const [_equipmentName, _equipment] of Object.entries(equipmentChanges)) {
|
||||||
@ -40,7 +38,7 @@ export const handleInventoryItemConfigChange = async (
|
|||||||
case "AdultOperatorLoadOuts": {
|
case "AdultOperatorLoadOuts": {
|
||||||
const operatorConfig = equipment as IOperatorConfigEntry;
|
const operatorConfig = equipment as IOperatorConfigEntry;
|
||||||
const operatorLoadout = inventory[equipmentName];
|
const operatorLoadout = inventory[equipmentName];
|
||||||
//console.log("loadout received", equipmentName, operatorConfig);
|
console.log("operator loadout received", equipmentName, operatorConfig);
|
||||||
// all non-empty entries are one loadout slot
|
// all non-empty entries are one loadout slot
|
||||||
for (const [loadoutId, loadoutConfig] of Object.entries(operatorConfig)) {
|
for (const [loadoutId, loadoutConfig] of Object.entries(operatorConfig)) {
|
||||||
// console.log("loadoutId", loadoutId, "loadoutconfig", loadoutConfig);
|
// console.log("loadoutId", loadoutId, "loadoutconfig", loadoutConfig);
|
||||||
@ -60,12 +58,13 @@ export const handleInventoryItemConfigChange = async (
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "LoadOuts": {
|
case "LoadOuts": {
|
||||||
//console.log("loadout received");
|
console.log("loadout received");
|
||||||
const loadout = await LoadoutModel.findOne({ loadoutOwnerId: accountId });
|
const loadout = await LoadoutModel.findOne({ loadoutOwnerId: accountId });
|
||||||
if (!loadout) {
|
if (!loadout) {
|
||||||
throw new Error("loadout not found");
|
throw new Error("loadout not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let newLoadoutId: Types.ObjectId | undefined;
|
||||||
for (const [_loadoutSlot, _loadout] of Object.entries(equipment)) {
|
for (const [_loadoutSlot, _loadout] of Object.entries(equipment)) {
|
||||||
const loadoutSlot = _loadoutSlot as keyof ILoadoutClient;
|
const loadoutSlot = _loadoutSlot as keyof ILoadoutClient;
|
||||||
const newLoadout = _loadout as ILoadoutEntry;
|
const newLoadout = _loadout as ILoadoutEntry;
|
||||||
@ -84,6 +83,16 @@ export const handleInventoryItemConfigChange = async (
|
|||||||
// if no config with this id exists, create a new one
|
// if no config with this id exists, create a new one
|
||||||
if (!oldLoadoutConfig) {
|
if (!oldLoadoutConfig) {
|
||||||
const { ItemId, ...loadoutConfigItemIdRemoved } = loadoutConfig;
|
const { ItemId, ...loadoutConfigItemIdRemoved } = loadoutConfig;
|
||||||
|
|
||||||
|
//save the new object id and assign it for every ffff return at the end
|
||||||
|
if (ItemId.$oid === "ffffffffffffffffffffffff") {
|
||||||
|
if (!newLoadoutId) {
|
||||||
|
newLoadoutId = new Types.ObjectId();
|
||||||
|
}
|
||||||
|
loadout[loadoutSlot].push({ _id: newLoadoutId, ...loadoutConfigItemIdRemoved });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
loadout[loadoutSlot].push({
|
loadout[loadoutSlot].push({
|
||||||
_id: ItemId.$oid,
|
_id: ItemId.$oid,
|
||||||
...loadoutConfigItemIdRemoved
|
...loadoutConfigItemIdRemoved
|
||||||
@ -101,6 +110,11 @@ export const handleInventoryItemConfigChange = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await loadout.save();
|
await loadout.save();
|
||||||
|
|
||||||
|
//only return an id if a new loadout was added
|
||||||
|
if (newLoadoutId) {
|
||||||
|
return newLoadoutId.toString();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "LongGuns":
|
case "LongGuns":
|
||||||
@ -112,7 +126,7 @@ export const handleInventoryItemConfigChange = async (
|
|||||||
case "DrifterMelee":
|
case "DrifterMelee":
|
||||||
case "Sentinels":
|
case "Sentinels":
|
||||||
case "Horses": {
|
case "Horses": {
|
||||||
//console.log("general Item config saved", equipmentName, equipment);
|
console.log("general Item config saved", equipmentName, equipment);
|
||||||
|
|
||||||
const itemEntries = equipment as IItemEntry;
|
const itemEntries = equipment as IItemEntry;
|
||||||
for (const [itemId, itemConfigEntries] of Object.entries(itemEntries)) {
|
for (const [itemId, itemConfigEntries] of Object.entries(itemEntries)) {
|
||||||
|
@ -3,7 +3,7 @@ import { IPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { IItemConfig } from "./commonInventoryTypes";
|
import { IItemConfig } from "./commonInventoryTypes";
|
||||||
|
|
||||||
export interface ISuitClient extends ISuitDatabase {
|
export interface ISuitClient extends Omit<ISuitDatabase, "_id"> {
|
||||||
ItemId: IOid;
|
ItemId: IOid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +341,7 @@ export interface ICrewShipHarnessConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ISlots {
|
export interface ISlots {
|
||||||
Extra?: number;
|
Extra: number; // can be undefined, but not if used via mongoose
|
||||||
Slots: number;
|
Slots: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ISuitDatabase } from "@/src/types/inventoryTypes/SuitTypes";
|
import { slotPurchaseNameToSlotName } from "@/src/services/purchaseService";
|
||||||
|
import { ISuitClient } from "@/src/types/inventoryTypes/SuitTypes";
|
||||||
import { IFlavourItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { IFlavourItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { IWeaponClient } from "@/src/types/inventoryTypes/weaponTypes";
|
import { IWeaponClient } from "@/src/types/inventoryTypes/weaponTypes";
|
||||||
|
|
||||||
@ -22,11 +23,14 @@ export interface IPurchaseResponse {
|
|||||||
InventoryChanges: {
|
InventoryChanges: {
|
||||||
SuitBin?: IBinChanges;
|
SuitBin?: IBinChanges;
|
||||||
WeaponBin?: IBinChanges;
|
WeaponBin?: IBinChanges;
|
||||||
Suits?: ISuitDatabase[];
|
MechBin?: IBinChanges;
|
||||||
|
MechSuits?: ISuitClient[];
|
||||||
|
Suits?: ISuitClient[];
|
||||||
LongGuns?: IWeaponClient[];
|
LongGuns?: IWeaponClient[];
|
||||||
Pistols?: IWeaponClient[];
|
Pistols?: IWeaponClient[];
|
||||||
Melee?: IWeaponClient[];
|
Melee?: IWeaponClient[];
|
||||||
PremiumCredits?: number;
|
PremiumCredits?: number;
|
||||||
|
PremiumCreditsFree?: number;
|
||||||
RegularCredits?: number;
|
RegularCredits?: number;
|
||||||
FlavourItems?: IFlavourItem[];
|
FlavourItems?: IFlavourItem[];
|
||||||
};
|
};
|
||||||
@ -36,10 +40,42 @@ export type IBinChanges = {
|
|||||||
count: number;
|
count: number;
|
||||||
platinum: number;
|
platinum: number;
|
||||||
Slots: number;
|
Slots: number;
|
||||||
|
Extra?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum SlotType {
|
export enum SlotNameToInventoryName {
|
||||||
SUIT = "SuitBin",
|
SUIT = "SuitBin",
|
||||||
WEAPON = "WeaponBin",
|
WEAPON = "WeaponBin",
|
||||||
MECHSUIT = "MechBin"
|
MECHSUIT = "MechBin",
|
||||||
|
LOADOUT = "PveBonusLoadoutBin",
|
||||||
|
SENTINEL = "SentinelBin"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SlotPurchaseName =
|
||||||
|
| "SuitSlotItem"
|
||||||
|
| "TwoSentinelSlotItem"
|
||||||
|
| "TwoWeaponSlotItem"
|
||||||
|
| "SpaceSuitSlotItem"
|
||||||
|
| "TwoSpaceWeaponSlotItem"
|
||||||
|
| "MechSlotItem"
|
||||||
|
| "TwoOperatorWeaponSlotItem"
|
||||||
|
| "RandomModSlotItem"
|
||||||
|
| "TwoCrewShipSalvageSlotItem"
|
||||||
|
| "CrewMemberSlotItem";
|
||||||
|
|
||||||
|
export type SlotNames =
|
||||||
|
| "SuitBin"
|
||||||
|
| "WeaponBin"
|
||||||
|
| "MechBin"
|
||||||
|
| "PveBonusLoadoutBin"
|
||||||
|
| "SentinelBin"
|
||||||
|
| "SpaceSuitBin"
|
||||||
|
| "SpaceWeaponBin"
|
||||||
|
| "OperatorAmpBin"
|
||||||
|
| "RandomModBin"
|
||||||
|
| "CrewShipSalvageBin"
|
||||||
|
| "CrewMemberBin";
|
||||||
|
|
||||||
|
export type SlotPurchase = {
|
||||||
|
[P in SlotPurchaseName]: { name: SlotNames; slotsPerPurchase: number };
|
||||||
|
};
|
||||||
|
@ -10,14 +10,14 @@ import {
|
|||||||
import { IWeaponClient } from "./inventoryTypes/weaponTypes";
|
import { IWeaponClient } from "./inventoryTypes/weaponTypes";
|
||||||
import { ISuitClient } from "./inventoryTypes/SuitTypes";
|
import { ISuitClient } from "./inventoryTypes/SuitTypes";
|
||||||
|
|
||||||
interface IArtifactsRequest {
|
export interface IArtifactsRequest {
|
||||||
Upgrade: ICrewShipSalvagedWeaponSkin;
|
Upgrade: ICrewShipSalvagedWeaponSkin;
|
||||||
LevelDiff: number;
|
LevelDiff: number;
|
||||||
Cost: number;
|
Cost: number;
|
||||||
FusionPointCost: number;
|
FusionPointCost: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMissionInventoryUpdateRequest {
|
export interface IMissionInventoryUpdateRequest {
|
||||||
rewardsMultiplier?: number;
|
rewardsMultiplier?: number;
|
||||||
ActiveBoosters?: IBooster[];
|
ActiveBoosters?: IBooster[];
|
||||||
LongGuns?: IWeaponClient[];
|
LongGuns?: IWeaponClient[];
|
||||||
@ -35,7 +35,7 @@ interface IMissionInventoryUpdateRequest {
|
|||||||
Missions?: IMission;
|
Missions?: IMission;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMissionInventoryUpdateRequestRewardInfo {
|
export interface IMissionInventoryUpdateRequestRewardInfo {
|
||||||
node: string;
|
node: string;
|
||||||
rewardTier?: number;
|
rewardTier?: number;
|
||||||
nightmareMode?: boolean;
|
nightmareMode?: boolean;
|
||||||
@ -50,4 +50,6 @@ interface IMissionInventoryUpdateRequestRewardInfo {
|
|||||||
rewardSeed?: number;
|
rewardSeed?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { IArtifactsRequest, IMissionInventoryUpdateRequest };
|
export interface IInventorySlotsRequest {
|
||||||
|
Bin: "PveBonusLoadoutBin";
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user