const game *ourgame;
game_params **presets;
- char **preset_names;
+ char **preset_names, **preset_encodings;
int npresets, presetsize;
/*
me->frontend = fe;
me->ourgame = ourgame;
- me->random = random_init(randseed, randseedsize);
+ me->random = random_new(randseed, randseedsize);
me->nstates = me->statesize = me->statepos = 0;
me->states = NULL;
me->params = ourgame->default_params();
me->oldstate = NULL;
me->presets = NULL;
me->preset_names = NULL;
+ me->preset_encodings = NULL;
me->npresets = me->presetsize = 0;
me->anim_time = me->anim_pos = 0.0F;
me->flash_time = me->flash_pos = 0.0F;
me->elapsed = 0.0F;
me->tilesize = me->winwidth = me->winheight = 0;
if (drapi)
- me->drawing = drawing_init(drapi, drhandle);
+ me->drawing = drawing_new(drapi, me, drhandle);
else
me->drawing = NULL;
for (i = 0; i < me->npresets; i++) {
sfree(me->presets[i]);
sfree(me->preset_names[i]);
+ sfree(me->preset_encodings[i]);
}
sfree(me->presets);
sfree(me->preset_names);
+ sfree(me->preset_encodings);
}
if (me->ui)
me->ourgame->free_ui(me->ui);
}
}
-void midend_size(midend *me, int *x, int *y, int expand)
+void midend_size(midend *me, int *x, int *y, int user_size)
{
int min, max;
int rx, ry;
/*
* Find the tile size that best fits within the given space. If
- * `expand' is TRUE, we must actually find the _largest_ such
- * tile size; otherwise, we bound above at the game's preferred
- * tile size.
+ * `user_size' is TRUE, we must actually find the _largest_ such
+ * tile size, in order to get as close to the user's explicit
+ * request as possible; otherwise, we bound above at the game's
+ * preferred tile size, so that the game gets what it wants
+ * provided that this doesn't break the constraint from the
+ * front-end (which is likely to be a screen size or similar).
*/
- if (expand) {
+ if (user_size) {
max = 1;
do {
max *= 2;
*/
me->tilesize = min;
+ if (user_size)
+ /* If the user requested a change in size, make it permanent. */
+ me->preferred_tilesize = me->tilesize;
midend_size_new_drawstate(me);
*x = me->winwidth;
*y = me->winheight;
}
+int midend_tilesize(midend *me) { return me->tilesize; }
+
void midend_set_params(midend *me, game_params *params)
{
me->ourgame->free_params(me->params);
char newseed[16];
int i;
newseed[15] = '\0';
- newseed[0] = '1' + random_upto(me->random, 9);
+ newseed[0] = '1' + (char)random_upto(me->random, 9);
for (i = 1; i < 15; i++)
- newseed[i] = '0' + random_upto(me->random, 10);
+ newseed[i] = '0' + (char)random_upto(me->random, 10);
sfree(me->seedstr);
me->seedstr = dupstr(newseed);
sfree(me->aux_info);
me->aux_info = NULL;
- rs = random_init(me->seedstr, strlen(me->seedstr));
+ rs = random_new(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
}
ensure(me);
+
+ /*
+ * It might seem a bit odd that we're using me->params to
+ * create the initial game state, rather than me->curparams
+ * which is better tailored to this specific game and which we
+ * always know.
+ *
+ * It's supposed to be an invariant in the midend that
+ * me->params and me->curparams differ in no aspect that is
+ * important after generation (i.e. after new_desc()). By
+ * deliberately passing the _less_ specific of these two
+ * parameter sets, we provoke play-time misbehaviour in the
+ * case where a game has failed to encode a play-time parameter
+ * in the non-full version of encode_params().
+ */
me->states[me->nstates].state =
me->ourgame->new_game(me, me->params, me->desc);
+
+ /*
+ * As part of our commitment to self-testing, test the aux
+ * string to make sure nothing ghastly went wrong.
+ */
+ if (me->ourgame->can_solve && me->aux_info) {
+ game_state *s;
+ char *msg, *movestr;
+
+ msg = NULL;
+ movestr = me->ourgame->solve(me->states[0].state,
+ me->states[0].state,
+ me->aux_info, &msg);
+ assert(movestr && !msg);
+ s = me->ourgame->execute_move(me->states[0].state, movestr);
+ assert(s);
+ me->ourgame->free_game(s);
+ sfree(movestr);
+ }
+
me->states[me->nstates].movestr = NULL;
me->states[me->nstates].movetype = NEWGAME;
me->nstates++;
midend_stop_anim(me);
if (!midend_redo(me))
goto done;
+ } else if (button == '\x13' && me->ourgame->can_solve) {
+ if (midend_solve(me))
+ goto done;
} else if (button == 'q' || button == 'Q' || button == '\x11') {
ret = 0;
goto done;
* state has been updated and a redraw is called for.
*/
midend_redraw(me);
+ midend_set_timer(me);
goto done;
} else if (s) {
midend_stop_anim(me);
* See if this move requires an animation.
*/
if (special(type) && !(type == SOLVE &&
- (me->ourgame->mouse_priorities & SOLVE_ANIMATES))) {
+ (me->ourgame->flags & SOLVE_ANIMATES))) {
anim_time = 0;
} else {
anim_time = me->ourgame->anim_length(oldstate,
* like a left click for the benefit of users of other
* implementations. So the last of the above points is modified
* in the presence of an (optional) button priority order.
+ *
+ * A further addition: we translate certain keyboard presses to
+ * cursor key 'select' buttons, so that a) frontends don't have
+ * to translate these themselves (like they do for CURSOR_UP etc),
+ * and b) individual games don't have to hard-code button presses
+ * of '\n' etc for keyboard-based cursors. The choice of buttons
+ * here could eventually be controlled by a runtime configuration
+ * option.
*/
if (IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) {
if (me->pressed_mouse_button) {
* If the new button has lower priority than the old one,
* don't bother doing this.
*/
- if (me->ourgame->mouse_priorities &
+ if (me->ourgame->flags &
BUTTON_BEATS(me->pressed_mouse_button, button))
return ret; /* just ignore it */
}
/*
+ * Translate keyboard presses to cursor selection.
+ */
+ if (button == '\n' || button == '\r')
+ button = CURSOR_SELECT;
+ if (button == ' ')
+ button = CURSOR_SELECT2;
+
+ /*
* Now send on the event we originally received.
*/
ret = ret && midend_really_process_key(me, x, y, button);
}
}
+/*
+ * Nasty hacky function used to implement the --redo option in
+ * gtk.c. Only used for generating the puzzles' icons.
+ */
+void midend_freeze_timer(midend *me, float tprop)
+{
+ me->anim_pos = me->anim_time * tprop;
+ midend_redraw(me);
+ deactivate_timer(me->frontend);
+}
+
void midend_timer(midend *me, float tplus)
{
+ int need_redraw = (me->anim_time > 0 || me->flash_time > 0);
+
me->anim_pos += tplus;
if (me->anim_pos >= me->anim_time ||
me->anim_time == 0 || !me->oldstate) {
me->flash_pos = me->flash_time = 0;
}
- midend_redraw(me);
+ if (need_redraw)
+ midend_redraw(me);
if (me->timing) {
float oldelapsed = me->elapsed;
float *midend_colours(midend *me, int *ncolours)
{
- game_state *state = NULL;
float *ret;
- if (me->nstates == 0) {
- char *aux = NULL;
- char *desc = me->ourgame->new_desc(me->params, me->random,
- &aux, TRUE);
- state = me->ourgame->new_game(me, me->params, desc);
- sfree(desc);
- sfree(aux);
- } else
- state = me->states[0].state;
-
- ret = me->ourgame->colours(me->frontend, state, ncolours);
+ ret = me->ourgame->colours(me->frontend, ncolours);
{
int i;
buf[k] = '\0';
if ((e = getenv(buf)) != NULL &&
sscanf(e, "%2x%2x%2x", &r, &g, &b) == 3) {
- ret[i*3 + 0] = r / 255.0;
- ret[i*3 + 1] = g / 255.0;
- ret[i*3 + 2] = b / 255.0;
+ ret[i*3 + 0] = r / 255.0F;
+ ret[i*3 + 1] = g / 255.0F;
+ ret[i*3 + 2] = b / 255.0F;
}
}
}
- if (me->nstates == 0)
- me->ourgame->free_game(state);
-
return ret;
}
game_params *);
me->preset_names = sresize(me->preset_names, me->presetsize,
char *);
+ me->preset_encodings = sresize(me->preset_encodings,
+ me->presetsize, char *);
}
me->presets[me->npresets] = preset;
me->preset_names[me->npresets] = name;
+ me->preset_encodings[me->npresets] =
+ me->ourgame->encode_params(preset, TRUE);;
me->npresets++;
}
}
game_params *);
me->preset_names = sresize(me->preset_names,
me->presetsize, char *);
+ me->preset_encodings = sresize(me->preset_encodings,
+ me->presetsize, char *);
}
me->presets[me->npresets] = preset;
me->preset_names[me->npresets] = dupstr(name);
+ me->preset_encodings[me->npresets] =
+ me->ourgame->encode_params(preset, TRUE);
me->npresets++;
}
+ sfree(e);
}
}
*params = me->presets[n];
}
+int midend_which_preset(midend *me)
+{
+ char *encoding = me->ourgame->encode_params(me->params, TRUE);
+ int i, ret;
+
+ ret = -1;
+ for (i = 0; i < me->npresets; i++)
+ if (!strcmp(encoding, me->preset_encodings[i])) {
+ ret = i;
+ break;
+ }
+
+ sfree(encoding);
+ return ret;
+}
+
int midend_wants_statusbar(midend *me)
{
- return me->ourgame->wants_statusbar();
+ return me->ourgame->wants_statusbar;
}
void midend_supersede_game_desc(midend *me, char *desc, char *privdesc)
return NULL;
}
+int midend_can_format_as_text_now(midend *me)
+{
+ if (me->ourgame->can_format_as_text_ever)
+ return me->ourgame->can_format_as_text_now(me->params);
+ else
+ return FALSE;
+}
+
char *midend_text_format(midend *me)
{
- if (me->ourgame->can_format_as_text && me->statepos > 0)
+ if (me->ourgame->can_format_as_text_ever && me->statepos > 0 &&
+ me->ourgame->can_format_as_text_now(me->params))
return me->ourgame->text_format(me->states[me->statepos-1].state);
else
return NULL;
if (me->statepos < 1)
return "No game set up to solve"; /* _shouldn't_ happen! */
- msg = "Solve operation failed"; /* game _should_ overwrite on error */
+ msg = NULL;
movestr = me->ourgame->solve(me->states[0].state,
me->states[me->statepos-1].state,
me->aux_info, &msg);
- if (!movestr)
+ if (!movestr) {
+ if (!msg)
+ msg = "Solve operation failed"; /* _shouldn't_ happen, but can */
return msg;
+ }
s = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr);
assert(s);
me->states[me->statepos-2].state,
me->states[me->statepos-1].state);
me->dir = +1;
- if (me->ourgame->mouse_priorities & SOLVE_ANIMATES) {
+ if (me->ourgame->flags & 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,
char timebuf[100], *ret;
int min, sec;
- sec = me->elapsed;
+ sec = (int)me->elapsed;
min = sec / 60;
sec %= 60;
sprintf(timebuf, "[%d:%02d] ", min, sec);
uistr = val;
val = NULL;
} else if (!strcmp(key, "TIME")) {
- elapsed = atof(val);
+ elapsed = (float)atof(val);
} else if (!strcmp(key, "NSTATES")) {
nstates = atoi(val);
if (nstates <= 0) {