// pour.js also means empty
(function () {
/*global adventurejs A*/
/**
* @augments {adventurejs.Verb}
* @class pour
* @ajsnode game.dictionary.verbs.pour
* @ajsconstruct MyGame.createVerb({ "name": "pour", [...] });
* @ajsconstructedby adventurejs.Dictionary#createVerb
* @hideconstructor
* @ajsinstanceof Verb
* @ajsnavheading ManipulationVerbs
* @summary Verb meaning pour substance from 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">> pour bottle into volcano</span>
* You pour the bottle of vinegar into the papier-mâché volcano.
* Nothing happens. As you lean in to investigate, you're disturbed
* by a deep rumbling sound. You lean back. Nothing happens. You wait
* for a long moment. You lean in and THE VOLCANO ERUPTS WITH THE SOULS
* OF THE DAMNED! Wraiths stream from the sculpture, rise into the air
* above the stone plinth, begin circling around the crowded
* amphitheatre. The wisps spin faster and faster, raising a ghostly
* vortex that climbs toward the darkening sky. Dammit, you knew you
* should have used the Mentos and Coke method.
* </pre>
* <p>
* <strong>Pour</strong> tries to pour the contents of the
* given direct object. Because
* {@link adventurejs.Aspect|Aspects} can contain both
* {@link adventurejs.Tangible|Tangibles} and
* {@link adventurejs.Substance|Substances}, pouring
* 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 doAddSubstanceToThis, doSubtractSubstanceFromThis, doRemoveThisFromThat, doRemoveThatFromThis, doMoveThisToThat, doMoveThatToThis
* @ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
*/
A.Preverbs.pour = {
name: "pour",
prettyname: "pour",
past_tense: "poured",
synonyms: ["pour"],
gerund: "pouring",
/**
* @ajsverbstructures
* @memberof pour
*/
accepts_structures: [
"verb noun",
"verb preposition noun",
"verb noun preposition noun",
"verb noun preposition noun preposition noun",
],
/**
* @memberof pour
* @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 pour
* @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 pour
* @ajsverbphrase
* phrase3:
* {
* accepts_noun:true,
* accepts_preposition:true,
* requires_preposition: true,
* noun_must_be:
* {
* known: true,
* matter: true,
* present_if_tangible: true,
* reachable_if_tangible: true,
* },
* },
*/
phrase3: {
accepts_noun: true,
accepts_preposition: true,
requires_preposition: true,
noun_must_be: {
known: true,
matter: true,
present_if_tangible: true,
reachable_if_tangible: true,
},
},
/**
* @memberof pour
* @ajsverbparams
* with_params: {},
*/
with_params: {},
doTry: function () {
var input = this.game.getInput();
var verb_phrase = input.verb_phrase;
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 noun3 = input.getAsset(3);
var preposition3 = input.getPreposition(3);
var player = this.game.getPlayer();
var containers, container;
var msg = "";
var results;
// our goal is to get to
// "verb noun preposition noun" aka "pour bucket into sink"
// from whatever parsed sentence structure we've received
// if input is "pour water"
// or input is "pour water into sink"
// find a container
// if input is "pour bucket"
// or input is "pour from bucket"
// verify that bucket contains anything
// if input is "pour water from bucket"
// or input is "pour water from bucket into sink"
// verify that bucket contains water
// if input is "pour bucket into sink"
// verify that bucket contains anything
// sentence structure: verb
if (input.hasStructure("verb")) {
}
// sentence structure: verb noun
// ex: pour water / pour bucket
if (input.hasStructure("verb noun")) {
} // verb noun
if (input.hasStructure("verb preposition noun")) {
}
// call actions
if (input.hasStructure("verb noun preposition noun")) {
// pour bucket into sink
}
// call actions
if (input.hasStructure("verb noun preposition noun preposition noun")) {
// pour water from bucket into sink
// empty water from bucket into sink
}
if (input.hasStructure("verb preposition noun")) {
// pour from bucket
// is preposition one that we support?
// for many verbs we pre-define accepted prepositions
// and wouldn't have to ask this here but pour needs
// to be flexible to handle either substance or tangible
if ("from" !== direct_preposition) {
this.game.debug(
`D1584 | ${this.name}.js | pour ${direct_preposition} isn't currently handled `
);
msg += `$(We) don't know how to pour ${direct_preposition} ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
// is direct object a substance?
// usually we pre-check using NounMustBe but pour needs
// to be flexible to handle either substance or tangible
if (direct_object instanceof adventurejs.Substance) {
this.game.debug(
`D1736 | ${this.name}.js | ${direct_object.id} isn't Tangible class `
);
msg += `$(We) don't know how to pour from ${direct_object.name}. `;
this.handleFailure(msg);
return null;
}
// remove the "from" and treat it as "pour bucket"
input.setPreposition(1, "");
input.setStructure("verb noun");
} // verb preposition noun
// did player input "pour substance" with or without indirect object?
if (
direct_object instanceof adventurejs.Substance &&
(input.hasStructure("verb noun") ||
(input.hasStructure("verb noun preposition noun") &&
"from" !== indirect_preposition))
) {
// if so we need to look for a container in inventory
containers = this.game.findSubstanceContainers(
direct_object.id,
player,
["InInventory"]
);
// no containers?
if (!containers.length) {
this.game.debug(`D1739 | ${this.name}.js | no containers found `);
msg += `$(We're) not carrying anything with ${direct_object.name} in it. `;
this.handleFailure(msg);
return null;
}
// multiple containers?
if (
containers.length > 1 &&
!this.game.settings.infer_containers_automatically_picks
) {
// disambiguate - set parsedNoun.matches for next turn
this.game.debug(
`D1740 | ${this.name}.js | multiple containers found, disambiguate `
);
// save containers back to input for next turn disambiguation
input.setParsedNounMatchesQualified(1, containers);
this.game.parser.printNounDisambiguation({
parsedNoun: input.getParsedNoun(1),
nounIndex: 1,
});
return null;
}
// use the first container found
if (!container) {
container = containers[0];
}
// set direct object to the container
// ie "pour water" becomes "pour bowl"
input.verb_params.substance = direct_object;
direct_object = this.game.getAsset(containers[0]);
input.setAsset(1, direct_object);
input.setInferred(1, true);
this.game.printInferred(`from ${direct_object.articlename}`);
} // find substance container
if (input.hasStructure("verb noun")) {
// pour water / pour bucket
if (direct_object instanceof adventurejs.Tangible) {
// does direct object contain any substance?
if (!direct_object.containsAnySubstance()) {
this.game.debug(
`D1737 | ${this.name}.js | ${direct_object.id} doesn't contain substance `
);
msg += `${direct_object.Articlename} doesn't contain anything pourable. `;
this.handleFailure(msg);
return null;
}
// find target - player parent / room
indirect_object = player.getNestOrPlaceAsset();
indirect_preposition = player.getNestOrPlacePreposition();
input.setAsset(2, indirect_object);
input.setPreposition(2, indirect_preposition);
input.setInferred(2, true);
input.setStructure("verb noun preposition noun");
this.game.printInferred(
indirect_object.hasClass("Room")
? `on the floor`
: `${indirect_preposition} ${indirect_object.articlename}`
);
}
} // verb noun
if (input.hasStructure("verb noun preposition noun preposition noun")) {
// pour water from bucket into sink
// "from" is the only preposition we accept
// for many verbs we already would have pre-checked this
// but pour needs to be flexible to accommodate several syntax forms
if ("from" !== indirect_preposition) {
this.game.debug(
`D1737 | ${this.name}.js | pour ${indirect_preposition} isn't currently handled `
);
msg += `$(We) don't know how to pour ${indirect_preposition} ${indirect_object.articlename}. `;
this.handleFailure(msg);
return null;
}
// is direct object a substance? that's the only class we handle in this form
if (!(direct_object instanceof adventurejs.Substance)) {
this.game.debug(
`D1738 | ${this.name}.js | ${direct_object.id} isn't Substance class `
);
msg += `$(We) don't know how to pour ${direct_object.name}. `;
this.handleFailure(msg);
return null;
}
// does indirect object contain direct object?
if (!indirect_object.containsSubstance(direct_object.id)) {
this.game.debug(
`D1557 | ${this.name}.js | ${indirect_object.id} does not contain ${direct_object.id} `
);
msg += `${indirect_object.Articlename} doesn't contain ${direct_object.name}. `;
this.handleFailure(msg);
return null;
}
// did player input "pour substance from A to B"?
// that's a reasonable phrase, but 'to' isn't a
// normal aspect so change it to default aspect
if ("to" === preposition3) {
preposition3 = noun3.default_aspect;
input.setPreposition(3, preposition3);
}
// though player explicitly named substance as direct object,
// now that we've confirmed its presence in indirect object,
// we're removing it from the input to streamline handling
input.verb_params.substance = direct_object;
input.setPhrase(1, input.getPhrase(2));
direct_object = input.getAsset(1);
direct_preposition = input.getPreposition(1);
input.setPhrase(2, input.getPhrase(3));
indirect_object = input.getAsset(2);
indirect_preposition = input.getPreposition(2);
input.setPhrase(3, {});
input.setStructure("verb noun preposition noun");
}
if (input.hasStructure("verb noun preposition noun")) {
// pour bucket into sink
// we've already checked for "pour water into sink"
// and replaced water with bucket
// is direct_object in player?
if (!direct_object.isWithin(player)) {
this.game.debug(
`D1552 | ${this.name}.js | ${direct_object.id}.place not in player `
);
msg += `$(We're) not carrying ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
// does direct_object have vessel?
if (!direct_object.hasVessel()) {
this.game.debug(
`D1271 | ${this.name}.js | ${direct_object.id} has no aspect with vessel `
);
msg += `$(We) can't pour ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
// does target have aspect?
if (
!indirect_object.hasAspectAt(indirect_preposition) &&
"on" !== indirect_preposition
) {
// took away a check for floor here as in "pour bucket on floor"
// but we're going to allow "pour on" anything regardless of aspects
this.game.debug(
`D1550 | ${this.name}.js | ${indirect_object.id} does not have ${indirect_preposition} aspect `
);
msg += `$(We) can't pour ${direct_object.articlename} ${indirect_preposition} ${indirect_object.articlename}. `;
this.handleFailure(msg);
return null;
}
// is direct_object closed?
if (direct_object.isDOV("open")) {
this.game.debug(
`D1272 | ${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.containsAnySubstance()) {
this.game.debug(
`D1551 | ${this.name}.js | ${direct_object.id}.in.vessel.getVolume is 0 `
);
msg += `${direct_object.Articlename_is} empty. `;
this.handleFailure(msg);
return null;
}
// is target closed?
if ("in" === indirect_preposition && indirect_object.isDOV("open")) {
this.game.debug(
`D1554 | ${this.name}.js | ${indirect_object.id}.is.closed `
);
msg += `${indirect_object.Articlename_is} closed. `;
this.handleFailure(msg);
return null;
}
// is target the floor? set to room
if (indirect_object instanceof adventurejs.Floor) {
indirect_object = this.game.getCurrentRoom();
input.setAsset(2, indirect_object);
}
}
return true;
},
doSuccess: function () {
var input = this.game.getInput();
var verb_phrase = input.verb_phrase;
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;
// sentence structure: verb
if (input.hasStructure("verb")) {
}
// sentence structure: verb noun
if (input.hasStructure("verb noun")) {
} // verb noun
// all parsed sentence structures should have resolved into
// "verb noun preposition noun" because we always need
// a source and a target
if (indirect_object) {
}
msg += `$(We) tip ${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.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
return this.handleSuccess(msg, direct_object);
},
};
})(); // pour