Pre-release
AdventureJS Docs Downloads
Score: 0 Moves: 0
// Atom.js
(function () {
  /* global adventurejs A */
  /**
   * @class adventurejs.Atom
   * @ajsinternal
   * @ajsnavheading FrameworkReference
   * @param {String} game_name A reference back to the top level
   * game object, by way of window[game_name]. Done this way
   * rather than using an object reference in order to avoid circular
   * references, because they're difficult to parse into JSON, the
   * method that's used for saving/restoring game data.
   * @param {String} name The name of the object in the game world,
   * ie "brass lantern". The name is also used to create the object's
   * <a href="#id">id</a> by means of removing periods and spaces and
   * converting to lowercase.
   * @param {String} context_id Optional param used by some supporting
   * classes such as Aspects and Vessels to indicate context.
   * @summary Experimental class made with Javascript ES6 class keyword.
   * @classdesc
   * <strong>Atom</strong> is a bedrock class that is used as the
   * prime ancestor for all in-game classes. It defines several
   * important base properties including <a href="#name">name</a>,
   * <a href="#class">class</a>, <a href="#id">id</a>,
   * and the <a href="#set">set</a> method.
   * Authors should not need to instantiate or subclass Atom.
   * Start with the {@link adventurejs.Tangible|Tangible} class
   * to create new physical Assets, or the
   * {@link adventurejs.Intangible|Intangible} class to create
   * new abstract Assets.
   */
  class Atom {
    constructor(name, game_name, context_id = "") {
      /**
       * game_name stores the name of
       * the current game instance as scoped to window.
       * (By default, we use "MyGame", but you can use
       * any name.) In order
       * to refer to the game object, we use window[this.game_name].
       * We store a string instead of the object itself in order to
       * avoid problems caused by circular references during
       * JSON encoding, which is used extensively for managing game
       * state and saved games.
       * @alias game_name
       * @type {string}
       * @memberof adventurejs.Atom
       */
      this.game_name = game_name;

      /**
       * Class identifier to be provided in the asset definition.
       * All game objects start as generic objects that get passed to
       * {@link adventurejs.Game#createAsset|createAsset},
       * which uses an object's class field to specify
       * a class constructor.
       * @alias class
       * @type {string}
       * @memberof adventurejs.Atom
       */
      this.class = "Atom";

      // Name should only be undefined when restoring from JSON.
      if ("undefined" !== typeof name) {
        /**
         * Class identifier to be provided in the asset definition.
         * All game objects start as generic objects that get passed to
         * {@link adventurejs.Game#createAsset|createAsset},
         * which uses an object's class field to specify
         * a class constructor.
         * @alias name
         * @type {string}
         * @memberof adventurejs.Atom
         */
        this.name = name;

        if (this.name) {
          /**
           * A unique ID for the game asset, based on the object name
           * provided in the asset definition.
           * @alias id
           * @type {string}
           * @memberof adventurejs.Atom
           */
          // this.id = `${context_id ? context_id + ":" : ""}${A.serialize(name)}`;
          this.id = A.serialize(name);
        }
      }

      /**
       * context_id is set in some child classes including
       * <a href="adventurejs.Aspect.html">Aspect</a>,
       * <a href="adventurejs.Nest.html">Nest</a>, and
       * <a href="adventurejs.Vessel.html">Vessel</a>
       * to get the object's context, or parent asset.
       * @alias context_id
       * @type {string}
       * @memberof adventurejs.Atom
       */
      this.context_id = context_id || "";
    }

    /**
     * Name returns the name of the class instance
     * with the first character uppercased.
     * @var {String} adventurejs.Atom#Name
     */
    get Name() {
      return A.propercase(this.name);
    }

    get self() {
      return this;
    }

    /**
     * Returns the top level game object. Use <code class="property">this.game</code>.
     * @var {Getter} adventurejs.Atom#game
     * @returns {adventurejs.Game}
     */
    get game() {
      return window[this.game_name] || false;
    }

    get context_id() {
      return this._context_id;
    }
    set context_id(id) {
      id = A.serialize(id);
      this._context_id = id;
    }

    get context() {
      return this.game.getAsset(this._context_id);
    }

    /**
     * Provides a chainable shortcut method for setting a number of properties on the instance.
     * @method adventurejs.Atom#set
     * @memberOf adventurejs.Atom
     * @param {Object} props A generic object containing properties to copy to the Object instance.
     * @returns {Object} Returns the instance the method is called on (useful for chaining calls.)
     * @chainable
     */
    set(props) {
      return A.deepSet.call(this.game, props, this);
    }

    /**
     * A method to test whether the Atom is an instance of a given class.
     * @method adventurejs.Atom#hasClass
     * @memberOf adventurejs.Atom
     * @param {String} prop Name of the class to test for.
     * @returns {Boolean}
     */
    hasClass(classname) {
      if (!classname || "string" !== typeof classname) return false;
      if (!adventurejs[classname]) {
        // classes have leading capitalization so we'll make an effort
        // to see if that's the problem
        classname = A.propercase(classname);
      }
      if (!adventurejs[classname]) {
        return false;
      }
      return this instanceof adventurejs[classname];
    }

    /**
     * <strong>getClassInheritance</strong> is a utility method to get
     * an asset's class inheritance chain. Returns a list of class names
     * from high to low.
     * @method adventurejs.Atom#getClassInheritance
     * @memberOf adventurejs.Atom
     * @returns {Array}
     */
    getClassInheritance() {
      const classlist = [];
      let current = Object.getPrototypeOf(this); // Start from the prototype

      while (current) {
        const constructorName = current.constructor?.name;
        if (constructorName) {
          classlist.push(constructorName);
          if (constructorName === "Atom") break;
        }
        current = Object.getPrototypeOf(current);
      }

      return classlist;
    }
  }

  adventurejs.Atom = Atom;
})();