In addition to my
linked list library I've just added a
HookRetrace priority queue thingy. Let me see if I can explain how it makes things easier for you.
Suppose you're writing a simple game of Pong, and you're using HookRetrace to draw rectangles on the screen to represent the paddles and ball, as well as the score, win/loss messages, etc. You want it to start out showing a title screen, then game screen with the paddles on both sides until the player pushes a button, at which point the ball would appear and start moving around. Then, after a score, you'd want to go back to the paused paddles-only view until a button is pressed again. Your system.vc might look something like this:
// All of this code was scribbled down at the spur of
// the moment - I make no claims as to its accuracy. ;P
// The general idea should come through even if I've
// made a few typos or logic errors.
int mainMenuVisible, paddlesVisible, ballVisible;
void autoexec() {
mainMenuVisible = 1;
HookRetrace('MyRetrace');
// blah blah blah
}
void MyRetrace() {
if(mainMenuVisible) {
// display main menu
if(b1) {
paddlesVisible = 1;
mainMenuVisible = 0;
ballVisible = 0;
}
} else if(paddlesVisible) {
// display paddles
if(b1) {
ballVisible = 1;
}
}
if(ballVisible) {
// display ball, handle ball movement
}
}
That looks easy enough, right? But what if you wanted to go back in and add a high score screen that shows up after the game is over? Maybe you'd do this:
int mainMenuVisible, paddlesVisible, ballVisible, gameOver;
void autoexec() {
mainMenuVisible = 1;
HookRetrace('MyRetrace');
// blah blah blah
}
void MyRetrace() {
if(gameOver) {
// display high score screen
if(b1) {
gameOver = 0;
paddlesVisible = 0;
ballVisible = 0;
mainMenu = 1;
}
} else if(mainMenuVisible) {
// display main menu
if(b1) {
paddlesVisible = 1;
mainMenuVisible = 0;
ballVisible = 0;
}
} else if(paddlesVisible) {
// display paddles
if(b1) {
ballVisible = 1;
}
}
if(ballVisible) {
// display ball, handle ball movement
if(GameOver()) {
ballVisible = 0;
paddlesVisible = 0;
gameOver = 1;
}
}
}
And what about a demo mode that plays if the main menu has been visible too long? And a score display at the top of the screen while the game is running? Things could start to get messy very quickly...the more possibilities you add, the more tangled the logic in your retrace function will get. You could get around some of that by writing separate retraces for each stage, but what if you wanted the player to have the option of displaying the frames-per-second in the corner of the screen at all times? (You just can't play Pong without 70+FPS, you know.) You'd have to duplicate the FPS display code in ever HookRetrace function...definitely not cool. Here's how you might do it with the HookRetrace priority queue library:
int paused;
void autoexec() {
int wantFPS;
HookSetup();
AddHook('MainMenu', 10);
// Read the value of wantFPS from a config file or something
if(wantFPS)
AddHook('ShowFPS', 100); // on top of everything
Map('pong.map');
}
void ShowFPS() {
// display the frames per second
}
void MainMenu() {
// display main menu
if(b1) {
RemoveHook('MainMenu');
paused = 1;
AddHook('DrawPaddles', 1);
AddHook('DrawScore', 10);
}
}
void DrawPaddles() {
if(paused) {
if(b1) {
paused = 0;
AddHook('MoveBall', 2);
}
} else {
// handle paddle movement
}
}
void DrawScore() {
// print the score at the top of the screen
}
void MoveBall() {
// Handle ball movement
if(BallOutOfBounds()) {
// determine which player scored, etc
paused = 1;
RemoveHook('MoveBall');
if(GameOver()) {
RemoveHook('ShowPaddles');
RemoveHook('ShowScore');
AddHook('ShowHighScores', 10);
}
}
}
void ShowHighScores() {
// Show high scores
if(b1) {
RemoveHook('ShowHighScores');
AddHook('MainMenu', 10);
}
}
Now your display logic is divided more or less neatly into manageable chunks. Plus you have the option to overlay the FPS counter, no matter what else is going on at the time. Imagine having that with faders, wavy drunk filters that are applied when you visit a tavern, dialogue boxes, status icons for poisoning/etc. Just AddHook() when some special display starts, and RemoveHook() when it needs to stop.
Also available is the RemoveHookPriority(), which removes anything with the given priority level. The first use that comes to my mind is for paper doll-style clothing. You could decide that all clothing will be drawn at priority level 5, for example. Then when you want to remove everyone's clothing (ooh la la!), you'd do like this:
RemoveHookPriority(5);
and everyone would be nekkid, no matter what function was drawing their clothes.
Okay, that was a bit long-winded, but I was annoyed by my inability to properly explain the use of my code in the space available. Hope some of you get some use out of it. Feedback and bug reports are welcome, of course.