]> git.lizzy.rs Git - minetest.git/blobdiff - src/particles.cpp
Remove `mathconstants.h` and use the correct way to get `M_PI` in MSVC. (#5072)
[minetest.git] / src / particles.cpp
index 19bdaf1215f7c5258f168f39ecc514ea1954fedd..5f17763e013373cf2ef4423facbb1122635496fe 100644 (file)
@@ -18,12 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "particles.h"
 */
 
 #include "particles.h"
-#include "constants.h"
-#include "debug.h"
-#include "main.h" // For g_profiler and g_settings
-#include "settings.h"
-#include "tile.h"
-#include "gamedef.h"
+#include "client.h"
 #include "collision.h"
 #include <stdlib.h>
 #include "util/numeric.h"
 #include "collision.h"
 #include <stdlib.h>
 #include "util/numeric.h"
@@ -31,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "environment.h"
 #include "clientmap.h"
 #include "mapnode.h"
 #include "environment.h"
 #include "clientmap.h"
 #include "mapnode.h"
+#include "client.h"
 
 /*
        Utility
 
 /*
        Utility
@@ -43,29 +39,30 @@ v3f random_v3f(v3f min, v3f max)
                        rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z);
 }
 
                        rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z);
 }
 
-std::vector<Particle*> all_particles;
-std::map<u32, ParticleSpawner*> all_particlespawners;
-
 Particle::Particle(
        IGameDef *gamedef,
        scene::ISceneManager* smgr,
        LocalPlayer *player,
 Particle::Particle(
        IGameDef *gamedef,
        scene::ISceneManager* smgr,
        LocalPlayer *player,
-       ClientEnvironment &env,
+       ClientEnvironment *env,
        v3f pos,
        v3f velocity,
        v3f acceleration,
        float expirationtime,
        float size,
        bool collisiondetection,
        v3f pos,
        v3f velocity,
        v3f acceleration,
        float expirationtime,
        float size,
        bool collisiondetection,
+       bool collision_removal,
        bool vertical,
        video::ITexture *texture,
        v2f texpos,
        bool vertical,
        video::ITexture *texture,
        v2f texpos,
-       v2f texsize
+       v2f texsize,
+       const struct TileAnimationParams &anim,
+       u8 glow
 ):
        scene::ISceneNode(smgr->getRootSceneNode(), smgr)
 {
        // Misc
        m_gamedef = gamedef;
 ):
        scene::ISceneNode(smgr->getRootSceneNode(), smgr)
 {
        // Misc
        m_gamedef = gamedef;
+       m_env = env;
 
        // Texture
        m_material.setFlag(video::EMF_LIGHTING, false);
 
        // Texture
        m_material.setFlag(video::EMF_LIGHTING, false);
@@ -76,7 +73,9 @@ Particle::Particle(
        m_material.setTexture(0, texture);
        m_texpos = texpos;
        m_texsize = texsize;
        m_material.setTexture(0, texture);
        m_texpos = texpos;
        m_texsize = texsize;
-
+       m_animation = anim;
+       m_animation_frame = 0;
+       m_animation_time = 0.0;
 
        // Particle related
        m_pos = pos;
 
        // Particle related
        m_pos = pos;
@@ -87,20 +86,20 @@ Particle::Particle(
        m_player = player;
        m_size = size;
        m_collisiondetection = collisiondetection;
        m_player = player;
        m_size = size;
        m_collisiondetection = collisiondetection;
+       m_collision_removal = collision_removal;
        m_vertical = vertical;
        m_vertical = vertical;
+       m_glow = glow;
 
        // Irrlicht stuff
 
        // Irrlicht stuff
-       m_collisionbox = core::aabbox3d<f32>
+       m_collisionbox = aabb3f
                        (-size/2,-size/2,-size/2,size/2,size/2,size/2);
        this->setAutomaticCulling(scene::EAC_OFF);
 
        // Init lighting
                        (-size/2,-size/2,-size/2,size/2,size/2,size/2);
        this->setAutomaticCulling(scene::EAC_OFF);
 
        // Init lighting
-       updateLight(env);
+       updateLight();
 
        // Init model
        updateVertices();
 
        // Init model
        updateVertices();
-
-       all_particles.push_back(this);
 }
 
 Particle::~Particle()
 }
 
 Particle::~Particle()
@@ -110,20 +109,13 @@ Particle::~Particle()
 void Particle::OnRegisterSceneNode()
 {
        if (IsVisible)
 void Particle::OnRegisterSceneNode()
 {
        if (IsVisible)
-       {
-               SceneManager->registerNodeForRendering
-                               (this, scene::ESNRP_TRANSPARENT);
-               SceneManager->registerNodeForRendering
-                               (this, scene::ESNRP_SOLID);
-       }
+               SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
 
        ISceneNode::OnRegisterSceneNode();
 }
 
 void Particle::render()
 {
 
        ISceneNode::OnRegisterSceneNode();
 }
 
 void Particle::render()
 {
-       // TODO: Render particles in front of water and the selectionbox
-
        video::IVideoDriver* driver = SceneManager->getVideoDriver();
        driver->setMaterial(m_material);
        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
        video::IVideoDriver* driver = SceneManager->getVideoDriver();
        driver->setMaterial(m_material);
        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
@@ -134,61 +126,89 @@ void Particle::render()
                        scene::EPT_TRIANGLES, video::EIT_16BIT);
 }
 
                        scene::EPT_TRIANGLES, video::EIT_16BIT);
 }
 
-void Particle::step(float dtime, ClientEnvironment &env)
+void Particle::step(float dtime)
 {
        m_time += dtime;
 {
        m_time += dtime;
-       if (m_collisiondetection)
-       {
-               core::aabbox3d<f32> box = m_collisionbox;
-               v3f p_pos = m_pos*BS;
-               v3f p_velocity = m_velocity*BS;
-               v3f p_acceleration = m_acceleration*BS;
-               collisionMoveSimple(&env, m_gamedef,
-                       BS*0.5, box,
-                       0, dtime,
-                       p_pos, p_velocity, p_acceleration);
-               m_pos = p_pos/BS;
-               m_velocity = p_velocity/BS;
-               m_acceleration = p_acceleration/BS;
-       }
-       else
-       {
+       if (m_collisiondetection) {
+               aabb3f box = m_collisionbox;
+               v3f p_pos = m_pos * BS;
+               v3f p_velocity = m_velocity * BS;
+               collisionMoveResult r = collisionMoveSimple(m_env,
+                       m_gamedef, BS * 0.5, box, 0, dtime, &p_pos,
+                       &p_velocity, m_acceleration * BS);
+               if (m_collision_removal && r.collides) {
+                       // force expiration of the particle
+                       m_expiration = -1.0;
+               } else {
+                       m_pos = p_pos / BS;
+                       m_velocity = p_velocity / BS;
+               }
+       } else {
                m_velocity += m_acceleration * dtime;
                m_pos += m_velocity * dtime;
        }
                m_velocity += m_acceleration * dtime;
                m_pos += m_velocity * dtime;
        }
+       if (m_animation.type != TAT_NONE) {
+               m_animation_time += dtime;
+               int frame_length_i, frame_count;
+               m_animation.determineParams(
+                               m_material.getTexture(0)->getSize(),
+                               &frame_count, &frame_length_i, NULL);
+               float frame_length = frame_length_i / 1000.0;
+               while (m_animation_time > frame_length) {
+                       m_animation_frame++;
+                       m_animation_time -= frame_length;
+               }
+       }
 
        // Update lighting
 
        // Update lighting
-       updateLight(env);
+       updateLight();
 
        // Update model
        updateVertices();
 }
 
 
        // Update model
        updateVertices();
 }
 
-void Particle::updateLight(ClientEnvironment &env)
+void Particle::updateLight()
 {
        u8 light = 0;
 {
        u8 light = 0;
-       try{
-               v3s16 p = v3s16(
-                       floor(m_pos.X+0.5),
-                       floor(m_pos.Y+0.5),
-                       floor(m_pos.Z+0.5)
-               );
-               MapNode n = env.getClientMap().getNode(p);
-               light = n.getLightBlend(env.getDayNightRatio(), m_gamedef->ndef());
-       }
-       catch(InvalidPositionException &e){
-               light = blend_light(env.getDayNightRatio(), LIGHT_SUN, 0);
-       }
-       m_light = decode_light(light);
+       bool pos_ok;
+
+       v3s16 p = v3s16(
+               floor(m_pos.X+0.5),
+               floor(m_pos.Y+0.5),
+               floor(m_pos.Z+0.5)
+       );
+       MapNode n = m_env->getClientMap().getNodeNoEx(p, &pos_ok);
+       if (pos_ok)
+               light = n.getLightBlend(m_env->getDayNightRatio(), m_gamedef->ndef());
+       else
+               light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
+
+       m_light = decode_light(light + m_glow);
 }
 
 void Particle::updateVertices()
 {
        video::SColor c(255, m_light, m_light, m_light);
 }
 
 void Particle::updateVertices()
 {
        video::SColor c(255, m_light, m_light, m_light);
-       f32 tx0 = m_texpos.X;
-       f32 tx1 = m_texpos.X + m_texsize.X;
-       f32 ty0 = m_texpos.Y;
-       f32 ty1 = m_texpos.Y + m_texsize.Y;
+       f32 tx0, tx1, ty0, ty1;
+
+       if (m_animation.type != TAT_NONE) {
+               const v2u32 texsize = m_material.getTexture(0)->getSize();
+               v2f texcoord, framesize_f;
+               v2u32 framesize;
+               texcoord = m_animation.getTextureCoords(texsize, m_animation_frame);
+               m_animation.determineParams(texsize, NULL, NULL, &framesize);
+               framesize_f = v2f(framesize.X / (float) texsize.X, framesize.Y / (float) texsize.Y);
+
+               tx0 = m_texpos.X + texcoord.X;
+               tx1 = m_texpos.X + texcoord.X + framesize_f.X * m_texsize.X;
+               ty0 = m_texpos.Y + texcoord.Y;
+               ty1 = m_texpos.Y + texcoord.Y + framesize_f.Y * m_texsize.Y;
+       } else {
+               tx0 = m_texpos.X;
+               tx1 = m_texpos.X + m_texsize.X;
+               ty0 = m_texpos.Y;
+               ty1 = m_texpos.Y + m_texsize.Y;
+       }
 
        m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
                        c, tx0, ty1);
 
        m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
                        c, tx0, ty1);
@@ -199,6 +219,7 @@ void Particle::updateVertices()
        m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
                        c, tx0, ty0);
 
        m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
                        c, tx0, ty0);
 
+       v3s16 camera_offset = m_env->getCameraOffset();
        for(u16 i=0; i<4; i++)
        {
                if (m_vertical) {
        for(u16 i=0; i<4; i++)
        {
                if (m_vertical) {
@@ -209,103 +230,10 @@ void Particle::updateVertices()
                        m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
                }
                m_box.addInternalPoint(m_vertices[i].Pos);
                        m_vertices[i].Pos.rotateXZBy(m_player->getYaw());
                }
                m_box.addInternalPoint(m_vertices[i].Pos);
-               m_vertices[i].Pos += m_pos*BS;
-       }
-}
-
-
-/*
-       Helpers
-*/
-
-
-void allparticles_step (float dtime, ClientEnvironment &env)
-{
-       for(std::vector<Particle*>::iterator i = all_particles.begin();
-                       i != all_particles.end();)
-       {
-               if ((*i)->get_expired())
-               {
-                       (*i)->remove();
-                       delete *i;
-                       i = all_particles.erase(i);
-               }
-               else
-               {
-                       (*i)->step(dtime, env);
-                       i++;
-               }
-       }
-}
-
-void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
-               LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
-               const TileSpec tiles[])
-{
-       for (u16 j = 0; j < 32; j++) // set the amount of particles here
-       {
-               addNodeParticle(gamedef, smgr, player, env, pos, tiles);
+               m_vertices[i].Pos += m_pos*BS - intToFloat(camera_offset, BS);
        }
 }
 
        }
 }
 
-void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
-               LocalPlayer *player, ClientEnvironment &env,
-               v3s16 pos, const TileSpec tiles[])
-{
-       addNodeParticle(gamedef, smgr, player, env, pos, tiles);
-}
-
-// add a particle of a node
-// used by digging and punching particles
-void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
-               LocalPlayer *player, ClientEnvironment &env, v3s16 pos,
-               const TileSpec tiles[])
-{
-       // Texture
-       u8 texid = myrand_range(0,5);
-       video::ITexture *texture = tiles[texid].texture;
-
-       // Only use first frame of animated texture
-       f32 ymax = 1;
-       if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
-               ymax /= tiles[texid].animation_frame_count;
-
-       float size = rand()%64/512.;
-       float visual_size = BS*size;
-       v2f texsize(size*2, ymax*size*2);
-       v2f texpos;
-       texpos.X = ((rand()%64)/64.-texsize.X);
-       texpos.Y = ymax*((rand()%64)/64.-texsize.Y);
-
-       // Physics
-       v3f velocity(   (rand()%100/50.-1)/1.5,
-                       rand()%100/35.,
-                       (rand()%100/50.-1)/1.5);
-
-       v3f acceleration(0,-9,0);
-       v3f particlepos = v3f(
-               (f32)pos.X+rand()%100/200.-0.25,
-               (f32)pos.Y+rand()%100/200.-0.25,
-               (f32)pos.Z+rand()%100/200.-0.25
-       );
-
-       new Particle(
-               gamedef,
-               smgr,
-               player,
-               env,
-               particlepos,
-               velocity,
-               acceleration,
-               rand()%100/100., // expiration time
-               visual_size,
-               true,
-               false,
-               texture,
-               texpos,
-               texsize);
-}
-
 /*
        ParticleSpawner
 */
 /*
        ParticleSpawner
 */
@@ -314,7 +242,11 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
        u16 amount, float time,
        v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
        float minexptime, float maxexptime, float minsize, float maxsize,
        u16 amount, float time,
        v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
        float minexptime, float maxexptime, float minsize, float maxsize,
-       bool collisiondetection, bool vertical, video::ITexture *texture, u32 id)
+       bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical,
+       video::ITexture *texture, u32 id, const struct TileAnimationParams &anim,
+       u8 glow,
+       ParticleManager *p_manager) :
+       m_particlemanager(p_manager)
 {
        m_gamedef = gamedef;
        m_smgr = smgr;
 {
        m_gamedef = gamedef;
        m_smgr = smgr;
@@ -332,25 +264,41 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
        m_minsize = minsize;
        m_maxsize = maxsize;
        m_collisiondetection = collisiondetection;
        m_minsize = minsize;
        m_maxsize = maxsize;
        m_collisiondetection = collisiondetection;
+       m_collision_removal = collision_removal;
+       m_attached_id = attached_id;
        m_vertical = vertical;
        m_texture = texture;
        m_time = 0;
        m_vertical = vertical;
        m_texture = texture;
        m_time = 0;
+       m_animation = anim;
+       m_glow = glow;
 
        for (u16 i = 0; i<=m_amount; i++)
        {
                float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
                m_spawntimes.push_back(spawntime);
        }
 
        for (u16 i = 0; i<=m_amount; i++)
        {
                float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime;
                m_spawntimes.push_back(spawntime);
        }
-
-       all_particlespawners.insert(std::pair<u32, ParticleSpawner*>(id, this));
 }
 
 ParticleSpawner::~ParticleSpawner() {}
 
 }
 
 ParticleSpawner::~ParticleSpawner() {}
 
-void ParticleSpawner::step(float dtime, ClientEnvironment &env)
+void ParticleSpawner::step(float dtime, ClientEnvironmentenv)
 {
        m_time += dtime;
 
 {
        m_time += dtime;
 
+       bool unloaded = false;
+       bool is_attached = false;
+       v3f attached_pos = v3f(0,0,0);
+       float attached_yaw = 0;
+       if (m_attached_id != 0) {
+               if (ClientActiveObject *attached = env->getActiveObject(m_attached_id)) {
+                       attached_pos = attached->getPosition() / BS;
+                       attached_yaw = attached->getYaw();
+                       is_attached = true;
+               } else {
+                       unloaded = true;
+               }
+       }
+
        if (m_spawntime != 0) // Spawner exists for a predefined timespan
        {
                for(std::vector<float>::iterator i = m_spawntimes.begin();
        if (m_spawntime != 0) // Spawner exists for a predefined timespan
        {
                for(std::vector<float>::iterator i = m_spawntimes.begin();
@@ -360,41 +308,62 @@ void ParticleSpawner::step(float dtime, ClientEnvironment &env)
                        {
                                m_amount--;
 
                        {
                                m_amount--;
 
-                               v3f pos = random_v3f(m_minpos, m_maxpos);
-                               v3f vel = random_v3f(m_minvel, m_maxvel);
-                               v3f acc = random_v3f(m_minacc, m_maxacc);
-                               float exptime = rand()/(float)RAND_MAX
-                                               *(m_maxexptime-m_minexptime)
-                                               +m_minexptime;
-                               float size = rand()/(float)RAND_MAX
-                                               *(m_maxsize-m_minsize)
-                                               +m_minsize;
-
-                               new Particle(
-                                       m_gamedef,
-                                       m_smgr,
-                                       m_player,
-                                       env,
-                                       pos,
-                                       vel,
-                                       acc,
-                                       exptime,
-                                       size,
-                                       m_collisiondetection,
-                                       m_vertical,
-                                       m_texture,
-                                       v2f(0.0, 0.0),
-                                       v2f(1.0, 1.0));
+                               // Pretend to, but don't actually spawn a
+                               // particle if it is attached to an unloaded
+                               // object.
+                               if (!unloaded) {
+                                       v3f pos = random_v3f(m_minpos, m_maxpos);
+                                       v3f vel = random_v3f(m_minvel, m_maxvel);
+                                       v3f acc = random_v3f(m_minacc, m_maxacc);
+
+                                       if (is_attached) {
+                                               // Apply attachment yaw and position
+                                               pos.rotateXZBy(attached_yaw);
+                                               pos += attached_pos;
+                                               vel.rotateXZBy(attached_yaw);
+                                               acc.rotateXZBy(attached_yaw);
+                                       }
+
+                                       float exptime = rand()/(float)RAND_MAX
+                                                       *(m_maxexptime-m_minexptime)
+                                                       +m_minexptime;
+                                       float size = rand()/(float)RAND_MAX
+                                                       *(m_maxsize-m_minsize)
+                                                       +m_minsize;
+
+                                       Particle* toadd = new Particle(
+                                               m_gamedef,
+                                               m_smgr,
+                                               m_player,
+                                               env,
+                                               pos,
+                                               vel,
+                                               acc,
+                                               exptime,
+                                               size,
+                                               m_collisiondetection,
+                                               m_collision_removal,
+                                               m_vertical,
+                                               m_texture,
+                                               v2f(0.0, 0.0),
+                                               v2f(1.0, 1.0),
+                                               m_animation,
+                                               m_glow);
+                                       m_particlemanager->addParticle(toadd);
+                               }
                                i = m_spawntimes.erase(i);
                        }
                        else
                        {
                                i = m_spawntimes.erase(i);
                        }
                        else
                        {
-                               i++;
+                               ++i;
                        }
                }
        }
        else // Spawner exists for an infinity timespan, spawn on a per-second base
        {
                        }
                }
        }
        else // Spawner exists for an infinity timespan, spawn on a per-second base
        {
+               // Skip this step if attached to an unloaded object
+               if (unloaded)
+                       return;
                for (int i = 0; i <= m_amount; i++)
                {
                        if (rand()/(float)RAND_MAX < dtime)
                for (int i = 0; i <= m_amount; i++)
                {
                        if (rand()/(float)RAND_MAX < dtime)
@@ -402,6 +371,15 @@ void ParticleSpawner::step(float dtime, ClientEnvironment &env)
                                v3f pos = random_v3f(m_minpos, m_maxpos);
                                v3f vel = random_v3f(m_minvel, m_maxvel);
                                v3f acc = random_v3f(m_minacc, m_maxacc);
                                v3f pos = random_v3f(m_minpos, m_maxpos);
                                v3f vel = random_v3f(m_minvel, m_maxvel);
                                v3f acc = random_v3f(m_minacc, m_maxacc);
+
+                               if (is_attached) {
+                                       // Apply attachment yaw and position
+                                       pos.rotateXZBy(attached_yaw);
+                                       pos += attached_pos;
+                                       vel.rotateXZBy(attached_yaw);
+                                       acc.rotateXZBy(attached_yaw);
+                               }
+
                                float exptime = rand()/(float)RAND_MAX
                                                *(m_maxexptime-m_minexptime)
                                                +m_minexptime;
                                float exptime = rand()/(float)RAND_MAX
                                                *(m_maxexptime-m_minexptime)
                                                +m_minexptime;
@@ -409,7 +387,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment &env)
                                                *(m_maxsize-m_minsize)
                                                +m_minsize;
 
                                                *(m_maxsize-m_minsize)
                                                +m_minsize;
 
-                               new Particle(
+                               Particle* toadd = new Particle(
                                        m_gamedef,
                                        m_smgr,
                                        m_player,
                                        m_gamedef,
                                        m_smgr,
                                        m_player,
@@ -420,59 +398,270 @@ void ParticleSpawner::step(float dtime, ClientEnvironment &env)
                                        exptime,
                                        size,
                                        m_collisiondetection,
                                        exptime,
                                        size,
                                        m_collisiondetection,
+                                       m_collision_removal,
                                        m_vertical,
                                        m_texture,
                                        v2f(0.0, 0.0),
                                        m_vertical,
                                        m_texture,
                                        v2f(0.0, 0.0),
-                                       v2f(1.0, 1.0));
+                                       v2f(1.0, 1.0),
+                                       m_animation,
+                                       m_glow);
+                               m_particlemanager->addParticle(toadd);
                        }
                }
        }
 }
 
                        }
                }
        }
 }
 
-void allparticlespawners_step (float dtime, ClientEnvironment &env)
+
+ParticleManager::ParticleManager(ClientEnvironment* env) :
+       m_env(env)
+{}
+
+ParticleManager::~ParticleManager()
 {
 {
-       for(std::map<u32, ParticleSpawner*>::iterator i = 
-                       all_particlespawners.begin();
-                       i != all_particlespawners.end();)
+       clearAll();
+}
+
+void ParticleManager::step(float dtime)
+{
+       stepParticles (dtime);
+       stepSpawners (dtime);
+}
+
+void ParticleManager::stepSpawners (float dtime)
+{
+       MutexAutoLock lock(m_spawner_list_lock);
+       for (std::map<u32, ParticleSpawner*>::iterator i =
+                       m_particle_spawners.begin();
+                       i != m_particle_spawners.end();)
        {
                if (i->second->get_expired())
                {
                        delete i->second;
        {
                if (i->second->get_expired())
                {
                        delete i->second;
-                       all_particlespawners.erase(i++);
+                       m_particle_spawners.erase(i++);
                }
                else
                {
                }
                else
                {
-                       i->second->step(dtime, env);
-                       i++;
+                       i->second->step(dtime, m_env);
+                       ++i;
                }
        }
 }
 
                }
        }
 }
 
-void delete_particlespawner (u32 id)
+void ParticleManager::stepParticles (float dtime)
 {
 {
-       if (all_particlespawners.find(id) != all_particlespawners.end())
+       MutexAutoLock lock(m_particle_list_lock);
+       for(std::vector<Particle*>::iterator i = m_particles.begin();
+                       i != m_particles.end();)
        {
        {
-               delete all_particlespawners.find(id)->second;
-               all_particlespawners.erase(id);
+               if ((*i)->get_expired())
+               {
+                       (*i)->remove();
+                       delete *i;
+                       i = m_particles.erase(i);
+               }
+               else
+               {
+                       (*i)->step(dtime);
+                       ++i;
+               }
        }
 }
 
        }
 }
 
-void clear_particles ()
+void ParticleManager::clearAll ()
 {
 {
+       MutexAutoLock lock(m_spawner_list_lock);
+       MutexAutoLock lock2(m_particle_list_lock);
        for(std::map<u32, ParticleSpawner*>::iterator i =
        for(std::map<u32, ParticleSpawner*>::iterator i =
-                       all_particlespawners.begin();
-                       i != all_particlespawners.end();)
+                       m_particle_spawners.begin();
+                       i != m_particle_spawners.end();)
        {
                delete i->second;
        {
                delete i->second;
-               all_particlespawners.erase(i++);
+               m_particle_spawners.erase(i++);
        }
 
        for(std::vector<Particle*>::iterator i =
        }
 
        for(std::vector<Particle*>::iterator i =
-                       all_particles.begin();
-                       i != all_particles.end();)
+                       m_particles.begin();
+                       i != m_particles.end();)
        {
                (*i)->remove();
                delete *i;
        {
                (*i)->remove();
                delete *i;
-               i = all_particles.erase(i);
+               i = m_particles.erase(i);
        }
 }
        }
 }
+
+void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
+               scene::ISceneManager* smgr, LocalPlayer *player)
+{
+       switch (event->type) {
+               case CE_DELETE_PARTICLESPAWNER: {
+                       MutexAutoLock lock(m_spawner_list_lock);
+                       if (m_particle_spawners.find(event->delete_particlespawner.id) !=
+                                       m_particle_spawners.end()) {
+                               delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
+                               m_particle_spawners.erase(event->delete_particlespawner.id);
+                       }
+                       // no allocated memory in delete event
+                       break;
+               }
+               case CE_ADD_PARTICLESPAWNER: {
+                       {
+                               MutexAutoLock lock(m_spawner_list_lock);
+                               if (m_particle_spawners.find(event->add_particlespawner.id) !=
+                                               m_particle_spawners.end()) {
+                                       delete m_particle_spawners.find(event->add_particlespawner.id)->second;
+                                       m_particle_spawners.erase(event->add_particlespawner.id);
+                               }
+                       }
+
+                       video::ITexture *texture =
+                               client->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
+
+                       ParticleSpawner* toadd = new ParticleSpawner(client, smgr, player,
+                                       event->add_particlespawner.amount,
+                                       event->add_particlespawner.spawntime,
+                                       *event->add_particlespawner.minpos,
+                                       *event->add_particlespawner.maxpos,
+                                       *event->add_particlespawner.minvel,
+                                       *event->add_particlespawner.maxvel,
+                                       *event->add_particlespawner.minacc,
+                                       *event->add_particlespawner.maxacc,
+                                       event->add_particlespawner.minexptime,
+                                       event->add_particlespawner.maxexptime,
+                                       event->add_particlespawner.minsize,
+                                       event->add_particlespawner.maxsize,
+                                       event->add_particlespawner.collisiondetection,
+                                       event->add_particlespawner.collision_removal,
+                                       event->add_particlespawner.attached_id,
+                                       event->add_particlespawner.vertical,
+                                       texture,
+                                       event->add_particlespawner.id,
+                                       event->add_particlespawner.animation,
+                                       event->add_particlespawner.glow,
+                                       this);
+
+                       /* delete allocated content of event */
+                       delete event->add_particlespawner.minpos;
+                       delete event->add_particlespawner.maxpos;
+                       delete event->add_particlespawner.minvel;
+                       delete event->add_particlespawner.maxvel;
+                       delete event->add_particlespawner.minacc;
+                       delete event->add_particlespawner.texture;
+                       delete event->add_particlespawner.maxacc;
+
+                       {
+                               MutexAutoLock lock(m_spawner_list_lock);
+                               m_particle_spawners.insert(
+                                               std::pair<u32, ParticleSpawner*>(
+                                                               event->add_particlespawner.id,
+                                                               toadd));
+                       }
+                       break;
+               }
+               case CE_SPAWN_PARTICLE: {
+                       video::ITexture *texture =
+                               client->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
+
+                       Particle* toadd = new Particle(client, smgr, player, m_env,
+                                       *event->spawn_particle.pos,
+                                       *event->spawn_particle.vel,
+                                       *event->spawn_particle.acc,
+                                       event->spawn_particle.expirationtime,
+                                       event->spawn_particle.size,
+                                       event->spawn_particle.collisiondetection,
+                                       event->spawn_particle.collision_removal,
+                                       event->spawn_particle.vertical,
+                                       texture,
+                                       v2f(0.0, 0.0),
+                                       v2f(1.0, 1.0),
+                                       event->spawn_particle.animation,
+                                       event->spawn_particle.glow);
+
+                       addParticle(toadd);
+
+                       delete event->spawn_particle.pos;
+                       delete event->spawn_particle.vel;
+                       delete event->spawn_particle.acc;
+                       delete event->spawn_particle.texture;
+
+                       break;
+               }
+               default: break;
+       }
+}
+
+void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
+               LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
+{
+       for (u16 j = 0; j < 32; j++) // set the amount of particles here
+       {
+               addNodeParticle(gamedef, smgr, player, pos, tiles);
+       }
+}
+
+void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
+               LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
+{
+       addNodeParticle(gamedef, smgr, player, pos, tiles);
+}
+
+void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
+               LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
+{
+       // Texture
+       u8 texid = myrand_range(0, 5);
+       video::ITexture *texture;
+       struct TileAnimationParams anim;
+       anim.type = TAT_NONE;
+
+       // Only use first frame of animated texture
+       if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION)
+               texture = tiles[texid].frames[0].texture;
+       else
+               texture = tiles[texid].texture;
+
+       float size = rand() % 64 / 512.;
+       float visual_size = BS * size;
+       v2f texsize(size * 2, size * 2);
+       v2f texpos;
+       texpos.X = ((rand() % 64) / 64. - texsize.X);
+       texpos.Y = ((rand() % 64) / 64. - texsize.Y);
+
+       // Physics
+       v3f velocity((rand() % 100 / 50. - 1) / 1.5,
+                       rand() % 100 / 35.,
+                       (rand() % 100 / 50. - 1) / 1.5);
+
+       v3f acceleration(0,-9,0);
+       v3f particlepos = v3f(
+               (f32) pos.X + rand() %100 /200. - 0.25,
+               (f32) pos.Y + rand() %100 /200. - 0.25,
+               (f32) pos.Z + rand() %100 /200. - 0.25
+       );
+
+       Particle* toadd = new Particle(
+               gamedef,
+               smgr,
+               player,
+               m_env,
+               particlepos,
+               velocity,
+               acceleration,
+               rand() % 100 / 100., // expiration time
+               visual_size,
+               true,
+               false,
+               false,
+               texture,
+               texpos,
+               texsize,
+               anim,
+               0);
+
+       addParticle(toadd);
+}
+
+void ParticleManager::addParticle(Particle* toadd)
+{
+       MutexAutoLock lock(m_particle_list_lock);
+       m_particles.push_back(toadd);
+}