// drink.js
(function () {
/* global adventurejs A */
/**
* @augments {adventurejs.Verb}
* @class drink
* @ajsnode game.dictionary.verbs.drink
* @ajsconstruct MyGame.createVerb({ "name": "drink", [...] });
* @ajsconstructedby adventurejs.Dictionary#createVerb
* @hideconstructor
* @ajsinstanceof Verb
* @ajsnavheading ConsumptionVerbs
* @summary Verb meaning drink substance.
* @todo finish writing
* @tutorial Scripting_VerbSubscriptions
* @tutorial Verbs_VerbAnatomy
* @tutorial Verbs_VerbProcess
* @tutorial Verbs_ModifyVerbs
* @tutorial Verbs_WriteVerbs
* @classdesc
* <pre class="display border outline">
* <span class="ajs-player-input">> drink milk</span>
* You drink milk from the carton. It does a body good.
* </pre>
* <p>
* <strong>Drink</strong> a {@link adventurejs.Substance|Substance}
* {@link adventurejs.Asset|Asset}.
* Requires that a quantity of the Substance be present and reachable.
* Parser will search for the specified Substance among available
* Assets.
* </p>
* @ajsverbreactions doDestroy, doSubtractSubstanceFromThis, doRemoveThisFromThat, doRemoveThatFromThis
* @ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
*/
A.Preverbs.drink = {
name: "drink",
prettyname: "drink",
past_tense: "drank",
synonyms: ["drink", "sip"],
gerund: "drinking",
/**
* @ajsverbstructures
* @memberof drink
*/
accepts_structures: [
"verb noun",
"verb preposition noun",
"verb noun preposition noun",
],
/**
* @memberof drink
* @ajsverbphrase
* phrase1:
* {
* accepts_noun:true,
* requires_noun:true,
* noun_must_be:
* {
* known: true,
* matter: true,
* present_if_tangible: true,
* reachable_if_tangible: true,
* },
* accepts_preposition: true,
* preposition_must_be: ["from"],
* },
*/
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,
},
accepts_preposition: true,
preposition_must_be: ["from"],
},
/**
* @memberof drink
* @ajsverbphrase
* phrase2:
* {
* accepts_noun:true,
* noun_must_be:
* {
* known: true,
* tangible: true,
* present: true,
* visible: true,
* reachable: true,
* },
* accepts_preposition:true,
* requires_preposition: true,
* preposition_must_be: [ "from" ],
* },
*/
phrase2: {
accepts_noun: true,
noun_must_be: {
known: true,
tangible: true,
present: true,
visible: true,
reachable: true,
},
accepts_preposition: true,
requires_preposition: true,
preposition_must_be: ["from"],
},
/**
* @memberof drink
* @ajsverbparams
* with_params: {},
*/
with_params: { on_drink_empty: false },
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 room = this.game.getRoom();
var containers, container;
var msg = "";
var results, hook;
var find_container_for_direct_object = false;
var find_substance_for_direct_object = false;
// get ahead of this common situation - player inputs
// "drink water from sink" and the sink may have a
// running faucet and yet be technically empty and we
// don't want to stupidly return an "empty sink" message
// in which case make the faucet the object
if (
direct_object instanceof adventurejs.Substance &&
indirect_object instanceof adventurejs.Tangible &&
!indirect_object.containsAnySubstance() &&
indirect_object.hasAnyPartContainingAnySubstance()
) {
const substance_part =
indirect_object.getAnyPartContainingAnySubstance();
indirect_object = substance_part;
input.setAsset(2, substance_part);
}
// has player entered "drink asset"?
if (
input.hasStructure("verb noun") ||
input.hasStructure("verb preposition noun")
) {
// is asset a substance? > get container
if (direct_object instanceof adventurejs.Substance) {
if (this.game.settings.infer_containers) {
find_container_for_direct_object = true;
} else {
this.game.debug(
`D1173 | ${this.name}.js | this.game.settings.infer_containers is false `
);
msg += `What would {we} like to ${this.name} ${direct_object.id} from? `;
// soft prompt for container
input.setPreposition(2, "from");
input.setSoftPrompt({ index: 2, type: "noun", noun2: true });
input.setStructure(`${input.getStructure()} preposition noun`);
this.handleFailure(msg);
return null;
}
} else {
const substance_part =
direct_object.getAnyPartContainingAnySubstance();
if (direct_object.containsAnySubstance()) {
find_substance_for_direct_object = true; // check open / reachable etc
} else if (substance_part) {
direct_object = substance_part;
input.setAsset(1, substance_part);
find_substance_for_direct_object = true; // check open / reachable etc
} else if (
direct_object.canContainSubstances() ||
direct_object.canAnyPartContainSubstances()
) {
this.game.debug(
`D1575 | ${this.name}.js | ${direct_object.id} is empty `
);
msg += `${direct_object.Articlename_is} empty. `;
this.handleFailure(msg);
return null;
} else if (direct_object.isDOV("drink")) {
// is player carrying the thing?
if (
direct_object.isDOV("take") &&
!direct_object.isWithin(subject)
) {
this.game.debug(
`D1748 | ${this.name}.js | ${direct_object.id} is not in subject `
);
msg += `{We're} not carrying ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
// special handling for things that are not substance containers
// but which can be drunk, as in a case like "drink potion"
// where potion asset responds directly to drink
input.verb_params.can_be_drunk = true;
return true;
} else {
this.game.debug(
`D1171 | ${this.name}.js | ${direct_object.id}.dov.drink.enabled is false `
);
msg += `${direct_object.Articlename} can't be drunk from. `;
this.handleFailure(msg);
return null;
}
}
} // verb noun
// player input "drink substance" and we need to infer container
if (find_container_for_direct_object) {
containers = this.game.findSubstanceContainers(
direct_object.id,
this.game.getRoom(),
["Present", "Known", "Visible", "Reachable"]
);
// no containers?
if (!containers.length) {
this.game.debug(`D1576 | ${this.name}.js | no containers found `);
msg += `{We} {don't} see any ${direct_object.name}. `;
this.handleFailure(msg);
return null;
}
// multiple containers?
if (containers.length > 1) {
console.warn({ containers });
// are any containers being carried by subject?
let carried_containers = [];
for (let i = 0; i < containers.length; i++) {
// let asset = this.game.getAsset(containers[i]);
let asset = containers[i];
if (asset.$is("carried")) {
carried_containers.push(asset);
}
}
if (carried_containers.length) {
switch (carried_containers.length) {
case 0:
break;
case 1:
container = carried_containers[0];
break;
default:
outer: for (let i = 0; i < carried_containers.length; i++) {
let carried = carried_containers[i];
// was this item used last turn? if so prefer that
let last_turn = this.game.getLastTurn();
for (
let phrase = 1;
phrase < last_turn.getPhraseCount();
phrase++
) {
let last_turn_asset = last_turn.getAsset(phrase);
if (last_turn_asset.id === carried.id) {
container = carried;
break outer; // breaks out of BOTH loops
}
}
}
break;
}
// if no carried container was referred to last turn,
// just use the first one found
if (!container) {
container = carried_containers[0];
}
} // carried_containers
// prefer reservoir?
if (
!container &&
this.game.settings.infer_containers_prefers_reservoir
) {
for (var i = 0; i < containers.length; i++) {
// let asset = this.game.getAsset(containers[i]);
let asset = containers[i];
if (asset.$is("reservoir") || asset.$is("emitting")) {
container = asset;
break;
}
}
} // prefer reservoir
// prompt if multiple?
if (!container && !this.game.settings.auto_pick_inferred_container) {
// disambiguate - set parsedNoun.matches for next turn
this.game.debug(
`D1578 | ${this.name}.js | multiple containers found, disambiguate `
);
// set preposition to "from" for disambiguation
// because we're asking player to choose a container
input.setPreposition(1, "from");
// 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;
} // prompt if multiple
} // containers.length > 1
// we found containers but none of our conditions were met?
// use the first container found
if (!container) {
container = containers[0];
}
// set indirect object to the container
// ie "drink water" becomes "drink water from bowl"
if (container.id === room.id) {
input.verb_params.drink_from_room = true;
}
input.setAsset(2, this.game.getAsset(container));
input.setPreposition(2, "from");
input.setInferred(2, true);
input.setStructure("verb noun preposition noun");
indirect_object = input.getAsset(2);
indirect_preposition = input.getPreposition(2);
this.game.printInferred(`from ${indirect_object.articlename}`);
} // find_container_for_direct_object
// player input "drink container" and we need to infer substance
if (find_substance_for_direct_object) {
indirect_object = direct_object;
direct_object = this.game.getAsset(
indirect_object.getAnySubstanceThisContains()
);
input.setAsset(1, direct_object);
input.setPreposition(1, "");
input.setAsset(2, indirect_object);
input.setPreposition(2, "from");
input.setStructure("verb noun preposition noun");
this.game.printInferred(
`${direct_object.name} from ${indirect_object.articlename}`
);
} // find_substance_for_direct_object
// now we can get into verb noun preposition noun
if (input.hasStructure("verb noun preposition noun")) {
// is substance unpotable? for instance could be sand
if (!direct_object.isDOV("drink")) {
this.game.debug(
`D1577 | ${this.name}.js | ${direct_object.id}.dov.drink.enabled is false `
);
msg += `{We} can't drink ${direct_object.name}. `;
this.handleFailure(msg);
return null;
}
const substance_part =
indirect_object.getAnyPartContainingAnySubstance();
// can indirect object contain substance?
if (!indirect_object.canContainSubstances()) {
this.game.debug(
`D1582 | ${this.name}.js | ${indirect_object.id} has no substance container `
);
msg += `${indirect_object.Articlename} can't be drunk from. `;
this.handleFailure(msg);
return null;
}
// Is indirect object closed?
if (
indirect_object.getVesselPreposition() === "in" &&
indirect_object.isDOV("open")
) {
this.game.debug(
`D1571 | ${this.name}.js | ${indirect_object.id}.is.closed `
);
msg += `${indirect_object.Articlename_is} closed. `;
this.handleFailure(msg);
return null;
}
// DOES indirect object contain any substance?
if (!indirect_object.containsAnySubstance()) {
if (indirect_object instanceof adventurejs.SubstanceEmitter) {
this.game.debug(
`D1580 | ${this.name}.js | ${indirect_object.id} is class SubstanceEmitter and .is.emitting is false `
);
msg += `Nothing is coming out of ${indirect_object.articlename}. `;
} else {
this.game.debug(
`D1581 | ${this.name}.js | ${indirect_object.id}.aspects.in.vessel.volume is 0 `
);
msg += `${indirect_object.Articlename_is} empty. `;
}
this.handleFailure(msg);
return null;
}
// does indirect object contain the specified substance?
if (!indirect_object.containsSubstance(direct_object.id)) {
if (indirect_object instanceof adventurejs.SubstanceEmitter) {
this.game.debug(
`D1339 | ${this.name}.js | ${indirect_object.id} is not emitting ${direct_object.id} `
);
msg += `{We} don't see any ${direct_object.name} coming out of ${indirect_object.articlename}. `;
} else {
this.game.debug(
`D1340 | ${this.name}.js | ${indirect_object.id}.aspects.in.vessel.volume is 0 `
);
msg += `${indirect_object.Articlename_is} doesn't contain any ${direct_object.name}. `;
}
this.handleFailure(msg);
return null;
}
// Is indirect object takeable but not in subject?
if (
indirect_object.isDOV("take") &&
!indirect_object.isWithin(subject)
) {
this.game.debug(
`D1579 | ${this.name}.js | ${indirect_object.id} is not in subject `
);
msg += `{We're} not holding ${indirect_object.articlename}. `;
this.handleFailure(msg);
return null;
}
} // verb noun preposition noun
return true;
},
doSuccess: 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 msg = "";
var results;
var source_aspect;
var source_vessel;
var substance;
if (input.verb_params.can_be_drunk) {
// we only arrive here if the object can be direct object of drink but
// no substance involved. For simple things like "drink potion"
// sentence structure: verb
if (input.hasStructure("verb")) {
}
// sentence structure: verb noun
if (input.hasStructure("verb noun")) {
} // verb noun
msg += `{We} drink ${direct_object.articlename}. `;
results = subject.onIngestThat(direct_object);
if ("undefined" !== typeof results) return results;
if (direct_object && direct_object.dov.drink?.then_destroy) {
direct_object.destroy();
}
} else if (input.hasStructure("verb noun")) {
// shouldn't happen because we inferred verb noun preposition noun
} else if (input.hasStructure("verb preposition noun")) {
// shouldn't happen because we inferred verb noun preposition noun
} else if (input.hasStructure("verb noun preposition noun")) {
source_aspect = indirect_object.getVesselPreposition();
source_vessel = indirect_object.getVesselAt(source_aspect);
substance = this.game.getAsset(source_vessel.substance_id);
if (indirect_object.iov.drink?.with_params.on_drink_empty) {
source_vessel.setVolume(0);
} else {
source_vessel.subtractVolume(this.game.settings.mouthful);
}
msg += `{We} drink some ${substance.name} from ${indirect_object.articlename}`;
if (0 >= source_vessel.getVolume()) {
msg += `, emptying it in one gulp`;
}
msg = msg.trim();
msg += `. `;
results = subject.onIngestThat(substance);
if ("undefined" !== typeof results) return results;
}
// --------------------------------------------------
// print output
// --------------------------------------------------
return this.handleSuccess(msg, direct_object);
},
};
})(); // drink