diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/plugins/puzzles/SOURCES.games | 2 | ||||
-rw-r--r-- | apps/plugins/puzzles/untangle.c | 155 |
2 files changed, 151 insertions, 6 deletions
diff --git a/apps/plugins/puzzles/SOURCES.games b/apps/plugins/puzzles/SOURCES.games index 26f74e4005..b16cf0f70d 100644 --- a/apps/plugins/puzzles/SOURCES.games +++ b/apps/plugins/puzzles/SOURCES.games | |||
@@ -38,7 +38,7 @@ twiddle.c | |||
38 | /*undead.c*/ | 38 | /*undead.c*/ |
39 | /*unequal.c*/ | 39 | /*unequal.c*/ |
40 | unruly.c | 40 | unruly.c |
41 | /*untangle.c*/ | 41 | untangle.c |
42 | 42 | ||
43 | /* disabled for now */ | 43 | /* disabled for now */ |
44 | /*unfinished/group.c*/ | 44 | /*unfinished/group.c*/ |
diff --git a/apps/plugins/puzzles/untangle.c b/apps/plugins/puzzles/untangle.c index 67da03d8c0..839013809b 100644 --- a/apps/plugins/puzzles/untangle.c +++ b/apps/plugins/puzzles/untangle.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #define CIRCLE_RADIUS 6 | 39 | #define CIRCLE_RADIUS 6 |
40 | #define DRAG_THRESHOLD (CIRCLE_RADIUS * 2) | 40 | #define DRAG_THRESHOLD (CIRCLE_RADIUS * 2) |
41 | #define PREFERRED_TILESIZE 64 | 41 | #define PREFERRED_TILESIZE 64 |
42 | #define CURSOR_GRANULARITY 5 | ||
42 | 43 | ||
43 | #define FLASH_TIME 0.30F | 44 | #define FLASH_TIME 0.30F |
44 | #define ANIM_TIME 0.13F | 45 | #define ANIM_TIME 0.13F |
@@ -54,6 +55,7 @@ enum { | |||
54 | COL_OUTLINE, | 55 | COL_OUTLINE, |
55 | COL_POINT, | 56 | COL_POINT, |
56 | COL_DRAGPOINT, | 57 | COL_DRAGPOINT, |
58 | COL_CURSORPOINT, | ||
57 | COL_NEIGHBOUR, | 59 | COL_NEIGHBOUR, |
58 | COL_FLASH1, | 60 | COL_FLASH1, |
59 | COL_FLASH2, | 61 | COL_FLASH2, |
@@ -1038,6 +1040,11 @@ static char *game_text_format(const game_state *state) | |||
1038 | 1040 | ||
1039 | struct game_ui { | 1041 | struct game_ui { |
1040 | int dragpoint; /* point being dragged; -1 if none */ | 1042 | int dragpoint; /* point being dragged; -1 if none */ |
1043 | |||
1044 | int cursorpoint; /* point being highlighted, but | ||
1045 | * not dragged by the cursor, | ||
1046 | * again -1 if none */ | ||
1047 | |||
1041 | point newpoint; /* where it's been dragged to so far */ | 1048 | point newpoint; /* where it's been dragged to so far */ |
1042 | int just_dragged; /* reset in game_changed_state */ | 1049 | int just_dragged; /* reset in game_changed_state */ |
1043 | int just_moved; /* _set_ in game_changed_state */ | 1050 | int just_moved; /* _set_ in game_changed_state */ |
@@ -1048,6 +1055,7 @@ static game_ui *new_ui(const game_state *state) | |||
1048 | { | 1055 | { |
1049 | game_ui *ui = snew(game_ui); | 1056 | game_ui *ui = snew(game_ui); |
1050 | ui->dragpoint = -1; | 1057 | ui->dragpoint = -1; |
1058 | ui->cursorpoint = -1; | ||
1051 | ui->just_moved = ui->just_dragged = FALSE; | 1059 | ui->just_moved = ui->just_dragged = FALSE; |
1052 | return ui; | 1060 | return ui; |
1053 | } | 1061 | } |
@@ -1076,7 +1084,7 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
1076 | 1084 | ||
1077 | struct game_drawstate { | 1085 | struct game_drawstate { |
1078 | long tilesize; | 1086 | long tilesize; |
1079 | int bg, dragpoint; | 1087 | int bg, dragpoint, cursorpoint; |
1080 | long *x, *y; | 1088 | long *x, *y; |
1081 | }; | 1089 | }; |
1082 | 1090 | ||
@@ -1150,6 +1158,135 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1150 | ui->just_dragged = TRUE; | 1158 | ui->just_dragged = TRUE; |
1151 | return dupstr(buf); | 1159 | return dupstr(buf); |
1152 | } | 1160 | } |
1161 | else if(IS_CURSOR_MOVE(button)) | ||
1162 | { | ||
1163 | if(ui->dragpoint < 0) | ||
1164 | { | ||
1165 | if(ui->cursorpoint < 0) | ||
1166 | { | ||
1167 | ui->cursorpoint = 0; | ||
1168 | return ""; | ||
1169 | } | ||
1170 | |||
1171 | /* We're selecting a point here. */ | ||
1172 | /* Search all the points and find the closest one (2-D) in | ||
1173 | * the given direction. */ | ||
1174 | int i, best; | ||
1175 | long bestd; | ||
1176 | |||
1177 | /* | ||
1178 | * Begin drag. We drag the vertex _nearest_ to the pointer, | ||
1179 | * just in case one is nearly on top of another and we want | ||
1180 | * to drag the latter. However, we drag nothing at all if | ||
1181 | * the nearest vertex is outside DRAG_THRESHOLD. | ||
1182 | */ | ||
1183 | best = -1; | ||
1184 | bestd = 0; | ||
1185 | |||
1186 | for (i = 0; i < n; i++) { | ||
1187 | if(i == ui->cursorpoint) | ||
1188 | continue; | ||
1189 | |||
1190 | long px = state->pts[i].x * ds->tilesize / state->pts[i].d; | ||
1191 | long py = state->pts[i].y * ds->tilesize / state->pts[i].d; | ||
1192 | long dx = px - state->pts[ui->cursorpoint].x * ds->tilesize / state->pts[ui->cursorpoint].d; | ||
1193 | long dy = py - state->pts[ui->cursorpoint].y * ds->tilesize / state->pts[ui->cursorpoint].d; | ||
1194 | long d = dx*dx + dy*dy; | ||
1195 | |||
1196 | /* Figure out if this point falls into a 90 degree | ||
1197 | * range extending from the current point */ | ||
1198 | |||
1199 | float angle = atan2(-dy, dx); /* adjust for raster coordinates */ | ||
1200 | |||
1201 | /* offset to [0..2*PI] */ | ||
1202 | if(angle < 0) | ||
1203 | angle += 2*PI; | ||
1204 | |||
1205 | int right_direction = FALSE; | ||
1206 | |||
1207 | if((button == CURSOR_UP && (1*PI/4 <= angle && angle <= 3*PI/4)) || | ||
1208 | (button == CURSOR_LEFT && (3*PI/4 <= angle && angle <= 5*PI/4)) || | ||
1209 | (button == CURSOR_DOWN && (5*PI/4 <= angle && angle <= 7*PI/4)) || | ||
1210 | (button == CURSOR_RIGHT && (angle >= 7*PI/4 || angle <= 1*PI/4))) | ||
1211 | right_direction = TRUE; | ||
1212 | |||
1213 | if ((best == -1 || bestd > d) && right_direction) { | ||
1214 | best = i; | ||
1215 | bestd = d; | ||
1216 | } | ||
1217 | } | ||
1218 | |||
1219 | if(best >= 0) | ||
1220 | { | ||
1221 | ui->cursorpoint = best; | ||
1222 | return ""; | ||
1223 | } | ||
1224 | } | ||
1225 | else if(ui->dragpoint >= 0) | ||
1226 | { | ||
1227 | /* dragging */ | ||
1228 | switch(button) | ||
1229 | { | ||
1230 | case CURSOR_UP: | ||
1231 | ui->newpoint.y -= ds->tilesize / CURSOR_GRANULARITY; | ||
1232 | return ""; | ||
1233 | case CURSOR_DOWN: | ||
1234 | ui->newpoint.y += ds->tilesize / CURSOR_GRANULARITY; | ||
1235 | return ""; | ||
1236 | case CURSOR_LEFT: | ||
1237 | ui->newpoint.x -= ds->tilesize / CURSOR_GRANULARITY; | ||
1238 | return ""; | ||
1239 | case CURSOR_RIGHT: | ||
1240 | ui->newpoint.x += ds->tilesize / CURSOR_GRANULARITY; | ||
1241 | return ""; | ||
1242 | default: | ||
1243 | break; | ||
1244 | } | ||
1245 | } | ||
1246 | } | ||
1247 | else if(IS_CURSOR_SELECT(button)) | ||
1248 | { | ||
1249 | if(ui->dragpoint < 0 && ui->cursorpoint >= 0) | ||
1250 | { | ||
1251 | /* begin drag */ | ||
1252 | ui->dragpoint = ui->cursorpoint; | ||
1253 | ui->cursorpoint = -1; | ||
1254 | ui->newpoint.x = state->pts[ui->dragpoint].x * ds->tilesize / state->pts[ui->dragpoint].d; | ||
1255 | ui->newpoint.y = state->pts[ui->dragpoint].y * ds->tilesize / state->pts[ui->dragpoint].d; | ||
1256 | ui->newpoint.d = ds->tilesize; | ||
1257 | return ""; | ||
1258 | } | ||
1259 | else if(ui->dragpoint >= 0) | ||
1260 | { | ||
1261 | /* end drag */ | ||
1262 | int p = ui->dragpoint; | ||
1263 | char buf[80]; | ||
1264 | |||
1265 | ui->cursorpoint = ui->dragpoint; | ||
1266 | ui->dragpoint = -1; /* terminate drag, no matter what */ | ||
1267 | |||
1268 | /* | ||
1269 | * First, see if we're within range. The user can cancel a | ||
1270 | * drag by dragging the point right off the window. | ||
1271 | */ | ||
1272 | if (ui->newpoint.x < 0 || | ||
1273 | ui->newpoint.x >= (long)state->w*ui->newpoint.d || | ||
1274 | ui->newpoint.y < 0 || | ||
1275 | ui->newpoint.y >= (long)state->h*ui->newpoint.d) | ||
1276 | return ""; | ||
1277 | |||
1278 | /* | ||
1279 | * We aren't cancelling the drag. Construct a move string | ||
1280 | * indicating where this point is going to. | ||
1281 | */ | ||
1282 | sprintf(buf, "P%d:%ld,%ld/%ld", p, | ||
1283 | ui->newpoint.x, ui->newpoint.y, ui->newpoint.d); | ||
1284 | ui->just_dragged = TRUE; | ||
1285 | return dupstr(buf); | ||
1286 | } | ||
1287 | else if(ui->cursorpoint < 0) | ||
1288 | ui->cursorpoint = 0; | ||
1289 | } | ||
1153 | 1290 | ||
1154 | return NULL; | 1291 | return NULL; |
1155 | } | 1292 | } |
@@ -1241,6 +1378,10 @@ static float *game_colours(frontend *fe, int *ncolours) | |||
1241 | ret[COL_DRAGPOINT * 3 + 1] = 1.0F; | 1378 | ret[COL_DRAGPOINT * 3 + 1] = 1.0F; |
1242 | ret[COL_DRAGPOINT * 3 + 2] = 1.0F; | 1379 | ret[COL_DRAGPOINT * 3 + 2] = 1.0F; |
1243 | 1380 | ||
1381 | ret[COL_CURSORPOINT * 3 + 0] = 0.5F; | ||
1382 | ret[COL_CURSORPOINT * 3 + 1] = 0.5F; | ||
1383 | ret[COL_CURSORPOINT * 3 + 2] = 0.5F; | ||
1384 | |||
1244 | ret[COL_NEIGHBOUR * 3 + 0] = 1.0F; | 1385 | ret[COL_NEIGHBOUR * 3 + 0] = 1.0F; |
1245 | ret[COL_NEIGHBOUR * 3 + 1] = 0.0F; | 1386 | ret[COL_NEIGHBOUR * 3 + 1] = 0.0F; |
1246 | ret[COL_NEIGHBOUR * 3 + 2] = 0.0F; | 1387 | ret[COL_NEIGHBOUR * 3 + 2] = 0.0F; |
@@ -1269,6 +1410,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) | |||
1269 | ds->x[i] = ds->y[i] = -1; | 1410 | ds->x[i] = ds->y[i] = -1; |
1270 | ds->bg = -1; | 1411 | ds->bg = -1; |
1271 | ds->dragpoint = -1; | 1412 | ds->dragpoint = -1; |
1413 | ds->cursorpoint = -1; | ||
1272 | 1414 | ||
1273 | return ds; | 1415 | return ds; |
1274 | } | 1416 | } |
@@ -1345,7 +1487,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
1345 | ds->y[i] = y; | 1487 | ds->y[i] = y; |
1346 | } | 1488 | } |
1347 | 1489 | ||
1348 | if (ds->bg == bg && ds->dragpoint == ui->dragpoint && !points_moved) | 1490 | if (ds->bg == bg && ds->dragpoint == ui->dragpoint && ds->cursorpoint == ui->cursorpoint && !points_moved) |
1349 | return; /* nothing to do */ | 1491 | return; /* nothing to do */ |
1350 | 1492 | ||
1351 | ds->dragpoint = ui->dragpoint; | 1493 | ds->dragpoint = ui->dragpoint; |
@@ -1373,15 +1515,18 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
1373 | * When dragging, we should not only vary the colours, but | 1515 | * When dragging, we should not only vary the colours, but |
1374 | * leave the point being dragged until last. | 1516 | * leave the point being dragged until last. |
1375 | */ | 1517 | */ |
1376 | for (j = 0; j < 3; j++) { | 1518 | for (j = 0; j < 4; j++) { |
1377 | int thisc = (j == 0 ? COL_POINT : | 1519 | int thisc = (j == 0 ? COL_POINT : |
1378 | j == 1 ? COL_NEIGHBOUR : COL_DRAGPOINT); | 1520 | (j == 1 ? COL_NEIGHBOUR : |
1521 | j == 2 ? COL_CURSORPOINT : COL_DRAGPOINT)); | ||
1379 | for (i = 0; i < state->params.n; i++) { | 1522 | for (i = 0; i < state->params.n; i++) { |
1380 | int c; | 1523 | int c; |
1381 | 1524 | ||
1382 | if (ui->dragpoint == i) { | 1525 | if (ui->dragpoint == i) { |
1383 | c = COL_DRAGPOINT; | 1526 | c = COL_DRAGPOINT; |
1384 | } else if (ui->dragpoint >= 0 && | 1527 | } else if(ui->cursorpoint == i) { |
1528 | c = COL_CURSORPOINT; | ||
1529 | } else if (ui->dragpoint >= 0 && | ||
1385 | isedge(state->graph->edges, ui->dragpoint, i)) { | 1530 | isedge(state->graph->edges, ui->dragpoint, i)) { |
1386 | c = COL_NEIGHBOUR; | 1531 | c = COL_NEIGHBOUR; |
1387 | } else { | 1532 | } else { |