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

  /**
   * @ajspath adventurejs.Atom.SubstanceMixer
   * @ajsconstruct var mixer = new adventurejs.SubstanceMixer( this.game.game_name )
   * @augments adventurejs.Atom
   * @class adventurejs.SubstanceMixer
   * @ajsinternal
   * @ajsnavheading BaseClasses
   * @param {String} game_name Name of top level game instance that is scoped to window.
   * @summary Shaken, not stirred.
   * @todo Solid+liquid->slurry.
   * @classdesc
   * <p>
   * <strong>SubstanceMixer</strong> is a special internal class
   * that is instantiated whenever
   * {@link adventurejs.Substance|Substances} are mixed
   * together from two (or in theory, more) sources.
   * For instance, if player pours a glass full of liquid
   * into a bowl that also contains liquid, we make a new
   * SubstanceMixer to handle the interaction. This is
   * true even if both Vessels contain the same
   * Substance. SubstanceMixer handles:
   * <ul>
   * <li>temperature conversion, if SubstancesContainers are
   * at different temperatures</li>
   * <li>volume operations - comparing available volume in
   * source and destination, removing part or all from
   * source as needed</li>
   * <li>mixwith handling, if two Substances are set to mix
   * with each other to form a third Substance</li>
   * <li>overflow of the target container if needed</li>
   * </ul>
   * </p>
   * <h3 class="examples">Example:</h3>
   * <pre class="display"><code class="language-javascript">
   * </code></pre>
   **/
  class SubstanceMixer extends adventurejs.Atom {
    constructor(game_name) {
      super(undefined, game_name);
      this.game_name = game_name;

      this.class = "SubstanceMixer";
      //this.game_name = game_name;

      /**
       * <b>source_input</b> might be an asset id or a tri-part
       * asset:aspect:substance id string. We're prepared to
       * handle either to get an asset, or receive an asset object directly.
       * @var {String} adventurejs.SubstanceMixer#source_input
       * @default ""
       */
      this.source_input = "";

      /**
       * <b>source_asset</b> is an asset with a vessel that contains the source substance.
       * @var {Object|null} adventurejs.SubstanceMixer#source_asset
       * @default null
       */
      this.source_asset = null;

      /**
       * <b>source_aspect</b> is the asset aspect that contains
       * the source substance.
       * @var {String} adventurejs.SubstanceMixer#source_aspect
       * @default ""
       */
      this.source_aspect = "";

      /**
       * <b>source_vessel</b> is a reference to the Vessel object in the source asset.
       * @var {Object|null} adventurejs.SubstanceMixer#source_vessel
       * @default null
       */
      this.source_vessel = null;

      /**
       * <b>source_substance_asset</b> is the asset of the substance that is contained.
       * @var {Object|null} adventurejs.SubstanceMixer#source_substance_asset
       * @default null
       */
      this.source_substance_asset = null;

      /**
       * <b>source_substance_id</b> is the id of the substance asset that is contained, ie 'sand' or 'water'.
       * @var {String} adventurejs.SubstanceMixer#source_substance_id
       * @default ""
       */
      this.source_substance_id = "";

      /**
       * <b>source_split</b> is used to store the provided source_input if it is a tri-part string.
       * @var {Array|String|null} adventurejs.SubstanceMixer#source_split
       * @default null
       */
      this.source_split = null;

      /**
       * <b>source_volume</b> is the volume of the substance in the source vessel.
       * @var {int} adventurejs.SubstanceMixer#source_volume
       * @default 0
       */
      this.source_volume = 0;

      /**
       * <b>source_volume_used</b> stores the volume of the source substance that has been used.
       * @var {int} adventurejs.SubstanceMixer#source_volume_used
       * @default 0
       */
      this.source_volume_used = 0;

      /**
       * <b>target_input</b> might be an asset id or a tri-part
       * asset:aspect:substance id string. We're prepared to
       * handle either to get an asset, or receive an asset object directly.
       * @var {String} adventurejs.SubstanceMixer#target_input
       * @default ""
       */
      this.target_input = "";

      /**
       * <b>target_asset</b> is an asset with a vessel that receives the source substance.
       * @var {Object|null} adventurejs.SubstanceMixer#target_asset
       * @default null
       */
      this.target_asset = null;

      /**
       * <b>target_aspect</b>  is the asset aspect which will receive
       * the source substance.
       * @var {String} adventurejs.SubstanceMixer#target_aspect
       * @default ""
       */
      this.target_aspect = "";

      /**
       * <b>target_vessel</b> is a reference to the Vessel object in the target asset.
       * @var {Object|null} adventurejs.SubstanceMixer#target_vessel
       * @default null
       */
      this.target_vessel = null;

      /**
       * <b>target_substance_asset</b> is the asset of the substance that is
       * contained by the target asset.
       * @var {Object|null} adventurejs.SubstanceMixer#target_substance_asset
       * @default null
       */
      this.target_substance_asset = null;

      /**
       * <b>target_substance_id</b> is the id of the substance asset that
       * is contained in the target asset, ie 'sand' or 'water'.
       * @var {String} adventurejs.SubstanceMixer#target_substance_id
       * @default ""
       */
      this.target_substance_id = "";

      /**
       * <b>target_split</b> is used to store the provided target_input if it is a tri-part string.
       * @var {Array|String|null} adventurejs.SubstanceMixer#target_split
       * @default null
       */
      this.target_split = null;

      /**
       * <b>target_volume</b> is the volume of the substance in the target vessel.
       * @var {float} adventurejs.SubstanceMixer#target_volume
       * @default 0.0
       */
      this.target_volume = 0.0;

      /**
       * <b>target_freevolume</b> is the free volume of the substance in the target vessel.
       * @var {float} adventurejs.SubstanceMixer#target_freevolume
       * @default 0.0
       */
      this.target_freevolume = 0.0;

      /**
       * <b>output_substance_asset</b> is the asset of the substance that
       * results from mixing (which may just be the source substance).
       * @var {Object|null} adventurejs.SubstanceMixer#output_substance_asset
       * @default null
       */
      this.output_substance_asset = null;

      /**
       * <b>output_substance_id</b> is the id of the substance asset that
       * is that results from mixing, ie 'sand' or 'water'.
       * @var {String} adventurejs.SubstanceMixer#output_substance_id
       * @default ""
       */
      this.output_substance_id = "";

      /**
       * <b>target_already_full</b> is used to indicate that the target vessel
       * is already full.
       * @var {Boolean} adventurejs.SubstanceMixer#target_already_full
       * @default false
       */
      this.target_already_full = false;

      /**
       * <b>did_fill_target</b> is used to indicate that the target vessel
       * was filled by the source substance.
       * @var {Boolean} adventurejs.SubstanceMixer#did_fill_target
       * @default false
       */
      this.did_fill_target = false;

      /**
       * <b>did_mix_substances</b>
       * @var {Boolean} adventurejs.SubstanceMixer#did_mix_substances
       * @default false
       */
      this.did_mix_substances = false;

      /**
       * <b>did_displace_substance</b> is used if no mixwiths
       * are provided. Source substance will simply displace
       * target substance.
       * @var {Boolean} adventurejs.SubstanceMixer#did_displace_substance
       * @default false
       */
      this.did_displace_substance = false;

      /**
       * <b>can_drain_target</b> is used to indicate that the target vessel
       * can be drained.
       * @var {Boolean} adventurejs.SubstanceMixer#can_drain_target
       * @default false
       */
      this.can_drain_target = false;

      /**
       * <b>did_overflow_target</b> may be set to true
       * if the source is an emitter that emits a greater
       * volume than the target can hold.
       * @var {Boolean} adventurejs.SubstanceMixer#did_overflow_target
       * @default false
       */
      this.did_overflow_target = false;

      /**
       * <b>can_overflow_target</b> overrides did_overflow_target
       * if it's true. It's used by fill verb, the idea being that,
       * while an automated emitter might overflow a target,
       * a person consciously filling a vessel would not.
       * @var {Boolean} adventurejs.SubstanceMixer#can_overflow_target
       * @default false
       */
      this.can_overflow_target = false;
    }

    /**
     * Mix two substances. This function acts upon the source /
     * target objects, and saves its results to the
     * SubstanceMixer object for reference by the caller.
     * @memberof adventurejs.SubstanceMixer
     * @method adventurejs.SubstanceMixer#mix
     * @returns {boolean}
     */
    mix() {
      // SOURCE ASSET
      // asset:aspect:substance
      this.source_split = this.source_input.split(":");
      if (!this.source_asset) {
        // regardless of whether player input a substance containing object
        // or they entered a substance and we parsed asset:aspect:substance,
        // source_split[0] will be the container
        this.source_asset = this.game.getAsset(this.source_split[0]);
      }
      if (Object(this.source_asset) !== this.source_asset) {
        this.game.log(
          "warn",
          0,
          "SubstanceMixer.source_asset undefined",
          "SubstanceMixer"
        );
        return false;
      }
      this.game.log(
        "log",
        "high",
        "SubstanceMixer.js > source_asset " + this.source_asset.id,
        "SubstanceMixer"
      );

      // SOURCE ASPECT
      // check for input that was parsed into "bowl:in:water" format
      if (!this.source_aspect) {
        if (1 < this.source_split.length) {
          // if they entered a substance and we parsed asset:aspect:substance,
          // source_split[1] will be the aspect
          this.source_aspect = this.source_split[1];
        } else {
          // we did not receive a triplet, presumably player referred
          // directly to container, not substance, so get
          // the object's substance location...
          this.source_aspect = this.source_asset.getAspectWithVessel();
        }
      }
      if (!this.source_aspect) {
        this.game.log(
          "warn",
          0,
          "SubstanceMixer.source_aspect undefined",
          "SubstanceMixer"
        );
        return false;
      }
      this.game.log(
        "log",
        "high",
        "SubstanceMixer.js > source_aspect " + this.source_aspect,
        "SubstanceMixer"
      );

      // SOURCE SUBSTANCE
      if (!this.source_substance_id) {
        if (3 === this.source_split.length) {
          // asset:aspect:substance
          this.source_substance_id = this.source_split[2];
        } else {
          this.source_substance_id =
            this.source_asset.aspects[this.source_aspect].vessel.substance_id;
        }
      }
      if (!this.source_substance_id) {
        this.game.log(
          "warn",
          0,
          "SubstanceMixer.source_substance_id undefined",
          "SubstanceMixer"
        );
        return false;
      }
      this.game.log(
        "log",
        "high",
        "SubstanceMixer.js > source_substance_id " + this.source_substance_id,
        "SubstanceMixer"
      );

      // SOURCE SUBSTANCE CONTAINER
      if (!this.source_vessel) {
        // ... and then get the substance
        this.source_vessel =
          this.source_asset.aspects[this.source_aspect].vessel; //.substance_id;
      }
      if (!this.source_vessel) {
        this.game.log(
          "warn",
          0,
          "SubstanceMixer.source_vessel undefined",
          "SubstanceMixer"
        );
        return false;
      }
      this.game.log(
        "log",
        "high",
        "SubstanceMixer.js > source_vessel " + this.source_vessel.id,
        "SubstanceMixer"
      );

      // SOURCE SUBSTANCE ASSET
      if (this.source_substance_id) {
        // get substance object by id from lookup table
        this.source_substance_asset = this.game.getAsset(
          this.source_substance_id
        );
      }
      if (Object(this.source_substance_asset) !== this.source_substance_asset) {
        this.game.log(
          "warn",
          0,
          "SubstanceMixer.source_substance_asset undefined",
          "SubstanceMixer"
        );
        return false;
      }
      this.game.log(
        "log",
        "high",
        "SubstanceMixer.js > source_substance_asset " +
          this.source_substance_asset.id,
        "SubstanceMixer"
      );

      // TARGET ASSET
      this.target_split = this.target_input.split(":");
      if (!this.target_asset) {
        this.target_asset = this.game.getAsset(this.target_split[0]);
      }
      this.game.log(
        "log",
        "high",
        "SubstanceMixer.js > target_asset " + this.target_asset.id,
        "SubstanceMixer"
      );

      // TARGET ASPECT
      if (!this.target_aspect && 1 < this.target_split.length) {
        this.target_aspect = this.target_split[1];
      }
      console.warn("target_asset", this.target_asset);
      console.warn("target_aspect", this.target_aspect);
      if (!this.target_aspect) {
        // we did not receive a triplet, presumably player referred
        // directly to container, not substance, so get
        // the object's substance location...
        this.target_aspect = this.target_asset.getAspectWithVessel();
      }
      console.warn("target_aspect", this.target_aspect);
      this.game.log(
        "log",
        "high",
        "SubstanceMixer.js > target_aspect " + this.target_aspect,
        "SubstanceMixer"
      );

      // TARGET SUBSTANCE
      if (!this.target_substance_id && 2 < this.target_split.length) {
        this.target_substance_id = this.target_split[2];
      }
      this.game.log(
        "log",
        "high",
        "SubstanceMixer.js > target_substance_id " + this.target_substance_id,
        "SubstanceMixer"
      );

      // TARGET SUBSTANCE CONTAINER
      if (!this.target_vessel) {
        // ... and the substance it contains
        this.target_vessel =
          this.target_asset.aspects[this.target_aspect].vessel;
      }
      this.game.log(
        "log",
        "high",
        "SubstanceMixer.js > target_vessel " + this.target_vessel.id,
        "SubstanceMixer"
      );

      // TARGET SUBSTANCE ASSET
      if (!this.target_substance_id) {
        // ... and the substance it contains
        this.target_substance_id = this.target_vessel.substance_id;
      }
      if (this.target_substance_id) {
        // get the substance object from its id
        this.target_substance_asset = this.game.getAsset(
          this.target_substance_id
        );
      }
      this.game.log(
        "log",
        "high",
        "SubstanceMixer.js > target_substance_id " + this.target_substance_id,
        "SubstanceMixer"
      );

      // Is target a drain or has target got a drain?
      this.can_drain_target = this.target_vessel.can_drain;

      // Is target draining or a body of water?
      // Either case effectively means it can accept
      // infinite volume without mixing substance or temp.
      if (
        Infinity === this.target_vessel.volume ||
        this.target_vessel.can_drain === true
      ) {
        // Is source an emitter?
        if (this.source_asset instanceof adventurejs.SubstanceEmitter) {
          /**
           * @todo source emitter mixed into infinite/draining target?
           */
          // ? check this.source_asset.volume_of_flow_per_turn ?
        } // Or is source a container?
        else {
          // if the source is finite and the target is infinite,
          // target will take all of source
          // and we will not mix
          if (
            false ===
              this.source_asset instanceof adventurejs.SubstanceEmitter &&
            isFinite(this.source_vessel.volume)
          ) {
            this.game.log(
              "log",
              "high",
              "SubstanceMixer.js > target is infinite, source is finite, so target will take all of source",
              "SubstanceMixer"
            );
            this.source_vessel.volume = 0;
          }
        }
      } else {
        // Target is not draining nor is it a body of water

        // if target contains a different substance from source
        // see if there's a mixwith for the two substances
        // also mix temps
        // TODO multiple substance mixing

        if (
          Object(this.source_substance_asset) === this.source_substance_asset &&
          this.target_vessel.volume > 0 &&
          this.target_substance_id != this.source_vessel.substance_id
        ) {
          // if either substance has a mixwith for the other
          if (
            this.source_vessel.substance.mixwith[
              this.target_vessel.substance_id
            ]
          ) {
            this.output_substance_id =
              this.source_vessel.substance.mixwith[
                this.target_vessel.substance_id
              ];
            this.did_mix_substances = true;
          } else if (
            this.target_vessel.substance.mixwith[
              this.source_vessel.substance_id
            ]
          ) {
            this.output_substance_id =
              this.target_vessel.substance.mixwith[
                this.source_vessel.substance_id
              ];
            this.did_mix_substances = true;
          }

          // otherwise the source substance simply displaces the target substance
          else {
            this.output_substance_id = this.source_vessel.substance_id;
            this.did_displace_substance = true;
          }
        } else {
          this.output_substance_id = this.source_vessel.substance_id;
        }

        this.output_substance_asset = this.game.getAsset(
          this.output_substance_id
        );

        // current source level
        this.source_volume = this.source_vessel.getVolume();

        // current target level
        if (this.did_displace_substance) this.target_vessel.volume = 0;
        this.target_volume = this.target_vessel.volume;
        this.target_freevolume =
          this.target_vessel.maxvolume - this.target_volume;
        this.target_already_full =
          this.target_vessel.volume === this.target_vessel.maxvolume;

        if (this.source_volume > this.target_freevolume) {
          // target is full so set it to its maxvolume
          this.target_vessel.volume = this.target_vessel.maxvolume;

          if (
            this.source_asset instanceof adventurejs.SubstanceEmitter ||
            !isFinite(this.source_vessel.volume)
          ) {
            // it's assumed that emitters are automated and won't
            // stop emitting just because the target is full
            // so it overflows regardless of overflow setting
            if (this.can_overflow_target) {
              this.did_overflow_target = true;
            }

            if (this.target_already_full) {
              // let's arbitrarily say that we're pouring an amount
              // equal to half the target's free volume - an even mix
              this.source_volume_used = this.source_volume;
            } else {
              this.source_volume_used = this.target_freevolume;
            }
          }

          // else if ( !isFinite( this.source_vessel.volume ) )
          // {
          //   // source is infinite but we need to determine quantity used
          //   // for mixtemps calculation further down the line
          // }

          // else if( false === this.target_already_full
          // && Infinity === this.target_vessel.volume )
          // {
          //   // don't overflow
          // }
          else if (
            !this.target_already_full &&
            (!this.target_vessel.can_overflow || !this.can_overflow_target)
          ) {
            // target is not full and not can_overflow

            // subtract poured volume from source
            // and don't overflow target
            //this.source_asset.aspects[ this.source_aspect ].vessel.volume = this.source_asset.aspects[ this.source_aspect ].vessel.volume - this.target_freevolume;
            this.source_vessel.volume =
              this.source_vessel.volume - this.target_freevolume;
            this.source_volume_used = this.target_freevolume;
          } else if (
            this.target_already_full &&
            !this.target_vessel.can_overflow &&
            !this.can_overflow_target
          ) {
            // target is full and not can_overflow
            // do nothing...?
          } else if (
            this.target_vessel.can_overflow &&
            this.can_overflow_target
          ) {
            // overflow target and set source volume to zero
            this.source_volume_used = this.source_vessel.volume;
            //this.source_asset.aspects[ this.source_aspect ].vessel.volume = 0;
            this.source_vessel.volume = 0;
            this.did_overflow_target = true;
          } else {
            // subtract poured volume from source
            this.source_asset.aspects[this.source_aspect].vessel.volume =
              this.source_asset.aspects[this.source_aspect].vessel.volume -
              this.target_freevolume;
            this.source_volume_used = this.target_freevolume;
          }
        } // this.source_volume is not > this.target_freevolume
        else {
          // add source volume to target volume
          this.target_vessel.volume =
            this.target_vessel.volume + this.source_volume;

          if (this.source_asset instanceof adventurejs.SubstanceEmitter) {
            // @TODO figure out rate of flow
            // temp: set to free volume
            this.source_volume_used = this.target_freevolume;
          } else if (!isFinite(this.source_vessel.volume)) {
            this.source_volume_used = this.target_freevolume;
          } else {
            // empty source
            this.source_volume_used = this.source_vessel.volume;
            //this.source_asset.aspects[ this.source_aspect ].vessel.volume = 0;
            this.source_vessel.volume = 0;
          }
        }

        this.target_vessel.substance_id = this.output_substance_id;

        //this.mixTemps();
        if (!isFinite(this.target_volume)) {
          // recipient is infinite, no temperature change
        } else if (this.target_volume <= 0) {
          this.target_vessel.temperature = Number(
            this.source_vessel.temperature
          );
        } else if (this.target_volume > 0) {
          // liters to grams
          // grams = liters × 1000 × ingredient density

          // grams to liters
          // liters = grams / ( 1000 x ingredient density )

          // Mixing Liquids and/or Solids - Final Temperatures
          // Calculate the final temperature when liquids or solids are mixed
          // https://www.engineeringtoolbox.com/temperature-mixing-liquid-solids-d_1754.html

          var m1 =
            this.source_volume_used * this.source_substance_asset.density;
          var cp1 = this.source_substance_asset.specific_heat;
          var t1 = this.source_vessel.temperature;

          var m2 = this.target_volume * this.target_substance_asset.density;
          var cp2 = this.target_substance_asset.specific_heat;
          var t2 = this.target_vessel.temperature;

          console.warn(
            "m1",
            m1,
            "cp1",
            cp1,
            "t1",
            t1,
            "m2",
            m2,
            "cp2",
            cp2,
            "t2",
            t2
          );

          var temperature; // = this.target_vessel.room_temperature;

          if (
            !isNaN(m1) &&
            !isNaN(cp1) &&
            !isNaN(t1) &&
            !isNaN(m2) &&
            !isNaN(cp2) &&
            !isNaN(t2)
          ) {
            temperature =
              (m1 * cp1 * t1 + m2 * cp2 * t2) / (m1 * cp1 + m2 * cp2);
            this.target_vessel.temperature = Number(temperature);
          }

          this.game.log(
            "log",
            "high",
            "SubstanceMixer.js > source temp " + this.source_vessel.temperature,
            "SubstanceMixer"
          );
          this.game.log(
            "log",
            "high",
            "SubstanceMixer.js > target temp " + this.target_vessel.temperature,
            "SubstanceMixer"
          );
          this.game.log(
            "log",
            "high",
            "SubstanceMixer.js > mixed temp " + temperature,
            "SubstanceMixer"
          );
        } // mixTemps
      }

      this.did_fill_target =
        this.target_vessel.volume === this.target_vessel.maxvolume;

      if (this.source_substance_asset) {
        this.game.log(
          "log",
          "high",
          "SubstanceMixer.js > source_substance_asset " +
            this.source_substance_asset.id,
          "SubstanceMixer"
        );
        this.source_substance_asset.setKnown();
      }
      if (this.target_substance_asset) {
        this.game.log(
          "log",
          "high",
          "SubstanceMixer.js > target_substance_asset " +
            this.target_substance_asset.id,
          "SubstanceMixer"
        );
        this.target_substance_asset.setKnown();
      }
      //if( "undefined" !== typeof this.output_substance_asset )
      if (Object(this.output_substance_asset) === this.output_substance_asset) {
        this.game.log(
          "log",
          "high",
          "SubstanceMixer.js > output_substance_asset " +
            this.output_substance_asset.id,
          "SubstanceMixer"
        );
        this.output_substance_asset.setKnown();
      }

      // check for custom logic
      var results = this.source_asset.onSubtractSubstanceFromThis(
        this.source_substance_asset
      );
      if ("undefined" !== typeof results) return results;

      // check for custom logic
      results = this.target_asset.onAddSubstanceToThis(
        this.source_substance_asset
      );
      if ("undefined" !== typeof results) return results;

      return;
    }

    // liters to grams
    // grams = liters × 1000 × ingredient density

    // grams to liters
    // liters = grams / ( 1000 x ingredient density )

    // Mixing Liquids and/or Solids - Final Temperatures
    // Calculate the final temperature when liquids or solids are mixed
    // https://www.engineeringtoolbox.com/temperature-mixing-liquid-solids-d_1754.html

    // p.mixTemps = function SubstanceMixer_mixTemps(){
    //   //var m1  = this.source_volume * this.source_substance_asset.density;
    //   var m1  = this.source_volume_used * this.source_substance_asset.density;
    //   var cp1 = this.source_substance_asset.specific_heat;
    //   var t1  = this.source_vessel.temperature;

    //   var m2  = this.target_volume * this.target_substance_asset.density;
    //   var cp2 = this.target_substance_asset.specific_heat;
    //   var t2  = this.target_vessel.temperature;

    //   var temperature;

    //   if(m1 && cp1 && t1 && m2 && cp2 && t2)
    //   {
    //     temperature = ( m1 * cp1 * t1 + m2 * cp2 * t2 ) / ( m1 * cp1 + m2 * cp2 );
    //   }
    //   //return Conv.rounding(temperature);
    //   //console.warn( "SubstanceMixer.mixTemps > " + temperature );
    //   return temperature;
    // }

    // Mix Liquids of Different Temperatures
    // https://rechneronline.de/chemie-rechner/mix-temperatures.php

    // p.mixTempOfLiquids = function SubstanceMixer_mixTempOfLiquids( source, target ){

    //   // our measure of volume is stored in milliliters
    //   // water is roughly 1 gram per milliliter but other substances are not
    //   var m1 = source.volume; // 'amount' in source script
    //   var c1 = source.heat_capacity; // 'heat capacity' in source script // ex: water = 4.2
    //   var t1 = source.temperature;

    //   var m2 = target.volume; // 'amount' in source script;
    //   var c2 = target.heat_capacity; // 'heat capacity' in source script // ex: water = 4.2
    //   var t2 = target.temperature;

    //   var result;

    //   if (m1 && m2) {
    //       target.volume = m1 + m2;
    //   }
    //   if (!isNaN(t1) && !isNaN(t2))
    //       result = ( ( m1 * c1 * t1 ) + ( m2 * c2 * t2 ) ) / ( ( m1 * c1 ) + ( m2 * c2 ) );

    //   return result;

    // }
  }
  adventurejs.SubstanceMixer = SubstanceMixer;
})();