// 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 Verbs_Subscriptions
* @tutorial AdvancedVerbs_VerbAnatomy
* @tutorial AdvancedVerbs_VerbProcess
* @tutorial AdvancedVerbs_ModifyVerbs
* @tutorial AdvancedVerbs_ModifyVerbs
* @classdesc
* <pre class="display border outline">
* <span class="ajs-player-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,
prefer_carried_if_ambiguous: 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 subject = input.getSubject();
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 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,
subject,
["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.auto_pick_inferred_container
) {
// 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() &&
direct_object.hasAnyPartContainingAnySubstance()
) {
direct_object = direct_object.getAnyPartContainingAnySubstance();
input.setAsset(1, direct_object);
}
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 - subject parent / room
indirect_object = subject.getNestOrPlaceAsset();
indirect_preposition = subject.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 subject?
if (!direct_object.isWithin(subject)) {
this.game.debug(
`D1552 | ${this.name}.js | ${direct_object.id}.place not in subject `
);
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.getRoom();
input.setAsset(2, indirect_object);
}
}
return true;
},
doSuccess: function () {
var input = this.game.getInput();
var subject = input.getSubject();
var direct_object = input.getAsset(1);
var indirect_object = input.getAsset(2);
var indirect_preposition = input.getPreposition(2);
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 = msg.trim();
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.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 = msg.trim();
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.getRoom();
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 subject'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);
},
};
})(); // pour