diff --git a/blackbox.c b/blackbox.c index 114a2ac..1b7241d 100644 --- a/blackbox.c +++ b/blackbox.c @@ -1556,6 +1556,7 @@ const struct game thegame = { free_game, true, solve_game, false, NULL, NULL, /* can_format_as_text_now, text_format */ + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, encode_ui, diff --git a/bridges.c b/bridges.c index ce79640..acb64dc 100644 --- a/bridges.c +++ b/bridges.c @@ -3289,6 +3289,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/cube.c b/cube.c index 41057fc..c4073b7 100644 --- a/cube.c +++ b/cube.c @@ -1760,6 +1760,7 @@ const struct game thegame = { free_game, false, NULL, /* solve */ false, NULL, NULL, /* can_format_as_text_now, text_format */ + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/devel.but b/devel.but index 0fb1a14..68a0df2 100644 --- a/devel.but +++ b/devel.but @@ -332,23 +332,49 @@ game ID etc). It persists until the user finishes playing that game and begins another one (or closes the window); in particular, \q{Restart Game} does \e{not} destroy the \c{game_ui}. -\c{game_ui} is useful for implementing user-interface state which is -not part of \c{game_state}. Common examples are keyboard control -(you wouldn't want to have to separately Undo through every cursor -motion) and mouse dragging. See \k{writing-keyboard-cursor} and -\k{writing-howto-dragging}, respectively, for more details. +There are various things that you might store in \c{game_ui}, which +are conceptually different from each other, but I haven't yet found a +need to split them out into smaller sub-structures for different +purposes: -Another use for \c{game_ui} is to store highly persistent data such -as the Mines death counter. This is conceptually rather different: -where the Net cursor position was \e{not important enough} to -preserve for the player to restore by Undo, the Mines death counter -is \e{too important} to permit the player to revert by Undo! +\dt Transient UI state: -A final use for \c{game_ui} is to pass information to the redraw -function about recent changes to the game state. This is used in -Mines, for example, to indicate whether a requested \q{flash} should -be a white flash for victory or a red flash for defeat; see -\k{writing-flash-types}. +\dd Storing a piece of UI state in \c{game_state} means that you can +only update it by appending a move to the undo chain. Some UI state +shouldn't really be treated this way. For example, if your puzzle has +a keyboard-controlled cursor, you probably don't want every cursor +movement to be an undoable action, because the history of where the +cursor went just isn't interesting. More likely the cursor should just +move freely, and the only undoable actions are the ones where you +modify the element under the cursor. So you'd store the cursor +position in \c{game_ui} rather than \c{game_state}. See +\k{writing-keyboard-cursor} for more details. + +\lcont{ Another example of this is the state of an ongoing mouse drag. +If there's an undoable action involved, it will probably occur when +the drag is released. In between, you still need to store state that +the redraw function will use to update the display \dash and that can +live in \c{game_ui}. See \k{writing-howto-dragging} for more details +of this. } + +\dt Persistent UI state: + +\dd An example of this is the counter of deaths in Mines or Inertia. +This shouldn't be reverted by pressing Undo, for the opposite reason +to the cursor position: the cursor position is too boring to store the +history of, but the deaths counter is too \e{important}! + +\dt Information about recent changes to the game state: + +\dd This is used in Mines, for example, to indicate whether a +requested \q{flash} should be a white flash for victory or a red flash +for defeat; see \k{writing-flash-types}. + +\dt User preferences: + +\dd Any user preference about display or UI handled by +\cw{get_prefs()} and \cw{set_prefs()} will need to live in +\c{game_ui}, because that's the structure that those functions access. \H{backend-simple} Simple data in the back end @@ -579,7 +605,8 @@ its initial value; the front end will modify the value fields and return the updated array to \cw{custom_params()} (see \k{backend-custom-params}). -The \cw{config_item} structure contains the following elements: +The \cw{config_item} structure contains the following elements used by +this function: \c const char *name; \c int type; @@ -688,6 +715,57 @@ the dialog box will stay open.) If the game's \c{can_configure} flag is set to \cw{false}, this function is never called and can be \cw{NULL}. +\S{backend-get-prefs} \cw{get_prefs()} + +\c config_item *(*get_prefs)(game_ui *ui); + +This function works very like \cw{configure()}, but instead of +receiving a \c{game_params} and returning GUI elements describing the +data in it, this function receives a \c{game_ui} and returns GUI +elements describing any user preferences stored in that. + +This function should only deal with fields of \c{game_ui} that are +user-settable preferences. In-game state like cursor position and +mouse drags, or per-game state like death counters, are nothing to do +with this function. + +If there are no user preferences, you can set both this function +pointer and \c{set_prefs} to \cw{NULL}. + +In every \c{config_item} returned from this function, you must set an +additional field beyond the ones described in \k{backend-configure}: + +\c const char *kw; + +This should be an identifying keyword for the user preference in +question, suitable for use in configuration files. That means it +should remain stable, even if the user-facing wording in the \c{name} +field is reworded for clarity. If it doesn't stay stable, old +configuration files will not be read correctly. + +For \c{config_item}s of type \cw{C_CHOICES}, you must also set an +extra field in \c{u.choices}: + +\c const char *choicekws; + +This has the same structure as the \c{choicenames} field (a list of +values delimited by the first character in the whole string), and it +provides an identifying keyword for each individual choice in the +list, in the same order as the entries of \c{choicenames}. + +\S{backend-set-prefs} \cw{set_prefs()} + +\c void (*set_prefs)(game_ui *ui, const config_item *cfg); + +This function is the counterpart to \cw{set_prefs()}, as +\cw{custom_params()} is to \cw{configure()}. It receives an array of +\c{config_item}s which was originally created by \cw{get_prefs()}, +with the controls' values updated from user input, and it should +transcribe the new settings into the provided \c{game_ui}. + +If there are no user preferences, you can set both this function +pointer and \c{get_prefs} to \cw{NULL}. + \S{backend-validate-params} \cw{validate_params()} \c const char *(*validate_params)(const game_params *params, diff --git a/dominosa.c b/dominosa.c index 8589018..e1bd96a 100644 --- a/dominosa.c +++ b/dominosa.c @@ -3442,6 +3442,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/fifteen.c b/fifteen.c index d02d0c0..a454a13 100644 --- a/fifteen.c +++ b/fifteen.c @@ -484,6 +484,30 @@ static game_ui *new_ui(const game_state *state) return ui; } +static config_item *get_prefs(game_ui *ui) +{ + config_item *ret; + + ret = snewn(2, config_item); + + ret[0].name = "Sense of arrow keys"; + ret[0].kw = "arrow-semantics"; + ret[0].type = C_CHOICES; + ret[0].u.choices.choicenames = ":Move the tile:Move the gap"; + ret[0].u.choices.choicekws = ":tile:gap"; + ret[0].u.choices.selected = ui->invert_cursor; + + ret[1].name = NULL; + ret[1].type = C_END; + + return ret; +} + +static void set_prefs(game_ui *ui, const config_item *cfg) +{ + ui->invert_cursor = cfg[0].u.choices.selected; +} + static void free_ui(game_ui *ui) { sfree(ui); @@ -1122,6 +1146,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + get_prefs, set_prefs, new_ui, free_ui, NULL, /* encode_ui */ diff --git a/filling.c b/filling.c index 970301c..09156b0 100644 --- a/filling.c +++ b/filling.c @@ -2174,6 +2174,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/flip.c b/flip.c index 10a71b0..1a24e81 100644 --- a/flip.c +++ b/flip.c @@ -1337,6 +1337,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/flood.c b/flood.c index 6274252..87d0bf2 100644 --- a/flood.c +++ b/flood.c @@ -1365,6 +1365,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/galaxies.c b/galaxies.c index dfa1274..ffc64e3 100644 --- a/galaxies.c +++ b/galaxies.c @@ -4129,6 +4129,7 @@ const struct game thegame = { true, solve_game, #endif true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/guess.c b/guess.c index 27aeabd..a59e54e 100644 --- a/guess.c +++ b/guess.c @@ -1518,6 +1518,7 @@ const struct game thegame = { free_game, true, solve_game, false, NULL, NULL, /* can_format_as_text_now, text_format */ + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, encode_ui, diff --git a/inertia.c b/inertia.c index 4ac3eae..7a97562 100644 --- a/inertia.c +++ b/inertia.c @@ -2231,6 +2231,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, encode_ui, diff --git a/keen.c b/keen.c index 0b28a90..333164f 100644 --- a/keen.c +++ b/keen.c @@ -2479,6 +2479,7 @@ const struct game thegame = { free_game, true, solve_game, false, NULL, NULL, /* can_format_as_text_now, text_format */ + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/lightup.c b/lightup.c index 779437b..39ada9f 100644 --- a/lightup.c +++ b/lightup.c @@ -1867,6 +1867,28 @@ static game_ui *new_ui(const game_state *state) return ui; } +static config_item *get_prefs(game_ui *ui) +{ + config_item *ret; + + ret = snewn(2, config_item); + + ret[0].name = "Draw non-light marks even when lit"; + ret[0].kw = "show-lit-blobs"; + ret[0].type = C_BOOLEAN; + ret[0].u.boolean.bval = ui->draw_blobs_when_lit; + + ret[1].name = NULL; + ret[1].type = C_END; + + return ret; +} + +static void set_prefs(game_ui *ui, const config_item *cfg) +{ + ui->draw_blobs_when_lit = cfg[0].u.boolean.bval; +} + static void free_ui(game_ui *ui) { sfree(ui); @@ -2353,6 +2375,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + get_prefs, set_prefs, new_ui, free_ui, NULL, /* encode_ui */ diff --git a/loopy.c b/loopy.c index 0f9dc52..f227e17 100644 --- a/loopy.c +++ b/loopy.c @@ -931,6 +931,37 @@ static void free_ui(game_ui *ui) sfree(ui); } +static config_item *get_prefs(game_ui *ui) +{ + config_item *ret; + + ret = snewn(3, config_item); + + ret[0].name = "Draw excluded grid lines faintly"; + ret[0].kw = "draw-faint-lines"; + ret[0].type = C_BOOLEAN; + ret[0].u.boolean.bval = ui->draw_faint_lines; + + ret[1].name = "Auto-follow unique paths of edges"; + ret[1].kw = "auto-follow"; + ret[1].type = C_CHOICES; + ret[1].u.choices.choicenames = + ":No:Based on grid only:Based on grid and game state"; + ret[1].u.choices.choicekws = ":off:fixed:adaptive"; + ret[1].u.choices.selected = ui->autofollow; + + ret[2].name = NULL; + ret[2].type = C_END; + + return ret; +} + +static void set_prefs(game_ui *ui, const config_item *cfg) +{ + ui->draw_faint_lines = cfg[0].u.boolean.bval; + ui->autofollow = cfg[1].u.choices.selected; +} + static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { @@ -3710,6 +3741,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + get_prefs, set_prefs, new_ui, free_ui, NULL, /* encode_ui */ diff --git a/magnets.c b/magnets.c index 6f216df..e58d8bf 100644 --- a/magnets.c +++ b/magnets.c @@ -2459,6 +2459,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/map.c b/map.c index 31dc96c..966ab75 100644 --- a/map.c +++ b/map.c @@ -2333,6 +2333,30 @@ static game_ui *new_ui(const game_state *state) return ui; } +static config_item *get_prefs(game_ui *ui) +{ + config_item *ret; + + ret = snewn(2, config_item); + + ret[0].name = "Victory flash effect"; + ret[0].kw = "flash-type"; + ret[0].type = C_CHOICES; + ret[0].u.choices.choicenames = ":Cyclic:Each to white:All to white"; + ret[0].u.choices.choicekws = ":cyclic:each-white:all-white"; + ret[0].u.choices.selected = ui->flash_type; + + ret[1].name = NULL; + ret[1].type = C_END; + + return ret; +} + +static void set_prefs(game_ui *ui, const config_item *cfg) +{ + ui->flash_type = cfg[0].u.choices.selected; +} + static void free_ui(game_ui *ui) { sfree(ui); @@ -3291,6 +3315,7 @@ const struct game thegame = { free_game, true, solve_game, false, NULL, NULL, /* can_format_as_text_now, text_format */ + get_prefs, set_prefs, new_ui, free_ui, NULL, /* encode_ui */ diff --git a/mines.c b/mines.c index 5781c4c..9b77683 100644 --- a/mines.c +++ b/mines.c @@ -3193,6 +3193,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, encode_ui, diff --git a/mosaic.c b/mosaic.c index 51d379e..1fb58b6 100644 --- a/mosaic.c +++ b/mosaic.c @@ -1609,6 +1609,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/net.c b/net.c index 2c8909d..9650ef3 100644 --- a/net.c +++ b/net.c @@ -3270,6 +3270,7 @@ const struct game thegame = { free_game, true, solve_game, false, NULL, NULL, /* can_format_as_text_now, text_format */ + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, encode_ui, diff --git a/netslide.c b/netslide.c index 6472e23..d58a615 100644 --- a/netslide.c +++ b/netslide.c @@ -1858,6 +1858,7 @@ const struct game thegame = { free_game, true, solve_game, false, NULL, NULL, /* can_format_as_text_now, text_format */ + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/nullgame.c b/nullgame.c index a779bc6..c1c2ed1 100644 --- a/nullgame.c +++ b/nullgame.c @@ -237,6 +237,7 @@ const struct game thegame = { free_game, false, NULL, /* solve */ false, NULL, NULL, /* can_format_as_text_now, text_format */ + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/palisade.c b/palisade.c index 0af19e5..2d9c990 100644 --- a/palisade.c +++ b/palisade.c @@ -1380,6 +1380,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/pattern.c b/pattern.c index fc01d1f..9ca42f6 100644 --- a/pattern.c +++ b/pattern.c @@ -2078,6 +2078,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/pearl.c b/pearl.c index 7caaae2..7265c70 100644 --- a/pearl.c +++ b/pearl.c @@ -1916,6 +1916,30 @@ static void free_ui(game_ui *ui) sfree(ui); } +static config_item *get_prefs(game_ui *ui) +{ + config_item *ret; + + ret = snewn(2, config_item); + + ret[0].name = "Puzzle appearance"; + ret[0].kw = "appearance"; + ret[0].type = C_CHOICES; + ret[0].u.choices.choicenames = ":Traditional:Loopy-style"; + ret[0].u.choices.choicekws = ":traditional:loopy"; + ret[0].u.choices.selected = ui->gui_style; + + ret[1].name = NULL; + ret[1].type = C_END; + + return ret; +} + +static void set_prefs(game_ui *ui, const config_item *cfg) +{ + ui->gui_style = cfg[0].u.choices.selected; +} + static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { @@ -2744,6 +2768,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + get_prefs, set_prefs, new_ui, free_ui, NULL, /* encode_ui */ diff --git a/pegs.c b/pegs.c index 054d198..35c9a1e 100644 --- a/pegs.c +++ b/pegs.c @@ -1361,6 +1361,7 @@ const struct game thegame = { free_game, false, NULL, /* solve */ true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/puzzles.h b/puzzles.h index b7c34f7..7e192c0 100644 --- a/puzzles.h +++ b/puzzles.h @@ -127,8 +127,13 @@ typedef struct psdata psdata; */ enum { C_STRING, C_CHOICES, C_BOOLEAN, C_END }; struct config_item { - /* Not dynamically allocated */ + /* Not dynamically allocated: the GUI display name for the option */ const char *name; + /* Not dynamically allocated: the keyword identifier for the + * option. Only examined in the case where this structure is being + * used for options that appear in config files, i.e. the + * get_prefs method fills this in but configure does not. */ + const char *kw; /* Value from the above C_* enum */ int type; union { @@ -145,6 +150,13 @@ struct config_item { * options `Foo', `Bar' and `Baz'. */ const char *choicenames; + /* + * choicekws is non-NULL, not dynamically allocated, and + * contains a parallel list of keyword strings used to + * represent the enumeration in config files. As with 'kw' + * above, this is only expected to be set by get_prefs. + */ + const char *choicekws; /* * Indicates the chosen index from the options in * choicenames. In the above example, 0==Foo, 1==Bar and @@ -676,6 +688,8 @@ struct game { bool can_format_as_text_ever; bool (*can_format_as_text_now)(const game_params *params); char *(*text_format)(const game_state *state); + config_item *(*get_prefs)(game_ui *ui); + void (*set_prefs)(game_ui *ui, const config_item *cfg); game_ui *(*new_ui)(const game_state *state); void (*free_ui)(game_ui *ui); char *(*encode_ui)(const game_ui *ui); diff --git a/range.c b/range.c index cd5e89e..4cb771a 100644 --- a/range.c +++ b/range.c @@ -1271,6 +1271,31 @@ static game_ui *new_ui(const game_state *state) return ui; } +static config_item *get_prefs(game_ui *ui) +{ + config_item *ret; + + ret = snewn(2, config_item); + + ret[0].name = "Mouse button order"; + ret[0].kw = "left-mouse-button"; + ret[0].type = C_CHOICES; + ret[0].u.choices.choicenames = + ":Left to fill, right to dot:Left to dot, right to fill"; + ret[0].u.choices.choicekws = ":fill:dot"; + ret[0].u.choices.selected = ui->swap_buttons; + + ret[1].name = NULL; + ret[1].type = C_END; + + return ret; +} + +static void set_prefs(game_ui *ui, const config_item *cfg) +{ + ui->swap_buttons = cfg[0].u.choices.selected; +} + static void free_ui(game_ui *ui) { sfree(ui); @@ -1834,6 +1859,7 @@ struct game const thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + get_prefs, set_prefs, new_ui, free_ui, NULL, /* encode_ui */ diff --git a/rect.c b/rect.c index a4ff8d2..2c41db8 100644 --- a/rect.c +++ b/rect.c @@ -2995,6 +2995,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/samegame.c b/samegame.c index 5c57a55..f228b19 100644 --- a/samegame.c +++ b/samegame.c @@ -1666,6 +1666,7 @@ const struct game thegame = { free_game, false, NULL, /* solve */ true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/signpost.c b/signpost.c index 787239c..9c1bae3 100644 --- a/signpost.c +++ b/signpost.c @@ -1437,6 +1437,30 @@ static void free_ui(game_ui *ui) sfree(ui); } +static config_item *get_prefs(game_ui *ui) +{ + config_item *ret; + + ret = snewn(2, config_item); + + ret[0].name = "Victory rotation effect"; + ret[0].kw = "flash-type"; + ret[0].type = C_CHOICES; + ret[0].u.choices.choicenames = ":Unidirectional:Meshing gears"; + ret[0].u.choices.choicekws = ":unidirectional:gears"; + ret[0].u.choices.selected = ui->gear_mode; + + ret[1].name = NULL; + ret[1].type = C_END; + + return ret; +} + +static void set_prefs(game_ui *ui, const config_item *cfg) +{ + ui->gear_mode = cfg[0].u.choices.selected; +} + static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { @@ -2292,6 +2316,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + get_prefs, set_prefs, new_ui, free_ui, NULL, /* encode_ui */ diff --git a/singles.c b/singles.c index 6eb5a2c..7b7b48f 100644 --- a/singles.c +++ b/singles.c @@ -1852,6 +1852,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/sixteen.c b/sixteen.c index 3f5ed5b..57be015 100644 --- a/sixteen.c +++ b/sixteen.c @@ -1191,6 +1191,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/slant.c b/slant.c index ee39d74..77fec1e 100644 --- a/slant.c +++ b/slant.c @@ -1617,6 +1617,30 @@ static game_ui *new_ui(const game_state *state) return ui; } +static config_item *get_prefs(game_ui *ui) +{ + config_item *ret; + + ret = snewn(2, config_item); + + ret[0].name = "Mouse button order"; + ret[0].kw = "left-button"; + ret[0].type = C_CHOICES; + ret[0].u.choices.choicenames = ":Left \\, right /:Left /, right \\"; + ret[0].u.choices.choicekws = ":\\:/"; + ret[0].u.choices.selected = ui->swap_buttons; + + ret[1].name = NULL; + ret[1].type = C_END; + + return ret; +} + +static void set_prefs(game_ui *ui, const config_item *cfg) +{ + ui->swap_buttons = cfg[0].u.choices.selected; +} + static void free_ui(game_ui *ui) { sfree(ui); @@ -2204,6 +2228,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + get_prefs, set_prefs, new_ui, free_ui, NULL, /* encode_ui */ diff --git a/solo.c b/solo.c index 4005f2d..27ac7c9 100644 --- a/solo.c +++ b/solo.c @@ -5630,6 +5630,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/tents.c b/tents.c index c6a3514..e772e57 100644 --- a/tents.c +++ b/tents.c @@ -2649,6 +2649,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/towers.c b/towers.c index 0c2bf3a..84a8e71 100644 --- a/towers.c +++ b/towers.c @@ -1209,6 +1209,30 @@ static void free_ui(game_ui *ui) sfree(ui); } +static config_item *get_prefs(game_ui *ui) +{ + config_item *ret; + + ret = snewn(2, config_item); + + ret[0].name = "Puzzle appearance"; + ret[0].kw = "appearance"; + ret[0].type = C_CHOICES; + ret[0].u.choices.choicenames = ":2D:3D"; + ret[0].u.choices.choicekws = ":2d:3d"; + ret[0].u.choices.selected = ui->three_d; + + ret[1].name = NULL; + ret[1].type = C_END; + + return ret; +} + +static void set_prefs(game_ui *ui, const config_item *cfg) +{ + ui->three_d = cfg[0].u.choices.selected; +} + static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { @@ -2084,6 +2108,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + get_prefs, set_prefs, new_ui, free_ui, NULL, /* encode_ui */ diff --git a/tracks.c b/tracks.c index 294ae04..4578296 100644 --- a/tracks.c +++ b/tracks.c @@ -3047,6 +3047,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/twiddle.c b/twiddle.c index 3095c99..f7216df 100644 --- a/twiddle.c +++ b/twiddle.c @@ -1305,6 +1305,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/undead.c b/undead.c index 2e18e11..74dbefc 100644 --- a/undead.c +++ b/undead.c @@ -2784,6 +2784,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/unequal.c b/unequal.c index dff5885..e67f028 100644 --- a/unequal.c +++ b/unequal.c @@ -2154,6 +2154,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/unfinished/group.c b/unfinished/group.c index dfa466d..7faee89 100644 --- a/unfinished/group.c +++ b/unfinished/group.c @@ -2323,6 +2323,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/unfinished/separate.c b/unfinished/separate.c index 635d7c6..6ca0725 100644 --- a/unfinished/separate.c +++ b/unfinished/separate.c @@ -835,6 +835,7 @@ const struct game thegame = { free_game, false, solve_game, false, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/unfinished/slide.c b/unfinished/slide.c index 6368eba..c797d7e 100644 --- a/unfinished/slide.c +++ b/unfinished/slide.c @@ -2328,6 +2328,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/unfinished/sokoban.c b/unfinished/sokoban.c index 3b847af..1f3c688 100644 --- a/unfinished/sokoban.c +++ b/unfinished/sokoban.c @@ -1450,6 +1450,7 @@ const struct game thegame = { free_game, false, solve_game, false, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/unruly.c b/unruly.c index 3ee1767..2f2e51e 100644 --- a/unruly.c +++ b/unruly.c @@ -2030,6 +2030,7 @@ const struct game thegame = { free_game, true, solve_game, true, game_can_format_as_text_now, game_text_format, + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */ diff --git a/untangle.c b/untangle.c index 8ae1a7f..024a167 100644 --- a/untangle.c +++ b/untangle.c @@ -1454,6 +1454,7 @@ const struct game thegame = { free_game, true, solve_game, false, NULL, NULL, /* can_format_as_text_now, text_format */ + NULL, NULL, /* get_prefs, set_prefs */ new_ui, free_ui, NULL, /* encode_ui */