Cleanup of generateContainerLoot() to improve readability

This commit is contained in:
Dev 2023-07-02 15:05:32 +01:00
parent 63ac0d6fe2
commit d6fed1aa0f
3 changed files with 121 additions and 67 deletions

View File

@ -77,22 +77,22 @@ export class LocationController
output.Loot = []; output.Loot = [];
// mounted weapons // Mounted weapons
for (const mi of staticWeapons) for (const mi of staticWeapons)
{ {
output.Loot.push(mi); output.Loot.push(mi);
} }
let count = 0; let staticContainerCount = 0;
// static loot // static loot
for (const ci of staticContainers) for (const staticContainer of staticContainers)
{ {
const container = this.locationGenerator.generateContainerLoot(ci, staticForced, staticLootDist, staticAmmoDist, name); const container = this.locationGenerator.generateContainerLoot(staticContainer, staticForced, staticLootDist, staticAmmoDist, name);
output.Loot.push(container); output.Loot.push(container);
count++; staticContainerCount++;
} }
this.logger.success(this.localisationService.getText("location-containers_generated_success", count)); this.logger.success(this.localisationService.getText("location-containers_generated_success", staticContainerCount));
// dyanmic loot // dyanmic loot
const dynamicLootDist: ILooseLoot = this.jsonUtil.clone(location.looseLoot); const dynamicLootDist: ILooseLoot = this.jsonUtil.clone(location.looseLoot);

View File

@ -5,7 +5,7 @@ import { ItemHelper } from "../helpers/ItemHelper";
import { PresetHelper } from "../helpers/PresetHelper"; import { PresetHelper } from "../helpers/PresetHelper";
import { RagfairServerHelper } from "../helpers/RagfairServerHelper"; import { RagfairServerHelper } from "../helpers/RagfairServerHelper";
import { import {
ILooseLoot, Spawnpoint, SpawnpointsForced, SpawnpointTemplate ILooseLoot, Spawnpoint, SpawnpointTemplate, SpawnpointsForced
} from "../models/eft/common/ILooseLoot"; } from "../models/eft/common/ILooseLoot";
import { Item } from "../models/eft/common/tables/IItem"; import { Item } from "../models/eft/common/tables/IItem";
import { import {
@ -55,43 +55,142 @@ export class LocationGenerator
} }
/** /**
* Choose loot to put into a static container * Choose loot to put into a static container based on weighting
* @param containerIn * Handle forced items + seasonal item removal when not in season
* @param staticForced * @param staticContainer The container itself we will add loot to
* @param staticLootDist * @param staticForced Loot we need to force into the container
* @param staticAmmoDist * @param staticLootDist staticLoot.json
* @param staticAmmoDist staticAmmo.json
* @param locationName Name of the map to generate static loot for * @param locationName Name of the map to generate static loot for
* @returns IStaticContainerProps * @returns IStaticContainerProps
*/ */
public generateContainerLoot( public generateContainerLoot(
containerIn: IStaticContainerProps, staticContainer: IStaticContainerProps,
staticForced: IStaticForcedProps[], staticForced: IStaticForcedProps[],
staticLootDist: Record<string, IStaticLootDetails>, staticLootDist: Record<string, IStaticLootDetails>,
staticAmmoDist: Record<string, IStaticAmmoDetails[]>, staticAmmoDist: Record<string, IStaticAmmoDetails[]>,
locationName: string): IStaticContainerProps locationName: string): IStaticContainerProps
{ {
const container = this.jsonUtil.clone(containerIn); const container = this.jsonUtil.clone(staticContainer);
const containerTypeId = container.Items[0]._tpl; const containerTpl = container.Items[0]._tpl;
// Create new unique parent id to prevent any collisions
const parentId = this.objectId.generate(); const parentId = this.objectId.generate();
container.Root = parentId; container.Root = parentId;
container.Items[0]._id = parentId; container.Items[0]._id = parentId;
const containerTemplate = this.itemHelper.getItem(containerTypeId)[1]; let containerMap = this.getContainerMapping(containerTpl);
// Choose count of items to add to container
const itemCountToAdd = this.getWeightedCountOfContainerItems(containerTpl, staticLootDist, locationName);
// Get all possible loot items for container
const containerLootPool = this.getPossibleLootItemsForContainer(containerTpl, staticLootDist);
// Some containers need to have items forced into it (quest keys etc)
const tplsForced = staticForced.filter(x => x.containerId === container.Id).map(x => x.itemTpl);
// Draw random loot
// Money spawn more than once in container
let failedToFitCount = 0;
const locklist = [Money.ROUBLES, Money.DOLLARS, Money.EUROS];
// Choose items to add to container, factor in weighting + lock money down
const chosenTpls = containerLootPool.draw(itemCountToAdd, false, locklist);
// Add forced loot to chosen item pool
const tplsToAddToContainer = tplsForced.concat(chosenTpls);
for (const tplToAdd of tplsToAddToContainer)
{
const chosenItemWithChildren = this.createStaticLootItem(tplToAdd, staticAmmoDist, parentId);
const items = chosenItemWithChildren.items;
const width = chosenItemWithChildren.width;
const height = chosenItemWithChildren.height;
// look for open slot to put chosen item into
const result = this.containerHelper.findSlotForItem(containerMap, width, height);
if (!result.success)
{
// 2 attempts to fit an item, container is probably full, stop trying to add more
if (failedToFitCount >= this.locationConfig.fitLootIntoContainerAttempts)
{
break;
}
// Can't fit item, skip
failedToFitCount++;
continue;
}
containerMap = this.containerHelper.fillContainerMapWithItem(containerMap, result.x, result.y, width, height, result.rotation);
const rotation = result.rotation ? 1 : 0;
items[0].slotId = "main";
items[0].location = { "x": result.x, "y": result.y, "r": rotation };
// Add loot to container before returning
for (const item of items)
{
container.Items.push(item);
}
}
return container;
}
/**
* Get a 2d grid of a containers item slots
* @param containerTpl Tpl id of the container
* @returns number[][]
*/
protected getContainerMapping(containerTpl: string): number[][]
{
// Get template from db
const containerTemplate = this.itemHelper.getItem(containerTpl)[1];
// Get height/width
const height = containerTemplate._props.Grids[0]._props.cellsV; const height = containerTemplate._props.Grids[0]._props.cellsV;
const width = containerTemplate._props.Grids[0]._props.cellsH; const width = containerTemplate._props.Grids[0]._props.cellsH;
let container2D: number[][] = Array(height).fill(0).map(() => Array(width).fill(0));
// Calcualte 2d array and return
return Array(height).fill(0).map(() => Array(width).fill(0));
}
/**
* Look up a containers itemcountDistribution data and choose an item count based on the found weights
* @param containerTypeId Container to get item count for
* @param staticLootDist staticLoot.json
* @param locationName Map name (to get per-map multiplier for from config)
* @returns item count
*/
protected getWeightedCountOfContainerItems(containerTypeId: string, staticLootDist: Record<string, IStaticLootDetails>, locationName: string): number
{
// Create probability array to calcualte the total count of lootable items inside container
const itemCountArray = new ProbabilityObjectArray<number>(this.mathUtil); const itemCountArray = new ProbabilityObjectArray<number>(this.mathUtil);
for (const icd of staticLootDist[containerTypeId].itemcountDistribution) for (const itemCountDistribution of staticLootDist[containerTypeId].itemcountDistribution)
{ {
// Add each count of items into array
itemCountArray.push( itemCountArray.push(
new ProbabilityObject(icd.count, icd.relativeProbability) new ProbabilityObject(itemCountDistribution.count, itemCountDistribution.relativeProbability)
); );
} }
const numberItems = Math.round(this.getStaticLootMultiplerForLocation(locationName) * itemCountArray.draw()[0]);
return Math.round(this.getStaticLootMultiplerForLocation(locationName) * itemCountArray.draw()[0]);
}
/**
* Get all possible loot items that can be placed into a container
* Do not add seasonal items if found + current date is inside seasonal event
* @param containerTypeId Contianer to get possible loot for
* @param staticLootDist staticLoot.json
* @returns ProbabilityObjectArray of item tpls + probabilty
*/
protected getPossibleLootItemsForContainer(containerTypeId: string, staticLootDist: Record<string, IStaticLootDetails>): ProbabilityObjectArray<string, number>
{
const seasonalEventActive = this.seasonalEventService.seasonalEventEnabled(); const seasonalEventActive = this.seasonalEventService.seasonalEventEnabled();
const seasonalItemTplBlacklist = this.seasonalEventService.getSeasonalEventItemsToBlock(); const seasonalItemTplBlacklist = this.seasonalEventService.getSeasonalEventItemsToBlock();
const itemDistribution = new ProbabilityObjectArray<string>(this.mathUtil); const itemDistribution = new ProbabilityObjectArray<string>(this.mathUtil);
for (const icd of staticLootDist[containerTypeId].itemDistribution) for (const icd of staticLootDist[containerTypeId].itemDistribution)
{ {
@ -106,52 +205,7 @@ export class LocationGenerator
); );
} }
// Get forced container loot tpls return itemDistribution;
const tplsForced = staticForced.filter(x => x.containerId === container.Id).map(x => x.itemTpl);
// Draw random loot
// money spawn more than once in container
let failedToFitCount = 0;
const locklist = [Money.ROUBLES, Money.DOLLARS, Money.EUROS];
const tplsDraw = itemDistribution.draw(numberItems, false, locklist);
const tpls = tplsForced.concat(tplsDraw);
for (const tpl of tpls)
{
const chosenItemWithChildren = this.createStaticLootItem(tpl, staticAmmoDist, parentId);
const items = chosenItemWithChildren.items;
const width = chosenItemWithChildren.width;
const height = chosenItemWithChildren.height;
// look for open slot to put chosen item into
const result = this.containerHelper.findSlotForItem(container2D, width, height);
if (!result.success)
{
// 2 attempts to fit an item, container is probably full, stop trying to add more
if (failedToFitCount >= this.locationConfig.fitLootIntoContainerAttempts)
{
break;
}
// Can't fit item, skip
failedToFitCount++;
continue;
}
container2D = this.containerHelper.fillContainerMapWithItem(container2D, result.x, result.y, width, height, result.rotation);
const rot = result.rotation ? 1 : 0;
items[0].slotId = "main";
items[0].location = { "x": result.x, "y": result.y, "r": rot };
for (const item of items)
{
container.Items.push(item);
}
}
return container;
} }
protected getLooseLootMultiplerForLocation(location: string): number protected getLooseLootMultiplerForLocation(location: string): number

View File

@ -98,7 +98,7 @@ export class ContainerHelper
return new FindSlotResult(); return new FindSlotResult();
} }
public fillContainerMapWithItem(container2D: number[][], x: number, y: number, itemW: number, itemH: number, rotate: boolean): any public fillContainerMapWithItem(container2D: number[][], x: number, y: number, itemW: number, itemH: number, rotate: boolean): number[][]
{ {
const itemWidth = rotate ? itemH : itemW; const itemWidth = rotate ? itemH : itemW;
const itemHeight = rotate ? itemW : itemH; const itemHeight = rotate ? itemW : itemH;