chiark / gitweb /
Keyboard control patch for Twiddle, from James H.
authorSimon Tatham <anakin@pobox.com>
Wed, 28 Jan 2009 18:27:10 +0000 (18:27 +0000)
committerSimon Tatham <anakin@pobox.com>
Wed, 28 Jan 2009 18:27:10 +0000 (18:27 +0000)
[originally from svn r8438]

puzzles.but
twiddle.c

index 32f331a8db83d682ebf45f46975bd72fccca3b8e..b143d5095c2fef39f2e7c183b7db6d5e61b8f7e7 100644 (file)
@@ -707,6 +707,11 @@ the centre tile of the square you want to rotate.
 Clicking with the left mouse button rotates the group anticlockwise.
 Clicking with the right button rotates it clockwise.
 
 Clicking with the left mouse button rotates the group anticlockwise.
 Clicking with the right button rotates it clockwise.
 
+You can also move an outline square around the grid with the cursor
+keys; the square is the size above (2\by\.2 by default, or larger).
+Pressing the return key or space bar will rotate the current square
+anticlockwise or clockwise respectively.
+
 (All the actions described in \k{common-actions} are also available.)
 
 \H{twiddle-parameters} \I{parameters, for Twiddle}Twiddle parameters
 (All the actions described in \k{common-actions} are also available.)
 
 \H{twiddle-parameters} \I{parameters, for Twiddle}Twiddle parameters
index c12872fa94ba83f297e76bc4c2ca6810a99c0b91..8c97a7094a5456f8447a16017ce9e36dfe346da6 100644 (file)
--- a/twiddle.c
+++ b/twiddle.c
@@ -31,6 +31,7 @@ enum {
     COL_HIGHLIGHT_GENTLE,
     COL_LOWLIGHT,
     COL_LOWLIGHT_GENTLE,
     COL_HIGHLIGHT_GENTLE,
     COL_LOWLIGHT,
     COL_LOWLIGHT_GENTLE,
+    COL_HIGHCURSOR, COL_LOWCURSOR,
     NCOLOURS
 };
 
     NCOLOURS
 };
 
@@ -597,13 +598,25 @@ static char *game_text_format(game_state *state)
     return ret;
 }
 
     return ret;
 }
 
+struct game_ui {
+    int cur_x, cur_y;
+    int cur_visible;
+};
+
 static game_ui *new_ui(game_state *state)
 {
 static game_ui *new_ui(game_state *state)
 {
-    return NULL;
+    game_ui *ui = snew(game_ui);
+
+    ui->cur_x = 0;
+    ui->cur_y = 0;
+    ui->cur_visible = FALSE;
+
+    return ui;
 }
 
 static void free_ui(game_ui *ui)
 {
 }
 
 static void free_ui(game_ui *ui)
 {
+    sfree(ui);
 }
 
 static char *encode_ui(game_ui *ui)
 }
 
 static char *encode_ui(game_ui *ui)
@@ -625,6 +638,7 @@ struct game_drawstate {
     int w, h, bgcolour;
     int *grid;
     int tilesize;
     int w, h, bgcolour;
     int *grid;
     int tilesize;
+    int cur_x, cur_y;
 };
 
 static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
 };
 
 static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
@@ -636,6 +650,19 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
 
     button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
 
 
     button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
 
+    if (IS_CURSOR_MOVE(button)) {
+        if (button == CURSOR_LEFT && ui->cur_x > 0)
+            ui->cur_x--;
+        if (button == CURSOR_RIGHT && (ui->cur_x+n) < (w))
+            ui->cur_x++;
+        if (button == CURSOR_UP && ui->cur_y > 0)
+            ui->cur_y--;
+        if (button == CURSOR_DOWN && (ui->cur_y+n) < (h))
+            ui->cur_y++;
+        ui->cur_visible = 1;
+        return "";
+    }
+
     if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
        /*
         * Determine the coordinates of the click. We offset by n-1
     if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
        /*
         * Determine the coordinates of the click. We offset by n-1
@@ -649,6 +676,16 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
        dir = (button == LEFT_BUTTON ? 1 : -1);
        if (x < 0 || x > w-n || y < 0 || y > h-n)
            return NULL;
        dir = (button == LEFT_BUTTON ? 1 : -1);
        if (x < 0 || x > w-n || y < 0 || y > h-n)
            return NULL;
+        ui->cur_visible = 0;
+    } else if (IS_CURSOR_SELECT(button)) {
+        if (ui->cur_visible) {
+            x = ui->cur_x;
+            y = ui->cur_y;
+            dir = (button == CURSOR_SELECT2) ? -1 : +1;
+        } else {
+            ui->cur_visible = 1;
+            return "";
+        }
     } else if (button == 'a' || button == 'A' || button==MOD_NUM_KEYPAD+'7') {
         x = y = 0;
         dir = (button == 'A' ? -1 : +1);
     } else if (button == 'a' || button == 'A' || button==MOD_NUM_KEYPAD+'7') {
         x = y = 0;
         dir = (button == 'A' ? -1 : +1);
@@ -770,10 +807,16 @@ static float *game_colours(frontend *fe, int *ncolours)
 
     game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
 
 
     game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
 
+    /* cursor is light-background with a red tinge. */
+    ret[COL_HIGHCURSOR * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 1.0F;
+    ret[COL_HIGHCURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.5F;
+    ret[COL_HIGHCURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.5F;
+
     for (i = 0; i < 3; i++) {
         ret[COL_HIGHLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 1.1F;
         ret[COL_LOWLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.9F;
         ret[COL_TEXT * 3 + i] = 0.0;
     for (i = 0; i < 3; i++) {
         ret[COL_HIGHLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 1.1F;
         ret[COL_LOWLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.9F;
         ret[COL_TEXT * 3 + i] = 0.0;
+        ret[COL_LOWCURSOR * 3 + i] = ret[COL_HIGHCURSOR * 3 + i] * 0.6F;
     }
 
     *ncolours = NCOLOURS;
     }
 
     *ncolours = NCOLOURS;
@@ -793,6 +836,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->grid[i] = -1;
     ds->tilesize = 0;                  /* haven't decided yet */
     for (i = 0; i < ds->w*ds->h; i++)
         ds->grid[i] = -1;
+    ds->cur_x = ds->cur_y = -state->n;
 
     return ds;
 }
 
     return ds;
 }
@@ -813,20 +857,25 @@ struct rotation {
 static void rotate(int *xy, struct rotation *rot)
 {
     if (rot) {
 static void rotate(int *xy, struct rotation *rot)
 {
     if (rot) {
-       float xf = xy[0] - rot->ox, yf = xy[1] - rot->oy;
+       float xf = (float)xy[0] - rot->ox, yf = (float)xy[1] - rot->oy;
        float xf2, yf2;
 
        xf2 = rot->c * xf + rot->s * yf;
        yf2 = - rot->s * xf + rot->c * yf;
 
        float xf2, yf2;
 
        xf2 = rot->c * xf + rot->s * yf;
        yf2 = - rot->s * xf + rot->c * yf;
 
-       xy[0] = xf2 + rot->ox + 0.5;   /* round to nearest */
-       xy[1] = yf2 + rot->oy + 0.5;   /* round to nearest */
+       xy[0] = (int)(xf2 + rot->ox + 0.5);   /* round to nearest */
+       xy[1] = (int)(yf2 + rot->oy + 0.5);   /* round to nearest */
     }
 }
 
     }
 }
 
+#define CUR_TOP         1
+#define CUR_RIGHT       2
+#define CUR_BOTTOM      4
+#define CUR_LEFT        8
+
 static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state,
                       int x, int y, int tile, int flash_colour,
 static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state,
                       int x, int y, int tile, int flash_colour,
-                      struct rotation *rot)
+                      struct rotation *rot, unsigned cedges)
 {
     int coords[8];
     char str[40];
 {
     int coords[8];
     char str[40];
@@ -863,28 +912,28 @@ static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state,
     coords[3] = y;
     rotate(coords+2, rot);
     draw_polygon(dr, coords, 3, rot ? rot->rc : COL_LOWLIGHT,
     coords[3] = y;
     rotate(coords+2, rot);
     draw_polygon(dr, coords, 3, rot ? rot->rc : COL_LOWLIGHT,
-                rot ? rot->rc : COL_LOWLIGHT);
+                rot ? rot->rc : (cedges & CUR_RIGHT) ? COL_LOWCURSOR : COL_LOWLIGHT);
 
     /* Bottom side. */
     coords[2] = x;
     coords[3] = y + TILE_SIZE - 1;
     rotate(coords+2, rot);
     draw_polygon(dr, coords, 3, rot ? rot->bc : COL_LOWLIGHT,
 
     /* Bottom side. */
     coords[2] = x;
     coords[3] = y + TILE_SIZE - 1;
     rotate(coords+2, rot);
     draw_polygon(dr, coords, 3, rot ? rot->bc : COL_LOWLIGHT,
-                rot ? rot->bc : COL_LOWLIGHT);
+                rot ? rot->bc : (cedges & CUR_BOTTOM) ? COL_LOWCURSOR : COL_LOWLIGHT);
 
     /* Left side. */
     coords[0] = x;
     coords[1] = y;
     rotate(coords+0, rot);
     draw_polygon(dr, coords, 3, rot ? rot->lc : COL_HIGHLIGHT,
 
     /* Left side. */
     coords[0] = x;
     coords[1] = y;
     rotate(coords+0, rot);
     draw_polygon(dr, coords, 3, rot ? rot->lc : COL_HIGHLIGHT,
-                rot ? rot->lc : COL_HIGHLIGHT);
+                rot ? rot->lc : (cedges & CUR_LEFT) ? COL_HIGHCURSOR : COL_HIGHLIGHT);
 
     /* Top side. */
     coords[2] = x + TILE_SIZE - 1;
     coords[3] = y;
     rotate(coords+2, rot);
     draw_polygon(dr, coords, 3, rot ? rot->tc : COL_HIGHLIGHT,
 
     /* Top side. */
     coords[2] = x + TILE_SIZE - 1;
     coords[3] = y;
     rotate(coords+2, rot);
     draw_polygon(dr, coords, 3, rot ? rot->tc : COL_HIGHLIGHT,
-                rot ? rot->tc : COL_HIGHLIGHT);
+                rot ? rot->tc : (cedges & CUR_TOP) ? COL_HIGHCURSOR : COL_HIGHLIGHT);
 
     /*
      * Now the main blank area in the centre of the tile.
 
     /*
      * Now the main blank area in the centre of the tile.
@@ -1008,7 +1057,7 @@ static int highlight_colour(float angle)
 static float game_anim_length(game_state *oldstate, game_state *newstate,
                              int dir, game_ui *ui)
 {
 static float game_anim_length(game_state *oldstate, game_state *newstate,
                              int dir, game_ui *ui)
 {
-    return ANIM_PER_RADIUS_UNIT * sqrt(newstate->n-1);
+    return (float)(ANIM_PER_RADIUS_UNIT * sqrt(newstate->n-1));
 }
 
 static float game_flash_length(game_state *oldstate, game_state *newstate,
 }
 
 static float game_flash_length(game_state *oldstate, game_state *newstate,
@@ -1028,6 +1077,12 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
     int i, bgcolour;
     struct rotation srot, *rot;
     int lastx = -1, lasty = -1, lastr = -1;
     int i, bgcolour;
     struct rotation srot, *rot;
     int lastx = -1, lasty = -1, lastr = -1;
+    int cx, cy, cmoved = 0, n = state->n;
+
+    cx = ui->cur_visible ? ui->cur_x : -state->n;
+    cy = ui->cur_visible ? ui->cur_y : -state->n;
+    if (cx != ds->cur_x || cy != ds->cur_y)
+        cmoved = 1;
 
     if (flashtime > 0) {
         int frame = (int)(flashtime / FLASH_FRAME);
 
     if (flashtime > 0) {
         int frame = (int)(flashtime / FLASH_FRAME);
@@ -1092,17 +1147,17 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
        rot->cw = rot->ch = TILE_SIZE * state->n;
        rot->ox = rot->cx + rot->cw/2;
        rot->oy = rot->cy + rot->ch/2;
        rot->cw = rot->ch = TILE_SIZE * state->n;
        rot->ox = rot->cx + rot->cw/2;
        rot->oy = rot->cy + rot->ch/2;
-       angle = (-PI/2 * lastr) * (1.0 - animtime / anim_max);
-       rot->c = cos(angle);
-       rot->s = sin(angle);
+       angle = (float)((-PI/2 * lastr) * (1.0 - animtime / anim_max));
+       rot->c = (float)cos(angle);
+       rot->s = (float)sin(angle);
 
        /*
         * Sort out the colours of the various sides of the tile.
         */
 
        /*
         * Sort out the colours of the various sides of the tile.
         */
-       rot->lc = highlight_colour(PI + angle);
+       rot->lc = highlight_colour((float)PI + angle);
        rot->rc = highlight_colour(angle);
        rot->rc = highlight_colour(angle);
-       rot->tc = highlight_colour(PI/2 + angle);
-       rot->bc = highlight_colour(-PI/2 + angle);
+       rot->tc = highlight_colour((float)(PI/2.0) + angle);
+       rot->bc = highlight_colour((float)(-PI/2.0) + angle);
 
        draw_rect(dr, rot->cx, rot->cy, rot->cw, rot->ch, bgcolour);
     } else
 
        draw_rect(dr, rot->cx, rot->cy, rot->cw, rot->ch, bgcolour);
     } else
@@ -1112,7 +1167,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
      * Now draw each tile.
      */
     for (i = 0; i < state->w * state->h; i++) {
      * Now draw each tile.
      */
     for (i = 0; i < state->w * state->h; i++) {
-       int t;
+       int t, cc = 0;
        int tx = i % state->w, ty = i / state->w;
 
        /*
        int tx = i % state->w, ty = i / state->w;
 
        /*
@@ -1129,15 +1184,31 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
        else
            t = state->grid[i];
 
        else
            t = state->grid[i];
 
+        if (cmoved) {
+            /* cursor has moved (or changed visibility)... */
+            if (tx == cx || tx == cx+n-1 || ty == cy || ty == cy+n-1)
+                cc = 1; /* ...we're on new cursor, redraw */
+            if (tx == ds->cur_x || tx == ds->cur_x+n-1 ||
+                ty == ds->cur_y || ty == ds->cur_y+n-1)
+                cc = 1; /* ...we were on old cursor, redraw */
+        }
+
        if (ds->bgcolour != bgcolour ||   /* always redraw when flashing */
        if (ds->bgcolour != bgcolour ||   /* always redraw when flashing */
-           ds->grid[i] != t || ds->grid[i] == -1 || t == -1) {
+           ds->grid[i] != t || ds->grid[i] == -1 || t == -1 || cc) {
            int x = COORD(tx), y = COORD(ty);
            int x = COORD(tx), y = COORD(ty);
+            unsigned cedges = 0;
+
+            if (tx == cx     && ty >= cy && ty <= cy+n-1) cedges |= CUR_LEFT;
+            if (ty == cy     && tx >= cx && tx <= cx+n-1) cedges |= CUR_TOP;
+            if (tx == cx+n-1 && ty >= cy && ty <= cy+n-1) cedges |= CUR_RIGHT;
+            if (ty == cy+n-1 && tx >= cx && tx <= cx+n-1) cedges |= CUR_BOTTOM;
 
 
-           draw_tile(dr, ds, state, x, y, state->grid[i], bgcolour, rot);
+           draw_tile(dr, ds, state, x, y, state->grid[i], bgcolour, rot, cedges);
             ds->grid[i] = t;
         }
     }
     ds->bgcolour = bgcolour;
             ds->grid[i] = t;
         }
     }
     ds->bgcolour = bgcolour;
+    ds->cur_x = cx; ds->cur_y = cy;
 
     /*
      * Update the status bar.
 
     /*
      * Update the status bar.
@@ -1221,3 +1292,5 @@ const struct game thegame = {
     FALSE, game_timing_state,
     0,                                /* flags */
 };
     FALSE, game_timing_state,
     0,                                /* flags */
 };
+
+/* vim: set shiftwidth=4 tabstop=8: */