X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/ee67eb171acde7124b49eddb6515834c6a0f60b9..43ae41784d3ef4fe3ada6e9c2f24245b83b67691:/towers.c diff --git a/towers.c b/towers.c index 56c69ba..be0a730 100644 --- 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) @@ -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,