feat: randomly generate daily modular weapon sales (#1199)
Re #685 Reviewed-on: OpenWF/SpaceNinjaServer#1199
This commit is contained in:
		
							parent
							
								
									ab11f67f0b
								
							
						
					
					
						commit
						ecc2e35535
					
				
							
								
								
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -17,7 +17,7 @@
 | 
				
			|||||||
        "mongoose": "^8.11.0",
 | 
					        "mongoose": "^8.11.0",
 | 
				
			||||||
        "morgan": "^1.10.0",
 | 
					        "morgan": "^1.10.0",
 | 
				
			||||||
        "typescript": ">=5.5 <5.6.0",
 | 
					        "typescript": ">=5.5 <5.6.0",
 | 
				
			||||||
        "warframe-public-export-plus": "^0.5.44",
 | 
					        "warframe-public-export-plus": "^0.5.46",
 | 
				
			||||||
        "warframe-riven-info": "^0.1.2",
 | 
					        "warframe-riven-info": "^0.1.2",
 | 
				
			||||||
        "winston": "^3.17.0",
 | 
					        "winston": "^3.17.0",
 | 
				
			||||||
        "winston-daily-rotate-file": "^5.0.0"
 | 
					        "winston-daily-rotate-file": "^5.0.0"
 | 
				
			||||||
@ -4006,9 +4006,9 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/warframe-public-export-plus": {
 | 
					    "node_modules/warframe-public-export-plus": {
 | 
				
			||||||
      "version": "0.5.44",
 | 
					      "version": "0.5.46",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.44.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.46.tgz",
 | 
				
			||||||
      "integrity": "sha512-0EH3CQBCuuELiLBL1brc/o6Qx8CK723TJF5o68VXc60ha93Juo6LQ+dV+QgzFvVQ5RZTjBLtKB4MP8qw3YHCUQ=="
 | 
					      "integrity": "sha512-bgxM8A+ccIydpTDRbISKmGt3XJb0rwX5cx04xGtqqhKX1Qs1OJM6NMGa3CKdqy6OiB7xXCPNLbi+KdqvJp9p9A=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/warframe-riven-info": {
 | 
					    "node_modules/warframe-riven-info": {
 | 
				
			||||||
      "version": "0.1.2",
 | 
					      "version": "0.1.2",
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,7 @@
 | 
				
			|||||||
    "mongoose": "^8.11.0",
 | 
					    "mongoose": "^8.11.0",
 | 
				
			||||||
    "morgan": "^1.10.0",
 | 
					    "morgan": "^1.10.0",
 | 
				
			||||||
    "typescript": ">=5.5 <5.6.0",
 | 
					    "typescript": ">=5.5 <5.6.0",
 | 
				
			||||||
    "warframe-public-export-plus": "^0.5.44",
 | 
					    "warframe-public-export-plus": "^0.5.46",
 | 
				
			||||||
    "warframe-riven-info": "^0.1.2",
 | 
					    "warframe-riven-info": "^0.1.2",
 | 
				
			||||||
    "winston": "^3.17.0",
 | 
					    "winston": "^3.17.0",
 | 
				
			||||||
    "winston-daily-rotate-file": "^5.0.0"
 | 
					    "winston-daily-rotate-file": "^5.0.0"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,123 @@
 | 
				
			|||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
import modularWeaponSale from "@/static/fixed_responses/modularWeaponSale.json";
 | 
					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";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const modularWeaponSaleController: RequestHandler = (_req, res) => {
 | 
					// op=SyncAll
 | 
				
			||||||
    res.json(modularWeaponSale);
 | 
					export const modularWeaponSaleController: RequestHandler = (_req, res) => {
 | 
				
			||||||
 | 
					    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);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const today: number = Math.trunc(Date.now() / 86400000);
 | 
				
			||||||
 | 
					    const kitgunIsPrimary: boolean = (today & 1) != 0;
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					        SaleInfos: [
 | 
				
			||||||
 | 
					            getModularWeaponSale(
 | 
				
			||||||
 | 
					                partTypeToParts,
 | 
				
			||||||
 | 
					                today,
 | 
				
			||||||
 | 
					                "Ostron",
 | 
				
			||||||
 | 
					                ["LWPT_HILT", "LWPT_BLADE", "LWPT_HILT_WEIGHT"],
 | 
				
			||||||
 | 
					                () => "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon"
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            getModularWeaponSale(
 | 
				
			||||||
 | 
					                partTypeToParts,
 | 
				
			||||||
 | 
					                today,
 | 
				
			||||||
 | 
					                "SolarisUnitedHoverboard",
 | 
				
			||||||
 | 
					                ["LWPT_HB_DECK", "LWPT_HB_ENGINE", "LWPT_HB_FRONT", "LWPT_HB_JET"],
 | 
				
			||||||
 | 
					                () => "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit"
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            getModularWeaponSale(
 | 
				
			||||||
 | 
					                partTypeToParts,
 | 
				
			||||||
 | 
					                today,
 | 
				
			||||||
 | 
					                "SolarisUnitedMoaPet",
 | 
				
			||||||
 | 
					                ["LWPT_MOA_LEG", "LWPT_MOA_HEAD", "LWPT_MOA_ENGINE", "LWPT_MOA_PAYLOAD"],
 | 
				
			||||||
 | 
					                () => "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit"
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            getModularWeaponSale(
 | 
				
			||||||
 | 
					                partTypeToParts,
 | 
				
			||||||
 | 
					                today,
 | 
				
			||||||
 | 
					                "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];
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { modularWeaponSaleController };
 | 
					const priceFactor: Record<string, number> = {
 | 
				
			||||||
 | 
					    Ostron: 0.9,
 | 
				
			||||||
 | 
					    SolarisUnitedHoverboard: 0.85,
 | 
				
			||||||
 | 
					    SolarisUnitedMoaPet: 0.95,
 | 
				
			||||||
 | 
					    SolarisUnitedKitGun: 0.9
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -69,3 +69,26 @@ export const getRandomWeightedRewardUc = <T extends { Rarity: TRarity }>(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return getRandomReward(resultPool);
 | 
					    return getRandomReward(resultPool);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class CRng {
 | 
				
			||||||
 | 
					    state: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(seed: number = 1) {
 | 
				
			||||||
 | 
					        this.state = seed;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    random(): number {
 | 
				
			||||||
 | 
					        this.state = (this.state * 1103515245 + 12345) & 0x7fffffff;
 | 
				
			||||||
 | 
					        return (this.state & 0x3fffffff) / 0x3fffffff;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    randomInt(min: number, max: number): number {
 | 
				
			||||||
 | 
					        min = Math.ceil(min);
 | 
				
			||||||
 | 
					        max = Math.floor(max);
 | 
				
			||||||
 | 
					        return Math.floor(this.random() * (max - min + 1)) + min;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    randomElement<T>(arr: T[]): T {
 | 
				
			||||||
 | 
					        return arr[Math.floor(this.random() * arr.length)];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,86 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "SaleInfos": [
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "Name": "Ostron",
 | 
					 | 
				
			||||||
      "Expiry": {
 | 
					 | 
				
			||||||
        "$date": {
 | 
					 | 
				
			||||||
          "$numberLong": "9999999900000"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "Revision": 3453,
 | 
					 | 
				
			||||||
      "Weapons": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "ItemType": "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon",
 | 
					 | 
				
			||||||
          "PremiumPrice": 162,
 | 
					 | 
				
			||||||
          "ModularParts": [
 | 
					 | 
				
			||||||
            "/Lotus/Weapons/Ostron/Melee/ModularMelee01/Handle/HandleFive",
 | 
					 | 
				
			||||||
            "/Lotus/Weapons/Ostron/Melee/ModularMelee01/Tip/TipFour",
 | 
					 | 
				
			||||||
            "/Lotus/Weapons/Ostron/Melee/ModularMelee01/Balance/BalanceSpeedICritII"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "Name": "SolarisUnitedHoverboard",
 | 
					 | 
				
			||||||
      "Expiry": {
 | 
					 | 
				
			||||||
        "$date": {
 | 
					 | 
				
			||||||
          "$numberLong": "9999999900000"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "Revision": 2058,
 | 
					 | 
				
			||||||
      "Weapons": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "ItemType": "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit",
 | 
					 | 
				
			||||||
          "PremiumPrice": 51,
 | 
					 | 
				
			||||||
          "ModularParts": [
 | 
					 | 
				
			||||||
            "/Lotus/Types/Vehicles/Hoverboard/HoverboardParts/PartComponents/HoverboardSolarisA/HoverboardSolarisADeck",
 | 
					 | 
				
			||||||
            "/Lotus/Types/Vehicles/Hoverboard/HoverboardParts/PartComponents/HoverboardCorpusA/HoverboardCorpusAEngine",
 | 
					 | 
				
			||||||
            "/Lotus/Types/Vehicles/Hoverboard/HoverboardParts/PartComponents/HoverboardSolarisA/HoverboardSolarisAFront",
 | 
					 | 
				
			||||||
            "/Lotus/Types/Vehicles/Hoverboard/HoverboardParts/PartComponents/HoverboardCorpusB/HoverboardCorpusBJet"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "Name": "SolarisUnitedMoaPet",
 | 
					 | 
				
			||||||
      "Expiry": {
 | 
					 | 
				
			||||||
        "$date": {
 | 
					 | 
				
			||||||
          "$numberLong": "9999999900000"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "Revision": 2058,
 | 
					 | 
				
			||||||
      "Weapons": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "ItemType": "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit",
 | 
					 | 
				
			||||||
          "PremiumPrice": 180,
 | 
					 | 
				
			||||||
          "ModularParts": [
 | 
					 | 
				
			||||||
            "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetParts/MoaPetLegB",
 | 
					 | 
				
			||||||
            "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetParts/MoaPetHeadPara",
 | 
					 | 
				
			||||||
            "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetParts/MoaPetEngineArcotek",
 | 
					 | 
				
			||||||
            "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetParts/MoaPetPayloadMunitron"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "Name": "SolarisUnitedKitGun",
 | 
					 | 
				
			||||||
      "Expiry": {
 | 
					 | 
				
			||||||
        "$date": {
 | 
					 | 
				
			||||||
          "$numberLong": "9999999900000"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "Revision": 2058,
 | 
					 | 
				
			||||||
      "Weapons": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "ItemType": "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary",
 | 
					 | 
				
			||||||
          "PremiumPrice": 184,
 | 
					 | 
				
			||||||
          "ModularParts": [
 | 
					 | 
				
			||||||
            "/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Handle/SUModularSecondaryHandleCPart",
 | 
					 | 
				
			||||||
            "/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelBPart",
 | 
					 | 
				
			||||||
            "/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Clip/SUModularStatIReloadIIClipPart"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  ]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user