create_container.js

import { bagSpecParser, parseDelimiters, splitOn, splitOnLast } from "./string_utils.js";
import { createBag } from "./create_bag.js";
import { createItem } from "./create_item.js";
import { logger } from "./utils.js";
import { random, roll, test } from "./random_utils.js";
import { ymlParser } from "./data_loaders.js";
import Bag from "./models/bag.js";

async function parseContainers() {
  const config = {};
  const parser = ymlParser({
    keyProp: "name",
    lists: ["desc", "items"],
  });
  const data = await parser("containers.data");
  for (const entry of data) {
    if (entry.desc) {
      entry.description = entry.desc;
      delete entry.desc;
    }
    if (entry.items) {
      entry.items = entry.items.reduce((acc, item) => {
        let [itemTag, itemCount] = splitOnLast(item, " ");
        acc[itemTag] = parseInt(itemCount);
        return acc;
      }, {});
    }
    let [hasDelimiters, nameStr, usedStr] = parseDelimiters(entry.name);
    entry.name = nameStr;
    if (hasDelimiters) {
      let [percent, text] = splitOn(usedStr, " ");
      entry.qualPercent = parseInt(percent.replace("%", ""));
      entry.qual = text;
    }
    config[entry.name] = entry;
  }
  return config;
}

const CONFIG = await parseContainers();

const CONTAINER_TYPES = Object.keys(CONFIG);

export function getContainerTypes() {
  return CONTAINER_TYPES;
}

function getCount(isUsed, maxCount) {
  if (isUsed && maxCount === 1) {
    return test(70) ? 1 : 0;
  }
  return isUsed ? roll(maxCount) : maxCount;
}

/**
 * Creates a container (e.g., a first aid kit, cabinet, or ammunition box) of a given type,
 * potentially pre-filled with items. Containers may be empty or show signs of prior use.
 *
 * @param {Object} [options={}] - Container creation options
 * @param {string} [options.type] - Type of container to create, selected from the values returned by
 *    `getContainerTypes()`. If not provided, a type is randomly selected.
 * @returns {Bag} A new Bag instance (extends Bag) with a name, optional description, and items
 */
export function createContainer({ type = random(CONTAINER_TYPES) } = {}) {
  logger.start("createContainer", { type });
  if (!CONFIG[type]) {
    throw new Error('Invalid container type: "' + type + '"');
  }
  let config = CONFIG[type];
  let isUsed = config.qual === "used" && test(config.qualPercent ?? 0);
  let isEmpty = config.qual === "empty" && test(config.qualPercent ?? 0);
  let descParts = [
    config.description ? random(config.description) : undefined,
    isUsed ? "used" : undefined,
  ];
  let description = descParts.filter(Boolean).join(" ") || undefined;
  let bag = new Bag({ name: config.name, description });
  if (!isEmpty) {
    if (config.query) {
      let bagConfig = bagSpecParser(config.query);
      bag = createBag({ ...bagConfig, name: config.name, description });
    } else if (config.items) {
      bag = new Bag({ name: config.name, description });
      for (let tags of Object.keys(config.items)) {
        let maxCount = config.items[tags];
        let item = createItem({ tags });
        let count = getCount(isUsed, maxCount);
        bag.add(item, count);
      }
    }
  }
  return logger.end(bag);
}