From f0ee053c2a4a214a0e77b22462d0c42806d0462b Mon Sep 17 00:00:00 2001 From: jacob Date: Thu, 26 May 2005 13:40:38 +0000 Subject: [PATCH] Add origin-shifting (Shift+cursors) and source-shifting (Ctrl+cursors) to Net. (Adding modifier+cursors handling has had minor knock-on effects on the other puzzles, so that they can continue to ignore modifiers.) (An unfortunate side effect of this is some artifacts in exterior barrier drawing; notably, a disconnected corner can now appear at the corner of the grid under some circumstances. I haven't found a satisfactory way round this yet.) git-svn-id: svn://svn.tartarus.org/sgt/puzzles@5844 cda61777-01e9-0310-a592-d414129be87e --- cube.c | 2 + fifteen.c | 2 + gtk.c | 10 ++- net.c | 265 +++++++++++++++++++++++++++++++++++------------------------- netslide.c | 2 + pattern.c | 2 + puzzles.but | 15 ++++ puzzles.h | 5 +- rect.c | 2 + sixteen.c | 1 + solo.c | 2 +- twiddle.c | 2 + windows.c | 20 +++-- 13 files changed, 208 insertions(+), 122 deletions(-) diff --git a/cube.c b/cube.c index 12e3ece..ddf5527 100644 --- a/cube.c +++ b/cube.c @@ -1013,6 +1013,8 @@ static game_state *make_move(game_state *from, game_ui *ui, int i, j, dest, mask; struct solid *poly; + button = button & (~MOD_MASK | MOD_NUM_KEYPAD); + /* * All moves are made with the cursor keys or numeric keypad. */ diff --git a/fifteen.c b/fifteen.c index 9d138b1..700c26c 100644 --- a/fifteen.c +++ b/fifteen.c @@ -456,6 +456,8 @@ static game_state *make_move(game_state *from, game_ui *ui, int gx, gy, dx, dy, ux, uy, up, p; game_state *ret; + button &= ~MOD_MASK; + gx = X(from, from->gap_pos); gy = Y(from, from->gap_pos); diff --git a/gtk.c b/gtk.c index ab10a11..3fdfc87 100644 --- a/gtk.c +++ b/gtk.c @@ -317,24 +317,26 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { frontend *fe = (frontend *)data; int keyval; + int shift = (event->state & GDK_SHIFT_MASK) ? MOD_SHFT : 0; + int ctrl = (event->state & GDK_CONTROL_MASK) ? MOD_CTRL : 0; if (!fe->pixmap) return TRUE; if (event->keyval == GDK_Up) - keyval = CURSOR_UP; + keyval = shift | ctrl | CURSOR_UP; else if (event->keyval == GDK_KP_Up || event->keyval == GDK_KP_8) keyval = MOD_NUM_KEYPAD | '8'; else if (event->keyval == GDK_Down) - keyval = CURSOR_DOWN; + keyval = shift | ctrl | CURSOR_DOWN; else if (event->keyval == GDK_KP_Down || event->keyval == GDK_KP_2) keyval = MOD_NUM_KEYPAD | '2'; else if (event->keyval == GDK_Left) - keyval = CURSOR_LEFT; + keyval = shift | ctrl | CURSOR_LEFT; else if (event->keyval == GDK_KP_Left || event->keyval == GDK_KP_4) keyval = MOD_NUM_KEYPAD | '4'; else if (event->keyval == GDK_Right) - keyval = CURSOR_RIGHT; + keyval = shift | ctrl | CURSOR_RIGHT; else if (event->keyval == GDK_KP_Right || event->keyval == GDK_KP_6) keyval = MOD_NUM_KEYPAD | '6'; else if (event->keyval == GDK_KP_Home || event->keyval == GDK_KP_7) diff --git a/net.c b/net.c index db8c3c4..59effe6 100644 --- a/net.c +++ b/net.c @@ -57,6 +57,13 @@ #define ROTATE_TIME 0.13F #define FLASH_FRAME 0.07F +/* Transform physical coords to game coords using game_drawstate ds */ +#define GX(x) (((x) + ds->org_x) % ds->width) +#define GY(y) (((y) + ds->org_y) % ds->height) +/* ...and game coords to physical coords */ +#define RX(x) (((x) + ds->width - ds->org_x) % ds->width) +#define RY(y) (((y) + ds->height - ds->org_y) % ds->height) + enum { COL_BACKGROUND, COL_LOCKED, @@ -82,7 +89,7 @@ struct game_aux_info { }; struct game_state { - int width, height, cx, cy, wrapping, completed; + int width, height, wrapping, completed; int last_rotate_x, last_rotate_y, last_rotate_dir; int used_solve, just_used_solve; unsigned char *tiles; @@ -1570,8 +1577,6 @@ static game_state *new_game(game_params *params, char *desc) state = snew(game_state); w = state->width = params->width; h = state->height = params->height; - state->cx = state->width / 2; - state->cy = state->height / 2; state->wrapping = params->wrapping; state->last_rotate_dir = state->last_rotate_x = state->last_rotate_y = 0; state->completed = state->used_solve = state->just_used_solve = FALSE; @@ -1623,6 +1628,22 @@ static game_state *new_game(game_params *params, char *desc) barrier(state, 0, y) |= L; barrier(state, state->width-1, y) |= R; } + } else { + /* + * We check whether this is de-facto a non-wrapping game + * despite the parameters, in case we were passed the + * description of a non-wrapping game. This is so that we + * can change some aspects of the UI behaviour. + */ + state->wrapping = FALSE; + for (x = 0; x < state->width; x++) + if (!(barrier(state, x, 0) & U) || + !(barrier(state, x, state->height-1) & D)) + state->wrapping = TRUE; + for (y = 0; y < state->width; y++) + if (!(barrier(state, 0, y) & L) || + !(barrier(state, state->width-1, y) & R)) + state->wrapping = TRUE; } /* @@ -1644,30 +1665,20 @@ static game_state *new_game(game_params *params, char *desc) if (barrier(state, x, y) & dir2) corner = TRUE; - x1 = x + X(dir), y1 = y + Y(dir); - if (x1 >= 0 && x1 < state->width && - y1 >= 0 && y1 < state->height && - (barrier(state, x1, y1) & dir2)) + OFFSET(x1, y1, x, y, dir, state); + if (barrier(state, x1, y1) & dir2) corner = TRUE; - x2 = x + X(dir2), y2 = y + Y(dir2); - if (x2 >= 0 && x2 < state->width && - y2 >= 0 && y2 < state->height && - (barrier(state, x2, y2) & dir)) + OFFSET(x2, y2, x, y, dir2, state); + if (barrier(state, x2, y2) & dir) corner = TRUE; if (corner) { barrier(state, x, y) |= (dir << 4); - if (x1 >= 0 && x1 < state->width && - y1 >= 0 && y1 < state->height) - barrier(state, x1, y1) |= (A(dir) << 4); - if (x2 >= 0 && x2 < state->width && - y2 >= 0 && y2 < state->height) - barrier(state, x2, y2) |= (C(dir) << 4); - x3 = x + X(dir) + X(dir2), y3 = y + Y(dir) + Y(dir2); - if (x3 >= 0 && x3 < state->width && - y3 >= 0 && y3 < state->height) - barrier(state, x3, y3) |= (F(dir) << 4); + barrier(state, x1, y1) |= (A(dir) << 4); + barrier(state, x2, y2) |= (C(dir) << 4); + OFFSET(x3, y3, x1, y1, dir2, state); + barrier(state, x3, y3) |= (F(dir) << 4); } } } @@ -1683,8 +1694,6 @@ static game_state *dup_game(game_state *state) ret = snew(game_state); ret->width = state->width; ret->height = state->height; - ret->cx = state->cx; - ret->cy = state->cy; ret->wrapping = state->wrapping; ret->completed = state->completed; ret->used_solve = state->used_solve; @@ -1748,7 +1757,7 @@ static char *game_text_format(game_state *state) * completed - just call this function and see whether every square * is marked active. */ -static unsigned char *compute_active(game_state *state) +static unsigned char *compute_active(game_state *state, int cx, int cy) { unsigned char *active; tree234 *todo; @@ -1762,8 +1771,8 @@ static unsigned char *compute_active(game_state *state) * xyd_cmp and just store direction 0 every time. */ todo = newtree234(xyd_cmp_nc); - index(state, active, state->cx, state->cy) = ACTIVE; - add234(todo, new_xyd(state->cx, state->cy, 0)); + index(state, active, cx, cy) = ACTIVE; + add234(todo, new_xyd(cx, cy, 0)); while ( (xyd = delpos234(todo, 0)) != NULL) { int x1, y1, d1, x2, y2, d2; @@ -1799,6 +1808,8 @@ static unsigned char *compute_active(game_state *state) } struct game_ui { + int org_x, org_y; /* origin */ + int cx, cy; /* source tile (game coordinates) */ int cur_x, cur_y; int cur_visible; random_state *rs; /* used for jumbling */ @@ -1809,8 +1820,9 @@ static game_ui *new_ui(game_state *state) void *seed; int seedsize; game_ui *ui = snew(game_ui); - ui->cur_x = state->width / 2; - ui->cur_y = state->height / 2; + ui->org_x = ui->org_y = 0; + ui->cur_x = ui->cx = state->width / 2; + ui->cur_y = ui->cy = state->height / 2; ui->cur_visible = FALSE; get_random_seed(&seed, &seedsize); ui->rs = random_init(seed, seedsize); @@ -1833,7 +1845,9 @@ static game_state *make_move(game_state *state, game_ui *ui, { game_state *ret, *nullret; int tx, ty, orig; + int shift = button & MOD_SHFT, ctrl = button & MOD_CTRL; + button &= ~MOD_MASK; nullret = NULL; if (button == LEFT_BUTTON || @@ -1856,23 +1870,44 @@ static game_state *make_move(game_state *state, game_ui *ui, ty = y / TILE_SIZE; if (tx >= state->width || ty >= state->height) return nullret; + /* Transform from physical to game coords */ + tx = (tx + ui->org_x) % state->width; + ty = (ty + ui->org_y) % state->height; if (x % TILE_SIZE >= TILE_SIZE - TILE_BORDER || y % TILE_SIZE >= TILE_SIZE - TILE_BORDER) return nullret; } else if (button == CURSOR_UP || button == CURSOR_DOWN || button == CURSOR_RIGHT || button == CURSOR_LEFT) { - if (button == CURSOR_UP && ui->cur_y > 0) - ui->cur_y--; - else if (button == CURSOR_DOWN && ui->cur_y < state->height-1) - ui->cur_y++; - else if (button == CURSOR_LEFT && ui->cur_x > 0) - ui->cur_x--; - else if (button == CURSOR_RIGHT && ui->cur_x < state->width-1) - ui->cur_x++; - else - return nullret; /* no cursor movement */ - ui->cur_visible = TRUE; - return state; /* UI activity has occurred */ + int dir; + switch (button) { + case CURSOR_UP: dir = U; break; + case CURSOR_DOWN: dir = D; break; + case CURSOR_LEFT: dir = L; break; + case CURSOR_RIGHT: dir = R; break; + default: return nullret; + } + if (shift) { + /* + * Move origin. + */ + if (state->wrapping) { + OFFSET(ui->org_x, ui->org_y, ui->org_x, ui->org_y, dir, state); + } else return nullret; /* disallowed for non-wrapping grids */ + } + if (ctrl) { + /* + * Change source tile. + */ + OFFSET(ui->cx, ui->cy, ui->cx, ui->cy, dir, state); + } + if (!shift && !ctrl) { + /* + * Move keyboard cursor. + */ + OFFSET(ui->cur_x, ui->cur_y, ui->cur_x, ui->cur_y, dir, state); + ui->cur_visible = TRUE; + } + return state; /* UI activity has occurred */ } else if (button == 'a' || button == 's' || button == 'd' || button == 'A' || button == 'S' || button == 'D') { tx = ui->cur_x; @@ -1961,7 +1996,7 @@ static game_state *make_move(game_state *state, game_ui *ui, * Check whether the game has been completed. */ { - unsigned char *active = compute_active(ret); + unsigned char *active = compute_active(ret, ui->cx, ui->cy); int x1, y1; int complete = TRUE; @@ -1989,6 +2024,7 @@ static game_state *make_move(game_state *state, game_ui *ui, struct game_drawstate { int started; int width, height; + int org_x, org_y; unsigned char *visible; }; @@ -1999,6 +2035,7 @@ static game_drawstate *game_new_drawstate(game_state *state) ds->started = FALSE; ds->width = state->width; ds->height = state->height; + ds->org_x = ds->org_y = -1; ds->visible = snewn(state->width * state->height, unsigned char); memset(ds->visible, 0xFF, state->width * state->height); @@ -2096,7 +2133,11 @@ static void draw_rect_coords(frontend *fe, int x1, int y1, int x2, int y2, draw_rect(fe, mx, my, dx, dy, colour); } -static void draw_barrier_corner(frontend *fe, int x, int y, int dir, int phase) +/* + * draw_barrier_corner() and draw_barrier() are passed physical coords + */ +static void draw_barrier_corner(frontend *fe, int x, int y, int dir, int phase, + int barrier) { int bx = WINDOW_OFFSET + TILE_SIZE * x; int by = WINDOW_OFFSET + TILE_SIZE * y; @@ -2113,18 +2154,19 @@ static void draw_barrier_corner(frontend *fe, int x, int y, int dir, int phase) if (phase == 0) { draw_rect_coords(fe, bx+x1, by+y1, bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy, - COL_WIRE); + barrier ? COL_WIRE : COL_BACKGROUND); draw_rect_coords(fe, bx+x1, by+y1, bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy, - COL_WIRE); + barrier ? COL_WIRE : COL_BACKGROUND); } else { draw_rect_coords(fe, bx+x1, by+y1, bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy, - COL_BARRIER); + barrier ? COL_BARRIER : COL_BORDER); } } -static void draw_barrier(frontend *fe, int x, int y, int dir, int phase) +static void draw_barrier(frontend *fe, int x, int y, int dir, int phase, + int barrier) { int bx = WINDOW_OFFSET + TILE_SIZE * x; int by = WINDOW_OFFSET + TILE_SIZE * y; @@ -2136,14 +2178,19 @@ static void draw_barrier(frontend *fe, int x, int y, int dir, int phase) h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER); if (phase == 0) { - draw_rect(fe, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE); + draw_rect(fe, bx+x1-X(dir), by+y1-Y(dir), w, h, + barrier ? COL_WIRE : COL_BACKGROUND); } else { - draw_rect(fe, bx+x1, by+y1, w, h, COL_BARRIER); + draw_rect(fe, bx+x1, by+y1, w, h, + barrier ? COL_BARRIER : COL_BORDER); } } -static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, - float angle, int cursor) +/* + * draw_tile() is passed physical coordinates + */ +static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds, + int x, int y, int tile, int src, float angle, int cursor) { int bx = WINDOW_OFFSET + TILE_SIZE * x; int by = WINDOW_OFFSET + TILE_SIZE * y; @@ -2238,7 +2285,7 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, * otherwise not at all. */ col = -1; - if (x == state->cx && y == state->cy) + if (src) col = COL_WIRE; else if (COUNT(tile) == 1) { col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT); @@ -2279,7 +2326,7 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height) continue; - if (!(tile(state, ox, oy) & F(dir))) + if (!(tile(state, GX(ox), GY(oy)) & F(dir))) continue; px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx); @@ -2315,11 +2362,11 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, */ for (phase = 0; phase < 2; phase++) { for (dir = 1; dir < 0x10; dir <<= 1) - if (barrier(state, x, y) & (dir << 4)) - draw_barrier_corner(fe, x, y, dir << 4, phase); + if (barrier(state, GX(x), GY(y)) & (dir << 4)) + draw_barrier_corner(fe, x, y, dir << 4, phase, TRUE); for (dir = 1; dir < 0x10; dir <<= 1) - if (barrier(state, x, y) & dir) - draw_barrier(fe, x, y, dir, phase); + if (barrier(state, GX(x), GY(y)) & dir) + draw_barrier(fe, x, y, dir, phase, TRUE); } draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); @@ -2328,57 +2375,60 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, game_state *state, int dir, game_ui *ui, float t, float ft) { - int x, y, tx, ty, frame, last_rotate_dir; + int x, y, tx, ty, frame, last_rotate_dir, moved_origin = FALSE; unsigned char *active; float angle = 0.0; /* - * Clear the screen and draw the exterior barrier lines if this - * is our first call. + * Clear the screen if this is our first call. */ if (!ds->started) { - int phase; - ds->started = TRUE; draw_rect(fe, 0, 0, WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER, WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER, COL_BACKGROUND); + + } + + /* + * If the origin has changed, we need to redraw the exterior + * barrier lines. + */ + if (ui->org_x != ds->org_x || ui->org_y != ds->org_y) { + int phase; + + ds->org_x = ui->org_x; + ds->org_y = ui->org_y; + moved_origin = TRUE; + draw_update(fe, 0, 0, WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER, WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER); - + for (phase = 0; phase < 2; phase++) { for (x = 0; x < ds->width; x++) { - if (barrier(state, x, 0) & UL) - draw_barrier_corner(fe, x, -1, LD, phase); - if (barrier(state, x, 0) & RU) - draw_barrier_corner(fe, x, -1, DR, phase); - if (barrier(state, x, 0) & U) - draw_barrier(fe, x, -1, D, phase); - if (barrier(state, x, ds->height-1) & DR) - draw_barrier_corner(fe, x, ds->height, RU, phase); - if (barrier(state, x, ds->height-1) & LD) - draw_barrier_corner(fe, x, ds->height, UL, phase); - if (barrier(state, x, ds->height-1) & D) - draw_barrier(fe, x, ds->height, U, phase); + int ub = barrier(state, GX(x), GY(0)); + int db = barrier(state, GX(x), GY(ds->height-1)); + draw_barrier_corner(fe, x, -1, LD, phase, ub & UL); + draw_barrier_corner(fe, x, -1, DR, phase, ub & RU); + draw_barrier(fe, x, -1, D, phase, ub & U); + draw_barrier_corner(fe, x, ds->height, RU, phase, db & DR); + draw_barrier_corner(fe, x, ds->height, UL, phase, db & LD); + draw_barrier(fe, x, ds->height, U, phase, db & D); } for (y = 0; y < ds->height; y++) { - if (barrier(state, 0, y) & UL) - draw_barrier_corner(fe, -1, y, RU, phase); - if (barrier(state, 0, y) & LD) - draw_barrier_corner(fe, -1, y, DR, phase); - if (barrier(state, 0, y) & L) - draw_barrier(fe, -1, y, R, phase); - if (barrier(state, ds->width-1, y) & RU) - draw_barrier_corner(fe, ds->width, y, UL, phase); - if (barrier(state, ds->width-1, y) & DR) - draw_barrier_corner(fe, ds->width, y, LD, phase); - if (barrier(state, ds->width-1, y) & R) - draw_barrier(fe, ds->width, y, L, phase); + int lb = barrier(state, GX(0), GY(y)); + int rb = barrier(state, GX(ds->width-1), GY(y)); + draw_barrier_corner(fe, -1, y, RU, phase, lb & UL); + draw_barrier_corner(fe, -1, y, DR, phase, lb & LD); + draw_barrier(fe, -1, y, R, phase, lb & L); + draw_barrier_corner(fe, ds->width, y, UL, phase, rb & RU); + draw_barrier_corner(fe, ds->width, y, LD, phase, rb & DR); + draw_barrier(fe, ds->width, y, L, phase, rb & R); } } } @@ -2409,11 +2459,16 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, /* * Draw any tile which differs from the way it was last drawn. */ - active = compute_active(state); + active = compute_active(state, ui->cx, ui->cy); for (x = 0; x < ds->width; x++) for (y = 0; y < ds->height; y++) { - unsigned char c = tile(state, x, y) | index(state, active, x, y); + unsigned char c = tile(state, GX(x), GY(y)) | + index(state, active, GX(x), GY(y)); + int is_src = GX(x) == ui->cx && GY(y) == ui->cy; + int is_anim = GX(x) == tx && GY(y) == ty; + int is_cursor = ui->cur_visible && + GX(x) == ui->cur_x && GY(y) == ui->cur_y; /* * In a completion flash, we adjust the LOCKED bit @@ -2421,9 +2476,10 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, * the frame number. */ if (frame >= 0) { + int rcx = RX(ui->cx), rcy = RY(ui->cy); int xdist, ydist, dist; - xdist = (x < state->cx ? state->cx - x : x - state->cx); - ydist = (y < state->cy ? state->cy - y : y - state->cy); + xdist = (x < rcx ? rcx - x : x - rcx); + ydist = (y < rcy ? rcy - y : y - rcy); dist = (xdist > ydist ? xdist : ydist); if (frame >= dist && frame < dist+4) { @@ -2433,15 +2489,13 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, } } - if (index(state, ds->visible, x, y) != c || + if (moved_origin || + index(state, ds->visible, x, y) != c || index(state, ds->visible, x, y) == 0xFF || - (x == tx && y == ty) || - (ui->cur_visible && x == ui->cur_x && y == ui->cur_y)) { - draw_tile(fe, state, x, y, c, - (x == tx && y == ty ? angle : 0.0F), - (ui->cur_visible && x == ui->cur_x && y == ui->cur_y)); - if ((x == tx && y == ty) || - (ui->cur_visible && x == ui->cur_x && y == ui->cur_y)) + is_src || is_anim || is_cursor) { + draw_tile(fe, state, ds, x, y, c, + is_src, (is_anim ? angle : 0.0F), is_cursor); + if (is_src || is_anim || is_cursor) index(state, ds->visible, x, y) = 0xFF; else index(state, ds->visible, x, y) = c; @@ -2505,16 +2559,11 @@ static float game_flash_length(game_state *oldstate, */ if (!oldstate->completed && newstate->completed && !oldstate->used_solve && !newstate->used_solve) { - int size; - size = 0; - if (size < newstate->cx+1) - size = newstate->cx+1; - if (size < newstate->cy+1) - size = newstate->cy+1; - if (size < newstate->width - newstate->cx) - size = newstate->width - newstate->cx; - if (size < newstate->height - newstate->cy) - size = newstate->height - newstate->cy; + int size = 0; + if (size < newstate->width) + size = newstate->width; + if (size < newstate->height) + size = newstate->height; return FLASH_FRAME * (size+4); } diff --git a/netslide.c b/netslide.c index b72706a..6bac447 100644 --- a/netslide.c +++ b/netslide.c @@ -1006,6 +1006,8 @@ static game_state *make_move(game_state *state, game_ui *ui, int n, dx, dy; game_state *ret; + button &= ~MOD_MASK; + if (button != LEFT_BUTTON && button != RIGHT_BUTTON) return NULL; diff --git a/pattern.c b/pattern.c index 63b856c..e09ed3d 100644 --- a/pattern.c +++ b/pattern.c @@ -769,6 +769,8 @@ static game_state *make_move(game_state *from, game_ui *ui, { game_state *ret; + button &= ~MOD_MASK; + x = FROMCOORD(from->w, x); y = FROMCOORD(from->h, y); diff --git a/puzzles.but b/puzzles.but index 865aad7..9635e20 100644 --- a/puzzles.but +++ b/puzzles.but @@ -320,6 +320,21 @@ controls are: also unlock it again, but while it's locked you can't accidentally turn it. +The following controls are not necessary to complete the game, but may +be useful: + +\dt \e{Shift grid}: Shift + arrow keys + +\dd On grids that wrap, you can move the origin of the grid, so that +tiles that were on opposite sides of the grid can be seen together. + +\dt \e{Move centre}: Ctrl + arrow keys + +\dd You can change which tile is used as the source of highlighting. +(It doesn't ultimately matter which tile this is, as every tile will +be connected to every other tile in a correct solution, but it may be +helpful in the intermediate stages of solving the puzzle.) + \dt \e{Jumble tiles}: \q{J} key \dd This key turns all tiles that are not locked to random diff --git a/puzzles.h b/puzzles.h index a18aefa..ec818d5 100644 --- a/puzzles.h +++ b/puzzles.h @@ -32,7 +32,10 @@ enum { CURSOR_LEFT, CURSOR_RIGHT, - MOD_NUM_KEYPAD = 0x40000000 + MOD_CTRL = 0x10000000, + MOD_SHFT = 0x20000000, + MOD_NUM_KEYPAD = 0x40000000, + MOD_MASK = 0x70000000 /* mask for all modifiers */ }; #define IS_MOUSE_DOWN(m) ( (unsigned)((m) - LEFT_BUTTON) <= \ diff --git a/rect.c b/rect.c index 848dcd3..76abeed 100644 --- a/rect.c +++ b/rect.c @@ -2127,6 +2127,8 @@ static game_state *make_move(game_state *from, game_ui *ui, int startdrag = FALSE, enddrag = FALSE, active = FALSE; game_state *ret; + button &= ~MOD_MASK; + if (button == LEFT_BUTTON) { startdrag = TRUE; } else if (button == LEFT_RELEASE) { diff --git a/sixteen.c b/sixteen.c index 966cc0f..2bfaadd 100644 --- a/sixteen.c +++ b/sixteen.c @@ -572,6 +572,7 @@ static game_state *make_move(game_state *from, game_ui *ui, int dx, dy, tx, ty, n; game_state *ret; + button &= ~MOD_MASK; if (button != LEFT_BUTTON && button != RIGHT_BUTTON) return NULL; diff --git a/solo.c b/solo.c index ad6c2f4..6a964ea 100644 --- a/solo.c +++ b/solo.c @@ -1828,7 +1828,7 @@ static game_state *make_move(game_state *from, game_ui *ui, int x, int y, int tx, ty; game_state *ret; - button &= ~MOD_NUM_KEYPAD; /* we treat this the same as normal */ + button &= ~MOD_MASK; tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1; ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1; diff --git a/twiddle.c b/twiddle.c index 871bd7f..a681059 100644 --- a/twiddle.c +++ b/twiddle.c @@ -594,6 +594,8 @@ static game_state *make_move(game_state *from, game_ui *ui, int x, int y, game_state *ret; int dir; + button = button & (~MOD_MASK | MOD_NUM_KEYPAD); + if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { /* * Determine the coordinates of the click. We offset by n-1 diff --git a/windows.c b/windows.c index baa9998..383b742 100644 --- a/windows.c +++ b/windows.c @@ -1221,31 +1221,35 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_KEYDOWN: { int key = -1; + BYTE keystate[256]; + int r = GetKeyboardState(keystate); + int shift = (r && (keystate[VK_SHIFT] & 0x80)) ? MOD_SHFT : 0; + int ctrl = (r && (keystate[VK_CONTROL] & 0x80)) ? MOD_CTRL : 0; switch (wParam) { case VK_LEFT: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '4'; - else - key = CURSOR_LEFT; + else + key = shift | ctrl | CURSOR_LEFT; break; case VK_RIGHT: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '6'; - else - key = CURSOR_RIGHT; + else + key = shift | ctrl | CURSOR_RIGHT; break; case VK_UP: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '8'; - else - key = CURSOR_UP; + else + key = shift | ctrl | CURSOR_UP; break; case VK_DOWN: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '2'; - else - key = CURSOR_DOWN; + else + key = shift | ctrl | CURSOR_DOWN; break; /* * Diagonal keys on the numeric keypad. -- 2.11.0