First cut at a game timer. Yet another backend function which
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Mon, 30 May 2005 16:15:34 +0000 (16:15 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Mon, 30 May 2005 16:15:34 +0000 (16:15 +0000)
indicates whether a particular game state should have the timer
going (for Mines the initial indeterminate state does not have this
property, and neither does a dead or won state); a midend function
that optionally (on request from the game) prepends a timer to the
front of the status bar text; some complicated midend timing code.

It's not great. It's ugly; it's probably slightly inaccurate; it's
got no provision for anyone but the game author decreeing whether a
game is timed or not. But Mines can't be taken seriously without a
timer, so it's a start.

git-svn-id: svn://svn.tartarus.org/sgt/puzzles@5866 cda61777-01e9-0310-a592-d414129be87e

16 files changed:
cube.c
fifteen.c
gtk.c
midend.c
mines.c
net.c
netslide.c
nullgame.c
osx.m
pattern.c
puzzles.h
rect.c
sixteen.c
solo.c
twiddle.c
windows.c

diff --git a/cube.c b/cube.c
index 428569c..a65b918 100644 (file)
--- a/cube.c
+++ b/cube.c
@@ -1542,6 +1542,11 @@ static int game_wants_statusbar(void)
     return TRUE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return TRUE;
+}
+
 #ifdef COMBINED
 #define thegame cube
 #endif
@@ -1575,4 +1580,5 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
 };
index 310e70a..bbdb939 100644 (file)
--- a/fifteen.c
+++ b/fifteen.c
@@ -802,6 +802,11 @@ static int game_wants_statusbar(void)
     return TRUE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return TRUE;
+}
+
 #ifdef COMBINED
 #define thegame fifteen
 #endif
@@ -835,4 +840,5 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
 };
diff --git a/gtk.c b/gtk.c
index 3fdfc87..84e5e72 100644 (file)
--- a/gtk.c
+++ b/gtk.c
@@ -98,10 +98,14 @@ void frontend_default_colour(frontend *fe, float *output)
 
 void status_bar(frontend *fe, char *text)
 {
+    char *rewritten;
+
     assert(fe->statusbar);
 
+    rewritten = midend_rewrite_statusbar(fe->me, text);
     gtk_statusbar_pop(GTK_STATUSBAR(fe->statusbar), fe->statusctx);
-    gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, text);
+    gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, rewritten);
+    sfree(rewritten);
 }
 
 void start_draw(frontend *fe)
index e31ce26..8adc1c3 100644 (file)
--- a/midend.c
+++ b/midend.c
@@ -43,6 +43,10 @@ struct midend_data {
     float flash_time, flash_pos;
     int dir;
 
+    int timing;
+    float elapsed;
+    char *laststatus;
+
     int pressed_mouse_button;
 };
 
@@ -83,6 +87,9 @@ midend_data *midend_new(frontend *fe, const game *ourgame)
     me->dir = 0;
     me->ui = NULL;
     me->pressed_mouse_button = 0;
+    me->laststatus = NULL;
+    me->timing = FALSE;
+    me->elapsed = 0.0F;
 
     sfree(randseed);
 
@@ -100,6 +107,7 @@ void midend_free(midend_data *me)
     me->ourgame->free_params(me->params);
     if (me->curparams)
         me->ourgame->free_params(me->curparams);
+    sfree(me->laststatus);
     sfree(me);
 }
 
@@ -114,6 +122,16 @@ void midend_set_params(midend_data *me, game_params *params)
     me->params = me->ourgame->dup_params(params);
 }
 
+static void midend_set_timer(midend_data *me)
+{
+    me->timing = (me->ourgame->is_timed &&
+                 me->ourgame->timing_state(me->states[me->statepos-1].state));
+    if (me->timing || me->flash_time || me->anim_time)
+       activate_timer(me->frontend);
+    else
+       deactivate_timer(me->frontend);
+}
+
 void midend_new_game(midend_data *me)
 {
     while (me->nstates > 0)
@@ -171,6 +189,8 @@ void midend_new_game(midend_data *me)
     me->nstates++;
     me->statepos = 1;
     me->drawstate = me->ourgame->new_drawstate(me->states[0].state);
+    me->elapsed = 0.0F;
+    midend_set_timer(me);
     if (me->ui)
         me->ourgame->free_ui(me->ui);
     me->ui = me->ourgame->new_ui(me->states[0].state);
@@ -227,10 +247,7 @@ static void midend_finish_move(midend_data *me)
     me->anim_pos = me->anim_time = 0;
     me->dir = 0;
 
-    if (me->flash_time == 0 && me->anim_time == 0)
-       deactivate_timer(me->frontend);
-    else
-       activate_timer(me->frontend);
+    midend_set_timer(me);
 }
 
 static void midend_stop_anim(midend_data *me)
@@ -266,7 +283,7 @@ void midend_restart_game(midend_data *me)
     me->anim_time = 0.0;
     midend_finish_move(me);
     midend_redraw(me);
-    activate_timer(me->frontend);
+    midend_set_timer(me);
 }
 
 static int midend_really_process_key(midend_data *me, int x, int y, int button)
@@ -349,7 +366,7 @@ static int midend_really_process_key(midend_data *me, int x, int y, int button)
 
     midend_redraw(me);
 
-    activate_timer(me->frontend);
+    midend_set_timer(me);
 
     return 1;
 }
@@ -479,13 +496,22 @@ void midend_timer(midend_data *me, float tplus)
        if (me->anim_time > 0)
            midend_finish_move(me);
     }
+
     me->flash_pos += tplus;
     if (me->flash_pos >= me->flash_time || me->flash_time == 0) {
        me->flash_pos = me->flash_time = 0;
     }
-    if (me->flash_time == 0 && me->anim_time == 0)
-       deactivate_timer(me->frontend);
+
     midend_redraw(me);
+
+    if (me->timing) {
+       float oldelapsed = me->elapsed;
+       me->elapsed += tplus;
+       if ((int)oldelapsed != (int)me->elapsed)
+           status_bar(me->frontend, me->laststatus ? me->laststatus : "");
+    }
+
+    midend_set_timer(me);
 }
 
 float *midend_colours(midend_data *me, int *ncolours)
@@ -866,6 +892,36 @@ char *midend_solve(midend_data *me)
     me->anim_time = 0.0;
     midend_finish_move(me);
     midend_redraw(me);
-    activate_timer(me->frontend);
+    midend_set_timer(me);
     return NULL;
 }
+
+char *midend_rewrite_statusbar(midend_data *me, char *text)
+{
+    /*
+     * An important special case is that we are occasionally called
+     * with our own laststatus, to update the timer.
+     */
+    if (me->laststatus != text) {
+       sfree(me->laststatus);
+       me->laststatus = dupstr(text);
+    }
+
+    if (me->ourgame->is_timed) {
+       char timebuf[100], *ret;
+       int min, sec;
+
+       sec = me->elapsed;
+       min = sec / 60;
+       sec %= 60;
+       sprintf(timebuf, "[%d:%02d] ", min, sec);
+
+       ret = snewn(strlen(timebuf) + strlen(text) + 1, char);
+       strcpy(ret, timebuf);
+       strcat(ret, text);
+       return ret;
+
+    } else {
+       return dupstr(text);
+    }
+}
diff --git a/mines.c b/mines.c
index b2bc82c..881cad0 100644 (file)
--- a/mines.c
+++ b/mines.c
@@ -10,8 +10,6 @@
  *       That hook can talk to the game_ui and set the cheated flag,
  *       and then make_move can avoid setting the `won' flag after that.
  *
- *  - timer
- * 
  *  - question marks (arrgh, preferences?)
  * 
  *  - sensible parameter constraints
@@ -2700,6 +2698,13 @@ static int game_wants_statusbar(void)
     return TRUE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    if (state->dead || state->won || !state->layout->mines)
+       return FALSE;
+    return TRUE;
+}
+
 #ifdef COMBINED
 #define thegame mines
 #endif
@@ -2733,4 +2738,5 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    TRUE, game_timing_state,
 };
diff --git a/net.c b/net.c
index 8793886..fa4c979 100644 (file)
--- a/net.c
+++ b/net.c
@@ -2568,6 +2568,11 @@ static int game_wants_statusbar(void)
     return TRUE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return TRUE;
+}
+
 #ifdef COMBINED
 #define thegame net
 #endif
@@ -2601,4 +2606,5 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
 };
index 5209842..bf5fc75 100644 (file)
@@ -1725,6 +1725,11 @@ static int game_wants_statusbar(void)
     return TRUE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return FALSE;
+}
+
 #ifdef COMBINED
 #define thegame netslide
 #endif
@@ -1758,4 +1763,5 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
 };
index 7e27c4a..abb6cc5 100644 (file)
@@ -215,6 +215,11 @@ static int game_wants_statusbar(void)
     return FALSE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return TRUE;
+}
+
 #ifdef COMBINED
 #define thegame nullgame
 #endif
@@ -248,4 +253,5 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
 };
diff --git a/osx.m b/osx.m
index ed33bf6..039f081 100644 (file)
--- a/osx.m
+++ b/osx.m
@@ -383,7 +383,7 @@ struct frontend {
 - (void)keyDown:(NSEvent *)ev;
 - (void)activateTimer;
 - (void)deactivateTimer;
-- (void)setStatusLine:(NSString *)text;
+- (void)setStatusLine:(char *)text;
 @end
 
 @implementation MyImageView
@@ -1131,9 +1131,11 @@ struct frontend {
     [self sheetEndWithStatus:NO];
 }
 
-- (void)setStatusLine:(NSString *)text
+- (void)setStatusLine:(char *)text
 {
-    [[status cell] setTitle:text];
+    char *rewritten = midend_rewrite_statusbar(me, text);
+    [[status cell] setTitle:[NSString stringWithCString:rewritten]];
+    sfree(rewritten);
 }
 
 @end
@@ -1267,7 +1269,7 @@ void activate_timer(frontend *fe)
 
 void status_bar(frontend *fe, char *text)
 {
-    [fe->window setStatusLine:[NSString stringWithCString:text]];
+    [fe->window setStatusLine:text];
 }
 
 /* ----------------------------------------------------------------------
index df67c83..c5fd9dc 100644 (file)
--- a/pattern.c
+++ b/pattern.c
@@ -1105,6 +1105,11 @@ static int game_wants_statusbar(void)
     return FALSE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return TRUE;
+}
+
 #ifdef COMBINED
 #define thegame pattern
 #endif
@@ -1138,6 +1143,7 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
 };
 
 #ifdef STANDALONE_SOLVER
index 9eb991a..f3db36f 100644 (file)
--- a/puzzles.h
+++ b/puzzles.h
@@ -145,6 +145,7 @@ char *midend_game_id(midend_data *me, char *id);
 char *midend_text_format(midend_data *me);
 char *midend_solve(midend_data *me);
 void midend_supersede_game_desc(midend_data *me, char *desc);
+char *midend_rewrite_statusbar(midend_data *me, char *text);
 
 /*
  * malloc.c
@@ -239,6 +240,8 @@ struct game {
     float (*flash_length)(game_state *oldstate, game_state *newstate, int dir,
                          game_ui *ui);
     int (*wants_statusbar)(void);
+    int is_timed;
+    int (*timing_state)(game_state *state);
 };
 
 /*
diff --git a/rect.c b/rect.c
index b0e0f9c..0ea0b8c 100644 (file)
--- a/rect.c
+++ b/rect.c
@@ -2515,6 +2515,11 @@ static int game_wants_statusbar(void)
     return FALSE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return TRUE;
+}
+
 #ifdef COMBINED
 #define thegame rect
 #endif
@@ -2548,4 +2553,5 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
 };
index 8f43b7a..1871e5f 100644 (file)
--- a/sixteen.c
+++ b/sixteen.c
@@ -961,6 +961,11 @@ static int game_wants_statusbar(void)
     return TRUE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return TRUE;
+}
+
 #ifdef COMBINED
 #define thegame sixteen
 #endif
@@ -994,4 +999,5 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
 };
diff --git a/solo.c b/solo.c
index 9e350f6..addae60 100644 (file)
--- a/solo.c
+++ b/solo.c
@@ -2140,6 +2140,11 @@ static int game_wants_statusbar(void)
     return FALSE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return TRUE;
+}
+
 #ifdef COMBINED
 #define thegame solo
 #endif
@@ -2173,6 +2178,7 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
 };
 
 #ifdef STANDALONE_SOLVER
index 6434c64..2523920 100644 (file)
--- a/twiddle.c
+++ b/twiddle.c
@@ -1111,6 +1111,11 @@ static int game_wants_statusbar(void)
     return TRUE;
 }
 
+static int game_timing_state(game_state *state)
+{
+    return TRUE;
+}
+
 #ifdef COMBINED
 #define thegame twiddle
 #endif
@@ -1144,4 +1149,5 @@ const struct game thegame = {
     game_anim_length,
     game_flash_length,
     game_wants_statusbar,
+    FALSE, game_timing_state,
 };
index 383b742..0a61f3c 100644 (file)
--- a/windows.c
+++ b/windows.c
@@ -118,6 +118,7 @@ struct frontend {
     HFONT cfgfont;
     char *help_path;
     int help_has_contents;
+    char *laststatus;
 };
 
 void fatal(char *fmt, ...)
@@ -144,7 +145,14 @@ void get_random_seed(void **randseed, int *randseedsize)
 
 void status_bar(frontend *fe, char *text)
 {
-    SetWindowText(fe->statusbar, text);
+    char *rewritten = midend_rewrite_statusbar(fe->me, text);
+    if (!fe->laststatus || strcmp(rewritten, fe->laststatus)) {
+       SetWindowText(fe->statusbar, rewritten);
+       sfree(fe->laststatus);
+       fe->laststatus = rewritten;
+    } else {
+       sfree(rewritten);
+    }
 }
 
 void frontend_default_colour(frontend *fe, float *output)
@@ -437,6 +445,8 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error)
     fe->fonts = NULL;
     fe->nfonts = fe->fontsize = 0;
 
+    fe->laststatus = NULL;
+
     {
        int i, ncolours;
         float *colours;