create_item.js

import { CONDITION_MULTIPLIER } from "./constants.js";
import { createBookTitle } from "./create_book_title.js";
import { createHorseDescription } from "./create_horse_description.js";
import { createMagazineTitle } from "./create_magazine_title.js";
import { lineLoader } from "./data_loaders.js";
import { logger } from "./utils.js";
import { MEMORABILIA } from "./create_memorabilia.js";
import { test } from "./random_utils.js";
import Item from "./models/item.js";
import ItemDatabase from "./db/item_database.js";
import RarityTable from "./tables/rarity_table.js";

export const CONDITIONS = new RarityTable()
  .add("uncommon", "poor")
  .add("common", "fair")
  .add("common", "good")
  .add("uncommon", "excellent")
  .add("rare", "mint");

// 10mm, 5.56, 5mm
// Cigarette brands: Chelsea, Chesterfield, Craven, Lucky Strike, Old Gold, Raleigh, and Wings.

const itemDb = new ItemDatabase();

const items = await lineLoader("items.data");
itemDb.add.apply(itemDb, items);
MEMORABILIA.forEach((memo) => itemDb.table.add(memo.frequency, memo));

export { itemDb };

/**
 * Creates a random item from the item database, optionally filtered by name, tags, value range,
 * or encumbrance range. Automatically generates titles for books, magazines, and horses.
 *
 * @param {Object} [options={}] - Item creation options
 * @param {string} [options.name] - Specific item name to look up. If not provided, an item is selected by tags.
 * @param {string} [options.tags="*"] - Tag expression to filter items (e.g. "firearm", "ammo:22",
 *    "food -luxury"). Supports the full tag expression syntax including `|`, space (AND), and parentheses.
 * @param {number} [options.minValue=0] - Minimum item value (in caps).
 * @param {number} [options.maxValue=Number.MAX_VALUE] - Maximum item value (in caps).
 * @param {number} [options.minEnc=0] - Minimum item encumbrance.
 * @param {number} [options.maxEnc=Number.MAX_VALUE] - Maximum item encumbrance.
 * @param {string} [options.condition] - Item condition: "poor", "fair", "good", "excellent", or "mint".
 *    Affects value for items that can break. If not provided, randomly selected by rarity.
 * @returns {Item} An Item instance matching the given criteria, or null if no match is found
 */
export function createItem({
  name,
  tags = "*",
  minValue = 0,
  maxValue = Number.MAX_VALUE,
  minEnc = 0,
  maxEnc = Number.MAX_VALUE,
  condition = CONDITIONS.get(),
} = {}) {
  logger.start("createItem", { name, tags, minValue, maxValue, minEnc, maxEnc, condition });
  // items from the database are archetypes, so some qualities are determined per instance,
  // such as condition, breakage, and flavor text
  let item = itemDb.findOne({ name, tags, minValue, maxValue, minEnc, maxEnc });
  if (!item) {
    console.warn(
      "No item found for terms: " +
        JSON.stringify({ name, tags, minValue, maxValue, minEnc, maxEnc }),
    );
  } else if (item.name === "horse") {
    item = new Item({ ...item, title: createHorseDescription() });
  } else if (item.has("book") && !item.title && !item.series.title && item.is("titled")) {
    item = new Item({ ...item, title: createBookTitle() });
  } else if (item.has("magazine") && item.not("collectible") && item.is("titled")) {
    item = new Item({ ...item, title: createMagazineTitle() });
  } else if (item.has("br")) {
    item.tags.delete("br");
    item.condition = condition;
    item.value = item.value * CONDITION_MULTIPLIER[item.condition];
    if (["poor", "fair"].includes(condition) && test(75)) {
      item.tags.add("br");
    }
  }
  return logger.end(item);
}