// type.js
(function () {
/* global adventurejs A */
/**
* @augments {adventurejs.Verb}
* @class type
* @ajsnode game.dictionary.verbs.type
* @ajsconstruct MyGame.createVerb({ "name": "type", [...] });
* @ajsconstructedby adventurejs.Dictionary#createVerb
* @hideconstructor
* @ajsinstanceof Verb
* @ajsnavheading CompositionVerbs
* @summary Verb meaning type, as in "type on keyboard".
* @tutorial Scripting_VerbSubscriptions
* @tutorial Verbs_VerbAnatomy
* @tutorial Verbs_VerbProcess
* @tutorial Verbs_ModifyVerbs
* @tutorial Verbs_WriteVerbs
* @ajsdemo WritingDemo, Office, Playroom, Classroom, Library, Scorecard
* @ajscss Styles
* @classdesc
* <pre class="display border outline">
* <span class="ajs-player-input">> type on keyboard</span>
* You type on the keyboard. PEFBIJOVAS;DVLKJ ;LAJKSVJVJLS J.
* Well, walk across it, more like. Your paws aren't very precise.
* The computer blares a warning: "CAT-LIKE TYPING DETECTED!"
* </pre>
* <p>
* <strong>Type</strong> on a {@link adventurejs.Tangible|Tangible}
* {@link adventurejs.Asset|Asset}.
* Requires that the Asset has
* asset.dov.type.enabled set to true. <strong>Type</strong>
* considers a direct object's parent. For instance, type can
* infer the meaning of
* <code class="property">type on paper</code>
* if the specified paper is in a typewriter.
* </p>
* @ajsverbreactions
* @ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
*/
A.Preverbs.type = {
name: "type",
prettyname: "type on",
past_tense: "typed",
synonyms: [],
gerund: "typing",
allow_iov_on_iov: true,
/**
* @ajsverbstructures
* @memberof type
*/
accepts_structures: [
"verb noun", // type "foo"
"verb preposition noun",
// type on typewriter, type on paper
"verb noun preposition noun",
// type "foo" on typewriter, type "foo" on paper
"verb preposition noun preposition noun",
// type on paper with typewriter
"verb noun preposition noun preposition noun",
// type "foo" on paper with typewriter ?
],
/**
* @memberof type
* @ajsverbphrase
* phrase1:
* {
* accepts_noun: true,
* requires_noun: true,
* noun_must_be:
* {
* known: true,
* present_if_tangible: true,
* reachable_if_tangible: true,
* },
* accepts_preposition: true,
* preposition_must_be: ["on", "with"],
* },
*/
phrase1: {
accepts_noun: true,
requires_noun: true,
noun_must_be: {
known: true,
present_if_tangible: true,
reachable_if_tangible: true,
},
accepts_preposition: true,
preposition_must_be: ["on", "with"],
},
/**
* @memberof type
* @ajsverbphrase
* phrase2:
* {
* accepts_noun: true,
* requires_noun: true,
* noun_must_be:
* {
* known: true,
* present_if_tangible: true,
* reachable_if_tangible: true,
* },
* accepts_preposition: true,
* requires_preposition: true,
* preposition_must_be: ["on", "with"],
* },
*/
phrase2: {
accepts_noun: true,
requires_noun: true,
noun_must_be: {
known: true,
present_if_tangible: true,
reachable_if_tangible: true,
},
accepts_preposition: true,
requires_preposition: true,
preposition_must_be: ["on", "with"],
},
/**
* @memberof type
* @ajsverbphrase
* phrase3:
* {
* accepts_noun: true,
* requires_noun: true,
* noun_must_be:
* {
* known: true,
* present_if_tangible: true,
* reachable_if_tangible: true,
* },
* accepts_preposition: true,
* requires_preposition: true,
* preposition_must_be: ["on", "with"],
* },
*/
phrase3: {
accepts_noun: true,
requires_noun: true,
noun_must_be: {
known: true,
present_if_tangible: true,
reachable_if_tangible: true,
},
accepts_preposition: true,
requires_preposition: true,
preposition_must_be: ["on", "with"],
},
/**
* Type breaks our usual direct / indirect pattern
* because technically, the information being typed is
* the direct object, even if no information is specified.
* So for example "type 'foo' on paper" treats 'foo', aka the
* information object, as the direct object, and the paper
* as the direct object. Whereas "type on paper", even though
* no information object has been provided, must infer one,
* leading to the same result.
*
* "type 'foo' on paper with typewriter" treats both the paper
* and the typewriter as indirect objects. So, though it may
* seem that "type on paper" should treat paper as the
* direct object, in fact it does not.
*
* Usually, we can use isDOV() and isIOV() to determine which
* asset acts on another - as with lock and key - but here,
* in order to distinguish direct from indirect object, we add
* a custom verb param to make the relationship explicit.
* @memberof type
* @ajsverbparams
* with_params: {
* // tool is a typing implement
* tool: false,
* // target is a typing target
* target: false,
* },
*/
with_params: {
// tool is a typing implement
tool: false,
// target is a typing target
target: false,
},
doTry: function () {
var input = this.game.getInput();
var target_asset_place;
var room = this.game.getRoom();
var msg = "";
let info_asset,
info_preposition,
target_asset,
target_preposition,
tool_asset,
tool_preposition;
let count = input.getPhraseCount();
for (let i = count; i > 0; i--) {
const asset = input.getAsset(i);
const preposition = input.getPreposition(i);
// is asset information?
if (asset?.is.information) {
if (info_asset) {
this.game.debug(
`D1496 | ${this.name}.js | can't handle multiple information assets`
);
msg += `{We} don't know how to ${input.input}. `;
this.handleFailure(msg);
return null;
}
// we've done minimal noun/preposition gates in phrases
// to allow for input flexibility, but information asset
// shouldn't have a preposition, so check that now
if (preposition) {
this.game.debug(
`D1529 | ${this.name}.js | can't handle preposition`
);
msg += `{We} don't know how to ${this.name} ${preposition} "${asset.articlename}." `;
this.handleFailure(msg);
return null;
}
info_asset = input.verb_params.info_asset = asset;
continue;
}
// is asset a tool?
if (asset?.isIOV(this.name) && asset.iov[this.name].with_params.tool) {
if (tool_asset) {
this.game.debug(
`D1505 | ${this.name}.js | can't handle multiple tool assets`
);
msg += `{We} don't know how to ${input.input}. `;
this.handleFailure(msg);
return null;
}
if (!["on", "with"].includes(preposition)) {
this.game.debug(
`D1524 | ${this.name}.js | can't handle preposition`
);
msg += `{We} don't know how to ${this.name} ${preposition} ${asset.articlename}. `;
this.handleFailure(msg);
return null;
}
tool_asset = input.verb_params.tool_asset = asset;
tool_preposition = input.verb_params.tool_preposition = preposition;
continue;
}
// is asset a target?
if (
asset?.isIOV(this.name) &&
asset.iov[this.name].with_params.target
) {
if (target_asset) {
this.game.debug(
`D1504 | ${this.name}.js | can't handle multiple target assets`
);
msg += `{We} don't know how to ${input.input}. `;
this.handleFailure(msg);
return null;
}
if (preposition !== "on") {
this.game.debug(
`D1514 | ${this.name}.js | can't handle preposition`
);
msg += `{We} don't know how to ${this.name} ${preposition} ${asset.articlename}. `;
this.handleFailure(msg);
return null;
}
target_asset = input.verb_params.target_asset = asset;
target_preposition = input.verb_params.target_preposition =
preposition;
continue;
}
// is asset none of these things?
this.game.debug(
`D1665 | ${this.name}.js | unusable asset ${asset.id} found`
);
msg += `{We} don't know how to ${this.name} ${preposition ? preposition : ""} ${asset ? asset.articlename : ""}. `;
this.handleFailure(msg);
return null;
}
// if we didn't receive one of these three things
// then we got nothin' to work with
if (!info_asset && !target_asset && !tool_asset) {
this.game.debug(`D1663 | ${this.name}.js | no relevant assets found`);
msg += `{We} don't know how to ${input.input}. `;
this.handleFailure(msg);
return null;
}
// if no info_asset we need to make one for consistency with dov/iov write
if (!info_asset) {
info_asset = input.verb_params.info_asset =
this.game.getAsset("global_string");
input.shiftPhrase({ asset: info_asset });
}
// @TODO how to limit input to alpha or numeric
// if no tool_asset, is target_asset in a tool?
if (target_asset && !tool_asset) {
target_asset_place = target_asset.getPlaceAsset();
if (!target_asset_place.isIOV(this.name)) {
this.game.debug(
`D1510 | ${this.name}.js | ${target_asset.id} is not in a typewriter `
);
msg += `{We} can't ${this.name} directly on ${target_asset.articlename}. `;
this.handleFailure(msg);
return null;
}
count++;
tool_asset = input.verb_params.tool_asset = target_asset_place;
input.setAsset(count, tool_asset);
input.setInferred(count, true);
this.game.printInferred(`with ${tool_asset.articlename}`);
}
// if no tool_asset, can we infer tool_asset?
if (!tool_asset) {
// see if there's an obvious keyboard
var room_contents = room.getAllNestedContents();
var asset = null;
var keyboards = [];
for (var i = 0; i < room_contents.length; i++) {
var id = room_contents[i];
var asset = this.game.getAsset(id);
if (!asset) continue;
if (asset.isIOV(this.name) && asset.iov[this.name].with_params.tool) {
keyboards.push(id);
}
}
if (keyboards.length) {
keyboards = this.game.parser.selectKnown(keyboards);
keyboards = this.game.parser.selectReachable(keyboards);
keyboards = this.game.parser.selectVisible(keyboards);
}
if (!keyboards.length) {
this.game.debug(
`D1663 | ${this.name}.js | no assets found with .iov.type.enabled set to true `
);
msg += `{We} {don't} see anything to type on. `;
this.handleFailure(msg);
return null;
}
// increment phrase count because we're adding a new word
count++;
// choice of containers?
if (
keyboards.length > 1 &&
!this.game.settings.auto_pick_inferred_objects
) {
this.game.debug(
`D1664 | ${this.name}.js | multiple keyboards found, disambiguate `
);
// ask player to choose a keyboard
input.setParsedNoun(count, new adventurejs.ParsedNoun());
input.setPreposition(count, "on");
// save containers back to input for next turn disambiguation
input.setParsedNounMatchesQualified(count, keyboards);
// revise the sentence structure
input.setStructure(`${input.getStructure()} preposition noun`);
this.game.parser.printNounDisambiguation({
parsedNoun: input.getParsedNoun(count),
nounIndex: count,
});
return null;
}
// set new input phrase to the keyboard
tool_asset = input.verb_params.tool_asset = this.game.getAsset(
keyboards[0]
);
tool_preposition = input.verb_params.tool_preposition = "on";
input.setPhrase(count, {});
input.setAsset(count, tool_asset);
input.setPreposition(count, "on");
input.setInferred(count, true);
input.setStructure(`${input.getStructure()} preposition noun`);
this.game.printInferred(`on ${tool_asset.articlename}`);
} // !tool_asset
if (tool_asset && !target_asset) {
const tool_aspect = tool_asset.getAspectAt("in");
let contents;
if (tool_aspect) {
contents = tool_aspect.contents;
}
if (contents?.length) {
for (var i = 0; i < contents.length; i++) {
var asset = this.game.getAsset(contents[i]);
if (!asset) continue;
if (asset.isDOV(this.name)) {
// take the first thing found
target_asset = input.verb_params.target_asset = asset;
break;
}
}
}
}
if (target_asset && tool_asset && !target_asset.isIn(tool_asset)) {
this.game.debug(
`D1402 | ${this.name}.js | ${target_asset.id} is not in ${tool_asset.id} `
);
msg += `${target_asset.Articlename} isn't in ${tool_asset.articlename}. `;
this.handleFailure(msg);
return null;
}
// reorder input if needed
// (1) info_asset (2) tool_asset (3) target_asset
let phrase_count = input.getPhraseCount();
let asset_in_info_slot = input.getAsset(1);
if (
info_asset &&
asset_in_info_slot &&
info_asset.id !== asset_in_info_slot.id
) {
// move info_asset to slot 1
let info_found_in_slot;
for (let i = 1; i <= phrase_count; i++) {
if (input.getAsset(i).id === info_asset.id) {
info_found_in_slot = i;
break;
}
}
input.swapPhrases(info_found_in_slot, 1);
}
let asset_in_target_slot = input.getAsset(2);
if (
target_asset &&
asset_in_target_slot &&
target_asset.id !== asset_in_target_slot.id
) {
// move target_asset to slot 2
let target_found_in_slot;
for (let i = 1; i <= phrase_count; i++) {
if (input.getAsset(i).id === target_asset.id) {
target_found_in_slot = i;
break;
}
}
if (target_found_in_slot) input.swapPhrases(target_found_in_slot, 2);
}
let asset_in_tool_slot = input.getAsset(phrase_count);
if (
tool_asset &&
asset_in_tool_slot &&
tool_asset.id !== asset_in_tool_slot.id
) {
// move tool_asset to last slot [phraseCount]
let tool_found_in_slot;
for (let i = 1; i <= phrase_count; i++) {
let asset = input.getAsset(i);
if (asset && asset.id === tool_asset.id) {
tool_found_in_slot = i;
break;
}
}
if (tool_found_in_slot)
input.swapPhrases(tool_found_in_slot, phrase_count);
}
return true;
},
doSuccess: function () {
var input = this.game.getInput();
var subject = input.getSubject();
var results;
var msg = "";
let {
info_asset,
info_preposition,
target_asset,
target_preposition,
tool_asset,
tool_preposition,
} = input.verb_params;
if (target_asset?.isIn(tool_asset)) {
msg += `Using ${tool_asset.articlename}, {we} `;
} else msg += `{We} `;
msg += `type `;
if (input.strings.length) {
if (target_asset) target_asset.typed_strings.push(input.strings[0]);
msg += `"${input.strings[0]}" `;
}
if (target_asset?.isIn(tool_asset)) {
msg += `on ${target_asset.articlename}. `;
} else {
msg += `on ${tool_asset.articlename}. `;
}
// --------------------------------------------------
// print output
// --------------------------------------------------
return this.handleSuccess(msg, target_asset);
},
};
})(); // type