feat: handle acquisition of booster packs #452
							
								
								
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -12,7 +12,7 @@
 | 
				
			|||||||
        "copyfiles": "^2.4.1",
 | 
					        "copyfiles": "^2.4.1",
 | 
				
			||||||
        "express": "^5.0.0-beta.3",
 | 
					        "express": "^5.0.0-beta.3",
 | 
				
			||||||
        "mongoose": "^8.1.1",
 | 
					        "mongoose": "^8.1.1",
 | 
				
			||||||
        "warframe-public-export-plus": "^0.4.0",
 | 
					        "warframe-public-export-plus": "^0.4.1",
 | 
				
			||||||
        "warframe-riven-info": "^0.1.0",
 | 
					        "warframe-riven-info": "^0.1.0",
 | 
				
			||||||
        "winston": "^3.11.0",
 | 
					        "winston": "^3.11.0",
 | 
				
			||||||
        "winston-daily-rotate-file": "^4.7.1"
 | 
					        "winston-daily-rotate-file": "^4.7.1"
 | 
				
			||||||
@ -3669,9 +3669,9 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/warframe-public-export-plus": {
 | 
					    "node_modules/warframe-public-export-plus": {
 | 
				
			||||||
      "version": "0.4.0",
 | 
					      "version": "0.4.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.4.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.4.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-8wOkh9dET4IHmHDSZ8g8RW0GlfEevHnBwEETAqy3jRhwssyF0TgQsOOpJVuhcPKedCYeudR92HJ3JoXoiTCr6A=="
 | 
					      "integrity": "sha512-5SwnT/K/rMI0zJpdodzeEPlO/UnMlHiKv8NZGH647/5u52LZf8xfOpJHP4/yr/anjVVzDQJwY5K3CmbX0uMQdw=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/warframe-riven-info": {
 | 
					    "node_modules/warframe-riven-info": {
 | 
				
			||||||
      "version": "0.1.0",
 | 
					      "version": "0.1.0",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,7 @@
 | 
				
			|||||||
    "copyfiles": "^2.4.1",
 | 
					    "copyfiles": "^2.4.1",
 | 
				
			||||||
    "express": "^5.0.0-beta.3",
 | 
					    "express": "^5.0.0-beta.3",
 | 
				
			||||||
    "mongoose": "^8.1.1",
 | 
					    "mongoose": "^8.1.1",
 | 
				
			||||||
    "warframe-public-export-plus": "^0.4.0",
 | 
					    "warframe-public-export-plus": "^0.4.1",
 | 
				
			||||||
    "warframe-riven-info": "^0.1.0",
 | 
					    "warframe-riven-info": "^0.1.0",
 | 
				
			||||||
    "winston": "^3.11.0",
 | 
					    "winston": "^3.11.0",
 | 
				
			||||||
    "winston-daily-rotate-file": "^4.7.1"
 | 
					    "winston-daily-rotate-file": "^4.7.1"
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@ 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 "@/src/services/configService";
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
import { Types } from "mongoose";
 | 
					import { Types } from "mongoose";
 | 
				
			||||||
import { SlotNames, IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
					import { SlotNames, IInventoryChanges, IBinChanges } from "@/src/types/purchaseTypes";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    IChallengeProgress,
 | 
					    IChallengeProgress,
 | 
				
			||||||
    IConsumable,
 | 
					    IConsumable,
 | 
				
			||||||
@ -27,9 +27,16 @@ import {
 | 
				
			|||||||
} from "../types/requestTypes";
 | 
					} from "../types/requestTypes";
 | 
				
			||||||
import { logger } from "@/src/utils/logger";
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
import { getWeaponType, getExalted } from "@/src/services/itemDataService";
 | 
					import { getWeaponType, getExalted } from "@/src/services/itemDataService";
 | 
				
			||||||
 | 
					import { getRandomWeightedReward } from "@/src/services/rngService";
 | 
				
			||||||
import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndicateTypes";
 | 
					import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndicateTypes";
 | 
				
			||||||
import { IEquipmentClient } from "../types/inventoryTypes/commonInventoryTypes";
 | 
					import { IEquipmentClient } from "../types/inventoryTypes/commonInventoryTypes";
 | 
				
			||||||
import { ExportCustoms, ExportFlavour, ExportRecipes, ExportResources } from "warframe-public-export-plus";
 | 
					import {
 | 
				
			||||||
 | 
					    ExportBoosterPacks,
 | 
				
			||||||
 | 
					    ExportCustoms,
 | 
				
			||||||
 | 
					    ExportFlavour,
 | 
				
			||||||
 | 
					    ExportRecipes,
 | 
				
			||||||
 | 
					    ExportResources
 | 
				
			||||||
 | 
					} from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const createInventory = async (
 | 
					export const createInventory = async (
 | 
				
			||||||
    accountOwnerId: Types.ObjectId,
 | 
					    accountOwnerId: Types.ObjectId,
 | 
				
			||||||
@ -59,6 +66,31 @@ export const createInventory = async (
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, delta: IInventoryChanges): void => {
 | 
				
			||||||
 | 
					    for (const key in delta) {
 | 
				
			||||||
 | 
					        if (!(key in InventoryChanges)) {
 | 
				
			||||||
 | 
					            InventoryChanges[key] = delta[key];
 | 
				
			||||||
 | 
					        } else if (Array.isArray(delta[key])) {
 | 
				
			||||||
 | 
					            const left = InventoryChanges[key] as object[];
 | 
				
			||||||
 | 
					            const right = delta[key] as object[];
 | 
				
			||||||
 | 
					            for (const item of right) {
 | 
				
			||||||
 | 
					                left.push(item);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            console.assert(key.substring(-3) == "Bin");
 | 
				
			||||||
 | 
					            const left = InventoryChanges[key] as IBinChanges;
 | 
				
			||||||
 | 
					            const right = delta[key] as IBinChanges;
 | 
				
			||||||
 | 
					            left.count += right.count;
 | 
				
			||||||
 | 
					            left.platinum += right.platinum;
 | 
				
			||||||
 | 
					            left.Slots += right.Slots;
 | 
				
			||||||
 | 
					            if (right.Extra) {
 | 
				
			||||||
 | 
					                left.Extra ??= 0;
 | 
				
			||||||
 | 
					                left.Extra += right.Extra;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -121,6 +153,21 @@ export const addItem = async (
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (typeName in ExportBoosterPacks) {
 | 
				
			||||||
 | 
					        const pack = ExportBoosterPacks[typeName];
 | 
				
			||||||
 | 
					        const InventoryChanges = {};
 | 
				
			||||||
 | 
					        for (const weights of pack.rarityWeightsPerRoll) {
 | 
				
			||||||
 | 
					            const result = getRandomWeightedReward(pack.components, weights);
 | 
				
			||||||
 | 
					            if (result) {
 | 
				
			||||||
 | 
					                logger.debug(`booster pack rolled`, result);
 | 
				
			||||||
 | 
					                combineInventoryChanges(
 | 
				
			||||||
 | 
					                    InventoryChanges,
 | 
				
			||||||
 | 
					                    (await addItem(accountId, result.type, result.itemCount)).InventoryChanges
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return { InventoryChanges };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Path-based duck typing
 | 
					    // Path-based duck typing
 | 
				
			||||||
    switch (typeName.substr(1).split("/")[1]) {
 | 
					    switch (typeName.substr(1).split("/")[1]) {
 | 
				
			||||||
@ -261,6 +308,25 @@ export const addItem = async (
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                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
 | 
					                case "Restoratives": // Codex Scanner, Remote Observer, Starburst
 | 
				
			||||||
                    const inventory = await getInventory(accountId);
 | 
					                    const inventory = await getInventory(accountId);
 | 
				
			||||||
                    const consumablesChanges = [
 | 
					                    const consumablesChanges = [
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,7 @@ import {
 | 
				
			|||||||
} from "warframe-public-export-plus";
 | 
					} from "warframe-public-export-plus";
 | 
				
			||||||
import { IMissionInventoryUpdateRequest } from "../types/requestTypes";
 | 
					import { IMissionInventoryUpdateRequest } from "../types/requestTypes";
 | 
				
			||||||
import { logger } from "@/src/utils/logger";
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
 | 
					import { IRngResult, getRandomReward } from "@/src/services/rngService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// need reverse engineer rewardSeed, otherwise ingame displayed rotation reward will be different than added to db or displayed on mission end
 | 
					// need reverse engineer rewardSeed, otherwise ingame displayed rotation reward will be different than added to db or displayed on mission end
 | 
				
			||||||
const getRewards = ({
 | 
					const getRewards = ({
 | 
				
			||||||
@ -23,7 +24,7 @@ const getRewards = ({
 | 
				
			|||||||
        return { InventoryChanges: {}, MissionRewards: [] };
 | 
					        return { InventoryChanges: {}, MissionRewards: [] };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const drops: IReward[] = [];
 | 
					    const drops: IRngResult[] = [];
 | 
				
			||||||
    if (RewardInfo.node in ExportRegions) {
 | 
					    if (RewardInfo.node in ExportRegions) {
 | 
				
			||||||
        const region = ExportRegions[RewardInfo.node];
 | 
					        const region = ExportRegions[RewardInfo.node];
 | 
				
			||||||
        const rewardManifests = region.rewardManifests ?? [];
 | 
					        const rewardManifests = region.rewardManifests ?? [];
 | 
				
			||||||
@ -117,21 +118,8 @@ const getRotations = (rotationCount: number): number[] => {
 | 
				
			|||||||
    return rotatedValues;
 | 
					    return rotatedValues;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getRandomRewardByChance = (data: IReward[]): IReward | undefined => {
 | 
					const getRandomRewardByChance = (pool: IReward[]): IRngResult | undefined => {
 | 
				
			||||||
    if (data.length == 0) return;
 | 
					    return getRandomReward(pool as IRngResult[]);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    const totalChance = data.reduce((sum, item) => sum + item.probability!, 0);
 | 
					 | 
				
			||||||
    const randomValue = Math.random() * totalChance;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let cumulativeChance = 0;
 | 
					 | 
				
			||||||
    for (const item of data) {
 | 
					 | 
				
			||||||
        cumulativeChance += item.probability!;
 | 
					 | 
				
			||||||
        if (randomValue <= cumulativeChance) {
 | 
					 | 
				
			||||||
            return item;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const creditBundles: Record<string, number> = {
 | 
					const creditBundles: Record<string, number> = {
 | 
				
			||||||
@ -157,7 +145,7 @@ const fusionBundles: Record<string, number> = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const formatRewardsToInventoryType = (
 | 
					const formatRewardsToInventoryType = (
 | 
				
			||||||
    rewards: IReward[]
 | 
					    rewards: IRngResult[]
 | 
				
			||||||
): { InventoryChanges: IMissionInventoryUpdateRequest; MissionRewards: IMissionRewardResponse[] } => {
 | 
					): { InventoryChanges: IMissionInventoryUpdateRequest; MissionRewards: IMissionRewardResponse[] } => {
 | 
				
			||||||
    const InventoryChanges: IMissionInventoryUpdateRequest = {};
 | 
					    const InventoryChanges: IMissionInventoryUpdateRequest = {};
 | 
				
			||||||
    const MissionRewards: IMissionRewardResponse[] = [];
 | 
					    const MissionRewards: IMissionRewardResponse[] = [];
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,13 @@
 | 
				
			|||||||
import { parseSlotPurchaseName } from "@/src/helpers/purchaseHelpers";
 | 
					import { parseSlotPurchaseName } from "@/src/helpers/purchaseHelpers";
 | 
				
			||||||
import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
 | 
					import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
 | 
				
			||||||
import { addItem, addBooster, updateCurrency, updateSlots } from "@/src/services/inventoryService";
 | 
					import {
 | 
				
			||||||
import { IPurchaseRequest, SlotPurchase, IInventoryChanges, IBinChanges } from "@/src/types/purchaseTypes";
 | 
					    addItem,
 | 
				
			||||||
 | 
					    addBooster,
 | 
				
			||||||
 | 
					    combineInventoryChanges,
 | 
				
			||||||
 | 
					    updateCurrency,
 | 
				
			||||||
 | 
					    updateSlots
 | 
				
			||||||
 | 
					} from "@/src/services/inventoryService";
 | 
				
			||||||
 | 
					import { IPurchaseRequest, SlotPurchase, IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
				
			||||||
import { logger } from "@/src/utils/logger";
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
import { ExportBundles, ExportGear, TRarity } from "warframe-public-export-plus";
 | 
					import { ExportBundles, ExportGear, TRarity } from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -46,31 +52,6 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI
 | 
				
			|||||||
    return purchaseResponse;
 | 
					    return purchaseResponse;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const addInventoryChanges = (InventoryChanges: IInventoryChanges, delta: IInventoryChanges): void => {
 | 
					 | 
				
			||||||
    for (const key in delta) {
 | 
					 | 
				
			||||||
        if (!(key in InventoryChanges)) {
 | 
					 | 
				
			||||||
            InventoryChanges[key] = delta[key];
 | 
					 | 
				
			||||||
        } else if (Array.isArray(delta[key])) {
 | 
					 | 
				
			||||||
            const left = InventoryChanges[key] as object[];
 | 
					 | 
				
			||||||
            const right = delta[key] as object[];
 | 
					 | 
				
			||||||
            for (const item of right) {
 | 
					 | 
				
			||||||
                left.push(item);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            console.assert(key.substring(-3) == "Bin");
 | 
					 | 
				
			||||||
            const left = InventoryChanges[key] as IBinChanges;
 | 
					 | 
				
			||||||
            const right = delta[key] as IBinChanges;
 | 
					 | 
				
			||||||
            left.count += right.count;
 | 
					 | 
				
			||||||
            left.platinum += right.platinum;
 | 
					 | 
				
			||||||
            left.Slots += right.Slots;
 | 
					 | 
				
			||||||
            if (right.Extra) {
 | 
					 | 
				
			||||||
                left.Extra ??= 0;
 | 
					 | 
				
			||||||
                left.Extra += right.Extra;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const handleStoreItemAcquisition = async (
 | 
					const handleStoreItemAcquisition = async (
 | 
				
			||||||
    storeItemName: string,
 | 
					    storeItemName: string,
 | 
				
			||||||
    accountId: string,
 | 
					    accountId: string,
 | 
				
			||||||
@ -86,7 +67,7 @@ const handleStoreItemAcquisition = async (
 | 
				
			|||||||
        const bundle = ExportBundles[storeItemName];
 | 
					        const bundle = ExportBundles[storeItemName];
 | 
				
			||||||
        logger.debug("acquiring bundle", bundle);
 | 
					        logger.debug("acquiring bundle", bundle);
 | 
				
			||||||
        for (const component of bundle.components) {
 | 
					        for (const component of bundle.components) {
 | 
				
			||||||
            addInventoryChanges(
 | 
					            combineInventoryChanges(
 | 
				
			||||||
                purchaseResponse.InventoryChanges,
 | 
					                purchaseResponse.InventoryChanges,
 | 
				
			||||||
                (
 | 
					                (
 | 
				
			||||||
                    await handleStoreItemAcquisition(
 | 
					                    await handleStoreItemAcquisition(
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										42
									
								
								src/services/rngService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/services/rngService.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import { TRarity } from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IRngResult {
 | 
				
			||||||
 | 
					    type: string;
 | 
				
			||||||
 | 
					    itemCount: number;
 | 
				
			||||||
 | 
					    probability: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getRandomReward = (pool: IRngResult[]): IRngResult | undefined => {
 | 
				
			||||||
 | 
					    if (pool.length == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const totalChance = pool.reduce((accum, item) => accum + item.probability, 0);
 | 
				
			||||||
 | 
					    const randomValue = Math.random() * totalChance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let cumulativeChance = 0;
 | 
				
			||||||
 | 
					    for (const item of pool) {
 | 
				
			||||||
 | 
					        cumulativeChance += item.probability;
 | 
				
			||||||
 | 
					        if (randomValue <= cumulativeChance) {
 | 
				
			||||||
 | 
					            return item;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    throw new Error("What the fuck?");
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getRandomWeightedReward = (
 | 
				
			||||||
 | 
					    pool: { Item: string; Rarity: TRarity }[],
 | 
				
			||||||
 | 
					    weights: Record<TRarity, number>
 | 
				
			||||||
 | 
					): IRngResult | undefined => {
 | 
				
			||||||
 | 
					    const resultPool: IRngResult[] = [];
 | 
				
			||||||
 | 
					    const rarityCounts: Record<TRarity, number> = { COMMON: 0, UNCOMMON: 0, RARE: 0, LEGENDARY: 0 };
 | 
				
			||||||
 | 
					    for (const entry of pool) {
 | 
				
			||||||
 | 
					        ++rarityCounts[entry.Rarity];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (const entry of pool) {
 | 
				
			||||||
 | 
					        resultPool.push({
 | 
				
			||||||
 | 
					            type: entry.Item,
 | 
				
			||||||
 | 
					            itemCount: 1,
 | 
				
			||||||
 | 
					            probability: weights[entry.Rarity] / rarityCounts[entry.Rarity]
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return getRandomReward(resultPool);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user