shithub: puzzles

Download patch

ref: 7cd075870ed216c3197cf5322ca88af1140af482
parent: ceb12cb0804d437c4b4acd1540cfed0a621c5bcf
author: Simon Tatham <anakin@pobox.com>
date: Thu Jan 7 13:42:01 EST 2010

Upgrade the graphics in Towers to include a thematically appropriate
3D effect. (I only committed it without so that I could preserve a
record of how the code looked before I added it.) The 3D effect can
be disabled by setting TOWERS_2D in the environment, should anybody
seriously dislike it.

[originally from svn r8817]

--- a/towers.c
+++ b/towers.c
@@ -1160,8 +1160,10 @@
 
 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;
 };
 
@@ -1465,10 +1467,12 @@
     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 +1482,7 @@
 {
     sfree(ds->errtmp);
     sfree(ds->tiles);
+    sfree(ds->drawn);
     sfree(ds);
 }
 
@@ -1485,36 +1490,75 @@
 		      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;
     char str[64];
 
-    tx = BORDER + x * TILESIZE + 1;
-    ty = BORDER + y * TILESIZE + 1;
+    tx = COORD(x);
+    ty = COORD(y);
 
-    cx = tx;
-    cy = ty;
-    cw = tw = TILESIZE-1;
-    ch = th = TILESIZE-1;
+    /* draw tower */
+    if (ds->three_d && (tile & DF_PLAYAREA) && (tile & DF_DIGIT_MASK)) {
+	int coords[8];
+	int xoff = (tile & DF_DIGIT_MASK) * TILESIZE / (8 * w);
+	int yoff = (tile & DF_DIGIT_MASK) * TILESIZE / (4 * w);
 
-    clip(dr, cx, cy, cw, ch);
+	/* 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, COL_BACKGROUND, COL_GRID);
 
-    /* background needs erasing */
-    draw_rect(dr, cx, cy, cw, ch,
+	/* 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, COL_BACKGROUND, COL_GRID);
+
+	/* now offset all subsequent drawing to the top of the tower */
+	tx += xoff;
+	ty -= yoff;
+    }
+
+    /* erase background */
+    draw_rect(dr, tx, ty, TILESIZE, TILESIZE,
 	      (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND);
 
     /* 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 +1587,11 @@
 	     * 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 + TILESIZE/8;
+	    pr = tx + TILESIZE;
 	    pt = ty;
-	    pb = pt + TILESIZE;
+	    pb = ty + TILESIZE - TILESIZE/4;
 
 	    /*
 	     * We arrange our pencil marks in a grid layout, with
@@ -1588,8 +1632,8 @@
 	    /*
 	     * 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 +1652,6 @@
 		}
 	}
     }
-
-    unclip(dr);
-
-    draw_update(dr, cx, cy, cw, ch);
 }
 
 static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
@@ -1630,13 +1670,6 @@
 	 */
 	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 +1678,22 @@
     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 +1717,42 @@
 	    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;
 	    }
 	}
     }