diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6bdcd4fe4..a1d598e7d 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4120,6 +4120,8 @@ The Biome API is still in an experimental phase and subject to change. collision_removal = false, -- ^ collision_removal: if true then particle is removed when it collides, -- ^ requires collisiondetection = true to have any effect + attached = ObjectRef, + -- ^ attached: if defined, makes particle positions relative to this object. vertical = false, -- ^ vertical: if true faces player using y axis only texture = "image.png", diff --git a/src/client.h b/src/client.h index 6d24c0b1d..9f5bda059 100644 --- a/src/client.h +++ b/src/client.h @@ -201,6 +201,7 @@ struct ClientEvent f32 maxsize; bool collisiondetection; bool collision_removal; + u16 attached_id; bool vertical; std::string *texture; u32 id; diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 5d3ed38bc..375a43c90 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -156,6 +156,11 @@ LuaEntitySAO::~LuaEntitySAO() if(m_registered){ m_env->getScriptIface()->luaentity_Remove(m_id); } + + for (UNORDERED_SET::iterator it = m_attached_particle_spawners.begin(); + it != m_attached_particle_spawners.end(); ++it) { + m_env->deleteParticleSpawner(*it, false); + } } void LuaEntitySAO::addedToEnvironment(u32 dtime_s) @@ -817,7 +822,6 @@ PlayerSAO::~PlayerSAO() { if(m_inventory != &m_player->inventory) delete m_inventory; - } std::string PlayerSAO::getDescription() @@ -844,6 +848,10 @@ void PlayerSAO::removingFromEnvironment() m_player->peer_id = 0; m_env->savePlayer(m_player); m_env->removePlayer(m_player); + for (UNORDERED_SET::iterator it = m_attached_particle_spawners.begin(); + it != m_attached_particle_spawners.end(); ++it) { + m_env->deleteParticleSpawner(*it, false); + } } } diff --git a/src/environment.cpp b/src/environment.cpp index ceaa01d7a..ceaf40d89 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -1518,6 +1518,30 @@ u32 ServerEnvironment::addParticleSpawner(float exptime) return id; } +u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id) +{ + u32 id = addParticleSpawner(exptime); + m_particle_spawner_attachments[id] = attached_id; + if (ServerActiveObject *obj = getActiveObject(attached_id)) { + obj->attachParticleSpawner(id); + } + return id; +} + +void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object) +{ + m_particle_spawners.erase(id); + UNORDERED_MAP::iterator it = m_particle_spawner_attachments.find(id); + if (it != m_particle_spawner_attachments.end()) { + u16 obj_id = (*it).second; + ServerActiveObject *sao = getActiveObject(obj_id); + if (sao != NULL && remove_from_object) { + sao->detachParticleSpawner(id); + } + m_particle_spawner_attachments.erase(id); + } +} + ServerActiveObject* ServerEnvironment::getActiveObject(u16 id) { ActiveObjectMap::iterator n = m_active_objects.find(id); diff --git a/src/environment.h b/src/environment.h index 83ad69562..3f3c1cf2c 100644 --- a/src/environment.h +++ b/src/environment.h @@ -329,7 +329,8 @@ public: void loadDefaultMeta(); u32 addParticleSpawner(float exptime); - void deleteParticleSpawner(u32 id) { m_particle_spawners.erase(id); } + u32 addParticleSpawner(float exptime, u16 attached_id); + void deleteParticleSpawner(u32 id, bool remove_from_object = true); /* External ActiveObject interface @@ -519,6 +520,7 @@ private: // Particles IntervalLimiter m_particle_management_interval; UNORDERED_MAP m_particle_spawners; + UNORDERED_MAP m_particle_spawner_attachments; }; #ifndef SERVER diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index b39356e92..090741f9f 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -944,9 +944,11 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) bool vertical = false; bool collision_removal = false; + u16 attached_id = 0; try { *pkt >> vertical; *pkt >> collision_removal; + *pkt >> attached_id; } catch (...) {} @@ -966,6 +968,7 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) event.add_particlespawner.maxsize = maxsize; event.add_particlespawner.collisiondetection = collisiondetection; event.add_particlespawner.collision_removal = collision_removal; + event.add_particlespawner.attached_id = attached_id; event.add_particlespawner.vertical = vertical; event.add_particlespawner.texture = new std::string(texture); event.add_particlespawner.id = id; diff --git a/src/particles.cpp b/src/particles.cpp index ccca691d1..2efee6ada 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -213,7 +213,7 @@ 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, - bool collisiondetection, bool collision_removal, bool vertical, + bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical, video::ITexture *texture, u32 id, ParticleManager *p_manager) : m_particlemanager(p_manager) { @@ -234,6 +234,7 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, 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; @@ -251,6 +252,15 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) { m_time += dtime; + bool unloaded = false; + v3f attached_offset = v3f(0,0,0); + if (m_attached_id != 0) { + if (ClientActiveObject *attached = env->getActiveObject(m_attached_id)) + attached_offset = attached->getPosition() / BS; + else + unloaded = true; + } + if (m_spawntime != 0) // Spawner exists for a predefined timespan { for(std::vector::iterator i = m_spawntimes.begin(); @@ -260,33 +270,41 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) { 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; + // 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) + + attached_offset; + v3f vel = random_v3f(m_minvel, m_maxvel); + v3f acc = random_v3f(m_minacc, m_maxacc); + // Make relative to offest + pos += attached_offset; + 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_particlemanager->addParticle(toadd); + 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_particlemanager->addParticle(toadd); + } i = m_spawntimes.erase(i); } else @@ -297,11 +315,15 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) } 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) { - v3f pos = random_v3f(m_minpos, m_maxpos); + v3f pos = random_v3f(m_minpos, m_maxpos) + + attached_offset; v3f vel = random_v3f(m_minvel, m_maxvel); v3f acc = random_v3f(m_minacc, m_maxacc); float exptime = rand()/(float)RAND_MAX @@ -453,6 +475,7 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, 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, diff --git a/src/particles.h b/src/particles.h index bc3ca53b7..eb8c6665d 100644 --- a/src/particles.h +++ b/src/particles.h @@ -119,6 +119,7 @@ class ParticleSpawner float minsize, float maxsize, bool collisiondetection, bool collision_removal, + u16 attached_id, bool vertical, video::ITexture *texture, u32 id, @@ -154,7 +155,7 @@ class ParticleSpawner bool m_collisiondetection; bool m_collision_removal; bool m_vertical; - + u16 m_attached_id; }; /** diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 263e35407..667ac7272 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "lua_api/l_particles.h" +#include "lua_api/l_object.h" #include "lua_api/l_internal.h" #include "common/c_converter.h" #include "server.h" @@ -138,6 +139,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) time= minexptime= maxexptime= minsize= maxsize= 1; bool collisiondetection, vertical, collision_removal; collisiondetection = vertical = collision_removal = false; + ServerActiveObject *attached = NULL; std::string texture = ""; std::string playername = ""; @@ -198,6 +200,14 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) "collisiondetection", collisiondetection); collision_removal = getboolfield_default(L, 1, "collision_removal", collision_removal); + + lua_getfield(L, 1, "attached"); + if (!lua_isnil(L, -1)) { + ObjectRef *ref = ObjectRef::checkobject(L, -1); + lua_pop(L, 1); + attached = ObjectRef::getobject(ref); + } + vertical = getboolfield_default(L, 1, "vertical", vertical); texture = getstringfield_default(L, 1, "texture", ""); playername = getstringfield_default(L, 1, "playername", ""); @@ -211,6 +221,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) minsize, maxsize, collisiondetection, collision_removal, + attached, vertical, texture, playername); lua_pushnumber(L, id); diff --git a/src/server.cpp b/src/server.cpp index a93c143c7..e67f37d56 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1678,7 +1678,7 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture, u32 id) + u16 attached_id, bool vertical, const std::string &texture, u32 id) { DSTACK(FUNCTION_NAME); @@ -1692,6 +1692,7 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3 pkt << id << vertical; pkt << collision_removal; + pkt << attached_id; if (peer_id != PEER_ID_INEXISTENT) { Send(&pkt); @@ -3156,7 +3157,7 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture, + ServerActiveObject *attached, bool vertical, const std::string &texture, const std::string &playername) { // m_env will be NULL if the server is initializing @@ -3171,11 +3172,19 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime, peer_id = player->peer_id; } - u32 id = m_env->addParticleSpawner(spawntime); + u16 attached_id = attached ? attached->getId() : 0; + + u32 id; + if (attached_id == 0) + id = m_env->addParticleSpawner(spawntime); + else + id = m_env->addParticleSpawner(spawntime, attached_id); + SendAddParticleSpawner(peer_id, amount, spawntime, minpos, maxpos, minvel, maxvel, minacc, maxacc, minexptime, maxexptime, minsize, maxsize, - collisiondetection, collision_removal, vertical, texture, id); + collisiondetection, collision_removal, attached_id, vertical, + texture, id); return id; } diff --git a/src/server.h b/src/server.h index 6ee61a0eb..5fc2a9133 100644 --- a/src/server.h +++ b/src/server.h @@ -258,6 +258,7 @@ public: float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, bool collision_removal, + ServerActiveObject *attached, bool vertical, const std::string &texture, const std::string &playername); @@ -434,6 +435,7 @@ private: float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, bool collision_removal, + u16 attached_id, bool vertical, const std::string &texture, u32 id); void SendDeleteParticleSpawner(u16 peer_id, u32 id); diff --git a/src/serverobject.cpp b/src/serverobject.cpp index 236d7e8dc..191247829 100644 --- a/src/serverobject.cpp +++ b/src/serverobject.cpp @@ -98,4 +98,3 @@ bool ServerActiveObject::setWieldedItem(const ItemStack &item) } return false; } - diff --git a/src/serverobject.h b/src/serverobject.h index cfe2b6bcc..63650e3be 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -188,6 +188,15 @@ public: { return 0; } virtual ItemStack getWieldedItem() const; virtual bool setWieldedItem(const ItemStack &item); + inline void attachParticleSpawner(u32 id) + { + m_attached_particle_spawners.insert(id); + } + inline void detachParticleSpawner(u32 id) + { + m_attached_particle_spawners.erase(id); + } + /* Number of players which know about this object. Object won't be @@ -242,6 +251,7 @@ protected: ServerEnvironment *m_env; v3f m_base_position; + UNORDERED_SET m_attached_particle_spawners; private: // Used for creating objects based on type