/* $Id$ */ /******************************************************************************/ #include #include "particles.hpp" #include "world.hpp" #include "map.hpp" #include "constants.hpp" #include "world.hpp" #include "global.hpp" #include "serialize.hpp" /******************************************************************************/ Particles::Particles( World& world ) : m_nParticles( 0 ), m_world( world ) { int c; #define GEN_COLOR(r, g, b) (((((r) << 8) | (g)) << 8) | (b)) for( c = 0; c < m_N_SPARK_COLORS; c++ ) { if ( c <= 16 ) m_sparkColor[m_N_SPARK_COLORS-1-c] = GEN_COLOR( 255, 255, 255-15*c ); else if( c <= 33 ) m_sparkColor[m_N_SPARK_COLORS-1-c] = GEN_COLOR( 255, 255-15*(c-17), 0 ); else m_sparkColor[m_N_SPARK_COLORS-1-c] = GEN_COLOR( 255-15*(c-34), 0, 0 ); } #undef GEN_COLOR } /******************************************************************************/ bool Particles::addParticle( const ParticleData& particleData ) { const Map* const map = m_world.getMap(); if( ROUND( particleData.pos.x ) < 0 || ROUND( particleData.pos.x ) >= (signed int) map->getSizeX() || ROUND( particleData.pos.y ) < 0 || ROUND( particleData.pos.y ) >= (signed int) map->getSizeY() ) { INFO( "Particles::addParticle: particle (type: %i, color: %x) out of map " "(%7.2f, %7.2f)\n", particleData.type, particleData.color, particleData.pos.x, particleData.pos.y ); return false; } if( particleData.type & REDUNDANT && m_nParticles >= m_MAX_REDUNDANT_PARTICLES ) { LOG( 4 ) INFO( "Particles::add: cannot add redundant particle, maximum " "redundant number (%i) reached\n", m_MAX_REDUNDANT_PARTICLES ); return false; } if( m_nParticles == m_MAX_PARTICLES ) { LOG( 3 ) CHECK( false, "Particles::add: cannot add particle, maximum " "number (%i) reached\n", m_MAX_PARTICLES ); return false; } m_particleData[m_nParticles++] = particleData; return true; } /******************************************************************************/ void Particles::doTimestep() { int p, pcx = 0, pcy = 0, cx, cy; Vector oldPos; Map* const map = m_world.getMap(); for( p = 0; p < m_nParticles; p++ ) { // 'part' is just a reference to the current particle ParticleData& part = m_particleData[p]; oldPos = part.pos; const real DT = m_world.getDT( oldPos ); real factor = DT; factor *= GRAVITATION; part.vel.y += factor; Vector posAdd = part.vel; posAdd *= DT; part.pos += posAdd; DBG( 5 ) INFO( "Particles::doTimestep: particle (type: %i, color: %x) moves " "from (%.2f, %.2f) to (%.2f, %.2f)\n", part.type, part.color, oldPos.x, oldPos.y, part.pos.x, part.pos.y ); if( (part.type & BOUNCE_OBSTACLE_ANY) && ! map->testLinePassableSmoothing( ROUND( oldPos.x ), ROUND( oldPos.y ), ROUND( part.pos.x ), ROUND( part.pos.y ), pcx, pcy, cx, cy, part.type & BOUNCE_OBSTACLE_ANY )) { DBG( 5 ) INFO( "Particles::doTimestep: particle (type: %i, color: %x) " "collision at (%i, %i)\n", part.type, part.color, cx, cy ); // if particle stains earth if( part.type & STAIN ) { // only stain if point is earth if( map->getFlags()->getValueAt( cx, cy ) & Flags::OBSTACLE_EARTH ) { map->setColorAt( cx, cy, part.color ); } // remove particle removeParticle( &p ); continue; } // if the particle hit something that makes it explode if( ((part.type & EXPL_OBSTACLE_ANY) >> 3) & map->getFlags()->getValueAt( cx, cy )) { // if particle damages if( (part.type & DAMAGE) && (map->getFlags()->getValueAt( cx, cy ) & Flags::OBSTACLE_OBJECT) ) { part.pos.set( cx, cy ); m_world.damageObject( part ); } else { // make hole map->makeHole( cx, cy, 3, Flags::INDESTRUCTIBLE ); if( part.type & EXPL_GEN_SPARKS ) { // create the explosion effect m_world.newObject(EXPLOSION_15)->setPos( Vector( cx, cy )); // create sparks for( int i = 0; i < 5; i++ ) { real length = m_world.getRandom().getNormedReal(); length *= 7; length += 1; addParticle( ParticleData( Vector( pcx, pcy ), m_world.getRandom().getVector( length ), BOUNCE_EARTH | BOUNCE_OBJECT | REDUNDANT | SPARK, 0, 0, m_N_SPARK_COLORS/2 + (m_world.getRandom().getUint32() % (m_N_SPARK_COLORS/2+1)), 0, 0xffffff)); } } } // remove particle removeParticle( &p ); continue; } // do not explode, just bounce off else { real speed = part.vel.abs(); if( part.type & SPECIAL_BOUNCE ) { speed *= 0.01; speed *= static_cast( part.modifier ); } else { speed *= (speed < 1.5 ? 1.0 : 0.6); } part.vel.setAngle( map->calculateBounceAngle( part.vel.x, part.vel.y, cx, cy, part.type & BOUNCE_OBSTACLE_ANY ), speed ); part.pos.set( pcx, pcy ); } } // if TTL relevant (>= 0) and TTL exceeded else if ( part.decaying ) { part.ttl.countDown( DT ); if ( part.ttl.isElapsed() ) { if( (part.type & EXPL_TTL) && (part.type & EXPL_GEN_SPARKS) ) { // create the explosion effect m_world.newObject(EXPLOSION_15)->setPos( part.pos ); for( int i = 0; i < 5; i++ ) { real length = m_world.getRandom().getNormedReal(); length *= 7; length += 1; addParticle( ParticleData( Vector( pcx, pcy ), m_world.getRandom().getVector( length ), BOUNCE_EARTH | BOUNCE_OBJECT | REDUNDANT | SPARK, 0, 0, m_N_SPARK_COLORS/2 + (m_world.getRandom().getUint32() % (m_N_SPARK_COLORS/2+1)), 0, 0xffffff)); } } removeParticle( &p ); continue; } } // particle out of map? DBG( 4 ) { if( ROUND( part.pos.x ) < 0 || ROUND( part.pos.x ) >= (signed int) map->getSizeX() || ROUND( part.pos.y ) < 0 || ROUND( part.pos.y ) >= (signed int) map->getSizeY() ) { CHECK( false, "Particles::doTimestep: particle (type: %i, color: %x, " "oldPos: (%7.2f, %7.2f)) out of map (%7.2f, %7.2f)\n", part.type, part.color, oldPos.x, oldPos.y, part.pos.x, part.pos.y ); } } if( part.type & SPARK ) { /* part.ttl -= part.modifier; if( part.ttl <= 0 ) part.ttl = 1; part.color = m_sparkColor[part.ttl];*/ part.ttl.countDown( DT * part.modifier ); if( part.ttl.isElapsed() ) part.ttl.setTimeToLive( 1.0 ); part.color = m_sparkColor[static_cast( part.ttl.getTimeToLive() )]; } } } /******************************************************************************/ Uint32 Particles::getSerializeBufferSize() const { return Serialize::sizeOf( m_nParticles ) + m_nParticles * m_particleData[0].getSerializeBufferSize(); } /******************************************************************************/ void Particles::serialize( Uint8*& bufferPointer ) const { START_SERIALIZED_SIZE_CHECK( bufferPointer ); Serialize::serialize( m_nParticles, bufferPointer ); for( int p = 0; p < m_nParticles; p++ ) { m_particleData[p].serialize( bufferPointer ); } END_SERIALIZED_SIZE_CHECK( bufferPointer, Particles ); } /******************************************************************************/ void Particles::deserialize( Uint8*& bufferPointer ) { Serialize::deserialize( bufferPointer, m_nParticles ); for( int p = 0; p < m_nParticles; p++ ) { m_particleData[p].deserialize( bufferPointer ); } }