diff options
Diffstat (limited to 'apps/plugins/puzzles/src/palisade.c')
-rw-r--r-- | apps/plugins/puzzles/src/palisade.c | 174 |
1 files changed, 154 insertions, 20 deletions
diff --git a/apps/plugins/puzzles/src/palisade.c b/apps/plugins/puzzles/src/palisade.c index 811204b1fe..ecbbbb4d6f 100644 --- a/apps/plugins/puzzles/src/palisade.c +++ b/apps/plugins/puzzles/src/palisade.c | |||
@@ -868,18 +868,48 @@ static char *game_text_format(const game_state *state) | |||
868 | } | 868 | } |
869 | 869 | ||
870 | struct game_ui { | 870 | struct game_ui { |
871 | /* These are half-grid coordinates - (0,0) is the top left corner | ||
872 | * of the top left square; (1,1) is the center of the top left | ||
873 | * grid square. */ | ||
871 | int x, y; | 874 | int x, y; |
872 | bool show; | 875 | bool show; |
876 | |||
877 | bool legacy_cursor; | ||
873 | }; | 878 | }; |
874 | 879 | ||
875 | static game_ui *new_ui(const game_state *state) | 880 | static game_ui *new_ui(const game_state *state) |
876 | { | 881 | { |
877 | game_ui *ui = snew(game_ui); | 882 | game_ui *ui = snew(game_ui); |
878 | ui->x = ui->y = 0; | 883 | ui->x = ui->y = 1; |
879 | ui->show = getenv_bool("PUZZLES_SHOW_CURSOR", false); | 884 | ui->show = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
885 | ui->legacy_cursor = false; | ||
880 | return ui; | 886 | return ui; |
881 | } | 887 | } |
882 | 888 | ||
889 | static config_item *get_prefs(game_ui *ui) | ||
890 | { | ||
891 | config_item *cfg; | ||
892 | |||
893 | cfg = snewn(2, config_item); | ||
894 | |||
895 | cfg[0].name = "Cursor mode"; | ||
896 | cfg[0].kw = "cursor-mode"; | ||
897 | cfg[0].type = C_CHOICES; | ||
898 | cfg[0].u.choices.choicenames = ":Half-grid:Full-grid"; | ||
899 | cfg[0].u.choices.choicekws = ":half:full"; | ||
900 | cfg[0].u.choices.selected = ui->legacy_cursor; | ||
901 | |||
902 | cfg[1].name = NULL; | ||
903 | cfg[1].type = C_END; | ||
904 | |||
905 | return cfg; | ||
906 | } | ||
907 | |||
908 | static void set_prefs(game_ui *ui, const config_item *cfg) | ||
909 | { | ||
910 | ui->legacy_cursor = cfg[0].u.choices.selected; | ||
911 | } | ||
912 | |||
883 | static void free_ui(game_ui *ui) | 913 | static void free_ui(game_ui *ui) |
884 | { | 914 | { |
885 | sfree(ui); | 915 | sfree(ui); |
@@ -890,7 +920,7 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
890 | { | 920 | { |
891 | } | 921 | } |
892 | 922 | ||
893 | typedef unsigned short dsflags; | 923 | typedef int dsflags; |
894 | 924 | ||
895 | struct game_drawstate { | 925 | struct game_drawstate { |
896 | int tilesize; | 926 | int tilesize; |
@@ -921,9 +951,6 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
921 | 951 | ||
922 | if (OUT_OF_BOUNDS(gx, gy, w, h)) return NULL; | 952 | if (OUT_OF_BOUNDS(gx, gy, w, h)) return NULL; |
923 | 953 | ||
924 | ui->x = gx; | ||
925 | ui->y = gy; | ||
926 | |||
927 | /* find edge closest to click point */ | 954 | /* find edge closest to click point */ |
928 | possible &=~ (2*px < TILESIZE ? BORDER_R : BORDER_L); | 955 | possible &=~ (2*px < TILESIZE ? BORDER_R : BORDER_L); |
929 | possible &=~ (2*py < TILESIZE ? BORDER_D : BORDER_U); | 956 | possible &=~ (2*py < TILESIZE ? BORDER_D : BORDER_U); |
@@ -934,6 +961,9 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
934 | for (dir = 0; dir < 4 && BORDER(dir) != possible; ++dir); | 961 | for (dir = 0; dir < 4 && BORDER(dir) != possible; ++dir); |
935 | if (dir == 4) return NULL; /* there's not exactly one such edge */ | 962 | if (dir == 4) return NULL; /* there's not exactly one such edge */ |
936 | 963 | ||
964 | ui->x = min(max(2*gx + 1 + dx[dir], 1), 2*w-1); | ||
965 | ui->y = min(max(2*gy + 1 + dy[dir], 1), 2*h-1); | ||
966 | |||
937 | hx = gx + dx[dir]; | 967 | hx = gx + dx[dir]; |
938 | hy = gy + dy[dir]; | 968 | hy = gy + dy[dir]; |
939 | 969 | ||
@@ -963,17 +993,17 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
963 | } | 993 | } |
964 | 994 | ||
965 | if (IS_CURSOR_MOVE(button)) { | 995 | if (IS_CURSOR_MOVE(button)) { |
966 | if (control || shift) { | 996 | if(ui->legacy_cursor && (control || shift)) { |
967 | borderflag flag = 0, newflag; | 997 | borderflag flag = 0, newflag; |
968 | int dir, i = ui->y * w + ui->x; | 998 | int dir, i = (ui->y/2) * w + (ui->x/2); |
969 | ui->show = true; | 999 | ui->show = true; |
970 | x = ui->x; | 1000 | x = ui->x/2; |
971 | y = ui->y; | 1001 | y = ui->y/2; |
972 | move_cursor(button, &x, &y, w, h, false, NULL); | 1002 | move_cursor(button, &x, &y, w, h, false, NULL); |
973 | if (OUT_OF_BOUNDS(x, y, w, h)) return NULL; | 1003 | if (OUT_OF_BOUNDS(x, y, w, h)) return NULL; |
974 | 1004 | ||
975 | for (dir = 0; dir < 4; ++dir) | 1005 | for (dir = 0; dir < 4; ++dir) |
976 | if (dx[dir] == x - ui->x && dy[dir] == y - ui->y) break; | 1006 | if (dx[dir] == x - ui->x/2 && dy[dir] == y - ui->y/2) break; |
977 | if (dir == 4) return NULL; /* how the ... ?! */ | 1007 | if (dir == 4) return NULL; /* how the ... ?! */ |
978 | 1008 | ||
979 | if (control) flag |= BORDER(dir); | 1009 | if (control) flag |= BORDER(dir); |
@@ -987,9 +1017,67 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
987 | if (control) newflag |= BORDER(FLIP(dir)); | 1017 | if (control) newflag |= BORDER(FLIP(dir)); |
988 | if (shift) newflag |= DISABLED(BORDER(FLIP(dir))); | 1018 | if (shift) newflag |= DISABLED(BORDER(FLIP(dir))); |
989 | return string(80, "F%d,%d,%dF%d,%d,%d", | 1019 | return string(80, "F%d,%d,%dF%d,%d,%d", |
990 | ui->x, ui->y, flag, x, y, newflag); | 1020 | ui->x/2, ui->y/2, flag, x, y, newflag); |
991 | } else | 1021 | } else { |
992 | return move_cursor(button, &ui->x, &ui->y, w, h, false, &ui->show); | 1022 | /* TODO: Refactor this and other half-grid cursor games |
1023 | * (Tracks, etc.) */ | ||
1024 | int dx = (button == CURSOR_LEFT) ? -1 : ((button == CURSOR_RIGHT) ? +1 : 0); | ||
1025 | int dy = (button == CURSOR_DOWN) ? +1 : ((button == CURSOR_UP) ? -1 : 0); | ||
1026 | |||
1027 | if(ui->legacy_cursor) { | ||
1028 | dx *= 2; dy *= 2; | ||
1029 | |||
1030 | ui->x |= 1; | ||
1031 | ui->y |= 1; | ||
1032 | } | ||
1033 | |||
1034 | if (!ui->show) { | ||
1035 | ui->show = true; | ||
1036 | } | ||
1037 | |||
1038 | ui->x = min(max(ui->x + dx, 1), 2*w-1); | ||
1039 | ui->y = min(max(ui->y + dy, 1), 2*h-1); | ||
1040 | |||
1041 | return MOVE_UI_UPDATE; | ||
1042 | } | ||
1043 | } else if (IS_CURSOR_SELECT(button)) { | ||
1044 | int px = ui->x % 2, py = ui->y % 2; | ||
1045 | int gx = ui->x / 2, gy = ui->y / 2; | ||
1046 | int dir = (px == 0) ? 3 : 0; /* left = 3; up = 0 */ | ||
1047 | int hx = gx + dx[dir]; | ||
1048 | int hy = gy + dy[dir]; | ||
1049 | |||
1050 | int i = gy * w + gx; | ||
1051 | |||
1052 | if(!ui->show) { | ||
1053 | ui->show = true; | ||
1054 | return MOVE_UI_UPDATE; | ||
1055 | } | ||
1056 | |||
1057 | /* clicks on square corners and centers do nothing */ | ||
1058 | if (px == py) | ||
1059 | return MOVE_NO_EFFECT; | ||
1060 | |||
1061 | /* TODO: Refactor this and the mouse click handling code | ||
1062 | * above. */ | ||
1063 | switch ((button == CURSOR_SELECT2) | | ||
1064 | ((state->borders[i] & BORDER(dir)) >> dir << 1) | | ||
1065 | ((state->borders[i] & DISABLED(BORDER(dir))) >> dir >> 2)) { | ||
1066 | |||
1067 | case MAYBE_LEFT: | ||
1068 | case ON_LEFT: | ||
1069 | case ON_RIGHT: | ||
1070 | return string(80, "F%d,%d,%dF%d,%d,%d", | ||
1071 | gx, gy, BORDER(dir), | ||
1072 | hx, hy, BORDER(FLIP(dir))); | ||
1073 | |||
1074 | case MAYBE_RIGHT: | ||
1075 | case OFF_LEFT: | ||
1076 | case OFF_RIGHT: | ||
1077 | return string(80, "F%d,%d,%dF%d,%d,%d", | ||
1078 | gx, gy, DISABLED(BORDER(dir)), | ||
1079 | hx, hy, DISABLED(BORDER(FLIP(dir)))); | ||
1080 | } | ||
993 | } | 1081 | } |
994 | 1082 | ||
995 | return NULL; | 1083 | return NULL; |
@@ -1098,7 +1186,7 @@ static float *game_colours(frontend *fe, int *ncolours) | |||
1098 | #define F_ERROR_L BORDER_ERROR(BORDER_L) /* BIT(11) */ | 1186 | #define F_ERROR_L BORDER_ERROR(BORDER_L) /* BIT(11) */ |
1099 | #define F_ERROR_CLUE BIT(12) | 1187 | #define F_ERROR_CLUE BIT(12) |
1100 | #define F_FLASH BIT(13) | 1188 | #define F_FLASH BIT(13) |
1101 | #define F_CURSOR BIT(14) | 1189 | #define CONTAINS_CURSOR(x) ((x) << 14) |
1102 | 1190 | ||
1103 | static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | 1191 | static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) |
1104 | { | 1192 | { |
@@ -1132,9 +1220,6 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int r, int c, | |||
1132 | draw_rect(dr, x + WIDTH, y + WIDTH, TILESIZE - WIDTH, TILESIZE - WIDTH, | 1220 | draw_rect(dr, x + WIDTH, y + WIDTH, TILESIZE - WIDTH, TILESIZE - WIDTH, |
1133 | (flags & F_FLASH ? COL_FLASH : COL_BACKGROUND)); | 1221 | (flags & F_FLASH ? COL_FLASH : COL_BACKGROUND)); |
1134 | 1222 | ||
1135 | if (flags & F_CURSOR) | ||
1136 | draw_rect_corners(dr, x + CENTER, y + CENTER, TILESIZE / 3, COL_GRID); | ||
1137 | |||
1138 | if (clue != EMPTY) { | 1223 | if (clue != EMPTY) { |
1139 | char buf[2]; | 1224 | char buf[2]; |
1140 | buf[0] = '0' + clue; | 1225 | buf[0] = '0' + clue; |
@@ -1158,6 +1243,47 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int r, int c, | |||
1158 | draw_update(dr, x, y, TILESIZE + WIDTH, TILESIZE + WIDTH); | 1243 | draw_update(dr, x, y, TILESIZE + WIDTH, TILESIZE + WIDTH); |
1159 | } | 1244 | } |
1160 | 1245 | ||
1246 | static void draw_cursor(drawing *dr, game_drawstate *ds, | ||
1247 | int cur_x, int cur_y, bool legacy_cursor) | ||
1248 | { | ||
1249 | int off_x = cur_x % 2, off_y = cur_y % 2; | ||
1250 | |||
1251 | /* Figure out the tile coordinates corresponding to these cursor | ||
1252 | * coordinates. */ | ||
1253 | int x = MARGIN + TILESIZE * (cur_x / 2), y = MARGIN + TILESIZE * (cur_y / 2); | ||
1254 | |||
1255 | /* off_x and off_y are either 0 or 1. The possible cases are | ||
1256 | * therefore: | ||
1257 | * | ||
1258 | * (0, 0): the cursor is in the top left corner of the tile. | ||
1259 | * (0, 1): the cursor is on the left border of the tile. | ||
1260 | * (1, 0): the cursor is on the top border of the tile. | ||
1261 | * (1, 1): the cursor is in the center of the tile. | ||
1262 | */ | ||
1263 | enum { TOP_LEFT_CORNER, LEFT_BORDER, TOP_BORDER, TILE_CENTER } cur_type = (off_x << 1) + off_y; | ||
1264 | |||
1265 | int center_x = x + ((off_x == 0) ? WIDTH/2 : CENTER), | ||
1266 | center_y = y + ((off_y == 0) ? WIDTH/2 : CENTER); | ||
1267 | |||
1268 | struct { int w, h; } cursor_dimensions[] = { | ||
1269 | { TILESIZE / 3, TILESIZE / 3 }, /* top left corner */ | ||
1270 | { TILESIZE / 3, 2 * TILESIZE / 3}, /* left border */ | ||
1271 | { 2 * TILESIZE / 3, TILESIZE / 3}, /* top border */ | ||
1272 | { 2 * TILESIZE / 3, 2 * TILESIZE / 3 } /* center */ | ||
1273 | }, *dims = cursor_dimensions + cur_type; | ||
1274 | |||
1275 | if(legacy_cursor && cur_type == TILE_CENTER) | ||
1276 | draw_rect_corners(dr, center_x, center_y, TILESIZE / 3, COL_GRID); | ||
1277 | else | ||
1278 | draw_rect_outline(dr, | ||
1279 | center_x - dims->w / 2, center_y - dims->h / 2, | ||
1280 | dims->w, dims->h, COL_GRID); | ||
1281 | |||
1282 | draw_update(dr, | ||
1283 | center_x - dims->w / 2, center_y - dims->h / 2, | ||
1284 | dims->w, dims->h); | ||
1285 | } | ||
1286 | |||
1161 | #define FLASH_TIME 0.7F | 1287 | #define FLASH_TIME 0.7F |
1162 | 1288 | ||
1163 | static void game_redraw(drawing *dr, game_drawstate *ds, | 1289 | static void game_redraw(drawing *dr, game_drawstate *ds, |
@@ -1203,8 +1329,13 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
1203 | if (clue != EMPTY && (on > clue || clue > 4 - off)) | 1329 | if (clue != EMPTY && (on > clue || clue > 4 - off)) |
1204 | flags |= F_ERROR_CLUE; | 1330 | flags |= F_ERROR_CLUE; |
1205 | 1331 | ||
1206 | if (ui->show && ui->x == c && ui->y == r) | 1332 | if (ui->show) { |
1207 | flags |= F_CURSOR; | 1333 | int u, v; |
1334 | for(u = 0; u < 3; u++) | ||
1335 | for(v = 0; v < 3; v++) | ||
1336 | if(ui->x == 2*c+u && ui->y == 2*r+v) | ||
1337 | flags |= CONTAINS_CURSOR(BIT(3*u+v)); | ||
1338 | } | ||
1208 | 1339 | ||
1209 | /* border errors */ | 1340 | /* border errors */ |
1210 | for (dir = 0; dir < 4; ++dir) { | 1341 | for (dir = 0; dir < 4; ++dir) { |
@@ -1248,6 +1379,9 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
1248 | draw_tile(dr, ds, r, c, ds->grid[i], clue); | 1379 | draw_tile(dr, ds, r, c, ds->grid[i], clue); |
1249 | } | 1380 | } |
1250 | 1381 | ||
1382 | if (ui->show) | ||
1383 | draw_cursor(dr, ds, ui->x, ui->y, ui->legacy_cursor); | ||
1384 | |||
1251 | dsf_free(black_border_dsf); | 1385 | dsf_free(black_border_dsf); |
1252 | dsf_free(yellow_border_dsf); | 1386 | dsf_free(yellow_border_dsf); |
1253 | } | 1387 | } |
@@ -1375,7 +1509,7 @@ const struct game thegame = { | |||
1375 | free_game, | 1509 | free_game, |
1376 | true, solve_game, | 1510 | true, solve_game, |
1377 | true, game_can_format_as_text_now, game_text_format, | 1511 | true, game_can_format_as_text_now, game_text_format, |
1378 | NULL, NULL, /* get_prefs, set_prefs */ | 1512 | get_prefs, set_prefs, /* get_prefs, set_prefs */ |
1379 | new_ui, | 1513 | new_ui, |
1380 | free_ui, | 1514 | free_ui, |
1381 | NULL, /* encode_ui */ | 1515 | NULL, /* encode_ui */ |