chiark / gitweb /
Further general development. Net is now playable, though
[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 <assert.h>
10
11 #include "puzzles.h"
12
13 struct midend_data {
14     frontend *frontend;
15     char *seed;
16     int nstates, statesize, statepos;
17     game_params *params;
18     game_state **states;
19     game_drawstate *drawstate;
20     game_state *oldstate;
21     float anim_time, anim_pos;
22 };
23
24 #define ensure(me) do { \
25     if ((me)->nstates >= (me)->statesize) { \
26         (me)->statesize = (me)->nstates + 128; \
27         (me)->states = sresize((me)->states, (me)->statesize, game_state *); \
28     } \
29 } while (0)
30
31 midend_data *midend_new(frontend *frontend)
32 {
33     midend_data *me = snew(midend_data);
34
35     me->frontend = frontend;
36     me->nstates = me->statesize = me->statepos = 0;
37     me->states = NULL;
38     me->params = default_params();
39     me->seed = NULL;
40     me->drawstate = NULL;
41     me->oldstate = NULL;
42
43     return me;
44 }
45
46 void midend_free(midend_data *me)
47 {
48     sfree(me->states);
49     sfree(me->seed);
50     free_params(me->params);
51     sfree(me);
52 }
53
54 void midend_size(midend_data *me, int *x, int *y)
55 {
56     game_size(me->params, x, y);
57 }
58
59 void midend_set_params(midend_data *me, game_params *params)
60 {
61     free_params(me->params);
62     me->params = params;
63 }
64
65 void midend_new_game(midend_data *me, char *seed)
66 {
67     while (me->nstates > 0)
68         free_game(me->states[--me->nstates]);
69
70     if (me->drawstate)
71         game_free_drawstate(me->drawstate);
72
73     assert(me->nstates == 0);
74
75     sfree(me->seed);
76     if (seed)
77         me->seed = dupstr(seed);
78     else
79         me->seed = new_game_seed(me->params);
80
81     ensure(me);
82     me->states[me->nstates++] = new_game(me->params, me->seed);
83     me->statepos = 1;
84     me->drawstate = game_new_drawstate(me->states[0]);
85 }
86
87 void midend_restart_game(midend_data *me)
88 {
89     while (me->nstates > 1)
90         free_game(me->states[--me->nstates]);
91     me->statepos = me->nstates;
92 }
93
94 void midend_undo(midend_data *me)
95 {
96     if (me->statepos > 1)
97         me->statepos--;
98 }
99
100 void midend_redo(midend_data *me)
101 {
102     if (me->statepos < me->nstates)
103         me->statepos++;
104 }
105
106 int midend_process_key(midend_data *me, int x, int y, int button)
107 {
108     game_state *oldstate = dup_game(me->states[me->statepos - 1]);
109     float anim_time;
110
111     if (me->oldstate || me->anim_time) {
112         if (me->oldstate)
113             free_game(me->oldstate);
114         me->oldstate = NULL;
115         me->anim_pos = me->anim_time = 0;
116         deactivate_timer(me->frontend);
117         midend_redraw(me);
118     }
119
120     if (button == 'n' || button == 'N' || button == '\x0E') {
121         midend_new_game(me, NULL);
122         midend_redraw(me);
123         return 1;                      /* never animate */
124     } else if (button == 'r' || button == 'R') {
125         midend_restart_game(me);
126         midend_redraw(me);
127         return 1;                      /* never animate */
128     } else if (button == 'u' || button == 'u' ||
129                button == '\x1A' || button == '\x1F') {
130         midend_undo(me);
131     } else if (button == '\x12') {
132         midend_redo(me);
133     } else if (button == 'q' || button == 'Q' || button == '\x11') {
134         free_game(oldstate);
135         return 0;
136     } else {
137         game_state *s = make_move(me->states[me->statepos-1], x, y, button);
138
139         if (s) {
140             while (me->nstates > me->statepos)
141                 free_game(me->states[--me->nstates]);
142             ensure(me);
143             me->states[me->nstates] = s;
144             me->statepos = ++me->nstates;
145         } else {
146             free_game(oldstate);
147             return 1;
148         }
149     }
150
151     /*
152      * See if this move requires an animation.
153      */
154     anim_time = game_anim_length(oldstate, me->states[me->statepos-1]);
155
156     if (anim_time > 0) {
157         me->oldstate = oldstate;
158         me->anim_time = anim_time;
159     } else {
160         free_game(oldstate);
161         me->oldstate = NULL;
162         me->anim_time = 0.0;
163     }
164     me->anim_pos = 0.0;
165
166     midend_redraw(me);
167
168     activate_timer(me->frontend);
169
170     return 1;
171 }
172
173 void midend_redraw(midend_data *me)
174 {
175     if (me->statepos > 0 && me->drawstate) {
176         start_draw(me->frontend);
177         if (me->oldstate && me->anim_time > 0 &&
178             me->anim_pos < me->anim_time) {
179             game_redraw(me->frontend, me->drawstate, me->oldstate,
180                         me->states[me->statepos-1], me->anim_pos);
181         } else {
182             game_redraw(me->frontend, me->drawstate, NULL,
183                         me->states[me->statepos-1], 0.0);
184         }
185         end_draw(me->frontend);
186     }
187 }
188
189 void midend_timer(midend_data *me, float tplus)
190 {
191     me->anim_pos += tplus;
192     if (me->anim_pos >= me->anim_time ||
193         me->anim_time == 0 || !me->oldstate) {
194         if (me->oldstate)
195             free_game(me->oldstate);
196         me->oldstate = NULL;
197         me->anim_pos = me->anim_time = 0;
198         deactivate_timer(me->frontend);
199     }
200     midend_redraw(me);
201 }
202
203 float *midend_colours(midend_data *me, int *ncolours)
204 {
205     game_state *state = NULL;
206     float *ret;
207
208     if (me->nstates == 0) {
209         char *seed = new_game_seed(me->params);
210         state = new_game(me->params, seed);
211         sfree(seed);
212     } else
213         state = me->states[0];
214
215     ret = game_colours(me->frontend, state, ncolours);
216
217     if (me->nstates == 0)
218         free_game(state);
219
220     return ret;
221 }