Correct one other leaked change that broke encoding (damn some of this
[xiph/unicode.git] / planarity / levelstate.c
blob6332f37592e084b2a72655632b5612792e727da6
1 /*
3 * gPlanarity:
4 * The geeky little puzzle game with a big noodly crunch!
5 *
6 * gPlanarity copyright (C) 2005 Monty <monty@xiph.org>
7 * Original Flash game by John Tantalo <john.tantalo@case.edu>
8 * Original game concept by Mary Radcliffe
10 * gPlanarity is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
15 * gPlanarity is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with Postfish; see the file COPYING. If not, write to the
22 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
27 #define _GNU_SOURCE
28 #include <gtk/gtk.h>
29 #include <gtk/gtkmain.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include "graph.h"
34 #include "levelstate.h"
35 #include "gameboard.h"
36 #include "dialog_pause.h"
37 #include "dialog_finish.h"
38 #include "dialog_level.h"
39 #include "main.h"
40 #include "graph_generate.h"
42 #define CHUNK 64
43 #define SAVENAME "levelstate"
45 typedef struct levelstate{
46 struct levelstate *prev;
47 struct levelstate *next;
49 graphmeta gm;
51 int in_progress;
52 long highscore;
54 } levelstate;
56 static levelstate *head=0;
57 static levelstate *tail=0;
58 static levelstate *curr=0;
59 static levelstate *pool=0;
60 static int graph_dirty = 1;
62 static int aboutflag = 0;
63 static int pauseflag = 0;
64 static int finishflag = 0;
65 static int selectflag = 0;
67 static int level_limit = 1;
69 static levelstate *new_level(){
70 levelstate *ret;
71 int num=0;
73 if(pool==0){
74 int i;
75 pool = calloc(CHUNK,sizeof(*pool));
76 for(i=0;i<CHUNK-1;i++) /* last addition's ->next points to nothing */
77 pool[i].next=pool+i+1;
80 ret=pool;
81 if(tail)
82 num=tail->gm.num+1;
84 if(generate_get_meta(num, &ret->gm)) return 0;
86 pool=ret->next;
88 ret->next=0;
89 ret->prev=tail;
91 if(tail){
92 ret->prev->next=ret;
93 }else{
94 head=ret;
96 tail=ret;
98 ret->highscore=0;
99 ret->in_progress=0;
101 /* write out a 'fresh' board icon if it doesn't already exist */
102 if(!gameboard_icon_exists(ret->gm.id,"1")){
103 // generate a graph we can use to make the icon
104 graph g;
105 memset(&g,0,sizeof(g));
106 g.width=gameboard->g.orig_width;
107 g.height=gameboard->g.orig_height;
108 g.orig_width=gameboard->g.orig_width;
109 g.orig_height=gameboard->g.orig_height;
111 generate_board(&g,num);
112 impress_location(&g);
114 gameboard_write_icon(ret->gm.id,"1",gameboard,&g,1,1);
116 // releases the temporary graph memory
117 graph_release(&g);
121 return ret;
124 static levelstate *ensure_level_num(int level){
126 if(level<0)return 0;
128 if(!tail)new_level();
130 if(level <= tail->gm.num){
131 // find it in the existing levels
132 levelstate *l=tail;
133 while(l){
134 if(level == l->gm.num) return l;
135 l=l->prev;
137 return 0;
138 }else{
139 // make new levels to fill
141 while(tail->gm.num<level)
142 new_level();
144 return tail;
148 static levelstate *ensure_level(char *name){
149 int level=generate_find_number(name);
150 return ensure_level_num(level);
153 int levelstate_write(){
154 FILE *f;
155 char *name=alloca(strlen(statedir)+strlen(SAVENAME)+1);
156 name[0]=0;
157 strcat(name,statedir);
158 strcat(name,SAVENAME);
160 if(!graph_dirty && (curr->in_progress || gameboard->finish_dialog_active)){
161 gameboard_write(curr->gm.id,gameboard);
162 gameboard_write_icon(curr->gm.id,"2",gameboard,&gameboard->g,
163 !gameboard->hide_lines,gameboard->show_intersections);
167 f = fopen(name,"wb");
168 if(f==NULL){
169 fprintf(stderr,_("ERROR: Could not save game state file \"%s\":\n\t%s\n"),
170 curr->gm.id,strerror(errno));
171 return errno;
174 fprintf(f,"current %d : %s\n",strlen(curr->gm.id),curr->gm.id);
177 levelstate *l=head;
178 while(l){
179 fprintf(f,"level %ld %d %d : %s\n",
180 l->highscore,l->in_progress,
181 strlen(l->gm.id),l->gm.id);
182 l=l->next;
186 if(gameboard->about_dialog_active)
187 fprintf(f,"about 1\n");
188 if(gameboard->pause_dialog_active)
189 fprintf(f,"pause 1\n");
190 if(gameboard->finish_dialog_active)
191 fprintf(f,"finish 1\n");
192 if(gameboard->level_dialog_active)
193 fprintf(f,"select 1\n");
195 fclose(f);
196 return 0;
199 // also functions as the levelstate init; always called once upon startup
200 int levelstate_read(){
201 char *line=NULL;
202 size_t n=0;
204 FILE *f;
205 char *name=alloca(strlen(statedir)+strlen(SAVENAME)+1);
206 name[0]=0;
207 strcat(name,statedir);
208 strcat(name,SAVENAME);
210 if(!head)new_level();
211 if(!curr)curr=head;
213 f = fopen(name,"rb");
214 if(f==NULL){
215 if(errno!=ENOENT){
216 fprintf(stderr,_("ERROR: Could not read game state file \"%s\":\n\t%s\n"),
217 curr->gm.id,strerror(errno));
219 return errno;
222 // get all levels we've seen.
223 while(getline(&line,&n,f)>0){
224 long l;
225 int i;
226 unsigned int j;
227 if (sscanf(line,"level %ld %d %d : ",&l,&i,&j)==3){
228 char *name=strchr(line,':');
229 // guard against bad edits
230 if(name &&
231 (strlen(line) - (name - line + 2) >= j)){
232 levelstate *le;
233 name += 2;
234 name[j]=0;
235 le = ensure_level(name);
236 if(le){
237 le->highscore=l;
238 le->in_progress=i;
240 if(le->highscore)
241 if(le->gm.unlock_plus + le->gm.num > level_limit)
242 level_limit = le->gm.unlock_plus + le->gm.num;
248 rewind(f);
250 // get current
251 while(getline(&line,&n,f)>0){
252 int i;
253 if (sscanf(line,"current %d : ",&i)==1){
254 char *name=strchr(line,':');
255 // guard against bad edits
256 if(name &&
257 (strlen(line) - (name - line + 2) >= (unsigned)i)){
258 levelstate *le;
259 name += 2;
260 name[i]=0;
261 le = ensure_level(name);
262 if(le)
263 curr=le;
267 if(sscanf(line,"about %d",&i)==1)
268 if(i==1)
269 aboutflag=1;
271 if(sscanf(line,"pause %d",&i)==1)
272 if(i==1)
273 pauseflag=1;
275 if(sscanf(line,"finish %d",&i)==1)
276 if(i==1)
277 finishflag=1;
279 if(sscanf(line,"select %d",&i)==1)
280 if(i==1)
281 selectflag=1;
285 while(curr->gm.num >= level_limit){
286 levelstate_prev();
289 return 0;
292 void levelstate_resume(){
294 levelstate_go();
296 if(pauseflag){
297 prepare_reenter_game(gameboard);
298 pause_dialog(gameboard);
299 }else if (aboutflag){
300 prepare_reenter_game(gameboard);
301 about_dialog(gameboard);
302 }else if (finishflag){
303 prepare_reenter_game(gameboard);
304 finish_level_dialog(gameboard);
305 }else if (selectflag){
306 prepare_reenter_game(gameboard);
307 level_dialog(gameboard,0);
308 }else{
309 prepare_reenter_game(gameboard);
310 reenter_game(gameboard);
312 aboutflag=0;
313 pauseflag=0;
314 finishflag=0;
315 selectflag=0;
319 void set_in_progress(){
320 curr->in_progress=1;
323 long levelstate_total_hiscore(){
324 long score=0;
325 levelstate *l=head;
327 while(l){
328 score+=l->highscore;
329 l=l->next;
331 return score;
334 long levelstate_get_hiscore(){
335 if(!curr)return 0;
336 return curr->highscore;
339 int levelstate_next(){
340 if(!curr->next)
341 new_level();
343 if(curr->next){
344 curr=curr->next;
345 graph_dirty=1;
346 return 1;
348 return 0;
351 int levelstate_prev(){
352 if(curr->prev){
353 curr=curr->prev;
354 graph_dirty=1;
355 return 1;
357 return 0;
360 int get_level_num(){
361 return curr->gm.num;
364 char *get_level_desc(){
365 return _(curr->gm.desc);
368 void levelstate_finish(){
369 int score = graphscore_get_score(&gameboard->g) +
370 graphscore_get_bonus(&gameboard->g);
371 curr->in_progress=0;
372 if(score > curr->highscore)
373 curr->highscore = score;
375 if(curr->gm.unlock_plus + curr->gm.num > level_limit)
376 level_limit = curr->gm.unlock_plus + curr->gm.num;
380 void levelstate_reset(){
381 curr->in_progress=0;
382 graph_dirty=1;
385 int levelstate_in_progress(){
386 return curr->in_progress;
389 int levelstate_limit(){
390 return level_limit;
393 /* commit to the currently selected level and set the game state to
394 readiness using it */
395 void levelstate_go(){
397 // we need to load the board if we're currently playing the board or sitting in the finish dialog right after
398 if(curr->in_progress || finishflag){
399 if(gameboard_read(curr->gm.id,gameboard)){
400 /* not on disk or couldn't load it. clear level state flags and get a fresh version */
401 aboutflag=0;
402 pauseflag=0;
403 finishflag=0;
404 selectflag=0;
405 generate_board(&gameboard->g,curr->gm.num);
406 activate_verticies(&gameboard->g);
407 impress_location(&gameboard->g);
408 set_timer(0);
410 }else{
411 /* no board in progress; fetch a new board */
412 generate_board(&gameboard->g,curr->gm.num);
413 activate_verticies(&gameboard->g);
414 impress_location(&gameboard->g);
415 set_timer(0);
418 graph_dirty=0;
421 cairo_surface_t *levelstate_get_icon(int num){
422 levelstate *l=ensure_level_num(num);
423 if(l==0)return 0;
424 return gameboard_read_icon(l->gm.id,(l->in_progress?"2":"1"),gameboard);