Add particle animation, glow

This is implemented by reusing and extending the
TileAnimation code for the methods used by particles.
This commit is contained in:
sfan5 2017-01-14 16:48:49 +01:00
parent c5967f75f0
commit 7279f0b373
16 changed files with 311 additions and 78 deletions

@ -4180,10 +4180,15 @@ The Biome API is still in an experimental phase and subject to change.
-- ^ vertical: if true faces player using y axis only -- ^ vertical: if true faces player using y axis only
texture = "image.png", texture = "image.png",
-- ^ Uses texture (string) -- ^ Uses texture (string)
playername = "singleplayer" playername = "singleplayer",
-- ^ optional, if specified spawns particle only on the player's client -- ^ optional, if specified spawns particle only on the player's client
animation = {Tile Animation definition},
-- ^ optional, specifies how to animate the particle texture
glow = 0
-- ^ optional, specify particle self-luminescence in darkness
} }
### `ParticleSpawner` definition (`add_particlespawner`) ### `ParticleSpawner` definition (`add_particlespawner`)
{ {

@ -523,6 +523,43 @@ minetest.register_craft({
} }
}) })
minetest.register_craftitem("experimental:tester_tool_2", {
description = "Tester Tool 2",
inventory_image = "experimental_tester_tool_1.png^[invert:g",
on_use = function(itemstack, user, pointed_thing)
local pos = minetest.get_pointed_thing_position(pointed_thing, true)
if pos == nil then return end
pos = vector.add(pos, {x=0, y=0.5, z=0})
local tex, anim
if math.random(0, 1) == 0 then
tex = "default_lava_source_animated.png"
anim = {type="sheet_2d", frames_w=3, frames_h=2, frame_length=0.5}
else
tex = "default_lava_flowing_animated.png"
anim = {type="vertical_frames", aspect_w=16, aspect_h=16, length=3.3}
end
minetest.add_particle({
pos = pos,
velocity = {x=0, y=0, z=0},
acceleration = {x=0, y=0.04, z=0},
expirationtime = 6,
collisiondetection = true,
texture = tex,
animation = anim,
size = 4,
glow = math.random(0, 5),
})
end,
})
minetest.register_craft({
output = 'experimental:tester_tool_2',
recipe = {
{'group:crumbly','group:crumbly'},
}
})
--[[minetest.register_on_joinplayer(function(player) --[[minetest.register_on_joinplayer(function(player)
minetest.after(3, function() minetest.after(3, function()
player:set_inventory_formspec("size[8,7.5]".. player:set_inventory_formspec("size[8,7.5]"..

@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "hud.h" #include "hud.h"
#include "particles.h" #include "particles.h"
#include "mapnode.h" #include "mapnode.h"
#include "tileanimation.h"
struct MeshMakeData; struct MeshMakeData;
class MapBlockMesh; class MapBlockMesh;
@ -186,6 +187,8 @@ struct ClientEvent
bool collision_removal; bool collision_removal;
bool vertical; bool vertical;
std::string *texture; std::string *texture;
struct TileAnimationParams animation;
u8 glow;
} spawn_particle; } spawn_particle;
struct{ struct{
u16 amount; u16 amount;
@ -206,6 +209,8 @@ struct ClientEvent
bool vertical; bool vertical;
std::string *texture; std::string *texture;
u32 id; u32 id;
struct TileAnimationParams animation;
u8 glow;
} add_particlespawner; } add_particlespawner;
struct{ struct{
u32 id; u32 id;

@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "network/clientopcodes.h" #include "network/clientopcodes.h"
#include "util/serialize.h" #include "util/serialize.h"
#include "util/srp.h" #include "util/srp.h"
#include "tileanimation.h"
void Client::handleCommand_Deprecated(NetworkPacket* pkt) void Client::handleCommand_Deprecated(NetworkPacket* pkt)
{ {
@ -896,9 +897,14 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
std::string texture = deSerializeLongString(is); std::string texture = deSerializeLongString(is);
bool vertical = false; bool vertical = false;
bool collision_removal = false; bool collision_removal = false;
struct TileAnimationParams animation;
animation.type = TAT_NONE;
u8 glow = 0;
try { try {
vertical = readU8(is); vertical = readU8(is);
collision_removal = readU8(is); collision_removal = readU8(is);
animation.deSerialize(is, m_proto_ver);
glow = readU8(is);
} catch (...) {} } catch (...) {}
ClientEvent event; ClientEvent event;
@ -912,6 +918,8 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
event.spawn_particle.collision_removal = collision_removal; event.spawn_particle.collision_removal = collision_removal;
event.spawn_particle.vertical = vertical; event.spawn_particle.vertical = vertical;
event.spawn_particle.texture = new std::string(texture); event.spawn_particle.texture = new std::string(texture);
event.spawn_particle.animation = animation;
event.spawn_particle.glow = glow;
m_client_event_queue.push(event); m_client_event_queue.push(event);
} }
@ -943,12 +951,20 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
bool vertical = false; bool vertical = false;
bool collision_removal = false; bool collision_removal = false;
struct TileAnimationParams animation;
animation.type = TAT_NONE;
u8 glow = 0;
u16 attached_id = 0; u16 attached_id = 0;
try { try {
*pkt >> vertical; *pkt >> vertical;
*pkt >> collision_removal; *pkt >> collision_removal;
*pkt >> attached_id; *pkt >> attached_id;
// This is horrible but required (why are there two ways to deserialize pkts?)
std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
std::istringstream is(datastring, std::ios_base::binary);
animation.deSerialize(is, m_proto_ver);
glow = readU8(is);
} catch (...) {} } catch (...) {}
ClientEvent event; ClientEvent event;
@ -971,6 +987,8 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
event.add_particlespawner.vertical = vertical; event.add_particlespawner.vertical = vertical;
event.add_particlespawner.texture = new std::string(texture); event.add_particlespawner.texture = new std::string(texture);
event.add_particlespawner.id = id; event.add_particlespawner.id = id;
event.add_particlespawner.animation = animation;
event.add_particlespawner.glow = glow;
m_client_event_queue.push(event); m_client_event_queue.push(event);
} }

@ -63,7 +63,7 @@ void NetworkPacket::putRawPacket(u8 *data, u32 datasize, u16 peer_id)
m_data = std::vector<u8>(&data[2], &data[2 + m_datasize]); m_data = std::vector<u8>(&data[2], &data[2 + m_datasize]);
} }
char* NetworkPacket::getString(u32 from_offset) const char* NetworkPacket::getString(u32 from_offset)
{ {
checkReadOffset(from_offset, 0); checkReadOffset(from_offset, 0);

@ -41,12 +41,15 @@ public:
u16 getPeerId() { return m_peer_id; } u16 getPeerId() { return m_peer_id; }
u16 getCommand() { return m_command; } u16 getCommand() { return m_command; }
const u32 getRemainingBytes() const { return m_datasize - m_read_offset; } const u32 getRemainingBytes() const { return m_datasize - m_read_offset; }
const char* getRemainingString() { return getString(m_read_offset); }
// Returns a c-string without copying. // Returns a c-string without copying.
// A better name for this would be getRawString() // A better name for this would be getRawString()
char* getString(u32 from_offset); const char* getString(u32 from_offset);
// major difference to putCString(): doesn't write len into the buffer // major difference to putCString(): doesn't write len into the buffer
void putRawString(const char* src, u32 len); void putRawString(const char* src, u32 len);
void putRawString(const std::string &src)
{ putRawString(src.c_str(), src.size()); }
NetworkPacket& operator>>(std::string& dst); NetworkPacket& operator>>(std::string& dst);
NetworkPacket& operator<<(std::string src); NetworkPacket& operator<<(std::string src);

@ -541,7 +541,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
if (tile->material_flags & MATERIAL_FLAG_ANIMATION) { if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
int frame_length_ms; int frame_length_ms;
tiledef->animation.determineParams(tile->texture->getOriginalSize(), tiledef->animation.determineParams(tile->texture->getOriginalSize(),
&frame_count, &frame_length_ms); &frame_count, &frame_length_ms, NULL);
tile->animation_frame_count = frame_count; tile->animation_frame_count = frame_count;
tile->animation_frame_length_ms = frame_length_ms; tile->animation_frame_length_ms = frame_length_ms;
} }

@ -54,7 +54,9 @@ Particle::Particle(
bool vertical, bool vertical,
video::ITexture *texture, video::ITexture *texture,
v2f texpos, v2f texpos,
v2f texsize v2f texsize,
const struct TileAnimationParams &anim,
u8 glow
): ):
scene::ISceneNode(smgr->getRootSceneNode(), smgr) scene::ISceneNode(smgr->getRootSceneNode(), smgr)
{ {
@ -71,7 +73,9 @@ Particle::Particle(
m_material.setTexture(0, texture); m_material.setTexture(0, texture);
m_texpos = texpos; m_texpos = texpos;
m_texsize = texsize; m_texsize = texsize;
m_animation = anim;
m_animation_frame = 0;
m_animation_time = 0.0;
// Particle related // Particle related
m_pos = pos; m_pos = pos;
@ -84,6 +88,7 @@ Particle::Particle(
m_collisiondetection = collisiondetection; m_collisiondetection = collisiondetection;
m_collision_removal = collision_removal; m_collision_removal = collision_removal;
m_vertical = vertical; m_vertical = vertical;
m_glow = glow;
// Irrlicht stuff // Irrlicht stuff
m_collisionbox = aabb3f m_collisionbox = aabb3f
@ -142,6 +147,18 @@ void Particle::step(float dtime)
m_velocity += m_acceleration * dtime; m_velocity += m_acceleration * dtime;
m_pos += m_velocity * 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(); updateLight();
@ -166,16 +183,32 @@ void Particle::updateLight()
else else
light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0); light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
m_light = decode_light(light); m_light = decode_light(light + m_glow);
} }
void Particle::updateVertices() void Particle::updateVertices()
{ {
video::SColor c(255, m_light, m_light, m_light); video::SColor c(255, m_light, m_light, m_light);
f32 tx0 = m_texpos.X; f32 tx0, tx1, ty0, ty1;
f32 tx1 = m_texpos.X + m_texsize.X;
f32 ty0 = m_texpos.Y; if (m_animation.type != TAT_NONE) {
f32 ty1 = m_texpos.Y + m_texsize.Y; 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, m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
c, tx0, ty1); c, tx0, ty1);
@ -210,7 +243,9 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
float minexptime, float maxexptime, float minsize, float maxsize, float minexptime, float maxexptime, float minsize, float maxsize,
bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical, bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical,
video::ITexture *texture, u32 id, ParticleManager *p_manager) : video::ITexture *texture, u32 id, const struct TileAnimationParams &anim,
u8 glow,
ParticleManager *p_manager) :
m_particlemanager(p_manager) m_particlemanager(p_manager)
{ {
m_gamedef = gamedef; m_gamedef = gamedef;
@ -234,6 +269,8 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
m_vertical = vertical; m_vertical = vertical;
m_texture = texture; m_texture = texture;
m_time = 0; m_time = 0;
m_animation = anim;
m_glow = glow;
for (u16 i = 0; i<=m_amount; i++) for (u16 i = 0; i<=m_amount; i++)
{ {
@ -309,7 +346,9 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
m_vertical, m_vertical,
m_texture, m_texture,
v2f(0.0, 0.0), v2f(0.0, 0.0),
v2f(1.0, 1.0)); v2f(1.0, 1.0),
m_animation,
m_glow);
m_particlemanager->addParticle(toadd); m_particlemanager->addParticle(toadd);
} }
i = m_spawntimes.erase(i); i = m_spawntimes.erase(i);
@ -363,7 +402,9 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
m_vertical, m_vertical,
m_texture, m_texture,
v2f(0.0, 0.0), v2f(0.0, 0.0),
v2f(1.0, 1.0)); v2f(1.0, 1.0),
m_animation,
m_glow);
m_particlemanager->addParticle(toadd); m_particlemanager->addParticle(toadd);
} }
} }
@ -494,6 +535,8 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
event->add_particlespawner.vertical, event->add_particlespawner.vertical,
texture, texture,
event->add_particlespawner.id, event->add_particlespawner.id,
event->add_particlespawner.animation,
event->add_particlespawner.glow,
this); this);
/* delete allocated content of event */ /* delete allocated content of event */
@ -529,13 +572,16 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
event->spawn_particle.vertical, event->spawn_particle.vertical,
texture, texture,
v2f(0.0, 0.0), v2f(0.0, 0.0),
v2f(1.0, 1.0)); v2f(1.0, 1.0),
event->spawn_particle.animation,
event->spawn_particle.glow);
addParticle(toadd); addParticle(toadd);
delete event->spawn_particle.pos; delete event->spawn_particle.pos;
delete event->spawn_particle.vel; delete event->spawn_particle.vel;
delete event->spawn_particle.acc; delete event->spawn_particle.acc;
delete event->spawn_particle.texture;
break; break;
} }
@ -564,6 +610,8 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s
// Texture // Texture
u8 texid = myrand_range(0, 5); u8 texid = myrand_range(0, 5);
video::ITexture *texture; video::ITexture *texture;
struct TileAnimationParams anim;
anim.type = TAT_NONE;
// Only use first frame of animated texture // Only use first frame of animated texture
if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION) if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION)
@ -605,7 +653,9 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s
false, false,
texture, texture,
texpos, texpos,
texsize); texsize,
anim,
0);
addParticle(toadd); addParticle(toadd);
} }

@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/tile.h" #include "client/tile.h"
#include "localplayer.h" #include "localplayer.h"
#include "environment.h" #include "environment.h"
#include "tileanimation.h"
struct ClientEvent; struct ClientEvent;
class ParticleManager; class ParticleManager;
@ -50,7 +51,9 @@ class Particle : public scene::ISceneNode
bool vertical, bool vertical,
video::ITexture *texture, video::ITexture *texture,
v2f texpos, v2f texpos,
v2f texsize v2f texsize,
const struct TileAnimationParams &anim,
u8 glow
); );
~Particle(); ~Particle();
@ -102,6 +105,10 @@ private:
bool m_collision_removal; bool m_collision_removal;
bool m_vertical; bool m_vertical;
v3s16 m_camera_offset; v3s16 m_camera_offset;
struct TileAnimationParams m_animation;
float m_animation_time;
int m_animation_frame;
u8 m_glow;
}; };
class ParticleSpawner class ParticleSpawner
@ -123,6 +130,7 @@ class ParticleSpawner
bool vertical, bool vertical,
video::ITexture *texture, video::ITexture *texture,
u32 id, u32 id,
const struct TileAnimationParams &anim, u8 glow,
ParticleManager* p_manager); ParticleManager* p_manager);
~ParticleSpawner(); ~ParticleSpawner();
@ -156,6 +164,8 @@ class ParticleSpawner
bool m_collision_removal; bool m_collision_removal;
bool m_vertical; bool m_vertical;
u16 m_attached_id; u16 m_attached_id;
struct TileAnimationParams m_animation;
u8 m_glow;
}; };
/** /**

@ -322,7 +322,7 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype)
} }
else if(lua_istable(L, index)) else if(lua_istable(L, index))
{ {
// {name="default_lava.png", animation={}} // name="default_lava.png"
tiledef.name = ""; tiledef.name = "";
getstringfield(L, index, "name", tiledef.name); getstringfield(L, index, "name", tiledef.name);
getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat. getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat.
@ -334,28 +334,7 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype)
L, index, "tileable_vertical", default_tiling); L, index, "tileable_vertical", default_tiling);
// animation = {} // animation = {}
lua_getfield(L, index, "animation"); lua_getfield(L, index, "animation");
if(lua_istable(L, -1)){ tiledef.animation = read_animation_definition(L, -1);
tiledef.animation.type = (TileAnimationType)
getenumfield(L, -1, "type", es_TileAnimationType,
TAT_NONE);
if (tiledef.animation.type == TAT_VERTICAL_FRAMES) {
// {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}
tiledef.animation.vertical_frames.aspect_w =
getintfield_default(L, -1, "aspect_w", 16);
tiledef.animation.vertical_frames.aspect_h =
getintfield_default(L, -1, "aspect_h", 16);
tiledef.animation.vertical_frames.length =
getfloatfield_default(L, -1, "length", 1.0);
} else if (tiledef.animation.type == TAT_SHEET_2D) {
// {type="sheet_2d", frames_w=5, frames_h=3, frame_length=0.5}
getintfield(L, -1, "frames_w",
tiledef.animation.sheet_2d.frames_w);
getintfield(L, -1, "frames_h",
tiledef.animation.sheet_2d.frames_h);
getfloatfield(L, -1, "frame_length",
tiledef.animation.sheet_2d.frame_length);
}
}
lua_pop(L, 1); lua_pop(L, 1);
} }
@ -925,6 +904,41 @@ void read_inventory_list(lua_State *L, int tableindex,
} }
} }
/******************************************************************************/
struct TileAnimationParams read_animation_definition(lua_State *L, int index)
{
if(index < 0)
index = lua_gettop(L) + 1 + index;
struct TileAnimationParams anim;
anim.type = TAT_NONE;
if (!lua_istable(L, index))
return anim;
anim.type = (TileAnimationType)
getenumfield(L, index, "type", es_TileAnimationType,
TAT_NONE);
if (anim.type == TAT_VERTICAL_FRAMES) {
// {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}
anim.vertical_frames.aspect_w =
getintfield_default(L, index, "aspect_w", 16);
anim.vertical_frames.aspect_h =
getintfield_default(L, index, "aspect_h", 16);
anim.vertical_frames.length =
getfloatfield_default(L, index, "length", 1.0);
} else if (anim.type == TAT_SHEET_2D) {
// {type="sheet_2d", frames_w=5, frames_h=3, frame_length=0.5}
getintfield(L, index, "frames_w",
anim.sheet_2d.frames_w);
getintfield(L, index, "frames_h",
anim.sheet_2d.frames_h);
getfloatfield(L, index, "frame_length",
anim.sheet_2d.frame_length);
}
return anim;
}
/******************************************************************************/ /******************************************************************************/
ToolCapabilities read_tool_capabilities( ToolCapabilities read_tool_capabilities(
lua_State *L, int table) lua_State *L, int table)

@ -79,6 +79,7 @@ void push_hit_params (lua_State *L,
ItemStack read_item (lua_State *L, int index, Server *srv); ItemStack read_item (lua_State *L, int index, Server *srv);
struct TileAnimationParams read_animation_definition(lua_State *L, int index);
ToolCapabilities read_tool_capabilities (lua_State *L, int table); ToolCapabilities read_tool_capabilities (lua_State *L, int table);
void push_tool_capabilities (lua_State *L, void push_tool_capabilities (lua_State *L,

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_object.h" #include "lua_api/l_object.h"
#include "lua_api/l_internal.h" #include "lua_api/l_internal.h"
#include "common/c_converter.h" #include "common/c_converter.h"
#include "common/c_content.h"
#include "server.h" #include "server.h"
#include "particles.h" #include "particles.h"
@ -34,6 +35,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// collision_removal = bool // collision_removal = bool
// vertical = bool // vertical = bool
// texture = e.g."default_wood.png" // texture = e.g."default_wood.png"
// animation = TileAnimation definition
// glow = num
int ModApiParticles::l_add_particle(lua_State *L) int ModApiParticles::l_add_particle(lua_State *L)
{ {
MAP_LOCK_REQUIRED; MAP_LOCK_REQUIRED;
@ -47,10 +50,13 @@ int ModApiParticles::l_add_particle(lua_State *L)
bool collisiondetection, vertical, collision_removal; bool collisiondetection, vertical, collision_removal;
collisiondetection = vertical = collision_removal = false; collisiondetection = vertical = collision_removal = false;
struct TileAnimationParams animation;
std::string texture = ""; std::string texture = "";
std::string playername = ""; std::string playername = "";
u8 glow = 0;
if (lua_gettop(L) > 1) // deprecated if (lua_gettop(L) > 1) // deprecated
{ {
log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition"); log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition");
@ -101,11 +107,18 @@ int ModApiParticles::l_add_particle(lua_State *L)
collision_removal = getboolfield_default(L, 1, collision_removal = getboolfield_default(L, 1,
"collision_removal", collision_removal); "collision_removal", collision_removal);
vertical = getboolfield_default(L, 1, "vertical", vertical); vertical = getboolfield_default(L, 1, "vertical", vertical);
lua_getfield(L, 1, "animation");
animation = read_animation_definition(L, -1);
lua_pop(L, 1);
texture = getstringfield_default(L, 1, "texture", ""); texture = getstringfield_default(L, 1, "texture", "");
playername = getstringfield_default(L, 1, "playername", ""); playername = getstringfield_default(L, 1, "playername", "");
glow = getintfield_default(L, 1, "glow", 0);
} }
getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size, getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size,
collisiondetection, collision_removal, vertical, texture); collisiondetection, collision_removal, vertical, texture, animation, glow);
return 1; return 1;
} }
@ -127,6 +140,8 @@ int ModApiParticles::l_add_particle(lua_State *L)
// collision_removal = bool // collision_removal = bool
// vertical = bool // vertical = bool
// texture = e.g."default_wood.png" // texture = e.g."default_wood.png"
// animation = TileAnimation definition
// glow = num
int ModApiParticles::l_add_particlespawner(lua_State *L) int ModApiParticles::l_add_particlespawner(lua_State *L)
{ {
MAP_LOCK_REQUIRED; MAP_LOCK_REQUIRED;
@ -139,9 +154,11 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
time= minexptime= maxexptime= minsize= maxsize= 1; time= minexptime= maxexptime= minsize= maxsize= 1;
bool collisiondetection, vertical, collision_removal; bool collisiondetection, vertical, collision_removal;
collisiondetection = vertical = collision_removal = false; collisiondetection = vertical = collision_removal = false;
struct TileAnimationParams animation;
ServerActiveObject *attached = NULL; ServerActiveObject *attached = NULL;
std::string texture = ""; std::string texture = "";
std::string playername = ""; std::string playername = "";
u8 glow = 0;
if (lua_gettop(L) > 1) //deprecated if (lua_gettop(L) > 1) //deprecated
{ {
@ -201,6 +218,10 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
collision_removal = getboolfield_default(L, 1, collision_removal = getboolfield_default(L, 1,
"collision_removal", collision_removal); "collision_removal", collision_removal);
lua_getfield(L, 1, "animation");
animation = read_animation_definition(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "attached"); lua_getfield(L, 1, "attached");
if (!lua_isnil(L, -1)) { if (!lua_isnil(L, -1)) {
ObjectRef *ref = ObjectRef::checkobject(L, -1); ObjectRef *ref = ObjectRef::checkobject(L, -1);
@ -211,6 +232,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
vertical = getboolfield_default(L, 1, "vertical", vertical); vertical = getboolfield_default(L, 1, "vertical", vertical);
texture = getstringfield_default(L, 1, "texture", ""); texture = getstringfield_default(L, 1, "texture", "");
playername = getstringfield_default(L, 1, "playername", ""); playername = getstringfield_default(L, 1, "playername", "");
glow = getintfield_default(L, 1, "glow", 0);
} }
u32 id = getServer(L)->addParticleSpawner(amount, time, u32 id = getServer(L)->addParticleSpawner(amount, time,
@ -223,7 +245,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
collision_removal, collision_removal,
attached, attached,
vertical, vertical,
texture, playername); texture, playername,
animation, glow);
lua_pushnumber(L, id); lua_pushnumber(L, id);
return 1; return 1;

@ -1662,12 +1662,28 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
} }
// Spawns a particle on peer with peer_id // Spawns a particle on peer with peer_id
void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size, bool collisiondetection, float expirationtime, float size, bool collisiondetection,
bool collision_removal, bool collision_removal,
bool vertical, const std::string &texture) bool vertical, const std::string &texture,
const struct TileAnimationParams &animation, u8 glow)
{ {
DSTACK(FUNCTION_NAME); DSTACK(FUNCTION_NAME);
if (peer_id == PEER_ID_INEXISTENT) {
// This sucks and should be replaced by a better solution in a refactor:
std::vector<u16> clients = m_clients.getClientIDs();
for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
RemotePlayer *player = m_env->getPlayer(*i);
if (!player)
continue;
SendSpawnParticle(*i, player->protocol_version,
pos, velocity, acceleration,
expirationtime, size, collisiondetection,
collision_removal, vertical, texture, animation, glow);
}
return;
}
NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id); NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
@ -1676,22 +1692,39 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat
pkt.putLongString(texture); pkt.putLongString(texture);
pkt << vertical; pkt << vertical;
pkt << collision_removal; pkt << collision_removal;
// This is horrible but required (why are there two ways to serialize pkts?)
std::ostringstream os(std::ios_base::binary);
animation.serialize(os, protocol_version);
pkt.putRawString(os.str());
pkt << glow;
if (peer_id != PEER_ID_INEXISTENT) {
Send(&pkt); Send(&pkt);
} }
else {
m_clients.sendToAll(0, &pkt, true);
}
}
// Adds a ParticleSpawner on peer with peer_id // Adds a ParticleSpawner on peer with peer_id
void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
u16 amount, float spawntime, v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
float minsize, float maxsize, bool collisiondetection, bool collision_removal, float minsize, float maxsize, bool collisiondetection, bool collision_removal,
u16 attached_id, bool vertical, const std::string &texture, u32 id) u16 attached_id, bool vertical, const std::string &texture, u32 id,
const struct TileAnimationParams &animation, u8 glow)
{ {
DSTACK(FUNCTION_NAME); DSTACK(FUNCTION_NAME);
if (peer_id == PEER_ID_INEXISTENT) {
// This sucks and should be replaced:
std::vector<u16> clients = m_clients.getClientIDs();
for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
RemotePlayer *player = m_env->getPlayer(*i);
if (!player)
continue;
SendAddParticleSpawner(*i, player->protocol_version,
amount, spawntime, minpos, maxpos,
minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
minsize, maxsize, collisiondetection, collision_removal,
attached_id, vertical, texture, id, animation, glow);
}
return;
}
NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id); NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
@ -1704,14 +1737,14 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3
pkt << id << vertical; pkt << id << vertical;
pkt << collision_removal; pkt << collision_removal;
pkt << attached_id; pkt << attached_id;
// This is horrible but required
std::ostringstream os(std::ios_base::binary);
animation.serialize(os, protocol_version);
pkt.putRawString(os.str());
pkt << glow;
if (peer_id != PEER_ID_INEXISTENT) {
Send(&pkt); Send(&pkt);
} }
else {
m_clients.sendToAll(0, &pkt, true);
}
}
void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
{ {
@ -3165,23 +3198,25 @@ void Server::spawnParticle(const std::string &playername, v3f pos,
v3f velocity, v3f acceleration, v3f velocity, v3f acceleration,
float expirationtime, float size, bool float expirationtime, float size, bool
collisiondetection, bool collision_removal, collisiondetection, bool collision_removal,
bool vertical, const std::string &texture) bool vertical, const std::string &texture,
const struct TileAnimationParams &animation, u8 glow)
{ {
// m_env will be NULL if the server is initializing // m_env will be NULL if the server is initializing
if (!m_env) if (!m_env)
return; return;
u16 peer_id = PEER_ID_INEXISTENT; u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
if (playername != "") { if (playername != "") {
RemotePlayer *player = m_env->getPlayer(playername.c_str()); RemotePlayer *player = m_env->getPlayer(playername.c_str());
if (!player) if (!player)
return; return;
peer_id = player->peer_id; peer_id = player->peer_id;
proto_ver = player->protocol_version;
} }
SendSpawnParticle(peer_id, pos, velocity, acceleration, SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
expirationtime, size, collisiondetection, expirationtime, size, collisiondetection,
collision_removal, vertical, texture); collision_removal, vertical, texture, animation, glow);
} }
u32 Server::addParticleSpawner(u16 amount, float spawntime, u32 Server::addParticleSpawner(u16 amount, float spawntime,
@ -3189,18 +3224,20 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime,
float minexptime, float maxexptime, float minsize, float maxsize, float minexptime, float maxexptime, float minsize, float maxsize,
bool collisiondetection, bool collision_removal, bool collisiondetection, bool collision_removal,
ServerActiveObject *attached, bool vertical, const std::string &texture, ServerActiveObject *attached, bool vertical, const std::string &texture,
const std::string &playername) const std::string &playername, const struct TileAnimationParams &animation,
u8 glow)
{ {
// m_env will be NULL if the server is initializing // m_env will be NULL if the server is initializing
if (!m_env) if (!m_env)
return -1; return -1;
u16 peer_id = PEER_ID_INEXISTENT; u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
if (playername != "") { if (playername != "") {
RemotePlayer *player = m_env->getPlayer(playername.c_str()); RemotePlayer *player = m_env->getPlayer(playername.c_str());
if (!player) if (!player)
return -1; return -1;
peer_id = player->peer_id; peer_id = player->peer_id;
proto_ver = player->protocol_version;
} }
u16 attached_id = attached ? attached->getId() : 0; u16 attached_id = attached ? attached->getId() : 0;
@ -3211,11 +3248,11 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime,
else else
id = m_env->addParticleSpawner(spawntime, attached_id); id = m_env->addParticleSpawner(spawntime, attached_id);
SendAddParticleSpawner(peer_id, amount, spawntime, SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc, minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize, minexptime, maxexptime, minsize, maxsize,
collisiondetection, collision_removal, attached_id, vertical, collisiondetection, collision_removal, attached_id, vertical,
texture, id); texture, id, animation, glow);
return id; return id;
} }

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mods.h" #include "mods.h"
#include "inventorymanager.h" #include "inventorymanager.h"
#include "subgame.h" #include "subgame.h"
#include "tileanimation.h" // struct TileAnimationParams
#include "util/numeric.h" #include "util/numeric.h"
#include "util/thread.h" #include "util/thread.h"
#include "util/basic_macros.h" #include "util/basic_macros.h"
@ -252,7 +253,8 @@ public:
v3f pos, v3f velocity, v3f acceleration, v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size, float expirationtime, float size,
bool collisiondetection, bool collision_removal, bool collisiondetection, bool collision_removal,
bool vertical, const std::string &texture); bool vertical, const std::string &texture,
const struct TileAnimationParams &animation, u8 glow);
u32 addParticleSpawner(u16 amount, float spawntime, u32 addParticleSpawner(u16 amount, float spawntime,
v3f minpos, v3f maxpos, v3f minpos, v3f maxpos,
@ -263,7 +265,8 @@ public:
bool collisiondetection, bool collision_removal, bool collisiondetection, bool collision_removal,
ServerActiveObject *attached, ServerActiveObject *attached,
bool vertical, const std::string &texture, bool vertical, const std::string &texture,
const std::string &playername); const std::string &playername, const struct TileAnimationParams &animation,
u8 glow);
void deleteParticleSpawner(const std::string &playername, u32 id); void deleteParticleSpawner(const std::string &playername, u32 id);
@ -428,7 +431,8 @@ private:
void sendDetachedInventories(u16 peer_id); void sendDetachedInventories(u16 peer_id);
// Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all)
void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, void SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
u16 amount, float spawntime,
v3f minpos, v3f maxpos, v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel, v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc, v3f minacc, v3f maxacc,
@ -436,16 +440,18 @@ private:
float minsize, float maxsize, float minsize, float maxsize,
bool collisiondetection, bool collision_removal, bool collisiondetection, bool collision_removal,
u16 attached_id, u16 attached_id,
bool vertical, const std::string &texture, u32 id); bool vertical, const std::string &texture, u32 id,
const struct TileAnimationParams &animation, u8 glow);
void SendDeleteParticleSpawner(u16 peer_id, u32 id); void SendDeleteParticleSpawner(u16 peer_id, u32 id);
// Spawns particle on peer with peer_id (PEER_ID_INEXISTENT == all) // Spawns particle on peer with peer_id (PEER_ID_INEXISTENT == all)
void SendSpawnParticle(u16 peer_id, void SendSpawnParticle(u16 peer_id, u16 protocol_version,
v3f pos, v3f velocity, v3f acceleration, v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size, float expirationtime, float size,
bool collisiondetection, bool collision_removal, bool collisiondetection, bool collision_removal,
bool vertical, const std::string &texture); bool vertical, const std::string &texture,
const struct TileAnimationParams &animation, u8 glow);
u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas); u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas);
void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true); void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true);

@ -69,7 +69,8 @@ void TileAnimationParams::deSerialize(std::istream &is, u16 protocol_version)
} }
} }
void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms) const void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count,
int *frame_length_ms, v2u32 *frame_size) const
{ {
if (type == TAT_VERTICAL_FRAMES) { if (type == TAT_VERTICAL_FRAMES) {
int frame_height = (float)texture_size.X / int frame_height = (float)texture_size.X /
@ -80,15 +81,17 @@ void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count,
*frame_count = _frame_count; *frame_count = _frame_count;
if (frame_length_ms) if (frame_length_ms)
*frame_length_ms = 1000.0 * vertical_frames.length / _frame_count; *frame_length_ms = 1000.0 * vertical_frames.length / _frame_count;
if (frame_size)
*frame_size = v2u32(texture_size.X, frame_height);
} else if (type == TAT_SHEET_2D) { } else if (type == TAT_SHEET_2D) {
if (frame_count) if (frame_count)
*frame_count = sheet_2d.frames_w * sheet_2d.frames_h; *frame_count = sheet_2d.frames_w * sheet_2d.frames_h;
if (frame_length_ms) if (frame_length_ms)
*frame_length_ms = 1000 * sheet_2d.frame_length; *frame_length_ms = 1000 * sheet_2d.frame_length;
} else { // TAT_NONE if (frame_size)
*frame_count = 1; *frame_size = v2u32(texture_size.X / sheet_2d.frames_w, texture_size.Y / sheet_2d.frames_h);
*frame_length_ms = 1000;
} }
// caller should check for TAT_NONE
} }
void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const
@ -97,7 +100,7 @@ void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size
return; return;
if (type == TAT_VERTICAL_FRAMES) { if (type == TAT_VERTICAL_FRAMES) {
int frame_count; int frame_count;
determineParams(texture_size, &frame_count, NULL); determineParams(texture_size, &frame_count, NULL, NULL);
os << "^[verticalframe:" << frame_count << ":" << frame; os << "^[verticalframe:" << frame_count << ":" << frame;
} else if (type == TAT_SHEET_2D) { } else if (type == TAT_SHEET_2D) {
int q, r; int q, r;
@ -107,3 +110,22 @@ void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size
<< ":" << r << "," << q; << ":" << r << "," << q;
} }
} }
v2f TileAnimationParams::getTextureCoords(v2u32 texture_size, int frame) const
{
v2u32 ret(0, 0);
if (type == TAT_VERTICAL_FRAMES) {
int frame_height = (float)texture_size.X /
(float)vertical_frames.aspect_w *
(float)vertical_frames.aspect_h;
ret = v2u32(0, frame_height * frame);
} else if (type == TAT_SHEET_2D) {
v2u32 frame_size;
determineParams(texture_size, NULL, NULL, &frame_size);
int q, r;
q = frame / sheet_2d.frames_w;
r = frame % sheet_2d.frames_w;
ret = v2u32(r * frame_size.X, q * frame_size.Y);
}
return v2f(ret.X / (float) texture_size.X, ret.Y / (float) texture_size.Y);
}

@ -48,8 +48,10 @@ struct TileAnimationParams {
void serialize(std::ostream &os, u16 protocol_version) const; void serialize(std::ostream &os, u16 protocol_version) const;
void deSerialize(std::istream &is, u16 protocol_version); void deSerialize(std::istream &is, u16 protocol_version);
void determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms) const; void determineParams(v2u32 texture_size, int *frame_count,
int *frame_length_ms, v2u32 *frame_size) const;
void getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const; void getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const;
v2f getTextureCoords(v2u32 texture_size, int frame) const;
}; };
#endif #endif