diff options
Diffstat (limited to 'apps/plugins/puzzles/src/net.c')
-rw-r--r-- | apps/plugins/puzzles/src/net.c | 759 |
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 | |||
85 | enum { | 71 | enum { |
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 | ||
91 | typedef struct game_immutable_state { | ||
92 | int refcount; | ||
93 | unsigned char *barriers; | ||
94 | } game_immutable_state; | ||
95 | |||
105 | struct game_state { | 96 | struct 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 | ||
124 | struct xyd { | 115 | struct 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 | */ | ||
465 | static int net_solver(int w, int h, unsigned char *tiles, | 461 | static 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 | ||
1742 | static void free_game(game_state *state) | 1744 | static 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, | |||
1990 | static int *compute_loops(const game_state *state) | 2003 | static 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 | ||
1996 | struct game_ui { | 2009 | struct game_ui { |
@@ -2051,9 +2064,8 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
2051 | struct game_drawstate { | 2064 | struct 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) | |||
2434 | static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | 2446 | static 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 | |||
2451 | static void game_free_drawstate(drawing *dr, game_drawstate *ds) | 2468 | static 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) | |||
2457 | static void game_compute_size(const game_params *params, int tilesize, | 2474 | static 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 | ||
2464 | static void game_set_size(drawing *dr, game_drawstate *ds, | 2485 | static 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 | ||
2535 | static void draw_filled_line(drawing *dr, int x1, int y1, int x2, int y2, | 2556 | static 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 | ||
2545 | static 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 | |
2559 | static void draw_barrier_corner(drawing *dr, game_drawstate *ds, | 2577 | static 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 | ||
2583 | static 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 | /* | 2619 | static 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 | */ | ||
2605 | static 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 | ||
2828 | static void game_redraw(drawing *dr, game_drawstate *ds, | 2838 | static 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(¶ms, 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. |