X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/9d6c3859071b207a37cbd56624937a6b2daafdab..f487468f67fa996caab7de5b08a5da2c25ebd551:/midend.c diff --git a/midend.c b/midend.c index 4c28ec8..75d51bb 100644 --- a/midend.c +++ b/midend.c @@ -25,7 +25,7 @@ struct midend_state_entry { int movetype; }; -struct midend_data { +struct midend { frontend *frontend; random_state *random; const game *ourgame; @@ -76,6 +76,8 @@ struct midend_data { float elapsed; char *laststatus; + drawing *drawing; + int pressed_mouse_button; int tilesize, winwidth, winheight; @@ -89,9 +91,10 @@ struct midend_data { } \ } 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; @@ -122,13 +125,17 @@ midend_data *midend_new(frontend *fe, const game *ourgame) 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--; @@ -137,15 +144,17 @@ static void midend_free_game(midend_data *me) } 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); @@ -169,7 +178,7 @@ void midend_free(midend_data *me) 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 @@ -178,11 +187,12 @@ static void midend_size_new_drawstate(midend_data *me) 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; @@ -228,13 +238,13 @@ void midend_size(midend_data *me, int *x, int *y, int expand) *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) +static void midend_set_timer(midend *me) { me->timing = (me->ourgame->is_timed && me->ourgame->timing_state(me->states[me->statepos-1].state, @@ -245,16 +255,17 @@ static void midend_set_timer(midend_data *me) 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); @@ -296,8 +307,14 @@ void midend_new_game(midend_data *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); } @@ -309,7 +326,8 @@ void midend_new_game(midend_data *me) 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; if (me->ui) @@ -319,7 +337,7 @@ void midend_new_game(midend_data *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) @@ -333,7 +351,7 @@ static int midend_undo(midend_data *me) return 0; } -static int midend_redo(midend_data *me) +static int midend_redo(midend *me) { if (me->statepos < me->nstates) { if (me->ui) @@ -347,7 +365,7 @@ static int midend_redo(midend_data *me) return 0; } -static void midend_finish_move(midend_data *me) +static void midend_finish_move(midend *me) { float flashtime; @@ -380,7 +398,7 @@ static void midend_finish_move(midend_data *me) 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); @@ -388,7 +406,7 @@ void midend_stop_anim(midend_data *me) } } -void midend_restart_game(midend_data *me) +void midend_restart_game(midend *me) { game_state *s; @@ -428,7 +446,7 @@ void midend_restart_game(midend_data *me) 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); @@ -534,7 +552,7 @@ static int midend_really_process_key(midend_data *me, int x, int y, int button) 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; @@ -648,26 +666,28 @@ int midend_process_key(midend_data *me, int x, int y, int button) 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 || @@ -687,13 +707,13 @@ void midend_timer(midend_data *me, float tplus) 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; @@ -722,11 +742,13 @@ float *midend_colours(midend_data *me, int *ncolours) 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; @@ -742,7 +764,7 @@ float *midend_colours(midend_data *me, int *ncolours) return ret; } -int midend_num_presets(midend_data *me) +int midend_num_presets(midend *me) { if (!me->npresets) { char *name; @@ -772,11 +794,13 @@ int midend_num_presets(midend_data *me) * 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); @@ -819,7 +843,7 @@ int midend_num_presets(midend_data *me) 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); @@ -827,12 +851,12 @@ void midend_fetch_preset(midend_data *me, int n, *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); @@ -840,7 +864,7 @@ void midend_supersede_game_desc(midend_data *me, char *desc, char *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; @@ -905,7 +929,7 @@ config_item *midend_get_config(midend_data *me, int which, char **wintitle) 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; @@ -1039,12 +1063,25 @@ static char *midend_game_id_int(midend_data *me, char *id, int defmode) 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; @@ -1075,7 +1112,7 @@ char *midend_set_config(midend_data *me, int which, config_item *cfg) 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); @@ -1083,7 +1120,7 @@ char *midend_text_format(midend_data *me) return NULL; } -char *midend_solve(midend_data *me) +char *midend_solve(midend *me) { game_state *s; char *msg, *movestr; @@ -1125,6 +1162,7 @@ char *midend_solve(midend_data *me) 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); @@ -1134,7 +1172,7 @@ char *midend_solve(midend_data *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 @@ -1167,7 +1205,7 @@ char *midend_rewrite_statusbar(midend_data *me, char *text) #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) { @@ -1316,7 +1354,7 @@ void midend_serialise(midend_data *me, /* * 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) { @@ -1332,7 +1370,7 @@ char *midend_deserialise(midend_data *me, * 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; @@ -1627,9 +1665,10 @@ char *midend_deserialise(midend_data *me, 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! */ @@ -1662,3 +1701,43 @@ char *midend_deserialise(midend_data *me, 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; +}