aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fifteen.c38
-rw-r--r--lightup.c33
-rw-r--r--loopy.c182
-rw-r--r--map.c62
-rw-r--r--misc.c2
-rw-r--r--pearl.c76
-rw-r--r--puzzles.h4
-rw-r--r--range.c72
-rw-r--r--signpost.c48
-rw-r--r--slant.c26
-rw-r--r--towers.c54
11 files changed, 402 insertions, 195 deletions
diff --git a/fifteen.c b/fifteen.c
index 224a712..d02d0c0 100644
--- a/fifteen.c
+++ b/fifteen.c
@@ -449,13 +449,44 @@ static char *game_text_format(const game_state *state)
return ret;
}
+struct game_ui {
+ /*
+ * User-preference option: invert the direction of arrow-key
+ * control, so that the arrow on the key you press indicates in
+ * which direction you want the _space_ to move, rather than in
+ * which direction you want a tile to move to fill the space.
+ */
+ bool invert_cursor;
+};
+
+static void legacy_prefs_override(struct game_ui *ui_out)
+{
+ static bool initialised = false;
+ static int invert_cursor = -1;
+
+ if (!initialised) {
+ initialised = true;
+ invert_cursor = getenv_bool("FIFTEEN_INVERT_CURSOR", -1);
+ }
+
+ if (invert_cursor != -1)
+ ui_out->invert_cursor = invert_cursor;
+}
+
static game_ui *new_ui(const game_state *state)
{
- return NULL;
+ struct game_ui *ui = snew(struct game_ui);
+
+ ui->invert_cursor = false;
+
+ legacy_prefs_override(ui);
+
+ return ui;
}
static void free_ui(game_ui *ui)
{
+ sfree(ui);
}
static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -708,11 +739,8 @@ static char *interpret_move(const game_state *state, game_ui *ui,
if (nx < 0 || nx >= state->w || ny < 0 || ny >= state->h)
return NULL; /* out of bounds */
} else if (IS_CURSOR_MOVE(button)) {
- static int invert_cursor = -1;
- if (invert_cursor == -1)
- invert_cursor = getenv_bool("FIFTEEN_INVERT_CURSOR", false);
button = flip_cursor(button); /* the default */
- if (invert_cursor)
+ if (ui->invert_cursor)
button = flip_cursor(button); /* undoes the first flip */
move_cursor(button, &nx, &ny, state->w, state->h, false);
} else if ((button == 'h' || button == 'H') && !state->completed) {
diff --git a/lightup.c b/lightup.c
index ca0d962..779437b 100644
--- a/lightup.c
+++ b/lightup.c
@@ -1833,13 +1833,37 @@ static char *game_text_format(const game_state *state)
struct game_ui {
int cur_x, cur_y;
bool cur_visible;
+
+ /*
+ * User preference: when a square contains both a black blob for
+ * 'user is convinced this isn't a light' and a yellow highlight
+ * for 'this square is lit by a light', both of which rule out it
+ * being a light, should we still bother to show the blob?
+ */
+ bool draw_blobs_when_lit;
};
+static void legacy_prefs_override(struct game_ui *ui_out)
+{
+ static bool initialised = false;
+ static int draw_blobs_when_lit = -1;
+
+ if (!initialised) {
+ initialised = true;
+ draw_blobs_when_lit = getenv_bool("LIGHTUP_LIT_BLOBS", -1);
+ }
+
+ if (draw_blobs_when_lit != -1)
+ ui_out->draw_blobs_when_lit = draw_blobs_when_lit;
+}
+
static game_ui *new_ui(const game_state *state)
{
game_ui *ui = snew(game_ui);
ui->cur_x = ui->cur_y = 0;
ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
+ ui->draw_blobs_when_lit = true;
+ legacy_prefs_override(ui);
return ui;
}
@@ -2130,7 +2154,7 @@ static unsigned int tile_flags(game_drawstate *ds, const game_state *state,
return ret;
}
-static void tile_redraw(drawing *dr, game_drawstate *ds,
+static void tile_redraw(drawing *dr, game_drawstate *ds, const game_ui *ui,
const game_state *state, int x, int y)
{
unsigned int ds_flags = GRID(ds, flags, x, y);
@@ -2160,10 +2184,7 @@ static void tile_redraw(drawing *dr, game_drawstate *ds,
draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, TILE_RADIUS,
lcol, COL_BLACK);
} else if ((ds_flags & DF_IMPOSSIBLE)) {
- static int draw_blobs_when_lit = -1;
- if (draw_blobs_when_lit < 0)
- draw_blobs_when_lit = getenv_bool("LIGHTUP_LIT_BLOBS", true);
- if (!(ds_flags & DF_LIT) || draw_blobs_when_lit) {
+ if (!(ds_flags & DF_LIT) || ui->draw_blobs_when_lit) {
int rlen = TILE_SIZE / 4;
draw_rect(dr, dx + TILE_SIZE/2 - rlen/2,
dy + TILE_SIZE/2 - rlen/2,
@@ -2208,7 +2229,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
unsigned int ds_flags = tile_flags(ds, state, ui, x, y, flashing);
if (ds_flags != GRID(ds, flags, x, y)) {
GRID(ds, flags, x, y) = ds_flags;
- tile_redraw(dr, ds, state, x, y);
+ tile_redraw(dr, ds, ui, state, x, y);
}
}
}
diff --git a/loopy.c b/loopy.c
index e7bf9c0..0f9dc52 100644
--- a/loopy.c
+++ b/loopy.c
@@ -868,13 +868,67 @@ static char *encode_solve_move(const game_state *state)
return ret;
}
+struct game_ui {
+ /*
+ * User preference: should grid lines in LINE_NO state be drawn
+ * very faintly so users can still see where they are, or should
+ * they be completely invisible?
+ */
+ bool draw_faint_lines;
+
+ /*
+ * User preference: when clicking an edge that has only one
+ * possible edge connecting to one (or both) of its ends, should
+ * that edge also change to the same state as the edge we just
+ * clicked?
+ */
+ enum {
+ AF_OFF, /* no, all grid edges are independent in the UI */
+ AF_FIXED, /* yes, but only based on the grid itself */
+ AF_ADAPTIVE /* yes, and consider edges user has already set to NO */
+ } autofollow;
+};
+
+static void legacy_prefs_override(struct game_ui *ui_out)
+{
+ static bool initialised = false;
+ static int draw_faint_lines = -1;
+ static int autofollow = -1;
+
+ if (!initialised) {
+ char *env;
+
+ initialised = true;
+ draw_faint_lines = getenv_bool("LOOPY_FAINT_LINES", -1);
+
+ if ((env = getenv("LOOPY_AUTOFOLLOW")) != NULL) {
+ if (!strcmp(env, "off"))
+ autofollow = AF_OFF;
+ else if (!strcmp(env, "fixed"))
+ autofollow = AF_FIXED;
+ else if (!strcmp(env, "adaptive"))
+ autofollow = AF_ADAPTIVE;
+ }
+ }
+
+ if (draw_faint_lines != -1)
+ ui_out->draw_faint_lines = draw_faint_lines;
+ if (autofollow != -1)
+ ui_out->autofollow = autofollow;
+}
+
static game_ui *new_ui(const game_state *state)
{
- return NULL;
+ game_ui *ui = snew(game_ui);
+ ui->draw_faint_lines = true;
+ ui->autofollow = AF_OFF;
+ legacy_prefs_override(ui);
+ return ui;
}
static void free_ui(game_ui *ui)
{
+ sfree(ui);
}
static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -3024,73 +3078,58 @@ static char *interpret_move(const game_state *state, game_ui *ui,
movesize = 80;
movebuf = snewn(movesize, char);
movelen = sprintf(movebuf, "%d%c", i, (int)button_char);
- {
- static enum { OFF, FIXED, ADAPTIVE, DUNNO } autofollow = DUNNO;
- if (autofollow == DUNNO) {
- const char *env = getenv("LOOPY_AUTOFOLLOW");
- if (env && !strcmp(env, "off"))
- autofollow = OFF;
- else if (env && !strcmp(env, "fixed"))
- autofollow = FIXED;
- else if (env && !strcmp(env, "adaptive"))
- autofollow = ADAPTIVE;
- else
- autofollow = OFF;
- }
- if (autofollow != OFF) {
- int dotid;
- for (dotid = 0; dotid < 2; dotid++) {
- grid_dot *dot = (dotid == 0 ? e->dot1 : e->dot2);
- grid_edge *e_this = e;
-
- while (1) {
- int j, n_found;
- grid_edge *e_next = NULL;
-
- for (j = n_found = 0; j < dot->order; j++) {
- grid_edge *e_candidate = dot->edges[j];
- int i_candidate = e_candidate - g->edges;
- if (e_candidate != e_this &&
- (autofollow == FIXED ||
- state->lines[i] == LINE_NO ||
- state->lines[i_candidate] != LINE_NO)) {
- e_next = e_candidate;
- n_found++;
- }
+ if (ui->autofollow != AF_OFF) {
+ int dotid;
+ for (dotid = 0; dotid < 2; dotid++) {
+ grid_dot *dot = (dotid == 0 ? e->dot1 : e->dot2);
+ grid_edge *e_this = e;
+
+ while (1) {
+ int j, n_found;
+ grid_edge *e_next = NULL;
+
+ for (j = n_found = 0; j < dot->order; j++) {
+ grid_edge *e_candidate = dot->edges[j];
+ int i_candidate = e_candidate - g->edges;
+ if (e_candidate != e_this &&
+ (ui->autofollow == AF_FIXED ||
+ state->lines[i] == LINE_NO ||
+ state->lines[i_candidate] != LINE_NO)) {
+ e_next = e_candidate;
+ n_found++;
}
+ }
- if (n_found != 1 ||
- state->lines[e_next - g->edges] != state->lines[i])
- break;
-
- if (e_next == e) {
- /*
- * Special case: we might have come all the
- * way round a loop and found our way back to
- * the same edge we started from. In that
- * situation, we must terminate not only this
- * while loop, but the 'for' outside it that
- * was tracing in both directions from the
- * starting edge, because if we let it trace
- * in the second direction then we'll only
- * find ourself traversing the same loop in
- * the other order and generate an encoded
- * move string that mentions the same set of
- * edges twice.
- */
- goto autofollow_done;
- }
+ if (n_found != 1 ||
+ state->lines[e_next - g->edges] != state->lines[i])
+ break;
+
+ if (e_next == e) {
+ /*
+ * Special case: we might have come all the way
+ * round a loop and found our way back to the same
+ * edge we started from. In that situation, we
+ * must terminate not only this while loop, but
+ * the 'for' outside it that was tracing in both
+ * directions from the starting edge, because if
+ * we let it trace in the second direction then
+ * we'll only find ourself traversing the same
+ * loop in the other order and generate an encoded
+ * move string that mentions the same set of edges
+ * twice.
+ */
+ goto autofollow_done;
+ }
- dot = (e_next->dot1 != dot ? e_next->dot1 : e_next->dot2);
- if (movelen > movesize - 40) {
- movesize = movesize * 5 / 4 + 128;
- movebuf = sresize(movebuf, movesize, char);
- }
- e_this = e_next;
- movelen += sprintf(movebuf+movelen, "%d%c",
- (int)(e_this - g->edges), button_char);
+ dot = (e_next->dot1 != dot ? e_next->dot1 : e_next->dot2);
+ if (movelen > movesize - 40) {
+ movesize = movesize * 5 / 4 + 128;
+ movebuf = sresize(movebuf, movesize, char);
}
+ e_this = e_next;
+ movelen += sprintf(movebuf+movelen, "%d%c",
+ (int)(e_this - g->edges), button_char);
}
autofollow_done:;
}
@@ -3261,7 +3300,7 @@ static const int loopy_line_redraw_phases[] = {
};
#define NPHASES lenof(loopy_line_redraw_phases)
-static void game_redraw_line(drawing *dr, game_drawstate *ds,
+static void game_redraw_line(drawing *dr, game_drawstate *ds,const game_ui *ui,
const game_state *state, int i, int phase)
{
grid *g = state->game_grid;
@@ -3287,10 +3326,7 @@ static void game_redraw_line(drawing *dr, game_drawstate *ds,
grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2);
if (line_colour == COL_FAINT) {
- static int draw_faint_lines = -1;
- if (draw_faint_lines < 0)
- draw_faint_lines = getenv_bool("LOOPY_FAINT_LINES", true);
- if (draw_faint_lines)
+ if (ui->draw_faint_lines)
draw_thick_line(dr, ds->tilesize/24.0,
x1 + 0.5, y1 + 0.5,
x2 + 0.5, y2 + 0.5,
@@ -3326,7 +3362,7 @@ static bool boxes_intersect(int x0, int y0, int w0, int h0,
}
static void game_redraw_in_rect(drawing *dr, game_drawstate *ds,
- const game_state *state,
+ const game_ui *ui, const game_state *state,
int x, int y, int w, int h)
{
grid *g = state->game_grid;
@@ -3347,7 +3383,7 @@ static void game_redraw_in_rect(drawing *dr, game_drawstate *ds,
for (i = 0; i < g->num_edges; i++) {
edge_bbox(ds, g, &g->edges[i], &bx, &by, &bw, &bh);
if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
- game_redraw_line(dr, ds, state, i, phase);
+ game_redraw_line(dr, ds, ui, state, i, phase);
}
}
for (i = 0; i < g->num_dots; i++) {
@@ -3504,7 +3540,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
int w = grid_width * ds->tilesize / g->tilesize;
int h = grid_height * ds->tilesize / g->tilesize;
- game_redraw_in_rect(dr, ds, state,
+ game_redraw_in_rect(dr, ds, ui, state,
0, 0, w + 2*border + 1, h + 2*border + 1);
} else {
@@ -3515,7 +3551,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
int x, y, w, h;
face_text_bbox(ds, g, f, &x, &y, &w, &h);
- game_redraw_in_rect(dr, ds, state, x, y, w, h);
+ game_redraw_in_rect(dr, ds, ui, state, x, y, w, h);
}
for (i = 0; i < nedges; i++) {
@@ -3523,7 +3559,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
int x, y, w, h;
edge_bbox(ds, g, e, &x, &y, &w, &h);
- game_redraw_in_rect(dr, ds, state, x, y, w, h);
+ game_redraw_in_rect(dr, ds, ui, state, x, y, w, h);
}
}
diff --git a/map.c b/map.c
index b8f5970..31dc96c 100644
--- a/map.c
+++ b/map.c
@@ -46,12 +46,6 @@ static bool verbose = false;
#define SIX (FOUR+2)
/*
- * Ghastly run-time configuration option, just for Gareth (again).
- */
-static int flash_type = -1;
-static float flash_length;
-
-/*
* Difficulty levels. I do some macro ickery here to ensure that my
* enum and the various forms of my name list always match up.
*/
@@ -2292,8 +2286,37 @@ struct game_ui {
int cur_x, cur_y, cur_lastmove;
bool cur_visible, cur_moved;
+
+ /*
+ * User preference to enable alternative versions of the
+ * completion flash. Some users have found the colour-cycling
+ * default version to be a bit eye-twisting.
+ */
+ enum {
+ FLASH_CYCLIC, /* cycle the four colours of the map */
+ FLASH_EACH_TO_WHITE, /* turn each colour white in turn */
+ FLASH_ALL_TO_WHITE /* flash the whole map to white in one go */
+ } flash_type;
};
+static void legacy_prefs_override(struct game_ui *ui_out)
+{
+ static bool initialised = false;
+ static int flash_type = -1;
+
+ if (!initialised) {
+ char *env;
+
+ initialised = true;
+
+ if ((env = getenv("MAP_ALTERNATIVE_FLASH")) != NULL)
+ flash_type = FLASH_EACH_TO_WHITE;
+ }
+
+ if (flash_type != -1)
+ ui_out->flash_type = flash_type;
+}
+
static game_ui *new_ui(const game_state *state)
{
game_ui *ui = snew(game_ui);
@@ -2305,6 +2328,8 @@ static game_ui *new_ui(const game_state *state)
ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
ui->cur_moved = false;
ui->cur_lastmove = 0;
+ ui->flash_type = FLASH_CYCLIC;
+ legacy_prefs_override(ui);
return ui;
}
@@ -2882,6 +2907,11 @@ static void draw_square(drawing *dr, game_drawstate *ds,
draw_update(dr, COORD(x), COORD(y), TILESIZE, TILESIZE);
}
+static float flash_length(const game_ui *ui)
+{
+ return (ui->flash_type == FLASH_EACH_TO_WHITE ? 0.50F : 0.30F);
+}
+
static void game_redraw(drawing *dr, game_drawstate *ds,
const game_state *oldstate, const game_state *state,
int dir, const game_ui *ui,
@@ -2905,10 +2935,10 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
}
if (flashtime) {
- if (flash_type == 1)
- flash = (int)(flashtime * FOUR / flash_length);
+ if (ui->flash_type == FLASH_EACH_TO_WHITE)
+ flash = (int)(flashtime * FOUR / flash_length(ui));
else
- flash = 1 + (int)(flashtime * THREE / flash_length);
+ flash = 1 + (int)(flashtime * THREE / flash_length(ui));
} else
flash = -1;
@@ -2927,12 +2957,12 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
bv = FOUR;
if (flash >= 0) {
- if (flash_type == 1) {
+ if (ui->flash_type == FLASH_EACH_TO_WHITE) {
if (tv == flash)
tv = FOUR;
if (bv == flash)
bv = FOUR;
- } else if (flash_type == 2) {
+ } else if (ui->flash_type == FLASH_ALL_TO_WHITE) {
if (flash % 2)
tv = bv = FOUR;
} else {
@@ -3062,15 +3092,7 @@ static float game_flash_length(const game_state *oldstate,
{
if (!oldstate->completed && newstate->completed &&
!oldstate->cheated && !newstate->cheated) {
- if (flash_type < 0) {
- char *env = getenv("MAP_ALTERNATIVE_FLASH");
- if (env)
- flash_type = atoi(env);
- else
- flash_type = 0;
- flash_length = (flash_type == 1 ? 0.50F : 0.30F);
- }
- return flash_length;
+ return flash_length(ui);
} else
return 0.0F;
}
diff --git a/misc.c b/misc.c
index d9695c8..6b85533 100644
--- a/misc.c
+++ b/misc.c
@@ -202,7 +202,7 @@ char *fgetline(FILE *fp)
return ret;
}
-bool getenv_bool(const char *name, bool dflt)
+int getenv_bool(const char *name, int dflt)
{
char *env = getenv(name);
if (env == NULL) return dflt;
diff --git a/pearl.c b/pearl.c
index 745985a..7caaae2 100644
--- a/pearl.c
+++ b/pearl.c
@@ -1859,8 +1859,41 @@ struct game_ui {
int curx, cury; /* grid position of keyboard cursor */
bool cursor_active; /* true iff cursor is shown */
+
+ /*
+ * User preference: general visual style of the GUI. GUI_MASYU is
+ * how this puzzle is traditionally presented, with clue dots in
+ * the middle of grid squares, and the solution loop connecting
+ * square-centres. GUI_LOOPY shifts the grid by half a square in
+ * each direction, so that the clue dots are at _vertices_ of the
+ * grid and the solution loop follows the grid edges, which you
+ * could argue is more logical.
+ */
+ enum { GUI_MASYU, GUI_LOOPY } gui_style;
};
+static void legacy_prefs_override(struct game_ui *ui_out)
+{
+ static bool initialised = false;
+ static int gui_style = -1;
+
+ if (!initialised) {
+ initialised = true;
+
+ switch (getenv_bool("PEARL_GUI_LOOPY", -1)) {
+ case 0:
+ gui_style = GUI_MASYU;
+ break;
+ case 1:
+ gui_style = GUI_LOOPY;
+ break;
+ }
+ }
+
+ if (gui_style != -1)
+ ui_out->gui_style = gui_style;
+}
+
static game_ui *new_ui(const game_state *state)
{
game_ui *ui = snew(game_ui);
@@ -1871,6 +1904,9 @@ static game_ui *new_ui(const game_state *state)
ui->cursor_active = getenv_bool("PUZZLES_SHOW_CURSOR", false);
ui->curx = ui->cury = 0;
+ ui->gui_style = GUI_MASYU;
+ legacy_prefs_override(ui);
+
return ui;
}
@@ -1903,7 +1939,7 @@ static const char *current_key_label(const game_ui *ui,
#define HALFSZ (ds->halfsz)
#define TILE_SIZE (ds->halfsz*2 + 1)
-#define BORDER ((get_gui_style() == GUI_LOOPY) ? (TILE_SIZE/8) : (TILE_SIZE/2))
+#define BORDER ((ui->gui_style == GUI_LOOPY) ? (TILE_SIZE/8) : (TILE_SIZE/2))
#define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
@@ -1919,21 +1955,6 @@ static const char *current_key_label(const game_ui *ui,
#define DS_FLASH (1 << 21)
#define DS_CURSOR (1 << 22)
-enum { GUI_MASYU, GUI_LOOPY };
-
-static int get_gui_style(void)
-{
- static int gui_style = -1;
-
- if (gui_style == -1) {
- if (getenv_bool("PEARL_GUI_LOOPY", false))
- gui_style = GUI_LOOPY;
- else
- gui_style = GUI_MASYU;
- }
- return gui_style;
-}
-
struct game_drawstate {
int halfsz;
bool started;
@@ -2422,8 +2443,8 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
}
static void draw_lines_specific(drawing *dr, game_drawstate *ds,
- int x, int y, unsigned int lflags,
- unsigned int shift, int c)
+ const game_ui *ui, int x, int y,
+ unsigned int lflags, unsigned int shift, int c)
{
int ox = COORD(x), oy = COORD(y);
int t2 = HALFSZ, t16 = HALFSZ/4;
@@ -2472,7 +2493,7 @@ static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui,
COL_CURSOR_BACKGROUND : COL_BACKGROUND);
- if (get_gui_style() == GUI_LOOPY) {
+ if (ui->gui_style == GUI_LOOPY) {
/* Draw small dot, underneath any lines. */
draw_circle(dr, cx, cy, t16, COL_GRID, COL_GRID);
} else {
@@ -2499,7 +2520,7 @@ static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui,
draw_line(dr, mx-msz, my-msz, mx+msz, my+msz, COL_BLACK);
draw_line(dr, mx-msz, my+msz, mx+msz, my-msz, COL_BLACK);
} else {
- if (get_gui_style() == GUI_LOOPY) {
+ if (ui->gui_style == GUI_LOOPY) {
/* draw grid lines connecting centre of cells */
draw_line(dr, cx, cy, cx+xoff, cy+yoff, COL_GRID);
}
@@ -2509,11 +2530,11 @@ static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui,
/* Draw each of the four directions, where laid (or error, or drag, etc.)
* Order is important here, specifically for the eventual colours of the
* exposed end caps. */
- draw_lines_specific(dr, ds, x, y, lflags, 0,
+ draw_lines_specific(dr, ds, ui, x, y, lflags, 0,
(lflags & DS_FLASH ? COL_FLASH : COL_BLACK));
- draw_lines_specific(dr, ds, x, y, lflags, DS_ESHIFT, COL_ERROR);
- draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGOFF);
- draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGON);
+ draw_lines_specific(dr, ds, ui, x, y, lflags, DS_ESHIFT, COL_ERROR);
+ draw_lines_specific(dr, ds, ui, x, y, lflags, DS_DSHIFT, COL_DRAGOFF);
+ draw_lines_specific(dr, ds, ui, x, y, lflags, DS_DSHIFT, COL_DRAGON);
/* Draw a clue, if present */
if (clue != NOCLUE) {
@@ -2540,7 +2561,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
bool force = false;
if (!ds->started) {
- if (get_gui_style() == GUI_MASYU) {
+ if (ui->gui_style == GUI_MASYU) {
/*
* Black rectangle which is the main grid.
*/
@@ -2657,7 +2678,7 @@ static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
game_drawstate *ds = game_new_drawstate(dr, state);
game_set_size(dr, ds, NULL, tilesize);
- if (get_gui_style() == GUI_MASYU) {
+ if (ui->gui_style == GUI_MASYU) {
/* Draw grid outlines (black). */
for (x = 0; x <= w; x++)
draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black);
@@ -2689,7 +2710,8 @@ static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ;
int clue = state->shared->clues[y*w+x];
- draw_lines_specific(dr, ds, x, y, state->lines[y*w+x], 0, black);
+ draw_lines_specific(dr, ds, ui, x, y,
+ state->lines[y*w+x], 0, black);
if (clue != NOCLUE) {
int c = (clue == CORNER) ? black : white;
diff --git a/puzzles.h b/puzzles.h
index cb7fbca..b7c34f7 100644
--- a/puzzles.h
+++ b/puzzles.h
@@ -377,7 +377,9 @@ char *fgetline(FILE *fp);
char *bin2hex(const unsigned char *in, int inlen);
unsigned char *hex2bin(const char *in, int outlen);
-bool getenv_bool(const char *name, bool dflt);
+/* Returns 0 or 1 if the environment variable is set, or dflt if not.
+ * dflt may be a third value if it needs to be. */
+int getenv_bool(const char *name, int dflt);
/* Mixes two colours in specified proportions. */
void colour_mix(const float src1[3], const float src2[3], float p,
diff --git a/range.c b/range.c
index 1fbb87c..cd5e89e 100644
--- a/range.c
+++ b/range.c
@@ -1225,13 +1225,49 @@ static char *game_text_format(const game_state *state)
struct game_ui {
puzzle_size r, c; /* cursor position */
bool cursor_show;
+
+ /*
+ * User preference option to swap the left and right mouse
+ * buttons.
+ *
+ * The original puzzle submitter thought it would be more useful
+ * to have the left button turn an empty square into a dotted one,
+ * on the grounds that that was what you did most often; I (SGT)
+ * felt instinctively that the left button ought to place black
+ * squares and the right button place dots, on the grounds that
+ * that was consistent with many other puzzles in which the left
+ * button fills in the data used by the solution checker while the
+ * right button places pencil marks for the user's convenience.
+ *
+ * My first beta-player wasn't sure either, so I thought I'd
+ * pre-emptively put in a 'configuration' mechanism just in case.
+ */
+ bool swap_buttons;
};
+static void legacy_prefs_override(struct game_ui *ui_out)
+{
+ static int initialised = false;
+ static int swap_buttons = -1;
+
+ if (!initialised) {
+ initialised = true;
+ swap_buttons = getenv_bool("RANGE_SWAP_BUTTONS", -1);
+ }
+
+ if (swap_buttons != -1)
+ ui_out->swap_buttons = swap_buttons;
+}
+
static game_ui *new_ui(const game_state *state)
{
struct game_ui *ui = snew(game_ui);
ui->r = ui->c = 0;
ui->cursor_show = getenv_bool("PUZZLES_SHOW_CURSOR", false);
+
+ ui->swap_buttons = false;
+ legacy_prefs_override(ui);
+
return ui;
}
@@ -1298,36 +1334,12 @@ static char *interpret_move(const game_state *state, game_ui *ui,
}
if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
- /*
- * Utterly awful hack, exactly analogous to the one in Slant,
- * to configure the left and right mouse buttons the opposite
- * way round.
- *
- * The original puzzle submitter thought it would be more
- * useful to have the left button turn an empty square into a
- * dotted one, on the grounds that that was what you did most
- * often; I (SGT) felt instinctively that the left button
- * ought to place black squares and the right button place
- * dots, on the grounds that that was consistent with many
- * other puzzles in which the left button fills in the data
- * used by the solution checker while the right button places
- * pencil marks for the user's convenience.
- *
- * My first beta-player wasn't sure either, so I thought I'd
- * pre-emptively put in a 'configuration' mechanism just in
- * case.
- */
- {
- static int swap_buttons = -1;
- if (swap_buttons < 0)
- swap_buttons = getenv_bool("RANGE_SWAP_BUTTONS", false);
- if (swap_buttons) {
- if (button == LEFT_BUTTON)
- button = RIGHT_BUTTON;
- else
- button = LEFT_BUTTON;
- }
- }
+ if (ui->swap_buttons) {
+ if (button == LEFT_BUTTON)
+ button = RIGHT_BUTTON;
+ else
+ button = LEFT_BUTTON;
+ }
}
switch (button) {
diff --git a/signpost.c b/signpost.c
index d166b0c..787239c 100644
--- a/signpost.c
+++ b/signpost.c
@@ -1387,8 +1387,32 @@ struct game_ui {
bool dragging, drag_is_from;
int sx, sy; /* grid coords of start cell */
int dx, dy; /* pixel coords of drag posn */
+
+ /*
+ * Trivial and foolish configurable option done on purest whim.
+ * With this option enabled, the victory flash is done by rotating
+ * each square in the opposite direction from its immediate
+ * neighbours, so that they behave like a field of interlocking
+ * gears. With it disabled, they all rotate in the same direction.
+ * Choose for yourself which is more brain-twisting :-)
+ */
+ bool gear_mode;
};
+static void legacy_prefs_override(struct game_ui *ui_out)
+{
+ static bool initialised = false;
+ static int gear_mode = -1;
+
+ if (!initialised) {
+ initialised = true;
+ gear_mode = getenv_bool("SIGNPOST_GEARS", -1);
+ }
+
+ if (gear_mode != -1)
+ ui_out->gear_mode = gear_mode;
+}
+
static game_ui *new_ui(const game_state *state)
{
game_ui *ui = snew(game_ui);
@@ -1402,6 +1426,9 @@ static game_ui *new_ui(const game_state *state)
ui->dragging = false;
ui->sx = ui->sy = ui->dx = ui->dy = 0;
+ ui->gear_mode = false;
+ legacy_prefs_override(ui);
+
return ui;
}
@@ -2143,26 +2170,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
if (state->nums[i] != ds->nums[i] ||
f != ds->f[i] || dirp != ds->dirp[i] ||
force || !ds->started) {
- int sign;
- {
- /*
- * Trivial and foolish configurable option done on
- * purest whim. With this option enabled, the
- * victory flash is done by rotating each square
- * in the opposite direction from its immediate
- * neighbours, so that they behave like a field of
- * interlocking gears. With it disabled, they all
- * rotate in the same direction. Choose for
- * yourself which is more brain-twisting :-)
- */
- static int gear_mode = -1;
- if (gear_mode < 0)
- gear_mode = getenv_bool("SIGNPOST_GEARS", false);
- if (gear_mode)
- sign = 1 - 2 * ((x ^ y) & 1);
- else
- sign = 1;
- }
+ int sign = (ui->gear_mode ? 1 - 2 * ((x ^ y) & 1) : 1);
tile_redraw(dr, ds,
BORDER + x * TILE_SIZE,
BORDER + y * TILE_SIZE,
diff --git a/slant.c b/slant.c
index 5230f25..ee39d74 100644
--- a/slant.c
+++ b/slant.c
@@ -1581,13 +1581,39 @@ static char *game_text_format(const game_state *state)
struct game_ui {
int cur_x, cur_y;
bool cur_visible;
+
+ /*
+ * User preference option to swap the left and right mouse
+ * buttons. There isn't a completely obvious mapping of left and
+ * right buttons to the two directions of slash, and at least one
+ * player turned out not to have the same intuition as me.
+ */
+ bool swap_buttons;
};
+static void legacy_prefs_override(struct game_ui *ui_out)
+{
+ static bool initialised = false;
+ static int swap_buttons = -1;
+
+ if (!initialised) {
+ initialised = true;
+ swap_buttons = getenv_bool("SLANT_SWAP_BUTTONS", -1);
+ }
+
+ if (swap_buttons != -1)
+ ui_out->swap_buttons = swap_buttons;
+}
+
static game_ui *new_ui(const game_state *state)
{
game_ui *ui = snew(game_ui);
ui->cur_x = ui->cur_y = 0;
ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
+
+ ui->swap_buttons = false;
+ legacy_prefs_override(ui);
+
return ui;
}
diff --git a/towers.c b/towers.c
index 012c8b3..0c2bf3a 100644
--- a/towers.c
+++ b/towers.c
@@ -1159,8 +1159,37 @@ struct game_ui {
* allowed on immutable squares.
*/
bool hcursor;
+
+ /*
+ * User preference option which can be set to FALSE to disable the
+ * 3D graphical style, and instead just display the puzzle as if
+ * it was a Sudoku variant, i.e. each square just has a digit in
+ * it.
+ *
+ * I was initially a bit uncertain about whether the 3D style
+ * would be the right thing, on the basis that it uses up space in
+ * the cells and makes it hard to use many pencil marks. Actually
+ * nobody seems to have complained, but having put in the option
+ * while I was still being uncertain, it seems silly not to leave
+ * it in just in case.
+ */
+ int three_d;
};
+static void legacy_prefs_override(struct game_ui *ui_out)
+{
+ static bool initialised = false;
+ static int three_d = -1;
+
+ if (!initialised) {
+ initialised = true;
+ three_d = getenv_bool("TOWERS_2D", -1);
+ }
+
+ if (three_d != -1)
+ ui_out->three_d = three_d;
+}
+
static game_ui *new_ui(const game_state *state)
{
game_ui *ui = snew(game_ui);
@@ -1169,6 +1198,9 @@ static game_ui *new_ui(const game_state *state)
ui->hpencil = false;
ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
+ ui->three_d = true;
+ legacy_prefs_override(ui);
+
return ui;
}
@@ -1224,7 +1256,6 @@ static const char *current_key_label(const game_ui *ui,
struct game_drawstate {
int tilesize;
- bool three_d; /* default 3D graphics are user-disableable */
long *tiles; /* (w+2)*(w+2) temp space */
long *drawn; /* (w+2)*(w+2)*4: current drawn data */
bool *errtmp;
@@ -1356,7 +1387,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
tx = FROMCOORD(x);
ty = FROMCOORD(y);
- if (ds->three_d) {
+ if (ui->three_d) {
/*
* In 3D mode, just locating the mouse click in the natural
* square grid may not be sufficient to tell which tower the
@@ -1630,7 +1661,6 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
int i;
ds->tilesize = 0;
- ds->three_d = !getenv_bool("TOWERS_2D", false);
ds->tiles = snewn((w+2)*(w+2), long);
ds->drawn = snewn((w+2)*(w+2)*4, long);
for (i = 0; i < (w+2)*(w+2)*4; i++)
@@ -1648,8 +1678,8 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
sfree(ds);
}
-static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
- int x, int y, long tile)
+static void draw_tile(drawing *dr, game_drawstate *ds, const game_ui *ui,
+ struct clues *clues, int x, int y, long tile)
{
int w = clues->w /* , a = w*w */;
int tx, ty, bg;
@@ -1661,7 +1691,7 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
bg = (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND;
/* draw tower */
- if (ds->three_d && (tile & DF_PLAYAREA) && (tile & DF_DIGIT_MASK)) {
+ if (ui->three_d && (tile & DF_PLAYAREA) && (tile & DF_DIGIT_MASK)) {
int coords[8];
int xoff = X_3D_DISP(tile & DF_DIGIT_MASK, w);
int yoff = Y_3D_DISP(tile & DF_DIGIT_MASK, w);
@@ -1762,10 +1792,10 @@ static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues,
* to put the pencil marks.
*/
/* Start with the whole square, minus space for impinging towers */
- pl = tx + (ds->three_d ? X_3D_DISP(w,w) : 0);
+ pl = tx + (ui->three_d ? X_3D_DISP(w,w) : 0);
pr = tx + TILESIZE;
pt = ty;
- pb = ty + TILESIZE - (ds->three_d ? Y_3D_DISP(w,w) : 0);
+ pb = ty + TILESIZE - (ui->three_d ? Y_3D_DISP(w,w) : 0);
/*
* We arrange our pencil marks in a grid layout, with
@@ -1901,13 +1931,13 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
ds->drawn[i*4+2] != bl || ds->drawn[i*4+3] != br) {
clip(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);
- draw_tile(dr, ds, state->clues, x-1, y-1, tr);
+ draw_tile(dr, ds, ui, state->clues, x-1, y-1, tr);
if (x > 0)
- draw_tile(dr, ds, state->clues, x-2, y-1, tl);
+ draw_tile(dr, ds, ui, state->clues, x-2, y-1, tl);
if (y <= w)
- draw_tile(dr, ds, state->clues, x-1, y, br);
+ draw_tile(dr, ds, ui, state->clues, x-1, y, br);
if (x > 0 && y <= w)
- draw_tile(dr, ds, state->clues, x-2, y, bl);
+ draw_tile(dr, ds, ui, state->clues, x-2, y, bl);
unclip(dr);
draw_update(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE);