diff --git a/project/assets/configs/inventory.json b/project/assets/configs/inventory.json index 035ffa3f..fa0bda16 100644 --- a/project/assets/configs/inventory.json +++ b/project/assets/configs/inventory.json @@ -19,5 +19,141 @@ } } }, + "sealedAirdropContainer": { + "weaponRewardWeight": { + "5447a9cd4bdc2dbd208b4567": 1, + "5bb2475ed4351e00853264e3": 1, + "5bd70322209c4d00d7167b8f": 1, + "5ac66d2e5acfc43b321d4b53": 1, + "5ac66d725acfc43b321d4b60": 1, + "5ac66d9b5acfc4001633997a": 1, + "62e7c4fba689e8c9c50dfc38": 1, + "63171672192e68c5460cebc5": 1, + "5c488a752e221602b412af63": 1, + "5dcbd56fdbd3d91b3e5468d5": 1, + "623063e994fc3f7b302a9696": 1, + "5fbcc1d9016cce60e8341ab3": 1, + "606587252535c57a13424cfd": 1, + "5b0bbe4e5acfc40dc528a72d": 1, + "6184055050224f204c1da540": 1, + "6183afd850224f204c1da514": 1, + "5beed0f50db834001c062b12": 1, + "5cc82d76e24e8d00134b4b83": 1, + "5fc3e272f8b6a877a729eac5": 1, + "5fb6548dd1409e5ca04b54f9": 1, + "5aafa857e5b5b00018480968": 1, + "5bfea6e90db834001b7347f3": 1, + "5cadfbf7ae92152ac412eeef": 1, + "628a60ae6b1d481ff772e9c8": 1, + "628b5638ad252a16da6dd245": 1, + "5d43021ca4b9362eab4b5e25": 1, + "58948c8e86f77409493f7266": 1, + "62e14904c2699c0ec93adc47": 1, + "5c46fbd72e2216398b5a8c9c": 1, + "5df8ce05b11454561e39243b": 1, + "5df24cf80dee1b22f862e9bc": 1 + }, + "defaultPresetsOnly": true, + "weaponModRewardLimits": { + "5448bc234bdc2d3c308b4569": { + "type": "magazine", + "min": 2, + "max": 4 + }, + "55818b164bdc2ddc698b456c": { + "type": "laserLight", + "min": 1, + "max": 2 + }, + "55818ad54bdc2ddc698b4569": { + "type": "collimator", + "min": 0, + "max": 2 + }, + "55818acf4bdc2dde698b456b": { + "type": "compactCollimator", + "min": 0, + "max": 2 + }, + "55818b224bdc2dde698b456f": { + "type": "mount", + "min": 0, + "max": 2 + }, + "555ef6e44bdc2de9068b457e": { + "type": "barrel", + "min": 1, + "max": 1 + }, + "55818add4bdc2d5b648b456f": { + "type": "assaultScope", + "min": 0, + "max": 1 + }, + "55818ae44bdc2dde698b456c": { + "type": "opticScope", + "min": 0, + "max": 1 + }, + "55818af64bdc2d5b648b4570": { + "type": "foregrip", + "min": 0, + "max": 1 + }, + "550aa4cd4bdc2dd8348b456c": { + "type": "silencer", + "min": 0, + "max": 1 + }, + "55818b084bdc2d5b648b4571": { + "type": "flashlight", + "min": 0, + "max": 1 + }, + "55818a104bdc2db9688b4569": { + "type": "handguard", + "min": 0, + "max": 2 + } + }, + "rewardTypeLimits": { + "5448e8d04bdc2ddf718b4569": { + "type": "food", + "min": 2, + "max": 8 + }, + "5448f3a64bdc2d60728b456a": { + "type": "stim", + "min": 2, + "max": 5 + }, + "543be5cb4bdc2deb348b4568": { + "type": "ammobox", + "min": 2, + "max": 5 + }, + "5448f3ac4bdc2dce718b4569": { + "type": "medical", + "min": 2, + "max": 7 + } + }, + "ammoBoxWhitelist": [ + "648983d6b5a2df1c815a04ec", + "6489848173c462723909a14b", + "648984b8d5b4df6140000a1a", + "648984e3f09d032aa9399d53", + "6489851fc827d4637f01791b", + "6489854673c462723909a14e", + "648985c074a806211e4fb682", + "6489875745f9ca4ba51c4808", + "648987d673c462723909a151", + "648986bbc827d4637f01791e", + "64898583d5b4df6140000a1d", + "64898602f09d032aa9399d56", + "6489870774a806211e4fb685", + "6489879db5a2df1c815a04ef" + ] + } "customMoneyTpls": [] } diff --git a/project/src/controllers/InventoryController.ts b/project/src/controllers/InventoryController.ts index 827c4fac..e34734fe 100644 --- a/project/src/controllers/InventoryController.ts +++ b/project/src/controllers/InventoryController.ts @@ -1,10 +1,11 @@ import { inject, injectable } from "tsyringe"; +import { LootGenerator } from "../generators/LootGenerator"; import { InventoryHelper } from "../helpers/InventoryHelper"; +import { ItemHelper } from "../helpers/ItemHelper"; import { PaymentHelper } from "../helpers/PaymentHelper"; import { PresetHelper } from "../helpers/PresetHelper"; import { ProfileHelper } from "../helpers/ProfileHelper"; -import { WeightedRandomHelper } from "../helpers/WeightedRandomHelper"; import { IPmcData } from "../models/eft/common/IPmcData"; import { Item } from "../models/eft/common/tables/IItem"; import { IAddItemRequestData } from "../models/eft/inventory/IAddItemRequestData"; @@ -38,7 +39,9 @@ import { IOpenRandomLootContainerRequestData } from "../models/eft/inventory/IOpenRandomLootContainerRequestData"; import { IItemEventRouterResponse } from "../models/eft/itemEvent/IItemEventRouterResponse"; +import { BackendErrorCodes } from "../models/enums/BackendErrorCodes"; import { Traders } from "../models/enums/Traders"; +import { RewardDetails } from "../models/spt/config/IInventoryConfig"; import { ILogger } from "../models/spt/utils/ILogger"; import { EventOutputHolder } from "../routers/EventOutputHolder"; import { DatabaseServer } from "../servers/DatabaseServer"; @@ -57,6 +60,7 @@ export class InventoryController @inject("WinstonLogger") protected logger: ILogger, @inject("HashUtil") protected hashUtil: HashUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil, + @inject("ItemHelper") protected itemHelper: ItemHelper, @inject("RandomUtil") protected randomUtil: RandomUtil, @inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("FenceService") protected fenceService: FenceService, @@ -64,9 +68,9 @@ export class InventoryController @inject("InventoryHelper") protected inventoryHelper: InventoryHelper, @inject("RagfairOfferService") protected ragfairOfferService: RagfairOfferService, @inject("ProfileHelper") protected profileHelper: ProfileHelper, - @inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper, @inject("PaymentHelper") protected paymentHelper: PaymentHelper, @inject("LocalisationService") protected localisationService: LocalisationService, + @inject("LootGenerator") protected lootGenerator: LootGenerator, @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder, @inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil ) @@ -97,7 +101,7 @@ export class InventoryController // Dont move items from trader to profile, this can happen when editing a traders preset weapons if (moveRequest.fromOwner?.type === "Trader" && !items.isMail) { - return this.httpResponseUtil.appendErrorToOutput(output, this.localisationService.getText("inventory-edit_trader_item"), 228); + return this.httpResponseUtil.appendErrorToOutput(output, this.localisationService.getText("inventory-edit_trader_item"), 228); } this.inventoryHelper.moveItemInternal(pmcData, items.from, moveRequest); @@ -760,29 +764,28 @@ export class InventoryController public openRandomLootContainer(pmcData: IPmcData, body: IOpenRandomLootContainerRequestData, sessionID: string): IItemEventRouterResponse { const openedItem = pmcData.Inventory.items.find(x => x._id === body.item); - const rewardContainerDetails = this.inventoryHelper.getRandomLootContainerRewardDetails(openedItem._tpl); + const containerDetails = this.itemHelper.getItem(openedItem._tpl); + const isSealedWeaponBox = containerDetails[1]._name.includes("event_container_airdrop"); const newItemRequest: IAddItemRequestData = { tid: "RandomLootContainer", items: [] }; - // Get random items and add to newItemRequest - for (let index = 0; index < rewardContainerDetails.rewardCount; index++) + let rewardContainerDetails: RewardDetails = { + rewardCount: 0, + foundInRaid: true + }; + + if (isSealedWeaponBox) { - // Pick random reward from pool, add to request object - const chosenRewardItemTpl = this.weightedRandomHelper.getWeightedInventoryItem(rewardContainerDetails.rewardTplPool); - const existingItemInRequest = newItemRequest.items.find(x => x.item_id === chosenRewardItemTpl); - if (existingItemInRequest) - { - // Exists in request already, increment count - existingItemInRequest.count++; - } - else - { - // eslint-disable-next-line @typescript-eslint/naming-convention - newItemRequest.items.push({item_id: chosenRewardItemTpl, count: 1}); - } + newItemRequest.items.push(...this.lootGenerator.getSealedWeaponCaseLoot()); + } + else + { + // Get summary of loot from config + rewardContainerDetails = this.inventoryHelper.getRandomLootContainerRewardDetails(openedItem._tpl); + newItemRequest.items.push(...this.lootGenerator.getRandomLootContainerLoot(rewardContainerDetails)); } const output = this.eventOutputHolder.getOutput(sessionID); diff --git a/project/src/generators/LootGenerator.ts b/project/src/generators/LootGenerator.ts index 8fe8f4a5..a89e3e03 100644 --- a/project/src/generators/LootGenerator.ts +++ b/project/src/generators/LootGenerator.ts @@ -1,15 +1,21 @@ import { inject, injectable } from "tsyringe"; +import { InventoryHelper } from "../helpers/InventoryHelper"; import { ItemHelper } from "../helpers/ItemHelper"; +import { PresetHelper } from "../helpers/PresetHelper"; +import { WeightedRandomHelper } from "../helpers/WeightedRandomHelper"; import { Preset } from "../models/eft/common/IGlobals"; import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem"; +import { AddItem } from "../models/eft/inventory/IAddItemRequestData"; import { BaseClasses } from "../models/enums/BaseClasses"; +import { ISealedAirdropContainerSettings, RewardDetails } from "../models/spt/config/IInventoryConfig"; import { LootItem } from "../models/spt/services/LootItem"; import { LootRequest } from "../models/spt/services/LootRequest"; import { ILogger } from "../models/spt/utils/ILogger"; import { DatabaseServer } from "../servers/DatabaseServer"; import { ItemFilterService } from "../services/ItemFilterService"; import { LocalisationService } from "../services/LocalisationService"; +import { RagfairLinkedItemService } from "../services/RagfairLinkedItemService"; import { HashUtil } from "../utils/HashUtil"; import { RandomUtil } from "../utils/RandomUtil"; @@ -27,7 +33,11 @@ export class LootGenerator @inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("RandomUtil") protected randomUtil: RandomUtil, @inject("ItemHelper") protected itemHelper: ItemHelper, + @inject("PresetHelper") protected presetHelper: PresetHelper, + @inject("InventoryHelper") protected inventoryHelper: InventoryHelper, + @inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper, @inject("LocalisationService") protected localisationService: LocalisationService, + @inject("RagfairLinkedItemService") protected ragfairLinkedItemService: RagfairLinkedItemService, @inject("ItemFilterService") protected itemFilterService: ItemFilterService ) {} @@ -228,4 +238,204 @@ export class LootGenerator // item added okay return true; } + + /** + * Sealed weapon containers have a weapon + associated mods inside them + assortment of other things (food/meds) + * @returns Array of items to add to player inventory + */ + public getSealedWeaponCaseLoot(): AddItem[] + { + const itemsToReturn: AddItem[] = []; + const containerSettings = this.inventoryHelper.getInventoryConfig().sealedAirdropContainer; + + // choose a weapon to give to the player (weighted) + const chosenWeaponTpl = this.weightedRandomHelper.getWeightedInventoryItem(containerSettings.weaponRewardWeight); + const weaponDetailsDb = this.itemHelper.getItem(chosenWeaponTpl); + if (!weaponDetailsDb[0]) + { + this.logger.warning(`Non-item was picked as reward ${chosenWeaponTpl}, unable to continue`); + + return itemsToReturn; + } + + // Get weapon preset - default or choose a random one from all possible + const chosenWeaponPreset = containerSettings.defaultPresetsOnly + ? this.presetHelper.getDefaultPreset(chosenWeaponTpl) + : this.randomUtil.getArrayValue(this.presetHelper.getPresets(chosenWeaponTpl)); + + // Add preset to return object + itemsToReturn.push({ + count: 1, + // eslint-disable-next-line @typescript-eslint/naming-convention + item_id: chosenWeaponPreset._id, + isPreset: true + }); + + // Get items related to chosen weapon + const linkedItemsToWeapon = this.ragfairLinkedItemService.getLinkedDbItems(chosenWeaponTpl); + itemsToReturn.push(...this.getSealedContainerWeaponModRewards(containerSettings, linkedItemsToWeapon, chosenWeaponPreset)); + + // Handle non-weapon mod reward types + itemsToReturn.push(...this.getSealedContainerNonWeaponModRewards(containerSettings, weaponDetailsDb[1])); + + return itemsToReturn; + } + + /** + * Get non-weapon mod rewards for a sealed container + * @param containerSettings Sealed weapon container settings + * @param weaponDetailsDb Details for the weapon to reward player + * @returns AddItem array + */ + protected getSealedContainerNonWeaponModRewards(containerSettings: ISealedAirdropContainerSettings, weaponDetailsDb: ITemplateItem): AddItem[] + { + const rewards: AddItem[] = []; + + for (const rewardTypeId in containerSettings.rewardTypeLimits) + { + const settings = containerSettings.rewardTypeLimits[rewardTypeId]; + const rewardCount = this.randomUtil.getInt(settings.min, settings.max); + + if (rewardCount === 0) + { + continue; + } + + // Edge case - ammo boxes + if (rewardTypeId === BaseClasses.AMMO_BOX) + { + // Get ammoboxes from db + const ammoBoxesDetails = containerSettings.ammoBoxWhitelist.map(x => + { + const itemDetails = this.itemHelper.getItem(x); + return itemDetails[1]; + }); + + // Need to find boxes that matches weapons caliber + const weaponCaliber = weaponDetailsDb._props.ammoCaliber; + const ammoBoxesMatchingCaliber = ammoBoxesDetails.filter(x => x._props.ammoCaliber === weaponCaliber); + if (ammoBoxesMatchingCaliber.length === 0) + { + this.logger.debug(`No ammo box with caliber ${weaponCaliber} found, skipping`); + + continue; + } + + // No need to add ammo to box, inventoryHelper.addItem() will handle it + const chosenAmmoBox = this.randomUtil.getArrayValue(ammoBoxesMatchingCaliber); + rewards.push({ + count: rewardCount, + // eslint-disable-next-line @typescript-eslint/naming-convention + item_id: chosenAmmoBox._id, + isPreset: false + }); + + continue; + } + + // Get all items of the desired type + not quest items + not globally blacklisted + const possibleRewardItems = Object.values(this.databaseServer.getTables().templates.items) + .filter(x => x._parent === rewardTypeId + && x._type.toLowerCase() === "item" + && !this.itemFilterService.isItemBlacklisted(x._id) + && !x._props.QuestItem); + + if (possibleRewardItems.length === 0) + { + this.logger.debug(`No items with base type of ${rewardTypeId} found, skipping`); + + continue; + } + + for (let index = 0; index < rewardCount; index++) + { + // choose a random item from pool + const chosenRewardItem = this.randomUtil.getArrayValue(possibleRewardItems); + this.addOrIncrementItemToArray(chosenRewardItem._id, rewards); + } + } + + return rewards; + } + + /** + * Iterate over the container weaponModRewardLimits settings and create an array of weapon mods to reward player + * @param containerSettings Sealed weapon container settings + * @param linkedItemsToWeapon All items that can be attached/inserted into weapon + * @param chosenWeaponPreset The weapon preset given to player as reward + * @returns AddItem array + */ + protected getSealedContainerWeaponModRewards(containerSettings: ISealedAirdropContainerSettings, linkedItemsToWeapon: ITemplateItem[], chosenWeaponPreset: Preset): AddItem[] + { + const modRewards: AddItem[] = []; + for (const rewardTypeId in containerSettings.weaponModRewardLimits) + { + const settings = containerSettings.weaponModRewardLimits[rewardTypeId]; + const rewardCount = this.randomUtil.getInt(settings.min, settings.max); + + // Nothing to add, skip reward type + if (rewardCount === 0) + { + continue; + } + + // Get items that fulfil reward type criteral from items that fit on gun + const relatedItems = linkedItemsToWeapon.filter(x => x._parent === rewardTypeId); + if (!relatedItems || relatedItems.length === 0) + { + this.logger.debug(`no items found to fulfil reward type ${rewardTypeId} for weapon: ${chosenWeaponPreset._name}, skipping`); + continue; + } + + // Find a random item of the desired type and add as reward + for (let index = 0; index < rewardCount; index++) + { + const chosenItem = this.randomUtil.drawRandomFromList(relatedItems); + this.addOrIncrementItemToArray(chosenItem[0]._id, modRewards); + } + } + + return modRewards; + } + + /** + * Handle event-related loot containers - currently just the halloween jack-o-lanterns that give food rewards + * @param rewardContainerDetails + * @returns AddItem array + */ + public getRandomLootContainerLoot(rewardContainerDetails: RewardDetails): AddItem[] + { + const itemsToReturn: AddItem[] = []; + + // Get random items and add to newItemRequest + for (let index = 0; index < rewardContainerDetails.rewardCount; index++) + { + // Pick random reward from pool, add to request object + const chosenRewardItemTpl = this.weightedRandomHelper.getWeightedInventoryItem(rewardContainerDetails.rewardTplPool); + this.addOrIncrementItemToArray(chosenRewardItemTpl, itemsToReturn); + } + + return itemsToReturn; + } + + /** + * A bug in inventoryHelper.addItem() means you cannot add the same item to the array twice with a count of 1, it causes duplication + * Default adds 1, or increments count + * @param itemTplToAdd items tpl we want to add to array + * @param resultsArray Array to add item tpl to + */ + protected addOrIncrementItemToArray(itemTplToAdd: string, resultsArray: AddItem[]): void + { + const existingItemIndex = resultsArray.findIndex(x => x.item_id === itemTplToAdd); + if (existingItemIndex > -1) + { + // Exists in array already, increment count + resultsArray[existingItemIndex].count++; + } + else + { + // eslint-disable-next-line @typescript-eslint/naming-convention + resultsArray.push({item_id: itemTplToAdd, count: 1, isPreset: false}); + } + } } \ No newline at end of file diff --git a/project/src/generators/RagfairOfferGenerator.ts b/project/src/generators/RagfairOfferGenerator.ts index c77585fe..27eaa4ab 100644 --- a/project/src/generators/RagfairOfferGenerator.ts +++ b/project/src/generators/RagfairOfferGenerator.ts @@ -467,7 +467,7 @@ export class RagfairOfferGenerator if ("Repairable" in item.upd) { // Randomise non-0 class armor - if (itemDetails._props.armorClass && itemDetails._props.armorClass >= 1) + if (itemDetails._props.armorClass && itemDetails._props.armorClass >= 1) { this.randomiseDurabilityValues(item, multiplier); } diff --git a/project/src/helpers/InventoryHelper.ts b/project/src/helpers/InventoryHelper.ts index ce3f5658..c46a0666 100644 --- a/project/src/helpers/InventoryHelper.ts +++ b/project/src/helpers/InventoryHelper.ts @@ -333,6 +333,7 @@ export class InventoryHelper ...itemLocation, upd: upd }); + this.logger.debug(`Added ${itemLib[tmpKey]._tpl} with id: ${idForItemToAdd} to inventory`); } toDo.push([itemLib[tmpKey]._id, idForItemToAdd]); @@ -958,6 +959,11 @@ export class InventoryHelper { return this.inventoryConfig.randomLootContainers[itemTpl]; } + + public getInventoryConfig(): IInventoryConfig + { + return this.inventoryConfig; + } } namespace InventoryHelper diff --git a/project/src/helpers/RagfairHelper.ts b/project/src/helpers/RagfairHelper.ts index f5f384dc..3e88b3ff 100644 --- a/project/src/helpers/RagfairHelper.ts +++ b/project/src/helpers/RagfairHelper.ts @@ -76,7 +76,7 @@ export class RagfairHelper const data = this.ragfairLinkedItemService.getLinkedItems(info.linkedSearchId); result = !data ? [] - : Array.from(data); + : [...data]; } // Case: category diff --git a/project/src/models/spt/config/IInventoryConfig.ts b/project/src/models/spt/config/IInventoryConfig.ts index 00157ffe..4be35a48 100644 --- a/project/src/models/spt/config/IInventoryConfig.ts +++ b/project/src/models/spt/config/IInventoryConfig.ts @@ -1,3 +1,4 @@ +import { MinMax } from "../../../models/common/MinMax"; import { IBaseConfig } from "./IBaseConfig"; export interface IInventoryConfig extends IBaseConfig @@ -5,6 +6,7 @@ export interface IInventoryConfig extends IBaseConfig kind: "aki-inventory" newItemsMarkedFound: boolean randomLootContainers: Record + sealedAirdropContainer: ISealedAirdropContainerSettings /** Contains item tpls that the server should consider money and treat the same as roubles/euros/dollars */ customMoneyTpls: string[] } @@ -13,5 +15,15 @@ export interface RewardDetails { rewardCount: number foundInRaid: boolean - rewardTplPool: Record + rewardTplPool?: Record + rewardTypePool?: Record +} + +export interface ISealedAirdropContainerSettings +{ + weaponRewardWeight: Record + defaultPresetsOnly: boolean + weaponModRewardLimits: Record + rewardTypeLimits: Record + ammoBoxWhitelist: string[] } \ No newline at end of file diff --git a/project/src/services/RagfairLinkedItemService.ts b/project/src/services/RagfairLinkedItemService.ts index 44c12337..f289462c 100644 --- a/project/src/services/RagfairLinkedItemService.ts +++ b/project/src/services/RagfairLinkedItemService.ts @@ -8,7 +8,7 @@ import { DatabaseServer } from "../servers/DatabaseServer"; @injectable() export class RagfairLinkedItemService { - protected linkedItemsCache: Record> = {}; + protected linkedItemsCache: Record> = {}; constructor( @inject("DatabaseServer") protected databaseServer: DatabaseServer, @@ -16,7 +16,7 @@ export class RagfairLinkedItemService ) { } - public getLinkedItems(linkedSearchId: string): Iterable + public getLinkedItems(linkedSearchId: string): Set { if (Object.keys(this.linkedItemsCache).length === 0) { @@ -26,6 +26,21 @@ export class RagfairLinkedItemService return this.linkedItemsCache[linkedSearchId]; } + /** + * Use ragfair linked item service to get an array of items that can fit on or in designated itemtpl + * @param itemTpl Item to get sub-items for + * @returns ITemplateItem array + */ + public getLinkedDbItems(itemTpl: string): ITemplateItem[] + { + const linkedItemsToWeaponTpls = this.getLinkedItems(itemTpl); + return [...linkedItemsToWeaponTpls].map(x => + { + const itemDetails = this.itemHelper.getItem(x); + return itemDetails[1]; + }); + } + /** * Create Dictionary of every item and the items associated with it */ @@ -91,7 +106,12 @@ export class RagfairLinkedItemService } } - /* Scans a given slot type for filters and returns them as a Set */ + /** + * Scans a given slot type for filters and returns them as a Set + * @param item + * @param slot + * @returns array of ids + */ protected getFilters(item: ITemplateItem, slot: string): string[] { if (!(slot in item._props && item._props[slot].length)) diff --git a/project/src/services/RagfairPriceService.ts b/project/src/services/RagfairPriceService.ts index 8e71719b..3e24ff6d 100644 --- a/project/src/services/RagfairPriceService.ts +++ b/project/src/services/RagfairPriceService.ts @@ -51,6 +51,8 @@ export class RagfairPriceService implements OnLoad */ public async onLoad(): Promise { + this.addMissingHandbookPrices(); + if (!this.generatedStaticPrices) { this.generateStaticPrices(); @@ -64,6 +66,24 @@ export class RagfairPriceService implements OnLoad } } + /** + * Add placeholder values for the new sealed weapon containers + */ + protected addMissingHandbookPrices(): void + { + const db = this.databaseServer.getTables(); + const sealedWeaponContainers = Object.values(db.templates.items).filter(x => x._name.includes("event_container_airdrop")); + + for (const container of sealedWeaponContainers) + { + // doesnt have a handbook value + if (db.templates.handbook.Items.findIndex(x => x.Id === container._id) === -1) + { + db.templates.handbook.Items.push({Id: container._id, ParentId: container._parent, Price: 100}); + } + } + } + public getRoute(): string { return "RagfairPriceService"; diff --git a/project/src/utils/RandomUtil.ts b/project/src/utils/RandomUtil.ts index 5380ab34..f7c8a908 100644 --- a/project/src/utils/RandomUtil.ts +++ b/project/src/utils/RandomUtil.ts @@ -300,7 +300,7 @@ export class RandomUtil * Drawing can be with or without replacement * @param {array} list The array we want to draw randomly from * @param {integer} count The number of times we want to draw - * @param {boolean} replacement Draw with or without replacement from the input array + * @param {boolean} replacement Draw with or without replacement from the input array(defult true) * @return {array} Array consisting of N random elements */ public drawRandomFromList(list: Array, count = 1, replacement = true): Array