home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2011 November
/
CHIP_2011_11.iso
/
Programy
/
Inne
/
Gry
/
Atomic_Tanks
/
Atomic-Tanks-5.1.exe
/
src
/
explosion.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2009-11-08
|
16KB
|
440 lines
/*
* atanks - obliterate each other with oversize weapons
* Copyright (C) 2003 Thomas Hudson
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* */
#include "environment.h"
#include "globaldata.h"
#include "explosion.h"
#include "missile.h"
#include "decor.h"
#include "tank.h"
#include "player.h"
EXPLOSION::~EXPLOSION ()
{
/* the immediate destruction of the background creates a need for a second dirt slide,
or there are holes left
(important for chain missiles to work properly!) */
for (int z = 0; z <= (radius + 2); z++)
{
setSlideColumnDimensions (_global, _env, x + z, true);
setSlideColumnDimensions (_global, _env, x - z, true);
}
_env->removeObject (this);
weap = NULL;
_global = NULL;
_env = NULL;
}
EXPLOSION::EXPLOSION (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos,
int weaponType):PHYSICAL_OBJECT(),weap(NULL)
{
drag = 0.95;
mass = 3;
a = 1;
peaked = 0;
exclock = 500;
bIsWeaponExplosion = true; // Exploding tanks set them to false themselves!
setEnvironment (env);
player = NULL;
_align = LEFT;
_global = global;
#ifdef NETWORK
/*
char buffer[256];
sprintf(buffer, "EXPLOSION %d %d %d", (int) xpos, (int) ypos, weaponType);
global->Send_To_Clients(buffer);
*/
#endif
x = xpos;
y = ypos;
type = weaponType;
if (type < WEAPONS)
weap = &weapon[type];
else
weap = &naturals[type - WEAPONS];
radius = weap->radius;
etime = weap->etime;
damage = weap->damage;
eframes = weap->eframes;
// make sure dirt appears on the screen, not above the playing area
if ((type >= DIRT_BALL) && (type <= SMALL_DIRT_SPREAD))
{
if (y < DIRT_CEILING)
y = DIRT_CEILING + radius;
}
}
EXPLOSION::EXPLOSION (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos, double xvel, double yvel,
int weaponType):PHYSICAL_OBJECT(),weap(NULL)
{
initialise ();
setEnvironment (env);
_align = LEFT;
_global = global;
x = xpos;
y = ypos;
xv = xvel;
yv = yvel;
angle = (int)(atan2(xv, yv) / PI * 180);
while (angle < 0)
angle += 360;
while (angle > 360)
angle -= 360;
type = weaponType;
if (type < WEAPONS)
weap = &weapon[type];
else
weap = &naturals[type - WEAPONS];
radius = weap->radius;
etime = weap->etime;
damage = weap->damage;
eframes = weap->eframes;
}
void EXPLOSION::initialise ()
{
PHYSICAL_OBJECT::initialise ();
drag = 0.95;
mass = 3;
a = 1;
peaked = 0;
exclock = 500;
bIsWeaponExplosion = true; // Exploding tanks set them to false themselves!
}
int EXPLOSION::applyPhysics ()
{
if (type == NAPALM_JELLY)
{
if (rand () % 300 == 0)
{
DECOR *decor;
decor = new DECOR (_global, _env, x, y, xv, yv, radius / 2, DECOR_SMOKE);
if (!decor)
{
perror ( "explosion.cc: Failed allocating memory for decor in applyPhysics");
// exit (1);
}
// If the dirt below is falling away, napalm can fall, too:
if (getpixel(_env->terrain, x, y + 1) == PINK)
{
// yv = _env->gravity * (100.0 / FRAMES_PER_SECOND);
yv = _env->gravity * (100.0 / _global->frames_per_second);
PHYSICAL_OBJECT::applyPhysics();
}
else
yv = 0.0;
}
}
/* if (dispersing)
{
double accel = (_env->wind - xv) / mass * drag * _env->viscosity;
xv += accel;
x += xv;
y += yv;
}
*/
return (hitSomething);
}
void EXPLOSION::explode ()
{
int calcblow;
double distance;
int z;
calcblow = 1;
/* to allow synchronous tank explosions, explosions need to know what they are */
if (player && player->tank && (type < WEAPONS))
bIsWeaponExplosion = true;
else
bIsWeaponExplosion = false;
if (peaked == 1 && a <= EXPLOSIONFRAMES + 1)
{
exclock++;
if (exclock > weap->etime)
{
exclock = 0;
a++;
requireUpdate ();
}
if (a > EXPLOSIONFRAMES + 1)
{
destroy = TRUE;
}
}
if (a <= EXPLODEFRAMES + 1)
{
TANK *tank;
if (a == 1)
{
for (int index = 0; (tank = (TANK*)_env->getNextOfClass (TANK_CLASS, &index)) && tank; index++)
{
// is tank directly above explosion?
if ((fabs (x - tank->x) < radius) && (y - tank->y >= 0))
tank->creditTo = player;
}
}
if ((a == 1) && (type <= TECTONIC || type >= WEAPONS || type == PERCENT_BOMB || type == REDUCER))
{
for (int index = 0; (tank = (TANK*)_env->getNextOfClass (TANK_CLASS, &index)) && tank; index++)
{
if (type >= SHAPED_CHARGE && type <= CUTTER)
{
double dXDistance = fabs (x - tank->x);
double dYDistance;
if (y > tank->y)
dYDistance = fabs (y - tank->y) - (TANKHEIGHT * (3.0 / 4.0)); // To include the bottom of the tank
else
dYDistance = fabs (y - tank->y) - (TANKHEIGHT * (3.0 / 8.0)); // The top has to be hit, but not only brushed
if (dYDistance < 0) dYDistance = 0;
if ( (dYDistance <= (radius / 20))
&& (dXDistance >= (TANKWIDTH / 2)))
distance = ABSDISTANCE(dXDistance, dYDistance, 0, 0);
else
distance = 2 * radius; // clear outside the explosion!
#ifdef DEBUG
if ((dXDistance < (radius + TANKHEIGHT / 2)) && (dYDistance < 25))
{
cout << endl << "Shape: " << radius << " x " << (radius / 20) << endl;
printf( "Tank X = % 4d, Tank Y = % 4d\n", (int)tank->x, (int)tank->y);
printf( "Explo X = % 4d, Explo Y = % 4d\n", (int)x, (int)y);
printf( "Dist X = % 4d, Dist Y = % 4d\n", (int)dXDistance, (int)dYDistance);
cout << "Distance: " << distance << endl;
}
#endif // DEBUG
}
else
distance = ABSDISTANCE(x, y, tank->x, tank->y);
if (distance <= (radius + TANKHEIGHT/2) && tank->l > 0)
{
_global->updateMenu = 1;
if (type == PERCENT_BOMB)
tank->damage = (int) ( (tank->l + tank->sh) / 2) + 1;
else if ( type == REDUCER )
{
if (tank->player->damageMultiplier > 0.1)
tank->player->damageMultiplier *= 0.75;
}
else if (player)
tank->damage = (int) ((float) damage * ((float) 1 - ((fabs (distance) / (float)radius) / 2)) * player->damageMultiplier);
// player is not used in weather attacks
else
tank->damage = (int) (float) damage * ((float) 1 - ((fabs (distance) / (float)radius) / 2));
tank->creditTo = player;
tank->applyDamage ();
}
}
if (type >= TREMOR && type <= TECTONIC)
{
angle = (int)(atan2 (yv, xv) / PI * 180);
if (angle < 0)
angle = angle + 360;
angle = angle % 360;
}
}
exclock++;
if (exclock > weap->etime)
{
exclock = 0;
a++;
requireUpdate ();
}
if (a >= EXPLODEFRAMES + 1 && !peaked)
{
calcblow = 0;
}
}
if (!calcblow)
{
if (type >= SHAPED_CHARGE && type <= CUTTER)
{
ellipsefill (_env->terrain, (int)x, (int)y, radius, radius / 20, PINK);
setUpdateArea ((int)x - (radius + 1), (int)y - (radius / 20 + 1), (radius + 1) * 2, (radius / 20 + 1) * 2);
}
else if (type == DRILLER)
{
ellipsefill (_env->terrain, (int) x, (int) y, radius / 20, radius, PINK);
setUpdateArea( (int) x - (radius + 1), (int) y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
}
else if (type <= LAST_EXPLOSIVE || type >= WEAPONS || type == PERCENT_BOMB)
{
if (type != NAPALM_JELLY)
{
circlefill (_env->terrain, (int)x, (int)y, radius, PINK);
// too much? setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
// still too much! setUpdateArea ((int)x - radius, (int)y - radius, radius * 2, radius * 2);
setUpdateArea ((int)x - radius, (int)y - radius, (radius * 2) - 2, (radius * 2) - 2);
}
}
else if ((type >= RIOT_BOMB) && (type <= HVY_RIOT_BOMB))
{
circlefill (_env->terrain, (int)x, (int)y, radius, PINK);
setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
}
else if ((type >= RIOT_CHARGE) && (type <= RIOT_BLAST))
{
double sx = x - _global->slope[angle][0] * 15;
double sy = y - _global->slope[angle][1] * 15;
triangle (_env->terrain,
(int)sx,
(int)sy,
(int)(sx + _global->slope[(angle + 45) % 360][0] * radius),
(int)(sy + _global->slope[(angle + 45) % 360][1] * radius),
(int)(sx + _global->slope[(angle + 315) % 360][0] * radius),
(int)(sy + _global->slope[(angle + 315) % 360][1] * radius), PINK);
setUpdateArea ((int)sx - (radius + 1), (int)sy - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
}
else if ((type >= DIRT_BALL) && (type <= SMALL_DIRT_SPREAD))
{
BITMAP *tmp; //for mixing
tmp = create_bitmap(radius * 2, radius * 2);
clear_to_color(tmp, PINK);
for (int count = 0; count < radius ; count++)
{
circle (tmp, radius, radius, count, (player)?player->color: makecol (0, 255, 0) );
}
setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
//copy terrain over explosion
masked_blit(_env->terrain, tmp, (int)x - radius, (int)y - radius, 0, 0, radius*2, radius*2);
//blit back exploded terrain
masked_blit(tmp, _env->terrain, 0, 0,(int)x - radius, (int)y - radius, radius*2, radius*2);
destroy_bitmap(tmp);
}
for (z = 0; z < (_current.w + 2); z++)
setSlideColumnDimensions (_global, _env, _current.x + z, TRUE);
calcblow = 1;
peaked = 1;
dispersing = 1;
}
}
void EXPLOSION::draw (BITMAP *dest)
{
if (type >= SHAPED_CHARGE && type <= CUTTER)
{
if (a > 1 && a <= EXPLOSIONFRAMES + 1)
{
rotate_scaled_sprite (dest, (BITMAP *) _global->gfxData.flameFront[(a + (EXPLOSIONFRAMES * weap->eframes)) - 2],
(int)x - radius, (int)y - (radius / 20), itofix (0), ftofix ((double) radius / 300));
setUpdateArea ((int)x - (radius + 1), (int)y - (radius / 20 + 1), (radius + 1) * 2, (radius / 20 + 1) * 2);
}
}
else if (type == DRILLER)
{
if (a > 1 && a <= EXPLOSIONFRAMES + 1)
{
rotate_scaled_sprite(dest, (BITMAP *) _global->gfxData.flameFront[(a + (EXPLOSIONFRAMES * weap->eframes)) - 2], (int) x - (radius), (int) y - (radius / 20), itofix(192), ftofix(radius / 300));
// rotate_scaled_sprite (dest, (BITMAP *) _global->gfxData.flameFront[(a + (EXPLOSIONFRAMES * weap->eframes)) - 2], (int)x - radius, (int)y - (radius / 20), itofix (0), ftofix ((double) radius / 300));
setUpdateArea( (int) x - (radius + 1), (int) y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
}
}
else if (type == NAPALM_JELLY)
{
int blobSize = (int)(radius - ((double)a / EXPLOSIONFRAMES) * radius + 1);
if (blobSize < 2) blobSize = 2; // avoid circle size crash
circlefill (_env->db, (int)x, (int)y, blobSize - 2, makecol (255, 0, 0));
circle(_env->db, (int)x, (int)y, blobSize - 1, makecol(255, 150, 0));
circle(_env->db, (int)x, (int)y, blobSize, makecol(255, 150, 0));
setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
}
else if (type <= LAST_EXPLOSIVE || type >= WEAPONS || type == PERCENT_BOMB)
{
if (a > 1 && a <= EXPLOSIONFRAMES + 1)
{
/* - background needs to be cleard immediately to allow chain missiles to work
(and horizontal spreads look *far* better, too! ;) )
Note: Shaped charges are always shot alone, and they live off their visual effect.
Adding the immediate destruction on them, too, would cut off some of the effect... */
if (a <= EXPLODEFRAMES)
circlefill (_env->terrain, (int)x, (int)y, (int)((radius / EXPLODEFRAMES) * a), PINK);
rotate_scaled_sprite(dest, (BITMAP *) _global->gfxData.explosions[(a + (EXPLOSIONFRAMES * weap->eframes)) - 2],
(int)x - radius, (int)y - radius, itofix (0), ftofix ((double) radius / 107));
setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
}
}
else if ( (type <= TECTONIC) && (type >= TREMOR) )
{
if (a > 1 && a <= EXPLODEFRAMES + 1)
{
drawFracture (_global, _env, _env->terrain, &_current, (int)x, (int)y, angle, (int)(((double)a / EXPLODEFRAMES) * (radius / 4)), radius, 5, 0);
}
}
else if ((type >= RIOT_BOMB) && (type <= HVY_RIOT_BOMB))
{
if (a > 1 && a <= EXPLODEFRAMES + 1)
{
int startCirc = (radius / EXPLODEFRAMES) * a;
int colour = (player) ? player->color : makecol(0, 0, 255);
circlefill (_env->terrain, (int)x, (int)y, startCirc, PINK);
circle (dest, (int)x, (int)y, startCirc, colour);
setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
}
}
else if ((type >= RIOT_CHARGE) && (type <= RIOT_BLAST))
{
if (a > 1 && a <= EXPLODEFRAMES + 1)
{
double sx = x - _global->slope[angle][0] * 15;
double sy = y - _global->slope[angle][1] * 15;
int startCirc = (radius / EXPLODEFRAMES) * a;
triangle (dest, (int)sx, (int)sy, (int)(sx + _global->slope[(angle + 45) % 360][0] * startCirc), (int)(sy + _global->slope[(angle + 45) % 360][1] * startCirc),(int)(sx + _global->slope[(angle + 315) % 360][0] * startCirc),(int)(sy + _global->slope[(angle + 315) % 360][1] * startCirc), player->color);
setUpdateArea ((int)sx - (startCirc + 1), (int)sy - (startCirc + 1), (startCirc + 1) * 2, (startCirc + 1) * 2);
}
}
else
{
if (a > 1 && a <= EXPLODEFRAMES + 1)
{
int startCirc = (radius / EXPLODEFRAMES) * a;
circlefill (dest, (int)x, (int)y, startCirc, (player)?player->color: makecol (0, 255, 0) );
startCirc += (radius / EXPLODEFRAMES) * 2;
setUpdateArea ((int)x - startCirc, (int)y - startCirc, startCirc * 2, startCirc * 2);
}
}
}
int EXPLOSION::isSubClass (int classNum)
{
if (classNum == EXPLOSION_CLASS)
return (TRUE);
else
return (FALSE);
//return (PHYSICAL_OBJECT::isSubClass (classNum));
}