// fill.js
(function () {
/*global adventurejs A*/
"use strict";
/**
* @augments {adventurejs.Verb}
* @class fill
* @ajsnode game.dictionary.verbs.fill
* @ajsconstruct MyGame.createVerb({ "name": "fill", [...] });
* @ajsconstructedby adventurejs.Dictionary#createVerb
* @hideconstructor
* @ajsinstanceof Verb
* @ajsnavheading ManipulationVerbs
* @summary Verb meaning fill an asset.
* @tutorial Scripting_VerbSubscriptions
* @tutorial Verbs_VerbAnatomy
* @tutorial Verbs_VerbProcess
* @tutorial Verbs_ModifyVerbs
* @tutorial Verbs_WriteVerbs
* @classdesc
* <pre class="display border outline">
* <span class="input">> fill goblet with wine from cask</span>
* You fill the stone goblet with foul smelling wine
* from the goblin's cask.
* </pre>
* <p>
* <strong>Fill</strong> a
* {@link adventurejs.Tangible|Tangible}
* {@link adventurejs.Asset|Asset} with a
* {@link adventurejs.Substance|Substance} Asset
* from another Tangible Asset.
* Requires that both Tangible Assets have
* {@link adventurejs.Vessel|Vessels}
* and that the second actually contains the specified Substance.
* </p>
* <p>Substances are a unique subclass of Matter that have
* no location, but only exist as properties in Tangible Assets,
* ie Tangible.Aspect.Vessel.
* Players may refer directly to a
* substance, and we infer its container if we're able.
* </p>
* <p>
* Ex: the player can input something like "fill jug with water",
* as opposed to "fill jug from sink".
* In such cases, instead of an object id, we get a ':' delimited
* string in the form of tangible id : aspect id : substance id.
* Ex: sink:in:water
* If we did receive a triplet, we'll use that pre-parsed info
* to get the aspect and substance.
* If we did not receive a triplet, we'll get aspect and substance
* via methods on the tangible object.
* </p>
* @ajsverbreactions
* @ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
*/
A.Preverbs.fill = {
name: "fill",
prettyname: "fill",
past_tense: "filled",
synonyms: ["fill"],
/**
* @ajsverbstructures
* @memberof fill
*/
accepts_structures: [
"verb noun preposition noun",
"verb noun preposition noun preposition noun",
],
/**
* @memberof fill
* @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,
},
},
// phrase2 can be either substance or tangible which
// means we're not going to check present/visible/reachable
// though we may have to in doTry
/**
* @memberof fill
* @ajsverbphrase
* phrase2:
* {
* accepts_noun:true,
* noun_must_be:
* {
* known: true,
* matter: true,
* present_if_tangible: true,
* // may be a substance or tangible and we
* // don't run these checks on substances
* //present: true,
* //visible: true,
* //reachable: true,
* },
* accepts_preposition:true,
* requires_preposition: true,
* accepts_these_prepositions: [ "with", "from" ],
* },
*/
phrase2: {
accepts_noun: true,
noun_must_be: {
known: true,
matter: true,
present_if_tangible: true,
},
accepts_preposition: true,
requires_preposition: true,
accepts_these_prepositions: ["with", "from"],
},
/**
* @memberof fill
* @ajsverbphrase
* phrase3:
* {
* accepts_noun:true,
* accepts_preposition:true,
* noun_must_be:
* {
* known: true,
* matter: true,
* present: true,
* visible: true,
* reachable: true,
* },
* requires_preposition: true,
* accepts_these_prepositions: [ "from" ],
* },
*/
phrase3: {
accepts_noun: true,
accepts_preposition: true,
noun_must_be: {
known: true,
matter: true,
present: true,
visible: true,
reachable: true,
},
requires_preposition: true,
accepts_these_prepositions: ["from"],
},
/**
* @memberof fill
* @ajsverbparams
* with_params: {},
*/
with_params: {},
doTry: function () {
var input = this.game.getInput();
var target_asset = input.getAsset(1);
var source_asset = input.getAsset(2);
var preposition2 = input.getPreposition(2);
var source_asset2 = input.getAsset(3);
var preposition3 = input.getPreposition(3);
var player = this.game.getPlayer();
var substance_id = "";
var containers;
var msg = "";
var source_volume;
var source_vessel, target_vessel;
var source_aspect, target_aspect;
// player input "fill asset with substance from asset"
if ("with" === preposition2 && "from" === preposition3) {
// is source_asset a substance?
substance_id = input.getParsedNoun(2).matches.substance;
if (!substance_id) {
this.game.debug(
`F1559 | ${this.name}.js | ${source_asset.id} is not a substance `,
);
msg += `$(We) can't fill ${target_asset.articlename} with ${source_asset.articlename}. `;
this.handleFailure(msg);
return null;
}
// does source_asset2 contain same substance?
if (!source_asset2.doesContainSubstance(substance_id)) {
this.game.debug(
`F1563 | ${this.name}.js | ${source_asset2.id} does not contain ${substance_id} `,
);
msg += `${source_asset2.articlename} doesn't contain ${substance_id}. `;
this.handleFailure(msg);
return null;
}
// we've established that source_asset2 contains substance
// so we can simplify the input to "fill target_asset from source_asset2"
input.setPhrase(2, input.getPhrase(3));
input.setPhrase(3, {});
source_asset = input.getAsset(2);
preposition2 = input.getPreposition(2);
} else if ("with" === preposition2) {
// is source_asset a substance?
substance_id = input.getParsedNoun(2).matches.substance;
if (substance_id) {
// if source_asset is a substance, need to find a container
containers = this.game.findSubstanceContainers(substance_id, [
"Present",
"Known",
"Visible",
"Reachable",
]);
switch (containers.length) {
case 0:
this.game.debug(`F1565 | ${this.name}.js | no containers found `);
msg += `There's no ${
this.game.getAsset(substance_id).name
} to fill ${target_asset.articlename} with. `;
this.handleFailure(msg);
return null;
case 1:
// set input phrase2 to the container
input.setPhrase(2, {});
input.setPreposition(2, "from");
input.setAsset(2, this.game.getAsset(containers[0]));
input.setAssumed(2, true);
source_asset = input.getAsset(2);
preposition2 = "from";
break;
default:
var asset, room;
for (var i = 0; i < containers.length; i++) {
asset = this.game.getAsset(containers[i]);
if (asset.id === this.game.getCurrentRoom().id) {
room = true;
break;
}
}
if (room) {
input.setPhrase(2, {});
input.setPreposition(2, "from");
input.setAsset(2, asset);
input.setAssumed(2, true);
source_asset = input.getAsset(2);
preposition2 = "from";
break;
} else {
// disambiguate - need to set parsedNoun.matches ?
this.game.debug(
`F1564 | ${this.name}.js | multiple containers found, disambiguate `,
);
input.setPreposition(2, "from");
// save containers back to input for next turn disambiguation
input.setParsedNounMatchesQualified(2, containers);
this.game.parser.printNounDisambiguation({
parsedNoun: input.getParsedNoun(2),
nounIndex: 2,
});
return null;
}
} // switch
} // if substance // not substance
else {
// @TODO should be able to say things like
// "fill printer with paper"
// source_asset is not a substance, so it must be a container
// does source_asset contain anything to fill with?
if (!source_asset.doesContainAnySubstance()) {
this.game.debug(
`F1566 | ${this.name}.js | "+${source_asset.id}+" does not contain anything `,
);
msg += `${source_asset.Articlename} doesn't contain anything with which to fill ${target_asset.articlename}. `;
this.handleFailure(msg);
return null;
}
// change preposition2 to "from"
input.setPreposition(2, "from");
preposition2 = "from";
} // else
} // if with
else if ("from" === preposition2) {
// does asset contain anything to fill with?
if (source_asset && !source_asset.doesContainAnySubstance()) {
this.game.debug(
`F1560 | ${this.name}.js | ${source_asset.id} does not contain anything `,
);
msg += `${source_asset.Articlename} doesn't contain anything with which to fill ${target_asset.articlename}. `;
this.handleFailure(msg);
return null;
}
}
// can target_asset be filled?
if (!target_asset.hasVessel()) {
this.game.debug(
`F1566 | ${this.name}.js | ${target_asset.id} has no substance container `,
);
msg += `${target_asset.Articlename} can't be filled. `;
this.handleFailure(msg);
return null;
}
// is player holding neither asset?
if (!target_asset.isIn(player) && !source_asset.isIn(player)) {
// If player is holding neither asset, we need
// to consider the takeability of each asset.
// Are both assets takeable?
if (target_asset.isDOV("take") && source_asset.isDOV("take")) {
this.game.debug(
`F1567 | ${this.name}.js | neither ${target_asset.id} nor ${source_asset.id} are in player `,
);
msg += `$(We're) holding neither ${target_asset.articlename} nor ${source_asset.articlename}. `;
this.handleFailure(msg);
return null;
}
// Are neither assets takeable?
if (!target_asset.isDOV("take") && !source_asset.isDOV("take")) {
this.game.debug(
`F1569 | ${this.name}.js | both ${target_asset.id} and ${source_asset.id} are stationary `,
);
msg += `$(We) try transferring ${
this.game.getAsset(source_asset.getAnySubstanceThisContains()).name
}
from ${target_asset.articlename} to ${
source_asset.articlename
} by hand, to little effect. `;
this.handleFailure(msg);
return null;
}
// Is only one asset takeable?
if (target_asset.isDOV("take") || source_asset.isDOV("take")) {
var unheld = target_asset.isDOV("take") ? target_asset : source_asset;
this.game.debug(
`F1570 | ${this.name}.js | ${unheld.id}.dov.take.enabled is true, but is unheld `,
);
msg += `$(We're) not holding ${unheld.articlename}. `;
this.handleFailure(msg);
return null;
}
}
// Is either asset closed?
if (
(target_aspect === "in" &&
target_asset.isDOV("close") &&
target_asset.is.closed) ||
(source_aspect === "in" &&
source_asset.isDOV("close") &&
source_asset.is.closed)
) {
var closed = target_asset.is.closed ? target_asset : source_asset;
this.game.debug(
`F1471 | ${this.name}.js | ${closed.id}.is.closed is true `,
);
msg += `${closed.Articlename} is closed. `;
this.handleFailure(msg);
return null;
}
// practically vessels are always 'in'
// but in theory we support substances at any aspect
target_aspect = target_asset.getAspectWithVessel();
source_aspect = source_asset.getAspectWithVessel();
target_vessel = target_asset.getVesselAt(target_aspect);
source_vessel = source_asset.getVesselAt(source_aspect);
// has source got volume?
source_volume = source_vessel.getVolume();
// Is source asset empty?
if (0 >= source_volume) {
if (source_asset instanceof adventurejs.SubstanceEmitter) {
this.game.debug(
`F1572 | ${this.name}.js | ${source_asset.id} is class SubstanceEmitter, but .is_emitting is false `,
);
msg += `Nothing is coming out of ${source_asset.articlename}. `;
this.handleFailure(msg);
return null;
} else {
this.game.debug(
`F1573 | ${this.name}.js | ${source_asset.id}.aspects.in.vessel.volume is 0 `,
);
msg += `Nothing is coming out of ${source_asset.articlename}. `;
this.handleFailure(msg);
return null;
}
}
if (
target_vessel.getVolume() === target_vessel.maxvolume &&
target_vessel.substance_id === source_vessel.substance_id
) {
this.game.debug(
`F1574 | ${this.name}.js | ${target_asset.id}.aspects.${target_aspect}.vessel.volume is equal to .maxvolume `,
);
msg += `${target_asset.Articlename} is already full of ${
this.game.getAsset(target_vessel.substance_id).name
}. `;
this.handleFailure(msg);
return null;
}
return true;
},
doSuccess: function () {
var input = this.game.getInput();
var target_asset = input.getAsset(1);
var source_asset = input.getAsset(2);
var room = source_asset instanceof adventurejs.Room;
var msg = "";
var results;
var mixer;
this.game.debug(`F1558 | ${this.name}.js | print doSuccess `);
// instantiate a SubstanceMixer to handle the transfer
mixer = new adventurejs.SubstanceMixer(this.game.game_name).set({
can_overflow_target: false,
source_input: source_asset.id,
source_aspect: source_asset.getAspectWithVessel(),
source_substance_id: source_asset.getAspectWithVessel().substance_id,
target_input: target_asset.id,
target_aspect: target_asset.getAspectWithVessel(),
});
results = mixer.mix();
if (A.isFalseOrNull(results)) return results;
mixer.target_vessel.vessel_is_known = true;
msg = `$(We) fill ${mixer.target_asset.articlename} with ${
mixer.source_substance_asset.name
}${room ? "" : " from " + mixer.source_asset.articlename}`;
// if source_asset is the room, don't say "from the room"
// does the target drain?
if (mixer.can_drain_target) {
msg += `, but it quickly drains away`;
}
// was there a mixwith?
else if (mixer.did_mix_substances) {
msg += `, resulting in ${mixer.output_substance_asset.name}`;
}
// did source displace content of target?
else if (mixer.did_displace_substance) {
msg += `, displacing the ${
this.game.getAsset(mixer.target_substance_id).name
} that was already there`;
}
msg += `. `;
// print output
this.handleSuccess(msg, target_asset);
return true;
},
};
})(); // fill