Stop the analysis pass in Loopy's redraw routine from being
[sgt/puzzles] / towers.c
index 56c69ba..bd39554 100644 (file)
--- a/towers.c
+++ b/towers.c
@@ -1148,6 +1148,10 @@ static void game_changed_state(game_ui *ui, game_state *oldstate,
 #define COORD(x) ((x)*TILESIZE + BORDER)
 #define FROMCOORD(x) (((x)+(TILESIZE-BORDER)) / TILESIZE - 1)
 
+/* These always return positive values, though y offsets are actually -ve */
+#define X_3D_DISP(height, w) ((height) * TILESIZE / (8 * (w)))
+#define Y_3D_DISP(height, w) ((height) * TILESIZE / (4 * (w)))
+
 #define FLASH_TIME 0.4F
 
 #define DF_PENCIL_SHIFT 16
@@ -1160,8 +1164,10 @@ static void game_changed_state(game_ui *ui, game_state *oldstate,
 
 struct game_drawstate {
     int tilesize;
+    int three_d;               /* default 3D graphics are user-disableable */
     int started;
-    long *tiles;
+    long *tiles;                      /* (w+2)*(w+2) temp space */
+    long *drawn;                      /* (w+2)*(w+2)*4: current drawn data */
     int *errtmp;
 };
 
@@ -1219,14 +1225,13 @@ static int check_errors(game_state *state, int *errors)
     }
 
     for (i = 0; i < 4*w; i++) {
-       int start, step, j, k, n, best;
+       int start, step, j, n, best;
        STARTSTEP(start, step, i, w);
 
        if (!clues[i])
            continue;
 
        best = n = 0;
-       k = 0;
        for (j = 0; j < w; j++) {
            int number = grid[start+j*step];
            if (!number)
@@ -1250,7 +1255,7 @@ static int check_errors(game_state *state, int *errors)
     return errs;
 }
 
-static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
+static char *interpret_move(game_state *state, game_ui *ui, const game_drawstate *ds,
                            int x, int y, int button)
 {
     int w = state->par.w;
@@ -1262,6 +1267,41 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
     tx = FROMCOORD(x);
     ty = FROMCOORD(y);
 
+    if (ds->three_d) {
+       /*
+        * In 3D mode, just locating the mouse click in the natural
+        * square grid may not be sufficient to tell which tower the
+        * user clicked on. Investigate the _tops_ of the nearby
+        * towers to see if a click on one grid square was actually
+        * a click on a tower protruding into that region from
+        * another.
+        */
+       int dx, dy;
+       for (dy = 0; dy <= 1; dy++)
+           for (dx = 0; dx >= -1; dx--) {
+               int cx = tx + dx, cy = ty + dy;
+               if (cx >= 0 && cx < w && cy >= 0 && cy < w) {
+                   int height = state->grid[cy*w+cx];
+                   int bx = COORD(cx), by = COORD(cy);
+                   int ox = bx + X_3D_DISP(height, w);
+                   int oy = by - Y_3D_DISP(height, w);
+                   if (/* on top face? */
+                       (x - ox >= 0 && x - ox < TILESIZE &&
+                        y - oy >= 0 && y - oy < TILESIZE) ||
+                       /* in triangle between top-left corners? */
+                       (ox > bx && x >= bx && x <= ox && y <= by &&
+                        (by-y) * (ox-bx) <= (by-oy) * (x-bx)) ||
+                       /* in triangle between bottom-right corners? */
+                       (ox > bx && x >= bx+TILESIZE && x <= ox+TILESIZE &&
+                        y >= oy+TILESIZE &&
+                        (by-y+TILESIZE)*(ox-bx) >= (by-oy)*(x-bx-TILESIZE))) {
+                       tx = cx;
+                       ty = cy;
+                   }
+               }
+           }
+    }
+
     if (tx >= 0 && tx < w && ty >= 0 && ty < w) {
         if (button == LEFT_BUTTON) {
            if (tx == ui->hx && ty == ui->hy &&
@@ -1454,10 +1494,6 @@ static float *game_colours(frontend *fe, int *ncolours)
     return ret;
 }
 
-static const char *const minus_signs[] = { "\xE2\x88\x92", "-" };
-static const char *const times_signs[] = { "\xC3\x97", "*" };
-static const char *const divide_signs[] = { "\xC3\xB7", "/" };
-
 static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
 {
     int w = state->par.w /*, a = w*w */;
@@ -1465,10 +1501,12 @@ static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
     int i;
 
     ds->tilesize = 0;
+    ds->three_d = !getenv("TOWERS_2D");
     ds->started = FALSE;
     ds->tiles = snewn((w+2)*(w+2), long);
-    for (i = 0; i < (w+2)*(w+2); i++)
-       ds->tiles[i] = -1;
+    ds->drawn = snewn((w+2)*(w+2)*4, long);
+    for (i = 0; i < (w+2)*(w+2)*4; i++)
+       ds->drawn[i] = -1;
     ds->errtmp = snewn((w+2)*(w+2), int);
 
     return ds;
@@ -1478,6 +1516,7 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
 {
     sfree(ds->errtmp);
     sfree(ds->tiles);
+    sfree(ds->drawn);
     sfree(ds);
 }
 
@@ -1485,36 +1524,76 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
                      int x, int y, long tile)
 {
     int w = clues->w /* , a = w*w */;
-    int tx, ty, tw, th;
-    int cx, cy, cw, ch;
+    int tx, ty, bg;
     char str[64];
 
-    tx = BORDER + x * TILESIZE + 1;
-    ty = BORDER + y * TILESIZE + 1;
-
-    cx = tx;
-    cy = ty;
-    cw = tw = TILESIZE-1;
-    ch = th = TILESIZE-1;
-
-    clip(dr, cx, cy, cw, ch);
+    tx = COORD(x);
+    ty = COORD(y);
+
+    bg = (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND;
+
+    /* draw tower */
+    if (ds->three_d && (tile & DF_PLAYAREA) && (tile & DF_DIGIT_MASK)) {
+       int coords[8];
+       int xoff = X_3D_DISP(tile & DF_DIGIT_MASK, w);
+       int yoff = Y_3D_DISP(tile & DF_DIGIT_MASK, w);
+
+       /* left face of tower */
+       coords[0] = tx;
+       coords[1] = ty - 1;
+       coords[2] = tx;
+       coords[3] = ty + TILESIZE - 1;
+       coords[4] = coords[2] + xoff;
+       coords[5] = coords[3] - yoff;
+       coords[6] = coords[0] + xoff;
+       coords[7] = coords[1] - yoff;
+       draw_polygon(dr, coords, 4, bg, COL_GRID);
+
+       /* bottom face of tower */
+       coords[0] = tx + TILESIZE;
+       coords[1] = ty + TILESIZE - 1;
+       coords[2] = tx;
+       coords[3] = ty + TILESIZE - 1;
+       coords[4] = coords[2] + xoff;
+       coords[5] = coords[3] - yoff;
+       coords[6] = coords[0] + xoff;
+       coords[7] = coords[1] - yoff;
+       draw_polygon(dr, coords, 4, bg, COL_GRID);
+
+       /* now offset all subsequent drawing to the top of the tower */
+       tx += xoff;
+       ty -= yoff;
+    }
 
-    /* background needs erasing */
-    draw_rect(dr, cx, cy, cw, ch,
-             (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND);
+    /* erase background */
+    draw_rect(dr, tx, ty, TILESIZE, TILESIZE, bg);
 
     /* pencil-mode highlight */
     if (tile & DF_HIGHLIGHT_PENCIL) {
         int coords[6];
-        coords[0] = cx;
-        coords[1] = cy;
-        coords[2] = cx+cw/2;
-        coords[3] = cy;
-        coords[4] = cx;
-        coords[5] = cy+ch/2;
+        coords[0] = tx;
+        coords[1] = ty;
+        coords[2] = tx+TILESIZE/2;
+        coords[3] = ty;
+        coords[4] = tx;
+        coords[5] = ty+TILESIZE/2;
         draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
     }
 
+    /* draw box outline */
+    if (tile & DF_PLAYAREA) {
+        int coords[8];
+        coords[0] = tx;
+        coords[1] = ty - 1;
+        coords[2] = tx + TILESIZE;
+        coords[3] = ty - 1;
+        coords[4] = tx + TILESIZE;
+        coords[5] = ty + TILESIZE - 1;
+        coords[6] = tx;
+        coords[7] = ty + TILESIZE - 1;
+        draw_polygon(dr, coords, 4, -1, COL_GRID);
+    }
+
     /* new number needs drawing? */
     if (tile & DF_DIGIT_MASK) {
        str[1] = '\0';
@@ -1543,11 +1622,11 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
             * Determine the bounding rectangle within which we're going
             * to put the pencil marks.
             */
-           /* Start with the whole square */
-           pl = tx;
-           pr = pl + TILESIZE;
+           /* Start with the whole square, minus space for impinging towers */
+           pl = tx + (ds->three_d ? X_3D_DISP(w,w) : 0);
+           pr = tx + TILESIZE;
            pt = ty;
-           pb = pt + TILESIZE;
+           pb = ty + TILESIZE - (ds->three_d ? Y_3D_DISP(w,w) : 0);
 
            /*
             * We arrange our pencil marks in a grid layout, with
@@ -1588,8 +1667,8 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
            /*
             * Centre the resulting figure in the square.
             */
-           pl = tx + (TILESIZE - fontsize * pw) / 2;
-           pt = ty + (TILESIZE - fontsize * ph) / 2;
+           pl = pl + (pr - pl - fontsize * pw) / 2;
+           pt = pt + (pb - pt - fontsize * ph) / 2;
 
            /*
             * Now actually draw the pencil marks.
@@ -1608,10 +1687,6 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
                }
        }
     }
-
-    unclip(dr);
-
-    draw_update(dr, cx, cy, cw, ch);
 }
 
 static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
@@ -1630,13 +1705,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
         */
        draw_rect(dr, 0, 0, SIZE(w), SIZE(w), COL_BACKGROUND);
 
-       /*
-        * Big containing rectangle.
-        */
-       draw_rect(dr, COORD(0), COORD(0),
-                 w*TILESIZE+1, w*TILESIZE+1,
-                 COL_GRID);
-
        draw_update(dr, 0, 0, SIZE(w), SIZE(w));
 
        ds->started = TRUE;
@@ -1645,28 +1713,22 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
     check_errors(state, ds->errtmp);
 
     /*
-     * Draw the clues.
+     * Work out what data each tile should contain.
      */
+    for (i = 0; i < (w+2)*(w+2); i++)
+       ds->tiles[i] = 0;              /* completely blank square */
+    /* The clue squares... */
     for (i = 0; i < 4*w; i++) {
        long tile = state->clues->clues[i];
 
-       if (!tile)
-           continue;
-
        CLUEPOS(x, y, i, w);
 
        if (ds->errtmp[(y+1)*(w+2)+(x+1)])
            tile |= DF_ERROR;
 
-       if (ds->tiles[(y+1)*(w+2)+(x+1)] != tile) {
-           ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
-           draw_tile(dr, ds, state->clues, x, y, tile);
-       }
+       ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
     }
-
-    /*
-     * Draw the main grid.
-     */
+    /* ... and the main grid. */
     for (y = 0; y < w; y++) {
        for (x = 0; x < w; x++) {
            long tile = DF_PLAYAREA;
@@ -1690,9 +1752,42 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
            if (ds->errtmp[(y+1)*(w+2)+(x+1)])
                tile |= DF_ERROR;
 
-           if (ds->tiles[(y+1)*(w+2)+(x+1)] != tile) {
-               ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
-               draw_tile(dr, ds, state->clues, x, y, tile);
+           ds->tiles[(y+1)*(w+2)+(x+1)] = tile;
+       }
+    }
+
+    /*
+     * Now actually draw anything that needs to be changed.
+     */
+    for (y = 0; y < w+2; y++) {
+       for (x = 0; x < w+2; x++) {
+           long tl, tr, bl, br;
+           int i = y*(w+2)+x;
+
+           tr = ds->tiles[y*(w+2)+x];
+           tl = (x == 0 ? 0 : ds->tiles[y*(w+2)+(x-1)]);
+           br = (y == w+1 ? 0 : ds->tiles[(y+1)*(w+2)+x]);
+           bl = (x == 0 || y == w+1 ? 0 : ds->tiles[(y+1)*(w+2)+(x-1)]);
+
+           if (ds->drawn[i*4] != tl || ds->drawn[i*4+1] != tr ||
+               ds->drawn[i*4+2] != bl || ds->drawn[i*4+3] != br) {
+               clip(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
+
+               draw_tile(dr, ds, state->clues, x-1, y-1, tr);
+               if (x > 0)
+                   draw_tile(dr, ds, state->clues, x-2, y-1, tl);
+               if (y <= w)
+                   draw_tile(dr, ds, state->clues, x-1, y, br);
+               if (x > 0 && y <= w)
+                   draw_tile(dr, ds, state->clues, x-2, y, bl);
+
+               unclip(dr);
+               draw_update(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
+
+               ds->drawn[i*4] = tl;
+               ds->drawn[i*4+1] = tr;
+               ds->drawn[i*4+2] = bl;
+               ds->drawn[i*4+3] = br;
            }
        }
     }
@@ -1713,6 +1808,11 @@ static float game_flash_length(game_state *oldstate, game_state *newstate,
     return 0.0F;
 }
 
+static int game_status(game_state *state)
+{
+    return state->completed ? +1 : 0;
+}
+
 static int game_timing_state(game_state *state, game_ui *ui)
 {
     if (state->completed)
@@ -1832,6 +1932,7 @@ const struct game thegame = {
     game_redraw,
     game_anim_length,
     game_flash_length,
+    game_status,
     TRUE, FALSE, game_print_size, game_print,
     FALSE,                            /* wants_statusbar */
     FALSE, game_timing_state,