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

(function () {
  /*global adventurejs A*/
  "use strict";

  var p = adventurejs.Parser.prototype;

  /**
   * Handle noun input.
   * @memberOf adventurejs.Parser
   * @method adventurejs.Parser#parseNoun
   * @param {String} word
   * @returns {adventurejs.parsedNoun}
   */
  p.parseNoun = function Parser_parseNoun(word) {
    this.game.log("log", "high", "parseNoun.js > BEGIN", "Parser");
    this.game.log("log", "high", "parseNoun.js received > " + word, "Parser");
    var currentRoom = this.game.getCurrentRoom();
    var lastTurn = this.input_history[1];

    /**
     * We're using the input word to create a parsedNoun
     * with properties needed by logic down the road.
     */
    var parsedNoun = new adventurejs.ParsedNoun();
    parsedNoun.input = word;
    parsedNoun.serialized_input = A.serialize(word);
    parsedNoun.deserialized_input = A.deserialize(word);

    // player can enter "it" to refer to last turn's direct object
    if (
      "it" === word &&
      lastTurn &&
      lastTurn.parsedNoun1 &&
      lastTurn.parsedNoun1.qualified_object_id
    ) {
      word = lastTurn.parsedNoun1.qualified_object_id;
    }

    /*
     * Does the input word match a direction?
     * getDirection converts shortcuts to full name,
     * eg ne to northeast
     * and returns the full name as a string.
     */
    var direction = this.dictionary.getDirection(word);
    if (direction) {
      // save the direction string to matches object
      parsedNoun.matches.direction = direction;
    }

    var asset = this.game.getAsset(word);
    if (asset && asset instanceof adventurejs.Substance) {
      // save the substance id to matches object
      parsedNoun.matches.substance = asset.id;
    }

    /**
     * Noun is direction and room has an exit in that direction.
     */
    if (direction && "undefined" !== typeof currentRoom.exits[direction]) {
      // save the room's direction object id to matches object
      // TODO have we confirmed the room has a direction there?
      // console.log("*** currentRoom.exits", currentRoom.exits);
      var exitID = currentRoom.exits[direction];
      parsedNoun.matches.all.push(exitID);
      parsedNoun.matches.qualified.push(exitID);
      //parsedNoun.matches.direction = direction;
      parsedNoun.qualified_object_id = exitID;
    } else if (
      /**
       * Noun is direction and room does not have an exit in that direction.
       * Get global direction object.
       */
      direction &&
      "undefined" === typeof currentRoom.exits[direction]
    ) {
      // global direction
      parsedNoun.matches.all.push("global_" + direction);
      parsedNoun.matches.qualified.push("global_" + direction);
    } // direction

    // if player entered "all" as a noun, we're only looking at
    // the current room's contents
    else if ("all" === word) {
      parsedNoun.matches.all = currentRoom.getAllNestedContents();
    } else if (1 === word.split("&").length && 1 === word.split("=").length) {
      /**
       * User did not ask for "this AND that"
       * and world_lookup search returned single ID string
       */
      parsedNoun.matches.all = this.game.parser.selectAll(word);
    } else if (1 < word.split("&").length || 1 < word.split("=").length) {
      /**
       * User asked for "this AND that"
       * or world_lookup search returned multiple IDs
       */
      /**
       * Multiple inputs come from player input such as:
       * "get brass key and silver key"
       */
      // console.log("=======================");
      // console.log("MULTIPLE INPUTS!!!");
      var multipleInput = word.split("&");
      for (var mI = 0; mI < multipleInput.length; mI++) {
        console.log(multipleInput);
        /**
         * Ambiguous inputs come from combineCompoundPhrases,
         * which searches world_lookup trying to match
         * player input with world objects.
         * It doesn't do disambiguation but just passes
         * along multiple values.
         *
         * Example:
         * brass_key=old_brass_key,new_brass_key,small_brass_key
         *
         * The item before the = is the original input.
         */
        var ambiguousInput = multipleInput[mI].split("=");
        if (1 === ambiguousInput.length) {
          parsedNoun.matches.all = parsedNoun.matches.all.concat(
            this.game.parser.selectAll(ambiguousInput[0]),
          );
          this.game.log(
            "log",
            "low",
            "parseNoun.js > Unambiguous match: " + ambiguousInput[0],
            "Parser",
          );
        } else {
          this.game.log(
            "log",
            "low",
            "parseNoun.js > Ambiguous matches:",
            "Parser",
          );
          var ambiguousMatches = ambiguousInput[1].split(",");
          parsedNoun.input = ambiguousInput[0];
          parsedNoun.serialized_input = ambiguousInput[0];
          parsedNoun.deserialized_input = A.deserialize(ambiguousInput[0]);

          for (var aM = 0; aM < ambiguousMatches.length; aM++) {
            var tempParsedNoun = this.game.parser.selectAll(
              ambiguousMatches[aM],
            );
            this.game.log(
              "log",
              "low",
              "parseNoun.js > tempParsedNoun: " + tempParsedNoun,
              "Parser",
            );
            for (var tPN = 0; tPN < tempParsedNoun.length; tPN++) {
              parsedNoun.matches.all.push(tempParsedNoun[tPN]);
            }
            this.game.log(
              "log",
              "low",
              "parseNoun.js > ambiguousMatches: " + ambiguousMatches[aM],
              "Parser",
            );
          } // for loop
        } // if( 1 === ambiguousInput.length )
      } // for loop
    } // else if

    // get an array of objects
    // we clone all -> qualified so that we can delete
    // unqualified objects but still keep a record of all found
    parsedNoun.matches.qualified = Object.assign([], parsedNoun.matches.all);

    if (0 === parsedNoun.matches.all.length) {
      // no objects this word, but it might be a direction
      return parsedNoun;
    }

    //console.error( 'parsedNoun.matches.all',parsedNoun.matches.all );
    // for( var i = 0; i < parsedNoun.matches.all.length; i++)
    // {
    //   if( word === parsedNoun.matches.all[i] )
    //   {
    //     parsedNoun.matches.all = [word];
    //     parsedNoun.matches.qualified = [word];
    //   }
    // }
    // console.error( 'parsedNoun.matches.all',parsedNoun.matches.all );

    if (1 === parsedNoun.matches.all.length) {
      // exact match! whoo hoo!
      // we still need to know if it's present/visible/reachable
      parsedNoun.matches.unambiguous = parsedNoun.matches.all[0];
    }

    //this.game.log( "log", "high", [ "parseNoun.js > matches.all > " + parsedNoun.matches.all ] , 'Parser' );

    /**
     * Is the input word plural?
     * Bearing in mind that we might have received
     * for example, the word "keys" which would simply mean "all keys"
     * or we might've received a compound string like
     * "grass_keys=red_grass_key,green_grass_key,blue_grass_key"
     */
    var lookup = this.game.world_lookup[parsedNoun.deserialized_input];
    if ("undefined" !== typeof lookup) {
      parsedNoun.type = lookup.type;
      if ("singular" === parsedNoun.type) {
        parsedNoun.plural = lookup.plural;
      }
      if ("plural" === lookup.type) {
        parsedNoun.isPlural = true;
        parsedNoun.singular = lookup.singular;
      }
      if ("group" === lookup.type) {
        parsedNoun.isGroup = true;
        parsedNoun.singular = lookup.singular;
      }
    }
    //if( -1 < word.indexOf( "=" ) ) {
    //  parsedNoun.plural = true;
    //}

    this.game.log(
      "log",
      "high",
      "parseNoun.js > returns matches.qualified: " +
        parsedNoun.matches.qualified,
      "Parser",
    );
    this.game.log("log", "high", "parseNoun.js > END", "Parser");
    //console.warn( parsedNoun );
    return parsedNoun;
  };
})();