src/particles/particle.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  *  Wormux is a convivial mass murder game.
00003  *  Copyright (C) 2001-2004 Lawrence Azzoug.
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
00018  ******************************************************************************
00019  * Particle Engine
00020  *****************************************************************************/
00021 
00022 #include "particle.h"
00023 #include <SDL.h>
00024 #include <map>
00025 #include "../game/time.h"
00026 #include "../graphic/sprite.h"
00027 #include "../include/app.h"
00028 #include "../object/objects_list.h"
00029 #include "../tool/resource_manager.h"
00030 #include "../tool/random.h"
00031 #include "../tool/point.h"
00032 #include "../weapon/explosion.h"
00033 #include "../map/map.h"
00034 
00035 #include "body_member.h"
00036 #include "teleport_member.h"
00037 #include "bullet.h"
00038 #include "dark_smoke.h"
00039 #include "explosion_smoke.h"
00040 #include "fire.h"
00041 #include "ground_particles.h"
00042 #include "ill_bubble.h"
00043 #include "magic_star.h"
00044 #include "polecat_fart.h"
00045 #include "smoke.h"
00046 #include "star.h"
00047 
00048 ParticleEngine global_particle_engine;
00049 
00050 Particle::Particle(const std::string &name) :
00051   PhysicalObj(name)
00052 {
00053   SetCollisionModel(true, false, false);
00054   m_initial_time_to_live = 20;
00055   m_check_move_on_end_turn = false;
00056   m_left_time_to_live = 0;
00057   m_last_refresh = Time::GetInstance()->Read();
00058 }
00059 
00060 Particle::~Particle()
00061 {
00062   delete image;
00063 }
00064 
00065 void Particle::Draw()
00066 {
00067   if (m_left_time_to_live > 0)
00068     image->Draw(GetPosition());
00069 }
00070 
00071 void Particle::Refresh()
00072 {
00073   uint time = Time::GetInstance()->Read() - m_last_refresh;
00074 
00075   UpdatePosition();
00076 
00077   image->Update();
00078 
00079   if (time >= m_time_between_scale) {
00080 
00081     //assert(m_left_time_to_live > 0);
00082     if (m_left_time_to_live <= 0) return ;
00083 
00084     m_left_time_to_live--;
00085 
00086     float lived_time = m_initial_time_to_live - m_left_time_to_live;
00087 
00088     //during the 1st quarter of the time increase size of particle
00089     //after the 1st quarter, decrease the alpha value
00090     if((float)lived_time<m_initial_time_to_live/2.0)
00091     {
00092       float coeff = sin((M_PI/2.0)*((float)lived_time/((float)m_initial_time_to_live/2.0)));
00093       image->Scale(coeff,coeff);
00094       SetSize(image->GetSize());
00095       image->SetAlpha(1.0);
00096     }
00097     else
00098     {
00099       float alpha = 1.0 - sin((M_PI/2.0)*((float)lived_time-((float)m_initial_time_to_live/2.0))/((float)m_initial_time_to_live/2.0));
00100       image->Scale(1.0,1.0);
00101       image->SetAlpha(alpha);
00102     }
00103     m_last_refresh = Time::GetInstance()->Read() ;
00104   }
00105 }
00106 
00107 bool Particle::StillUseful()
00108 {
00109   return (m_left_time_to_live > 0);
00110 }
00111 
00112 // ==============================================
00113 
00114 ParticleEngine::ParticleEngine(uint time)
00115 {
00116   m_time_between_add = time ;
00117   m_last_refresh = Time::GetInstance()->Read();
00118 }
00119 
00120 
00121 void ParticleEngine::AddPeriodic(const Point2i &position, particle_t type,
00122                                  bool upper,
00123                                  double angle, double norme)
00124 {
00125   // time spent since last refresh (in milliseconds)
00126   uint time = Time::GetInstance()->Read() - m_last_refresh;
00127   uint tmp = Time::GetInstance()->Read();
00128 
00129   uint delta = uint(m_time_between_add * double(randomObj.GetLong(3,40))/10);
00130   if (time >= delta) {
00131     m_last_refresh = tmp;
00132     ParticleEngine::AddNow(position, 1, type, upper, angle, norme);
00133   }
00134 }
00135 
00136 //-----------------------------------------------------------------------------
00137 // Static methods
00138 
00139 std::list<Particle*> ParticleEngine::lst_particles;
00140 Sprite* ParticleEngine::particle_sprite[particle_spr_nbr];
00141 
00142 void ParticleEngine::Init()
00143 {
00144   // Pre-load the sprite of each particle
00145   Profile *res = resource_manager.LoadXMLProfile( "weapons.xml", false);
00146   particle_sprite[SMOKE_spr] = resource_manager.LoadSprite(res,"smoke");
00147   particle_sprite[EXPLOSION_SMOKE_spr] = resource_manager.LoadSprite(res,"smoke_explosion");
00148   particle_sprite[ILL_BUBBLE_spr] = resource_manager.LoadSprite(res,"ill_bubble");
00149   particle_sprite[FIRE_spr]  = resource_manager.LoadSprite(res,"fire_particle");
00150   particle_sprite[FIRE_spr]->EnableRotationCache(32);
00151   particle_sprite[STAR_spr]  = resource_manager.LoadSprite(res,"star_particle");
00152   particle_sprite[DARK_SMOKE_spr]  = resource_manager.LoadSprite(res,"dark_smoke");
00153   particle_sprite[MAGIC_STAR_R_spr] = resource_manager.LoadSprite(res,"pink_star_particle");
00154   particle_sprite[MAGIC_STAR_R_spr]->EnableRotationCache(32);
00155   particle_sprite[MAGIC_STAR_Y_spr] = resource_manager.LoadSprite(res,"yellow_star_particle");
00156   particle_sprite[MAGIC_STAR_Y_spr]->EnableRotationCache(32);
00157   particle_sprite[MAGIC_STAR_B_spr] = resource_manager.LoadSprite(res,"blue_star_particle");
00158   particle_sprite[MAGIC_STAR_B_spr]->EnableRotationCache(32);
00159   particle_sprite[BULLET_spr] = resource_manager.LoadSprite(res,"bullet_particle");
00160   particle_sprite[BULLET_spr]->EnableRotationCache(6);
00161   particle_sprite[POLECAT_FART_spr] = resource_manager.LoadSprite(res,"polecat_fart");
00162   particle_sprite[POLECAT_FART_spr]->EnableRotationCache(6);
00163   resource_manager.UnLoadXMLProfile(res);
00164 }
00165 
00166 void ParticleEngine::FreeMem()
00167 {
00168   for(int i=0; i<particle_spr_nbr ; i++)
00169     delete global_particle_engine.particle_sprite[i];
00170 }
00171 
00172 Sprite* ParticleEngine::GetSprite(particle_spr type)
00173 {
00174   assert(type < particle_spr_nbr);
00175   return new Sprite(*(global_particle_engine.particle_sprite[type]));
00176 }
00177 
00178 void ParticleEngine::AddNow(const Point2i &position,
00179                             uint nb_particles, particle_t type,
00180                             bool upper,
00181                             double angle, double norme)
00182 {
00183   Particle *particle = NULL;
00184   double tmp_angle, tmp_norme;
00185 
00186   for (uint i=0 ; i < nb_particles ; i++) {
00187     switch (type) {
00188       case particle_SMOKE : particle = new Smoke();
00189                             break;
00190       case particle_ILL_BUBBLE : particle = new IllBubble();
00191                             break;
00192       case particle_DARK_SMOKE : particle = new DarkSmoke();
00193                             break;
00194       case particle_FIRE : particle = new FireParticle();
00195                            break;
00196       case particle_STAR : particle = new StarParticle();
00197                            break;
00198       case particle_BULLET : particle = new BulletParticle();
00199                            break;
00200       case particle_POLECAT_FART : particle = new PolecatFart();
00201                            break;
00202       case particle_GROUND : particle = new GroundParticle(Point2i(10,10), position);
00203                            break;
00204       case particle_AIR_HAMMER : particle = new GroundParticle(Point2i(21,18), position); 
00205         // Half the size of the airhammer impact
00206         // Dirty, but we have no way to read the
00207         // impact size from here ...
00208                            break;
00209       case particle_MAGIC_STAR : particle = new MagicStarParticle();
00210                                  break;
00211       default : particle = NULL;
00212                 assert(0);
00213                 break;
00214     }
00215 
00216     if (particle != NULL) {
00217       if( norme == -1 )
00218                   tmp_norme = double(randomObj.GetLong(0, 5000))/100;
00219       else
00220                   tmp_norme = norme;
00221 
00222       if( angle == -1 )
00223                   tmp_angle = - double(randomObj.GetLong(0, 3000))/1000;
00224       else
00225                   tmp_angle = angle;
00226 
00227       particle->SetXY(position);
00228       particle->SetOnTop(upper);
00229       particle->SetSpeed(tmp_norme, tmp_angle);
00230       lst_particles.push_back(particle);
00231     }
00232   }
00233 }
00234 
00235 void ParticleEngine::AddNow(Particle* particle)
00236 {
00237   lst_particles.push_back(particle);
00238 }
00239 
00240 void ParticleEngine::AddBigESmoke(const Point2i &position, const uint &radius)
00241 {
00242   //Add many little smoke particles
00243   // Sin / cos  precomputed value, to avoid recomputing them and speed up.
00244   // see the commented value of 'angle' to see how it was generated
00245   const uint little_partic_nbr = 10;
00246   const float little_cos[] = { 1.000000, 0.809017, 0.309017, -0.309017, -0.809017, -1.000000, -0.809017, -0.309017, 0.309017, 0.809017 };
00247   const float little_sin[] = { 0.000000, 0.587785, 0.951057, 0.951056, 0.587785, -0.000000, -0.587785, -0.951056, -0.951056, -0.587785 };
00248 
00249   Particle *particle = NULL;
00250   float norme;
00251   uint size;
00252 
00253   for(uint i=0; i < little_partic_nbr ; i++)
00254   {
00255 //      angle = (float) i * M_PI * 2.0 / (float) little_partic_nbr;
00256       size = uint(radius / 1.5);
00257       norme = 2.5 * radius / 3.0;
00258 
00259       particle = new ExplosionSmoke(size);
00260       particle->SetOnTop(true);
00261 
00262       Point2i pos = position; //Set position to center of explosion
00263       pos = pos - size / 2;       //Set the center of the smoke to the center..
00264       pos = pos + Point2i(int(norme * little_cos[i]),int(norme * little_sin[i])); //Put inside the circle of the explosion
00265 
00266       particle->SetXY(pos);
00267       lst_particles.push_back(particle);
00268   }
00269 }
00270 
00271 void ParticleEngine::AddLittleESmoke(const Point2i &position, const uint &radius)
00272 {
00273   //Add a few big smoke particles
00274   const uint big_partic_nbr = 5;
00275   // Sin / cos  precomputed value, to avoid recomputing them and speed up.
00276   // see the commented value of 'angle' to see how it was generated
00277   const float big_cos[] = { 1.000000, -0.809017, 0.309017, 0.309017, -0.809017 };
00278   const float big_sin[] = { 0.000000, 0.587785, -0.951056, 0.951057, -0.587785 };
00279 
00280   Particle *particle = NULL;
00281   float norme;
00282   uint size;
00283   for(uint i=0; i < big_partic_nbr ; i++)
00284   {
00285 //      angle = (float) i * M_PI * 4.0 / (float)big_partic_nbr;
00286       size = radius;
00287       norme = radius / 3.0;
00288 
00289       Point2i pos = position; //Set position to center of explosion
00290       pos = pos - size / 2;       //Set the center of the smoke to the center..
00291       pos = pos + Point2i(int(norme * big_cos[i]),int(norme * big_sin[i])); //Put inside the circle of the explosion
00292 
00293       particle = new ExplosionSmoke(size);
00294       particle->SetXY(pos);
00295       particle->SetOnTop(true);
00296 
00297       lst_particles.push_back(particle);
00298   }
00299 }
00300 
00301 void ParticleEngine::AddExplosionSmoke(const Point2i &position, const uint &radius, ESmokeStyle &style)
00302 {
00303   if(style == NoESmoke) return;
00304   AddLittleESmoke (position, radius);
00305   if(style == BigESmoke) AddBigESmoke (position, radius);
00306 }
00307 
00308 void ParticleEngine::Draw(bool upper)
00309 {
00310   std::list<Particle *>::iterator Particle_it;
00311   // draw the particles
00312   for (Particle_it=lst_particles.begin(); Particle_it!=lst_particles.end(); ++Particle_it){
00313     if ( (*Particle_it)->IsOnTop() == upper) {
00314       (*Particle_it)->Draw();
00315     }
00316   }
00317 
00318 }
00319 
00320 void ParticleEngine::Refresh()
00321 {
00322   // remove old particles
00323   std::list<Particle*>::iterator it=lst_particles.begin(), end=lst_particles.end();
00324   while (it != end) {
00325     if (! (*it)->StillUseful()) {
00326       delete *it;
00327       it = lst_particles.erase(it);
00328     }
00329     else
00330       it++;
00331   }
00332 
00333   // update the particles
00334   for(it=lst_particles.begin(); it!=lst_particles.end(); ++it) {
00335     (*it)->Refresh();
00336   }
00337 }
00338 
00339 void ParticleEngine::Stop()
00340 {
00341   // remove all the particles
00342   std::list<Particle*>::iterator it=lst_particles.begin(), end=lst_particles.end();
00343   while (it != end) {
00344     delete *it;
00345     it = lst_particles.erase(it);
00346   }
00347 }
00348 
00349 PhysicalObj * ParticleEngine::IsSomethingMoving()
00350 {
00351   std::list<Particle *>::iterator Particle_it;
00352   // check if particle need to be check in end of turn
00353   for (Particle_it=lst_particles.begin(); Particle_it!=lst_particles.end(); ++Particle_it)
00354     if ((*Particle_it)->CheckOnEndTurn() && (*Particle_it)->StillUseful())
00355       return *Particle_it;
00356   return NULL;
00357 }

Generated on Mon Jan 1 13:10:59 2007 for Wormux by  doxygen 1.4.7