You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bomberclone/src/bomb.c

590 lines
18 KiB

/* $Id: bomb.c,v 1.59 2004/08/30 20:01:32 stpohle Exp $ */
/* everything what have to do with the bombs */
#include "bomberclone.h"
#include "player.h"
#include "bomb.h"
#include <math.h>
void
draw_bomb (_bomb * bomb)
{
SDL_Rect src,
dest;
int x = floorf (bomb->pos.x),
y = floorf (bomb->pos.y);
float w,
x1,
x2,
y1,
y2;
if (x < 0 || y < 0 || x >= map.size.x || y >= map.size.y) {
d_printf ("FATAL: Draw Bomb out of range [%f,%f]\n", x, y);
return;
}
if (bomb->state != BS_trigger || ((bomb->state == BS_trigger) && (bomb->to < bman.bomb_tickingtime))) {
/*
* check the framenumber
*/
bomb->frame += (timefactor / 3.0);
if (bomb->frame < 0 || bomb->frame >= gfx.bomb.frames)
bomb->frame = 0.0f;
}
dest.w = src.w = gfx.bomb.image->w;
dest.h = src.h = gfx.block.y;
if (bomb->mode == BM_kicked) {
/*
* draw the kicked bomb
*/
w = sqrt (bomb->speed);
w *= absol (sin (w));
x1 = bomb->dest.x - bomb->pos.x;
y1 = bomb->dest.y - bomb->pos.y;
if (x1 > 0) {
x2 = bomb->pos.x + x1 * bomb->speed / 88 + y1 * w / 20;
y2 = bomb->pos.y + y1 * bomb->speed / 88 - x1 * w / 20;
}
else {
x2 = bomb->pos.x + x1 * bomb->speed / 88 - y1 * w / 20;
y2 = bomb->pos.y + y1 * bomb->speed / 88 + x1 * w / 20;
}
x = floorf (x2);
y = floorf (y2);
bomb->speed -= timediff * 100;
if (bomb->speed < 0) {
dest.x = gfx.offset.x + bomb->pos.x * gfx.block.x;
dest.y = gfx.offset.y + bomb->pos.y * gfx.block.y;
bomb->mode = BM_normal;
}
else {
w = sqrt (bomb->speed);
w *= absol (sin (w));
if (x1 > 0) {
x2 = bomb->pos.x + x1 * bomb->speed / 88 + y1 * w / 20;
y2 = bomb->pos.y + y1 * bomb->speed / 88 - x1 * w / 20;
}
else {
x2 = bomb->pos.x + x1 * bomb->speed / 88 - y1 * w / 20;
y2 = bomb->pos.y + y1 * bomb->speed / 88 + x1 * w / 20;
}
dest.x = gfx.offset.x + x2 * gfx.block.x;
dest.y = gfx.offset.y + y2 * gfx.block.y;
}
}
else {
dest.x = gfx.offset.x + bomb->pos.x * gfx.block.x;
dest.y = gfx.offset.y + bomb->pos.y * gfx.block.y;
}
src.x = 0;
src.y = src.h * (int) bomb->frame;
stonelist_add (x, y);
if (bomb->mode != BM_normal) {
stonelist_add (x + 1, y);
stonelist_add (x, y + 1);
stonelist_add (x + 1, y + 1);
}
gfx_blit (gfx.bomb.image, &src, gfx.screen, &dest, (y * 256) + 2);
};
/*
* the bomb is going to explode, prepare all values,
* set: ex_nr - explosion number for the bomb, and all resulting explosions
* to - timeout
* firer[d] - range of the bomb.
*/
void
bomb_explode (_bomb *bomb, int net)
{
int d;
d_printf ("Bomb Explode p:%d, b:%d [%f,%f]\n", bomb->id.p, bomb->id.b, bomb->pos.x, bomb->pos.y);
if (bomb->ex_nr == -1)
bomb->ex_nr = bman.last_ex_nr++; // set bomb explosion id
players[bomb->id.p].bomb_lastex = bomb->id.b;
bomb->to = EXPLOSIONTIMEOUT; /* set the timeout for the fireexplosion */
bomb->state = BS_exploding;
explosion_check_field ((int)bomb->pos.x, (int)bomb->pos.y, bomb);
for (d = 0; d < 4; d++) {
bomb->firer[d] = 0.0f;
bomb->firemaxr[d] = 0;
map.field[(int)bomb->pos.x][(int)bomb->pos.y].ex[d].count++;
map.field[(int)bomb->pos.x][(int)bomb->pos.y].ex[d].frame = 0.0f;
map.field[(int)bomb->pos.x][(int)bomb->pos.y].ex_nr = bomb->ex_nr;
map.field[(int)bomb->pos.x][(int)bomb->pos.y].ex[d].bomb_p = bomb->id.p;
map.field[(int)bomb->pos.x][(int)bomb->pos.y].ex[d].bomb_b = bomb->id.b;
stonelist_add ((int)bomb->pos.x, (int)bomb->pos.y);
}
if (GT_MP_PTPM && net) /* from now on only the server let the bomb explode */
net_game_send_bomb (bomb->id.p, bomb->id.b);
snd_play (SND_explode);
};
/* moves the bomb with it's speed,
dest.x|y = dx, dy of the current move */
void
bomb_move (_bomb * bomb)
{
int keepdir = 0;
_pointf fpos,
rpos;
float dist = 0.0f,
step = 0.0f;
map.bfield[(int) bomb->pos.x][(int) bomb->pos.y] = 0; /* delete bfield */
stonelist_add (bomb->pos.x, bomb->pos.y);
/* do this once, and again if the direction is still ok */
do {
/* get the current position of the bomb */
fpos.x = (int) bomb->pos.x;
fpos.y = (int) bomb->pos.y;
rpos.x = CUTINT (bomb->pos.x);
rpos.y = CUTINT (bomb->pos.y);
/* calculate the next step speed or next full field..
depend on what is the smaler one */
if (bomb->dest.x < 0)
step = rpos.x;
else if (bomb->dest.x > 0) {
step = 1.0f - rpos.x;
fpos.x += 1.0f;
}
else if (bomb->dest.y < 0)
step = rpos.y;
else if (bomb->dest.y > 0) {
step = 1.0f - rpos.y;
fpos.y += 1.0f;
}
if (step > (timefactor * bomb->speed) || step == 0.0f)
step = (timefactor * bomb->speed);
/* move the bomb to the new position */
if (bomb->dest.x < 0)
bomb->pos.x -= step;
else if (bomb->dest.x > 0)
bomb->pos.x += step;
else if (bomb->dest.y < 0)
bomb->pos.y -= step;
else if (bomb->dest.y > 0)
bomb->pos.y += step;
/* if we are on a complete field, check if we
can move to the next one */
if ((CUTINT (bomb->pos.x) == 0.0f) && (CUTINT (bomb->pos.y) == 0.0f)) {
if (bomb->mode == BM_pushed)
bomb->mode = BM_normal;
else if (bomb->mode == BM_moving || bomb->mode == BM_liquid) {
/* it is a moving liquid bomb so check for another field */
_point b,
d;
b.x = (int) bomb->pos.x;
b.y = (int) bomb->pos.y;
d.x = b.x + bomb->dest.x;
d.y = b.y + bomb->dest.y;
if (map.bfield[d.x][d.y] == 0
&& (map.field[d.x][d.y].type == FT_nothing
|| map.field[d.x][d.y].type == FT_tunnel))
/* this direction is still oky */
keepdir = 1;
else if (bomb->mode == BM_liquid) {
/* liquid bomb so move to the other side */
keepdir = 0;
bomb->dest.x = -bomb->dest.x;
bomb->dest.y = -bomb->dest.y;
}
else {
/* stop moving this bomb */
keepdir = 0;
bomb->mode = BM_normal;
}
/* if a network game is running send bomb data with the
current information */
if (GT_MP) {
int b = -1,
i = 0;
do {
if (&players[bman.p_nr].bombs[i] == bomb)
b = i;
i++;
} while (b == -1 && i < MAX_BOMBS);
if (b != -1)
net_game_send_bomb (bman.p_nr, b);
}
}
}
dist += step;
} while (dist < (timefactor * bomb->speed)
&& (bomb->mode == BM_liquid || bomb->mode == BM_moving) && keepdir);
map.bfield[(int) bomb->pos.x][(int) bomb->pos.y] = 1; /* set new bfield */
stonelist_add (bomb->pos.x, bomb->pos.y);
}
int
bomb_loop ()
{
int p,
i,
b = 0;
_player *player;
_bomb *bomb;
for (p = 0; p < MAX_PLAYERS; p++) {
player = &players[p];
if ((players[p].state & PSFM_used) != 0) {
for (i = 0; i < MAX_BOMBS; i++) {
bomb = &player->bombs[i];
switch (bomb->state) {
case BS_ticking:
case BS_trigger:
if (GT_MP_PTPM || GT_SP) {
bomb->to -= timediff;
if (bomb->to <= 0.0f) // bomb will have to explode in the next loop
bomb_explode (bomb, 1);
else
draw_bomb (bomb);
}
else {
bomb->to -= timediff;
if (bomb->to <= 0.0f) { // bomb did not explode -> resend bombdata
if (bomb->state == BS_ticking)
bomb->to = bman.bomb_tickingtime;
else
bomb->to = SPECIAL_TRIGGER_TIMEOUT;
net_game_send_bomb (bman.p_nr, i);
bomb->to = bomb->to + 2 * RESENDCACHE_RETRY;
}
draw_bomb (bomb);
}
if (bomb->mode != BM_normal)
bomb_action (bomb);
b++; // Count ticking Bombs for Return value
break;
case BS_exploding:
if (bomb->to > 0.0f) {
explosion_do (bomb);
}
if (bomb->to <= 0.0f) { // explosion done
explosion_restore (bomb);
bomb->to = 0.0f;
bomb->state = BS_off;
}
bomb->to -= timediff;
b++;
break;
}
}
}
}
return b;
};
/* check if on the givin place is a bomb
bombs[].x = player, bombs[].y = bombnumber */
void
get_bomb_on (float x, float y, _point bombs[])
{
int p,
b,
i;
_bomb *bomb;
for (i = 0, p = 0; p < MAX_PLAYERS; p++)
if ((players[p].state & PSFM_used) != 0) {
for (b = 0; b < MAX_BOMBS; b++) {
bomb = &players[p].bombs[b];
if (bomb->state == BS_ticking || bomb->state == BS_trigger) {
if (bomb->pos.x - 1.0f < x && bomb->pos.x + 1.0f > x && bomb->pos.y - 1.0f < y
&& bomb->pos.y + 1.0f > y) {
bombs[i].x = p;
bombs[i].y = b;
i++;
}
}
}
}
bombs[i].x = bombs[i].y = -1;
};
/*
* restore the bombexplosion,
* will be excecuted after a explosion has finished.
* delete all old explosion data from the field.
*/
void explosion_restore (_bomb *bomb) {
int i,
d,
_x,
_y;
_point dir_ch [] ={ { -1, 0 },
{ 1, 0 },
{ 0, -1 },
{ 0, 1 } };
// printf ("342: bomb (%d,%d)\n", (int) bomb->pos.x, (int) bomb->pos.y);
for (d = 0; d < 4; d++) {
_x = bomb->pos.x;
_y = bomb->pos.y;
if (map.field[_x][_y].ex[d].count > 0)
map.field[_x][_y].ex[d].count--;
if (map.field[_x][_y].ex[d].count == 0)
map.field[_x][_y].ex[d].frame = 0; // reset the framenumber
if (d==3) stonelist_add (_x, _y);
/* with every field where was an fire on it decrease the ex[].count value
* and force an drawing of this field */
for (i = 0; i < bomb->firer[d]; i++) { // lower the number of explosions
_x = _x + dir_ch[d].x;
_y = _y + dir_ch[d].y;
if (map.field[_x][_y].ex[d].count > 0)
map.field[_x][_y].ex[d].count--;
if (map.field[_x][_y].ex[d].count == 0)
map.field[_x][_y].ex[d].frame = 0; // reset the framenumber
// printf ("361: d = %d, (%d,%d)\n", d, _x, _y);
stonelist_add (_x, _y);
}
/* delete the stone completly if there was any in the way
* push the values field->type = fiels->special */
if (bomb->firer[d] <= bomb->r && map.field[_x][_y].type != FT_block
&& map.field[_x][_y].type != FT_tunnel && bomb->ex_nr != map.field[_x][_y].ex_nr) {
map.field[_x][_y].ex_nr = bomb->ex_nr;
map.field[_x][_y].frame = 0.0f;
if (map.field[_x][_y].special != FT_nothing) {
map.field[_x][_y].type = map.field[_x][_y].special;
map.field[_x][_y].special = FT_nothing;
}
else
map.field[_x][_y].type = FT_nothing;
d_printf ("field_explode (%d,%d) ex_nr = %d\n", _x, _y, map.field[_x][_y].ex_nr);
// printf ("380: d = %d, (%d,%d)\n", d, _x, _y);
stonelist_add (_x, _y);
if (GT_MP_PTPM) /* send only if we are the master */
net_game_send_field (_x, _y);
}
}
_x = bomb->pos.x;
_y = bomb->pos.y;
/* delete field from the bfield map */
if (bomb->mode == BM_moving || bomb->mode == BM_pushed || bomb->mode == BM_liquid)
map.bfield[(int) bomb->pos.x + bomb->dest.x][(int) bomb->pos.y + bomb->dest.y] = 0;
map.bfield[(int) bomb->pos.x][(int) bomb->pos.y] = 0;
};
/*
* draw the explosion as far as she got
*/
void
explosion_draw (_bomb * bomb)
{
int d,
r,
dx,
dy;
_point p;
for (d = 0; d < 4; d++) {
switch (d) {
case (left):
dx = -1;
dy = 0;
break;
case (right):
dx = 1;
dy = 0;
break;
case (up):
dx = 0;
dy = -1;
break;
default:
dx = 0;
dy = 1;
break;
}
p.x = bomb->pos.x;
p.y = bomb->pos.y;
for (r = 0; r < bomb->firer[d]; r++) {
map.field[p.x][p.y].ex[d].bomb_p = bomb->id.p;
map.field[p.x][p.y].ex[d].bomb_b = bomb->id.b;
map.field[p.x][p.y].ex[d].frame += timefactor;
if (map.field[p.x][p.y].ex[d].frame >= gfx.fire.frames)
map.field[p.x][p.y].ex[d].frame = 0.0f;
stonelist_add (p.x, p.y);
p.x += dx;
p.y += dy;
}
}
}
/*
* calculate the explosion itself,
* check the direction of the explosion and and and
*
* as long as the explosion grows, make sure the explosion will reach it's 'maximum'
* (MAX_RANGE) end at 1.5 seconds. Make sure all explosion will have the same speed.
* range += (MAX_RANGE/1.0) * timediff;
*
*/
void explosion_do (_bomb *bomb) {
_point dir_ch [] ={ { -1, 0 },
{ 1, 0 },
{ 0, -1 },
{ 0, 1 } };
int d, dx, dy, ftype;
float range_grow = (timediff * (float) MAX_RANGE)/EXPLOSION_GROW_SPEED;
float range;
if (bomb->state == BS_exploding) {
for (d = 0; d < 4; d++) {
if (bomb->firemaxr[d] == 0 && bomb->firer[d] < bomb->r) {
range = bomb->firer[d];
bomb->firer[d] += range_grow;
if (bomb->firer[d] > bomb->r) bomb->firer[d] = bomb->r;
dx = bomb->pos.x + (dir_ch[d].x * rintf (range));
dy = bomb->pos.y + (dir_ch[d].y * rintf (range));
/* check all fields between [dx,dy] and [odx,ody] */
while (bomb->firemaxr[d] == 0 && (rintf (range) < rintf (bomb->firer[d]))) {
if (rintf (range) < rintf(bomb->firer[d])) {
range += 1.0f;
dx += dir_ch[d].x;
dy += dir_ch[d].y;
}
ftype = explosion_check_field (dx, dy, bomb);
if (ftype == FT_nothing || ftype == FT_tunnel) {
// printf ("484: count++ d=%d (%d,%d) c_old: %d\n", d, dx, dy, map.field[dx][dy].ex[d].count);
map.field[dx][dy].ex[d].count++;
map.field[dx][dy].ex[d].frame = 0.0f;
map.field[dx][dy].ex_nr = bomb->ex_nr;
map.field[dx][dy].ex[d].bomb_p = bomb->id.p;
map.field[dx][dy].ex[d].bomb_b = bomb->id.b;
stonelist_add (dx, dy);
}
else {
bomb->firemaxr[d] = 1;
bomb->firer[d] = rintf (bomb->firer[d]);
}
}
}
}
explosion_draw (bomb);
}
};
/* check the field if there is another bomb stone or wathever
* if a bomb is found let this one explode, on a player well this player
* will die and if a stone was found, start with the stone explosion
* RETURN: value of the stonetype (FT_*) */
int explosion_check_field (int x, int y, _bomb *bomb)
{
int pl[MAX_PLAYERS];
int i;
_point bo[MAX_PLAYERS * MAX_BOMBS];
_bomb *tmpbomb;
_player *tmpplayer;
if (x < 0 || x >= map.size.x || y < 0 || y >= map.size.y)
return FT_block;
get_player_on (x, y, pl);
get_bomb_on (x, y, bo);
/* check if any bomb have to explode.. */
for (i = 0; bo[i].x != -1; i++) {
tmpbomb = &players[bo[i].x].bombs[bo[i].y];
if (tmpbomb != bomb && tmpbomb->state != BS_exploding) {
tmpbomb->ex_nr = bomb->ex_nr; // set the ex_nr to identify explosions
bomb_explode (tmpbomb, 1);
}
}
// check if any player is in the explosion
for (i = 0; pl[i] != -1; i++) {
tmpplayer = &players[pl[i]];
if (((tmpplayer->state & PSF_alife) != 0)
&& (GT_SP
|| (GT_MP && (&players[bman.p_nr] == tmpplayer || (IS_LPLAYER2 && &players[bman.p2_nr] == tmpplayer)))
|| (GT_MP_PTPM && PS_IS_aiplayer (tmpplayer->state))))
player_died (tmpplayer, bomb->id.p);
}
// let the stones right beside explode
if (map.field[x][y].type != FT_nothing && map.field[x][y].type != FT_tunnel
&& map.field[x][y].type != FT_block && bomb->ex_nr != map.field[x][y].ex_nr)
if (map.field[x][y].frame <= 0.0f) {
map.field[x][y].frame = 1.0f;
stonelist_add (x, y);
}
return map.field[x][y].type;
};
inline void
bomb_action (_bomb * bomb)
{
switch (bomb->mode) {
case (BM_moving):
case (BM_liquid):
case (BM_pushed):
bomb_move (bomb);
break;
case (BM_kicked):
break;
default:
bomb->mode = BM_normal;
break;
}
};