create_kit.js

import { augment } from "./create_bag.js";
import { createGender } from "./create_character.js";
import { createItem } from "./create_item.js";
import { createProfession, getPostProfessions } from "./create_profession.js";
import { getConditionGroup } from "./condition.js";
import { logger } from "./utils.js";
import { roll, random, shuffle, test } from "./random_utils.js";
import { TRAITS as T } from "./constants.js";
import Bag from "./models/bag.js";
import Kit from "./models/kit.js";

// TODO: Use these so they git picked up in the generator configs
const EQUIP_OPTIONS = Object.freeze(["None", "Patrol", "Travel"]);

export const getKitOptions = () => EQUIP_OPTIONS;

function add(bag, tags, count = 1) {
  for (let i = 0; i < count; i++) {
    let item = createItem({ tags });
    if (item) {
      bag.add(item, 1);
      return true;
    }
    return false;
  }
}

function addUntil(bag, ...allTagArrays) {
  shuffle(allTagArrays);
  for (let i = 0; i < allTagArrays.length; i++) {
    let tagArray = allTagArrays[i];
    let haveAdded = false;
    for (let j = 0; j < tagArray.length; j++) {
      let item = createItem({ tags: tagArray[j] });
      if (item) {
        bag.add(item, 1);
        haveAdded = true;
      }
    }
    if (haveAdded) {
      return;
    }
  }
}

/**
 * Generate the possessions that would be on the person of an active NPC (e.g.
 * out on patrol, out for a night on the town, out on a raid or in the middle
 * of criminal activity).
 *
 * @param params {Object}
 *   @param [params.profession] {String|Profession} profession name or instance
 *   @param [params.preProfession] {String|Profession} profession name or instance
 *   @param [params.gender] {String} gender of character (male or female)
 *   @param [params.kit] {String} One of three options, “None” (clothing only);
 *    “Patrol” (weapons only); and “Travel” (the full amount of equipment a person
 *    would carry to explore or travel).
 *
 * @return {Object} An object of two bags, one under the property "clothing" and another
 *    under the bag of "possessions".
 */
export function createKit({
  profession = random(getPostProfessions()),
  gender = createGender(),
  kit = random(EQUIP_OPTIONS),
} = {}) {
  logger.start("createKit", { profession, gender, kit });

  const clothing = new Bag();
  const possessions = new Bag();
  const selGender = gender.toLowerCase();
  const profConfig = createProfession({ name: profession });
  const kitTag = profConfig.kit;

  addUntil(
    clothing,
    [`post clothing body -armor kit:${kitTag} (${selGender} | unisex)`],
    [
      `post clothing torso -armor -coat kit:${kitTag} (${selGender} | unisex)`,
      `post clothing legs -armor kit:${kitTag} (${selGender} | unisex)`,
    ],
    [`post clothing body -armor -kit (${selGender} | unisex)`],
    [
      `post clothing torso -armor -coat -kit (${selGender} | unisex)`,
      `post clothing legs -armor -coat -kit (${selGender} | unisex)`,
    ],
  );
  // shoes
  addUntil(
    clothing,
    [`post clothing feet -kit (${selGender} | unisex)`],
    [`post clothing feet kit:${kitTag} (${selGender} | unisex)`],
  );
  // hat
  if (test(80)) {
    addUntil(
      clothing,
      [`post clothing head -armor -kit (${selGender} | unisex)`],
      [`post clothing head -armor kit:${kitTag} (${selGender} | unisex)`],
    );
  }

  // weapons and ammo. people don't carry broken weapons
  if (kit !== "None") {
    const condition = random(getConditionGroup({ group: "U" })).name;
    const firearm = createItem({ tags: `firearm`, condition });
    firearm.tags.delete("br");
    const melee = createItem({ tags: `melee` });
    const result = roll("1d10");
    if (result < 4) {
      augment(possessions, firearm, profession);
    } else if (result < 8) {
      possessions.add(melee);
      augment(possessions, firearm, profession);
    } else {
      possessions.add(melee);
    }
    if (!profConfig.tags.has("prestige:high") && test(20)) {
      addUntil(possessions, [`armor head -kit`]);
      addUntil(possessions, [`armor (body | torso) -kit`]);
    }
    // criminal, cowboy, science, military, worker, doctor, guard, gang, scavenger
    if (kit) {
      add(possessions, `kit:${kitTag}`);
    }
  }
  if (kit === "Travel") {
    // TODO: separate trinkets and accessories, accessories go under clothing
    add(possessions, `accessory`, roll("1d4-2"));
    add(possessions, `food -candy (preserved | ration | fruit)`, roll("2d3"));
    add(possessions, `travel`, roll("1d4-1"));
    // horses, motorcycles, pets are handled under encounters, where the can be more complex
    // intersections of people, transport, and animals
  }
  return logger.end(
    new Kit({
      clothing,
      possessions,
      profession: random(profConfig.names),
      gender: selGender,
    }),
  );
}