4 * The geeky little puzzle game with a big noodly crunch!
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)
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.
29 #include <gtk/gtkmain.h>
34 #include "levelstate.h"
35 #include "gameboard.h"
36 #include "dialog_pause.h"
37 #include "dialog_finish.h"
38 #include "dialog_level.h"
40 #include "graph_generate.h"
43 #define SAVENAME "levelstate"
45 typedef struct levelstate
{
46 struct levelstate
*prev
;
47 struct levelstate
*next
;
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(){
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;
84 if(generate_get_meta(num
, &ret
->gm
)) return 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
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
124 static levelstate
*ensure_level_num(int level
){
128 if(!tail
)new_level();
130 if(level
<= tail
->gm
.num
){
131 // find it in the existing levels
134 if(level
== l
->gm
.num
) return l
;
139 // make new levels to fill
141 while(tail
->gm
.num
<level
)
148 static levelstate
*ensure_level(char *name
){
149 int level
=generate_find_number(name
);
150 return ensure_level_num(level
);
153 int levelstate_write(){
155 char *name
=alloca(strlen(statedir
)+strlen(SAVENAME
)+1);
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");
169 fprintf(stderr
,_("ERROR: Could not save game state file \"%s\":\n\t%s\n"),
170 curr
->gm
.id
,strerror(errno
));
174 fprintf(f
,"current %d : %s\n",strlen(curr
->gm
.id
),curr
->gm
.id
);
179 fprintf(f
,"level %ld %d %d : %s\n",
180 l
->highscore
,l
->in_progress
,
181 strlen(l
->gm
.id
),l
->gm
.id
);
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");
199 // also functions as the levelstate init; always called once upon startup
200 int levelstate_read(){
205 char *name
=alloca(strlen(statedir
)+strlen(SAVENAME
)+1);
207 strcat(name
,statedir
);
208 strcat(name
,SAVENAME
);
210 if(!head
)new_level();
213 f
= fopen(name
,"rb");
216 fprintf(stderr
,_("ERROR: Could not read game state file \"%s\":\n\t%s\n"),
217 curr
->gm
.id
,strerror(errno
));
222 // get all levels we've seen.
223 while(getline(&line
,&n
,f
)>0){
227 if (sscanf(line
,"level %ld %d %d : ",&l
,&i
,&j
)==3){
228 char *name
=strchr(line
,':');
229 // guard against bad edits
231 (strlen(line
) - (name
- line
+ 2) >= j
)){
235 le
= ensure_level(name
);
241 if(le
->gm
.unlock_plus
+ le
->gm
.num
> level_limit
)
242 level_limit
= le
->gm
.unlock_plus
+ le
->gm
.num
;
251 while(getline(&line
,&n
,f
)>0){
253 if (sscanf(line
,"current %d : ",&i
)==1){
254 char *name
=strchr(line
,':');
255 // guard against bad edits
257 (strlen(line
) - (name
- line
+ 2) >= (unsigned)i
)){
261 le
= ensure_level(name
);
267 if(sscanf(line
,"about %d",&i
)==1)
271 if(sscanf(line
,"pause %d",&i
)==1)
275 if(sscanf(line
,"finish %d",&i
)==1)
279 if(sscanf(line
,"select %d",&i
)==1)
285 while(curr
->gm
.num
>= level_limit
){
292 void levelstate_resume(){
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);
309 prepare_reenter_game(gameboard
);
310 reenter_game(gameboard
);
319 void set_in_progress(){
323 long levelstate_total_hiscore(){
334 long levelstate_get_hiscore(){
336 return curr
->highscore
;
339 int levelstate_next(){
351 int levelstate_prev(){
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
);
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(){
385 int levelstate_in_progress(){
386 return curr
->in_progress
;
389 int levelstate_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 */
405 generate_board(&gameboard
->g
,curr
->gm
.num
);
406 activate_verticies(&gameboard
->g
);
407 impress_location(&gameboard
->g
);
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
);
421 cairo_surface_t
*levelstate_get_icon(int num
){
422 levelstate
*l
=ensure_level_num(num
);
424 return gameboard_read_icon(l
->gm
.id
,(l
->in_progress
?"2":"1"),gameboard
);