Patch from James H to add keyboard control in Sixteen and Netslide

(and also belatedly document the keyboard support in Unequal).

[originally from svn r8432]
This commit is contained in:
Simon Tatham
2009-01-26 19:14:44 +00:00
parent d2b0d8cf3f
commit cc0f957d82
5 changed files with 230 additions and 50 deletions

29
misc.c
View File

@ -280,6 +280,35 @@ int c2pos(int w, int h, int cx, int cy)
return -1; /* not reached */
}
int c2diff(int w, int h, int cx, int cy, int button)
{
int diff = 0;
assert(IS_CURSOR_MOVE(button));
/* Obvious moves around edge. */
if (cy == -1)
diff = (button == CURSOR_RIGHT) ? +1 : (button == CURSOR_LEFT) ? -1 : diff;
if (cy == h)
diff = (button == CURSOR_RIGHT) ? -1 : (button == CURSOR_LEFT) ? +1 : diff;
if (cx == -1)
diff = (button == CURSOR_UP) ? +1 : (button == CURSOR_DOWN) ? -1 : diff;
if (cx == w)
diff = (button == CURSOR_UP) ? -1 : (button == CURSOR_DOWN) ? +1 : diff;
if (button == CURSOR_LEFT && cx == w && (cy == 0 || cy == h-1))
diff = (cy == 0) ? -1 : +1;
if (button == CURSOR_RIGHT && cx == -1 && (cy == 0 || cy == h-1))
diff = (cy == 0) ? +1 : -1;
if (button == CURSOR_DOWN && cy == -1 && (cx == 0 || cx == w-1))
diff = (cx == 0) ? -1 : +1;
if (button == CURSOR_UP && cy == h && (cx == 0 || cx == w-1))
diff = (cx == 0) ? +1 : -1;
debug(("cx,cy = %d,%d; w%d h%d, diff = %d", cx, cy, w, h, diff));
return diff;
}
void pos2c(int w, int h, int pos, int *cx, int *cy)
{
int max = w+h+w+h;

View File

@ -159,15 +159,15 @@ static game_params *default_params(void)
static const struct { int x, y, wrap, bprob; const char* desc; }
netslide_presets[] = {
{3, 3, FALSE, 1.0, " easy"},
{3, 3, FALSE, 0.0, " medium"},
{3, 3, TRUE, 0.0, " hard"},
{4, 4, FALSE, 1.0, " easy"},
{4, 4, FALSE, 0.0, " medium"},
{4, 4, TRUE, 0.0, " hard"},
{5, 5, FALSE, 1.0, " easy"},
{5, 5, FALSE, 0.0, " medium"},
{5, 5, TRUE, 0.0, " hard"},
{3, 3, FALSE, 1, " easy"},
{3, 3, FALSE, 0, " medium"},
{3, 3, TRUE, 0, " hard"},
{4, 4, FALSE, 1, " easy"},
{4, 4, FALSE, 0, " medium"},
{4, 4, TRUE, 0, " hard"},
{5, 5, FALSE, 1, " easy"},
{5, 5, FALSE, 0, " medium"},
{5, 5, TRUE, 0, " hard"},
};
static int game_fetch_preset(int i, char **name, game_params **params)
@ -182,7 +182,7 @@ static int game_fetch_preset(int i, char **name, game_params **params)
ret->width = netslide_presets[i].x;
ret->height = netslide_presets[i].y;
ret->wrapping = netslide_presets[i].wrap;
ret->barrier_probability = netslide_presets[i].bprob;
ret->barrier_probability = (float)netslide_presets[i].bprob;
ret->movetarget = 0;
sprintf(str, "%dx%d%s", ret->width, ret->height, netslide_presets[i].desc);
@ -221,7 +221,7 @@ static void decode_params(game_params *ret, char const *string)
if ( (ret->wrapping = (*p == 'w')) != 0 )
p++;
if (*p == 'b') {
ret->barrier_probability = atof(++p);
ret->barrier_probability = (float)atof(++p);
while (*p && (isdigit((unsigned char)*p) || *p == '.')) p++;
}
if (*p == 'm') {
@ -979,8 +979,8 @@ struct game_ui {
static game_ui *new_ui(game_state *state)
{
game_ui *ui = snew(game_ui);
ui->cur_x = state->width / 2;
ui->cur_y = state->height / 2;
ui->cur_x = 0;
ui->cur_y = -1;
ui->cur_visible = FALSE;
return ui;
@ -1052,6 +1052,7 @@ struct game_drawstate {
int width, height;
int tilesize;
unsigned char *visible;
int cur_x, cur_y;
};
static char *interpret_move(game_state *state, game_ui *ui,
@ -1063,11 +1064,37 @@ static char *interpret_move(game_state *state, game_ui *ui,
button &= ~MOD_MASK;
if (button != LEFT_BUTTON && button != RIGHT_BUTTON)
return NULL;
if (IS_CURSOR_MOVE(button)) {
int cpos, diff = 0;
cpos = c2pos(state->width, state->height, ui->cur_x, ui->cur_y);
diff = c2diff(state->width, state->height, ui->cur_x, ui->cur_y, button);
if (diff != 0) {
do { /* we might have to do this more than once to skip missing arrows */
cpos += diff;
pos2c(state->width, state->height, cpos, &ui->cur_x, &ui->cur_y);
} while (ui->cur_x == state->cx || ui->cur_y == state->cy);
}
ui->cur_visible = 1;
return "";
}
if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
cx = (x - (BORDER + WINDOW_OFFSET + TILE_BORDER) + 2*TILE_SIZE) / TILE_SIZE - 2;
cy = (y - (BORDER + WINDOW_OFFSET + TILE_BORDER) + 2*TILE_SIZE) / TILE_SIZE - 2;
ui->cur_visible = 0;
} else if (IS_CURSOR_SELECT(button)) {
if (ui->cur_visible) {
cx = ui->cur_x;
cy = ui->cur_y;
} else {
/* 'click' when cursor is invisible just makes cursor visible. */
ui->cur_visible = 1;
return "";
}
} else
return NULL;
if (cy >= 0 && cy < state->height && cy != state->cy)
{
@ -1187,6 +1214,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
ds->visible = snewn(state->width * state->height, unsigned char);
ds->tilesize = 0; /* not decided yet */
memset(ds->visible, 0xFF, state->width * state->height);
ds->cur_x = ds->cur_y = -1;
return ds;
}
@ -1351,8 +1379,8 @@ static void draw_barrier(drawing *dr, game_drawstate *ds,
static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state,
int x, int y, int tile, float xshift, float yshift)
{
int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x + (xshift * TILE_SIZE);
int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y + (yshift * TILE_SIZE);
int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x + (int)(xshift * TILE_SIZE);
int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y + (int)(yshift * TILE_SIZE);
float cx, cy, ex, ey;
int dir, col;
@ -1509,7 +1537,7 @@ static void draw_tile_barriers(drawing *dr, game_drawstate *ds,
}
static void draw_arrow(drawing *dr, game_drawstate *ds,
int x, int y, int xdx, int xdy)
int x, int y, int xdx, int xdy, int cur)
{
int coords[14];
int ydy = -xdx, ydx = xdy;
@ -1529,7 +1557,29 @@ static void draw_arrow(drawing *dr, game_drawstate *ds,
POINT(5, 3 * TILE_SIZE / 8, TILE_SIZE / 2); /* left concave */
POINT(6, TILE_SIZE / 4, TILE_SIZE / 2); /* left corner */
draw_polygon(dr, coords, 7, COL_LOWLIGHT, COL_TEXT);
draw_polygon(dr, coords, 7, cur ? COL_POWERED : COL_LOWLIGHT, COL_TEXT);
}
static void draw_arrow_for_cursor(drawing *dr, game_drawstate *ds,
int cur_x, int cur_y, int cur)
{
if (cur_x == -1 && cur_y == -1)
return; /* 'no cursur here */
else if (cur_x == -1) /* LH column. */
draw_arrow(dr, ds, 0, cur_y+1, 0, -1, cur);
else if (cur_x == ds->width) /* RH column */
draw_arrow(dr, ds, ds->width, cur_y, 0, +1, cur);
else if (cur_y == -1) /* Top row */
draw_arrow(dr, ds, cur_x, 0, +1, 0, cur);
else if (cur_y == ds->height) /* Bottom row */
draw_arrow(dr, ds, cur_x+1, ds->height, -1, 0, cur);
else
assert(!"Invalid cursor position");
draw_update(dr,
cur_x * TILE_SIZE + BORDER + WINDOW_OFFSET,
cur_y * TILE_SIZE + BORDER + WINDOW_OFFSET,
TILE_SIZE, TILE_SIZE);
}
static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
@ -1539,6 +1589,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
unsigned char *active;
float xshift = 0.0;
float yshift = 0.0;
int cur_x = -1, cur_y = -1;
/*
* Clear the screen and draw the exterior barrier lines if this
@ -1595,15 +1646,26 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
*/
for (x = 0; x < ds->width; x++) {
if (x == state->cx) continue;
draw_arrow(dr, ds, x, 0, +1, 0);
draw_arrow(dr, ds, x+1, ds->height, -1, 0);
draw_arrow(dr, ds, x, 0, +1, 0, 0);
draw_arrow(dr, ds, x+1, ds->height, -1, 0, 0);
}
for (y = 0; y < ds->height; y++) {
if (y == state->cy) continue;
draw_arrow(dr, ds, ds->width, y, 0, +1);
draw_arrow(dr, ds, 0, y+1, 0, -1);
draw_arrow(dr, ds, ds->width, y, 0, +1, 0);
draw_arrow(dr, ds, 0, y+1, 0, -1, 0);
}
}
if (ui->cur_visible) {
cur_x = ui->cur_x; cur_y = ui->cur_y;
}
if (cur_x != ds->cur_x || cur_y != ds->cur_y) {
/* Cursor has changed; redraw two (prev and curr) arrows. */
assert(cur_x != state->cx && cur_y != state->cy);
draw_arrow_for_cursor(dr, ds, cur_x, cur_y, 1);
draw_arrow_for_cursor(dr, ds, ds->cur_x, ds->cur_y, 0);
ds->cur_x = cur_x; ds->cur_y = cur_y;
}
/* Check if this is an undo. If so, we will need to run any animation
* backwards.
@ -1622,9 +1684,9 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
* state->last_move_pos, in direction
* state->last_move_dir
*/
xshift = state->last_move_row == -1 ? 0.0 :
xshift = state->last_move_row == -1 ? 0.0F :
(1 - t / ANIM_TIME) * state->last_move_dir;
yshift = state->last_move_col == -1 ? 0.0 :
yshift = state->last_move_col == -1 ? 0.0F :
(1 - t / ANIM_TIME) * state->last_move_dir;
}
@ -1678,8 +1740,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
index(state, ds->visible, x, y) == 0xFF ||
(x == state->last_move_col || y == state->last_move_row))
{
float xs = (y == state->last_move_row ? xshift : 0.0);
float ys = (x == state->last_move_col ? yshift : 0.0);
float xs = (y == state->last_move_row ? xshift : (float)0.0);
float ys = (x == state->last_move_col ? yshift : (float)0.0);
draw_tile(dr, ds, state, x, y, c, xs, ys);
if (xs < 0 && x == 0)
@ -1820,3 +1882,5 @@ const struct game thegame = {
FALSE, game_timing_state,
0, /* flags */
};
/* vim: set shiftwidth=4 tabstop=8: */

View File

@ -646,9 +646,13 @@ rather than just engineering.
\H{sixteen-controls} \I{controls, for Sixteen}Sixteen controls
This game is played with the mouse. Left-clicking on an arrow will
move the appropriate row or column in the direction indicated.
Right-clicking will move it in the opposite direction.
Left-clicking on an arrow will move the appropriate row or column in
the direction indicated. Right-clicking will move it in the opposite
direction.
Alternatively, use the cursor keys to move the position indicator
around the edge of the grid, and use the return key to move the
row/column in the direction indicated.
(All the actions described in \k{common-actions} are also available.)
@ -833,8 +837,8 @@ movement of Sixteen (see \k{sixteen}): you have a Net grid, but
instead of rotating tiles back into place you have to slide them
into place by moving a whole row at a time.
As in Sixteen, \I{controls, for Netslide}control is with the mouse.
See \k{sixteen-controls}.
As in Sixteen, \I{controls, for Netslide}control is with the mouse or
cursor keys. See \k{sixteen-controls}.
\I{parameters, for Netslide}The available game parameters have similar
meanings to those in Net (see \k{net-params}) and Sixteen (see
@ -2210,7 +2214,8 @@ Space to clear it again (or use the Undo feature).
If you \e{right}-click in a square and then type a number, that
number will be entered in the square as a \q{pencil mark}. You can
have pencil marks for multiple numbers in the same square.
have pencil marks for multiple numbers in the same square. Squares
containing filled-in numbers cannot also contain pencil marks.
The game pays no attention to pencil marks, so exactly what you use
them for is up to you: you can use them as reminders that a
@ -2227,7 +2232,14 @@ pressing space will also erase pencil marks.
As for Solo, the cursor keys can be used in conjunction with the digit
keys to set numbers or pencil marks. You can also use the 'M' key to
auto-fill every numeric hint, ready for removal as required.
auto-fill every numeric hint, ready for removal as required, or the 'H'
key to do the same but also to remove all obvious hints.
Alternatively, use the cursor keys to move the mark around the grid.
Pressing the return key toggles the mark (from a normal mark to a
pencil mark), and typing a number in is entered in the square in the
appropriate way; typing in a 0 or using the space bar will clear a
filled square.
(All the actions described in \k{common-actions} are also available.)

View File

@ -306,6 +306,7 @@ void move_cursor(int button, int *x, int *y, int maxw, int maxh, int wrap);
/* Used in netslide.c and sixteen.c for cursor movement around edge. */
int c2pos(int w, int h, int cx, int cy);
int c2diff(int w, int h, int cx, int cy, int button);
void pos2c(int w, int h, int pos, int *cx, int *cy);
/* Draws text with an 'outline' formed by offsetting the text

View File

@ -553,13 +553,24 @@ static char *game_text_format(game_state *state)
return ret;
}
struct game_ui {
int cur_x, cur_y;
int cur_visible;
};
static game_ui *new_ui(game_state *state)
{
return NULL;
game_ui *ui = snew(game_ui);
ui->cur_x = 0;
ui->cur_y = -1;
ui->cur_visible = FALSE;
return ui;
}
static void free_ui(game_ui *ui)
{
sfree(ui);
}
static char *encode_ui(game_ui *ui)
@ -581,20 +592,47 @@ struct game_drawstate {
int w, h, bgcolour;
int *tiles;
int tilesize;
int cur_x, cur_y;
};
static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
int x, int y, int button)
{
int cx, cy, dx, dy;
int cx = -1, cy = -1, dx, dy;
char buf[80];
button &= ~MOD_MASK;
if (button != LEFT_BUTTON && button != RIGHT_BUTTON)
return NULL;
if (IS_CURSOR_MOVE(button)) {
/* right/down rotates cursor clockwise,
* left/up rotates anticlockwise. */
int cpos, diff;
cpos = c2pos(state->w, state->h, ui->cur_x, ui->cur_y);
diff = c2diff(state->w, state->h, ui->cur_x, ui->cur_y, button);
cpos += diff;
pos2c(state->w, state->h, cpos, &ui->cur_x, &ui->cur_y);
ui->cur_visible = 1;
return "";
}
if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
cx = FROMCOORD(x);
cy = FROMCOORD(y);
ui->cur_visible = 0;
} else if (IS_CURSOR_SELECT(button)) {
if (ui->cur_visible) {
cx = ui->cur_x;
cy = ui->cur_y;
} else {
ui->cur_visible = 1;
return "";
}
} else {
return NULL;
}
if (cx == -1 && cy >= 0 && cy < state->h)
dx = -1, dy = 0;
else if (cx == state->w && cy >= 0 && cy < state->h)
@ -604,10 +642,10 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
else if (cy == state->h && cx >= 0 && cx < state->w)
dy = +1, dx = 0;
else
return NULL; /* invalid click location */
return ""; /* invalid click location */
/* reverse direction if right hand button is pressed */
if (button == RIGHT_BUTTON) {
if (button == RIGHT_BUTTON || button == CURSOR_SELECT2) {
dx = -dx;
dy = -dy;
}
@ -731,6 +769,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
ds->tilesize = 0; /* haven't decided yet */
for (i = 0; i < ds->w*ds->h; i++)
ds->tiles[i] = -1;
ds->cur_x = ds->cur_y = -1;
return ds;
}
@ -777,7 +816,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds,
}
static void draw_arrow(drawing *dr, game_drawstate *ds,
int x, int y, int xdx, int xdy)
int x, int y, int xdx, int xdy, int cur)
{
int coords[14];
int ydy = -xdx, ydx = xdy;
@ -794,7 +833,27 @@ static void draw_arrow(drawing *dr, game_drawstate *ds,
POINT(5, 3 * TILE_SIZE / 8, TILE_SIZE / 2); /* left concave */
POINT(6, TILE_SIZE / 4, TILE_SIZE / 2); /* left corner */
draw_polygon(dr, coords, 7, COL_LOWLIGHT, COL_TEXT);
draw_polygon(dr, coords, 7, cur ? COL_HIGHLIGHT : COL_LOWLIGHT, COL_TEXT);
}
static void draw_arrow_for_cursor(drawing *dr, game_drawstate *ds,
int cur_x, int cur_y, int cur)
{
if (cur_x == -1 && cur_y == -1)
return; /* 'no cursur here */
else if (cur_x == -1) /* LH column. */
draw_arrow(dr, ds, COORD(0), COORD(cur_y+1), 0, -1, cur);
else if (cur_x == ds->w) /* RH column */
draw_arrow(dr, ds, COORD(ds->w), COORD(cur_y), 0, +1, cur);
else if (cur_y == -1) /* Top row */
draw_arrow(dr, ds, COORD(cur_x), COORD(0), +1, 0, cur);
else if (cur_y == ds->h) /* Bottom row */
draw_arrow(dr, ds, COORD(cur_x+1), COORD(ds->h), -1, 0, cur);
else
assert(!"Invalid cursor position");
draw_update(dr, COORD(cur_x), COORD(cur_y),
TILE_SIZE, TILE_SIZE);
}
static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
@ -802,6 +861,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
float animtime, float flashtime)
{
int i, bgcolour;
int cur_x = -1, cur_y = -1;
if (flashtime > 0) {
int frame = (int)(flashtime / FLASH_FRAME);
@ -842,16 +902,28 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
* Arrows for making moves.
*/
for (i = 0; i < state->w; i++) {
draw_arrow(dr, ds, COORD(i), COORD(0), +1, 0);
draw_arrow(dr, ds, COORD(i+1), COORD(state->h), -1, 0);
draw_arrow(dr, ds, COORD(i), COORD(0), +1, 0, 0);
draw_arrow(dr, ds, COORD(i+1), COORD(state->h), -1, 0, 0);
}
for (i = 0; i < state->h; i++) {
draw_arrow(dr, ds, COORD(state->w), COORD(i), 0, +1);
draw_arrow(dr, ds, COORD(0), COORD(i+1), 0, -1);
draw_arrow(dr, ds, COORD(state->w), COORD(i), 0, +1, 0);
draw_arrow(dr, ds, COORD(0), COORD(i+1), 0, -1, 0);
}
ds->started = TRUE;
}
/*
* Cursor (highlighted arrow around edge)
*/
if (ui->cur_visible) {
cur_x = ui->cur_x; cur_y = ui->cur_y;
}
if (cur_x != ds->cur_x || cur_y != ds->cur_y) {
/* Cursor has changed; redraw two (prev and curr) arrows. */
draw_arrow_for_cursor(dr, ds, cur_x, cur_y, 1);
draw_arrow_for_cursor(dr, ds, ds->cur_x, ds->cur_y, 0);
ds->cur_x = cur_x; ds->cur_y = cur_y;
}
/*
* Now draw each tile.
@ -1054,3 +1126,5 @@ const struct game thegame = {
FALSE, game_timing_state,
0, /* flags */
};
/* vim: set shiftwidth=4 tabstop=8: */