Added system to randomise the mods on equipment found as loose/static loot
controlled via config
This commit is contained in:
parent
f227872ddc
commit
e27f52c505
@ -1051,5 +1051,12 @@
|
|||||||
"adjustWaves": true
|
"adjustWaves": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"armorLootSettings": {
|
||||||
|
"modSpawnChancePercent": {
|
||||||
|
"mod_nvg": 20,
|
||||||
|
"front_plate": 50,
|
||||||
|
"back_plate": 50
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
IStaticForcedProps,
|
IStaticForcedProps,
|
||||||
IStaticLootDetails,
|
IStaticLootDetails,
|
||||||
} from "@spt-aki/models/eft/common/tables/ILootBase";
|
} from "@spt-aki/models/eft/common/tables/ILootBase";
|
||||||
|
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
||||||
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
|
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
|
||||||
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
||||||
import { Money } from "@spt-aki/models/enums/Money";
|
import { Money } from "@spt-aki/models/enums/Money";
|
||||||
@ -23,6 +24,7 @@ import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
|||||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
||||||
import { LocalisationService } from "@spt-aki/services/LocalisationService";
|
import { LocalisationService } from "@spt-aki/services/LocalisationService";
|
||||||
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
|
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
|
||||||
|
import { HashUtil } from "@spt-aki/utils/HashUtil";
|
||||||
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
||||||
import { MathUtil } from "@spt-aki/utils/MathUtil";
|
import { MathUtil } from "@spt-aki/utils/MathUtil";
|
||||||
import { ObjectId } from "@spt-aki/utils/ObjectId";
|
import { ObjectId } from "@spt-aki/utils/ObjectId";
|
||||||
@ -52,6 +54,7 @@ export class LocationGenerator
|
|||||||
@inject("WinstonLogger") protected logger: ILogger,
|
@inject("WinstonLogger") protected logger: ILogger,
|
||||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||||
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||||
@inject("ObjectId") protected objectId: ObjectId,
|
@inject("ObjectId") protected objectId: ObjectId,
|
||||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||||
@inject("RagfairServerHelper") protected ragfairServerHelper: RagfairServerHelper,
|
@inject("RagfairServerHelper") protected ragfairServerHelper: RagfairServerHelper,
|
||||||
@ -812,6 +815,7 @@ export class LocationGenerator
|
|||||||
{
|
{
|
||||||
const chosenItem = spawnPoint.template.Items.find((x) => x._id === chosenComposedKey);
|
const chosenItem = spawnPoint.template.Items.find((x) => x._id === chosenComposedKey);
|
||||||
const chosenTpl = chosenItem._tpl;
|
const chosenTpl = chosenItem._tpl;
|
||||||
|
const itemTemplate = this.itemHelper.getItem(chosenTpl)[1];
|
||||||
|
|
||||||
// Item array to return
|
// Item array to return
|
||||||
const itemWithMods: Item[] = [];
|
const itemWithMods: Item[] = [];
|
||||||
@ -822,7 +826,7 @@ export class LocationGenerator
|
|||||||
|| this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.AMMO)
|
|| this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.AMMO)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
const itemTemplate = this.itemHelper.getItem(chosenTpl)[1];
|
|
||||||
|
|
||||||
const stackCount = itemTemplate._props.StackMaxSize === 1
|
const stackCount = itemTemplate._props.StackMaxSize === 1
|
||||||
? 1
|
? 1
|
||||||
@ -837,25 +841,30 @@ export class LocationGenerator
|
|||||||
else if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.AMMO_BOX))
|
else if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.AMMO_BOX))
|
||||||
{
|
{
|
||||||
// Fill with cartridges
|
// Fill with cartridges
|
||||||
const ammoBoxTemplate = this.itemHelper.getItem(chosenTpl)[1];
|
|
||||||
const ammoBoxItem: Item[] = [{ _id: this.objectId.generate(), _tpl: chosenTpl }];
|
const ammoBoxItem: Item[] = [{ _id: this.objectId.generate(), _tpl: chosenTpl }];
|
||||||
this.itemHelper.addCartridgesToAmmoBox(ammoBoxItem, ammoBoxTemplate);
|
this.itemHelper.addCartridgesToAmmoBox(ammoBoxItem, itemTemplate); // ammo box template
|
||||||
itemWithMods.push(...ammoBoxItem);
|
itemWithMods.push(...ammoBoxItem);
|
||||||
}
|
}
|
||||||
else if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.MAGAZINE))
|
else if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.MAGAZINE))
|
||||||
{
|
{
|
||||||
// Create array with just magazine + randomised amount of cartridges
|
// Create array with just magazine + randomised amount of cartridges
|
||||||
const magazineTemplate = this.itemHelper.getItem(chosenTpl)[1];
|
|
||||||
const magazineItem: Item[] = [{ _id: this.objectId.generate(), _tpl: chosenTpl }];
|
const magazineItem: Item[] = [{ _id: this.objectId.generate(), _tpl: chosenTpl }];
|
||||||
this.itemHelper.fillMagazineWithRandomCartridge(
|
this.itemHelper.fillMagazineWithRandomCartridge(
|
||||||
magazineItem,
|
magazineItem,
|
||||||
magazineTemplate,
|
itemTemplate, // Magazine template
|
||||||
staticAmmoDist,
|
staticAmmoDist,
|
||||||
null,
|
null,
|
||||||
this.locationConfig.minFillLooseMagazinePercent / 100,
|
this.locationConfig.minFillLooseMagazinePercent / 100,
|
||||||
);
|
);
|
||||||
itemWithMods.push(...magazineItem);
|
itemWithMods.push(...magazineItem);
|
||||||
}
|
}
|
||||||
|
else if (this.itemHelper.isOfBaseclasses(chosenTpl, [BaseClasses.VEST, BaseClasses.ARMOR, BaseClasses.HEADWEAR]))
|
||||||
|
{
|
||||||
|
if (itemTemplate._props.Slots?.length > 0)
|
||||||
|
{
|
||||||
|
this.addModsToEquipmentItem(itemWithMods, itemTemplate)
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Get item + children and add into array we return
|
// Get item + children and add into array we return
|
||||||
@ -917,15 +926,15 @@ export class LocationGenerator
|
|||||||
|
|
||||||
// TODO: rewrite, BIG yikes
|
// TODO: rewrite, BIG yikes
|
||||||
protected createStaticLootItem(
|
protected createStaticLootItem(
|
||||||
tpl: string,
|
chosenTpl: string,
|
||||||
staticAmmoDist: Record<string, IStaticAmmoDetails[]>,
|
staticAmmoDist: Record<string, IStaticAmmoDetails[]>,
|
||||||
parentId: string = undefined,
|
parentId: string = undefined,
|
||||||
): IContainerItem
|
): IContainerItem
|
||||||
{
|
{
|
||||||
const itemTemplate = this.itemHelper.getItem(tpl)[1];
|
const itemTemplate = this.itemHelper.getItem(chosenTpl)[1];
|
||||||
let width = itemTemplate._props.Width;
|
let width = itemTemplate._props.Width;
|
||||||
let height = itemTemplate._props.Height;
|
let height = itemTemplate._props.Height;
|
||||||
let items: Item[] = [{ _id: this.objectId.generate(), _tpl: tpl }];
|
let items: Item[] = [{ _id: this.objectId.generate(), _tpl: chosenTpl }];
|
||||||
|
|
||||||
// Use passed in parentId as override for new item
|
// Use passed in parentId as override for new item
|
||||||
if (parentId)
|
if (parentId)
|
||||||
@ -934,8 +943,8 @@ export class LocationGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.itemHelper.isOfBaseclass(tpl, BaseClasses.MONEY)
|
this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.MONEY)
|
||||||
|| this.itemHelper.isOfBaseclass(tpl, BaseClasses.AMMO)
|
|| this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.AMMO)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Edge case - some ammos e.g. flares or M406 grenades shouldn't be stacked
|
// Edge case - some ammos e.g. flares or M406 grenades shouldn't be stacked
|
||||||
@ -945,10 +954,10 @@ export class LocationGenerator
|
|||||||
items[0].upd = { StackObjectsCount: stackCount };
|
items[0].upd = { StackObjectsCount: stackCount };
|
||||||
}
|
}
|
||||||
// No spawn point, use default template
|
// No spawn point, use default template
|
||||||
else if (this.itemHelper.isOfBaseclass(tpl, BaseClasses.WEAPON))
|
else if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.WEAPON))
|
||||||
{
|
{
|
||||||
let children: Item[] = [];
|
let children: Item[] = [];
|
||||||
const defaultPreset = this.jsonUtil.clone(this.presetHelper.getDefaultPreset(tpl));
|
const defaultPreset = this.jsonUtil.clone(this.presetHelper.getDefaultPreset(chosenTpl));
|
||||||
if (defaultPreset)
|
if (defaultPreset)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -962,7 +971,7 @@ export class LocationGenerator
|
|||||||
// 5ba26383d4351e00334c93d9 //mp7_devgru
|
// 5ba26383d4351e00334c93d9 //mp7_devgru
|
||||||
this.logger.warning(
|
this.logger.warning(
|
||||||
this.localisationService.getText("location-preset_not_found", {
|
this.localisationService.getText("location-preset_not_found", {
|
||||||
tpl: tpl,
|
tpl: chosenTpl,
|
||||||
defaultId: defaultPreset._id,
|
defaultId: defaultPreset._id,
|
||||||
defaultName: defaultPreset._name,
|
defaultName: defaultPreset._name,
|
||||||
parentId: parentId,
|
parentId: parentId,
|
||||||
@ -975,14 +984,14 @@ export class LocationGenerator
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// RSP30 (62178be9d0050232da3485d9/624c0b3340357b5f566e8766/6217726288ed9f0845317459) doesnt have any default presets and kills this code below as it has no chidren to reparent
|
// RSP30 (62178be9d0050232da3485d9/624c0b3340357b5f566e8766/6217726288ed9f0845317459) doesnt have any default presets and kills this code below as it has no chidren to reparent
|
||||||
this.logger.debug(`createItem() No preset found for weapon: ${tpl}`);
|
this.logger.debug(`createItem() No preset found for weapon: ${chosenTpl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootItem = items[0];
|
const rootItem = items[0];
|
||||||
if (!rootItem)
|
if (!rootItem)
|
||||||
{
|
{
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
this.localisationService.getText("location-missing_root_item", { tpl: tpl, parentId: parentId }),
|
this.localisationService.getText("location-missing_root_item", { tpl: chosenTpl, parentId: parentId }),
|
||||||
);
|
);
|
||||||
|
|
||||||
throw new Error(this.localisationService.getText("location-critical_error_see_log"));
|
throw new Error(this.localisationService.getText("location-critical_error_see_log"));
|
||||||
@ -999,7 +1008,7 @@ export class LocationGenerator
|
|||||||
{
|
{
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
this.localisationService.getText("location-unable_to_reparent_item", {
|
this.localisationService.getText("location-unable_to_reparent_item", {
|
||||||
tpl: tpl,
|
tpl: chosenTpl,
|
||||||
parentId: parentId,
|
parentId: parentId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -1016,7 +1025,7 @@ export class LocationGenerator
|
|||||||
if (magazine)
|
if (magazine)
|
||||||
{
|
{
|
||||||
const magTemplate = this.itemHelper.getItem(magazine._tpl)[1];
|
const magTemplate = this.itemHelper.getItem(magazine._tpl)[1];
|
||||||
const weaponTemplate = this.itemHelper.getItem(tpl)[1];
|
const weaponTemplate = this.itemHelper.getItem(chosenTpl)[1];
|
||||||
|
|
||||||
// Create array with just magazine
|
// Create array with just magazine
|
||||||
const magazineWithCartridges = [magazine];
|
const magazineWithCartridges = [magazine];
|
||||||
@ -1036,11 +1045,11 @@ export class LocationGenerator
|
|||||||
height = size.height;
|
height = size.height;
|
||||||
}
|
}
|
||||||
// No spawnpoint to fall back on, generate manually
|
// No spawnpoint to fall back on, generate manually
|
||||||
else if (this.itemHelper.isOfBaseclass(tpl, BaseClasses.AMMO_BOX))
|
else if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.AMMO_BOX))
|
||||||
{
|
{
|
||||||
this.itemHelper.addCartridgesToAmmoBox(items, itemTemplate);
|
this.itemHelper.addCartridgesToAmmoBox(items, itemTemplate);
|
||||||
}
|
}
|
||||||
else if (this.itemHelper.isOfBaseclass(tpl, BaseClasses.MAGAZINE))
|
else if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.MAGAZINE))
|
||||||
{
|
{
|
||||||
// Create array with just magazine
|
// Create array with just magazine
|
||||||
const magazineWithCartridges = [items[0]];
|
const magazineWithCartridges = [items[0]];
|
||||||
@ -1055,7 +1064,49 @@ export class LocationGenerator
|
|||||||
// Replace existing magazine with above array
|
// Replace existing magazine with above array
|
||||||
items.splice(items.indexOf(items[0]), 1, ...magazineWithCartridges);
|
items.splice(items.indexOf(items[0]), 1, ...magazineWithCartridges);
|
||||||
}
|
}
|
||||||
|
else if (this.itemHelper.isOfBaseclasses(chosenTpl, [BaseClasses.VEST, BaseClasses.ARMOR]))
|
||||||
|
{
|
||||||
|
if (itemTemplate._props.Slots?.length > 0)
|
||||||
|
{
|
||||||
|
this.addModsToEquipmentItem(items, itemTemplate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { items: items, width: width, height: height };
|
return {
|
||||||
|
items: items,
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add mod componenets to an equipment item (head/rig/armor)
|
||||||
|
* @param modItem
|
||||||
|
* @param itemTemplate
|
||||||
|
*/
|
||||||
|
protected addModsToEquipmentItem(modItem: Item[], itemTemplate: ITemplateItem): void
|
||||||
|
{
|
||||||
|
// Add armor plates
|
||||||
|
for (const slot of itemTemplate._props.Slots)
|
||||||
|
{
|
||||||
|
// Check if mod has % chance to be added
|
||||||
|
const modSpawnChance = this.locationConfig.equipmentLootSettings.modSpawnChancePercent[slot._name.toLowerCase()];
|
||||||
|
if (modSpawnChance && !slot._required)
|
||||||
|
{
|
||||||
|
// only run chance to not add item if its not a required mod
|
||||||
|
if (this.randomUtil.getChance100(modSpawnChance))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modItem.push(
|
||||||
|
{
|
||||||
|
_id: this.hashUtil.generate(),
|
||||||
|
_tpl: this.randomUtil.getArrayValue(slot._props.filters[0].Filter), // Choose random tpl from array of compatible
|
||||||
|
parentId: modItem[0]._id,
|
||||||
|
slotId: slot._name
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,6 +462,9 @@ export interface SlotProps
|
|||||||
export interface SlotFilter
|
export interface SlotFilter
|
||||||
{
|
{
|
||||||
Shift?: number;
|
Shift?: number;
|
||||||
|
locked?: boolean;
|
||||||
|
armorColliders?: any[];
|
||||||
|
armorPlateColliders?: string[];
|
||||||
Filter: string[];
|
Filter: string[];
|
||||||
AnimationIndex?: number;
|
AnimationIndex?: number;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,14 @@ export interface ILocationConfig extends IBaseConfig
|
|||||||
looseLootBlacklist: Record<string, string[]>;
|
looseLootBlacklist: Record<string, string[]>;
|
||||||
/** Key: map, value: settings to control how long scav raids are*/
|
/** Key: map, value: settings to control how long scav raids are*/
|
||||||
scavRaidTimeSettings: IScavRaidTimeSettings;
|
scavRaidTimeSettings: IScavRaidTimeSettings;
|
||||||
|
/** Settings to adjust mods for lootable equipment in raid */
|
||||||
|
equipmentLootSettings: IEquipmentLootSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEquipmentLootSettings
|
||||||
|
{
|
||||||
|
// Percentage chance item will be added to equipment
|
||||||
|
modSpawnChancePercent: Record<string, number>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFixEmptyBotWavesSettings
|
export interface IFixEmptyBotWavesSettings
|
||||||
|
Loading…
Reference in New Issue
Block a user