create_kit.js

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

const looseAmmo = {
  common: "8d8",
  uncommon: "4d8",
  rare: "3d6",
};

function addAmmo(bag, firearm) {
  const ammoType = firearm.consumes;
  const bundledAmmo = createItem({ tags: `ammo ${ammoType} bundled` });
  if (test(20) && bundledAmmo) {
    bag.add(bundledAmmo, roll("2d3"));
  } else {
    const ammo = createItem({ tags: `ammo ${ammoType} -bundled` });
    bag.add(ammo, roll(looseAmmo[ammo.frequency]));
  }
  if (firearm.is("pistol") && test("60")) {
    const holster = createItem({ tags: `holster` });
    bag.add(holster);
  }
}

function add(bag, tags, count = 1) {
  for (let i = 0; i < count; i++) {
    let item = createItem({ tags });
    if (item) {
      bag.add(item, 1);
    } else {
      console.warn("No item matches any of these tags: " + tags);
    }
  }
}

/**
 * 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.gender] {String} gender of character (male or female)
 *   @param [params.traveling] {Boolean} should this bag include extended equipment
 *    for exploring, and not just walk-abouts?
 *
 * @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(),
  traveling = false,
} = {}) {
  logger.start("createKit", { profession, gender, traveling });

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

  // clothes. stuff isn't tagged by profession
  if (test(20)) {
    add(clothing, `clothing body -armor -kit (${selGender} | unisex)`);
  } else {
    add(clothing, `clothing torso -armor -coat -kit (${selGender} | unisex)`);
    add(clothing, `clothing legs -armor -kit (${selGender} | unisex)`);
  }
  // shoes
  add(clothing, `clothing feet -kit (${selGender} | unisex)`);

  // hat
  if (test(60)) {
    add(clothing, `clothing head -kit (${selGender} | unisex)`);
  } else if (test(20)) {
    add(possessions, `armor head -kit`);
  }
  if (test(20)) {
    add(possessions, `armor (body | torso) -kit`);
  }

  // weapons and ammo. people don't carry broken weapons
  const condition = random(["fair", "good", "good", "good", "excellent"]);
  const firearm = createItem({ tags: `firearm`, condition });
  const melee = createItem({ tags: `melee` });
  const result = roll("1d10");
  if (result < 4) {
    possessions.add(firearm);
    addAmmo(possessions, firearm);
  } else if (result < 8) {
    possessions.add(melee);
    possessions.add(firearm);
    addAmmo(possessions, firearm);
  } else {
    possessions.add(melee);
  }

  // military, worker, doctor, guard, gang, cowboy
  if (kit) {
    add(possessions, `kit:${kit}`);
  }
  if (traveling) {
    // TODO: separate trinkets and accessories, accessories go under clothing
    // add(possessions, `trinket`, roll("1d4-2"));

    add(possessions, `food -candy (preserved | ration | fruit)`, roll("2d3"));

    add(possessions, `travel`, roll("1d4-1"));
    // Not entirely happy with the horse as a possession. The horse can carry stuff and probably
    // would be carrying some stuff like feed, there’s no way to describe this, nor the relationship
    // to the character. What if they have two horses, or six?
    createTransport({ bag: possessions, profConfig });
  }
  return logger.end({
    clothing,
    possessions,
    gender: selGender,
    profession: random(profConfig.names),
  });
}

function hasTrait(prof, trait) {
  return prof.seeds.includes(trait) || prof.traits.includes(trait);
}

function createTransport({ bag, profConfig } = {}) {
  const chanceHorse = hasTrait(profConfig, T.HORSEBACK_RIDING) ? 70 : 20;
  const chanceMotorcycle = hasTrait(profConfig, T.MOTORCYCLING) ? 30 : 5;
  console.log("chanceHorse", chanceHorse, "chanceMotorcycle", chanceMotorcycle);

  // TODO: If they have a motorcycle, they have some amount of gas and gas in tanks.
  const hasHorse = test(chanceHorse);
  const hasMotorcycle = test(chanceMotorcycle);

  if (chanceHorse > chanceMotorcycle && hasHorse) {
    add(bag, "horse");
    if (test(80)) {
      add(bag, `horse-tack`);
      add(bag, `saddle`);
      if (test(80)) {
        add(bag, `saddlebag`, roll("1d2"));
      }
    }
  } else if (hasMotorcycle) {
    add(bag, "motorcycle");
  }
}