diff options
Diffstat (limited to 'apps/plugins/puzzles/src/signpost.c')
-rw-r--r-- | apps/plugins/puzzles/src/signpost.c | 185 |
1 files changed, 115 insertions, 70 deletions
diff --git a/apps/plugins/puzzles/src/signpost.c b/apps/plugins/puzzles/src/signpost.c index 9aee255ebe..9aed67bb4a 100644 --- a/apps/plugins/puzzles/src/signpost.c +++ b/apps/plugins/puzzles/src/signpost.c | |||
@@ -7,7 +7,12 @@ | |||
7 | #include <string.h> | 7 | #include <string.h> |
8 | #include <assert.h> | 8 | #include <assert.h> |
9 | #include <ctype.h> | 9 | #include <ctype.h> |
10 | #include <math.h> | 10 | #include <limits.h> |
11 | #ifdef NO_TGMATH_H | ||
12 | # include <math.h> | ||
13 | #else | ||
14 | # include <tgmath.h> | ||
15 | #endif | ||
11 | 16 | ||
12 | #include "puzzles.h" | 17 | #include "puzzles.h" |
13 | 18 | ||
@@ -57,7 +62,7 @@ struct game_state { | |||
57 | int *nums; /* numbers, size n */ | 62 | int *nums; /* numbers, size n */ |
58 | unsigned int *flags; /* flags, size n */ | 63 | unsigned int *flags; /* flags, size n */ |
59 | int *next, *prev; /* links to other cell indexes, size n (-1 absent) */ | 64 | int *next, *prev; /* links to other cell indexes, size n (-1 absent) */ |
60 | int *dsf; /* connects regions with a dsf. */ | 65 | DSF *dsf; /* connects regions with a dsf. */ |
61 | int *numsi; /* for each number, which index is it in? (-1 absent) */ | 66 | int *numsi; /* for each number, which index is it in? (-1 absent) */ |
62 | }; | 67 | }; |
63 | 68 | ||
@@ -168,7 +173,7 @@ static bool isvalidmove(const game_state *state, bool clever, | |||
168 | 173 | ||
169 | /* can't create a new connection between cells in the same region | 174 | /* can't create a new connection between cells in the same region |
170 | * as that would create a loop. */ | 175 | * as that would create a loop. */ |
171 | if (dsf_canonify(state->dsf, from) == dsf_canonify(state->dsf, to)) | 176 | if (dsf_equivalent(state->dsf, from, to)) |
172 | return false; | 177 | return false; |
173 | 178 | ||
174 | /* if both cells are actual numbers, can't drag if we're not | 179 | /* if both cells are actual numbers, can't drag if we're not |
@@ -277,7 +282,7 @@ static void strip_nums(game_state *state) { | |||
277 | memset(state->next, -1, state->n*sizeof(int)); | 282 | memset(state->next, -1, state->n*sizeof(int)); |
278 | memset(state->prev, -1, state->n*sizeof(int)); | 283 | memset(state->prev, -1, state->n*sizeof(int)); |
279 | memset(state->numsi, -1, (state->n+1)*sizeof(int)); | 284 | memset(state->numsi, -1, (state->n+1)*sizeof(int)); |
280 | dsf_init(state->dsf, state->n); | 285 | dsf_reinit(state->dsf); |
281 | } | 286 | } |
282 | 287 | ||
283 | static bool check_nums(game_state *orig, game_state *copy, bool only_immutable) | 288 | static bool check_nums(game_state *orig, game_state *copy, bool only_immutable) |
@@ -421,6 +426,8 @@ static const char *validate_params(const game_params *params, bool full) | |||
421 | { | 426 | { |
422 | if (params->w < 1) return "Width must be at least one"; | 427 | if (params->w < 1) return "Width must be at least one"; |
423 | if (params->h < 1) return "Height must be at least one"; | 428 | if (params->h < 1) return "Height must be at least one"; |
429 | if (params->w > INT_MAX / params->h) | ||
430 | return "Width times height must not be unreasonably large"; | ||
424 | if (full && params->w == 1 && params->h == 1) | 431 | if (full && params->w == 1 && params->h == 1) |
425 | /* The UI doesn't let us move these from unsolved to solved, | 432 | /* The UI doesn't let us move these from unsolved to solved, |
426 | * so we disallow generating (but not playing) them. */ | 433 | * so we disallow generating (but not playing) them. */ |
@@ -454,7 +461,7 @@ static game_state *blank_game(int w, int h) | |||
454 | state->flags = snewn(state->n, unsigned int); | 461 | state->flags = snewn(state->n, unsigned int); |
455 | state->next = snewn(state->n, int); | 462 | state->next = snewn(state->n, int); |
456 | state->prev = snewn(state->n, int); | 463 | state->prev = snewn(state->n, int); |
457 | state->dsf = snew_dsf(state->n); | 464 | state->dsf = dsf_new(state->n); |
458 | state->numsi = snewn(state->n+1, int); | 465 | state->numsi = snewn(state->n+1, int); |
459 | 466 | ||
460 | blank_game_into(state); | 467 | blank_game_into(state); |
@@ -475,7 +482,7 @@ static void dup_game_to(game_state *to, const game_state *from) | |||
475 | memcpy(to->next, from->next, to->n*sizeof(int)); | 482 | memcpy(to->next, from->next, to->n*sizeof(int)); |
476 | memcpy(to->prev, from->prev, to->n*sizeof(int)); | 483 | memcpy(to->prev, from->prev, to->n*sizeof(int)); |
477 | 484 | ||
478 | memcpy(to->dsf, from->dsf, to->n*sizeof(int)); | 485 | dsf_copy(to->dsf, from->dsf); |
479 | memcpy(to->numsi, from->numsi, (to->n+1)*sizeof(int)); | 486 | memcpy(to->numsi, from->numsi, (to->n+1)*sizeof(int)); |
480 | } | 487 | } |
481 | 488 | ||
@@ -493,7 +500,7 @@ static void free_game(game_state *state) | |||
493 | sfree(state->flags); | 500 | sfree(state->flags); |
494 | sfree(state->next); | 501 | sfree(state->next); |
495 | sfree(state->prev); | 502 | sfree(state->prev); |
496 | sfree(state->dsf); | 503 | dsf_free(state->dsf); |
497 | sfree(state->numsi); | 504 | sfree(state->numsi); |
498 | sfree(state); | 505 | sfree(state); |
499 | } | 506 | } |
@@ -1011,7 +1018,7 @@ static void connect_numbers(game_state *state) | |||
1011 | { | 1018 | { |
1012 | int i, di, dni; | 1019 | int i, di, dni; |
1013 | 1020 | ||
1014 | dsf_init(state->dsf, state->n); | 1021 | dsf_reinit(state->dsf); |
1015 | for (i = 0; i < state->n; i++) { | 1022 | for (i = 0; i < state->n; i++) { |
1016 | if (state->next[i] != -1) { | 1023 | if (state->next[i] != -1) { |
1017 | assert(state->prev[state->next[i]] == i); | 1024 | assert(state->prev[state->next[i]] == i); |
@@ -1028,8 +1035,8 @@ static void connect_numbers(game_state *state) | |||
1028 | 1035 | ||
1029 | static int compare_heads(const void *a, const void *b) | 1036 | static int compare_heads(const void *a, const void *b) |
1030 | { | 1037 | { |
1031 | struct head_meta *ha = (struct head_meta *)a; | 1038 | const struct head_meta *ha = (const struct head_meta *)a; |
1032 | struct head_meta *hb = (struct head_meta *)b; | 1039 | const struct head_meta *hb = (const struct head_meta *)b; |
1033 | 1040 | ||
1034 | /* Heads with preferred colours first... */ | 1041 | /* Heads with preferred colours first... */ |
1035 | if (ha->preference && !hb->preference) return -1; | 1042 | if (ha->preference && !hb->preference) return -1; |
@@ -1380,8 +1387,32 @@ struct game_ui { | |||
1380 | bool dragging, drag_is_from; | 1387 | bool dragging, drag_is_from; |
1381 | int sx, sy; /* grid coords of start cell */ | 1388 | int sx, sy; /* grid coords of start cell */ |
1382 | int dx, dy; /* pixel coords of drag posn */ | 1389 | int dx, dy; /* pixel coords of drag posn */ |
1390 | |||
1391 | /* | ||
1392 | * Trivial and foolish configurable option done on purest whim. | ||
1393 | * With this option enabled, the victory flash is done by rotating | ||
1394 | * each square in the opposite direction from its immediate | ||
1395 | * neighbours, so that they behave like a field of interlocking | ||
1396 | * gears. With it disabled, they all rotate in the same direction. | ||
1397 | * Choose for yourself which is more brain-twisting :-) | ||
1398 | */ | ||
1399 | bool gear_mode; | ||
1383 | }; | 1400 | }; |
1384 | 1401 | ||
1402 | static void legacy_prefs_override(struct game_ui *ui_out) | ||
1403 | { | ||
1404 | static bool initialised = false; | ||
1405 | static int gear_mode = -1; | ||
1406 | |||
1407 | if (!initialised) { | ||
1408 | initialised = true; | ||
1409 | gear_mode = getenv_bool("SIGNPOST_GEARS", -1); | ||
1410 | } | ||
1411 | |||
1412 | if (gear_mode != -1) | ||
1413 | ui_out->gear_mode = gear_mode; | ||
1414 | } | ||
1415 | |||
1385 | static game_ui *new_ui(const game_state *state) | 1416 | static game_ui *new_ui(const game_state *state) |
1386 | { | 1417 | { |
1387 | game_ui *ui = snew(game_ui); | 1418 | game_ui *ui = snew(game_ui); |
@@ -1390,11 +1421,14 @@ static game_ui *new_ui(const game_state *state) | |||
1390 | * copy to clone, there's code that needs fixing in game_redraw too. */ | 1421 | * copy to clone, there's code that needs fixing in game_redraw too. */ |
1391 | 1422 | ||
1392 | ui->cx = ui->cy = 0; | 1423 | ui->cx = ui->cy = 0; |
1393 | ui->cshow = false; | 1424 | ui->cshow = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
1394 | 1425 | ||
1395 | ui->dragging = false; | 1426 | ui->dragging = false; |
1396 | ui->sx = ui->sy = ui->dx = ui->dy = 0; | 1427 | ui->sx = ui->sy = ui->dx = ui->dy = 0; |
1397 | 1428 | ||
1429 | ui->gear_mode = false; | ||
1430 | legacy_prefs_override(ui); | ||
1431 | |||
1398 | return ui; | 1432 | return ui; |
1399 | } | 1433 | } |
1400 | 1434 | ||
@@ -1403,13 +1437,28 @@ static void free_ui(game_ui *ui) | |||
1403 | sfree(ui); | 1437 | sfree(ui); |
1404 | } | 1438 | } |
1405 | 1439 | ||
1406 | static char *encode_ui(const game_ui *ui) | 1440 | static config_item *get_prefs(game_ui *ui) |
1407 | { | 1441 | { |
1408 | return NULL; | 1442 | config_item *ret; |
1443 | |||
1444 | ret = snewn(2, config_item); | ||
1445 | |||
1446 | ret[0].name = "Victory rotation effect"; | ||
1447 | ret[0].kw = "flash-type"; | ||
1448 | ret[0].type = C_CHOICES; | ||
1449 | ret[0].u.choices.choicenames = ":Unidirectional:Meshing gears"; | ||
1450 | ret[0].u.choices.choicekws = ":unidirectional:gears"; | ||
1451 | ret[0].u.choices.selected = ui->gear_mode; | ||
1452 | |||
1453 | ret[1].name = NULL; | ||
1454 | ret[1].type = C_END; | ||
1455 | |||
1456 | return ret; | ||
1409 | } | 1457 | } |
1410 | 1458 | ||
1411 | static void decode_ui(game_ui *ui, const char *encoding) | 1459 | static void set_prefs(game_ui *ui, const config_item *cfg) |
1412 | { | 1460 | { |
1461 | ui->gear_mode = cfg[0].u.choices.selected; | ||
1413 | } | 1462 | } |
1414 | 1463 | ||
1415 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 1464 | static void game_changed_state(game_ui *ui, const game_state *oldstate, |
@@ -1421,6 +1470,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
1421 | } | 1470 | } |
1422 | } | 1471 | } |
1423 | 1472 | ||
1473 | static const char *current_key_label(const game_ui *ui, | ||
1474 | const game_state *state, int button) | ||
1475 | { | ||
1476 | if (IS_CURSOR_SELECT(button) && ui->cshow) { | ||
1477 | if (ui->dragging) { | ||
1478 | if (ui->drag_is_from) { | ||
1479 | if (isvalidmove(state, false, ui->sx, ui->sy, ui->cx, ui->cy)) | ||
1480 | return "To here"; | ||
1481 | } else { | ||
1482 | if (isvalidmove(state, false, ui->cx, ui->cy, ui->sx, ui->sy)) | ||
1483 | return "From here"; | ||
1484 | } | ||
1485 | return "Cancel"; | ||
1486 | } else { | ||
1487 | return button == CURSOR_SELECT ? "From here" : "To here"; | ||
1488 | } | ||
1489 | } | ||
1490 | return ""; | ||
1491 | } | ||
1492 | |||
1424 | struct game_drawstate { | 1493 | struct game_drawstate { |
1425 | int tilesize; | 1494 | int tilesize; |
1426 | bool started, solved; | 1495 | bool started, solved; |
@@ -1442,26 +1511,27 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1442 | char buf[80]; | 1511 | char buf[80]; |
1443 | 1512 | ||
1444 | if (IS_CURSOR_MOVE(button)) { | 1513 | if (IS_CURSOR_MOVE(button)) { |
1445 | move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, false); | 1514 | char *ret; |
1446 | ui->cshow = true; | 1515 | ret = move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, false, |
1516 | &ui->cshow); | ||
1447 | if (ui->dragging) { | 1517 | if (ui->dragging) { |
1448 | ui->dx = COORD(ui->cx) + TILE_SIZE/2; | 1518 | ui->dx = COORD(ui->cx) + TILE_SIZE/2; |
1449 | ui->dy = COORD(ui->cy) + TILE_SIZE/2; | 1519 | ui->dy = COORD(ui->cy) + TILE_SIZE/2; |
1450 | } | 1520 | } |
1451 | return UI_UPDATE; | 1521 | return ret; |
1452 | } else if (IS_CURSOR_SELECT(button)) { | 1522 | } else if (IS_CURSOR_SELECT(button)) { |
1453 | if (!ui->cshow) | 1523 | if (!ui->cshow) |
1454 | ui->cshow = true; | 1524 | ui->cshow = true; |
1455 | else if (ui->dragging) { | 1525 | else if (ui->dragging) { |
1456 | ui->dragging = false; | 1526 | ui->dragging = false; |
1457 | if (ui->sx == ui->cx && ui->sy == ui->cy) return UI_UPDATE; | 1527 | if (ui->sx == ui->cx && ui->sy == ui->cy) return MOVE_UI_UPDATE; |
1458 | if (ui->drag_is_from) { | 1528 | if (ui->drag_is_from) { |
1459 | if (!isvalidmove(state, false, ui->sx, ui->sy, ui->cx, ui->cy)) | 1529 | if (!isvalidmove(state, false, ui->sx, ui->sy, ui->cx, ui->cy)) |
1460 | return UI_UPDATE; | 1530 | return MOVE_UI_UPDATE; |
1461 | sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, ui->cx, ui->cy); | 1531 | sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, ui->cx, ui->cy); |
1462 | } else { | 1532 | } else { |
1463 | if (!isvalidmove(state, false, ui->cx, ui->cy, ui->sx, ui->sy)) | 1533 | if (!isvalidmove(state, false, ui->cx, ui->cy, ui->sx, ui->sy)) |
1464 | return UI_UPDATE; | 1534 | return MOVE_UI_UPDATE; |
1465 | sprintf(buf, "L%d,%d-%d,%d", ui->cx, ui->cy, ui->sx, ui->sy); | 1535 | sprintf(buf, "L%d,%d-%d,%d", ui->cx, ui->cy, ui->sx, ui->sy); |
1466 | } | 1536 | } |
1467 | return dupstr(buf); | 1537 | return dupstr(buf); |
@@ -1473,7 +1543,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1473 | ui->dy = COORD(ui->cy) + TILE_SIZE/2; | 1543 | ui->dy = COORD(ui->cy) + TILE_SIZE/2; |
1474 | ui->drag_is_from = (button == CURSOR_SELECT); | 1544 | ui->drag_is_from = (button == CURSOR_SELECT); |
1475 | } | 1545 | } |
1476 | return UI_UPDATE; | 1546 | return MOVE_UI_UPDATE; |
1477 | } | 1547 | } |
1478 | if (IS_MOUSE_DOWN(button)) { | 1548 | if (IS_MOUSE_DOWN(button)) { |
1479 | if (ui->cshow) { | 1549 | if (ui->cshow) { |
@@ -1502,19 +1572,19 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1502 | ui->dx = mx; | 1572 | ui->dx = mx; |
1503 | ui->dy = my; | 1573 | ui->dy = my; |
1504 | ui->cshow = false; | 1574 | ui->cshow = false; |
1505 | return UI_UPDATE; | 1575 | return MOVE_UI_UPDATE; |
1506 | } else if (IS_MOUSE_DRAG(button) && ui->dragging) { | 1576 | } else if (IS_MOUSE_DRAG(button) && ui->dragging) { |
1507 | ui->dx = mx; | 1577 | ui->dx = mx; |
1508 | ui->dy = my; | 1578 | ui->dy = my; |
1509 | return UI_UPDATE; | 1579 | return MOVE_UI_UPDATE; |
1510 | } else if (IS_MOUSE_RELEASE(button) && ui->dragging) { | 1580 | } else if (IS_MOUSE_RELEASE(button) && ui->dragging) { |
1511 | ui->dragging = false; | 1581 | ui->dragging = false; |
1512 | if (ui->sx == x && ui->sy == y) return UI_UPDATE; /* single click */ | 1582 | if (ui->sx == x && ui->sy == y) return MOVE_UI_UPDATE; /* single click */ |
1513 | 1583 | ||
1514 | if (!INGRID(state, x, y)) { | 1584 | if (!INGRID(state, x, y)) { |
1515 | int si = ui->sy*w+ui->sx; | 1585 | int si = ui->sy*w+ui->sx; |
1516 | if (state->prev[si] == -1 && state->next[si] == -1) | 1586 | if (state->prev[si] == -1 && state->next[si] == -1) |
1517 | return UI_UPDATE; | 1587 | return MOVE_UI_UPDATE; |
1518 | sprintf(buf, "%c%d,%d", | 1588 | sprintf(buf, "%c%d,%d", |
1519 | (int)(ui->drag_is_from ? 'C' : 'X'), ui->sx, ui->sy); | 1589 | (int)(ui->drag_is_from ? 'C' : 'X'), ui->sx, ui->sy); |
1520 | return dupstr(buf); | 1590 | return dupstr(buf); |
@@ -1522,11 +1592,11 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1522 | 1592 | ||
1523 | if (ui->drag_is_from) { | 1593 | if (ui->drag_is_from) { |
1524 | if (!isvalidmove(state, false, ui->sx, ui->sy, x, y)) | 1594 | if (!isvalidmove(state, false, ui->sx, ui->sy, x, y)) |
1525 | return UI_UPDATE; | 1595 | return MOVE_UI_UPDATE; |
1526 | sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, x, y); | 1596 | sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, x, y); |
1527 | } else { | 1597 | } else { |
1528 | if (!isvalidmove(state, false, x, y, ui->sx, ui->sy)) | 1598 | if (!isvalidmove(state, false, x, y, ui->sx, ui->sy)) |
1529 | return UI_UPDATE; | 1599 | return MOVE_UI_UPDATE; |
1530 | sprintf(buf, "L%d,%d-%d,%d", x, y, ui->sx, ui->sy); | 1600 | sprintf(buf, "L%d,%d-%d,%d", x, y, ui->sx, ui->sy); |
1531 | } | 1601 | } |
1532 | return dupstr(buf); | 1602 | return dupstr(buf); |
@@ -1535,7 +1605,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1535 | else if ((button == 'x' || button == 'X') && ui->cshow) { | 1605 | else if ((button == 'x' || button == 'X') && ui->cshow) { |
1536 | int si = ui->cy*w + ui->cx; | 1606 | int si = ui->cy*w + ui->cx; |
1537 | if (state->prev[si] == -1 && state->next[si] == -1) | 1607 | if (state->prev[si] == -1 && state->next[si] == -1) |
1538 | return UI_UPDATE; | 1608 | return MOVE_UI_UPDATE; |
1539 | sprintf(buf, "%c%d,%d", | 1609 | sprintf(buf, "%c%d,%d", |
1540 | (int)((button == 'x') ? 'C' : 'X'), ui->cx, ui->cy); | 1610 | (int)((button == 'x') ? 'C' : 'X'), ui->cx, ui->cy); |
1541 | return dupstr(buf); | 1611 | return dupstr(buf); |
@@ -1641,7 +1711,7 @@ static game_state *execute_move(const game_state *state, const char *move) | |||
1641 | */ | 1711 | */ |
1642 | 1712 | ||
1643 | static void game_compute_size(const game_params *params, int tilesize, | 1713 | static void game_compute_size(const game_params *params, int tilesize, |
1644 | int *x, int *y) | 1714 | const game_ui *ui, int *x, int *y) |
1645 | { | 1715 | { |
1646 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ | 1716 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ |
1647 | struct { int tilesize, order; } ads, *ds = &ads; | 1717 | struct { int tilesize, order; } ads, *ds = &ads; |
@@ -1825,9 +1895,8 @@ static void draw_star(drawing *dr, int cx, int cy, int rad, int npoints, | |||
1825 | coords = snewn(npoints * 2 * 2, int); | 1895 | coords = snewn(npoints * 2 * 2, int); |
1826 | 1896 | ||
1827 | for (n = 0; n < npoints * 2; n++) { | 1897 | for (n = 0; n < npoints * 2; n++) { |
1828 | /* hack to accomodate rockbox's concave polygon drawing */ | 1898 | a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset; |
1829 | a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset - PI / npoints; | 1899 | r = (n % 2) ? (double)rad/2.0 : (double)rad; |
1830 | r = (n % 2 == 0) ? (double)rad/2.0 : (double)rad; | ||
1831 | 1900 | ||
1832 | /* We're rotating the point at (0, -r) by a degrees */ | 1901 | /* We're rotating the point at (0, -r) by a degrees */ |
1833 | coords[2*n+0] = cx + (int)( r * sin(a)); | 1902 | coords[2*n+0] = cx + (int)( r * sin(a)); |
@@ -2082,7 +2151,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
2082 | if (!ds->started) { | 2151 | if (!ds->started) { |
2083 | int aw = TILE_SIZE * state->w; | 2152 | int aw = TILE_SIZE * state->w; |
2084 | int ah = TILE_SIZE * state->h; | 2153 | int ah = TILE_SIZE * state->h; |
2085 | draw_rect(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER, COL_BACKGROUND); | ||
2086 | draw_rect_outline(dr, BORDER - 1, BORDER - 1, aw + 2, ah + 2, COL_GRID); | 2154 | draw_rect_outline(dr, BORDER - 1, BORDER - 1, aw + 2, ah + 2, COL_GRID); |
2087 | draw_update(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER); | 2155 | draw_update(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER); |
2088 | } | 2156 | } |
@@ -2127,28 +2195,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
2127 | if (state->nums[i] != ds->nums[i] || | 2195 | if (state->nums[i] != ds->nums[i] || |
2128 | f != ds->f[i] || dirp != ds->dirp[i] || | 2196 | f != ds->f[i] || dirp != ds->dirp[i] || |
2129 | force || !ds->started) { | 2197 | force || !ds->started) { |
2130 | int sign; | 2198 | int sign = (ui->gear_mode ? 1 - 2 * ((x ^ y) & 1) : 1); |
2131 | { | ||
2132 | /* | ||
2133 | * Trivial and foolish configurable option done on | ||
2134 | * purest whim. With this option enabled, the | ||
2135 | * victory flash is done by rotating each square | ||
2136 | * in the opposite direction from its immediate | ||
2137 | * neighbours, so that they behave like a field of | ||
2138 | * interlocking gears. With it disabled, they all | ||
2139 | * rotate in the same direction. Choose for | ||
2140 | * yourself which is more brain-twisting :-) | ||
2141 | */ | ||
2142 | static int gear_mode = -1; | ||
2143 | if (gear_mode < 0) { | ||
2144 | char *env = getenv("SIGNPOST_GEARS"); | ||
2145 | gear_mode = (env && (env[0] == 'y' || env[0] == 'Y')); | ||
2146 | } | ||
2147 | if (gear_mode) | ||
2148 | sign = 1 - 2 * ((x ^ y) & 1); | ||
2149 | else | ||
2150 | sign = 1; | ||
2151 | } | ||
2152 | tile_redraw(dr, ds, | 2199 | tile_redraw(dr, ds, |
2153 | BORDER + x * TILE_SIZE, | 2200 | BORDER + x * TILE_SIZE, |
2154 | BORDER + y * TILE_SIZE, | 2201 | BORDER + y * TILE_SIZE, |
@@ -2206,21 +2253,18 @@ static int game_status(const game_state *state) | |||
2206 | return state->completed ? +1 : 0; | 2253 | return state->completed ? +1 : 0; |
2207 | } | 2254 | } |
2208 | 2255 | ||
2209 | static bool game_timing_state(const game_state *state, game_ui *ui) | 2256 | static void game_print_size(const game_params *params, const game_ui *ui, |
2210 | { | 2257 | float *x, float *y) |
2211 | return true; | ||
2212 | } | ||
2213 | |||
2214 | static void game_print_size(const game_params *params, float *x, float *y) | ||
2215 | { | 2258 | { |
2216 | int pw, ph; | 2259 | int pw, ph; |
2217 | 2260 | ||
2218 | game_compute_size(params, 1300, &pw, &ph); | 2261 | game_compute_size(params, 1300, ui, &pw, &ph); |
2219 | *x = pw / 100.0F; | 2262 | *x = pw / 100.0F; |
2220 | *y = ph / 100.0F; | 2263 | *y = ph / 100.0F; |
2221 | } | 2264 | } |
2222 | 2265 | ||
2223 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 2266 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, |
2267 | int tilesize) | ||
2224 | { | 2268 | { |
2225 | int ink = print_mono_colour(dr, 0); | 2269 | int ink = print_mono_colour(dr, 0); |
2226 | int x, y; | 2270 | int x, y; |
@@ -2273,12 +2317,14 @@ const struct game thegame = { | |||
2273 | free_game, | 2317 | free_game, |
2274 | true, solve_game, | 2318 | true, solve_game, |
2275 | true, game_can_format_as_text_now, game_text_format, | 2319 | true, game_can_format_as_text_now, game_text_format, |
2320 | get_prefs, set_prefs, | ||
2276 | new_ui, | 2321 | new_ui, |
2277 | free_ui, | 2322 | free_ui, |
2278 | encode_ui, | 2323 | NULL, /* encode_ui */ |
2279 | decode_ui, | 2324 | NULL, /* decode_ui */ |
2280 | NULL, /* game_request_keys */ | 2325 | NULL, /* game_request_keys */ |
2281 | game_changed_state, | 2326 | game_changed_state, |
2327 | current_key_label, | ||
2282 | interpret_move, | 2328 | interpret_move, |
2283 | execute_move, | 2329 | execute_move, |
2284 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, | 2330 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, |
@@ -2292,7 +2338,7 @@ const struct game thegame = { | |||
2292 | game_status, | 2338 | game_status, |
2293 | true, false, game_print_size, game_print, | 2339 | true, false, game_print_size, game_print, |
2294 | false, /* wants_statusbar */ | 2340 | false, /* wants_statusbar */ |
2295 | false, game_timing_state, | 2341 | false, NULL, /* timing_state */ |
2296 | REQUIRE_RBUTTON, /* flags */ | 2342 | REQUIRE_RBUTTON, /* flags */ |
2297 | }; | 2343 | }; |
2298 | 2344 | ||
@@ -2301,10 +2347,9 @@ const struct game thegame = { | |||
2301 | #include <time.h> | 2347 | #include <time.h> |
2302 | #include <stdarg.h> | 2348 | #include <stdarg.h> |
2303 | 2349 | ||
2304 | const char *quis = NULL; | 2350 | static const char *quis = NULL; |
2305 | int verbose = 0; | ||
2306 | 2351 | ||
2307 | void usage(FILE *out) { | 2352 | static void usage(FILE *out) { |
2308 | fprintf(out, "usage: %s [--stdin] [--soak] [--seed SEED] <params>|<game id>\n", quis); | 2353 | fprintf(out, "usage: %s [--stdin] [--soak] [--seed SEED] <params>|<game id>\n", quis); |
2309 | } | 2354 | } |
2310 | 2355 | ||
@@ -2405,7 +2450,7 @@ static void process_desc(char *id) | |||
2405 | thegame.free_params(p); | 2450 | thegame.free_params(p); |
2406 | } | 2451 | } |
2407 | 2452 | ||
2408 | int main(int argc, const char *argv[]) | 2453 | int main(int argc, char *argv[]) |
2409 | { | 2454 | { |
2410 | char *id = NULL, *desc, *aux = NULL; | 2455 | char *id = NULL, *desc, *aux = NULL; |
2411 | const char *err; | 2456 | const char *err; |