summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/net.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/net.c')
-rw-r--r--apps/plugins/puzzles/src/net.c759
1 files changed, 390 insertions, 369 deletions
diff --git a/apps/plugins/puzzles/src/net.c b/apps/plugins/puzzles/src/net.c
index f479f03bb7..0b3b82446d 100644
--- a/apps/plugins/puzzles/src/net.c
+++ b/apps/plugins/puzzles/src/net.c
@@ -27,13 +27,6 @@
27#define USE_DRAGGING 27#define USE_DRAGGING
28#endif 28#endif
29 29
30#define MATMUL(xr,yr,m,x,y) do { \
31 float rx, ry, xx = (x), yy = (y), *mat = (m); \
32 rx = mat[0] * xx + mat[2] * yy; \
33 ry = mat[1] * xx + mat[3] * yy; \
34 (xr) = rx; (yr) = ry; \
35} while (0)
36
37/* Direction and other bitfields */ 30/* Direction and other bitfields */
38#define R 0x01 31#define R 0x01
39#define U 0x02 32#define U 0x02
@@ -65,7 +58,7 @@
65 58
66#define PREFERRED_TILE_SIZE 32 59#define PREFERRED_TILE_SIZE 32
67#define TILE_SIZE (ds->tilesize) 60#define TILE_SIZE (ds->tilesize)
68#define TILE_BORDER 1 61#define LINE_THICK ((TILE_SIZE+47)/48)
69#ifdef SMALL_SCREEN 62#ifdef SMALL_SCREEN
70#define WINDOW_OFFSET 4 63#define WINDOW_OFFSET 4
71#else 64#else
@@ -75,13 +68,6 @@
75#define ROTATE_TIME 0.13F 68#define ROTATE_TIME 0.13F
76#define FLASH_FRAME 0.07F 69#define FLASH_FRAME 0.07F
77 70
78/* Transform physical coords to game coords using game_drawstate ds */
79#define GX(x) (((x) + ds->org_x) % ds->width)
80#define GY(y) (((y) + ds->org_y) % ds->height)
81/* ...and game coords to physical coords */
82#define RX(x) (((x) + ds->width - ds->org_x) % ds->width)
83#define RY(y) (((y) + ds->height - ds->org_y) % ds->height)
84
85enum { 71enum {
86 COL_BACKGROUND, 72 COL_BACKGROUND,
87 COL_LOCKED, 73 COL_LOCKED,
@@ -102,12 +88,17 @@ struct game_params {
102 float barrier_probability; 88 float barrier_probability;
103}; 89};
104 90
91typedef struct game_immutable_state {
92 int refcount;
93 unsigned char *barriers;
94} game_immutable_state;
95
105struct game_state { 96struct game_state {
106 int width, height, wrapping, completed; 97 int width, height, wrapping, completed;
107 int last_rotate_x, last_rotate_y, last_rotate_dir; 98 int last_rotate_x, last_rotate_y, last_rotate_dir;
108 int used_solve; 99 int used_solve;
109 unsigned char *tiles; 100 unsigned char *tiles;
110 unsigned char *barriers; 101 struct game_immutable_state *imm;
111}; 102};
112 103
113#define OFFSETWH(x2,y2,x1,y1,dir,width,height) \ 104#define OFFSETWH(x2,y2,x1,y1,dir,width,height) \
@@ -119,7 +110,7 @@ struct game_state {
119 110
120#define index(state, a, x, y) ( a[(y) * (state)->width + (x)] ) 111#define index(state, a, x, y) ( a[(y) * (state)->width + (x)] )
121#define tile(state, x, y) index(state, (state)->tiles, x, y) 112#define tile(state, x, y) index(state, (state)->tiles, x, y)
122#define barrier(state, x, y) index(state, (state)->barriers, x, y) 113#define barrier(state, x, y) index(state, (state)->imm->barriers, x, y)
123 114
124struct xyd { 115struct xyd {
125 int x, y, direction; 116 int x, y, direction;
@@ -462,6 +453,11 @@ static int todo_get(struct todo *todo) {
462 return ret; 453 return ret;
463} 454}
464 455
456/*
457 * Return values: -1 means puzzle was proved inconsistent, 0 means we
458 * failed to narrow down to a unique solution, +1 means we solved it
459 * fully.
460 */
465static int net_solver(int w, int h, unsigned char *tiles, 461static int net_solver(int w, int h, unsigned char *tiles,
466 unsigned char *barriers, int wrapping) 462 unsigned char *barriers, int wrapping)
467{ 463{
@@ -736,7 +732,11 @@ static int net_solver(int w, int h, unsigned char *tiles,
736#endif 732#endif
737 } 733 }
738 734
739 assert(j > 0); /* we can't lose _all_ possibilities! */ 735 if (j == 0) {
736 /* If we've ruled out all possible orientations for a
737 * tile, then our puzzle has no solution at all. */
738 return -1;
739 }
740 740
741 if (j < i) { 741 if (j < i) {
742 done_something = TRUE; 742 done_something = TRUE;
@@ -816,14 +816,14 @@ static int net_solver(int w, int h, unsigned char *tiles,
816 /* 816 /*
817 * Mark all completely determined tiles as locked. 817 * Mark all completely determined tiles as locked.
818 */ 818 */
819 j = TRUE; 819 j = +1;
820 for (i = 0; i < w*h; i++) { 820 for (i = 0; i < w*h; i++) {
821 if (tilestate[i * 4 + 1] == 255) { 821 if (tilestate[i * 4 + 1] == 255) {
822 assert(tilestate[i * 4 + 0] != 255); 822 assert(tilestate[i * 4 + 0] != 255);
823 tiles[i] = tilestate[i * 4] | LOCKED; 823 tiles[i] = tilestate[i * 4] | LOCKED;
824 } else { 824 } else {
825 tiles[i] &= ~LOCKED; 825 tiles[i] &= ~LOCKED;
826 j = FALSE; 826 j = 0;
827 } 827 }
828 } 828 }
829 829
@@ -1337,7 +1337,7 @@ static char *new_game_desc(const game_params *params, random_state *rs,
1337 /* 1337 /*
1338 * Run the solver to check unique solubility. 1338 * Run the solver to check unique solubility.
1339 */ 1339 */
1340 while (!net_solver(w, h, tiles, NULL, params->wrapping)) { 1340 while (net_solver(w, h, tiles, NULL, params->wrapping) != 1) {
1341 int n = 0; 1341 int n = 0;
1342 1342
1343 /* 1343 /*
@@ -1647,12 +1647,14 @@ static game_state *new_game(midend *me, const game_params *params,
1647 w = state->width = params->width; 1647 w = state->width = params->width;
1648 h = state->height = params->height; 1648 h = state->height = params->height;
1649 state->wrapping = params->wrapping; 1649 state->wrapping = params->wrapping;
1650 state->imm = snew(game_immutable_state);
1651 state->imm->refcount = 1;
1650 state->last_rotate_dir = state->last_rotate_x = state->last_rotate_y = 0; 1652 state->last_rotate_dir = state->last_rotate_x = state->last_rotate_y = 0;
1651 state->completed = state->used_solve = FALSE; 1653 state->completed = state->used_solve = FALSE;
1652 state->tiles = snewn(state->width * state->height, unsigned char); 1654 state->tiles = snewn(state->width * state->height, unsigned char);
1653 memset(state->tiles, 0, state->width * state->height); 1655 memset(state->tiles, 0, state->width * state->height);
1654 state->barriers = snewn(state->width * state->height, unsigned char); 1656 state->imm->barriers = snewn(state->width * state->height, unsigned char);
1655 memset(state->barriers, 0, state->width * state->height); 1657 memset(state->imm->barriers, 0, state->width * state->height);
1656 1658
1657 /* 1659 /*
1658 * Parse the game description into the grid. 1660 * Parse the game description into the grid.
@@ -1723,6 +1725,8 @@ static game_state *dup_game(const game_state *state)
1723 game_state *ret; 1725 game_state *ret;
1724 1726
1725 ret = snew(game_state); 1727 ret = snew(game_state);
1728 ret->imm = state->imm;
1729 ret->imm->refcount++;
1726 ret->width = state->width; 1730 ret->width = state->width;
1727 ret->height = state->height; 1731 ret->height = state->height;
1728 ret->wrapping = state->wrapping; 1732 ret->wrapping = state->wrapping;
@@ -1733,16 +1737,17 @@ static game_state *dup_game(const game_state *state)
1733 ret->last_rotate_y = state->last_rotate_y; 1737 ret->last_rotate_y = state->last_rotate_y;
1734 ret->tiles = snewn(state->width * state->height, unsigned char); 1738 ret->tiles = snewn(state->width * state->height, unsigned char);
1735 memcpy(ret->tiles, state->tiles, state->width * state->height); 1739 memcpy(ret->tiles, state->tiles, state->width * state->height);
1736 ret->barriers = snewn(state->width * state->height, unsigned char);
1737 memcpy(ret->barriers, state->barriers, state->width * state->height);
1738 1740
1739 return ret; 1741 return ret;
1740} 1742}
1741 1743
1742static void free_game(game_state *state) 1744static void free_game(game_state *state)
1743{ 1745{
1746 if (--state->imm->refcount == 0) {
1747 sfree(state->imm->barriers);
1748 sfree(state->imm);
1749 }
1744 sfree(state->tiles); 1750 sfree(state->tiles);
1745 sfree(state->barriers);
1746 sfree(state); 1751 sfree(state);
1747} 1752}
1748 1753
@@ -1761,9 +1766,17 @@ static char *solve_game(const game_state *state, const game_state *currstate,
1761 * Run the internal solver on the provided grid. This might 1766 * Run the internal solver on the provided grid. This might
1762 * not yield a complete solution. 1767 * not yield a complete solution.
1763 */ 1768 */
1769 int solver_result;
1770
1764 memcpy(tiles, state->tiles, state->width * state->height); 1771 memcpy(tiles, state->tiles, state->width * state->height);
1765 net_solver(state->width, state->height, tiles, 1772 solver_result = net_solver(state->width, state->height, tiles,
1766 state->barriers, state->wrapping); 1773 state->imm->barriers, state->wrapping);
1774
1775 if (solver_result < 0) {
1776 *error = "No solution exists for this puzzle";
1777 sfree(tiles);
1778 return NULL;
1779 }
1767 } else { 1780 } else {
1768 for (i = 0; i < state->width * state->height; i++) { 1781 for (i = 0; i < state->width * state->height; i++) {
1769 int c = aux[i]; 1782 int c = aux[i];
@@ -1990,7 +2003,7 @@ static int *compute_loops_inner(int w, int h, int wrapping,
1990static int *compute_loops(const game_state *state) 2003static int *compute_loops(const game_state *state)
1991{ 2004{
1992 return compute_loops_inner(state->width, state->height, state->wrapping, 2005 return compute_loops_inner(state->width, state->height, state->wrapping,
1993 state->tiles, state->barriers); 2006 state->tiles, state->imm->barriers);
1994} 2007}
1995 2008
1996struct game_ui { 2009struct game_ui {
@@ -2051,9 +2064,8 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
2051struct game_drawstate { 2064struct game_drawstate {
2052 int started; 2065 int started;
2053 int width, height; 2066 int width, height;
2054 int org_x, org_y;
2055 int tilesize; 2067 int tilesize;
2056 int *visible; 2068 unsigned long *visible, *to_draw;
2057}; 2069};
2058 2070
2059/* ---------------------------------------------------------------------- 2071/* ----------------------------------------------------------------------
@@ -2093,8 +2105,8 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2093 /* 2105 /*
2094 * The button must have been clicked on a valid tile. 2106 * The button must have been clicked on a valid tile.
2095 */ 2107 */
2096 x -= WINDOW_OFFSET + TILE_BORDER; 2108 x -= WINDOW_OFFSET + LINE_THICK;
2097 y -= WINDOW_OFFSET + TILE_BORDER; 2109 y -= WINDOW_OFFSET + LINE_THICK;
2098 if (x < 0 || y < 0) 2110 if (x < 0 || y < 0)
2099 return nullret; 2111 return nullret;
2100 tx = x / TILE_SIZE; 2112 tx = x / TILE_SIZE;
@@ -2104,8 +2116,8 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2104 /* Transform from physical to game coords */ 2116 /* Transform from physical to game coords */
2105 tx = (tx + ui->org_x) % state->width; 2117 tx = (tx + ui->org_x) % state->width;
2106 ty = (ty + ui->org_y) % state->height; 2118 ty = (ty + ui->org_y) % state->height;
2107 if (x % TILE_SIZE >= TILE_SIZE - TILE_BORDER || 2119 if (x % TILE_SIZE >= TILE_SIZE - LINE_THICK ||
2108 y % TILE_SIZE >= TILE_SIZE - TILE_BORDER) 2120 y % TILE_SIZE >= TILE_SIZE - LINE_THICK)
2109 return nullret; 2121 return nullret;
2110 2122
2111#ifdef USE_DRAGGING 2123#ifdef USE_DRAGGING
@@ -2434,20 +2446,25 @@ static game_state *execute_move(const game_state *from, const char *move)
2434static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) 2446static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2435{ 2447{
2436 game_drawstate *ds = snew(game_drawstate); 2448 game_drawstate *ds = snew(game_drawstate);
2437 int i; 2449 int i, ncells;
2438 2450
2439 ds->started = FALSE; 2451 ds->started = FALSE;
2440 ds->width = state->width; 2452 ds->width = state->width;
2441 ds->height = state->height; 2453 ds->height = state->height;
2442 ds->org_x = ds->org_y = -1; 2454 ncells = (state->width+2) * (state->height+2);
2443 ds->visible = snewn(state->width * state->height, int); 2455 ds->visible = snewn(ncells, unsigned long);
2456 ds->to_draw = snewn(ncells, unsigned long);
2444 ds->tilesize = 0; /* undecided yet */ 2457 ds->tilesize = 0; /* undecided yet */
2445 for (i = 0; i < state->width * state->height; i++) 2458 for (i = 0; i < ncells; i++)
2446 ds->visible[i] = -1; 2459 ds->visible[i] = -1;
2447 2460
2448 return ds; 2461 return ds;
2449} 2462}
2450 2463
2464#define dsindex(ds, field, x, y) ((ds)->field[((y)+1)*((ds)->width+2)+((x)+1)])
2465#define visible(ds, x, y) dsindex(ds, visible, x, y)
2466#define todraw(ds, x, y) dsindex(ds, to_draw, x, y)
2467
2451static void game_free_drawstate(drawing *dr, game_drawstate *ds) 2468static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2452{ 2469{
2453 sfree(ds->visible); 2470 sfree(ds->visible);
@@ -2457,8 +2474,12 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2457static void game_compute_size(const game_params *params, int tilesize, 2474static void game_compute_size(const game_params *params, int tilesize,
2458 int *x, int *y) 2475 int *x, int *y)
2459{ 2476{
2460 *x = WINDOW_OFFSET * 2 + tilesize * params->width + TILE_BORDER; 2477 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2461 *y = WINDOW_OFFSET * 2 + tilesize * params->height + TILE_BORDER; 2478 struct { int tilesize; } ads, *ds = &ads;
2479 ads.tilesize = tilesize;
2480
2481 *x = WINDOW_OFFSET * 2 + TILE_SIZE * params->width + LINE_THICK;
2482 *y = WINDOW_OFFSET * 2 + TILE_SIZE * params->height + LINE_THICK;
2462} 2483}
2463 2484
2464static void game_set_size(drawing *dr, game_drawstate *ds, 2485static void game_set_size(drawing *dr, game_drawstate *ds,
@@ -2532,297 +2553,286 @@ static float *game_colours(frontend *fe, int *ncolours)
2532 return ret; 2553 return ret;
2533} 2554}
2534 2555
2535static void draw_filled_line(drawing *dr, int x1, int y1, int x2, int y2, 2556static void rotated_coords(float *ox, float *oy, const float matrix[4],
2536 int colour) 2557 float cx, float cy, float ix, float iy)
2537{ 2558{
2538 draw_line(dr, x1-1, y1, x2-1, y2, COL_WIRE); 2559 *ox = matrix[0] * ix + matrix[2] * iy + cx;
2539 draw_line(dr, x1+1, y1, x2+1, y2, COL_WIRE); 2560 *oy = matrix[1] * ix + matrix[3] * iy + cy;
2540 draw_line(dr, x1, y1-1, x2, y2-1, COL_WIRE);
2541 draw_line(dr, x1, y1+1, x2, y2+1, COL_WIRE);
2542 draw_line(dr, x1, y1, x2, y2, colour);
2543} 2561}
2544 2562
2545static void draw_rect_coords(drawing *dr, int x1, int y1, int x2, int y2, 2563/* Flags describing the visible features of a tile. */
2546 int colour) 2564#define TILE_BARRIER_SHIFT 0 /* 4 bits: R U L D */
2547{ 2565#define TILE_BARRIER_CORNER_SHIFT 4 /* 4 bits: RU UL LD DR */
2548 int mx = (x1 < x2 ? x1 : x2); 2566#define TILE_KEYBOARD_CURSOR (1<<8) /* 1 bit if cursor is here */
2549 int my = (y1 < y2 ? y1 : y2); 2567#define TILE_WIRE_SHIFT 9 /* 8 bits: RR UU LL DD
2550 int dx = (x2 + x1 - 2*mx + 1); 2568 * Each pair: 0=no wire, 1=unpowered,
2551 int dy = (y2 + y1 - 2*my + 1); 2569 * 2=powered, 3=loop err highlight */
2552 2570#define TILE_ENDPOINT_SHIFT 17 /* 2 bits: 0=no endpoint, 1=unpowered,
2553 draw_rect(dr, mx, my, dx, dy, colour); 2571 * 2=powered, 3=power-source square */
2554} 2572#define TILE_WIRE_ON_EDGE_SHIFT 19 /* 8 bits: RR UU LL DD,
2555 2573 * same encoding as TILE_WIRE_SHIFT */
2556/* 2574#define TILE_ROTATING (1UL<<27) /* 1 bit if tile is rotating */
2557 * draw_barrier_corner() and draw_barrier() are passed physical coords 2575#define TILE_LOCKED (1UL<<28) /* 1 bit if tile is locked */
2558 */ 2576
2559static void draw_barrier_corner(drawing *dr, game_drawstate *ds, 2577static void draw_wires(drawing *dr, int cx, int cy, int radius,
2560 int x, int y, int dx, int dy, int phase) 2578 unsigned long tile, int bitmap,
2579 int colour, int halfwidth, const float matrix[4])
2561{ 2580{
2562 int bx = WINDOW_OFFSET + TILE_SIZE * x; 2581 float fpoints[12*2];
2563 int by = WINDOW_OFFSET + TILE_SIZE * y; 2582 int points[12*2];
2564 int x1, y1; 2583 int npoints, d, dsh, i;
2565 2584 int any_wire_this_colour = FALSE;
2566 x1 = (dx > 0 ? TILE_SIZE+TILE_BORDER-1 : 0); 2585 float xf, yf;
2567 y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0); 2586
2568 2587 npoints = 0;
2569 if (phase == 0) { 2588 for (d = 1, dsh = 0; d < 16; d *= 2, dsh++) {
2570 draw_rect_coords(dr, bx+x1+dx, by+y1, 2589 int wiretype = (tile >> (TILE_WIRE_SHIFT + 2*dsh)) & 3;
2571 bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy, 2590
2572 COL_WIRE); 2591 fpoints[2*npoints+0] = halfwidth * (X(d) + X(C(d)));
2573 draw_rect_coords(dr, bx+x1, by+y1+dy, 2592 fpoints[2*npoints+1] = halfwidth * (Y(d) + Y(C(d)));
2574 bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy, 2593 npoints++;
2575 COL_WIRE); 2594
2576 } else { 2595 if (bitmap & (1 << wiretype)) {
2577 draw_rect_coords(dr, bx+x1, by+y1, 2596 fpoints[2*npoints+0] = radius * X(d) + halfwidth * X(C(d));
2578 bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy, 2597 fpoints[2*npoints+1] = radius * Y(d) + halfwidth * Y(C(d));
2579 COL_BARRIER); 2598 npoints++;
2599 fpoints[2*npoints+0] = radius * X(d) + halfwidth * X(A(d));
2600 fpoints[2*npoints+1] = radius * Y(d) + halfwidth * Y(A(d));
2601 npoints++;
2602
2603 any_wire_this_colour = TRUE;
2604 }
2580 } 2605 }
2581}
2582 2606
2583static void draw_barrier(drawing *dr, game_drawstate *ds, 2607 if (!any_wire_this_colour)
2584 int x, int y, int dir, int phase) 2608 return;
2585{
2586 int bx = WINDOW_OFFSET + TILE_SIZE * x;
2587 int by = WINDOW_OFFSET + TILE_SIZE * y;
2588 int x1, y1, w, h;
2589 2609
2590 x1 = (X(dir) > 0 ? TILE_SIZE : X(dir) == 0 ? TILE_BORDER : 0); 2610 for (i = 0; i < npoints; i++) {
2591 y1 = (Y(dir) > 0 ? TILE_SIZE : Y(dir) == 0 ? TILE_BORDER : 0); 2611 rotated_coords(&xf, &yf, matrix, cx, cy, fpoints[2*i], fpoints[2*i+1]);
2592 w = (X(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER); 2612 points[2*i] = 0.5 + xf;
2593 h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER); 2613 points[2*i+1] = 0.5 + yf;
2594
2595 if (phase == 0) {
2596 draw_rect(dr, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE);
2597 } else {
2598 draw_rect(dr, bx+x1, by+y1, w, h, COL_BARRIER);
2599 } 2614 }
2615
2616 draw_polygon(dr, points, npoints, colour, colour);
2600} 2617}
2601 2618
2602/* 2619static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y,
2603 * draw_tile() is passed physical coordinates 2620 unsigned long tile, float angle)
2604 */
2605static void draw_tile(drawing *dr, const game_state *state, game_drawstate *ds,
2606 int x, int y, int tile, int src, float angle, int cursor)
2607{ 2621{
2608 int bx = WINDOW_OFFSET + TILE_SIZE * x; 2622 int tx, ty;
2609 int by = WINDOW_OFFSET + TILE_SIZE * y; 2623 int clipx, clipy, clipX, clipY, clipw, cliph;
2624 int border_br = LINE_THICK/2, border_tl = LINE_THICK - border_br;
2625 int barrier_outline_thick = (LINE_THICK+1)/2;
2626 int bg, d, dsh, pass;
2627 int cx, cy, radius;
2610 float matrix[4]; 2628 float matrix[4];
2611 float cx, cy, ex, ey, tx, ty; 2629
2612 int dir, col, phase; 2630 tx = WINDOW_OFFSET + TILE_SIZE * x + border_br;
2631 ty = WINDOW_OFFSET + TILE_SIZE * y + border_br;
2613 2632
2614 /* 2633 /*
2615 * When we draw a single tile, we must draw everything up to 2634 * Clip to the tile boundary, with adjustments if we're drawing
2616 * and including the borders around the tile. This means that 2635 * just outside the grid.
2617 * if the neighbouring tiles have connections to those borders,
2618 * we must draw those connections on the borders themselves.
2619 */ 2636 */
2620 2637 clipx = tx; clipX = tx + TILE_SIZE;
2621 clip(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); 2638 clipy = ty; clipY = ty + TILE_SIZE;
2639 if (x == -1) {
2640 clipx = clipX - border_br - barrier_outline_thick;
2641 } else if (x == ds->width) {
2642 clipX = clipx + border_tl + barrier_outline_thick;
2643 }
2644 if (y == -1) {
2645 clipy = clipY - border_br - barrier_outline_thick;
2646 } else if (y == ds->height) {
2647 clipY = clipy + border_tl + barrier_outline_thick;
2648 }
2649 clipw = clipX - clipx;
2650 cliph = clipY - clipy;
2651 clip(dr, clipx, clipy, clipw, cliph);
2622 2652
2623 /* 2653 /*
2624 * So. First blank the tile out completely: draw a big 2654 * Clear the clip region.
2625 * rectangle in border colour, and a smaller rectangle in
2626 * background colour to fill it in.
2627 */ 2655 */
2628 draw_rect(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER, 2656 bg = (tile & TILE_LOCKED) ? COL_LOCKED : COL_BACKGROUND;
2629 COL_BORDER); 2657 draw_rect(dr, clipx, clipy, clipw, cliph, bg);
2630 draw_rect(dr, bx+TILE_BORDER, by+TILE_BORDER,
2631 TILE_SIZE-TILE_BORDER, TILE_SIZE-TILE_BORDER,
2632 tile & LOCKED ? COL_LOCKED : COL_BACKGROUND);
2633 2658
2634 /* 2659 /*
2635 * Draw an inset outline rectangle as a cursor, in whichever of 2660 * Draw the grid lines.
2636 * COL_LOCKED and COL_BACKGROUND we aren't currently drawing
2637 * in.
2638 */ 2661 */
2639 if (cursor) { 2662 {
2640 draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8, 2663 int gridl = (x == -1 ? tx+TILE_SIZE-border_br : tx);
2641 bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, 2664 int gridr = (x == ds->width ? tx+border_tl : tx+TILE_SIZE);
2642 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); 2665 int gridu = (y == -1 ? ty+TILE_SIZE-border_br : ty);
2643 draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8, 2666 int gridd = (y == ds->height ? ty+border_tl : ty+TILE_SIZE);
2644 bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8, 2667 if (x >= 0)
2645 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); 2668 draw_rect(dr, tx, gridu, border_tl, gridd-gridu, COL_BORDER);
2646 draw_line(dr, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8, 2669 if (y >= 0)
2647 bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, 2670 draw_rect(dr, gridl, ty, gridr-gridl, border_tl, COL_BORDER);
2648 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); 2671 if (x < ds->width)
2649 draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, 2672 draw_rect(dr, tx+TILE_SIZE-border_br, gridu,
2650 bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, 2673 border_br, gridd-gridu, COL_BORDER);
2651 tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); 2674 if (y < ds->height)
2675 draw_rect(dr, gridl, ty+TILE_SIZE-border_br,
2676 gridr-gridl, border_br, COL_BORDER);
2652 } 2677 }
2653 2678
2654 /* 2679 /*
2655 * Set up the rotation matrix. 2680 * Draw the keyboard cursor.
2656 */ 2681 */
2657 matrix[0] = (float)cos(angle * PI / 180.0); 2682 if (tile & TILE_KEYBOARD_CURSOR) {
2658 matrix[1] = (float)-sin(angle * PI / 180.0); 2683 int cursorcol = (tile & TILE_LOCKED) ? COL_BACKGROUND : COL_LOCKED;
2659 matrix[2] = (float)sin(angle * PI / 180.0); 2684 int inset_outer = TILE_SIZE/8, inset_inner = inset_outer + LINE_THICK;
2660 matrix[3] = (float)cos(angle * PI / 180.0); 2685 draw_rect(dr, tx + inset_outer, ty + inset_outer,
2686 TILE_SIZE - 2*inset_outer, TILE_SIZE - 2*inset_outer,
2687 cursorcol);
2688 draw_rect(dr, tx + inset_inner, ty + inset_inner,
2689 TILE_SIZE - 2*inset_inner, TILE_SIZE - 2*inset_inner,
2690 bg);
2691 }
2692
2693 radius = (TILE_SIZE+1)/2;
2694 cx = tx + radius;
2695 cy = ty + radius;
2696 radius++;
2661 2697
2662 /* 2698 /*
2663 * Draw the wires. 2699 * Draw protrusions into this cell's edges of wires in
2700 * neighbouring cells, as given by the TILE_WIRE_ON_EDGE_SHIFT
2701 * flags. We only draw each of these if there _isn't_ a wire of
2702 * our own that's going to overlap it, which means either the
2703 * corresponding TILE_WIRE_SHIFT flag is zero, or else the
2704 * TILE_ROTATING flag is set (so that our main wire won't be drawn
2705 * in quite that place anyway).
2664 */ 2706 */
2665 cx = cy = TILE_BORDER + (TILE_SIZE-TILE_BORDER) / 2.0F - 0.5F; 2707 for (d = 1, dsh = 0; d < 16; d *= 2, dsh++) {
2666 col = (tile & ACTIVE ? COL_POWERED : COL_WIRE); 2708 int edgetype = ((tile >> (TILE_WIRE_ON_EDGE_SHIFT + 2*dsh)) & 3);
2667 for (dir = 1; dir < 0x10; dir <<= 1) { 2709 if (edgetype == 0)
2668 if (tile & dir) { 2710 continue; /* there isn't a wire on the edge */
2669 ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir); 2711 if (!(tile & TILE_ROTATING) &&
2670 ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir); 2712 ((tile >> (TILE_WIRE_SHIFT + 2*dsh)) & 3) != 0)
2671 MATMUL(tx, ty, matrix, ex, ey); 2713 continue; /* wire on edge would be overdrawn anyway */
2672 draw_filled_line(dr, bx+(int)cx, by+(int)cy, 2714
2673 bx+(int)(cx+tx), by+(int)(cy+ty), 2715 for (pass = 0; pass < 2; pass++) {
2674 COL_WIRE); 2716 int x, y, w, h;
2675 } 2717 int col = (pass == 0 || edgetype == 1 ? COL_WIRE :
2676 } 2718 edgetype == 2 ? COL_POWERED : COL_LOOP);
2677 for (dir = 1; dir < 0x10; dir <<= 1) { 2719 int halfwidth = pass == 0 ? 2*LINE_THICK-1 : LINE_THICK-1;
2678 if (tile & dir) { 2720
2679 ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir); 2721 if (X(d) < 0) {
2680 ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir); 2722 x = tx;
2681 MATMUL(tx, ty, matrix, ex, ey); 2723 w = border_tl;
2682 draw_line(dr, bx+(int)cx, by+(int)cy, 2724 } else if (X(d) > 0) {
2683 bx+(int)(cx+tx), by+(int)(cy+ty), 2725 x = tx + TILE_SIZE - border_br;
2684 (tile & LOOP(dir)) ? COL_LOOP : col); 2726 w = border_br;
2727 } else {
2728 x = cx - halfwidth;
2729 w = 2 * halfwidth + 1;
2730 }
2731
2732 if (Y(d) < 0) {
2733 y = ty;
2734 h = border_tl;
2735 } else if (Y(d) > 0) {
2736 y = ty + TILE_SIZE - border_br;
2737 h = border_br;
2738 } else {
2739 y = cy - halfwidth;
2740 h = 2 * halfwidth + 1;
2741 }
2742
2743 draw_rect(dr, x, y, w, h, col);
2685 } 2744 }
2686 } 2745 }
2687 /* If we've drawn any loop-highlighted arms, make sure the centre
2688 * point is loop-coloured rather than a later arm overwriting it. */
2689 if (tile & (RLOOP | ULOOP | LLOOP | DLOOP))
2690 draw_rect(dr, bx+(int)cx, by+(int)cy, 1, 1, COL_LOOP);
2691 2746
2692 /* 2747 /*
2693 * Draw the box in the middle. We do this in blue if the tile 2748 * Set up the rotation matrix for the main cell contents, i.e.
2694 * is an unpowered endpoint, in cyan if the tile is a powered 2749 * everything that is centred in the grid square and optionally
2695 * endpoint, in black if the tile is the centrepiece, and 2750 * rotated by an arbitrary angle about that centre point.
2696 * otherwise not at all.
2697 */ 2751 */
2698 col = -1; 2752 if (tile & TILE_ROTATING) {
2699 if (src) 2753 matrix[0] = (float)cos(angle * PI / 180.0);
2700 col = COL_WIRE; 2754 matrix[2] = (float)sin(angle * PI / 180.0);
2701 else if (COUNT(tile) == 1) { 2755 } else {
2702 col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT); 2756 matrix[0] = 1.0F;
2703 } 2757 matrix[2] = 0.0F;
2704 if (col >= 0) {
2705 int i, points[8];
2706
2707 points[0] = +1; points[1] = +1;
2708 points[2] = +1; points[3] = -1;
2709 points[4] = -1; points[5] = -1;
2710 points[6] = -1; points[7] = +1;
2711
2712 for (i = 0; i < 8; i += 2) {
2713 ex = (TILE_SIZE * 0.24F) * points[i];
2714 ey = (TILE_SIZE * 0.24F) * points[i+1];
2715 MATMUL(tx, ty, matrix, ex, ey);
2716 points[i] = bx+(int)(cx+tx);
2717 points[i+1] = by+(int)(cy+ty);
2718 }
2719
2720 draw_polygon(dr, points, 4, col, COL_WIRE);
2721 } 2758 }
2759 matrix[3] = matrix[0];
2760 matrix[1] = -matrix[2];
2722 2761
2723 /* 2762 /*
2724 * Draw the points on the border if other tiles are connected 2763 * Draw the wires.
2725 * to us.
2726 */ 2764 */
2727 for (dir = 1; dir < 0x10; dir <<= 1) { 2765 draw_wires(dr, cx, cy, radius, tile,
2728 int dx, dy, px, py, lx, ly, vx, vy, ox, oy; 2766 0xE, COL_WIRE, 2*LINE_THICK-1, matrix);
2729 2767 draw_wires(dr, cx, cy, radius, tile,
2730 dx = X(dir); 2768 0x4, COL_POWERED, LINE_THICK-1, matrix);
2731 dy = Y(dir); 2769 draw_wires(dr, cx, cy, radius, tile,
2732 2770 0x8, COL_LOOP, LINE_THICK-1, matrix);
2733 ox = x + dx;
2734 oy = y + dy;
2735
2736 if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height)
2737 continue;
2738
2739 if (!(tile(state, GX(ox), GY(oy)) & F(dir)))
2740 continue;
2741 2771
2742 px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx); 2772 /*
2743 py = by + (int)(dy>0 ? TILE_SIZE + TILE_BORDER - 1 : dy<0 ? 0 : cy); 2773 * Draw the central box.
2744 lx = dx * (TILE_BORDER-1); 2774 */
2745 ly = dy * (TILE_BORDER-1); 2775 for (pass = 0; pass < 2; pass++) {
2746 vx = (dy ? 1 : 0); 2776 int endtype = (tile >> TILE_ENDPOINT_SHIFT) & 3;
2747 vy = (dx ? 1 : 0); 2777 if (endtype) {
2778 int i, points[8], col;
2779 float boxr = TILE_SIZE * 0.24F + (pass == 0 ? LINE_THICK-1 : 0);
2780
2781 col = (pass == 0 || endtype == 3 ? COL_WIRE :
2782 endtype == 2 ? COL_POWERED : COL_ENDPOINT);
2783
2784 points[0] = +1; points[1] = +1;
2785 points[2] = +1; points[3] = -1;
2786 points[4] = -1; points[5] = -1;
2787 points[6] = -1; points[7] = +1;
2788
2789 for (i = 0; i < 8; i += 2) {
2790 float x, y;
2791 rotated_coords(&x, &y, matrix, cx, cy,
2792 boxr * points[i], boxr * points[i+1]);
2793 points[i] = x + 0.5;
2794 points[i+1] = y + 0.5;
2795 }
2748 2796
2749 if (angle == 0.0 && (tile & dir)) { 2797 draw_polygon(dr, points, 4, col, COL_WIRE);
2750 /*
2751 * If we are fully connected to the other tile, we must
2752 * draw right across the tile border. (We can use our
2753 * own ACTIVE state to determine what colour to do this
2754 * in: if we are fully connected to the other tile then
2755 * the two ACTIVE states will be the same.)
2756 */
2757 draw_rect_coords(dr, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE);
2758 draw_rect_coords(dr, px, py, px+lx, py+ly,
2759 ((tile & LOOP(dir)) ? COL_LOOP :
2760 (tile & ACTIVE) ? COL_POWERED :
2761 COL_WIRE));
2762 } else {
2763 /*
2764 * The other tile extends into our border, but isn't
2765 * actually connected to us. Just draw a single black
2766 * dot.
2767 */
2768 draw_rect_coords(dr, px, py, px, py, COL_WIRE);
2769 } 2798 }
2770 } 2799 }
2771 2800
2772 /* 2801 /*
2773 * Draw barrier corners, and then barriers. 2802 * Draw barriers along grid edges.
2774 */ 2803 */
2775 for (phase = 0; phase < 2; phase++) { 2804 for (pass = 0; pass < 2; pass++) {
2776 for (dir = 1; dir < 0x10; dir <<= 1) { 2805 int btl = border_tl, bbr = border_br, col = COL_BARRIER;
2777 int x1, y1, corner = FALSE; 2806 if (pass == 0) {
2778 /* 2807 btl += barrier_outline_thick;
2779 * If at least one barrier terminates at the corner 2808 bbr += barrier_outline_thick;
2780 * between dir and A(dir), draw a barrier corner. 2809 col = COL_WIRE;
2781 */
2782 if (barrier(state, GX(x), GY(y)) & (dir | A(dir))) {
2783 corner = TRUE;
2784 } else {
2785 /*
2786 * Only count barriers terminating at this corner
2787 * if they're physically next to the corner. (That
2788 * is, if they've wrapped round from the far side
2789 * of the screen, they don't count.)
2790 */
2791 x1 = x + X(dir);
2792 y1 = y + Y(dir);
2793 if (x1 >= 0 && x1 < state->width &&
2794 y1 >= 0 && y1 < state->height &&
2795 (barrier(state, GX(x1), GY(y1)) & A(dir))) {
2796 corner = TRUE;
2797 } else {
2798 x1 = x + X(A(dir));
2799 y1 = y + Y(A(dir));
2800 if (x1 >= 0 && x1 < state->width &&
2801 y1 >= 0 && y1 < state->height &&
2802 (barrier(state, GX(x1), GY(y1)) & dir))
2803 corner = TRUE;
2804 }
2805 }
2806
2807 if (corner) {
2808 /*
2809 * At least one barrier terminates here. Draw a
2810 * corner.
2811 */
2812 draw_barrier_corner(dr, ds, x, y,
2813 X(dir)+X(A(dir)), Y(dir)+Y(A(dir)),
2814 phase);
2815 }
2816 } 2810 }
2817 2811
2818 for (dir = 1; dir < 0x10; dir <<= 1) 2812 if (tile & (L << TILE_BARRIER_SHIFT))
2819 if (barrier(state, GX(x), GY(y)) & dir) 2813 draw_rect(dr, tx, ty, btl, TILE_SIZE, col);
2820 draw_barrier(dr, ds, x, y, dir, phase); 2814 if (tile & (R << TILE_BARRIER_SHIFT))
2815 draw_rect(dr, tx+TILE_SIZE-bbr, ty, bbr, TILE_SIZE, col);
2816 if (tile & (U << TILE_BARRIER_SHIFT))
2817 draw_rect(dr, tx, ty, TILE_SIZE, btl, col);
2818 if (tile & (D << TILE_BARRIER_SHIFT))
2819 draw_rect(dr, tx, ty+TILE_SIZE-bbr, TILE_SIZE, bbr, col);
2820
2821 if (tile & (R << TILE_BARRIER_CORNER_SHIFT))
2822 draw_rect(dr, tx+TILE_SIZE-bbr, ty, bbr, btl, col);
2823 if (tile & (U << TILE_BARRIER_CORNER_SHIFT))
2824 draw_rect(dr, tx, ty, btl, btl, col);
2825 if (tile & (L << TILE_BARRIER_CORNER_SHIFT))
2826 draw_rect(dr, tx, ty+TILE_SIZE-bbr, btl, bbr, col);
2827 if (tile & (D << TILE_BARRIER_CORNER_SHIFT))
2828 draw_rect(dr, tx+TILE_SIZE-bbr, ty+TILE_SIZE-bbr, bbr, bbr, col);
2821 } 2829 }
2822 2830
2831 /*
2832 * Unclip and draw update, to finish.
2833 */
2823 unclip(dr); 2834 unclip(dr);
2824 2835 draw_update(dr, clipx, clipy, clipw, cliph);
2825 draw_update(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
2826} 2836}
2827 2837
2828static void game_redraw(drawing *dr, game_drawstate *ds, 2838static void game_redraw(drawing *dr, game_drawstate *ds,
@@ -2830,73 +2840,26 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2830 int dir, const game_ui *ui, 2840 int dir, const game_ui *ui,
2831 float t, float ft) 2841 float t, float ft)
2832{ 2842{
2833 int x, y, tx, ty, frame, last_rotate_dir, moved_origin = FALSE; 2843 int tx, ty, dx, dy, d, dsh, last_rotate_dir, frame;
2834 unsigned char *active; 2844 unsigned char *active;
2835 int *loops; 2845 int *loops;
2836 float angle = 0.0; 2846 float angle = 0.0;
2837 2847
2838 /* 2848 /*
2839 * Clear the screen, and draw the exterior barrier lines, if 2849 * Clear the screen on our first call.
2840 * this is our first call or if the origin has changed.
2841 */ 2850 */
2842 if (!ds->started || ui->org_x != ds->org_x || ui->org_y != ds->org_y) { 2851 if (!ds->started) {
2843 int phase; 2852 int w, h;
2853 game_params params;
2844 2854
2845 ds->started = TRUE; 2855 ds->started = TRUE;
2846 2856
2847 draw_rect(dr, 0, 0, 2857 params.width = ds->width;
2848 WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER, 2858 params.height = ds->height;
2849 WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER, 2859 game_compute_size(&params, TILE_SIZE, &w, &h);
2850 COL_BACKGROUND);
2851
2852 ds->org_x = ui->org_x;
2853 ds->org_y = ui->org_y;
2854 moved_origin = TRUE;
2855
2856 draw_update(dr, 0, 0,
2857 WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER,
2858 WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER);
2859
2860 for (phase = 0; phase < 2; phase++) {
2861 2860
2862 for (x = 0; x < ds->width; x++) { 2861 draw_rect(dr, 0, 0, w, h, COL_BACKGROUND);
2863 if (x+1 < ds->width) { 2862 draw_update(dr, 0, 0, w, h);
2864 if (barrier(state, GX(x), GY(0)) & R)
2865 draw_barrier_corner(dr, ds, x, -1, +1, +1, phase);
2866 if (barrier(state, GX(x), GY(ds->height-1)) & R)
2867 draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase);
2868 }
2869 if (barrier(state, GX(x), GY(0)) & U) {
2870 draw_barrier_corner(dr, ds, x, -1, -1, +1, phase);
2871 draw_barrier_corner(dr, ds, x, -1, +1, +1, phase);
2872 draw_barrier(dr, ds, x, -1, D, phase);
2873 }
2874 if (barrier(state, GX(x), GY(ds->height-1)) & D) {
2875 draw_barrier_corner(dr, ds, x, ds->height, -1, -1, phase);
2876 draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase);
2877 draw_barrier(dr, ds, x, ds->height, U, phase);
2878 }
2879 }
2880
2881 for (y = 0; y < ds->height; y++) {
2882 if (y+1 < ds->height) {
2883 if (barrier(state, GX(0), GY(y)) & D)
2884 draw_barrier_corner(dr, ds, -1, y, +1, +1, phase);
2885 if (barrier(state, GX(ds->width-1), GY(y)) & D)
2886 draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase);
2887 }
2888 if (barrier(state, GX(0), GY(y)) & L) {
2889 draw_barrier_corner(dr, ds, -1, y, +1, -1, phase);
2890 draw_barrier_corner(dr, ds, -1, y, +1, +1, phase);
2891 draw_barrier(dr, ds, -1, y, R, phase);
2892 }
2893 if (barrier(state, GX(ds->width-1), GY(y)) & R) {
2894 draw_barrier_corner(dr, ds, ds->width, y, -1, -1, phase);
2895 draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase);
2896 draw_barrier(dr, ds, ds->width, y, L, phase);
2897 }
2898 }
2899 }
2900 } 2863 }
2901 2864
2902 tx = ty = -1; 2865 tx = ty = -1;
@@ -2913,30 +2876,83 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2913 state = oldstate; 2876 state = oldstate;
2914 } 2877 }
2915 2878
2916 frame = -1;
2917 if (ft > 0) { 2879 if (ft > 0) {
2918 /* 2880 /*
2919 * We're animating a completion flash. Find which frame 2881 * We're animating a completion flash. Find which frame
2920 * we're at. 2882 * we're at.
2921 */ 2883 */
2922 frame = (int)(ft / FLASH_FRAME); 2884 frame = (int)(ft / FLASH_FRAME);
2885 } else {
2886 frame = 0;
2923 } 2887 }
2924 2888
2925 /* 2889 /*
2926 * Draw any tile which differs from the way it was last drawn. 2890 * Build up a map of what we want every tile to look like. We
2891 * include tiles one square outside the grid, for the outer edges
2892 * of barriers.
2927 */ 2893 */
2928 active = compute_active(state, ui->cx, ui->cy); 2894 active = compute_active(state, ui->cx, ui->cy);
2929 loops = compute_loops(state); 2895 loops = compute_loops(state);
2930 2896
2931 for (x = 0; x < ds->width; x++) 2897 for (dy = -1; dy < ds->height+1; dy++) {
2932 for (y = 0; y < ds->height; y++) { 2898 for (dx = -1; dx < ds->width+1; dx++) {
2933 int c = tile(state, GX(x), GY(y)) | 2899 todraw(ds, dx, dy) = 0;
2934 index(state, active, GX(x), GY(y)) | 2900 }
2935 index(state, loops, GX(x), GY(y)); 2901 }
2936 int is_src = GX(x) == ui->cx && GY(y) == ui->cy; 2902
2937 int is_anim = GX(x) == tx && GY(y) == ty; 2903 for (dy = 0; dy < ds->height; dy++) {
2938 int is_cursor = ui->cur_visible && 2904 int gy = (dy + ui->org_y) % ds->height;
2939 GX(x) == ui->cur_x && GY(y) == ui->cur_y; 2905 for (dx = 0; dx < ds->width; dx++) {
2906 int gx = (dx + ui->org_x) % ds->width;
2907 int t = (tile(state, gx, gy) |
2908 index(state, loops, gx, gy) |
2909 index(state, active, gx, gy));
2910
2911 for (d = 1, dsh = 0; d < 16; d *= 2, dsh++) {
2912 if (barrier(state, gx, gy) & d) {
2913 todraw(ds, dx, dy) |=
2914 d << TILE_BARRIER_SHIFT;
2915 todraw(ds, dx + X(d), dy + Y(d)) |=
2916 F(d) << TILE_BARRIER_SHIFT;
2917 todraw(ds, dx + X(A(d)), dy + Y(A(d))) |=
2918 C(d) << TILE_BARRIER_CORNER_SHIFT;
2919 todraw(ds, dx + X(A(d)) + X(d), dy + Y(A(d)) + Y(d)) |=
2920 F(d) << TILE_BARRIER_CORNER_SHIFT;
2921 todraw(ds, dx + X(C(d)), dy + Y(C(d))) |=
2922 d << TILE_BARRIER_CORNER_SHIFT;
2923 todraw(ds, dx + X(C(d)) + X(d), dy + Y(C(d)) + Y(d)) |=
2924 A(d) << TILE_BARRIER_CORNER_SHIFT;
2925 }
2926
2927 if (t & d) {
2928 int edgeval = (t & LOOP(d) ? 3 : t & ACTIVE ? 2 : 1);
2929 todraw(ds, dx, dy) |= edgeval << (TILE_WIRE_SHIFT + dsh*2);
2930 if (!(gx == tx && gy == ty)) {
2931 todraw(ds, dx + X(d), dy + Y(d)) |=
2932 edgeval << (TILE_WIRE_ON_EDGE_SHIFT + (dsh ^ 2)*2);
2933 }
2934 }
2935 }
2936
2937 if (ui->cur_visible && gx == ui->cur_x && gy == ui->cur_y)
2938 todraw(ds, dx, dy) |= TILE_KEYBOARD_CURSOR;
2939
2940 if (gx == tx && gy == ty)
2941 todraw(ds, dx, dy) |= TILE_ROTATING;
2942
2943 if (gx == ui->cx && gy == ui->cy) {
2944 todraw(ds, dx, dy) |= 3 << TILE_ENDPOINT_SHIFT;
2945 } else if ((t & 0xF) != R && (t & 0xF) != U &&
2946 (t & 0xF) != L && (t & 0xF) != D) {
2947 /* this is not an endpoint tile */
2948 } else if (t & ACTIVE) {
2949 todraw(ds, dx, dy) |= 2 << TILE_ENDPOINT_SHIFT;
2950 } else {
2951 todraw(ds, dx, dy) |= 1 << TILE_ENDPOINT_SHIFT;
2952 }
2953
2954 if (t & LOCKED)
2955 todraw(ds, dx, dy) |= TILE_LOCKED;
2940 2956
2941 /* 2957 /*
2942 * In a completion flash, we adjust the LOCKED bit 2958 * In a completion flash, we adjust the LOCKED bit
@@ -2944,31 +2960,36 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2944 * the frame number. 2960 * the frame number.
2945 */ 2961 */
2946 if (frame >= 0) { 2962 if (frame >= 0) {
2947 int rcx = RX(ui->cx), rcy = RY(ui->cy); 2963 int rcx = (ui->cx + ds->width - ui->org_x) % ds->width;
2964 int rcy = (ui->cy + ds->height - ui->org_y) % ds->height;
2948 int xdist, ydist, dist; 2965 int xdist, ydist, dist;
2949 xdist = (x < rcx ? rcx - x : x - rcx); 2966 xdist = (dx < rcx ? rcx - dx : dx - rcx);
2950 ydist = (y < rcy ? rcy - y : y - rcy); 2967 ydist = (dy < rcy ? rcy - dy : dy - rcy);
2951 dist = (xdist > ydist ? xdist : ydist); 2968 dist = (xdist > ydist ? xdist : ydist);
2952 2969
2953 if (frame >= dist && frame < dist+4) { 2970 if (frame >= dist && frame < dist+4 &&
2954 int lock = (frame - dist) & 1; 2971 ((frame - dist) & 1))
2955 lock = lock ? LOCKED : 0; 2972 todraw(ds, dx, dy) ^= TILE_LOCKED;
2956 c = (c &~ LOCKED) | lock;
2957 }
2958 } 2973 }
2974 }
2975 }
2959 2976
2960 if (moved_origin || 2977 /*
2961 index(state, ds->visible, x, y) != c || 2978 * Now draw any tile that differs from the way it was last drawn.
2962 index(state, ds->visible, x, y) == -1 || 2979 * An exception is that if either the previous _or_ current state
2963 is_src || is_anim || is_cursor) { 2980 * has the TILE_ROTATING bit set, we must draw it regardless,
2964 draw_tile(dr, state, ds, x, y, c, 2981 * because it will have rotated to a different angle.q
2965 is_src, (is_anim ? angle : 0.0F), is_cursor); 2982 */
2966 if (is_src || is_anim || is_cursor) 2983 for (dy = -1; dy < ds->height+1; dy++) {
2967 index(state, ds->visible, x, y) = -1; 2984 for (dx = -1; dx < ds->width+1; dx++) {
2968 else 2985 int prev = visible(ds, dx, dy);
2969 index(state, ds->visible, x, y) = c; 2986 int curr = todraw(ds, dx, dy);
2987 if (prev != curr || ((prev | curr) & TILE_ROTATING) != 0) {
2988 draw_tile(dr, ds, dx, dy, curr, angle);
2989 visible(ds, dx, dy) = curr;
2970 } 2990 }
2971 } 2991 }
2992 }
2972 2993
2973 /* 2994 /*
2974 * Update the status bar. 2995 * Update the status bar.