Another contribution instead of a question. This is a simple bit of code that handles things like startup menus.
It lets you declare the menu text, the keywords that can be entered from the menu, and to associate each menu with a method on the menu object which will be called when the player enters the corresponding keyword.
There are also a couple convenience methods for “hit any key to continue” and “hit enter to continue” prompts—a frequent task I want to accomplish in this kind of menu is to display some information (the game’s ABOUT, CREDITS, or other informational messages).
The code:
class TextMenu: object
menuText = nil
menuPrompt = '\b>'
menuOptions = nil
menuClear = true
allowBlank = nil
_showMenu() {
if(menuClear == true)
cls();
"<<menuText>> ";
}
showMenu() {
local cmd, r;
_showMenu();
for(;;) {
"<<menuPrompt>>";
cmd = inputManager.getInputLine(nil, nil);
r = parseInput(cmd);
if(r != nil)
return(r);
_showMenu();
}
}
parseInput(txt) {
local kw, r;
if(!txt)
return(nil);
if(allowBlank && (rexMatch('<space>*$', txt) != nil)) {
handleDefault();
return('');
}
if(rexMatch('<space>*(<alpha>+)<space>*$', txt) != nil) {
kw = rexGroup(1)[3].toLower();
} else {
return(nil);
}
r = nil;
menuOptions.forEachAssoc(function(k, v) {
if(r != nil) return;
if(k.startsWith(kw)) {
r = k;
handleMatch(v);
}
});
return(r);
}
handleDefault() {
}
handleMatch(v) {
switch(dataTypeXlat(v)) {
case TypeProp:
self.(v)();
break;
case TypeFuncPtr:
v();
break;
}
}
anyKeyToContinue(txt?, prompt?) {
if(txt) "<<txt>>";
if(prompt == true) {
"<br><br>Press <b>any key</b> to continue ";
} else if(prompt) {
"<<prompt>> ";
}
for(;;) {
inputManager.getKey(nil, nil);
return;
}
}
enterToContinue(txt?, prompt?) {
if(txt) "<<txt>>";
if(prompt == true) {
"<br><br>[<b>Enter</b>] to continue ";
} else if(prompt) {
"<<prompt>> ";
}
for(;;) {
inputManager.getInputLine(nil, nil);
return;
}
}
;
This allows you do to declare a menu something like:
mainMenu: TextMenu
menuPrompt = '>>'
menuText = "<br><b>textMenu Test</b><br><br>
<b>ABOUT</b> for information about this game<br>
<b>FOO</b> for some filler text<br>
<b>BAR</b> for an inline function<br>
<b>QUIT</b> to exit<br><br><br> "
menuOptions = static [
'about' -> &aboutMethod,
'foo' -> &fooMethod,
'bar' -> function() {
enterToContinue('Bar.\n ', true); showMenu();
},
'quit' -> &quitMethod
]
aboutMethod() {
versionInfo.showAbout();
enterToContinue(nil, true);
showMenu();
}
fooMethod() {
enterToContinue('This is some "foo" text.\n ', true);
showMenu();
}
quitMethod() {
"Quitting the game\n ";
}
;
When invoked, that gives you something like:
textMenu Test
ABOUT for information about this game
FOO for some filler text
BAR for an inline function
QUIT to exit
>>
If the player types ABOUT
(or any shortened form of it: “ABOU”, “ABO”, “AB”, or “A”) they get the game’s ABOUT text (such as it is), followed by a prompt to hit any key to continue, after which they’re returned to the menu.
This is controlled by mainMenu.aboutMethod()
, which is associated with the ABOUT
keyword in mainMenu.menuOptions
.
Here’s a complete example that includes all the code above, wrapped in a simple “game” that just displays the menu at startup. Included just because you should be able to cut and paste it and it’ll compile:
#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>
class TextMenu: object
menuText = nil
menuPrompt = '\b>'
menuOptions = nil
menuClear = true
allowBlank = nil
_showMenu() {
if(menuClear == true)
cls();
"<<menuText>> ";
}
showMenu() {
local cmd, r;
_showMenu();
for(;;) {
"<<menuPrompt>>";
cmd = inputManager.getInputLine(nil, nil);
r = parseInput(cmd);
if(r != nil)
return(r);
_showMenu();
}
}
parseInput(txt) {
local kw, r;
if(!txt)
return(nil);
if(allowBlank && (rexMatch('<space>*$', txt) != nil)) {
handleDefault();
return('');
}
if(rexMatch('<space>*(<alpha>+)<space>*$', txt) != nil) {
kw = rexGroup(1)[3].toLower();
} else {
return(nil);
}
r = nil;
menuOptions.forEachAssoc(function(k, v) {
if(r != nil) return;
if(k.startsWith(kw)) {
r = k;
handleMatch(v);
}
});
return(r);
}
handleDefault() {
}
handleMatch(v) {
switch(dataTypeXlat(v)) {
case TypeProp:
self.(v)();
break;
case TypeFuncPtr:
v();
break;
}
}
anyKeyToContinue(txt?, prompt?) {
if(txt) "<<txt>>";
if(prompt == true) {
"<br><br>Press <b>any key</b> to continue ";
} else if(prompt) {
"<<prompt>> ";
}
for(;;) {
inputManager.getKey(nil, nil);
return;
}
}
enterToContinue(txt?, prompt?) {
if(txt) "<<txt>>";
if(prompt == true) {
"<br><br>[<b>Enter</b>] to continue ";
} else if(prompt) {
"<<prompt>> ";
}
for(;;) {
inputManager.getInputLine(nil, nil);
return;
}
}
;
versionInfo: GameID
name = 'sample'
byline = 'nobody'
authorEmail = 'nobody <foo@bar.com>'
desc = '[This space intentionally left blank]'
version = '1.0'
showAbout() {
"This is the ABOUT text. ";
}
;
mainMenu: TextMenu
menuPrompt = '>>'
menuText = "<br><b>textMenu Test</b><br><br>
<b>ABOUT</b> for information about this game<br>
<b>FOO</b> for some filler text<br>
<b>BAR</b> for an inline function<br>
<b>QUIT</b> to exit<br><br><br> "
menuOptions = static [
'about' -> &aboutMethod,
'foo' -> &fooMethod,
'bar' -> function() {
enterToContinue('Bar.\n ', true); showMenu();
},
'quit' -> &quitMethod
]
aboutMethod() {
versionInfo.showAbout();
enterToContinue(nil, true);
showMenu();
}
fooMethod() {
enterToContinue('This is some "foo" text.\n ', true);
showMenu();
}
quitMethod() {
"Quitting the game\n ";
}
;
gameMain: GameMainDef
newGame() {
mainMenu.showMenu();
}
;
I’m also making it available as a github repo; I’m thinking about doing this with the other little bite-sized libraries/modules I’ve been coding up for my current WIP.