1181 lines
42 KiB
C
1181 lines
42 KiB
C
/***************************************************************************
|
|
bricks.c - description
|
|
-------------------
|
|
begin : Thu Sep 6 2001
|
|
copyright : (C) 2001 by Michael Speck
|
|
email : kulkanie@gmx.net
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "../client/lbreakout.h"
|
|
#include "extras.h"
|
|
#include "balls.h"
|
|
#include "bricks.h"
|
|
#include "mathfuncs.h"
|
|
|
|
extern Game *cur_game;
|
|
extern int ball_dia;
|
|
|
|
/* extras conversion table */
|
|
Extra_Conv extra_conv_table[EX_NUMBER] = {
|
|
{ EX_SCORE200, '0' },
|
|
{ EX_SCORE500, '1' },
|
|
{ EX_SCORE1000, '2' },
|
|
{ EX_SCORE2000, '3' },
|
|
{ EX_SCORE5000, '4' },
|
|
{ EX_SCORE10000, '5' },
|
|
{ EX_GOLDSHOWER, 'g' },
|
|
{ EX_LENGTHEN, '+' },
|
|
{ EX_SHORTEN, '-' },
|
|
{ EX_LIFE, 'l' },
|
|
{ EX_SLIME, 's' },
|
|
{ EX_METAL, 'm' },
|
|
{ EX_BALL, 'b' },
|
|
{ EX_WALL, 'w' },
|
|
{ EX_FROZEN, 'f' },
|
|
{ EX_WEAPON, 'p' },
|
|
{ EX_RANDOM, '?' },
|
|
{ EX_FAST, '>' },
|
|
{ EX_SLOW, '<' },
|
|
{ EX_JOKER, 'j' },
|
|
{ EX_DARKNESS, 'd' },
|
|
{ EX_CHAOS, 'c' },
|
|
{ EX_GHOST_PADDLE, '~' },
|
|
{ EX_DISABLE, '!' },
|
|
{ EX_TIME_ADD, '&' },
|
|
{ EX_EXPL_BALL, '*' },
|
|
{ EX_BONUS_MAGNET, '}' },
|
|
{ EX_MALUS_MAGNET, '{' },
|
|
{ EX_WEAK_BALL, 'W' }
|
|
};
|
|
/* brick conversion table: brick id, char */
|
|
Brick_Conv brick_conv_table[BRICK_COUNT] = {
|
|
{ 'E', MAP_WALL, 0, -1, 0 },
|
|
{ '#', MAP_BRICK, 1, -1, 1000 },
|
|
{ '@', MAP_BRICK_CHAOS, 2, -1, 1000 },
|
|
{ 'a', MAP_BRICK, 3, 1, BRICK_SCORE * 1 },
|
|
{ 'b', MAP_BRICK, 4, 2, BRICK_SCORE * 2 },
|
|
{ 'c', MAP_BRICK, 5, 3, BRICK_SCORE * 3 },
|
|
{ 'v', MAP_BRICK, 6, 4, BRICK_SCORE * 4 },
|
|
{ 'x', MAP_BRICK_HEAL, 7, 1, BRICK_SCORE * 2},
|
|
{ 'y', MAP_BRICK_HEAL, 8, 2, BRICK_SCORE * 4},
|
|
{ 'z', MAP_BRICK_HEAL, 9, 3, BRICK_SCORE * 6},
|
|
{ 'd', MAP_BRICK, 10, 1, BRICK_SCORE },
|
|
{ 'e', MAP_BRICK, 11, 1, BRICK_SCORE },
|
|
{ 'f', MAP_BRICK, 12, 1, BRICK_SCORE },
|
|
{ 'g', MAP_BRICK, 13, 1, BRICK_SCORE },
|
|
{ 'h', MAP_BRICK, 14, 1, BRICK_SCORE },
|
|
{ 'i', MAP_BRICK, 15, 1, BRICK_SCORE },
|
|
{ 'j', MAP_BRICK, 16, 1, BRICK_SCORE },
|
|
{ 'k', MAP_BRICK, 17, 1, BRICK_SCORE },
|
|
{ '*', MAP_BRICK_EXP, 18, 1, BRICK_SCORE * 2 },
|
|
{ '!', MAP_BRICK_GROW, GROW_BRICK_ID, 1, BRICK_SCORE * 2 },
|
|
};
|
|
|
|
/*
|
|
====================================================================
|
|
Locals
|
|
====================================================================
|
|
*/
|
|
|
|
static void brick_create_instable( Game *game, int life_time );
|
|
|
|
/*
|
|
====================================================================
|
|
Initiate a brick explosion.
|
|
====================================================================
|
|
*/
|
|
void brick_start_expl( int x, int y, int time, Paddle *paddle )
|
|
{
|
|
cur_game->bricks[x][y].exp_time = time;
|
|
cur_game->bricks[x][y].exp_paddle = paddle;
|
|
cur_game->bricks[x][y].mx = x; cur_game->bricks[x][y].my = y;
|
|
list_add( cur_game->exp_bricks, &cur_game->bricks[x][y] );
|
|
}
|
|
|
|
/*
|
|
====================================================================
|
|
Grow a brick at mx,my if the ball does not block the tile and the
|
|
tile is not blocked by a brick.
|
|
====================================================================
|
|
*/
|
|
static void brick_grow( int mx, int my, int id )
|
|
{
|
|
Ball *ball;
|
|
|
|
/* tile empty? */
|
|
if (cur_game->bricks[mx][my].type!=MAP_EMPTY) return;
|
|
|
|
/* check all balls */
|
|
list_reset( cur_game->balls );
|
|
while ( (ball = list_next( cur_game->balls )) != 0 ) {
|
|
if ( mx == (ball->x) / BRICK_WIDTH )
|
|
if ( my == (ball->y) / BRICK_HEIGHT )
|
|
return;
|
|
if ( mx == (ball->x + ball_dia) / BRICK_WIDTH )
|
|
if ( my == (ball->y) / BRICK_HEIGHT )
|
|
return;
|
|
if ( mx == (ball->x) / BRICK_WIDTH )
|
|
if ( my == (ball->y + ball_dia) / BRICK_HEIGHT )
|
|
return;
|
|
if ( mx == (ball->x + ball_dia) / BRICK_WIDTH )
|
|
if ( my == (ball->y + ball_dia) / BRICK_HEIGHT )
|
|
return;
|
|
}
|
|
|
|
/* add brick */
|
|
cur_game->bricks[mx][my].brick_c = brick_conv_table[id].c;
|
|
cur_game->bricks[mx][my].id = brick_conv_table[id].id;
|
|
cur_game->bricks[mx][my].type = brick_conv_table[id].type;
|
|
cur_game->bricks[mx][my].score = brick_conv_table[id].score;
|
|
cur_game->bricks[mx][my].dur = brick_conv_table[id].dur;
|
|
/* keep the extra that is already assigned to this position */
|
|
cur_game->bricks[mx][my].exp_time = -1;
|
|
cur_game->bricks[mx][my].heal_time = -1;
|
|
/* adjust brick count */
|
|
cur_game->bricks_left++;
|
|
cur_game->brick_count++;
|
|
/* add modification */
|
|
bricks_add_mod( mx, my, HT_GROW, 0, vector_get(0,0), 0 );
|
|
|
|
/* get new targets */
|
|
balls_check_targets( -1, 0 );
|
|
}
|
|
|
|
/*
|
|
====================================================================
|
|
Remove brick from offscreen and screen.
|
|
Create shrapnells by type and impulse.
|
|
'paddle' is the paddle that initiated hit either by shot or ball.
|
|
====================================================================
|
|
*/
|
|
void brick_remove( int mx, int my, int type, Vector imp, Paddle *paddle )
|
|
{
|
|
int i,j,px,py;
|
|
int dir;
|
|
|
|
/* if explosive set exp_time of surrounding bricks */
|
|
if ( cur_game->bricks[mx][my].type == MAP_BRICK_EXP ) {
|
|
for ( i = mx - 1; i <= mx + 1; i++ )
|
|
for ( j = my - 1; j <= my + 1; j++ )
|
|
if ( i != mx || j != my )
|
|
if ( cur_game->bricks[i][j].type != MAP_EMPTY )
|
|
if ( cur_game->bricks[i][j].dur > 0 )
|
|
if ( cur_game->bricks[i][j].exp_time == -1 )
|
|
brick_start_expl( i,j,BRICK_EXP_TIME,paddle );
|
|
}
|
|
if ( cur_game->bricks[mx][my].type == MAP_BRICK_GROW ) {
|
|
for ( i = mx - 1; i <= mx + 1; i++ )
|
|
for ( j = my - 1; j <= my + 1; j++ )
|
|
if ( cur_game->bricks[i][j].type == MAP_EMPTY )
|
|
brick_grow( i, j, RANDOM( BRICK_GROW_FIRST, BRICK_GROW_LAST ) );
|
|
}
|
|
|
|
/* decrease brick count if no indestructible brick was destroyed */
|
|
if ( cur_game->bricks[mx][my].dur != -1 ) {
|
|
--cur_game->bricks_left;
|
|
|
|
/* update stats */
|
|
paddle->bricks_cleared++;
|
|
}
|
|
|
|
/* remove brick from map */
|
|
cur_game->bricks[mx][my].id = -1;
|
|
cur_game->bricks[mx][my].dur = -1;
|
|
cur_game->bricks[mx][my].exp_time = -1;
|
|
cur_game->bricks[mx][my].heal_time = -1;
|
|
cur_game->bricks[mx][my].type = MAP_EMPTY;
|
|
cur_game->bricks[mx][my].brick_c = ' ';
|
|
|
|
px = mx*BRICK_WIDTH;
|
|
py = my*BRICK_HEIGHT;
|
|
|
|
/* release extra if one exists */
|
|
dir = ( paddle->type == PADDLE_TOP ) ? -1 : 1;
|
|
if ( cur_game->bricks[mx][my].extra != EX_NONE ) {
|
|
if ( cur_game->diff->allow_maluses ||
|
|
!extra_is_malus( cur_game->bricks[mx][my].extra ) )
|
|
list_add( cur_game->extras,
|
|
extra_create( cur_game->bricks[mx][my].extra, px, py, dir ) );
|
|
}
|
|
else if ( paddle->extra_active[EX_GOLDSHOWER] )
|
|
list_add( cur_game->extras,
|
|
extra_create( EX_SCORE1000, px, py, dir ) );
|
|
cur_game->bricks[mx][my].extra = EX_NONE;
|
|
cur_game->bricks[mx][my].extra_c = ' ';
|
|
|
|
/* add score */
|
|
paddle->score += cur_game->bricks[mx][my].score;
|
|
|
|
}
|
|
|
|
/* Set a brick at position by looking up id in conv table.
|
|
(score is not set,brickcount is not set)
|
|
If id is -1 the brick is cleared. */
|
|
static void brick_set_by_id( Game *game, int mx, int my, int id )
|
|
{
|
|
int k;
|
|
if (id==-1)
|
|
{
|
|
game->bricks[mx][my].id = -1;
|
|
game->bricks[mx][my].dur = -1;
|
|
game->bricks[mx][my].type = MAP_EMPTY;
|
|
game->bricks[mx][my].brick_c = ' ';
|
|
game->bricks[mx][my].extra_c = ' ';
|
|
game->bricks[mx][my].extra = EX_NONE;
|
|
game->bricks[mx][my].score = 0;
|
|
return;
|
|
}
|
|
for (k=0;k<BRICK_COUNT;k++)
|
|
if (brick_conv_table[k].id==id)
|
|
{
|
|
game->bricks[mx][my].exp_time = -1;
|
|
game->bricks[mx][my].heal_time = -1;
|
|
game->bricks[mx][my].brick_c = brick_conv_table[k].c;
|
|
game->bricks[mx][my].type = brick_conv_table[k].type;
|
|
game->bricks[mx][my].id = brick_conv_table[k].id;
|
|
game->bricks[mx][my].dur = brick_conv_table[k].dur;
|
|
game->bricks[mx][my].extra_c = ' ';
|
|
game->bricks[mx][my].extra = EX_NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Create a barrier of 'level' many full lines starting in the forth row
|
|
from the top. In the most upper line we have all explosive bricks. Set
|
|
max score and move delay as well. */
|
|
static void bricks_create_barrier( Game *game, int level)
|
|
{
|
|
int i,j,barrierSize=2+level;
|
|
if (barrierSize>12) barrierSize = 12;
|
|
game->brick_count = game->bricks_left = 0;
|
|
for (i = 1; i < MAP_WIDTH-1; i++)
|
|
for (j = 1; j < MAP_HEIGHT-1; j++)
|
|
brick_set_by_id(game,i,j,-1);
|
|
for (i=1;i<MAP_WIDTH-1;i++)
|
|
{
|
|
brick_set_by_id(game,i,1,GROW_BRICK_ID);
|
|
game->brick_count++; game->bricks_left++;
|
|
}
|
|
for (j=1;j<1+barrierSize;j++)
|
|
for (i=1;i<MAP_WIDTH-1;i++)
|
|
{
|
|
brick_set_by_id(game,i,j+1,10+j%8);
|
|
game->brick_count++; game->bricks_left++;
|
|
}
|
|
game->blBarrierMaxMoves = MAP_HEIGHT - 4 - barrierSize;
|
|
game->blRefreshBricks = 1;
|
|
/* get new targets */
|
|
balls_check_targets( -1, 0 );
|
|
}
|
|
|
|
/* Move barrier one down, that is move all bricks below third line down.
|
|
If this puts one brick into the way of the paddle, set paddleHit which
|
|
finishs the level. */
|
|
static void bricks_move_barrier( Game *game, int *paddleHit )
|
|
{
|
|
Ball *ball = (Ball*)(cur_game->balls->head->next->item) /* we have only one ball */;
|
|
int i,j,blocked;
|
|
*paddleHit = 0;
|
|
for (j=MAP_HEIGHT-3;j>2;j--)
|
|
for (i=1;i<MAP_WIDTH-1;i++)
|
|
{
|
|
/* check whether i,j is blocked by the ball */
|
|
blocked = 0;
|
|
if ( i == (ball->x) / BRICK_WIDTH )
|
|
if ( j == (ball->y) / BRICK_HEIGHT )
|
|
blocked=1;
|
|
if ( i == (ball->x + ball_dia) / BRICK_WIDTH )
|
|
if ( j == (ball->y) / BRICK_HEIGHT )
|
|
blocked=1;
|
|
if ( i == (ball->x) / BRICK_WIDTH )
|
|
if ( j == (ball->y + ball_dia) / BRICK_HEIGHT )
|
|
blocked=1;
|
|
if ( i == (ball->x + ball_dia) / BRICK_WIDTH )
|
|
if ( j == (ball->y + ball_dia) / BRICK_HEIGHT )
|
|
blocked=1;
|
|
if (!blocked)
|
|
brick_set_by_id(game,i,j,game->bricks[i][j-1].id);
|
|
brick_set_by_id(game,i,j-1,-1);
|
|
}
|
|
cur_game->blBarrierMoves++;
|
|
if (cur_game->blBarrierMoves==cur_game->blBarrierMaxMoves)
|
|
*paddleHit = 1;
|
|
game->blRefreshBricks = 1;
|
|
/* get new targets */
|
|
balls_check_targets( -1, 0 );
|
|
}
|
|
|
|
/* reset the ball to top and restart release delay */
|
|
static void attach_ball_to_ceiling( Game *game )
|
|
{
|
|
Ball *ball = list_first(game->balls);
|
|
ball->x = (BRICK_WIDTH*MAP_WIDTH-ball_dia)/2;
|
|
ball->cur.x = ball->x;
|
|
ball->y = BRICK_HEIGHT;
|
|
ball->cur.y = ball->y;
|
|
ball->attached = 0;
|
|
ball->vel.x = ball->vel.y = 0;
|
|
ball_clear_target(&ball->target);
|
|
game->blBallAttached = 1;
|
|
game->blActionTime = 1000; /* time until ball released */
|
|
delay_set(&game->blDelay,game->blActionTime);
|
|
}
|
|
|
|
/* select a sitting duck and highlight it */
|
|
static void select_random_duck( Game *game )
|
|
{
|
|
int i, k;
|
|
/* go to first correct brick */
|
|
i = 0;
|
|
while (game->blDuckPositions[i<<1]==-1) i++;
|
|
/* get number to go */
|
|
k = rand()%game->blNumDucks;
|
|
/* go */
|
|
while (k>0)
|
|
{
|
|
do {i++;} while (game->blDuckPositions[i<<1]==-1);
|
|
k--;
|
|
}
|
|
game->blCurrentDuck = i;
|
|
brick_set_by_id(game,game->blDuckPositions[game->blCurrentDuck*2],
|
|
game->blDuckPositions[game->blCurrentDuck*2+1], 5);
|
|
bricks_add_grow_mod(game->blDuckPositions[game->blCurrentDuck*2],game->blDuckPositions[game->blCurrentDuck*2+1],5);
|
|
}
|
|
|
|
static void bricks_create_new_prey( Game *game, int mx, int my)
|
|
{
|
|
/* get position */
|
|
while (mx==-1||my==-1||game->bricks[mx][my].type!=MAP_EMPTY)
|
|
{
|
|
if (mx==-1) mx = RANDOM(game->blHunterAreaX1+2,game->blHunterAreaX2-2);
|
|
if (my==-1) my = RANDOM(game->blHunterAreaY1+2,game->blHunterAreaY2-2);
|
|
}
|
|
/* set brick */
|
|
brick_set_by_id(game,mx,my,game->blHunterPreyId);
|
|
bricks_add_grow_mod(mx,my,game->blHunterPreyId);
|
|
/* no count update since when we call this function; hunter is on prey and
|
|
thus a brick is missing */
|
|
//game->bricks_left++;
|
|
//game->brick_count++;
|
|
game->blHunterPreyX = mx; game->blHunterPreyY = my;
|
|
game->blHunterTimeLeft = game->blActionTime;
|
|
}
|
|
|
|
static void bricks_create_hunter_area( Game *game )
|
|
{
|
|
int i,j;
|
|
game->blHunterUpId = 15;
|
|
game->blHunterDownId = 13;
|
|
game->blHunterRightId = 11;
|
|
game->blHunterLeftId = 10;
|
|
game->blHunterAreaX1 = 2;
|
|
game->blHunterAreaY1 = 1;
|
|
game->blHunterAreaX2 = MAP_WIDTH-3;
|
|
game->blHunterAreaY2 = 11;
|
|
game->blHunterX = 4;
|
|
game->blHunterY = 6;
|
|
game->blHunterPreyX = 11;
|
|
game->blHunterPreyY = 6;
|
|
game->blHunterPreyId = 5;
|
|
game->blHunterId = 2;
|
|
for (i=game->blHunterAreaX1;i<=game->blHunterAreaX2;i++)
|
|
{
|
|
brick_set_by_id(game,i,game->blHunterAreaY1,game->blHunterUpId);
|
|
brick_set_by_id(game,i,game->blHunterAreaY2,game->blHunterDownId);
|
|
}
|
|
for (i=1;i<=MAP_WIDTH-2;i++)
|
|
{
|
|
brick_set_by_id(game,i,game->blHunterAreaY2+1,1);
|
|
brick_set_by_id(game,i,game->blHunterAreaY2+2,1);
|
|
}
|
|
for (j=game->blHunterAreaY1;j<=game->blHunterAreaY2;j++)
|
|
{
|
|
brick_set_by_id(game,game->blHunterAreaX1,j,game->blHunterLeftId);
|
|
brick_set_by_id(game,game->blHunterAreaX2,j,game->blHunterRightId);
|
|
}
|
|
for (i=2;i<4;i++) brick_set_by_id(game,i,game->blHunterAreaY2+2,game->blHunterUpId);
|
|
for (i=5;i<7;i++) brick_set_by_id(game,i,game->blHunterAreaY2+2,game->blHunterLeftId);
|
|
for (i=9;i<11;i++) brick_set_by_id(game,i,game->blHunterAreaY2+2,game->blHunterRightId);
|
|
for (i=12;i<14;i++) brick_set_by_id(game,i,game->blHunterAreaY2+2,game->blHunterDownId);
|
|
brick_set_by_id(game,game->blHunterX,game->blHunterY,game->blHunterId);
|
|
bricks_create_new_prey(game,game->blHunterPreyX,game->blHunterPreyY);
|
|
}
|
|
|
|
static void bricks_move_hunter( Game *game, int x, int y, int *result )
|
|
{
|
|
*result = 0;
|
|
brick_set_by_id(game,game->blHunterX,game->blHunterY,-1);
|
|
bricks_add_mod( game->blHunterX,game->blHunterY, HT_REMOVE_NO_SOUND, SHR_BY_ENERGY_BALL, vector_get(0,0), game->paddles[0] );
|
|
game->blHunterX+=x; game->blHunterY+=y;
|
|
brick_set_by_id(game,game->blHunterX,game->blHunterY,game->blHunterId);
|
|
bricks_add_grow_mod(game->blHunterX,game->blHunterY,game->blHunterId);
|
|
/* check if still in playing field */
|
|
if (game->blHunterX==game->blHunterAreaX1||game->blHunterX==game->blHunterAreaX2||
|
|
game->blHunterY==game->blHunterAreaY1||game->blHunterY==game->blHunterAreaY2)
|
|
{
|
|
*result = 1;
|
|
return;
|
|
}
|
|
/* check whether we got the pray */
|
|
if (game->blHunterX==game->blHunterPreyX&&game->blHunterY==game->blHunterPreyY)
|
|
{
|
|
*result = 2;
|
|
}
|
|
}
|
|
|
|
/* add a new brick in the first row */
|
|
static void bricks_add_invader( Game *game, int *wave_over )
|
|
{
|
|
Invader *inv;
|
|
int mx, my;
|
|
*wave_over = 0;
|
|
if (game->blInvadersWaveOver) return;
|
|
if (game->blNumInvaders==game->blInvaderLimit)
|
|
{
|
|
if (game->blNumKilledInvaders==game->blInvaderLimit)
|
|
*wave_over = 1;
|
|
return;
|
|
}
|
|
my = 1;
|
|
do { mx = RANDOM(1,MAP_WIDTH-2); }
|
|
while (game->bricks[mx][my].type!=MAP_EMPTY);
|
|
inv = &game->blInvaders[game->blNumInvaders++];
|
|
inv->id = RANDOM(BRICK_GROW_FIRST,BRICK_GROW_LAST);
|
|
inv->x = mx; inv->y = my;
|
|
game->blInvaderTime = 99*game->blInvaderTime/100; /* get faster and faster */
|
|
/* DEBUG: printf("%d\n",game->blInvaderTime); */
|
|
delay_set(&inv->delay,RANDOM(95,105)*game->blInvaderTime/100);
|
|
brick_set_by_id(game,mx,my,inv->id);
|
|
game->bricks_left++;
|
|
game->brick_count++;
|
|
bricks_add_grow_mod(mx,my,inv->id);
|
|
/* get new targets */
|
|
balls_check_targets( -1, 0 );
|
|
}
|
|
|
|
static void bricks_init_next_wave( Game *game )
|
|
{
|
|
if (game->blNumCompletedRuns==0)
|
|
{
|
|
game->blInvaderLimit= 50; /* total number of invaders in this wave */
|
|
if (game->blInvaders) free(game->blInvaders);
|
|
game->blInvaders = (Invader*)calloc(game->blInvaderLimit,sizeof(Invader));
|
|
}
|
|
game->blInvaderTime = game->blActionTime;
|
|
game->blNumInvaders = 0;
|
|
game->blNumKilledInvaders = 0;
|
|
game->blInvadersWaveOver = 0;
|
|
delay_set(&game->blDelay,game->blInvaderTime);
|
|
bricks_add_invader(game,&game->blInvadersWaveOver);
|
|
bricks_add_invader(game,&game->blInvadersWaveOver);
|
|
bricks_add_invader(game,&game->blInvadersWaveOver);
|
|
}
|
|
|
|
static void bricks_move_invaders( Game *game, int ms, int *paddleHit )
|
|
{
|
|
Invader *inv;
|
|
int i;
|
|
*paddleHit = 0;
|
|
for (i=0;i<game->blNumInvaders;i++)
|
|
if (game->blInvaders[i].x!=-1)
|
|
{
|
|
inv = &game->blInvaders[i];
|
|
if (delay_timed_out(&inv->delay,ms))
|
|
if (game->bricks[inv->x][inv->y+1].type==MAP_EMPTY)
|
|
{
|
|
brick_set_by_id(game,inv->x,inv->y,-1);
|
|
bricks_add_mod(inv->x,inv->y, HT_REMOVE_NO_SOUND, SHR_BY_ENERGY_BALL, vector_get(0,0), game->paddles[0] );
|
|
inv->y++;
|
|
if (inv->y==MAP_HEIGHT-2) *paddleHit = 1;
|
|
brick_set_by_id(game,inv->x,inv->y,inv->id);
|
|
bricks_add_grow_mod(inv->x,inv->y,inv->id);
|
|
}
|
|
}
|
|
/* get new targets */
|
|
balls_check_targets( -1, 0 );
|
|
}
|
|
|
|
/*
|
|
====================================================================
|
|
Create a brick at a random position and set it as explosive with the
|
|
given time or stable if life_time = -1.
|
|
====================================================================
|
|
*/
|
|
static void brick_create_instable( Game *game, int life_time )
|
|
{
|
|
int mx,my,id;
|
|
|
|
/* choose id of a normal brick */
|
|
id = RANDOM(10,17);
|
|
|
|
/* do again if nothing added due to ball or existing brick */
|
|
do
|
|
{
|
|
mx = RANDOM(1,MAP_WIDTH-2);
|
|
my = RANDOM(1,1+EDIT_HEIGHT-1); /* only senseful for local game */
|
|
}
|
|
while (game->bricks[mx][my].type!=MAP_EMPTY);
|
|
|
|
/* build brick */
|
|
brick_set_by_id(game,mx,my,id);
|
|
game->bricks[mx][my].score = 0; /* score is given in brick_hit by special formula */
|
|
/* adjust brick count */
|
|
game->bricks_left++;
|
|
game->brick_count++;
|
|
|
|
/* set as explosive for first paddle */
|
|
if (life_time>=0)
|
|
{
|
|
game->bricks[mx][my].exp_time = life_time;
|
|
game->bricks[mx][my].exp_paddle = game->paddles[0];
|
|
game->bricks[mx][my].mx = mx; game->bricks[mx][my].my = my;
|
|
list_add( game->exp_bricks, &game->bricks[mx][my] );
|
|
}
|
|
|
|
/* position of jumping jack */
|
|
game->bl_jj_mx = mx; game->bl_jj_my = my;
|
|
|
|
/* send to client */
|
|
bricks_add_grow_mod( mx, my, id );
|
|
|
|
/* get new targets */
|
|
balls_check_targets( -1, 0 );
|
|
|
|
//printf("Created instable brick with %d msecs lifetime.\n", life_time );
|
|
}
|
|
|
|
/*
|
|
====================================================================
|
|
Generate bonus level. While this can be extended to NETWORK game,
|
|
it is only implemented for local games by now.
|
|
====================================================================
|
|
*/
|
|
static void bricks_init_bonus_level( Game *game, int game_type, int level_type )
|
|
{
|
|
int i;
|
|
game->blNumCompletedRuns = 0;
|
|
game->blRatioSum = 0.0;
|
|
switch (level_type)
|
|
{
|
|
case LT_JUMPING_JACK:
|
|
game->blActionTime = 20000; /* time in millisecs */
|
|
game->blMaxScore = 2000;
|
|
brick_create_instable( game, game->blActionTime );
|
|
break;
|
|
case LT_OUTBREAK:
|
|
game->blActionTime = 4000; /* time until new brick */
|
|
game->blMaxScore = 6000;
|
|
game->blCancerCount = 0;
|
|
game->blCancerLimit = 50;
|
|
delay_set(&game->blDelay,game->blActionTime);
|
|
for (i=0;i<5;i++) brick_create_instable( game, -1 );
|
|
break;
|
|
case LT_BARRIER:
|
|
game->blActionTime = 3000; /* time until move down */
|
|
game->blMaxScore = 4000;
|
|
game->blBarrierLevel = 1;
|
|
game->blBarrierMoves = 0;
|
|
delay_set(&game->blDelay,game->blActionTime);
|
|
bricks_create_barrier(game,game->blBarrierLevel);
|
|
break;
|
|
case LT_SITTING_DUCKS:
|
|
if (game->blDuckPositions) free(game->blDuckPositions);
|
|
game->blTotalNumDucks = game->blNumDucks = 8;
|
|
game->blDuckPositions = (int*)calloc(2*game->blTotalNumDucks,sizeof(int));
|
|
game->blDuckPositions[0] = 1; game->blDuckPositions[1] = 5;
|
|
game->blDuckPositions[2] = 2; game->blDuckPositions[3] = 3;
|
|
game->blDuckPositions[4] = 4; game->blDuckPositions[5] = 2;
|
|
game->blDuckPositions[6] = 6; game->blDuckPositions[7] = 1;
|
|
game->blDuckPositions[8] = 9; game->blDuckPositions[9] = 1;
|
|
game->blDuckPositions[10] = 11; game->blDuckPositions[11] = 2;
|
|
game->blDuckPositions[12] = 13; game->blDuckPositions[13] = 3;
|
|
game->blDuckPositions[14] = 14; game->blDuckPositions[15] = 5;
|
|
game->blDuckBaseScore = 6000;
|
|
game->blMaxScore = game->blDuckBaseScore;
|
|
for (i=0;i<game->blTotalNumDucks;i++)
|
|
brick_set_by_id(game,game->blDuckPositions[2*i],game->blDuckPositions[2*i+1],3);
|
|
attach_ball_to_ceiling(game);
|
|
break;
|
|
case LT_HUNTER:
|
|
game->blActionTime = 30000; /* time in millisecs */
|
|
game->blMaxScore = 10000;
|
|
bricks_create_hunter_area( game ); /* includes setting hunter and first prey */
|
|
break;
|
|
case LT_DEFENDER:
|
|
game->blActionTime = 2000; /* time until new invader */
|
|
game->blMaxScore = 40000; /* max score per wave */
|
|
game->blInvaderScore = 400;
|
|
bricks_init_next_wave( game );
|
|
break;
|
|
default:
|
|
fprintf(stderr,"Unknown Bonus Level Type: %d\n", level_type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
====================================================================
|
|
Publics
|
|
====================================================================
|
|
*/
|
|
|
|
/*
|
|
====================================================================
|
|
Init bricks from level data, set the warp limit (percent) and
|
|
add regenerating bricks. As this function is called when
|
|
initializing a level it does not use the 'cur_game' context.
|
|
'score_mod' is percentual and 100 means normal score.
|
|
====================================================================
|
|
*/
|
|
void bricks_init( Game *game, int game_type, Level *level, int score_mod, int rel_warp_limit )
|
|
{
|
|
int i, j, k;
|
|
int y_off;
|
|
|
|
/* clear everything */
|
|
for (i = 0; i < MAP_WIDTH; i++)
|
|
for (j = 0; j < MAP_HEIGHT; j++) {
|
|
game->bricks[i][j].id = -1;
|
|
game->bricks[i][j].dur = -1;
|
|
game->bricks[i][j].type = MAP_EMPTY;
|
|
game->bricks[i][j].brick_c = ' ';
|
|
game->bricks[i][j].extra_c = ' ';
|
|
game->bricks[i][j].extra = EX_NONE;
|
|
game->bricks[i][j].score = 0;
|
|
}
|
|
|
|
/* clear explosion/healing list */
|
|
list_clear( game->exp_bricks );
|
|
list_clear( game->heal_bricks );
|
|
|
|
/* build walls */
|
|
for (i = 0; i < MAP_WIDTH; i++)
|
|
if ( game_type == GT_LOCAL ) {
|
|
/* in multiplayer this is open */
|
|
game->bricks[i][0].id = 0;
|
|
game->bricks[i][0].dur = -1;
|
|
game->bricks[i][0].type = MAP_WALL; /* this means - indestructible */
|
|
}
|
|
for (j = 0; j < MAP_HEIGHT; j++) {
|
|
game->bricks[0][j].id = 0;
|
|
game->bricks[0][j].dur = -1;
|
|
game->bricks[0][j].type = MAP_WALL; /* this means - indestructible */
|
|
game->bricks[MAP_WIDTH - 1][j].id = 0;
|
|
game->bricks[MAP_WIDTH - 1][j].dur = -1;
|
|
game->bricks[MAP_WIDTH - 1][j].type = MAP_WALL;
|
|
}
|
|
|
|
/* load map (centered if multiplayer) if level::type is LT_NORMAL. Otherwise
|
|
generate the special bricks for the special bonus levels. */
|
|
if (level->type==LT_NORMAL)
|
|
{
|
|
if ( game_type == GT_NETWORK )
|
|
y_off = ( MAP_HEIGHT - EDIT_HEIGHT ) / 2;
|
|
else
|
|
y_off = 1;
|
|
for (i = 0; i < EDIT_WIDTH; i++)
|
|
for (j = 0; j < EDIT_HEIGHT; j++) {
|
|
/* create bricks */
|
|
game->bricks[i + 1][j + y_off].exp_time = -1;
|
|
game->bricks[i + 1][j + y_off].heal_time = -1;
|
|
for ( k = 0; k < BRICK_COUNT; k++ )
|
|
if ( level->bricks[i][j] == brick_conv_table[k].c ) {
|
|
brick_set_by_id( game, i+1,j+y_off,brick_conv_table[k].id );
|
|
game->bricks[i + 1][j + y_off].score = (score_mod * brick_conv_table[k].score) / 10;
|
|
break;
|
|
}
|
|
if ( k == BRICK_COUNT && level->bricks[i][j] != '.' && level->bricks[i][j] != ' ' )
|
|
printf( "unknown: %i,%i: %c\n", i, j, level->bricks[i][j] );
|
|
/* create extras */
|
|
game->bricks[i + 1][j + y_off].extra = EX_NONE;
|
|
for ( k = 0; k < EX_NUMBER; k++ )
|
|
if ( level->extras[i][j] == extra_conv_table[k].c ) {
|
|
game->bricks[i + 1][j + y_off].extra_c = extra_conv_table[k].c;
|
|
game->bricks[i + 1][j + y_off].extra = extra_conv_table[k].type;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* generate bonus level */
|
|
if (game->localServerGame)
|
|
bricks_init_bonus_level( game, game_type, level->type );
|
|
}
|
|
|
|
/* count bricks & extras */
|
|
game->bricks_left = 0; game->extra_count = 0;
|
|
for (i = 1; i < MAP_WIDTH - 1; i++)
|
|
for (j = 1; j < MAP_HEIGHT - 1; j++) {
|
|
if ( game->bricks[i][j].dur > 0 ) {
|
|
game->bricks_left++;
|
|
if ( game->bricks[i][j].extra != EX_NONE )
|
|
game->extra_count++;
|
|
}
|
|
}
|
|
game->brick_count = game->bricks_left;
|
|
|
|
/* the above code is fine for levels that are loaded from a
|
|
* snapshot identical to the original level which is the
|
|
* case for first load in local game and always in network
|
|
* game (as in this case, the complete level is sent and
|
|
* never restarted). for a local game where a ball gets
|
|
* lost the total number of bricks gets distorted. to allow
|
|
* fair usage of the warp function the level's original
|
|
* brick count is used in any case. 'game->brick_count as
|
|
* computed above is screwed anyway for re-initiated levels
|
|
* as it does not remember the cleared bricks. the original
|
|
* number does not take the grown bricks in account. i don't
|
|
* see an easy way to sync both, so let's keep it, nobody
|
|
* is hurt as long as no one asks for stats on local games.
|
|
* DONT ASK FOR STATS ON LOCAL GAMES!!!
|
|
* okay, grown bricks are not taken in account for the
|
|
* warp limit, it is computed based on the initial brick
|
|
* count, thus the additional bricks from a growth must be
|
|
* cleared without helping to hit the warp limit. that is
|
|
* not too unfair i think, as it will allow to warp when
|
|
* you come below the limit and then loose the ball.
|
|
* why do i write so much anyway? */
|
|
game->warp_limit = ( 100 - rel_warp_limit ) *
|
|
level->normal_brick_count / 100;
|
|
|
|
/* add regenerating bricks */
|
|
for ( i = 1; i < MAP_WIDTH - 1; i++ )
|
|
for ( j = 1; j < MAP_HEIGHT - 1; j++ )
|
|
if ( game->bricks[i][j].type == MAP_BRICK_HEAL )
|
|
if ( game->bricks[i][j].dur < 3 ) {
|
|
game->bricks[i][j].mx = i;
|
|
game->bricks[i][j].my = j;
|
|
game->bricks[i][j].heal_time = BRICK_HEAL_TIME;
|
|
list_add( game->heal_bricks, &game->bricks[i][j] );
|
|
}
|
|
}
|
|
/*
|
|
====================================================================
|
|
Hit brick and remove if destroyed. 'metal' means the ball
|
|
destroys any brick with the first try.
|
|
type and imp are used for shrapnell creation.
|
|
'extra' contains the pushed extra if one was released.
|
|
'paddle' is the paddle that initiated hit either by shot or ball.
|
|
Return true on destruction
|
|
====================================================================
|
|
*/
|
|
int brick_hit( int mx, int my, int metal, int type, Vector imp, Paddle *paddle )
|
|
{
|
|
int remove = 0;
|
|
int loose_dur = 0;
|
|
int i;
|
|
double ratio;
|
|
|
|
/* perform action of special levels */
|
|
if (cur_game->localServerGame)
|
|
switch (cur_game->level_type)
|
|
{
|
|
case LT_HUNTER:
|
|
/* no brick is broken, just move hunter if correct brick hit */
|
|
if (cur_game->bricks[mx][my].id==cur_game->blHunterUpId)
|
|
bricks_move_hunter(cur_game,0,-1,&i);
|
|
else
|
|
if (cur_game->bricks[mx][my].id==cur_game->blHunterDownId)
|
|
bricks_move_hunter(cur_game,0,1,&i);
|
|
else
|
|
if (cur_game->bricks[mx][my].id==cur_game->blHunterLeftId)
|
|
bricks_move_hunter(cur_game,-1,0,&i);
|
|
else
|
|
if (cur_game->bricks[mx][my].id==cur_game->blHunterRightId)
|
|
bricks_move_hunter(cur_game,1,0,&i);
|
|
if (i==1)
|
|
{
|
|
/* we screwed it */
|
|
cur_game->bricks_left = 0;
|
|
}
|
|
else if (i==2)
|
|
{
|
|
/* we caught a brick! */
|
|
ratio = ((double)cur_game->blHunterTimeLeft)/cur_game->blActionTime;
|
|
paddle->score += ratio*cur_game->blMaxScore;
|
|
cur_game->totalBonusLevelScore += ratio*cur_game->blMaxScore;
|
|
printf("H: maxScore: %d, ratio: %f, respawn time: %d\n",cur_game->blMaxScore,ratio,cur_game->blActionTime);
|
|
cur_game->blActionTime *= 0.95;
|
|
cur_game->blMaxScore *= 1.05;
|
|
cur_game->blNumCompletedRuns++;
|
|
cur_game->blRatioSum += ratio;
|
|
bricks_create_new_prey(cur_game,-1,-1);
|
|
}
|
|
return 0;
|
|
case LT_SITTING_DUCKS:
|
|
if (imp.y<0)
|
|
{
|
|
/* any hit results in reseting the ball if direction is up;
|
|
if not the highlighted brick was hit, it will explode */
|
|
if (mx==cur_game->blDuckPositions[cur_game->blCurrentDuck*2]&&
|
|
my==cur_game->blDuckPositions[cur_game->blCurrentDuck*2+1])
|
|
{
|
|
/* wow! give some points for that */
|
|
ratio = 1.0;
|
|
paddle->score += ratio*cur_game->blMaxScore;
|
|
cur_game->totalBonusLevelScore += ratio*cur_game->blMaxScore;
|
|
//printf("SD: hit!\n");
|
|
cur_game->blMaxScore *= 1.05;
|
|
cur_game->blNumCompletedRuns++;
|
|
cur_game->blRatioSum += ratio;
|
|
brick_set_by_id(cur_game,mx,my,3);
|
|
bricks_add_grow_mod(mx,my,3);
|
|
}
|
|
else
|
|
{
|
|
/* dude, you suck! */
|
|
mx = cur_game->blDuckPositions[cur_game->blCurrentDuck*2];
|
|
my = cur_game->blDuckPositions[cur_game->blCurrentDuck*2+1];
|
|
cur_game->bricks[mx][my].exp_time = 1;
|
|
cur_game->bricks[mx][my].exp_paddle = cur_game->paddles[0];
|
|
cur_game->bricks[mx][my].mx = mx;
|
|
cur_game->bricks[mx][my].my = my;
|
|
list_add( cur_game->exp_bricks, &cur_game->bricks[mx][my] );
|
|
cur_game->blMaxScore = cur_game->blDuckBaseScore;
|
|
cur_game->blNumDucks--;
|
|
cur_game->blDuckPositions[cur_game->blCurrentDuck*2] = -1;
|
|
}
|
|
attach_ball_to_ceiling(cur_game);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* a map wall can't be touched */
|
|
if ( cur_game->bricks[mx][my].type == MAP_WALL ) return 0;
|
|
|
|
/* if metal ball resistance is futile */
|
|
if ( metal )
|
|
remove = 1;
|
|
else {
|
|
if ( cur_game->bricks[mx][my].dur == -1 )
|
|
return 0; /* duration of -1 means only breakable
|
|
by engery ball (metal ball) */
|
|
if ( cur_game->bricks[mx][my].dur <= 1 )
|
|
remove = 1;
|
|
else
|
|
loose_dur = 1;
|
|
}
|
|
|
|
/* perform action of special levels */
|
|
if (cur_game->localServerGame)
|
|
switch (cur_game->level_type)
|
|
{
|
|
case LT_JUMPING_JACK:
|
|
/* grow another brick (since a hit means sure removal) */
|
|
ratio = ((double)cur_game->bricks[cur_game->bl_jj_mx][cur_game->bl_jj_my].exp_time)/cur_game->blActionTime;
|
|
paddle->score += ratio*cur_game->blMaxScore;
|
|
cur_game->totalBonusLevelScore += ratio*cur_game->blMaxScore;
|
|
//printf("JJ: maxScore: %d, ratio: %f, respawn time: %d\n",cur_game->blMaxScore,ratio,cur_game->blActionTime);
|
|
cur_game->blActionTime *= 0.95;
|
|
cur_game->blMaxScore *= 1.05;
|
|
cur_game->blNumCompletedRuns++;
|
|
cur_game->blRatioSum += ratio;
|
|
brick_create_instable( cur_game, cur_game->blActionTime );
|
|
break;
|
|
}
|
|
|
|
if ( remove ) {
|
|
bricks_add_mod( mx, my, HT_REMOVE, type, imp, paddle );
|
|
brick_remove( mx, my, type, imp, paddle );
|
|
}
|
|
else
|
|
if ( loose_dur ) {
|
|
bricks_add_mod( mx, my, HT_HIT, type, imp, paddle );
|
|
brick_loose_dur( mx, my, 1 );
|
|
}
|
|
|
|
/* perform action of special levels */
|
|
if (cur_game->localServerGame)
|
|
switch (cur_game->level_type)
|
|
{
|
|
case LT_DEFENDER:
|
|
for (i=0;i<cur_game->blNumInvaders;i++)
|
|
if (cur_game->blInvaders[i].x==mx&&cur_game->blInvaders[i].y==my)
|
|
{
|
|
cur_game->blInvaders[i].x = -1;
|
|
paddle->score += cur_game->blInvaderScore;
|
|
cur_game->totalBonusLevelScore += cur_game->blInvaderScore;
|
|
break;
|
|
}
|
|
cur_game->blNumKilledInvaders++;
|
|
cur_game->blTotalNumKilledInvaders++;
|
|
if (cur_game->bricks_left==0) /* cleared this wave, next one please! */
|
|
{
|
|
ratio = ((double)(cur_game->blInvaderLimit - cur_game->blNumKilledInvaders))/cur_game->blInvaderLimit;
|
|
paddle->score += ratio*cur_game->blMaxScore;
|
|
cur_game->totalBonusLevelScore += ratio*cur_game->blMaxScore;
|
|
cur_game->blActionTime *= 0.95;
|
|
cur_game->blMaxScore *= 1.05;
|
|
cur_game->blInvaderScore *= 1.05;
|
|
cur_game->blNumCompletedRuns++;
|
|
cur_game->blRatioSum += ratio;
|
|
bricks_init_next_wave( cur_game );
|
|
}
|
|
break;
|
|
case LT_OUTBREAK:
|
|
if (cur_game->bricks_left==0)
|
|
{
|
|
/* reset scene */
|
|
ratio = ((double)(cur_game->blCancerLimit - cur_game->blCancerCount))/cur_game->blCancerLimit;
|
|
paddle->score += ratio*cur_game->blMaxScore;
|
|
cur_game->totalBonusLevelScore += ratio*cur_game->blMaxScore;
|
|
//printf("OB: maxScore: %d, ratio: %f, respawn time: %d\n",cur_game->blMaxScore,ratio,cur_game->blActionTime);
|
|
cur_game->blActionTime *= 0.95;
|
|
cur_game->blMaxScore *= 1.05;
|
|
delay_set(&cur_game->blDelay,cur_game->blActionTime);
|
|
cur_game->blCancerCount = 0;
|
|
cur_game->blNumCompletedRuns++;
|
|
cur_game->blRatioSum += ratio;
|
|
for (i=0;i<5;i++) brick_create_instable( cur_game, -1 );
|
|
}
|
|
break;
|
|
case LT_BARRIER:
|
|
if (my == 1)
|
|
{
|
|
/* build a tougher barrier, enter next level */
|
|
ratio = ((double)(cur_game->blBarrierMaxMoves - cur_game->blBarrierMoves))/cur_game->blBarrierMaxMoves;
|
|
if (ratio<0) ratio=0;
|
|
paddle->score += 500*cur_game->blBarrierLevel + ratio*cur_game->blMaxScore;
|
|
cur_game->totalBonusLevelScore += 500*cur_game->blBarrierLevel + ratio*cur_game->blMaxScore;
|
|
//printf("BR: maxScore: %d, ratio: %f, respawn time: %d\n",cur_game->blMaxScore,ratio,cur_game->blActionTime);
|
|
//cur_game->blActionTime *= 0.95;
|
|
cur_game->blMaxScore += 1000;
|
|
delay_set(&cur_game->blDelay,cur_game->blActionTime);
|
|
cur_game->blBarrierMoves = 0;
|
|
cur_game->blNumCompletedRuns++;
|
|
cur_game->blRatioSum += ratio;
|
|
cur_game->blBarrierLevel++;
|
|
bricks_create_barrier(cur_game,cur_game->blBarrierLevel);
|
|
((Ball*)(cur_game->balls->head->next->item))->moving_back = 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return remove;
|
|
}
|
|
/*
|
|
====================================================================
|
|
Make brick at mx,my loose 'points' duration. It must have been
|
|
previously checked that this operation is completely valid.
|
|
It does not update net_bricks or the player's duration reference.
|
|
====================================================================
|
|
*/
|
|
void brick_loose_dur( int mx, int my, int points )
|
|
{
|
|
while ( points-- > 0 ) {
|
|
cur_game->bricks[mx][my].dur--;
|
|
cur_game->bricks[mx][my].id--;
|
|
/* adjust brick character:
|
|
* a,b,c - multiple hits
|
|
* v - invisible */
|
|
if ( cur_game->bricks[mx][my].brick_c == 'v' )
|
|
cur_game->bricks[mx][my].brick_c = 'c';
|
|
else
|
|
cur_game->bricks[mx][my].brick_c--; /* successive order */
|
|
/* set regeneration time if it's a healing brick */
|
|
if ( cur_game->bricks[mx][my].type == MAP_BRICK_HEAL ) {
|
|
/* if this brick is already healing just reset the time
|
|
but don't add to the list again */
|
|
if ( cur_game->bricks[mx][my].heal_time != -1 )
|
|
cur_game->bricks[mx][my].heal_time = BRICK_HEAL_TIME;
|
|
else {
|
|
cur_game->bricks[mx][my].mx = mx;
|
|
cur_game->bricks[mx][my].my = my;
|
|
cur_game->bricks[mx][my].heal_time = BRICK_HEAL_TIME;
|
|
list_add( cur_game->heal_bricks, &cur_game->bricks[mx][my] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* add a modification to the list. if 'mod' is HT_HIT and the
|
|
* tile is empty it is an HT_REMOVE. 'type' is the type of
|
|
* the responsible source and 'src' its impact vector. */
|
|
void bricks_add_mod( int x, int y, int mod, int dest_type, Vector imp, Paddle *paddle )
|
|
{
|
|
BrickHit *hit;
|
|
|
|
if ( cur_game->mod.brick_hit_count > MAX_MODS ) return; /* drop hit */
|
|
|
|
hit = &cur_game->mod.brick_hits[cur_game->mod.brick_hit_count++];
|
|
memset(hit,0,sizeof(BrickHit)); /* clear hit */
|
|
|
|
if (mod == HT_REMOVE_NO_SOUND )
|
|
{
|
|
mod = HT_REMOVE;
|
|
hit->no_sound = 1;
|
|
}
|
|
|
|
if ( mod == HT_REMOVE ) {
|
|
if ( paddle->extra_active[EX_GOLDSHOWER] )
|
|
if ( cur_game->bricks[x][y].extra == EX_NONE )
|
|
hit->gold_shower = 1;
|
|
if (cur_game->bricks[x][y].type==MAP_BRICK_EXP)
|
|
hit->draw_explosion = 1;
|
|
if (dest_type==SHR_BY_DELAYED_EXPL)
|
|
{
|
|
dest_type = SHR_BY_EXPL; /* delayed explosion thus initiated by a close-by
|
|
explosion have no explosion animation */
|
|
}
|
|
else if (dest_type==SHR_BY_NORMAL_BALL && cur_game->extra_active[EX_EXPL_BALL])
|
|
{
|
|
dest_type = SHR_BY_EXPL;
|
|
hit->draw_explosion = 1;
|
|
}
|
|
}
|
|
|
|
hit->x = x; hit->y = y;
|
|
hit->type = mod;
|
|
hit->dest_type = dest_type;
|
|
hit->paddle = (cur_game->paddles[PADDLE_BOTTOM]==paddle)?PADDLE_BOTTOM:PADDLE_TOP;
|
|
|
|
hit->degrees = 0;
|
|
if ( mod == HT_REMOVE && dest_type == SHR_BY_NORMAL_BALL ) {
|
|
hit->degrees = vec2angle( &imp );
|
|
}
|
|
else
|
|
if ( dest_type == SHR_BY_SHOT ) {
|
|
if ( hit->paddle == PADDLE_BOTTOM )
|
|
hit->degrees = 270 / 2;
|
|
else
|
|
hit->degrees = 90 / 2;
|
|
}
|
|
|
|
if (mod==HT_GROW)
|
|
hit->brick_id = RANDOM( BRICK_GROW_FIRST, BRICK_GROW_LAST );
|
|
;
|
|
}
|
|
void bricks_add_grow_mod( int x, int y, int id )
|
|
{
|
|
BrickHit *hit;
|
|
|
|
if ( cur_game->mod.brick_hit_count > MAX_MODS ) return; /* drop hit */
|
|
|
|
hit = &cur_game->mod.brick_hits[cur_game->mod.brick_hit_count++];
|
|
memset(hit,0,sizeof(BrickHit)); /* clear hit */
|
|
|
|
hit->x = x; hit->y = y;
|
|
hit->brick_id = id;
|
|
hit->type = HT_GROW;
|
|
}
|
|
|
|
/* update regeneration and explosion of bricks */
|
|
void bricks_update( int ms )
|
|
{
|
|
int paddleHit;
|
|
Brick *brick;
|
|
Ball *ball;
|
|
|
|
/* check if bricks were destroyed by explosion */
|
|
if ( cur_game->exp_bricks->count > 0 ) {
|
|
list_reset( cur_game->exp_bricks );
|
|
while ( ( brick = list_next( cur_game->exp_bricks ) ) != 0 ) {
|
|
if ( (brick->exp_time -= ms) <= 0 ) {
|
|
brick->exp_time = -1;
|
|
bricks_add_mod( brick->mx, brick->my,
|
|
HT_REMOVE, SHR_BY_DELAYED_EXPL,
|
|
vector_get( 0, 0 ), brick->exp_paddle );
|
|
brick_remove( brick->mx, brick->my, SHR_BY_EXPL,
|
|
vector_get( 0, 0 ), brick->exp_paddle );
|
|
balls_check_targets( brick->mx, brick->my );
|
|
list_delete_current( cur_game->exp_bricks );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check if bricks regenerate */
|
|
if ( cur_game->heal_bricks->count > 0 ) {
|
|
list_reset( cur_game->heal_bricks );
|
|
while ( ( brick = list_next( cur_game->heal_bricks ) ) != 0 ) {
|
|
/* skip brick if destroyed meanwhile */
|
|
if ( brick->type != MAP_BRICK_HEAL ) {
|
|
list_delete_current( cur_game->heal_bricks );
|
|
continue;
|
|
}
|
|
if ( (brick->heal_time -= ms) < 0 ) {
|
|
brick->dur++;
|
|
brick->id++;
|
|
bricks_add_mod( brick->mx, brick->my,
|
|
HT_HEAL, 0, vector_get( 0, 0 ), 0 );
|
|
if ( brick->dur < 3 ) {
|
|
/* initate next healing step */
|
|
brick->heal_time = BRICK_HEAL_TIME;
|
|
}
|
|
else {
|
|
brick->heal_time = -1;
|
|
list_delete_current( cur_game->heal_bricks );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check bonus level stuff */
|
|
if (cur_game->localServerGame)
|
|
switch (cur_game->level_type)
|
|
{
|
|
case LT_HUNTER:
|
|
cur_game->blHunterTimeLeft -= ms;
|
|
if (cur_game->blHunterTimeLeft<0)
|
|
cur_game->bricks_left = 0;
|
|
break;
|
|
case LT_OUTBREAK:
|
|
if (delay_timed_out(&cur_game->blDelay,ms))
|
|
{
|
|
brick_create_instable(cur_game,-1);
|
|
cur_game->blCancerCount++;
|
|
if (cur_game->blCancerCount>cur_game->blCancerLimit)
|
|
cur_game->bricks_left = 0; /* fake level cleared */
|
|
}
|
|
break;
|
|
case LT_BARRIER:
|
|
if (delay_timed_out(&cur_game->blDelay,ms))
|
|
{
|
|
bricks_move_barrier(cur_game,&paddleHit);
|
|
if (paddleHit)
|
|
cur_game->bricks_left = 0; /* fake level cleared */
|
|
}
|
|
break;
|
|
case LT_SITTING_DUCKS:
|
|
if (cur_game->blBallAttached&&delay_timed_out(&cur_game->blDelay,ms))
|
|
{
|
|
ball = list_first(cur_game->balls);
|
|
cur_game->blBallAttached = 0;
|
|
ball_set_random_angle( ball, cur_game->ball_v );
|
|
ball->vel.y *= -1.0;
|
|
ball->vel.x *= 0.2;
|
|
balls_set_velocity( cur_game->balls, cur_game->ball_v );
|
|
balls_check_targets( -1, 0 );
|
|
select_random_duck(cur_game);
|
|
}
|
|
break;
|
|
case LT_DEFENDER:
|
|
if (delay_timed_out(&cur_game->blDelay,ms))
|
|
bricks_add_invader(cur_game,&cur_game->blInvadersWaveOver);
|
|
bricks_move_invaders(cur_game,ms,&paddleHit);
|
|
if (paddleHit)
|
|
cur_game->bricks_left = 0; /* fake level cleared */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* return the character that represents the brick with this type id */
|
|
char brick_get_char( int type )
|
|
{
|
|
int i;
|
|
for ( i = 0; i < BRICK_COUNT; i++ )
|
|
if ( brick_conv_table[i].id == type )
|
|
return brick_conv_table[i].c;
|
|
return ' ';
|
|
}
|