1 /***************************************************************************
2                           bricks.c  -  description
3                              -------------------
4     begin                : Thu Sep 6 2001
5     copyright            : (C) 2001 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 "../client/lbreakout.h"
19 #include "extras.h"
20 #include "balls.h"
21 #include "bricks.h"
22 #include "mathfuncs.h"
23 
24 extern Game *cur_game;
25 extern int ball_dia;
26 
27 /* extras conversion table */
28 Extra_Conv extra_conv_table[EX_NUMBER] = {
29 	{ EX_SCORE200,   '0' },
30 	{ EX_SCORE500,   '1' },
31 	{ EX_SCORE1000,  '2' },
32 	{ EX_SCORE2000,  '3' },
33 	{ EX_SCORE5000,  '4' },
34 	{ EX_SCORE10000, '5' },
35 	{ EX_GOLDSHOWER, 'g' },
36 	{ EX_LENGTHEN,   '+' },
37 	{ EX_SHORTEN,    '-' },
38 	{ EX_LIFE,       'l' },
39 	{ EX_SLIME,      's' },
40 	{ EX_METAL,      'm' },
41 	{ EX_BALL,       'b' },
42 	{ EX_WALL,       'w' },
43 	{ EX_FROZEN,     'f' },
44 	{ EX_WEAPON,     'p' },
45 	{ EX_RANDOM,     '?' },
46 	{ EX_FAST,       '>' },
47 	{ EX_SLOW,       '<' },
48 	{ EX_JOKER,      'j' },
49 	{ EX_DARKNESS,   'd' },
50 	{ EX_CHAOS,      'c' },
51 	{ EX_GHOST_PADDLE, '~' },
52 	{ EX_DISABLE,      '!' },
53 	{ EX_TIME_ADD,     '&' },
54 	{ EX_EXPL_BALL,    '*' },
55 	{ EX_BONUS_MAGNET, '}' },
56 	{ EX_MALUS_MAGNET, '{' },
57 	{ EX_WEAK_BALL,    'W' }
58 };
59 /* brick conversion table: brick id, char */
60 Brick_Conv brick_conv_table[BRICK_COUNT] = {
61 	{ 'E', MAP_WALL,        0, -1, 0 },
62 	{ '#', MAP_BRICK,       1, -1, 1000 },
63 	{ '@', MAP_BRICK_CHAOS, 2, -1, 1000 },
64 	{ 'a', MAP_BRICK,       3,  1, BRICK_SCORE * 1 },
65 	{ 'b', MAP_BRICK,       4,  2, BRICK_SCORE * 2 },
66 	{ 'c', MAP_BRICK,       5,  3, BRICK_SCORE * 3 },
67 	{ 'v', MAP_BRICK,       6,  4, BRICK_SCORE * 4 },
68 	{ 'x', MAP_BRICK_HEAL,  7,  1, BRICK_SCORE * 2},
69 	{ 'y', MAP_BRICK_HEAL,  8,  2, BRICK_SCORE * 4},
70 	{ 'z', MAP_BRICK_HEAL,  9,  3, BRICK_SCORE * 6},
71 	{ 'd', MAP_BRICK,      10,  1, BRICK_SCORE },
72 	{ 'e', MAP_BRICK,      11,  1, BRICK_SCORE },
73 	{ 'f', MAP_BRICK,      12,  1, BRICK_SCORE },
74 	{ 'g', MAP_BRICK,      13,  1, BRICK_SCORE },
75 	{ 'h', MAP_BRICK,      14,  1, BRICK_SCORE },
76 	{ 'i', MAP_BRICK,      15,  1, BRICK_SCORE },
77 	{ 'j', MAP_BRICK,      16,  1, BRICK_SCORE },
78 	{ 'k', MAP_BRICK,      17,  1, BRICK_SCORE },
79 	{ '*', MAP_BRICK_EXP,  18,  1, BRICK_SCORE * 2 },
80 	{ '!', MAP_BRICK_GROW, GROW_BRICK_ID,  1, BRICK_SCORE * 2 },
81 	/* grown bricks use these ids to be distinguished for warp limit;
82 	 * id remains the same! regular bricks d and e are not used since
83 	 * E is in use already. */
84 	{ 'F', MAP_BRICK,      12,  1, BRICK_SCORE },
85 	{ 'G', MAP_BRICK,      13,  1, BRICK_SCORE },
86 	{ 'H', MAP_BRICK,      14,  1, BRICK_SCORE },
87 	{ 'I', MAP_BRICK,      15,  1, BRICK_SCORE },
88 	{ 'J', MAP_BRICK,      16,  1, BRICK_SCORE },
89 	{ 'K', MAP_BRICK,      17,  1, BRICK_SCORE },
90 
91 };
92 
93 /*
94 ====================================================================
95 Locals
96 ====================================================================
97 */
98 
99 static void brick_create_instable( Game *game, int life_time );
100 
101 /*
102 ====================================================================
103 Initiate a brick explosion.
104 ====================================================================
105 */
brick_start_expl(int x,int y,int time,Paddle * paddle)106 void brick_start_expl( int x, int y, int time, Paddle *paddle )
107 {
108 	cur_game->bricks[x][y].exp_time = time;
109 	cur_game->bricks[x][y].exp_paddle = paddle;
110 	cur_game->bricks[x][y].mx = x; cur_game->bricks[x][y].my = y;
111 	list_add( cur_game->exp_bricks, &cur_game->bricks[x][y] );
112 }
113 
114 /*
115 ====================================================================
116 Grow a brick at mx,my if the ball does not block the tile and the
117 tile is not blocked by a brick.
118 ====================================================================
119 */
brick_grow(int mx,int my,int id)120 static void brick_grow( int mx, int my, int id )
121 {
122 	Ball *ball;
123 
124         /* tile empty? */
125 	if (cur_game->bricks[mx][my].type!=MAP_EMPTY) return;
126 
127 	/* check all balls */
128 	list_reset( cur_game->balls );
129 	while ( (ball = list_next( cur_game->balls )) != 0 )  {
130 		if ( mx == (ball->x) / BRICK_WIDTH )
131 		if ( my == (ball->y) / BRICK_HEIGHT )
132 			return;
133 		if ( mx == (ball->x + ball_dia) / BRICK_WIDTH )
134 		if ( my == (ball->y) / BRICK_HEIGHT )
135 			return;
136 		if ( mx == (ball->x) / BRICK_WIDTH )
137 		if ( my == (ball->y + ball_dia) / BRICK_HEIGHT )
138 			return;
139 		if ( mx == (ball->x + ball_dia) / BRICK_WIDTH )
140 		if ( my == (ball->y + ball_dia) / BRICK_HEIGHT )
141 			return;
142 	}
143 
144 	/* add brick */
145 	cur_game->bricks[mx][my].brick_c = brick_conv_table[id].c;
146 	cur_game->bricks[mx][my].id = brick_conv_table[id].id;
147 	cur_game->bricks[mx][my].type = brick_conv_table[id].type;
148 	cur_game->bricks[mx][my].score = brick_conv_table[id].score;
149 	cur_game->bricks[mx][my].dur = brick_conv_table[id].dur;
150 	/* XXX mark grown bricks by upper case. with this trick we can store
151 	 * this information in the level snapshot. */
152 	cur_game->bricks[mx][my].brick_c -= 32; /* f->F, ... */
153 	/* keep the extra that is already assigned to this position */
154 	cur_game->bricks[mx][my].exp_time = -1;
155 	cur_game->bricks[mx][my].heal_time = -1;
156 	/* adjust brick count */
157 	cur_game->bricks_left++;
158 	cur_game->brick_count++;
159 	/* adjust warp limit (grown bricks don't help hitting the limit) */
160 	cur_game->warp_limit++;
161 	/* add modification */
162 	bricks_add_mod( mx, my, HT_GROW, 0, vector_get(0,0), 0 );
163 
164 	/* get new targets */
165 	balls_check_targets( -1, 0 );
166 }
167 
168 /*
169 ====================================================================
170 Remove brick from offscreen and screen.
171 Create shrapnells by type and impulse.
172 'paddle' is the paddle that initiated hit either by shot or ball.
173 ====================================================================
174 */
brick_remove(int mx,int my,int type,Vector imp,Paddle * paddle)175 void brick_remove( int mx, int my, int type, Vector imp, Paddle *paddle )
176 {
177 	int i,j,px,py;
178 	int dir;
179 
180 	/* if explosive set exp_time of surrounding bricks */
181 	if ( cur_game->bricks[mx][my].type == MAP_BRICK_EXP ) {
182 		for ( i = mx - 1; i <= mx + 1; i++ )
183 		for ( j = my - 1; j <= my + 1; j++ )
184 			if ( i != mx || j != my )
185 			if ( cur_game->bricks[i][j].type != MAP_EMPTY )
186 			if ( cur_game->bricks[i][j].dur > 0 )
187 			if ( cur_game->bricks[i][j].exp_time == -1 )
188 				brick_start_expl( i,j,BRICK_EXP_TIME,paddle );
189 	}
190 	if ( cur_game->bricks[mx][my].type == MAP_BRICK_GROW ) {
191 		for ( i = mx - 1; i <= mx + 1; i++ )
192 		for ( j = my - 1; j <= my + 1; j++ )
193 			if ( cur_game->bricks[i][j].type == MAP_EMPTY )
194 				brick_grow( i, j, RANDOM( BRICK_GROW_FIRST, BRICK_GROW_LAST ) );
195 	}
196 
197 	/* decrease brick count if no indestructible brick was destroyed */
198 	if ( cur_game->bricks[mx][my].dur != -1 ) {
199 		--cur_game->bricks_left;
200 
201 		/* adjust warp limit which was increased for grown brick (since
202 		 * these don't count for warp). */
203 		if (IS_GROWN_BRICK_CHAR(cur_game->bricks[mx][my].brick_c))
204 			cur_game->warp_limit--;
205 
206 		/* update stats */
207 		paddle->bricks_cleared++;
208 	}
209 
210 	/* remove brick from map */
211 	cur_game->bricks[mx][my].id = -1;
212 	cur_game->bricks[mx][my].dur = -1;
213 	cur_game->bricks[mx][my].exp_time = -1;
214 	cur_game->bricks[mx][my].heal_time = -1;
215 	cur_game->bricks[mx][my].type = MAP_EMPTY;
216 	cur_game->bricks[mx][my].brick_c = ' ';
217 
218 	px = mx*BRICK_WIDTH;
219 	py = my*BRICK_HEIGHT;
220 
221 	/* release extra if one exists */
222 	dir = ( paddle->type == PADDLE_TOP ) ? -1 : 1;
223 	if ( cur_game->bricks[mx][my].extra != EX_NONE ) {
224 		if ( cur_game->diff->allow_maluses ||
225 		     !extra_is_malus( cur_game->bricks[mx][my].extra ) )
226 			list_add( cur_game->extras,
227 			   	  extra_create( cur_game->bricks[mx][my].extra, px, py, dir ) );
228 	}
229 	else if ( paddle->extra_active[EX_GOLDSHOWER] )
230 		list_add( cur_game->extras,
231 				  extra_create( EX_SCORE1000, px, py, dir ) );
232 	cur_game->bricks[mx][my].extra = EX_NONE;
233 	cur_game->bricks[mx][my].extra_c = ' ';
234 
235 	/* add score */
236 	paddle->score += cur_game->bricks[mx][my].score;
237 
238 }
239 
240 /* Set a brick at position by looking up id in conv table.
241    (score is not set,brickcount is not set)
242    If id is -1 the brick is cleared. */
brick_set_by_id(Game * game,int mx,int my,int id)243 static void brick_set_by_id( Game *game, int mx, int my, int id )
244 {
245     int k;
246     if (id==-1)
247     {
248         game->bricks[mx][my].id = -1;
249         game->bricks[mx][my].dur = -1;
250         game->bricks[mx][my].type = MAP_EMPTY;
251         game->bricks[mx][my].brick_c = ' ';
252         game->bricks[mx][my].extra_c = ' ';
253         game->bricks[mx][my].extra   = EX_NONE;
254         game->bricks[mx][my].score = 0;
255         return;
256     }
257     for (k=0;k<BRICK_COUNT;k++)
258         if (brick_conv_table[k].id==id)
259         {
260             game->bricks[mx][my].exp_time = -1;
261             game->bricks[mx][my].heal_time = -1;
262             game->bricks[mx][my].brick_c = brick_conv_table[k].c;
263             game->bricks[mx][my].type = brick_conv_table[k].type;
264             game->bricks[mx][my].id = brick_conv_table[k].id;
265             game->bricks[mx][my].dur = brick_conv_table[k].dur;
266             game->bricks[mx][my].extra_c = ' ';
267             game->bricks[mx][my].extra   = EX_NONE;
268             break;
269         }
270 }
271 
272 /* Create a barrier of 'level' many full lines starting in the forth row
273    from the top. In the most upper line we have all explosive bricks. Set
274    max score and move delay as well. */
bricks_create_barrier(Game * game,int level)275 static void bricks_create_barrier( Game *game, int level)
276 {
277     int i,j,barrierSize=2+level;
278     if (barrierSize>12) barrierSize = 12;
279     game->brick_count = game->bricks_left = 0;
280     for (i = 1; i < MAP_WIDTH-1; i++)
281         for (j = 1; j < MAP_HEIGHT-1; j++)
282             brick_set_by_id(game,i,j,-1);
283     for (i=1;i<MAP_WIDTH-1;i++)
284     {
285         brick_set_by_id(game,i,1,GROW_BRICK_ID);
286         game->brick_count++; game->bricks_left++;
287     }
288     for (j=1;j<1+barrierSize;j++)
289         for (i=1;i<MAP_WIDTH-1;i++)
290         {
291             brick_set_by_id(game,i,j+1,10+j%8);
292             game->brick_count++; game->bricks_left++;
293         }
294     game->blBarrierMaxMoves = MAP_HEIGHT - 4 - barrierSize;
295     game->blRefreshBricks = 1;
296 	/* get new targets */
297 	balls_check_targets( -1, 0 );
298 }
299 
300 /* Move barrier one down, that is move all bricks below third line down.
301    If this puts one brick into the way of the paddle, set paddleHit which
302    finishs the level. */
bricks_move_barrier(Game * game,int * paddleHit)303 static void bricks_move_barrier( Game *game, int *paddleHit )
304 {
305     Ball *ball = (Ball*)(cur_game->balls->head->next->item) /* we have only one ball */;
306     int i,j,blocked;
307     *paddleHit = 0;
308     for (j=MAP_HEIGHT-3;j>2;j--)
309         for (i=1;i<MAP_WIDTH-1;i++)
310         {
311             /* check whether i,j is blocked by the ball */
312             blocked = 0;
313             if ( i == (ball->x) / BRICK_WIDTH )
314             if ( j == (ball->y) / BRICK_HEIGHT )
315                 blocked=1;
316             if ( i == (ball->x + ball_dia) / BRICK_WIDTH )
317             if ( j == (ball->y) / BRICK_HEIGHT )
318                 blocked=1;
319             if ( i == (ball->x) / BRICK_WIDTH )
320             if ( j == (ball->y + ball_dia) / BRICK_HEIGHT )
321                 blocked=1;
322             if ( i == (ball->x + ball_dia) / BRICK_WIDTH )
323             if ( j == (ball->y + ball_dia) / BRICK_HEIGHT )
324                 blocked=1;
325             if (!blocked)
326                 brick_set_by_id(game,i,j,game->bricks[i][j-1].id);
327             brick_set_by_id(game,i,j-1,-1);
328         }
329     cur_game->blBarrierMoves++;
330     if (cur_game->blBarrierMoves==cur_game->blBarrierMaxMoves)
331         *paddleHit = 1;
332     game->blRefreshBricks = 1;
333 	/* get new targets */
334 	balls_check_targets( -1, 0 );
335 }
336 
337 /* reset the ball to top and restart release delay */
attach_ball_to_ceiling(Game * game)338 static void attach_ball_to_ceiling( Game *game )
339 {
340     Ball *ball = list_first(game->balls);
341     ball->x = (BRICK_WIDTH*MAP_WIDTH-ball_dia)/2;
342 	ball->cur.x = ball->x;
343 	ball->y = BRICK_HEIGHT;
344 	ball->cur.y = ball->y;
345     ball->attached = 0;
346     ball->vel.x = ball->vel.y = 0;
347     ball_clear_target(&ball->target);
348     game->blBallAttached = 1;
349     game->blActionTime = 1000; /* time until ball released */
350     delay_set(&game->blDelay,game->blActionTime);
351 }
352 
353 /* select a sitting duck and highlight it */
select_random_duck(Game * game)354 static void select_random_duck( Game *game )
355 {
356     int i, k;
357     /* go to first correct brick */
358     i = 0;
359     while (game->blDuckPositions[i<<1]==-1) i++;
360     /* get number to go */
361     k = rand()%game->blNumDucks;
362     /* go */
363     while (k>0)
364     {
365         do {i++;} while (game->blDuckPositions[i<<1]==-1);
366         k--;
367     }
368     game->blCurrentDuck = i;
369     brick_set_by_id(game,game->blDuckPositions[game->blCurrentDuck*2],
370                     game->blDuckPositions[game->blCurrentDuck*2+1], 5);
371 	bricks_add_grow_mod(game->blDuckPositions[game->blCurrentDuck*2],game->blDuckPositions[game->blCurrentDuck*2+1],5);
372 }
373 
bricks_create_new_prey(Game * game,int mx,int my)374 static void bricks_create_new_prey( Game *game, int mx, int my)
375 {
376     /* get position */
377     while (mx==-1||my==-1||game->bricks[mx][my].type!=MAP_EMPTY)
378     {
379         if (mx==-1) mx = RANDOM(game->blHunterAreaX1+2,game->blHunterAreaX2-2);
380         if (my==-1) my = RANDOM(game->blHunterAreaY1+2,game->blHunterAreaY2-2);
381     }
382     /* set brick */
383     brick_set_by_id(game,mx,my,game->blHunterPreyId);
384 	bricks_add_grow_mod(mx,my,game->blHunterPreyId);
385     /* no count update since when we call this function; hunter is on prey and
386        thus a brick is missing */
387     //game->bricks_left++;
388     //game->brick_count++;
389     game->blHunterPreyX = mx; game->blHunterPreyY = my;
390     game->blHunterTimeLeft = game->blActionTime;
391 }
392 
bricks_create_hunter_area(Game * game)393 static void bricks_create_hunter_area( Game *game )
394 {
395     int i,j;
396     game->blHunterUpId = 15;
397     game->blHunterDownId = 13;
398     game->blHunterRightId = 11;
399     game->blHunterLeftId = 10;
400     game->blHunterAreaX1 = 2;
401     game->blHunterAreaY1 = 1;
402     game->blHunterAreaX2 = MAP_WIDTH-3;
403     game->blHunterAreaY2 = 11;
404     game->blHunterX = 4;
405     game->blHunterY = 6;
406     game->blHunterPreyX = 11;
407     game->blHunterPreyY = 6;
408     game->blHunterPreyId = 5;
409     game->blHunterId = 2;
410     for (i=game->blHunterAreaX1;i<=game->blHunterAreaX2;i++)
411     {
412         brick_set_by_id(game,i,game->blHunterAreaY1,game->blHunterUpId);
413         brick_set_by_id(game,i,game->blHunterAreaY2,game->blHunterDownId);
414     }
415     for (i=1;i<=MAP_WIDTH-2;i++)
416 	{
417         brick_set_by_id(game,i,game->blHunterAreaY2+1,1);
418         brick_set_by_id(game,i,game->blHunterAreaY2+2,1);
419 	}
420     for (j=game->blHunterAreaY1;j<=game->blHunterAreaY2;j++)
421     {
422         brick_set_by_id(game,game->blHunterAreaX1,j,game->blHunterLeftId);
423         brick_set_by_id(game,game->blHunterAreaX2,j,game->blHunterRightId);
424     }
425     for (i=2;i<4;i++) brick_set_by_id(game,i,game->blHunterAreaY2+2,game->blHunterUpId);
426     for (i=5;i<7;i++) brick_set_by_id(game,i,game->blHunterAreaY2+2,game->blHunterLeftId);
427     for (i=9;i<11;i++) brick_set_by_id(game,i,game->blHunterAreaY2+2,game->blHunterRightId);
428     for (i=12;i<14;i++) brick_set_by_id(game,i,game->blHunterAreaY2+2,game->blHunterDownId);
429     brick_set_by_id(game,game->blHunterX,game->blHunterY,game->blHunterId);
430     bricks_create_new_prey(game,game->blHunterPreyX,game->blHunterPreyY);
431 }
432 
bricks_move_hunter(Game * game,int x,int y,int * result)433 static void bricks_move_hunter( Game *game, int x, int y, int *result )
434 {
435     *result = 0;
436     brick_set_by_id(game,game->blHunterX,game->blHunterY,-1);
437     bricks_add_mod( game->blHunterX,game->blHunterY, HT_REMOVE_NO_SOUND, SHR_BY_ENERGY_BALL, vector_get(0,0), game->paddles[0] );
438     game->blHunterX+=x; game->blHunterY+=y;
439     brick_set_by_id(game,game->blHunterX,game->blHunterY,game->blHunterId);
440 	bricks_add_grow_mod(game->blHunterX,game->blHunterY,game->blHunterId);
441     /* check if still in playing field */
442     if (game->blHunterX==game->blHunterAreaX1||game->blHunterX==game->blHunterAreaX2||
443         game->blHunterY==game->blHunterAreaY1||game->blHunterY==game->blHunterAreaY2)
444     {
445         *result = 1;
446         return;
447     }
448     /* check whether we got the pray */
449     if (game->blHunterX==game->blHunterPreyX&&game->blHunterY==game->blHunterPreyY)
450     {
451         *result = 2;
452     }
453 }
454 
455 /* add a new brick in the first row */
bricks_add_invader(Game * game,int * wave_over)456 static void bricks_add_invader( Game *game, int *wave_over )
457 {
458 	Invader *inv;
459 	int mx, my;
460 	*wave_over = 0;
461 	if (game->blInvadersWaveOver) return;
462 	if (game->blNumInvaders==game->blInvaderLimit)
463 	{
464 		if (game->blNumKilledInvaders==game->blInvaderLimit)
465 			*wave_over = 1;
466 		return;
467 	}
468 	my = 1;
469 	do { mx = RANDOM(1,MAP_WIDTH-2); }
470 	while (game->bricks[mx][my].type!=MAP_EMPTY);
471 	inv = &game->blInvaders[game->blNumInvaders++];
472 	inv->id = RANDOM(BRICK_GROW_FIRST,BRICK_GROW_LAST);
473 	inv->x = mx; inv->y = my;
474 	game->blInvaderTime = 99*game->blInvaderTime/100; /* get faster and faster */
475 	/* DEBUG: printf("%d\n",game->blInvaderTime); */
476 	delay_set(&inv->delay,RANDOM(95,105)*game->blInvaderTime/100);
477 	brick_set_by_id(game,mx,my,inv->id);
478 	game->bricks_left++;
479 	game->brick_count++;
480 	bricks_add_grow_mod(mx,my,inv->id);
481 	/* get new targets */
482 	balls_check_targets( -1, 0 );
483 }
484 
bricks_init_next_wave(Game * game)485 static void bricks_init_next_wave( Game *game )
486 {
487 	if (game->blNumCompletedRuns==0)
488 	{
489 		game->blInvaderLimit= 50;  /* total number of invaders in this wave */
490 		if (game->blInvaders) free(game->blInvaders);
491 		game->blInvaders = (Invader*)calloc(game->blInvaderLimit,sizeof(Invader));
492 	}
493 	game->blInvaderTime = game->blActionTime;
494 	game->blNumInvaders = 0;
495 	game->blNumKilledInvaders = 0;
496 	game->blInvadersWaveOver = 0;
497 	delay_set(&game->blDelay,game->blInvaderTime);
498 	bricks_add_invader(game,&game->blInvadersWaveOver);
499 	bricks_add_invader(game,&game->blInvadersWaveOver);
500 	bricks_add_invader(game,&game->blInvadersWaveOver);
501 }
502 
bricks_move_invaders(Game * game,int ms,int * paddleHit)503 static void bricks_move_invaders( Game *game, int ms, int *paddleHit )
504 {
505 	Invader *inv;
506 	int i;
507 	*paddleHit = 0;
508 	for (i=0;i<game->blNumInvaders;i++)
509 		if (game->blInvaders[i].x!=-1)
510 		{
511 			inv = &game->blInvaders[i];
512 			if (delay_timed_out(&inv->delay,ms))
513 			if (game->bricks[inv->x][inv->y+1].type==MAP_EMPTY)
514 			{
515 				brick_set_by_id(game,inv->x,inv->y,-1);
516 			    bricks_add_mod(inv->x,inv->y, HT_REMOVE_NO_SOUND, SHR_BY_ENERGY_BALL, vector_get(0,0), game->paddles[0] );
517 				inv->y++;
518 				if (inv->y==MAP_HEIGHT-2) *paddleHit = 1;
519 				brick_set_by_id(game,inv->x,inv->y,inv->id);
520 				bricks_add_grow_mod(inv->x,inv->y,inv->id);
521 			}
522 		}
523 	/* get new targets */
524 	balls_check_targets( -1, 0 );
525 }
526 
527 /*
528 ====================================================================
529 Create a brick at a random position and set it as explosive with the
530 given time or stable if life_time = -1.
531 ====================================================================
532 */
brick_create_instable(Game * game,int life_time)533 static void brick_create_instable( Game *game, int life_time )
534 {
535   int mx,my,id;
536 
537     /* choose id of a normal brick */
538   id = RANDOM(10,17);
539 
540   /* do again if nothing added due to ball or existing brick */
541   do
542     {
543       mx = RANDOM(1,MAP_WIDTH-2);
544       my = RANDOM(1,1+EDIT_HEIGHT-1); /* only senseful for local game */
545     }
546   while (game->bricks[mx][my].type!=MAP_EMPTY);
547 
548   /* build brick */
549   brick_set_by_id(game,mx,my,id);
550   game->bricks[mx][my].score = 0; /* score is given in brick_hit by special formula */
551   /* adjust brick count */
552   game->bricks_left++;
553   game->brick_count++;
554 
555   /* set as explosive for first paddle */
556   if (life_time>=0)
557   {
558       game->bricks[mx][my].exp_time = life_time;
559       game->bricks[mx][my].exp_paddle = game->paddles[0];
560       game->bricks[mx][my].mx = mx; game->bricks[mx][my].my = my;
561       list_add( game->exp_bricks, &game->bricks[mx][my] );
562   }
563 
564   /* position of jumping jack */
565   game->bl_jj_mx = mx; game->bl_jj_my = my;
566 
567   /* send to client */
568   bricks_add_grow_mod( mx, my, id );
569 
570 	/* get new targets */
571 	balls_check_targets( -1, 0 );
572 
573   //printf("Created instable brick with %d msecs lifetime.\n", life_time );
574 }
575 
576 /*
577 ====================================================================
578 Generate bonus level. While this can be extended to NETWORK game,
579 it is only implemented for local games by now.
580 ====================================================================
581 */
bricks_init_bonus_level(Game * game,int game_type,int level_type)582 static void bricks_init_bonus_level( Game *game, int game_type, int level_type )
583 {
584     int i;
585     game->blNumCompletedRuns = 0;
586     game->blRatioSum = 0.0;
587     switch (level_type)
588     {
589         case LT_JUMPING_JACK:
590             game->blActionTime = 20000; /* time in millisecs */
591             game->blMaxScore = 2000;
592             brick_create_instable( game, game->blActionTime );
593             break;
594         case LT_OUTBREAK:
595             game->blActionTime = 4000; /* time until new brick */
596             game->blMaxScore = 6000;
597             game->blCancerCount = 0;
598             game->blCancerLimit = 50;
599             delay_set(&game->blDelay,game->blActionTime);
600             for (i=0;i<5;i++) brick_create_instable( game, -1 );
601             break;
602         case LT_BARRIER:
603             game->blActionTime = 3000; /* time until move down */
604             game->blMaxScore = 4000;
605             game->blBarrierLevel = 1;
606             game->blBarrierMoves = 0;
607             delay_set(&game->blDelay,game->blActionTime);
608             bricks_create_barrier(game,game->blBarrierLevel);
609             break;
610         case LT_SITTING_DUCKS:
611             if (game->blDuckPositions) free(game->blDuckPositions);
612             game->blTotalNumDucks = game->blNumDucks = 8;
613             game->blDuckPositions = (int*)calloc(2*game->blTotalNumDucks,sizeof(int));
614             game->blDuckPositions[0]  = 1; game->blDuckPositions[1]  = 5;
615             game->blDuckPositions[2]  = 2; game->blDuckPositions[3]  = 3;
616             game->blDuckPositions[4]  = 4; game->blDuckPositions[5]  = 2;
617             game->blDuckPositions[6]  = 6; game->blDuckPositions[7]  = 1;
618             game->blDuckPositions[8]  = 9; game->blDuckPositions[9]  = 1;
619             game->blDuckPositions[10] = 11; game->blDuckPositions[11] = 2;
620             game->blDuckPositions[12] = 13; game->blDuckPositions[13] = 3;
621             game->blDuckPositions[14] = 14; game->blDuckPositions[15] = 5;
622             game->blDuckBaseScore = 6000;
623             game->blMaxScore = game->blDuckBaseScore;
624             for (i=0;i<game->blTotalNumDucks;i++)
625                 brick_set_by_id(game,game->blDuckPositions[2*i],game->blDuckPositions[2*i+1],3);
626             attach_ball_to_ceiling(game);
627             break;
628         case LT_HUNTER:
629             game->blActionTime = 30000; /* time in millisecs */
630             game->blMaxScore = 10000;
631             bricks_create_hunter_area( game ); /* includes setting hunter and first prey */
632             break;
633 		case LT_DEFENDER:
634 			game->blActionTime = 2000; /* time until new invader */
635 			game->blMaxScore = 40000;  /* max score per wave */
636 			game->blInvaderScore = 400;
637 			bricks_init_next_wave( game );
638 			break;
639         default:
640             fprintf(stderr,"Unknown Bonus Level Type: %d\n", level_type);
641             break;
642     }
643 }
644 
645 /*
646 ====================================================================
647 Publics
648 ====================================================================
649 */
650 
651 /*
652 ====================================================================
653 Init bricks from level data, set the warp limit (percent) and
654 add regenerating bricks. As this function is called when
655 initializing a level it does not use the 'cur_game' context.
656 'score_mod' is percentual and 100 means normal score.
657 ====================================================================
658 */
bricks_init(Game * game,int game_type,Level * level,int score_mod,int rel_warp_limit)659 void bricks_init( Game *game, int game_type, Level *level, int score_mod, int rel_warp_limit  )
660 {
661   int i, j, k;
662   int y_off;
663   int num_grown_bricks = 0; /* count grown bricks for proper warp limit */
664 
665   /* clear everything */
666   for (i = 0; i < MAP_WIDTH; i++)
667     for (j = 0; j < MAP_HEIGHT; j++) {
668       game->bricks[i][j].id = -1;
669       game->bricks[i][j].dur = -1;
670       game->bricks[i][j].type = MAP_EMPTY;
671       game->bricks[i][j].brick_c = ' ';
672       game->bricks[i][j].extra_c = ' ';
673       game->bricks[i][j].extra   = EX_NONE;
674       game->bricks[i][j].score = 0;
675     }
676 
677   /* clear explosion/healing list */
678   list_clear( game->exp_bricks );
679   list_clear( game->heal_bricks );
680 
681   /* build walls */
682   for (i = 0; i < MAP_WIDTH; i++)
683     if ( game_type == GT_LOCAL ) {
684       /* in multiplayer this is open */
685       game->bricks[i][0].id = 0;
686       game->bricks[i][0].dur = -1;
687       game->bricks[i][0].type = MAP_WALL; /* this means - indestructible */
688     }
689   for (j = 0; j < MAP_HEIGHT; j++) {
690     game->bricks[0][j].id = 0;
691     game->bricks[0][j].dur = -1;
692     game->bricks[0][j].type = MAP_WALL; /* this means - indestructible */
693     game->bricks[MAP_WIDTH - 1][j].id = 0;
694     game->bricks[MAP_WIDTH - 1][j].dur = -1;
695     game->bricks[MAP_WIDTH - 1][j].type = MAP_WALL;
696   }
697 
698   /* load map (centered if multiplayer) if level::type is LT_NORMAL. Otherwise
699      generate the special bricks for the special bonus levels. */
700   if (level->type==LT_NORMAL)
701     {
702       if ( game_type == GT_NETWORK )
703 	y_off = ( MAP_HEIGHT - EDIT_HEIGHT ) / 2;
704       else
705 	y_off = 1;
706       for (i = 0; i < EDIT_WIDTH; i++)
707 	for (j = 0; j < EDIT_HEIGHT; j++) {
708 	  /* create bricks */
709 	  game->bricks[i + 1][j + y_off].exp_time = -1;
710 	  game->bricks[i + 1][j + y_off].heal_time = -1;
711 	  for ( k = 0; k < BRICK_COUNT; k++ )
712 	    if ( level->bricks[i][j] == brick_conv_table[k].c ) {
713           brick_set_by_id( game, i+1,j+y_off,brick_conv_table[k].id );
714           game->bricks[i + 1][j + y_off].score = (score_mod * brick_conv_table[k].score) / 10;
715 
716 		/* count grown bricks */
717 		if (IS_GROWN_BRICK_CHAR(level->bricks[i][j]))
718 			num_grown_bricks++;
719 
720 	      break;
721 	    }
722 	  if ( k == BRICK_COUNT && level->bricks[i][j] != '.' && level->bricks[i][j] != ' ' )
723 	    printf( "unknown: %i,%i: %c\n", i, j, level->bricks[i][j] );
724 	  /* create extras */
725 	  game->bricks[i + 1][j + y_off].extra = EX_NONE;
726 	  for ( k = 0; k < EX_NUMBER; k++ )
727 	    if ( level->extras[i][j] == extra_conv_table[k].c ) {
728 	      game->bricks[i + 1][j + y_off].extra_c = extra_conv_table[k].c;
729 	      game->bricks[i + 1][j + y_off].extra = extra_conv_table[k].type;
730 	      break;
731 	    }
732 	}
733     }
734   else
735     {
736       /* generate bonus level */
737       if (game->localServerGame)
738           bricks_init_bonus_level( game, game_type, level->type );
739     }
740 
741   /* count bricks & extras */
742   game->bricks_left = 0; game->extra_count = 0;
743   for (i = 1; i < MAP_WIDTH - 1; i++)
744     for (j = 1; j < MAP_HEIGHT - 1; j++) {
745       if ( game->bricks[i][j].dur > 0 ) {
746 	game->bricks_left++;
747 	if ( game->bricks[i][j].extra != EX_NONE )
748 	  game->extra_count++;
749       }
750     }
751   game->brick_count = game->bricks_left;
752 
753   /* to compute the warp limit we always use the number of initially
754    * destructible bricks in the level (level::normal_brick_count). the
755    * snapshot might have less (some bricks already cleared by the player)
756    * or more (grown bricks) bricks, so game::brick_count cannot be used for
757    * computation. cleared original bricks are okay but to take grown bricks
758    * into the limit is tricky: on the one hand they should not increase the
759    * number of bricks to be cleared for warp, on the other hand they should
760    * not help to hit the limit easily. so the solution is to ignore them for
761    * the warp limit. thus the limit is computed from number of initially
762    * present bricks. whatever bricks where grown (marked by special ids) are
763    * added to this warp limit (and warp limit is decreased again when grown
764    * bricks get removed).
765    * quite confusing here is the duplication of the brick functions.
766    * client/bricks.c seems to be the correct one but for safety I put the
767    * code also to game/bricks.c (this here is same, I mean the grow/remove
768    * brick warp limit adjustment). */
769   game->warp_limit = ( 100 - rel_warp_limit ) *
770     level->normal_brick_count / 100;
771   game->warp_limit += num_grown_bricks;
772   //printf("Currently %d bricks in level (initially %d), %d grown.\n"
773   //			"  => Warp allowed if less than %d bricks "
774   //			"remain (%d%% destroyed).\n",
775   //			game->bricks_left, level->normal_brick_count,
776   //			num_grown_bricks, game->warp_limit, rel_warp_limit);
777 
778   /* add regenerating bricks */
779   for ( i = 1; i < MAP_WIDTH - 1; i++ )
780     for ( j = 1; j < MAP_HEIGHT - 1; j++ )
781       if ( game->bricks[i][j].type == MAP_BRICK_HEAL )
782 	if ( game->bricks[i][j].dur < 3 ) {
783 	  game->bricks[i][j].mx = i;
784 	  game->bricks[i][j].my = j;
785 	  game->bricks[i][j].heal_time = BRICK_HEAL_TIME * cur_game->diff-> time_mod;
786 	  list_add( game->heal_bricks, &game->bricks[i][j] );
787 	}
788 }
789 /*
790 ====================================================================
791 Hit brick and remove if destroyed. 'metal' means the ball
792 destroys any brick with the first try.
793 type and imp are used for shrapnell creation.
794 'extra' contains the pushed extra if one was released.
795 'paddle' is the paddle that initiated hit either by shot or ball.
796 Return true on destruction
797 ====================================================================
798 */
brick_hit(int mx,int my,int metal,int type,Vector imp,Paddle * paddle)799 int brick_hit( int mx, int my, int metal, int type, Vector imp, Paddle *paddle )
800 {
801 	int remove = 0;
802 	int loose_dur = 0;
803     int i;
804     double ratio;
805 
806     /* perform action of special levels */
807     if (cur_game->localServerGame)
808         switch (cur_game->level_type)
809         {
810             case LT_HUNTER:
811                 /* no brick is broken, just move hunter if correct brick hit */
812                 if (cur_game->bricks[mx][my].id==cur_game->blHunterUpId)
813                     bricks_move_hunter(cur_game,0,-1,&i);
814                 else
815                 if (cur_game->bricks[mx][my].id==cur_game->blHunterDownId)
816                     bricks_move_hunter(cur_game,0,1,&i);
817                 else
818                 if (cur_game->bricks[mx][my].id==cur_game->blHunterLeftId)
819                     bricks_move_hunter(cur_game,-1,0,&i);
820                 else
821                 if (cur_game->bricks[mx][my].id==cur_game->blHunterRightId)
822                     bricks_move_hunter(cur_game,1,0,&i);
823                 if (i==1)
824                 {
825                     /* we screwed it */
826                     cur_game->bricks_left = 0;
827                 }
828                 else if (i==2)
829                 {
830                     /* we caught a brick! */
831                     ratio = ((double)cur_game->blHunterTimeLeft)/cur_game->blActionTime;
832                     paddle->score += ratio*cur_game->blMaxScore;
833                     cur_game->totalBonusLevelScore += ratio*cur_game->blMaxScore;
834                     printf("H: maxScore: %d, ratio: %f, respawn time: %d\n",cur_game->blMaxScore,ratio,cur_game->blActionTime);
835                     cur_game->blActionTime *= 0.95;
836                     cur_game->blMaxScore *= 1.05;
837                     cur_game->blNumCompletedRuns++;
838                     cur_game->blRatioSum += ratio;
839                     bricks_create_new_prey(cur_game,-1,-1);
840                 }
841                 return 0;
842             case LT_SITTING_DUCKS:
843                 if (imp.y<0)
844                 {
845                     /* any hit results in reseting the ball if direction is up;
846                        if not the highlighted brick was hit, it will explode */
847                     if (mx==cur_game->blDuckPositions[cur_game->blCurrentDuck*2]&&
848                         my==cur_game->blDuckPositions[cur_game->blCurrentDuck*2+1])
849                     {
850                         /* wow! give some points for that */
851                         ratio = 1.0;
852                         paddle->score += ratio*cur_game->blMaxScore;
853                         cur_game->totalBonusLevelScore += ratio*cur_game->blMaxScore;
854                         //printf("SD: hit!\n");
855                         cur_game->blMaxScore *= 1.05;
856                         cur_game->blNumCompletedRuns++;
857                         cur_game->blRatioSum += ratio;
858                         brick_set_by_id(cur_game,mx,my,3);
859 						bricks_add_grow_mod(mx,my,3);
860                     }
861                     else
862                     {
863                         /* dude, you suck! */
864                         mx = cur_game->blDuckPositions[cur_game->blCurrentDuck*2];
865                         my = cur_game->blDuckPositions[cur_game->blCurrentDuck*2+1];
866                         cur_game->bricks[mx][my].exp_time = 1;
867                         cur_game->bricks[mx][my].exp_paddle = cur_game->paddles[0];
868                         cur_game->bricks[mx][my].mx = mx;
869                         cur_game->bricks[mx][my].my = my;
870                         list_add( cur_game->exp_bricks, &cur_game->bricks[mx][my] );
871                         cur_game->blMaxScore = cur_game->blDuckBaseScore;
872                         cur_game->blNumDucks--;
873                         cur_game->blDuckPositions[cur_game->blCurrentDuck*2] = -1;
874                     }
875                     attach_ball_to_ceiling(cur_game);
876                     return 0;
877                 }
878                 break;
879         }
880 
881 	/* a map wall can't be touched */
882 	if ( cur_game->bricks[mx][my].type == MAP_WALL ) return 0;
883 
884 	/* if metal ball resistance is futile */
885 	if ( metal )
886 		remove = 1;
887 	else {
888 		if ( cur_game->bricks[mx][my].dur == -1 )
889 			return 0; /* duration of -1 means only breakable
890 				     by engery ball (metal ball) */
891 		if ( cur_game->bricks[mx][my].dur <= 1 )
892 			remove = 1;
893 		else
894 			loose_dur = 1;
895 	}
896 
897     /* perform action of special levels */
898     if (cur_game->localServerGame)
899         switch (cur_game->level_type)
900         {
901             case LT_JUMPING_JACK:
902                 /* grow another brick (since a hit means sure removal) */
903                 ratio = ((double)cur_game->bricks[cur_game->bl_jj_mx][cur_game->bl_jj_my].exp_time)/cur_game->blActionTime;
904                 paddle->score += ratio*cur_game->blMaxScore;
905                 cur_game->totalBonusLevelScore += ratio*cur_game->blMaxScore;
906                 //printf("JJ: maxScore: %d, ratio: %f, respawn time: %d\n",cur_game->blMaxScore,ratio,cur_game->blActionTime);
907                 cur_game->blActionTime *= 0.95;
908                 cur_game->blMaxScore *= 1.05;
909                 cur_game->blNumCompletedRuns++;
910                 cur_game->blRatioSum += ratio;
911                 brick_create_instable( cur_game, cur_game->blActionTime );
912                 break;
913         }
914 
915 	if ( remove ) {
916 		bricks_add_mod( mx, my, HT_REMOVE, type, imp, paddle );
917 		brick_remove( mx, my, type, imp, paddle );
918 	}
919 	else
920 		if ( loose_dur )  {
921 			bricks_add_mod( mx, my, HT_HIT, type, imp, paddle );
922 			brick_loose_dur( mx, my, 1 );
923 		}
924 
925     /* perform action of special levels */
926     if (cur_game->localServerGame)
927         switch (cur_game->level_type)
928         {
929 			case LT_DEFENDER:
930 				for (i=0;i<cur_game->blNumInvaders;i++)
931 					if (cur_game->blInvaders[i].x==mx&&cur_game->blInvaders[i].y==my)
932 					{
933 						cur_game->blInvaders[i].x = -1;
934 						paddle->score += cur_game->blInvaderScore;
935 						cur_game->totalBonusLevelScore += cur_game->blInvaderScore;
936 						break;
937 					}
938 				cur_game->blNumKilledInvaders++;
939 				cur_game->blTotalNumKilledInvaders++;
940 				if (cur_game->bricks_left==0) /* cleared this wave, next one please! */
941 				{
942                     ratio = ((double)(cur_game->blInvaderLimit - cur_game->blNumKilledInvaders))/cur_game->blInvaderLimit;
943 					paddle->score += ratio*cur_game->blMaxScore;
944 					cur_game->totalBonusLevelScore += ratio*cur_game->blMaxScore;
945 					cur_game->blActionTime *= 0.95;
946 					cur_game->blMaxScore *= 1.05;
947 					cur_game->blInvaderScore *= 1.05;
948                     cur_game->blNumCompletedRuns++;
949                     cur_game->blRatioSum += ratio;
950 					bricks_init_next_wave( cur_game );
951 				}
952 				break;
953             case LT_OUTBREAK:
954                 if (cur_game->bricks_left==0)
955                 {
956                     /* reset scene */
957                     ratio = ((double)(cur_game->blCancerLimit - cur_game->blCancerCount))/cur_game->blCancerLimit;
958                     paddle->score += ratio*cur_game->blMaxScore;
959                     cur_game->totalBonusLevelScore += ratio*cur_game->blMaxScore;
960                     //printf("OB: maxScore: %d, ratio: %f, respawn time: %d\n",cur_game->blMaxScore,ratio,cur_game->blActionTime);
961                     cur_game->blActionTime *= 0.95;
962                     cur_game->blMaxScore *= 1.05;
963                     delay_set(&cur_game->blDelay,cur_game->blActionTime);
964                     cur_game->blCancerCount = 0;
965                     cur_game->blNumCompletedRuns++;
966                     cur_game->blRatioSum += ratio;
967                     for (i=0;i<5;i++) brick_create_instable( cur_game, -1 );
968                 }
969                 break;
970             case LT_BARRIER:
971                 if (my == 1)
972                 {
973                     /* build a tougher barrier, enter next level */
974                     ratio = ((double)(cur_game->blBarrierMaxMoves - cur_game->blBarrierMoves))/cur_game->blBarrierMaxMoves;
975                     if (ratio<0) ratio=0;
976                     paddle->score += 500*cur_game->blBarrierLevel + ratio*cur_game->blMaxScore;
977                     cur_game->totalBonusLevelScore += 500*cur_game->blBarrierLevel + ratio*cur_game->blMaxScore;
978                     //printf("BR: maxScore: %d, ratio: %f, respawn time: %d\n",cur_game->blMaxScore,ratio,cur_game->blActionTime);
979                     //cur_game->blActionTime *= 0.95;
980                     cur_game->blMaxScore += 1000;
981                     delay_set(&cur_game->blDelay,cur_game->blActionTime);
982                     cur_game->blBarrierMoves = 0;
983                     cur_game->blNumCompletedRuns++;
984                     cur_game->blRatioSum += ratio;
985                     cur_game->blBarrierLevel++;
986                     bricks_create_barrier(cur_game,cur_game->blBarrierLevel);
987                     ((Ball*)(cur_game->balls->head->next->item))->moving_back = 1;
988                 }
989                 break;
990         }
991 
992 	return remove;
993 }
994 /*
995 ====================================================================
996 Make brick at mx,my loose 'points' duration. It must have been
997 previously checked that this operation is completely valid.
998 It does not update net_bricks or the player's duration reference.
999 ====================================================================
1000 */
brick_loose_dur(int mx,int my,int points)1001 void brick_loose_dur( int mx, int my, int points )
1002 {
1003 	while ( points-- > 0 ) {
1004 		cur_game->bricks[mx][my].dur--;
1005 		cur_game->bricks[mx][my].id--;
1006 		/* adjust brick character:
1007 		 * a,b,c - multiple hits
1008 		 * v - invisible */
1009 		if ( cur_game->bricks[mx][my].brick_c == 'v' )
1010 			cur_game->bricks[mx][my].brick_c = 'c';
1011 		else
1012 			cur_game->bricks[mx][my].brick_c--; /* successive order */
1013 		/* set regeneration time if it's a healing brick */
1014 		if ( cur_game->bricks[mx][my].type == MAP_BRICK_HEAL ) {
1015 			/* if this brick is already healing just reset the time
1016 			   but don't add to the list again */
1017 			if ( cur_game->bricks[mx][my].heal_time != -1 )
1018 				cur_game->bricks[mx][my].heal_time = BRICK_HEAL_TIME * cur_game->diff->time_mod;
1019 			else {
1020 				cur_game->bricks[mx][my].mx = mx;
1021 				cur_game->bricks[mx][my].my = my;
1022 				cur_game->bricks[mx][my].heal_time = BRICK_HEAL_TIME * cur_game->diff-> time_mod;
1023 				list_add( cur_game->heal_bricks, &cur_game->bricks[mx][my] );
1024 			}
1025 		}
1026 	}
1027 }
1028 
1029 /* add a modification to the list. if 'mod' is HT_HIT and the
1030  * tile is empty it is an HT_REMOVE. 'type' is the type of
1031  * the responsible source and 'src' its impact vector. */
bricks_add_mod(int x,int y,int mod,int dest_type,Vector imp,Paddle * paddle)1032 void bricks_add_mod( int x, int y, int mod, int dest_type, Vector imp, Paddle *paddle )
1033 {
1034 	BrickHit *hit;
1035 
1036 	if ( cur_game->mod.brick_hit_count > MAX_MODS ) return; /* drop hit */
1037 
1038 	hit = &cur_game->mod.brick_hits[cur_game->mod.brick_hit_count++];
1039 	memset(hit,0,sizeof(BrickHit)); /* clear hit */
1040 
1041 	if (mod == HT_REMOVE_NO_SOUND )
1042 	{
1043 		mod = HT_REMOVE;
1044 		hit->no_sound = 1;
1045 	}
1046 
1047 	if ( mod == HT_REMOVE ) {
1048 		if ( paddle->extra_active[EX_GOLDSHOWER] )
1049 			if ( cur_game->bricks[x][y].extra == EX_NONE )
1050 				hit->gold_shower = 1;
1051 		if (cur_game->bricks[x][y].type==MAP_BRICK_EXP)
1052 		      hit->draw_explosion = 1;
1053 		if (dest_type==SHR_BY_DELAYED_EXPL)
1054 		  {
1055 		    dest_type = SHR_BY_EXPL; /* delayed explosion thus initiated by a close-by
1056 						explosion have no explosion animation */
1057 		  }
1058 		else if (dest_type==SHR_BY_NORMAL_BALL && cur_game->extra_active[EX_EXPL_BALL])
1059 		  {
1060 		    dest_type = SHR_BY_EXPL;
1061 		    hit->draw_explosion = 1;
1062 		  }
1063 	}
1064 
1065 	hit->x = x; hit->y = y;
1066 	hit->type = mod;
1067 	hit->dest_type = dest_type;
1068 	hit->paddle = (cur_game->paddles[PADDLE_BOTTOM]==paddle)?PADDLE_BOTTOM:PADDLE_TOP;
1069 
1070 	hit->degrees = 0;
1071 	if ( mod == HT_REMOVE && dest_type == SHR_BY_NORMAL_BALL ) {
1072 		hit->degrees = vec2angle( &imp );
1073 	}
1074 	else
1075 	if ( dest_type == SHR_BY_SHOT ) {
1076 		if ( hit->paddle == PADDLE_BOTTOM )
1077 			hit->degrees = 270 / 2;
1078 		else
1079 			hit->degrees = 90 / 2;
1080 	}
1081 
1082 	if (mod==HT_GROW)
1083 		hit->brick_id = RANDOM( BRICK_GROW_FIRST, BRICK_GROW_LAST );
1084 ;
1085 }
bricks_add_grow_mod(int x,int y,int id)1086 void bricks_add_grow_mod( int x, int y, int id )
1087 {
1088 	BrickHit *hit;
1089 
1090 	if ( cur_game->mod.brick_hit_count > MAX_MODS ) return; /* drop hit */
1091 
1092 	hit = &cur_game->mod.brick_hits[cur_game->mod.brick_hit_count++];
1093 	memset(hit,0,sizeof(BrickHit)); /* clear hit */
1094 
1095 	hit->x = x; hit->y = y;
1096 	hit->brick_id = id;
1097 	hit->type = HT_GROW;
1098 }
1099 
1100 /* update regeneration and explosion of bricks */
bricks_update(int ms)1101 void bricks_update( int ms )
1102 {
1103     int paddleHit;
1104 	Brick *brick;
1105     Ball *ball;
1106 
1107 	/* check if bricks were destroyed by explosion */
1108 	if ( cur_game->exp_bricks->count > 0 ) {
1109 		list_reset( cur_game->exp_bricks );
1110 		while ( ( brick = list_next( cur_game->exp_bricks ) ) != 0 ) {
1111 			if ( (brick->exp_time -= ms) <= 0 ) {
1112 				brick->exp_time = -1;
1113 				bricks_add_mod( brick->mx, brick->my,
1114 						HT_REMOVE, SHR_BY_DELAYED_EXPL,
1115 						vector_get( 0, 0 ), brick->exp_paddle );
1116 				brick_remove( brick->mx, brick->my, SHR_BY_EXPL,
1117 						vector_get( 0, 0 ), brick->exp_paddle );
1118 				balls_check_targets( brick->mx, brick->my );
1119 				list_delete_current( cur_game->exp_bricks );
1120 			}
1121 		}
1122 	}
1123 
1124 	/* check if bricks regenerate */
1125 	if ( cur_game->heal_bricks->count > 0 ) {
1126 		list_reset( cur_game->heal_bricks );
1127 		while ( ( brick = list_next( cur_game->heal_bricks ) ) != 0 ) {
1128 			/* skip brick if destroyed meanwhile */
1129 			if ( brick->type != MAP_BRICK_HEAL ) {
1130 				list_delete_current( cur_game->heal_bricks );
1131 				continue;
1132 			}
1133 			if ( (brick->heal_time -= ms) < 0 ) {
1134 				brick->dur++;
1135 				brick->id++;
1136 				bricks_add_mod( brick->mx, brick->my,
1137 						HT_HEAL, 0, vector_get( 0, 0 ), 0 );
1138 				if ( brick->dur < 3 ) {
1139 					/* initate next healing step */
1140 					brick->heal_time = BRICK_HEAL_TIME;
1141 				}
1142 				else {
1143 					brick->heal_time = -1;
1144 					list_delete_current( cur_game->heal_bricks );
1145 				}
1146 			}
1147 		}
1148 	}
1149 
1150     /* check bonus level stuff */
1151     if (cur_game->localServerGame)
1152         switch (cur_game->level_type)
1153         {
1154             case LT_HUNTER:
1155                 cur_game->blHunterTimeLeft -= ms;
1156                 if (cur_game->blHunterTimeLeft<0)
1157                     cur_game->bricks_left = 0;
1158                 break;
1159             case LT_OUTBREAK:
1160                 if (delay_timed_out(&cur_game->blDelay,ms))
1161                 {
1162                     brick_create_instable(cur_game,-1);
1163                     cur_game->blCancerCount++;
1164                     if (cur_game->blCancerCount>cur_game->blCancerLimit)
1165                         cur_game->bricks_left = 0; /* fake level cleared */
1166                 }
1167                 break;
1168             case LT_BARRIER:
1169                 if (delay_timed_out(&cur_game->blDelay,ms))
1170                 {
1171                     bricks_move_barrier(cur_game,&paddleHit);
1172                     if (paddleHit)
1173                         cur_game->bricks_left = 0; /* fake level cleared */
1174                 }
1175                 break;
1176             case LT_SITTING_DUCKS:
1177                 if (cur_game->blBallAttached&&delay_timed_out(&cur_game->blDelay,ms))
1178                 {
1179                     ball = list_first(cur_game->balls);
1180                     cur_game->blBallAttached = 0;
1181                     ball_set_random_angle( ball, cur_game->ball_v );
1182                     ball->vel.y *= -1.0;
1183                     ball->vel.x *= 0.2;
1184                     balls_set_velocity( cur_game->balls, cur_game->ball_v );
1185                     balls_check_targets( -1, 0 );
1186                     select_random_duck(cur_game);
1187                 }
1188 				break;
1189 			case LT_DEFENDER:
1190 				if (delay_timed_out(&cur_game->blDelay,ms))
1191 					bricks_add_invader(cur_game,&cur_game->blInvadersWaveOver);
1192 				bricks_move_invaders(cur_game,ms,&paddleHit);
1193 				if (paddleHit)
1194 					cur_game->bricks_left = 0; /* fake level cleared */
1195                 break;
1196         }
1197 }
1198 
1199 /* return the character that represents the brick with this type id */
brick_get_char(int type)1200 char brick_get_char( int type )
1201 {
1202 	int i;
1203 	for ( i = 0; i < BRICK_COUNT; i++ )
1204 		if ( brick_conv_table[i].id == type )
1205 			return brick_conv_table[i].c;
1206 	return ' ';
1207 }
1208