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 >
C/C++ Source or Header  |  2009-11-08  |  16KB  |  440 lines

  1. /*
  2.  * atanks - obliterate each other with oversize weapons
  3.  * Copyright (C) 2003  Thomas Hudson
  4.  *
  5.  * This program is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU General Public License
  7.  * as published by the Free Software Foundation; either version 2
  8.  * of the License, or (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  * */
  19.  
  20. #include "environment.h"
  21. #include "globaldata.h"
  22. #include "explosion.h"
  23. #include "missile.h"
  24. #include "decor.h"
  25. #include "tank.h"
  26. #include "player.h"
  27.  
  28.  
  29. EXPLOSION::~EXPLOSION ()
  30. {
  31.   /* the immediate destruction of the background creates a need for a second dirt slide,
  32.     or there are holes left
  33.       (important for chain missiles to work properly!) */
  34.   for (int z = 0; z <= (radius + 2); z++)
  35.     {
  36.       setSlideColumnDimensions (_global, _env, x + z, true);
  37.       setSlideColumnDimensions (_global, _env, x - z, true);
  38.     }
  39.   _env->removeObject (this);
  40.   weap    = NULL;
  41.   _global = NULL;
  42.   _env    = NULL;
  43. }
  44.  
  45. EXPLOSION::EXPLOSION (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos,
  46.                       int weaponType):PHYSICAL_OBJECT(),weap(NULL)
  47. {
  48.   drag = 0.95;
  49.   mass = 3;
  50.   a = 1;
  51.   peaked = 0;
  52.   exclock = 500;
  53.   bIsWeaponExplosion = true; // Exploding tanks set them to false themselves!
  54.   setEnvironment (env);
  55.   player = NULL;
  56.   _align = LEFT;
  57.   _global = global;
  58.   #ifdef NETWORK
  59.   /*
  60.   char buffer[256];
  61.   sprintf(buffer, "EXPLOSION %d %d %d", (int) xpos, (int) ypos, weaponType);
  62.   global->Send_To_Clients(buffer);
  63.   */
  64.   #endif
  65.   x = xpos;
  66.   y = ypos;
  67.   type = weaponType;
  68.   if (type < WEAPONS)
  69.     weap = &weapon[type];
  70.   else
  71.     weap = &naturals[type - WEAPONS];
  72.   radius = weap->radius;
  73.   etime = weap->etime;
  74.   damage = weap->damage;
  75.   eframes = weap->eframes;
  76.  
  77.   // make sure dirt appears on the screen, not above the playing area
  78.   if ((type >= DIRT_BALL) && (type <= SMALL_DIRT_SPREAD))
  79.     {
  80.       if (y < DIRT_CEILING)
  81.         y = DIRT_CEILING + radius;
  82.     }
  83. }
  84.  
  85. EXPLOSION::EXPLOSION (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos, double xvel, double yvel,
  86.                       int weaponType):PHYSICAL_OBJECT(),weap(NULL)
  87. {
  88.   initialise ();
  89.   setEnvironment (env);
  90.   _align = LEFT;
  91.   _global = global;
  92.   x = xpos;
  93.   y = ypos;
  94.   xv = xvel;
  95.   yv = yvel;
  96.   angle = (int)(atan2(xv, yv) / PI * 180);
  97.   while (angle < 0)
  98.     angle += 360;
  99.   while (angle > 360)
  100.     angle -= 360;
  101.  
  102.   type = weaponType;
  103.   if (type < WEAPONS)
  104.     weap = &weapon[type];
  105.   else
  106.     weap = &naturals[type - WEAPONS];
  107.   radius = weap->radius;
  108.   etime = weap->etime;
  109.   damage = weap->damage;
  110.   eframes = weap->eframes;
  111. }
  112.  
  113. void EXPLOSION::initialise ()
  114. {
  115.   PHYSICAL_OBJECT::initialise ();
  116.   drag = 0.95;
  117.   mass = 3;
  118.   a = 1;
  119.   peaked = 0;
  120.   exclock = 500;
  121.     bIsWeaponExplosion = true; // Exploding tanks set them to false themselves!
  122. }
  123.  
  124. int EXPLOSION::applyPhysics ()
  125. {
  126.   if (type == NAPALM_JELLY)
  127.     {
  128.       if (rand () % 300 == 0)
  129.       {
  130.           DECOR *decor;
  131.           decor = new DECOR (_global, _env, x, y, xv, yv, radius / 2, DECOR_SMOKE);
  132.           if (!decor)
  133.             {
  134.               perror ( "explosion.cc: Failed allocating memory for decor in applyPhysics");
  135.               // exit (1);
  136.             }
  137.         // If the dirt below is falling away, napalm can fall, too:
  138.         if (getpixel(_env->terrain, x, y + 1) == PINK)
  139.         {
  140.            // yv = _env->gravity * (100.0 / FRAMES_PER_SECOND);
  141.            yv = _env->gravity * (100.0 / _global->frames_per_second);
  142.            PHYSICAL_OBJECT::applyPhysics();
  143.         }
  144.         else
  145.           yv = 0.0;
  146.       }
  147.     }
  148.   /*    if (dispersing)
  149.       {
  150.           double accel = (_env->wind - xv) / mass * drag * _env->viscosity;
  151.           xv += accel;
  152.           x += xv;
  153.           y += yv;
  154.       }
  155.   */
  156.   return (hitSomething);
  157. }
  158.  
  159.  
  160. void EXPLOSION::explode ()
  161. {
  162.   int calcblow;
  163.   double distance;
  164.   int z;
  165.   calcblow = 1;
  166.  
  167.     /* to allow synchronous tank explosions, explosions need to know what they are */
  168.     if (player && player->tank && (type < WEAPONS))
  169.         bIsWeaponExplosion = true;
  170.     else
  171.         bIsWeaponExplosion = false;
  172.  
  173.   if (peaked == 1 && a <= EXPLOSIONFRAMES + 1)
  174.     {
  175.       exclock++;
  176.       if (exclock > weap->etime)
  177.         {
  178.           exclock = 0;
  179.           a++;
  180.           requireUpdate ();
  181.         }
  182.       if (a > EXPLOSIONFRAMES + 1)
  183.         {
  184.           destroy = TRUE;
  185.         }
  186.     }
  187.   if (a <= EXPLODEFRAMES + 1)
  188.     {
  189.       TANK *tank;
  190.       if (a == 1)
  191.         {
  192.           for (int index = 0; (tank = (TANK*)_env->getNextOfClass (TANK_CLASS, &index)) && tank; index++)
  193.             {
  194.               // is tank directly above explosion?
  195.               if ((fabs (x - tank->x) < radius) && (y - tank->y >= 0))
  196.                 tank->creditTo = player;
  197.             }
  198.         }
  199.       if ((a == 1) && (type <= TECTONIC || type >= WEAPONS || type == PERCENT_BOMB || type == REDUCER))
  200.         {
  201.           for (int index = 0; (tank = (TANK*)_env->getNextOfClass (TANK_CLASS, &index)) && tank; index++)
  202.             {
  203.               if (type >= SHAPED_CHARGE && type <= CUTTER)
  204.                 {
  205.                   double dXDistance = fabs (x - tank->x);
  206.                   double dYDistance;
  207.                   if (y > tank->y)
  208.                     dYDistance     = fabs (y - tank->y) - (TANKHEIGHT * (3.0 / 4.0)); // To include the bottom of the tank
  209.                   else
  210.                     dYDistance     = fabs (y - tank->y) - (TANKHEIGHT * (3.0 / 8.0)); // The top has to be hit, but not only brushed
  211.  
  212.                   if (dYDistance < 0) dYDistance = 0;
  213.  
  214.                   if ( (dYDistance  <= (radius / 20))
  215.                        && (dXDistance  >= (TANKWIDTH / 2)))
  216.                     distance = ABSDISTANCE(dXDistance, dYDistance, 0, 0);
  217.                   else
  218.                     distance = 2 * radius; // clear outside the explosion!
  219. #ifdef DEBUG
  220.                   if ((dXDistance < (radius + TANKHEIGHT / 2)) && (dYDistance < 25))
  221.                     {
  222.                       cout << endl << "Shape: " << radius << " x " << (radius / 20) << endl;
  223.                       printf( "Tank  X = % 4d, Tank  Y = % 4d\n", (int)tank->x, (int)tank->y);
  224.                       printf( "Explo X = % 4d, Explo Y = % 4d\n", (int)x, (int)y);
  225.                       printf( "Dist  X = % 4d, Dist  Y = % 4d\n", (int)dXDistance, (int)dYDistance);
  226.                       cout << "Distance: " << distance << endl;
  227.                     }
  228. #endif // DEBUG
  229.                 }
  230.               else
  231.                 distance = ABSDISTANCE(x, y, tank->x, tank->y);
  232.  
  233.               if (distance <= (radius + TANKHEIGHT/2) && tank->l > 0)
  234.                 {
  235.                   _global->updateMenu = 1;
  236.  
  237.                   if (type == PERCENT_BOMB)
  238.                     tank->damage = (int) ( (tank->l + tank->sh) / 2) + 1;
  239.                   else if ( type == REDUCER )
  240.                   {
  241.                     if (tank->player->damageMultiplier > 0.1)
  242.                        tank->player->damageMultiplier *= 0.75;
  243.                   }
  244.                   else if (player)
  245.                     tank->damage = (int) ((float) damage * ((float) 1 - ((fabs (distance) / (float)radius) / 2)) * player->damageMultiplier);
  246.                   // player is not used in weather attacks
  247.                   else
  248.                     tank->damage = (int) (float) damage * ((float) 1 - ((fabs (distance) / (float)radius) / 2));
  249.                   tank->creditTo = player;
  250.                   tank->applyDamage ();
  251.                 }
  252.             }
  253.           if (type >= TREMOR && type <= TECTONIC)
  254.             {
  255.               angle = (int)(atan2 (yv, xv) / PI * 180);
  256.               if (angle < 0)
  257.                 angle = angle + 360;
  258.               angle = angle % 360;
  259.             }
  260.         }
  261.       exclock++;
  262.       if (exclock > weap->etime)
  263.         {
  264.           exclock = 0;
  265.           a++;
  266.           requireUpdate ();
  267.         }
  268.       if (a >= EXPLODEFRAMES + 1 && !peaked)
  269.         {
  270.           calcblow = 0;
  271.         }
  272.     }
  273.   if (!calcblow)
  274.     {
  275.       if (type >= SHAPED_CHARGE && type <= CUTTER)
  276.         {
  277.           ellipsefill (_env->terrain, (int)x, (int)y, radius, radius / 20, PINK);
  278.           setUpdateArea ((int)x - (radius + 1), (int)y - (radius / 20 + 1), (radius + 1) * 2, (radius / 20 + 1) * 2);
  279.  
  280.         }
  281.       
  282.       else if (type == DRILLER)
  283.       {
  284.            ellipsefill (_env->terrain, (int) x, (int) y, radius / 20, radius, PINK);
  285.            setUpdateArea( (int) x - (radius + 1), (int) y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
  286.       }
  287.       
  288.       else if (type <= LAST_EXPLOSIVE || type >= WEAPONS || type == PERCENT_BOMB)
  289.         {
  290.  
  291.           if (type != NAPALM_JELLY)
  292.             {
  293.               circlefill (_env->terrain, (int)x, (int)y, radius, PINK);
  294. // too much?  setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
  295. // still too much! setUpdateArea ((int)x - radius, (int)y - radius, radius * 2, radius * 2);
  296.               setUpdateArea ((int)x - radius, (int)y - radius, (radius * 2) - 2, (radius * 2) - 2);
  297.             }
  298.         }
  299.       else  if ((type >= RIOT_BOMB) && (type <= HVY_RIOT_BOMB))
  300.         {
  301.           circlefill (_env->terrain, (int)x, (int)y, radius, PINK);
  302.           setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
  303.         }
  304.       else if ((type >= RIOT_CHARGE) && (type <= RIOT_BLAST))
  305.         {
  306.           double sx = x - _global->slope[angle][0] * 15;
  307.           double sy = y - _global->slope[angle][1] * 15;
  308.           triangle (_env->terrain,
  309.                     (int)sx,
  310.                     (int)sy,
  311.                     (int)(sx + _global->slope[(angle + 45) % 360][0] * radius),
  312.                     (int)(sy + _global->slope[(angle + 45) % 360][1] * radius),
  313.                     (int)(sx + _global->slope[(angle + 315) % 360][0] * radius),
  314.                     (int)(sy + _global->slope[(angle + 315) % 360][1] * radius), PINK);
  315.           setUpdateArea ((int)sx - (radius + 1), (int)sy - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
  316.         }
  317.       else if ((type >= DIRT_BALL) && (type <= SMALL_DIRT_SPREAD))
  318.         {
  319.           BITMAP *tmp;        //for mixing
  320.  
  321.           tmp = create_bitmap(radius * 2, radius * 2);
  322.           clear_to_color(tmp, PINK);
  323.           for (int count = 0; count < radius ; count++)
  324.             {
  325.               circle (tmp, radius, radius, count, (player)?player->color: makecol (0, 255, 0) );
  326.             }
  327.           setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
  328.  
  329.           //copy terrain over explosion
  330.           masked_blit(_env->terrain, tmp, (int)x - radius, (int)y - radius, 0, 0, radius*2, radius*2);
  331.  
  332.           //blit back exploded terrain
  333.           masked_blit(tmp, _env->terrain, 0, 0,(int)x - radius, (int)y - radius, radius*2, radius*2);
  334.  
  335.           destroy_bitmap(tmp);
  336.         }
  337.  
  338.       for (z = 0; z < (_current.w + 2); z++)
  339.         setSlideColumnDimensions (_global, _env, _current.x + z, TRUE);
  340.       calcblow = 1;
  341.       peaked = 1;
  342.       dispersing = 1;
  343.     }
  344. }
  345.  
  346. void EXPLOSION::draw (BITMAP *dest)
  347. {
  348.   if (type >= SHAPED_CHARGE && type <= CUTTER)
  349.     {
  350.       if (a > 1 && a <= EXPLOSIONFRAMES + 1)
  351.         {
  352.           rotate_scaled_sprite (dest, (BITMAP *) _global->gfxData.flameFront[(a + (EXPLOSIONFRAMES * weap->eframes)) - 2],
  353.                                 (int)x - radius, (int)y - (radius / 20), itofix (0), ftofix ((double) radius / 300));
  354.           setUpdateArea ((int)x - (radius + 1), (int)y - (radius / 20 + 1), (radius + 1) * 2, (radius / 20 + 1) * 2);
  355.         }
  356.     }
  357.   else if (type == DRILLER)
  358.   {
  359.      if (a > 1 && a <= EXPLOSIONFRAMES + 1)
  360.      {
  361.         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));
  362.         // 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));
  363.  
  364.         setUpdateArea( (int) x - (radius + 1), (int) y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
  365.      }
  366.   }
  367.   else if (type == NAPALM_JELLY)
  368.     {
  369.       int blobSize = (int)(radius - ((double)a / EXPLOSIONFRAMES) * radius + 1);
  370.       if (blobSize < 2) blobSize = 2;       // avoid circle size crash
  371.       circlefill (_env->db, (int)x, (int)y, blobSize - 2, makecol (255, 0, 0));
  372.       circle(_env->db, (int)x, (int)y, blobSize - 1, makecol(255, 150, 0));
  373.       circle(_env->db, (int)x, (int)y, blobSize, makecol(255, 150, 0));
  374.       setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
  375.     }
  376.   else if (type <= LAST_EXPLOSIVE || type >= WEAPONS || type == PERCENT_BOMB)
  377.     {
  378.       if (a > 1 && a <= EXPLOSIONFRAMES + 1)
  379.         {
  380.           /*  - background needs to be cleard immediately to allow chain missiles to work
  381.                     (and horizontal spreads look *far* better, too! ;) )
  382.                   Note: Shaped charges are always shot alone, and they live off their visual effect.
  383.                               Adding the immediate destruction on them, too, would cut off some of the effect... */
  384.           if    (a <= EXPLODEFRAMES)
  385.             circlefill (_env->terrain, (int)x, (int)y, (int)((radius / EXPLODEFRAMES) * a), PINK);
  386.           rotate_scaled_sprite(dest, (BITMAP *) _global->gfxData.explosions[(a + (EXPLOSIONFRAMES * weap->eframes)) - 2],
  387.                                (int)x - radius, (int)y - radius, itofix (0), ftofix ((double) radius / 107));
  388.           setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
  389.         }
  390.     }
  391.   else if ( (type <= TECTONIC) && (type >= TREMOR) )
  392.     {
  393.       if (a > 1 && a <= EXPLODEFRAMES + 1)
  394.         {
  395.           drawFracture (_global, _env, _env->terrain, &_current, (int)x, (int)y, angle, (int)(((double)a / EXPLODEFRAMES) * (radius / 4)), radius, 5, 0);
  396.         }
  397.     }
  398.   else if ((type >= RIOT_BOMB) && (type <= HVY_RIOT_BOMB))
  399.     {
  400.       if (a > 1 && a <= EXPLODEFRAMES + 1)
  401.         {
  402.           int startCirc = (radius / EXPLODEFRAMES) * a;
  403.           int colour = (player) ? player->color : makecol(0, 0, 255);
  404.           circlefill (_env->terrain, (int)x, (int)y, startCirc, PINK);
  405.           circle (dest, (int)x, (int)y, startCirc, colour);
  406.           setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
  407.         }
  408.     }
  409.   else if ((type >= RIOT_CHARGE) && (type <= RIOT_BLAST))
  410.     {
  411.       if (a > 1 && a <= EXPLODEFRAMES + 1)
  412.         {
  413.           double sx = x - _global->slope[angle][0] * 15;
  414.           double sy = y - _global->slope[angle][1] * 15;
  415.           int startCirc = (radius / EXPLODEFRAMES) * a;
  416.           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);
  417.           setUpdateArea ((int)sx - (startCirc + 1), (int)sy - (startCirc + 1), (startCirc + 1) * 2, (startCirc + 1) * 2);
  418.         }
  419.     }
  420.   else
  421.     {
  422.       if (a > 1 && a <= EXPLODEFRAMES + 1)
  423.         {
  424.           int startCirc = (radius / EXPLODEFRAMES) * a;
  425.           circlefill (dest, (int)x, (int)y, startCirc, (player)?player->color: makecol (0, 255, 0) );
  426.           startCirc += (radius / EXPLODEFRAMES) * 2;
  427.           setUpdateArea ((int)x - startCirc, (int)y - startCirc, startCirc * 2, startCirc * 2);
  428.         }
  429.     }
  430. }
  431.  
  432. int EXPLOSION::isSubClass (int classNum)
  433. {
  434.   if (classNum == EXPLOSION_CLASS)
  435.     return (TRUE);
  436.   else
  437.     return (FALSE);
  438.   //return (PHYSICAL_OBJECT::isSubClass (classNum));
  439. }
  440.