Pre-release
Adventure.js Docs Downloads
Score: 0 Moves: 0
// empty.js
(function () {
  /*global adventurejs A*/
  "use strict";

  /**
   * @augments {adventurejs.Verb}
   * @class empty
   * @ajsnode game.dictionary.verbs.empty
   * @ajsconstruct MyGame.createVerb({ "name": "empty", [...] });
   * @ajsconstructedby adventurejs.Dictionary#createVerb
   * @hideconstructor
   * @ajsinstanceof Verb
   * @ajsnavheading ManipulationVerbs
   * @summary Verb meaning empty asset of other assets and/or substances.
   * @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; empty satchel on desk</span>
   * You empty the contents of the leather satchel on the desk.
   * </pre>
   * <p>
   * <strong>Pour</strong> is a special case that also doubles
   * for <strong>empty</strong>. Because
   * {@link adventurejs.Aspect|Aspects} can contain both
   * {@link adventurejs.Tangible|Tangibles} and
   * {@link adventurejs.Substance|Substances}, pouring or emptying
   * means that both the Tangibles and the Substances will fall out
   * of the source {@link adventurejs.Asset|Asset}
   * and into the destination Asset. If the target is a Tangible,
   * depending on its capacity, it may not be able to hold all of the
   * contents of the source Asset. In this case, excess Tangibles and
   * Substances will overflow onto the floor.
   * </p>
   * @ajsverbreactions
   * @ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
   */
  A.Preverbs.empty = {
    name: "empty",
    prettyname: "empty",
    past_tense: "emptied",
    synonyms: ["empty"],

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

    /**
     * @memberof empty
     * @ajsverbphrase
     * phrase1:
     * {
     *   accepts_noun:true,
     *   requires_noun: true,
     *   noun_must_be:
     *   {
     *     known: true,
     *     matter: true,
     *     present_if_tangible: true,
     *     reachable_if_tangible: true,
     *   },
     * },
     */
    phrase1: {
      accepts_noun: true,
      requires_noun: true,
      noun_must_be: {
        known: true,
        matter: true,
        present_if_tangible: true,
        reachable_if_tangible: true,
      },
    },

    /**
     * @memberof empty
     * @ajsverbphrase
     * phrase2:
     * {
     *   accepts_noun:true,
     *   noun_must_be:
     *   {
     *     known: true,
     *     matter: true,
     *     present_if_tangible: true,
     *     reachable_if_tangible: true,
     *   },
     *   accepts_preposition:true,
     *   requires_preposition: true,
     * },
     */
    phrase2: {
      accepts_noun: true,
      noun_must_be: {
        known: true,
        matter: true,
        present_if_tangible: true,
        reachable_if_tangible: true,
      },
      accepts_preposition: true,
      requires_preposition: true,
    },

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

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

      if (direct_object instanceof adventurejs.Tangible) {
        // true of verb noun and verb noun preposition noun

        // is direct_object closed?
        if (direct_object.isDOV("open")) {
          this.game.debug(
            `F1741 | ${this.name}.js | ${direct_object.id}.is.closed `
          );
          msg += `${direct_object.Articlename} is closed. `;
          this.handleFailure(msg);
          return null;
        }

        // is direct_object empty?
        if (
          !direct_object.doesContainAnySubstance() &&
          !direct_object.doesContainAnyAsset()
        ) {
          this.game.debug(
            `F1551 | ${this.name}.js | ${direct_object.id}.doesContainAnySubstance and .doesContainAnyAsset are false `
          );
          msg += `${direct_object.Articlename} is empty. `;
          this.handleFailure(msg);
          return null;
        }
      }

      if (input.hasStructure("verb noun")) {
        // empty knapsack
        if (direct_object instanceof adventurejs.Substance) {
          // can't empty a substance
          this.game.debug(
            `F1742 | ${this.name}.js | ${direct_object.id} is a substance rather than a container `
          );
          msg += `$(We) don't know how to empty ${direct_object.name}. `;
          this.handleFailure(msg);
          return null;
        }

        // already done closed and empty

        // is it not takeable? we'll allow input
        // like "empty drawer" to mean "take all from drawer"
        if (!direct_object.isDOV("take")) {
          if (direct_object.doesContainAnyAssetAt("in")) {
            var takeable_assets = [];
            for (var i = 0; i < direct_object.aspects.in.contents.length; i++) {
              var asset = this.game.getAsset(
                direct_object.aspects.in.contents[i]
              );
              if (asset.isDOV("take")) {
                takeable_assets.push(asset);
              }
              if (takeable_assets.length) {
                empty_means_take_all = true;
                input.verb_params.empty_means_take_all = true;
                input.verb_params.takeable_assets = takeable_assets;
              }
            }
          }

          if (
            !empty_means_take_all
            /* && !empty_means_unplug
          && !empty_means_take_plug */
          ) {
            this.game.debug(
              `F1744 | ${this.name}.js | ${direct_object.id}.dov.take.enabled is false and .aspects.in.contents is empty `
            );
            msg += `$(We) don't know how to empty ${direct_object.articlename}. `;
            this.handleFailure(msg);
            return null;
          }
        } else {
          // find target - player parent / room
          indirect_object = player.getNestOrPlaceAsset();
          indirect_preposition = player.getNestOrPlacePreposition();
          input.setAsset(2, indirect_object);
          input.setPreposition(2, indirect_preposition);
          input.setAssumed(2, true);
          input.setStructure("verb noun preposition noun");
        }
      }

      if (input.hasStructure("verb noun preposition noun")) {
        // empty suitcase onto bed
        // "empty water from bottle" is a thing you could say

        // is target the floor? set to room
        if (indirect_object instanceof adventurejs.Floor) {
          indirect_object = this.game.getCurrentRoom();
          input.setAsset(2, indirect_object);
        }

        if (!direct_object.isDOV("take")) {
          // we don't handle "empty a onto b" where a is not carried
          this.game.debug(
            `F1745 | ${this.name}.js | ${direct_object.id}.dov.take.enabled is false `
          );
          msg += `$(We) don't know how to empty ${direct_object.articlename} ${indirect_preposition} ${indirect_object.articlename}. `;
          this.handleFailure(msg);
          return null;
        }

        // is direct_object in player?
        // is it takeable? asking because "empty drawer" should mean take all from drawer
        if (direct_object.isDOV("take") && !direct_object.isIn(player)) {
          this.game.debug(
            `F1743 | ${this.name}.js | ${direct_object.id}.place not in player `
          );
          msg += `$(We're) not carrying ${direct_object.articlename}. `;
          this.handleFailure(msg);
          return null;
        }
      } // verb noun preposition noun

      return true;
    },

    doSuccess: function () {
      var input = this.game.getInput();
      var direct_object = input.getAsset(1);
      var indirect_object = input.getAsset(2);
      var indirect_preposition = input.getPreposition(2);
      var player = this.game.getPlayer();
      var msg = "";
      var results;
      var room = indirect_object instanceof adventurejs.Room;
      var target_object, target_preposition;
      var emptied_count = 0;
      var transferred = [];
      var spillover = [];
      var overflow = false;
      var substance_asset;
      var empty_this = false;
      var mixer;

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

      // parsed sentence structure: verb noun
      if (input.hasStructure("verb noun")) {
        if (input.verb_params.empty_means_take_all) {
          // take all from direct object
          var takeable_assets = input.verb_params.takeable_assets;
          for (var i = 0; i < takeable_assets.length; i++) {
            var asset = takeable_assets[i];
            // remove thing from its current container
            //results = asset_parent.onRemoveThatFromThis( direct_object );
            results = asset.moveFrom(direct_object);
            if ("undefined" !== typeof results) return results;

            // add thing to player's contents
            //results = player.onMoveThatToThis( direct_object, "in" );
            results = asset.moveTo("in", player);
            if ("undefined" !== typeof results) return results;
          }
          this.game.debug(`F1746 | ${this.name}.js | empty means take `);
          msg += `$(We) take ${this.game.getPrintableObjectList({
            objects: takeable_assets,
          })} from ${direct_object.articlename}. `;
        }

        // if(input.verb_params.empty_means_unplug)
        // {
        //   // unplug direct object
        // }
        // if(input.verb_params.empty_means_take_plug)
        // {
        //   // take plug from direct object
        // }

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

      if (input.hasStructure("verb noun preposition noun")) {
        this.game.debug(`F1747 | ${this.name}.js | empty means pour `);
        msg += `$(We) empty ${direct_object.articlename}`;
        if (
          "under" === indirect_preposition ||
          "behind" === indirect_preposition
        ) {
          msg += ` ${indirect_preposition} ${indirect_object.articlename}`;
        } else {
          msg += ` over ${room ? "the floor" : indirect_object.articlename}`;
        }
        msg += `. `;

        // handle substances
        if (direct_object.hasVesselAtAspect("in")) {
          if (0 < direct_object.aspects.in.vessel.getVolume()) {
            // direct_object has substance
            substance_asset = this.game.getAsset(
              direct_object.aspects.in.vessel.substance_id
            );

            if (
              room ||
              "under" === indirect_preposition ||
              "behind" === indirect_preposition
            ) {
              // if indirect_object is room, substance just spills out
              msg += `${substance_asset.Name} spills to the floor. `;
              empty_this = true;
            } else if (
              "on" === indirect_preposition &&
              !indirect_object.hasVesselAtAspect(indirect_preposition)
            ) {
              // player said "pour on" and target has no on aspect
              msg += `${substance_asset.Name} pours over ${indirect_object.articlename} and then drains away. `;
              empty_this = true;
            } else if (
              indirect_object.hasVesselAtAspect(indirect_preposition)
            ) {
              mixer = new adventurejs.SubstanceMixer(this.game.game_name).set({
                source_input: direct_object.id,
                source_aspect: "in",
                source_substance_id:
                  direct_object.aspects.in.vessel.substance_id,
                target_input: indirect_object.id,
                target_aspect: indirect_preposition,
              });
              results = mixer.mix();
              if (A.isFalseOrNull(results)) return results;
              mixer.target_vessel.vessel_is_known = true;

              msg += `${mixer.source_substance_asset.Name} pours from 
              ${mixer.source_asset.articlename} ${indirect_preposition} 
              ${
                indirect_preposition === "in" || indirect_preposition === "on"
                  ? " to"
                  : ""
              } 
              ${mixer.target_asset.articlename}`;

              if (mixer.can_drain_target) {
                msg += `, where it quickly drains away`;
              } else if (mixer.did_overflow_target) {
                msg += `, overflowing ${mixer.target_asset.articlename} with 
                ${
                  mixer.did_mix_substances
                    ? mixer.output_substance_asset.name
                    : mixer.source_substance_asset.name
                }`;
              } else if (mixer.did_fill_target) {
                msg += `, filling ${mixer.target_asset.articlename} with 
                ${
                  mixer.did_mix_substances
                    ? mixer.output_substance_asset.name
                    : mixer.source_substance_asset.name
                }`;
              } else if (mixer.did_mix_substances) {
                msg += `, resulting in ${mixer.output_substance_asset.name}`;
              }
              msg += `. `;
            } else {
              msg += `${substance_asset.Name} pours over ${indirect_object.articlename} and then spills to the floor. `;
              empty_this = true;
            }
          }

          if (
            empty_this &&
            isFinite(direct_object.aspects.in.vessel.getVolume())
          ) {
            direct_object.aspects.in.vessel.setVolume(0);
          }
        } // handle substances

        // handle physical assets
        if (0 < direct_object.aspects.in.contents.length) {
          for (
            var i = direct_object.aspects.in.contents.length - 1;
            i > -1;
            i--
          ) {
            var content_object = this.game.getAsset(
              direct_object.aspects.in.contents[i]
            );
            target_object = indirect_object;
            target_preposition = indirect_preposition;
            overflow = false;
            emptied_count++;

            // if indirect_object is not the room, we need to check
            // if it can hold each object
            // if it can't, objects should fall to the floor

            if (room) {
              transferred.push(content_object);
            } else {
              if (
                indirect_object.canContainAssetAt(
                  content_object,
                  indirect_preposition
                )
              ) {
                transferred.push(content_object);
              } else {
                target_object = this.game.getCurrentRoom();
                target_preposition = "in";
                spillover.push(content_object);
                overflow = true;
              }
            }

            // remove content_object from direct_object
            results = direct_object.onRemoveThatFromThis(content_object);
            if ("undefined" !== typeof results) return results;

            // set content_object's target_object
            // if no target_object was specified, set to player's parent
            results = target_object.onMoveThatToThis(
              content_object,
              target_preposition
            );
            if ("undefined" !== typeof results) return results;

            // msg += "and you succeed. ";
          }
        }

        // print the results of physical assets
        if (room) {
          // everything falls to the floor
          switch (emptied_count) {
            case 0:
              break;
            case 1:
              msg += "A single item tumbles out and falls to the floor. ";
              break;
            case 2:
              msg += "Two items tumble out and fall to the floor. ";
              break;
            case 3:
              msg += "Several items tumble out and fall to the floor. ";
              break;
            default:
              msg += "A number of items tumble out and fall to the floor. ";
              break;
          }
        } else {
          var to_target =
            indirect_preposition +
            (indirect_preposition === "in" || indirect_preposition === "on"
              ? " to "
              : " ") +
            indirect_object.articlename;

          switch (emptied_count) {
            // some items are transferred to target, some may fall to the floor
            case 0:
              break;
            case 1:
              msg += `A single item tumbles ${to_target}`;
              switch (spillover.length) {
                case 0:
                  msg += ". ";
                  break;
                case 1:
                  msg += " then falls to the floor. ";
                  break;
              }
              break;
            case 2:
              msg += `Two items tumble ${to_target}`;
              switch (spillover.length) {
                case 0:
                  msg += ". ";
                  break;
                case 1:
                  msg += ", and one falls to the floor. ";
                  break;
                case 2:
                  msg += " then fall to the floor. ";
                  break;
              }
              break;
            case 3:
              msg += `Several items tumble ${to_target}`;
              switch (spillover.length) {
                case 0:
                  msg += ". ";
                  break;
                case 1:
                  msg += ", and one falls to the floor. ";
                  break;
                case 2:
                  msg += ", and a couple of them fall to the floor. ";
                  break;
                case 3:
                  msg += " then fall to the floor. ";
                  break;
              }
              break;
            default:
              msg += `A number of items tumble ${to_target}`;
              switch (spillover.length) {
                case 0:
                  msg += ". ";
                  break;
                case 1:
                  msg += ", and one falls to the floor. ";
                  break;
                case 2:
                  msg += ", and a couple of them fall to the floor. ";
                  break;
                case 3:
                  msg += ", and several fall to the floor. ";
                  break;
                default:
                  msg += " and some fall to the floor. ";
                  break;
              }
              break;
          }
        }
      }

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