Pre-release
Adventure.js Docs Downloads
Score: 0 Moves: 0
// Tangible_Is.js
(function () {
  /*global adventurejs A*/
  "use strict";
  var p = adventurejs.Tangible.prototype;
  /**
   * <strong>$is()</strong> is a convenience method for authors that
   * provides an easy way to test for various conditions.
   * <ul>
   * <li><dt>assetA.$is("body")</dt> <dd>asking, is this asset a body of substance such as a lake or sandy desert?</li>
   * <li><dt>assetA.$is("closed")</dt> <dd>asking, is this asset closed?</li>
   * <li><dt>assetA.$is("held", assetB)</dt> <dd>asking, is this asset held by that asset, as in a bannister held by player?</dd></li>
   * <li><dt>assetA.$is("holding", assetB)</dt> <dd>asking, is this asset holding that asset, as in player holding a rope?</dd></li>
   * <li><dt>assetA.$is("in", assetB)</dt> <dd>accepts any preposition, asking, is this asset in that aspect of that asset?</dd> </li>
   * <li><dt>assetA.$is("locked")</dt> <dd>asking, is this asset locked?</li>
   * <li><dt>assetA.$is("nested in", assetB)</dt> <dd>nested in, specific to character classes, asking, is this asset nested in that asset?</dd></li>
   * <li><dt>assetA.$is("open")</dt> <dd>asking, is this asset open?</li>
   * <li><dt>assetA.$is("plugged")</dt> <dd>asking, is this asset plugged?</li>
   * <li><dt>assetA.$is("sealed")</dt> <dd>asking, is this asset sealed?</li>
   * <li><dt>assetA.$is("takeable")</dt> <dd>asking, can this asset be taken?</li>
   * <li><dt>assetA.$is("unlocked")</dt> <dd>asking, is this asset unlocked?</li>
   * <li><dt>assetA.$is("unplugged")</dt> <dd>asking, is this asset unplugged?</li>
   * <li><dt>assetA.$is("unsealed")</dt> <dd>asking, is this asset unsealed?</li>
   * <li><dt>assetA.$is("worn")</dt> <dd>asking, is this asset being worn?</li>
   * <li><dt>assetA.$is("zipped")</dt> <dd>asking, is this asset zipped?</li>
   * </ul>
   * @memberOf adventurejs.Tangible
   * @method adventurejs.Tangible#$is
   * @param {String} property
   * @param {Object} asset
   * @todo Leaving open the possibility for other params.
   */
  p.$is = function Tangible_$is(property, asset) {
    var msg = "";
    // property is required, asset is optional

    let shadow_props = {
      open: "closed",
      unlocked: "locked",
      unplugged: "plugged",
      unsealed: "sealed",
    };

    if ("string" !== typeof property) {
      msg = "Tangible_is received a value for property that is not a string. ";
      this.game.log("warn", "high", msg, "Tangible");
      return false;
    }

    if ("string" === typeof asset) {
      asset = this.game.getAsset(asset);
    }

    // shadow props are properties we don't actually track
    // but which are opposites of props we do track
    // ie we track asset.is.sealed and using this we allow
    // authors to ask if asset.is.unsealed
    if (!asset && shadow_props[property]) {
      // only return true if opposite property is explicitly
      // set to false, vs null or undefined,
      // so we don't get false positives, ie returning
      // open === true for a thing that doesn't actually open
      return false === asset.is[shadow_props[property]];
    }

    if (!asset && "undefined" !== typeof this.is[property]) {
      // we understand this to mean that author is asking for
      // something like asset.$is('closed') where we can return
      // asset.is.closed
      return this.is[property];
    }

    // check for preposition aka aspect
    if (this.game.dictionary.isPreposition(property)) {
      // property is a preposition
      // we take this to mean that author is asking
      // something like MyGame.$('comb').is('on','dresser')

      if (!asset) return false;

      // characters are special because they have two ways to be children
      // we have to check place and nest
      if (asset instanceof adventurejs.Character) {
        if (property !== this.getNestOrPlacePreposition()) return false;
        if (asset.id !== this.getNestOrPlaceAsset().id) return false;
        return true;
      }

      // for anything other than character, just check place
      if (property !== this.getPlacePreposition()) return false;
      if (asset.id !== this.getPlaceAssetId()) return false;
      return true;
    }

    // otherwise we got some random property that we may or may not support
    switch (property) {
      case "body":
        if (
          this.hasVessel() &&
          this.getVesselAt(this.getAspectWithVessel()).is_body_of_substance
        ) {
          return true;
        }
        return false;

      case "takeable":
        return asset.isDOV("take");

      case "holding":
        msg = `Is ${this.id} holding ${asset.id}? `;
        this.game.log("log", "high", msg, "Tangible");
        return this.IOVisConnectedToAsset("hold", asset);

      case "held":
        msg = `Is ${this.id} held by ${asset.id}? `;
        this.game.log("log", "high", msg, "Tangible");
        return this.DOVisConnectedToAsset("hold", asset);

      case "nested in":
        msg = `Is ${this.id} nested in ${asset.id}? `;
        this.game.log("log", "high", msg, "Tangible");
        if (this.getNestId) {
          return this.getNestId() === asset.id;
        }
        return false;

      default:
        // no property
        if ("undefined" === typeof this[property]) {
          msg = `Tangible.$is() couldn't find a property ${property} on ${this.name}. `;
          this.game.log("warn", "high", msg, "Tangible");
          return false;
        }

        // property is object
        if ("object" === typeof this[property]) {
          msg = `Tangible.$is() is meant to return a boolean. ${this.name}'s ${property} is an object, which isn't handled by asset.$is(). `;
          this.game.log("warn", "high", msg, "Tangible");
          return false;
        }

        // property is something else
        if ("boolean" !== typeof this[property]) {
          // we'll return a string or a number with a warning
          msg = `Tangible.$is() is meant to return a boolean. ${
            this.name
          }'s ${property} is ${typeof this[
            property
          ]}, which isn't handled by asset.$is(). `;
          this.game.log("warn", "high", msg, "Tangible");
          return false;
        }

        return this[property];
    } // switch
  }; // adventurejs.Tangible.prototype.is
})();