Quest 6: HACK - Adding the OOPS command to Quest 6 v0.3

I believe an OOPS command is essential.

Even if the player is not using a mobile browser, it's still nice to be able to enter 'OOPS RALPH' after accidentally entering 'GIVE GREEN LASER SWORD TO ALPH'.

Even with a full keyboard and the up arrow, the player would still have to manually edit existing text.

OOPS is much easier. It makes typographical errors much less frustrating.


This seems to work without causing any errors with the parser.

NOTE: Everything I added has KV on the line (or in a comment just above the new code).


CLICK HERE TO VIEW THE CODE!
//===========
// Add OOPS |
//===========

parser.commandHistory = [] //KV added for modded parser functions
parser.enteredCmdArr = [] //KV added for modded parser functions


//  KV MODIFIED this function to allow OOPS (and to generally keep histories of commands)
  parser.parse = function(inputText) {
    parser.msg("Input string: " + inputText);
    parser.enteredCmdArr.push(inputText) //KV added to keep entered command history
    // This allows the command system to be temporarily overriden,
    // say if the game asks a question
    if (parser.override) {
      parser.msg("Parser overriden");
      parser.override(inputText)
      delete parser.override
      return
    }
    
    if (inputText) {
      const res = parser.convertInputTextToCommandCandidate(inputText);
      if (typeof res === "string") {
        parsermsg(res);
        world.endTurn(world.PARSER_FAILURE);
        return;
      }
      parser.commandHistory.push(parser.currentCommand) //KV added to keep command history
      parser.currentCommand = res;
    }
    
    // Need to disambiguate, until each of the lowest level lists has exactly one member
    let flag = false;
    for (let i = 0; i < parser.currentCommand.objects.length; i++) {
      for (let j = 0; j < parser.currentCommand.objects[i].length; j++) {
        if (parser.currentCommand.objects[i][j] instanceof Array) {
          if (parser.currentCommand.objects[i][j].length === 1) {
            parser.currentCommand.objects[i][j] = parser.currentCommand.objects[i][j][0];
          }
          else {
            flag = true;
            parser.currentCommand.disambiguate1 = i;
            parser.currentCommand.disambiguate2 = j;
            showMenu(lang.disambig_msg, parser.currentCommand.objects[i][j], function(result) {
              parser.currentCommand.objects[parser.currentCommand.disambiguate1][parser.currentCommand.disambiguate2] = result;
              parser.parse(null);
            });
          }
        }
      }
    }
    if (!flag) {
		
      parser.execute();
 
		parser.lastCmdWasExecuted = true //KV added for OOPS
		parser.Unfound = false //KV added for OOPS
		parser.failedCmd = false //KV added for OOPS
    }
  };
 


//  KV MODIFIED this function to allow OOPS (and to generally keep histories of commands)
  parser.matchItemsToCmd = function(s, cmd) {
    const res = {cmd:cmd, objectTexts:[], objects:[], matches:[]};
    res.score = cmd.score ? cmd.score : 0;
    
    const arr = cmd.regexes.find(el => el.test(s)).exec(s)
    //for (let regex of el.regexes) {
    //  if (regex.test(cmdString)) arr = regex.exec(s)
    //}

    
    const fallbackScope = parser.scope(parser.isVisible);
    arr.shift();  // first element is the whole match, so discard
    
    parser.msg("..Base score: " + res.score);

    for (let i = 0; i < arr.length; i++) {
      if (!cmd.objects[i]) {
        errormsg("That command seems to have an error. It has more capture groups than there are elements in the 'objects' attribute.");
        return false;
      }
      if (cmd.objects[i].ignore) {
        // this capture group has been flagged to be ignored
        continue;
      }
      let objectNames, score = 0;
      res.objectTexts.push(arr[i]);
      if (cmd.objects[i].text) {
        // this capture group has been flagged to be text
        res.objects.push(arr[i]);
        score = 1;
      }
      
      else if (lang.all_regex.test(arr[i]) || lang.all_exclude_regex.test(arr[i])) {
        // Handle ALL and ALL BUT
        if (!cmd.objects[i].scope) console.log("WARNING: Command without scope - " + cmd.name)
        let list = cmd.objects[i].scope ? parser.scope(cmd.objects[i].scope) : fallbackScope;
        let exclude = [game.player];
        
        // anything flagged as scenery should be excluded
        for (let item of list) {
          if (item.scenery || item.excludeFromAll) {
            exclude.push(item);
          }
        }
        
        if (list.length === 0) {
          res.error = cmd.nothingForAll ? cmd.nothingForAll : lang.nothing_msg;
          res.score = -1;
          return res;
        }
        if (lang.all_exclude_regex.test(arr[i])) {
          // if this is ALL BUT we need to remove some things from the list
          // excludes must be in isVisible
          // if it is ambiguous or not recognised it does not get added to the list
          let s = arr[i].replace(all_exclude_regex, "").trim();
          objectNames = s.split(joiner_regex).map(function(el){ return el.trim(); });
          for (let s in objectNames) {
            items = parser.findInList(s, fallbackScope);
            if (items.length === 1) {
              exclude.push(items[0]);
            }
          }
        }
        list = list.filter(function(el) { return !exclude.includes(el); });
        if (list.length > 1 && !cmd.objects[i].multiple) {
          res.error = no_multiples_msg;
          res.score = -1;
          return res;
        }
        score = 2;
        res.objects.push(list.map(function(el) { return [el]; }));
        res.matches.push(arr[i]);
        res.all = true;
      }
      
      else {
        objectNames = arr[i].split(lang.joiner_regex).map(function(el){ return el.trim() });
        if (objectNames.length > 1 && !cmd.objects[i].multiple) {
          res.error = no_multiples_msg;
          res.score = -1;
          return res;
        }
        if (!cmd.objects[i].scope) console.log("WARNING: No scope (or scope not found) in command " + cmd.name)
        let scopes = cmd.objects[i].scope ? [parser.scope(cmd.objects[i].scope), fallbackScope] : [fallbackScope];
        //console.log(scopes)
        
        let objs = [], matches = [];
        let objs2, n;
        for (let s of objectNames) {
          const objNameMatch = lang.article_filter_regex.exec(s);
          if (objNameMatch === null) {
            errormsg("Failed to match to article_filter_regex with '" + s + "', - probably an error in article_filter_regex!");
            return null;
          }
          [objs2, n] = this.findInScope(objNameMatch[1], scopes, cmd.objects[i]);
          if (n === 0) {
			  console.log("KV says: unfound object (s) set to parser.unFound!") //KV added for OOPS
			  parser.unFound = s // KV added for OOPS
			  parser.lastCmdWasExecuted = false //KV added for OOPS
			  parser.failedCmd = cmd  //KV added for OOPS
            res.error = cmd.noobjecterror(s);
            res.score = -1;
            return res;
          }
          else {
            if (n > score) { score = n; }
            objs.push(objs2);
            matches.push(s);
          }
        }
        res.objects.push(objs);
        res.matches.push(matches);
      }
      parser.msg("...Adding to the score: " + score);
      res.score += score;
    }
    return res;
  };

//=============
// OOPS command 
//-------------
//  Added by KV
// 
//  I believe an  OOPS command is essential.
//
//  Even if the player is not using a mobile browser,
//  it's still nice to be able to enter OOPS RALPH after
//  accidentally entering GIVE GREEN LASER SWORD TO ALPH.
//
//  Even with a full keyboard and the up arrow, the player
//  would still have to edit existing text.  OOPS is much easier.
//
//  TODO:  Should OOPS handle commands with no objects?
//
//
//  REQUIREMENTS!
//  -------------
//
//  My modded 'parser.matchItemsToCmd' and 'parser.parse' functions.
//
//  A new array: parser.commandHistory
//  Another new array:  parser.enteredCmdArr
//
//==========================================

commands.push(new Cmd('Oops', {
  regex:/^(?:oops) (.+)$/,
  objects:[
    {scope:parser.isPresent},
  ],
  default:function(item) {
	if (parser.lastCmdWasExecuted) {
		metamsg("There is nothing to correct.");
		return false;
	}
	parser.quickCmd(parser.failedCmd,item)
	return true
  },
}));

//==================
// END OF Add OOPS |
//==================

//KV added this function to make life simple.
function lastCmd(){
	return parser.commandHistory[parser.commandHistory.length-1]
}

//KV added this function to make life simple.

function lastCmdEntered(){
	return parser.enteredCmdArr[parser.enteredCmdArr.length-2]
}

version 3


EDIT

Fixed a line of code (thanks to mrangel).


return parser.commandHistory[parser.commandHistory.length]

That doesn't look right. Shouldn't that be length-1?

If I was doing it I'd probably use unshift rather than push to add commands to the history, so the list is in reverse order (unshift adds an item to the beginning of an array). It seems more likely they'll be accessed in this order, as most things you might use a command history for will be most interested in the last few commands. So keeping the list in most-recent-first order would mean the most recent command is parser.commandHistory[0]


That doesn't look right. Shouldn't that be length-1?

You're right! I fixed it.


If I was doing it I'd probably use unshift rather than push to add commands to the history, so the list is in reverse order

That is a very good idea.


If I Whenever I get rich, remind me to by all of your Kindle books at one time (excluding the few I already own).


I have added an OOPS command, but you will not like it - it only does it for commands typed in.


but you will not like it - it only does it for commands typed in

Ha! I genuinely laughed when I read that.

I tend to turn off the sidepanes in my games, anyway.

...and my added code makes it all work the way I like it, too.

So, I'm content.

Plus, I just realized that I don't have a clue how the player could end up in an OOPS scenario via mouse-click.

Now my logic circuits have overloaded . . .

Somebody call XanMag! Tell him I need a cold one and some fried chicken, stat!


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums