// Vessel.js
(function () {
/*global adventurejs A*/
/**
* @ajspath adventurejs.Atom.Vessel
* @augments adventurejs.Atom
* @class adventurejs.Vessel
* @ajsinternal
* @ajsnavheading BaseClasses
* @param {String} game_name Name of top level game instance that is scoped to window.
* @param {String} name Instance name.
* @summary Class added to an Aspect (aka aspects) to allow it to hold substances.
* @tutorial Substances_Vessels
* @classdesc
* <p>
* <strong>Vessel</strong> is a special class that
* adds the ability to contain substances to
* {@link adventurejs.Tangible|Tangible}
* {@link adventurejs.Asset|Assets}.
* Vessels must exist within
* {@link adventurejs.Aspect|Aspects},
* which exist within
* {@link adventurejs.Tangible|Tangibles}.
* In other words,
* <code class="property">Tangible.Aspect.Vessel</code>,
* or as a practical example: <code class="property">sink.aspects.in.vessel</code>.
* </p>
* <h3 class="examples">Example:</h3>
* <pre class="display"><code class="language-javascript">MyGame.createAsset({
* class: "Bowl",
* name: "stone bowl",
* place: { on: "blood stained shrine" },
* descriptions:{look:"It's a stained, chipped stone bowl. ",}
* in:
* {
* vessel: {
* maxvolume: 500,
* volume: 350,
* substance_id: "viscous fluid",
* },
* },
* });
* </code></pre>
* <p>
* To learn more, see how to use
* <a href="/doc/Substances_AboutSubstances.html">Substances</a>.
* </p>
**/
class Vessel extends adventurejs.Atom {
constructor(name, game_name, parent_id) {
super(name, game_name);
this.class = "Vessel";
if ("string" === typeof parent_id && parent_id) {
this.parent_id = parent_id;
}
this.list_in_room = true; // <- to contents
this.list_in_examine = true; // <- to contents
/**
* Substance vessels are not automatically made
* known with their parent. For example, picture a sealed
* oil can with unidentified contents: the can will become
* known as soon as the player enters a room with it; but
* the can's contents should not become known until the
* player opens it. Set this to true if you want a vessel
* to become known with its parent, for example: water in
* a drinking glass might be immediately apparent.
* @var {Boolean} adventurejs.Vessel#know_with_parent
* @default false
*/
this.know_with_parent = false;
/**
* The knowability of substance vessels is indepenent from
* the vessel's parent asset. For example, picture a sealed
* oil can with unidentified contents: the can will become
* known as soon as the player enters a room with it; but
* the can's contents should not become known until the
* player opens it.
* @var {Boolean} adventurejs.Vessel#known
* @default false
*/
this.known = false;
this.with_classes = []; // defined
this.substance_id = "";
this.can_only_contain_these_substances = []; // defined
this.maxvolume = 0;
this.can_overflow = true;
this.volume = 0;
this.mix_volume = 0;
this.drain_id = "";
this.temperature = this.game.settings.room_temperature;
this.temperature_equilibrates = false; // TODO equilibrium calculations
this.density = 1; // water // TODO function of temp
//this.state = this.game.settings.states.LIQUID; // liquid is default
// when state change occurs, swap substances
// for drains
this.is_drain = false;
this.max_volume_of_flow_per_turn = -1;
this.rate_of_flow = -1; // 0 to 1
// for emitters
this.is_emitter = false;
this.is_emitting = false;
this.target_id = "";
this.reservoir = false;
}
get temperature() {
return this.__temperature;
}
set temperature(temperature) {
temperature = Number(temperature);
if (isNaN(temperature) || "number" !== typeof temperature) {
var msg =
"Vessel.js > instance " +
this.id +
" received invalid temperature " +
String(temperature);
this.game.log("L1404", "error", "high", msg, "Vessel");
} else {
//this.game.log( "log", "high", "Vessel.js > instance " + this.id + " received valid temperature " + temperature, "Vessel" );
this.__temperature = temperature;
}
}
/**
* @var {Getter} adventurejs.Vessel#can_drain
* @default false
*/
get can_drain() {
// is this a drain?
if (this.is_drain && !this.game.getAsset(this.parent_id).is.plugged) {
return true;
}
// or does it have a separate drain asset?
else if (this.drain_id) {
var drain = this.game.getAsset(this.drain_id);
if (drain && !drain.is.plugged) {
return true;
}
}
return false;
}
get parent_id() {
return this.__parent_id;
}
set parent_id(id) {
id = A.serialize(id);
this.__parent_id = id;
}
get drain_id() {
return this.__drain_id;
}
set drain_id(id) {
id = A.serialize(id);
this.__drain_id = id;
}
get substance_id() {
if (this.volume > 0 || this.is_emitter) {
return this.__substance_id;
} else return "";
}
set substance_id(substance_id) {
this.__substance_id = A.serialize(substance_id);
}
get maxvolume() {
if (isNaN(this.__maxvolume)) return 0;
return this.__maxvolume;
}
set maxvolume(volume) {
if (Infinity === volume) {
this.__maxvolume = Infinity;
//this.__volume = Infinity;
return;
}
this.__maxvolume = A.convertVolume.call(this, volume, this.parent_id);
}
get volume() {
if (isNaN(this.__volume)) return 0;
return this.__volume;
}
set volume(volume) {
//console.warn( "volume: " + volume );
if (true === this.is_drain) {
this.__volume = 0;
return;
}
if (Infinity === volume) {
// infinite
this.__volume = Infinity;
this.__maxvolume = Infinity;
return;
}
this.__volume = A.convertVolume.call(this, volume, this.parent_id);
}
get percent_of_maxvolume() {
if (isFinite(this.maxvolume)) {
return this.volume / this.maxvolume;
} else {
return 1;
}
}
set percent_of_maxvolume(percent) {
if (isFinite(this.maxvolume)) {
this.volume = this.maxvolume * percent;
} else {
this.volume = this.maxvolume;
}
}
get game_name() {
return this.__game_name;
}
set game_name(game_name) {
this.__game_name = game_name;
}
get game() {
return window[this.game_name] || false;
}
get with_classes() {
return this.__with_classes;
}
set with_classes(arr) {
if (false === Array.isArray(this.__with_classes)) {
this.__with_classes = [];
}
this.__with_classes = A.validateClassList(arr);
}
get can_only_contain_these_substances() {
return this.__can_only_contain_these_substances;
}
set can_only_contain_these_substances(arr) {
if (false === Array.isArray(this.__can_only_contain_these_substances)) {
this.__can_only_contain_these_substances = [];
}
this.__can_only_contain_these_substances = A.validateAssetList(arr);
}
get max_volume_of_flow_per_turn() {
if (isNaN(this.__max_volume_of_flow_per_turn)) return 0;
return this.__max_volume_of_flow_per_turn;
}
set max_volume_of_flow_per_turn(volume) {
if (Infinity === volume) {
this.__max_volume_of_flow_per_turn = Infinity;
return;
}
this.__max_volume_of_flow_per_turn = A.convertVolume.call(
this,
volume,
this.parent_id
);
}
get volume_of_flow_per_turn() {
return this.max_volume_of_flow_per_turn * this.rate_of_flow;
}
get mass() {
if (!this.substance) return 0;
return this.volume * this.density;
}
get mix_mass() {
if (!this.substance) return 0;
return this.mix_volume * this.density;
}
get substance() {
return this.game.getAsset(this.__substance_id);
}
get heat_capacity() {
if (!this.substance) return 0;
return this.specific_heat * this.mass;
}
/**
* <strong>empty</strong> sets the volume of this vessel to 0.
* @memberOf adventurejs.Vessel
* @method adventurejs.Vessel#empty
* @returns {number}
*/
empty() {
if (!this.is_emitting && Infinity !== this.volume) {
this.volume = 0;
}
return this.volume;
}
/**
* <strong>getVolume</strong> is called to get the current volume of this vessel.
* Takes into consideration whether vessel is emitting.
* @memberOf adventurejs.Vessel
* @method adventurejs.Vessel#getVolume
* @returns {number}
*/
getVolume() {
if (this.is_emitting) {
return this.volume_of_flow_per_turn;
} else return this.volume;
}
/**
* <strong>setVolume</strong> is called to set a
* vessel to a specified volume.
* @memberOf adventurejs.Vessel
* @method adventurejs.Vessel#setVolume
* @param {number} volume The volume to set the vessel to.
*/
setVolume(volume) {
this.volume = volume;
return this.volume;
}
/**
* <strong>addVolume</strong> is called to raise the
* vessel's volume by a specified amount.
* @memberOf adventurejs.Vessel
* @method adventurejs.Vessel#addVolume
* @param {number} volume The volume to add to the vessel.
*/
addVolume(volume) {
if (Infinity !== this.volume) {
volume = this.volume + volume;
if (volume > this.maxvolume) volume = this.maxvolume;
this.volume = volume;
}
return this.volume;
}
/**
* <strong>addSubstance</strong> tries to add specified
* volume of substance. If vessel already contains another
* substance, will try to mixwith.
* @memberOf adventurejs.Vessel
* @method adventurejs.Vessel#addSubstance
* @param {number} volume The volume to add to the vessel.
* @param {String} substance_id The id of a substance to add to the vessel.
*/
addSubstance(volume, substance_id) {
if (isNaN(volume) || volume <= 0 || Infinity === this.volume) {
return { volume: this.volume, substance: this.substance_id };
}
if (this.can_drain) {
return { volume: 0, substance: "" };
}
if (this.volume <= 0) {
this.substance_id = substance_id;
} else if (this.volume > 0 && substance_id !== this.substance_id) {
this.substance_id = this.mixwith(substance_id);
}
this.volume = Math.min(this.volume + volume, this.maxvolume);
return { volume: this.volume, substance: this.substance_id };
}
/**
* <strong>getSubstance</strong> is called to get the
* substance of this vessel.
* @memberOf adventurejs.Vessel
* @method adventurejs.Vessel#getSubstance
* @returns {number}
*/
getSubstance() {
return this.game.getAsset(this.substance_id);
// return this.substance_id;
}
/**
* <strong>mixwith</strong> checks for a substance mixwith.
* @memberOf adventurejs.Vessel
* @method adventurejs.Vessel#mixwith
* @param {String} substance_id
*/
mixwith(substance_id) {
if (this.substance?.mixwith[substance_id]) {
return this.substance.mixwith[substance_id];
} else if (this.game.getAsset(substance_id)?.mixwith[this.substance_id]) {
return this.game.getAsset(substance_id).mixwith[this.substance_id];
}
return this.substance_id;
}
/**
* <strong>subtractVolume</strong> is called to reduce the
* vessel's volume by a specified amount.
* @memberOf adventurejs.Vessel
* @method adventurejs.Vessel#subtractVolume
* @param {number} volume The volume to subtract from the vessel.
*/
subtractVolume(volume) {
if (Infinity !== this.volume) {
volume = this.volume - volume;
if (0 > volume) volume = 0;
this.volume = volume;
}
return this.volume;
}
}
adventurejs.Vessel = Vessel;
})();