2023-06-01 17:08:05 -07:00
|
|
|
import { RequestHandler } from "express";
|
2025-03-16 04:32:57 -07:00
|
|
|
import { ExportWeapons } from "warframe-public-export-plus";
|
|
|
|
import { IMongoDate } from "@/src/types/commonTypes";
|
|
|
|
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
|
|
|
import { CRng } from "@/src/services/rngService";
|
2025-03-17 05:10:44 -07:00
|
|
|
import { ArtifactPolarity, EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
|
|
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
|
|
|
import {
|
|
|
|
addEquipment,
|
|
|
|
applyDefaultUpgrades,
|
|
|
|
getInventory,
|
|
|
|
occupySlot,
|
|
|
|
productCategoryToInventoryBin,
|
|
|
|
updateCurrency
|
|
|
|
} from "@/src/services/inventoryService";
|
|
|
|
import { getDefaultUpgrades } from "@/src/services/itemDataService";
|
|
|
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
|
|
|
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
|
|
|
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
2023-06-01 17:08:05 -07:00
|
|
|
|
2025-03-17 05:10:44 -07:00
|
|
|
export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
2025-03-16 04:32:57 -07:00
|
|
|
const partTypeToParts: Record<string, string[]> = {};
|
|
|
|
for (const [uniqueName, data] of Object.entries(ExportWeapons)) {
|
|
|
|
if (data.partType) {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
|
|
partTypeToParts[data.partType] ??= [];
|
|
|
|
partTypeToParts[data.partType].push(uniqueName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-17 05:10:44 -07:00
|
|
|
if (req.query.op == "SyncAll") {
|
|
|
|
res.json({
|
|
|
|
SaleInfos: getSaleInfos(partTypeToParts, Math.trunc(Date.now() / 86400000))
|
|
|
|
});
|
|
|
|
} else if (req.query.op == "Purchase") {
|
|
|
|
const accountId = await getAccountIdForRequest(req);
|
|
|
|
const inventory = await getInventory(accountId);
|
|
|
|
const payload = getJSONfromString<IModularWeaponPurchaseRequest>(String(req.body));
|
|
|
|
const weaponInfo = getSaleInfos(partTypeToParts, payload.Revision).find(x => x.Name == payload.SaleName)!
|
|
|
|
.Weapons[payload.ItemIndex];
|
|
|
|
const category = modularWeaponTypes[weaponInfo.ItemType];
|
|
|
|
const defaultUpgrades = getDefaultUpgrades(weaponInfo.ModularParts);
|
|
|
|
const configs = applyDefaultUpgrades(inventory, defaultUpgrades);
|
|
|
|
const inventoryChanges: IInventoryChanges = {
|
|
|
|
...addEquipment(
|
|
|
|
inventory,
|
|
|
|
category,
|
|
|
|
weaponInfo.ItemType,
|
|
|
|
weaponInfo.ModularParts,
|
|
|
|
{},
|
|
|
|
{
|
|
|
|
Features: EquipmentFeatures.DOUBLE_CAPACITY | EquipmentFeatures.GILDED,
|
|
|
|
ItemName: payload.ItemName,
|
|
|
|
Configs: configs,
|
|
|
|
Polarity: [
|
|
|
|
{
|
|
|
|
Slot: payload.PolarizeSlot,
|
|
|
|
Value: payload.PolarizeValue
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
2025-03-16 04:32:57 -07:00
|
|
|
),
|
2025-03-17 05:10:44 -07:00
|
|
|
...occupySlot(inventory, productCategoryToInventoryBin(category)!, true),
|
|
|
|
...updateCurrency(inventory, weaponInfo.PremiumPrice, true)
|
|
|
|
};
|
|
|
|
if (defaultUpgrades) {
|
|
|
|
inventoryChanges.RawUpgrades = defaultUpgrades.map(x => ({ ItemType: x.ItemType, ItemCount: 1 }));
|
|
|
|
}
|
|
|
|
await inventory.save();
|
|
|
|
res.json({
|
|
|
|
InventoryChanges: inventoryChanges
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
throw new Error(`unknown modularWeaponSale op: ${String(req.query.op)}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const getSaleInfos = (partTypeToParts: Record<string, string[]>, day: number): IModularWeaponSaleInfo[] => {
|
|
|
|
const kitgunIsPrimary: boolean = (day & 1) != 0;
|
|
|
|
return [
|
|
|
|
getModularWeaponSale(
|
|
|
|
partTypeToParts,
|
|
|
|
day,
|
|
|
|
"Ostron",
|
|
|
|
["LWPT_HILT", "LWPT_BLADE", "LWPT_HILT_WEIGHT"],
|
|
|
|
() => "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon"
|
|
|
|
),
|
|
|
|
getModularWeaponSale(
|
|
|
|
partTypeToParts,
|
|
|
|
day,
|
|
|
|
"SolarisUnitedHoverboard",
|
|
|
|
["LWPT_HB_DECK", "LWPT_HB_ENGINE", "LWPT_HB_FRONT", "LWPT_HB_JET"],
|
|
|
|
() => "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit"
|
|
|
|
),
|
|
|
|
getModularWeaponSale(
|
|
|
|
partTypeToParts,
|
|
|
|
day,
|
|
|
|
"SolarisUnitedMoaPet",
|
|
|
|
["LWPT_MOA_LEG", "LWPT_MOA_HEAD", "LWPT_MOA_ENGINE", "LWPT_MOA_PAYLOAD"],
|
|
|
|
() => "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit"
|
|
|
|
),
|
|
|
|
getModularWeaponSale(
|
|
|
|
partTypeToParts,
|
|
|
|
day,
|
|
|
|
"SolarisUnitedKitGun",
|
|
|
|
[
|
|
|
|
kitgunIsPrimary ? "LWPT_GUN_PRIMARY_HANDLE" : "LWPT_GUN_SECONDARY_HANDLE",
|
|
|
|
"LWPT_GUN_BARREL",
|
|
|
|
"LWPT_GUN_CLIP"
|
|
|
|
],
|
|
|
|
(parts: string[]) => {
|
|
|
|
const barrel = parts[1];
|
|
|
|
const gunType = ExportWeapons[barrel].gunType!;
|
|
|
|
if (kitgunIsPrimary) {
|
|
|
|
return {
|
|
|
|
GT_RIFLE: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
|
|
|
|
GT_SHOTGUN: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
|
|
|
|
GT_BEAM: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam"
|
|
|
|
}[gunType];
|
|
|
|
} else {
|
|
|
|
return {
|
|
|
|
GT_RIFLE: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary",
|
|
|
|
GT_SHOTGUN: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun",
|
|
|
|
GT_BEAM: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
|
|
|
|
}[gunType];
|
2025-03-16 04:32:57 -07:00
|
|
|
}
|
2025-03-17 05:10:44 -07:00
|
|
|
}
|
|
|
|
)
|
|
|
|
];
|
2025-03-16 04:32:57 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
const priceFactor: Record<string, number> = {
|
|
|
|
Ostron: 0.9,
|
|
|
|
SolarisUnitedHoverboard: 0.85,
|
|
|
|
SolarisUnitedMoaPet: 0.95,
|
|
|
|
SolarisUnitedKitGun: 0.9
|
2023-06-01 17:08:05 -07:00
|
|
|
};
|
|
|
|
|
2025-03-16 04:32:57 -07:00
|
|
|
const getModularWeaponSale = (
|
|
|
|
partTypeToParts: Record<string, string[]>,
|
|
|
|
day: number,
|
|
|
|
name: string,
|
|
|
|
partTypes: string[],
|
|
|
|
getItemType: (parts: string[]) => string
|
|
|
|
): IModularWeaponSaleInfo => {
|
|
|
|
const rng = new CRng(day);
|
|
|
|
const parts = partTypes.map(partType => rng.randomElement(partTypeToParts[partType]));
|
|
|
|
let partsCost = 0;
|
|
|
|
for (const part of parts) {
|
|
|
|
const meta = ExportWeapons[part];
|
|
|
|
if (!meta.premiumPrice) {
|
|
|
|
throw new Error(`no premium price for ${part}`);
|
|
|
|
}
|
|
|
|
partsCost += meta.premiumPrice;
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
Name: name,
|
|
|
|
Expiry: toMongoDate(new Date((day + 1) * 86400000)),
|
|
|
|
Revision: day,
|
|
|
|
Weapons: [
|
|
|
|
{
|
|
|
|
ItemType: getItemType(parts),
|
|
|
|
PremiumPrice: Math.trunc(partsCost * priceFactor[name]),
|
|
|
|
ModularParts: parts
|
|
|
|
}
|
|
|
|
]
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
interface IModularWeaponSaleInfo {
|
|
|
|
Name: string;
|
|
|
|
Expiry: IMongoDate;
|
|
|
|
Revision: number;
|
|
|
|
Weapons: IModularWeaponSaleItem[];
|
|
|
|
}
|
|
|
|
|
|
|
|
interface IModularWeaponSaleItem {
|
|
|
|
ItemType: string;
|
|
|
|
PremiumPrice: number;
|
|
|
|
ModularParts: string[];
|
|
|
|
}
|
2025-03-17 05:10:44 -07:00
|
|
|
|
|
|
|
interface IModularWeaponPurchaseRequest {
|
|
|
|
SaleName: string;
|
|
|
|
ItemIndex: number;
|
|
|
|
Revision: number;
|
|
|
|
ItemName: string;
|
|
|
|
PolarizeSlot: number;
|
|
|
|
PolarizeValue: ArtifactPolarity;
|
|
|
|
}
|