diff options
| author | Ben Harris <bjh21@bjh21.me.uk> | 2022-12-05 01:13:26 +0000 |
|---|---|---|
| committer | Ben Harris <bjh21@bjh21.me.uk> | 2022-12-09 20:48:30 +0000 |
| commit | a3310ab857f088489b35ebf10733ba284a24d27f (patch) | |
| tree | 6ae25aaf1bfce3151fbe17e51c49ec12d0c74538 | |
| parent | 1d91522babe41fcf7cbfb06633ae6bc5606db367 (diff) | |
| download | puzzles-a3310ab857f088489b35ebf10733ba284a24d27f.zip puzzles-a3310ab857f088489b35ebf10733ba284a24d27f.tar.gz puzzles-a3310ab857f088489b35ebf10733ba284a24d27f.tar.bz2 puzzles-a3310ab857f088489b35ebf10733ba284a24d27f.tar.xz | |
New backend function: current_key_label()
This provides a way for the front end to ask how a particular key should
be labelled right now (specifically, for a given game_state and
game_ui). This is useful on feature phones where it's conventional to
put a small caption above each soft key indicating what it currently
does.
The function currently provides labels only for CURSOR_SELECT and
CURSOR_SELECT2. This is because these are the only keys that need
labelling on KaiOS.
The concept of labelling keys also turns up in the request_keys() call,
but there are quite a few differences. The labels returned by
current_key_label() are dynamic and likely to vary with each move, while
the labels provided by request_keys() are constant for a given
game_params. Also, the keys returned by request_keys() don't generally
include CURSOR_SELECT and CURSOR_SELECT2, because those aren't necessary
on platforms with pointing devices. It might be possible to provide a
unified API covering both of this, but I think it would be quite
difficult to work with.
Where a key is to be unlabelled, current_key_label() is expected to
return an empty string. This leaves open the possibility of NULL
indicating a fallback to button2label or the label specified by
request_keys() in the future.
It's tempting to try to implement current_key_label() by calling
interpret_move() and parsing its output. This doesn't work for two
reasons. One is that interpret_move() is entitled to modify the
game_ui, and there isn't really a practical way to back those changes
out. The other is that the information returned by interpret_move()
isn't sufficient to generate a label. For instance, in many puzzles it
generates moves that toggle the state of a square, but we want the label
to reflect which state the square will be toggled to. The result is
that I've generally ended up pulling bits of code from interpret_move()
and execute_move() together to implement current_key_label().
Alongside the back-end function, there's a midend_current_key_label()
that's a thin wrapper around the back-end function. It just adds an
assertion about which key's being requested and a default null
implementation so that back-ends can avoid defining the function if it
will do nothing useful.
| -rw-r--r-- | blackbox.c | 36 | ||||
| -rw-r--r-- | bridges.c | 15 | ||||
| -rw-r--r-- | cube.c | 1 | ||||
| -rw-r--r-- | devel.but | 51 | ||||
| -rw-r--r-- | dominosa.c | 28 | ||||
| -rw-r--r-- | fifteen.c | 1 | ||||
| -rw-r--r-- | filling.c | 18 | ||||
| -rw-r--r-- | flip.c | 8 | ||||
| -rw-r--r-- | flood.c | 13 | ||||
| -rw-r--r-- | galaxies.c | 32 | ||||
| -rw-r--r-- | guess.c | 14 | ||||
| -rw-r--r-- | inertia.c | 10 | ||||
| -rw-r--r-- | keen.c | 9 | ||||
| -rw-r--r-- | lightup.c | 21 | ||||
| -rw-r--r-- | loopy.c | 1 | ||||
| -rw-r--r-- | magnets.c | 31 | ||||
| -rw-r--r-- | map.c | 21 | ||||
| -rw-r--r-- | midend.c | 9 | ||||
| -rw-r--r-- | mines.c | 30 | ||||
| -rw-r--r-- | mosaic.c | 21 | ||||
| -rw-r--r-- | net.c | 13 | ||||
| -rw-r--r-- | netslide.c | 9 | ||||
| -rw-r--r-- | nullgame.c | 1 | ||||
| -rw-r--r-- | palisade.c | 1 | ||||
| -rw-r--r-- | pattern.c | 18 | ||||
| -rw-r--r-- | pearl.c | 15 | ||||
| -rw-r--r-- | pegs.c | 14 | ||||
| -rw-r--r-- | puzzles.h | 3 | ||||
| -rw-r--r-- | range.c | 22 | ||||
| -rw-r--r-- | rect.c | 16 | ||||
| -rw-r--r-- | samegame.c | 20 | ||||
| -rw-r--r-- | signpost.c | 21 | ||||
| -rw-r--r-- | singles.c | 13 | ||||
| -rw-r--r-- | sixteen.c | 16 | ||||
| -rw-r--r-- | slant.c | 17 | ||||
| -rw-r--r-- | solo.c | 9 | ||||
| -rw-r--r-- | tents.c | 17 | ||||
| -rw-r--r-- | towers.c | 9 | ||||
| -rw-r--r-- | tracks.c | 30 | ||||
| -rw-r--r-- | twiddle.c | 12 | ||||
| -rw-r--r-- | undead.c | 15 | ||||
| -rw-r--r-- | unequal.c | 9 | ||||
| -rw-r--r-- | unfinished/group.c | 21 | ||||
| -rw-r--r-- | unfinished/separate.c | 1 | ||||
| -rw-r--r-- | unfinished/slide.c | 1 | ||||
| -rw-r--r-- | unfinished/sokoban.c | 1 | ||||
| -rw-r--r-- | unruly.c | 22 | ||||
| -rw-r--r-- | untangle.c | 1 |
48 files changed, 717 insertions, 0 deletions
@@ -532,6 +532,41 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->newmove = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cur_visible && !state->reveal) { + int gx = ui->cur_x, gy = ui->cur_y, rangeno = -1; + if (gx == 0 && gy == 0 && button == CURSOR_SELECT) return "Check"; + if (gx >= 1 && gx <= state->w && gy >= 1 && gy <= state->h) { + /* Cursor somewhere in the arena. */ + if (button == CURSOR_SELECT && !(GRID(state, gx,gy) & BALL_LOCK)) + return (GRID(state, gx, gy) & BALL_GUESS) ? "Clear" : "Ball"; + if (button == CURSOR_SELECT2) + return (GRID(state, gx, gy) & BALL_LOCK) ? "Unlock" : "Lock"; + } + if (grid2range(state, gx, gy, &rangeno)) { + if (button == CURSOR_SELECT && + state->exits[rangeno] == LASER_EMPTY) + return "Fire"; + if (button == CURSOR_SELECT2) { + int n = 0; + /* Row or column lock or unlock. */ + if (gy == 0 || gy > state->h) { /* Column lock */ + for (gy = 1; gy <= state->h; gy++) + n += !!(GRID(state, gx, gy) & BALL_LOCK); + return n > state->h/2 ? "Unlock" : "Lock"; + } else { /* Row lock */ + for (gx = 1; gx <= state->w; gx++) + n += !!(GRID(state, gx, gy) & BALL_LOCK); + return n > state->w/2 ? "Unlock" : "Lock"; + } + } + } + } + return ""; +} + #define OFFSET(gx,gy,o) do { \ int off = (4 + (o) % 4) % 4; \ (gx) += offsets[off].x; \ @@ -1543,6 +1578,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -2138,6 +2138,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) { + if (!ui->cur_visible) + return ""; /* Actually shows cursor. */ + if (ui->dragging || button == CURSOR_SELECT2) + return "Finished"; + if (GRID(state, ui->cur_x, ui->cur_y) & G_ISLAND) + return "Select"; + } + return ""; +} + struct game_drawstate { int tilesize; int w, h; @@ -3269,6 +3283,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1774,6 +1774,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_GRID_SCALE, game_compute_size, game_set_size, @@ -1692,6 +1692,43 @@ This function should not be called directly by frontends. Instead, frontends should use \cw{midend_request_keys()} (\k{midend-request-keys}). +\S{backend-current-key-label} \cw{current_key_label()} + +\c const char *(*current_key_label)(const game_ui *ui, +\c const game_state *state, int button); + +This function is called to ask the back-end how certain keys should be +labelled on platforms (such a feature phones) where this is +conventional. +These labels are expected to reflect what the keys will do right now, +so they can change depending on the game and UI state. + +The \c{ui} and \c{state} arguments describe the state of the game for +which key labels are required. +The \c{button} argument is the same as the one passed to +\cw{interpret_move()}. +At present, the only values of \c{button} that can be passed to +\cw{current_key_label()} are \cw{CURSOR_SELECT} and \cw{CURSOR_SELECT2}. +The return value is a short string describing what the requested key +will do if pressed. +Usually the string should be a static string constant. +If it's really necessary to use a dynamically-allocated string, it +should remain valid until the next call to \cw{current_key_label()} or +\cw{free_ui()} with the same \cw{game_ui} (so it can be referenced from +the \cw{game_ui} and freed at the next one of those calls). + +There's no fixed upper limit on the length of string that this +function can return, but more than about 12 characters is likely to +cause problems for front-ends. If two buttons have the same effect, +their labels should be identical so that the front end can detect +this. Similarly, keys that do different things should have different +labels. The label should be an empty string (\cw{""}) if the key does +nothing. + +Like \cw{request_keys()}, the \cw{current_key_label} pointer in the +\c{game} structure is allowed to be \cw{NULL}, in which case the +mid-end will treat it as though it always returned \cw{""}. + \S{backend-flags} \c{flags} \c int flags; @@ -3232,6 +3269,20 @@ labels (i.e. the \cw{key_label} items that have their \cw{label} fields set to \cw{NULL}) by using \cw{button2label()} (\k{utils-button2label}). +\H{midend-current-key-label} \cw{midend_current_key_label()} + +\c const char *midend_current_key_label(midend *me, int button); + +This is a thin wrapper around the backend's \cw{current_key_label()} +function (\k{backend-current-key-label}). Front ends that need to +label \cw{CURSOR_SELECT} or \cw{CURSOR_SELECT2} should call this +function after each move (at least after each call to +\cw{midend_process_key()}) to get the current labels. The front end +should arrange to copy the returned string somewhere before the next +call to the mid-end, just in case it's dynamically allocated. If the +button supplied does nothing, the label returned will be an empty +string. + \H{midend-colours} \cw{midend_colours()} \c float *midend_colours(midend *me, int *ncolours); @@ -2734,6 +2734,33 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cur_visible = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) { + int d1, d2, w = state->w; + + if (!((ui->cur_x ^ ui->cur_y) & 1)) + return ""; /* must have exactly one dimension odd */ + d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); + d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); + + /* We can't mark an edge next to any domino. */ + if (button == CURSOR_SELECT2 && + (state->grid[d1] != d1 || state->grid[d2] != d2)) + return ""; + if (button == CURSOR_SELECT) { + if (state->grid[d1] == d2) return "Remove"; + return "Place"; + } else { + int edge = d2 == d1 + 1 ? EDGE_R : EDGE_B; + if (state->edges[d1] & edge) return "Remove"; + return "Line"; + } + } + return ""; +} + #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE * 3 / 4) @@ -3417,6 +3444,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, @@ -1110,6 +1110,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1429,6 +1429,23 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->keydragging = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + const int w = state->shared->params.w; + + if (IS_CURSOR_SELECT(button) && ui->cur_visible) { + if (button == CURSOR_SELECT) { + if (ui->keydragging) return "Stop"; + return "Multiselect"; + } + if (button == CURSOR_SELECT2 && + !state->shared->clues[w*ui->cur_y + ui->cur_x]) + return (ui->sel[w*ui->cur_y + ui->cur_x]) ? "Deselect" : "Select"; + } + return ""; +} + #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 2) @@ -2166,6 +2183,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -928,6 +928,13 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) return "Flip"; + return ""; +} + struct game_drawstate { int w, h; bool started; @@ -1346,6 +1353,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -793,6 +793,18 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (button == CURSOR_SELECT && + state->grid[0] != state->grid[ui->cy*state->w+ui->cx]) + return "Fill"; + if (button == CURSOR_SELECT2 && + state->soln && state->solnpos < state->soln->nmoves) + return "Advance"; + return ""; +} + struct game_drawstate { bool started; int tilesize; @@ -1357,6 +1369,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, @@ -2539,6 +2539,33 @@ static bool edge_placement_legal(const game_state *state, int x, int y) return true; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + space *sp; + + if (IS_CURSOR_SELECT(button) && ui->cur_visible) { + sp = &SPACE(state, ui->cur_x, ui->cur_y); + if (ui->dragging) { + if (ui->cur_x == ui->srcx && ui->cur_y == ui->srcy) + return "Cancel"; + if (ok_to_add_assoc_with_opposite( + state, &SPACE(state, ui->cur_x, ui->cur_y), + &SPACE(state, ui->dotx, ui->doty))) + return "Place"; + return (ui->srcx == ui->dotx && ui->srcy == ui->doty) ? + "Cancel" : "Remove"; + } else if (sp->flags & F_DOT) + return "New arrow"; + else if (sp->flags & F_TILE_ASSOC) + return "Move arrow"; + else if (sp->type == s_edge && + edge_placement_legal(state, ui->cur_x, ui->cur_y)) + return (sp->flags & F_EDGE_SET) ? "Clear" : "Edge"; + } + return ""; +} + static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) @@ -3813,6 +3840,11 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, +#ifdef EDITOR + NULL, +#else + current_key_label, +#endif interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -510,6 +510,19 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->peg_cur = 0; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (state->solved) return ""; + if (button == CURSOR_SELECT) { + if (ui->peg_cur == state->params.npegs) return "Submit"; + return "Place"; + } + if (button == CURSOR_SELECT2 && ui->peg_cur != state->params.npegs) + return "Hold"; + return ""; +} + #define PEGSZ (ds->pegsz) #define PEGOFF (ds->pegsz + ds->gapsz) #define HINTSZ (ds->hintsz) @@ -1522,6 +1535,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PEG_PREFER_SZ, game_compute_size, game_set_size, @@ -1545,6 +1545,15 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->just_made_move = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && + state->soln && state->solnpos < state->soln->len) + return "Advance"; + return ""; +} + struct game_drawstate { game_params p; int tilesize; @@ -2233,6 +2242,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, @@ -1569,6 +1569,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (ui->hshow && (button == CURSOR_SELECT)) + return ui->hpencil ? "Ink" : "Pencil"; + return ""; +} + #define PREFERRED_TILESIZE 48 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE / 2) @@ -2477,6 +2485,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, @@ -1855,6 +1855,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cur_visible = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int cx = ui->cur_x, cy = ui->cur_y; + unsigned int flags = GRID(state, flags, cx, cy); + + if (!ui->cur_visible) return ""; + if (button == CURSOR_SELECT) { + if (flags & (F_BLACK | F_IMPOSSIBLE)) return ""; + if (flags & F_LIGHT) return "Clear"; + return "Light"; + } + if (button == CURSOR_SELECT2) { + if (flags & (F_BLACK | F_LIGHT)) return ""; + if (flags & F_IMPOSSIBLE) return "Clear"; + return "Mark"; + } + return ""; +} + #define DF_BLACK 1 /* black square */ #define DF_NUMBERED 2 /* black square with number */ #define DF_LIT 4 /* display (white) square lit up */ @@ -2324,6 +2344,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -3677,6 +3677,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1745,6 +1745,36 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cur_visible = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int idx; + + if (IS_CURSOR_SELECT(button)) { + if (!ui->cur_visible) return ""; + idx = ui->cur_y * state->w + ui->cur_x; + if (button == CURSOR_SELECT) { + if (state->grid[idx] == NEUTRAL && state->flags[idx] & GS_SET) + return ""; + switch (state->grid[idx]) { + case EMPTY: return "+"; + case POSITIVE: return "-"; + case NEGATIVE: return "Clear"; + } + } + if (button == CURSOR_SELECT2) { + if (state->grid[idx] != NEUTRAL) return ""; + if (state->flags[idx] & GS_SET) /* neutral */ + return "?"; + if (state->flags[idx] & GS_NOTNEUTRAL) /* !neutral */ + return "Clear"; + else + return "X"; + } + } + return ""; +} + struct game_drawstate { int tilesize; bool started, solved; @@ -2431,6 +2461,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -2392,6 +2392,26 @@ static int region_from_ui_cursor(const game_state *state, const game_ui *ui) EPSILON_Y(ui->cur_lastmove)); } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int r; + + if (IS_CURSOR_SELECT(button) && ui->cur_visible) { + if (ui->drag_colour == -2) return "Pick"; + r = region_from_ui_cursor(state, ui); + if (state->map->immutable[r]) return "Cancel"; + if (!ui->cur_moved) return ui->drag_pencil ? "Cancel" : "Clear"; + if (button == CURSOR_SELECT2) { + if (state->colouring[r] >= 0) return "Cancel"; + if (ui->drag_colour >= 0) return "Stipple"; + } + if (ui->drag_pencil) return "Stipple"; + return ui->drag_colour >= 0 ? "Fill" : "Clear"; + } + return ""; +} + static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) @@ -3257,6 +3277,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, 20, game_compute_size, game_set_size, @@ -1220,6 +1220,15 @@ key_label *midend_request_keys(midend *me, int *n) return keys; } +/* Return a good label to show next to a key right now. */ +const char *midend_current_key_label(midend *me, int button) +{ + assert(IS_CURSOR_SELECT(button)); + if (!me->ourgame->current_key_label) return ""; + return me->ourgame->current_key_label( + me->ui, me->states[me->statepos-1].state, button); +} + void midend_redraw(midend *me) { assert(me->drawing); @@ -2400,6 +2400,35 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->completed = true; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int cx = ui->cur_x, cy = ui->cur_y; + int v = state->grid[cy * state->w + cx]; + + if (state->dead || state->won || !ui->cur_visible) return ""; + if (button == CURSOR_SELECT2) { + if (v == -2) return "Mark"; + if (v == -1) return "Unmark"; + return ""; + } + if (button == CURSOR_SELECT) { + int dy, dx, n = 0; + if (v == -2 || v == -3) return "Uncover"; + if (v == 0) return ""; + /* Count mine markers. */ + for (dy = -1; dy <= +1; dy++) + for (dx = -1; dx <= +1; dx++) + if (cx+dx >= 0 && cx+dx < state->w && + cy+dy >= 0 && cy+dy < state->h) { + if (state->grid[(cy+dy)*state->w+(cx+dx)] == -1) + n++; + } + if (n == v) return "Clear"; + } + return ""; +} + struct game_drawstate { int w, h, tilesize, bg; bool started; @@ -3208,6 +3237,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1033,6 +1033,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + char *cell; + + if (IS_CURSOR_SELECT(button)) { + if (!ui->cur_visible || state->not_completed_clues == 0) return ""; + cell = get_coords(state, state->cells_contents, ui->cur_x, ui->cur_y); + switch (*cell & STATE_OK_NUM) { + case STATE_UNMARKED: + return button == CURSOR_SELECT ? "Black" : "White"; + case STATE_MARKED: + return button == CURSOR_SELECT ? "White" : "Empty"; + case STATE_BLANK: + return button == CURSOR_SELECT ? "Empty" : "Black"; + } + } + return ""; +} + static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) @@ -1598,6 +1618,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, DEFAULT_TILE_SIZE, game_compute_size, game_set_size, @@ -2053,6 +2053,18 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (tile(state, ui->cur_x, ui->cur_y) & LOCKED) { + if (button == CURSOR_SELECT2) return "Unlock"; + } else { + if (button == CURSOR_SELECT) return "Rotate"; + if (button == CURSOR_SELECT2) return "Lock"; + } + return ""; +} + struct game_drawstate { int width, height; int tilesize; @@ -3254,6 +3266,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1050,6 +1050,14 @@ struct game_drawstate { int cur_x, cur_y; }; +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cur_visible) + return "Slide"; + return ""; +} + static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) @@ -1875,6 +1883,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -287,6 +287,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, 20 /* FIXME */, game_compute_size, game_set_size, @@ -1392,6 +1392,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, 48, game_compute_size, game_set_size, @@ -1241,6 +1241,23 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) { + if (!ui->cur_visible) return ""; + switch (state->grid[ui->cur_y * state->common->w + ui->cur_x]) { + case GRID_UNKNOWN: + return button == CURSOR_SELECT ? "Black" : "White"; + case GRID_FULL: + return button == CURSOR_SELECT ? "White" : "Grey"; + case GRID_EMPTY: + return button == CURSOR_SELECT ? "Grey" : "Black"; + } + } + return ""; +} + struct game_drawstate { bool started; int w, h; @@ -2025,6 +2042,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1847,6 +1847,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cursor_active) { + if (button == CURSOR_SELECT) { + if (ui->ndragcoords == -1) return "Start"; + return "Stop"; + } + if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0) + return "Cancel"; + } + return ""; +} + #define PREFERRED_TILE_SIZE 31 #define HALFSZ (ds->halfsz) #define TILE_SIZE (ds->halfsz*2 + 1) @@ -2661,6 +2675,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -800,6 +800,19 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cur_jumping = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int w = state->w; + + if (IS_CURSOR_SELECT(button)) { + if (!ui->cur_visible) return ""; + if (ui->cur_jumping) return "Cancel"; + if (state->grid[ui->cur_y*w+ui->cur_x] == GRID_PEG) return "Select"; + } + return ""; +} + #define PREFERRED_TILE_SIZE 33 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE / 2) @@ -1338,6 +1351,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -307,6 +307,7 @@ void midend_restart_game(midend *me); void midend_stop_anim(midend *me); bool midend_process_key(midend *me, int x, int y, int button, bool *handled); key_label *midend_request_keys(midend *me, int *nkeys); +const char *midend_current_key_label(midend *me, int button); void midend_force_redraw(midend *me); void midend_redraw(midend *me); float *midend_colours(midend *me, int *ncolours); @@ -657,6 +658,8 @@ struct game { key_label *(*request_keys)(const game_params *params, int *nkeys); void (*changed_state)(game_ui *ui, const game_state *oldstate, const game_state *newstate); + const char *(*current_key_label)(const game_ui *ui, + const game_state *state, int button); char *(*interpret_move)(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button); game_state *(*execute_move)(const game_state *state, const char *move); @@ -1245,6 +1245,27 @@ static void decode_ui(game_ui *ui, const char *encoding) { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int cell; + + if (IS_CURSOR_SELECT(button)) { + cell = state->grid[idx(ui->r, ui->c, state->params.w)]; + if (!ui->cursor_show || cell > 0) return ""; + switch (cell) { + case EMPTY: + return button == CURSOR_SELECT ? "Fill" : "Dot"; + case WHITE: + return button == CURSOR_SELECT ? "Empty" : "Fill"; + case BLACK: + return button == CURSOR_SELECT ? "Dot" : "Empty"; + } + } + return ""; + +} + typedef struct drawcell { puzzle_size value; bool error, cursor, flash; @@ -1818,6 +1839,7 @@ struct game const thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -2374,6 +2374,21 @@ struct game_drawstate { unsigned long *visible; }; +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cur_visible && + !(ui->drag_start_x >= 0 && !ui->cur_dragging)) { + if (ui->cur_dragging) { + if (!ui->dragged) return "Cancel"; + if ((button == CURSOR_SELECT2) == ui->erasing) return "Done"; + return "Cancel"; + } + return button == CURSOR_SELECT ? "Mark" : "Erase"; + } + return ""; +} + static char *interpret_move(const game_state *from, game_ui *ui, const game_drawstate *ds, int x, int y, int button) @@ -2992,6 +3007,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1109,6 +1109,25 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->displaysel = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button)) { + int x = ui->xsel, y = ui->ysel, c = COL(state,x,y); + if (c == 0) return ""; + if (ISSEL(ui, x, y)) + return button == CURSOR_SELECT2 ? "Unselect" : "Remove"; + if ((x > 0 && COL(state,x-1,y) == c) || + (x+1 < state->params.w && COL(state,x+1,y) == c) || + (y > 0 && COL(state,x,y-1) == c) || + (y+1 < state->params.h && COL(state,x,y+1) == c)) + return "Select"; + /* Cursor is over a lone square, so we can't select it. */ + if (ui->nselected) return "Unselect"; + } + return ""; +} + static char *sel_movedesc(game_ui *ui, const game_state *state) { int i; @@ -1670,6 +1689,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1421,6 +1421,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cshow) { + if (ui->dragging) { + if (ui->drag_is_from) { + if (isvalidmove(state, false, ui->sx, ui->sy, ui->cx, ui->cy)) + return "To here"; + } else { + if (isvalidmove(state, false, ui->cx, ui->cy, ui->sx, ui->sy)) + return "From here"; + } + return "Cancel"; + } else { + return button == CURSOR_SELECT ? "From here" : "To here"; + } + } + return ""; +} + struct game_drawstate { int tilesize; bool started, solved; @@ -2277,6 +2297,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1473,6 +1473,18 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, ui->cshow = false; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cshow) { + unsigned int f = state->flags[ui->cy * state->w + ui->cx]; + if (f & F_BLACK) return "Restore"; + if (f & F_CIRCLE) return "Remove"; + return button == CURSOR_SELECT ? "Black" : "Circle"; + } + return ""; +} + #define DS_BLACK 0x1 #define DS_CIRCLE 0x2 #define DS_CURSOR 0x4 @@ -1853,6 +1865,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -591,6 +591,21 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cur_visible) { + if (ui->cur_x == -1 || ui->cur_x == state->w || + ui->cur_y == -1 || ui->cur_y == state->h) + return button == CURSOR_SELECT2 ? "Back" : "Slide"; + if (button == CURSOR_SELECT) + return ui->cur_mode == lock_tile ? "Unlock" : "Lock tile"; + if (button == CURSOR_SELECT2) + return ui->cur_mode == lock_position ? "Unlock" : "Lock pos"; + } + return ""; +} + struct game_drawstate { bool started; int w, h, bgcolour; @@ -1197,6 +1212,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1603,6 +1603,22 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cur_visible) { + switch (state->soln[ui->cur_y*state->p.w+ui->cur_x]) { + case 0: + return button == CURSOR_SELECT ? "\\" : "/"; + case -1: + return button == CURSOR_SELECT ? "/" : "Blank"; + case +1: + return button == CURSOR_SELECT ? "Blank" : "\\"; + } + } + return ""; +} + #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) #define BORDER TILESIZE @@ -2175,6 +2191,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, @@ -4594,6 +4594,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (ui->hshow && (button == CURSOR_SELECT)) + return ui->hpencil ? "Ink" : "Pencil"; + return ""; +} + struct game_drawstate { bool started, xtype; int cr; @@ -5618,6 +5626,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1459,6 +1459,22 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int w = state->p.w; + int v = state->grid[ui->cy*w+ui->cx]; + + if (IS_CURSOR_SELECT(button) && ui->cdisp) { + switch (v) { + case BLANK: + return button == CURSOR_SELECT ? "Tent" : "Green"; + case TENT: case NONTENT: return "Clear"; + } + } + return ""; +} + struct game_drawstate { int tilesize; bool started; @@ -2626,6 +2642,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, @@ -1198,6 +1198,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (ui->hshow && (button == CURSOR_SELECT)) + return ui->hpencil ? "Ink" : "Pencil"; + return ""; +} + #define PREFERRED_TILESIZE 48 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE * 9 / 8) @@ -2048,6 +2056,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, @@ -2121,6 +2121,35 @@ static bool ui_can_flip_square(const game_state *state, int x, int y, bool notra return true; } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (IS_CURSOR_SELECT(button) && ui->cursor_active) { + int gx = ui->curx / 2, gy = ui->cury / 2; + int w = state->p.w; + int direction = + ((ui->curx % 2) == 0) ? L : ((ui->cury % 2) == 0) ? U : 0; + if (direction && + ui_can_flip_edge(state, gx, gy, direction, + button == CURSOR_SELECT2)) { + unsigned ef = S_E_FLAGS(state, gx, gy, direction); + switch (button) { + case CURSOR_SELECT: return (ef & E_TRACK) ? "Clear" : "Track"; + case CURSOR_SELECT2: return (ef & E_NOTRACK) ? "Clear" : "X"; + } + } + if (!direction && + ui_can_flip_square(state, gx, gy, button == CURSOR_SELECT2)) { + unsigned sf = state->sflags[gy*w+gx]; + switch (button) { + case CURSOR_SELECT: return (sf & S_TRACK) ? "Clear" : "Track"; + case CURSOR_SELECT2: return (sf & S_NOTRACK) ? "Clear" : "X"; + } + } + } + return ""; +} + static char *edge_flip_str(const game_state *state, int x, int y, int dir, bool notrack, char *buf) { unsigned ef = S_E_FLAGS(state, x, y, dir); char c; @@ -2973,6 +3002,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -637,6 +637,17 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (!ui->cur_visible) return ""; + switch (button) { + case CURSOR_SELECT: return "Turn left"; + case CURSOR_SELECT2: return "Turn right"; + } + return ""; +} + struct game_drawstate { bool started; int w, h, bgcolour; @@ -1313,6 +1324,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1681,6 +1681,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int xi; + + if (ui->hshow && button == CURSOR_SELECT) + return ui->hpencil ? "Ink" : "Pencil"; + if (button == CURSOR_SELECT2) { + xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)]; + if (xi >= 0 && !state->common->fixed[xi]) return "Clear"; + } + return ""; +} + struct game_drawstate { int tilesize; bool started, solved; @@ -2783,6 +2797,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, @@ -1470,6 +1470,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (ui->hshow && IS_CURSOR_SELECT(button)) + return ui->hpencil ? "Ink" : "Pencil"; + return ""; +} + struct game_drawstate { int tilesize, order; bool started; @@ -2139,6 +2147,7 @@ const struct game thegame = { decode_ui, game_request_keys, game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, diff --git a/unfinished/group.c b/unfinished/group.c index 562dcbb..e72e3ce 100644 --- a/unfinished/group.c +++ b/unfinished/group.c @@ -1326,6 +1326,26 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, } } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + if (ui->hshow && button == CURSOR_SELECT) + return ui->hpencil ? "Ink" : "Pencil"; + if (ui->hshow && button == CURSOR_SELECT2) { + int w = state->par.w; + int i; + for (i = 0; i < ui->odn; i++) { + int x = state->sequence[ui->ohx + i*ui->odx]; + int y = state->sequence[ui->ohy + i*ui->ody]; + int index = y*w+x; + if (ui->hpencil && state->grid[index]) return ""; + if (state->common->immutable[index]) return ""; + } + return "Clear"; + } + return ""; +} + #define PREFERRED_TILESIZE 48 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE / 2) @@ -2311,6 +2331,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/unfinished/separate.c b/unfinished/separate.c index daa03c7..1a8edc6 100644 --- a/unfinished/separate.c +++ b/unfinished/separate.c @@ -845,6 +845,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, 20 /* FIXME */, game_compute_size, game_set_size, diff --git a/unfinished/slide.c b/unfinished/slide.c index 9c26acc..acddd93 100644 --- a/unfinished/slide.c +++ b/unfinished/slide.c @@ -2337,6 +2337,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, diff --git a/unfinished/sokoban.c b/unfinished/sokoban.c index 4f37085..26c957c 100644 --- a/unfinished/sokoban.c +++ b/unfinished/sokoban.c @@ -1459,6 +1459,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, @@ -1535,6 +1535,27 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, { } +static const char *current_key_label(const game_ui *ui, + const game_state *state, int button) +{ + int hx = ui->cx, hy = ui->cy; + int w2 = state->w2; + char i = state->grid[hy * w2 + hx]; + + if (ui->cursor && IS_CURSOR_SELECT(button)) { + if (state->common->immutable[hy * w2 + hx]) return ""; + switch (i) { + case EMPTY: + return button == CURSOR_SELECT ? "Black" : "White"; + case N_ONE: + return button == CURSOR_SELECT ? "White" : "Empty"; + case N_ZERO: + return button == CURSOR_SELECT ? "Empty" : "Black"; + } + } + return ""; +} + struct game_drawstate { int tilesize; int w2, h2; @@ -2021,6 +2042,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + current_key_label, interpret_move, execute_move, DEFAULT_TILE_SIZE, game_compute_size, game_set_size, @@ -1481,6 +1481,7 @@ const struct game thegame = { decode_ui, NULL, /* game_request_keys */ game_changed_state, + NULL, /* current_key_label */ interpret_move, execute_move, PREFERRED_TILESIZE, game_compute_size, game_set_size, |