Server/project/src/generators/LootGenerator.ts

501 lines
19 KiB
TypeScript
Raw Normal View History

2023-03-03 16:23:46 +01:00
import { inject, injectable } from "tsyringe";
import { InventoryHelper } from "@spt-aki/helpers/InventoryHelper";
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
import { PresetHelper } from "@spt-aki/helpers/PresetHelper";
import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper";
import { IPreset } from "@spt-aki/models/eft/common/IGlobals";
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
import { AddItem } from "@spt-aki/models/eft/inventory/IAddItemRequestData";
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
import { ISealedAirdropContainerSettings, RewardDetails } from "@spt-aki/models/spt/config/IInventoryConfig";
import { LootItem } from "@spt-aki/models/spt/services/LootItem";
import { LootRequest } from "@spt-aki/models/spt/services/LootRequest";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { ItemFilterService } from "@spt-aki/services/ItemFilterService";
import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { RagfairLinkedItemService } from "@spt-aki/services/RagfairLinkedItemService";
import { HashUtil } from "@spt-aki/utils/HashUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
2023-03-03 16:23:46 +01:00
2023-11-16 02:35:05 +01:00
type ItemLimit = { current: number; max: number; };
2023-03-03 16:23:46 +01:00
@injectable()
export class LootGenerator
{
constructor(
@inject("WinstonLogger") protected logger: ILogger,
@inject("HashUtil") protected hashUtil: HashUtil,
@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,
2023-03-03 16:23:46 +01:00
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("RagfairLinkedItemService") protected ragfairLinkedItemService: RagfairLinkedItemService,
2023-11-16 02:35:05 +01:00
@inject("ItemFilterService") protected itemFilterService: ItemFilterService,
2023-03-03 16:23:46 +01:00
)
{}
/**
* Generate a list of items based on configuration options parameter
* @param options parameters to adjust how loot is generated
* @returns An array of loot items
*/
public createRandomLoot(options: LootRequest): LootItem[]
2023-03-03 16:23:46 +01:00
{
const result: LootItem[] = [];
const itemTypeCounts = this.initItemLimitCounter(options.itemLimits);
const tables = this.databaseServer.getTables();
2023-11-13 17:05:05 +01:00
const itemBlacklist = new Set<string>([
...this.itemFilterService.getBlacklistedItems(),
...options.itemBlacklist,
]);
if (!options.allowBossItems)
{
2023-10-31 18:46:14 +01:00
for (const bossItem of this.itemFilterService.getBossItems())
{
itemBlacklist.add(bossItem);
}
}
2023-06-20 17:59:15 +02:00
// Handle sealed weapon containers
2023-11-16 02:35:05 +01:00
const desiredWeaponCrateCount = this.randomUtil.getInt(
options.weaponCrateCount.min,
options.weaponCrateCount.max,
);
2023-06-20 17:59:15 +02:00
if (desiredWeaponCrateCount > 0)
{
// Get list of all sealed containers from db
2023-11-16 02:35:05 +01:00
const sealedWeaponContainerPool = Object.values(tables.templates.items).filter((x) =>
x._name.includes("event_container_airdrop")
);
2023-06-20 17:59:15 +02:00
for (let index = 0; index < desiredWeaponCrateCount; index++)
{
// Choose one at random + add to results array
const chosenSealedContainer = this.randomUtil.getArrayValue(sealedWeaponContainerPool);
result.push({
id: this.hashUtil.generate(),
tpl: chosenSealedContainer._id,
isPreset: false,
2023-11-16 02:35:05 +01:00
stackCount: 1,
2023-06-20 17:59:15 +02:00
});
}
}
2023-03-03 16:23:46 +01:00
// Get items from items.json that have a type of item + not in global blacklist + basetype is in whitelist
2023-11-16 02:35:05 +01:00
const items = Object.entries(tables.templates.items).filter((x) =>
!itemBlacklist.has(x[1]._id)
&& x[1]._type.toLowerCase() === "item"
2023-03-03 16:23:46 +01:00
&& !x[1]._props.QuestItem
2023-11-16 02:35:05 +01:00
&& options.itemTypeWhitelist.includes(x[1]._parent)
);
2023-03-03 16:23:46 +01:00
const randomisedItemCount = this.randomUtil.getInt(options.itemCount.min, options.itemCount.max);
for (let index = 0; index < randomisedItemCount; index++)
{
if (!this.findAndAddRandomItemToLoot(items, itemTypeCounts, options, result))
{
index--;
2023-11-16 02:35:05 +01:00
}
2023-03-03 16:23:46 +01:00
}
2023-11-16 02:35:05 +01:00
const globalDefaultPresets = Object.entries(tables.globals.ItemPresets).filter((x) =>
x[1]._encyclopedia !== undefined
);
2023-03-03 16:23:46 +01:00
const randomisedPresetCount = this.randomUtil.getInt(options.presetCount.min, options.presetCount.max);
const itemBlacklistArray = Array.from(itemBlacklist);
2023-03-03 16:23:46 +01:00
for (let index = 0; index < randomisedPresetCount; index++)
{
if (!this.findAndAddRandomPresetToLoot(globalDefaultPresets, itemTypeCounts, itemBlacklistArray, result))
2023-03-03 16:23:46 +01:00
{
index--;
}
}
return result;
}
/**
* Construct item limit record to hold max and current item count for each item type
2023-03-03 16:23:46 +01:00
* @param limits limits as defined in config
* @returns record, key: item tplId, value: current/max item count allowed
*/
protected initItemLimitCounter(limits: Record<string, number>): Record<string, ItemLimit>
2023-03-03 16:23:46 +01:00
{
const itemTypeCounts: Record<string, ItemLimit> = {};
for (const itemTypeId in limits)
2023-03-03 16:23:46 +01:00
{
2023-11-16 02:35:05 +01:00
itemTypeCounts[itemTypeId] = { current: 0, max: limits[itemTypeId] };
2023-03-03 16:23:46 +01:00
}
return itemTypeCounts;
}
/**
* Find a random item in items.json and add to result array
* @param items items to choose from
* @param itemTypeCounts item limit counts
* @param options item filters
* @param result array to add found item to
* @returns true if item was valid and added to pool
*/
protected findAndAddRandomItemToLoot(
items: [string, ITemplateItem][],
itemTypeCounts: Record<string, { current: number; max: number; }>,
options: LootRequest,
2023-11-16 02:35:05 +01:00
result: LootItem[],
): boolean
2023-03-03 16:23:46 +01:00
{
const randomItem = this.randomUtil.getArrayValue(items)[1];
const itemLimitCount = itemTypeCounts[randomItem._parent];
if (itemLimitCount && itemLimitCount.current > itemLimitCount.max)
{
return false;
}
const newLootItem: LootItem = {
id: this.hashUtil.generate(),
tpl: randomItem._id,
isPreset: false,
2023-11-16 02:35:05 +01:00
stackCount: 1,
2023-03-03 16:23:46 +01:00
};
// Check if armor has level in allowed whitelist
2023-11-16 02:35:05 +01:00
if (randomItem._parent === BaseClasses.ARMOR || randomItem._parent === BaseClasses.VEST)
2023-03-03 16:23:46 +01:00
{
if (!options.armorLevelWhitelist.includes(Number(randomItem._props.armorClass)))
{
2023-11-16 02:35:05 +01:00
return false;
2023-03-03 16:23:46 +01:00
}
}
// Special case - handle items that need a stackcount > 1
if (randomItem._props.StackMaxSize > 1)
{
newLootItem.stackCount = this.getRandomisedStackCount(randomItem, options);
}
2023-11-16 02:35:05 +01:00
2023-03-03 16:23:46 +01:00
newLootItem.tpl = randomItem._id;
result.push(newLootItem);
if (itemLimitCount)
{
// Increment item count as it's in limit array
itemLimitCount.current++;
}
// Item added okay
return true;
}
/**
* Get a randomised stack count for an item between its StackMinRandom and StackMaxSize values
* @param item item to get stack count of
* @param options loot options
* @returns stack count
*/
protected getRandomisedStackCount(item: ITemplateItem, options: LootRequest): number
{
let min = item._props.StackMinRandom;
let max = item._props.StackMaxSize;
if (options.itemStackLimits[item._id])
{
min = options.itemStackLimits[item._id].min;
max = options.itemStackLimits[item._id].max;
}
return this.randomUtil.getInt(min ?? 1, max ?? 1);
2023-03-03 16:23:46 +01:00
}
/**
* Find a random item in items.json and add to result array
* @param globalDefaultPresets presets to choose from
* @param itemTypeCounts item limit counts
* @param itemBlacklist items to skip
* @param result array to add found preset to
* @returns true if preset was valid and added to pool
*/
protected findAndAddRandomPresetToLoot(
globalDefaultPresets: [string, IPreset][],
2023-03-03 16:23:46 +01:00
itemTypeCounts: Record<string, { current: number; max: number; }>,
itemBlacklist: string[],
2023-11-16 02:35:05 +01:00
result: LootItem[],
): boolean
2023-03-03 16:23:46 +01:00
{
// Choose random preset and get details from item.json using encyclopedia value (encyclopedia === tplId)
const randomPreset = this.randomUtil.getArrayValue(globalDefaultPresets)[1];
if (!randomPreset?._encyclopedia)
{
this.logger.debug(`Airdrop - preset with id: ${randomPreset._id} lacks encyclopedia property, skipping`);
return false;
}
const itemDetails = this.itemHelper.getItem(randomPreset._encyclopedia);
if (!itemDetails[0])
{
this.logger.debug(`Airdrop - Unable to find preset with tpl: ${randomPreset._encyclopedia}, skipping`);
return false;
}
2023-03-03 16:23:46 +01:00
// Skip blacklisted items
if (itemBlacklist.includes(randomPreset._items[0]._tpl))
{
return false;
}
// Some custom mod items are lacking a parent property
if (!itemDetails[1]._parent)
2023-03-03 16:23:46 +01:00
{
this.logger.error(this.localisationService.getText("loot-item_missing_parentid", itemDetails[1]?._name));
2023-03-03 16:23:46 +01:00
return false;
}
// Check picked preset hasn't exceeded spawn limit
const itemLimitCount = itemTypeCounts[itemDetails[1]._parent];
2023-03-03 16:23:46 +01:00
if (itemLimitCount && itemLimitCount.current > itemLimitCount.max)
{
return false;
}
2023-11-16 02:35:05 +01:00
const newLootItem: LootItem = { tpl: randomPreset._items[0]._tpl, isPreset: true, stackCount: 1 };
2023-03-03 16:23:46 +01:00
result.push(newLootItem);
if (itemLimitCount)
{
// increment item count as its in limit array
itemLimitCount.current++;
}
2023-11-16 02:35:05 +01:00
2023-03-03 16:23:46 +01:00
// item added okay
return true;
}
/**
* Sealed weapon containers have a weapon + associated mods inside them + assortment of other things (food/meds)
* @param containerSettings sealed weapon container settings
* @returns Array of items to add to player inventory
*/
public getSealedWeaponCaseLoot(containerSettings: ISealedAirdropContainerSettings): AddItem[]
{
const itemsToReturn: AddItem[] = [];
// choose a weapon to give to the player (weighted)
2023-11-16 02:35:05 +01:00
const chosenWeaponTpl = this.weightedRandomHelper.getWeightedValue<string>(
containerSettings.weaponRewardWeight,
);
const weaponDetailsDb = this.itemHelper.getItem(chosenWeaponTpl);
if (!weaponDetailsDb[0])
{
2023-11-16 02:35:05 +01:00
this.logger.error(
this.localisationService.getText("loot-non_item_picked_as_sealed_weapon_crate_reward", chosenWeaponTpl),
);
return itemsToReturn;
}
2023-11-16 02:35:05 +01:00
// Get weapon preset - default or choose a random one from all possible
let chosenWeaponPreset = containerSettings.defaultPresetsOnly
? this.presetHelper.getDefaultPreset(chosenWeaponTpl)
: this.randomUtil.getArrayValue(this.presetHelper.getPresets(chosenWeaponTpl));
if (!chosenWeaponPreset)
{
this.logger.warning(`Default preset for weapon ${chosenWeaponTpl} not found, choosing random instead`);
chosenWeaponPreset = this.randomUtil.getArrayValue(this.presetHelper.getPresets(chosenWeaponTpl));
}
// Add preset to return object
Merge branch 'master' into 3.8.0 # Conflicts: # project/.vscode/launch.json # project/assets/database/locations/bigmap/base.json # project/assets/database/locations/interchange/base.json # project/assets/database/locations/rezervbase/base.json # project/gulpfile.mjs # project/package.json # project/src/ErrorHandler.ts # project/src/Program.ts # project/src/callbacks/DataCallbacks.ts # project/src/callbacks/DialogueCallbacks.ts # project/src/callbacks/GameCallbacks.ts # project/src/callbacks/HandbookCallbacks.ts # project/src/callbacks/HealthCallbacks.ts # project/src/callbacks/HttpCallbacks.ts # project/src/callbacks/LauncherCallbacks.ts # project/src/callbacks/LocationCallbacks.ts # project/src/callbacks/MatchCallbacks.ts # project/src/callbacks/ModCallbacks.ts # project/src/callbacks/NotifierCallbacks.ts # project/src/callbacks/PresetCallbacks.ts # project/src/callbacks/ProfileCallbacks.ts # project/src/callbacks/RagfairCallbacks.ts # project/src/callbacks/TraderCallbacks.ts # project/src/context/ApplicationContext.ts # project/src/context/ContextVariableType.ts # project/src/controllers/BotController.ts # project/src/controllers/CustomizationController.ts # project/src/controllers/DialogueController.ts # project/src/controllers/GameController.ts # project/src/controllers/HealthController.ts # project/src/controllers/HideoutController.ts # project/src/controllers/InraidController.ts # project/src/controllers/InsuranceController.ts # project/src/controllers/InventoryController.ts # project/src/controllers/LauncherController.ts # project/src/controllers/LocationController.ts # project/src/controllers/MatchController.ts # project/src/controllers/QuestController.ts # project/src/controllers/RagfairController.ts # project/src/controllers/RepeatableQuestController.ts # project/src/controllers/TradeController.ts # project/src/di/Container.ts # project/src/di/Router.ts # project/src/generators/BotEquipmentModGenerator.ts # project/src/generators/BotLevelGenerator.ts # project/src/generators/BotWeaponGenerator.ts # project/src/generators/LocationGenerator.ts # project/src/generators/LootGenerator.ts # project/src/generators/RepeatableQuestGenerator.ts # project/src/generators/WeatherGenerator.ts # project/src/generators/weapongen/InventoryMagGen.ts # project/src/generators/weapongen/implementations/BarrelInventoryMagGen.ts # project/src/generators/weapongen/implementations/ExternalInventoryMagGen.ts # project/src/helpers/AssortHelper.ts # project/src/helpers/BotGeneratorHelper.ts # project/src/helpers/InRaidHelper.ts # project/src/helpers/ProfileHelper.ts # project/src/helpers/RagfairHelper.ts # project/src/helpers/RagfairOfferHelper.ts # project/src/helpers/TraderHelper.ts # project/src/loaders/ModLoadOrder.ts # project/src/loaders/PostDBModLoader.ts # project/src/loaders/PreAkiModLoader.ts # project/src/models/eft/common/IGlobals.ts # project/src/models/eft/common/ILocationBase.ts # project/src/models/eft/common/tables/IBotBase.ts # project/src/models/eft/common/tables/IProfileTemplate.ts # project/src/models/eft/common/tables/ITemplateItem.ts # project/src/models/eft/dialog/IAcceptFriendRequestData.ts # project/src/models/eft/dialog/IDeleteFriendRequest.ts # project/src/models/eft/game/IGameConfigResponse.ts # project/src/models/eft/game/IGameKeepAliveResponse.ts # project/src/models/eft/game/IGameStartResponse.ts # project/src/models/eft/match/IJoinMatchResult.ts # project/src/models/eft/notifier/INotifier.ts # project/src/models/eft/profile/GetProfileStatusResponseData.ts # project/src/models/eft/trade/IProcessBuyTradeRequestData.ts # project/src/models/eft/trade/IProcessSellTradeRequestData.ts # project/src/models/enums/WildSpawnTypeNumber.ts # project/src/models/spt/bots/BotGenerationDetails.ts # project/src/models/spt/config/IBotConfig.ts # project/src/models/spt/config/IBotDurability.ts # project/src/models/spt/config/IInRaidConfig.ts # project/src/models/spt/config/ILocationConfig.ts # project/src/models/spt/config/IQuestConfig.ts # project/src/models/spt/config/ISeasonalEventConfig.ts # project/src/models/spt/server/ILocations.ts # project/src/models/spt/utils/IUuidGenerator.ts # project/src/routers/dynamic/BotDynamicRouter.ts # project/src/routers/dynamic/BundleDynamicRouter.ts # project/src/routers/dynamic/CustomizationDynamicRouter.ts # project/src/routers/dynamic/DataDynamicRouter.ts # project/src/routers/dynamic/HttpDynamicRouter.ts # project/src/routers/dynamic/InraidDynamicRouter.ts # project/src/routers/dynamic/LocationDynamicRouter.ts # project/src/routers/dynamic/NotifierDynamicRouter.ts # project/src/routers/dynamic/TraderDynamicRouter.ts # project/src/routers/save_load/InsuranceSaveLoadRouter.ts # project/src/routers/save_load/ProfileSaveLoadRouter.ts # project/src/routers/serializers/NotifySerializer.ts # project/src/routers/static/BotStaticRouter.ts # project/src/routers/static/BundleStaticRouter.ts # project/src/routers/static/ClientLogStaticRouter.ts # project/src/routers/static/CustomizationStaticRouter.ts # project/src/routers/static/DataStaticRouter.ts # project/src/routers/static/DialogStaticRouter.ts # project/src/routers/static/GameStaticRouter.ts # project/src/routers/static/HealthStaticRouter.ts # project/src/routers/static/InraidStaticRouter.ts # project/src/routers/static/InsuranceStaticRouter.ts # project/src/routers/static/ItemEventStaticRouter.ts # project/src/routers/static/LauncherStaticRouter.ts # project/src/routers/static/LocationStaticRouter.ts # project/src/routers/static/MatchStaticRouter.ts # project/src/routers/static/NotifierStaticRouter.ts # project/src/routers/static/PresetStaticRouter.ts # project/src/routers/static/ProfileStaticRouter.ts # project/src/routers/static/QuestStaticRouter.ts # project/src/routers/static/RagfairStaticRouter.ts # project/src/routers/static/TraderStaticRouter.ts # project/src/routers/static/WeatherStaticRouter.ts # project/src/services/BotEquipmentFilterService.ts # project/src/services/BotGenerationCacheService.ts # project/src/services/BotWeaponModLimitService.ts # project/src/services/PaymentService.ts # project/src/services/ProfileFixerService.ts # project/src/services/RagfairOfferService.ts # project/src/services/RagfairTaxService.ts # project/src/services/RepairService.ts # project/src/services/SeasonalEventService.ts # project/src/utils/RagfairOfferHolder.ts # project/src/utils/TimeUtil.ts # project/src/utils/UUidGenerator.ts # project/src/utils/VFS.ts # project/src/utils/collections/queue/Queue.ts # project/src/utils/logging/AbstractWinstonLogger.ts # project/src/utils/logging/WinstonMainLogger.ts # project/src/utils/logging/WinstonRequestLogger.ts # project/tests/utils/TimeUtil.test.ts Manually resolved by Refringe.
2023-11-17 05:35:11 +01:00
itemsToReturn.push({ count: 1, item_id: chosenWeaponPreset._id, isPreset: true });
// Get items related to chosen weapon
const linkedItemsToWeapon = this.ragfairLinkedItemService.getLinkedDbItems(chosenWeaponTpl);
2023-11-16 02:35:05 +01:00
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
*/
2023-11-16 02:35:05 +01:00
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
2023-11-16 02:35:05 +01:00
const ammoBoxesDetails = containerSettings.ammoBoxWhitelist.map((x) =>
{
const itemDetails = this.itemHelper.getItem(x);
return itemDetails[1];
});
2023-11-16 02:35:05 +01:00
// Need to find boxes that matches weapons caliber
const weaponCaliber = weaponDetailsDb._props.ammoCaliber;
2023-11-16 02:35:05 +01:00
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);
Merge branch 'master' into 3.8.0 # Conflicts: # project/.vscode/launch.json # project/assets/database/locations/bigmap/base.json # project/assets/database/locations/interchange/base.json # project/assets/database/locations/rezervbase/base.json # project/gulpfile.mjs # project/package.json # project/src/ErrorHandler.ts # project/src/Program.ts # project/src/callbacks/DataCallbacks.ts # project/src/callbacks/DialogueCallbacks.ts # project/src/callbacks/GameCallbacks.ts # project/src/callbacks/HandbookCallbacks.ts # project/src/callbacks/HealthCallbacks.ts # project/src/callbacks/HttpCallbacks.ts # project/src/callbacks/LauncherCallbacks.ts # project/src/callbacks/LocationCallbacks.ts # project/src/callbacks/MatchCallbacks.ts # project/src/callbacks/ModCallbacks.ts # project/src/callbacks/NotifierCallbacks.ts # project/src/callbacks/PresetCallbacks.ts # project/src/callbacks/ProfileCallbacks.ts # project/src/callbacks/RagfairCallbacks.ts # project/src/callbacks/TraderCallbacks.ts # project/src/context/ApplicationContext.ts # project/src/context/ContextVariableType.ts # project/src/controllers/BotController.ts # project/src/controllers/CustomizationController.ts # project/src/controllers/DialogueController.ts # project/src/controllers/GameController.ts # project/src/controllers/HealthController.ts # project/src/controllers/HideoutController.ts # project/src/controllers/InraidController.ts # project/src/controllers/InsuranceController.ts # project/src/controllers/InventoryController.ts # project/src/controllers/LauncherController.ts # project/src/controllers/LocationController.ts # project/src/controllers/MatchController.ts # project/src/controllers/QuestController.ts # project/src/controllers/RagfairController.ts # project/src/controllers/RepeatableQuestController.ts # project/src/controllers/TradeController.ts # project/src/di/Container.ts # project/src/di/Router.ts # project/src/generators/BotEquipmentModGenerator.ts # project/src/generators/BotLevelGenerator.ts # project/src/generators/BotWeaponGenerator.ts # project/src/generators/LocationGenerator.ts # project/src/generators/LootGenerator.ts # project/src/generators/RepeatableQuestGenerator.ts # project/src/generators/WeatherGenerator.ts # project/src/generators/weapongen/InventoryMagGen.ts # project/src/generators/weapongen/implementations/BarrelInventoryMagGen.ts # project/src/generators/weapongen/implementations/ExternalInventoryMagGen.ts # project/src/helpers/AssortHelper.ts # project/src/helpers/BotGeneratorHelper.ts # project/src/helpers/InRaidHelper.ts # project/src/helpers/ProfileHelper.ts # project/src/helpers/RagfairHelper.ts # project/src/helpers/RagfairOfferHelper.ts # project/src/helpers/TraderHelper.ts # project/src/loaders/ModLoadOrder.ts # project/src/loaders/PostDBModLoader.ts # project/src/loaders/PreAkiModLoader.ts # project/src/models/eft/common/IGlobals.ts # project/src/models/eft/common/ILocationBase.ts # project/src/models/eft/common/tables/IBotBase.ts # project/src/models/eft/common/tables/IProfileTemplate.ts # project/src/models/eft/common/tables/ITemplateItem.ts # project/src/models/eft/dialog/IAcceptFriendRequestData.ts # project/src/models/eft/dialog/IDeleteFriendRequest.ts # project/src/models/eft/game/IGameConfigResponse.ts # project/src/models/eft/game/IGameKeepAliveResponse.ts # project/src/models/eft/game/IGameStartResponse.ts # project/src/models/eft/match/IJoinMatchResult.ts # project/src/models/eft/notifier/INotifier.ts # project/src/models/eft/profile/GetProfileStatusResponseData.ts # project/src/models/eft/trade/IProcessBuyTradeRequestData.ts # project/src/models/eft/trade/IProcessSellTradeRequestData.ts # project/src/models/enums/WildSpawnTypeNumber.ts # project/src/models/spt/bots/BotGenerationDetails.ts # project/src/models/spt/config/IBotConfig.ts # project/src/models/spt/config/IBotDurability.ts # project/src/models/spt/config/IInRaidConfig.ts # project/src/models/spt/config/ILocationConfig.ts # project/src/models/spt/config/IQuestConfig.ts # project/src/models/spt/config/ISeasonalEventConfig.ts # project/src/models/spt/server/ILocations.ts # project/src/models/spt/utils/IUuidGenerator.ts # project/src/routers/dynamic/BotDynamicRouter.ts # project/src/routers/dynamic/BundleDynamicRouter.ts # project/src/routers/dynamic/CustomizationDynamicRouter.ts # project/src/routers/dynamic/DataDynamicRouter.ts # project/src/routers/dynamic/HttpDynamicRouter.ts # project/src/routers/dynamic/InraidDynamicRouter.ts # project/src/routers/dynamic/LocationDynamicRouter.ts # project/src/routers/dynamic/NotifierDynamicRouter.ts # project/src/routers/dynamic/TraderDynamicRouter.ts # project/src/routers/save_load/InsuranceSaveLoadRouter.ts # project/src/routers/save_load/ProfileSaveLoadRouter.ts # project/src/routers/serializers/NotifySerializer.ts # project/src/routers/static/BotStaticRouter.ts # project/src/routers/static/BundleStaticRouter.ts # project/src/routers/static/ClientLogStaticRouter.ts # project/src/routers/static/CustomizationStaticRouter.ts # project/src/routers/static/DataStaticRouter.ts # project/src/routers/static/DialogStaticRouter.ts # project/src/routers/static/GameStaticRouter.ts # project/src/routers/static/HealthStaticRouter.ts # project/src/routers/static/InraidStaticRouter.ts # project/src/routers/static/InsuranceStaticRouter.ts # project/src/routers/static/ItemEventStaticRouter.ts # project/src/routers/static/LauncherStaticRouter.ts # project/src/routers/static/LocationStaticRouter.ts # project/src/routers/static/MatchStaticRouter.ts # project/src/routers/static/NotifierStaticRouter.ts # project/src/routers/static/PresetStaticRouter.ts # project/src/routers/static/ProfileStaticRouter.ts # project/src/routers/static/QuestStaticRouter.ts # project/src/routers/static/RagfairStaticRouter.ts # project/src/routers/static/TraderStaticRouter.ts # project/src/routers/static/WeatherStaticRouter.ts # project/src/services/BotEquipmentFilterService.ts # project/src/services/BotGenerationCacheService.ts # project/src/services/BotWeaponModLimitService.ts # project/src/services/PaymentService.ts # project/src/services/ProfileFixerService.ts # project/src/services/RagfairOfferService.ts # project/src/services/RagfairTaxService.ts # project/src/services/RepairService.ts # project/src/services/SeasonalEventService.ts # project/src/utils/RagfairOfferHolder.ts # project/src/utils/TimeUtil.ts # project/src/utils/UUidGenerator.ts # project/src/utils/VFS.ts # project/src/utils/collections/queue/Queue.ts # project/src/utils/logging/AbstractWinstonLogger.ts # project/src/utils/logging/WinstonMainLogger.ts # project/src/utils/logging/WinstonRequestLogger.ts # project/tests/utils/TimeUtil.test.ts Manually resolved by Refringe.
2023-11-17 05:35:11 +01:00
rewards.push({ count: rewardCount, item_id: chosenAmmoBox._id, isPreset: false });
continue;
}
// Get all items of the desired type + not quest items + not globally blacklisted
2023-11-16 02:35:05 +01:00
const rewardItemPool = Object.values(this.databaseServer.getTables().templates.items).filter((x) =>
x._parent === rewardTypeId
&& x._type.toLowerCase() === "item"
&& !this.itemFilterService.isItemBlacklisted(x._id)
&& (!(containerSettings.allowBossItems || this.itemFilterService.isBossItem(x._id)))
2023-11-16 02:35:05 +01:00
&& !x._props.QuestItem
);
if (rewardItemPool.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(rewardItemPool);
2023-11-16 02:35:05 +01:00
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
*/
2023-11-16 02:35:05 +01:00
protected getSealedContainerWeaponModRewards(
containerSettings: ISealedAirdropContainerSettings,
linkedItemsToWeapon: ITemplateItem[],
chosenWeaponPreset: IPreset,
): 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;
}
2023-11-13 17:05:05 +01:00
// Get items that fulfil reward type criteria from items that fit on gun
2023-11-16 02:35:05 +01:00
const relatedItems = linkedItemsToWeapon.filter((x) =>
x._parent === rewardTypeId && !this.itemFilterService.isItemBlacklisted(x._id)
);
if (!relatedItems || relatedItems.length === 0)
{
2023-11-16 02:35:05 +01:00
this.logger.debug(
`No items found to fulfil reward type ${rewardTypeId} for weapon: ${chosenWeaponPreset._name}, skipping type`,
);
continue;
}
// Find a random item of the desired type and add as reward
2023-11-16 02:35:05 +01:00
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
2023-11-16 02:35:05 +01:00
* @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
2023-11-16 02:35:05 +01:00
const chosenRewardItemTpl = this.weightedRandomHelper.getWeightedValue<string>(
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
{
2023-11-16 02:35:05 +01:00
const existingItemIndex = resultsArray.findIndex((x) => x.item_id === itemTplToAdd);
if (existingItemIndex > -1)
{
// Exists in array already, increment count
resultsArray[existingItemIndex].count++;
}
else
{
2023-11-16 02:35:05 +01:00
resultsArray.push({ item_id: itemTplToAdd, count: 1, isPreset: false });
}
}
2023-11-16 02:35:05 +01:00
}