Add Russian translation provided by Валерий Крувялис <valkru@mail.ru>
[xiph-mirror.git] / planarity / levelstate.c
blob452304daaeb319ffc2c498d9858d8d50ec53cab8
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 "timer.h"
35 #include "levelstate.h"
36 #include "gameboard.h"
37 #include "dialog_pause.h"
38 #include "dialog_finish.h"
39 #include "dialog_level.h"
40 #include "main.h"
41 #include "graph_generate.h"
43 #define CHUNK 64
44 #define SAVENAME "levelstate"
46 typedef struct levelstate{
47 struct levelstate *prev;
48 struct levelstate *next;
50 graphmeta gm;
52 int in_progress;
53 long highscore;
55 } levelstate;
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(){
71 levelstate *ret;
72 int num=0;
74 if(pool==0){
75 int i;
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;
81 ret=pool;
82 if(tail)
83 num=tail->gm.num+1;
85 if(generate_get_meta(num, &ret->gm)) return 0;
87 pool=ret->next;
89 ret->next=0;
90 ret->prev=tail;
92 if(tail){
93 ret->prev->next=ret;
94 }else{
95 head=ret;
97 tail=ret;
99 ret->highscore=0;
100 ret->in_progress=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
105 graph g;
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
118 graph_release(&g);
122 return ret;
125 static levelstate *ensure_level_num(int level){
127 if(level<0)return 0;
129 if(!tail)new_level();
131 if(level <= tail->gm.num){
132 // find it in the existing levels
133 levelstate *l=tail;
134 while(l){
135 if(level == l->gm.num) return l;
136 l=l->prev;
138 return 0;
139 }else{
140 // make new levels to fill
142 while(tail->gm.num<level)
143 new_level();
145 return tail;
149 static levelstate *ensure_level(char *name){
150 int level=generate_find_number(name);
151 return ensure_level_num(level);
154 int levelstate_write(){
155 FILE *f;
156 char *name=alloca(strlen(statedir)+strlen(SAVENAME)+1);
157 name[0]=0;
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");
169 if(f==NULL){
170 fprintf(stderr,_("ERROR: Could not save game state file \"%s\":\n\t%s\n"),
171 curr->gm.id,strerror(errno));
172 return errno;
175 fprintf(f,"current %d : %s\n",(int)strlen(curr->gm.id),curr->gm.id);
178 levelstate *l=head;
179 while(l){
180 fprintf(f,"level %ld %d %d : %s\n",
181 l->highscore,l->in_progress,
182 (int)strlen(l->gm.id),l->gm.id);
183 l=l->next;
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");
196 fclose(f);
197 return 0;
200 // also functions as the levelstate init; always called once upon startup
201 int levelstate_read(){
202 char *line=NULL;
203 size_t n=0;
205 FILE *f;
206 char *name=alloca(strlen(statedir)+strlen(SAVENAME)+1);
207 name[0]=0;
208 strcat(name,statedir);
209 strcat(name,SAVENAME);
211 if(!head)new_level();
212 if(!curr)curr=head;
214 f = fopen(name,"rb");
215 if(f==NULL){
216 if(errno!=ENOENT){
217 fprintf(stderr,_("ERROR: Could not read game state file \"%s\":\n\t%s\n"),
218 curr->gm.id,strerror(errno));
220 return errno;
223 // get all levels we've seen.
224 while(getline(&line,&n,f)>0){
225 long l;
226 int i;
227 unsigned int j;
228 if (sscanf(line,"level %ld %d %d : ",&l,&i,&j)==3){
229 char *name=strchr(line,':');
230 // guard against bad edits
231 if(name &&
232 (strlen(line) - (name - line + 2) >= j)){
233 levelstate *le;
234 name += 2;
235 name[j]=0;
236 le = ensure_level(name);
237 if(le){
238 le->highscore=l;
239 le->in_progress=i;
241 if(le->highscore)
242 if(le->gm.unlock_plus + le->gm.num > level_limit)
243 level_limit = le->gm.unlock_plus + le->gm.num;
249 rewind(f);
251 // get current
252 while(getline(&line,&n,f)>0){
253 int i;
254 if (sscanf(line,"current %d : ",&i)==1){
255 char *name=strchr(line,':');
256 // guard against bad edits
257 if(name &&
258 (strlen(line) - (name - line + 2) >= (unsigned)i)){
259 levelstate *le;
260 name += 2;
261 name[i]=0;
262 le = ensure_level(name);
263 if(le)
264 curr=le;
268 if(sscanf(line,"about %d",&i)==1)
269 if(i==1)
270 aboutflag=1;
272 if(sscanf(line,"pause %d",&i)==1)
273 if(i==1)
274 pauseflag=1;
276 if(sscanf(line,"finish %d",&i)==1)
277 if(i==1)
278 finishflag=1;
280 if(sscanf(line,"select %d",&i)==1)
281 if(i==1)
282 selectflag=1;
286 while(curr->gm.num >= level_limit){
287 levelstate_prev();
290 return 0;
293 void levelstate_resume(){
295 levelstate_go();
297 if(pauseflag){
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);
309 }else{
310 prepare_reenter_game(gameboard);
311 reenter_game(gameboard);
313 aboutflag=0;
314 pauseflag=0;
315 finishflag=0;
316 selectflag=0;
320 void set_in_progress(){
321 curr->in_progress=1;
324 long levelstate_total_hiscore(){
325 long score=0;
326 levelstate *l=head;
328 while(l){
329 score+=l->highscore;
330 l=l->next;
332 return score;
335 long levelstate_get_hiscore(){
336 if(!curr)return 0;
337 return curr->highscore;
340 int levelstate_next(){
341 if(!curr->next)
342 new_level();
344 if(curr->next){
345 curr=curr->next;
346 graph_dirty=1;
347 return 1;
349 return 0;
352 int levelstate_prev(){
353 if(curr->prev){
354 curr=curr->prev;
355 graph_dirty=1;
356 return 1;
358 return 0;
361 int get_level_num(){
362 return curr->gm.num;
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);
372 curr->in_progress=0;
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(){
382 curr->in_progress=0;
383 graph_dirty=1;
386 int levelstate_in_progress(){
387 return curr->in_progress;
390 int levelstate_limit(){
391 return level_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 */
402 aboutflag=0;
403 pauseflag=0;
404 finishflag=0;
405 selectflag=0;
406 generate_board(&gameboard->g,curr->gm.num);
407 activate_verticies(&gameboard->g);
408 impress_location(&gameboard->g);
409 set_timer(0);
411 }else{
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);
416 set_timer(0);
419 graph_dirty=0;
422 cairo_surface_t *levelstate_get_icon(int num){
423 levelstate *l=ensure_level_num(num);
424 if(l==0)return 0;
425 return gameboard_read_icon(l->gm.id,(l->in_progress?"2":"1"),gameboard);