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