// handleSentence.js
(function () {
/*global adventurejs A*/
"use strict";
var p = adventurejs.Parser.prototype;
/**
* Handle multi-word input.
* @memberOf adventurejs.Parser
* @method adventurejs.Parser#handleSentence
*/
p.handleSentence = function Parser_handleSentence() {
this.game.log("log", "high", "handleSentence.js > BEGIN.", "Parser");
var this_turn = this.input_history[0];
var last_turn = this.input_history[1];
var parsed_verb_name = "",
dictionary_verb = null,
dictionary_verb_name = "",
nouns = [],
exclusions = [],
prepositions = [],
parsedNouns = [],
count = { phrase: 0 },
msg = "";
// ----------
// parsed one word
// ----------
if (this_turn.parsed_word.enabled) {
// we've arrived here via handleWord and
// we're probably amending last turn's input
this_turn.verified_sentence = A.clone.call(
this.game,
last_turn.verified_sentence,
);
// get verb + nouns from last turn prompt,
// which may differ from last turn's input
if (this_turn.parsed_word.verb) {
this_turn.setVerb(1, this_turn.parsed_word.verb);
}
for (let i = 1; i <= 3; i++) {
if (this_turn.parsed_word["noun" + i]) {
this_turn.setNoun(i, this_turn.parsed_word["noun" + i]);
}
if (this_turn.parsed_word["preposition" + i]) {
this_turn.setPreposition(i, this_turn.parsed_word["preposition" + i]);
}
}
} // parsed_word
// ----------
// get verb and nouns
// ----------
if (this_turn.hasInput()) {
//console.warn('this_turn.hasInput');
// we've got fresh input from parseInput
if (this_turn.hasVerb()) {
parsed_verb_name = this_turn.getVerb();
}
for (let i = 1; i <= 3; i++) {
if (this_turn.hasPhrase(i)) {
prepositions[i] = this_turn.getPreposition(i);
nouns[i] = this_turn.getNoun(i);
exclusions[i] = this_turn.getExclusion(i);
}
}
} else {
// we shouldn't arrive here but may need an error message
this.game.debug(`F1207 | handleSentence.js | didn't receive words `);
msg += this.getUnparsedMessage(this_turn.input);
this.game.print(msg, this_turn.output_class);
}
// ----------
// oops
// ----------
if ("oops " === this_turn.input.substring(0, 5)) {
this.game.printInput(this_turn.input);
this.game.dictionary.doVerb("oops");
return false;
}
// ----------
// verify verb
// ----------
if (!parsed_verb_name || !this.dictionary.verbs[parsed_verb_name]) {
let err = `handleSentence.js > Verb not found. This may happen if a compound phrase is improperly found. Original input: ${this_turn.input} Parsed input: ${this_turn.parsed_input}`;
this.game.log("warn", "high", err, "Parser");
this.game.debug(
`F1721 | handleSentence.js | verb not found. Check the console for more info. `,
);
msg += this.getUnparsedMessage(this_turn.input);
this.game.print(msg, this_turn.output_class);
return false;
}
// if verb.let_verb_handle_remaining_input, bypass handleSentence
// originally used for oops and left in for future cases
if (
this.dictionary.verbs[parsed_verb_name].let_verb_handle_remaining_input
) {
this.dictionary.doVerb(parsed_verb_name);
return false;
}
// ----------
// qualify verb
// ----------
// Check verb against circumstances that prevent its use,
// such as if player is constrained, lying on the floor, etc.
dictionary_verb = this.qualifyParsedVerb({
parsed_verb_name: parsed_verb_name,
});
// If qualifyParsedVerb returned false, then it already
// printed an error msg to the player, so we can just...
if (!dictionary_verb) return false;
// ----------
// get phrase count
// ----------
for (const key in this_turn.verified_sentence) {
if (key.startsWith("phrase")) {
count.phrase++;
}
}
//console.warn("count.phrase", count.phrase);
// ----------
// for each phrase
// ----------
for (let n = 1; n <= count.phrase; n++) {
//var n = parseInt(key.replace("phrase", ""), 10);
var phrase = this_turn.verified_sentence[`phrase${n}`];
var noun = phrase.noun;
var preposition = phrase.preposition;
var exclusion = phrase.exclusion;
this.game.log(
"log",
"high",
`handleSentence.js > handle phrase ${n}`,
"Parser",
);
// this shouldn't happen
if (!noun && !preposition) {
this.game.debug(
`F1034 | handleSentence.js | ${dictionary_verb.name} didn't receive a noun or preposition`,
);
msg += `How did $(we) want to ${this_turn.input}? `;
this.game.print(msg, this_turn.output_class);
return false;
}
// unsupported noun? ----------
if (noun && !dictionary_verb[`phrase${n}`].accepts_noun) {
this.game.debug(
`F1036 | handleSentence.js | ${dictionary_verb.name} received a noun it can't handle`,
);
msg += this.getUnparsedMessage(this_turn.input);
this.game.print(msg, this_turn.output_class);
return false;
}
// unsupported preposition without noun? ----------
if (
preposition &&
!noun &&
!dictionary_verb[`phrase${n}`].accepts_preposition_without_noun
) {
this.game.debug(
`F1033 | handleSentence.js | ${dictionary_verb.name} received preposition without noun, soft prompt noun${n}`,
);
msg += `What would $(we) like to ${this_turn.input}? `;
this_turn.setSoftPrompt({
[`noun${n}`]: true,
verb: this_turn.input_verb,
});
this.game.print(msg, this_turn.output_class);
return false;
}
// unsupported noun without preposition? ----------
if (
noun &&
!preposition &&
dictionary_verb[`phrase${n}`].requires_preposition
) {
this.game.debug(
`F1041 | handleSentence.js | ${dictionary_verb.name} received noun without preposition, soft prompt preposition${n}`,
);
//msg += `How did $(we) want to ${this_turn.input_verb} ${phrase.noun}? `;
switch (n) {
case 1:
msg += `Where in relation to ${
this.game.getAsset(parsedNouns[1].object_id).articlename
} did $(we) want to ${dictionary_verb.prettyname}? `;
break;
case 2:
msg += `Where in relation to ${
this.game.getAsset(parsedNouns[2].object_id).articlename
} did $(we) want to ${dictionary_verb.prettyname} ${
this.game.getAsset(parsedNouns[1].object_id).articlename
}? `;
break;
case 3:
msg += `That seems to be missing a preposition. Can $(we) try phrasing that another way? `;
break;
}
this_turn.setSoftPrompt({
[`preposition${n}`]: true,
verb: this_turn.input_verb,
});
this.game.print(msg, this_turn.output_class);
return false;
}
// unsupported preposition? ----------
if (
preposition &&
dictionary_verb[`phrase${n}`].accepts_these_prepositions.length &&
-1 ===
dictionary_verb[`phrase${n}`].accepts_these_prepositions.indexOf(
preposition,
)
) {
this.game.debug(
`F1222 | handleSentence.js | ${
dictionary_verb.name
}.phrase${n}.accepts_these_prepositions ${dictionary_verb[
`phrase${n}`
].accepts_these_prepositions.toString()}`,
);
msg += `<em class='unparsed'>${A.propercase(preposition)} ${
noun ? noun : ""
}</em> doesn't seem to work here. `;
this.game.print(msg, this_turn.output_class);
return false;
}
// noun ----------
if (noun) {
// plural ----------
// "this and that" is parsed as "this&that"
let split_noun = noun.split("&");
// "all keys" might be parsed as "key=keya,keyb,keyc"
// we deliberately do not handle that here as
// it conflicts with a later disambiguation handler
let noun_is_plural = "all" === noun || split_noun.length > 1;
if (
noun_is_plural &&
!dictionary_verb[`phrase${n}`].accepts_plural_noun
) {
this.game.debug(
`F1032 | handleSentence.js | ${dictionary_verb.name}.phrase${n}.accepts_plural_noun is false`,
);
msg += `$(We) can't ${dictionary_verb.prettyname} more than one thing at a time. `;
this.game.print(msg, this_turn.output_class);
return false;
}
// Split noun string and queue verb-noun inputs for each item
if (split_noun.length > 1) {
for (let i = 1; i < split_noun.length; i++) {
this.input_queue.push({
input: this_turn.parsed_input.replace(noun, split_noun[i]),
printInput: false,
});
}
noun = split_noun[0];
} // plural
// write a parsedNoun back to the phrase
phrase.parsedNoun = this.parseNoun(noun);
} // noun
// parsedNoun empty? ----------
if (phrase.parsedNoun && !phrase.parsedNoun.matches.all.length) {
this.game.log(
"warn",
"high",
[
"handleSentence.js > input: " +
this_turn.input +
", parsedNouns[1]: ",
parsedNouns[1],
],
"Parser",
);
// save record for "oops"
this_turn.unknown_word = noun;
this.game.debug(
`F1035 | handleSentence.js | ${noun} isn't recognized `,
);
msg += this.getUnparsedMessage(phrase.parsedNoun.deserialized_input);
this.game.print(msg, this_turn.output_class);
return false;
} // parsedNoun
this.game.log(
"log",
"medium",
[`handleSentence.js > phrase${n}.parsedNoun:`, phrase.parsedNoun],
"Parser",
);
// in means on? ----------
// Replace 'in' with 'on' where applicable such as 'sit in chair'
if (phrase.parsedNoun && !dictionary_verb[`phrase${n}`].accepts_noun) {
this.game.debug(
`F1036 | handleSentence.js | ${dictionary_verb.name} received a noun it can't handle`,
);
msg += this.getUnparsedMessage(this_turn.input);
this.game.print(msg, this_turn.output_class);
return false;
}
if (
phrase.parsedNoun &&
"in" === preposition &&
this.game.getAsset(phrase.parsedNoun.object_id).quirks.in_means_on &&
dictionary_verb.in_can_mean_on
) {
this_turn.setInPhrase(n, "preposition", "on");
}
// exclusion (aka but) ----------
// examples of exclusions:
// "take all but sword"
// "take all gems but green gem"
// "take all gems but green gem and blue gem"
if (exclusion && "string" === typeof exclusion) {
var exclusion_split = exclusion.split("&");
for (let x = 0; x < exclusion_split.length; x++) {
var excluded_noun = exclusion_split[x];
var excluded_parsed_noun = this.parseNoun(excluded_noun);
this.game.log(
"log",
"medium",
["handleSentence.js > Exclude:", excluded_parsed_noun],
"Parser",
);
if (excluded_parsed_noun) {
for (let i = 0; i < excluded_parsed_noun.matches.all.length; i++) {
let excluded_id = excluded_parsed_noun.matches.all[i];
// remove excluded_id from parsedNoun.matches.all
phrase.parsedNoun.matches.all.splice(
phrase.parsedNoun.matches.all.indexOf(excluded_id),
1,
);
// remove excluded_id from parsedNoun.matches.qualified
phrase.parsedNoun.matches.qualified.splice(
phrase.parsedNoun.matches.qualified.indexOf(excluded_id),
1,
);
// remove excluded_id from parsedNoun.matches.unambiguous
if (phrase.parsedNoun.matches.unambiguous === excluded_id)
phrase.parsedNoun.matches.unambiguous = "";
} // for excluded_parsed_noun.matches.all
} // excluded_parsed_noun
} // for exclusion_split
this.game.log(
"log",
"medium",
[
`handleSentence.js > phrase${n}.parsedNoun minus exclusions: `,
phrase.parsedNoun,
],
"Parser",
);
} // exclusion
// exclude from prior
if (n > 1) {
let first_phrase = this_turn.getPhrase(1);
let last_split_noun = [];
last_split_noun = first_phrase.noun && first_phrase.noun.split("&");
let last_noun_is_plural =
first_phrase.noun === "all" || last_split_noun.length > 1;
let exclude_from_plural =
dictionary_verb[`phrase${n}`].not_in_prior_plural;
if (last_noun_is_plural && exclude_from_plural) {
// was there an unambiguous match for this?
if (phrase.parsedNoun.matches.unambiguous) {
// remove unambiguous from prior noun's matches
let excluded_id = phrase.parsedNoun.matches.unambiguous;
let allindex =
first_phrase.parsedNoun.matches.all.indexOf(excluded_id);
let qualindex =
first_phrase.parsedNoun.matches.qualified.indexOf(excluded_id);
// remove excluded_id from parsedNoun.matches.all
first_phrase.parsedNoun.matches.all.splice(allindex, 1);
// remove excluded_id from parsedNoun.matches.qualified
first_phrase.parsedNoun.matches.qualified.splice(qualindex, 1);
// remove excluded_id from parsedNoun.matches.unambiguous
if (first_phrase.parsedNoun.matches.unambiguous === excluded_id)
first_phrase.parsedNoun.matches.unambiguous = "";
if (allindex > -1 || qualindex > -1) {
// we might need to re-qualify the last noun
if (first_phrase.parsedNoun) {
first_phrase.parsedNoun = this.qualifyParsedNoun({
parsedNoun: first_phrase.parsedNoun,
parsedVerb: dictionary_verb.name,
nounIndex: n - 1,
});
// If qualifyParsedNoun returned false, then it already
// printed an error msg to the player, so we can just...
if (!phrase.parsedNoun) return false;
}
}
}
}
// remove iobj from dobj collection ----------
// @TODO reexamine - this seems like it should be caught in prior block
if (
phrase.parsedNoun &&
phrase.parsedNoun.object_id &&
first_phrase.parsedNoun.object_id &&
first_phrase.parsedNoun.matches.qualified.length > 1
) {
for (
var i = first_phrase.parsedNoun.matches.qualified.length - 1;
i > -1;
i--
) {
if (
phrase.parsedNoun.qualified_object_id ===
first_phrase.parsedNoun.matches.qualified[i]
) {
first_phrase.parsedNoun.matches.qualified.splice(i, 1);
}
}
} // remove iobj from dobj collection
} // n>1 / exclude from prior
// qualify noun ----------
if (phrase.parsedNoun) {
phrase.parsedNoun = this.qualifyParsedNoun({
parsedNoun: phrase.parsedNoun,
parsedVerb: dictionary_verb.name,
nounIndex: n,
});
// If qualifyParsedNoun returned false, then it already
// printed an error msg to the player, so we can just...
if (!phrase.parsedNoun) return false;
}
// redirectVerb ----------
// Has author used redirectVerb for this verb on direct_object?
if (
n === 1 &&
phrase.parsedNoun &&
phrase.parsedNoun.qualified_object_id
) {
let direct_object = this.game.getAsset(
phrase.parsedNoun.qualified_object_id,
);
if (direct_object.redirected_verbs[dictionary_verb.name]) {
// requalify verb
dictionary_verb = this.qualifyParsedVerb({
parsed_verb_name:
direct_object.redirected_verbs[dictionary_verb_name],
});
if (!dictionary_verb) return false;
}
} // redirectVerb
} // for each phrase
// ----------
// each phrase redux
// ----------
// I think this is reiterating to account for changes made during the first pass
for (let n = 1; n <= count.phrase; n++) {
var phrase = this_turn.verified_sentence[`phrase${n}`];
var parsedNoun = phrase.parsedNoun;
if (!parsedNoun) continue;
this.game.log(
"log",
"high",
`handleSentence.js > final check on phrase ${n}`,
"Parser",
);
if (parsedNoun.matches.qualified.length > 1) {
var lookup = this.game.world_lookup[parsedNoun.serialized_input];
var is_plural = false;
if (1 < parsedNoun.deserialized_input.split("&").length) {
is_plural = true;
}
if (lookup) {
if ("plural" === lookup.type || "group" === lookup.type) {
is_plural = true;
}
}
if (is_plural) {
if (!dictionary_verb[`phrase${n}`].accepts_plural_noun) {
// verb doesn't accept a plural noun
this.game.debug(
`F1202 | handleSentence.js | ${dictionary_verb.name} doesn't handle multiple objects `,
);
this.game.log(
"warn",
"high",
"handleSentence.js > " +
dictionary_verb.name +
" doesn't handle multiple objecs: " +
parsedNoun.deserialized_input,
"Parser",
);
this.printNounDisambiguation({
parsedNoun: parsedNoun,
nounIndex: n,
});
return false;
}
}
// meaning, parsedNoun.matches.qualified.length > 1
// but verb doesn't handle plurals
if (!is_plural && !dictionary_verb.let_verb_handle_disambiguation) {
this.game.debug(
`F1201 | handleSentence.js | phrase${n}.parsedNoun needs disambiguation `,
);
this.game.log(
"log",
"high",
"handleSentence.js > parsedNoun" +
n +
" needs disambiguation: " +
parsedNoun.matches.qualified,
"Parser",
);
this.printNounDisambiguation({
parsedNoun: parsedNoun,
nounIndex: n,
});
return false;
}
} // more than one qualified
} // each phrase redux
// do the thing! ----------
this.game.log("log", "high", "handleSentence.js > END.", "Parser");
let direct_object = this_turn.getAsset(1);
if (direct_object?.is.collection && dictionary_verb.enqueue_collections) {
this.game.debug(
`F1091 | handleSentence.js | ${
this_turn.getAsset(1).name
}.is.collection, enqueueing collection`,
);
this.game.print(msg);
dictionary_verb.enqueueCollection(direct_object);
} else {
this.game.debug(
`F1143 | handleSentence.js | doVerb ${dictionary_verb.name} `,
);
dictionary_verb.do();
}
return true;
}; // handleSentence
})();