Arrange that random seeds are as harmonised as they can reasonably
[sgt/puzzles] / midend.c
index 6ae10f9..cde2729 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,24 @@ 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_force_redraw(midend_data *me)
+{
+    if (me->drawstate)
+        me->ourgame->free_drawstate(me->drawstate);
+    me->drawstate = me->ourgame->new_drawstate(me->states[0].state);
+    midend_redraw(me);
+}
+
 void midend_new_game(midend_data *me)
 {
     while (me->nstates > 0)
@@ -160,16 +186,20 @@ void midend_new_game(midend_data *me)
        me->aux_info = NULL;
 
         rs = random_init(me->seedstr, strlen(me->seedstr));
-        me->desc = me->ourgame->new_desc(me->curparams, rs, &me->aux_info);
+        me->desc = me->ourgame->new_desc(me->curparams, rs,
+                                        &me->aux_info, TRUE);
         random_free(rs);
     }
 
     ensure(me);
-    me->states[me->nstates].state = me->ourgame->new_game(me->params, me->desc);
+    me->states[me->nstates].state =
+       me->ourgame->new_game(me, me->params, me->desc);
     me->states[me->nstates].special = TRUE;
     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);
@@ -226,10 +256,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)
@@ -265,7 +292,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)
@@ -288,7 +315,7 @@ static int midend_really_process_key(midend_data *me, int x, int y, int button)
        if (!midend_undo(me))
             return 1;
     } else if (button == 'r' || button == 'R' ||
-               button == '\x12') {
+               button == '\x12' || button == '\x19') {
        midend_stop_anim(me);
        if (!midend_redo(me))
             return 1;
@@ -298,7 +325,7 @@ static int midend_really_process_key(midend_data *me, int x, int y, int button)
     } else {
         game_state *s =
             me->ourgame->make_move(me->states[me->statepos-1].state,
-                                   me->ui, x, y, button);
+                                   me->ui, me->drawstate, x, y, button);
 
         if (s == me->states[me->statepos-1].state) {
             /*
@@ -348,7 +375,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;
 }
@@ -414,6 +441,14 @@ int midend_process_key(midend_data *me, int x, int y, int button)
      *    pressed, invent a button-up for the first one and then
      *    pass the button-down through as before.
      * 
+     * 2005-05-31: An addendum to the above. Some games might want
+     * a `priority order' among buttons, such that if one button is
+     * pressed while another is down then a fixed one of the
+     * buttons takes priority no matter what order they're pressed
+     * in. Mines, in particular, wants to treat a left+right click
+     * like a left click for the benefit of users of other
+     * implementations. So the last of the above points is modified
+     * in the presence of an (optional) button priority order.
      */
     if (IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) {
         if (me->pressed_mouse_button) {
@@ -427,6 +462,14 @@ int midend_process_key(midend_data *me, int x, int y, int button)
         } else
             return ret;                /* ignore it */
     } else if (IS_MOUSE_DOWN(button) && me->pressed_mouse_button) {
+       /*
+        * If the new button has lower priority than the old one,
+        * don't bother doing this.
+        */
+       if (me->ourgame->mouse_priorities &
+           BUTTON_BEATS(me->pressed_mouse_button, button))
+           return ret;                /* just ignore it */
+
         /*
          * Fabricate a button-up for the previously pressed button.
          */
@@ -478,13 +521,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)
@@ -494,8 +546,9 @@ float *midend_colours(midend_data *me, int *ncolours)
 
     if (me->nstates == 0) {
        game_aux_info *aux = NULL;
-        char *desc = me->ourgame->new_desc(me->params, me->random, &aux);
-        state = me->ourgame->new_game(me->params, desc);
+        char *desc = me->ourgame->new_desc(me->params, me->random,
+                                          &aux, TRUE);
+        state = me->ourgame->new_game(me, me->params, desc);
         sfree(desc);
        if (aux)
            me->ourgame->free_aux_info(aux);
@@ -626,6 +679,12 @@ int midend_wants_statusbar(midend_data *me)
     return me->ourgame->wants_statusbar();
 }
 
+void midend_supersede_game_desc(midend_data *me, char *desc)
+{
+    sfree(me->desc);
+    me->desc = dupstr(desc);
+}
+
 config_item *midend_get_config(midend_data *me, int which, char **wintitle)
 {
     char *titlebuf, *parstr;
@@ -859,6 +918,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);
+    }
+}