feat: purchase modular weapon from daily special (#1217)

Closes #685

Reviewed-on: OpenWF/SpaceNinjaServer#1217
This commit is contained in:
Sainan 2025-03-17 05:10:44 -07:00
parent 1d091e3c4c
commit 0be54dd7ce
5 changed files with 162 additions and 79 deletions

View File

@ -1,7 +1,6 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
import { import {
getInventory, getInventory,
updateCurrency, updateCurrency,
@ -11,26 +10,9 @@ import {
occupySlot, occupySlot,
productCategoryToInventoryBin productCategoryToInventoryBin
} from "@/src/services/inventoryService"; } from "@/src/services/inventoryService";
import { ExportWeapons } from "warframe-public-export-plus";
import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { getDefaultUpgrades } from "@/src/services/itemDataService";
const modularWeaponTypes: Record<string, TEquipmentKey> = { import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryLauncher": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimarySniper": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary": "Pistols",
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": "Pistols",
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": "Pistols",
"/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": "Melee",
"/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon": "OperatorAmps",
"/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit": "Hoverboards",
"/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit": "MoaPets",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit": "MoaPets",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit": "MoaPets",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit": "MoaPets"
};
interface IModularCraftRequest { interface IModularCraftRequest {
WeaponType: string; WeaponType: string;
@ -46,14 +28,15 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
const category = modularWeaponTypes[data.WeaponType]; const category = modularWeaponTypes[data.WeaponType];
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const defaultUpgrades = getDefaultUpgrades(data.Parts);
const configs = applyDefaultUpgrades(inventory, ExportWeapons[data.Parts[0]]?.defaultUpgrades); const configs = applyDefaultUpgrades(inventory, defaultUpgrades);
// Give weapon
const inventoryChanges: IInventoryChanges = { const inventoryChanges: IInventoryChanges = {
...addEquipment(inventory, category, data.WeaponType, data.Parts, {}, { Configs: configs }), ...addEquipment(inventory, category, data.WeaponType, data.Parts, {}, { Configs: configs }),
...occupySlot(inventory, productCategoryToInventoryBin(category)!, false) ...occupySlot(inventory, productCategoryToInventoryBin(category)!, false)
}; };
if (defaultUpgrades) {
inventoryChanges.RawUpgrades = defaultUpgrades.map(x => ({ ItemType: x.ItemType, ItemCount: 1 }));
}
// Remove credits & parts // Remove credits & parts
const miscItemChanges = []; const miscItemChanges = [];

View File

@ -3,9 +3,22 @@ import { ExportWeapons } from "warframe-public-export-plus";
import { IMongoDate } from "@/src/types/commonTypes"; import { IMongoDate } from "@/src/types/commonTypes";
import { toMongoDate } from "@/src/helpers/inventoryHelpers"; import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { CRng } from "@/src/services/rngService"; import { CRng } from "@/src/services/rngService";
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";
// op=SyncAll export const modularWeaponSaleController: RequestHandler = async (req, res) => {
export const modularWeaponSaleController: RequestHandler = (_req, res) => {
const partTypeToParts: Record<string, string[]> = {}; const partTypeToParts: Record<string, string[]> = {};
for (const [uniqueName, data] of Object.entries(ExportWeapons)) { for (const [uniqueName, data] of Object.entries(ExportWeapons)) {
if (data.partType) { if (data.partType) {
@ -15,60 +28,105 @@ export const modularWeaponSaleController: RequestHandler = (_req, res) => {
} }
} }
const today: number = Math.trunc(Date.now() / 86400000); if (req.query.op == "SyncAll") {
const kitgunIsPrimary: boolean = (today & 1) != 0; res.json({
res.json({ SaleInfos: getSaleInfos(partTypeToParts, Math.trunc(Date.now() / 86400000))
SaleInfos: [ });
getModularWeaponSale( } else if (req.query.op == "Purchase") {
partTypeToParts, const accountId = await getAccountIdForRequest(req);
today, const inventory = await getInventory(accountId);
"Ostron", const payload = getJSONfromString<IModularWeaponPurchaseRequest>(String(req.body));
["LWPT_HILT", "LWPT_BLADE", "LWPT_HILT_WEIGHT"], const weaponInfo = getSaleInfos(partTypeToParts, payload.Revision).find(x => x.Name == payload.SaleName)!
() => "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon" .Weapons[payload.ItemIndex];
), const category = modularWeaponTypes[weaponInfo.ItemType];
getModularWeaponSale( const defaultUpgrades = getDefaultUpgrades(weaponInfo.ModularParts);
partTypeToParts, const configs = applyDefaultUpgrades(inventory, defaultUpgrades);
today, const inventoryChanges: IInventoryChanges = {
"SolarisUnitedHoverboard", ...addEquipment(
["LWPT_HB_DECK", "LWPT_HB_ENGINE", "LWPT_HB_FRONT", "LWPT_HB_JET"], inventory,
() => "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit" category,
), weaponInfo.ItemType,
getModularWeaponSale( weaponInfo.ModularParts,
partTypeToParts, {},
today, {
"SolarisUnitedMoaPet", Features: EquipmentFeatures.DOUBLE_CAPACITY | EquipmentFeatures.GILDED,
["LWPT_MOA_LEG", "LWPT_MOA_HEAD", "LWPT_MOA_ENGINE", "LWPT_MOA_PAYLOAD"], ItemName: payload.ItemName,
() => "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit" Configs: configs,
), Polarity: [
getModularWeaponSale( {
partTypeToParts, Slot: payload.PolarizeSlot,
today, Value: payload.PolarizeValue
"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];
}
} }
) ),
] ...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];
}
}
)
];
}; };
const priceFactor: Record<string, number> = { const priceFactor: Record<string, number> = {
@ -121,3 +179,12 @@ interface IModularWeaponSaleItem {
PremiumPrice: number; PremiumPrice: number;
ModularParts: string[]; ModularParts: string[];
} }
interface IModularWeaponPurchaseRequest {
SaleName: string;
ItemIndex: number;
Revision: number;
ItemName: string;
PolarizeSlot: number;
PolarizeValue: ArtifactPolarity;
}

View File

@ -0,0 +1,19 @@
import { TEquipmentKey } from "../types/inventoryTypes/inventoryTypes";
export const modularWeaponTypes: Record<string, TEquipmentKey> = {
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryLauncher": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimarySniper": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary": "Pistols",
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": "Pistols",
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": "Pistols",
"/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": "Melee",
"/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon": "OperatorAmps",
"/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit": "Hoverboards",
"/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit": "MoaPets",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit": "MoaPets",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit": "MoaPets",
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit": "MoaPets"
};

View File

@ -201,6 +201,7 @@ apiRouter.post("/joinSession.php", joinSessionController);
apiRouter.post("/login.php", loginController); apiRouter.post("/login.php", loginController);
apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController); apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController);
apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController); apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController);
apiRouter.post("/modularWeaponSale.php", modularWeaponSaleController);
apiRouter.post("/nameWeapon.php", nameWeaponController); apiRouter.post("/nameWeapon.php", nameWeaponController);
apiRouter.post("/placeDecoInComponent.php", placeDecoInComponentController); apiRouter.post("/placeDecoInComponent.php", placeDecoInComponentController);
apiRouter.post("/playerSkills.php", playerSkillsController); apiRouter.post("/playerSkills.php", playerSkillsController);

View File

@ -29,6 +29,7 @@ import {
ExportSentinels, ExportSentinels,
ExportWarframes, ExportWarframes,
ExportWeapons, ExportWeapons,
IDefaultUpgrade,
IInboxMessage, IInboxMessage,
IMissionReward, IMissionReward,
IPowersuit, IPowersuit,
@ -256,3 +257,15 @@ export const toStoreItem = (type: string): string => {
export const fromStoreItem = (type: string): string => { export const fromStoreItem = (type: string): string => {
return "/Lotus/" + type.substring("/Lotus/StoreItems/".length); return "/Lotus/" + type.substring("/Lotus/StoreItems/".length);
}; };
export const getDefaultUpgrades = (parts: string[]): IDefaultUpgrade[] | undefined => {
const allDefaultUpgrades: IDefaultUpgrade[] = [];
for (const part of parts) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const defaultUpgrades = ExportWeapons[part]?.defaultUpgrades;
if (defaultUpgrades) {
allDefaultUpgrades.push(...defaultUpgrades);
}
}
return allDefaultUpgrades.length == 0 ? undefined : allDefaultUpgrades;
};