chiark / gitweb /
Implement selection of game seeds, by reusing the config box
[sgt-puzzles.git] / midend.c
1 /*
2  * midend.c: general middle fragment sitting between the
3  * platform-specific front end and game-specific back end.
4  * Maintains a move list, takes care of Undo and Redo commands, and
5  * processes standard keystrokes for undo/redo/new/restart/quit.
6  */
7
8 #include <stdio.h>
9 #include <string.h>
10 #include <assert.h>
11
12 #include "puzzles.h"
13
14 struct midend_data {
15     frontend *frontend;
16     char *seed;
17     int fresh_seed;
18     int nstates, statesize, statepos;
19
20     game_params **presets;
21     char **preset_names;
22     int npresets, presetsize;
23
24     game_params *params;
25     game_state **states;
26     game_drawstate *drawstate;
27     game_state *oldstate;
28     float anim_time, anim_pos;
29     float flash_time, flash_pos;
30 };
31
32 #define ensure(me) do { \
33     if ((me)->nstates >= (me)->statesize) { \
34         (me)->statesize = (me)->nstates + 128; \
35         (me)->states = sresize((me)->states, (me)->statesize, game_state *); \
36     } \
37 } while (0)
38
39 midend_data *midend_new(frontend *frontend)
40 {
41     midend_data *me = snew(midend_data);
42
43     me->frontend = frontend;
44     me->nstates = me->statesize = me->statepos = 0;
45     me->states = NULL;
46     me->params = default_params();
47     me->seed = NULL;
48     me->fresh_seed = FALSE;
49     me->drawstate = NULL;
50     me->oldstate = NULL;
51     me->presets = NULL;
52     me->preset_names = NULL;
53     me->npresets = me->presetsize = 0;
54     me->anim_time = me->anim_pos = 0.0F;
55     me->flash_time = me->flash_pos = 0.0F;
56
57     return me;
58 }
59
60 void midend_free(midend_data *me)
61 {
62     sfree(me->states);
63     sfree(me->seed);
64     free_params(me->params);
65     sfree(me);
66 }
67
68 void midend_size(midend_data *me, int *x, int *y)
69 {
70     game_size(me->params, x, y);
71 }
72
73 void midend_set_params(midend_data *me, game_params *params)
74 {
75     free_params(me->params);
76     me->params = dup_params(params);
77 }
78
79 void midend_new_game(midend_data *me)
80 {
81     while (me->nstates > 0)
82         free_game(me->states[--me->nstates]);
83
84     if (me->drawstate)
85         game_free_drawstate(me->drawstate);
86
87     assert(me->nstates == 0);
88
89     if (!me->fresh_seed) {
90         sfree(me->seed);
91         me->seed = new_game_seed(me->params);
92     } else
93         me->fresh_seed = FALSE;
94
95     ensure(me);
96     me->states[me->nstates++] = new_game(me->params, me->seed);
97     me->statepos = 1;
98     me->drawstate = game_new_drawstate(me->states[0]);
99 }
100
101 void midend_restart_game(midend_data *me)
102 {
103     while (me->nstates > 1)
104         free_game(me->states[--me->nstates]);
105     me->statepos = me->nstates;
106 }
107
108 static int midend_undo(midend_data *me)
109 {
110     if (me->statepos > 1) {
111         me->statepos--;
112         return 1;
113     } else
114         return 0;
115 }
116
117 static int midend_redo(midend_data *me)
118 {
119     if (me->statepos < me->nstates) {
120         me->statepos++;
121         return 1;
122     } else
123         return 0;
124 }
125
126 static void midend_finish_move(midend_data *me)
127 {
128     float flashtime;
129
130     if (me->oldstate || me->statepos > 1) {
131         flashtime = game_flash_length(me->oldstate ? me->oldstate :
132                                       me->states[me->statepos-2],
133                                       me->states[me->statepos-1]);
134         if (flashtime > 0) {
135             me->flash_pos = 0.0F;
136             me->flash_time = flashtime;
137         }
138     }
139
140     if (me->oldstate)
141         free_game(me->oldstate);
142     me->oldstate = NULL;
143     me->anim_pos = me->anim_time = 0;
144
145     if (me->flash_time == 0 && me->anim_time == 0)
146         deactivate_timer(me->frontend);
147     else
148         activate_timer(me->frontend);
149 }
150
151 int midend_process_key(midend_data *me, int x, int y, int button)
152 {
153     game_state *oldstate = dup_game(me->states[me->statepos - 1]);
154     float anim_time;
155
156     if (me->oldstate || me->anim_time) {
157         midend_finish_move(me);
158         midend_redraw(me);
159     }
160
161     if (button == 'n' || button == 'N' || button == '\x0E') {
162         midend_new_game(me);
163         midend_redraw(me);
164         return 1;                      /* never animate */
165     } else if (button == 'r' || button == 'R') {
166         midend_restart_game(me);
167         midend_redraw(me);
168         return 1;                      /* never animate */
169     } else if (button == 'u' || button == 'u' ||
170                button == '\x1A' || button == '\x1F') {
171         if (!midend_undo(me))
172             return 1;
173     } else if (button == '\x12') {
174         if (!midend_redo(me))
175             return 1;
176     } else if (button == 'q' || button == 'Q' || button == '\x11') {
177         free_game(oldstate);
178         return 0;
179     } else {
180         game_state *s = make_move(me->states[me->statepos-1], x, y, button);
181
182         if (s) {
183             while (me->nstates > me->statepos)
184                 free_game(me->states[--me->nstates]);
185             ensure(me);
186             me->states[me->nstates] = s;
187             me->statepos = ++me->nstates;
188         } else {
189             free_game(oldstate);
190             return 1;
191         }
192     }
193
194     /*
195      * See if this move requires an animation.
196      */
197     anim_time = game_anim_length(oldstate, me->states[me->statepos-1]);
198
199     me->oldstate = oldstate;
200     if (anim_time > 0) {
201         me->anim_time = anim_time;
202     } else {
203         me->anim_time = 0.0;
204         midend_finish_move(me);
205     }
206     me->anim_pos = 0.0;
207
208     midend_redraw(me);
209
210     activate_timer(me->frontend);
211
212     return 1;
213 }
214
215 void midend_redraw(midend_data *me)
216 {
217     if (me->statepos > 0 && me->drawstate) {
218         start_draw(me->frontend);
219         if (me->oldstate && me->anim_time > 0 &&
220             me->anim_pos < me->anim_time) {
221             game_redraw(me->frontend, me->drawstate, me->oldstate,
222                         me->states[me->statepos-1], me->anim_pos,
223                         me->flash_pos);
224         } else {
225             game_redraw(me->frontend, me->drawstate, NULL,
226                         me->states[me->statepos-1], 0.0, me->flash_pos);
227         }
228         end_draw(me->frontend);
229     }
230 }
231
232 void midend_timer(midend_data *me, float tplus)
233 {
234     me->anim_pos += tplus;
235     if (me->anim_pos >= me->anim_time ||
236         me->anim_time == 0 || !me->oldstate) {
237         if (me->anim_time > 0)
238             midend_finish_move(me);
239     }
240     me->flash_pos += tplus;
241     if (me->flash_pos >= me->flash_time || me->flash_time == 0) {
242         me->flash_pos = me->flash_time = 0;
243     }
244     if (me->flash_time == 0 && me->anim_time == 0)
245         deactivate_timer(me->frontend);
246     midend_redraw(me);
247 }
248
249 float *midend_colours(midend_data *me, int *ncolours)
250 {
251     game_state *state = NULL;
252     float *ret;
253
254     if (me->nstates == 0) {
255         char *seed = new_game_seed(me->params);
256         state = new_game(me->params, seed);
257         sfree(seed);
258     } else
259         state = me->states[0];
260
261     ret = game_colours(me->frontend, state, ncolours);
262
263     if (me->nstates == 0)
264         free_game(state);
265
266     return ret;
267 }
268
269 int midend_num_presets(midend_data *me)
270 {
271     if (!me->npresets) {
272         char *name;
273         game_params *preset;
274
275         while (game_fetch_preset(me->npresets, &name, &preset)) {
276             if (me->presetsize <= me->npresets) {
277                 me->presetsize = me->npresets + 10;
278                 me->presets = sresize(me->presets, me->presetsize,
279                                       game_params *);
280                 me->preset_names = sresize(me->preset_names, me->presetsize,
281                                            char *);
282             }
283
284             me->presets[me->npresets] = preset;
285             me->preset_names[me->npresets] = name;
286             me->npresets++;
287         }
288     }
289
290     return me->npresets;
291 }
292
293 void midend_fetch_preset(midend_data *me, int n,
294                          char **name, game_params **params)
295 {
296     assert(n >= 0 && n < me->npresets);
297     *name = me->preset_names[n];
298     *params = me->presets[n];
299 }
300
301 int midend_wants_statusbar(midend_data *me)
302 {
303     return game_wants_statusbar();
304 }
305
306 config_item *midend_get_config(midend_data *me, int which, char **wintitle)
307 {
308     char *titlebuf;
309     config_item *ret;
310
311     titlebuf = snewn(40 + strlen(game_name), char);
312
313     switch (which) {
314       case CFG_SETTINGS:
315         sprintf(titlebuf, "%s configuration", game_name);
316         *wintitle = dupstr(titlebuf);
317         return game_configure(me->params);
318       case CFG_SEED:
319         sprintf(titlebuf, "%s game selection", game_name);
320         *wintitle = dupstr(titlebuf);
321
322         ret = snewn(2, config_item);
323
324         ret[0].type = C_STRING;
325         ret[0].name = "Game ID";
326         ret[0].ival = 0;
327         ret[0].sval = dupstr(me->seed);
328
329         ret[1].type = C_END;
330         ret[1].name = ret[1].sval = NULL;
331         ret[1].ival = 0;
332
333         return ret;
334     }
335
336     assert(!"We shouldn't be here");
337     return NULL;
338 }
339
340 char *midend_set_config(midend_data *me, int which, config_item *cfg)
341 {
342     char *error;
343     game_params *params;
344
345     switch (which) {
346       case CFG_SETTINGS:
347         params = custom_params(cfg);
348         error = validate_params(params);
349
350         if (error) {
351             free_params(params);
352             return error;
353         }
354
355         free_params(me->params);
356         me->params = params;
357         break;
358
359       case CFG_SEED:
360         error = validate_seed(me->params, cfg[0].sval);
361         if (error)
362             return error;
363
364         sfree(me->seed);
365         me->seed = dupstr(cfg[0].sval);
366         me->fresh_seed = TRUE;
367
368         break;
369     }
370
371     return NULL;
372 }