mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 16:05:44 -07:00
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.) [originally from svn r5844]
This commit is contained in:
2
cube.c
2
cube.c
@ -1013,6 +1013,8 @@ static game_state *make_move(game_state *from, game_ui *ui,
|
|||||||
int i, j, dest, mask;
|
int i, j, dest, mask;
|
||||||
struct solid *poly;
|
struct solid *poly;
|
||||||
|
|
||||||
|
button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All moves are made with the cursor keys or numeric keypad.
|
* All moves are made with the cursor keys or numeric keypad.
|
||||||
*/
|
*/
|
||||||
|
@ -456,6 +456,8 @@ static game_state *make_move(game_state *from, game_ui *ui,
|
|||||||
int gx, gy, dx, dy, ux, uy, up, p;
|
int gx, gy, dx, dy, ux, uy, up, p;
|
||||||
game_state *ret;
|
game_state *ret;
|
||||||
|
|
||||||
|
button &= ~MOD_MASK;
|
||||||
|
|
||||||
gx = X(from, from->gap_pos);
|
gx = X(from, from->gap_pos);
|
||||||
gy = Y(from, from->gap_pos);
|
gy = Y(from, from->gap_pos);
|
||||||
|
|
||||||
|
10
gtk.c
10
gtk.c
@ -317,24 +317,26 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
|
|||||||
{
|
{
|
||||||
frontend *fe = (frontend *)data;
|
frontend *fe = (frontend *)data;
|
||||||
int keyval;
|
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)
|
if (!fe->pixmap)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
if (event->keyval == GDK_Up)
|
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)
|
else if (event->keyval == GDK_KP_Up || event->keyval == GDK_KP_8)
|
||||||
keyval = MOD_NUM_KEYPAD | '8';
|
keyval = MOD_NUM_KEYPAD | '8';
|
||||||
else if (event->keyval == GDK_Down)
|
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)
|
else if (event->keyval == GDK_KP_Down || event->keyval == GDK_KP_2)
|
||||||
keyval = MOD_NUM_KEYPAD | '2';
|
keyval = MOD_NUM_KEYPAD | '2';
|
||||||
else if (event->keyval == GDK_Left)
|
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)
|
else if (event->keyval == GDK_KP_Left || event->keyval == GDK_KP_4)
|
||||||
keyval = MOD_NUM_KEYPAD | '4';
|
keyval = MOD_NUM_KEYPAD | '4';
|
||||||
else if (event->keyval == GDK_Right)
|
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)
|
else if (event->keyval == GDK_KP_Right || event->keyval == GDK_KP_6)
|
||||||
keyval = MOD_NUM_KEYPAD | '6';
|
keyval = MOD_NUM_KEYPAD | '6';
|
||||||
else if (event->keyval == GDK_KP_Home || event->keyval == GDK_KP_7)
|
else if (event->keyval == GDK_KP_Home || event->keyval == GDK_KP_7)
|
||||||
|
263
net.c
263
net.c
@ -57,6 +57,13 @@
|
|||||||
#define ROTATE_TIME 0.13F
|
#define ROTATE_TIME 0.13F
|
||||||
#define FLASH_FRAME 0.07F
|
#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 {
|
enum {
|
||||||
COL_BACKGROUND,
|
COL_BACKGROUND,
|
||||||
COL_LOCKED,
|
COL_LOCKED,
|
||||||
@ -82,7 +89,7 @@ struct game_aux_info {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct game_state {
|
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 last_rotate_x, last_rotate_y, last_rotate_dir;
|
||||||
int used_solve, just_used_solve;
|
int used_solve, just_used_solve;
|
||||||
unsigned char *tiles;
|
unsigned char *tiles;
|
||||||
@ -1570,8 +1577,6 @@ static game_state *new_game(game_params *params, char *desc)
|
|||||||
state = snew(game_state);
|
state = snew(game_state);
|
||||||
w = state->width = params->width;
|
w = state->width = params->width;
|
||||||
h = state->height = params->height;
|
h = state->height = params->height;
|
||||||
state->cx = state->width / 2;
|
|
||||||
state->cy = state->height / 2;
|
|
||||||
state->wrapping = params->wrapping;
|
state->wrapping = params->wrapping;
|
||||||
state->last_rotate_dir = state->last_rotate_x = state->last_rotate_y = 0;
|
state->last_rotate_dir = state->last_rotate_x = state->last_rotate_y = 0;
|
||||||
state->completed = state->used_solve = state->just_used_solve = FALSE;
|
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, 0, y) |= L;
|
||||||
barrier(state, state->width-1, y) |= R;
|
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)
|
if (barrier(state, x, y) & dir2)
|
||||||
corner = TRUE;
|
corner = TRUE;
|
||||||
|
|
||||||
x1 = x + X(dir), y1 = y + Y(dir);
|
OFFSET(x1, y1, x, y, dir, state);
|
||||||
if (x1 >= 0 && x1 < state->width &&
|
if (barrier(state, x1, y1) & dir2)
|
||||||
y1 >= 0 && y1 < state->height &&
|
|
||||||
(barrier(state, x1, y1) & dir2))
|
|
||||||
corner = TRUE;
|
corner = TRUE;
|
||||||
|
|
||||||
x2 = x + X(dir2), y2 = y + Y(dir2);
|
OFFSET(x2, y2, x, y, dir2, state);
|
||||||
if (x2 >= 0 && x2 < state->width &&
|
if (barrier(state, x2, y2) & dir)
|
||||||
y2 >= 0 && y2 < state->height &&
|
|
||||||
(barrier(state, x2, y2) & dir))
|
|
||||||
corner = TRUE;
|
corner = TRUE;
|
||||||
|
|
||||||
if (corner) {
|
if (corner) {
|
||||||
barrier(state, x, y) |= (dir << 4);
|
barrier(state, x, y) |= (dir << 4);
|
||||||
if (x1 >= 0 && x1 < state->width &&
|
barrier(state, x1, y1) |= (A(dir) << 4);
|
||||||
y1 >= 0 && y1 < state->height)
|
barrier(state, x2, y2) |= (C(dir) << 4);
|
||||||
barrier(state, x1, y1) |= (A(dir) << 4);
|
OFFSET(x3, y3, x1, y1, dir2, state);
|
||||||
if (x2 >= 0 && x2 < state->width &&
|
barrier(state, x3, y3) |= (F(dir) << 4);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1683,8 +1694,6 @@ static game_state *dup_game(game_state *state)
|
|||||||
ret = snew(game_state);
|
ret = snew(game_state);
|
||||||
ret->width = state->width;
|
ret->width = state->width;
|
||||||
ret->height = state->height;
|
ret->height = state->height;
|
||||||
ret->cx = state->cx;
|
|
||||||
ret->cy = state->cy;
|
|
||||||
ret->wrapping = state->wrapping;
|
ret->wrapping = state->wrapping;
|
||||||
ret->completed = state->completed;
|
ret->completed = state->completed;
|
||||||
ret->used_solve = state->used_solve;
|
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
|
* completed - just call this function and see whether every square
|
||||||
* is marked active.
|
* 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;
|
unsigned char *active;
|
||||||
tree234 *todo;
|
tree234 *todo;
|
||||||
@ -1762,8 +1771,8 @@ static unsigned char *compute_active(game_state *state)
|
|||||||
* xyd_cmp and just store direction 0 every time.
|
* xyd_cmp and just store direction 0 every time.
|
||||||
*/
|
*/
|
||||||
todo = newtree234(xyd_cmp_nc);
|
todo = newtree234(xyd_cmp_nc);
|
||||||
index(state, active, state->cx, state->cy) = ACTIVE;
|
index(state, active, cx, cy) = ACTIVE;
|
||||||
add234(todo, new_xyd(state->cx, state->cy, 0));
|
add234(todo, new_xyd(cx, cy, 0));
|
||||||
|
|
||||||
while ( (xyd = delpos234(todo, 0)) != NULL) {
|
while ( (xyd = delpos234(todo, 0)) != NULL) {
|
||||||
int x1, y1, d1, x2, y2, d2;
|
int x1, y1, d1, x2, y2, d2;
|
||||||
@ -1799,6 +1808,8 @@ static unsigned char *compute_active(game_state *state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct game_ui {
|
struct game_ui {
|
||||||
|
int org_x, org_y; /* origin */
|
||||||
|
int cx, cy; /* source tile (game coordinates) */
|
||||||
int cur_x, cur_y;
|
int cur_x, cur_y;
|
||||||
int cur_visible;
|
int cur_visible;
|
||||||
random_state *rs; /* used for jumbling */
|
random_state *rs; /* used for jumbling */
|
||||||
@ -1809,8 +1820,9 @@ static game_ui *new_ui(game_state *state)
|
|||||||
void *seed;
|
void *seed;
|
||||||
int seedsize;
|
int seedsize;
|
||||||
game_ui *ui = snew(game_ui);
|
game_ui *ui = snew(game_ui);
|
||||||
ui->cur_x = state->width / 2;
|
ui->org_x = ui->org_y = 0;
|
||||||
ui->cur_y = state->height / 2;
|
ui->cur_x = ui->cx = state->width / 2;
|
||||||
|
ui->cur_y = ui->cy = state->height / 2;
|
||||||
ui->cur_visible = FALSE;
|
ui->cur_visible = FALSE;
|
||||||
get_random_seed(&seed, &seedsize);
|
get_random_seed(&seed, &seedsize);
|
||||||
ui->rs = random_init(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;
|
game_state *ret, *nullret;
|
||||||
int tx, ty, orig;
|
int tx, ty, orig;
|
||||||
|
int shift = button & MOD_SHFT, ctrl = button & MOD_CTRL;
|
||||||
|
|
||||||
|
button &= ~MOD_MASK;
|
||||||
nullret = NULL;
|
nullret = NULL;
|
||||||
|
|
||||||
if (button == LEFT_BUTTON ||
|
if (button == LEFT_BUTTON ||
|
||||||
@ -1856,23 +1870,44 @@ static game_state *make_move(game_state *state, game_ui *ui,
|
|||||||
ty = y / TILE_SIZE;
|
ty = y / TILE_SIZE;
|
||||||
if (tx >= state->width || ty >= state->height)
|
if (tx >= state->width || ty >= state->height)
|
||||||
return nullret;
|
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 ||
|
if (x % TILE_SIZE >= TILE_SIZE - TILE_BORDER ||
|
||||||
y % TILE_SIZE >= TILE_SIZE - TILE_BORDER)
|
y % TILE_SIZE >= TILE_SIZE - TILE_BORDER)
|
||||||
return nullret;
|
return nullret;
|
||||||
} else if (button == CURSOR_UP || button == CURSOR_DOWN ||
|
} else if (button == CURSOR_UP || button == CURSOR_DOWN ||
|
||||||
button == CURSOR_RIGHT || button == CURSOR_LEFT) {
|
button == CURSOR_RIGHT || button == CURSOR_LEFT) {
|
||||||
if (button == CURSOR_UP && ui->cur_y > 0)
|
int dir;
|
||||||
ui->cur_y--;
|
switch (button) {
|
||||||
else if (button == CURSOR_DOWN && ui->cur_y < state->height-1)
|
case CURSOR_UP: dir = U; break;
|
||||||
ui->cur_y++;
|
case CURSOR_DOWN: dir = D; break;
|
||||||
else if (button == CURSOR_LEFT && ui->cur_x > 0)
|
case CURSOR_LEFT: dir = L; break;
|
||||||
ui->cur_x--;
|
case CURSOR_RIGHT: dir = R; break;
|
||||||
else if (button == CURSOR_RIGHT && ui->cur_x < state->width-1)
|
default: return nullret;
|
||||||
ui->cur_x++;
|
}
|
||||||
else
|
if (shift) {
|
||||||
return nullret; /* no cursor movement */
|
/*
|
||||||
ui->cur_visible = TRUE;
|
* Move origin.
|
||||||
return state; /* UI activity has occurred */
|
*/
|
||||||
|
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' ||
|
} else if (button == 'a' || button == 's' || button == 'd' ||
|
||||||
button == 'A' || button == 'S' || button == 'D') {
|
button == 'A' || button == 'S' || button == 'D') {
|
||||||
tx = ui->cur_x;
|
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.
|
* 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 x1, y1;
|
||||||
int complete = TRUE;
|
int complete = TRUE;
|
||||||
|
|
||||||
@ -1989,6 +2024,7 @@ static game_state *make_move(game_state *state, game_ui *ui,
|
|||||||
struct game_drawstate {
|
struct game_drawstate {
|
||||||
int started;
|
int started;
|
||||||
int width, height;
|
int width, height;
|
||||||
|
int org_x, org_y;
|
||||||
unsigned char *visible;
|
unsigned char *visible;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1999,6 +2035,7 @@ static game_drawstate *game_new_drawstate(game_state *state)
|
|||||||
ds->started = FALSE;
|
ds->started = FALSE;
|
||||||
ds->width = state->width;
|
ds->width = state->width;
|
||||||
ds->height = state->height;
|
ds->height = state->height;
|
||||||
|
ds->org_x = ds->org_y = -1;
|
||||||
ds->visible = snewn(state->width * state->height, unsigned char);
|
ds->visible = snewn(state->width * state->height, unsigned char);
|
||||||
memset(ds->visible, 0xFF, state->width * state->height);
|
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);
|
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 bx = WINDOW_OFFSET + TILE_SIZE * x;
|
||||||
int by = WINDOW_OFFSET + TILE_SIZE * y;
|
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) {
|
if (phase == 0) {
|
||||||
draw_rect_coords(fe, bx+x1, by+y1,
|
draw_rect_coords(fe, bx+x1, by+y1,
|
||||||
bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy,
|
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,
|
draw_rect_coords(fe, bx+x1, by+y1,
|
||||||
bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy,
|
bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy,
|
||||||
COL_WIRE);
|
barrier ? COL_WIRE : COL_BACKGROUND);
|
||||||
} else {
|
} else {
|
||||||
draw_rect_coords(fe, bx+x1, by+y1,
|
draw_rect_coords(fe, bx+x1, by+y1,
|
||||||
bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy,
|
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 bx = WINDOW_OFFSET + TILE_SIZE * x;
|
||||||
int by = WINDOW_OFFSET + TILE_SIZE * y;
|
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);
|
h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
|
||||||
|
|
||||||
if (phase == 0) {
|
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 {
|
} 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 bx = WINDOW_OFFSET + TILE_SIZE * x;
|
||||||
int by = WINDOW_OFFSET + TILE_SIZE * y;
|
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.
|
* otherwise not at all.
|
||||||
*/
|
*/
|
||||||
col = -1;
|
col = -1;
|
||||||
if (x == state->cx && y == state->cy)
|
if (src)
|
||||||
col = COL_WIRE;
|
col = COL_WIRE;
|
||||||
else if (COUNT(tile) == 1) {
|
else if (COUNT(tile) == 1) {
|
||||||
col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT);
|
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)
|
if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!(tile(state, ox, oy) & F(dir)))
|
if (!(tile(state, GX(ox), GY(oy)) & F(dir)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx);
|
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 (phase = 0; phase < 2; phase++) {
|
||||||
for (dir = 1; dir < 0x10; dir <<= 1)
|
for (dir = 1; dir < 0x10; dir <<= 1)
|
||||||
if (barrier(state, x, y) & (dir << 4))
|
if (barrier(state, GX(x), GY(y)) & (dir << 4))
|
||||||
draw_barrier_corner(fe, x, y, dir << 4, phase);
|
draw_barrier_corner(fe, x, y, dir << 4, phase, TRUE);
|
||||||
for (dir = 1; dir < 0x10; dir <<= 1)
|
for (dir = 1; dir < 0x10; dir <<= 1)
|
||||||
if (barrier(state, x, y) & dir)
|
if (barrier(state, GX(x), GY(y)) & dir)
|
||||||
draw_barrier(fe, x, y, dir, phase);
|
draw_barrier(fe, x, y, dir, phase, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
|
draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
|
||||||
@ -2328,23 +2375,34 @@ 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,
|
static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
|
||||||
game_state *state, int dir, game_ui *ui, float t, float ft)
|
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;
|
unsigned char *active;
|
||||||
float angle = 0.0;
|
float angle = 0.0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clear the screen and draw the exterior barrier lines if this
|
* Clear the screen if this is our first call.
|
||||||
* is our first call.
|
|
||||||
*/
|
*/
|
||||||
if (!ds->started) {
|
if (!ds->started) {
|
||||||
int phase;
|
|
||||||
|
|
||||||
ds->started = TRUE;
|
ds->started = TRUE;
|
||||||
|
|
||||||
draw_rect(fe, 0, 0,
|
draw_rect(fe, 0, 0,
|
||||||
WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER,
|
WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER,
|
||||||
WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER,
|
WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER,
|
||||||
COL_BACKGROUND);
|
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,
|
draw_update(fe, 0, 0,
|
||||||
WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER,
|
WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER,
|
||||||
WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER);
|
WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER);
|
||||||
@ -2352,33 +2410,25 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
|
|||||||
for (phase = 0; phase < 2; phase++) {
|
for (phase = 0; phase < 2; phase++) {
|
||||||
|
|
||||||
for (x = 0; x < ds->width; x++) {
|
for (x = 0; x < ds->width; x++) {
|
||||||
if (barrier(state, x, 0) & UL)
|
int ub = barrier(state, GX(x), GY(0));
|
||||||
draw_barrier_corner(fe, x, -1, LD, phase);
|
int db = barrier(state, GX(x), GY(ds->height-1));
|
||||||
if (barrier(state, x, 0) & RU)
|
draw_barrier_corner(fe, x, -1, LD, phase, ub & UL);
|
||||||
draw_barrier_corner(fe, x, -1, DR, phase);
|
draw_barrier_corner(fe, x, -1, DR, phase, ub & RU);
|
||||||
if (barrier(state, x, 0) & U)
|
draw_barrier(fe, x, -1, D, phase, ub & U);
|
||||||
draw_barrier(fe, x, -1, D, phase);
|
draw_barrier_corner(fe, x, ds->height, RU, phase, db & DR);
|
||||||
if (barrier(state, x, ds->height-1) & DR)
|
draw_barrier_corner(fe, x, ds->height, UL, phase, db & LD);
|
||||||
draw_barrier_corner(fe, x, ds->height, RU, phase);
|
draw_barrier(fe, x, ds->height, U, phase, db & D);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (y = 0; y < ds->height; y++) {
|
for (y = 0; y < ds->height; y++) {
|
||||||
if (barrier(state, 0, y) & UL)
|
int lb = barrier(state, GX(0), GY(y));
|
||||||
draw_barrier_corner(fe, -1, y, RU, phase);
|
int rb = barrier(state, GX(ds->width-1), GY(y));
|
||||||
if (barrier(state, 0, y) & LD)
|
draw_barrier_corner(fe, -1, y, RU, phase, lb & UL);
|
||||||
draw_barrier_corner(fe, -1, y, DR, phase);
|
draw_barrier_corner(fe, -1, y, DR, phase, lb & LD);
|
||||||
if (barrier(state, 0, y) & L)
|
draw_barrier(fe, -1, y, R, phase, lb & L);
|
||||||
draw_barrier(fe, -1, y, R, phase);
|
draw_barrier_corner(fe, ds->width, y, UL, phase, rb & RU);
|
||||||
if (barrier(state, ds->width-1, y) & RU)
|
draw_barrier_corner(fe, ds->width, y, LD, phase, rb & DR);
|
||||||
draw_barrier_corner(fe, ds->width, y, UL, phase);
|
draw_barrier(fe, ds->width, y, L, phase, rb & R);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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.
|
* 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 (x = 0; x < ds->width; x++)
|
||||||
for (y = 0; y < ds->height; y++) {
|
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
|
* 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.
|
* the frame number.
|
||||||
*/
|
*/
|
||||||
if (frame >= 0) {
|
if (frame >= 0) {
|
||||||
|
int rcx = RX(ui->cx), rcy = RY(ui->cy);
|
||||||
int xdist, ydist, dist;
|
int xdist, ydist, dist;
|
||||||
xdist = (x < state->cx ? state->cx - x : x - state->cx);
|
xdist = (x < rcx ? rcx - x : x - rcx);
|
||||||
ydist = (y < state->cy ? state->cy - y : y - state->cy);
|
ydist = (y < rcy ? rcy - y : y - rcy);
|
||||||
dist = (xdist > ydist ? xdist : ydist);
|
dist = (xdist > ydist ? xdist : ydist);
|
||||||
|
|
||||||
if (frame >= dist && frame < dist+4) {
|
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 ||
|
index(state, ds->visible, x, y) == 0xFF ||
|
||||||
(x == tx && y == ty) ||
|
is_src || is_anim || is_cursor) {
|
||||||
(ui->cur_visible && x == ui->cur_x && y == ui->cur_y)) {
|
draw_tile(fe, state, ds, x, y, c,
|
||||||
draw_tile(fe, state, x, y, c,
|
is_src, (is_anim ? angle : 0.0F), is_cursor);
|
||||||
(x == tx && y == ty ? angle : 0.0F),
|
if (is_src || is_anim || is_cursor)
|
||||||
(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))
|
|
||||||
index(state, ds->visible, x, y) = 0xFF;
|
index(state, ds->visible, x, y) = 0xFF;
|
||||||
else
|
else
|
||||||
index(state, ds->visible, x, y) = c;
|
index(state, ds->visible, x, y) = c;
|
||||||
@ -2505,16 +2559,11 @@ static float game_flash_length(game_state *oldstate,
|
|||||||
*/
|
*/
|
||||||
if (!oldstate->completed && newstate->completed &&
|
if (!oldstate->completed && newstate->completed &&
|
||||||
!oldstate->used_solve && !newstate->used_solve) {
|
!oldstate->used_solve && !newstate->used_solve) {
|
||||||
int size;
|
int size = 0;
|
||||||
size = 0;
|
if (size < newstate->width)
|
||||||
if (size < newstate->cx+1)
|
size = newstate->width;
|
||||||
size = newstate->cx+1;
|
if (size < newstate->height)
|
||||||
if (size < newstate->cy+1)
|
size = newstate->height;
|
||||||
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;
|
|
||||||
return FLASH_FRAME * (size+4);
|
return FLASH_FRAME * (size+4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1006,6 +1006,8 @@ static game_state *make_move(game_state *state, game_ui *ui,
|
|||||||
int n, dx, dy;
|
int n, dx, dy;
|
||||||
game_state *ret;
|
game_state *ret;
|
||||||
|
|
||||||
|
button &= ~MOD_MASK;
|
||||||
|
|
||||||
if (button != LEFT_BUTTON && button != RIGHT_BUTTON)
|
if (button != LEFT_BUTTON && button != RIGHT_BUTTON)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -769,6 +769,8 @@ static game_state *make_move(game_state *from, game_ui *ui,
|
|||||||
{
|
{
|
||||||
game_state *ret;
|
game_state *ret;
|
||||||
|
|
||||||
|
button &= ~MOD_MASK;
|
||||||
|
|
||||||
x = FROMCOORD(from->w, x);
|
x = FROMCOORD(from->w, x);
|
||||||
y = FROMCOORD(from->h, y);
|
y = FROMCOORD(from->h, y);
|
||||||
|
|
||||||
|
15
puzzles.but
15
puzzles.but
@ -320,6 +320,21 @@ controls are:
|
|||||||
also unlock it again, but while it's locked you can't accidentally
|
also unlock it again, but while it's locked you can't accidentally
|
||||||
turn it.
|
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
|
\dt \e{Jumble tiles}: \q{J} key
|
||||||
|
|
||||||
\dd This key turns all tiles that are not locked to random
|
\dd This key turns all tiles that are not locked to random
|
||||||
|
@ -32,7 +32,10 @@ enum {
|
|||||||
CURSOR_LEFT,
|
CURSOR_LEFT,
|
||||||
CURSOR_RIGHT,
|
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) <= \
|
#define IS_MOUSE_DOWN(m) ( (unsigned)((m) - LEFT_BUTTON) <= \
|
||||||
|
2
rect.c
2
rect.c
@ -2127,6 +2127,8 @@ static game_state *make_move(game_state *from, game_ui *ui,
|
|||||||
int startdrag = FALSE, enddrag = FALSE, active = FALSE;
|
int startdrag = FALSE, enddrag = FALSE, active = FALSE;
|
||||||
game_state *ret;
|
game_state *ret;
|
||||||
|
|
||||||
|
button &= ~MOD_MASK;
|
||||||
|
|
||||||
if (button == LEFT_BUTTON) {
|
if (button == LEFT_BUTTON) {
|
||||||
startdrag = TRUE;
|
startdrag = TRUE;
|
||||||
} else if (button == LEFT_RELEASE) {
|
} else if (button == LEFT_RELEASE) {
|
||||||
|
@ -572,6 +572,7 @@ static game_state *make_move(game_state *from, game_ui *ui,
|
|||||||
int dx, dy, tx, ty, n;
|
int dx, dy, tx, ty, n;
|
||||||
game_state *ret;
|
game_state *ret;
|
||||||
|
|
||||||
|
button &= ~MOD_MASK;
|
||||||
if (button != LEFT_BUTTON && button != RIGHT_BUTTON)
|
if (button != LEFT_BUTTON && button != RIGHT_BUTTON)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
2
solo.c
2
solo.c
@ -1828,7 +1828,7 @@ static game_state *make_move(game_state *from, game_ui *ui, int x, int y,
|
|||||||
int tx, ty;
|
int tx, ty;
|
||||||
game_state *ret;
|
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;
|
tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1;
|
||||||
ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1;
|
ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1;
|
||||||
|
@ -594,6 +594,8 @@ static game_state *make_move(game_state *from, game_ui *ui, int x, int y,
|
|||||||
game_state *ret;
|
game_state *ret;
|
||||||
int dir;
|
int dir;
|
||||||
|
|
||||||
|
button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
|
||||||
|
|
||||||
if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
|
if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
|
||||||
/*
|
/*
|
||||||
* Determine the coordinates of the click. We offset by n-1
|
* Determine the coordinates of the click. We offset by n-1
|
||||||
|
20
windows.c
20
windows.c
@ -1221,31 +1221,35 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
|
|||||||
case WM_KEYDOWN:
|
case WM_KEYDOWN:
|
||||||
{
|
{
|
||||||
int key = -1;
|
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) {
|
switch (wParam) {
|
||||||
case VK_LEFT:
|
case VK_LEFT:
|
||||||
if (!(lParam & 0x01000000))
|
if (!(lParam & 0x01000000))
|
||||||
key = MOD_NUM_KEYPAD | '4';
|
key = MOD_NUM_KEYPAD | '4';
|
||||||
else
|
else
|
||||||
key = CURSOR_LEFT;
|
key = shift | ctrl | CURSOR_LEFT;
|
||||||
break;
|
break;
|
||||||
case VK_RIGHT:
|
case VK_RIGHT:
|
||||||
if (!(lParam & 0x01000000))
|
if (!(lParam & 0x01000000))
|
||||||
key = MOD_NUM_KEYPAD | '6';
|
key = MOD_NUM_KEYPAD | '6';
|
||||||
else
|
else
|
||||||
key = CURSOR_RIGHT;
|
key = shift | ctrl | CURSOR_RIGHT;
|
||||||
break;
|
break;
|
||||||
case VK_UP:
|
case VK_UP:
|
||||||
if (!(lParam & 0x01000000))
|
if (!(lParam & 0x01000000))
|
||||||
key = MOD_NUM_KEYPAD | '8';
|
key = MOD_NUM_KEYPAD | '8';
|
||||||
else
|
else
|
||||||
key = CURSOR_UP;
|
key = shift | ctrl | CURSOR_UP;
|
||||||
break;
|
break;
|
||||||
case VK_DOWN:
|
case VK_DOWN:
|
||||||
if (!(lParam & 0x01000000))
|
if (!(lParam & 0x01000000))
|
||||||
key = MOD_NUM_KEYPAD | '2';
|
key = MOD_NUM_KEYPAD | '2';
|
||||||
else
|
else
|
||||||
key = CURSOR_DOWN;
|
key = shift | ctrl | CURSOR_DOWN;
|
||||||
break;
|
break;
|
||||||
/*
|
/*
|
||||||
* Diagonal keys on the numeric keypad.
|
* Diagonal keys on the numeric keypad.
|
||||||
|
Reference in New Issue
Block a user