diff options
Diffstat (limited to 'apps/plugins/puzzles/src/map.c')
-rw-r--r-- | apps/plugins/puzzles/src/map.c | 260 |
1 files changed, 168 insertions, 92 deletions
diff --git a/apps/plugins/puzzles/src/map.c b/apps/plugins/puzzles/src/map.c index 9df2d22b52..2ef156e72a 100644 --- a/apps/plugins/puzzles/src/map.c +++ b/apps/plugins/puzzles/src/map.c | |||
@@ -14,7 +14,12 @@ | |||
14 | #include <string.h> | 14 | #include <string.h> |
15 | #include <assert.h> | 15 | #include <assert.h> |
16 | #include <ctype.h> | 16 | #include <ctype.h> |
17 | #include <math.h> | 17 | #include <limits.h> |
18 | #ifdef NO_TGMATH_H | ||
19 | # include <math.h> | ||
20 | #else | ||
21 | # include <tgmath.h> | ||
22 | #endif | ||
18 | 23 | ||
19 | #include "puzzles.h" | 24 | #include "puzzles.h" |
20 | 25 | ||
@@ -25,7 +30,7 @@ | |||
25 | */ | 30 | */ |
26 | #if defined STANDALONE_SOLVER | 31 | #if defined STANDALONE_SOLVER |
27 | #define SOLVER_DIAGNOSTICS | 32 | #define SOLVER_DIAGNOSTICS |
28 | bool verbose = false; | 33 | static bool verbose = false; |
29 | #elif defined SOLVER_DIAGNOSTICS | 34 | #elif defined SOLVER_DIAGNOSTICS |
30 | #define verbose true | 35 | #define verbose true |
31 | #endif | 36 | #endif |
@@ -41,12 +46,6 @@ bool verbose = false; | |||
41 | #define SIX (FOUR+2) | 46 | #define SIX (FOUR+2) |
42 | 47 | ||
43 | /* | 48 | /* |
44 | * Ghastly run-time configuration option, just for Gareth (again). | ||
45 | */ | ||
46 | static int flash_type = -1; | ||
47 | static float flash_length; | ||
48 | |||
49 | /* | ||
50 | * Difficulty levels. I do some macro ickery here to ensure that my | 49 | * Difficulty levels. I do some macro ickery here to ensure that my |
51 | * enum and the various forms of my name list always match up. | 50 | * enum and the various forms of my name list always match up. |
52 | */ | 51 | */ |
@@ -180,7 +179,9 @@ static void decode_params(game_params *params, char const *string) | |||
180 | params->n = atoi(p); | 179 | params->n = atoi(p); |
181 | while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++; | 180 | while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++; |
182 | } else { | 181 | } else { |
183 | params->n = params->w * params->h / 8; | 182 | if (params->h > 0 && params->w > 0 && |
183 | params->w <= INT_MAX / params->h) | ||
184 | params->n = params->w * params->h / 8; | ||
184 | } | 185 | } |
185 | if (*p == 'd') { | 186 | if (*p == 'd') { |
186 | int i; | 187 | int i; |
@@ -252,6 +253,8 @@ static const char *validate_params(const game_params *params, bool full) | |||
252 | { | 253 | { |
253 | if (params->w < 2 || params->h < 2) | 254 | if (params->w < 2 || params->h < 2) |
254 | return "Width and height must be at least two"; | 255 | return "Width and height must be at least two"; |
256 | if (params->w > INT_MAX / 2 / params->h) | ||
257 | return "Width times height must not be unreasonably large"; | ||
255 | if (params->n < 5) | 258 | if (params->n < 5) |
256 | return "Must have at least five regions"; | 259 | return "Must have at least five regions"; |
257 | if (params->n > params->w * params->h) | 260 | if (params->n > params->w * params->h) |
@@ -1659,6 +1662,10 @@ static char *new_game_desc(const game_params *params, random_state *rs, | |||
1659 | } | 1662 | } |
1660 | } | 1663 | } |
1661 | 1664 | ||
1665 | if (retlen + 10 >= retsize) { | ||
1666 | retsize = retlen + 256; | ||
1667 | ret = sresize(ret, retsize, char); | ||
1668 | } | ||
1662 | ret[retlen++] = 'a'-1 + run; | 1669 | ret[retlen++] = 'a'-1 + run; |
1663 | ret[retlen++] = ','; | 1670 | ret[retlen++] = ','; |
1664 | 1671 | ||
@@ -1711,8 +1718,8 @@ static const char *parse_edge_list(const game_params *params, | |||
1711 | int i, k, pos; | 1718 | int i, k, pos; |
1712 | bool state; | 1719 | bool state; |
1713 | const char *p = *desc; | 1720 | const char *p = *desc; |
1714 | 1721 | const char *err = NULL; | |
1715 | dsf_init(map+wh, wh); | 1722 | DSF *dsf = dsf_new(wh); |
1716 | 1723 | ||
1717 | pos = -1; | 1724 | pos = -1; |
1718 | state = false; | 1725 | state = false; |
@@ -1723,8 +1730,10 @@ static const char *parse_edge_list(const game_params *params, | |||
1723 | * pairs of squares whenever the edge list shows a non-edge). | 1730 | * pairs of squares whenever the edge list shows a non-edge). |
1724 | */ | 1731 | */ |
1725 | while (*p && *p != ',') { | 1732 | while (*p && *p != ',') { |
1726 | if (*p < 'a' || *p > 'z') | 1733 | if (*p < 'a' || *p > 'z') { |
1727 | return "Unexpected character in edge list"; | 1734 | err = "Unexpected character in edge list"; |
1735 | goto out; | ||
1736 | } | ||
1728 | if (*p == 'z') | 1737 | if (*p == 'z') |
1729 | k = 25; | 1738 | k = 25; |
1730 | else | 1739 | else |
@@ -1747,10 +1756,12 @@ static const char *parse_edge_list(const game_params *params, | |||
1747 | y = (pos - w*(h-1)) % h; | 1756 | y = (pos - w*(h-1)) % h; |
1748 | dx = 1; | 1757 | dx = 1; |
1749 | dy = 0; | 1758 | dy = 0; |
1750 | } else | 1759 | } else { |
1751 | return "Too much data in edge list"; | 1760 | err = "Too much data in edge list"; |
1761 | goto out; | ||
1762 | } | ||
1752 | if (!state) | 1763 | if (!state) |
1753 | dsf_merge(map+wh, y*w+x, (y+dy)*w+(x+dx)); | 1764 | dsf_merge(dsf, y*w+x, (y+dy)*w+(x+dx)); |
1754 | 1765 | ||
1755 | pos++; | 1766 | pos++; |
1756 | } | 1767 | } |
@@ -1759,8 +1770,10 @@ static const char *parse_edge_list(const game_params *params, | |||
1759 | p++; | 1770 | p++; |
1760 | } | 1771 | } |
1761 | assert(pos <= 2*wh-w-h); | 1772 | assert(pos <= 2*wh-w-h); |
1762 | if (pos < 2*wh-w-h) | 1773 | if (pos < 2*wh-w-h) { |
1763 | return "Too little data in edge list"; | 1774 | err = "Too little data in edge list"; |
1775 | goto out; | ||
1776 | } | ||
1764 | 1777 | ||
1765 | /* | 1778 | /* |
1766 | * Now go through again and allocate region numbers. | 1779 | * Now go through again and allocate region numbers. |
@@ -1769,17 +1782,22 @@ static const char *parse_edge_list(const game_params *params, | |||
1769 | for (i = 0; i < wh; i++) | 1782 | for (i = 0; i < wh; i++) |
1770 | map[i] = -1; | 1783 | map[i] = -1; |
1771 | for (i = 0; i < wh; i++) { | 1784 | for (i = 0; i < wh; i++) { |
1772 | k = dsf_canonify(map+wh, i); | 1785 | k = dsf_canonify(dsf, i); |
1773 | if (map[k] < 0) | 1786 | if (map[k] < 0) |
1774 | map[k] = pos++; | 1787 | map[k] = pos++; |
1775 | map[i] = map[k]; | 1788 | map[i] = map[k]; |
1776 | } | 1789 | } |
1777 | if (pos != n) | 1790 | if (pos != n) { |
1778 | return "Edge list defines the wrong number of regions"; | 1791 | err = "Edge list defines the wrong number of regions"; |
1792 | goto out; | ||
1793 | } | ||
1779 | 1794 | ||
1780 | *desc = p; | 1795 | *desc = p; |
1796 | err = NULL; /* no error */ | ||
1781 | 1797 | ||
1782 | return NULL; | 1798 | out: |
1799 | dsf_free(dsf); | ||
1800 | return err; | ||
1783 | } | 1801 | } |
1784 | 1802 | ||
1785 | static const char *validate_desc(const game_params *params, const char *desc) | 1803 | static const char *validate_desc(const game_params *params, const char *desc) |
@@ -1789,7 +1807,7 @@ static const char *validate_desc(const game_params *params, const char *desc) | |||
1789 | int *map; | 1807 | int *map; |
1790 | const char *ret; | 1808 | const char *ret; |
1791 | 1809 | ||
1792 | map = snewn(2*wh, int); | 1810 | map = snewn(wh, int); |
1793 | ret = parse_edge_list(params, &desc, map); | 1811 | ret = parse_edge_list(params, &desc, map); |
1794 | sfree(map); | 1812 | sfree(map); |
1795 | if (ret) | 1813 | if (ret) |
@@ -2252,16 +2270,6 @@ static char *solve_game(const game_state *state, const game_state *currstate, | |||
2252 | return dupstr(aux); | 2270 | return dupstr(aux); |
2253 | } | 2271 | } |
2254 | 2272 | ||
2255 | static bool game_can_format_as_text_now(const game_params *params) | ||
2256 | { | ||
2257 | return true; | ||
2258 | } | ||
2259 | |||
2260 | static char *game_text_format(const game_state *state) | ||
2261 | { | ||
2262 | return NULL; | ||
2263 | } | ||
2264 | |||
2265 | struct game_ui { | 2273 | struct game_ui { |
2266 | /* | 2274 | /* |
2267 | * drag_colour: | 2275 | * drag_colour: |
@@ -2275,11 +2283,41 @@ struct game_ui { | |||
2275 | int drag_pencil; | 2283 | int drag_pencil; |
2276 | int dragx, dragy; | 2284 | int dragx, dragy; |
2277 | bool show_numbers; | 2285 | bool show_numbers; |
2286 | bool large_stipples; | ||
2278 | 2287 | ||
2279 | int cur_x, cur_y, cur_lastmove; | 2288 | int cur_x, cur_y, cur_lastmove; |
2280 | bool cur_visible, cur_moved; | 2289 | bool cur_visible, cur_moved; |
2290 | |||
2291 | /* | ||
2292 | * User preference to enable alternative versions of the | ||
2293 | * completion flash. Some users have found the colour-cycling | ||
2294 | * default version to be a bit eye-twisting. | ||
2295 | */ | ||
2296 | enum { | ||
2297 | FLASH_CYCLIC, /* cycle the four colours of the map */ | ||
2298 | FLASH_EACH_TO_WHITE, /* turn each colour white in turn */ | ||
2299 | FLASH_ALL_TO_WHITE /* flash the whole map to white in one go */ | ||
2300 | } flash_type; | ||
2281 | }; | 2301 | }; |
2282 | 2302 | ||
2303 | static void legacy_prefs_override(struct game_ui *ui_out) | ||
2304 | { | ||
2305 | static bool initialised = false; | ||
2306 | static int flash_type = -1; | ||
2307 | |||
2308 | if (!initialised) { | ||
2309 | char *env; | ||
2310 | |||
2311 | initialised = true; | ||
2312 | |||
2313 | if ((env = getenv("MAP_ALTERNATIVE_FLASH")) != NULL) | ||
2314 | flash_type = FLASH_EACH_TO_WHITE; | ||
2315 | } | ||
2316 | |||
2317 | if (flash_type != -1) | ||
2318 | ui_out->flash_type = flash_type; | ||
2319 | } | ||
2320 | |||
2283 | static game_ui *new_ui(const game_state *state) | 2321 | static game_ui *new_ui(const game_state *state) |
2284 | { | 2322 | { |
2285 | game_ui *ui = snew(game_ui); | 2323 | game_ui *ui = snew(game_ui); |
@@ -2288,24 +2326,56 @@ static game_ui *new_ui(const game_state *state) | |||
2288 | ui->drag_pencil = 0; | 2326 | ui->drag_pencil = 0; |
2289 | ui->show_numbers = false; | 2327 | ui->show_numbers = false; |
2290 | ui->cur_x = ui->cur_y = 0; | 2328 | ui->cur_x = ui->cur_y = 0; |
2291 | ui->cur_visible = false; | 2329 | ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
2292 | ui->cur_moved = false; | 2330 | ui->cur_moved = false; |
2293 | ui->cur_lastmove = 0; | 2331 | ui->cur_lastmove = 0; |
2332 | ui->flash_type = FLASH_CYCLIC; | ||
2333 | ui->large_stipples = false; | ||
2334 | legacy_prefs_override(ui); | ||
2294 | return ui; | 2335 | return ui; |
2295 | } | 2336 | } |
2296 | 2337 | ||
2297 | static void free_ui(game_ui *ui) | 2338 | static config_item *get_prefs(game_ui *ui) |
2298 | { | 2339 | { |
2299 | sfree(ui); | 2340 | config_item *ret; |
2341 | |||
2342 | ret = snewn(4, config_item); | ||
2343 | |||
2344 | ret[0].name = "Victory flash effect"; | ||
2345 | ret[0].kw = "flash-type"; | ||
2346 | ret[0].type = C_CHOICES; | ||
2347 | ret[0].u.choices.choicenames = ":Cyclic:Each to white:All to white"; | ||
2348 | ret[0].u.choices.choicekws = ":cyclic:each-white:all-white"; | ||
2349 | ret[0].u.choices.selected = ui->flash_type; | ||
2350 | |||
2351 | ret[1].name = "Number regions"; | ||
2352 | ret[1].kw = "show-numbers"; | ||
2353 | ret[1].type = C_BOOLEAN; | ||
2354 | ret[1].u.boolean.bval = ui->show_numbers; | ||
2355 | |||
2356 | ret[2].name = "Display style for stipple marks"; | ||
2357 | ret[2].kw = "stipple-style"; | ||
2358 | ret[2].type = C_CHOICES; | ||
2359 | ret[2].u.choices.choicenames = ":Small:Large"; | ||
2360 | ret[2].u.choices.choicekws = ":small:large"; | ||
2361 | ret[2].u.choices.selected = ui->large_stipples; | ||
2362 | |||
2363 | ret[3].name = NULL; | ||
2364 | ret[3].type = C_END; | ||
2365 | |||
2366 | return ret; | ||
2300 | } | 2367 | } |
2301 | 2368 | ||
2302 | static char *encode_ui(const game_ui *ui) | 2369 | static void set_prefs(game_ui *ui, const config_item *cfg) |
2303 | { | 2370 | { |
2304 | return NULL; | 2371 | ui->flash_type = cfg[0].u.choices.selected; |
2372 | ui->show_numbers = cfg[1].u.boolean.bval; | ||
2373 | ui->large_stipples = cfg[2].u.choices.selected; | ||
2305 | } | 2374 | } |
2306 | 2375 | ||
2307 | static void decode_ui(game_ui *ui, const char *encoding) | 2376 | static void free_ui(game_ui *ui) |
2308 | { | 2377 | { |
2378 | sfree(ui); | ||
2309 | } | 2379 | } |
2310 | 2380 | ||
2311 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 2381 | static void game_changed_state(game_ui *ui, const game_state *oldstate, |
@@ -2388,6 +2458,26 @@ static int region_from_ui_cursor(const game_state *state, const game_ui *ui) | |||
2388 | EPSILON_Y(ui->cur_lastmove)); | 2458 | EPSILON_Y(ui->cur_lastmove)); |
2389 | } | 2459 | } |
2390 | 2460 | ||
2461 | static const char *current_key_label(const game_ui *ui, | ||
2462 | const game_state *state, int button) | ||
2463 | { | ||
2464 | int r; | ||
2465 | |||
2466 | if (IS_CURSOR_SELECT(button) && ui->cur_visible) { | ||
2467 | if (ui->drag_colour == -2) return "Pick"; | ||
2468 | r = region_from_ui_cursor(state, ui); | ||
2469 | if (state->map->immutable[r]) return "Cancel"; | ||
2470 | if (!ui->cur_moved) return ui->drag_pencil ? "Cancel" : "Clear"; | ||
2471 | if (button == CURSOR_SELECT2) { | ||
2472 | if (state->colouring[r] >= 0) return "Cancel"; | ||
2473 | if (ui->drag_colour >= 0) return "Stipple"; | ||
2474 | } | ||
2475 | if (ui->drag_pencil) return "Stipple"; | ||
2476 | return ui->drag_colour >= 0 ? "Fill" : "Clear"; | ||
2477 | } | ||
2478 | return ""; | ||
2479 | } | ||
2480 | |||
2391 | static char *interpret_move(const game_state *state, game_ui *ui, | 2481 | static char *interpret_move(const game_state *state, game_ui *ui, |
2392 | const game_drawstate *ds, | 2482 | const game_drawstate *ds, |
2393 | int x, int y, int button) | 2483 | int x, int y, int button) |
@@ -2401,21 +2491,21 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2401 | */ | 2491 | */ |
2402 | if (button == 'l' || button == 'L') { | 2492 | if (button == 'l' || button == 'L') { |
2403 | ui->show_numbers = !ui->show_numbers; | 2493 | ui->show_numbers = !ui->show_numbers; |
2404 | return UI_UPDATE; | 2494 | return MOVE_UI_UPDATE; |
2405 | } | 2495 | } |
2406 | 2496 | ||
2407 | if (IS_CURSOR_MOVE(button)) { | 2497 | if (IS_CURSOR_MOVE(button)) { |
2408 | move_cursor(button, &ui->cur_x, &ui->cur_y, state->p.w, state->p.h, | 2498 | move_cursor(button, &ui->cur_x, &ui->cur_y, state->p.w, state->p.h, |
2409 | false); | 2499 | false, NULL); |
2410 | ui->cur_visible = true; | 2500 | ui->cur_visible = true; |
2411 | ui->cur_moved = true; | 2501 | ui->cur_moved = true; |
2412 | ui->cur_lastmove = button; | 2502 | ui->cur_lastmove = button; |
2413 | return UI_UPDATE; | 2503 | return MOVE_UI_UPDATE; |
2414 | } | 2504 | } |
2415 | if (IS_CURSOR_SELECT(button)) { | 2505 | if (IS_CURSOR_SELECT(button)) { |
2416 | if (!ui->cur_visible) { | 2506 | if (!ui->cur_visible) { |
2417 | ui->cur_visible = true; | 2507 | ui->cur_visible = true; |
2418 | return UI_UPDATE; | 2508 | return MOVE_UI_UPDATE; |
2419 | } | 2509 | } |
2420 | if (ui->drag_colour == -2) { /* not currently cursor-dragging, start. */ | 2510 | if (ui->drag_colour == -2) { /* not currently cursor-dragging, start. */ |
2421 | int r = region_from_ui_cursor(state, ui); | 2511 | int r = region_from_ui_cursor(state, ui); |
@@ -2427,7 +2517,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2427 | ui->drag_pencil = 0; | 2517 | ui->drag_pencil = 0; |
2428 | } | 2518 | } |
2429 | ui->cur_moved = false; | 2519 | ui->cur_moved = false; |
2430 | return UI_UPDATE; | 2520 | return MOVE_UI_UPDATE; |
2431 | } else { /* currently cursor-dragging; drop the colour in the new region. */ | 2521 | } else { /* currently cursor-dragging; drop the colour in the new region. */ |
2432 | alt_button = (button == CURSOR_SELECT2); | 2522 | alt_button = (button == CURSOR_SELECT2); |
2433 | /* Double-select removes current colour. */ | 2523 | /* Double-select removes current colour. */ |
@@ -2452,14 +2542,14 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
2452 | ui->dragx = x; | 2542 | ui->dragx = x; |
2453 | ui->dragy = y; | 2543 | ui->dragy = y; |
2454 | ui->cur_visible = false; | 2544 | ui->cur_visible = false; |
2455 | return UI_UPDATE; | 2545 | return MOVE_UI_UPDATE; |
2456 | } | 2546 | } |
2457 | 2547 | ||
2458 | if ((button == LEFT_DRAG || button == RIGHT_DRAG) && | 2548 | if ((button == LEFT_DRAG || button == RIGHT_DRAG) && |
2459 | ui->drag_colour > -2) { | 2549 | ui->drag_colour > -2) { |
2460 | ui->dragx = x; | 2550 | ui->dragx = x; |
2461 | ui->dragy = y; | 2551 | ui->dragy = y; |
2462 | return UI_UPDATE; | 2552 | return MOVE_UI_UPDATE; |
2463 | } | 2553 | } |
2464 | 2554 | ||
2465 | if ((button == LEFT_RELEASE || button == RIGHT_RELEASE) && | 2555 | if ((button == LEFT_RELEASE || button == RIGHT_RELEASE) && |
@@ -2484,18 +2574,18 @@ drag_dropped: | |||
2484 | ui->drag_colour = -2; | 2574 | ui->drag_colour = -2; |
2485 | 2575 | ||
2486 | if (r < 0) | 2576 | if (r < 0) |
2487 | return UI_UPDATE; /* drag into border; do nothing else */ | 2577 | return MOVE_UI_UPDATE; /* drag into border; do nothing else */ |
2488 | 2578 | ||
2489 | if (state->map->immutable[r]) | 2579 | if (state->map->immutable[r]) |
2490 | return UI_UPDATE; /* can't change this region */ | 2580 | return MOVE_UI_UPDATE; /* can't change this region */ |
2491 | 2581 | ||
2492 | if (state->colouring[r] == c && state->pencil[r] == p) | 2582 | if (state->colouring[r] == c && state->pencil[r] == p) |
2493 | return UI_UPDATE; /* don't _need_ to change this region */ | 2583 | return MOVE_UI_UPDATE; /* don't _need_ to change this region */ |
2494 | 2584 | ||
2495 | if (alt_button) { | 2585 | if (alt_button) { |
2496 | if (state->colouring[r] >= 0) { | 2586 | if (state->colouring[r] >= 0) { |
2497 | /* Can't pencil on a coloured region */ | 2587 | /* Can't pencil on a coloured region */ |
2498 | return UI_UPDATE; | 2588 | return MOVE_UI_UPDATE; |
2499 | } else if (c >= 0) { | 2589 | } else if (c >= 0) { |
2500 | /* Right-dragging from colour to blank toggles one pencil */ | 2590 | /* Right-dragging from colour to blank toggles one pencil */ |
2501 | p = state->pencil[r] ^ (1 << c); | 2591 | p = state->pencil[r] ^ (1 << c); |
@@ -2605,7 +2695,7 @@ static game_state *execute_move(const game_state *state, const char *move) | |||
2605 | */ | 2695 | */ |
2606 | 2696 | ||
2607 | static void game_compute_size(const game_params *params, int tilesize, | 2697 | static void game_compute_size(const game_params *params, int tilesize, |
2608 | int *x, int *y) | 2698 | const game_ui *ui, int *x, int *y) |
2609 | { | 2699 | { |
2610 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ | 2700 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ |
2611 | struct { int tilesize; } ads, *ds = &ads; | 2701 | struct { int tilesize; } ads, *ds = &ads; |
@@ -2729,7 +2819,7 @@ static void draw_error(drawing *dr, game_drawstate *ds, int x, int y) | |||
2729 | 2819 | ||
2730 | static void draw_square(drawing *dr, game_drawstate *ds, | 2820 | static void draw_square(drawing *dr, game_drawstate *ds, |
2731 | const game_params *params, struct map *map, | 2821 | const game_params *params, struct map *map, |
2732 | int x, int y, unsigned long v) | 2822 | int x, int y, unsigned long v, bool large_stipples) |
2733 | { | 2823 | { |
2734 | int w = params->w, h = params->h, wh = w*h; | 2824 | int w = params->w, h = params->h, wh = w*h; |
2735 | int tv, bv, xo, yo, i, j, oldj; | 2825 | int tv, bv, xo, yo, i, j, oldj; |
@@ -2802,7 +2892,8 @@ static void draw_square(drawing *dr, game_drawstate *ds, | |||
2802 | 2892 | ||
2803 | draw_circle(dr, COORD(x) + (xo+1)*TILESIZE/5, | 2893 | draw_circle(dr, COORD(x) + (xo+1)*TILESIZE/5, |
2804 | COORD(y) + (yo+1)*TILESIZE/5, | 2894 | COORD(y) + (yo+1)*TILESIZE/5, |
2805 | TILESIZE/4, COL_0 + c, COL_0 + c); | 2895 | large_stipples ? TILESIZE/4 : TILESIZE/7, |
2896 | COL_0 + c, COL_0 + c); | ||
2806 | } | 2897 | } |
2807 | 2898 | ||
2808 | /* | 2899 | /* |
@@ -2857,6 +2948,11 @@ static void draw_square(drawing *dr, game_drawstate *ds, | |||
2857 | draw_update(dr, COORD(x), COORD(y), TILESIZE, TILESIZE); | 2948 | draw_update(dr, COORD(x), COORD(y), TILESIZE, TILESIZE); |
2858 | } | 2949 | } |
2859 | 2950 | ||
2951 | static float flash_length(const game_ui *ui) | ||
2952 | { | ||
2953 | return (ui->flash_type == FLASH_EACH_TO_WHITE ? 0.50F : 0.30F); | ||
2954 | } | ||
2955 | |||
2860 | static void game_redraw(drawing *dr, game_drawstate *ds, | 2956 | static void game_redraw(drawing *dr, game_drawstate *ds, |
2861 | const game_state *oldstate, const game_state *state, | 2957 | const game_state *oldstate, const game_state *state, |
2862 | int dir, const game_ui *ui, | 2958 | int dir, const game_ui *ui, |
@@ -2872,29 +2968,18 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
2872 | ds->drag_visible = false; | 2968 | ds->drag_visible = false; |
2873 | } | 2969 | } |
2874 | 2970 | ||
2875 | /* | ||
2876 | * The initial contents of the window are not guaranteed and | ||
2877 | * can vary with front ends. To be on the safe side, all games | ||
2878 | * should start by drawing a big background-colour rectangle | ||
2879 | * covering the whole window. | ||
2880 | */ | ||
2881 | if (!ds->started) { | 2971 | if (!ds->started) { |
2882 | int ww, wh; | ||
2883 | |||
2884 | game_compute_size(&state->p, TILESIZE, &ww, &wh); | ||
2885 | draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND); | ||
2886 | draw_rect(dr, COORD(0), COORD(0), w*TILESIZE+1, h*TILESIZE+1, | 2972 | draw_rect(dr, COORD(0), COORD(0), w*TILESIZE+1, h*TILESIZE+1, |
2887 | COL_GRID); | 2973 | COL_GRID); |
2888 | 2974 | draw_update(dr, COORD(0), COORD(0), w*TILESIZE+1, h*TILESIZE+1); | |
2889 | draw_update(dr, 0, 0, ww, wh); | ||
2890 | ds->started = true; | 2975 | ds->started = true; |
2891 | } | 2976 | } |
2892 | 2977 | ||
2893 | if (flashtime) { | 2978 | if (flashtime) { |
2894 | if (flash_type == 1) | 2979 | if (ui->flash_type == FLASH_EACH_TO_WHITE) |
2895 | flash = (int)(flashtime * FOUR / flash_length); | 2980 | flash = (int)(flashtime * FOUR / flash_length(ui)); |
2896 | else | 2981 | else |
2897 | flash = 1 + (int)(flashtime * THREE / flash_length); | 2982 | flash = 1 + (int)(flashtime * THREE / flash_length(ui)); |
2898 | } else | 2983 | } else |
2899 | flash = -1; | 2984 | flash = -1; |
2900 | 2985 | ||
@@ -2913,12 +2998,12 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
2913 | bv = FOUR; | 2998 | bv = FOUR; |
2914 | 2999 | ||
2915 | if (flash >= 0) { | 3000 | if (flash >= 0) { |
2916 | if (flash_type == 1) { | 3001 | if (ui->flash_type == FLASH_EACH_TO_WHITE) { |
2917 | if (tv == flash) | 3002 | if (tv == flash) |
2918 | tv = FOUR; | 3003 | tv = FOUR; |
2919 | if (bv == flash) | 3004 | if (bv == flash) |
2920 | bv = FOUR; | 3005 | bv = FOUR; |
2921 | } else if (flash_type == 2) { | 3006 | } else if (ui->flash_type == FLASH_ALL_TO_WHITE) { |
2922 | if (flash % 2) | 3007 | if (flash % 2) |
2923 | tv = bv = FOUR; | 3008 | tv = bv = FOUR; |
2924 | } else { | 3009 | } else { |
@@ -2990,7 +3075,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
2990 | for (x = 0; x < w; x++) { | 3075 | for (x = 0; x < w; x++) { |
2991 | unsigned long v = ds->todraw[y*w+x]; | 3076 | unsigned long v = ds->todraw[y*w+x]; |
2992 | if (ds->drawn[y*w+x] != v) { | 3077 | if (ds->drawn[y*w+x] != v) { |
2993 | draw_square(dr, ds, &state->p, state->map, x, y, v); | 3078 | draw_square(dr, ds, &state->p, state->map, x, y, v, ui->large_stipples); |
2994 | ds->drawn[y*w+x] = v; | 3079 | ds->drawn[y*w+x] = v; |
2995 | } | 3080 | } |
2996 | } | 3081 | } |
@@ -3048,15 +3133,7 @@ static float game_flash_length(const game_state *oldstate, | |||
3048 | { | 3133 | { |
3049 | if (!oldstate->completed && newstate->completed && | 3134 | if (!oldstate->completed && newstate->completed && |
3050 | !oldstate->cheated && !newstate->cheated) { | 3135 | !oldstate->cheated && !newstate->cheated) { |
3051 | if (flash_type < 0) { | 3136 | return flash_length(ui); |
3052 | char *env = getenv("MAP_ALTERNATIVE_FLASH"); | ||
3053 | if (env) | ||
3054 | flash_type = atoi(env); | ||
3055 | else | ||
3056 | flash_type = 0; | ||
3057 | flash_length = (flash_type == 1 ? 0.50F : 0.30F); | ||
3058 | } | ||
3059 | return flash_length; | ||
3060 | } else | 3137 | } else |
3061 | return 0.0F; | 3138 | return 0.0F; |
3062 | } | 3139 | } |
@@ -3079,12 +3156,8 @@ static int game_status(const game_state *state) | |||
3079 | return state->completed ? +1 : 0; | 3156 | return state->completed ? +1 : 0; |
3080 | } | 3157 | } |
3081 | 3158 | ||
3082 | static bool game_timing_state(const game_state *state, game_ui *ui) | 3159 | static void game_print_size(const game_params *params, const game_ui *ui, |
3083 | { | 3160 | float *x, float *y) |
3084 | return true; | ||
3085 | } | ||
3086 | |||
3087 | static void game_print_size(const game_params *params, float *x, float *y) | ||
3088 | { | 3161 | { |
3089 | int pw, ph; | 3162 | int pw, ph; |
3090 | 3163 | ||
@@ -3093,12 +3166,13 @@ static void game_print_size(const game_params *params, float *x, float *y) | |||
3093 | * compute this size is to compute the pixel puzzle size at a | 3166 | * compute this size is to compute the pixel puzzle size at a |
3094 | * given tile size and then scale. | 3167 | * given tile size and then scale. |
3095 | */ | 3168 | */ |
3096 | game_compute_size(params, 400, &pw, &ph); | 3169 | game_compute_size(params, 400, ui, &pw, &ph); |
3097 | *x = pw / 100.0F; | 3170 | *x = pw / 100.0F; |
3098 | *y = ph / 100.0F; | 3171 | *y = ph / 100.0F; |
3099 | } | 3172 | } |
3100 | 3173 | ||
3101 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 3174 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, |
3175 | int tilesize) | ||
3102 | { | 3176 | { |
3103 | int w = state->p.w, h = state->p.h, wh = w*h, n = state->p.n; | 3177 | int w = state->p.w, h = state->p.h, wh = w*h, n = state->p.n; |
3104 | int ink, c[FOUR], i; | 3178 | int ink, c[FOUR], i; |
@@ -3257,13 +3331,15 @@ const struct game thegame = { | |||
3257 | dup_game, | 3331 | dup_game, |
3258 | free_game, | 3332 | free_game, |
3259 | true, solve_game, | 3333 | true, solve_game, |
3260 | false, game_can_format_as_text_now, game_text_format, | 3334 | false, NULL, NULL, /* can_format_as_text_now, text_format */ |
3335 | get_prefs, set_prefs, | ||
3261 | new_ui, | 3336 | new_ui, |
3262 | free_ui, | 3337 | free_ui, |
3263 | encode_ui, | 3338 | NULL, /* encode_ui */ |
3264 | decode_ui, | 3339 | NULL, /* decode_ui */ |
3265 | NULL, /* game_request_keys */ | 3340 | NULL, /* game_request_keys */ |
3266 | game_changed_state, | 3341 | game_changed_state, |
3342 | current_key_label, | ||
3267 | interpret_move, | 3343 | interpret_move, |
3268 | execute_move, | 3344 | execute_move, |
3269 | 20, game_compute_size, game_set_size, | 3345 | 20, game_compute_size, game_set_size, |
@@ -3277,7 +3353,7 @@ const struct game thegame = { | |||
3277 | game_status, | 3353 | game_status, |
3278 | true, true, game_print_size, game_print, | 3354 | true, true, game_print_size, game_print, |
3279 | false, /* wants_statusbar */ | 3355 | false, /* wants_statusbar */ |
3280 | false, game_timing_state, | 3356 | false, NULL, /* timing_state */ |
3281 | 0, /* flags */ | 3357 | 0, /* flags */ |
3282 | }; | 3358 | }; |
3283 | 3359 | ||