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

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

  /**
   * @augments {adventurejs.Verb}
   * @class remove
   * @ajsnode game.dictionary.verbs.remove
   * @ajsconstruct MyGame.createVerb({ "name": "remove", [...] });
   * @ajsconstructedby adventurejs.Dictionary#createVerb
   * @hideconstructor
   * @ajsinstanceof Verb
   * @ajsnavheading ManipulationVerbs
   * @summary Verb meaning remove, as in "remove cap from toothpaste".
   * @tutorial Scripting_VerbSubscriptions
   * @tutorial Verbs_VerbAnatomy
   * @tutorial Verbs_VerbProcess
   * @tutorial Verbs_ModifyVerbs
   * @tutorial Verbs_WriteVerbs
   * @classdesc
   * <pre class="display border outline">
   * <span class="input">&gt; remove hat from from</span>
   * You try to remove the top hat from the singing frog. He leans
   * neatly away without moving from his spot on the piano, causing you to
   * miss and plunge hands first into the taut piano strings. You leap
   * backwards with strings wrapped around you and crash into the drum
   * set, trodding loudly upon the snare drum.
   * The sudden cacophony draws the conductor, who whacks you about
   * the head and shoulders with his baton and chases you into the
   * stage curtains. You wrestle with the curtains, which tear and fall
   * heavily down upon you. You climb to your feet, wrapped in twisted
   * curtains and piano strings, one foot stuck in a drum, and find
   * yourself on stage, bathed in spotlight, facing the shocked crowd.
   * </pre>
   * <p>
   * <strong>Remove</strong> may be interpreted ambiguously. It tries to respond
   * contextually depending on the properties of the objects provided.
   * Remove may redirect to
   * {@link untie},
   * {@link detach}, or
   * {@link take}.
   * Remove may also remove items worn by the player or
   * {@link adventurejs.NPC|NPCs}, automatically unzipping or unbuttoning them if needed.
   * </p>
   * @ajsverbreactions
   * @ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
   */
  A.Preverbs.remove = {
    name: "remove",
    prettyname: "remove",
    past_tense: "removed",
    synonyms: ["remove"],

    /**
     * @ajsverbstructures
     * @memberof remove
     */
    accepts_structures: ["verb noun", "verb noun preposition noun"],

    /**
     * @memberof remove
     * @ajsverbphrase
     * phrase1:
     * {
     *   accepts_noun:true,
     *   requires_noun: true,
     *   noun_must_be:
     *   {
     *     known: true,
     *     tangible: true,
     *     present: true,
     *     visible: true,
     *     reachable: true,
     *   },
     * },
     */
    phrase1: {
      accepts_noun: true,
      requires_noun: true,
      noun_must_be: {
        known: true,
        tangible: true,
        present: true,
        visible: true,
        reachable: true,
      },
    },

    /**
     * @memberof remove
     * @ajsverbphrase
     * phrase2:
     * {
     *   accepts_noun:true,
     *   requires_noun: true,
     *   noun_must_be:
     *   {
     *     known: true,
     *     tangible: true,
     *     present: true,
     *     visible: true,
     *     reachable: true,
     *   },
     *   accepts_preposition: true,
     *   requires_preposition: true,
     *   accepts_these_prepositions: [ 'from' ],
     * },
     */
    phrase2: {
      accepts_noun: true,
      requires_noun: true,
      noun_must_be: {
        known: true,
        tangible: true,
        present: true,
        visible: true,
        reachable: true,
      },
      accepts_preposition: true,
      requires_preposition: true,
      accepts_these_prepositions: ["from"],
    },

    /**
     * @memberof remove
     * @ajsverbparams
     * with_params: {},
     */
    with_params: {},

    doTry: function () {
      var input = this.game.getInput();
      var player = this.game.getPlayer();
      var direct_object = input.getAsset(1);
      var indirect_object = input.getAsset(2);
      var msg = "";

      // if (!direct_object.isDOV("remove")) {
      //   this.game.debug(
      //     `F1923 | ${this.name}.js | ${direct_object.id}.dov.${this.name} not enabled `
      //   );
      //   msg += `$(We) can't ${this.name} ${direct_object.articlename}. `;
      //   this.handleFailure(msg);
      //   return null;
      // }

      // parsed sentence structure: verb noun
      if (input.hasStructure("verb noun")) {
        var parent = direct_object.getPlaceAsset();

        input.setAsset(2, parent);
        input.setPreposition(2, "from");
        input.setAssumed(2);
        input.setStructure("verb noun preposition noun");
        indirect_object = parent;
      }

      // sentence structure: verb noun preposition noun
      if (input.hasStructure("verb noun preposition noun")) {
        // if direct_object is tied, redirect to untie
        if (direct_object.dov.tie?.with_params.connections.length) {
          this.game.debug(
            `F1254 | ${this.name}.js | ${direct_object.id}.dov.tie, infer untie `,
          );
          this.game.dictionary.doVerb("untie");
          return null;
        }

        // if direct_object is attached, redirect to detach
        if (direct_object.isPlacedAtAspect("attached")) {
          this.game.debug(
            `F1254 | ${this.name}.js | ${direct_object.id} is attached, infer detach `,
          );
          this.game.dictionary.doVerb("detach");
          return null;
        }

        // thing not in other thing
        if (!direct_object.isIn(indirect_object)) {
          this.game.debug(
            `F1255 | ${this.name}.js | ${direct_object.id} place is not ${indirect_object.id} `,
          );
          msg += `${direct_object.Articlename} isn't in ${indirect_object.articlename}. `;
          this.handleFailure(msg);
          return null;
        }

        // if direct_object is worn & can be removed, take it off
        if (
          direct_object.is.worn &&
          direct_object.isDOV("remove") &&
          indirect_object.id === player.id
        ) {
          return true;
        }

        // direct_object is worn by player but can't be removed
        // handcuffs for instance
        if (
          direct_object.is.worn &&
          !direct_object.isDOV("remove") &&
          indirect_object.id === player.id
        ) {
          this.game.debug(
            `F1256 | ${this.name}.js | ${direct_object.id}.dov.remove.enabled is false `,
          );
          msg += `$(We) can't remove ${direct_object.articlename}. `;

          this.handleFailure(msg);
          return null;
        }

        // direct_object is worn by other char but can't be removed
        if (
          direct_object.is.worn &&
          indirect_object !== player.id &&
          (!direct_object.isDOV("remove") || !indirect_object.isIOV("take"))
        ) {
          this.game.debug(
            `F1257 | ${this.name}.js | ${direct_object.id}.dov.remove is unset or ${indirect_object.id}.iov.take is unset`,
          );
          msg += `$(We) can't remove ${direct_object.articlename} from ${indirect_object.articlename}. `;
          this.handleFailure(msg);
          return null;
        }

        // worn by a character like a pet that you can take from?
        // as in "remove collar from cat"
        if (
          direct_object.is.worn &&
          indirect_object.id !== player.id &&
          direct_object.isDOV("remove") &&
          indirect_object.isIOV("take")
        ) {
          // any conditions that would prevent removing clothing?
          // none at the moment so just proceed to success
          return true;
        }

        // try taking a thing from a character
        // who can't be taken from
        // authors will probably want some control over this interation
        if (!indirect_object.isIOV("take")) {
          this.game.debug(
            `F1258 | ${this.name}.js | ${indirect_object.id}.iov.take is unset`,
          );
          msg += `$(We) can't take anything from ${indirect_object.articlename}. `;
          this.handleFailure(msg);
          return null;
        }

        if (direct_object.is.worn && !direct_object.isDOV("remove")) {
          // TODO article + noun or proper noun, depending on if is character
          this.game.debug(
            `F1259 | ${this.name}.js | ${direct_object.id}.dov.remove.enabled is false`,
          );
          msg += `$(We) can't remove ${direct_object.articlename} from ${indirect_object.articlename}. `;
          this.handleFailure(msg);
          return null;
        }
      }

      // if it's none of these things, redirect to take
      this.game.debug(`F1260 | ${this.name}.js | infer take`);
      this.game.dictionary.doVerb("take");
      return null;
    },

    doSuccess: function () {
      var input = this.game.getInput();
      var player = this.game.getPlayer();
      var direct_object = input.getAsset(1);
      var indirect_object = input.getAsset(2);
      var parent = direct_object.getPlaceAsset();
      var msg = "";
      var closedAnscestors = [];
      var results;

      this.game.debug(`F1261 | ${this.name}.js | print doSuccess`);

      // open any containers if necessary
      // we only do this for items nested in inventory
      if (direct_object.isIn(player) && direct_object.areAnscestorsClosed()) {
        closedAnscestors = direct_object.getClosedAnscestors();

        for (var i = 0; i < closedAnscestors.length; i++) {
          this.game.getAsset(closedAnscestors[i]).is.closed = false;
          closedAnscestors[i] = this.game.getAsset(closedAnscestors[i]).name;
        }

        if (closedAnscestors.length > 0) {
          msg += "$(We) open the ";
          for (var i = 0; i < closedAnscestors.length; i++) {
            if (
              closedAnscestors.length > 2 &&
              i < closedAnscestors.length - 2
            ) {
              msg += ", ";
            }
            if (
              closedAnscestors.length > 1 &&
              i === closedAnscestors.length - 1
            ) {
              msg += " and the ";
            }
            msg += closedAnscestors[i];
          }
          msg += ". ";
        }
      }

      // if direct_object is worn, take it off
      if (direct_object.is.worn && indirect_object.id == player.id) {
        results = direct_object.unfasten();
        msg += results ? `$(We) ${results}, then remove, ` : `$(We) remove `;
        msg += `${direct_object.articlename}. `;
        direct_object.is.worn = false;
      }

      // worn by a character like a pet that you can take from
      // what if it's sexy sex? removing clothes from a partner
      if (
        direct_object.is.worn &&
        indirect_object.id !== player.id &&
        indirect_object.isIOV("take")
      ) {
        results = direct_object.unfasten();
        msg += results ? `$(We) ${results}, then remove, ` : `$(We) remove `;
        msg += `${direct_object.articlename} from ${indirect_object.articlename}. `;
        direct_object.is.worn = false;
      }

      if (indirect_object.id !== player.id && direct_object.takeable) {
        // remove thing from its current container
        results = parent.onRemoveThatFromThis(direct_object);
        if ("undefined" !== typeof results) return results;

        // add thing to player's contents
        results = player.onMoveThatToThis(direct_object, "in");
        if ("undefined" !== typeof results) return results;
      }

      // print output
      this.handleSuccess(msg, direct_object);
      return true;
    },
  };
})(); // remove