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

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

  /**
   * @augments {adventurejs.Verb}
   * @class untie
   * @ajsnode game.dictionary.verbs.untie
   * @ajsconstruct MyGame.createVerb({ "name": "untie", [...] });
   * @ajsconstructedby adventurejs.Dictionary#createVerb
   * @hideconstructor
   * @ajsinstanceof Verb
   * @ajsnavheading ManipulationVerbs
   * @summary Verb meaning untie, as in "untie string from finger".
   * @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; untie shoelace</span>
   * You untie your shoelace. Hung from the tree branch by your shoe
   * as you are, this promptly results in you resuming your fall
   * toward the ground. Yoink! Your parachute catches in the same branch
   * and you're caught again, but thankfully only a couple of feet off
   * the ground this time.
   * </pre>
   * <p>
   * <strong>Untie</strong> a {@link adventurejs.Tangible|Tangible}
   * {@link adventurejs.Asset|Asset}.
   * Requires that the asset has
   * <code>asset.dov.untie.enabled</code> set to true. If the asset also has
   * its <code>asset.dov.tie.enabled</code> property set true and asset.is.tied
   * is true, asset.is.tied will be set to false. If the asset is connected to
   * another asset by tie, the connection will be broken.
   * </p>
   * <p>
   * To apply untie to a non-rope asset such as a shoe
   * or a bowtie, set its
   * <code class="property">asset.dov.tie.with_params.with_nothing</code>
   * to true.
   * </p>
   * <h3 class="example">Example</h3>
   * <pre class="display"><code class="language-javascript">MyGame.createAsset({
   *   class: "Clothing",
   *   name: "shoe"
   *   dov: {
   *     untie: true,
   *   },
   *   is: { tied: true },
   * });
   * <p>
   * If a rope is being tied to another asset, the other asset must have its
   * <code class="property">asset.iov.tie.enabled</code> set to true.
   * This is true even if a player inputs the other asset as a direct object.
   * For example, "tie rope to zebra" and "tie zebra with rope" will both be
   * parsed with the rope as the direct object and the zebra as the indirect object.
   * (Instances of the {@link adventurejs.Rope|Rope} class will inherit
   * a direct object subscription, though you may redefine it in order
   * to set custom parameters.)
   * </p>
   * <pre class="display"><code class="language-javascript">MyGame.createAsset({
   *   class: "Rope",
   *   name: "sisal rope"
   *   dov: {
   *     tie: {
   *       with_assets: [ "balustrade" ],
   *     },
   *     untie: true,
   *   },
   * });
   * MyGame.createAsset({
   *   class: "Thing",
   *   name: "balustrade",
   *   dov: { climb: true },
   *   iov: {
   *     tie: {
   *       with_assets: [ "sisal rope" ],
   *     },
   *     untie: true,
   *   },
   * });
   * </code></pre>
   * @ajsverbreactions doRemoveThisFromThat, doRemoveThatFromThis, doMoveThisToThat, doMoveThatToThis
   * @ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
   */
  A.Preverbs.untie = {
    name: "untie",
    synonyms: ["untie"],
    past_tense: "untied",
    prettyname: "untie",
    gerund: "untying",
    unstate: "tied",

    /**
     * @ajsverbstructures
     * @memberof untie
     */
    accepts_structures: [
      "verb noun", // untie rope, untie shoelace
      "verb noun preposition noun",
      // untie rope from thing, untie rope with thing?
      // "verb noun preposition noun preposition noun",
      // untie rope from thing with thing?
    ],

    /**
     * @memberof untie
     * @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 untie
     * @ajsverbphrase
     * phrase2:
     * {
     *   accepts_noun: true,
     *   requires_noun: true,
     *   noun_must_be:
     *   {
     *     known: true,
     *     tangible: true,
     *     present: true,
     *     visible: true,
     *     reachable: true,
     *   },
     * },
     */
    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", "with", "in"],
    },

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

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

      // example: untie rope (from thing)
      // example: untie table (untie rope from table)

      // sentence structure: verb noun
      if (input.hasStructure("verb noun")) {
        // does it have state?
        // only applies to non-ropes such as a shoe or a bowtie
        // which don't require an indirect object
        if (direct_object.is.tied) {
          return true;
        }

        // did player try to untie a rope without specifying
        // the thing it's tied to?
        if (
          direct_object.isDOV("tie") &&
          direct_object.getVerbConnectionCount("tie", "to_iov")
        ) {
          // get the first thing the rope is tied to
          // not currently accounting for multiple targets
          input.setAsset(
            2,
            this.game.getAsset(
              direct_object.getVerbConnections("tie", "to_iov")[0]
            )
          );
          input.setPreposition(2, "from");
          indirect_object = input.getAsset(2);
          indirect_preposition = "from";
          input.setStructure("verb noun preposition noun");
        }

        // did player try to untie a thing a rope is tied to
        // rather than the rope?
        if (
          direct_object.isIOV("tie") &&
          direct_object.getVerbConnectionCount("tie", "to_dov")
        ) {
          // get the first rope that is tied to this thing
          // not currently accounting for multiple ropes
          input.setAsset(
            2,
            this.game.getAsset(
              direct_object.getVerbConnections("tie", "to_dov")[0]
            )
          );
          input.swapNouns(1, 2);
          input.setPreposition(2, "from");
          direct_object = input.getAsset(1);
          indirect_object = input.getAsset(2);
          indirect_preposition = "from";
          input.setStructure("verb noun preposition noun");
        }
      } // verb noun

      // @TODO handling for "untie knots in/from rope"

      // can't tie to or tie with
      if (!direct_object.isDOV(this.name) && !direct_object.isIOV(this.name)) {
        this.game.debug(
          `D1482 | ${this.name}.js | ${direct_object.id}.dov.untie and .iov.untie are unset `
        );
        msg += `${direct_object.Articlename} can't be untied. `;
        this.handleFailure(msg);
        return null;
      }

      if (input.hasStructure("verb noun preposition noun")) {
        if (indirect_preposition === "") {
        }
      }

      // explicitly separate iov/dov check here because
      // ropes may have both, leading to false results
      if (direct_object.isDOV("tie")) {
        // if object is rope but not tied to anything
        if (!direct_object.getVerbConnectionCount("tie", "to_iov")) {
          // is another rope tied to this rope? that is a real thing
          if (
            direct_object.isIOV("tie") &&
            direct_object.getVerbConnectionCount("tie", "to_dov")
          ) {
            let other_rope = this.game.getAsset(
              direct_object.getVerbConnections("tie", "to_dov")[0]
            );
            if (other_rope) {
              input.setAsset(1, other_rope);
              direct_object = other_rope;
            }
          } else {
            // rope isn't tied to anything
            this.game.debug(
              `D1484 | ${this.name}.js | ${direct_object.id}.is.connected_by.tie.to_iov is unset `
            );
            msg += `${direct_object.Articlename_isnt} tied to anything. `;
            this.handleFailure(msg);
            return null;
          }
        }
      } else if (direct_object.isIOV("tie")) {
        // if object can have rope tied to it but doesn't
        if (!direct_object.getVerbConnectionCount("tie", "to_dov")) {
          this.game.debug(
            `D1485 | ${this.name}.js | ${direct_object.id}.is.connected_by.tie.to_dov is empty`
          );

          if (direct_object.allowVerbWithNothing("tie", "dov")) {
            msg += `${direct_object.Articlename_is} already ${this.past_tense}. `;
          } else {
            msg += `Nothing is tied to ${direct_object.articlename}. `;
          }

          this.handleFailure(msg);
          return null;
        }
      }
      return true;
    },

    doSuccess: function () {
      var input = this.game.getInput();
      var verb_phrase = input.verb_phrase;
      var player = this.game.getPlayer();
      var direct_object = input.getAsset(1);
      var direct_preposition = input.getPreposition(1);
      var indirect_object = input.getAsset(2);
      var indirect_preposition = input.getPreposition(2);
      var results;
      var msg = "";

      if (input.hasStructure("verb noun")) {
        // if we got here without an indirect object
        // it means none is required

        // apply state changes
        direct_object.is.tied = false;

        // compose output
        msg = `$(We) ${this.name} ${direct_object.articlename}. `;

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

      // apply state changes
      let tie = this.game.dictionary.verbs.tie;
      if (tie) {
        tie.unsetVerbConnection(direct_object, indirect_object);
      }

      // compose output
      msg +=
        `$(We) ${this.name}` +
        `${direct_preposition ? " " + direct_preposition : ""}` +
        `${direct_object ? " " + direct_object.articlename : ""}` +
        `${indirect_preposition ? " " + indirect_preposition : ""}` +
        `${indirect_object ? " " + indirect_object.articlename : ""}`;
      if (direct_object.getVerbConnectionCount("tie", "dov")) {
        msg += `, leaving it tied to ${this.game.getPrintableObjectList({
          objects: direct_object.getVerbConnections("tie", "to_iov"),
        })}`;
      }
      msg += `. `;

      if (
        this.game.parser.isParsingMultiple() &&
        -1 === input.output_class.indexOf("concatenate_output")
      ) {
        input.output_class += " concatenate_output ";
      }

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