/* $Id$ */ /******************************************************************************/ #ifndef _PARTICLES_HPP_ #define _PARTICLES_HPP_ /******************************************************************************/ #include #include "global.hpp" #include "vector.hpp" #include "flag.hpp" #include "video.hpp" #include "graphics.hpp" #include "serializable.hpp" #include "counter.hpp" /******************************************************************************/ // predeclaration of World, to allow the particle class to // own a pointer to the world, it lives in class World; /******************************************************************************/ /*! This class handles all kinds of particles, e.g. dirt, blood, and shots, which are drawn as simple dots. */ class Particles : public Serializable { public: enum { BOUNCE_EARTH = Flags::OBSTACLE_EARTH, BOUNCE_OBJECT = Flags::OBSTACLE_OBJECT, BOUNCE_PARTICLE = Flags::OBSTACLE_PARTICLE, BOUNCE_OBSTACLE_ANY = BOUNCE_EARTH | BOUNCE_OBJECT | BOUNCE_PARTICLE, EXPL_EARTH = (1 << 3) | BOUNCE_EARTH, EXPL_OBJECT = (1 << 4) | BOUNCE_OBJECT, EXPL_PARTICLE = (1 << 5) | BOUNCE_PARTICLE, EXPL_OBSTACLE_ANY = EXPL_EARTH | EXPL_OBJECT | EXPL_PARTICLE, EXPL_TTL = (1 << 6), DAMAGE = (1 << 7), STAIN = (1 << 8), EXPL_GEN_SPARKS = (1 << 9), INVISIBLE = (1 << 10), REDUNDANT = (1 << 11), SPARK = (1 << 12), SPECIAL_BOUNCE = (1 << 13)}; struct ParticleData : public Serializable { Vector pos, //!< position vel; //!< velocity Uint16 type; //!< type (e.g. dirt, blood, shot, spark) Sint16 modifier; //!< modifies the behavior depending on particle type Uint8 owner; //!< owner ID bool decaying; Counter ttl; //!< time to live /*!< If \a ttl is larger than zero, it will be decremented in each time step. When it reaches zero, the particle is removed. If \a ttl is initialized with a negative value, the time to live is unlimited. */ Sint32 damage; //!< damage caused by particle Uint32 color; //!< color ParticleData( const Vector& posInit = 0, const Vector& velInit = 0, const Uint16 typeInit = 0, const Sint16 modifierInit = 0, const Uint8 ownerInit = 0, const real ttlInit = -1, const Sint32 damageInit = 1, const Uint32 colorInit = 0xffffff ): pos( posInit ), vel( velInit ), type( typeInit ), modifier( modifierInit ), owner( ownerInit ), damage( damageInit ), color( colorInit ) { if ( ttlInit < 0.0 ) { decaying = false; } else { decaying = true; ttl.setTimeToLive( ttlInit ); } } Uint32 getSerializeBufferSize() const { return 2 * Serialize::sizeOf() + Serialize::sizeOf() + Serialize::sizeOf() + Serialize::sizeOf() + Serialize::sizeOf() + ttl.getSerializeBufferSize() + Serialize::sizeOf() + Serialize::sizeOf(); } void serialize( Uint8*& bufferPointer ) const { Serialize::serialize( pos, bufferPointer ); Serialize::serialize( vel, bufferPointer ); Serialize::serialize( type, bufferPointer ); Serialize::serialize( modifier, bufferPointer ); Serialize::serialize( owner, bufferPointer ); Serialize::serialize( decaying, bufferPointer ); ttl.serialize( bufferPointer ); Serialize::serialize( damage, bufferPointer ); Serialize::serialize( color, bufferPointer ); } void deserialize( Uint8*& bufferPointer ) { Serialize::deserialize( bufferPointer, pos ); Serialize::deserialize( bufferPointer, vel ); Serialize::deserialize( bufferPointer, type ); Serialize::deserialize( bufferPointer, modifier ); Serialize::deserialize( bufferPointer, owner ); Serialize::deserialize( bufferPointer, decaying ); ttl.deserialize( bufferPointer ); Serialize::deserialize( bufferPointer, damage ); Serialize::deserialize( bufferPointer, color ); } }; Particles( World& world ); //! Adds a particle to the particle array. If #m_MAX_PARTICLES already //! exist, the particle will not be created. //! \return \arg \c true if the particle could be added. //! \arg \c false if the particle could not be added, because //! #m_MAX_PARTICLES is exceeded. bool addParticle( const ParticleData& particleData ); //! Removes a particle from the particle array. \a index is decremented, //! because it points to a different particle after deleting the old one. void removeParticle( int* index ); //! Moves all particles according to velocity ParticleData::vel by using //! an explicit Euler step and handles collisions. void doTimestep(); //! Draws all particles with a simple dot. void draw( SDL_Surface* surface, const SDL_Rect& rect ) const; void reset( void ) { m_nParticles = 0; } virtual Uint32 getSerializeBufferSize() const; virtual void serialize( Uint8*& bufferPointer ) const; virtual void deserialize( Uint8*& bufferPointer ); static const int m_N_SPARK_COLORS = 52; private: static const int m_MAX_PARTICLES = 40000; //!< Maximum number of particles. static const int m_MAX_REDUNDANT_PARTICLES = int( 0.9*m_MAX_PARTICLES ); Sint32 m_nParticles; //!< Current number of particles. ParticleData m_particleData[m_MAX_PARTICLES]; //!< Array of particles. World& m_world; //!< a reference to the world the particles live in Uint32 m_sparkColor[m_N_SPARK_COLORS]; }; /******************************************************************************/ inline void Particles::removeParticle( int* index ) { DBG( 3 ) { ASSERT( *index < m_nParticles, "Particles::remove: index larger than current number of particles\n" ); } // If index particle is not the last particle in the list if( *index < m_nParticles-1 ) { // Yes, then copy data from last particle onto index particle m_particleData[*index] = m_particleData[m_nParticles-1]; // Decrease index, since we have to work on the same index in the next // loop iteration (*index)--; } // No, then there's no need to copy anything, just decrease m_nParticles m_nParticles--; } /******************************************************************************/ inline void Particles::draw( SDL_Surface* surface, const SDL_Rect& rect ) const { int x, y; if ( SDL_MUSTLOCK( surface ) ) SDL_LockSurface( surface ); for( int p = 0; p < m_nParticles; p++) { if( ! (m_particleData[p].type & INVISIBLE) ) { x = ROUND( m_particleData[p].pos.x ) - rect.x; y = ROUND( m_particleData[p].pos.y ) - rect.y; Graphics::setPixelClip( surface, x, y, m_particleData[p].color ); } } if ( SDL_MUSTLOCK( surface ) ) SDL_UnlockSurface( surface ); } /******************************************************************************/ #endif // _PARTICLES_HPP_