Pre-release
AdventureJS Docs Downloads
Score: 0 Moves: 0
// getModifiedDescription.js
(function () {
  /*global adventurejs A*/
  var p = adventurejs.Game.prototype;
  /**
   * Get complex indirect descriptions,
   * such as "look at this through magnifying glass with candle", where
   * "through magnifying glass, with candle" is a key at
   * asset.descriptions[identifier]["through magnifying glass, with candle"]
   * "look" is always the default direct object description.
   * @memberOf adventurejs.Game
   * @method adventurejs.Game#getModifiedDescription
   * @param {Object} params
   * @return {String}
   */
  p.getModifiedDescription = function Game_getModifiedDescription(params) {
    var asset = params.asset;
    var identifier = params.identifier;
    var find_modifiers = params.find_modifiers;
    var fallback_base = params.fallback_base;
    var append_base = params.append_base;
    var prepend_base = params.prepend_base;
    var msg = "";
    if (!identifier || identifier === "at") identifier = "look";

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

    if (asset.proxy) {
      let proxy = this.game.getAsset(asset.proxy);
      if (proxy) {
        let proxy_description = this.game.getModifiedDescription({
          asset: proxy,
          identifier: identifier,
          find_modifiers: find_modifiers,
          fallback_base: fallback_base,
        });
        if (proxy_description) return proxy_description;
      }
    }

    if (identifier === "at") {
      identifier = "look";
    }

    var input = this.game.getInput();
    var base_description =
      this.game.getDescription({ asset: asset, identifier: identifier }) || "";

    // does object have descriptions at all?
    if (!asset.descriptions) {
      // @TODO if asset.description return that
      return fallback_base ? base_description : "";
    }

    // does this description identifier exist?
    if (!asset.descriptions[identifier]) {
      // if identifier was look then we got nuthin
      if (identifier === "look" || !asset.descriptions.look) {
        return fallback_base ? base_description : "";
      }

      // we have look - does it exist under look?
      if (!asset.descriptions.look[identifier]) {
        // no such identifier
        return fallback_base ? base_description : "";
      }

      // it exists - treat it as a modifier on look
      input.pushViewModifier(identifier, null, "input");
      identifier = "look";
    }

    // view_modifiers is expected to look like:
    // view_modifiers: [ { asset: ClassedObject, prep: "string", type: "string" } ]

    if (find_modifiers) {
      this.game.findViewModifiers();
    }

    if (input.view_modifiers.length === 0) {
      return fallback_base ? base_description : "";
    }

    // look for valid descriptions
    for (let index = 0; index < input.view_modifiers.length; index++) {
      let indirect_identifier = input.view_modifiers[index].identifier;
      let indirect_asset = input.view_modifiers[index].asset;
      let indirect_aspects;

      if (!indirect_identifier || !indirect_asset) {
        continue;
      }

      // account for equivalencies: for example,
      // in the case of glasses, binoculars, etc player might say
      // "look at x with binoculars" while author has coded for
      // "look at x through binoculars" and we'll allow it
      // though it means we have to search for all acceptable equivalencies
      indirect_aspects = [indirect_identifier]; // convert to array

      // "look with" may be equivalent to "look through"
      if (
        indirect_identifier === "with" &&
        indirect_asset.hasQuirk("look_with_means_look_through")
      ) {
        if (!indirect_aspects.includes("through"))
          indirect_aspects.push("through");
      }
      if (
        indirect_identifier === "through" &&
        indirect_asset.hasQuirk("look_with_means_look_through")
      ) {
        if (!indirect_aspects.includes("with")) indirect_aspects.push("with");
      }

      // "look in" may be equivalent to "look on"
      if (
        indirect_identifier === "in" &&
        indirect_asset.hasQuirk("in_means_on")
      ) {
        if (!indirect_aspects.includes("on")) indirect_aspects.push("on");
      }

      // save equivalencies
      for (let i = 0; i < indirect_aspects.length; i++) {
        let indirect_identifier = indirect_aspects[i];
        let target = `${indirect_identifier} ${indirect_asset.name}`;
        if (!input.view_modifiers[index].equivalencies) {
          input.view_modifiers[index].equivalencies = [];
        }
        if (!input.view_modifiers[index].equivalencies.includes(target)) {
          input.view_modifiers[index].equivalencies.push(target);
        }
      }
    }

    // example of a view_modifier
    // {aspect: 'with', asset: Candle, type: 'auto', equivalencies: Array(2)}
    // equivalencies: (2) ['with occult candle', 'through occult candle']
    let provided_modifier_targets = [];
    for (let index = 0; index < input.view_modifiers.length; index++) {
      provided_modifier_targets.push(input.view_modifiers[index].equivalencies);
    }

    // we're allowing authors to set view modifiers in any order:
    // for instance "in pit, from tree" or "from tree, in pit"
    // so we have to look for every possible combination
    let expanded_modifier_targets = A.generateCombinations(
      provided_modifier_targets
    );

    if ("string" === typeof asset.descriptions[identifier]) {
      return asset.descriptions[identifier];
    }

    let comparisons = [];
    let description_keys = Object.keys(asset.descriptions[identifier]);
    // loop through expanded_modifier_targets
    // inside each loop, loop through indirect descriptions
    for (let i = 0; i < expanded_modifier_targets.length; i++) {
      for (let j = 0; j < description_keys.length; j++) {
        // modifier can be greater than description
        // but description can not be greater than modifier
        // alternately
        // modifier can have elements that are not in description
        // description can't have any elements that are not in modifier
        if (
          description_keys[j].split(",").length >
          expanded_modifier_targets[i].split(",").length
        ) {
          continue;
        }
        let count = A.countCommonElements(
          expanded_modifier_targets[i],
          description_keys[j]
        );
        comparisons.push({
          view_modifiers: expanded_modifier_targets[i],
          description: description_keys[j],
          count: count,
        });
        // console.warn(
        //   "expanded_modifier_targets[i]:",
        //   expanded_modifier_targets[i],
        //   "\n",
        //   "description_keys[j]:",
        //   description_keys[j]
        // );
        // console.warn("count", count);
      }
    }

    // @TODO modify this so if it finds something with multiples it prints that
    // but if it finds multiple singles it prints those

    var highestcount = -1;
    var highestindex = -1;
    for (let i = 0; i < comparisons.length; i++) {
      // console.warn(comparisons[i].count);
      let count = comparisons[i].count;
      let description_length = comparisons[i].description.split(",").length;
      if (count === description_length && count > highestcount) {
        highestcount = comparisons[i].count;
        highestindex = i;
      }
    }
    // console.warn(
    //   "getModifiedDescription > Highest count is " + highestcount,
    //   comparisons[highestindex]
    // );

    if (highestcount > -1) {
      // save a record of use back to the original view_modifiers
      // console.warn(
      //   "getModifiedDescription > comparisons[highestindex].description",
      //   comparisons[highestindex].description
      // );
      let modifiers_set = comparisons[highestindex].description;
      let best_description =
        asset.descriptions[identifier][comparisons[highestindex].description];
      let modifiers = modifiers_set.split(",").map((item) => item.trim());
      let user_modified = false;
      for (let i = 0; i < modifiers.length; i++) {
        let modifier = modifiers[i];
        for (let j = 0; j < input.view_modifiers.length; j++) {
          if (input.view_modifiers[j].string === modifier) {
            input.view_modifiers[j].used = true;
            if (input.view_modifiers[j].type === "input") user_modified = true;
          }
        }
      }

      // call the description
      msg = A.getSAF.call(this.game, best_description);
      if (prepend_base || this.game.settings.concatenate_descriptions) {
        msg = base_description + msg;
      }
      if (append_base) {
        msg = msg + base_description;
      }
      return msg;
    }

    // fallback
    return fallback_base ? base_description : "";
  };
})();