Pre-release
AdventureJS Docs Downloads
Score: 0 Moves: 0
// qualifyParsedNoun.js

(function () {
  /* global adventurejs A */

  var p = adventurejs.Parser.prototype;

  /**
   * Exclude nouns found by parseNoun that don't meet
   * requirements for verb found by parseVerb. Unique
   * handling for each condition allows for precise
   * responses. If it appears that the parser has found
   * an unambiguous object, we refer directly to that
   * object. When possible, we return that asset's name
   * as the player input it rather than returning the
   * object's proper name.
   * @memberOf adventurejs.Parser
   * @method adventurejs.Parser#qualifyParsedNoun
   * @param {Object} params
   * @returns {adventurejs.parsedNoun}
   */
  p.qualifyParsedNoun = function Parser_qualifyParsedNoun({
    parsedNoun,
    parsedVerb,
    nounIndex,
  }) {
    const fx = `qualifyParsedNoun.js`;
    this.game.log(
      "L1111",
      "log",
      "high",
      `[${fx}] qualifyParsedNoun() begin`,
      "Parser"
    );

    const getQualifiedName = (qualified_asset) => {
      let qualified_name;
      let source =
        this.game.getInput().replacements[qualified_asset.id]?.source;
      if (source) {
        qualified_name = source;
      } else {
        qualified_name = qualified_asset.noun;
      }
      return qualified_name;
    };

    const doVerbAction = function (qualified_asset, verb) {
      return qualified_asset.doVerbAction({
        action: `try${verb.Name}`,
        type: "VerbAction",
      });
    };

    const getQualifiedId = function (parsedNoun) {
      return 1 === parsedNoun.matches.qualified.length
        ? parsedNoun.matches.qualified[0]
        : null;
    };

    const input = this.game.getInput();
    const subject = input.getSubject();
    const player = this.game.getPlayer();
    let output_class = "";
    let msg = "";
    const verb = this.dictionary.verbs[parsedVerb];
    const prettyverb = verb.prettyname;
    const gerund = verb.gerund;

    const must_be =
      this.dictionary.verbs[parsedVerb]["phrase" + nounIndex].noun_must_be;

    if (must_be.known) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectKnown(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1113",
          "log",
          "high",
          `[${fx}] must_be.known eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1056 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.known`
        );
        /**
         * Try to get base noun or original player input for this object.
         * Player might have entered a partial name for this.
         * Since player doesn't know this object, if they input
         * a partial name, we might accidentally reveal too
         * much information if we return the full name.
         * ex player asks for "sword" and we return "cursed bronze sword"
         * Now player knows there is a cursed bronze sword.
         */
        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          qualified_name = getQualifiedName(qualified_asset);
        }
        if (qualified_name) {
          msg += `{We} {don't} know of any ${qualified_name}. `;
        } else {
          // This response doesn't read well if player has asked for a substance.
          // msg += "{We} {don't} know of anything matching that description. ";

          // This response is a little too anonymous.
          // msg += "{We} {don't} see any of that here. ";

          // This response is sketchy because player input might have been substituted
          // with a list of possible matches.
          msg += `{We} {don't} know of any ${A.deserialize(parsedNoun.input)}. `;
        }
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.extant) {
      parsedNoun.matches.qualified = this.selectExtant(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1190",
          "log",
          "high",
          `[${fx}] must_be.visible eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1310 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.extant`
        );
        msg += `Was there any ${parsedNoun.original_input}? {We} can't recall. `;
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.present) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectPresent(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1115",
          "log",
          "high",
          `[${fx}] must_be.present eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1067 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.present`
        );
        // try to get base noun or original player input for this object
        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          qualified_name = getQualifiedName(qualified_asset);
        }
        if (qualified_name) {
          msg += `There doesn't appear to be any ${qualified_name} present. `;
        } else {
          msg += `There doesn't appear to be any ${parsedNoun.original_input} present. `;
        }
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.present_if_tangible) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectPresentIfTangible(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1117",
          "log",
          "high",
          `[${fx}] must_be.present_if_tangible eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1596 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.present_if_tangible`
        );
        // try to get base noun or original player input for this object
        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          qualified_name = getQualifiedName(qualified_asset);
        }
        if (qualified_name) {
          msg += `There doesn't appear to be any ${qualified_name} present. `;
        } else {
          msg += `There doesn't appear to be any ${parsedNoun.original_input} present. `;
        }
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.direction) {
      if (false === parsedNoun.matches.direction) {
        this.game.log(
          "L1118",
          "log",
          "high",
          `[${fx}] must_be.direction eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1054 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.direction`
        );
        msg += `It appears that {we} meant to specify a direction, but that wasn't recognized as such. `;
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_direction) {
      if (parsedNoun.matches.direction) {
        this.game.log(
          "L1119",
          "log",
          "high",
          `[${fx}] must_be.not_direction eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1055 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_direction`
        );
        msg += `{We} can't ${prettyverb} a direction. `;
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_global) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectNotGlobal(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1121",
          "log",
          "high",
          `[${fx}] must_be.not_global eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1058 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_global`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_scenery) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectNotScenery(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1123",
          "log",
          "high",
          `[${fx}] must_be.not_scenery eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1059 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_scenery`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_exit) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectNotExit(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1125",
          "log",
          "high",
          `[${fx}] must_be.not_exit eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1060 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_exit`
        );
        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.intangible) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectIntangible(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1127",
          "log",
          "high",
          `[${fx}] must_be.intangible eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1061 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.intangible`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_substance) {
      parsedNoun.matches.qualified = this.selectNotSubstance(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1129",
          "log",
          "high",
          `[${fx}] must_be.not_substance eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1062 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_substance`
        );
        msg += `{We} need to specify a source. `;
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.matter) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectMatter(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1131",
          "log",
          "high",
          `[${fx}] must_be.matter eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1534 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.matter`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          // msg += `{We} can't ${prettyverb} ${parsedNoun.original_input}. `;
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.tangible) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectTangible(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1133",
          "log",
          "high",
          `[${fx}] must_be.tangible eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1063 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.tangible`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          // msg += `{We} {don't} see any ${parsedNoun.original_input} to ${prettyverb}. `;
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.dov) {
      let qualified_count = parsedNoun.matches.qualified.length;
      let qualified_id, qualified_name, qualified_asset;
      if (1 === qualified_count) {
        qualified_id = parsedNoun.matches.qualified[0];
      }

      parsedNoun.matches.qualified = this.selectDOV(
        parsedNoun.matches.qualified,
        parsedVerb
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1135",
          "log",
          "high",
          `[${fx}] must_be.dov eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1585 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.dov`
        );
        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = `${qualified_asset.definite_article} ${getQualifiedName(qualified_asset)}`;
        }
        if (qualified_name) {
          msg += `{We} can't ${prettyverb} ${qualified_name}. `;
        } else {
          msg += `{We} can't ${prettyverb} any ${A.deserialize(
            parsedNoun.input
          )}. `;
        }
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.iov) {
      let qualified_count = parsedNoun.matches.qualified.length;
      let qualified_id, qualified_name, qualified_asset;
      if (1 === qualified_count) {
        qualified_id = parsedNoun.matches.qualified[0];
      }

      parsedNoun.matches.qualified = this.selectIOV(
        parsedNoun.matches.qualified,
        parsedVerb
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1137",
          "log",
          "high",
          `[${fx}] must_be.iov eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1057 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.iov`
        );

        let prep = this.game.getInput().getPreposition(nounIndex) || "";
        let dasset;
        let dname = "";
        if (nounIndex > 1) {
          dasset = this.game.getInput().getAsset(1);
          dname =
            dasset.noun ||
            (dasset.singlePluralPairs[0] && dasset.singlePluralPairs[0][0]
              ? dasset.singlePluralPairs[0][0]
              : this.game.getInput().getParsedNoun(1).input);
        }
        qualified_asset = this.game.getAsset(qualified_id);
        let results = doVerbAction(qualified_asset, verb);
        if ("undefined" !== typeof results) return results;
        qualified_name = `${qualified_asset.definite_article} ${getQualifiedName(qualified_asset)}`;

        // if player entered, for example, "wear jacket over bed"...
        // ...this version returns "You can't wear the jacket over the bed."

        // if (qualified_name) {
        //   msg += `{We} can't ${prettyverb} ${
        //     this.game.getInput().getAsset(1).definite_article
        //   } ${dname} ${prep} ${qualified_name}. `;
        // } else {
        //   msg += `{We} can't ${prettyverb} ${
        //     this.game.getInput().getAsset(1).definite_article
        //   } ${dname} ${prep} any ${A.deserialize(parsedNoun.input)}. `;
        // }

        // ...this version returns "You can't use the bed to wear the jacket."
        if (qualified_name) {
          msg += `{We} can't use ${qualified_name} to ${prettyverb} ${
            this.game.getInput().getAsset(1).definite_article
          } ${dname}. `;
        } else {
          msg += `{We} can't any ${A.deserialize(
            parsedNoun.input
          )} to ${prettyverb} ${
            this.game.getInput().getAsset(1).definite_article
          } ${dname}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    // if(must_be.tangible_or_substance)
    // {
    //   this.game.log( "log", "high", `[${fx}] must_be.tangible_or_substance receive:\n${parsedNoun.matches.qualified}`, 'Parser' );
    //   parsedNoun.matches.qualified = this.selectTangibleOrSubstance( parsedNoun.matches.qualified );
    //   if( !parsedNoun.matches.qualified.length )
    //   {
    //     this.game.log( "log", "high", `[${fx}] must_be.tangible_or_substance eliminated remaining assets ` , 'Parser' );
    //     this.game.debug(` | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.tangible_or_substance`);
    //     msg += `{We} {don't} see any "${parsedNoun.original_input}" {we} can ${this.dictionary.verbs[ parsedVerb ].prettyname}. `;
    //     if(msg) this.game.print( msg, output_class );
    //     return false;
    //   }
    //   this.game.log( "log", "high", `[${fx}] tangible_or_substance ${parsedNoun.matches.qualified}`, 'Parser' );
    // }

    if (must_be.global_substance) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectGlobalSubstance(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1140",
          "log",
          "high",
          `[${fx}] must_be.global_substance eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1064 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.global_substance`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          // msg += `{We} can't ${prettyverb} that. `;
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.substance) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectSubstance(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1142",
          "log",
          "high",
          `[${fx}] must_be.substance eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1065 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.substance`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          // msg += `{We} can't ${prettyverb} that. `;
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.reservoir) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectReservoir(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1144",
          "log",
          "high",
          `[${fx}] must_be.reservoir eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D2099 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.reservoir`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          // msg += `{We} can't ${prettyverb} that. `;
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.reservoir_if_substance) {
      let substance_id, substance;
      if (parsedNoun?.matches?.substance) {
        substance_id = parsedNoun.matches.substance;
        substance = this.game.getAsset(parsedNoun.matches.substance);
      }
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectReservoirIfSubstance(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1146",
          "log",
          "high",
          `[${fx}] must_be.reservoir_if_substance eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D2101 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.reservoir_if_substance`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          // msg += `{We} can't ${input.getInput()}. `;
          msg += `{We} {don't} see any ${
            substance ? substance.name : ""
          } suitable for ${gerund}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.reservoir_or_carried_if_substance) {
      let substance_id, substance;
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      if (parsedNoun?.matches?.substance) {
        substance_id = parsedNoun.matches.substance;
        substance = this.game.getAsset(parsedNoun.matches.substance);
      }
      parsedNoun.matches.qualified = this.selectReservoirOrCarriedIfSubstance(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1148",
          "log",
          "high",
          `[${fx}] must_be.reservoir_or_carried_if_substance eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D2126 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.reservoir_or_carried_if_substance`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          // msg += `{We} can't ${input.getInput()}. `;
          msg += `{We} {don't} see any ${
            substance ? substance.name : ""
          } suitable for ${gerund}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.character) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectCharacter(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1150",
          "log",
          "high",
          `[${fx}] must_be.character eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1066 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.character`
        );

        let prep = this.game.getInput().getPreposition(nounIndex) || "";
        let dobj = "";
        if (nounIndex === 2) {
          dobj = this.game.getInput().getAsset(1)?.articlename || "";
        }
        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }
        // otherwise offer generic response
        if (qualified_name) {
          // msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
          msg += `{We} can't ${prettyverb} ${dobj} ${prep} ${
            this.game.getAsset(qualified_id).articlename
          }. `;
        } else {
          msg += `{We} {don't} know of anyone named "${parsedNoun.original_input}". `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.parent) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectParent(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1152",
          "log",
          "high",
          `[${fx}] must_be.self_parent eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1069 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.self_parent`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} not ${qualified_asset.default_aspect ? qualified_asset.default_aspect : "on"} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We're} not on that. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_parent) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectNotParent(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1154",
          "log",
          "high",
          `[${fx}] must_be.not_parent eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1071 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_parent`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} already ${subject.getPlacePreposition()} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_parent_or_room) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectNotParentOrRoom(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1521",
          "log",
          "high",
          `[${fx}] must_be.not_parent_or_room eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1398 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_parent_or_room`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} already ${
            qualified_asset.default_aspect
              ? qualified_asset.default_aspect
              : "on"
          } ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can't ${input.getInput()}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.parent_or_room) {
      let qualified_id, qualified_asset, qualified_name;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectParentOrRoom(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1519",
          "log",
          "high",
          `[${fx}] must_be.self_parent eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1390 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.self_parent`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} not ${
            qualified_asset.default_aspect
              ? qualified_asset.default_aspect
              : "on"
          } ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          // msg += `{We} can't ${input.getInput()}. `;
          msg += `{We're} not on that. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.singular) {
      parsedNoun.matches.qualified = this.selectSingular(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1156",
          "log",
          "high",
          `[${fx}] must_be.singular eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1073 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.singular`
        );
        msg += `{We'll} have to do that one at a time. `;
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.in_inventory_if_takeable) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectInInventoryIfTakeable(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1158",
          "log",
          "high",
          `[${fx}] must_be.in_inventory_if_takeable eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1074 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.in_inventory_if_takeable`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} not carrying ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          let it = parsedNoun.original_input;
          msg += `{We're} not carrying any${
            it === "all" ? "thing" : " " + it
          }. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.in_inventory) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectInInventory(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1160",
          "log",
          "high",
          `[${fx}] must_be.in_inventory eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1075 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.in_inventory`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} not carrying ${
            player.knowsAbout(qualified_asset)
              ? qualified_asset.articlename
              : "any " + parsedNoun.original_input
          }. `;
        } else {
          let it = parsedNoun.original_input;
          msg += `{We're} not carrying any${
            it === "all" ? "thing" : " " + it
          }. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.in_hands) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectInHands(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1162",
          "log",
          "high",
          `[${fx}] must_be.in_hands eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1076 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.in_hands`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} not holding ${
            player.knowsAbout(qualified_asset)
              ? qualified_asset.articlename
              : "any " + parsedNoun.original_input
          }. `;
        } else {
          msg += `{We're} not holding any ${parsedNoun.original_input}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.in_hands_unless_reservoir) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectInHandsUnlessReservoir(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1164",
          "log",
          "high",
          `[${fx}] must_be.in_hands_unless_reservoir eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1448 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.in_hands_unless_reservoir`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} not holding ${
            player.knowsAbout(qualified_asset)
              ? qualified_asset.articlename
              : "any " + parsedNoun.original_input
          }. `;
        } else {
          msg += `{We're} not holding any ${parsedNoun.original_input}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_in_hands) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectNotInHands(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1166",
          "log",
          "high",
          `[${fx}] must_be.not_in_hands eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1077 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_in_hands`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} carrying ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We're} carrying the ${parsedNoun.original_input}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.held) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectHeld(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1168",
          "log",
          "high",
          `[${fx}] must_be.held eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1716 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.held`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} not holding ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We're} not holding any ${parsedNoun.original_input}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_in_inventory) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectNotInInventory(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1170",
          "log",
          "high",
          `[${fx}] must_be.not_in_inventory eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1078 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_in_inventory`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} not carrying ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We're} carrying the ${parsedNoun.original_input}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.worn) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectWorn(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1172",
          "log",
          "high",
          `[${fx}] must_be.worn eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1079 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.worn`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} not wearing ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We're} not wearing any ${parsedNoun.original_input}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_worn) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectNotWorn(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1174",
          "log",
          "high",
          `[${fx}] must_be.not_worn eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1080 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_worn`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We're} wearing ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We're} wearing any ${parsedNoun.original_input}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if ("all" === parsedNoun.input && must_be.not_worn_if_all) {
      parsedNoun.matches.qualified = this.selectNotWornIfAll(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1176",
          "log",
          "high",
          `[${fx}] must_be.not_worn_if_all eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1081 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_worn_if_all`
        );

        msg += `{We're} not carrying anything. `;

        this.game.print(msg, output_class);
        return false;
      }
    }

    if ("all" === parsedNoun.input && must_be.not_nested_inventory_if_all) {
      parsedNoun.matches.qualified = this.selectNotNestedInventoryIfAll(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1178",
          "log",
          "high",
          `[${fx}] must_be.not_nested_inventory_if_all eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1082 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_nested_in_inventory_if_all`
        );

        msg += `{We're} not carrying anything. `;

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.visible) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectVisible(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1180",
          "log",
          "high",
          `[${fx}] must_be.visible eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1083 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.visible`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't see ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can't see any ${parsedNoun.original_input}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.visible_if_tangible) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectVisibleIfTangible(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1182",
          "log",
          "high",
          `[${fx}] must_be.visible_if_tangible eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1610 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.visible`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't see ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can't see any ${parsedNoun.original_input}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.reachable) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectReachable(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1184",
          "log",
          "high",
          `[${fx}] must_be.reachable eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1084 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.reachable`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (parsedNoun.matches.direction) {
          msg += `{We} can't go ${parsedNoun.matches.direction}`;
        } else if (parsedNoun.isPlural && qualified_name) {
          msg += `{We} can't reach ${qualified_asset.definite_article} ${qualified_name}`;
        } else {
          msg += `{We} can't reach the ${parsedNoun.original_input}`;
        }

        if (subject.isNested()) {
          msg += ` from {our} position ${subject.getPostureGerund()} ${subject.getNestPreposition()} ${
            subject.getNestAsset().articlename
          }`;
        } else if (subject.posture !== "stand") {
          msg += ` while ${subject.getPostureGerund()}`;
          msg += `${subject.isOnFloor() ? " on the floor" : ""}`;
        }

        msg = msg.trim();
        msg += `. `;
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.reachable_if_tangible) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectReachableIfTangible(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1186",
          "log",
          "high",
          `[${fx}] must_be.reachable_if_tangible eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1176 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.reachable`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't reach ${qualified_asset.definite_article} ${qualified_name}`;
        } else {
          msg += `{We} can't reach the ${parsedNoun.original_input}`;
        }

        if (subject.isNested()) {
          msg += ` from your position ${subject.getPostureGerund()} ${subject.getNestPreposition()} ${
            subject.getNestAsset().articlename
          }`;
        }
        msg = msg.trim();
        msg += `. `;
        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.takeable) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectTakeable(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length && "all" === parsedNoun.input) {
        this.game.log(
          "L1188",
          "log",
          "high",
          `[${fx}] must_be.takeable eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1085 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.takeable`
        );
        msg += `There's nothing takeable in reach. `;
        this.game.print(msg, output_class);
        return false;
      }
      if (!parsedNoun.matches.qualified.length) {
        this.game.debug(
          `D1086 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.takeable`
        );
        var plural = false;
        if (parsedNoun.input.split("&").length > 1) {
          plural = true;
        } else if (parsedNoun.isPlural || parsedNoun.isGroup) {
          plural = true;
        }

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (plural) {
          msg += `{We} can't take them. `;
        } else if (qualified_name) {
          msg += `{We} can't take ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can't take ${parsedNoun.original_input}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.player) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectPlayer(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1192",
          "log",
          "high",
          `[${fx}] must_be.player eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D2109 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.player`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can only do that to {ourself}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_player) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectNotPlayer(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1194",
          "log",
          "high",
          `[${fx}] must_be.not_player eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D2110 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_player`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        msg += `{We} can't do that to {ourself}. `;

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.self) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectSelf(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1334",
          "log",
          "high",
          `[${fx}] must_be.self eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1184 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.self`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        if (qualified_name) {
          msg += `{We} can't ${parsedVerb} ${qualified_asset.definite_article} ${qualified_name}. `;
        } else {
          msg += `{We} can only do that to {ourself}. `;
        }

        this.game.print(msg, output_class);
        return false;
      }
    }

    if (must_be.not_self) {
      let qualified_id, qualified_name, qualified_asset;
      qualified_id = getQualifiedId(parsedNoun);
      parsedNoun.matches.qualified = this.selectNotSelf(
        parsedNoun.matches.qualified
      );
      if (!parsedNoun.matches.qualified.length) {
        this.game.log(
          "L1396",
          "log",
          "high",
          `[${fx}] must_be.not_self eliminated remaining assets `,
          "Parser"
        );
        this.game.debug(
          `D1185 | ${fx} | ${parsedVerb}.phrase${nounIndex}.noun.must_be.not_self`
        );

        if (qualified_id) {
          qualified_asset = this.game.getAsset(qualified_id);
          let results = doVerbAction(qualified_asset, verb);
          if ("undefined" !== typeof results) return results;
          qualified_name = getQualifiedName(qualified_asset);
        }

        msg += `{We} can't do that to {ourself}. `;

        this.game.print(msg, output_class);
        return false;
      }
    }

    // check qualified for exact match(es)
    if (1 < parsedNoun.matches.qualified.length) {
      var exactMatches = [];
      for (let i = 0; i < parsedNoun.matches.qualified.length; i++) {
        var object = this.game.getAsset(parsedNoun.matches.qualified[i]);
        if (object.id === parsedNoun.serialized_input) {
          exactMatches.push(object.id);
          continue;
        }
        for (var s = 0; s < object.synonyms.length; s++) {
          if (object.synonyms[s] === parsedNoun.serialized_input) {
            exactMatches.push(object.id);
            continue;
          }
        }
      }
      // copy exactMatches back to qualified
      if (0 < exactMatches.length) {
        // parsedNoun.matches.qualified = exactMatches.splice(0);
        // commented on 1/4/25 - this was a good idea for returning
        // exact matches but turned out to be a point of failure
        // ex: game has "window" and "playground_window" and searching
        // for "window" fails to return valid "playground_window"
        // leaving this note for @FUTURE consideration
      }
    }

    // can we narrow by class?
    var ajsclass = A.propercase(parsedNoun.serialized_input.toLowerCase());
    if (1 < parsedNoun.matches.qualified.length && adventurejs[ajsclass]) {
      var classMatches = [];
      for (let i = 0; i < parsedNoun.matches.qualified.length; i++) {
        var object = this.game.getAsset(parsedNoun.matches.qualified[i]);
        if (object instanceof adventurejs[ajsclass]) {
          classMatches.push(object.id);
          continue;
        }
      }
      // copy classMatches back to qualified
      if (0 < classMatches.length) {
        parsedNoun.matches.qualified = classMatches.splice(0);
      }
    }

    /**
     * If there's only one qualified match, we make a shortcut
     * reference at parsedNoun.qualified_object_id, mostly for use with
     * singular verbs.
     */
    parsedNoun.matches.qualifiedIndex = 0;
    if (1 === parsedNoun.matches.qualified.length) {
      this.game.log(
        "L1195",
        "log",
        "high",
        `[${fx}] found singular qualified ${parsedNoun.matches.qualified}`,
        "Parser"
      );
      parsedNoun.qualified_object_id = parsedNoun.matches.qualified[0];
    }

    this.game.log(
      "L1196",
      "log",
      "high",
      `[${fx}] qualifyParsedNoun() return: ${parsedNoun.matches.qualified}`,
      "Parser"
    );
    return parsedNoun;
  };
})();