Implement spawning particles with node texture appearance
authorsfan5 <sfan5@live.de>
Fri, 22 May 2020 12:17:03 +0000 (14:17 +0200)
committersfan5 <sfan5@live.de>
Sat, 23 May 2020 20:52:21 +0000 (22:52 +0200)
build/android/native/jni/Android.mk
doc/lua_api.txt
src/client/particles.cpp
src/client/particles.h
src/network/clientpackethandler.cpp
src/particles.cpp
src/particles.h
src/script/lua_api/l_particles.cpp
src/script/lua_api/l_particles_local.cpp
src/server.cpp

index a5cb099e69dfdfc2e4f6692ff8f98a5e25f438e7..140947e6a2fbc5fa0da8e3e725a6d18782dde001 100644 (file)
@@ -164,6 +164,7 @@ LOCAL_SRC_FILES := \
        ../../../src/noise.cpp                          \
        ../../../src/objdef.cpp                         \
        ../../../src/object_properties.cpp              \
+       ../../../src/particles.cpp                      \
        ../../../src/pathfinder.cpp                     \
        ../../../src/player.cpp                         \
        ../../../src/porting.cpp                        \
index 26061eccbe93430fc420b1bb0d7bf2cce336185f..5b3f61c99530592d702fe87e892f14e5c949b7ef 100644 (file)
@@ -7835,6 +7835,8 @@ Used by `minetest.add_particle`.
 
         size = 1,
         -- Scales the visual size of the particle texture.
+        -- If `node` is set, size can be set to 0 to spawn a randomly-sized
+        -- particle (just like actual node dig particles).
 
         collisiondetection = false,
         -- If true collides with `walkable` nodes and, depending on the
@@ -7853,6 +7855,7 @@ Used by `minetest.add_particle`.
         -- If true faces player using y axis only
 
         texture = "image.png",
+        -- The texture of the particle
 
         playername = "singleplayer",
         -- Optional, if specified spawns particle only on the player's client
@@ -7863,6 +7866,17 @@ Used by `minetest.add_particle`.
         glow = 0
         -- Optional, specify particle self-luminescence in darkness.
         -- Values 0-14.
+
+        node = {name = "ignore", param2 = 0},
+        -- Optional, if specified the particle will have the same appearance as
+        -- node dig particles for the given node.
+        -- `texture` and `animation` will be ignored if this is set.
+
+        node_tile = 0,
+        -- Optional, only valid in combination with `node`
+        -- If set to a valid number 1-6, specifies the tile from which the
+        -- particle texture is picked.
+        -- Otherwise, the default behavior is used. (currently: any random tile)
     }
 
 
@@ -7892,7 +7906,9 @@ Used by `minetest.add_particlespawner`.
         maxsize = 1,
         -- The particles' properties are random values between the min and max
         -- values.
-        -- pos, velocity, acceleration, expirationtime, size
+        -- applies to: pos, velocity, acceleration, expirationtime, size
+        -- If `node` is set, min and maxsize can be set to 0 to spawn
+        -- randomly-sized particles (just like actual node dig particles).
 
         collisiondetection = false,
         -- If true collide with `walkable` nodes and, depending on the
@@ -7915,6 +7931,7 @@ Used by `minetest.add_particlespawner`.
         -- If true face player using y axis only
 
         texture = "image.png",
+        -- The texture of the particle
 
         playername = "singleplayer",
         -- Optional, if specified spawns particles only on the player's client
@@ -7925,6 +7942,17 @@ Used by `minetest.add_particlespawner`.
         glow = 0
         -- Optional, specify particle self-luminescence in darkness.
         -- Values 0-14.
+
+        node = {name = "ignore", param2 = 0},
+        -- Optional, if specified the particles will have the same appearance as
+        -- node dig particles for the given node.
+        -- `texture` and `animation` will be ignored if this is set.
+
+        node_tile = 0,
+        -- Optional, only valid in combination with `node`
+        -- If set to a valid number 1-6, specifies the tile from which the
+        -- particle texture is picked.
+        -- Otherwise, the default behavior is used. (currently: any random tile)
     }
 
 `HTTPRequest` definition
index c2e751b4fddf290d513c7cf63688dc0c341af646..c78a3e71a12535c2d4951502c9435729448ae4fd 100644 (file)
@@ -304,18 +304,37 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius,
        }
 
        pp.expirationtime = random_f32(p.minexptime, p.maxexptime);
-       pp.size = random_f32(p.minsize, p.maxsize);
-
        p.copyCommon(pp);
 
+       video::ITexture *texture;
+       v2f texpos, texsize;
+       video::SColor color(0xFFFFFFFF);
+
+       if (p.node.getContent() != CONTENT_IGNORE) {
+               const ContentFeatures &f =
+                       m_particlemanager->m_env->getGameDef()->ndef()->get(p.node);
+               if (!ParticleManager::getNodeParticleParams(p.node, f, pp, &texture,
+                               texpos, texsize, &color, p.node_tile))
+                       return;
+       } else {
+               texture = m_texture;
+               texpos = v2f(0.0f, 0.0f);
+               texsize = v2f(1.0f, 1.0f);
+       }
+
+       // Allow keeping default random size
+       if (p.maxsize > 0.0f)
+               pp.size = random_f32(p.minsize, p.maxsize);
+
        m_particlemanager->addParticle(new Particle(
                m_gamedef,
                m_player,
                env,
                pp,
-               m_texture,
-               v2f(0.0, 0.0),
-               v2f(1.0, 1.0)
+               texture,
+               texpos,
+               texsize,
+               color
        ));
 }
 
@@ -460,17 +479,35 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
                        break;
                }
                case CE_SPAWN_PARTICLE: {
-                       const ParticleParameters &p = *event->spawn_particle;
-                       video::ITexture *texture =
-                               client->tsrc()->getTextureForMesh(p.texture);
+                       ParticleParameters &p = *event->spawn_particle;
 
-                       Particle *toadd = new Particle(client, player, m_env,
-                                       p,
-                                       texture,
-                                       v2f(0.0, 0.0),
-                                       v2f(1.0, 1.0));
+                       video::ITexture *texture;
+                       v2f texpos, texsize;
+                       video::SColor color(0xFFFFFFFF);
+
+                       f32 oldsize = p.size;
+
+                       if (p.node.getContent() != CONTENT_IGNORE) {
+                               const ContentFeatures &f = m_env->getGameDef()->ndef()->get(p.node);
+                               if (!getNodeParticleParams(p.node, f, p, &texture, texpos,
+                                               texsize, &color, p.node_tile))
+                                       texture = nullptr;
+                       } else {
+                               texture = client->tsrc()->getTextureForMesh(p.texture);
+                               texpos = v2f(0.0f, 0.0f);
+                               texsize = v2f(1.0f, 1.0f);
+                       }
+
+                       // Allow keeping default random size
+                       if (oldsize > 0.0f)
+                               p.size = oldsize;
 
-                       addParticle(toadd);
+                       if (texture) {
+                               Particle *toadd = new Particle(client, player, m_env,
+                                               p, texture, texpos, texsize, color);
+
+                               addParticle(toadd);
+                       }
 
                        delete event->spawn_particle;
                        break;
@@ -480,15 +517,19 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
 }
 
 bool ParticleManager::getNodeParticleParams(const MapNode &n,
-       const ContentFeatures &f, ParticleParameters &p,
-       video::ITexture **texture, v2f &texpos, v2f &texsize, video::SColor *color)
+       const ContentFeatures &f, ParticleParameters &p, video::ITexture **texture,
+       v2f &texpos, v2f &texsize, video::SColor *color, u8 tilenum)
 {
        // No particles for "airlike" nodes
        if (f.drawtype == NDT_AIRLIKE)
                return false;
 
        // Texture
-       u8 texid = rand() % 6;
+       u8 texid;
+       if (tilenum > 0 && tilenum <= 6)
+               texid = tilenum - 1;
+       else
+               texid = rand() % 6;
        const TileLayer &tile = f.tiles[texid].layers[0];
        p.animation.type = TAT_NONE;
 
index 7dda0e1b15b6d37e04db941bbfc6c8da64b9fda9..2011f02626249071dee3a57adb4a47bb49428ef3 100644 (file)
@@ -42,7 +42,7 @@ class Particle : public scene::ISceneNode
                video::ITexture *texture,
                v2f texpos,
                v2f texsize,
-               video::SColor color = video::SColor(0xFFFFFFFF)
+               video::SColor color
        );
        ~Particle() = default;
 
@@ -171,7 +171,7 @@ public:
 protected:
        static bool getNodeParticleParams(const MapNode &n, const ContentFeatures &f,
                ParticleParameters &p, video::ITexture **texture, v2f &texpos,
-               v2f &texsize, video::SColor *color);
+               v2f &texsize, video::SColor *color, u8 tilenum = 0);
 
        void addParticle(Particle* toadd);
 
index 054e60c3c84c1084b5ee9d126046061e4a239e65..e000acc928d767a56d6c27386a5bf522a14e30a2 100644 (file)
@@ -1003,6 +1003,16 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
        p.glow = readU8(is);
        p.object_collision = readU8(is);
 
+       // This is kinda awful
+       do {
+               u16 tmp_param0 = readU16(is);
+               if (is.eof())
+                       break;
+               p.node.param0 = tmp_param0;
+               p.node.param2 = readU8(is);
+               p.node_tile   = readU8(is);
+       } while (0);
+
        auto event = new ClientEvent();
        event->type                            = CE_ADD_PARTICLESPAWNER;
        event->add_particlespawner.p           = new ParticleSpawnerParameters(p);
index 711d189f612eb182cb0faa9be50928d95de3bc9f..fd81238dc5e0f5bb41d0f1d852a39e00045a0b9c 100644 (file)
@@ -34,6 +34,9 @@ void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const
        animation.serialize(os, 6); /* NOT the protocol ver */
        writeU8(os, glow);
        writeU8(os, object_collision);
+       writeU16(os, node.param0);
+       writeU8(os, node.param2);
+       writeU8(os, node_tile);
 }
 
 void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver)
@@ -50,4 +53,11 @@ void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver)
        animation.deSerialize(is, 6); /* NOT the protocol ver */
        glow               = readU8(is);
        object_collision   = readU8(is);
+       // This is kinda awful
+       u16 tmp_param0 = readU16(is);
+       if (is.eof())
+               return;
+       node.param0 = tmp_param0;
+       node.param2 = readU8(is);
+       node_tile   = readU8(is);
 }
index 659c1249ff94df3d6aea4a332a82d0ee18a1fb93..6f518b7710056ae6bfe1d7f21d413a4636a29809 100644 (file)
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <string>
 #include "irrlichttypes_bloated.h"
 #include "tileanimation.h"
+#include "mapnode.h"
 
 // This file defines the particle-related structures that both the server and
 // client need. The ParticleManager and rendering is in client/particles.h
@@ -34,9 +35,12 @@ struct CommonParticleParams {
        std::string texture;
        struct TileAnimationParams animation;
        u8 glow = 0;
+       MapNode node;
+       u8 node_tile = 0;
 
        CommonParticleParams() {
                animation.type = TAT_NONE;
+               node.setContent(CONTENT_IGNORE);
        }
 
        /* This helper is useful for copying params from
@@ -49,6 +53,8 @@ struct CommonParticleParams {
                to.texture = texture;
                to.animation = animation;
                to.glow = glow;
+               to.node = node;
+               to.node_tile = node_tile;
        }
 };
 
index 7680aa17bd77b5dc67c94f0da0d929f94f6080ac..a51c4fe200756c1de8b68d3c7552e67eac3a9d92 100644 (file)
@@ -111,6 +111,13 @@ int ModApiParticles::l_add_particle(lua_State *L)
                p.texture = getstringfield_default(L, 1, "texture", p.texture);
                p.glow = getintfield_default(L, 1, "glow", p.glow);
 
+               lua_getfield(L, 1, "node");
+               if (lua_istable(L, -1))
+                       p.node = readnode(L, -1, getGameDef(L)->ndef());
+               lua_pop(L, 1);
+
+               p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile);
+
                playername = getstringfield_default(L, 1, "playername", "");
        }
 
@@ -231,6 +238,13 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
                p.texture = getstringfield_default(L, 1, "texture", p.texture);
                playername = getstringfield_default(L, 1, "playername", "");
                p.glow = getintfield_default(L, 1, "glow", p.glow);
+
+               lua_getfield(L, 1, "node");
+               if (lua_istable(L, -1))
+                       p.node = readnode(L, -1, getGameDef(L)->ndef());
+               lua_pop(L, 1);
+
+               p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile);
        }
 
        u32 id = getServer(L)->addParticleSpawner(p, attached, playername);
index 9595b2fab5a4b2f953c09f5320873e655b7691f3..cc68b13a58787fc35cfe50415d8099c922d1acc2 100644 (file)
@@ -67,6 +67,13 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L)
        p.texture = getstringfield_default(L, 1, "texture", p.texture);
        p.glow = getintfield_default(L, 1, "glow", p.glow);
 
+       lua_getfield(L, 1, "node");
+       if (lua_istable(L, -1))
+               p.node = readnode(L, -1, getGameDef(L)->ndef());
+       lua_pop(L, 1);
+
+       p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile);
+
        ClientEvent *event = new ClientEvent();
        event->type           = CE_SPAWN_PARTICLE;
        event->spawn_particle = new ParticleParameters(p);
@@ -134,6 +141,13 @@ int ModApiParticlesLocal::l_add_particlespawner(lua_State *L)
        p.texture = getstringfield_default(L, 1, "texture", p.texture);
        p.glow = getintfield_default(L, 1, "glow", p.glow);
 
+       lua_getfield(L, 1, "node");
+       if (lua_istable(L, -1))
+               p.node = readnode(L, -1, getGameDef(L)->ndef());
+       lua_pop(L, 1);
+
+       p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile);
+
        u64 id = getClient(L)->getParticleManager()->generateSpawnerId();
 
        auto event = new ClientEvent();
index 68b0131d42980368861ccede6c521d7a99e60129..d6e545498b32a96d487c4516cb0df65bf5dc2bd8 100644 (file)
@@ -1577,6 +1577,7 @@ void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
                pkt.putRawString(os.str());
        }
        pkt << p.glow << p.object_collision;
+       pkt << p.node.param0 << p.node.param2 << p.node_tile;
 
        Send(&pkt);
 }