1 /***************************************************************************
2                           breakout.cpp  -  description
3                              -------------------
4     begin                : Thu Apr 20 2000
5     copyright            : (C) 2000 by Michael Speck
6     email                : kulkanie@gmx.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "breakout.h"
19 #include "level.h"
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <sys/timeb.h>
24 #include <string.h>
25 #include <math.h>
26 
27 #define TOARC(d) (((float)d/180)*M_PI)
28 #define TODEG(a) (((float)a/M_PI)*180)
29 #define VLEN(x, y) ( sqrt( (x)*(x) + (y)*(y) ) )
30 
31 #define N   0
32 #define E   1
33 #define S   2
34 #define W   3
35 
36 extern Sdl sdl;
37 extern char *home_dir;
38 extern int fast_quit;
39 extern DrawRgn dr_src, dr_dst;
40 extern Level orig_levels[LEVEL_NUM];
41 #ifdef SOUND
42 extern SndSrv sndsrv;
43 #endif
44 
45 /*
46     egcs 2.91.66 got some problems with pow() in GetTarget
47     so I'll use this function instead
48 */
Square(float x)49 float Square(float x)
50 {
51     return (x * x);
52 }
53 
BreakOut()54 BreakOut::BreakOut()
55 {
56     // misc //
57     file_levels = 0;
58 
59     // load font //
60     font = SFnt_LoadFixed("f_yellow.bmp", 32, 96, 10);
61     font->algn = TA_X_CENTER | TA_Y_CENTER;
62 
63     // load hiscore //
64     hiscore = HiScore::CreateComposite("lbreakout.hscr");
65 
66     // config path //
67     cfg_path = new char[strlen(home_dir) + strlen(".lbreakout.cfg") + 2];
68     sprintf(cfg_path, "%s/.lbreakout.cfg", home_dir);
69 
70     // load init //
71     FILE	*f;
72     if ((f = fopen(cfg_path, "r")) == 0) {
73         strcpy(init_data.name, "Michael");
74         strcpy(init_data.lvl_file, "test.lbr");
75         strcpy(init_data.lvl_path, ".");
76         init_data.k_left = SDLK_LEFT;
77         init_data.k_right = SDLK_RIGHT;
78         init_data.k_fire = SDLK_SPACE;
79         init_data.snd_on = 1;
80         init_data.snd_vol = 8;
81         init_data.key_speed = 0.45;
82         init_data.trp = 1;
83         init_data.anim = 2;
84         init_data.warp = 0;
85         init_data.control = 2;
86         init_data.diff = 1;
87         init_data.startlevel = 0;
88         init_data.lvls_frm_file = 0;
89         init_data.motion_mod = 100;
90         init_data.bkgnd = 1;
91         init_data.fullscreen = 0;
92         init_data.convex = 0;
93         init_data.invert = 0;
94         init_data.rnd_start = 0;
95         init_data.no_exdisp = 0;
96 	}
97 	else {
98 		fread(&init_data, sizeof(InitData), 1, f);
99 		fclose(f);
100 	}
101 
102 	// background //
103 	ss_bkgnd = 0;
104 	ss_picture = 0;
105 
106 	// frame //
107 	ss_fr_luc = SSur_Load("fr_luc.bmp", SDL_HWSURFACE);
108 	ss_fr_left = SSur_Load("fr_l.bmp", SDL_HWSURFACE);
109 	ss_fr_top = SSur_Load("fr_t.bmp", SDL_HWSURFACE);
110 	ss_fr_ruc = SSur_Load("fr_ruc.bmp", SDL_HWSURFACE);
111 	ss_fr_right = SSur_Load("fr_r.bmp", SDL_HWSURFACE);
112 	ss_fr_rlc = SSur_Load("fr_rlc.bmp", SDL_HWSURFACE);
113 
114 	// bricks //
115 	brick_w = 32;
116 	brick_h = 16;
117 	brick_score = 100;
118 	if ((ss_bricks = SSur_Load("bricks.bmp", SDL_HWSURFACE)) == 0)
119 	    exit(1);
120 
121 	// club //
122 	club_cw = 10;
123 	club_ch = 10;
124 	if ((ss_club = SSur_Load("club.bmp", SDL_HWSURFACE)) == 0)
125 	    exit(1);
126 
127 	// ball //
128 	ball_vhmask = 0.363970234; // twenty degrees //
129 	ball_vvmask = 5.67128182; // ten degrees //
130 	ball_rad = 5;
131 	ball_dia = 11;
132 	ball_w = 12;
133 	ball_h = 12;
134 	if ((ss_ball = SSur_Load("ball.bmp", SDL_HWSURFACE)) == 0)
135         exit(1);
136     DL_Init(&ball_list);
137     ball_list.flags = DL_AUTODELETE | DL_NOCALLBACK;
138 
139     // ball points //
140     int i, j, a;
141     a = -45;
142     for (i = 0; i < 4; i++) {
143         for (j = 0; j < 7; j++) {
144             ball_pnts[i][j][0] = (int)ceil(sin(TOARC(a)) * ball_rad);
145             ball_pnts[i][j][1] = (int)ceil(cos(TOARC(a)) * ball_rad);
146             a += 15;
147         }
148         a -= 15;
149     }
150 
151 	// update rects //
152 	rect_num = 0;
153 
154 	// life //
155 	ss_life = SSur_Load("life.bmp", SDL_HWSURFACE);
156 
157 	// score //
158 	ss_numbers = SSur_Load("numbers.bmp", SDL_HWSURFACE);
159 	score_xoff = 44; // substracted from width
160 	score_y = 2; // added to 0
161 	score_lw = 8;
162 	score_h = 10;
163 	score_x = score_w = 0;
164 
165 	// extras //
166 	ss_extras = SSur_Load("extras.bmp", SDL_HWSURFACE);
167 	DL_Init(&extra_list);
168 	extra_list.flags = DL_AUTODELETE | DL_NOCALLBACK;
169 
170 	// shrapnells //
171 	DL_Init(&shrapnell_list);
172 	shrapnell_list.flags = DL_AUTODELETE;
173 	shrapnell_list.cb_destroy = Shr_Free;
174 
175 	// plasma weapon //
176 	ss_weapon = SSur_Load("weapon.bmp", SDL_HWSURFACE);
177 	wpn_y_off = 4;
178 	wpn_w = 14; wpn_h = 15;
179 	wpn_sx_off = 2;
180 	wpn_sy_off = -3;
181 	wpn_fr_num = 6;
182 	wpn_fpms = 0.006;
183 	wpn_cur = 0;
184 
185 	// plasma shots //
186 	ss_shot = SSur_Load("shot.bmp", SDL_HWSURFACE);
187 	DL_Init(&shot_list);
188 	shot_list.flags = DL_AUTODELETE | DL_NOCALLBACK;
189 	shot_w = shot_h = 10;
190 	shot_fr_num = 4;
191 	shot_fpms = 0.01;
192 	shot_v_y = -0.2;
193 	shot_alpha = 64;
194 	max_shots = 6;
195 	shot_time = 0;
196 	shot_delay = 100;
197 
198 	// snapshot //
199 	snapshot = 0;
200 
201 	// cursor //
202 	unsigned char mask = 0;
203 	original_cur = SDL_GetCursor();
204 	empty_cur = SDL_CreateCursor(&mask, &mask, 8, 1, 0, 0);
205 
206 	// difficulty //
207 	// easy
208 	diff[0].lives = 5;
209 	diff[0].max_lives = 9;
210 	diff[0].con_cost = 20000;
211 	diff[0].club_size = 3;
212 	diff[0].club_max_size = 8;
213 	diff[0].score_mod = 1.0;
214 	// medium
215 	diff[1].lives = 4;
216 	diff[1].max_lives = 6;
217 	diff[1].con_cost = 20000;
218 	diff[1].club_size = 2;
219 	diff[1].club_max_size = 6;
220 	diff[1].score_mod = 1.1;
221 	// hard
222 	diff[2].lives = 3;
223 	diff[2].max_lives = 4;
224 	diff[2].con_cost = 30000;
225 	diff[2].club_size = 2;
226 	diff[2].club_max_size = 4;
227 	diff[2].score_mod = 1.2;
228 
229 	// shine //
230 	ss_sh = SSur_Load("shine2.bmp", SDL_HWSURFACE);
231 	sh_cur = 0;
232 	sh_fr = 6;
233 	sh_pms = 0.024;
234 	sh_x = sh_y = 0;
235 
236 	// credit //
237 	cr_time = 4000;
238 	cr_pms = 0.2;
239 
240 	// extra display //
241 	ed_y = 40;
242 	memset(ed_offsets, 0, sizeof(ed_offsets));
243 	ed_offsets[EX_SLIME] = 20;
244 	ed_offsets[EX_METAL] = 40;
245 	ed_offsets[EX_WALL] = 60;
246 	ed_offsets[EX_WEAPON] = 80;
247 	ed_offsets[EX_FAST] = 100;
248 	ed_offsets[EX_SLOW] = 100;
249 
250 #ifdef SOUND
251     snd_ref = Wave_Load("reflect.wav");
252     snd_boom = Wave_Load("exp.wav");
253     snd_lup = Wave_Load("gainlife.wav");
254     snd_ldown = Wave_Load("looselife.wav");
255     snd_exp = Wave_Load("expand.wav");
256     snd_shr = Wave_Load("shrink.wav");
257     snd_sco = Wave_Load("score.wav");
258     snd_fre = Wave_Load("freeze.wav");
259     snd_shot = Wave_Load("shot.wav");
260     snd_sli = Wave_Load("slime.wav");
261     snd_wea = Wave_Load("weapon.wav");
262     snd_met = Wave_Load("metal.wav");
263     snd_wal = Wave_Load("wall.wav");
264     snd_damn1 = Wave_Load("damn.wav");
265     snd_damn2 = Wave_Load("damnit.wav");
266     snd_good = Wave_Load("verygood.wav");
267     snd_exc = Wave_Load("excellent.wav");
268     snd_click = Wave_Load("click.wav");
269     snd_wontgiveup = Wave_Load("wontgiveup.wav");
270     snd_speedup = Wave_Load("speedup.wav");
271     snd_speeddown = Wave_Load("speeddown.wav");
272 #endif
273 }
274 
~BreakOut()275 BreakOut::~BreakOut()
276 {
277     // save hiscore //
278     if (hiscore) {
279         hiscore->Save();
280         delete hiscore;
281     }
282 
283     //save init //
284     FILE	*f = fopen(cfg_path, "w");
285     fwrite(&init_data, sizeof(InitData), 1, f);
286     fclose(f);
287     if (cfg_path) delete cfg_path;
288 
289     if (ss_bricks) SDL_FreeSurface(ss_bricks);
290     if (ss_ball) SDL_FreeSurface(ss_ball);
291     if (ss_club) SDL_FreeSurface(ss_club);
292     if (ss_bkgnd) SDL_FreeSurface(ss_bkgnd);
293     if (ss_picture) SDL_FreeSurface(ss_picture);
294     if (ss_life) SDL_FreeSurface(ss_life);
295     if (ss_numbers) SDL_FreeSurface(ss_numbers);
296     if (ss_extras) SDL_FreeSurface(ss_extras);
297     if (ss_shot) SDL_FreeSurface(ss_shot);
298     if (ss_weapon) SDL_FreeSurface(ss_weapon);
299     if (font) SFnt_Free(font);
300     if (ss_fr_luc) SDL_FreeSurface(ss_fr_luc);
301     if (ss_fr_left) SDL_FreeSurface(ss_fr_left);
302     if (ss_fr_top) SDL_FreeSurface(ss_fr_top);
303     if (ss_fr_ruc) SDL_FreeSurface(ss_fr_ruc);
304     if (ss_fr_right) SDL_FreeSurface(ss_fr_right);
305     if (ss_fr_rlc) SDL_FreeSurface(ss_fr_rlc);
306     if (ss_sh) SDL_FreeSurface(ss_sh);
307 
308 #ifdef SOUND
309     if (snd_ref) Wave_Free(snd_ref);
310     if (snd_boom) Wave_Free(snd_boom);
311     if (snd_lup) Wave_Free(snd_lup);
312     if (snd_ldown) Wave_Free(snd_ldown);
313     if (snd_exp) Wave_Free(snd_exp);
314     if (snd_shr) Wave_Free(snd_shr);
315     if (snd_sco) Wave_Free(snd_sco);
316     if (snd_fre) Wave_Free(snd_fre);
317     if (snd_shot) Wave_Free(snd_shot);
318     if (snd_sli) Wave_Free(snd_sli);
319     if (snd_wea) Wave_Free(snd_wea);
320     if (snd_met) Wave_Free(snd_met);
321     if (snd_wal) Wave_Free(snd_wal);
322     if (snd_damn1) Wave_Free(snd_damn1);
323     if (snd_damn2) Wave_Free(snd_damn2);
324     if (snd_good) Wave_Free(snd_good);
325     if (snd_exc) Wave_Free(snd_exc);
326     if (snd_click) Wave_Free(snd_click);
327     if (snd_wontgiveup) Wave_Free(snd_wontgiveup);
328     if (snd_speedup) Wave_Free(snd_speedup);
329     if (snd_speeddown) Wave_Free(snd_speeddown);
330 #endif
331 
332     DL_Clear(&ball_list);
333     DL_Clear(&extra_list);
334     DL_Clear(&shrapnell_list);
335     DL_Clear(&shot_list);
336 
337     // cursor //
338     SDL_FreeCursor(empty_cur);
339 }
340 
Setup()341 InitData* BreakOut::Setup()
342 {
343     return &init_data;
344 }
345 
Run()346 int BreakOut::Run()
347 {
348     int         leave = 0, ask_for_con = 0, restart = 0, disappear = 0, appear = 0, con_bought = 0;
349     SDL_Event   event;
350     int         call, last_call;
351     int         ms = 0;
352     float       ca = 0;
353     DL_Entry    *e;
354 
355     // motion modifier if using relative mouse motion //
356     motion_mod = (float)init_data.motion_mod / 100;
357 
358     // check if path of own levels is ok //
359     if (init_data.lvls_frm_file)
360         Levels_LoadFromFile();
361     else
362         UseOrigLevels();
363 
364     // init game //
365     InitGame();
366 
367     // cursor ? //
368     if (init_data.warp || init_data.control == 0 || (!init_data.warp && fullscreen))
369     	SDL_SetCursor(empty_cur);
370 
371     SDL_UNDIM();
372 
373     // set time //
374     call = SDL_GetTicks();
375     last_call = call;
376 
377     // shine //
378     Sh_New();
379 
380     // main loop //
381     while (1) {
382         // clear input states //
383         while (SDL_PollEvent(&event));
384 
385         // game loop //
386         while (!fast_quit && !leave && !new_level && !restart) {
387             // reset //
388             mouse_moved = 0;
389             // check key/mousestate //
390             if (SDL_PollEvent(&event)) {
391                 switch (event.type) {
392                     case SDL_QUIT:
393                         fast_quit = 1;
394                         break;
395                     case SDL_MOUSEBUTTONDOWN:
396                         buttonstate[event.button.button] = 1;
397                         break;
398                     case SDL_MOUSEBUTTONUP:
399                         buttonstate[event.button.button] = 0;
400                         fire_shot = 0;
401                         shot_time = 0;
402                         break;
403                     case SDL_KEYDOWN:
404                         keystate[event.key.keysym.sym] = 1;
405                         break;
406                     case SDL_KEYUP:
407                         keystate[event.key.keysym.sym] = 0;
408                         if (!keystate[init_data.k_fire]) {
409                             fire_shot = 0;
410                             shot_time = 0;
411                         }
412                         // screenshot
413                         if (event.key.keysym.sym == SDLK_TAB)
414                             SnapShot();
415                         // quit
416                         if (event.key.keysym.sym == SDLK_ESCAPE) {
417                             if (ConfirmQuit())
418                                 leave = 1;
419                             else
420                                 last_call = SDL_GetTicks();
421                         }
422                         // restart
423                         if (event.key.keysym.sym == SDLK_r)
424                             disappear = 1;
425                         // turn on/off sound
426 #ifdef SOUND
427                         if (event.key.keysym.sym == SDLK_s) {
428                             init_data.snd_on = !init_data.snd_on;
429                     		SndSrv_SetActive(init_data.snd_on);
430                     	}
431 #endif
432                     	// turn on/off transparancy
433                         if (event.key.keysym.sym == SDLK_t)
434                             init_data.trp = !init_data.trp;
435                         // animation level
436                         if (event.key.keysym.sym == SDLK_a) {
437                             init_data.anim++;
438                             if (init_data.anim >= 3)
439                                 init_data.anim = 0;
440                         }
441                         // set fullscreen
442                         if (event.key.keysym.sym == SDLK_f) {
443                             fullscreen = !fullscreen;
444                             if (fullscreen) {
445                                 Sdl_SetVideoMode(lev_w * brick_w, lev_h * brick_h, 16, SDL_HWSURFACE | SDL_FULLSCREEN);
446                                 SDL_SetCursor(empty_cur);
447                             }
448                             else {
449                                 Sdl_SetVideoMode(lev_w * brick_w, lev_h * brick_h, 16, SDL_HWSURFACE);
450                                 if (!init_data.warp && !init_data.control == 0)
451                                 	SDL_SetCursor(original_cur);
452                             }
453                             DR_SETFULLDST(sdl.scr);
454                             DR_SETFULLSRC(ss_bkgnd);
455                             SSur_Blit();
456                             Sdl_FullUpdate();
457                             last_call = SDL_GetTicks();
458                         }
459                         // pause
460                         if (event.key.keysym.sym == SDLK_p) {
461                             Pause();
462                             last_call = SDL_GetTicks();
463                         }
464                         break;
465                     }
466                 if (!disappear)
467                     switch (event.type) {
468                         case SDL_KEYDOWN:
469                             if (cur_extras[EX_WEAPON] && event.key.keysym.sym == init_data.k_fire) {
470                                 fire_shot = 1;
471                                 shot_time = shot_delay;
472                             }
473                             break;
474                         case SDL_MOUSEBUTTONDOWN:
475                             if (cur_extras[EX_WEAPON]) {
476                                 fire_shot = 1;
477                                 shot_time = shot_delay;
478                             }
479                             break;
480                         case SDL_MOUSEMOTION:
481                             mouse_moved = 1;
482                             if (init_data.warp) {
483                                 mx = (int)(motion_mod * event.motion.xrel);
484                                 if (init_data.invert)
485                                     mx = -mx;
486                             }
487                             else
488                                 mx = event.motion.x;
489                             break;
490                     }
491             }
492             if (leave)
493                 break;
494 
495             // hide //
496             Life_Hide();
497             Club_Hide();
498             Balls_Hide();
499             Shots_Hide();
500             Wall_Hide();
501             Extras_Hide();
502             Shr_Hide();
503             Score_Hide();
504             ExDisp_Hide();
505             if ( init_data.anim )
506                 Sh_Hide();
507             Credit_Hide();
508 
509             // compute time //
510             call = SDL_GetTicks();
511             ms = call - last_call;
512             last_call = call;
513             if (ms <= 0) {
514                 ms = 1;
515                 SDL_Delay(1);
516             }
517 
518             // update //
519             if (!disappear && !appear) {
520                 Club_Update(ms);
521                 if (Balls_Update(ms))
522                     disappear = 1;
523             }
524             Shots_Update(ms);
525             Wall_Update(ms);
526             Wpn_Update(ms);
527             Extras_Update(ms);
528             Shr_Update(ms);
529             Score_Update(ms);
530             if ( init_data.anim )
531                 Sh_Update(ms);
532             Credit_Update(ms);
533 
534             // update club alpha //
535             if (appear) {
536                 ca -= 0.5 * ms;
537                 if (ca < 0) {
538                     restart = 1;
539                     appear = 0;
540                     ca = 0;
541                 }
542             }
543             else
544                 if (disappear) {
545                     ca += 0.5 * ms;
546                     if (ca > 255) {
547                         appear = 1;
548                         disappear = 0;
549                         ca = 255;
550                         Club_Reset();
551                         Balls_Reset();
552                         cur_extras[EX_SLIME] =
553                         cur_extras[EX_METAL] =
554                         cur_extras[EX_WEAPON] =
555                         cur_extras[EX_FROZEN] =
556                         cur_extras[EX_SLOW] =
557                         cur_extras[EX_FAST] = 0;
558                     }
559                 }
560 
561             // increase and adjust ball's speed //
562             if ( !cur_extras[EX_SLOW] && !cur_extras[EX_FAST] ) {
563 
564                 if (ball_v < ball_vm && !(ball_list.counter == 1 && ((Ball*)(ball_list.head.next->data))->attached)) {
565                     ball_v += ball_v_add * ms;
566                     e = ball_list.head.next;
567                     while (e != &ball_list.tail) {
568                         Ball_AdjustSpeed((Ball*)e->data, ball_v);
569                         e = e->next;
570                     }
571                 }
572 
573             }
574 
575             // update lifes //
576             if (!con_bought && ( (restart && player.lives-- == 0) || (disappear && player.lives == 0) ) )
577                ask_for_con = 1;
578 
579             // show //
580             Life_Show();
581             Shots_Show();
582             if (disappear || appear) {
583                 Balls_AlphaShow(ca);
584                 Club_AlphaShow(ca);
585             }
586             else {
587                 Balls_Show();
588                 Club_Show();
589             }
590             Wall_Show();
591             Extras_Show();
592             if ( init_data.anim )
593                 Sh_Show();
594             Score_Show();
595             ExDisp_Show();
596             Shr_Show();
597             Credit_Show();
598 
599             // refresh //
600             Refresh();
601 
602             if (ask_for_con) {
603                 if (BuyContinue()) {
604                     last_call = SDL_GetTicks();
605                     con_bought = 1;
606                 }
607                 else
608                     leave = 1;
609                 ask_for_con = 0;
610             }
611         }
612         if (!fast_quit && !leave) {
613             if (new_level) {
614                 if (++level >= lev_num)
615                     break;
616                 SDL_DIM();
617                 InitLevel(level);
618                 SDL_UNDIM();
619             }
620             else {
621 #ifdef SOUND
622     SndSrv_Play(snd_ldown, 0);
623 #endif
624                	// reset event stuff //
625             	mx = 0;
626             	memset(buttonstate, 0, sizeof(buttonstate));
627             	memset(keystate, 0, sizeof(keystate));
628             	ball_v = levels[level].v;
629             	con_bought = 0;
630             }
631             new_level = 0;
632             restart = 0;
633         }
634         else
635             break;
636     }
637 
638     fullscreen = 0;
639 
640     // cursor ? //
641   	SDL_SetCursor(original_cur);
642 
643     // delete file levels //
644     if (levels == file_levels)
645         Levels_Delete();
646 
647     // modify score //
648     player.score = (int)(player.score * diff[init_data.diff].score_mod);
649 
650     SDL_DIM();
651 
652     if (!fast_quit)
653         GameOver();
654 
655     Sdl_SetVideoMode(450, 325, 16, SDL_HWSURFACE);
656 
657     return hiscore->CheckEntry(&player);
658 }
659 
InitGame()660 void BreakOut::InitGame()
661 {
662     // reset player //
663     player.score = 0;
664     cur_score = 0;
665     player.lives = diff[init_data.diff].lives;
666     strcpy(player.name, init_data.name);
667     player.level = init_data.startlevel;
668 
669     // fullscreen ? //
670     fullscreen = init_data.fullscreen;
671 
672     // int first level //
673     lev_w = lev_h = 0;
674     level = init_data.startlevel;
675     InitLevel(level);
676 
677 }
678 
InitLevel(int l)679 void BreakOut::InitLevel(int l)
680 {
681     char    str[256];
682     SDL_Surface *pic;
683     int     flgs = SDL_HWSURFACE;
684     int     old_w = lev_w, old_h = lev_h;
685 
686     // load map //
687      LoadLevel(l);
688 
689     // set new resolution //
690     if (lev_w != old_w || lev_h != old_h) {
691         if (fullscreen)
692             flgs = flgs | SDL_FULLSCREEN;
693         Sdl_SetVideoMode(lev_w * brick_w, lev_h * brick_h, 16, flgs);
694     }
695 
696     // reset background //
697     if (ss_bkgnd) SDL_FreeSurface(ss_bkgnd);
698     ss_bkgnd = SSur_Create(sdl.scr->w, sdl.scr->h, SDL_HWSURFACE);
699     SDL_SetColorKey(ss_bkgnd, 0, 0);
700     if (ss_picture) SDL_FreeSurface(ss_picture);
701     ss_picture = SSur_Create(sdl.scr->w, sdl.scr->h, SDL_HWSURFACE);
702     SDL_SetColorKey(ss_picture, 0, 0);
703     if (init_data.bkgnd) {
704         sprintf(str, "bkgnd%i.bmp", level / 10);
705       	if ((pic = SSur_Load(str, SDL_HWSURFACE)) == 0)
706        	    if ((pic = SSur_Load("bkgnd0.bmp", SDL_HWSURFACE)) == 0) {
707                 pic = SSur_Create(sdl.scr->w, sdl.scr->h, SDL_HWSURFACE);
708                 DR_SETFULLDST(pic);
709                 SSur_Fill(0x0);
710             }
711         SDL_SetColorKey(pic, 0, 0);
712         DR_SETDST(ss_picture, (ss_picture->w - pic->w) / 2, (ss_picture->h - pic->h) / 2, pic->w, pic->h);
713         DR_SETFULLSRC(pic);
714         SSur_Blit();
715         DR_SETFULLDST(ss_bkgnd);
716         DR_SETFULLSRC(ss_picture);
717         SSur_Blit();
718         SDL_FreeSurface(pic);
719     }
720     else {
721         DR_SETFULLDST(ss_bkgnd);
722         SSur_Fill(0x0);
723     }
724 
725     // draw frame to background //
726     int i, j;
727     for (i = 1; i < lev_w - 1; i++)
728         for (j = 1; j < lev_h - 1; j++)
729             if (lev_map[i][j].type == MAP_WALL) {
730                 DR_SETDST(ss_bkgnd, i * brick_w, j * brick_h, brick_w, brick_h);
731                 DR_SETSRC(ss_bricks, lev_map[i][j].id * brick_w, 0);
732                 SSur_Blit();
733             }
734     // left upper corner
735     DR_SETDST(ss_bkgnd, 0, 0, ss_fr_luc->w, ss_fr_luc->h);
736     DR_SETFULLSRC(ss_fr_luc);
737     SSur_Blit();
738     // life lights //
739     for (j = 11; j < lev_h; j++) {
740          DR_SETDST(ss_bkgnd, 0, j * brick_h, brick_w, brick_h);
741          DR_SETFULLSRC(ss_fr_left);
742          SSur_Blit();
743     }
744     // top tiles
745     for (i = 4; i < lev_w - 4; i++) {
746          DR_SETDST(ss_bkgnd, i * brick_w, 0, brick_w, brick_h);
747          DR_SETFULLSRC(ss_fr_top);
748          SSur_Blit();
749     }
750     // right upper corner
751     DR_SETDST(ss_bkgnd, (lev_w - 4) * brick_w, 0, ss_fr_ruc->w, ss_fr_ruc->h);
752     DR_SETFULLSRC(ss_fr_ruc);
753     SSur_Blit();
754     // right tiles
755     for (j = 1; j < lev_h - 4; j++) {
756          DR_SETDST(ss_bkgnd, (lev_w - 1) * brick_w, j * brick_h, brick_w, brick_h);
757          DR_SETFULLSRC(ss_fr_right);
758          SSur_Blit();
759     }
760     // right lower corner
761     DR_SETDST(ss_bkgnd, (lev_w - 1) * brick_w, (lev_h - 4) * brick_h, ss_fr_rlc->w, ss_fr_rlc->h);
762     DR_SETFULLSRC(ss_fr_rlc);
763     SSur_Blit();
764     // draw lifes //
765     max_lives = lev_h - 11;
766     if (diff[init_data.diff].max_lives <= max_lives)
767         life_y = (lev_h - diff[init_data.diff].max_lives) * brick_h;
768     else
769         life_y = (lev_h - max_lives) * brick_h;
770     for (j = life_y; j < sdl.scr->h; j += brick_h) {
771          DR_SETDST(ss_bkgnd, 0, j, brick_w, brick_h);
772          DR_SETSRC(ss_life, 0, 0);
773          SSur_Blit();
774     }
775 
776     // draw bricks to background //
777     for (int i = 0; i < lev_w; i++)
778         for (int j = 0; j < lev_h; j++)
779             if (lev_map[i][j].type > MAP_WALL) {
780                 DR_SETDST(ss_bkgnd, i * brick_w, j * brick_h, brick_w, brick_h);
781                 DR_SETSRC(ss_bricks, lev_map[i][j].id * brick_w, 0);
782                 SSur_Blit();
783             }
784 
785     // copy background to screen //
786     DR_SETFULLDST(sdl.scr);
787     DR_SETFULLSRC(ss_bkgnd);
788     SSur_Blit();
789 
790     // reset //
791     Club_Reset();
792     Balls_Reset();
793     DL_Clear(&extra_list);
794     DL_Clear(&shrapnell_list);
795     DL_Clear(&shot_list);
796     new_level = 0;
797     memset(cur_extras, 0, sizeof(cur_extras));
798 	player.level = level;
799 
800 	// reset event stuff //
801 	mx = 0;
802 	memset(buttonstate, 0, sizeof(buttonstate));
803 	memset(keystate, 0, sizeof(keystate));
804 
805 	// credit init //
806 	Credit_Init();
807 }
808 
LoadLevel(int l)809 void BreakOut::LoadLevel(int l)
810 {
811     int i, j;
812     strcpy(lev_author, levels[l].author);
813     strcpy(lev_name, levels[l].name);
814     lev_w = levels[l].w + 2;
815     lev_h = levels[l].h + 2;
816     ball_v = levels[l].v;
817     ball_v_add = levels[l].v_pms;
818     ball_vm = levels[l].vm;
819 
820     // build walls //
821     for (i = 0; i < lev_w; i++) {
822         lev_map[i][0].type = MAP_WALL;
823         lev_map[i][0].id = 0;
824         lev_map[i][lev_h - 1].type = MAP_EMPTY;
825         lev_map[i][lev_h - 1].id = -1;
826     }
827     for (j = 0; j < lev_h; j++) {
828         lev_map[0][j].type = MAP_WALL;
829         lev_map[lev_w - 1][j].type = MAP_WALL;
830         lev_map[0][j].id = 0;
831         lev_map[lev_w - 1][j].id = 0;
832     }
833 
834     // load map //
835     for (i = 0; i < levels[l].w; i++)
836         for (j = 0; j < levels[l].h; j++) {
837             // create map //
838             switch (levels[l].map[j * levels[l].w + i]) {
839                 case '#':
840                     // wall //
841                     lev_map[i + 1][j + 1].type = MAP_WALL;
842                     lev_map[i + 1][j + 1].id = 0;
843                     lev_map[i + 1][j + 1].score = brick_score;
844                     break;
845                 case 'a':
846                 case 'b':
847                 case 'c':
848                 case 'd':
849                 case 'e':
850                 case 'f':
851                 case 'g':
852                 case 'h':
853                 case 'i':
854                     // brick //
855                     lev_map[i + 1][j + 1].type = MAP_BRICK;
856                     lev_map[i + 1][j + 1].id = levels[l].map[j * levels[l].w + i] - 96;
857                     lev_map[i + 1][j + 1].dur = 1;
858                     // some bricks need more shots //
859                     if (lev_map[i + 1][j + 1].id < 4)
860                         lev_map[i + 1][j + 1].dur = lev_map[i + 1][j + 1].id + 1;
861                     lev_map[i + 1][j + 1].score = brick_score * lev_map[i + 1][j + 1].dur;
862                     break;
863                 default:
864                     // empty //
865                     lev_map[i + 1][j + 1].type = MAP_EMPTY;
866                     lev_map[i + 1][j + 1].id = -1;
867                     break;
868             }
869             // create extras //
870             switch (levels[l].extras[j * levels[l].w + i]) {
871                 case '0':
872                     lev_map[i + 1][j + 1].extra = EX_SCORE200;
873                     break;
874                 case '1':
875                     lev_map[i + 1][j + 1].extra = EX_SCORE500;
876                     break;
877                 case '2':
878                     lev_map[i + 1][j + 1].extra = EX_SCORE1000;
879                     break;
880                 case '3':
881                     lev_map[i + 1][j + 1].extra = EX_SCORE2000;
882                     break;
883                 case '4':
884                     lev_map[i + 1][j + 1].extra = EX_SCORE5000;
885                     break;
886                 case '5':
887                     lev_map[i + 1][j + 1].extra = EX_SCORE10000;
888                     break;
889                 case '+':
890                     lev_map[i + 1][j + 1].extra = EX_LENGTHEN;
891                     break;
892                 case '-':
893                     lev_map[i + 1][j + 1].extra = EX_SHORTEN;
894                     break;
895                 case 'l':
896                     lev_map[i + 1][j + 1].extra = EX_LIFE;
897                     break;
898                 case 's':
899                     lev_map[i + 1][j + 1].extra = EX_SLIME;
900                     break;
901                 case 'm':
902                     lev_map[i + 1][j + 1].extra = EX_METAL;
903                     break;
904                 case 'b':
905                     lev_map[i + 1][j + 1].extra = EX_BALL;
906                     break;
907                 case 'w':
908                     lev_map[i + 1][j + 1].extra = EX_WALL;
909                     break;
910                 case 'f':
911                     lev_map[i + 1][j + 1].extra = EX_FROZEN;
912                     break;
913                 case 'p':
914                     lev_map[i + 1][j + 1].extra = EX_WEAPON;
915                     break;
916                 case '?':
917                     lev_map[i + 1][j + 1].extra = EX_RANDOM;
918                     break;
919                 case '>':
920                     lev_map[i + 1][j + 1].extra = EX_FAST;
921                     break;
922                 case '<':
923                     lev_map[i + 1][j + 1].extra = EX_SLOW;
924                     break;
925                 default:
926                     lev_map[i + 1][j + 1].extra = EX_NONE;
927                     break;
928             }
929         }
930 
931     // count bricks //
932     lev_bricks = 0;
933     for (i = 1; i < lev_w - 1; i++)
934         for (j = 1; j < lev_h - 1; j++)
935             if (lev_map[i][j].type == MAP_BRICK)
936                 lev_bricks++;
937 }
938 
GetHiScore()939 HiScore* BreakOut::GetHiScore()
940 {
941     return hiscore;
942 }
943 
BuyContinue()944 int BreakOut::BuyContinue()
945 {
946     if (player.score < diff[init_data.diff].con_cost) return 0;
947 
948     if (init_data.warp && init_data.control != 0)
949     	SDL_SetCursor(original_cur);
950 
951     SDL_Surface *buffer = SSur_Create(sdl.scr->w, sdl.scr->h, SDL_HWSURFACE);
952     SDL_SetColorKey(buffer, 0, 0);
953 
954     DR_SETFULLDST(buffer);
955     DR_SETFULLSRC(sdl.scr);
956     SSur_Blit();
957     DR_SETFULLDST(sdl.scr);
958     SSur_Fill(0x0);
959     DR_SETFULLSRC(buffer);
960     SSur_AlphaBlit(128);
961     char    str[64];
962     sprintf(str, "Continue? y/n");
963     SFnt_Write(font, sdl.scr, sdl.scr->w / 2, sdl.scr->h / 2, str, 0);
964     sprintf(str, "(costs %i points)", diff[init_data.diff].con_cost);
965     SFnt_Write(font, sdl.scr, sdl.scr->w / 2, sdl.scr->h / 2 + font->lh, str, 0);
966     Sdl_FullUpdate();
967 
968     SDL_Event e;
969     int go_on = 1;
970     int ret = 0;
971     while (go_on && !fast_quit) {
972         SDL_WaitEvent(&e);
973         if (e.type == SDL_QUIT) {
974             fast_quit = 1;
975             break;
976         }
977         if (e.type == SDL_KEYUP)
978             switch (e.key.keysym.sym) {
979                 case SDLK_y:
980                     player.lives = 0;
981                     player.score -= diff[init_data.diff].con_cost;
982                     cur_score = player.score;
983                     DR_SETFULLDST(sdl.scr);
984                     DR_SETFULLSRC(buffer);
985                     SSur_Blit();
986                     Sdl_FullUpdate();
987                     go_on = 0;
988                     ret = 1;
989                     break;
990                 case SDLK_n:
991                     go_on = 0;
992                     ret = 0;
993                     break;
994                 default:
995                     break;
996             }
997     }
998     SDL_FreeSurface(buffer);
999     if (init_data.warp && init_data.control != 0)
1000        	SDL_SetCursor(empty_cur);
1001 #ifdef SOUND
1002     SndSrv_Play(snd_click, 0);
1003     if (ret)
1004         SndSrv_Play(snd_wontgiveup, 1);
1005 #endif
1006     return ret;
1007 }
1008 
ConfirmQuit()1009 int BreakOut::ConfirmQuit()
1010 {
1011 #ifdef SOUND
1012     SndSrv_Play(snd_click, 0);
1013 #endif
1014 
1015     SDL_Surface *buffer = SSur_Create(sdl.scr->w, sdl.scr->h, SDL_HWSURFACE);
1016     SDL_SetColorKey(buffer, 0, 0);
1017 
1018     if (init_data.warp && init_data.control != 0)
1019     	SDL_SetCursor(original_cur);
1020 
1021     DR_SETFULLDST(buffer);
1022     DR_SETFULLSRC(sdl.scr);
1023     SSur_Blit();
1024     DR_SETFULLDST(sdl.scr);
1025     SSur_Fill(0x0);
1026     DR_SETFULLSRC(buffer);
1027     SSur_AlphaBlit(128);
1028     char    str[64];
1029     sprintf(str, "Exit game? y/n");
1030     SFnt_Write(font, sdl.scr, sdl.scr->w / 2, sdl.scr->h / 2, str, 0);
1031     Sdl_FullUpdate();
1032 
1033     SDL_Event e;
1034     int go_on = 1;
1035     int ret = 0;
1036     while (go_on && !fast_quit) {
1037         SDL_WaitEvent(&e);
1038         if (e.type == SDL_QUIT) {
1039             fast_quit = 1;
1040             break;
1041         }
1042         if (e.type == SDL_KEYUP)
1043             switch (e.key.keysym.sym) {
1044                 case SDLK_ESCAPE:
1045                 case SDLK_y:
1046                     go_on = 0;
1047                     ret = 1;
1048                     break;
1049                 case SDLK_n:
1050                     DR_SETFULLDST(sdl.scr);
1051                     DR_SETFULLSRC(buffer);
1052                     SSur_Blit();
1053                     Sdl_FullUpdate();
1054                     go_on = 0;
1055                     ret = 0;
1056                 default:
1057                     break;
1058             }
1059     }
1060     SDL_FreeSurface(buffer);
1061 
1062     if (init_data.warp && init_data.control != 0)
1063     	SDL_SetCursor(empty_cur);
1064 
1065 #ifdef SOUND
1066     SndSrv_Play(snd_click, 0);
1067 #endif
1068     return ret;
1069 }
1070 
GameOver()1071 void BreakOut::GameOver()
1072 {
1073     char    str[64];
1074     sprintf(str, "Game Over");
1075     SFnt_Write(font, sdl.scr, sdl.scr->w / 2, sdl.scr->h / 2, str, 0);
1076     sprintf(str, "Your score: %i\n", player.score);
1077     SFnt_Write(font, sdl.scr, sdl.scr->w / 2, sdl.scr->h / 2 + font->lh, str, 0);
1078     Sdl_FullUpdate();
1079 
1080 #ifdef SOUND
1081     if (rand() % 2)
1082         SndSrv_Play(snd_damn1, 1);
1083     else
1084         SndSrv_Play(snd_damn2, 1);
1085 #endif
1086 
1087     Sdl_WaitForClick();
1088 }
1089 
1090 // club //
Club_Reset()1091 void BreakOut::Club_Reset()
1092 {
1093     club.len = diff[init_data.diff].club_size;
1094     club.max_len = diff[init_data.diff].club_max_size;
1095     club.w = (club.len + 2) * club_cw;
1096     club.h = club_ch;
1097     club.y = (lev_h - 2) * brick_h;
1098     club.x = ((lev_w * brick_w) - club.w) / 2; // centered //
1099     club.cur_x = club.x;
1100     club.friction = 0.3;
1101     club.time = 0;
1102 }
1103 
Club_Hide()1104 void BreakOut::Club_Hide()
1105 {
1106     int h = club.h;
1107     int y = club.y;
1108     if (cur_extras[EX_WEAPON]) {
1109         y -= wpn_y_off;
1110         h = wpn_h;
1111     }
1112     DR_SETDST(sdl.scr, club.x, y, club.w, h);
1113     DR_SETSRC(ss_bkgnd, club.x, y);
1114     SSur_Blit();
1115     AddRefreshRect(club.x, y, club.w, h);
1116 }
1117 
Club_Update(int ms)1118 void BreakOut::Club_Update(int ms)
1119 {
1120     // old position //
1121     int old_x = club.x;
1122     int off_x;
1123     int moved = 0;
1124     SDL_Event event;
1125     int n;
1126 
1127     if (!init_data.warp)
1128         SDL_GetMouseState(&mx, 0);
1129 
1130     if (cur_extras[EX_FROZEN]) {
1131         club.v_x = 0;
1132         if (init_data.warp) {
1133             // reset the mouse pointer //
1134             SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
1135             SDL_WarpMouse(sdl.scr->w >> 1, sdl.scr->h >> 1);
1136             SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
1137         }
1138         return;
1139     }
1140 
1141     if (mouse_moved && init_data.control != 0) {
1142         if (init_data.warp)
1143             club.cur_x += mx;
1144         else
1145             club.cur_x = mx;
1146         club.time = 200;
1147         moved = 1;
1148     }
1149     if (keystate[init_data.k_left] && init_data.control != 1) {
1150         club.cur_x -= init_data.key_speed * ms;
1151         club.time = 0;
1152         moved = 1;
1153     }
1154     if (keystate[init_data.k_right] && init_data.control != 1) {
1155         club.cur_x += init_data.key_speed * ms;
1156         club.time = 0;
1157         moved = 1;
1158     }
1159 
1160     if (club.time > 0) {
1161         club.time -= ms;
1162         if (club.time < 0) {
1163             club.time = 0;
1164             club.v_x = 0.0;
1165         }
1166     }
1167     else
1168         club.v_x = 0.0;
1169 
1170     if (moved) {
1171         //check range//
1172         if (club.cur_x < brick_w)
1173             club.cur_x = brick_w;
1174         if (club.cur_x + club.w >= sdl.scr->w - brick_w)
1175             club.cur_x = sdl.scr->w - brick_w - club.w;
1176 
1177         club.x = (int)club.cur_x;
1178 
1179         off_x = club.x - old_x;
1180 
1181         // speed //
1182         club.v_x = (float)(off_x) / ms;
1183 
1184         if (mouse_moved) {
1185             // limit mouse speed //
1186             if (club.v_x > 5.0) club.v_x = 5.0;
1187             if (club.v_x < -5.0) club.v_x = -5.0;
1188             club.v_x /= 5;
1189             if (init_data.warp) {
1190                 // reset the mouse pointer //
1191                 // although only mouse motion is filtered it seems to handicap button events //
1192                 SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
1193                 SDL_WarpMouse(sdl.scr->w >> 1, sdl.scr->h >> 1);
1194                 SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
1195             }
1196         }
1197 
1198         // we need another ball reflection check here //
1199 //        Club_CheckBallReflection(old_x);
1200     }
1201 }
1202 
Club_Show()1203 void BreakOut::Club_Show()
1204 {
1205     int i, off, cy = 0;
1206     if (cur_extras[EX_FROZEN])
1207         cy = club_ch * 2;
1208     else
1209         if (cur_extras[EX_SLIME])
1210             cy = club_ch;
1211     DR_SETDST(sdl.scr, club.x, club.y, club_cw, club_ch);
1212     DR_SETSRC(ss_club, 0, cy);
1213     SSur_Blit();
1214     for (i = 0, off = club_cw; i < club.len; i++, off += club_cw) {
1215         DR_SETDST(sdl.scr, club.x + off, club.y, club_cw, club_ch);
1216         DR_SETSRC(ss_club, club_cw, cy);
1217         SSur_Blit();
1218     }
1219     DR_SETDST(sdl.scr, club.x + off, club.y, club_cw, club_ch);
1220     DR_SETSRC(ss_club, club_cw * 2, cy);
1221     SSur_Blit();
1222     if (cur_extras[EX_WEAPON]) {
1223         wpn_x = club.x + (club.w - wpn_w) / 2;
1224         wpn_y = club.y - wpn_y_off;
1225         DR_SETDST(sdl.scr, wpn_x, wpn_y, wpn_w, wpn_h);
1226         DR_SETSRC(ss_weapon, (int)wpn_cur * wpn_w, 0);
1227         SSur_Blit();
1228         AddRefreshRect(club.x, wpn_y, club.w, wpn_h);
1229     }
1230     else
1231         AddRefreshRect(club.x, club.y, club.w, club.h);
1232 }
1233 
Club_AlphaShow(int a)1234 void BreakOut::Club_AlphaShow(int a)
1235 {
1236     int i, off, cy = 0;
1237     if (cur_extras[EX_FROZEN])
1238         cy = club_ch * 2;
1239     else
1240         if (cur_extras[EX_SLIME])
1241             cy = club_ch;
1242     DR_SETDST(sdl.scr, club.x, club.y, club_cw, club_ch);
1243     DR_SETSRC(ss_club, 0, cy);
1244     SSur_AlphaBlit(a);
1245     for (i = 0, off = club_cw; i < club.len; i++, off += club_cw) {
1246         DR_SETDST(sdl.scr, club.x + off, club.y, club_cw, club_ch);
1247         DR_SETSRC(ss_club, club_cw, cy);
1248         SSur_AlphaBlit(a);
1249     }
1250     DR_SETDST(sdl.scr, club.x + off, club.y, club_cw, club_ch);
1251     DR_SETSRC(ss_club, club_cw * 2, cy);
1252     SSur_AlphaBlit(a);
1253     if (cur_extras[EX_WEAPON]) {
1254         wpn_x = club.x + (club.w - wpn_w) / 2;
1255         wpn_y = club.y - wpn_y_off;
1256         DR_SETDST(sdl.scr, wpn_x, wpn_y, wpn_w, wpn_h);
1257         DR_SETSRC(ss_weapon, (int)wpn_cur * wpn_w, 0);
1258         SSur_AlphaBlit(a);
1259         AddRefreshRect(club.x, wpn_y, club.w, wpn_h);
1260     }
1261     else
1262         AddRefreshRect(club.x, club.y, club.w, club.h);
1263 }
1264 
Club_Resize(int c)1265 int BreakOut::Club_Resize(int c)
1266 {
1267     DL_Entry *e;
1268     Ball *b;
1269     if (club.len + c > club.max_len || club.len + c < 1/*diff[init_data.diff].club_size*/)
1270         return 1;
1271 #ifdef SOUND
1272     if (c < 0)
1273         SndSrv_Play(snd_shr, 0);
1274     else
1275         SndSrv_Play(snd_exp, 0);
1276 #endif
1277     // move attached balls
1278     e = ball_list.head.next;
1279     while (e != &ball_list.tail) {
1280         b = (Ball*)e->data;
1281         if (b->attached) {
1282             if (c > 0 && b->x + ball_rad > club_cw)
1283                 b->x += club_cw * c;
1284             else
1285                 if (c < 0 && b->x + ball_rad > club_cw * 3)
1286                     b->x += club_cw * c;
1287         }
1288         e = e->next;
1289     }
1290 
1291     // resize
1292     club.cur_x -= (float)(c * club_cw) / 2;
1293     club.x = (int)club.cur_x;
1294     club.len += c;
1295     club.w = (club.len + 2) * club_cw;
1296 
1297     // check range
1298     if (club.x < brick_w)
1299         club.x = brick_w;
1300     if (club.x + club.w > sdl.scr->w - brick_w)
1301         club.x = sdl.scr->w - brick_w - club.w;
1302     return 0;
1303 }
1304 
Club_CheckBallReflection(int old_x)1305 void BreakOut::Club_CheckBallReflection(int old_x)
1306 {
1307     DL_Entry    *e = ball_list.head.next;
1308     Ball        *b;
1309     float       bx, by;
1310     float       cx, cy, cx_off;
1311     float       vlen, s;
1312     Vector      a;
1313 
1314     while (e != &ball_list.tail) {
1315         b = (Ball*)e->data;
1316         e = e->next;
1317         /* contact possible */
1318         if (!(club.v_x == 0 ||
1319             b->ign_club || b->club_con ||
1320             b->y + ball_dia - 1 < club.y ||
1321             b->y > club.y + club.h ||
1322             (club.v_x < 0 && b->cur_x > club.x + club.w && b->cur_x > old_x + club.w) ||
1323             (club.v_x > 0 && b->cur_x + ball_dia - 1 < club.x && b->cur_x + ball_dia - 1 < old_x)) ) {
1324 
1325             /* centre of ball */
1326             bx = b->cur_x + ball_rad;
1327             by = b->cur_y + ball_rad;
1328 
1329             /* maybe it just hit the club's top */
1330             if (b->cur_y + ball_dia < club.y + 1 && bx >= club.x + ball_rad - 1 && bx <= club.x + club.w - ball_rad) {
1331                 a.y = 1;
1332                 a.x = 0;
1333                 Club_HandleContact(b, a);
1334                 printf("club hits with top\n");
1335                 break;
1336             }
1337 
1338             /* horizontal line in the middle of the club */
1339             cy = club.y + ball_rad;
1340             /*
1341                 There are two points on C(cx+t|cy) which have a distance of ball_dia from B(bx|by)
1342                 P1(bx+cx_off|cy) and P2(bx-cx_off|cy). t computes as follows:
1343                                 ___
1344                     ball_dia = |BC|;
1345                     ball_dia� = (by - cy)� + (bx-cx-t)�
1346                     t = bx - cx (+-) cx_off
1347             */
1348             cx_off = ceil( sqrt( Square(ball_dia + 1) - Square(by - cy + 1) ) ) + 1;
1349             if (club.v_x < 0) {
1350                 /* moves to the left */
1351                 cx = bx + cx_off;
1352                 if (old_x + ball_rad >= cx && club.x + ball_rad <= cx) {
1353                     /* and it hits the ball */
1354                     /* normed reflection vector */
1355                     a.x = bx - cx;
1356                     a.y = by - cy;
1357                     vlen = VLEN(a.x, a.y);
1358                     a.x /= vlen; a.y /= vlen;
1359                     /*
1360                         ok, now will the ball be reflected and club.v_x added or do
1361                         we have to add only a vector v? computed as follows:
1362                             ->  ->
1363                             v = a * club.v_x
1364                         to check this we have to do this:
1365                                ->
1366                             b: x = s(b->v_x) + (bx)
1367                                     (b->v_y)   (by)
1368                                ->
1369                             c: x = r(-a.y) + (cx)
1370                                     (-a.x)   (cy)
1371 
1372                         these lines will intersect and we have to compute s
1373                         if s < 0 we'll need the vector v else we'll reflect
1374                     */
1375     //                s = ( (by - cy) * a.y / (-a.x) + cx - bx ) / (b->v_x - a.y * b->v_y / (-a.x));
1376                     /* momentarily it just uses the vector v because the reflection does not work */
1377                     if ( /*s <= 0*/ 1 ) {
1378                         /* use vector v */
1379                         b->v_x += a.x * fabs(club.v_x);
1380                         b->v_y += -fabs(a.y) * fabs(club.v_x);
1381                         a.x = a.y = 0;
1382                         Club_HandleContact(b, a);
1383 #ifdef DEBUG
1384     printf("club influences ball\n");
1385 #endif
1386                     }
1387                     else {
1388                         /* reflect */
1389                         Club_HandleContact(b, a);
1390 #ifdef DEBUG
1391     printf("club hits ball\n");
1392 #endif
1393                     }
1394                 }
1395             }
1396             else {
1397                 /* moves to the right */
1398                 cx = bx - cx_off;
1399                 if (old_x + club.w - ball_rad - 1 <= cx && club.x + club.w - ball_rad - 1 >= cx) {
1400                     /* and it hits the ball */
1401                     /* normed reflection vector */
1402                     a.x = bx - cx;
1403                     a.y = by - cy;
1404                     vlen = VLEN(a.x, a.y);
1405                     a.x /= vlen; a.y /= vlen;
1406                     // compute
1407     //                s = ( (by - cy) * a.y / (-a.x) + cx - bx ) / (b->v_x - a.y * b->v_y / (-a.x));
1408                     /* momentarily it just uses the vector v */
1409                     if ( /*s <= 0*/ 1 ) {
1410                         /* use vector v */
1411                         b->v_x += a.x * fabs(club.v_x);
1412                         b->v_y += -fabs(a.y) * fabs(club.v_x);
1413                         a.x = a.y = 0;
1414                         Club_HandleContact(b, a);
1415 #ifdef DEBUG
1416     printf("club influences ball\n");
1417 #endif
1418                     }
1419                     else {
1420                         /* reflect */
1421                         Club_HandleContact(b, a);
1422 #ifdef DEBUG
1423     printf("club hits ball\n");
1424 #endif
1425                     }
1426                 }
1427         }
1428         }
1429     }
1430 }
1431 
Club_HandleContact(Ball * b,Vector a)1432 void BreakOut::Club_HandleContact(Ball *b, Vector a)
1433 {
1434     Vector n;
1435     float   old_vx = b->v_x, d;
1436 
1437     // do not use club till a brick's been touched //
1438     b->club_con = 1;
1439 
1440     if (a.x != 0 && a.y != 0) {
1441         d = sqrt(2);
1442         a.x /= d; a.y /= d;
1443     }
1444 
1445     if (a.y != 0 || a.x != 0) {
1446         // reflect //
1447         n.x = (1 - 2 * a.x * a.x) * b->v_x + (-2 * a.x * a.y) * b->v_y;
1448         n.y = (-2 * a.x * a.y) * b->v_x + (1 - 2 * a.y * a.y) * b->v_y;
1449         b->v_x = n.x;
1450         b->v_y = n.y;
1451 
1452         // club speed //
1453         b->v_x += club.v_x * club.friction;
1454     }
1455 
1456     Ball_MaskV(b, old_vx);
1457 
1458     // attach if slimy //
1459     if (cur_extras[EX_SLIME]) {
1460         b->attached = 1;
1461         b->x -= club.x;
1462         b->y -= club.y;
1463         b->cur_x = b->x;
1464         b->cur_y = b->y;
1465 #ifdef SOUND
1466     SndSrv_Play(snd_sli, 0);
1467 #endif
1468     }
1469     else {
1470 #ifdef SOUND
1471     SndSrv_Play(snd_ref, 0);
1472 #endif
1473         // or reset position if in wall //
1474         if (b->x < brick_w) {
1475             b->cur_x = brick_w;
1476             b->x = (int)b->cur_x;
1477         }
1478         else
1479             if (b->x + ball_dia >= sdl.scr->w - brick_w) {
1480                 b->cur_x = sdl.scr->w - brick_w - ball_dia;
1481                 b->x = (int)b->cur_x;
1482             }
1483         // get new target //
1484         Ball_GetTarget(b);
1485     }
1486 }
1487 
1488 // balls //
Ball_Create(int x,int y,float a,float v)1489 Ball* BreakOut::Ball_Create(int x, int y, float a, float v)
1490 {
1491     Ball    *b = new Ball;
1492     b->cur_x = x;
1493     b->x = x;
1494     b->cur_y = y;
1495     b->y = y;
1496     b->attached = 0;
1497     b->club_con = 0;
1498     b->ign_club = 0;
1499     Ball_ComputeVec(b, a, v);
1500     Ball_ClearTarget(&b->t);
1501     Ball_ClearTarget(&b->t2);
1502     return b;
1503 }
1504 
Ball_ComputeVec(Ball * b,float a,float v)1505 void BreakOut::Ball_ComputeVec(Ball *b, float a, float v)
1506 {
1507 #ifdef DEBUG
1508     printf("wanted ball angle: %4.2f\n", a);
1509 #endif
1510     b->v_x = sin(TOARC(a)) * v;
1511     b->v_y = cos(TOARC(a)) * v;
1512     // y-coordinates are upside down //
1513     b->v_y = -b->v_y;
1514     // mask angles //
1515     Ball_MaskV(b, b->v_x);
1516 }
1517 
Ball_AdjustSpeed(Ball * b,float v)1518 void BreakOut::Ball_AdjustSpeed(Ball *b, float v)
1519 {
1520     float d = sqrt(b->v_x * b->v_x + b->v_y * b->v_y);
1521     b->v_x /= d; b->v_y /= d;
1522     b->v_x *= v; b->v_y *= v;
1523 }
1524 
1525 /*
1526     input:
1527         Ball* b
1528     function:
1529         -check if the time's up for a brick
1530         -remove this brick
1531         -if balls are reflected:
1532         -reset position, reflect, get new target
1533 */
Ball_CheckBrickReflection(Ball * b)1534 void BreakOut::Ball_CheckBrickReflection(Ball *b)
1535 {
1536     if (b->t.cur_tm < b->t.time) return;
1537 
1538     // remove brick //
1539     Brick_Remove(b->t.mx, b->t.my, 0);
1540     if (b->t2.exists)
1541         Brick_Remove(b->t2.mx, b->t2.my, 0);
1542 
1543     if (!cur_extras[EX_METAL] || (cur_extras[EX_METAL] && (b->t.mx == 0 || b->t.mx == lev_w - 1 || b->t.my == 0 || b->t.my == lev_h - 1) ) ) {
1544 #ifdef SOUND
1545     SndSrv_Play(snd_ref, 0);
1546 #endif
1547         float old_vx = b->v_x;
1548 
1549         // reset position
1550         b->cur_x = b->t.x;
1551         b->x = (int)b->cur_x;
1552         b->cur_y = b->t.y;
1553         b->y = (int)b->cur_y;
1554 
1555         // reflection
1556         Vector n,a = b->t.a;
1557         float d;
1558 
1559         // norm //
1560         if (a.x != 0 && a.y != 0) {
1561             d = sqrt(2);
1562             a.x /= d; a.y /= d;
1563         }
1564 
1565         n.x = (1 - 2 * a.x * a.x) * b->v_x + (-2 * a.x * a.y) * b->v_y;
1566         n.y = (-2 * a.x * a.y) * b->v_x + (1 - 2 * a.y * a.y) * b->v_y;
1567         b->v_x = n.x;
1568         b->v_y = n.y;
1569 
1570         // disable club contact
1571         b->club_con = 0;
1572 
1573         Ball_MaskV(b, old_vx);
1574 
1575         // only use club if y + ball_dia < club.y coz anything else would get the ball in the wall
1576         if (b->y + ball_rad> club.y)
1577             b->ign_club = 1;
1578         else
1579             b->ign_club = 0;
1580     }
1581 
1582     // check targets //
1583     Ball_NewTargets(b->t.mx, b->t.my);
1584     if (b->t2.exists)
1585         Ball_NewTargets(b->t2.mx, b->t2.my);
1586 
1587     // check shot targets //
1588     Shots_NewTargets(b->t.mx, b->t.my);
1589     if (b->t2.exists)
1590         Shots_NewTargets(b->t2.mx, b->t2.my);
1591 }
1592 
Ball_NewTargets(int mx,int my)1593 void BreakOut::Ball_NewTargets(int mx, int my)
1594 {
1595     DL_Entry    *e = ball_list.head.next;
1596     Ball        *ball;
1597     while (e != &ball_list.tail) {
1598         ball = (Ball*)e->data;
1599         if (mx == -1 || (ball->t.mx == mx && ball->t.my == my))
1600             Ball_GetTarget(ball);
1601         e = e->next;
1602     }
1603 }
1604 
1605 /*
1606     reflect ball at paddle when paddle is understood convex
1607 
1608 void BreakOut::Ball_CheckConvexReflection(Ball *bl, float old_x, float old_y)
1609 {
1610     float bx, by; // centre of ball //
1611     float   m; // monotony //
1612     Vector  p; // point //
1613     float   mod = 0.3; // convex modifier //
1614     Vector  c = {0, 0}; // convex change //
1615     int contact = 0;
1616     Vector a = {0, 0}; // reflection vector //
1617 
1618     // centre of ball //
1619     bx = old_x + ball_rad;
1620     by = old_y + ball_rad;
1621 
1622     // monotony //
1623     m = bl->v_y / bl->v_x;
1624 
1625     // checking top horizontal line //
1626     p.y = club.y - ball_rad - 1;
1627     p.x = (p.y - by) / m + bx;
1628     if (p.x >= club.x - ball_rad && p.x <= club.x + club.w + ball_rad - 1 && bl->v_y > 0) {
1629         c.x = ( (p.x - (club.x + club.w / 2)) / (club.w / 2) ) * mod;
1630         bl->v_x += c.x;
1631         a.y = 1;
1632         contact = 1;
1633 #ifdef DEBUG
1634     printf("ball hits convex top\n");
1635 #endif
1636     }
1637     else {
1638         // checking left vertical line //
1639         p.x = club.x - ball_rad - 1;
1640         p.y = (p.x - bx) * m + by;
1641         if (p.y >= club.y - ball_rad && p.y <= club.y + club.h && bl->v_x > 0) {
1642             // hit//
1643             a.x = 1;
1644             contact = 1;
1645 #ifdef DEBUG
1646             printf("ball hits convex left vertical line\n");
1647 #endif
1648         }
1649         else {
1650             // checking right vertical line //
1651             p.x = club.x + club.w + ball_rad;
1652             p.y = (p.x - bx) * m + by;
1653             if (p.y >= club.y - ball_rad && p.y <= club.y + club.h && bl->v_x < 0) {
1654                 // hit //
1655                 a.x = 1;
1656                 contact = 1;
1657 #ifdef DEBUG
1658                 printf("ball hits convex right vertical line\n");
1659 #endif
1660             }
1661         }
1662     }
1663 
1664     if (contact) {
1665         if (club.v_x == 0) {
1666             bl->cur_x = p.x - ball_rad;
1667             bl->cur_y = p.y - ball_rad;
1668             bl->x = (int)bl->cur_x;
1669             bl->y = (int)bl->cur_y;
1670         }
1671         Club_HandleContact(bl, a);
1672     }
1673 }*/
1674 
1675 /*
1676     input
1677         Ball* b
1678     function:
1679         -check if a ball hits the club and reflect it
1680 */
Ball_CheckClubReflection(Ball * bl,float old_x,float old_y)1681 void BreakOut::Ball_CheckClubReflection(Ball *bl, float old_x, float old_y)
1682 {
1683     float bx, by; // centre of ball //
1684     int cx, cy; // centre of club circle //
1685     Vector  rv = {0, 0};
1686     int     contact = 0;
1687     Vector  p; // point //
1688     float   m; // monotony //
1689     float   mod = 0.3; // convex modifier //
1690     Vector  c = {0, 0}; // convex change //
1691 
1692     // no contact possible ? //
1693     if (bl->club_con || bl->ign_club || bl->y + ball_dia < club.y || (bl->y >= club.y + club.h - 4 && old_y > club.y) || bl->x + ball_dia < club.x || bl->x > club.x + club.w)
1694         return;
1695 
1696     // centre of ball //
1697     bx = old_x + ball_rad;
1698     by = old_y + ball_rad;
1699 
1700     // monotony //
1701     m = bl->v_y / bl->v_x;
1702 
1703     // checking top horizontal line //
1704     p.y = club.y - ball_rad - 1;
1705     p.x = (p.y - by) / m + bx;
1706     if (p.x >= club.x + ball_rad - 3 && p.x <= club.x + club.w - ball_rad + 2 && bl->v_y > 0) {
1707         // hit //
1708         if (init_data.convex) {
1709             // convex behaviour //
1710             c.x = ( (p.x - (club.x + club.w / 2)) / (club.w / 2) ) * mod;
1711             bl->v_x += c.x;
1712         }
1713         rv.y = 1;
1714         contact = 1;
1715 #ifdef DEBUG
1716         printf("ball hits top\n");
1717 #endif
1718     }
1719     else {
1720         // checking left vertical line //
1721         p.x = club.x - ball_rad - 1;
1722         p.y = (p.x - bx) * m + by;
1723         if (p.y >= club.y + 2 && p.y <= club.y + club.h - 3 && bl->v_x > 0) {
1724             // hit//
1725 //            rv.y = 1;
1726             rv.x = 1;
1727             contact = 1;
1728 #ifdef DEBUG
1729             printf("ball hits left vertical line\n");
1730 #endif
1731         }
1732         else {
1733             // checking right vertical line //
1734             p.x = club.x + club.w + ball_rad;
1735             p.y = (p.x - bx) * m + by;
1736             if (p.y >= club.y + 2 && p.y <= club.y + club.h - 3 && bl->v_x < 0) {
1737                 // hit //
1738 //                rv.y = 1;
1739                 rv.x = 1;
1740                 contact = 1;
1741 #ifdef DEBUG
1742                 printf("ball hits right vertical line\n");
1743 #endif
1744             }
1745             else {
1746                 // checking left upper diagonal line //
1747                 // it is: x=(xb * mb - xc * mc + yc - yb) / (mb - mc)
1748                 // mb = m; mc = -1;
1749                 cx = club.x - ball_rad - 1;
1750                 cy = club.y + 2;
1751                 if (m != -1) {
1752                     p.x = (bx * m - cx * (-1) + cy - by) / (m - (-1));
1753                     p.y = (p.x - bx) * m + by;
1754                 }
1755                 if (m != -1 && p.x >= cx && p.x <= club.x + ball_rad - 3) {
1756                     // hit
1757 //                    rv.y = 1;
1758                     rv.x = rv.y = 1;
1759                     contact = 1;
1760 #ifdef DEBUG
1761                     printf("ball hits left upper corner\n");
1762 #endif
1763                 }
1764                 else {
1765                     // checking right upper diagonal line //
1766                     cx = club.x + club.w + ball_rad;
1767                     cy = club.y + 2;
1768                     if (m != 1) {
1769                         p.x = (bx * m - cx * (1) + cy - by) / (m - (1));
1770                         p.y = (p.x - bx) * m + by;
1771                     }
1772                     if (m != 1 && p.x <= cx && p.x >= club.x + club.w - ball_rad + 2) {
1773                         // hit
1774 //                        rv.y = 1;
1775                         rv.x = 1; rv.y = -1;
1776                         contact = 1;
1777 #ifdef DEBUG
1778                         printf("ball hits right upper corner\n");
1779 #endif
1780                     }
1781 /*                    else {
1782                         // checking left lower dia line //
1783                         cx = club.x - ball_rad - 1;
1784                         cy = club.y + club.h - 3;
1785                         if (m != 1) {
1786                             p.x = (bx * m - cx * (1) + cy - by) / (m - (1));
1787                             p.y = (p.x - bx) * m + by;
1788                         }
1789                         if (m != 1 && p.x >= cx && p.x <= club.x + ball_rad - 3) {
1790                             // hit
1791                             rv.x = 1; rv.y = -1;
1792                             contact = 1;
1793                         }
1794                         else {
1795                             // checking right lower dia line //
1796                             cx = club.x + club.w + ball_rad;
1797                             cy = club.y + 2;
1798                             if (m != -1) {
1799                                 p.x = (bx * m - cx * (-1) + cy - by) / (m - (-1));
1800                                 p.y = (p.x - bx) * m + by;
1801                             }
1802                             if (m != -1 && p.x <= cx && p.x >= club.x + club.w - ball_rad + 2) {
1803                                 // hit
1804                                 rv.x = rv.y = 1;
1805                                 contact = 1;
1806                             }
1807                         }
1808                     }*/
1809                 }
1810             }
1811         }
1812     }
1813 
1814     if (contact) {
1815         if (club.v_x == 0 || cur_extras[EX_SLIME]) {
1816             bl->cur_x = p.x - ball_rad;
1817             bl->cur_y = p.y - ball_rad;
1818             bl->x = (int)bl->cur_x;
1819             bl->y = (int)bl->cur_y;
1820         }
1821         Club_HandleContact(bl, rv);
1822     }
1823 }
1824 
1825 /*
1826     input :
1827         Ball* b
1828     function :
1829         -check if ball b hits a brick and if so:
1830         -compute the hitten brick in lev_map (int mx, my)
1831         -the reset position of the ball after destroying the brick (float x, y)
1832         -the time in milliseconds it takes the ball to hit the brick from its current position
1833         by using ball_v as velocity (int time)
1834         -the side at which the ball hits; might be LEFT, RIGHT, TOP, BOTTOM (int side)
1835         -the reflection vector (float a); if reflecting at an horizontal wall it's a = {0, 1} else a = {1, 0}
1836 */
Ball_GetTarget(Ball * b)1837 void BreakOut::Ball_GetTarget(Ball *b)
1838 {
1839     float bx = b->cur_x + ball_rad, by = b->cur_y + ball_rad; // centre of ball
1840     float x, y; // positions
1841     float m = (b->v_y / b->v_x); // monotony
1842     Vector v = {b->v_x, b->v_y}; // normed radial vector
1843     float n = sqrt(v.x * v.x + v.y * v.y);
1844     v.x /= n; v.y /= n;
1845     // tangential points
1846     Vector  p[2] = { {bx + v.y * ball_rad, by - v.x * ball_rad}, {bx - v.y * ball_rad, by + v.x * ball_rad} };
1847     Target  t[2];
1848     Corner  cor[2]; // 0 == x, 1 == y //
1849     int     corner = -1; // which is it? -1 means no corner //
1850     int d; // direction
1851     int i; // index
1852     int c_mod; // needed to get centre of ball
1853     float cx, cy;
1854     Vector pos;
1855     int contact = 0;
1856     int br_x, br_y;
1857 //    int same_brick;
1858 
1859     // clear targets //
1860     Ball_ClearTarget(&b->t);
1861     Ball_ClearTarget(&b->t2);
1862     Ball_ClearTarget(&t[0]);
1863     Ball_ClearTarget(&t[1]);
1864 
1865     for (int j = 0; j < 2; j++) {
1866         /* check hori grid */
1867         d = b->v_x >= 0 ? 1 : -1;
1868         i = (int)(p[j].x / brick_w) + d;
1869         x = i * brick_w;
1870         if (d == -1)
1871             x += brick_w - 1;
1872         t[j].exists = 0;
1873         while (i < lev_w && i >= 0) {
1874             /* compute y */
1875             y = ((m * (x - p[j].x)) + p[j].y);
1876             /* check for brick */
1877             if (lev_map[i][(int)(y / brick_h)].type != MAP_EMPTY && y >= 0 && y < lev_h * brick_h) {
1878                 t[j].x = x;
1879                 t[j].y = y;
1880                 if (d == 1)
1881                     t[j].side = LEFT;
1882                 else
1883                     t[j].side = RIGHT;
1884                 t[j].exists = 1;
1885                 // check if it's a corner //
1886                 cor[0].x = (int)x;
1887                 cor[0].y = (int)y;
1888                 Corner_Check(&cor[0], b->v_x, b->v_y);
1889                 break;
1890             }
1891 
1892             // next line //
1893             i += d;
1894             x += brick_w * d;
1895         }
1896         // check vert grid
1897         d = b->v_y >= 0 ? 1 : -1;
1898         i = (int)(p[j].y / brick_h) + d;
1899         y = i * brick_h;
1900         if (d == -1)
1901             y += brick_h - 1;
1902         while (i < lev_h && i >= 0) {
1903                 // compute y
1904                 x = (( (y - p[j].y) / m ) + p[j].x);
1905                 // check for brick
1906                 if (lev_map[(int)(x / brick_w)][i].type != MAP_EMPTY  && x >= 0 && x < lev_w * brick_w) {
1907                     if (!t[j].exists) {
1908                         t[j].x = x;
1909                         t[j].y = y;
1910                         if (d == 1)
1911                             t[j].side = TOP;
1912                         else
1913                             t[j].side = BOTTOM;
1914                         t[j].exists = 1;
1915                     }
1916                     else {
1917                         // check if it's a corner //
1918                         cor[1].x = (int)x;
1919                         cor[1].y = (int)y;
1920                         Corner_Check(&cor[1], b->v_x, b->v_y);
1921 
1922                         // get point //
1923                         if (VLEN(t[j].x - bx, t[j].y - by) > VLEN(x - bx, y - by)) {
1924                             t[j].x = x;
1925                             t[j].y = y;
1926                             corner = 1;
1927                             if (cor[corner].type == NONE) {
1928                                 if (d == 1)
1929                                     t[j].side = TOP;
1930                                 else
1931                                     t[j].side = BOTTOM;
1932                             }
1933                         }
1934                         else
1935                             corner = 0;
1936                         if (cor[corner].type != NONE) {
1937                             // it's a corner
1938                             t[j].x = cor[corner].x;
1939                             t[j].y = cor[corner].y;
1940                             switch (cor[corner].type) {
1941                                 case UPPERLEFT:
1942                                     if (j == 1)
1943                                         t[j].side = TOP;
1944                                     break;
1945                                 case UPPERRIGHT:
1946                                     if (j == 0)
1947                                         t[j].side = TOP;
1948                                     break;
1949                                 case LOWERLEFT:
1950                                     if (j == 0)
1951                                         t[j].side = BOTTOM;
1952                                     break;
1953                                 case LOWERRIGHT:
1954                                     if (j == 1)
1955                                         t[j].side = BOTTOM;
1956                                     break;
1957                             };
1958                         }
1959                     }
1960                     break;
1961                 }
1962 
1963             // next line
1964             i += d;
1965             y += brick_h * d;
1966         }
1967     }
1968 
1969     // if both targets hit the same brick but not the same side it's a corner //
1970     if ((int)(t[0].x / brick_w) == (int)(t[1].x / brick_w) && (int)(t[0].y / brick_h) == (int)(t[1].y / brick_h)) {
1971         if (t[0].side == TOP && t[1].side == LEFT)
1972             corner = UPPERLEFT;
1973         else
1974             if (t[0].side == LEFT && t[1].side == BOTTOM)
1975                 corner = LOWERLEFT;
1976             else
1977                 if (t[0].side == BOTTOM && t[1].side == RIGHT)
1978                     corner = LOWERRIGHT;
1979                 else
1980                     if (t[0].side == RIGHT && t[1].side == TOP)
1981                         corner = UPPERRIGHT;
1982                     else
1983                         corner = -1;
1984     }
1985     else
1986         corner = -1;
1987 
1988     // which target is nearer ? //
1989     if (t[0].exists && !t[1].exists) {
1990         b->t = t[0];
1991         c_mod = -1;
1992     }
1993     else
1994         if (!t[0].exists && t[1].exists) {
1995             b->t = t[1];
1996             c_mod = 1;
1997         }
1998         else
1999             if (t[0].exists && t[1].exists) {
2000                 if (sqrt(Square(bx - t[0].x) + Square(by - t[0].y)) > sqrt(Square(bx - t[1].x) + Square(by - t[1].y))) {
2001                     b->t = t[1];
2002                     c_mod = 1;
2003                 }
2004                 else {
2005                     b->t = t[0];
2006                     c_mod = -1;
2007                 }
2008             }
2009             else
2010                 return;
2011 
2012     // brick in map
2013     b->t.mx = (int)b->t.x / brick_w;
2014     b->t.my = (int)b->t.y / brick_h;
2015 
2016     // reset and reflection vector
2017     if (corner == -1) {
2018         // centre of ball when hitting target
2019         bx = b->t.x + c_mod * v.y * ball_rad;
2020         by = b->t.y - c_mod * v.x * ball_rad;
2021 
2022         if (b->t.side == BOTTOM || b->t.side == TOP) {
2023             if (b->t.side == BOTTOM)
2024                 b->t.y = b->t.y + ball_rad + 1;
2025             else
2026                 b->t.y = b->t.y - ball_rad - 1;
2027             b->t.x = (b->t.y - by) / m + bx;
2028             b->t.a.y = 1;
2029         }
2030         if (b->t.side == LEFT || b->t.side == RIGHT) {
2031             if (b->t.side == RIGHT)
2032                 b->t.x = b->t.x + ball_rad + 1;
2033             else
2034                 b->t.x = b->t.x - ball_rad - 1;
2035             b->t.y = m * (b->t.x - bx) + by;
2036             b->t.a.x = 1;
2037         }
2038     }
2039     else {
2040         br_x = (int)(b->t.x / brick_w); br_y = (int)(b->t.y / brick_h);
2041         br_x *= brick_w; br_y *= brick_h;
2042         if (corner == UPPERLEFT) {
2043             cx = br_x - ball_rad - 1;
2044             cy = br_y;
2045             if (m != -1) {
2046                 pos.x = (bx * m - cx * (-1) + cy - by) / (m - (-1));
2047                 pos.y = (pos.x - bx) * m + by;
2048             }
2049             if (m != -1) {
2050                 // hit
2051                 b->t.a.x = 1; b->t.a.y = 1;
2052                 contact = 1;
2053             }
2054         }
2055         else
2056             if (corner == UPPERRIGHT) {
2057                 cx = br_x + brick_w + ball_rad;
2058                 cy = br_y;
2059                 if (m != 1) {
2060                     pos.x = (bx * m - cx * (1) + cy - by) / (m - (1));
2061                     pos.y = (pos.x - bx) * m + by;
2062                 }
2063                 if (m != 1) {
2064                     // hit
2065                     b->t.a.x = 1; b->t.a.y = -1;
2066                     contact = 1;
2067                 }
2068             }
2069             else
2070                 if (corner == LOWERLEFT) {
2071                     cx = br_x - ball_rad - 1;
2072                     cy = br_y + brick_h - 1;
2073                     if (m != 1) {
2074                         pos.x = (bx * m - cx * (1) + cy - by) / (m - (1));
2075                         pos.y = (pos.x - bx) * m + by;
2076                     }
2077                     if (m != 1) {
2078                         // hit
2079                         b->t.a.x = 1; b->t.a.y = -1;
2080                         contact = 1;
2081                     }
2082                 }
2083                 else
2084                     if (corner == LOWERRIGHT) {
2085                         cx = br_x + brick_w + ball_rad;
2086                         cy = br_y + brick_h - 1;
2087                         if (m != -1) {
2088                             pos.x = (bx * m - cx * (-1) + cy - by) / (m - (-1));
2089                             pos.y = (pos.x - bx) * m + by;
2090                         }
2091                         if (m != -1) {
2092                             // hit
2093                             b->t.a.x = 1; b->t.a.y = 1;
2094                             contact = 1;
2095                         }
2096                     }
2097         if (contact) {
2098             b->t.x = pos.x;
2099             b->t.y = pos.y;
2100         }
2101         // final corner
2102         b->t.side = corner;
2103     }
2104 
2105     // possible second target //
2106     switch (b->t.side) {
2107         case 0:
2108             // top //
2109             b->t2.my = b->t.my;
2110             if (c_mod == -1 && lev_map[(int)(b->t.x - 3) / brick_w][b->t.my].type == MAP_BRICK) {
2111                 b->t2.mx = (int)(b->t.x - 3) / brick_w;
2112                 b->t2.exists = 1;
2113             }
2114             else
2115                 if (c_mod == 1 && lev_map[(int)(b->t.x + 3) / brick_w][b->t.my].type == MAP_BRICK) {
2116                     b->t2.mx = (int)(b->t.x + 3) / brick_w;
2117                     b->t2.exists = 1;
2118                 }
2119             break;
2120         case 2:
2121             // bottom //
2122             b->t2.my = b->t.my;
2123             if (c_mod == -1 && lev_map[(int)(b->t.x + 3) / brick_w][b->t.my].type == MAP_BRICK) {
2124                 b->t2.mx = (int)(b->t.x + 3) / brick_w;
2125                 b->t2.exists = 1;
2126             }
2127             else
2128                 if (c_mod == 1 && lev_map[(int)(b->t.x - 3) / brick_w][b->t.my].type == MAP_BRICK) {
2129                     b->t2.mx = (int)(b->t.x - 3) / brick_w;
2130                     b->t2.exists = 1;
2131                 }
2132             break;
2133         case 1:
2134             // right side //
2135             b->t2.mx = b->t.mx;
2136             if (c_mod == -1 && lev_map[b->t.mx][(int)(b->t.y - 3) / brick_h].type == MAP_BRICK) {
2137                 b->t2.my = (int)(b->t.y - 3) / brick_h;
2138                 b->t2.exists = 1;
2139             }
2140             else
2141                 if (c_mod == 1 && lev_map[b->t.mx][(int)(b->t.y + 3) / brick_h].type == MAP_BRICK) {
2142                     b->t2.my = (int)(b->t.y + 3) / brick_h;
2143                     b->t2.exists = 1;
2144                 }
2145             break;
2146         case 3:
2147             // left side //
2148             b->t2.mx = b->t.mx;
2149             if (c_mod == -1 && lev_map[b->t.mx][(int)(b->t.y + 3) / brick_h].type == MAP_BRICK) {
2150                 b->t2.my = (int)(b->t.y + 3) / brick_h;
2151                 b->t2.exists = 1;
2152             }
2153             else
2154                 if (c_mod == 1 && lev_map[b->t.mx][(int)(b->t.y - 3) / brick_h].type == MAP_BRICK) {
2155                     b->t2.my = (int)(b->t.y - 3) / brick_h;
2156                     b->t2.exists = 1;
2157                 }
2158             break;
2159     }
2160     if (b->t2.mx == b->t.mx && b->t2.my == b->t.my)
2161         b->t2.exists = 0;
2162 
2163     // reset position is left upper corner; not ball centre
2164     b->t.x -= ball_rad;
2165     b->t.y -= ball_rad;
2166 
2167     // estimate time //
2168     n = sqrt( Square(b->cur_x - b->t.x) + Square(b->cur_y - b->t.y) );
2169     b->t.time = (int)floor(n / ball_v);
2170 
2171 #ifdef DEBUG
2172     printf("*****\n");
2173     printf("current centre: %4.2f, %4.2f\n", b->cur_x + ball_rad, b->cur_y + ball_rad);
2174     printf("monotony %4.2f\n", m);
2175     printf("hits side %i of brick %i,%i\n", b->t.side, b->t.mx, b->t.my);
2176     printf("reflection vector: %4.2f,%4.2f\n", b->t.a.x, b->t.a.y);
2177     printf("reset position %4.2f,%4.2f\n", b->t.x, b->t.y);
2178     printf("takes %i ms\n", b->t.time);
2179     printf("*****\n");
2180 #endif
2181 }
2182 
Ball_ClearTarget(Target * t)2183 void BreakOut::Ball_ClearTarget(Target *t)
2184 {
2185     memset(t, 0, sizeof(Target));
2186 }
2187 
Ball_MaskV(Ball * b,float old_vx)2188 void BreakOut::Ball_MaskV(Ball *b, float old_vx)
2189 {
2190     float m;
2191 
2192     // b->v_x == 0 would cause seg faults //
2193     if (b->v_x == 0) {
2194         if (old_vx < 0)
2195             b->v_x = 0.01;
2196         else
2197             b->v_x = -0.01;
2198     }
2199 
2200     // avoid 45� angles //
2201     if (b->v_x == b->v_y)
2202         b->v_x *= 0.99;
2203 
2204     m = b->v_y / b->v_x;
2205 
2206     // mask angles from 70 to 110 and -110 to -70 //
2207     if (fabs(m) < ball_vhmask) {
2208         if (b->v_y < 0 || b->club_con)
2209             b->v_y = fabs(ball_vhmask * b->v_x) * -1;
2210         else
2211             b->v_y = fabs(ball_vhmask * b->v_x);
2212 #ifdef DEBUG
2213         printf("horizontal mask: %4.2f\n", b->v_y / b->v_x);
2214 #endif
2215     }
2216 
2217     // mask angles from -10 to 10 and 170 to 190 //
2218     if (fabs(m) > ball_vvmask) {
2219         if (b->v_x < 0)
2220             b->v_x = fabs(b->v_y / ball_vvmask) * -1;
2221         else
2222             b->v_x = fabs(b->v_y / ball_vvmask);
2223 #ifdef DEBUG
2224         printf("vertical mask; new m: %4.2f\n", b->v_y / b->v_x);
2225 #endif
2226     }
2227 
2228     // adjust speed
2229     Ball_AdjustSpeed(b, ball_v);
2230 }
2231 
Balls_Reset()2232 void BreakOut::Balls_Reset()
2233 {
2234     DL_Clear(&ball_list);
2235     // add one ball //
2236     Ball *b;
2237     b = Ball_Create((club.w - ball_w) / 2, -ball_dia, (rand() % 120) - 60, ball_v);
2238     b->attached = 1;
2239     DL_Add(&ball_list, b);
2240 }
2241 
Balls_Hide()2242 void BreakOut::Balls_Hide()
2243 {
2244     DL_Entry    *e = ball_list.head.next;
2245     Ball        *b;
2246     int         bx, by;
2247     while (e != &ball_list.tail) {
2248         b = (Ball*)e->data;
2249         // balls position; add club pos if attached //
2250         bx = b->x;
2251         by = b->y;
2252         if (b->attached) {
2253             bx += club.x;
2254             by += club.y;
2255         }
2256         // put background //
2257         DR_SETDST(sdl.scr, bx, by, ball_dia, ball_dia);
2258         DR_SETSRC(ss_bkgnd, bx, by);
2259         SSur_Blit();
2260         AddRefreshRect(bx, by, ball_dia, ball_dia);
2261         e = e->next;
2262     }
2263 }
2264 
Balls_Update(int ms)2265 int BreakOut::Balls_Update(int ms)
2266 {
2267     DL_Entry    *e, *next;
2268     Ball        *b;
2269     float       old_x, old_y;
2270     e = ball_list.head.next;
2271     while (e != &ball_list.tail) {
2272         next = e->next;
2273         b = (Ball*)e->data;
2274         old_x = b->cur_x;
2275         old_y = b->cur_y;
2276         if (b->attached && (buttonstate[MB_LEFT] || buttonstate[MB_RIGHT]|| keystate[init_data.k_fire])) {
2277             // if not in wall //
2278             if (b->x + club.x >= brick_w && b->x + ball_dia + club.x < sdl.scr->w - brick_w) {
2279                 // release ball //
2280 #ifdef SOUND
2281     SndSrv_Play(snd_ref, 0);
2282 #endif
2283                 b->attached = 0;
2284                 b->x += club.x;
2285                 b->y += club.y;
2286                 b->cur_x = b->x;
2287                 b->cur_y = b->y;
2288                 if (!init_data.rnd_start) {
2289                     if (buttonstate[MB_LEFT])
2290                         Ball_ComputeVec(b, -50, ball_v);
2291                     else
2292                         Ball_ComputeVec(b, 50, ball_v);
2293                 }
2294                 Ball_GetTarget(b);
2295             }
2296         }
2297         // new position //
2298         if (!b->attached) {
2299             b->cur_x += b->v_x * ms;
2300             b->cur_y += b->v_y * ms;
2301             b->x = (int)b->cur_x;
2302             b->y = (int)b->cur_y;
2303             // check if reflected by club //
2304             if (!b->club_con && !b->ign_club)
2305                 Ball_CheckClubReflection(b, old_x, old_y);
2306             // or by brick //
2307             if (b->t.exists) {
2308                 b->t.cur_tm += ms;
2309                 Ball_CheckBrickReflection(b);
2310             }
2311         }
2312         // delete ball if outside of window //
2313         if (!b->attached && (b->x >= sdl.scr->w || b->x + ball_dia < 0 || b->y >= sdl.scr->h))
2314             DL_DeleteEntry(&ball_list, e);
2315         e = next;
2316     }
2317     // game over ? //
2318     return ball_list.counter == 0;
2319 }
2320 
Balls_Show()2321 void BreakOut::Balls_Show()
2322 {
2323     DL_Entry    *e = ball_list.head.next;
2324     Ball        *b;
2325     int         bx, by;
2326     int         off = 0;
2327     if (cur_extras[EX_METAL])
2328         off = ball_w;
2329     while (e != &ball_list.tail) {
2330         b = (Ball*)e->data;
2331         // balls position; add club pos if attached //
2332         bx = b->x;
2333         by = b->y;
2334         if (b->attached) {
2335             bx += club.x;
2336             by += club.y;
2337         }
2338         // show ball //
2339         DR_SETDST(sdl.scr, bx, by, ball_w, ball_h);
2340         DR_SETSRC(ss_ball, off, 0);
2341         SSur_Blit();
2342         AddRefreshRect(bx, by, ball_w, ball_h);
2343         e = e->next;
2344     }
2345 }
2346 
Balls_AlphaShow(int a)2347 void BreakOut::Balls_AlphaShow(int a)
2348 {
2349     DL_Entry    *e = ball_list.head.next;
2350     Ball        *b;
2351     int         bx, by;
2352     int         off = 0;
2353     if (cur_extras[EX_METAL])
2354         off = ball_w;
2355     while (e != &ball_list.tail) {
2356         b = (Ball*)e->data;
2357         // balls position; add club pos if attached //
2358         bx = b->x;
2359         by = b->y;
2360         if (b->attached) {
2361             bx += club.x;
2362             by += club.y;
2363         }
2364         // show ball //
2365         DR_SETDST(sdl.scr, bx, by, ball_w, ball_h);
2366         DR_SETSRC(ss_ball, off, 0);
2367         SSur_AlphaBlit(a);
2368         AddRefreshRect(bx, by, ball_w, ball_h);
2369         e = e->next;
2370     }
2371 }
2372 
2373 // brick //
Brick_Remove(int mx,int my,int shot)2374 void BreakOut::Brick_Remove(int mx, int my, int shot)
2375 {
2376     int px, py;
2377     int was_wall = 0;
2378 
2379     if ( (lev_map[mx][my].type == MAP_BRICK && (--lev_map[mx][my].dur == 0 || cur_extras[EX_METAL])) ||
2380           (lev_map[mx][my].type == MAP_WALL && cur_extras[EX_METAL] && mx > 0 && mx < lev_w - 1 && my > 0 && my < lev_h - 1) ) {
2381         // remove //
2382         lev_map[mx][my].id = -1;
2383         if ( lev_map[mx][my].type == MAP_WALL)
2384             was_wall = 1;
2385         lev_map[mx][my].type = MAP_EMPTY;
2386         px = mx * brick_w;
2387         py = my * brick_h;
2388         // add shrapnells //
2389         Brick_CreateShrapnells(px, py, shot);
2390 #ifdef SOUND
2391     SndSrv_Play(snd_boom, 0);
2392 #endif
2393         // clear ss_bkgnd //
2394         DR_SETDST(ss_bkgnd, px, py, brick_w, brick_h);
2395         DR_SETSRC(ss_picture, px, py);
2396         SSur_Blit();
2397         DR_SETDST(sdl.scr, px, py, brick_w, brick_h);
2398         DR_SETSRC(ss_bkgnd, px, py);
2399         SSur_Blit();
2400         AddRefreshRect(px, py, brick_w, brick_h);
2401         // release extra if one exists //
2402         if (lev_map[mx][my].extra != EX_NONE)
2403             Extra_Create(lev_map[mx][my].extra, mx * brick_w, my * brick_h);
2404         // get score //
2405         player.score += lev_map[mx][my].score;
2406         // check for next level //
2407         if (!was_wall && --lev_bricks == 0) {
2408             new_level = 1;
2409 #ifdef SOUND
2410     if (rand() % 2)
2411         SndSrv_Play(snd_good, 1);
2412     else
2413         SndSrv_Play(snd_exc, 1);
2414 #endif
2415         }
2416         if (px == sh_x && py == sh_y)
2417             Sh_New();
2418     }
2419     else
2420         if (lev_map[mx][my].type == MAP_BRICK) {
2421             px = mx * brick_w;
2422             py = my * brick_h;
2423             DR_SETDST(ss_bkgnd, px, py, brick_w, brick_h);
2424             if (lev_map[mx][my].dur == 1) {
2425                 DR_SETSRC(ss_bricks, brick_w * 4, 0);
2426             }
2427             else
2428                 DR_SETSRC(ss_bricks, brick_w * (lev_map[mx][my].dur - 1), 0);
2429             SSur_Blit();
2430             DR_SETDST(sdl.scr, px, py, brick_w, brick_h);
2431             DR_SETSRC(ss_bkgnd, px, py);
2432             SSur_Blit();
2433             AddRefreshRect(px, py, brick_w, brick_h);
2434         }
2435 }
2436 
Brick_CreateShrapnells(int x,int y,int shot)2437 void BreakOut::Brick_CreateShrapnells(int x, int y, int shot)
2438 {
2439     if (!init_data.anim) return;
2440     int mod = init_data.anim;
2441     int i, j;
2442     int r;
2443     int w, h, dx, dy;
2444     if (cur_extras[EX_METAL] || shot)
2445         r = rand() % 3;
2446     else
2447         r = rand() % 5;
2448     if (r == 4) r = 3;
2449     switch (r) {
2450         case 0:
2451             w = brick_w / (8 * mod);
2452             h = brick_h;
2453             for (i = 0; i < (4 * mod); i++)
2454                 Shr_Create(x + i * w, y, w, h, -0.05 - i * 0.01, 0);
2455             for (i = (4 * mod) - 1; i >= 0; i--)
2456                 Shr_Create(x + brick_w - (i + 1) * w, y, w, h, 0.05 + i * 0.01, 0);
2457             break;
2458         case 1:
2459             w = brick_w;
2460             h = brick_h / (4 * mod);
2461             for (i = 0; i < (2 * mod); i++)
2462                 Shr_Create(x, y + i * h, w, h, 0, -0.05 - i * 0.01);
2463             for (i = (2 * mod) - 1; i >= 0; i--)
2464                 Shr_Create(x, y + brick_h - (i + 1) * h, w, h, 0, 0.05 + i * 0.01);
2465             break;
2466         case 2:
2467             w = brick_w / (8 * mod);
2468             h = brick_h;
2469             for (i = 0; i < (8 * mod); i++)
2470                 Shr_Create(x + i * w, y, w, h, 0, (1 - 2 * (i & 1)) * 0.1);
2471             break;
2472         case 3:
2473             w = init_data.anim == 1 ? 4 : 2;
2474             h = init_data.anim == 1 ? 4 : 2;
2475             for (i = 0; i < brick_w / w; i++)
2476                 for (j = 0; j < brick_h / h; j++) {
2477                     dx = rand() % 2 == 0 ? 1 : -1;
2478                     dy = rand() % 2 == 0 ? 1 : -1;
2479                     Shr_Create(x + i * w, y + j * h, w, h, float(rand()%6+5) / 100 * dx, float(rand()%6+5) / 100 * dy);
2480                  }
2481             break;
2482     }
2483 }
2484 
2485 // update //
AddRefreshRect(int x,int y,int w,int h)2486 void BreakOut::AddRefreshRect(int x, int y, int w, int h)
2487 {
2488     if (rect_num == UPDATERECTS) return;
2489     if (x < 0) {
2490         w += x;
2491         x = 0;
2492     }
2493     if (y < 0) {
2494         h += y;
2495         y = 0;
2496     }
2497     if (x + w > sdl.scr->w)
2498         w = sdl.scr->w - x;
2499     if (y + h > sdl.scr->h)
2500         h = sdl.scr->h - y;
2501     if (w <= 0 || h <= 0)
2502         return;
2503     rects[rect_num].x = x;
2504     rects[rect_num].y = y;
2505     rects[rect_num].w = w;
2506     rects[rect_num].h = h;
2507     rect_num++;
2508 }
2509 
Refresh()2510 void BreakOut::Refresh()
2511 {
2512     if (rect_num == UPDATERECTS)
2513         SDL_UpdateRect(sdl.scr, 0, 0, sdl.scr->w, sdl.scr->h);
2514     else
2515         SDL_UpdateRects(sdl.scr, rect_num, rects);
2516     rect_num = 0;
2517 }
2518 
Life_Hide()2519 void BreakOut::Life_Hide()
2520 {
2521     DR_SETDST(sdl.scr, 0, life_y, brick_w, sdl.scr->h - life_y);
2522     DR_SETSRC(ss_bkgnd, 0, life_y);
2523     SSur_Blit();
2524     AddRefreshRect(0, life_y, sdl.scr->h - life_y, brick_w);
2525 }
2526 
Life_Show()2527 void BreakOut::Life_Show()
2528 {
2529     int limit = player.lives > max_lives ? max_lives : player.lives;
2530     for (int i = 0; i < limit; i++) {
2531         DR_SETDST(sdl.scr, 0, sdl.scr->h - (i + 1) * brick_h, brick_w, brick_h);
2532         DR_SETSRC(ss_life, 0, brick_h);
2533         SSur_Blit();
2534     }
2535     AddRefreshRect(0, life_y, brick_w, sdl.scr->h - life_y);
2536 }
2537 
2538 // score //
Score_Hide()2539 void BreakOut::Score_Hide()
2540 {
2541     DR_SETDST(sdl.scr, score_x, score_y, score_w, score_h);
2542     DR_SETSRC(ss_bkgnd, score_x, score_y);
2543     SSur_Blit();
2544     AddRefreshRect(score_x, score_y, score_w, score_h);
2545 }
2546 
Score_Update(int ms)2547 void BreakOut::Score_Update(int ms)
2548 {
2549     float diff;
2550     float change;
2551 
2552     if ( cur_score < player.score) {
2553 
2554         diff = (float)player.score - cur_score;
2555         change = 0.005 * diff;
2556         if ( change < 0.5 )
2557             change = 0.5;
2558         cur_score += change * ms;
2559         if (cur_score > player.score)
2560             cur_score = player.score;
2561 
2562     }
2563 }
2564 
Score_Show()2565 void BreakOut::Score_Show()
2566 {
2567     char    str[8];
2568     sprintf(str, "%i", (int)cur_score);
2569     score_w = strlen(str) * score_lw;
2570     score_x = sdl.scr->w - score_w - score_xoff;
2571     for (unsigned int i = 0; i < strlen(str); i++) {
2572         DR_SETDST(sdl.scr, score_x + i * score_lw, score_y, score_lw, score_h);
2573         DR_SETSRC(ss_numbers, (str[i] - 48) * score_lw, 0);
2574         SSur_Blit();
2575     }
2576     AddRefreshRect(score_x, score_y, score_w, score_h);
2577 }
2578 
2579 // extras //
Extra_Create(ExtraType extra,int x,int y)2580 void BreakOut::Extra_Create(ExtraType extra, int x, int y)
2581 {
2582     Extra   *e;
2583     e = new Extra;
2584     e->extra = extra;
2585     e->x = x;
2586     e->y = y;
2587     if (!init_data.trp)
2588         e->alpha = 0;
2589     else
2590         e->alpha = 255;
2591     DL_Add(&extra_list, e);
2592 }
2593 
Extra_Use(int e)2594 void BreakOut::Extra_Use(int e)
2595 {
2596     int     i;
2597     Ball    *b;
2598     DL_Entry *ent;
2599 
2600     while( e == EX_RANDOM )
2601         e = rand() % (EX_NUMBER);
2602 
2603     switch (e) {
2604         case EX_SCORE200:
2605             player.score += 200;
2606 #ifdef SOUND
2607     SndSrv_Play(snd_sco, 0);
2608 #endif
2609             break;
2610         case EX_SCORE500:
2611             player.score += 500;
2612 #ifdef SOUND
2613     SndSrv_Play(snd_sco, 0);
2614 #endif
2615             break;
2616         case EX_SCORE1000:
2617             player.score += 1000;
2618 #ifdef SOUND
2619     SndSrv_Play(snd_sco, 0);
2620 #endif
2621             break;
2622         case EX_SCORE2000:
2623             player.score += 2000;
2624 #ifdef SOUND
2625     SndSrv_Play(snd_sco, 0);
2626 #endif
2627             break;
2628         case EX_SCORE5000:
2629             player.score += 5000;
2630 #ifdef SOUND
2631     SndSrv_Play(snd_sco, 0);
2632 #endif
2633             break;
2634         case EX_SCORE10000:
2635             player.score += 10000;
2636 #ifdef SOUND
2637     SndSrv_Play(snd_sco, 0);
2638 #endif
2639             break;
2640         case EX_LIFE:
2641             if (player.lives < diff[init_data.diff].max_lives) {
2642                 player.lives++;
2643 #ifdef SOUND
2644     SndSrv_Play(snd_lup, 0);
2645 #endif
2646             }
2647             else {
2648                 player.score += 10000;
2649 #ifdef SOUND
2650     SndSrv_Play(snd_sco, 0);
2651 #endif
2652             }
2653             break;
2654         case EX_SHORTEN:
2655             Club_Resize(-1);
2656             break;
2657         case EX_LENGTHEN:
2658             Club_Resize(1);
2659             break;
2660         case EX_BALL:
2661             b = Ball_Create(club.x + (club.w - ball_w) / 2, club.y - ball_dia, (rand() % 90) - 45, 0.15);
2662             Ball_MaskV(b, b->v_x);
2663             Ball_GetTarget(b);
2664             DL_Add(&ball_list, b);
2665 #ifdef SOUND
2666     SndSrv_Play(snd_ref, 0);
2667 #endif
2668             break;
2669         case EX_WALL:
2670             if (cur_extras[EX_WALL]) {
2671                 wall_time = 10000;
2672                 break;
2673             }
2674             for (i = 1; i < lev_w - 1; i++) {
2675                 lev_map[i][lev_h - 1].type = MAP_WALL;
2676                 lev_map[i][lev_h - 1].id = 0;
2677             }
2678             wall_time = 10000;
2679             wall_alpha = 255;
2680             Ball_NewTargets(-1, 0);
2681 #ifdef SOUND
2682     SndSrv_Play(snd_wal, 0);
2683 #endif
2684             break;
2685         case EX_METAL:
2686             metal_time = 5000;
2687 #ifdef SOUND
2688     SndSrv_Play(snd_met, 0);
2689 #endif
2690             break;
2691         case EX_FROZEN:
2692             frozen_time = 1000;
2693 #ifdef SOUND
2694     SndSrv_Play(snd_fre, 0);
2695 #endif
2696             break;
2697         case EX_WEAPON:
2698             wpn_time = 5000;
2699 #ifdef SOUND
2700     SndSrv_Play(snd_wea, 0);
2701 #endif
2702             break;
2703         case EX_SLIME:
2704             slime_time = 20000;
2705 #ifdef SOUND
2706     SndSrv_Play(snd_sli, 0);
2707 #endif
2708             break;
2709 
2710         case EX_FAST:
2711 
2712 #ifdef SOUND
2713     SndSrv_Play(snd_speedup, 0);
2714 #endif
2715             if ( cur_extras[EX_SLOW] || cur_extras[EX_FAST] )
2716                 ball_v = ball_old_v;
2717 
2718             if ( cur_extras[EX_SLOW] ) {
2719 
2720                 slow_time = 0;
2721                 cur_extras[EX_SLOW] = 0;
2722 
2723             }
2724             fast_time = 20000;
2725             ball_old_v = ball_v;
2726             ball_v = ball_vm;
2727 
2728             ent = ball_list.head.next;
2729             while ( ent != &ball_list.tail ) {
2730 
2731                 Ball_AdjustSpeed((Ball*)ent->data, ball_v);
2732                 Ball_GetTarget((Ball*)ent->data);
2733                 ent = ent->next;
2734 
2735             }
2736 
2737             break;
2738 
2739         case EX_SLOW:
2740 
2741 #ifdef SOUND
2742     SndSrv_Play(snd_speeddown, 0);
2743 #endif
2744             if ( cur_extras[EX_SLOW] || cur_extras[EX_FAST] )
2745                 ball_v = ball_old_v;
2746 
2747             if ( cur_extras[EX_FAST] ) {
2748 
2749                 fast_time = 0;
2750                 cur_extras[EX_FAST] = 0;
2751 
2752             }
2753             slow_time = 20000;
2754             ball_old_v = ball_v;
2755             ball_v = levels[level].v;
2756 
2757             ent = ball_list.head.next;
2758             while ( ent != &ball_list.tail ) {
2759 
2760                 Ball_AdjustSpeed((Ball*)ent->data, ball_v);
2761                 Ball_GetTarget((Ball*)ent->data);
2762                 ent = ent->next;
2763 
2764             }
2765 
2766             break;
2767 
2768     }
2769     cur_extras[e] = 1;
2770 }
2771 
Extras_Hide()2772 void BreakOut::Extras_Hide()
2773 {
2774     DL_Entry    *e = extra_list.head.next;
2775     Extra       *ex;
2776     int         x, y;
2777     while (e != &extra_list.tail) {
2778         ex = (Extra*)e->data;
2779         x = (int)ex->x;
2780         y = (int)ex->y;
2781         DR_SETDST(sdl.scr, x, y, brick_w, brick_h);
2782         DR_SETSRC(ss_bkgnd, x, y);
2783         SSur_Blit();
2784         AddRefreshRect(x, y, brick_w, brick_h);
2785         e = e->next;
2786     }
2787 }
2788 
Extras_Update(int ms)2789 void BreakOut::Extras_Update(int ms)
2790 {
2791     DL_Entry    *e, *next;
2792     Extra       *ex;
2793     SDL_Event   event;
2794 
2795    // slime
2796     if (cur_extras[EX_SLIME]) {
2797         slime_time -= ms;
2798         if (slime_time < 0) {
2799             cur_extras[EX_SLIME] = 0;
2800             slime_time = 0;
2801         }
2802     }
2803     // weapon
2804     if (cur_extras[EX_WEAPON]) {
2805         wpn_time -= ms;
2806         if (wpn_time < 0) {
2807             cur_extras[EX_WEAPON] = 0;
2808             wpn_time = 0;
2809         }
2810     }
2811     // metal balls //
2812     if (cur_extras[EX_METAL]) {
2813         metal_time -= ms;
2814         if (metal_time < 0) {
2815             cur_extras[EX_METAL] = 0;
2816             metal_time = 0;
2817         }
2818     }
2819     // fast balls //
2820     if ( cur_extras[EX_FAST] ) {
2821 
2822         fast_time -= ms;
2823         if ( fast_time < 0 ) {
2824 
2825             cur_extras[EX_FAST] = 0;
2826             fast_time = 0;
2827             ball_v = ball_old_v;
2828             e = ball_list.head.next;
2829             while ( e != &ball_list.tail ) {
2830 
2831                 Ball_AdjustSpeed((Ball*)e->data, ball_v);
2832                 Ball_GetTarget((Ball*)e->data);
2833                 e = e->next;
2834 
2835             }
2836 
2837         }
2838 
2839     }
2840 
2841     // slow balls //
2842     if ( cur_extras[EX_SLOW] ) {
2843 
2844         slow_time -= ms;
2845         if ( slow_time < 0 ) {
2846 
2847             cur_extras[EX_SLOW] = 0;
2848             slow_time = 0;
2849             ball_v = ball_old_v;
2850             e = ball_list.head.next;
2851             while ( e != &ball_list.tail ) {
2852 
2853                 Ball_AdjustSpeed((Ball*)e->data, ball_v);
2854                 Ball_GetTarget((Ball*)e->data);
2855                 e = e->next;
2856 
2857             }
2858 
2859         }
2860 
2861     }
2862     // frozen paddle //
2863     if (cur_extras[EX_FROZEN]) {
2864         frozen_time -= ms;
2865         if (frozen_time < 0) {
2866             cur_extras[EX_FROZEN] = 0;
2867             frozen_time = 0;
2868           	memset(buttonstate, 0, sizeof(buttonstate));
2869            	memset(keystate, 0, sizeof(keystate));
2870             while (SDL_PollEvent(&event));
2871         }
2872     }
2873 
2874     e = extra_list.head.next;
2875     while (e != &extra_list.tail) {
2876         next = e->next;
2877         ex = (Extra*)e->data;
2878         ex->y += 0.05 * ms;
2879         if (ex->alpha > 0)
2880             ex->alpha -= 0.25 * ms;
2881         if (ex->alpha < 0)
2882             ex->alpha = 0;
2883         if (ex->y >= sdl.scr->h) {
2884             DL_DeleteEntry(&extra_list, e);
2885             e = next;
2886         }
2887         else {
2888             // contact with club core ? //
2889             if (ex->x + brick_w > club.x && ex->x < club.x + club.w - 1 && ex->y + brick_h > club.y && ex->y < club.y + club.h) {
2890                 Extra_Use(ex->extra);
2891                 DL_DeleteEntry(&extra_list, e);
2892             }
2893             e = next;
2894         }
2895     }
2896 }
2897 
Extras_Show()2898 void BreakOut::Extras_Show()
2899 {
2900     DL_Entry    *e = extra_list.head.next;
2901     Extra       *ex;
2902     int         x, y;
2903     while (e != &extra_list.tail) {
2904         ex = (Extra*)e->data;
2905         x = (int)ex->x;
2906         y = (int)ex->y;
2907         DR_SETDST(sdl.scr, x, y, brick_w, brick_h);
2908         DR_SETSRC(ss_extras, (ex->extra - 1) * brick_w, 0);
2909         SSur_AlphaBlit((int)ex->alpha);
2910         AddRefreshRect(x, y, brick_w, brick_h);
2911         e = e->next;
2912     }
2913 }
2914 
2915 // shrapnells //
Shr_Create(int x,int y,int w,int h,float vx,float vy)2916 void BreakOut::Shr_Create(int x, int y, int w, int h, float vx, float vy)
2917 {
2918     Shrapnell *s = new Shrapnell;
2919     s->ss_pic = SSur_Create(w, h, SDL_HWSURFACE);
2920     SDL_SetColorKey(s->ss_pic, 0, 0);
2921     DR_SETDST(s->ss_pic, 0, 0, w, h);
2922     DR_SETSRC(ss_bkgnd, x, y);
2923     SSur_Blit();
2924     s->x = x;
2925     s->y = y;
2926     s->v.x = vx;
2927     s->v.y = vy;
2928     s->alpha = 0;
2929     DL_Add(&shrapnell_list, s);
2930 }
2931 
Shr_Free(void * p)2932 void Shr_Free(void *p)
2933 {
2934     Shrapnell *s = (Shrapnell*)p;
2935     SDL_FreeSurface(s->ss_pic);
2936     delete s;
2937 }
2938 
Shr_Hide()2939 void BreakOut::Shr_Hide()
2940 {
2941     DL_Entry    *e = shrapnell_list.head.next;
2942     Shrapnell   *s;
2943     int         x, y;
2944     while (e != &shrapnell_list.tail) {
2945         s = (Shrapnell*)e->data;
2946         x = (int)s->x;
2947         y = (int)s->y;
2948         DR_SETDST(sdl.scr, x, y, s->ss_pic->w, s->ss_pic->h);
2949         DR_SETSRC(ss_bkgnd, x, y);
2950         SSur_Blit();
2951         AddRefreshRect(x, y, s->ss_pic->w, s->ss_pic->h);
2952         e = e->next;
2953     }
2954 }
2955 
Shr_Update(int ms)2956 void BreakOut::Shr_Update(int ms)
2957 {
2958     DL_Entry    *e, *next;
2959     Shrapnell   *s;
2960     e = shrapnell_list.head.next;
2961     while (e != &shrapnell_list.tail) {
2962         next = e->next;
2963         s = (Shrapnell*)e->data;
2964         s->x += s->v.x * ms;
2965         s->y += s->v.y * ms;
2966         if (s->alpha < 255)
2967             s->alpha += 0.20 * ms;
2968         if (s->alpha > 255 || s->x + s->ss_pic->w < 0 || s->y + s->ss_pic->h < 0 || s->x > sdl.scr->w || s->y > sdl.scr->h)
2969             DL_DeleteEntry(&shrapnell_list, e);
2970         e = next;
2971     }
2972 }
2973 
Shr_Show()2974 void BreakOut::Shr_Show()
2975 {
2976     DL_Entry    *e = shrapnell_list.head.next;
2977     Shrapnell   *s;
2978     int         x, y;
2979     while (e != &shrapnell_list.tail) {
2980         s = (Shrapnell*)e->data;
2981         x = (int)s->x;
2982         y = (int)s->y;
2983         DR_SETDST(sdl.scr, x, y, s->ss_pic->w, s->ss_pic->h);
2984         DR_SETSRC(s->ss_pic, 0, 0);
2985         if (!init_data.trp || s->alpha == 0)
2986             SSur_Blit();
2987         else
2988             SSur_AlphaBlit((int)s->alpha);
2989         AddRefreshRect(x, y, s->ss_pic->w, s->ss_pic->h);
2990         e = e->next;
2991     }
2992 }
2993 
2994 // wall //
Wall_Hide()2995 void BreakOut::Wall_Hide()
2996 {
2997     if (!cur_extras[EX_WALL]) return;
2998     DR_SETDST(sdl.scr, brick_w, (lev_h - 1) * brick_h, brick_w * (lev_w - 2), brick_h);
2999     DR_SETSRC(ss_bkgnd, brick_w, (lev_h - 1) * brick_h);
3000     SSur_Blit();
3001 }
3002 
Wall_Update(int ms)3003 void BreakOut::Wall_Update(int ms)
3004 {
3005     if (!cur_extras[EX_WALL]) return;
3006     int i;
3007     wall_time -= ms;
3008     if (wall_time > 0) {
3009         if (wall_alpha > 0)
3010             wall_alpha -= 0.25 * ms;
3011         if (wall_alpha < 0)
3012             wall_alpha = 0;
3013     }
3014     else {
3015         if (wall_alpha < 255) {
3016             wall_alpha += 0.25 * ms;
3017             if (wall_alpha >= 255) {
3018                 // vanish //
3019                 cur_extras[EX_WALL] = 0;
3020                 for (i = 0; i < lev_w; i++)
3021                     lev_map[i][lev_h - 1].type = MAP_EMPTY;
3022                 if (!init_data.trp) {
3023                     DR_SETDST(sdl.scr, 0, (lev_h - 1) * brick_h, sdl.scr->w, brick_h);
3024                     DR_SETSRC(ss_bkgnd, 0, (lev_h - 1) * brick_h);
3025                     SSur_Blit();
3026                     AddRefreshRect(0, (lev_h - 1) * brick_h, sdl.scr->w, brick_h);
3027                 }
3028                 Ball_NewTargets(-1, 0);
3029             }
3030         }
3031     }
3032 }
3033 
Wall_Show()3034 void BreakOut::Wall_Show()
3035 {
3036     if (!cur_extras[EX_WALL]) return;
3037     int x, y = (lev_h - 1) * brick_h;
3038     for (int i = 1; i < lev_w - 1; i++) {
3039         x = i * brick_w;
3040         DR_SETDST(sdl.scr, x, y, brick_w, brick_h);
3041         DR_SETSRC(ss_bricks, 0, 0);
3042         if (!init_data.trp || wall_alpha == 0)
3043             SSur_Blit();
3044         else
3045             SSur_AlphaBlit((int)wall_alpha);
3046     }
3047     AddRefreshRect(0, y, sdl.scr->w, brick_h);
3048 }
3049 
3050 // plasma weapon //
Wpn_Update(int ms)3051 void BreakOut::Wpn_Update(int ms)
3052 {
3053     wpn_cur += ms * wpn_fpms;
3054     if (wpn_cur >= wpn_fr_num)
3055         wpn_cur -= wpn_fr_num;
3056 }
3057 
3058 // plasma shot //
Shots_Hide()3059 void BreakOut::Shots_Hide()
3060 {
3061     DL_Entry    *e = shot_list.head.next;
3062     Shot        *s;
3063     while (e != &shot_list.tail) {
3064         s = (Shot*)e->data;
3065         DR_SETDST(sdl.scr, (int)s->x, (int)s->y, shot_w, shot_h);
3066         DR_SETSRC(ss_bkgnd, (int)s->x, (int)s->y);
3067         SSur_Blit();
3068         AddRefreshRect((int)s->x, (int)s->y, shot_w, shot_h);
3069         e = e->next;
3070     }
3071 }
3072 
Shots_Update(int ms)3073 void BreakOut::Shots_Update(int ms)
3074 {
3075     DL_Entry    *e = shot_list.head.next, *old_e;
3076     Shot        *s;
3077     while (e != &shot_list.tail) {
3078         s = (Shot*)e->data;
3079         s->cur_fr += ms * shot_fpms;
3080         if (s->cur_fr >= shot_fr_num)
3081             s->cur_fr -= shot_fr_num;
3082         s->y += ms * shot_v_y;
3083         old_e = e; e = e->next;
3084         s->t.cur_tm += ms;
3085         if (s->t.cur_tm > s->t.time) {
3086             Brick_Remove(s->t.mx, s->t.my, 1);
3087             if (s->next_too) {
3088                 Brick_Remove(s->t.mx + 1, s->t.my, 1);
3089                 Shots_NewTargets(s->t.mx + 1, s->t.my);
3090                 Ball_NewTargets(s->t.mx + 1, s->t.my);
3091             }
3092             Ball_NewTargets(s->t.mx, s->t.my);
3093             Shots_NewTargets(s->t.mx, s->t.my);
3094             DL_DeleteEntry(&shot_list, old_e);
3095         }
3096     }
3097     if (cur_extras[EX_WEAPON] && fire_shot && (int)shot_list.counter < max_shots) {
3098         shot_time += ms;
3099         if (shot_time >= shot_delay) {
3100             DL_Add(&shot_list, Shot_Create());
3101             shot_time = 0;
3102         }
3103     }
3104 }
3105 
Shots_Show()3106 void BreakOut::Shots_Show()
3107 {
3108     DL_Entry    *e = shot_list.head.next;
3109     Shot        *s;
3110     while (e != &shot_list.tail) {
3111         s = (Shot*)e->data;
3112         DR_SETDST(sdl.scr, (int)s->x, (int)s->y, shot_w, shot_h);
3113         DR_SETSRC(ss_shot, (int)s->cur_fr * shot_w, 0);
3114         SSur_AlphaBlit(shot_alpha);
3115         AddRefreshRect((int)s->x, (int)s->y, shot_w, shot_h);
3116         e = e->next;
3117     }
3118 }
3119 
Shots_NewTargets(int mx,int my)3120 void BreakOut::Shots_NewTargets(int mx, int my)
3121 {
3122     DL_Entry    *e = shot_list.head.next;
3123     Shot        *s;
3124     while (e != &shot_list.tail) {
3125         s = (Shot*)e->data;
3126         if (s->t.mx == mx && s->t.my == my)
3127             Shot_GetTarget(s);
3128         e = e->next;
3129     }
3130 }
3131 
Shot_Create()3132 Shot* BreakOut::Shot_Create()
3133 {
3134 #ifdef SOUND
3135     SndSrv_Play(snd_shot, 0);
3136 #endif
3137     Shot    *s = new Shot;
3138     s->cur_fr = 0;
3139     s->x = wpn_x + wpn_sx_off;
3140     s->y = wpn_y + wpn_sy_off;
3141     Shot_GetTarget(s);
3142     return s;
3143 }
3144 
Shot_GetTarget(Shot * s)3145 void BreakOut::Shot_GetTarget(Shot *s)
3146 {
3147     int mx = (int)(s->x + 3) / brick_w;
3148     int my = (int)(s->y + 3) / brick_h;
3149     memset(&s->t, 0, sizeof(Target));
3150     while (lev_map[mx][my].type == MAP_EMPTY) my--;
3151     s->t.mx = mx; s->t.my = my;
3152     mx = (int)(s->x + 6) / brick_w;
3153     if (mx != s->t.mx) {
3154         my = (int)(s->y + 3) / brick_h;
3155         while(lev_map[mx][my].type == MAP_EMPTY) my--;
3156         if (my > s->t.my) {
3157             s->t.mx = mx;
3158             s->t.my = my;
3159             s->next_too = 0;
3160         }
3161         else
3162             if (my == s->t.my)
3163                 s->next_too = 1;
3164     }
3165     s->t.cur_tm = 0;
3166     s->t.time = (int)((s->y + 3 - (my * brick_h + brick_h - 1)) / fabs(shot_v_y));
3167 }
3168 
3169 // pause //
Pause()3170 void BreakOut::Pause()
3171 {
3172     SDL_Event event;
3173     int leave = 0;
3174 
3175 #ifdef SOUND
3176     SndSrv_Play(snd_click, 0);
3177 #endif
3178 
3179     if (init_data.warp && init_data.control != 0)
3180     	SDL_SetCursor(original_cur);
3181 
3182     SDL_Surface *buffer = SSur_Create(sdl.scr->w, sdl.scr->h, SDL_HWSURFACE);
3183     SDL_SetColorKey(buffer, 0, 0);
3184 
3185     DR_SETFULLDST(buffer);
3186     DR_SETFULLSRC(sdl.scr);
3187     SSur_Blit();
3188     DR_SETFULLDST(sdl.scr);
3189     SSur_Fill(0x0);
3190     DR_SETFULLSRC(buffer);
3191     SSur_AlphaBlit(128);
3192 
3193     char    str[64];
3194     sprintf(str, "Paused");
3195     SFnt_Write(font, sdl.scr, sdl.scr->w / 2, sdl.scr->h / 2, str, 0);
3196     Sdl_FullUpdate();
3197 
3198     while (!leave) {
3199         if (SDL_PollEvent(&event)) {
3200             switch (event.type) {
3201                 case SDL_KEYUP:
3202                     if (event.key.keysym.sym == SDLK_f) {
3203                         fullscreen = !fullscreen;
3204                         if (fullscreen) {
3205                             Sdl_SetVideoMode(lev_w * brick_w, lev_h * brick_h, 16, SDL_HWSURFACE | SDL_FULLSCREEN);
3206                             SDL_SetCursor(empty_cur);
3207                         }
3208                         else {
3209                             Sdl_SetVideoMode(lev_w * brick_w, lev_h * brick_h, 16, SDL_HWSURFACE);
3210                             if (!init_data.warp && !init_data.control == 0)
3211                             	SDL_SetCursor(original_cur);
3212                         }
3213                         DR_SETFULLDST(sdl.scr);
3214                         DR_SETFULLSRC(buffer);
3215                         SSur_AlphaBlit(128);
3216                         SFnt_Write(font, sdl.scr, sdl.scr->w / 2, sdl.scr->h / 2, str, 0);
3217                         Sdl_FullUpdate();
3218                         break;
3219                     }
3220                     if (event.key.keysym.sym == SDLK_p)
3221                         leave = 1;
3222                     break;
3223                 case SDL_QUIT:
3224                     leave = 1;
3225                     fast_quit = 1;
3226                     break;
3227                 default:
3228                     break;
3229             }
3230         }
3231     }
3232 #ifdef SOUND
3233     SndSrv_Play(snd_click, 0);
3234 #endif
3235 
3236     DR_SETFULLDST(sdl.scr);
3237     DR_SETFULLSRC(buffer);
3238     SSur_Blit();
3239     Sdl_FullUpdate();
3240     SDL_FreeSurface(buffer);
3241 
3242     if (init_data.warp && init_data.control != 0)
3243     	SDL_SetCursor(empty_cur);
3244 }
3245 
3246 // snap shot //
SnapShot()3247 void BreakOut::SnapShot()
3248 {
3249 #ifdef SOUND
3250     SndSrv_Play(snd_click, 0);
3251 #endif
3252 	char filename[32];
3253 	sprintf(filename, "snapshot_%i.bmp", snapshot++);
3254 	SDL_SaveBMP(sdl.scr, filename);
3255 }
3256 
3257 // corner //
Corner_Check(Corner * c,float v_x,float v_y)3258 void BreakOut::Corner_Check(Corner *c, float v_x, float v_y)
3259 {
3260     if (v_x > 0 && v_y > 0 && c->x % brick_w == 0 && c->y % brick_h == 0 )
3261         c->type = UPPERLEFT;
3262     else
3263         if (v_x < 0 && v_y > 0 && (c->x + 1) % brick_w == 0 && c->y % brick_h == 0 )
3264             c->type = UPPERRIGHT;
3265         else
3266             if (v_x > 0 && v_y < 0 && c->x % brick_w == 0 && (c->y + 1) % brick_h == 0 )
3267                 c->type = LOWERLEFT;
3268             else
3269                 if (v_x < 0 && v_y < 0 && (c->x + 1) % brick_w == 0 && (c->y + 1) % brick_h == 0 )
3270                     c->type = LOWERRIGHT;
3271                  else
3272                      c->type = NONE;
3273 }
3274 
3275 // levels //
Levels_OpenFile(char * f_str)3276 FILE* BreakOut::Levels_OpenFile(char *f_str)
3277 {
3278     if (init_data.lvl_path[0] == 0 || init_data.lvl_file[0] == 0) {
3279         printf("empty path or file - using original levels\n");
3280         levels = orig_levels;
3281         return 0;
3282     }
3283     if (init_data.lvl_path[0] == '~') {
3284         strcpy(f_str, home_dir);
3285         if (strlen(init_data.lvl_path) > 1)
3286             strcat(f_str, init_data.lvl_path + 1);
3287     }
3288     else
3289         strcpy(f_str, init_data.lvl_path);
3290     if (f_str[strlen(f_str) - 1] != '/')
3291         strcat(f_str, "/");
3292     strcat(f_str, init_data.lvl_file);
3293     FILE *f = fopen(f_str, "r");
3294     return f;
3295 }
3296 
Levels_LoadFromFile()3297 void BreakOut::Levels_LoadFromFile()
3298 {
3299     char f_str[64];
3300     FILE *f;
3301     if ((f = Levels_OpenFile(f_str)) == 0) {
3302         // not found; switch back to original levels
3303         printf("cannot find '%s' - using original levels\n", f_str);
3304         UseOrigLevels();
3305     }
3306     else {
3307         // exists //
3308         printf("attempting to parse file '%s'...\n", f_str);
3309         if (Levels_ParseFile(f))
3310             levels = file_levels;
3311         else {
3312             printf("failed somewhere in Levels_ParseFile() - using original levels\n");
3313             if (file_levels) Levels_Delete();
3314             UseOrigLevels();
3315         }
3316         fclose(f);
3317     }
3318 }
3319 
NextLine(char * buf,char * cp,char * l)3320 char* BreakOut::NextLine(char *buf, char *cp, char *l)
3321 {
3322     char *bgn = cp;
3323     while (*cp != 10 && cp < l) cp++;
3324     if (buf != 0 && bgn != cp) {
3325         strncpy(buf, bgn, cp - bgn);
3326         buf[cp - bgn] = 0;
3327     }
3328     while (*cp == 10 && cp < l) cp++;
3329     return cp;
3330 }
3331 
ParseEntry(char * buf,char * cp,char * l)3332 char* BreakOut::ParseEntry(char *buf, char *cp, char *l)
3333 {
3334     // look up entry from cp to next ';'
3335     // returns are converted to spaces
3336     char *bgn = cp;
3337     while (*cp != ';' && cp < l) cp++;
3338     if (buf != 0 && bgn != cp) {
3339         strncpy(buf, bgn, cp - bgn);
3340         buf[cp - bgn] = 0;
3341         for (unsigned int i = 0; i < strlen(buf); i++)
3342             if (buf[i] == 10)
3343                 buf[i] = 32;
3344     }
3345     while (*cp == ';' && cp < l) cp++;
3346     while (*cp == 10 && cp < l) cp++;
3347     return cp;
3348 }
3349 
Levels_ParseFile(FILE * f)3350 int BreakOut::Levels_ParseFile(FILE *f)
3351 {
3352     int i, j, k, l;
3353     char entry[256];
3354     char *cur_pos, *limit;
3355     char *str_lb = "[LEV";
3356     char *str_mb = "[MAP";
3357     char *str_eb = "[EXT";
3358     char *str_e = "[END";
3359     char extras[EX_NUMBER + 1] = {'0','1','2','3','4','5','b','l','+','-','s','m','w','p','?','>','<',' '};
3360     char bricks[11] = {'a','b','c','d','e','f','g','h','i','#',' '};
3361 
3362     // load file into mem //
3363     fseek(f, 0, SEEK_END);
3364     int fsize = ftell(f);
3365     char *fbuf = new char[fsize];
3366     fseek(f, 0, SEEK_SET);
3367     fread(fbuf, fsize, 1, f);
3368     cur_pos = fbuf;
3369     limit = fbuf + fsize;
3370 
3371     // how many levels ? //
3372     lev_num = 0;
3373     while (cur_pos < fbuf + fsize) {
3374         if (!strncmp(cur_pos, str_lb, strlen(str_lb)))
3375             lev_num++;
3376         cur_pos = NextLine(0, cur_pos, limit);
3377     }
3378     printf("levels found: %i...\n", lev_num);
3379     if (lev_num == 0) {
3380         printf("sorry but no game without any levels...\n");
3381         return 0;
3382     }
3383     file_levels = new Level[lev_num];
3384     memset(file_levels, 0, sizeof(Level) * lev_num);
3385 
3386     // parse levels //
3387     cur_pos = fbuf;
3388     for (i = 0; i < lev_num; i++) {
3389         // [LEVEL]
3390         cur_pos = ParseEntry(entry, cur_pos, limit);
3391 
3392         // Author
3393         cur_pos = ParseEntry(entry, cur_pos, limit);
3394         strcpy(file_levels[i].author, entry);
3395         // Name
3396         cur_pos = ParseEntry(entry, cur_pos, limit);
3397         strcpy(file_levels[i].name, entry);
3398         // start velocity
3399         cur_pos = ParseEntry(entry, cur_pos, limit);
3400         file_levels[i].v = strtod(entry, 0);
3401         // velocity change
3402         cur_pos = ParseEntry(entry, cur_pos, limit);
3403         file_levels[i].v_pms = strtod(entry, 0);
3404         // max velocity
3405         cur_pos = ParseEntry(entry, cur_pos, limit);
3406         file_levels[i].vm = strtod(entry, 0);
3407         // map width
3408         cur_pos = ParseEntry(entry, cur_pos, limit);
3409         file_levels[i].w = atoi(entry);
3410         // map height
3411         cur_pos = ParseEntry(entry, cur_pos, limit);
3412         file_levels[i].h = atoi(entry);
3413 
3414         // check size //
3415         if (file_levels[i].w < 14 || file_levels[i].w > MAX_MAP_W - 2 || file_levels[i].h < 18 || file_levels[i].h > MAX_MAP_H - 2) {
3416             printf("parse error in level %i: size out of range (w: 14->%i; h: 18->%i)\n", i, MAX_MAP_W - 2, MAX_MAP_H - 2);
3417             return 0;
3418         }
3419 
3420         // create map & extras
3421         file_levels[i].map = new char[file_levels[i].w * file_levels[i].h];
3422         file_levels[i].extras = new char[file_levels[i].w * file_levels[i].h];
3423 
3424         // [MAP]
3425         cur_pos = ParseEntry(entry, cur_pos, limit);
3426         if (strncmp(entry, str_mb, strlen(str_mb))) {
3427             printf("parse error in level %i: [MAP] expected\n", i);
3428             return 0;
3429         }
3430 
3431         // read map
3432         for (j = 0; j < file_levels[i].h; j++) {
3433             cur_pos = ParseEntry(entry, cur_pos, limit);
3434             // wrong size?
3435             if (strlen(entry) != (unsigned)file_levels[i].w) {
3436                 printf("parse error in map of level %i, line %i: invalid length %i\n", i, j, strlen(entry));
3437                 return 0;
3438             }
3439             // bad brick ?
3440             for (k = 0; k < file_levels[i].w; k++) {
3441                 for (l = 0; l < 11; l++)
3442                     if (entry[k] == bricks[l])
3443                         break;
3444                 if (l == 11)
3445                     printf("warning: in map of level %i, line %i: unknown brick '%c'\n", i, j, entry[k]);
3446             }
3447             strncpy(file_levels[i].map + j * file_levels[i].w, entry, strlen(entry));
3448         }
3449 
3450         //[EXTRAS]
3451         cur_pos = ParseEntry(entry, cur_pos, limit);
3452         if (strncmp(entry, str_eb, strlen(str_eb))) {
3453             printf("parse error in level %i: [EXTRAS] expected\n", i);
3454             return 0;
3455         }
3456 
3457         // read extras
3458         for (j = 0; j < file_levels[i].h; j++) {
3459             cur_pos = ParseEntry(entry, cur_pos, limit);
3460             // wrong size ?
3461             if (strlen(entry) != (unsigned)file_levels[i].w) {
3462                 printf("warning in extra map of level %i, line %i: invalid length %i\n", i, j, strlen(entry));
3463                 return 0;
3464             }
3465             // bad extra ?
3466             for (k = 0; k < file_levels[i].w; k++) {
3467                 for (l = 0; l < EX_NUMBER; l++)
3468                     if (entry[k] == extras[l])
3469                         break;
3470                 if (l == EX_NUMBER)
3471                     printf("warning: in extra map of level %i, line %i: unknown extra %c\n", i, j, entry[k]);
3472             }
3473             strncpy(file_levels[i].extras + j * file_levels[i].w, entry, strlen(entry));
3474         }
3475 
3476         // [END]
3477         cur_pos = ParseEntry(entry, cur_pos, limit);
3478         if (strncmp(entry, str_e, strlen(str_e))) {
3479             printf("parse error in level %i: [END] expected\n", i);
3480             return 0;
3481         }
3482     }
3483 
3484     // free mem
3485     delete fbuf;
3486 
3487     return 1;
3488 }
3489 
Levels_Delete()3490 void BreakOut::Levels_Delete()
3491 {
3492     // delete levels loaded from file //
3493     if (file_levels == 0) return;
3494     for (int i = 0; i < lev_num; i++) {
3495         if (file_levels[i].map)
3496             delete file_levels[i].map;
3497         if (file_levels[i].extras)
3498             delete file_levels[i].extras;
3499     }
3500     delete file_levels;
3501     file_levels = 0;
3502 }
3503 
UseOrigLevels()3504 void BreakOut::UseOrigLevels()
3505 {
3506     levels = orig_levels;
3507     lev_num = LEVEL_NUM;
3508 }
3509 
3510 // shine //
Sh_Hide()3511 void BreakOut::Sh_Hide()
3512 {
3513     if (sh_x == 0 && sh_y == 0) return;
3514     DR_SETDST(sdl.scr, sh_x, sh_y, brick_w, brick_h);
3515     DR_SETSRC(ss_bkgnd, sh_x, sh_y);
3516     SSur_Blit();
3517     AddRefreshRect(sh_x, sh_y, brick_w, brick_h);
3518 }
3519 
Sh_Update(int ms)3520 void BreakOut::Sh_Update(int ms)
3521 {
3522     if (sh_x == 0 && sh_y == 0)
3523         Sh_New();
3524     else {
3525         sh_cur += sh_pms * ms;
3526         if (sh_cur > sh_fr)
3527             Sh_New();
3528     }
3529 }
3530 
Sh_Show()3531 void BreakOut::Sh_Show()
3532 {
3533     if (sh_x == 0 && sh_y == 0) return;
3534     DR_SETDST(sdl.scr, sh_x, sh_y, brick_w, brick_h);
3535     DR_SETSRC(ss_sh, (int)sh_cur * brick_w, 0);
3536     SSur_Blit();
3537     AddRefreshRect(sh_x, sh_y, brick_w, brick_h);
3538 }
3539 
Sh_New()3540 void BreakOut::Sh_New()
3541 {
3542     int x_add, y_add, x, y;
3543     sh_cur = 0;
3544     sh_x = 0; sh_y = 0;
3545 
3546     x = (rand() % (brick_w - 2)) + 1;
3547     y = (rand() % (brick_h - 2)) + 1;
3548     x_add = rand() % 2 == 0 ? 1 : -1;
3549     y_add = rand() % 2 == 0 ? 1 : -1;
3550 
3551     while (x > 0 && x < lev_w - 1 && y > 0 && y <lev_h - 1) {
3552         if (lev_map[x][y].type != MAP_EMPTY && lev_map[x][y].id < 4) {
3553             sh_x = x * brick_w;
3554             sh_y = y * brick_h;
3555             break;
3556         }
3557         x += x_add; y += y_add;
3558     }
3559 }
3560 
3561 // credit //
Credit_Hide()3562 void BreakOut::Credit_Hide()
3563 {
3564     if (cr_status == 3) return;
3565     DR_SETDST(sdl.scr, cr_x, cr_y, cr_w, cr_h);
3566     DR_SETSRC(ss_bkgnd, cr_x, cr_y);
3567     SSur_Blit();
3568     AddRefreshRect(cr_x, cr_y, cr_w, cr_h);
3569 }
3570 
Credit_Update(int ms)3571 void BreakOut::Credit_Update(int ms)
3572 {
3573     if (cr_status == 0) {
3574         cr_alpha -= cr_pms * ms;
3575         if (cr_alpha <= 0) {
3576             cr_status = 1;
3577             cr_alpha = 0;
3578         }
3579     }
3580     else
3581         if (cr_status == 1) {
3582             cr_cur += ms;
3583             if (cr_cur > cr_time)
3584                 cr_status = 2;
3585         }
3586         else
3587             if (cr_status == 2) {
3588                 cr_alpha += cr_pms * ms;
3589                 if (cr_alpha >= 255)
3590                     cr_status = 3;
3591             }
3592 }
3593 
Credit_Show()3594 void BreakOut::Credit_Show()
3595 {
3596     if (cr_status == 3) return;
3597     font->algn = TA_X_LEFT | TA_Y_TOP;
3598     SFnt_Write(font, sdl.scr, cr_x, cr_y, cr_str, (int)cr_alpha);
3599     font->algn = TA_X_CENTER | TA_Y_CENTER;
3600     AddRefreshRect(cr_x, cr_y, cr_w, cr_h);
3601 }
3602 
Credit_Init()3603 void BreakOut::Credit_Init()
3604 {
3605     cr_cur = 0; cr_alpha = 255; cr_status = 0;
3606 
3607     // string //
3608     if (init_data.lvls_frm_file)
3609         sprintf(cr_str, "%s (%s)", lev_name, lev_author);
3610     else
3611         sprintf(cr_str, "%s", lev_name);
3612 
3613     // position //
3614     cr_w = SFnt_TextWidth(font, cr_str);
3615     cr_h = font->lh;
3616     cr_x = (sdl.scr->w - cr_w) / 2;
3617     cr_y = sdl.scr->h - 2 - cr_h;
3618 }
3619 
3620 // extra display //
ExDisp_Hide()3621 void BreakOut::ExDisp_Hide()
3622 {
3623     int i;
3624 
3625     if ( init_data.no_exdisp ) return;
3626 
3627     ed_x = sdl.scr->w - brick_w;
3628 
3629     for ( i = 0; i < EX_NUMBER; i++ )
3630         if ( ed_offsets[i] != 0 && (!cur_extras[i] || (i == EX_WALL && wall_time <= 0) ) ) {
3631 
3632             DR_SETDST(sdl.scr, ed_x, ed_y + ed_offsets[i], brick_w, brick_h);
3633             DR_SETSRC(ss_bkgnd, ed_x, ed_y + ed_offsets[i]);
3634             SSur_Blit();
3635             AddRefreshRect(ed_x, ed_y + ed_offsets[i], brick_w, brick_h);
3636 
3637         }
3638 
3639 }
3640 
ExDisp_Show()3641 void BreakOut::ExDisp_Show()
3642 {
3643     int i, j;
3644     char str[12];
3645     int w, x;
3646 
3647     if ( init_data.no_exdisp ) return;
3648 
3649     ed_x = sdl.scr->w - brick_w;
3650 
3651     font->algn = TA_X_CENTER | TA_Y_CENTER;
3652 
3653     for ( i = 0; i < EX_NUMBER; i++ )
3654         if ( ed_offsets[i] != 0 && cur_extras[i] ) {
3655 
3656             if (i == EX_WALL && wall_time <= 0) continue;
3657 
3658             // picture
3659             DR_SETDST(sdl.scr, ed_x, ed_y + ed_offsets[i], brick_w, brick_h);
3660             SSur_Fill(0x0);
3661             DR_SETSRC(ss_extras, ( i - 1 ) * brick_w, 0);
3662             SSur_AlphaBlit(128);
3663 
3664             // remaining time
3665             switch ( i ) {
3666 
3667                 case EX_SLIME: sprintf(str, "%i", (slime_time / 1000) + 1); break;
3668                 case EX_METAL: sprintf(str, "%i", (metal_time / 1000) + 1); break;
3669                 case EX_FAST: sprintf(str, "%i", (fast_time / 1000) + 1); break;
3670                 case EX_SLOW: sprintf(str, "%i", (slow_time / 1000) + 1); break;
3671                 case EX_WALL: sprintf(str, "%i", (wall_time / 1000) + 1); break;
3672                 case EX_WEAPON: sprintf(str, "%i", (wpn_time / 1000) + 1); break;
3673 
3674             }
3675             w = strlen(str) * score_lw;
3676             x = ed_x + (brick_w - w) / 2;
3677             for ( j = 0; j < strlen(str); j++ ) {
3678 
3679                 DR_SETDST(sdl.scr, x + j * score_lw, ed_y + ed_offsets[i] + (brick_h - score_h) / 2, score_lw, score_h);
3680                 DR_SETSRC(ss_numbers, (str[j] - 48) * score_lw, 0);
3681                 SSur_Blit();
3682 
3683             }
3684 
3685             AddRefreshRect(ed_x, ed_y + ed_offsets[i], brick_w, brick_h);
3686 
3687         }
3688 
3689 }
3690