int movetype;
};
-struct midend_data {
+struct midend {
frontend *frontend;
random_state *random;
const game *ourgame;
float elapsed;
char *laststatus;
+ drawing *drawing;
+
int pressed_mouse_button;
int tilesize, winwidth, winheight;
} \
} while (0)
-midend_data *midend_new(frontend *fe, const game *ourgame)
+midend *midend_new(frontend *fe, const game *ourgame,
+ const drawing_api *drapi, void *drhandle)
{
- midend_data *me = snew(midend_data);
+ midend *me = snew(midend);
void *randseed;
int randseedsize;
me->timing = FALSE;
me->elapsed = 0.0F;
me->tilesize = me->winwidth = me->winheight = 0;
+ if (drapi)
+ me->drawing = drawing_init(drapi, drhandle);
+ else
+ me->drawing = NULL;
sfree(randseed);
return me;
}
-static void midend_free_game(midend_data *me)
+static void midend_free_game(midend *me)
{
while (me->nstates > 0) {
me->nstates--;
}
if (me->drawstate)
- me->ourgame->free_drawstate(me->drawstate);
+ me->ourgame->free_drawstate(me->drawing, me->drawstate);
}
-void midend_free(midend_data *me)
+void midend_free(midend *me)
{
int i;
midend_free_game(me);
+ if (me->drawing)
+ drawing_free(me->drawing);
random_free(me->random);
sfree(me->states);
sfree(me->desc);
sfree(me);
}
-static void midend_size_new_drawstate(midend_data *me)
+static void midend_size_new_drawstate(midend *me)
{
/*
* Don't even bother, if we haven't worked out our tile size
if (me->tilesize > 0) {
me->ourgame->compute_size(me->params, me->tilesize,
&me->winwidth, &me->winheight);
- me->ourgame->set_size(me->drawstate, me->params, me->tilesize);
+ me->ourgame->set_size(me->drawing, me->drawstate,
+ me->params, me->tilesize);
}
}
-void midend_size(midend_data *me, int *x, int *y, int expand)
+void midend_size(midend *me, int *x, int *y, int expand)
{
int min, max;
int rx, ry;
*y = me->winheight;
}
-void midend_set_params(midend_data *me, game_params *params)
+void midend_set_params(midend *me, game_params *params)
{
me->ourgame->free_params(me->params);
me->params = me->ourgame->dup_params(params);
}
-static void midend_set_timer(midend_data *me)
+game_params *midend_get_params(midend *me)
+{
+ return me->ourgame->dup_params(me->params);
+}
+
+static void midend_set_timer(midend *me)
{
me->timing = (me->ourgame->is_timed &&
- me->ourgame->timing_state(me->states[me->statepos-1].state));
+ me->ourgame->timing_state(me->states[me->statepos-1].state,
+ me->ui));
if (me->timing || me->flash_time || me->anim_time)
activate_timer(me->frontend);
else
deactivate_timer(me->frontend);
}
-void midend_force_redraw(midend_data *me)
+void midend_force_redraw(midend *me)
{
if (me->drawstate)
- me->ourgame->free_drawstate(me->drawstate);
- me->drawstate = me->ourgame->new_drawstate(me->states[0].state);
+ me->ourgame->free_drawstate(me->drawing, me->drawstate);
+ me->drawstate = me->ourgame->new_drawstate(me->drawing,
+ me->states[0].state);
midend_size_new_drawstate(me);
midend_redraw(me);
}
-void midend_new_game(midend_data *me)
+void midend_new_game(midend *me)
{
midend_free_game(me);
me->aux_info = NULL;
rs = random_init(me->seedstr, strlen(me->seedstr));
+ /*
+ * If this midend has been instantiated without providing a
+ * drawing API, it is non-interactive. This means that it's
+ * being used for bulk game generation, and hence we should
+ * pass the non-interactive flag to new_desc.
+ */
me->desc = me->ourgame->new_desc(me->curparams, rs,
- &me->aux_info, TRUE);
+ &me->aux_info, (me->drawing != NULL));
me->privdesc = NULL;
random_free(rs);
}
me->states[me->nstates].movetype = NEWGAME;
me->nstates++;
me->statepos = 1;
- me->drawstate = me->ourgame->new_drawstate(me->states[0].state);
+ me->drawstate = me->ourgame->new_drawstate(me->drawing,
+ me->states[0].state);
midend_size_new_drawstate(me);
me->elapsed = 0.0F;
- midend_set_timer(me);
if (me->ui)
me->ourgame->free_ui(me->ui);
me->ui = me->ourgame->new_ui(me->states[0].state);
+ midend_set_timer(me);
me->pressed_mouse_button = 0;
}
-static int midend_undo(midend_data *me)
+static int midend_undo(midend *me)
{
if (me->statepos > 1) {
if (me->ui)
return 0;
}
-static int midend_redo(midend_data *me)
+static int midend_redo(midend *me)
{
if (me->statepos < me->nstates) {
if (me->ui)
return 0;
}
-static void midend_finish_move(midend_data *me)
+static void midend_finish_move(midend *me)
{
float flashtime;
midend_set_timer(me);
}
-void midend_stop_anim(midend_data *me)
+void midend_stop_anim(midend *me)
{
if (me->oldstate || me->anim_time != 0) {
midend_finish_move(me);
}
}
-void midend_restart_game(midend_data *me)
+void midend_restart_game(midend *me)
{
game_state *s;
midend_set_timer(me);
}
-static int midend_really_process_key(midend_data *me, int x, int y, int button)
+static int midend_really_process_key(midend *me, int x, int y, int button)
{
game_state *oldstate =
me->ourgame->dup_game(me->states[me->statepos - 1].state);
- int special = FALSE, gotspecial = FALSE, ret = 1;
+ int type = MOVE, gottype = FALSE, ret = 1;
float anim_time;
game_state *s;
char *movestr;
} else if (button == 'u' || button == 'u' ||
button == '\x1A' || button == '\x1F') {
midend_stop_anim(me);
- special = special(me->states[me->statepos-1].movetype);
- gotspecial = TRUE;
+ type = me->states[me->statepos-1].movetype;
+ gottype = TRUE;
if (!midend_undo(me))
goto done;
} else if (button == 'r' || button == 'R' ||
me->states[me->nstates].movetype = MOVE;
me->statepos = ++me->nstates;
me->dir = +1;
+ if (me->ui)
+ me->ourgame->changed_state(me->ui,
+ me->states[me->statepos-2].state,
+ me->states[me->statepos-1].state);
} else {
goto done;
}
}
- if (!gotspecial)
- special = special(me->states[me->statepos-1].movetype);
+ if (!gottype)
+ type = me->states[me->statepos-1].movetype;
/*
* See if this move requires an animation.
*/
- if (special) {
+ if (special(type) && !(type == SOLVE &&
+ (me->ourgame->mouse_priorities & SOLVE_ANIMATES))) {
anim_time = 0;
} else {
anim_time = me->ourgame->anim_length(oldstate,
return ret;
}
-int midend_process_key(midend_data *me, int x, int y, int button)
+int midend_process_key(midend *me, int x, int y, int button)
{
int ret = 1;
return ret;
}
-void midend_redraw(midend_data *me)
+void midend_redraw(midend *me)
{
+ assert(me->drawing);
+
if (me->statepos > 0 && me->drawstate) {
- start_draw(me->frontend);
+ start_draw(me->drawing);
if (me->oldstate && me->anim_time > 0 &&
me->anim_pos < me->anim_time) {
assert(me->dir != 0);
- me->ourgame->redraw(me->frontend, me->drawstate, me->oldstate,
+ me->ourgame->redraw(me->drawing, me->drawstate, me->oldstate,
me->states[me->statepos-1].state, me->dir,
me->ui, me->anim_pos, me->flash_pos);
} else {
- me->ourgame->redraw(me->frontend, me->drawstate, NULL,
+ me->ourgame->redraw(me->drawing, me->drawstate, NULL,
me->states[me->statepos-1].state, +1 /*shrug*/,
me->ui, 0.0, me->flash_pos);
}
- end_draw(me->frontend);
+ end_draw(me->drawing);
}
}
-void midend_timer(midend_data *me, float tplus)
+void midend_timer(midend *me, float tplus)
{
me->anim_pos += tplus;
if (me->anim_pos >= me->anim_time ||
float oldelapsed = me->elapsed;
me->elapsed += tplus;
if ((int)oldelapsed != (int)me->elapsed)
- status_bar(me->frontend, me->laststatus ? me->laststatus : "");
+ status_bar(me->drawing, me->laststatus ? me->laststatus : "");
}
midend_set_timer(me);
}
-float *midend_colours(midend_data *me, int *ncolours)
+float *midend_colours(midend *me, int *ncolours)
{
game_state *state = NULL;
float *ret;
for (i = 0; i < *ncolours; i++) {
char buf[80], *e;
unsigned int r, g, b;
- int j;
+ int j, k;
sprintf(buf, "%s_COLOUR_%d", me->ourgame->name, i);
- for (j = 0; buf[j]; j++)
- buf[j] = toupper((unsigned char)buf[j]);
+ for (j = k = 0; buf[j]; j++)
+ if (!isspace((unsigned char)buf[j]))
+ buf[k++] = toupper((unsigned char)buf[j]);
+ buf[k] = '\0';
if ((e = getenv(buf)) != NULL &&
sscanf(e, "%2x%2x%2x", &r, &g, &b) == 3) {
ret[i*3 + 0] = r / 255.0;
return ret;
}
-int midend_num_presets(midend_data *me)
+int midend_num_presets(midend *me)
{
if (!me->npresets) {
char *name;
* encoded parameter strings.
*/
char buf[80], *e, *p;
- int j;
+ int j, k;
sprintf(buf, "%s_PRESETS", me->ourgame->name);
- for (j = 0; buf[j]; j++)
- buf[j] = toupper((unsigned char)buf[j]);
+ for (j = k = 0; buf[j]; j++)
+ if (!isspace((unsigned char)buf[j]))
+ buf[k++] = toupper((unsigned char)buf[j]);
+ buf[k] = '\0';
if ((e = getenv(buf)) != NULL) {
p = e = dupstr(e);
return me->npresets;
}
-void midend_fetch_preset(midend_data *me, int n,
+void midend_fetch_preset(midend *me, int n,
char **name, game_params **params)
{
assert(n >= 0 && n < me->npresets);
*params = me->presets[n];
}
-int midend_wants_statusbar(midend_data *me)
+int midend_wants_statusbar(midend *me)
{
return me->ourgame->wants_statusbar();
}
-void midend_supersede_game_desc(midend_data *me, char *desc, char *privdesc)
+void midend_supersede_game_desc(midend *me, char *desc, char *privdesc)
{
sfree(me->desc);
sfree(me->privdesc);
me->privdesc = privdesc ? dupstr(privdesc) : NULL;
}
-config_item *midend_get_config(midend_data *me, int which, char **wintitle)
+config_item *midend_get_config(midend *me, int which, char **wintitle)
{
char *titlebuf, *parstr, *rest;
config_item *ret;
return NULL;
}
-static char *midend_game_id_int(midend_data *me, char *id, int defmode)
+static char *midend_game_id_int(midend *me, char *id, int defmode)
{
char *error, *par, *desc, *seed;
game_params *newcurparams, *newparams, *oldparams1, *oldparams2;
return NULL;
}
-char *midend_game_id(midend_data *me, char *id)
+char *midend_game_id(midend *me, char *id)
{
return midend_game_id_int(me, id, DEF_PARAMS);
}
-char *midend_set_config(midend_data *me, int which, config_item *cfg)
+char *midend_get_game_id(midend *me)
+{
+ char *parstr, *ret;
+
+ parstr = me->ourgame->encode_params(me->curparams, FALSE);
+ assert(parstr);
+ assert(me->desc);
+ ret = snewn(strlen(parstr) + strlen(me->desc) + 2, char);
+ sprintf(ret, "%s:%s", parstr, me->desc);
+ sfree(parstr);
+ return ret;
+}
+
+char *midend_set_config(midend *me, int which, config_item *cfg)
{
char *error;
game_params *params;
return NULL;
}
-char *midend_text_format(midend_data *me)
+char *midend_text_format(midend *me)
{
if (me->ourgame->can_format_as_text && me->statepos > 0)
return me->ourgame->text_format(me->states[me->statepos-1].state);
return NULL;
}
-char *midend_solve(midend_data *me)
+char *midend_solve(midend *me)
{
game_state *s;
char *msg, *movestr;
me->ourgame->changed_state(me->ui,
me->states[me->statepos-2].state,
me->states[me->statepos-1].state);
- me->anim_time = 0.0;
- midend_finish_move(me);
+ me->dir = +1;
+ if (me->ourgame->mouse_priorities & SOLVE_ANIMATES) {
+ me->oldstate = me->ourgame->dup_game(me->states[me->statepos-2].state);
+ me->anim_time =
+ me->ourgame->anim_length(me->states[me->statepos-2].state,
+ me->states[me->statepos-1].state,
+ +1, me->ui);
+ me->anim_pos = 0.0;
+ } else {
+ me->anim_time = 0.0;
+ midend_finish_move(me);
+ }
midend_redraw(me);
midend_set_timer(me);
return NULL;
}
-char *midend_rewrite_statusbar(midend_data *me, char *text)
+char *midend_rewrite_statusbar(midend *me, char *text)
{
/*
* An important special case is that we are occasionally called
#define SERIALISE_MAGIC "Simon Tatham's Portable Puzzle Collection"
#define SERIALISE_VERSION "1"
-void midend_serialise(midend_data *me,
+void midend_serialise(midend *me,
void (*write)(void *ctx, void *buf, int len),
void *wctx)
{
/*
* This function returns NULL on success, or an error message.
*/
-char *midend_deserialise(midend_data *me,
+char *midend_deserialise(midend *me,
int (*read)(void *ctx, void *buf, int len),
void *rctx)
{
* We construct all the new state in local variables while we
* check its sanity. Only once we have finished reading the
* serialised data and detected no errors at all do we start
- * modifying stuff in the midend_data passed in.
+ * modifying stuff in the midend passed in.
*/
char *seed = NULL, *parstr = NULL, *desc = NULL, *privdesc = NULL;
char *auxinfo = NULL, *uistr = NULL, *cparstr = NULL;
if (key[8] != ':') {
if (started)
ret = "Data was incorrectly formatted for a saved game file";
+ goto cleanup;
}
len = strcspn(key, ": ");
assert(len <= 8);
midend_set_timer(me);
if (me->drawstate)
- me->ourgame->free_drawstate(me->drawstate);
+ me->ourgame->free_drawstate(me->drawing, me->drawstate);
me->drawstate =
- me->ourgame->new_drawstate(me->states[me->statepos-1].state);
+ me->ourgame->new_drawstate(me->drawing,
+ me->states[me->statepos-1].state);
midend_size_new_drawstate(me);
ret = NULL; /* success! */
return ret;
}
+
+char *midend_print_puzzle(midend *me, document *doc, int with_soln)
+{
+ game_state *soln = NULL;
+
+ if (me->statepos < 1)
+ return "No game set up to print";/* _shouldn't_ happen! */
+
+ if (with_soln) {
+ char *msg, *movestr;
+
+ if (!me->ourgame->can_solve)
+ return "This game does not support the Solve operation";
+
+ msg = "Solve operation failed";/* game _should_ overwrite on error */
+ movestr = me->ourgame->solve(me->states[0].state,
+ me->states[me->statepos-1].state,
+ me->aux_info, &msg);
+ if (!movestr)
+ return msg;
+ soln = me->ourgame->execute_move(me->states[me->statepos-1].state,
+ movestr);
+ assert(soln);
+
+ sfree(movestr);
+ } else
+ soln = NULL;
+
+ /*
+ * This call passes over ownership of the two game_states and
+ * the game_params. Hence we duplicate the ones we want to
+ * keep, and we don't have to bother freeing soln if it was
+ * non-NULL.
+ */
+ document_add_puzzle(doc, me->ourgame,
+ me->ourgame->dup_params(me->curparams),
+ me->ourgame->dup_game(me->states[0].state), soln);
+
+ return NULL;
+}