PDA

View Full Version : Timer



pkmnfrk
05-06-2009, 02:05 AM
const int TIMER_SMALLFONT = 1b;
const int TIMER_SHOWFRAMES = 10b;
const int TIMER_DOSOUND = 100b;
const int TIMER_SMALLCOLON = 1000b;

const int TIMER_DEFAULT_FONT = 304;
const int TIMER_DEFAULT_CSET = 0;

const int TIMER_SOUND_MINUTE = 18;
const int TIMER_SOUND_SECOND = 6;


bool timer_active;
int timer_time;
int timer_flags;
int timer_font;
int timer_cset;

int Timer_makeTime(int min, int sec, int fr) {
return (min * 60) + sec + (fr /100);
}

void Timer_start(int time, int flags, int font, int cset) {
timer_time = time;
timer_flags = (flags) & 1111b;
timer_font = font;
timer_cset = cset;

if(timer_font == 0) timer_font = TIMER_DEFAULT_FONT;

timer_active = true;
}

int Timer_stop() {
timer_active = false;
int ret = timer_time;
timer_time = 0;
return ret;
}

void Timer_pause() {
timer_active = false;
}

void Timer_resume() {
timer_active = true;
}



//returns true if the timer expires
bool Timer_update() {
timer_time-=.01;
if(timer_time - Floor(timer_time) == .99) timer_time -= .4; // :'(

if(timer_time <= 0) {
timer_active = false;
return true;
}

if((timer_flags & TIMER_DOSOUND) != 0) {
if(timer_time <= 10) { //critical!
if((timer_time % 1) == 0 && Floor(timer_time) == timer_time) { //every second
Game->PlaySound(TIMER_SOUND_SECOND);
}
} else if(timer_time <= 60 && Floor(timer_time) == timer_time) { //last minute
if((timer_time % 10) == 0) { //every ten seconds
Game->PlaySound(TIMER_SOUND_MINUTE);
}
} else {
if((timer_time % 60) == 0 && Floor(timer_time) == timer_time) { //every minute
Game->PlaySound(TIMER_SOUND_MINUTE);
}
}
}

return false;
}

void Timer_draw(int x, int y, int layer) {
int xp;
int t = timer_time;
int hr = Floor(t / 3600);
t -= hr * 3600;
int min = Floor(t / 60);
t -= min * 60;
int sec = Floor(t);
t -= sec;
int fr = t * 100;

int w;
if((timer_flags & TIMER_SMALLFONT) != 0)
w = 8;
else
w = 16;

int cw;
if((timer_flags & TIMER_SMALLCOLON) != 0)
cw = w / 4;
else
cw = w;



xp = x;
//first, draw the hour if applicable
if(hr > 0) {
if(hr >= 10) {
Timer_drawNumber(Floor(hr / 10), xp, y, layer);
xp += w;
Timer_drawNumber(Floor(hr % 10), xp, y, layer);
xp += w;
Timer_drawNumber(10, xp, y, layer); //: is 10
xp += cw;
}
}

//next, draw the minute
if(min >= 10 || hr > 0) {
Timer_drawNumber(Floor(min / 10), xp, y, layer);
xp += w;
}
Timer_drawNumber(Floor(min % 10), xp, y, layer);
xp += w;
Timer_drawNumber(10, xp, y, layer); //: is 10
xp += cw;

//then, the seconds
Timer_drawNumber(Floor(sec / 10), xp, y, layer);
xp += w;
Timer_drawNumber(Floor(sec % 10), xp, y, layer);
xp += w;

if((timer_flags & TIMER_SHOWFRAMES) != 0) {
Timer_drawNumber(10, xp, y, layer); //: is 10
xp += cw;
Timer_drawNumber(Floor(fr / 10), xp, y, layer);
xp += w;
Timer_drawNumber(Floor(fr % 10), xp, y, layer);
xp += w;
}


}

void Timer_drawNumber(int num, int x, int y, int layer) {
Screen->DrawTile(layer, x, y, timer_font + num, 1, 1, timer_cset, 1, 0, 0, 0, 0, true, 128);
}

Alright, today we have a collection of functions that you can use to create an on-screen timer. That is, a count-down until some kind of cataclysmic event that Link must outrun!

I've designed this for maximum flexibility. You provide the font, in the form of a handful of tiles. You can make the font large (full tile, 16x16) or small (quarter tile, 8x8). You can display the timer wherever you want on screen. You can draw the timer independently of updating the timer (i.e., you can hide the timer even when it's running). You can pause the timer, and resume it later. You can make it "ding!" at regular intervals to remind the player that it's there. Et cetera.

So, how do we use it? It's not difficult. First, you need to make your font. You can use multiple fonts, if you want, it doesn't matter. But, you need at least one. Here's a sample you can use if you want:

http://zctut.com/other/timerfont.png

A small font, though only taking up 8x8 must still go into a full tile, due to limitations on drawing tiles. However, it will be drawn as though it were 8x8. So, only draw in the top-left corner of the tiles, and leave the rest blank.

A special note is the colon. Make sure it's pushed all the way to the left, and is no more than 1/4 of the normal font width. That is, for a small font, it should be 2 pixels. For the big font, you can use 4 pixels. The reason is for aesthetics. It looks better if the colon is small, and so there's an option to draw the colon small. (I hope I explained this correctly...)

Anyway. Once you have your font set up, then we can tweak a few constants. To recap, the constants you should play with are:


const int TIMER_DEFAULT_FONT = 304;
const int TIMER_DEFAULT_CSET = 0;

const int TIMER_SOUND_MINUTE = 18;
const int TIMER_SOUND_SECOND = 6;

The two defaults are the default font and cset used. They're given names, so that later on, you can just pass the constant instead of the numbers, and know you've gotten it right.

The two sound constants are for the "dink!" sound the timer can make. TIMER_SOUND_MINUTE is played after each minute expires, or every 10 seconds if there's less than a minute left. TIMER_SOUND_SECOND is played when there's less than 10 seconds left, and it plays once a second. I've set those to some reasonable defaults that should work for most quests, but feel free to change them.

--------

Okay, now that we've got all that out of the way, it's time to actually set up a timer! It's really easy.

First things first, you have to start the timer:


void Timer_start(int time, int flags, int font, int cset);

Timer_start takes four arguments. First is the time before the timer expires (the format of which I'll explain shortly). Next is a collection of flags (which I'll explain in a second). Then there's the font to use (or just pass TIMER_DEFAULT_FONT) and cset (use TIMER_DEFAULT_CSET).

There are a few flags you can pass to control the behaviour of the timer. Those flags are:


TIMER_SMALLFONT - Use if the font is an 8x8 font.
TIMER_SHOWFRAMES - In addition to minutes and seconds, display the remaining frames left as well (to add a sense of urgency. Try it and see!)
TIMER_DOSOUND - Make the "dink" noises at regular intervals.
TIMER_SMALLCOLON - Use if the colon character is no more than 1/4 the width of the rest of the characters.

To pass them, just use the OR operator ( "|" ). Just do something like:


TIMER_SMALLFONT | TIMER_SMALLCOLON

The time is specified in seconds, with a bizzare means of specifying frames. Don't worry about the details, though, as I have provided a helper function:


int Timer_makeTime(int min, int sec, int fr);

This takes the minutes, seconds and frame components of the time, and returns a value you can pass to Timer_start. Real easy.

---

Now that we have the hard part out of the way, we can move to the easy stuff. Each frame, the timer must be updated. Otherwise, it won't count down. It's real easy to do, too. Just call Timer_update each frame:


bool Timer_update();

This function normally returns false. However, if the time expires that frame, then it will return true. Use this to kill Link if he runs out of time:


if(Timer_update()) {
Link->HP = 0;
Waitframe();
}

But, you can do whatever you want.

The other thing is that you probably want to display the timer in some way. For maximum flexibility, you must call the drawing function each frame as well:


void Timer_draw(int x, int y, int layer);

Just tell it where to draw, and it will draw it. You'll probably want to stick this in the same spot you call Timer_update().

Finally, there's a few functions to control how the timer operates:


void Timer_pause();
void Timer_resume();
int Timer_stop();

Timer_pause() and Timer_resume() do exactly what you might suspect: They pause the timer temporarily. Timer_stop() does a little bit more. It blanks the timer outright, and returns the amount of time left. You would use this once Link accomplishes whatever goal he was set out to do.

-----

If you guys have any questions, or need a more concrete example of how to use this, just post, and I'll be glad to help!