diff --git a/project/assets/configs/airdrop.json b/project/assets/configs/airdrop.json index d4c40364..0a60e4ae 100644 --- a/project/assets/configs/airdrop.json +++ b/project/assets/configs/airdrop.json @@ -8,6 +8,12 @@ "reserve": 10, "tarkovStreets": 12 }, + "airdropTypeWeightings": { + "mixed": 2, + "weaponArmor": 1, + "foodMedical": 1, + "barter": 1 + }, "airdropMinStartTimeSeconds": 60, "airdropMaxStartTimeSeconds": 300, "planeMinFlyHeight": 400, @@ -16,103 +22,334 @@ "planeSpeed": 65, "crateFallSpeed": 3, "loot": { - "presetCount": { - "min": 3, - "max": 5 - }, - "itemCount": { - "min": 12, - "max": 25 - }, - "itemBlacklist": [ - "5e997f0b86f7741ac73993e2", - "5b44abe986f774283e2e3512", - "5e99711486f7744bfc4af328", - "5e99735686f7744bfc4af32c", - "6087e570b998180e9f76dc24", - "5d52d479a4b936793d58c76b", - "5e85aac65505fa48730d8af2", - "63495c500c297e20065a08b1", - "5cde8864d7f00c0010373be1", - "5b3b713c5acfc4330140bd8d", - "60c080eb991ac167ad1c3ad4" - ], - "itemTypeWhitelist": [ - "543be5dd4bdc2deb348b4569", - "5485a8684bdc2da71d8b4567", - "5d650c3e815116009f6201d2", - "5448e8d64bdc2dce718b4568", - "5448e8d04bdc2ddf718b4569", - "5447e1d04bdc2dff2f8b4567", - "57864ee62459775490116fc1", - "5448e54d4bdc2dcc718b4568", - "5448e5284bdc2dcb718b4567", - "5448e53e4bdc2d60728b4567", - "5448f3a64bdc2d60728b456a", - "5448f3ac4bdc2dce718b4569", - "55818ad54bdc2ddc698b4569", - "55818af64bdc2d5b648b4570", - "55818b0e4bdc2dde698b456e", - "5448bc234bdc2d3c308b4569", - "57864ada245977548638de91", - "5645bcb74bdc2ded0b8b4578", - "5448e5724bdc2ddf718b4568", - "55818add4bdc2d5b648b456f", - "543be6564bdc2df4348b4568", - "57864bb7245977548b3b66c2", - "550aa4cd4bdc2dd8348b456c", - "5448f39d4bdc2d0a728b4568", - "5448f3a14bdc2d27728b4569", - "5447e1d04bdc2dff2f8b4567", - "55818b164bdc2ddc698b456c", - "55818ae44bdc2dde698b456c" - ], - "itemLimits": { - "5447b5cf4bdc2d65278b4567": 1, - "5448e8d04bdc2ddf718b4569": 3, - "5448e8d64bdc2dce718b4568": 3, - "5448bc234bdc2d3c308b4569": 3, - "5448f3a64bdc2d60728b456a": 3, - "5448e54d4bdc2dcc718b4568": 3, - "5485a8684bdc2da71d8b4567": 4, - "57864bb7245977548b3b66c2": 2, - "57864ada245977548638de91": 3, - "5d650c3e815116009f6201d2": 2, - "5645bcb74bdc2ded0b8b4578": 2, - "5448e5724bdc2ddf718b4568": 2, - "55818add4bdc2d5b648b456f": 2, - "543be6564bdc2df4348b4568": 3, - "550aa4cd4bdc2dd8348b456c": 2, - "5448f39d4bdc2d0a728b4568": 4, - "5448f3a14bdc2d27728b4569": 2, - "5447e1d04bdc2dff2f8b4567": 1, - "55818ad54bdc2ddc698b4569": 3, - "55818b164bdc2ddc698b456c": 2, - "55818ae44bdc2dde698b456c": 2, - "5448e5284bdc2dcb718b4567": 2 - }, - "itemStackLimits": { - "5fc382a9d724d907e2077dab": { - "min": 5, + "mixed": { + "presetCount": { + "min": 3, "max": 5 }, - "59e690b686f7746c9f75e848": { - "min": 10, + "itemCount": { + "min": 12, "max": 25 }, - "5449016a4bdc2d6f028b456f": { - "min": 5000, - "max": 50000 + "itemBlacklist": [ + "5e997f0b86f7741ac73993e2", + "5b44abe986f774283e2e3512", + "5e99711486f7744bfc4af328", + "5e99735686f7744bfc4af32c", + "6087e570b998180e9f76dc24", + "5d52d479a4b936793d58c76b", + "5e85aac65505fa48730d8af2", + "63495c500c297e20065a08b1", + "5cde8864d7f00c0010373be1", + "5b3b713c5acfc4330140bd8d", + "60c080eb991ac167ad1c3ad4" + ], + "itemTypeWhitelist": [ + "543be5dd4bdc2deb348b4569", + "5485a8684bdc2da71d8b4567", + "5d650c3e815116009f6201d2", + "5448e8d64bdc2dce718b4568", + "5448e8d04bdc2ddf718b4569", + "5447e1d04bdc2dff2f8b4567", + "57864ee62459775490116fc1", + "5448e54d4bdc2dcc718b4568", + "5448e5284bdc2dcb718b4567", + "5448e53e4bdc2d60728b4567", + "5448f3a64bdc2d60728b456a", + "5448f3ac4bdc2dce718b4569", + "55818ad54bdc2ddc698b4569", + "55818af64bdc2d5b648b4570", + "55818b0e4bdc2dde698b456e", + "5448bc234bdc2d3c308b4569", + "57864ada245977548638de91", + "5645bcb74bdc2ded0b8b4578", + "5448e5724bdc2ddf718b4568", + "55818add4bdc2d5b648b456f", + "543be6564bdc2df4348b4568", + "57864bb7245977548b3b66c2", + "550aa4cd4bdc2dd8348b456c", + "5448f39d4bdc2d0a728b4568", + "5448f3a14bdc2d27728b4569", + "5447e1d04bdc2dff2f8b4567", + "55818b164bdc2ddc698b456c", + "55818ae44bdc2dde698b456c" + ], + "itemLimits": { + "5447b5cf4bdc2d65278b4567": 1, + "5448e8d04bdc2ddf718b4569": 3, + "5448e8d64bdc2dce718b4568": 3, + "5448bc234bdc2d3c308b4569": 3, + "5448f3a64bdc2d60728b456a": 3, + "5448e54d4bdc2dcc718b4568": 3, + "5485a8684bdc2da71d8b4567": 4, + "57864bb7245977548b3b66c2": 2, + "57864ada245977548638de91": 3, + "5d650c3e815116009f6201d2": 2, + "5645bcb74bdc2ded0b8b4578": 2, + "5448e5724bdc2ddf718b4568": 2, + "55818add4bdc2d5b648b456f": 2, + "543be6564bdc2df4348b4568": 3, + "550aa4cd4bdc2dd8348b456c": 2, + "5448f39d4bdc2d0a728b4568": 4, + "5448f3a14bdc2d27728b4569": 2, + "5447e1d04bdc2dff2f8b4567": 1, + "55818ad54bdc2ddc698b4569": 3, + "55818b164bdc2ddc698b456c": 2, + "55818ae44bdc2dde698b456c": 2, + "5448e5284bdc2dcb718b4567": 2 }, - "569668774bdc2da2298b4568": { - "min": 100, - "max": 500 + "itemStackLimits": { + "5fc382a9d724d907e2077dab": { + "min": 5, + "max": 5 + }, + "59e690b686f7746c9f75e848": { + "min": 10, + "max": 25 + }, + "5449016a4bdc2d6f028b456f": { + "min": 5000, + "max": 50000 + }, + "569668774bdc2da2298b4568": { + "min": 100, + "max": 500 + }, + "5696686a4bdc2da3298b456a": { + "min": 100, + "max": 500 + } }, - "5696686a4bdc2da3298b456a": { - "min": 100, - "max": 500 + "armorLevelWhitelist": [0, 4, 5, 6] + }, + "weaponArmor": { + "presetCount": { + "min": 6, + "max": 8 + }, + "itemCount": { + "min": 10, + "max": 20 + }, + "itemBlacklist": [ + "5e997f0b86f7741ac73993e2", + "5b44abe986f774283e2e3512", + "5e99711486f7744bfc4af328", + "5e99735686f7744bfc4af32c", + "6087e570b998180e9f76dc24", + "5d52d479a4b936793d58c76b", + "5e85aac65505fa48730d8af2", + "63495c500c297e20065a08b1", + "5cde8864d7f00c0010373be1", + "5b3b713c5acfc4330140bd8d", + "60c080eb991ac167ad1c3ad4" + ], + "itemTypeWhitelist": [ + "5485a8684bdc2da71d8b4567", + "5448e8d64bdc2dce718b4568", + "5448e8d04bdc2ddf718b4569", + "5447e1d04bdc2dff2f8b4567", + "5448e54d4bdc2dcc718b4568", + "5448e5284bdc2dcb718b4567", + "5448e53e4bdc2d60728b4567", + "55818ad54bdc2ddc698b4569", + "55818af64bdc2d5b648b4570", + "55818b0e4bdc2dde698b456e", + "5448bc234bdc2d3c308b4569", + "5645bcb74bdc2ded0b8b4578", + "5448e5724bdc2ddf718b4568", + "55818add4bdc2d5b648b456f", + "543be6564bdc2df4348b4568", + "550aa4cd4bdc2dd8348b456c", + "55818b164bdc2ddc698b456c", + "55818ae44bdc2dde698b456c" + + ], + "itemLimits": { + "5448e8d04bdc2ddf718b4569": 3, + "5448e8d64bdc2dce718b4568": 3, + "5448bc234bdc2d3c308b4569": 3, + "5448e54d4bdc2dcc718b4568": 3, + "5485a8684bdc2da71d8b4567": 4, + "5645bcb74bdc2ded0b8b4578": 2, + "5448e5724bdc2ddf718b4568": 2, + "55818add4bdc2d5b648b456f": 2, + "543be6564bdc2df4348b4568": 3, + "550aa4cd4bdc2dd8348b456c": 2, + "5447e1d04bdc2dff2f8b4567": 3, + "55818ad54bdc2ddc698b4569": 3, + "55818b164bdc2ddc698b456c": 2, + "55818ae44bdc2dde698b456c": 2, + "5448e5284bdc2dcb718b4567": 2 + }, + "itemStackLimits": { + "5fc382a9d724d907e2077dab": { + "min": 5, + "max": 5 + }, + "59e690b686f7746c9f75e848": { + "min": 10, + "max": 25 + } + }, + "armorLevelWhitelist": [0, 3, 4, 5, 6] + }, + "foodMedical": { + "presetCount": { + "min": 3, + "max": 5 + }, + "itemCount": { + "min": 17, + "max": 28 + }, + "itemBlacklist": [ + "5e997f0b86f7741ac73993e2", + "5b44abe986f774283e2e3512", + "5e99711486f7744bfc4af328", + "5e99735686f7744bfc4af32c", + "6087e570b998180e9f76dc24", + "5d52d479a4b936793d58c76b", + "5e85aac65505fa48730d8af2", + "63495c500c297e20065a08b1", + "5cde8864d7f00c0010373be1", + "5b3b713c5acfc4330140bd8d", + "60c080eb991ac167ad1c3ad4" + ], + "itemTypeWhitelist": [ + "543be5dd4bdc2deb348b4569", + "5448e8d64bdc2dce718b4568", + "5448e8d04bdc2ddf718b4569", + "5448f3a64bdc2d60728b456a", + "5448f3ac4bdc2dce718b4569", + "5448f39d4bdc2d0a728b4568", + "5448f3a14bdc2d27728b4569" + ], + "itemLimits": { + "5447b5cf4bdc2d65278b4567": 1, + "5448e8d04bdc2ddf718b4569": 3, + "5448e8d64bdc2dce718b4568": 3, + "5448bc234bdc2d3c308b4569": 3, + "5448f3a64bdc2d60728b456a": 3, + "5448e54d4bdc2dcc718b4568": 3, + "5485a8684bdc2da71d8b4567": 4, + "57864bb7245977548b3b66c2": 2, + "57864ada245977548638de91": 3, + "5d650c3e815116009f6201d2": 2, + "5645bcb74bdc2ded0b8b4578": 2, + "5448e5724bdc2ddf718b4568": 2, + "55818add4bdc2d5b648b456f": 2, + "543be6564bdc2df4348b4568": 3, + "550aa4cd4bdc2dd8348b456c": 2, + "5448f39d4bdc2d0a728b4568": 4, + "5448f3a14bdc2d27728b4569": 2, + "5447e1d04bdc2dff2f8b4567": 1, + "55818ad54bdc2ddc698b4569": 3, + "55818b164bdc2ddc698b456c": 2, + "55818ae44bdc2dde698b456c": 2, + "5448e5284bdc2dcb718b4567": 2 + }, + "itemStackLimits": { + "5fc382a9d724d907e2077dab": { + "min": 5, + "max": 5 + }, + "59e690b686f7746c9f75e848": { + "min": 10, + "max": 25 + }, + "5449016a4bdc2d6f028b456f": { + "min": 5000, + "max": 50000 + }, + "569668774bdc2da2298b4568": { + "min": 100, + "max": 500 + }, + "5696686a4bdc2da3298b456a": { + "min": 100, + "max": 500 + } } }, - "armorLevelWhitelist": [0, 4, 5, 6] + "barter": { + "presetCount": { + "min": 3, + "max": 5 + }, + "itemCount": { + "min": 16, + "max": 25 + }, + "itemBlacklist": [ + "5e997f0b86f7741ac73993e2", + "5b44abe986f774283e2e3512", + "5e99711486f7744bfc4af328", + "5e99735686f7744bfc4af32c", + "6087e570b998180e9f76dc24", + "5d52d479a4b936793d58c76b", + "5e85aac65505fa48730d8af2", + "63495c500c297e20065a08b1", + "5cde8864d7f00c0010373be1", + "5b3b713c5acfc4330140bd8d", + "60c080eb991ac167ad1c3ad4" + ], + "itemTypeWhitelist": [ + "5d650c3e815116009f6201d2", + "57864ee62459775490116fc1", + "57864ada245977548638de91", + "57864bb7245977548b3b66c2", + "57864e4c24597754843f8723", + "57864c322459775490116fbf", + "57864a66245977548f04a81f" + ], + "itemLimits": { + "5447b5cf4bdc2d65278b4567": 1, + "57864ee62459775490116fc1": 2, + "5448e8d04bdc2ddf718b4569": 3, + "5448e8d64bdc2dce718b4568": 3, + "5448bc234bdc2d3c308b4569": 3, + "5448f3a64bdc2d60728b456a": 3, + "5448e54d4bdc2dcc718b4568": 3, + "5485a8684bdc2da71d8b4567": 4, + "57864bb7245977548b3b66c2": 5, + "57864ada245977548638de91": 5, + "5d650c3e815116009f6201d2": 5, + "5645bcb74bdc2ded0b8b4578": 2, + "5448e5724bdc2ddf718b4568": 2, + "55818add4bdc2d5b648b456f": 2, + "543be6564bdc2df4348b4568": 3, + "550aa4cd4bdc2dd8348b456c": 2, + "5448f39d4bdc2d0a728b4568": 4, + "5448f3a14bdc2d27728b4569": 2, + "5447e1d04bdc2dff2f8b4567": 1, + "55818ad54bdc2ddc698b4569": 3, + "55818b164bdc2ddc698b456c": 2, + "55818ae44bdc2dde698b456c": 2, + "5448e5284bdc2dcb718b4567": 2 + }, + "itemStackLimits": { + "5fc382a9d724d907e2077dab": { + "min": 5, + "max": 5 + }, + "59e690b686f7746c9f75e848": { + "min": 10, + "max": 25 + }, + "5449016a4bdc2d6f028b456f": { + "min": 5000, + "max": 50000 + }, + "569668774bdc2da2298b4568": { + "min": 100, + "max": 500 + }, + "5696686a4bdc2da3298b456a": { + "min": 100, + "max": 500 + } + } + } } } diff --git a/project/src/controllers/LocationController.ts b/project/src/controllers/LocationController.ts index 74c5cea5..eab9d939 100644 --- a/project/src/controllers/LocationController.ts +++ b/project/src/controllers/LocationController.ts @@ -2,12 +2,14 @@ import { inject, injectable } from "tsyringe"; import { LocationGenerator } from "../generators/LocationGenerator"; import { LootGenerator } from "../generators/LootGenerator"; +import { WeightedRandomHelper } from "../helpers/WeightedRandomHelper"; import { ILocation } from "../models/eft/common/ILocation"; import { ILocationBase } from "../models/eft/common/ILocationBase"; import { ILocationsGenerateAllResponse } from "../models/eft/common/ILocationsSourceDestinationBase"; import { ILooseLoot, SpawnpointTemplate } from "../models/eft/common/ILooseLoot"; +import { AirdropTypeEnum } from "../models/enums/AirdropType"; import { ConfigTypes } from "../models/enums/ConfigTypes"; import { IAirdropConfig } from "../models/spt/config/IAirdropConfig"; import { ILocations } from "../models/spt/server/ILocations"; @@ -29,6 +31,7 @@ export class LocationController constructor( @inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("HashUtil") protected hashUtil: HashUtil, + @inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper, @inject("WinstonLogger") protected logger: ILogger, @inject("LocationGenerator") protected locationGenerator: LocationGenerator, @inject("LocalisationService") protected localisationService: LocalisationService, @@ -106,7 +109,10 @@ export class LocationController return output; } - /* get all locations without loot data */ + /** + * Get all maps base location properties without loot data + * @returns ILocationsGenerateAllResponse + */ public generateAll(): ILocationsGenerateAllResponse { const locations = this.databaseServer.getTables().locations; @@ -138,20 +144,52 @@ export class LocationController /** * Get loot for an airdop container * Generates it randomly based on config/airdrop.json values - * @returns Array of LootItem + * @returns Array of LootItem objects */ public getAirdropLoot(): LootItem[] { - const options: LootRequest = { - presetCount: this.airdropConfig.loot.presetCount, - itemCount: this.airdropConfig.loot.itemCount, - itemBlacklist: this.airdropConfig.loot.itemBlacklist, - itemTypeWhitelist: this.airdropConfig.loot.itemTypeWhitelist, - itemLimits: this.airdropConfig.loot.itemLimits, - itemStackLimits: this.airdropConfig.loot.itemStackLimits, - armorLevelWhitelist: this.airdropConfig.loot.armorLevelWhitelist - }; + const airdropType = this.chooseAirdropType(); - return this.lootGenerator.createRandomloot(options); + this.logger.debug(`Chose ${airdropType} for airdrop loot`); + + const airdropConfig = this.getAirdropLootConfigByType(airdropType); + + return this.lootGenerator.createRandomLoot(airdropConfig); + } + + /** + * Randomly pick a type of airdrop loot using weighted values from config + * @returns airdrop type value + */ + protected chooseAirdropType(): AirdropTypeEnum + { + const possibleAirdropTypes = this.airdropConfig.airdropTypeWeightings; + + return this.weightedRandomHelper.getWeightedValue(possibleAirdropTypes); + } + + /** + * Get the configuration for a specific type of airdrop + * @param airdropType Type of airdrop to get settings for + * @returns LootRequest + */ + protected getAirdropLootConfigByType(airdropType: AirdropTypeEnum): LootRequest + { + let lootSettingsByType = this.airdropConfig.loot[airdropType]; + if (!lootSettingsByType) + { + this.logger.error(`Unable to find airdrop config settings for type: ${airdropType}, falling back to mixed`); + lootSettingsByType = this.airdropConfig.loot[AirdropTypeEnum.MIXED]; + } + + return { + presetCount: lootSettingsByType.presetCount, + itemCount: lootSettingsByType.itemCount, + itemBlacklist: lootSettingsByType.itemBlacklist, + itemTypeWhitelist: lootSettingsByType.itemTypeWhitelist, + itemLimits: lootSettingsByType.itemLimits, + itemStackLimits: lootSettingsByType.itemStackLimits, + armorLevelWhitelist: lootSettingsByType.armorLevelWhitelist + }; } } \ No newline at end of file diff --git a/project/src/generators/LootGenerator.ts b/project/src/generators/LootGenerator.ts index 155f300c..7092eef9 100644 --- a/project/src/generators/LootGenerator.ts +++ b/project/src/generators/LootGenerator.ts @@ -32,7 +32,7 @@ export class LootGenerator * @param options parameters to adjust how loot is generated * @returns An array of loot items */ - public createRandomloot(options: LootRequest): LootItem[] + public createRandomLoot(options: LootRequest): LootItem[] { const result: LootItem[] = []; diff --git a/project/src/helpers/WeightedRandomHelper.ts b/project/src/helpers/WeightedRandomHelper.ts index 53d98951..f7af8f8d 100644 --- a/project/src/helpers/WeightedRandomHelper.ts +++ b/project/src/helpers/WeightedRandomHelper.ts @@ -4,6 +4,7 @@ import { injectable } from "tsyringe"; export class WeightedRandomHelper { /** + * USE getWeightedValue() WHERE POSSIBLE * Gets a tplId from a weighted dictionary * @param {tplId: weighting[]} itemArray * @returns tplId @@ -17,6 +18,15 @@ export class WeightedRandomHelper return chosenItem.item; } + public getWeightedValue(itemArray: { [key: string]: unknown; } | ArrayLike): T + { + const itemKeys = Object.keys(itemArray); + const weights = Object.values(itemArray); + const chosenItem = this.weightedRandom(itemKeys, weights); + + return chosenItem.item; + } + /** * Picks the random item based on its weight. * The items with higher weight will be picked more often (with a higher probability). diff --git a/project/src/models/enums/AirdropType.ts b/project/src/models/enums/AirdropType.ts new file mode 100644 index 00000000..ba55ab40 --- /dev/null +++ b/project/src/models/enums/AirdropType.ts @@ -0,0 +1,7 @@ +export enum AirdropTypeEnum + { + MIXED = "mixed", + WEAPONARMOR = "weaponarmor", + FOODMEDICAL = "foodmedical", + BARTER = "barter" +} \ No newline at end of file diff --git a/project/src/models/spt/config/IAirdropConfig.ts b/project/src/models/spt/config/IAirdropConfig.ts index 7f121e82..989eb646 100644 --- a/project/src/models/spt/config/IAirdropConfig.ts +++ b/project/src/models/spt/config/IAirdropConfig.ts @@ -1,10 +1,12 @@ -import { MinMax } from "../../common/MinMax" -import { IBaseConfig } from "./IBaseConfig" +import { AirdropTypeEnum } from "../../../models/enums/AirdropType"; +import { MinMax } from "../../common/MinMax"; +import { IBaseConfig } from "./IBaseConfig"; export interface IAirdropConfig extends IBaseConfig { kind: "aki-airdrop" airdropChancePercent: AirdropChancePercent + airdropTypeWeightings: Record planeMinFlyHeight: number planeMaxFlyHeight: number planeVolume: number @@ -12,7 +14,7 @@ export interface IAirdropConfig extends IBaseConfig crateFallSpeed: number airdropMinStartTimeSeconds: number airdropMaxStartTimeSeconds: number - loot: AirdropLoot + loot: Record } export interface AirdropChancePercent @@ -28,13 +30,13 @@ export interface AirdropChancePercent export interface AirdropLoot { - presetCount: MinMax + presetCount?: MinMax itemCount: MinMax itemBlacklist: string[] itemTypeWhitelist: string[] /** key: item base type: value: max count */ itemLimits: Record itemStackLimits: Record - armorLevelWhitelist: number[] + armorLevelWhitelist?: number[] } \ No newline at end of file diff --git a/project/src/models/spt/services/LootRequest.ts b/project/src/models/spt/services/LootRequest.ts index 9c7cb2e4..0829f0f7 100644 --- a/project/src/models/spt/services/LootRequest.ts +++ b/project/src/models/spt/services/LootRequest.ts @@ -1,6 +1,6 @@ -import { MinMax } from "../../common/MinMax" +import { MinMax } from "../../common/MinMax"; -export class LootRequest +export interface LootRequest { presetCount: MinMax itemCount: MinMax