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

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

  /**
   * @augments {adventurejs.Verb}
   * @class remove
   * @ajsnode game.dictionary.verbs.remove
   * @ajsredirectverb untie, detach, take
   * @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 Verbs_Subscriptions
   * @tutorial AdvancedVerbs_VerbAnatomy
   * @tutorial AdvancedVerbs_VerbProcess
   * @tutorial AdvancedVerbs_ModifyVerbs
   * @tutorial AdvancedVerbs_ModifyVerbs
   * @classdesc
   * <pre class="display border outline">
   * <span class="ajs-player-input">&gt; remove hat from frog</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 subject or
   * {@link adventurejs.NPC|NPCs}, automatically unzipping or unbuttoning them if needed.
   * </p>
   * @ajsverbreactions doRemoveThisFromThat, doRemoveThatFromThis, doMoveThisToThat, doMoveThatToThis
   * @ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
   */
  A.Preverbs.remove = {
    name: "remove",
    prettyname: "remove",
    past_tense: "removed",
    synonyms: ["remove"],
    gerund: "removing",

    /**
     * @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,
     *   preposition_must_be: [ '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,
      preposition_must_be: ["from"],
    },

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

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

      // if (!direct_object.isDOV("remove")) {
      //   this.game.debug(
      //     `D1923 | ${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;
      // }

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

        input.setAsset(2, parent);
        input.setPreposition(2, "from");
        input.setInferred(2);
        input.setStructure("verb noun preposition noun");
        indirect_object = parent;
        this.game.printInferred(`from ${parent.articlename}`);
      }

      // sentence structure: verb noun preposition noun
      if (input.hasStructure("verb noun preposition noun")) {
        // if direct_object is tied, redirect to untie
        if (
          direct_object.getVerbConnectionCount("tie", "to_iov") &&
          direct_object.isDOV("untie")
        ) {
          this.game.debug(
            `D1254 | ${this.name}.js | ${direct_object.id}.dov.tie, infer untie `
          );
          return this.game.dictionary.doVerb("untie");
        }

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

        // thing not in other thing
        if (!direct_object.isWithin(indirect_object)) {
          this.game.debug(
            `D1255 | ${this.name}.js | ${direct_object.id} place is not ${indirect_object.id} `
          );
          msg += `${direct_object.Articlename_isnt}  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 === subject.id
        ) {
          return true;
        }

        // direct_object is worn by subject but can't be removed
        // handcuffs for instance
        if (
          direct_object.is.worn &&
          !direct_object.isDOV("remove") &&
          indirect_object.id === subject.id
        ) {
          this.game.debug(
            `D1256 | ${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 !== subject.id &&
          (!direct_object.isDOV("remove") || !indirect_object.isIOV("take"))
        ) {
          this.game.debug(
            `D1257 | ${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 !== subject.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(
            `D1258 | ${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")) {
          this.game.debug(
            `D1259 | ${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
      if (this.game.hasVerb("take")) {
        this.game.debug(`D1260 | ${this.name}.js | infer take`);
        return this.game.dictionary.doVerb("take");
      }
    },

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

      // open any containers if necessary
      // we only do this for items nested in inventory
      if (
        direct_object.isWithin(subject) &&
        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 == subject.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 !== subject.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 !== subject.id && direct_object.takeable) {
        // remove thing from its current container
        results = parent.onRemoveThatFromThis(direct_object);
        if ("undefined" !== typeof results) return results;

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

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