free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
freeze the timer thereafter so that the user can undo back through
their solution process without altering their time.
+\S{backend-request-keys} \cw{request_keys()}
+
+\c key_label *(*request_keys)(const game_params *params, int *nkeys);
+
+This function returns a dynamically allocated array of \cw{key_label}
+items containing the buttons the back end deems absolutely
+\e{necessary} for gameplay, not an exhaustive list of every button the
+back end could accept. For example, Keen only returns the digits up to
+the game size and the backspace character, \cw{\\b}, even though it
+\e{could} accept \cw{M}, as only these buttons are actually needed to
+play the game. Each \cw{key_label} item contains the following fields:
+
+\c struct key_label {
+\c const char *label; /* label for frontend use */
+\c int button; /* button to pass to midend */
+\c } key_label;
+
+The \cw{label} field of this structure can (and often will) be set by
+the backend to \cw{NULL}, in which case the midend will instead call
+\c{button2label()} (\k{utils-button2label}) and fill in a generic
+label. The \cw{button} field is the associated code that can be passed
+to the midend when the frontend deems appropriate.
+
+The backend should set \cw{*nkeys} to the number of elements in the
+returned array.
+
+The field for this function point in the \cw{game} structure might be
+set to \cw{NULL} (and indeed it is for the majority of the games) to
+indicate that no additional buttons (apart from the cursor keys) are
+required to play the game.
+
+This function should not be called directly by frontends. Instead,
+frontends should use \cw{midend_request_keys()}
+(\k{midend-request-keys}).
+
\S{backend-flags} \c{flags}
\c int flags;
program. A front end should shut down the puzzle in response to a
zero return.
+\H{midend-request-keys} \cw{midend_request_keys()}
+
+\c key_label *midend_request_keys(midend *me, int *nkeys);
+
+This function behaves similarly to the backend's \cw{request_keys()}
+function (\k{backend-request-keys}). If the backend does not provide
+\cw{request_keys()}, this function will return \cw{NULL} and set
+\cw{*nkeys} to zero. Otherwise, this function will fill in the generic
+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-colours} \cw{midend_colours()}
\c float *midend_colours(midend *me, int *ncolours);
to RGB values defining a sensible background colour, and similary
\c{highlight} and \c{lowlight} will be set to sensible colours.
+\S{utils-button2label} \cw{button2label()}
+
+\c char *button2label(int button);
+
+This function generates a descriptive text label for \cw{button},
+which should be a button code that can be passed to the midend. For
+example, calling this function with \cw{CURSOR_UP} will result in the
+string \cw{"Up"}. This function should only be called when the
+\cw{key_label} item returned by a backend's \cw{request_keys()}
+(\k{backend-request-keys}) function has its \cw{label} field set to
+\cw{NULL}; in this case, the corresponding \cw{button} field can be
+passed to this function to obtain an appropriate label. If, however,
+the field is not \cw{NULL}, this function should not be called with
+the corresponding \cw{button} field.
+
+The returned string is dynamically allocated and should be
+\cw{sfree}'d by the caller.
+
\C{writing} How to write a new puzzle
This chapter gives a guide to how to actually write a new puzzle:
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
return (area < sz) ? "Not enough data to fill grid" : NULL;
}
+static key_label *game_request_keys(const game_params *params, int *nkeys)
+{
+ key_label *keys = snewn(11, key_label);
+ *nkeys = 11;
+
+ int i;
+
+ for(i = 0; i < 10; ++i)
+ {
+ keys[i].button = '0' + i;
+ keys[i].label = NULL;
+ }
+ keys[10].button = '\b';
+ keys[10].label = NULL;
+
+ return keys;
+}
+
static game_state *new_game(midend *me, const game_params *params,
const char *desc)
{
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
return NULL;
}
+static key_label *game_request_keys(const game_params *params, int *nkeys)
+{
+ int i;
+ int w = params->w;
+
+ key_label *keys = snewn(w+1, key_label);
+ *nkeys = w + 1;
+
+ for (i = 0; i < w; i++) {
+ if (i<9) keys[i].button = '1' + i;
+ else keys[i].button = 'a' + i - 9;
+
+ keys[i].label = NULL;
+ }
+ keys[w].button = '\b';
+ keys[w].label = NULL;
+
+
+ return keys;
+}
+
static game_state *new_game(midend *me, const game_params *params,
const char *desc)
{
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
return ret;
}
+key_label *midend_request_keys(midend *me, int *n)
+{
+ key_label *keys = NULL;
+ int nkeys = 0, i;
+
+ if(me->ourgame->request_keys)
+ {
+ keys = me->ourgame->request_keys(midend_get_params(me), &nkeys);
+ for(i = 0; i < nkeys; ++i)
+ {
+ if(!keys[i].label)
+ keys[i].label = button2label(keys[i].button);
+ }
+ }
+
+ if(n)
+ *n = nkeys;
+
+ return keys;
+}
+
void midend_redraw(midend *me)
{
assert(me->drawing);
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
sfree(cfg);
}
+void free_keys(key_label *keys, int nkeys)
+{
+ int i;
+
+ for(i = 0; i < nkeys; i++)
+ sfree(keys->label);
+ sfree(keys);
+}
+
/*
* The Mines (among others) game descriptions contain the location of every
* mine, and can therefore be used to cheat.
buf[sz - 1] = 0;
}
+/* Returns a dynamically allocated label for a generic button.
+ * Game-specific buttons should go into the `label' field of key_label
+ * instead. */
+char *button2label(int button)
+{
+ /* check if it's a keyboard button */
+ if(('A' <= button && button <= 'Z') ||
+ ('a' <= button && button <= 'z') ||
+ ('0' <= button && button <= '9') )
+ {
+ char str[2] = { button, '\0' };
+ return dupstr(str);
+ }
+
+ switch(button)
+ {
+ case CURSOR_UP:
+ return dupstr("Up");
+ case CURSOR_DOWN:
+ return dupstr("Down");
+ case CURSOR_LEFT:
+ return dupstr("Left");
+ case CURSOR_RIGHT:
+ return dupstr("Right");
+ case CURSOR_SELECT:
+ return dupstr("Select");
+ case '\b':
+ return dupstr("Clear");
+ default:
+ fatal("unknown generic key");
+ }
+
+ /* should never get here */
+ return NULL;
+}
+
/* vim: set shiftwidth=4 tabstop=8: */
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
* want to organise their preset menu usage */
game_params *preset_menu_lookup_by_id(struct preset_menu *menu, int id);
+/*
+ * Small structure specifying a UI button in a keyboardless front
+ * end. The button will have the text of "label" written on it, and
+ * pressing it causes the value "button" to be passed to
+ * midend_process_key() as if typed at the keyboard.
+ *
+ * If `label' is NULL (which it likely will be), a generic label can
+ * be generated with the button2label() function.
+ */
+typedef struct key_label {
+ /* What should be displayed to the user by the frontend. Backends
+ * can set this field to NULL and have it filled in by the midend
+ * with a generic label. Dynamically allocated, but frontends
+ * should probably use free_keys() to free instead. */
+ char *label;
+ int button; /* passed to midend_process_key when button is pressed */
+} key_label;
+
/*
* Platform routines
*/
void midend_restart_game(midend *me);
void midend_stop_anim(midend *me);
int midend_process_key(midend *me, int x, int y, int button);
+key_label *midend_request_keys(midend *me, int *nkeys);
void midend_force_redraw(midend *me);
void midend_redraw(midend *me);
float *midend_colours(midend *me, int *ncolours);
* misc.c
*/
void free_cfg(config_item *cfg);
+void free_keys(key_label *keys, int nkeys);
void obfuscate_bitmap(unsigned char *bmp, int bits, int decode);
char *fgetline(FILE *fp);
* less than buffer size. */
void copy_left_justified(char *buf, size_t sz, const char *str);
+/* Returns a generic label based on the value of `button.' To be used
+ whenever a `label' field returned by the request_keys() game
+ function is NULL. Dynamically allocated, to be freed by caller. */
+char *button2label(int button);
+
/*
* dsf.c
*/
void (*free_ui)(game_ui *ui);
char *(*encode_ui)(const game_ui *ui);
void (*decode_ui)(game_ui *ui, const char *encoding);
+ key_label *(*request_keys)(const game_params *params, int *nkeys);
void (*changed_state)(game_ui *ui, const game_state *oldstate,
const game_state *newstate);
char *(*interpret_move)(const game_state *state, game_ui *ui,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
return b;
}
+static key_label *game_request_keys(const game_params *params, int *nkeys)
+{
+ int i;
+ int cr = params->c * params->r;
+ key_label *keys = snewn(cr+1, key_label);
+ *nkeys = cr + 1;
+
+ for (i = 0; i < cr; i++) {
+ if (i<9) keys[i].button = '1' + i;
+ else keys[i].button = 'a' + i - 9;
+
+ keys[i].label = NULL;
+ }
+ keys[cr].button = '\b';
+ keys[cr].label = NULL;
+
+
+ return keys;
+}
+
static char *new_game_desc(const game_params *params, random_state *rs,
char **aux, int interactive)
{
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
return NULL;
}
+static key_label *game_request_keys(const game_params *params, int *nkeys)
+{
+ int i;
+ int w = params->w;
+ key_label *keys = snewn(w+1, key_label);
+ *nkeys = w + 1;
+
+ for (i = 0; i < w; i++) {
+ if (i<9) keys[i].button = '1' + i;
+ else keys[i].button = 'a' + i - 9;
+
+ keys[i].label = NULL;
+ }
+ keys[w].button = '\b';
+ keys[w].label = NULL;
+
+ return keys;
+}
+
static game_state *new_game(midend *me, const game_params *params,
const char *desc)
{
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
return;
}
+static key_label *game_request_keys(const game_params *params, int *nkeys)
+{
+ key_label *keys = snewn(4, key_label);
+ *nkeys = 4;
+
+ keys[0].button = 'G';
+ keys[0].label = dupstr("Ghost");
+
+ keys[1].button = 'V';
+ keys[1].label = dupstr("Vampire");
+
+ keys[2].button = 'Z';
+ keys[2].label = dupstr("Zombie");
+
+ keys[3].button = '\b';
+ keys[3].label = NULL;
+
+ return keys;
+}
+
static game_state *new_game(midend *me, const game_params *params,
const char *desc)
{
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
return NULL;
}
+static key_label *game_request_keys(const game_params *params, int *nkeys)
+{
+ int order = params->order;
+ char off = (order > 9) ? '0' : '1';
+ key_label *keys = snewn(order + 1, key_label);
+ *nkeys = order + 1;
+
+ int i;
+ for(i = 0; i < order; i++) {
+ if (i==10) off = 'a'-10;
+ keys[i].button = i + off;
+ keys[i].label = NULL;
+ }
+ keys[order].button = '\b';
+ keys[order].label = NULL;
+
+ return keys;
+}
+
static game_state *new_game(midend *me, const game_params *params,
const char *desc)
{
free_ui,
encode_ui,
decode_ui,
+ game_request_keys,
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,
free_ui,
encode_ui,
decode_ui,
+ NULL, /* game_request_keys */
game_changed_state,
interpret_move,
execute_move,