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