create_animal.js

import { createItem } from "./create_item.js";
import { format, sentenceCase } from "./string_utils.js";
import { logger } from "./utils.js";
import { mapLoader } from "./data_loaders.js";
import { random, roll, test } from "./random_utils.js";
import Bag from "./models/bag.js";
import Dog from "./models/dog.js";
import Horse from "./models/horse.js";
import Model from "./models/model.js";
import RarityTable from "./tables/rarity_table.js";

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

const HORSES_MALE_NAMES = map.get("horses.male.names");
const HORSES_FEMALE_NAMES = map.get("horses.female.names");
const HORSES_BOTH_NAMES = map.get("horses.unisex.names");
const HORSES_BEHAVIOR = map.get("horses.behavior");
const HORSES_PATTERNS = map.get("horses.patterns");
const HORSES_EFFECTS = map.get("horses.effects");

const HORSE_FEMALE_NAME_TABLE = new RarityTable({
  useStrict: false,
  outFunction: (a) => random(a),
})
  .add("common", HORSES_FEMALE_NAMES)
  .add("common", HORSES_BOTH_NAMES);

const HORSE_MALE_NAME_TABLE = new RarityTable({ useStrict: false, outFunction: (a) => random(a) })
  .add("common", HORSES_MALE_NAMES)
  .add("common", HORSES_BOTH_NAMES);

const HORSE_NAMES_BY_GENDER = {
  male: HORSE_MALE_NAME_TABLE,
  female: HORSE_FEMALE_NAME_TABLE,
};

const HORSE_FEMALE_TYPES = new RarityTable({ useStrict: false })
  .add("rare", "filly")
  .add("common", "mare");

const HORSE_MALE_TYPES = new RarityTable({ useStrict: false })
  .add("rare", "colt")
  .add("uncommon", "stallion")
  .add("common", "gelding");

const HORSE_TYPES_BY_GENDER = {
  male: HORSE_MALE_TYPES,
  female: HORSE_FEMALE_TYPES,
};

const DOGS_MALE_NAMES = map.get("dog.male.names");
const DOGS_FEMALE_NAMES = map.get("dog.female.names");

/**
 * Generates a descriptive string for a horse, including its type, optional behavior,
 * coat pattern, and name.
 *
 * @param {Object} [options={}] - Horse description options
 * @param {string} [options.gender] - Gender of the horse ("male" or "female"). Male horses may be
 *    stallions, geldings, or colts; female horses may be mares or fillies. If not provided,
 *    randomly selected.
 * @returns {string} A descriptive phrase for the horse (e.g. "a nervous spotted mare named Clover")
 */
export function createHorse({ gender = random(["male", "female"]) } = {}) {
  logger.start("createHorse", { gender });

  let pattern = random(HORSES_PATTERNS).trim();
  let type = random(HORSE_TYPES_BY_GENDER[gender].get()).trim();
  let behavior = test(50) ? random(HORSES_BEHAVIOR).trim() : undefined;
  let name = random(HORSE_NAMES_BY_GENDER[gender].get()).trim();
  let effect;
  if (test(40)) {
    effect = format(random(HORSES_EFFECTS), {
      possessive: gender === "male" ? "his" : "her",
      personal: gender === "male" ? "he" : "she",
    });
  }
  let bag = new Bag();
  if (test(80)) {
    bag.add(createItem({ tags: "horse-tack" }));
    bag.add(createItem({ tags: "saddle" }));
    if (test(80)) {
      for (let i = 1; i < roll("1d2"); i++) {
        bag.add(createItem({ tags: "saddlebag" }));
      }
    }
  }

  return logger.end(
    new Horse({
      name,
      behavior,
      pattern,
      gender,
      type,
      bag,
      effect,
    }),
  );
}

export function createDog({} = {}) {
  logger.start("createDog");

  const gender = random(["male", "female"]);
  const name = gender === "male" ? random(DOGS_MALE_NAMES) : random(DOGS_FEMALE_NAMES);

  const dog = new Dog({
    name,
    gender,
    coloring: "spotted",
    breed: "mutt",
  });
  return logger.end(dog);
}

export function getAnimalTypes() {
  return ["Dog", "Horse"];
}

export function createAnimal({ type = random(getAnimalTypes()) } = {}) {
  logger.start("createAnimal", { type });
  switch (type) {
    case "Dog":
      return logger.end(createDog());
      break;
    case "Horse":
      return logger.end(createHorse());
      break;
  }
}