create_gang.js

import { createCharacter } from "./create_character.js";
import { assignNickName } from "./create_character_name.js";
import { logger } from "./utils.js";
import { mapLoader } from "./data_loaders.js";
import { random, roll, test } from "./random_utils.js";
import { TRAITS as T } from "./constants.js";
import Gang from "./models/gang.js";

const map = await mapLoader("names.data");

/*
 * TODO: All girl gangs/all boy gangs mostly, with sometimes a connection between the two.
 patrols: no communications or medics in group.
 *
 * TODO: Non-criminal gangs? Like the "Eastside Motorcycle Club" or the "Southside Paladins", etc.?
 "nomadic": [
 "Raiding Parties (Banditry or Plundering)",
 "Scavenging",
 "Ambushes/Highway Robbery (Brigandry)"
 ],
 "street": [
 "Protection Rackets",
 "Burglery & Larceny",
 "Pickpocketing",
 "Scavenging",
 "Extracting tolls for passage or entry"
 ]

 Names for military patrols
 */
const TYPES = {
  "Biker Gang": {
    prof: "Raider",
    nicks: 100,
    traits: { [T.MOTORCYCLING]: 1 },
    count: "3d4-2",
    names: map.get("names.biker.gang"),
  },
  "Street Gang": {
    prof: "Thief",
    nicks: 100,
    count: "1d4+1",
    names: map.get("names.street.gang"),
  },
  "Raiding Gang": {
    prof: "Raider",
    count: "3d4-1",
    nicks: 90,
  },
  "Scavenger Party": {
    prof: "Scavenger",
    count: "1d4+1",
    nicks: 50,
  },
  // TODO: Which isn't a group of working buckeroos. Those guys might have a chuck wagon and the like
  "Cowboy Posse": {
    prof: "Rancher",
    count: "1d5+1",
    nicks: 20,
  },
  "Cattle Rustler Gang": {
    prof: ["Rancher", "Rancher", "Scavenger", "Thief"],
    count: "1d3+1",
    nicks: 50,
  },
  "Ex-Army Gang": {
    prof: "Raider",
    count: "1d3*4",
    military: "Army",
    nicks: 5,
  },
  "Ex-Marine Gang": {
    prof: "Raider",
    count: "1d3*4",
    military: "Marine",
    nicks: 15,
  },
};

const TYPE_KEYS = Object.keys(TYPES);

export function getGangTypes() {
  return TYPE_KEYS.sort();
}

/**
 * Creates a gang of characters with a shared type, name, and profession-appropriate traits.
 * Each member is a generated Character. Members may be assigned nicknames depending on gang type.
 *
 * @param {Object} [options={}] - Gang creation options
 * @param {string} [options.type] - Type of gang to create, selected from the values returned by
 *    `getGangTypes()` (e.g. "Biker Gang", "Street Gang", "Raiding Gang", "Cowboy Posse"). If not
 *    provided, randomly selected.
 * @param {number} [options.count=null] - Number of gang members to generate. If not provided, a count
 *    is rolled based on the gang type's configuration.
 * @returns {Gang} A new Gang instance containing the generated member characters
 */
export function createGang({ type = random(TYPE_KEYS), count = null } = {}) {
  logger.start("createGang", { type, count });
  const gangType = type;
  const gangSpec = TYPES[gangType];
  if (typeof gangSpec === "undefined") {
    throw new Error("Invalid gang type: " + gangType);
  }
  const opts = {
    kind: gangType,
    name: random(gangSpec.names),
  };
  const gang = new Gang({ name: opts.name, kind: gangType });

  let newCount = count ? count : roll(gangSpec.count);
  for (let i = 0; i < newCount; i++) {
    const c = createCharacter({
      preProfession: gangSpec.military,
      postProfession: random(gangSpec.prof),
    });
    if (test(gangSpec.nicks)) {
      assignNickName(c);
    }
    gang.add(c);
  }
  return logger.end(gang);
}