1 /*
2  * Copyright (c) 2008,2009 Bertrand Janin <tamentis@neopulsar.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 
28 #include <stdio.h>
29 
30 #include "SDL.h"
31 
32 #include "rezerwar.h"
33 
34 
35 extern Configuration *conf;
36 extern Board *board;
37 extern SDL_Surface *screen;
38 extern SDL_Surface *sprites;
39 extern Uint32 key;
40 
41 /* Colors of the text when displaying "4x combo!" */
42 int combo_colors[(MAX_COMBO - 1) * 6] = {
43 	/* foreground		border */
44 	255, 255,   0,		120, 120,   0,	// 2x
45 	255, 120,   0,		120,  60,   0,
46 	255,   0,   0,		120,   0,   0,
47 	255,   0, 120,		120,   0,  60,	// 5x
48 	255,   0, 255,		120,   0, 120,
49 	255, 120, 255,		120,  60, 120,
50 	255, 255, 255,		120, 120, 120,
51 	120, 255, 120,		 60, 120,  60,
52 	  0, 255,   0,		  0, 120,   0,	// 10x
53 };
54 
55 
56 /**
57  * @constructor
58  */
59 Board *
board_new()60 board_new()
61 {
62 	Board *b;
63 	size_t size = BOARD_WIDTH * BOARD_HEIGHT;
64 	char *path;
65 	int i;
66 
67 	b = r_malloc(sizeof(Board));
68 
69 	b->width = BOARD_WIDTH;
70 	b->height = BOARD_HEIGHT;
71 
72 	b->offset_x = BOARD_LEFT;
73 	b->offset_y = BOARD_TOP;
74 
75 	b->bg = NULL;
76 
77 	b->elapsed = 0;
78 
79 	/* Cube related members initialization. */
80 	b->cube_count = 0;
81 	b->cubes = r_malloc(size * sizeof(Cube *));
82 	for (i = 0; i < size; i++)
83 		b->cubes[i] = NULL;
84 
85 	/* Initialize the Cube Queue */
86 	b->cqueue = NULL;
87 	b->cqueue_len = 0;
88 
89 	/* Cube related members. */
90 	b->current_cube = NULL;
91 	b->hold = NULL;
92 	b->next_cube = NULL;
93 	b->remains = -1;
94 	b->launch_next = false;
95 	b->next_line = 1;
96 	b->time_limit = -1;
97 
98 	/* Mole related members */
99 	b->last_mole = -1;
100 	for (i = 0; i < MAX_MOLES; i++)
101 		b->moles[i] = NULL;
102 
103 	/* Initial flames initialization */
104 	board_initialize_flames(b);
105 
106 	/* Pipe status */
107 	for (i = 0; i < BOARD_HEIGHT * 2; i++)
108 		b->pipes[i] = pipe_new();
109 
110 	/* Texts (future OSD) related members */
111 	b->texts = NULL;
112 	b->text_count = 0;
113 
114 	/* Movement related (& controls) */
115 	b->moving_left = 0;
116 	b->moving_right = 0;
117 	b->lateral_speed = 100;
118 	b->lateral_tick = 0;
119 
120 	/* Player related */
121 	b->score = 0;
122 	b->settled = false;
123 	b->combo = 0;
124 	b->status = MTYPE_NOP;
125 	b->paused = false;
126 	b->gameover = false;
127 	b->success = false;
128 	b->silent = false;
129 	b->allow_bomb = true;
130 	b->allow_medic = true;
131 	b->objective_type = OBJTYPE_NONE;
132 	board_set_difficulty_from_score(b);
133 
134 	/* Prompt init. */
135 	b->prompt_text = NULL;
136 	b->prompt_func = NULL;
137 
138 	/* Modal and score */
139 	b->modal = false;
140 	b->score_t = board_add_text(b, "0", 10, 10);
141 	b->timeleft_t = board_add_text(b, "", 10, 30);
142 
143 	/* Status message */
144 	b->status_t = board_add_text(b, "", 0, 240);
145 	b->status_t->effect = EFFECT_SHAKE;
146 	b->status_t->centered = true;
147 	text_set_color1(b->status_t, 225, 186, 0);
148 	text_set_color2(b->status_t, 127,  55, 0);
149 
150 	/* (optional) FPS display */
151 	b->show_fps = false;
152 	b->fps_t = board_add_text(b, "", 550, 10);
153 	text_set_color1(b->status_t, 225, 40, 0);
154 	text_set_color2(b->status_t, 56,  8, 8);
155 
156 	/* Load background. */
157 	path = dpath("gfx/gameback.bmp");
158 	b->bg = SDL_LoadBMP(path);
159 	if (b->bg == NULL)
160 		fatal("Unable to load game background.");
161 	r_free(path);
162 	SDL_SetColorKey(b->bg, SDL_SRCCOLORKEY|SDL_RLEACCEL,
163 			SDL_MapRGB(b->bg->format, 0, 255, 255));
164 
165 	/* Level stuff */
166 	b->next_level = NULL;
167 
168 	return b;
169 }
170 
171 
172 /**
173  * Create a new board and populate the cubes from the given level.
174  */
175 Board *
board_new_from_level(Level * level)176 board_new_from_level(Level *level)
177 {
178 	int i;
179 	Board *board;
180 	Cube *cube = NULL;
181 	Text *title, *description, *prompt;
182 
183 	board = board_new();
184 
185 	/* Transfer the cubes */
186 	for (i = 0; i < (BOARD_WIDTH * BOARD_HEIGHT); i++) {
187 		cube = cube_new_from_char(level->cmap[i]);
188 		if (cube == NULL)
189 			continue;
190 		cube->y = i / BOARD_WIDTH;
191 		cube->x = i % BOARD_WIDTH;
192 		cube_sync_map(board, cube);
193 
194 		/* No need to count rocks.. their passive. */
195 		if (cube->type == CTYPE_ROCK)
196 			continue;
197 
198 		board->cube_count++;
199 	}
200 
201 	/* Transfer the queue */
202 	board->cqueue_len = level->queue_len;
203 	board->cqueue = r_malloc(sizeof(Cube *) * board->cqueue_len);
204 	for (i = 0; i < board->cqueue_len; i++) {
205 		cube = cube_new_from_char(level->queue[i]->cmap[0]);
206 		cube->speed = board->cube_speed;
207 		board->cqueue[i] = cube;
208 	}
209 
210 	/* Prepare the board to welcome the text */
211 	board->modal = true;
212 	board->silent = true;
213 	board->paused = true;
214 
215 	/* Copy level related stuff */
216 	board->objective_type = level->objective_type;
217 	board->allow_bomb = level->allow_bomb;
218 	board->allow_medic = level->allow_medic;
219 	if (level->next)
220 		board->next_level = r_strcp(level->next);
221 	if (level->max_cubes)
222 		board->remains = level->max_cubes;
223 	board->rising_speed = level->rising_speed;
224 	board->time_limit = level->time_limit;
225 	board->max_moles = level->max_moles;
226 
227 	/* Break a couple pipes at the bottom */
228 	for (i = 0; i < level->dead_pipes; i++) {
229 		board->pipes[BOARD_HEIGHT - i - 1]->status = 1;
230 		board->pipes[BOARD_HEIGHT * 2 - i - 1]->status = 1;
231 	}
232 
233 	/* Draw the title */
234 	title = board_add_text(board, level->name, 20, 20);
235 	title->centered = true;
236 	title->temp = true;
237 	text_set_colors(title, 0xFFE64B, 0xB35904);
238 
239 	/* Draw the description */
240 	description = board_add_text(board, level->description, 20, 90);
241 	description->temp = true;
242 	description->font = 1;
243 
244 	/* Draw the press p to contine */
245 	prompt = board_add_text(board, "press 'enter' to start", 350, 440);
246 	prompt->temp = true;
247 	text_set_colors(prompt, 0xFFE64B, 0xB35904);
248 
249 	return board;
250 }
251 
252 void
board_kill(Board * board)253 board_kill(Board *board)
254 {
255 	int i, size;
256 
257 	/* Cube cleanup (only if cubes we have) */
258 	if (board->cubes != NULL) {
259 		size = board->width * board->height;
260 		for (i = 0; i < size; i++) {
261 			if (board->cubes[i] == NULL)
262 				continue;
263 
264 			cube_kill(board->cubes[i]);
265 		}
266 		r_free(board->cubes);
267 		board->cubes = NULL;
268 	}
269 
270 
271 	/* Cube clean up */
272 	if (board->next_cube != NULL) {
273 		cube_kill(board->next_cube);
274 		board->next_cube = NULL;
275 	}
276 
277 	/* Text cleanup */
278 	for (i = 0; i < board->text_count; i++) {
279 		if (board->texts[i] == NULL)
280 			continue;
281 		text_kill(board->texts[i]);
282 	}
283 	free(board->texts);
284 
285 	/* Cube queue cleanup */
286 	r_free(board->cqueue);
287 	board->cqueue = NULL;
288 	board->cqueue_len = 0;
289 
290 	/* Mole cleanup */
291 	for (i = 0; i < MAX_MOLES; i++) {
292 		if (board->moles[i] == NULL)
293 			continue;
294 
295 		mole_kill(board->moles[i]);
296 	}
297 
298 	/* Pipe cleanup */
299 	for (i = 0; i < BOARD_HEIGHT * 2; i++) {
300 		pipe_kill(board->pipes[i]);
301 	}
302 
303 	/* Level stuff */
304 	r_free(board->next_level);
305 
306 	/* General board clean up */
307 	SDL_FreeSurface(board->bg);
308 	r_free(board);
309 }
310 
311 Text *
board_add_text(Board * board,char * value,int x,int y)312 board_add_text(Board *board, char *value, int x, int y)
313 {
314 	Text *t;
315 
316 	t = text_new(value);
317 
318 	board->texts = realloc(board->texts, (board->text_count + 1) * sizeof(Text*));
319 	board->texts[board->text_count] = t;
320 	board->text_count++;
321 
322 	t->x = x;
323 	t->y = y;
324 
325 	return t;
326 }
327 
328 /**
329  * Render the score text on the top left corner of the screen.
330  */
331 #define MAX_SCORESTR_LEN	20
332 void
board_render_texts_score(Board * board)333 board_render_texts_score(Board *board)
334 {
335 	char score[MAX_SCORESTR_LEN] = "";
336 
337 	/*
338 	 * If the board is not in silent mode and is not in tutorial mode
339 	 * (i.e. with objective), then draw the score, we are in normal mode.
340 	 */
341 	if (board->silent != true && board->objective_type == OBJTYPE_NONE) {
342 		snprintf(score, MAX_SCORESTR_LEN, "score: %d", board->score);
343 	}
344 
345 	text_set_value(board->score_t, score);
346 }
347 
348 /**
349  * Render all the texts of the board.
350  */
351 void
board_render_texts(Board * board)352 board_render_texts(Board *board)
353 {
354 	int i;
355 	Text *t;
356 	SDL_Rect r;
357 	SDL_Surface *s;
358 
359 	board_render_texts_score(board);
360 
361 	/* Update the time Text */
362 	if (board->time_limit > -1) {
363 		char tl[20];
364 		snprintf((char *)tl, 20, "time: %d", board->time_limit);
365 		text_set_value(board->timeleft_t, tl);
366 	}
367 
368 	/* Add a modal under all the text. */
369 	if (board->modal == true)
370 		gfx_modal(160);
371 
372 	/* Draw all the Texts, cleaning up trashed ones. */
373 	for (i = 0; i < board->text_count; i++) {
374 		t = board->texts[i];
375 		if (t == NULL) continue;
376 
377 		if (t->trashed == true) {
378 			text_kill(t);
379 			board->texts[i] = NULL;
380 			continue;
381 		}
382 
383 		s = text_get_surface(t);
384 
385 		/* It's legal for a text to have no surface, just skip it */
386 		if (s == NULL)
387 			continue;
388 
389 		text_get_rectangle(t, &r);
390 
391 		SDL_BlitSurface(s, NULL, screen, &r);
392 		SDL_FreeSurface(s);
393 	}
394 }
395 
396 /**
397  * Remove all the text marked 'temp'.
398  */
399 void
board_trash_temp_texts(Board * board)400 board_trash_temp_texts(Board *board)
401 {
402 	Text *t;
403 	int i;
404 
405 	for (i = 0; i < board->text_count; i++) {
406 		t = board->texts[i];
407 		if (t == NULL)
408 			continue;
409 
410 		if (t->temp == true)
411 			t->trashed = true;
412 	}
413 }
414 
415 /**
416  * Toggle paused state. Ignore the request in GameOver mode.
417  */
418 void
board_toggle_pause(Board * board)419 board_toggle_pause(Board *board)
420 {
421 	if (board->gameover)
422 		return;
423 
424 	if (board->paused == true) {
425 		board->modal = false;
426 		board->paused = false;
427 		text_set_value(board->status_t, "");
428 		board_trash_temp_texts(board);
429 	} else {
430 		board->silent = false;
431 		board->modal = true;
432 		board->paused = true;
433 		text_set_value(board->status_t, "paused!");
434 	}
435 }
436 
437 /**
438  * Apply a mask on top of all the rendered shit
439  */
440 void
board_render_transition(Board * board)441 board_render_transition(Board *board)
442 {
443 	switch (board->transition) {
444 		case TTYPE_SHUTTER_OPEN:
445 			gfx_shutter_open();
446 			board->transition = TTYPE_NONE;
447 			break;
448 		/*
449 		case TTYPE_PIXEL_OPEN:
450 			gfx_pixel_open();
451 			board->transition = TTYPE_NONE;
452 			break;
453 		case TTYPE_GREY_CURTAIN:
454 			gfx_transition_grey_curtain();
455 			board->transition = TTYPE_NONE;
456 			break;
457 		*/
458 		case TTYPE_NONE:
459 		default:
460 			break;
461 	}
462 }
463 
464 
465 /**
466  * Render the moles.
467  */
468 void
board_render_moles(Board * board)469 board_render_moles(Board *board)
470 {
471 	int i;
472 
473 	for (i = 0; i < MAX_MOLES; i++) {
474 		if (board->moles[i] == NULL)
475 			continue;
476 
477 		mole_render_trail(board->moles[i]);
478 	}
479 
480 	for (i = 0; i < MAX_MOLES; i++) {
481 		if (board->moles[i] == NULL)
482 			continue;
483 
484 		mole_render(board->moles[i]);
485 	}
486 }
487 
488 
489 /**
490  * Main render function, actually dump pixels on the screen.
491  */
492 void
board_render(Board * board)493 board_render(Board *board)
494 {
495 	/* Redraw the sky. */
496 	sky_render(board);
497 
498 	/* Redraw the background. */
499 	SDL_BlitSurface(board->bg, NULL, screen, NULL);
500 
501 	/* Redraw the next and hold cubes. */
502 	board_render_next(board);
503 	board_render_hold(board);
504 
505 	/* Redraw all the cubes. */
506 	board_render_cubes(board);
507 
508 	/* Render all the explosions/flames */
509 	board_render_flames(board);
510 
511 	/* Moles */
512 	board_render_moles(board);
513 
514 	/* Pipe statuses */
515 	board_render_pipes(board);
516 
517 	/* Animations */
518 	chimneys_render(board);
519 
520 	/* Draw texts elements (meant to replace OSD), and modal */
521 	board_render_texts(board);
522 
523 	/* Apply the transition if any */
524 	board_render_transition(board);
525 
526 	/* Dig up the back buffer. */
527 	SDL_Flip(screen);
528 }
529 
530 
531 /**
532  * Called when the game has ended, return the appropriate MTYPE.
533  */
534 enum mtype
board_gameover(Board * board)535 board_gameover(Board *board)
536 {
537 	/* Success is only for tutorial mode. */
538 	if (board->success) {
539 		if (board->next_level) {
540 			r_free(conf->next_level);
541 			conf->next_level = r_strcp(board->next_level);
542 		}
543 		board_render(board);
544 		return MTYPE_GAMEOVER_WIN;
545 	}
546 
547 	/* The player made it without using hold, extra pts! */
548 	if (board->hold == NULL)
549 		board->score += POINTS_NO_HOLD;
550 
551 	/* The player made a hiscore! */
552 	if (board->objective_type == OBJTYPE_NONE && hiscore_check(board->score) == true) {
553 		conf->last_score = board->score;
554 		return MTYPE_GAMEOVER_HISCORE;
555 	}
556 
557 	/* The player lost and didn't make a hiscore */
558 	return MTYPE_GAMEOVER_LOSE;
559 }
560 
561 
562 void
board_update_pipes(Board * board,uint32_t now)563 board_update_pipes(Board *board, uint32_t now)
564 {
565 	Pipe *pipe;
566 	int i;
567 
568 #define PIPE_ANIM_SPEED	100
569 
570 	for (i = 1; i < BOARD_HEIGHT * 2; i++) {
571 		pipe = board->pipes[i];
572 
573 		if (pipe->tick + PIPE_ANIM_SPEED > now)
574 			continue;
575 
576 		if (pipe->status != -1) {
577 			if (pipe->status < 10) { // b0rked pipe
578 				if (pipe->status >= 3)
579 					pipe->status = 0;
580 				else {
581 					pipe->status++;
582 				}
583 			}
584 		}
585 		pipe->tick = now;
586 	}
587 }
588 
589 
590 /**
591  * This function handles all the elements at ticking point, if anything
592  * pushed the board to a game over, let the caller know with an mtype.
593  */
594 enum mtype
board_update(Board * board,uint32_t now)595 board_update(Board *board, uint32_t now)
596 {
597 	/*
598 	 * The game is in pause, this means no internal logic runs, nothing
599 	 * is happening, not even teh 'elapsed' time flows.
600 	 */
601 	if (board->paused == true)
602 		return MTYPE_NOP;
603 
604 	/*
605 	 * Every seconds elapsed, decrement the amount of second left from
606 	 * the time limit.
607 	 */
608 	board->elapsed += TICK;
609 	if (board->elapsed > 1000) {
610 		board->elapsed = 0;
611 		board->time_limit--;
612 	}
613 
614 	/*
615 	 * If a time limit is set on this level and we've reached it, return
616 	 * with an MTYPE stating so.
617 	 */
618 	if (board->time_limit == 0) {
619 		board->gameover = true;
620 		return MTYPE_GAMEOVER_TIMEOUT;
621 	}
622 
623 	/*
624 	 * Update all the game logic components
625 	 */
626 	board_update_cubes(board, now);
627 	board_update_flames(board, now);
628 	board_update_water(board, now);
629 	board_update_moles(board, now);
630 	board_update_pipes(board, now);
631 
632 	/* Check the cube count for CLEARALL levels. */
633 	if (board->objective_type == OBJTYPE_CLEARALL &&
634 			board->cube_count == 0) {
635 		board->gameover = true;
636 		board->success = true;
637 	}
638 
639 	/* Stop here, something in the 'update' caused a gameover */
640 	if (board->gameover == true)
641 		return board_gameover(board);
642 
643 	/* We need a new line! */
644 	if (board->rising_speed > -1 && board->next_line <= now) {
645 		if (board->next_line != 1) {
646 			board_add_line(board);
647 			if (board->gameover == true)
648 				return board_gameover(board);
649 			/* Delay the current cube's tick, looks better */
650 			board->current_cube->tick = now;
651 			board->current_cube->prev_y--;
652 		}
653 		board->next_line = now + board->rising_speed * 1000;
654 	}
655 
656 	/* We were requested to launch the next cube */
657 	if (board->launch_next == true) {
658 		board_launch_next_cube(board);
659 		board->launch_next = false;
660 	}
661 
662 	/* Animations */
663 	chimneys_update(board, now);
664 	sky_update(board, now);
665 
666 	return MTYPE_NOP;
667 }
668 
669 
670 /**
671  * Generate a random line of cube at the bottom and move everything up one
672  * cube. Do whatever you can to avoid self-triggering lines.
673  */
674 void
board_add_line(Board * board)675 board_add_line(Board *board)
676 {
677 	Cube	*cube;
678 	int	 size = board->width * board->height;
679 	int	 i;
680 
681 	/* Raise all the cubes of the board of one */
682 	for (i = 0; i < size; i++) {
683 		cube = board->cubes[i];
684 
685 		if (cube == NULL)
686 			continue;
687 
688 		if (i < board->width) { // cube in the first row
689 			board->gameover = true;
690 			return;
691 		}
692 
693 		cube->y--;
694 
695 		if (cube->y < 0) {
696 			board->gameover = true;
697 			return;
698 		}
699 
700 		cube_sync_map(board, cube);
701 	}
702 
703 	/* Populate this new free line with random cubes */
704 	board_prepopulate(board, 1);
705 }
706 
707 
708 /**
709  * Load a new random cube or follow the queue depending on the type of
710  * game.
711  */
712 void
board_load_next_cube(Board * board)713 board_load_next_cube(Board *board)
714 {
715 	if (board->cqueue_len) {
716 		board->next_cube = board->cqueue[board->cqueue_len - 1];
717 		board->cqueue[board->cqueue_len - 1] = NULL;
718 		board->cqueue_len--;
719 	} else {
720 		board->next_cube = cube_new_one(board->allow_bomb,
721 				board->allow_medic);
722 	}
723 
724 	board->next_cube->speed = board->cube_speed;
725 	board->next_cube->falling = true;
726 }
727 
728 
729 /**
730  * Take whatever cube is currently loaded as next_cube, add it to the
731  * list of cubes for this board, place it at the right place and pick a
732  * new next_cube.
733  */
734 void
board_launch_next_cube(Board * board)735 board_launch_next_cube(Board *board)
736 {
737 	Cube *cube = board->next_cube;
738 	int i;
739 
740 	/* If we had 0 remaining cubes... you lost. */
741 	if (board->remains == 0) {
742 		board->gameover = true;
743 		return;
744 	}
745 
746 	/* Load the new cube */
747 	board->current_cube = cube;
748 	cube->x = (board->width - 1) / 2;
749 	cube->y = 0;
750 	board->cube_count++;
751 	i = cube->x + cube->y * board->width;
752 
753 	/* We already have someone there, you lose. */
754 	if (board->cubes[i] != NULL) {
755 		board->gameover = true;
756 		return;
757 	}
758 
759 	/* Position the cube on the board */
760 	cube_sync_map(board, cube);
761 
762 	/* Decrement the remaining cubes */
763 	if (board->remains > 0)
764 		board->remains--;
765 
766 	/* If we are NOW at 0, you are on your last cube */
767 	if (board->remains == 0) {
768 		board->next_cube = NULL;
769 		return;
770 	}
771 
772 	board_load_next_cube(board);
773 }
774 
775 
776 
777 void
board_change_next_cube(Board * board)778 board_change_next_cube(Board *board)
779 {
780 	cube_kill(board->next_cube);
781 	board_load_next_cube(board);
782 }
783 
784 
785 
786 /**
787  * Handle the rendering of the top "next" cube.
788  */
789 void
board_render_next(Board * board)790 board_render_next(Board *board)
791 {
792 	SDL_Surface	*surface;
793 	Cube		*cube = board->next_cube;
794 
795 	if (cube == NULL)
796 		return;
797 
798 	surface = cube_get_surface(cube);
799 
800 	gfx_toscreen(surface, cube->x * BSIZE + NEXT_CUBE_LEFT + BSIZE / 2,
801 			      cube->y * BSIZE + NEXT_CUBE_TOP + BSIZE / 2);
802 	gfx_free(surface);
803 }
804 
805 
806 /**
807  * Handle the rendering of the top "hold" cube.
808  */
809 void
board_render_hold(Board * board)810 board_render_hold(Board *board)
811 {
812 	SDL_Surface	*surface;
813 	Cube		*cube = board->hold;
814 
815 	if (cube == NULL)
816 		return;
817 
818 	surface = cube_get_surface(cube);
819 
820 	gfx_toscreen(surface, HOLD_LEFT + BSIZE / 2, HOLD_TOP + BSIZE / 2);
821 	gfx_free(surface);
822 }
823 
824 /**
825  * Transfer all the cubes from a cube to the board. This is anticipating the
826  * death of a cube. Set the cube_count to 0 to avoid duplicate killing. Also
827  * checks for special cubes and execute them.
828  */
829 void
cube_landing(Board * board,Cube * cube)830 cube_landing(Board *board, Cube *cube)
831 {
832 	/*
833 	 * This will let other functions in this loop know that a cube
834 	 * reach its final destination, it is reset to false at every
835 	 * new loops.
836 	 */
837 	if (board->current_cube == cube) {
838 		board->settled = true;
839 		board->launch_next = true;
840 	}
841 
842 	cube->falling = false;
843 
844 	/* Trigger special cubes */
845 	switch (cube->type) {
846 		case CTYPE_BOMB:
847 			board_cube_bomb(board, cube);
848 			break;
849 		case CTYPE_MEDIC:
850 			board_cube_medic(board, cube);
851 			/* !FALLTHROUGH! TODO: find medic sound */
852 		default:
853 			sfx_play_tack1();
854 			break;
855 	}
856 }
857 
858 
859 void
board_kill_row(Board * board,int row)860 board_kill_row(Board *board, int row)
861 {
862 	int i;
863 
864 	for (i = board->width * row; i < board->width * (row + 1); i++) {
865 		if (board->cubes[i] == NULL)
866 			continue;
867 		board_trash_cube(board, board->cubes[i]);
868 	}
869 }
870 
871 
872 void
board_kill_column(Board * board,int col)873 board_kill_column(Board *board, int col)
874 {
875 	int i;
876 
877 	for (i = col; i < board->width * board->height; i += board->width) {
878 		if (board->cubes[i] == NULL)
879 			continue;
880 		board_trash_cube(board, board->cubes[i]);
881 	}
882 }
883 
884 
885 /**
886  * Easy wrapper to pass directly a cube.
887  */
888 void
board_add_points_from_cube(Board * board,int points,Cube * cube)889 board_add_points_from_cube(Board *board, int points, Cube *cube)
890 {
891 	board_add_points(board, points, cube_get_abs_x(cube) + 2,
892 			cube_get_abs_y(cube) + BSIZE / 2);
893 }
894 
895 
896 /**
897  * Add new points to the score, displaying that on screen.
898  */
899 void
board_add_points(Board * board,int points,int x,int y)900 board_add_points(Board *board, int points, int x, int y)
901 {
902 	Text	*text;
903 	char	 value[16];
904 	int	 xpts = points * (board->combo + 1);
905 
906 	/* Don't bother counting score in tutorial mode */
907 	if (board->objective_type != OBJTYPE_NONE)
908 		return;
909 
910 	board->score += xpts;
911 
912 	snprintf(value, 16, "%d", xpts);
913 
914 	text = board_add_text(board, value, x, y);
915 	text->effect |= EFFECT_FADEOUT | EFFECT_FLOAT;
916 }
917 
918 
919 /**
920  * Load the combo color according to the array up there.
921  */
922 void
board_get_combo_colors(Board * board,int * fr,int * fg,int * fb,int * br,int * bg,int * bb)923 board_get_combo_colors(Board *board, int *fr, int *fg, int *fb, int *br, int *bg, int *bb)
924 {
925 	int m = (board->combo - 2) * 6;
926 
927 	*fr = combo_colors[m + 0];
928 	*fg = combo_colors[m + 1];
929 	*fb = combo_colors[m + 2];
930 	*br = combo_colors[m + 3];
931 	*bg = combo_colors[m + 4];
932 	*bb = combo_colors[m + 5];
933 }
934 
935 /**
936  * Show combos on screen as they get added.
937  */
938 void
board_show_combo(Board * board)939 board_show_combo(Board *board)
940 {
941 	Text	*text;
942 	char	 buf[16];
943 	int	 fr, fg, fb,	// foreground color
944 		 br, bg, bb;	// border color
945 
946 	if (board->combo < 2)
947 		return;
948 
949 	board_get_combo_colors(board, &fr, &fg, &fb, &br, &bg, &bb);
950 
951 	snprintf(buf, 16, "%dx combo!", board->combo);
952 	text = board_add_text(board, buf, 0, 200);
953 	text->centered = true;
954 	text->effect |= EFFECT_FADEOUT | EFFECT_SHAKE;
955 	text_set_color1(text, fr, fg, fb);
956 	text_set_color2(text, br, bg, bb);
957 }
958 
959 
960 /**
961  * A Cube Medic just dropped! If a broken pipe is around, fix it. In any case,
962  * it makes sure all the surrounding cubes are opened.
963  */
964 #define MAKE_SIDE_CUBE_ALL(RX, RY) \
965 	side_cube = board_get_cube(board, cube->x + RX, cube->y + RY); \
966 	if (side_cube && side_cube->type != CTYPE_ROCK) \
967 		side_cube->type = CTYPE_ALL;
968 void
board_cube_medic(Board * board,Cube * cube)969 board_cube_medic(Board *board, Cube *cube)
970 {
971 	Pipe	*left_pipe = board->pipes[cube->y];
972 	Pipe	*right_pipe = board->pipes[cube->y + BOARD_HEIGHT];
973 	Cube	*side_cube;
974 
975 	/* Medics shouldn't stop combos from happening, they are good things */
976 	board->settled = false;
977 
978 	/* In front of a broken pipe on the left */
979 	if (cube->x == 0 && left_pipe->status != -1) {
980 		pipe_fix(board, left_pipe);
981 
982 	/* In front of a broken pipe on the right */
983 	} else if (cube->x == (BOARD_WIDTH - 1) && right_pipe->status != -1) {
984 		pipe_fix(board, right_pipe);
985 
986 	/*
987 	 * Find adjacent cubes and convert them so that they all have an
988 	 * opening toward this new cube. For now, convert all of them into
989 	 * 'all-way' cubes.
990 	 */
991 	} else {
992 		/* Sides */
993 		MAKE_SIDE_CUBE_ALL( 0,  1);
994 		MAKE_SIDE_CUBE_ALL(-1,  0);
995 		MAKE_SIDE_CUBE_ALL( 0, -1);
996 		MAKE_SIDE_CUBE_ALL( 1,  0);
997 	}
998 
999 	board_trash_cube(board, cube);
1000 	sfx_play_menuselect();
1001 }
1002 
1003 
1004 /**
1005  * Set the speed of cubes, the number of moles, relative to the score.
1006  */
1007 void
board_set_difficulty_from_score(Board * board)1008 board_set_difficulty_from_score(Board *board)
1009 {
1010 	if (board->score < 5000) {
1011 		board->cube_speed = 1000;
1012 		board->max_moles = 2;
1013 		board->rising_speed = 30;
1014 	} else if (board->score < 10000) {
1015 		board->cube_speed = 850;
1016 		board->max_moles = 3;
1017 		board->rising_speed = 25;
1018 	} else if (board->score < 15000) {
1019 		board->cube_speed = 800;
1020 		board->max_moles = 4;
1021 		board->rising_speed = 25;
1022 	} else if (board->score < 20000) {
1023 		board->cube_speed = 750;
1024 		board->max_moles = 5;
1025 		board->rising_speed = 20;
1026 	} else if (board->score < 25000) {
1027 		board->cube_speed = 700;
1028 		board->max_moles = 6;
1029 		board->rising_speed = 20;
1030 	} else {
1031 		board->cube_speed = 650;
1032 		board->max_moles = 7;
1033 		board->rising_speed = 15;
1034 	}
1035 }
1036 
1037 
1038 /**
1039  * Update an individual cube during the current tick (which time is
1040  * reprensented by 'now'.
1041  */
1042 void
board_update_falling_cube(Board * board,uint32_t now,Cube * cube)1043 board_update_falling_cube(Board *board, uint32_t now, Cube *cube) {
1044 	/* This cube's tick has expired, we might need to move it. */
1045 	if (now - cube->tick > cube->speed) {
1046 		/* Can it fit one unit lower? */
1047 		if (board_move_check(board, cube, 0, 1) == 0) {
1048 			cube->y++;
1049 			cube->tick = now;
1050 			cube_sync_map(board, cube);
1051 		}
1052 
1053 		/* If the cube didn't move for a tick, it is landing. */
1054 		else if (cube->prev_y == cube->y) {
1055 			cube_landing(board, cube);
1056 			return;
1057 		}
1058 
1059 		cube->prev_y = cube->y;
1060 	}
1061 
1062 	/* Ticking for the lateral moves. */
1063 	if (now - board->lateral_tick > board->lateral_speed) {
1064 		if (board->moving_left > 1) {
1065 			board->moving_left--;
1066 		} else if (board->moving_left == 1) {
1067 			board_move_current_cube_left(board);
1068 		}
1069 
1070 		if (board->moving_right > 1) {
1071 			board->moving_right--;
1072 		} else if (board->moving_right == 1) {
1073 			board_move_current_cube_right(board);
1074 		}
1075 
1076 		board->lateral_tick = now;
1077 	}
1078 }
1079 
1080 
1081 /**
1082  * move_check()
1083  * 	bside is the value telling if the cube is on the left of the whole
1084  * 		cube (1) or on the right side (2).
1085  *
1086  * Return values:
1087  * 	0 when path is clear
1088  * 	3 when touching the bottom
1089  * 	1 when a cube from the left side blocked
1090  * 	2 when a cube from the right side blocked
1091  * 	4 when another cube is in the way
1092  */
1093 byte
board_move_check(Board * board,Cube * cube,Sint8 x,Sint8 y)1094 board_move_check(Board *board, Cube *cube, Sint8 x, Sint8 y)
1095 {
1096 	int	j;
1097 
1098 	/* Reached the bottom of the board. */
1099 	if (cube->y + y >= board->height)
1100 		return 3;
1101 
1102 	/* Reached the left border. */
1103 	if (cube->x + x < 0)
1104 		return 1;
1105 
1106 	/* Reach the right border. */
1107 	if (cube->x + x >= board->width)
1108 		return 2;
1109 
1110 	/* There is a cube in this direction. */
1111 	j = (cube->y + y) * board->width + (cube->x + x);
1112 	if (board->cubes[j]) {
1113 		return 1;
1114 	}
1115 
1116 	return 0;
1117 }
1118 
1119 
1120 void
board_move_current_cube_left(Board * board)1121 board_move_current_cube_left(Board *board)
1122 {
1123 	Cube *cube = board->current_cube;
1124 
1125 	if (cube == NULL)
1126 		return;
1127 
1128 	/* Don't rotate cube during pause. */
1129 	if (board->paused == true)
1130 		return;
1131 
1132 	if (board_move_check(board, cube, -1, 0) == 0) {
1133 		cube->x--;
1134 		cube_sync_map(board, cube);
1135 	}
1136 }
1137 
1138 
1139 void
board_move_current_cube_right(Board * board)1140 board_move_current_cube_right(Board *board)
1141 {
1142 	Cube *cube = board->current_cube;
1143 
1144 	if (cube == NULL)
1145 		return;
1146 
1147 	/* Don't rotate cube during pause. */
1148 	if (board->paused == true)
1149 		return;
1150 
1151 	if (board_move_check(board, cube, 1, 0) == 0) {
1152 		cube->x++;
1153 		cube_sync_map(board, cube);
1154 	}
1155 }
1156 
1157 
1158 /**
1159  * In charge of rotating a cube on a board. If there is no space for a
1160  * rotation, ignore, if against a cube, move a bit. If cube is NULL,
1161  * default to current.
1162  */
1163 void
board_rotate_cw(Board * board)1164 board_rotate_cw(Board *board)
1165 {
1166 	Cube *cube = board->current_cube;
1167 
1168 	/* Don't rotate cube during pause. */
1169 	if (board->paused == true)
1170 		return;
1171 
1172 	cube_rotate_cw(cube);
1173 	sfx_play_tick1();
1174 }
1175 
1176 
1177 /**
1178  * Update all the cubes logic (non-graphic stuff).
1179  */
1180 void
board_update_cubes(Board * board,uint32_t now)1181 board_update_cubes(Board *board, uint32_t now)
1182 {
1183 	int i;
1184 	int size = board->width * board->height;
1185 	int type;
1186 	Cube *cube;
1187 
1188 	board->settled = false;
1189 
1190 	/* Only re-adjust the difficulty in normal mode */
1191 	if (board->objective_type == OBJTYPE_NONE)
1192 		board_set_difficulty_from_score(board);
1193 
1194 	for (i = 0; i < size; i++) {
1195 		cube = board->cubes[i];
1196 
1197 		if (cube == NULL)
1198 			continue;
1199 
1200 		/* Check if the cube is trashed (if yes fade it to death) */
1201 		if (cube->trashed == true) {
1202 			cube->fade_status++;
1203 			if (cube->fade_status > BSIZE / 2) {
1204 				cube_kill(cube);
1205 				board->cubes[i] = NULL;
1206 			}
1207 			continue;
1208 		}
1209 
1210 		/* Falling cube have their own logic */
1211 		if (cube->falling == true) {
1212 			board_update_falling_cube(board, now, cube);
1213 			continue;
1214 		}
1215 
1216 
1217 		/* If the cube is a rock, just skip, rocks don't move */
1218 		if (cube->type == CTYPE_ROCK)
1219 			continue;
1220 
1221 		/*
1222 		 * Check if the cube has free space under itself, if yes
1223 		 * disconnect it as a cube and create a new cube falling
1224 		 * much faster.
1225 		 */
1226 		type = board_get_area_type(board, cube->x, cube->y + 1);
1227 		if (type == ATYPE_FREE) {
1228 			cube->falling = true;
1229 			cube->speed = 100;
1230 		}
1231 	}
1232 }
1233 
1234 
1235 /**
1236  * Rendering the cubes is actually handling the graphic part, i.e. blitting
1237  * the textures at the right place.
1238  */
1239 void
board_render_cubes(Board * board)1240 board_render_cubes(Board *board)
1241 {
1242 	int		 i;
1243 	int		 size = board->width * board->height;
1244 	SDL_Surface	*surface;
1245 	Cube		*cube;
1246 
1247 	for (i = 0; i < size; i++) {
1248 		cube = board->cubes[i];
1249 
1250 		if (cube == NULL)
1251 			continue;
1252 
1253 		surface = cube_get_surface(cube);
1254 
1255 		gfx_toscreen(surface, cube->x * BSIZE + BOARD_LEFT,
1256 				      cube->y * BSIZE + BOARD_TOP);
1257 		gfx_free(surface);
1258 	}
1259 }
1260 
1261 
1262 /**
1263  * Dump on stdout the map of the cube as it stands right now.
1264  */
1265 void
board_dump_cube_map(Board * board)1266 board_dump_cube_map(Board *board)
1267 {
1268 	byte x, y;
1269 	int i;
1270 
1271 	for (y = 0; y < board->height; y++) {
1272 		for (x = 0; x < board->width; x++) {
1273 			i = y * board->width + x;
1274 			if (board->cubes[i]) {
1275 				printf("x");
1276 			} else {
1277 				printf(" ");
1278 			}
1279 		}
1280 		printf("\n");
1281 	}
1282 }
1283 
1284 
1285 /**
1286  * Go through all the cubes and remove the water, untie all the current
1287  * networks.
1288  */
1289 void
board_remove_water(Board * board)1290 board_remove_water(Board *board)
1291 {
1292 	int i;
1293 	int bs = board->width * board->height;
1294 
1295 	/* Get rid of all the water. */
1296 	for (i = 0; i < bs; i++) {
1297 		if (board->cubes[i] == NULL)
1298 			continue;
1299 
1300 		board->cubes[i]->water = 0;
1301 		board->cubes[i]->root = NULL;
1302 		board->cubes[i]->network_integrity = 1;
1303 		cube_network_flush(board->cubes[i]);
1304 	}
1305 }
1306 
1307 
1308 /**
1309  * Check the left and right side for cubes. If any, check if they have opened
1310  * pipes on the same side.
1311  */
1312 void
board_update_water(Board * board,uint32_t now)1313 board_update_water(Board *board, uint32_t now)
1314 {
1315 	int	i,			// loop helper
1316 		combo = board->combo;	// backup the combo
1317 	Cube	*cube;			// current cube during the loops
1318 
1319 	board_remove_water(board);
1320 
1321 	/* Scan the left side... */
1322 	for (i = 0; i < board->height; i++) {
1323 		cube = board->cubes[i * board->width];
1324 		if (cube == NULL || cube->falling == true)
1325 			continue;
1326 
1327 		if (board->pipes[i]->status != -1)
1328 			continue;
1329 
1330 		if (cube_plug_match(cube, PLUG_WEST))
1331 			board_spread_water(board, cube, NULL, 1);
1332 	}
1333 
1334 	/*
1335 	 * Now while scanning the right side, also check if water made it all
1336 	 * the way through, in this case, taint the network.
1337 	 */
1338 	for (i = 0; i < board->height; i++) {
1339 		cube = board->cubes[(i + 1) * board->width - 1];
1340 		if (cube == NULL || cube->falling == true)
1341 			continue;
1342 
1343 		if (board->pipes[BOARD_HEIGHT+i]->status != -1)
1344 			continue;
1345 
1346 		if (cube_plug_match(cube, PLUG_EAST)) {
1347 			if (cube->water == 1) {
1348 				cube_network_taint(cube->root);
1349 				/* If we are in link mode, this is a win */
1350 				if (board->objective_type == OBJTYPE_LINK) {
1351 					board->gameover = true;
1352 					board->success = true;
1353 				}
1354 			} else
1355 				board_spread_water(board, cube, NULL, 2);
1356 		}
1357 	}
1358 
1359 	/*
1360 	 * Now we know what networks are tainted, go through the left side
1361 	 * again and look for a root cube (net length > 1), with red water (3),
1362 	 * and a network_integrity preserved. Toggle an avalanche if found.
1363 	 */
1364 	for (i = 0; i < board->height; i++) {
1365 		cube = board->cubes[i * board->width];
1366 		if (cube == NULL || cube->falling == true)
1367 			continue;
1368 
1369 		if (cube->network_size > 1 && cube->water == 3 &&
1370 				cube->network_integrity == 1) {
1371 			if (cube->fade_status > 0) continue;
1372 			board_run_avalanche(board, cube);
1373 		}
1374 	}
1375 
1376 	/*
1377 	 * If we are in a loop with a newly settled cube (reach its final
1378 	 * destination), check if the user has completed a new network (if
1379 	 * board->combo has changed). If no, reset it to zero.
1380 	 */
1381 	if (board->settled == true) {
1382 		if (combo == board->combo) {
1383 			board->combo = 0;
1384 		} else {
1385 			if (board->combo > MAX_COMBO)
1386 				board->combo = MAX_COMBO;
1387 			board_show_combo(board);
1388 		}
1389 	}
1390 }
1391 
1392 
1393 /**
1394  * Run avalanche on the specific cube. This means the cube was already
1395  * identified as the root of a tainted network.
1396  */
1397 void
board_run_avalanche(Board * board,Cube * cube)1398 board_run_avalanche(Board *board, Cube *cube)
1399 {
1400 	int	 i;
1401 	Text	*text;
1402 
1403 	/* Start a fading text... */
1404 	text = board_add_text(board, "Excellent!", 240, 200);
1405 	text->centered = true;
1406 	text->temp = true;
1407 	text_set_color1(text, 255, 0, 0);
1408 	text_set_color2(text, 80, 0, 0);
1409 	text->effect |= EFFECT_SHAKE | EFFECT_FADEOUT;
1410 
1411 	board_add_points_from_cube(board, POINTS_AVALANCHE, cube);
1412 
1413 	/* Run each columns individually */
1414 	board_run_avalanche_column(board, cube);
1415 	for (i = 0; i < cube->network_size; i++) {
1416 		board_run_avalanche_column(board, cube->network[i]);
1417 	}
1418 }
1419 
1420 
1421 /**
1422  * Run an avalanche on only one column, starting from the cube
1423  */
1424 void
board_run_avalanche_column(Board * board,Cube * cube)1425 board_run_avalanche_column(Board *board, Cube *cube)
1426 {
1427 	int y;
1428 	Cube *target;
1429 
1430 	for (y = cube->y; y < board->height; y++) {
1431 		target = board_get_cube(board, cube->x, y);
1432 
1433 		if (target->type == CTYPE_ROCK)
1434 			continue;
1435 
1436 		if (target != NULL && target->trashed == false) {
1437 			board_trash_cube(board, target);
1438 			board_add_points_from_cube(board, POINTS_NETWORK_FACTOR,
1439 					target);
1440 		}
1441 	}
1442 }
1443 
1444 
1445 /**
1446  * Return a cube at the given coordinates, return NULL if not found or if
1447  * border of the board.
1448  */
1449 Cube *
board_get_cube(Board * board,int x,int y)1450 board_get_cube(Board *board, int x, int y)
1451 {
1452 	/* Bad x value */
1453 	if (x < 0 || x >= board->width)
1454 		return NULL;
1455 
1456 	/* Bad y value */
1457 	if (y < 0 || y >= board->height)
1458 		return NULL;
1459 
1460 	return (board->cubes[x + board->width * y]);
1461 }
1462 
1463 
1464 /**
1465  * Return a cube at the given screen coordinates, return NULL if nothing
1466  * found.
1467  */
1468 Cube *
board_get_cube_absolute(Board * board,int x,int y)1469 board_get_cube_absolute(Board *board, int x, int y)
1470 {
1471 	int rx, ry;
1472 
1473 	rx = (x - BOARD_LEFT) / BSIZE;
1474 	ry = (y - BOARD_TOP) / BSIZE;
1475 
1476 	return board_get_cube(board, rx, ry);
1477 }
1478 
1479 
1480 /**
1481  * Returns an integer representing the type of area at x,y
1482  */
1483 int
board_get_area_type(Board * board,int x,int y)1484 board_get_area_type(Board *board, int x, int y)
1485 {
1486 	if (x < 0)
1487 		return ATYPE_BOARD_LEFT;
1488 
1489 	if (x >= board->width)
1490 		return ATYPE_BOARD_RIGHT;
1491 
1492 	if (y >= board->height)
1493 		return ATYPE_BOARD_BOTTOM;
1494 
1495 	if (board->cubes[x + board->width * y] != NULL)
1496 		return ATYPE_CUBE;
1497 
1498 	return ATYPE_FREE;
1499 }
1500 
1501 
1502 /**
1503  * Given specific x/y offsets, try to spread the water to a neighboring cube
1504  */
1505 void
board_spread_attempt(Board * board,Cube * cube,Cube * root,Sint8 ox,Sint8 oy,byte src_plug,byte dest_plug)1506 board_spread_attempt(Board *board, Cube *cube, Cube *root, Sint8 ox, Sint8 oy,
1507 		byte src_plug, byte dest_plug)
1508 {
1509 	Cube	*n;
1510 	int	 status;
1511 	int	 type;
1512 
1513 	type = board_get_area_type(board, cube->x + ox, cube->y + oy);
1514 	switch (type) {
1515 		case ATYPE_FREE:
1516 			if (cube_plug_match(cube, src_plug))
1517 				root->network_integrity = 0;
1518 			break;
1519 		case ATYPE_CUBE:
1520 			n = board_get_cube(board, cube->x + ox, cube->y + oy);
1521 
1522 			/* Falling blocks should not be part of any network */
1523 			if (n->falling && cube_plug_match(cube, src_plug)) {
1524 				root->network_integrity = 0;
1525 				return;
1526 			}
1527 
1528 			status = cube_get_plug_status(cube, src_plug, n,
1529 					dest_plug);
1530 			if (status == PSTAT_CONNECTED)
1531 				board_spread_water(board, n, root, root->water);
1532 			break;
1533 		default:
1534 			break;
1535 	}
1536 }
1537 
1538 
1539 /**
1540  * A network is going to be destroyed.
1541  */
1542 void
board_destroy_network(Board * board,Cube * cube)1543 board_destroy_network(Board *board, Cube *cube)
1544 {
1545 	int i;
1546 
1547 	if (cube->trashed == true)
1548 		return;
1549 
1550 	for (i = 0; i < cube->network_size; i++) {
1551 		board_trash_cube(board, cube->network[i]);
1552 	}
1553 
1554 	/* Only count combos for networks with more than one cubes. */
1555 	if (cube->network_size > 0)
1556 		board->combo++;
1557 
1558 	board_trash_cube(board, cube);
1559 	board_add_points_from_cube(board,
1560 			(cube->network_size + 1) * POINTS_NETWORK_FACTOR,
1561 			cube);
1562 
1563 	sfx_play_lazer();
1564 }
1565 
1566 
1567 /**
1568  * Mark a cube for deletion (trashed), it will fade and ultimately be removed
1569  */
1570 void
board_trash_cube(Board * board,Cube * cube)1571 board_trash_cube(Board *board, Cube *cube)
1572 {
1573 	cube->trashed = true;
1574 	cube_sync_map(board, cube);
1575 	board->cube_count--;
1576 }
1577 
1578 
1579 /**
1580  * Take a cube and check its neighbors for propagation. 'n' is always the
1581  * neighbor cube.
1582  */
1583 void
board_spread_water(Board * board,Cube * cube,Cube * root,int water_type)1584 board_spread_water(Board *board, Cube *cube, Cube *root, int water_type)
1585 {
1586 	/* Don't continue if this cube is already watered. */
1587 	if (cube->water >= 1)
1588 		return;
1589 
1590 	/* Add 'cube' to the network, it doesn't seem to be a starting point. */
1591 	if (root == NULL) {
1592 		root = cube;
1593 	} else {
1594 		cube_network_add(root, cube);
1595 	}
1596 
1597 	cube->water = water_type;
1598 
1599 	/* North, East, South, West */
1600 	board_spread_attempt(board, cube, root,  0, -1, PLUG_NORTH, PLUG_SOUTH);
1601 	board_spread_attempt(board, cube, root,  1,  0, PLUG_EAST,  PLUG_WEST);
1602 	board_spread_attempt(board, cube, root,  0,  1, PLUG_SOUTH, PLUG_NORTH);
1603 	board_spread_attempt(board, cube, root, -1,  0, PLUG_WEST,  PLUG_EAST);
1604 
1605 	if (root == cube && cube->network_integrity == 1) {
1606 		board_destroy_network(board, cube);
1607 	}
1608 }
1609 
1610 
1611 /**
1612  * Prepopulate a number of lines at the bottom of the board.
1613  */
1614 void
board_prepopulate(Board * board,int lines)1615 board_prepopulate(Board *board, int lines)
1616 {
1617 	int x, y;
1618 	Cube *cube;
1619 
1620 	for (x = 1; x < board->width - 1; x++) {
1621 		for (y = board->height - lines; y < board->height; y++) {
1622 			cube = cube_new_random();
1623 			cube->x = x;
1624 			cube->y = y;
1625 			cube_sync_map(board, cube);
1626 			board->cube_count++;
1627 		}
1628 	}
1629 }
1630 
1631 
1632 /**
1633  * Keep the current cube aside. If a cube is currenly held, replace with
1634  * the current one.
1635  */
1636 void
board_hold(Board * board)1637 board_hold(Board *board)
1638 {
1639 	Cube *cube;
1640 
1641 	if (board->hold == NULL) {
1642 		board->hold = board->current_cube;
1643 		board->hold->falling = false;
1644 		board->launch_next = true;
1645 		cube_sync_map(board, board->hold);
1646 	} else {
1647 		cube = board->hold;
1648 		cube->x = board->current_cube->x;
1649 		cube->y = board->current_cube->y;
1650 		cube->prev_y = board->current_cube->prev_y;
1651 		cube->tick = board->current_cube->tick;
1652 		cube->falling = true;
1653 		board->hold = board->current_cube;
1654 		cube_sync_map(board, board->hold);
1655 		board->current_cube = cube;
1656 		cube_sync_map(board, cube);
1657 	}
1658 }
1659 
1660 
1661 /**
1662  * Move the current cube as low as possible and trigger the cube transfer to
1663  * avoid the player to move it.
1664  */
1665 void
board_cube_fall(Board * board)1666 board_cube_fall(Board *board)
1667 {
1668 	int offset = 1;
1669 	Cube *cube = board->current_cube;
1670 
1671 	while (board_move_check(board, cube, 0, offset) == 0)
1672 		offset++;
1673 
1674 	cube->y += offset - 1;
1675 	cube->prev_y = cube->y;
1676 	cube->tick += cube->speed * 2;
1677 
1678 	cube_sync_map(board, cube);
1679 }
1680 
1681 
1682 /**
1683  * Generate a new mole. If reaching MAX, just remove the oldest.
1684  */
1685 void
board_spawn_mole(Board * board)1686 board_spawn_mole(Board *board)
1687 {
1688 	int idx = board->last_mole;
1689 
1690 	if (idx >= MAX_MOLES - 1)
1691 		idx = 0;
1692 	else
1693 		idx++;
1694 
1695 	board->last_mole = idx;
1696 
1697 	if (board->moles[idx] != NULL)
1698 		mole_kill(board->moles[idx]);
1699 
1700 	board->moles[idx] = mole_new();
1701 	board->moles[idx]->board = board;
1702 }
1703 
1704 void
board_update_moles(Board * board,uint32_t now)1705 board_update_moles(Board *board, uint32_t now)
1706 {
1707 	int	i,			// loop variable
1708 		alive_moles = 0;	// number of moles currently active
1709 
1710 
1711 	for (i = 0; i < MAX_MOLES; i++) {
1712 		if (board->moles[i] == NULL)
1713 			continue;
1714 
1715 		if (board->moles[i]->trashed == true) {
1716 			mole_kill(board->moles[i]);
1717 			board->moles[i] = NULL;
1718 			continue;
1719 		}
1720 
1721 		alive_moles++;
1722 		mole_update(board->moles[i], now);
1723 	}
1724 
1725 	/* Ensure we always have enough moles on screen. */
1726 	for (i = alive_moles; i < board->max_moles; i++) {
1727 		board_spawn_mole(board);
1728 	}
1729 }
1730 
1731