models/location.js

import { random } from "../random_utils.js";
import Model from "./model.js";

/**
 * Template for creating location instances. Defines the blueprint for creating location
 * instances, including their properties, sub-divisions, material contents, and behavior patterns.
 *
 * @extends Model
 */
export class LocationTemplate extends Model {
  /**
   * Creates a new LocationTemplate instance.
   *
   * @constructor
   * @param {Object} [params={}] - Configuration parameters
   * @param {String} [params.type] - Unambiguous and unique, human-readable label for the location type. Either `type` or `room` must be provided and they are mutually exclusive.
   * @param {String} [params.room] - If present, supplies the type of the location, and also indicates that is a room, which will not be not included in the list of locations from which you can start to build a top-down representation of a location. Either `type` or `room` must be provided and they are mutually exclusive.
   * @param {String} [params.name=params.type] - The display name of a location. Can contain alternatives in the string form "{A|B}"
   * @param {String[]} [params.tags] - Array of tags to categorize this location template
   * @param {Boolean} [params.abstract=false] - Whether this is an abstract template that cannot be instantiated directly
   * @param {Object} [params.contents] - Contents configuration to create content of this location in the format defined by the `selectElements` function.
   * @param {Object} [params.inventory] - An array of configurations for the inventory of this location, each in the format defined by the `bagSpecParser` function.
   * @param {Array|Object} [params.ch] - The child nodes of this location template, either as an array of types, or as an object in the format defined by the `selectElements` function.
   * @param {String} [params.descr] - Description of the location, store policies, etc.
   * @param {String} [params.owner] - The profession to use when generating an owner of a store
   * @param {Array} [params.sequences] - An array of sequence configurations for this location template. Each contains an `id`, and array of sequence naming `options`, from which one will be selected each time a location is instantiated from this template.
   */
  constructor({
    id, // e.g. House109
    type, // e.g. "House"
    name, // e.g. "{A|B}"
    tags = [],
    contents = {},
    inventory = {},
    ch = [],
    description,
    image,
    owner,
    traits = [],
    sequences = [],
  } = {}) {
    super({ tags });
    this.id = id || type;
    this.type = type;
    this.name = name || type;
    // this.room = room;
    // this.div = div;
    // this.abstract = abstract;
    this.description = description;
    this.image = image;
    this.contents = Object.freeze(contents);
    this.inventory = Object.freeze(inventory);
    this.ch = Object.freeze(ch);
    this.owner = owner;
    this.traits = Object.freeze(traits);
    this.sequences = Object.freeze(sequences);
  }
}

/**
 * Represents an actual location instance created from a LocationTemplate.
 * Contains the runtime state and hierarchical relationships between locations.
 *
 * @extends Model
 */
export class Location extends Model {
  /**
   * Creates a new Location instance from a template.
   *
   * @constructor
   * @param {LocationTemplate} template - The template to create this location from
   * @param {Location} [parent] - The parent location in the hierarchy
   */
  constructor(template, parent) {
    super({ tags: template.tags });
    this.id = template.id;
    this.name = template.name;
    this.type = template.type;
    this.description = template.description ? [template.description] : [];
    this.image = template.image;
    // this.room = template.room;
    // this.div = template.div;
    this.contents = [];
    this.inventory = [];
    this.children = [];
    this.parent = parent;
    this.sequences = template.sequences.map((sequence) => {
      return {
        id: sequence.id,
        seqType: random(sequence.options),
        // to support defining sequences in place with the "as" syntax, we need to
        // carry that actual list over because there's no defined sequence in the data
        // file, or the pre-canned types of sequences (e.g. "alpha")
        options: sequence.options,
        index: 0,
      };
    });
  }
  /**
   * Adds a child location to this location's hierarchy.
   *
   * @param {Location} child - The child location to add
   * @throws {Error} If the child is not a Location instance
   */
  addChild(child) {
    if (!(child instanceof Location)) {
      throw new Error("Cannot add a template to a location: " + child);
    }
    child.parent = this;
    this.children.push(child);
  }
}