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