1 /*
2 XBubble - board.c
3
4 Copyright (C) 2002 Ivan Djelic <ivan@savannah.gnu.org>
5 Copyright (C) 2003 Martin Quinson <martin.quinson@tuxfamily.org>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include <stdlib.h>
23 #include <string.h> /* memset */
24 #include <math.h>
25
26 #include "utils.h"
27 #include "cell.h"
28 #include "setting.h"
29 #include "frame.h"
30 #include "sprite.h"
31 #include "bubble.h"
32 #include "board.h"
33
34 extern int scale;
35 extern Set countdown_animation;
36 extern Set alert_animation;
37 extern Set canon_animation;
38
39 enum CountdownState {
40 INACTIVE,
41 ACTIVE,
42 VISIBLE
43 };
44
45 struct _Board {
46 enum BoardState state;
47 int period;
48 int launch_count;
49 int launch_requested;
50 int empty;
51 int clock;
52 int was_lowered;
53 int color;
54 int next_color;
55 int last_fire_delay;
56 int count;
57 int canon_empty;
58 int canon_angle;
59 int nb_rising_bubbles;
60 int nb_evaluations;
61 int alert_on;
62 int canon_direction;
63 double vx[NB_ANGLES];
64 double vy[NB_ANGLES];
65 double canon_virtual_angle;
66 double canon_speed; /* speed increase with time */
67 CellArray array;
68 Set bubbles;
69 Set tmpset;
70 Set explosive_bubbles;
71 Set malus_indicators; /* yellow bubbles indicating 1 malus ball waiting */
72 Set malus_indicators10; /* red bubbles indicating 10 malus balls waiting */
73 SpritePool sprite_pool;
74 Vector input;
75 Vector output;
76 Vector floating_cells;
77 Vector exposion_dates;
78 Sprite canon_sprite;
79 Sprite countdown_sprite;
80 Sprite alert;
81 enum CountdownState countdown_state;
82 RuleSet_t *ruleset;
83
84 Vector placed_balls; /* temp for place_received_bubble() placed here to
85 avoid mallocation at each call and do not get into trouble due to parallelism */
86
87 };
88
89 enum {
90 BOTTOM_LAYER = 0,
91 CANON_LAYER,
92 MEDIUM_LAYER,
93 TOP_LAYER
94 };
95
96
97 void stick_bubble( Board board, Bubble bubble, int target_cell);
98 void drop_bubbles(Board board);
99 void place_received_bubbles( Board board );
100
101
102
new_board_bubble(Board board,int color)103 static Bubble new_board_bubble( Board board, int color ) {
104 Bubble bubble = new_bubble( color, NEW_X, NEW_Y, MEDIUM_LAYER);
105 set_bubble_state( bubble, NEW, MEDIUM_LAYER, 0);
106 set_add( board->bubbles, bubble );
107 add_sprite_to_pool( board->sprite_pool, bubble->sprite );
108 return bubble;
109 }
110
111 /**
112 * new_bubble_color:
113 * @board:
114 *
115 * Compute which color the next bubble will be. It have to be one of the existing balls,
116 * and no color must be choosen more often than the others
117 */
118
new_bubble_color(Board board)119 static int new_bubble_color ( Board board ) {
120 int possible[ NB_COLORS ];
121 int i, count=0, choosen;
122
123 if (board->ruleset->single) {
124 memset( possible, 0, sizeof(possible) );
125 for( i = 0; i < board->bubbles->size; i++ ) {
126 possible[ ((Bubble) board->bubbles->element[i])->color ] = 1;
127 }
128 for( i = 0; i < NB_COLORS; i++ )
129 if (possible[i])
130 count++;
131 if (count == 0) count = 1;
132
133 choosen=rnd( count );
134 for( i = 0; i < NB_COLORS; i++ )
135 if (possible[i] && choosen-- <= 0) {
136 return i;
137 }
138 return 1;
139 } else {
140 return rnd(NB_COLORS);
141 }
142 }
143
new_board(int period,RuleSet_t * ruleset,int mov_ceiling,int * colors)144 Board new_board( int period, RuleSet_t *ruleset, int mov_ceiling,
145 int *colors /* vector containing the colors of the level */ ) {
146 int i;
147 int cell;
148 Bubble bubble;
149
150 Board board = (Board) xmalloc( sizeof( struct _Board ));
151 board->array = cell_array_new( mov_ceiling );
152 board->bubbles = set_new( 2*NB_CELLS );
153 board->tmpset = set_new( 2*NB_CELLS );
154 board->sprite_pool = new_sprite_pool( TOP_LAYER+1, 2*NB_CELLS );
155 board->input = vector_new( NB_CELLS );
156 board->output = vector_new( NB_CELLS );
157 board->explosive_bubbles = set_new( NB_CELLS );
158 board->malus_indicators = set_new( 10 );
159 board->malus_indicators10 = set_new( NB_CELLS / 10 + 1 );
160 board->exposion_dates = vector_new( NB_CELLS );
161 board->floating_cells = vector_new( NB_CELLS );
162 board->ruleset = ruleset;
163 board->period = period;
164 board->nb_rising_bubbles = 0;
165 board->placed_balls = vector_new( 10 );
166
167 /* alert blinking light */
168 board->alert = new_sprite( BOTTOM_LAYER, 30, alert_animation, 0, 0);
169 set_sprite_position( board->alert,
170 scale_x( ALERT_X, scale ),
171 scale_y( ALERT_Y, scale ));
172 add_sprite_to_pool( board->sprite_pool, board->alert );
173 board->alert_on = 0;
174 board->clock = 0;
175
176 /* countdown */
177 board->countdown_sprite = new_sprite( BOTTOM_LAYER, 30, countdown_animation,
178 0, 0);
179 set_sprite_position( board->countdown_sprite,
180 scale_x( COUNTDOWN_X, scale ),
181 scale_y( COUNTDOWN_Y, scale ));
182 board->countdown_state = INACTIVE;
183
184 /* canon */
185 board->canon_speed = 1;
186 board->canon_angle = 0;
187 board->canon_virtual_angle = 0.0;
188 board->canon_direction = 0;
189 board->canon_sprite = new_sprite( CANON_LAYER, 30, canon_animation, 0, 0);
190 add_sprite_to_pool( board->sprite_pool, board->canon_sprite );
191 canon_move( board, 0);
192
193 /* precompute launching vectors */
194 for ( i = 0; i < NB_ANGLES; i++ ) {
195 board->vx[i] = LAUNCHING_SPEED*sin((i-CANON_ANGLE_MAX)*ANGLE_STEP );
196 board->vy[i] =-LAUNCHING_SPEED*cos((i-CANON_ANGLE_MAX)*ANGLE_STEP );
197 }
198 board->launch_count = 0;
199 board->last_fire_delay = 1000;
200 board->launch_requested = 0;
201 board->empty = 0;
202
203 /* load the level */
204 for ( cell = 0; cell < NB_CELLS-COLS; cell++ )
205 if (( colors[cell] > 0 )&&
206 ( cell % COLS >= row_start( board->array, cell/COLS ))) {
207 bubble = new_board_bubble( board, colors[cell]-1 );
208 stick_bubble( board, bubble, cell);
209 }
210
211 /* start with a new bubble */
212 board->color = new_bubble_color( board );
213 new_board_bubble( board, board->color );
214 board->canon_empty = 1;
215
216 board->state = WAITING_FOR_CANON;
217 return board;
218 }
219
delete_board(Board board)220 void delete_board(Board board) {
221 int i;
222 cell_array_free( board->array );
223 for ( i = 0; i < board->bubbles->size; i++ )
224 delete_bubble( board->bubbles->element[i] );
225 set_free( board->bubbles );
226 set_free( board->tmpset );
227 set_free( board->explosive_bubbles );
228 set_free( board->malus_indicators );
229 set_free( board->malus_indicators10 );
230 vector_free( board->input );
231 vector_free( board->output );
232 vector_free( board->exposion_dates );
233 vector_free( board->floating_cells );
234 vector_free( board->placed_balls );
235 delete_sprite_pool( board->sprite_pool );
236 delete_sprite( board->canon_sprite );
237 delete_sprite( board->countdown_sprite );
238 delete_sprite( board->alert );
239 free( board );
240 }
241
board_overflow(Board board)242 static int board_overflow(Board board) {
243 return (board->nb_rising_bubbles == 0) &&
244 cell_array_is_overflow(board->array);
245 }
246
switch_alert_on(Board board)247 static void switch_alert_on( Board board ) {
248 set_sprite_frame( board->alert, 1);
249 board->alert_on = 1;
250 }
251
switch_alert_off(Board board)252 static void switch_alert_off( Board board ) {
253 set_sprite_frame( board->alert, 0);
254 board->alert_on = 0;
255 }
256
257 /************************** countdown functions ***************************/
258
start_countdown(Board board)259 static void start_countdown( Board board ) {
260 board->count = board->ruleset->max_fire_delay;
261 board->countdown_state = ACTIVE;
262 }
263
cancel_countdown(Board board)264 static void cancel_countdown( Board board ) {
265 switch ( board->countdown_state ) {
266 case VISIBLE:
267 remove_sprite_from_pool( board->sprite_pool, board->countdown_sprite);
268 case ACTIVE:
269 board->countdown_state = INACTIVE;
270 board->last_fire_delay = board->ruleset->max_fire_delay - board->count;
271 break;
272 default:
273 break;
274 }
275 }
276
update_countdown(Board board,int dt)277 static void update_countdown( Board board, int dt ) {
278 board->count -= dt;
279 switch ( board->countdown_state ) {
280 case ACTIVE:
281 if ( board->count <= 0 )
282 /* launch bubble automatically after timeout */
283 board->launch_requested = 1;
284 else
285 if ( board->count <= COUNTDOWN ) {
286 add_sprite_to_pool( board->sprite_pool, board->countdown_sprite );
287 set_sprite_frame( board->countdown_sprite, 1+board->count/1000 );
288 board->countdown_state = VISIBLE;
289 }
290 break;
291 case VISIBLE:
292 if ( board->count > 0 )
293 set_sprite_frame( board->countdown_sprite, 1+board->count/1000 );
294 else {
295 remove_sprite_from_pool( board->sprite_pool, board->countdown_sprite);
296 board->countdown_state = ACTIVE;
297 }
298 break;
299 default:
300 break;
301 }
302 }
303
304 /**************************************************************************/
305
explode_board(Board board)306 void explode_board( Board board ) {
307 int i, clock;
308 Bubble bubble;
309 /* sanity check */
310 if (( board->state != LOST )&&
311 ( board->state != WON )&&
312 ( board->state != FROZEN )) {
313 /* first explode all floating bubble sprites */
314 for( i = 0; i < board->bubbles->size; i++ ) {
315 bubble = board->bubbles->element[i];
316 if (( bubble->state != RUNAWAY )&&
317 ( bubble->state != STUCK )&&
318 ( bubble->state != EXPLODING )) {
319 bubble->vx = 0;
320 bubble->vy = EXPLODING_SPEED;
321 set_bubble_state( bubble, EXPLODING, TOP_LAYER, 0 );
322 }
323 }
324 /* now explode stuck bubbles and add a clock skew */
325 clock = 0;
326 for ( i = NB_CELLS-1; i >= 0; i-- )
327 if ( board->array->cell[i] != EMPTY_CELL ) {
328 bubble = board->array->cell[i];
329 bubble->vx = (rnd(100)-50)*EXPLODING_SPEED/10;
330 bubble->vy = -rnd(100)*EXPLODING_SPEED/100;
331 set_bubble_state( bubble, EXPLODING, TOP_LAYER, clock );
332 clock -= PROPAGATION_TIME;
333 }
334 cell_array_empty( board->array );
335 board->state = WON;
336 }
337 }
338
freeze_board(Board board)339 void freeze_board( Board board ) {
340 board->state = WON;
341 }
342
stick_bubble(Board board,Bubble bubble,int target_cell)343 void stick_bubble( Board board, Bubble bubble, int target_cell) {
344 double x, y;
345 cell_center( board->array, target_cell, &x, &y );
346 set_bubble_state( bubble, STUCK, BOTTOM_LAYER, 0);
347 set_bubble_position( bubble, x, y);
348 bubble->cell = target_cell;
349 board->array->cell[target_cell] = bubble;
350 }
351
kill_bubble(Board board,Bubble bubble)352 static void kill_bubble( Board board, Bubble bubble ) {
353 set_bubble_state( bubble, RUNAWAY, TOP_LAYER, 0);
354 remove_sprite_from_pool( board->sprite_pool, bubble->sprite );
355 }
356
animate_bubbles(Board board,int dt)357 int animate_bubbles( Board board, int dt ) {
358 int i;
359 Bubble tmp;
360 Bubble bubble;
361 int clock;
362 int active = 0;
363 int count;
364 int transition_allowed = 1;
365 double x, y, t;
366 /*
367 possible board state transitions:
368
369 1. case NEW: WAITING_FOR_CANON -> READY_TO_FIRE
370 2. case READY: READY_TO_FIRE -> REBOUND
371 3. case LAUNCHED: REBOUND -> WAITING_FOR_CANON | READY_TO_FIRE
372 4. case RISING: RECEIVING_BUBBLES -> WAITING_FOR_CANON | READY_TO_FIRE
373 */
374
375 /* make a fresh copy of bubble list */
376 set_copy( board->bubbles, board->tmpset );
377
378 /* process each bubble in copy */
379 while ( board->tmpset->size > 0 ) {
380
381 bubble = board->tmpset->element[0];
382 set_remove_at( board->tmpset, 0 );
383
384 /* update state of each bubble */
385 switch ( bubble->state ) {
386
387 case NEW:
388 increase_sprite_clock( bubble->sprite, dt );
389 if ( board->canon_empty ) {
390 /* compute new position of bubble */
391 t = M_PI * bubble->clock/2/CANON_LOAD_TIME;
392 /* elliptic trajectory */
393 x = CANON_X - ( CANON_X - NEW_X )*cos(t);
394 y = NEW_Y + ( CANON_Y - NEW_Y )*sin(t);
395 set_bubble_position( bubble, x, y);
396 bubble->clock += dt;
397
398 /* if bubble has reached canon then we're ready to fire */
399 if ( bubble->clock > CANON_LOAD_TIME ) {
400 set_bubble_state( bubble, READY, MEDIUM_LAYER, 0 );
401 set_bubble_position( bubble, CANON_X, CANON_Y );
402 board->canon_empty = 0;
403 /* add a new bubble */
404 board->next_color = new_bubble_color( board );
405 new_board_bubble( board, board->next_color );
406 start_countdown(board);
407 }
408 }
409 if (( ! board->canon_empty )&&
410 ( board->state == WAITING_FOR_CANON )&&( transition_allowed )) {
411 board->state = READY_TO_FIRE;
412 transition_allowed = 0;
413 }
414 break;
415
416 case READY:
417 increase_sprite_clock( bubble->sprite, dt );
418 update_countdown( board, dt );
419 if (( board->launch_requested )&&
420 ( board->state == READY_TO_FIRE )&&( transition_allowed )) {
421 /* launch bubble */
422 set_bubble_state( bubble, LAUNCHED, MEDIUM_LAYER, 0);
423 bubble->vx = board->vx[ board->canon_angle + CANON_ANGLE_MAX ];
424 bubble->vy = board->vy[ board->canon_angle + CANON_ANGLE_MAX ];
425 /* store target cell */
426 bubble->cell = target_cell( board->array, board->canon_angle+CANON_ANGLE_MAX,
427 &bubble->target_y,NULL );
428 transition_allowed = 0;
429 board->state = REBOUND;
430 board->launch_count++;
431 board->canon_empty = 1;
432 board->launch_requested = 0;
433 board->color = board->next_color;
434 cancel_countdown(board);
435 }
436 break;
437
438 case LAUNCHED:
439
440 /* move bubble */
441 bubble->x += dt*bubble->vx;
442 bubble->y += dt*bubble->vy;
443 /* bounce bubble against walls */
444 if ( bubble->x < 0.5 ) {
445 // bubble->x = 1.0 - bubble->x; //That is the other solution to stay inside. If you use it, change also cell.c
446 bubble->x = 0.5;
447 bubble->vx = -bubble->vx;
448 }
449 if ( bubble->x > COLS - 0.5 ) {
450 bubble->x = COLS - 0.5;
451 // bubble->x = 2*COLS - 1.0 - bubble->x;
452 bubble->vx = -bubble->vx;
453 }
454 /* check if bubble has reached its target cell */
455 if ( bubble->y <= bubble->target_y ) {
456 stick_bubble( board, bubble, bubble->cell);
457
458 /* check if bubble triggers an explosion */
459 if ( (count=count_explosive_bubbles( board->array,
460 bubble->cell,
461 board->explosive_bubbles,
462 board->exposion_dates )) >= 3 ) {
463
464 for ( i = 0; i < board->explosive_bubbles->size; i++ ) {
465 tmp = board->explosive_bubbles->element[i];
466 /* simulate propagation */
467 clock = (1 - board->exposion_dates->element[i])*PROPAGATION_TIME;
468 set_bubble_state( tmp, EXPLODING, TOP_LAYER, clock);
469 tmp->vx = EXPLODING_SPEED;
470 // tmp->vx = (rnd(100)-50)*EXPLODING_SPEED/10;
471 tmp->vy = EXPLODING_SPEED;
472 // FIXME also explode those balls, not only the unhooked ones
473 board->array->cell[tmp->cell] = EMPTY_CELL;
474 if ( i >= 3 ) /* immediately send extra bubbles to opponent */
475 vector_push( board->output, tmp->color );
476 /* this bubble has been processed */
477 set_remove( board->tmpset, tmp );
478 }
479 }
480 /* note: no need to check for (transition_allowed) here */
481 transition_allowed = 0;
482 drop_bubbles(board);
483 place_received_bubbles(board);
484 board->state = ( board->canon_empty )? WAITING_FOR_CANON:READY_TO_FIRE;
485 }
486 else
487 set_bubble_position( bubble, bubble->x, bubble->y);
488 break;
489
490 case STUCK:
491 if ( ! bubble->clock )
492 increase_sprite_clock( bubble->sprite, dt );
493 break;
494
495 case EXPLODING:
496 active = 1;
497 bubble->clock += dt;
498 if ( bubble->clock <= 0 )
499 break;
500 if ( bubble->clock > get_bubble_animation_duration(bubble) ) {
501 kill_bubble( board, bubble );
502 break;
503 }
504 /* caution: no break -> code continues below */
505
506 case FALLING:
507 active = 1;
508 /* FIXME: frottements */
509 bubble->vx += dt*GRAVITY*10*(- bubble->vx);
510 bubble->x += dt*bubble->vx;
511 /* Apply gravity */
512 bubble->y += dt*bubble->vy + 0.5*GRAVITY*dt*dt;
513 bubble->vy += dt*GRAVITY;
514 /* bounce bubble against walls */
515 while ( bubble->x < 0.5 || bubble->x > COLS - 0.5001 ) { /* explosions can be quite violente */
516
517 if ( bubble->x < 0.5 ) {
518 bubble->x = 1.0 - bubble->x;
519 bubble->vx = -bubble->vx*.8; /* Falling balls do not elasticly bounce against walls :) */
520 }
521 if ( bubble->x > COLS - 0.5001 ) {
522 bubble->x = 2*COLS - 1.0002 - bubble->x;
523 bubble->vx = -bubble->vx*.8;
524 }
525 }
526 if (bubble->y < ( 0.5 + board->array->first_row*ROW_HEIGHT )) { /* bounce against the upper wall */
527 bubble->y -= board->array->first_row*ROW_HEIGHT;
528 bubble->y = 1.0 - bubble->y;
529 bubble->y += board->array->first_row*ROW_HEIGHT;
530 bubble->vy = -bubble->vy*.8;
531 }
532
533 set_bubble_position( bubble, bubble->x, bubble->y);
534 increase_sprite_clock( bubble->sprite, dt );
535
536 /* detect runaway bubbles */
537 if ( bubble->y > BOARD_HEIGHT - 0.5 ) {
538 /* send bubble to opponent */
539 if ( bubble->state == FALLING )
540 vector_push( board->output, bubble->color );
541 kill_bubble( board, bubble );
542 }
543 break;
544
545 case RISING:
546 active = 1;
547 set_bubble_position( bubble,
548 bubble->x + dt*bubble->vx,
549 bubble->y + dt*bubble->vy );
550 increase_sprite_clock( bubble->sprite, dt );
551 /* check if bubble has reached its target cell */
552 if ( bubble->y <= cell_y( bubble->cell )) {
553 stick_bubble( board, bubble, bubble->cell);
554 set_bubble_state( bubble, STUCK, TOP_LAYER, 0 );
555 board->nb_rising_bubbles--;
556 /* if all bubbles have been received then unlock board */
557 if ( board->nb_rising_bubbles == 0 ) {
558 board->state =
559 ( board->canon_empty )? WAITING_FOR_CANON : READY_TO_FIRE;
560 transition_allowed = 0;
561 }
562 }
563 break;
564
565 case DEAD:
566 if ( bubble->clock <= 200 ) {
567 if ( bubble->clock >= 0 )
568 increase_sprite_clock( bubble->sprite, dt );
569 active = 1;
570 bubble->clock += dt;
571 }
572 break;
573
574 case RUNAWAY:
575 /* destroy bubble */
576 set_remove( board->bubbles, bubble );
577 delete_bubble( bubble );
578
579 default:
580 break;
581 }
582 }
583 return active;
584 }
585
place_received_bubbles(Board board)586 void place_received_bubbles( Board board ) {
587 Bubble bubble;
588 int color, target_cell;
589 double x, y;
590 Vector placed = board->placed_balls;
591
592 board->nb_rising_bubbles = 0;
593 if (board->input->size == 0)
594 return;
595 vector_increase_maxsize(placed, board->input->size);
596 vector_empty(placed);
597
598 while ( board->input->size > 0 && placed->size < 5) {
599 color = vector_pop(board->input);
600 /* find a random target cell */
601 target_cell = find_random_empty_cell(board->array,board->ruleset->malus_top);
602 if ( target_cell != OUT_OF_BOUNDS ) {
603 board->state = RECEIVING_BUBBLES;
604 /* create rising bubble */
605 bubble = new_board_bubble( board, color);
606 set_bubble_state( bubble, RISING, TOP_LAYER, 0);
607 cell_center( board->array, target_cell, &x, &y);
608 set_bubble_position( bubble, x, BOARD_HEIGHT - 0.5);
609 bubble->vx = 0;
610 bubble->vy = -RISING_SPEED;
611 bubble->cell = target_cell;
612 board->nb_rising_bubbles++;
613 /* lock cell to avoid 2 bubbles having the same target */
614 board->array->cell[target_cell] = bubble;
615 vector_push(placed, target_cell);
616 }
617 }
618 /* unlock cells */
619 while ( placed->size > 0 ) {
620 target_cell = vector_pop(placed);
621 if ( target_cell != OUT_OF_BOUNDS ) {
622 board->array->cell[target_cell] = EMPTY_CELL;
623 }
624 }
625 recompute_malus_indicator( board );
626 }
627
get_bubble(Board board,int color)628 void get_bubble( Board board, int color ) {
629 vector_push( board->input, color );
630 }
631
give_bubble(Board board)632 int give_bubble(Board board) {
633 if ( board->output->size > 0 )
634 return vector_pop(board->output);
635 return -1;
636 }
637
recompute_malus_indicator(Board board)638 void recompute_malus_indicator( Board board ) {
639 Bubble bubble;
640 int i;
641
642 return; /* disabled for now since there is no proper graphic for malus balls */
643 while (board->malus_indicators10->size > board->input->size / 10) {
644 bubble = board->malus_indicators10->element[board->malus_indicators10->size-1];
645 set_remove_at( board->malus_indicators10, board->malus_indicators10->size-1);
646 remove_sprite_from_pool( board->sprite_pool, bubble->sprite );
647 delete_bubble( bubble );
648 }
649 while (board->malus_indicators10->size < board->input->size / 10) {
650 bubble = new_bubble( 1, NEW_X, NEW_Y, TOP_LAYER);
651 set_bubble_state( bubble, NEW, TOP_LAYER, 0);
652 add_sprite_to_pool( board->sprite_pool, bubble->sprite );
653 set_add( board->malus_indicators10, bubble );
654 }
655
656 while (board->malus_indicators->size > board->input->size % 10) {
657 bubble = board->malus_indicators->element[board->malus_indicators->size-1];
658 set_remove_at( board->malus_indicators, board->malus_indicators->size-1);
659 remove_sprite_from_pool( board->sprite_pool, bubble->sprite );
660 delete_bubble( bubble );
661 }
662 while (board->malus_indicators->size < board->input->size % 10) {
663 bubble = new_bubble( 2, NEW_X, NEW_Y, TOP_LAYER);
664 set_bubble_state( bubble, NEW, TOP_LAYER, 0);
665 add_sprite_to_pool( board->sprite_pool, bubble->sprite );
666 set_add( board->malus_indicators, bubble);
667 }
668 for (i=0; i<board->malus_indicators10->size; i++) {
669 double x,y;
670 bubble=board->malus_indicators10->element[i];
671 cell_center( board->array, cellCL(board->array, -1, i+1), &x, &y);
672 set_bubble_position( bubble, x, y);
673 }
674 for (i=0; i<board->malus_indicators->size; i++) {
675 double x,y;
676 bubble=board->malus_indicators->element[i];
677 cell_center( board->array, cellCL(board->array, -1, i+board->malus_indicators10->size), &x, &y);
678 set_bubble_position( bubble, x, y);
679 }
680
681 }
682
drop_bubbles(Board board)683 void drop_bubbles(Board board) {
684 int i, cell;
685 float violence; /* big or small explosion ? */
686 float direction; /* direction of each ball */
687 Bubble bubble;
688 count_floating_bubbles( board->array, board->floating_cells );
689 if (board->floating_cells->size) {
690 /* Choose the violence of the explosion based on its size */
691 violence = board->floating_cells->size*20;
692 if (violence > 100) violence=100;
693
694 for ( i = 0; i < board->floating_cells->size; i++ ) {
695 cell = board->floating_cells->element[i];
696 bubble = board->array->cell[cell];
697 set_bubble_state( bubble, FALLING, TOP_LAYER, 0);
698 /* Which direction this ball goes to */
699 direction = ((float)rand())/((float)RAND_MAX+1.0) * -3.14159;
700 bubble->vx = cos(direction)*violence;
701 bubble->vy = sin(direction)*violence;
702 bubble->vx *= EXPLODING_SPEED;
703 bubble->vy *= EXPLODING_SPEED;
704 board->array->cell[cell] = EMPTY_CELL;
705 }
706 }
707 board->empty = cell_array_is_empty( board->array );
708 }
709
lower_board(Board board)710 void lower_board(Board board) {
711 int i, color;
712 Bubble bubble;
713 cell_array_lower( board->array );
714 for ( i = 0; i < board->bubbles->size; i++ ) {
715 bubble = board->bubbles->element[i];
716 if ( bubble->state == STUCK )
717 set_bubble_position( bubble, bubble->x, bubble->y+ROW_HEIGHT );
718 /* update cell record, avoiding overflow */
719 if (bubble->cell + COLS < NB_CELLS) {
720 bubble->cell += COLS;
721 } else {
722 set_remove( board->bubbles, bubble );
723 delete_bubble(bubble);
724 }
725 }
726 if ( ! board->array->moving_ceiling ) { /* add a new row of bubbles */
727 board->array->cell[0] = EMPTY_CELL;
728 for ( i = row_start( board->array, 0); i < COLS; i++ ) {
729 color = new_bubble_color( board );
730 bubble = new_board_bubble( board, color);
731 stick_bubble( board, bubble, i );
732 }
733 }
734 else /* lower alert light */
735 set_sprite_position( board->alert, scale_x( ALERT_X, scale ),
736 scale_y( ALERT_Y+board->array->first_row*ROW_HEIGHT,
737 scale ));
738 }
739
kill_board(Board board)740 void kill_board( Board board ) {
741 int i, clock;
742 Bubble bubble;
743 /* kill bubbles */
744 for( i = 0; i < board->bubbles->size; i++ ) {
745 bubble = board->bubbles->element[i];
746 if (( bubble->state != RUNAWAY )&&( bubble->state != STUCK )) {
747 if (( bubble->state == EXPLODING )||( bubble->state == FALLING ))
748 kill_bubble( board, bubble );
749 else
750 set_bubble_state( bubble, DEAD, BOTTOM_LAYER, 0 );
751 }
752 }
753 clock = 0;
754 for ( i = NB_CELLS-1; i >= 0; i-- )
755 if ( board->array->cell[i] != EMPTY_CELL ) {
756 bubble = board->array->cell[i];
757 set_bubble_state( bubble, DEAD, BOTTOM_LAYER, clock );
758 clock -= PROPAGATION_TIME;
759 }
760 }
761
canon_move(Board board,int dt)762 void canon_move( Board board, int dt ) {
763 int frame, x, y;
764 Frame cframe;
765 board->canon_virtual_angle += board->canon_direction*dt*CANON_ROTATING_SPEED*board->canon_speed;
766 board->canon_virtual_angle = clip( board->canon_virtual_angle,
767 -CANON_ANGLE_MAX, CANON_ANGLE_MAX );
768 board->canon_angle = (int) floor( board->canon_virtual_angle + 0.5 );
769 frame = (board->canon_angle+CANON_ANGLE_MAX)*canon_animation->size/NB_ANGLES;
770 /* compute canon center coordinates */
771 cframe = (Frame) canon_animation->element[frame];
772 x = scale_x( CANON_X, scale );
773 y = scale_y( CANON_Y, scale );
774 set_sprite_frame( board->canon_sprite, frame);
775 set_sprite_position( board->canon_sprite, x, y);
776 }
777
animate_alert(Board board)778 void animate_alert( Board board ) {
779 int blinking;
780 /* blink alert when launch count is almost equal to period */
781 if ( board->launch_count >= board->period - 1 ) {
782 blinking = ( board->launch_count >= board->period )?
783 FAST_BLINKING : SLOW_BLINKING;
784 if (( board->alert_on )&&(( board->clock/blinking ) % 2 ))
785 switch_alert_off(board);
786 if (( ! board->alert_on )&&(( board->clock/blinking ) % 2 == 0 ))
787 switch_alert_on(board);
788 }
789 }
790
animate_board(Board board,int dt)791 void animate_board( Board board, int dt ) {
792 board->clock += dt;
793 board->was_lowered = 0;
794
795 canon_move( board, dt );
796
797 switch( board->state ) {
798
799 case WAITING_FOR_CANON:
800 case READY_TO_FIRE:
801 if ( board_overflow(board) ) {
802 kill_board(board);
803 board->state = LOST;
804 return;
805 }
806 /* lower all bubbles if enough were launched */
807 if ( board->launch_count > board->period ) {
808 lower_board(board);
809 board->launch_count = 0;
810 switch_alert_off(board);
811 board->was_lowered = 1;
812 }
813 if ( board_overflow(board) ) {
814 kill_board(board);
815 board->state = LOST;
816 return;
817 }
818
819 case REBOUND:
820 case RECEIVING_BUBBLES:
821 animate_alert(board);
822 animate_bubbles( board, dt );
823 break;
824
825 case WON:
826 case LOST:
827 if ( ! animate_bubbles( board, dt ) )
828 board->state = FROZEN;
829 break;
830
831 case FROZEN:
832 default:
833 /* do nothing */
834 break;
835 }
836 board->launch_requested = 0;
837 }
838
canon_rotate_left(Board board)839 void canon_rotate_left( Board board ) {
840 if (board->canon_direction == -1)
841 board->canon_speed += MIN( (5-board->canon_speed)/10, 0.1 );
842 else
843 board->canon_speed=1;
844 board->canon_direction = -1;
845 }
846
canon_rotate_right(Board board)847 void canon_rotate_right( Board board ) {
848 if (board->canon_direction == 1)
849 board->canon_speed += MIN( (5-board->canon_speed)/10, 0.1 );
850 else
851 board->canon_speed=1;
852 board->canon_direction = 1;
853 }
854
canon_stop(Board board)855 void canon_stop( Board board ) {
856 board->canon_direction = 0;
857 board->canon_speed = 1;
858 }
859
canon_fire(Board board)860 void canon_fire(Board board) {
861 board->launch_requested = 1;
862 board->canon_speed = 1;
863 }
864
get_board_sprite_pool(Board board)865 SpritePool get_board_sprite_pool( Board board ) {
866 return board->sprite_pool;
867 }
868
get_board_state(Board board)869 enum BoardState get_board_state( Board board ) {
870 return board->state;
871 }
872
board_empty(Board board)873 int board_empty( Board board ) {
874 return board->empty;
875 }
876
get_canon_angle(Board board)877 int get_canon_angle( Board board ) {
878 return board->canon_angle;
879 }
880
board_was_lowered(Board board)881 int board_was_lowered( Board board ) {
882 return board->was_lowered;
883 }
884
get_board_array(Board board)885 CellArray get_board_array( Board board ) {
886 return board->array;
887 }
888
get_last_fire_delay(Board board)889 int get_last_fire_delay( Board board ) {
890 return board->last_fire_delay;
891 }
892
get_board_info(Board board,double ** vx,double ** vy,int * color,int * next_color,int * launch_count,int * period)893 void get_board_info( Board board,
894 double **vx,
895 double **vy,
896 int *color,
897 int *next_color,
898 int *launch_count,
899 int *period ) {
900 *vx = board->vx;
901 *vy = board->vy;
902 *color = board->color;
903 *next_color = board->next_color;
904 *launch_count = board->launch_count;
905 *period = board->period;
906 }
907
908