1 /* $Id$ */
2 
3 /******************************************************************************/
4 
5 #ifndef _PARTICLES_HPP_
6 #define _PARTICLES_HPP_
7 
8 /******************************************************************************/
9 
10 #include <SDL.h>
11 #include "global.hpp"
12 #include "vector.hpp"
13 #include "flag.hpp"
14 #include "video.hpp"
15 #include "graphics.hpp"
16 #include "serializable.hpp"
17 #include "counter.hpp"
18 
19 /******************************************************************************/
20 
21 // predeclaration of World, to allow the particle class to
22 // own a pointer to the world, it lives in
23 class World;
24 
25 /******************************************************************************/
26 
27 /*! This class handles all kinds of particles, e.g. dirt, blood, and shots,
28     which are drawn as simple dots.
29 */
30 
31 class Particles : public Serializable {
32 	public:
33 
34 		enum { BOUNCE_EARTH        = Flags::OBSTACLE_EARTH,
35 		       BOUNCE_OBJECT       = Flags::OBSTACLE_OBJECT,
36 		       BOUNCE_PARTICLE     = Flags::OBSTACLE_PARTICLE,
37 		       BOUNCE_OBSTACLE_ANY = BOUNCE_EARTH | BOUNCE_OBJECT | BOUNCE_PARTICLE,
38 		       EXPL_EARTH          = (1 <<  3) | BOUNCE_EARTH,
39 		       EXPL_OBJECT         = (1 <<  4) | BOUNCE_OBJECT,
40 		       EXPL_PARTICLE       = (1 <<  5) | BOUNCE_PARTICLE,
41 		       EXPL_OBSTACLE_ANY   = EXPL_EARTH | EXPL_OBJECT | EXPL_PARTICLE,
42 		       EXPL_TTL            = (1 <<  6),
43 		       DAMAGE              = (1 <<  7),
44 		       STAIN               = (1 <<  8),
45 		       EXPL_GEN_SPARKS     = (1 <<  9),
46 		       INVISIBLE           = (1 << 10),
47 		       REDUNDANT           = (1 << 11),
48 		       SPARK               = (1 << 12),
49 		       SPECIAL_BOUNCE      = (1 << 13)};
50 
51 		struct ParticleData : public Serializable {
52 			Vector pos,        //!< position
53 			       vel;        //!< velocity
54 			Uint16 type;       //!< type (e.g. dirt, blood, shot, spark)
55 			Sint16 modifier;   //!< modifies the behavior depending on particle type
56 
57 			Uint8 owner;       //!< owner ID
58 
59 			bool decaying;
60 			Counter ttl;       //!< time to live
61 			                   /*!< If \a ttl is larger than zero, it will be
62 			                        decremented in each time step. When it reaches
63 			                        zero, the particle is removed. If \a ttl is
64 			                        initialized with a negative value, the time to
65 			                        live is unlimited. */
66 			Sint32 damage;        //!< damage caused by particle
67 			Uint32 color;      //!< color
ParticleDataParticles::ParticleData68 			ParticleData( const Vector& posInit      = 0,
69 			              const Vector& velInit      = 0,
70 			              const Uint16  typeInit     = 0,
71 			              const Sint16  modifierInit = 0,
72 			              const Uint8   ownerInit    = 0,
73 			              const real    ttlInit      = -1,
74 			              const Sint32  damageInit   = 1,
75 			              const Uint32  colorInit    = 0xffffff ):
76 			                pos( posInit ), vel( velInit ), type( typeInit ),
77 			                modifier( modifierInit ), owner( ownerInit ),
78 			                damage( damageInit ), color( colorInit ) {
79 				if ( ttlInit < 0.0 ) {
80 					decaying = false;
81 				}
82 				else {
83 					decaying = true;
84 					ttl.setTimeToLive( ttlInit );
85 				}
86 			}
getSerializeBufferSizeParticles::ParticleData87 			Uint32 getSerializeBufferSize() const {
88 				return 2 * Serialize<Vector>::sizeOf()
89 				       + Serialize<Uint16>::sizeOf()
90 				       + Serialize<Sint16>::sizeOf()
91 				       + Serialize<Uint8>::sizeOf()
92 				       + Serialize<bool>::sizeOf()
93 				       + ttl.getSerializeBufferSize()
94 				       + Serialize<Sint32>::sizeOf()
95 				       + Serialize<Uint32>::sizeOf();
96 			}
serializeParticles::ParticleData97 			void serialize( Uint8*& bufferPointer ) const {
98 				Serialize<Vector>::serialize( pos, bufferPointer );
99 				Serialize<Vector>::serialize( vel, bufferPointer );
100 				Serialize<Uint16>::serialize( type, bufferPointer );
101 				Serialize<Sint16>::serialize( modifier, bufferPointer );
102 				Serialize<Uint8>::serialize( owner, bufferPointer );
103 				Serialize<bool>::serialize( decaying, bufferPointer );
104 				ttl.serialize( bufferPointer );
105 				Serialize<Sint32>::serialize( damage, bufferPointer );
106 				Serialize<Uint32>::serialize( color, bufferPointer );
107 			}
deserializeParticles::ParticleData108 			void deserialize( Uint8*& bufferPointer ) {
109 				Serialize<Vector>::deserialize( bufferPointer, pos );
110 				Serialize<Vector>::deserialize( bufferPointer, vel );
111 				Serialize<Uint16>::deserialize( bufferPointer, type );
112 				Serialize<Sint16>::deserialize( bufferPointer, modifier );
113 				Serialize<Uint8>::deserialize( bufferPointer, owner );
114 				Serialize<bool>::deserialize( bufferPointer, decaying );
115 				ttl.deserialize( bufferPointer );
116 				Serialize<Sint32>::deserialize( bufferPointer, damage );
117 				Serialize<Uint32>::deserialize( bufferPointer, color );
118 			}
119 		};
120 
121 		Particles( World& world );
122 
123 		//! Adds a particle to the particle array. If #m_MAX_PARTICLES already
124 		//! exist, the particle will not be created.
125 		//! \return \arg \c true if the particle could be added.
126 		//!         \arg \c false if the particle could not be added, because
127 		//!              #m_MAX_PARTICLES is exceeded.
128 		bool addParticle( const ParticleData& particleData );
129 
130 		//! Removes a particle from the particle array. \a index is decremented,
131 		//! because it points to a different particle after deleting the old one.
132 		void removeParticle( int* index );
133 
134 		//! Moves all particles according to velocity ParticleData::vel by using
135 		//! an explicit Euler step and handles collisions.
136 		void doTimestep();
137 
138 		//! Draws all particles with a simple dot.
139 		void draw( SDL_Surface* surface, const SDL_Rect& rect ) const;
140 
reset(void)141 		void reset( void ) { m_nParticles = 0; }
142 
143 		virtual Uint32 getSerializeBufferSize() const;
144 		virtual void serialize( Uint8*& bufferPointer ) const;
145 		virtual void deserialize( Uint8*& bufferPointer );
146 
147 		static const int m_N_SPARK_COLORS = 52;
148 
149 	private:
150 		static const int m_MAX_PARTICLES = 40000; //!< Maximum number of particles.
151 		static const int m_MAX_REDUNDANT_PARTICLES = int( 0.9*m_MAX_PARTICLES );
152 		Sint32 m_nParticles;                      //!< Current number of particles.
153 		ParticleData m_particleData[m_MAX_PARTICLES]; //!< Array of particles.
154 		World& m_world; //!< a reference to the world the particles live in
155 
156 		Uint32 m_sparkColor[m_N_SPARK_COLORS];
157 };
158 
159 /******************************************************************************/
160 
removeParticle(int * index)161 inline void Particles::removeParticle( int* index ) {
162 	DBG( 3 ) {
163 		ASSERT( *index < m_nParticles,
164 	          "Particles::remove: index larger than current number of particles\n" );
165 	}
166 
167 	// If index particle is not the last particle in the list
168 	if( *index < m_nParticles-1 ) {
169 		// Yes, then copy data from last particle onto index particle
170 		m_particleData[*index] = m_particleData[m_nParticles-1];
171 		// Decrease index, since we have to work on the same index in the next
172 		// loop iteration
173 		(*index)--;
174 	}
175 	// No, then there's no need to copy anything, just decrease m_nParticles
176 	m_nParticles--;
177 }
178 
179 /******************************************************************************/
180 
draw(SDL_Surface * surface,const SDL_Rect & rect) const181 inline void Particles::draw( SDL_Surface* surface, const SDL_Rect& rect ) const {
182 	int x, y;
183 
184 	if ( SDL_MUSTLOCK( surface ) )
185 		SDL_LockSurface( surface );
186 
187 	for( int p = 0; p < m_nParticles; p++) {
188 		if( ! (m_particleData[p].type & INVISIBLE) ) {
189 			x = ROUND( m_particleData[p].pos.x ) - rect.x;
190 			y = ROUND( m_particleData[p].pos.y ) - rect.y;
191 			Graphics::setPixelClip( surface, x, y, m_particleData[p].color );
192 		}
193 	}
194 
195 	if ( SDL_MUSTLOCK( surface ) )
196 		SDL_UnlockSurface( surface );
197 }
198 
199 /******************************************************************************/
200 
201 #endif // _PARTICLES_HPP_
202