Pre-release
Adventure.js Docs Downloads
Score: 0 Moves: 0
// getStringOrArrayOrFunction.js

/*global adventurejs A*/
"use strict";

/**
 * <p>
 * Get string or array or function.
 * Because Javascript is untyped, we can pass any kind of value in a
 * variable. We take advantage of that here to provide flexibility
 * to authors in properties that print a string back to the player.
 * </p>
 * <ul>
 * <li><strong>Strings</strong> will be printed as is.</li>
 * <li><strong>Arrays</strong> can be set to provide a string from
 * a randomized index, or a sequential index that increments each time
 * it's called.</li>
 * <li><strong>Functions</strong> can use their
 * own internal logic to return a string, allowing for dynamic
 * state-based descriptions, such as whether an Asset is open or closed.
 * </ul>
 * <h3 class="examples">Example:</h3>
 * <pre class="display"><code class="language-javascript">MyGame.createAsset({
 *   class: "Desk",
 *   name: "desk",
 *   descriptions: { look: "An old school wooden desk. ", },
 * });
 * MyGame.createAsset({
 *   class: "Drawer",
 *   name: "drawer",
 *   descriptions: {
 *     look: function(){
 *       return "The drawer is $( drawer is| open or| closed ). ";
 *     },
 *   },
 * });
 * MyGame.createAsset({
 *   class: "Blotter",
 *   name: "blotter",
 *   descriptions: {
 *     look: [
 *       {randomize: true},
 *       "The words 'live and let die' are scrawled on the blotter. ",
 *       "The desk blotter has 'born to bleed' carved into it. ",
 *       "You see 'zep rulez!' scratched in to the desk blotter. ",
 *     ],
 *   },
 * });
 * </code></pre>
 * <p>
 * For more information, see
 * <a href="/doc/Scripting_StringArrayFunction.html">How to Use String|Array|Function</a>.
 * </p>
 * <h3 class="examples">Properties that call getStringOrArrayOrFunction</h3>
 * <ul>
 *   <li>verb subscription on_success: asset.[i|d]ov[verb].on_success</li>
 *   <li>verb subscription then_destroy: asset.[i|d]ov[verb].then_destroy</li>
 *   <li>all descriptions: asset.description and asset.descriptions[any]</li>
 *   <li>room events: room.room_events</li>
 *   <li>zone events: room.zone.zone_events</li>
 *   <li>custom vars: MyGame.world._vars[ property ]</li>
 *   <li>constraint message: character.constrained_msg</li>
 * </ul>
 *
 * @method adventurejs#getStringOrArrayOrFunction
 * @memberOf adventurejs
 * @param {String|Array|Function|Boolean|null} obj Can be string or array or function.
 * @param {Object} scope Optional reference to set scope to an object.
 * @param {Object} params Optional params to pass to a function.
 * @returns {String}
 * @ajsalias A.getSAF()
 */
adventurejs.getStringOrArrayOrFunction = adventurejs.getSAF =
  function Adventurejs_getStringOrArrayOrFunction(obj, scope, params = {}) {
    // console.warn( 'getSAF', obj );

    var msg = "";
    var err = "";

    // if it's string or bool or null just pass it on
    if ("string" === typeof obj || "boolean" === typeof obj || null === obj) {
      return obj;
    } // string / bool / null

    // if it's an array, check for meta data in first position
    if (Array.isArray(obj)) {
      if ("object" !== typeof obj[0]) {
        obj.unshift({
          randomize: false,
          frequency: 1,
          index: 0,
        });
      } // object in first slot?

      // ensure that we have randomize setting
      if ("undefined" === typeof obj[0].randomize) {
        obj[0].randomize =
          this.game.settings.randomize_arrays_in_getStringOrArrayOrFunction;
      }

      // ensure that we have index object
      if ("undefined" === typeof obj[0].index) {
        obj[0].index = 0;
      }

      // ensure that we have frequency setting
      if ("undefined" === typeof obj[0].frequency) {
        obj[0].frequency = 1;
      }

      // are we randomizing?
      if (true === obj[0].randomize) {
        var rand = Math.floor(Math.random() * (obj.length - 2) + 1);

        msg = obj[rand];

        // we might be calling an arbitrary function set by an author
        // that isn't returning a string
        if ("string" !== typeof msg) {
          msg = "";
          err =
            "getStringOrArrayOrFunction found an array item that is something other than a string.";
          this.log("warn", 0, err, "Game");
        }
        return msg;
      } // random

      // if we're not randomizing then we're sequencing, so increment
      // we increment here instead of at the end because we may have
      // just changed the length by adding the meta object
      obj[0].index++;

      // if at end, reset to beginning
      if (obj[0].index === obj.length) {
        obj[0].index = 1;
      }

      // get it
      msg = obj[obj[0].index];

      // check it
      if ("string" !== typeof msg) {
        msg = "";
        err =
          "getStringOrArrayOrFunction found an array item that is something other than a string.";
        this.log("warn", 0, err, "Game");
        //console.warn( msg, obj[rand] );
      }

      return msg;
    } // isArray

    if (typeof obj === "function") {
      // it's a function so call it
      // passing this because there's some scope weirdness
      msg = scope ? obj.call(scope) : obj(this);

      // we might be calling an arbitrary function set by an author
      // that isn't returning a string
      if ("string" !== typeof msg) {
        msg = "";
        err =
          "getStringOrArrayOrFunction called a function that returned something other than a string.";
        this.log("warn", 0, err, "Game");
      }
      return msg;
    } // function
  };