David Nickerson reports odd behaviour involving a drag start point
[sgt/puzzles] / pearl.c
diff --git a/pearl.c b/pearl.c
index c018d5a..2ff58f6 100644 (file)
--- a/pearl.c
+++ b/pearl.c
@@ -1335,6 +1335,8 @@ static int new_clues(game_params *params, random_state *rs,
        break;                         /* got it */
     }
 
+    debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h));
+
     return ngen;
 }
 
@@ -1343,14 +1345,12 @@ static char *new_game_desc(game_params *params, random_state *rs,
 {
     char *grid, *clues;
     char *desc;
-    int ngen, w = params->w, h = params->h, i, j;
+    int w = params->w, h = params->h, i, j;
 
     grid = snewn(w*h, char);
     clues = snewn(w*h, char);
 
-    ngen = new_clues(params, rs, clues, grid);
-
-    debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h));
+    new_clues(params, rs, clues, grid);
 
     desc = snewn(w * h + 1, char);
     for (i = j = 0; i < w*h; i++) {
@@ -1717,7 +1717,8 @@ static char *game_text_format(game_state *state)
 
 struct game_ui {
     int *dragcoords;       /* list of (y*w+x) coords in drag so far */
-    int ndragcoords;       /* number of entries in dragcoords. 0 = no drag. */
+    int ndragcoords;       /* number of entries in dragcoords.
+                            * 0 = click but no drag yet. -1 = no drag at all */
     int clickx, clicky;    /* pixel position of initial click */
 };
 
@@ -1726,7 +1727,7 @@ static game_ui *new_ui(game_state *state)
     game_ui *ui = snew(game_ui);
     int sz = state->shared->sz;
 
-    ui->ndragcoords = 0;
+    ui->ndragcoords = -1;
     ui->dragcoords = snewn(sz, int);
 
     return ui;
@@ -1805,6 +1806,9 @@ static void update_ui_drag(game_state *state, game_ui *ui, int gx, int gy)
     if (!INGRID(state, gx, gy))
         return;                        /* square is outside grid */
 
+    if (ui->ndragcoords < 0)
+        return;                        /* drag not in progress anyway */
+
     pos = gy * w + gx;
 
     lastpos = ui->dragcoords[ui->ndragcoords > 0 ? ui->ndragcoords-1 : 0];
@@ -1836,7 +1840,15 @@ static void update_ui_drag(game_state *state, game_ui *ui, int gx, int gy)
     if (ox == gx || oy == gy) {
         int dx = (gx < ox ? -1 : gx > ox ? +1 : 0);
         int dy = (gy < oy ? -1 : gy > oy ? +1 : 0);
+        int dir = (dy>0 ? D : dy<0 ? U : dx>0 ? R : L);
         while (ox != gx || oy != gy) {
+            /*
+             * If the drag attempts to cross a 'no line here' mark,
+             * stop there. We physically don't allow the user to drag
+             * over those marks.
+             */
+            if (state->marks[oy*w+ox] & dir)
+                break;
             ox += dx;
             oy += dy;
             ui->dragcoords[ui->ndragcoords++] = oy * w + ox;
@@ -1907,8 +1919,11 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
     int gx = FROMCOORD(x), gy = FROMCOORD(y), i;
     char tmpbuf[80];
 
-    if (button == LEFT_BUTTON) {
-        if (!INGRID(state, gx, gy)) return NULL;
+    if (IS_MOUSE_DOWN(button)) {
+        if (!INGRID(state, gx, gy)) {
+            ui->ndragcoords = -1;
+            return NULL;
+        }
 
         ui->clickx = x; ui->clicky = y;
         ui->dragcoords[0] = gy * w + gx;
@@ -1917,13 +1932,13 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
         return "";
     }
 
-    if (IS_MOUSE_DRAG(button)) {
+    if (button == LEFT_DRAG && ui->ndragcoords >= 0) {
         update_ui_drag(state, ui, gx, gy);
         return "";
     }
 
     if (IS_MOUSE_RELEASE(button)) {
-        if (ui->ndragcoords) {
+        if (ui->ndragcoords > 0) {
             /* End of a drag: process the cached line data. */
             int buflen = 0, bufsize = 256, tmplen;
             char *buf = NULL;
@@ -1949,15 +1964,31 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
                 }
             }
 
-            ui->ndragcoords = 0;
+            ui->ndragcoords = -1;
 
             return buf ? buf : "";
-        } else {
-            /* Click (or tiny drag). Work out which edge we were closest to. */
-            int cx = COORD(gx) + TILE_SIZE/2, cy = COORD(gy) + TILE_SIZE/2;
+        } else if (ui->dragcoords == 0) {
+            /* Click (or tiny drag). Work out which edge we were
+             * closest to. */
+            int cx, cy;
             int gx2, gy2, l1, l2, ismark = (button == RIGHT_RELEASE);
             char movec = ismark ? 'M' : 'F';
 
+            ui->ndragcoords = -1;
+
+            /*
+             * We process clicks based on the mouse-down location,
+             * because that's more natural for a user to carefully
+             * control than the mouse-up.
+             */
+            x = ui->clickx;
+            y = ui->clicky;
+
+            gx = FROMCOORD(x);
+            gy = FROMCOORD(y);
+            cx = COORD(gx) + TILE_SIZE/2;
+            cy = COORD(gy) + TILE_SIZE/2;
+
             if (!INGRID(state, gx, gy)) return "";
 
             if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) {
@@ -2023,9 +2054,6 @@ static game_state *execute_move(game_state *state, char *move)
             if (!INGRID(state, x, y)) goto badmove;
             if (l < 0 || l > 15) goto badmove;
 
-            /* TODO trying to set a line over a no-line mark should be
-             * a failed move? */
-
             if (c == 'L')
                 ret->lines[y*w + x] |= (char)l;
             else if (c == 'N')
@@ -2038,6 +2066,16 @@ static game_state *execute_move(game_state *state, char *move)
             else if (c == 'M')
                 ret->marks[y*w + x] ^= (char)l;
 
+            /*
+             * If we ended up trying to lay a line _over_ a mark,
+             * that's a failed move: interpret_move() should have
+             * ensured we never received a move string like that in
+             * the first place.
+             */
+            if ((ret->lines[y*w + x] & (char)l) &&
+                (ret->marks[y*w + x] & (char)l))
+                goto badmove;
+
             move += n;
         } else if (strcmp(move, "H") == 0) {
             pearl_solve(ret->shared->w, ret->shared->h,
@@ -2292,7 +2330,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
         flashing = DS_FLASH;
 
     memset(ds->draglines, 0, sz);
-    if (ui->dragcoords) {
+    if (ui->ndragcoords > 0) {
         int i, clearing = TRUE;
         for (i = 0; i < ui->ndragcoords - 1; i++) {
             int sx, sy, dx, dy, dir, oldstate, newstate;