1 /*
2 Block Rage - the arcade game
3 Copyright (C) 1999-2005 Jiri Svoboda
4
5 This file is part of Block Rage.
6
7 Block Rage is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 Block Rage is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Block Rage; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21 Jiri Svoboda
22 jirik.svoboda@seznam.cz
23 */
24
25 /*
26 This module implements the gameplay itself, as well as
27 argument parsing, rc and configfile parsing.
28 */
29
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <sys/time.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <SDL.h>
40 #include "global.h"
41 #include "gfxout.h"
42 #include "gfxlib.h"
43 #include "menus.h"
44 #include "keyboard.h"
45 #include "bg.h"
46 #include "sound.h"
47 #include "timer.h"
48
49 #include "main.h"
50
51 static int first_execution;
52
53 player_t player[2];
54 int players;
55 int piecetypes;
56 static float fl_piecetypes;
57 int startinglevel;
58 static int board_x_size,board_y_size;
59 static int board_x_center;
60 int box_x_size,box_y_size;
61 int endgame,gameover;
62 int pausemenu;
63 static int paused;
64 static int board_left[3], board_bottom[3], board_top[3];
65
66 unsigned char *mypal;
67 #ifdef DIRECT_COLOR
68 pix_t mypalx[256];
69 #endif
70
71 unsigned char *fontn;
72 bmp_t bmp_menu;
73 bmp_t bmp_pmenu,bmp_intro;
74 unsigned char *bground;
75 bmp_t bmp_game[2];
76 static piece_t *piece;
77 static float piece_spd,piece_spd_drop; /* time in secs it takes the piece to fall
78 all the way down */
79 static float fall_spd, rise_spd;
80 float framerate;
81 static float gravity,maxdefspd;
82 int board_y_pix_size,board_x_pix_size;
83 static int nx_x0[3],nx_y0[3]; /* "NEXT" block coordinates for P1,P2, single P.*/
84 static int nx_t_x0[3],nx_t_y0[3]; /* "NEXT" label coordinates for P1,P2, single P.*/
85
86 static int key_quit;
87 int key_left[2],key_right[2],key_drop[2],key_shift_up[2],key_shift_down[2];
88
89 static int level,blocksleft;
90 static int lnumframes;
91 int bgnumframes; /* game frames till bg display */
92
93 int dbackground; /* 1: draw background effects, 0: bitmap background */
94 int showintro;
95
96 static int turndone;
97 char dataset[100];
98 static char *sys_config_file;
99 static char *usr_config_file;
100 char *datadir;
101 char *topten_file;
102
103 int immedstart;
104 int quit;
105 static int statx[6],staty[6];
106 int diffx,diffy; /* coordinates of the difficulty level display box */
107 int ts_x0,ts_x1; /* x-coordinates of the top-ten names(ts_x0) & scores(ts_x1) */
108 int no_of_sets;
109 int setn;
110 static int p_curframe[6]; /* piece: current frame number */
111 static float p_timeleft[6]; /* piece:remaining time for current frame (in secs) */
112 static float gboomdelay; /* delay(in secs) between line blowing when gameover */
113 int dispframenum;
114 static int framenum;
115
116 static int no_of_particle_frames;
117 static frame_t *particle_frame;
118
119 // returns an int between <base> and <base+range-1>
randint(long base,long range)120 long randint(long base,long range) {
121 float frange=range;
122
123 return base+(int) (frange*rand()/(RAND_MAX+1.0));
124 }
125
126 // returns a float between <base> and <base+range>
randfloat(float base,float range)127 static float randfloat(float base,float range) {
128 return base+(range*rand()/RAND_MAX);
129 }
130
131
box_draw(int x0,int y0,int type)132 void box_draw(int x0, int y0, int type)
133 {
134 int x,y;
135 pix_t c,*sp;
136 pix_t *dp;
137 pix_t key_color;
138 piece_t *piecep;
139
140 piecep = &piece[setn*P_PER_S+type-1];
141
142 sp=&(piecep->bmp[p_curframe[type-1]*(box_x_size*box_y_size)]);
143 key_color = piecep->key_color;
144
145 if(type) {
146 for(y=0;y<box_y_size;y++) {
147 dp=&(vscr[(y0+y)*scr_x_size+x0]);
148 for(x=0;x<box_x_size;x++) {
149 if((c=*sp++)!=key_color) *dp=c;
150 dp++;
151 }
152 }
153 }
154 }
155
156
piece_drawnext(player_t * p,int redraw)157 static void piece_drawnext(player_t *p, int redraw) {
158 int i,animated;
159 int bi=(players==1) ? 2 : (p->number);
160
161 animated=0;
162
163 for(i=0;i<3;i++)
164 if(piece[setn*P_PER_S+(p->p_nxt[i]-1)].frames>1) animated=1;
165
166 if(animated || redraw) {
167
168 t_align = T_CENTER;
169 v_print(nx_t_x0[bi],nx_t_y0[bi],FONT_NORMAL,"NEXT");
170 virt_cpyarea(nx_t_x0[bi],nx_t_y0[bi],
171 strpixlen("NEXT",FONT_NORMAL),font[FONT_NORMAL].ch);
172
173 v_copyarea(bmp_game[players-1].data,vscr,scr_x_size,scr_x_size,
174 nx_x0[bi],nx_y0[bi],
175 box_x_size,3*box_y_size,
176 nx_x0[bi],nx_y0[bi]);
177
178 for(i=0;i<3;i++)
179 box_draw(nx_x0[bi],nx_y0[bi]+i*box_y_size,p->p_nxt[2-i]);
180
181 virt_cpyarea(nx_x0[bi],nx_y0[bi],box_x_size,3*box_y_size);
182 }
183 }
184
game_over(void)185 static void game_over(void) { /* someone has died */
186 if(!gameover) {
187 gameover=1;
188 sound_fx(2);
189 }
190 }
191
game_drawscore(player_t * p,int bgvcpy)192 static void game_drawscore(player_t *p, int bgvcpy) {
193 if(bgvcpy) {
194 v_drawscrarea(bmp_game[players-1].data,statx[1],staty[1],
195 strpixlen("0000000",FONT_HIGHLIGHTED),font[FONT_HIGHLIGHTED].ch);
196 }
197 t_align=T_LEFT;
198 v_printf(statx[1],staty[1],FONT_HIGHLIGHTED,"%07d",p->score);
199
200 if(bgvcpy) {
201 virt_cpyarea(statx[1],staty[1],
202 strpixlen("00000000",FONT_HIGHLIGHTED),font[FONT_HIGHLIGHTED].ch);
203 }
204 }
205
game_drawlevelnum(int bgvcpy)206 static void game_drawlevelnum(int bgvcpy) {
207
208 if(bgvcpy) {
209 v_drawscrarea(bmp_game[players-1].data,statx[3],staty[3],
210 strpixlen("00",FONT_HIGHLIGHTED),font[FONT_HIGHLIGHTED].ch);
211 }
212 t_align=T_LEFT;
213 v_printf(statx[3],staty[3],FONT_HIGHLIGHTED,"%02d",level);
214
215 if(bgvcpy) {
216 virt_cpyarea(statx[3],staty[3],
217 strpixlen("00",FONT_HIGHLIGHTED),font[FONT_HIGHLIGHTED].ch);
218 }
219 }
220
game_drawblocksleft(int bgvcpy)221 static void game_drawblocksleft(int bgvcpy) {
222 if(bgvcpy) {
223 v_drawscrarea(bmp_game[players-1].data,statx[5],staty[5],
224 strpixlen("00",FONT_HIGHLIGHTED),font[FONT_HIGHLIGHTED].ch);
225 }
226 t_align=T_LEFT;
227 v_printf(statx[5],staty[5],FONT_HIGHLIGHTED,"%02d",blocksleft);
228
229 if(bgvcpy) {
230 virt_cpyarea(statx[5],staty[5],
231 strpixlen("00",FONT_HIGHLIGHTED),font[FONT_HIGHLIGHTED].ch);
232 }
233 }
234
board_draw(player_t * p,int hidepieces,int vcpy)235 void board_draw(player_t *p, int hidepieces, int vcpy) {
236 int i,x,y,yshift,x0,y0;
237 int pn=p->number;
238 int bi=(players==1)?2:pn;
239 char s[20];
240 p_list_t *pp;
241 unsigned char *ucp;
242
243 /* board background */
244 if(dbackground) { /* special effects */
245 if(bgnumframes==0) {
246 ucp=bground;
247 for(y=0;y<board_y_pix_size;y++)
248 for(x=0;x<board_x_pix_size;x++) {
249 vscr[(y+board_top[bi])*scr_x_size+x+board_left[bi]]=
250 #ifdef DIRECT_COLOR
251 mypalx[(*ucp++)+BGPALBASE];
252 #else
253 (*ucp++)+BGPALBASE;
254 #endif
255 }
256 } else { /* black */
257 ucp=bground;
258 for(y=0;y<board_y_pix_size;y++)
259 for(x=0;x<board_x_pix_size;x++) {
260 vscr[(y+board_top[bi])*scr_x_size+x+board_left[bi]]=0;
261 }
262 }
263 } else { /* bitmap bacground */
264 v_copyarea(bmp_game[players-1].data,vscr,scr_x_size,scr_x_size,
265 board_left[bi],board_top[bi],
266 board_x_size*box_x_size,board_y_size*box_y_size,
267 board_left[bi],board_top[bi]);
268 }
269
270 if(!hidepieces) {
271 /* pieces stuck on board */
272 for(y=0;y<board_y_size;y++)
273 for(x=0;x<board_x_size;x++) {
274 if((p->bflags[y*board_x_size+x] != BF_BLOW)
275 || /*!(p->explode_frames & 1)*/ (dispframenum & 1)) {
276 switch(p->status) {
277 case S_FALL: yshift=(p->bflags[y*board_x_size+x]==BF_FALL)?
278 -p->fall_pix : 0; break;
279 case S_RISE: yshift=p->rise_pix; break;
280 default: yshift=0; break;
281 }
282 box_draw(board_left[bi]+x*box_x_size,
283 board_bottom[bi]-y*box_y_size+yshift,
284 p->board[y*board_x_size+x]);
285
286 }
287 }
288
289 /* falling piece */
290 if(p->status==S_NORMAL && !gameover) {
291 for(i=0;i<3;i++) if(p->piecey+i<board_y_size) {
292 box_draw(board_left[bi]+p->piecex*box_x_size,
293 board_bottom[bi]-(p->piecey+i)*box_y_size-p->pieceypix,
294 p->p_cur[i]);
295 }
296 }
297
298 /* clean some garbage above and below the board */
299 v_copyarea(bmp_game[players-1].data,vscr,scr_x_size,scr_x_size,
300 board_left[bi],board_top[bi]-box_y_size,
301 board_x_size*box_x_size,box_y_size,
302 board_left[bi],board_top[bi]-box_y_size);
303
304 v_copyarea(bmp_game[players-1].data,vscr,scr_x_size,scr_x_size,
305 board_left[bi],board_bottom[bi]+box_y_size,
306 board_x_size*box_x_size,box_y_size,
307 board_left[bi],board_bottom[bi]+box_y_size);
308
309 /* adjust clipping for particle drawing */
310 clip_x0=board_left[bi]; clip_y0=board_top[bi];
311 clip_x1=clip_x0+board_x_pix_size-1;
312 clip_y1=clip_y0+board_y_pix_size-1;
313
314
315
316 // Draw all the particles
317 pp=p->particles;
318 while(pp) {
319 v_drawbmp(board_left[bi] + pp->x,
320 board_top[bi] + board_y_pix_size - pp->y,
321 particle_frame[pp->frame].bmp.key_color,
322 particle_frame[pp->frame].bmp.xs,
323 particle_frame[pp->frame].bmp.ys,
324 particle_frame[pp->frame].bmp.data);
325 pp=pp->next;
326 }
327 }
328
329 if(paused) {
330 t_align=T_CENTER;
331 x0=board_left[bi]+(board_x_pix_size/2);
332 y0=board_top[bi]+(board_y_pix_size/2);
333
334 v_print(x0,y0-(font[0].ch/2),FONT_NORMAL,"PAUSED");
335 }
336
337 /* loser/winner/gameover text */
338 if(gameover) {
339 t_align=T_CENTER;
340 x0=board_left[bi]+(board_x_pix_size/2);
341 y0=board_top[bi]+(board_y_pix_size/2);
342
343 if(players==2) {
344 if(player[0].loser && player[1].loser) strcpy(s,"TIE");
345 else {
346 if(p->loser) strcpy(s,"LOSER");
347 else strcpy(s,"WINNER");
348 }
349 } else {
350 if(player[0].loser) strcpy(s,"GAME OVER");
351 else strcpy(s,"GAME COMPLETED");
352 }
353
354 v_print(x0,y0-(font[0].ch),FONT_NORMAL,s);
355 }
356 else if(lnumframes>0) { /* level %d text */
357 t_align=T_CENTER;
358 x0=board_left[bi]+board_x_pix_size/2;
359 y0=board_top[bi]+board_y_pix_size/2;
360
361 v_printf(x0,y0-font[0].ch/2,FONT_NORMAL,"Level %d",level);
362 }
363
364 /* copy to screen */
365 if(vcpy) {
366 virt_cpyarea(board_left[bi],board_top[bi],
367 board_x_size*box_x_size,board_y_size*box_y_size);
368 }
369 }
370
game_statistics_draw(void)371 void game_statistics_draw(void) {
372 if(players==1) {
373 t_align=T_LEFT;
374 v_print(statx[0],staty[0],FONT_NORMAL,"Score:");
375 v_print(statx[2],staty[2],FONT_NORMAL,"Level:");
376 v_print(statx[4],staty[4],FONT_NORMAL,"Blocks:");
377 game_drawlevelnum(0);
378 game_drawblocksleft(0);
379 game_drawscore(&(player[0]),0);
380 }
381 }
382
game_draw(void)383 static void game_draw(void) {
384 int i;
385 #ifdef DEBUG
386 char s[30];
387 #endif
388
389 for(i=0;i<scr_x_size*scr_y_size;i++)
390 vscr[i]=bmp_game[players-1].data[i];
391
392 game_statistics_draw();
393
394 if(!paused) {
395 for(i=0;i<players;i++)
396 piece_drawnext(&(player[i]),1);
397 }
398
399 for(i=0;i<players;i++)
400 board_draw(&(player[i]),paused,1);
401
402 #ifdef DEBUG
403 t_align=T_LEFT;
404 v_printf(0,0,FONT_HIGHLIGHTED,"%f",
405 (float)framerate*(float)dispframenum/(float)framenum);
406 #endif
407
408 virt_cpy();
409 }
410
particle_create(p_list_t * next,float x,float y,float vx,float vy,float fr_age,int frame)411 static p_list_t *particle_create(p_list_t *next,
412 float x, float y, float vx, float vy, float fr_age, int frame) {
413 p_list_t *tmp;
414
415 tmp=calloc_verify(1,sizeof(p_list_t));
416
417 tmp->x=x;
418 tmp->y=y;
419 tmp->vx=vx;
420 tmp->vy=vy;
421 tmp->fr_age=fr_age;
422 tmp->next=next;
423 tmp->frame=frame;
424
425 return tmp;
426 }
427
piece_collides(player_t * p,int x,int y)428 static int piece_collides(player_t *p, int x, int y) {
429 return y<0 ? 1 : (p->board[y*board_x_size+x] |
430 p->board[(y+1)*board_x_size+x] |
431 p->board[(y+2)*board_x_size+x])!=0;
432 }
433
player_givepiece(player_t * p)434 static void player_givepiece(player_t *p) {
435 int i;
436
437
438 for(i=0;i<3;i++) {
439
440 p->p_cur[i]=p->p_nxt[i];
441 fl_piecetypes=piecetypes;
442 p->p_nxt[i]=randint(1,fl_piecetypes); // 1..fl_piecetypes
443 }
444 p->piecey=board_y_size-1;
445 p->pieceypix=box_y_size;
446 p->piecex=board_x_center;
447 p->pieceyfrac=0;
448
449 if(piece_collides(p,p->piecex,p->piecey)) {
450 //game_over();
451 p->loser=1;
452 }
453
454 if(!gameover)
455 piece_drawnext(p,1);
456 }
457
pgoto_normal(player_t * p)458 static void pgoto_normal(player_t *p) {
459 p->status=S_NORMAL;
460 player_givepiece(p);
461 }
462
player_init(player_t * p,int pn)463 static int player_init(player_t *p, int pn) {
464 int x,y;
465
466 p->number=pn;
467
468 p->score=0;
469
470 p->board=malloc_verify((board_y_size+5)*board_x_size);
471 if(!(p->board)) return 0;
472
473 p->bflags=malloc_verify((board_y_size+5)*board_x_size);
474 if(!(p->bflags)) return 0;
475
476 /* clear game board */
477 for(y=0;y<board_y_size+3;y++)
478 for(x=0;x<board_x_size;x++) {
479 p->board[y*board_x_size+x]=0;
480 p->bflags[y*board_x_size+x]=0;
481 }
482
483 p->piece_dropping=0;
484
485 p->explode_frames=-1; /* nothing exploding right now */
486 p->fall_pix=-1; /* nothing falling */
487 p->fall_frac=0;
488 p->rise_pix=-1; /* and we're not raising */
489 p->rise_frac=0;
490
491 p->status=S_NORMAL;
492
493 p->rows2rise=0;
494 p->loser=0;
495
496 p->particles=NULL;
497 p->gboomy=board_y_size-1;
498 p->gttb=gboomdelay;
499
500 p->chain=-1;
501 p->lines[0]=0;
502 p->lines[1]=0;
503 p->lines[2]=0;
504
505 return 1;
506 }
507
player_done(player_t * p)508 static void player_done(player_t *p) {
509 free(p->board);
510 free(p->bflags);
511 }
512
game_level_init(void)513 static void game_level_init(void) {
514 blocksleft+=35+5*level;
515 piece_spd=board_y_pix_size/framerate/(30/(level+2));
516 piece_spd_drop=board_y_pix_size/framerate/0.80;
517 if(piece_spd>piece_spd_drop) piece_spd_drop=piece_spd;
518
519 lnumframes=framerate*3; /* show for 3 seconds */
520 bgnumframes=framerate*2; /* display ater 2 seconds */
521 }
522
blocks_sub(int num)523 static void blocks_sub(int num) {
524 if(players==1) {
525 blocksleft-=num;
526 while(blocksleft<=0) { /* advance level */
527 if(level<99) level++; /* I DOUBT anybody will get to the level 99,
528 but I don't take any risks */
529 else game_over(); /* otherwise end the game and congratulate the player */
530 bg_done();
531 game_level_init();
532 bg_init(level);
533
534 DEBUG1(printf("bg_init(level=%d)\n",level));
535 }
536 }
537 }
538
539 /*
540 Add one row to the bottom of ones playfield.
541
542 The trick is that this row contains random tiles,
543 but must not trigger any explosions.
544 */
player_rise(player_t * p)545 static void player_rise(player_t *p) {
546 int x,y;
547 int i,t,nsuit;
548 float fnsuit;
549 int suits[MAX_PIECE_TYPES];
550
551 // suits = malloc_verify(sizeof(int)*(piecetypes+1));
552
553 for(y=board_y_size-1;y>0;y--)
554 for(x=0;x<board_x_size;x++)
555 p->board[y*board_x_size+x]=p->board[(y-1)*board_x_size+x];
556
557 for(i=1;i<=piecetypes;i++) suits[i]=1;
558 suits[(int)p->board[board_x_size+board_x_center-1]]=0;
559 suits[(int)p->board[board_x_size+board_x_center ]]=0;
560 suits[(int)p->board[board_x_size+board_x_center+1]]=0;
561
562 nsuit=0;
563 for(i=1;i<=piecetypes;i++) if(suits[i]) nsuit++;
564
565 t=1;
566 fnsuit=nsuit;
567 x=randint(0,fnsuit); // 0 ..fnsuit-1
568 while(!suits[t]) t++;
569 for(i=0;i<x;i++) {
570 t++;
571 while(!suits[t]) t++;
572 }
573 p->board[board_x_center]=t;
574
575 /* The center square -1 */
576 for(i=1;i<=piecetypes;i++) suits[i]=1;
577 suits[(int)p->board[board_x_size+board_x_center-1 ]]=0;
578 suits[(int)p->board[board_x_size+board_x_center-1+1]]=0;
579 suits[(int)p->board[ board_x_center-1+1]]=0;
580
581 nsuit=0;for(i=1;i<=piecetypes;i++) if(suits[i]) nsuit++;
582
583 t=1;
584 fnsuit=nsuit;
585 x=randint(0,fnsuit); // 0 ..fnsuit-1
586 while(!suits[t]) t++;
587 for(i=0;i<x;i++) {
588 t++;
589 while(!suits[t]) t++;
590 }
591 p->board[board_x_center-1]=t;
592
593 /* The center square +1 */
594 for(i=1;i<=piecetypes;i++) suits[i]=1;
595 suits[(int)p->board[board_x_size+board_x_center+1-1]]=0;
596 suits[(int)p->board[board_x_size+board_x_center+1 ]]=0;
597 suits[(int)p->board[ board_x_center+1-1]]=0;
598
599 nsuit=0;for(i=1;i<=piecetypes;i++) if(suits[i]) nsuit++;
600
601 t=1; //x=rand()%nsuit;
602 fnsuit=nsuit;
603 x=randint(0,fnsuit); // 0 ..fnsuit-1
604 while(!suits[t]) t++;
605 for(i=0;i<x;i++) {
606 t++;
607 while(!suits[t]) t++;
608 }
609 p->board[board_x_center+1]=t;
610
611 /* The left square */
612 for(i=1;i<=piecetypes;i++) suits[i]=1;
613 suits[(int)p->board[board_x_size ]]=0;
614 suits[(int)p->board[board_x_size+1]]=0;
615 suits[(int)p->board[ +1]]=0;
616
617 nsuit=0;for(i=1;i<=piecetypes;i++) if(suits[i]) nsuit++;
618
619 t=1;
620 fnsuit=nsuit;
621 x=randint(0,fnsuit); // 0 ..fnsuit-1
622 while(!suits[t]) t++;
623 for(i=0;i<x;i++) {
624 t++;
625 while(!suits[t]) t++;
626 }
627 p->board[board_x_center-2]=t;
628
629
630 /* The right square */
631 for(i=1;i<=piecetypes;i++) suits[i]=1;
632 suits[(int)p->board[board_x_size+4-1]]=0;
633 suits[(int)p->board[board_x_size+4 ]]=0;
634 suits[(int)p->board[ 4-1]]=0;
635
636 nsuit=0;for(i=1;i<=piecetypes;i++) if(suits[i]) nsuit++;
637
638 t=1;
639 fnsuit=nsuit;
640 x=randint(0,fnsuit); // 0 ..fnsuit-1
641 while(!suits[t]) t++;
642 for(i=0;i<x;i++) {
643 t++;
644 while(!suits[t]) t++;
645 }
646 p->board[board_x_center+2]=t;
647
648 // free(suits);
649 }
650
player_checkrows2rise(player_t * p)651 static void player_checkrows2rise(player_t *p) {
652 if(p->rows2rise>0) {
653 player_rise(p);
654 p->rise_pix+=box_y_size;
655 p->rise_frac=1;
656
657 p->rows2rise--;
658 p->status=S_RISE;
659 } else pgoto_normal(p);
660 }
661
piece_shift_up(player_t * p)662 static void piece_shift_up(player_t *p) {
663 char tmp;
664
665 tmp =p->p_cur[2];
666 p->p_cur[2]=p->p_cur[1];
667 p->p_cur[1]=p->p_cur[0];
668 p->p_cur[0]=tmp;
669
670 }
671
piece_shift_down(player_t * p)672 static void piece_shift_down(player_t *p) {
673 char tmp;
674
675 tmp =p->p_cur[0];
676 p->p_cur[0]=p->p_cur[1];
677 p->p_cur[1]=p->p_cur[2];
678 p->p_cur[2]=tmp;
679
680 }
681
less_num(int a,int b)682 static int less_num(int a, int b) {
683 return (a<b)?a:b;
684 }
685
player_checkexplosions_line(player_t * p,int x0,int y0,int dx,int dy,int len)686 static int player_checkexplosions_line(player_t *p,
687 int x0, int y0, int dx, int dy, int len) {
688 int i=0,anyboom=0,n,b;
689
690 while (i<len) {
691 n=1;
692 if(p->board[(y0+i*dy)*board_x_size+x0+i*dx] != 0) {
693 while ( (i+n < len) &&
694 (p->board[(y0+(i+n)*dy)*board_x_size+x0+(i+n)*dx]
695 ==p->board[(y0+i*dy)*board_x_size+x0+i*dx])) n++;
696 }
697 if(n>=3) {
698 for(b=0;b<n;b++) {
699 p->bflags[(y0+(i+b)*dy)*board_x_size+x0+((i+b)*dx)] = BF_BLOW;
700 }
701 anyboom=1;
702 p->lines[n-3]++;
703 }
704 i+=n;
705 }
706 return anyboom;
707 }
708
player_checkexplosions(player_t * p)709 static int player_checkexplosions(player_t *p) {
710 int i,anyboom=0;
711 int oldscore;
712
713 /* rows */
714 for(i=0;i<board_y_size;i++) {
715 anyboom |= player_checkexplosions_line(p,0,i,1,0,board_x_size);
716 }
717
718 /* columns */
719 for(i=0;i<board_x_size;i++) {
720 anyboom |= player_checkexplosions_line(p,i,0,0,1,board_y_size);
721 }
722
723 /* "/" diagonal */
724 for(i=board_x_size-1;i>0;i--) {
725 anyboom |= player_checkexplosions_line(p,i,0,1,1,
726 less_num(board_x_size-i,board_y_size));
727 }
728 for(i=0;i<board_y_size;i++) {
729 anyboom |= player_checkexplosions_line(p,0,i,1,1,
730 less_num(board_x_size,board_y_size-i));
731 }
732
733 /* "\" diagonal */
734 for(i=board_x_size-1;i>0;i--) {
735 anyboom |= player_checkexplosions_line(p,i,board_y_size-1,1,-1,
736 less_num(board_x_size-i,board_y_size));
737 }
738 for(i=board_y_size-1;i>=0;i--) {
739 anyboom |= player_checkexplosions_line(p,0,i,1,-1,
740 less_num(board_x_size,i+1));
741 }
742
743
744 if(anyboom) {
745 p->explode_frames=0.25*framerate;
746 p->status=S_BLOW;
747 p->chain++;
748 } else {
749 oldscore= p->score;
750 p->status=S_CHECKR;
751 p->rise_pix=-1;
752 if(players==2 && p->chain>0) player[1-(p->number)].rows2rise+=p->chain;
753 if(p->chain>0) p->score+=p->chain*1000;
754 p->score+=(p->lines[0]*500)+(p->lines[1]*1000)+(p->lines[2]*1500);
755 if(players==1 && oldscore!=p->score) {
756 //game_draw();
757 game_drawscore(p,1);
758 }
759 }
760 return anyboom;
761 }
762
player_fixpiece(player_t * p)763 static void player_fixpiece(player_t *p) {
764 int i;
765
766 if(p->piecey+2>=board_y_size) {
767 //game_over();
768 p->loser=1;
769 }
770
771 for(i=0;i<3;i++) {
772 p->board[(p->piecey + i)*board_x_size+(p->piecex)]
773 = p->p_cur[i];
774 }
775 if(!gameover) {
776 p->status=S_CHECKX;
777 p->chain=-1;
778 p->lines[0]=0;
779 p->lines[1]=0;
780 p->lines[2]=0;
781 }
782 }
783
board_clearflags(player_t * p)784 static void board_clearflags(player_t *p) {
785 int x,y;
786
787 for(y=0;y<board_y_size;y++)
788 for(x=0;x<board_x_size;x++)
789 p->bflags[y*board_x_size+x]=0;
790 }
791
player_spawnparticles(player_t * p,int x,int y)792 static void player_spawnparticles(player_t *p, int x, int y) {
793 int i;
794 for(i=0;i<2;i++) {
795 p->particles=particle_create(p->particles, x*box_x_size,y*box_y_size,
796 randfloat(-maxdefspd,2*maxdefspd),
797 randfloat(-maxdefspd,2*maxdefspd),
798 0,no_of_particle_frames-1);
799 }
800 }
801
player_blow(player_t * p)802 static void player_blow(player_t *p) {
803 int x,y,oldlevel,oldscore,oldblocks,anyblow=0;
804
805 oldlevel=level;
806 oldscore=p->score;
807 oldblocks=blocksleft;
808
809 for(y=0;y<board_y_size;y++) {
810 for(x=0;x<board_x_size;x++) {
811 if(p->bflags[y*board_x_size+x]!=0) {
812 if(p->bflags[y*board_x_size+x]==BF_BLOW) {
813 p->board[y*board_x_size+x]=0;
814 blocks_sub(1);
815 player_spawnparticles(p,x,y);
816 anyblow=1;
817 }
818 p->bflags[y*board_x_size+x]=0;
819 }
820 }
821 }
822 if(players==1) {
823 /* if(level!=oldlevel || p->score!=oldscore || blocksleft!=oldblocks) {
824
825 game_draw();
826
827 }*/
828 if(p->score!=oldscore) {
829 game_drawscore(p,1);
830 }
831 if(level!=oldlevel) {
832 game_drawlevelnum(1);
833 }
834 if(blocksleft!=oldblocks) {
835 game_drawblocksleft(1);
836 }
837 }
838 if(anyblow) sound_fx(FX_BLOW);
839 }
840
player_checkfall(player_t * p)841 static void player_checkfall(player_t *p) {
842 int x,y,anyfall=0;
843
844 for(y=1;y<board_y_size;y++) {
845 for(x=0;x<board_x_size;x++) {
846 if ((p->board[y*board_x_size+x]!=0)
847 && (p->board[(y-1)*board_x_size+x]==0)) {
848
849 p->board[(y-1)*board_x_size+x]=p->board[y*board_x_size+x];
850 p->board[y*board_x_size+x]=0;
851 p->bflags[(y-1)*board_x_size+x]=BF_FALL;
852 anyfall=1;
853 }
854 }
855 }
856 if(anyfall) {
857 //p->fall_pix=box_y_size-1;
858 p->status=S_FALL;
859 } else {
860 p->status=S_CHECKX;
861 }
862 }
863
864
865
player_updatepiece(player_t * p)866 static void player_updatepiece(player_t *p) {
867 if(!gameover)
868 p->pieceyfrac -= (p->piece_dropping ? piece_spd_drop : piece_spd);
869
870 while (p->pieceyfrac < 0.0) {
871 p->pieceypix--;
872 p->pieceyfrac+=1.0;
873 }
874 while (p->pieceypix < 0) {
875 if (piece_collides(p,p->piecex,p->piecey-1)) {
876 player_fixpiece(p);
877 sound_fx(FX_HIT);
878 return;
879 } else {
880 //p->score++;
881 p->piecey--;
882 p->pieceypix+=box_y_size;
883 }
884 }
885 turndone=1;
886 }
887
player_updateblow(player_t * p)888 static void player_updateblow(player_t *p) {
889 p->explode_frames--;
890 if(p->explode_frames<0) {
891 player_blow(p);
892 p->status=S_CHECKF;
893 p->fall_pix=box_y_size-1;
894 }
895 turndone=1;
896 }
897
player_updatefall(player_t * p)898 static void player_updatefall(player_t *p) {
899 p->fall_frac-=fall_spd;
900 while (p->fall_frac < 0.0) {
901 p->fall_pix--;
902 p->fall_frac+=1.0;
903 }
904 if(p->fall_pix<=0) {
905 p->status=S_CHECKF;
906 p->fall_pix+=box_y_size;
907 board_clearflags(p);
908 }
909 turndone=1;
910 }
911
player_updaterise(player_t * p)912 static void player_updaterise(player_t *p)
913 {
914 p->rise_frac-=rise_spd;
915 while (p->rise_frac < 0.0) {
916 p->rise_pix--;
917 p->rise_frac+=1.0;
918 }
919 if(p->rise_pix<=0) {
920 p->status=S_CHECKR;
921 }
922 turndone=1;
923 }
924
player_updateparticles(player_t * p)925 static void player_updateparticles(player_t *p) {
926 p_list_t *tmp, *pp=p->particles, *last=NULL;
927 int something,xsize;
928
929 while(pp) {
930 pp->fr_age+= 1/framerate;
931 while(pp->fr_age>particle_frame[pp->frame].delay) {
932 pp->frame--;
933 pp->fr_age=0;
934 }
935 pp->x += pp->vx*(1/framerate);
936 xsize=particle_frame[pp->frame].bmp.xs;
937 do {
938 something=0;
939 if(pp->vx<0 && pp->x < 0) {
940 pp->vx= -pp->vx;
941 pp->x = -pp->x;
942 something=1;
943 }
944 if(pp->vx>0 && pp->x > board_x_pix_size-xsize) {
945 pp->vx= -pp->vx;
946 pp->x = (board_x_pix_size)-(pp->x+xsize-(board_x_pix_size))-xsize;
947 something=1;
948 }
949 } while(something);
950
951 pp->vy -= gravity*(1/framerate);
952 pp->y += pp->vy*(1/framerate);
953
954 if(pp->y < 0 || pp->frame<0) {
955 tmp=pp->next;
956 if(last) last->next=tmp; else p->particles=tmp;
957 free(pp);
958 pp=tmp;
959 } else {
960 last=pp;
961 pp=pp->next;
962 }
963 }
964 }
965
player_blowline(player_t * p,int y)966 static void player_blowline(player_t *p, int y) {
967 int x;
968
969 for(x=0;x<board_x_size;x++) {
970 if(p->board[y*board_x_size+x]>0) {
971 player_spawnparticles(p,x,y);
972 p->board[y*board_x_size+x]=0;
973 p->bflags[y*board_x_size+x]=0;
974 }
975 }
976 }
977
player_update(player_t * p)978 static void player_update(player_t *p) {
979 player_updateparticles(p);
980
981 /* blowing the blocks line by line, when game over */
982 if(p->loser && p->gboomy>=0) {
983 p->gttb -= 1/framerate;
984 while(p->gttb<0) {
985 player_blowline(p,p->gboomy--);
986 p->gttb += gboomdelay;
987 }
988 }
989 do {
990 //if(gameover) return;
991 turndone=0;
992 switch(p->status) {
993 case S_NORMAL: player_updatepiece(p); break;
994 case S_CHECKX: player_checkexplosions(p); break;
995 case S_BLOW: player_updateblow(p); break;
996 case S_CHECKF: player_checkfall(p); break;
997 case S_FALL: player_updatefall(p); break;
998 case S_CHECKR: player_checkrows2rise(p); break;
999 case S_RISE: player_updaterise(p); break;
1000 }
1001 } while (!turndone);
1002 }
1003
1004 /*
1005 Make a screenshot and save it to blockrage-scr.pcx in the
1006 $HOME directory. If HOME is not set, save it to the working
1007 directory.
1008 */
make_screenshot(void)1009 static void make_screenshot(void) {
1010 char *home;
1011 char *fpathname;
1012 char *fname = "blockrage-scr.pcx";
1013 int home_l;
1014
1015 home = getenv("HOME");
1016 home_l = strlen(home);
1017
1018 if(home_l > SANE_PATH_LEN) {
1019 fprintf(stderr,"HOME length is not sane\n");
1020 home=NULL;
1021 }
1022
1023 if(!home) {
1024 fputs("Saving screenshot to ./blockrage-scr.pcx\n",stdout);
1025 pcx_save(scr_x_size,scr_y_size,vscr,mypal, "./blockrage-scr.pcx");
1026 } else {
1027 fpathname = malloc_verify(home_l+1+strlen(fname)+1);
1028 strncpy(fpathname,home,home_l);
1029 fpathname[home_l]=0;
1030 cat_with_slash(fpathname,fname);
1031
1032 printf("Saving screenshot to %s\n",fpathname);
1033 pcx_save(scr_x_size,scr_y_size,vscr,mypal, fpathname);
1034
1035 free(fpathname);
1036 }
1037 }
1038
global_key_hnd(int sym,int mod,int press)1039 static int global_key_hnd(int sym, int mod, int press) {
1040 mod = mod & ~(KMOD_CAPS|KMOD_NUM);
1041 if(press && mod == KMOD_LALT) {
1042 switch(sym) {
1043 case SDLK_x:
1044 quit=1;
1045 return 0;
1046
1047 case SDLK_RETURN:
1048 gfx_set_fullscreen(!gfx_get_fullscreen());
1049 virt_cpy();
1050 return 0;
1051
1052 case SDLK_BACKSLASH:
1053 gfx_set_doublesize(!gfx_get_doublesize());
1054 virt_cpy();
1055 return 0;
1056
1057 default:
1058 break;
1059 }
1060 }
1061
1062 if(press) {
1063 switch(sym) {
1064 case SDLK_PRINT:
1065 make_screenshot();
1066 return 0;
1067
1068 default:
1069 break;
1070 }
1071 }
1072
1073 return 1;
1074 }
1075
key_handler(int scancode,int press)1076 static void key_handler(int scancode, int press) {
1077 int i;
1078
1079 // printf("[KP:%d,%d]",scancode,press);
1080 if(scancode>=KSTAT_SIZE) return; /* error! */
1081 keyboard_state[scancode]=press;
1082
1083 if(!gameover && !paused) {
1084 for(i=0;i<players;i++) {
1085 if(scancode==key_left[i] && press) {
1086 if(!piece_collides(&(player[i]),player[i].piecex-1,player[i].piecey)) {
1087 if(player[i].piecex>0) player[i].piecex--;
1088 }
1089 }
1090 if(scancode==key_right[i] && press) {
1091 if(!piece_collides(&(player[i]),player[i].piecex+1,player[i].piecey)) {
1092 if(player[i].piecex+1<board_x_size) player[i].piecex++;
1093 }
1094 }
1095 if(scancode==key_shift_up[i] && press)
1096 piece_shift_up(&(player[i]));
1097 if(scancode==key_shift_down[i] && press)
1098 piece_shift_down(&(player[i]));
1099 if(scancode==key_drop[i])
1100 player[i].piece_dropping = !!press;
1101 }
1102 }
1103 if(scancode==key_quit && press) {
1104 if(gameover) endgame=1; /* go to main menu */
1105 else pausemenu=1; /* show the pause-menu */
1106 }
1107 if(scancode==SDLK_1 && press) sound_fx(0);
1108 if(scancode==SDLK_2 && press) sound_fx(1);
1109 if(scancode==SDLK_3 && press) sound_fx(2);
1110 if(scancode==SDLK_4 && press) sound_fx(3);
1111 if(scancode==SDLK_5 && press) sound_fx(4);
1112 if(scancode==SDLK_6 && press) sound_fx(5);
1113 if(scancode==SDLK_7 && press) sound_fx(6);
1114 if(scancode==SDLK_8 && press) sound_fx(7);
1115 if(scancode==SDLK_9 && press) sound_fx(8);
1116 if(scancode==SDLK_0 && press) sound_fx(9);
1117
1118 // if(scancode==SDLK_PRINT && press) make_screenshot();
1119
1120 if(scancode==SDLK_p && press) {
1121 if(!gameover) {
1122 paused ^=1;
1123 if(paused) {
1124 game_draw();
1125 }
1126 }
1127 }
1128 }
1129
input_update(void)1130 static void input_update(void) {
1131 int code, press;
1132
1133 while(key_get(&code,&press)!=0) {
1134 key_handler(code,press);
1135 }
1136 }
1137
game_init(void)1138 static int game_init(void) {
1139 int i;
1140
1141 DEBUG2(printf("running game_init\n");)
1142
1143 level=startinglevel;
1144 blocksleft=0;
1145
1146 lnumframes=0;
1147 gboomdelay=0.1; /* in secs */
1148
1149 paused=0;
1150
1151 board_bottom[0]=board_top[0]+(board_y_size-1)*box_y_size;
1152 board_bottom[1]=board_top[1]+(board_y_size-1)*box_y_size;
1153 board_bottom[2]=board_top[2]+(board_y_size-1)*box_y_size;
1154
1155 board_x_pix_size=board_x_size*box_x_size;
1156 board_y_pix_size=board_y_size*box_y_size;
1157
1158 /* the number equals the time it takes for the piece to fall
1159 from the top to the bottom (in seconds) */
1160 rise_spd = board_y_pix_size/framerate/2.00;
1161 fall_spd = board_y_pix_size/framerate/2.00;
1162
1163 dispframenum=0; /* init displayed frames' counter */
1164 framenum=0;
1165
1166 game_level_init();
1167
1168 player[1].loser=0;
1169 for(i=0;i<players;i++) {
1170 if(!player_init(&(player[i]),i)) {
1171 DEBUG1(printf("game_init failed on player_init\n");)
1172 return 0;
1173 }
1174 }
1175
1176 for(i=0;i<players;i++) {
1177 player_givepiece(&(player[i]));
1178 player_givepiece(&(player[i]));
1179 }
1180
1181 bg_init(level);
1182 DEBUG1(printf("bg_init(level=%d)\n",level));
1183
1184 // printf("time_init\n");
1185
1186 time_init();
1187
1188 // printf("game_i done\n");
1189
1190 return 1;
1191 }
1192
game_deinit(void)1193 static void game_deinit(void) {
1194 int i;
1195
1196 bg_done();
1197
1198 for(i=0;i<players;i++)
1199 player_done(&(player[i]));
1200 }
1201
1202
1203
pieces_anim_update(void)1204 void pieces_anim_update(void) {
1205 int i;
1206
1207 for(i=0;i<P_PER_S;i++) {
1208 p_timeleft[i] -= 1/framerate;
1209 if(p_timeleft[i]<0) {
1210 p_curframe[i]++;
1211 if(p_curframe[i]>=piece[setn*P_PER_S+i].frames) p_curframe[i]=0;
1212 p_timeleft[i] += piece[setn*P_PER_S+i].delay.data[p_curframe[i]];
1213 }
1214 }
1215 }
1216
1217
piece_readinfo(char * name,int * frames,flp_t * delay)1218 static int piece_readinfo(char *name, int *frames, flp_t *delay) {
1219 FILE *f;
1220 int i;
1221
1222 f = file_open(name,"rb");
1223 if(!f) {
1224 fprintf(stderr,"blockrage: cannot open %s in data dir\n",name);
1225 return 0;
1226 }
1227
1228 fscanf(f,"%d",frames);
1229 delay->data=calloc_verify(*frames,sizeof(float));
1230
1231 for(i=0;i<*frames;i++) {
1232 fscanf(f,"%f",&(delay->data[i]));
1233 }
1234
1235 fclose(f);
1236 return 1;
1237 }
1238
screen_setpal(void)1239 void screen_setpal(void) {
1240 #ifdef DIRECT_COLOR
1241 int i;
1242 unsigned rv,gv,bv;
1243
1244 for(i=0;i<256;i++) {
1245 rv=mypal[3*i]>>1;
1246 gv=mypal[3*i+1];
1247 bv=mypal[3*i+2]>>1;
1248 mypalx[i]=bv|(gv<<5)|(rv<<11);
1249 }
1250 #else
1251 gfx_setpalette(mypal,0,256);
1252 #endif
1253 }
1254
screen_init(void)1255 static int screen_init(void) {
1256 int i,j;
1257 char s[30];
1258 bmp_t tempbmp;
1259 int ok;
1260
1261 if(!gfx_init()) return 0;
1262 vscr=malloc_verify(scr_x_size*scr_y_size*sizeof(pix_t));
1263 bground=calloc_verify(board_x_size*box_x_size*board_y_size*box_y_size,sizeof(pix_t));
1264
1265 if(!(mypal=pal_load("game.col"))) return 0;
1266 screen_setpal();
1267
1268 ok = pcx_load(&bmp_menu,"menu.pcx");
1269 ok = ok && pcx_load(&bmp_pmenu,"pmenu.pcx");
1270 ok = ok && pcx_load(&bmp_game[0],"g1p.pcx");
1271 ok = ok && pcx_load(&bmp_game[1],"g2p.pcx");
1272 ok = ok && pcx_load(&bmp_intro,"logo.pcx");
1273
1274 if(!ok) return 0;
1275
1276 piece=calloc_verify(no_of_sets*P_PER_S,sizeof(piece_t));
1277
1278 for(j=0;j<no_of_sets;j++) {
1279 for(i=0;i<P_PER_S;i++) {
1280 sprintf(s,"s%02dp%d.pcx",j,i+1);
1281 if(!pcx_load(&tempbmp,s)) return 0;
1282 piece[j*P_PER_S+i].bmp=tempbmp.data;
1283 piece[j*P_PER_S+i].key_color = tempbmp.key_color;
1284 sprintf(s,"s%02dp%d.txt",j,i+1);
1285 if( !piece_readinfo(s,&(piece[j*P_PER_S+i].frames),
1286 &(piece[j*P_PER_S+i].delay) ) ) return 0;
1287 }
1288 }
1289
1290 /* init frame counters for the pieces */
1291 for(i=0;i<P_PER_S;i++) {
1292 p_curframe[i]=0;
1293 p_timeleft[i]=piece[setn*P_PER_S+i].delay.data[0];
1294 }
1295
1296 /* load particle bitmaps */
1297 for(i=0;i<no_of_particle_frames;i++) {
1298 sprintf(s,"pa%d.pcx",i);
1299 DEBUG2(printf("reading %s\n",s);)
1300 if(!pcx_load(&particle_frame[i].bmp,s)) return 0;
1301 }
1302
1303 if(!font_load("fonth",&(font[FONT_HIGHLIGHTED])))
1304 return 0;
1305
1306 if(!font_load("fontn",&(font[FONT_NORMAL])))
1307 return 0;
1308
1309 return 1;
1310 }
1311
screen_deinit(void)1312 static void screen_deinit(void) {
1313 // free(bmp_piece);
1314 gfx_deinit();
1315 }
1316
init(void)1317 static int init(void) {
1318 /* sound MUST be initialised BEFORE video (because this forks a child
1319 process */
1320 if(!sound_init()) {
1321 /* we can live even without this */
1322 fprintf(stderr,"sound could not be initialised.\n");
1323 }
1324 if(!screen_init()) return 0;
1325 if(!input_init(global_key_hnd)) return 0;
1326 topten_load();
1327 return 1;
1328 }
1329
deinit(void)1330 static void deinit(void) {
1331 DEBUG1(printf("sound_done()...\n"));
1332 sound_done();
1333 DEBUG1(printf("input_deinit()...\n"));
1334 input_deinit();
1335 DEBUG1(printf("screen_deinit()...\n"));
1336 screen_deinit();
1337 }
1338
pieces_checkfnum(void)1339 void pieces_checkfnum(void) {
1340 int i;
1341
1342 for(i=0;i<6;i++) {
1343 p_curframe[i] %= piece[setn*P_PER_S].frames;
1344 p_timeleft[i]=piece[setn*P_PER_S].delay.data[p_curframe[i]];
1345 }
1346 }
1347
game(void)1348 void game(void) {
1349 int i,turns=1;
1350
1351 endgame=0; gameover=0; pausemenu=0;
1352
1353 if(!game_init()) return;
1354
1355 game_draw();
1356
1357 while (!endgame && !quit) {
1358 if(turns<=1) {
1359 if(dbackground && bgnumframes==0) bg_update();
1360 for(i=0;i<players;i++) {
1361 board_draw(&(player[i]),paused,1);
1362
1363 if(!paused) piece_drawnext(&(player[i]),0);
1364 }
1365 dispframenum++;
1366 }
1367 framenum+=turns;
1368 if(!paused) {
1369 for(i=0;i<players;i++) {
1370 player_update(&(player[i]));
1371 }
1372 if(player[0].loser || player[1].loser) game_over();
1373 }
1374
1375 pieces_anim_update();
1376
1377 input_update();
1378
1379 //sound_update();
1380
1381 if(lnumframes>0) lnumframes--;
1382 if(bgnumframes>0) bgnumframes--;
1383
1384 while (!(turns=is_next_turn()))
1385 SDL_Delay(SLEEP_MS);
1386
1387 if(pausemenu) {
1388 pause_menu();
1389 game_draw();
1390 }
1391 }
1392 game_deinit();
1393 if(players==1) {
1394 if(topten_update(player[0].score)) {
1395 topten();
1396 };
1397 }
1398 }
1399
1400
process_args(int argc,char * argv[])1401 static int process_args(int argc,char *argv[]) {
1402 int i=1,j,res;
1403
1404 while (i<argc) {
1405
1406 if(strcmp(argv[i],"+p")==0) {
1407
1408 if(i>=argc) {
1409 fprintf(stderr,"missing value\n");
1410 return 0;
1411 }
1412 i++;
1413 res=sscanf(argv[i],"%d",&players);
1414 if(res==1 && players>=1 && players<=2) {
1415 immedstart=1;
1416 }
1417 else {
1418 fprintf(stderr,"Wrong # of players param %d '%s'\n",i,argv[i]);
1419 return 0;
1420 }
1421 } else
1422 if(strcmp(argv[i],"+d")==0) {
1423
1424 if(i>=argc) {
1425 fprintf(stderr,"missing value\n");
1426 return 0;
1427 }
1428 i++;
1429 res=sscanf(argv[i],"%d",&piecetypes);
1430 if(res!=1 || piecetypes<MIN_PIECE_TYPES || piecetypes>MAX_PIECE_TYPES) {
1431 fprintf(stderr,"Wrong difficulty #(must be %d-%d) param %d '%s'\n",
1432 MIN_PIECE_TYPES,MAX_PIECE_TYPES,i,argv[i]);
1433 return 0;
1434 }
1435 } else
1436 if(strcmp(argv[i],"+l")==0) {
1437
1438 if(i>=argc) {
1439 fprintf(stderr,"missing value\n");
1440 return 0;
1441 }
1442 i++;
1443 res=sscanf(argv[i],"%d",&startinglevel);
1444 if(res==1 && startinglevel>=1 && startinglevel<=10) {
1445 ;
1446 }
1447 else {
1448 fprintf(stderr,"Wrong startinglevel #(must be 1-10) param %d '%s'\n",i,argv[i]);
1449 return 0;
1450 }
1451 } else
1452 if(strcmp(argv[i],"+b")==0) {
1453
1454 if(i>=argc) {
1455 fprintf(stderr,"missing value\n");
1456 return 0;
1457 }
1458 i++;
1459 res=sscanf(argv[i],"%d",&setn);
1460 if(res==1 && setn>=0) {
1461 ;
1462 }
1463 else {
1464 fprintf(stderr,"Wrong blockset #(must be >=0) param %d '%s'\n",i,argv[i]);
1465 return 0;
1466 }
1467 } else
1468 if(strcmp(argv[i],"+k1")==0 || strcmp(argv[i],"+k2")==0) {
1469 j=argv[i][2]-'1'; // player's number ( 0 / 1 )
1470
1471 if(i+4>=argc) {
1472 fprintf(stderr,"missing value\n");
1473 return 0;
1474 }
1475
1476 res=1;
1477 res*=sscanf(argv[++i],"%d",&key_left[j]);
1478 res*=sscanf(argv[++i],"%d",&key_right[j]);
1479 res*=sscanf(argv[++i],"%d",&key_shift_up[j]);
1480 res*=sscanf(argv[++i],"%d",&key_shift_down[j]);
1481 res*=sscanf(argv[++i],"%d",&key_drop[j]);
1482 if(res!=1) {
1483 fprintf(stderr,"Wrong keys for player 1 (param %d \"%s\")\n",i,argv[i]);
1484 return 0;
1485 }
1486
1487 }
1488 else if(strcmp(argv[i],"+data")==0) {
1489
1490 if(i>=argc) return 0;
1491 i++;
1492 if(strlen(argv[i])<=32) {
1493 strcpy(dataset,argv[i]);
1494 } else {
1495 fprintf(stderr,"dataset name too long\n");
1496 return 0;
1497 }
1498 }
1499 else if(strcmp(argv[i],"+a8")==0) {
1500 sound_cfg_8bit();
1501 }
1502 else {
1503 fprintf(stderr,"Wrong param #%d '%s'\n",i,argv[i]);
1504 return 0;
1505 }
1506 i++;
1507 }
1508 return 1;
1509 }
1510
dataconfig_read(void)1511 static int dataconfig_read(void) {
1512 FILE *f;
1513 int i;
1514
1515 f = file_open("dataset.cfg","rb");
1516 if(!f) {
1517 fprintf(stderr,"blockrage: cannot open dataset.cfg in data dir\n");
1518 return 0;
1519 }
1520
1521 fscanf(f,"%d %d",&scr_x_size,&scr_y_size);
1522 fscanf(f,"%d %d",&box_x_size,&box_y_size);
1523
1524 fscanf(f,"%d %d %d",&board_left[0],&board_left[1],&board_left[2]);
1525 fscanf(f,"%d %d %d",&board_top[0],&board_top[1],&board_top[2]);
1526
1527 fscanf(f,"%d %d %d %d %d %d",&nx_t_x0[0],&nx_t_y0[0],
1528 &nx_t_x0[1],&nx_t_y0[1],&nx_t_x0[2],&nx_t_y0[2]);
1529
1530 fscanf(f,"%d %d %d %d %d %d",&nx_x0[0],&nx_y0[0],
1531 &nx_x0[1],&nx_y0[1],&nx_x0[2],&nx_y0[2]);
1532
1533 fscanf(f,"%d %d %d %d",&statx[0],&staty[0],&statx[1],&staty[1]);
1534 fscanf(f,"%d %d %d %d",&statx[2],&staty[2],&statx[3],&staty[3]);
1535 fscanf(f,"%d %d %d %d",&statx[4],&staty[4],&statx[5],&staty[5]);
1536
1537 fscanf(f,"%d %d",&diffx,&diffy);
1538 fscanf(f,"%d %d",&ts_x0,&ts_x1);
1539
1540 fscanf(f,"%d",&no_of_sets);
1541
1542
1543 fscanf(f,"%f %f",&gravity,&maxdefspd);
1544 fscanf(f,"%d",&no_of_particle_frames);
1545
1546 particle_frame=calloc_verify(no_of_particle_frames,sizeof(frame_t));
1547
1548 for(i=0;i<no_of_particle_frames;i++)
1549 fscanf(f,"%f",&(particle_frame[i].delay));
1550
1551 /* printf("%d %d\n%d %d\n%d %d %d\n%d %d %d\n%d %d %d %d %d %d\n",
1552 scr_x_size,scr_y_size,
1553 box_x_size,box_y_size,
1554 board_left[0],board_left[1],board_left[2],
1555 board_top[0],board_top[1],board_top[2],
1556 nx_x0[0],nx_y0[0],nx_x0[1],nx_y0[1],nx_x0[2],nx_y0[2]);*/
1557
1558 fclose(f);
1559
1560 return 1;
1561 }
1562
keys_default(void)1563 static void keys_default(void) {
1564
1565 key_left[0]=SDLK_LEFT;
1566 key_right[0]=SDLK_RIGHT;
1567 key_drop[0]=SDLK_RSHIFT;
1568 key_shift_up[0]=SDLK_UP;
1569 key_shift_down[0]=SDLK_DOWN;
1570 key_left[1]=SDLK_a;
1571 key_right[1]=SDLK_d;
1572 key_drop[1]=SDLK_q;
1573 key_shift_up[1]=SDLK_w;
1574 key_shift_down[1]=SDLK_s;
1575 }
1576
fget_rc_line(FILE * f)1577 static char *fget_rc_line(FILE *f) {
1578 char *buf;
1579 int buf_size;
1580 int c;
1581 int buf_pos;
1582 char *home;
1583 int home_l;
1584
1585 buf_size=64;
1586 buf_pos=0;
1587
1588 buf=malloc_verify(buf_size);
1589
1590 while(1) {
1591 c = fgetc(f);
1592 if(c==EOF || c=='\n') break;
1593
1594 if(!buf_pos && c=='~') {
1595 /* tilde expansion */
1596 home=getenv("HOME");
1597 if(!home) {
1598 fprintf(stderr,"error: HOME environment variable is not set.\n");
1599 return NULL;
1600 }
1601 home_l = strlen(home);
1602 if(home_l+1>buf_size) {
1603 buf_size=home_l+1;
1604 buf=realloc_verify(buf, buf_size);
1605 }
1606 buf_pos=home_l;
1607 strncpy(buf,home,home_l);
1608 } else {
1609
1610 if(buf_pos+1>=buf_size) {
1611 buf_size = 2*buf_size;
1612 buf = realloc(buf, buf_size);
1613 if(!buf) return NULL;
1614 }
1615 buf[buf_pos++]=c;
1616 }
1617 }
1618 buf[buf_pos]=0;
1619
1620 return buf;
1621 }
1622
1623 /*
1624 Load the blockrage.rc file containing paths to Block Rage files
1625 and directories
1626 */
rc_load(char * fname)1627 static int rc_load(char *fname) {
1628 FILE *f;
1629 char *tmp;
1630
1631 if((f=fopen(fname,"rt"))==NULL) return 0;
1632 sys_config_file = fget_rc_line(f);
1633 usr_config_file = fget_rc_line(f);
1634 datadir = fget_rc_line(f);
1635 topten_file = fget_rc_line(f);
1636 tmp = fget_rc_line(f);
1637 if(strlen(tmp)>32) {
1638 fprintf(stderr,"rc_load(): dataset name too long\n");
1639 return 0;
1640 }
1641 if(tmp) {
1642 strcpy(dataset,tmp);
1643 free(tmp);
1644 }
1645 fclose(f);
1646
1647 if(!datadir || !topten_file) return 0;
1648
1649 return 1;
1650 }
1651
1652 /*
1653 Find a rc file in one of the possible locations and load it.
1654 Returns 1 on success, 0 on failure.
1655 */
rc_find_load(void)1656 static int rc_find_load(void) {
1657 int rc_loaded;
1658 char *home;
1659 char *fpathname;
1660 char *frelname = ".blockrage/blockrage.rc";
1661 int home_l;
1662
1663 rc_loaded = rc_load("./blockrage.rc");
1664 if(!rc_loaded) {
1665 home = getenv("HOME");
1666 home_l = strlen(home);
1667 if(home_l > SANE_PATH_LEN) {
1668 fprintf(stderr,"Length of HOME is not sane\n");
1669 home=NULL;
1670 }
1671 if(home) {
1672 fpathname = malloc_verify( home_l + 1 + strlen(frelname) + 1);
1673 strncpy(fpathname,home,home_l);
1674 fpathname[home_l]=0;
1675 cat_with_slash(fpathname,frelname);
1676
1677 rc_loaded=rc_load(fpathname);
1678
1679 free(fpathname);
1680 }
1681 }
1682 if(!rc_loaded) {
1683 rc_loaded=rc_load(SYSCONFDIR "/blockrage.rc");
1684 }
1685 if(!rc_loaded) {
1686 fprintf(stderr,"cannot open any rc file, including %s\n",SYSCONFDIR "/blockrage.rc");
1687 }
1688 return rc_loaded;
1689 }
1690
config_load(char * fname)1691 static int config_load(char *fname) {
1692 FILE *f;
1693 int v1,v2,v3;
1694 int cfg_fs,cfg_ds;
1695
1696 if((f=fopen(fname,"rt"))==NULL) return 0;
1697 fscanf(f,"%d %d %d\n",&v1,&v2,&v3);
1698 if(v1!=VER1 || v2!=VER2 || v3!=VER3) {
1699 fclose(f);
1700 return 0;
1701 }
1702
1703 fscanf(f,"%d%d%d%d%d",&piecetypes,&startinglevel,&setn,&dbackground,
1704 &showintro);
1705 fscanf(f,"%d%d%d%d%d",&key_left[0],&key_right[0],&key_drop[0],
1706 &key_shift_up[0],&key_shift_down[0]);
1707 fscanf(f,"%d%d%d%d%d\n",&key_left[1],&key_right[1],&key_drop[1],
1708 &key_shift_up[1],&key_shift_down[1]);
1709
1710 fscanf(f,"%d%d\n",&cfg_fs,&cfg_ds);
1711 gfx_set_fullscreen(cfg_fs);
1712 gfx_set_doublesize(cfg_ds);
1713 fclose(f);
1714
1715 return 1;
1716 }
1717
1718 /*
1719 Create a directory
1720 */
make_dir(char * fname,int mode)1721 static int make_dir(char *fname, int mode) {
1722 #ifdef _WIN32
1723 return mkdir(fname);
1724 #else
1725 return mkdir(fname,mode);
1726 #endif
1727 }
1728
1729 /*
1730 Create any nonexistent directories in the file's path.
1731 */
make_pdir(char * fname)1732 static int make_pdir(char *fname) {
1733 char *p;
1734 int len;
1735 char *buf;
1736 int res;
1737
1738 printf("make_pdir('%s');\n",fname);
1739
1740 buf = NULL;
1741
1742 p= strrchr(fname,'/');
1743 if(!p) return 0;
1744
1745 len=p-fname;
1746 if(!len) return 0;
1747 buf=malloc_verify(len+1);
1748 strncpy(buf,fname,len);
1749 buf[len]=0;
1750
1751 res = 1;
1752
1753 printf("try mkdir('%s')\n",buf);
1754 if(make_dir(buf,0755)<0 && errno==ENOENT) {
1755 /* Probably neither the parent of this directory exists.
1756 Make it recursively. (Do some memory wasting...) */
1757 if(make_pdir(buf)==0) {
1758 res=0;
1759 } else {
1760 if(make_dir(buf,0755)<0)
1761 res=0;
1762 }
1763 }
1764 free(buf);
1765 return res;
1766 }
1767
config_save(char * fname)1768 static int config_save(char *fname) {
1769 FILE *f;
1770
1771 if((f=fopen(fname,"wt"))==NULL) {
1772 /* Maybe we need to create some directories in which
1773 the file resides */
1774 if(errno==ENOENT) {
1775 if(!make_pdir(fname)) {
1776 return 0;
1777 } else {
1778 f=fopen(fname,"wt");
1779 if(!f) return 0;
1780 }
1781 }
1782 }
1783
1784 fprintf(f,"%d %d %d\n",VER1,VER2,VER3);
1785 fprintf(f,"%d\n%d\n%d\n%d\n%d\n",piecetypes,startinglevel,setn,dbackground,
1786 showintro);
1787 fprintf(f,"%d %d %d %d %d\n",key_left[0],key_right[0],key_drop[0],
1788 key_shift_up[0],key_shift_down[0]);
1789 fprintf(f,"%d %d %d %d %d\n",key_left[1],key_right[1],key_drop[1],
1790 key_shift_up[1],key_shift_down[1]);
1791 fprintf(f,"%d %d\n",gfx_get_fullscreen(),gfx_get_doublesize());
1792 fclose(f);
1793 return 1;
1794 }
1795
config_default(void)1796 static void config_default(void) {
1797 /* default config */
1798
1799 piecetypes=5;
1800 startinglevel=1;
1801 setn=0;
1802 dbackground=1;
1803 showintro=0;
1804
1805 keys_default();
1806 }
1807
1808
main(int argc,char * argv[])1809 int main(int argc, char *argv[]) {
1810
1811 key_quit=SDLK_ESCAPE; /* important! */
1812
1813 framerate=50;
1814 turn_interval_usec=1000000L/framerate;
1815
1816 topscores_default();
1817 sound_cfg_defaults();
1818
1819 strcpy(dataset,"br320");
1820
1821 immedstart=0;
1822
1823 players=1;
1824
1825 board_x_size=5; board_y_size=13;
1826 board_x_center=2;
1827
1828 first_execution=0;
1829
1830 if(!rc_find_load())
1831 return EXIT_FAILURE;
1832
1833 if(!config_load(usr_config_file)) {
1834 first_execution = 1;
1835 printf("Welcome to Block Rage!\n");
1836
1837 if(!config_load(sys_config_file)) {
1838 fprintf(stderr,"No config file found, assuming defaults.\n");
1839 config_default();
1840 }
1841 }
1842
1843 if(!process_args(argc,argv)) return -1;
1844 if(!dataconfig_read()) return -1;
1845
1846 if(setn>=no_of_sets) {
1847 fprintf(stderr,"Wrong blockset # (must be 0-%d)\n",no_of_sets-1);
1848 return EXIT_FAILURE;
1849 }
1850
1851
1852 if(!init())
1853 return EXIT_FAILURE;
1854
1855 DEBUG1(printf("initied.\n"));
1856
1857 quit=0;
1858 if(first_execution || showintro) intro();
1859
1860 DEBUG1(printf("run menu...\n"));
1861
1862 quit=0;
1863 menu();
1864
1865 DEBUG1(printf("deinit...\n"));
1866
1867 deinit();
1868
1869 DEBUG1(printf("save config...\n"));
1870
1871 if(!config_save(usr_config_file)) {
1872 fprintf(stderr,"blockrage error: cannot write user config file '%s'\n",
1873 usr_config_file);
1874 }
1875
1876 DEBUG1(printf("exit...\n"));
1877
1878 return EXIT_SUCCESS;
1879 }
1880