// parseInput.js
(function () {
/*global adventurejs A*/
"use strict";
var p = adventurejs.Parser.prototype;
/**
* Parse an input string.
* @memberOf adventurejs.Parser
* @method adventurejs.Parser#parseInput
* @param {String} input Player input.
*/
p.parseInput = function Parser_parseInput(input) {
this.game.log("log", "high", "parseInput.js > " + input, "Parser");
let results;
// we already dispatched inputEnter when player entered input
// but parser.parseInput() can be called by other means so we distinguish
// between dispatching inputEnter vs inputParseBegin
this.game.reactor.dispatchEvent(this.game.events.inputParseBegin);
// The undo verb supercedes all parsing.
if ("undo" === input) {
//this.game.dictionary.verbs.undo.do();
this.game.printInput(input);
this.game.dictionary.doVerb("undo");
return;
}
/**
* At this point "input" is just the string entered by player.
* If we're processing stacked input, then input will already
* have been parsed giving us serialized object ids.
*/
var parsed_input = input;
var unparsed_input = input;
/**
* Save a snapshot of the game state.
* Technically this is saving LAST turn's snapshot
* before engaging in this turn.
* IMPORTANT: undo must come first.
*/
A.addWorldToHistory.call(this.game, A.getBaselineDiff.call(this.game)); // delta from baseline
/**
* input_history_index is part of a convenience that lets
* players use the arrow keys to re-enter their own
* prior input, as in a shell. Player might have used it
* to enter current input, so reset it now.
*/
this.input_history_index = -1;
/**
* output_class is one of the params that's carried through
* the entire parse and then used to apply css styles to
* the output, if applicable.
*/
var output_class = "";
/**
* Run any custom parsers created by author.
* If custom parser returns a string, parse will continue.
* If it returns null or false, parse will end.
*/
if (0 < this.custom_parsers.length) {
var keys = Object.keys(this.custom_parsers);
for (var i = 0; i < this.keys.length; i++) {
var parser_name = keys[i];
if (true === this.custom_parsers_enabled[parser_name]) {
input = this.custom_parsers[parser_name].parseInput();
// TODO at the moment false & null have same results
// but leaving open option to handle differently
if (false === input) {
this.is_input_queued = false;
this.game.reactor.dispatchEvent(
this.game.events.inputParseComplete
);
return false;
} else if (null === input) {
this.is_input_queued = false;
this.game.reactor.dispatchEvent(
this.game.events.inputParseComplete
);
return null;
}
}
}
}
/**
* Are we parsing stacked commands?
* Stacked commands are made when user inputs
* something like "do this THEN do that".
*/
// if we're parsing stacked input, there are several items
// we want to carry over from the original input
var carried_input = new adventurejs.Input({
game_name: this.game.game_name,
});
// when substrings are replaced in compound input,
// we want to keep track of replacements for subsequent output
if (this.input_queue.length > 0) {
// We're currently parsing the first item in the stack,
// so remove that item.
// In the event of stacked input, we want to carry forward
// a record of string replacements that have occurred
carried_input.replacements = Object.assign(
carried_input.replacements,
this.input_history[0].replacements
);
// also carry forward a record of the original input string
carried_input.unparsed_input = this.input_history[0].unparsed_input;
if (this.input_queue[0].printInput) {
this.game.printInput(A.deserialize(input));
}
if ("undefined" !== typeof this.input_queue[0].output_class) {
output_class = this.input_queue[0].output_class;
}
// it's possible to include pre-set output in the input_queue
// see goto for example
if ("undefined" !== typeof this.input_queue[0].output) {
this.game.print(this.input_queue[0].output, output_class);
}
//
if (
"undefined" !== typeof this.input_queue[0].linefeed &&
this.input_queue[0].linefeed
) {
this.game.print("", "linefeed");
}
this.input_queue.shift();
} // if( this.input_queue.length > 0 )
else {
// sanitizeInput cleans input of several conditions.
// Also has the ability to revise unparsed_input in the case of 'again'
var sanitized_input = this.sanitizeInput(parsed_input, unparsed_input);
this.game.log(
"log",
"high",
"parseInput.js > sanitizeInput return parsed: " +
sanitized_input[0] +
", unparsed: " +
sanitized_input[1],
"Parser"
);
parsed_input = sanitized_input[0];
unparsed_input = sanitized_input[1];
// Only prints the first item in queue
this.game.printInput(A.deserialize(parsed_input));
}
// make a new input object
this.input_history.unshift(
new adventurejs.Input({ game_name: this.game.game_name })
);
var this_turn = this.input_history[0];
// keep a reference to last turn
var last_turn = this.input_history[1];
// unparsed_input might not actually be the original input
// in cases like 'again' where we've subbed in the prior turn's input
this_turn.input = unparsed_input;
this_turn.output_class = output_class;
// if we're parsing compound input aka stacked commands,
// we want to carry a record of string replacements
// for later reference
this_turn.replacements = Object.assign(
this_turn.replacements,
carried_input.replacements
);
if (carried_input.unparsed_input) {
this_turn.unparsed_input = carried_input.unparsed_input;
} else {
this_turn.unparsed_input = unparsed_input;
}
// print the preparsed input back to the player
// this.game.printInput( A.deserialize(input) );
// when printed here, prints for each queue item,
// but including for "this and that" which I don't want
// Handle quote delimited substrings in input.
parsed_input = this.parseStrings(parsed_input);
// join compound names into asset IDs
// this was breaking on 'item but other item'
// parsed_input = this.joinCompoundPhrases(parsed_input);
// we're going to handle 'but' in a later step
// so we temporarily split the string by 'but'
let but_arr = parsed_input.split(" but ");
let but_str = "";
for (let i = 0; i < but_arr.length; i++) {
but_str += this.joinCompoundPhrases(but_arr[i]);
if (i < but_arr.length - 1) but_str += " but ";
}
parsed_input = but_str;
// strip out articles
parsed_input = this.stripArticles(parsed_input);
// join some common compound prepositions into single words
parsed_input = this.joinCompoundPrepositions(parsed_input);
// join some common compound verbs into single words
parsed_input = this.joinCompoundVerbs(parsed_input);
// strip conjunctions and convert them into symbols we can handle
parsed_input = this.stripConjunctions(parsed_input);
// Done parsing input, save parsed_input to the global input object.
this_turn.parsed_input = parsed_input;
// Split input into an array of individual words.
var parsed_input_array = parsed_input.split(" ");
// Save the input array to the global object.
this_turn.parsed_input_array = parsed_input_array;
/* *
* Each of the "compressVerb" functions can write to input_verb
* but if verb was just one word, it won't have been saved yet,
* and we may want to send to handleWord. But it's possible
* that player is replying to a soft prompt and hasn't entered
* a verb, so first let's verify whether the first word is in
* fact a verb.
*/
if (!this_turn.input_verb) {
var parsedVerb = this.parseVerb(parsed_input_array[0]);
// recognize it as a verb, save it
if (parsedVerb) {
this_turn.input_verb = parsed_input_array[0];
} else if (last_turn.soft_prompt.noun1) {
// last turn prompted for a word so assume we're recyling last turn's verb
parsed_input_array.unshift(last_turn.getVerb());
parsed_input = this.input_history[1].parsed_input + " " + parsed_input;
this_turn.parsed_input = parsed_input;
parsed_input_array = parsed_input.split(" ");
this_turn.parsed_input_array = parsed_input_array;
}
}
// for use in cases of number prompts
var isWordNumber =
null !== last_turn.disambiguate.index &&
!isNaN(Number(parsed_input_array[0]));
// IS INPUT USEABLE?
// We can't do anything with the input.
if (
1 === parsed_input_array.length &&
"" === parsed_input_array[0] &&
false === isWordNumber
) {
this.parseNoInput();
}
// valid one word inputs include intransitive verbs
// such as look, inventory, and directions
// we don't know that one of these has been input, but we can move forward
else if (1 === parsed_input_array.length) {
this_turn.found_word = parsed_input_array[0];
this.handleWord();
}
// Parse the full sentence
else {
if (false === this.parseSentence()) return false;
if (false === this.verifySentence()) return false;
if (false === this.verifySentenceStructure()) return false;
this.handleSentence();
}
// TODO special handling for say / ask / tell
this.game.updateDisplayRoom();
// If player used a period to input distinct actions,
// perform the next one in the queue.
if (this.input_queue.length > 0) {
this.game.reactor.dispatchEvent(this.game.events.inputQueueNext);
this.is_input_queued = true;
this.parseInput(this.input_queue[0].input);
} else {
this.is_input_queued = false;
//this.game.worldSave();
this.game.reactor.dispatchEvent(this.game.events.inputParseComplete);
return;
}
}; // parseInput
})();