Swap code/variable declaration to be pre-C99 compliant.
[xiph/unicode.git] / planarity / gameboard_logic.c
blobd3b33461675cbfecdacaa5a1e0d0703242b6c2a7
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 <gdk/gdk.h>
31 #include <gdk/gdkx.h>
32 #include <stdlib.h>
33 #include <math.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <errno.h>
38 #include "graph.h"
39 #include "timer.h"
40 #include "gameboard.h"
41 #include "levelstate.h"
42 #include "dialog_finish.h"
43 #include "dialog_pause.h"
44 #include "dialog_level.h"
45 #include "main.h"
47 /* game state helpers ************************************************************************************/
49 void prepare_reenter_game(Gameboard *g){
50 g->lit_vertex=0;
51 g->grabbed_vertex=0;
52 g->delayed_background=0;
54 g->group_drag=0;
55 g->button_grabbed=0;
56 g->selection_grab=0;
57 g->selection_active=num_selected_verticies(&g->g);
59 update_full(g);
60 update_score(g);
63 void reenter_game(Gameboard *g){
64 deploy_buttonbar(g);
65 unpause_timer();
66 set_in_progress();
69 void enter_game(Gameboard *g){
70 set_timer(0);
71 prepare_reenter_game(g);
72 reenter_game(g);
75 static void animate_verticies(Gameboard *g,
76 int *x_target,
77 int *y_target,
78 float *delta){
79 int flag=1,count=0;
80 vertex *v=g->g.verticies;
81 float *tx=alloca(g->g.vertex_num * sizeof(*tx));
82 float *ty=alloca(g->g.vertex_num * sizeof(*ty));
84 // hide intersections now; deactivating the verticies below will hide them anyway
85 g->show_intersections = 0;
86 g->realtime_background = 1;
87 update_full(g);
89 // deactivate all the verticies so they can be moved en-masse
90 // without graph metadata updates at each move
91 v=g->g.verticies;
92 while(v){
93 deactivate_vertex(&g->g,v);
94 tx[count]=v->x;
95 ty[count++]=v->y;
96 v=v->next;
99 // animate
100 while(flag){
101 count=0;
102 flag=0;
104 v=g->g.verticies;
105 while(v){
106 if(v->x!=x_target[count] || v->y!=y_target[count]){
108 float xd = x_target[count] - tx[count];
109 float yd = y_target[count] - ty[count];
110 float m = hypot(xd,yd);
112 tx[count]+= xd/m*delta[count];
113 ty[count]+= yd/m*delta[count];
115 invalidate_vertex(g,v);
116 invalidate_edges(GTK_WIDGET(g),v,0,0);
118 v->x = rint(tx[count]);
119 v->y = rint(ty[count]);
121 if( (v->x > x_target[count] && xd > 0) ||
122 (v->x < x_target[count] && xd < 0) ||
123 (v->y > y_target[count] && yd > 0) ||
124 (v->y < y_target[count] && yd < 0) ){
125 v->x = x_target[count];
126 v->y = y_target[count];
127 }else
128 flag=1;
130 invalidate_vertex(g,v);
131 invalidate_edges(GTK_WIDGET(g),v,0,0);
134 count++;
135 v=v->next;
138 gdk_window_process_all_updates();
139 gdk_flush();
143 // reactivate all the verticies
144 activate_verticies(&g->g);
146 // update the score
147 update_score(g);
149 // it's a reset; show lines is default. This also has the side
150 // effect of forcing a full board redraw and expose
151 g->realtime_background=0;
152 update_full(g);
154 // possible
155 if(g->g.active_intersections <= g->g.objective){
156 deploy_check(g);
157 }else{
158 undeploy_check(g);
164 static void scale(Gameboard *g,double scale){
165 int x=g->g.width/2;
166 int y=g->g.height/2;
167 int sel = num_selected_verticies(&g->g);
168 vertex *v;
169 int *tx=alloca(g->g.vertex_num * sizeof(*tx));
170 int *ty=alloca(g->g.vertex_num * sizeof(*ty));
171 float *tm=alloca(g->g.vertex_num * sizeof(*ty));
172 int i=0;
173 int minx=0;
174 int maxx=g->g.width-1;
175 int miny=0;
176 int maxy=g->g.height-1;
177 int okflag=0;
179 // if selected, expand from center of selected mass
180 if(sel){
181 double xac=0;
182 double yac=0;
184 v=g->g.verticies;
185 while(v){
186 if(v->selected){
187 xac+=v->x;
188 yac+=v->y;
190 v=v->next;
192 x = xac / sel;
193 y = yac / sel;
196 while(!okflag){
197 okflag=1;
198 i=0;
199 v=g->g.verticies;
200 while(v){
202 if(!sel || v->selected){
203 float nx = rint((v->x - x)*scale+x);
204 float ny = rint((v->y - y)*scale+y);
205 float m = hypot(nx - v->x,ny-v->y)/5;
207 if(nx<minx){
208 scale = (float)(minx-x) / (v->x-x);
209 okflag=0;
212 if(nx>maxx){
213 scale = (float)(maxx-x) / (v->x - x);
214 okflag=0;
217 if(ny<miny){
218 scale = (float)(miny-y) / (v->y-y);
219 okflag=0;
222 if(ny>maxy){
223 scale = (float)(maxy-y) / (v->y - y);
224 okflag=0;
226 if(m<1)m=1;
228 tx[i] = rint(nx);
229 ty[i] = rint(ny);
230 tm[i++] = m;
231 }else{
232 tx[i] = v->x;
233 ty[i] = v->y;
234 tm[i++] = 0;
236 v=v->next;
240 if(scale>=.99999 && scale<=1.00001){
241 ungrab_verticies(&g->g);
242 v=g->g.verticies;
243 while(v){
244 if(v->x<2 || v->x>=g->g.width-3)v->grabbed=1;
245 if(v->y<2 || v->y>=g->g.height-3)v->grabbed=1;
246 v=v->next;
248 fade_marked(g);
249 ungrab_verticies(&g->g);
250 }else{
251 animate_verticies(g,tx,ty,tm);
255 static void gtk_main_quit_wrapper(Gameboard *g){
256 fade_cancel(g);
257 gtk_main_quit();
260 static void level_wrapper(Gameboard *g){
261 fade_cancel(g);
262 level_dialog(g,0);
265 static void finish_wrapper(Gameboard *g){
266 fade_cancel(g);
267 finish_level_dialog(g);
270 /* toplevel main gameboard action entry points; when a button is
271 clicked and decoded to a specific action or a game state change
272 triggers an action, one of the below functions is the entry
273 point **************************************************************************************************************/
275 void quit_action(Gameboard *g){
276 pause_timer();
277 undeploy_buttons(g,gtk_main_quit_wrapper);
280 void level_action(Gameboard *g){
281 pause_timer();
282 undeploy_buttons(g,level_wrapper);
285 void finish_action(Gameboard *g){
286 if(g->g.active_intersections<=g->g.original_intersections){
287 pause_timer();
288 levelstate_finish();
289 undeploy_buttons(g,finish_wrapper);
293 void pause_action(Gameboard *g){
294 pause_timer();
295 undeploy_buttons(g,pause_dialog);
298 void about_action(Gameboard *g){
299 pause_timer();
300 undeploy_buttons(g,about_dialog);
303 void expand_action(Gameboard *g){
304 scale(g,1.2);
307 void shrink_action(Gameboard *g){
308 scale(g,.8);
311 void set_hide_lines(Gameboard *g, int state){
312 g->hide_lines=state;
313 update_full(g);
316 void toggle_hide_lines(Gameboard *g){
317 g->hide_lines= !g->hide_lines;
318 update_full(g);
321 void set_show_intersections(Gameboard *g, int state){
322 if(g->show_intersections != state){
323 g->show_intersections=state;
324 expose_full(g);
328 void toggle_show_intersections(Gameboard *g){
329 g->show_intersections=!g->show_intersections;
330 expose_full(g);
333 void reset_action(Gameboard *g){
334 vertex *v=g->g.verticies;
335 int *target_x = alloca(g->g.vertex_num * sizeof(*target_x));
336 int *target_y = alloca(g->g.vertex_num * sizeof(*target_y));
337 float *target_m = alloca(g->g.vertex_num * sizeof(*target_m));
338 int i=0;
339 int xd = (g->g.width-g->g.orig_width)>>1;
340 int yd = (g->g.height-g->g.orig_height)>>1;
342 while(v){
343 target_x[i]=v->orig_x+xd;
344 target_y[i]=v->orig_y+yd;
345 target_m[i++]=RESET_DELTA;
346 v=v->next;
349 animate_verticies(g,target_x,target_y,target_m);
351 // reset timer
352 set_timer(0);
353 unpause_timer();
357 /***************** save/load gameboard the widget state we want to be persistent **************/
359 // there are only a few things; lines, intersections
360 int gameboard_write(char *basename, Gameboard *g){
361 char *name;
362 FILE *f;
364 name=alloca(strlen(boarddir)+strlen(basename)+1);
365 name[0]=0;
366 strcat(name,boarddir);
367 strcat(name,basename);
369 f = fopen(name,"wb");
370 if(f==NULL){
371 fprintf(stderr,"ERROR: Could not save board state for \"%s\":\n\t%s\n",
372 get_level_desc(),strerror(errno));
373 return errno;
376 graph_write(&g->g,f);
378 if(g->hide_lines)
379 fprintf(f,"hide_lines 1\n");
380 if(g->show_intersections)
381 fprintf(f,"show_intersections 1\n");
383 fclose(f);
385 return 0;
388 int gameboard_read(char *basename, Gameboard *g){
389 FILE *f;
390 char *name;
391 char *line=NULL;
392 size_t n=0;
394 name=alloca(strlen(boarddir)+strlen(basename)+3);
395 name[0]=0;
396 strcat(name,boarddir);
397 strcat(name,basename);
399 f = fopen(name,"rb");
400 if(f==NULL){
401 fprintf(stderr,"ERROR: Could not read saved board state for \"%s\":\n\t%s\n",
402 get_level_desc(),strerror(errno));
403 return errno;
406 graph_read(&g->g,f);
408 // get local game state
409 while(getline(&line,&n,f)>0){
410 int i;
412 if (sscanf(line,"hide_lines %d",&i)==1)
413 if(i)
414 g->hide_lines = 1;
416 if (sscanf(line,"show_intersections %d",&i)==1)
417 if(i)
418 g->show_intersections = 1;
422 fclose (f);
423 free(line);
424 request_resize(g->g.width,g->g.height);
425 activate_verticies(&g->g);
427 return 0;