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>
35 #include "levelstate.h"
36 #include "gameboard.h"
37 #include "dialog_pause.h"
38 #include "dialog_finish.h"
39 #include "dialog_level.h"
41 #include "graph_generate.h"
44 #define SAVENAME "levelstate"
46 typedef struct levelstate
{
47 struct levelstate
*prev
;
48 struct levelstate
*next
;
57 static levelstate
*head
=0;
58 static levelstate
*tail
=0;
59 static levelstate
*curr
=0;
60 static levelstate
*pool
=0;
61 static int graph_dirty
= 1;
63 static int aboutflag
= 0;
64 static int pauseflag
= 0;
65 static int finishflag
= 0;
66 static int selectflag
= 0;
68 static int level_limit
= 1;
70 static levelstate
*new_level(){
76 pool
= calloc(CHUNK
,sizeof(*pool
));
77 for(i
=0;i
<CHUNK
-1;i
++) /* last addition's ->next points to nothing */
78 pool
[i
].next
=pool
+i
+1;
85 if(generate_get_meta(num
, &ret
->gm
)) return 0;
102 /* write out a 'fresh' board icon if it doesn't already exist */
103 if(!gameboard_icon_exists(ret
->gm
.id
,"1")){
104 // generate a graph we can use to make the icon
106 memset(&g
,0,sizeof(g
));
107 g
.width
=gameboard
->g
.orig_width
;
108 g
.height
=gameboard
->g
.orig_height
;
109 g
.orig_width
=gameboard
->g
.orig_width
;
110 g
.orig_height
=gameboard
->g
.orig_height
;
112 generate_board(&g
,num
);
113 impress_location(&g
);
115 gameboard_write_icon(ret
->gm
.id
,"1",gameboard
,&g
,1,1);
117 // releases the temporary graph memory
125 static levelstate
*ensure_level_num(int level
){
129 if(!tail
)new_level();
131 if(level
<= tail
->gm
.num
){
132 // find it in the existing levels
135 if(level
== l
->gm
.num
) return l
;
140 // make new levels to fill
142 while(tail
->gm
.num
<level
)
149 static levelstate
*ensure_level(char *name
){
150 int level
=generate_find_number(name
);
151 return ensure_level_num(level
);
154 int levelstate_write(){
156 char *name
=alloca(strlen(statedir
)+strlen(SAVENAME
)+1);
158 strcat(name
,statedir
);
159 strcat(name
,SAVENAME
);
161 if(!graph_dirty
&& (curr
->in_progress
|| gameboard
->finish_dialog_active
)){
162 gameboard_write(curr
->gm
.id
,gameboard
);
163 gameboard_write_icon(curr
->gm
.id
,"2",gameboard
,&gameboard
->g
,
164 !gameboard
->hide_lines
,gameboard
->show_intersections
);
168 f
= fopen(name
,"wb");
170 fprintf(stderr
,_("ERROR: Could not save game state file \"%s\":\n\t%s\n"),
171 curr
->gm
.id
,strerror(errno
));
175 fprintf(f
,"current %d : %s\n",(int)strlen(curr
->gm
.id
),curr
->gm
.id
);
180 fprintf(f
,"level %ld %d %d : %s\n",
181 l
->highscore
,l
->in_progress
,
182 (int)strlen(l
->gm
.id
),l
->gm
.id
);
187 if(gameboard
->about_dialog_active
)
188 fprintf(f
,"about 1\n");
189 if(gameboard
->pause_dialog_active
)
190 fprintf(f
,"pause 1\n");
191 if(gameboard
->finish_dialog_active
)
192 fprintf(f
,"finish 1\n");
193 if(gameboard
->level_dialog_active
)
194 fprintf(f
,"select 1\n");
200 // also functions as the levelstate init; always called once upon startup
201 int levelstate_read(){
206 char *name
=alloca(strlen(statedir
)+strlen(SAVENAME
)+1);
208 strcat(name
,statedir
);
209 strcat(name
,SAVENAME
);
211 if(!head
)new_level();
214 f
= fopen(name
,"rb");
217 fprintf(stderr
,_("ERROR: Could not read game state file \"%s\":\n\t%s\n"),
218 curr
->gm
.id
,strerror(errno
));
223 // get all levels we've seen.
224 while(getline(&line
,&n
,f
)>0){
228 if (sscanf(line
,"level %ld %d %d : ",&l
,&i
,&j
)==3){
229 char *name
=strchr(line
,':');
230 // guard against bad edits
232 (strlen(line
) - (name
- line
+ 2) >= j
)){
236 le
= ensure_level(name
);
242 if(le
->gm
.unlock_plus
+ le
->gm
.num
> level_limit
)
243 level_limit
= le
->gm
.unlock_plus
+ le
->gm
.num
;
252 while(getline(&line
,&n
,f
)>0){
254 if (sscanf(line
,"current %d : ",&i
)==1){
255 char *name
=strchr(line
,':');
256 // guard against bad edits
258 (strlen(line
) - (name
- line
+ 2) >= (unsigned)i
)){
262 le
= ensure_level(name
);
268 if(sscanf(line
,"about %d",&i
)==1)
272 if(sscanf(line
,"pause %d",&i
)==1)
276 if(sscanf(line
,"finish %d",&i
)==1)
280 if(sscanf(line
,"select %d",&i
)==1)
286 while(curr
->gm
.num
>= level_limit
){
293 void levelstate_resume(){
298 prepare_reenter_game(gameboard
);
299 pause_dialog(gameboard
);
300 }else if (aboutflag
){
301 prepare_reenter_game(gameboard
);
302 about_dialog(gameboard
);
303 }else if (finishflag
){
304 prepare_reenter_game(gameboard
);
305 finish_level_dialog(gameboard
);
306 }else if (selectflag
){
307 prepare_reenter_game(gameboard
);
308 level_dialog(gameboard
,0);
310 prepare_reenter_game(gameboard
);
311 reenter_game(gameboard
);
320 void set_in_progress(){
324 long levelstate_total_hiscore(){
335 long levelstate_get_hiscore(){
337 return curr
->highscore
;
340 int levelstate_next(){
352 int levelstate_prev(){
365 char *get_level_desc(){
366 return _(curr
->gm
.desc
);
369 void levelstate_finish(){
370 int score
= graphscore_get_score(&gameboard
->g
) +
371 graphscore_get_bonus(&gameboard
->g
);
373 if(score
> curr
->highscore
)
374 curr
->highscore
= score
;
376 if(curr
->gm
.unlock_plus
+ curr
->gm
.num
> level_limit
)
377 level_limit
= curr
->gm
.unlock_plus
+ curr
->gm
.num
;
381 void levelstate_reset(){
386 int levelstate_in_progress(){
387 return curr
->in_progress
;
390 int levelstate_limit(){
394 /* commit to the currently selected level and set the game state to
395 readiness using it */
396 void levelstate_go(){
398 // we need to load the board if we're currently playing the board or sitting in the finish dialog right after
399 if(curr
->in_progress
|| finishflag
){
400 if(gameboard_read(curr
->gm
.id
,gameboard
)){
401 /* not on disk or couldn't load it. clear level state flags and get a fresh version */
406 generate_board(&gameboard
->g
,curr
->gm
.num
);
407 activate_verticies(&gameboard
->g
);
408 impress_location(&gameboard
->g
);
412 /* no board in progress; fetch a new board */
413 generate_board(&gameboard
->g
,curr
->gm
.num
);
414 activate_verticies(&gameboard
->g
);
415 impress_location(&gameboard
->g
);
422 cairo_surface_t
*levelstate_get_icon(int num
){
423 levelstate
*l
=ensure_level_num(num
);
425 return gameboard_read_icon(l
->gm
.id
,(l
->in_progress
?"2":"1"),gameboard
);