// examine.js
(function () {
/*global adventurejs A*/
"use strict";
/**
* @augments {adventurejs.Verb}
* @class examine
* @ajsnode game.dictionary.verbs.examine
* @ajsconstruct MyGame.createVerb({ "name": "examine", [...] });
* @ajsconstructedby adventurejs.Dictionary#createVerb
* @hideconstructor
* @ajsinstanceof Verb
* @ajsnavheading SensationVerbs
* @summary Verb meaning examine 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">> examine foot</span>
* You examine the elephant's foot. Among the numerous black
* umbrellas parked in it, one yellow umbrella stands out.
* </pre>
* <p>
* <strong>Examine</strong> an {@link adventurejs.Asset|Asset}
* returns a description of the Asset.
* </p>
* @ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
*/
A.Preverbs.examine = {
name: "examine",
prettyname: "examine",
past_tense: "examined",
synonyms: ["examine", "x"],
verb_prep_noun: ["carefully examine"],
verb_noun_prep_noun: ["examine carefully"],
/**
* @ajsverbstructures
* @memberof examine
*/
accepts_structures: ["verb noun", "verb noun preposition noun"],
/**
* @memberof examine
* @ajsverbphrase
* phrase1:
* {
* accepts_noun:true,
* requires_noun:true,
* noun_must_be:
* {
* known: true,
* matter: true,
* present_if_tangible: true,
* },
* },
*/
phrase1: {
accepts_noun: true,
requires_noun: true,
noun_must_be: {
known: true,
matter: true,
present_if_tangible: true,
},
},
/**
* @memberof examine
* @ajsverbphrase
* phrase2:
* {
* accepts_noun:true,
* noun_must_be:
* {
* known: true,
* matter: true,
* },
* accepts_preposition:true,
* requires_preposition:true,
* },
*/
phrase2: {
accepts_noun: true,
noun_must_be: {
known: true,
matter: true,
},
accepts_preposition: true,
requires_preposition: true,
},
/**
* @memberof examine
* @ajsverbparams
* with_params: {},
*/
with_params: {},
msgNoObject: "What did you want to examine?",
doTry: function () {
var input = this.game.getInput();
var direct_object = input.getAsset(1);
var indirect_object = input.getAsset(2);
var indirect_preposition = input.getPreposition(2);
var currentRoom = this.game.getCurrentRoom();
var player = this.game.getPlayer();
var msg = "";
var available;
var containers;
var dPlace;
var dPlacePrep;
// is direct object a substance?
// we have a fully different set of rules for examining substances
if (direct_object instanceof adventurejs.Substance) {
//if( input.getParsedNoun(1).matches.substance )
// has user input "examine substance" vs "examine substance in container"?
if (indirect_object) {
// is indirect_object also a substance?
// @TODO there might be situations where this makes sense
// such as a body of water with sand at its bottom "examine sand in water"
// or a colloidal suspension - but would that be a new substance?
if (indirect_object instanceof adventurejs.Substance) {
this.game.debug(
`F1589 | ${this.name}.js | ${direct_object.id} and ${indirect_object.id} are both substances `
);
msg += `$(We) can't examine "${direct_object.name} in ${indirect_object.name}. `;
this.handleFailure(msg);
return null;
}
// in order to allow examining substances, we skipped our usual
// check for "present" and "visible" in the parser
// so we need to do it here for indirect_object
available = this.game.parser.selectPresent(indirect_object.id);
available = this.game.parser.selectVisible(available);
if (available.length === 0) {
this.game.debug(
`F1588 | ${this.name}.js | ${indirect_object.id} is either unknown or not present or not visible `
);
msg += `${indirect_object.Articlename} doesn't contain any ${direct_object.name}. `;
this.handleFailure(msg);
return null;
}
// does indirect_object contain substance?
if (!indirect_object.doesContainSubstance(direct_object.id)) {
// does it contain some other substance?
if (indirect_object.doesContainAnySubstance()) {
this.game.debug(
`F1595 | ${this.name}.js | ${
indirect_object.id
}.doesContainAnySubstance is ${indirect_object.getAnySubstanceThisContains()}`
);
msg += `${indirect_object.Articlename} contains ${
this.game.getAsset(
indirect_object.getAnySubstanceThisContains()
).name
}. `;
this.handleFailure(msg);
return null;
}
this.game.debug(
`F1587 | ${this.name}.js | ${indirect_object.id}.doesContainSubstance ${direct_object.id} is false `
);
msg += `${indirect_object.Articlename} doesn't contain any ${direct_object.name}. `;
this.handleFailure(msg);
return null;
}
} // indirect_object
else if (!indirect_object) {
// user has input "examine substance"
// is any substance present?
// get a list of available
containers = this.game.findSubstanceContainers(direct_object.id, [
"Present",
"Known",
"Visible",
]);
switch (containers.length) {
case 0:
this.game.debug(`F1585 | ${this.name}.js | no containers found `);
msg += `There's no ${
this.game.getAsset(direct_object.id).name
} available. `;
this.handleFailure(msg);
return null;
case 1:
// set container as indirect_object
// and treat like "examine water in bowl"
input.setPreposition(2, "in");
input.setAsset(2, this.game.getAsset(containers[0]));
input.setAssumed(2, true);
indirect_object = input.getAsset(2);
indirect_preposition = input.getPreposition(2);
break;
default:
var asset, body;
for (var i = 0; i < containers.length; i++) {
asset = this.game.getAsset(containers[i]);
//if( asset.id === currentRoom.id )
if (asset.$is("body")) {
body = true;
break;
}
}
// if body is among multiple sources, always prefer body
// meant for situations like player is on a beach,
// where sand is always available
if (body) {
// set input phrase1 to the body
// ie "examine sand" becomes "examine sand in beach"
// doSuccess will handle the rest
input.setPreposition(2, "in");
input.setAsset(2, asset);
input.setAssumed(2, true);
indirect_object = input.getAsset(2);
break;
} // body
else if (!body) {
// disambiguate - need to set parsedNoun.matches ?
this.game.print(
`F1586 | ${this.name}.js | multiple containers found, disambiguate `
);
// set preposition to "from" for disambiguation
// because we're asking player to choose a container
input.setAsset(2, direct_object);
input.setPreposition(2, "in");
// 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;
} // !body
} // switch
} // !indirect_object
// if we get here, we have a substance and a container
return true;
} // substance
// NOT A SUBSTANCE
// is direct_object present and visible?
available = this.game.parser.selectPresent(direct_object.id);
available = this.game.parser.selectVisible(available);
if (!available.length) {
this.game.debug(
`F1591 | ${this.name}.js | ${direct_object.id} either is not present or not visible `
);
msg += `$(We) don't see any ${direct_object.name}. `;
this.handleFailure(msg);
return null;
}
// has user input "examine object in substance"?
// can imagine situations like "examine boat in water"
if (input.hasStructure("verb noun preposition noun")) {
if (indirect_object instanceof adventurejs.Substance) {
// need to find a container that contains indirect_object
// and also has direct_object in it
// may just be able to get direct_object's parent
// and see if that contains Substance
dPlace = this.game.getAsset(direct_object.getPlaceAssetId());
dPlacePrep = direct_object.getPlacePreposition();
// if( !dPlace.doesContainSubstance(indirect_object.id) )
if (dPlace.getSubstanceAt(dPlacePrep) !== indirect_object.id) {
this.game.debug(
`F1590 | ${this.name}.js | ${direct_object.id}.place is ${dPlace.id} which doesn't contain ${indirect_object.id} `
);
msg += `${direct_object.Articlename} doesn't appear to be in ${indirect_object.name}. `;
this.handleFailure(msg);
return null;
}
} // indirect_object is Substance
else if (!(indirect_object instanceof adventurejs.Substance)) {
// is indirect_object present and visible?
available = this.game.parser.selectPresent(indirect_object.id);
available = this.game.parser.selectVisible(available);
if (available.length === 0) {
this.game.debug(
`F1592 | ${this.name}.js | ${indirect_object.id} either is not present or not visible `
);
msg += `$(We) don't see any ${indirect_object.name}. `;
this.handleFailure(msg);
return null;
}
// is direct_object in indirect_object?
if (
indirect_preposition !== direct_object.getPlacePreposition() ||
indirect_object.id !== direct_object.getPlaceAssetId()
) {
// did player input "on" but thing is attached? allow it
if (
indirect_preposition === "on" &&
direct_object.getPlacePreposition() === "attached"
) {
// allowed - do nothing
} else if (
indirect_preposition === "in" &&
direct_object.getPlacePreposition() === "on" &&
indirect_object.quirks.in_means_on
) {
// allowed - do nothing
} else if (
direct_object.hasIndirectDescription(
"look",
indirect_preposition,
indirect_object
)
) {
// allowed - do nothing
input.verb_params.hasIndirectDescription = true;
} else {
this.game.debug(
`F1593 | ${this.name}.js | ${direct_object.id} is not ${indirect_preposition} ${indirect_object.id} `
);
msg += `${direct_object.Articlename} doesn't appear to be ${indirect_preposition} ${indirect_object.articlename}. `;
this.handleFailure(msg);
return null;
}
}
} // indirect_object is not Substance
} // verb noun preposition noun
return true;
}, // doTry
doSuccess: function () {
var input = this.game.getInput();
var direct_object = input.getAsset(1);
var indirect_object = input.getAsset(2);
var indirect_preposition = input.getPreposition(2);
var currentRoom = this.game.getCurrentRoom();
var player = this.game.getPlayer();
var msg = "";
var room = indirect_object && indirect_object.id === currentRoom.id;
var careful = "";
if (input.verb_params.hasIndirectDescription) {
this.game.debug(
`F1844 | ${this.name}.js | doSuccess > ${direct_object.id} ${indirect_preposition} ${indirect_object.id} `
);
msg += direct_object.getIndirectDescription(
"look",
indirect_preposition,
indirect_object
);
this.handleSuccess(msg, direct_object);
return true;
}
if (direct_object instanceof adventurejs.Substance) {
// indirect_object will be set regardless if one was entered
this.game.debug(
`F1594 | ${this.name}.js | doSuccess > ${direct_object.id} ${indirect_preposition} ${indirect_object.id} `
);
msg += `$(We) look at ${direct_object.articlename}`;
msg += !room ? ` in ${indirect_object.articlename}` : ``;
msg += `. `;
msg += direct_object.getDescription("look");
this.handleSuccess(msg, direct_object);
return true;
}
if (direct_object.id === currentRoom.id) {
this.game.printCurrentRoom();
return true;
} else if (direct_object.is.global) {
/**
* direct_object.is.global applies to global
* floor, ceiling, walls, sun & sky, etc
* See Game.getGlobalAssetDescription
* for more info.
*/
let global_description =
this.game.getGlobalAssetDescription(direct_object);
if (global_description) {
msg = A.getSAF.call(this.game, global_description);
}
} // is.global
else if (
input.parsedNoun1.matches.direction ||
direct_object instanceof adventurejs.Exit
) {
var exit = direct_object;
if ("undefined" === typeof exit) {
msg += `$(We) don't see any exit in that direction. `;
this.handleFailure(msg);
return true;
}
var exitDescription = exit.getDescription("look");
if (exitDescription) {
// if the exit has a description, print that
msg += exitDescription;
} else {
// otherwise print this
msg += `It's a passage ${exit.direction}`;
msg +=
exit.destination && exit.is.used
? ` to the ${exit.destination}`
: ``;
msg += `. `;
}
if (msg) this.game.print(msg, input.output_class);
return true;
} // if(input.parsedNoun1.matches.direction)
// it's not global, just use its description
if (!direct_object.is.global) {
msg += direct_object.getDescription("look");
}
if (
direct_object.isDOV("write") &&
direct_object.written_strings.length &&
direct_object.append_written_strings_to_description
) {
msg += `Written on ${direct_object.articlename}`;
if (1 === direct_object.written_strings.length) {
msg += ` is a phrase: ${direct_object.written_strings[0]}`;
} else {
msg += ` are several phrases: `;
for (var i = 0; i < direct_object.written_strings.length; i++) {
msg += direct_object.written_strings[i];
if (direct_object.written_strings.length > i + 1) msg += `, `;
}
}
msg += `. `;
}
if (
direct_object.is.typing_target &&
direct_object.written_strings.length &&
direct_object.append_written_strings_to_description
) {
if (1 === direct_object.written_strings.length) {
msg += `A line of input has been entered on the screen: `;
msg += `<br> ${direct_object.written_strings[0]}`;
} else {
msg += `Several lines of input have been entered on the screen: `;
for (var i = 0; i < direct_object.written_strings.length; i++) {
msg += `<br> ${direct_object.written_strings[i]}`;
}
}
}
if (direct_object.hasAspectAt) {
for (var i = 0; i < this.game.dictionary.prepositions.length; i++) {
var preposition = this.game.dictionary.prepositions[i];
if (direct_object.hasAspectAt(preposition)) {
// if( preposition === "attached" )
// {
// // important for attached - getCountOfListableContentsAt
// // may be inaccurate until attachments are known
// // direct_object.setAttachmentsKnown();
// direct_object.setAspectContentsKnown("attached",true);
// }
direct_object.setAspectContentsKnown(preposition, true);
// 2023.04.06 not sure why this was here, it was preventing
// printing descriptions of substances in containers
// if( 0 >= direct_object.getCountOfListableContentsAt(preposition) )
// {
// continue;
// }
if (!direct_object.aspects[preposition].list_in_examine) {
continue;
}
if (
preposition === "in" &&
direct_object.isDOV("open") &&
direct_object.dimensions.opacity === 1
) {
// it's closed and opaque
continue;
}
msg += direct_object.getPrintableListOfContentsAt(preposition);
}
}
}
// connections
//if( direct_object.dov.tie?.with_params.connections.length )
if (direct_object.DOVisConnectedToAnything("tie")) {
let objects = { objects: direct_object.DOVgetConnections("tie") };
msg += `${direct_object.Articlename} is tied to `;
msg += this.game.getPrintableObjectList(objects);
msg += direct_object.isIn(player)
? `, while $(we) hold an end of it`
: ``;
msg += `. `;
}
if (direct_object.IOVisConnectedToAnything("tie")) {
let connections = direct_object.DOV.getConnections();
for (let r = 0; r < connections.length; r++) {
let rope = this.game.getAsset(connections[r]);
msg += `Tied to ${direct_object.articlename} is a ${rope.name}`;
if (1 < rope.DOVgetConnectionCount()) {
let objects = {
objects: rope.DOVgetConnections(),
exclusions: direct_object.id,
};
msg += `, which is also tied to ${this.game.getPrintableObjectList(
objects
)}`;
}
msg += rope.isIn(player) ? `, while $(we) hold an end of it` : ``;
msg += `. `;
}
}
// if player input carefully examine
if (-1 !== input.input_verb.indexOf("carefully")) {
msg += direct_object.hasDescription("careful")
? direct_object.getDescription("careful")
: `Careful examination reveals nothing new. `;
}
msg = msg || `${direct_object.Articlename} hasn't got a description. `;
this.handleSuccess(msg, direct_object);
return true;
},
};
})(); // examine