aboutsummaryrefslogtreecommitdiff
path: root/src/serverobject.h
Commit message (Expand)AuthorAge
* PlayerSAO/LocalPlayer refactor: (#4612)Ner'zhul2016-10-30
* Attached particle spawnersraymoo2016-10-13
* Player/LocalPlayer/RemotePlayer inheritance cleanup (part 2 on X)Loic Blot2016-10-08
* Prevent attached models from disappearing during parent reload (#4128)Foghrye42016-10-08
* Fix invisible player when the attached entity is removedTeTpaAka2015-07-18
* Fix some issues with animations, and allow non-looped animations to be definedMirceaKitsune2015-06-22
* Fix typo in variable nameKahrl2015-05-28
* Add some missing getter functions to the lua APITeTpaAka2015-05-28
* Consistent const string paramsest312015-05-27
* Remove Queue class which uses std::list and use native std::queueLoic Blot2015-03-05
* SAO: re-add old ActiveObjectTypes for a future migration layerLoic Blot2015-02-17
* SAO work: ActiveObject types & SAO cleanup * Replace u8 types with ActiveObje...Loic Blot2015-02-17
* Replace setting unlimited_player_transfer_distance with player_transfer_distanceSmallJoker2014-11-08
* Remove remenants of mob codePavel Puchkin2014-10-23
* Allow modifying movement speed, jump height and gravity per-player via the Lu...MirceaKitsune2013-04-05
* Migrate to STL containers/algorithms.Ilya Zhuravlev2013-03-11
* Update Copyright YearsSfan52013-02-24
* Change Minetest-c55 to MinetestPilzAdam2013-02-24
* Full protocol 13 compatibility on server sidePerttu Ahola2012-11-29
* A bunch of fixesMirceaKitsune2012-11-25
* Update attachments at the ending of the addToScene function for parents. And ...MirceaKitsune2012-11-25
* Framework for the attachment system, new object property which allows changin...MirceaKitsune2012-11-25
* Get the new animation framework properly workingMirceaKitsune2012-11-25
* Add dtime_s to entity activationPerttu Ahola2012-09-09
* Optimize headersPerttu Ahola2012-06-17
* Properly and efficiently use split utility headersPerttu Ahola2012-06-17
* Switch the license to be LGPLv2/later, with small parts still remaining as GP...Perttu Ahola2012-06-05
* ObjectRef:set_armor_groups() and ObjectRef:set_properties() - works on player...Perttu Ahola2012-03-30
* Add GenericCAO and player armor groups, but don't use them yetPerttu Ahola2012-03-29
* added PlayerSAO and RemotePlayer, removed ServerRemotePlayerKahrl2012-03-29
* Entity damage system WIP; Remove C++ mobsPerttu Ahola2012-03-10
* Fix some warnings on some compilersGiuseppe Bilotta2012-02-02
* The huge item definition and item namespace unification patch (itemdef), see ...Kahrl2012-01-12
* Make players be again visible from a long distancePerttu Ahola2011-12-02
* Players are more like objects + related stuffPerttu Ahola2011-12-01
* Player-is-SAO WIPPerttu Ahola2011-12-01
* Add ServerActiveObject::removingFromEnvironment()Perttu Ahola2011-12-01
* CraftItem rework and Lua interfaceKahrl2011-11-29
* Create framework for getting rid of global definitions of node/tool/item/what...Perttu Ahola2011-11-29
* Scripting WIP: dynamic object stuffPerttu Ahola2011-11-29
* ServerRemotePlayer implements ServerActiveObjectPerttu Ahola2011-11-29
* Scripting WIPPerttu Ahola2011-11-29
* Scripting WIPPerttu Ahola2011-11-29
* Scripting WIPPerttu Ahola2011-11-29
* Scripting WIPPerttu Ahola2011-11-29
* Add peaceful / not peaceful distinction in mobs and the only_peaceful_mobs se...Perttu Ahola2011-10-16
* Improve mobv2Perttu Ahola2011-10-15
* Rats are now eatable. Also made their selection box move smoothly.Perttu Ahola2011-07-30
* reorganized a lot of stuff and modified mapgen and objects slightly while doi...Perttu Ahola2011-06-26
* Preliminary "active block" stuff + set up test code to grow grass.Perttu Ahola2011-05-22
ref='#n459'>459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "particles.h"
#include <cmath>
#include "client.h"
#include "collision.h"
#include "client/clientevent.h"
#include "client/renderingengine.h"
#include "util/numeric.h"
#include "light.h"
#include "environment.h"
#include "clientmap.h"
#include "mapnode.h"
#include "nodedef.h"
#include "client.h"
#include "settings.h"

/*
	Utility
*/

v3f random_v3f(v3f min, v3f max)
{
	return v3f( rand()/(float)RAND_MAX*(max.X-min.X)+min.X,
			rand()/(float)RAND_MAX*(max.Y-min.Y)+min.Y,
			rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z);
}

Particle::Particle(
	IGameDef *gamedef,
	LocalPlayer *player,
	ClientEnvironment *env,
	v3f pos,
	v3f velocity,
	v3f acceleration,
	float expirationtime,
	float size,
	bool collisiondetection,
	bool collision_removal,
	bool vertical,
	video::ITexture *texture,
	v2f texpos,
	v2f texsize,
	const struct TileAnimationParams &anim,
	u8 glow,
	video::SColor color
):
	scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(),
		RenderingEngine::get_scene_manager())
{
	// Misc
	m_gamedef = gamedef;
	m_env = env;

	// Texture
	m_material.setFlag(video::EMF_LIGHTING, false);
	m_material.setFlag(video::EMF_BACK_FACE_CULLING, false);
	m_material.setFlag(video::EMF_BILINEAR_FILTER, false);
	m_material.setFlag(video::EMF_FOG_ENABLE, true);
	m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
	m_material.setTexture(0, texture);
	m_texpos = texpos;
	m_texsize = texsize;
	m_animation = anim;

	// Color
	m_base_color = color;
	m_color = color;

	// Particle related
	m_pos = pos;
	m_velocity = velocity;
	m_acceleration = acceleration;
	m_expiration = expirationtime;
	m_player = player;
	m_size = size;
	m_collisiondetection = collisiondetection;
	m_collision_removal = collision_removal;
	m_vertical = vertical;
	m_glow = glow;

	// Irrlicht stuff
	m_collisionbox = aabb3f
			(-size/2,-size/2,-size/2,size/2,size/2,size/2);
	this->setAutomaticCulling(scene::EAC_OFF);

	// Init lighting
	updateLight();

	// Init model
	updateVertices();
}

void Particle::OnRegisterSceneNode()
{
	if (IsVisible)
		SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);

	ISceneNode::OnRegisterSceneNode();
}

void Particle::render()
{
	video::IVideoDriver* driver = SceneManager->getVideoDriver();
	driver->setMaterial(m_material);
	driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);

	u16 indices[] = {0,1,2, 2,3,0};
	driver->drawVertexPrimitiveList(m_vertices, 4,
			indices, 2, video::EVT_STANDARD,
			scene::EPT_TRIANGLES, video::EIT_16BIT);
}

void Particle::step(float dtime)
{
	m_time += dtime;
	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;
	}
	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
	updateLight();

	// Update model
	updateVertices();
}

void Particle::updateLight()
{
	u8 light = 0;
	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);

	u8 m_light = decode_light(light + m_glow);
	m_color.set(255,
		m_light * m_base_color.getRed() / 255,
		m_light * m_base_color.getGreen() / 255,
		m_light * m_base_color.getBlue() / 255);
}

void Particle::updateVertices()
{
	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, m_color, tx0, ty1);
	m_vertices[1] = video::S3DVertex(m_size / 2, -m_size / 2,
		0, 0, 0, 0, m_color, tx1, ty1);
	m_vertices[2] = video::S3DVertex(m_size / 2, m_size / 2,
		0, 0, 0, 0, m_color, tx1, ty0);
	m_vertices[3] = video::S3DVertex(-m_size / 2, m_size / 2,
		0, 0, 0, 0, m_color, tx0, ty0);

	v3s16 camera_offset = m_env->getCameraOffset();
	for (video::S3DVertex &vertex : m_vertices) {
		if (m_vertical) {
			v3f ppos = m_player->getPosition()/BS;
			vertex.Pos.rotateXZBy(std::atan2(ppos.Z - m_pos.Z, ppos.X - m_pos.X) /
				core::DEGTORAD + 90);
		} else {
			vertex.Pos.rotateYZBy(m_player->getPitch());
			vertex.Pos.rotateXZBy(m_player->getYaw());
		}
		m_box.addInternalPoint(vertex.Pos);
		vertex.Pos += m_pos*BS - intToFloat(camera_offset, BS);
	}
}

/*
	ParticleSpawner
*/

ParticleSpawner::ParticleSpawner(IGameDef *gamedef, LocalPlayer *player,
	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, 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_player = player;
	m_amount = amount;
	m_spawntime = time;
	m_minpos = minpos;
	m_maxpos = maxpos;
	m_minvel = minvel;
	m_maxvel = maxvel;
	m_minacc = minacc;
	m_maxacc = maxacc;
	m_minexptime = minexptime;
	m_maxexptime = maxexptime;
	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_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);
	}
}

void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius,
	bool is_attached, const v3f &attached_pos, float attached_yaw)
{
	v3f ppos = m_player->getPosition() / BS;
	v3f pos = random_v3f(m_minpos, m_maxpos);

	// Need to apply this first or the following check
	// will be wrong for attached spawners
	if (is_attached) {
		pos.rotateXZBy(attached_yaw);
		pos += attached_pos;
	}

	if (pos.getDistanceFrom(ppos) > radius)
		return;

	v3f vel = random_v3f(m_minvel, m_maxvel);
	v3f acc = random_v3f(m_minacc, m_maxacc);

	if (is_attached) {
		// Apply attachment yaw
		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;

	m_particlemanager->addParticle(new Particle(
		m_gamedef,
		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
	));
}

void ParticleSpawner::step(float dtime, ClientEnvironment* env)
{
	m_time += dtime;

	static thread_local const float radius =
			g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE;

	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();
				i != m_spawntimes.end();) {
			if ((*i) <= m_time && m_amount > 0) {
				m_amount--;

				// Pretend to, but don't actually spawn a particle if it is
				// attached to an unloaded object or distant from player.
				if (!unloaded)
					spawnParticle(env, radius, is_attached, attached_pos, attached_yaw);

				i = m_spawntimes.erase(i);
			} else {
				++i;
			}
		}
	} 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)
				spawnParticle(env, radius, is_attached, attached_pos, attached_yaw);
		}
	}
}


ParticleManager::ParticleManager(ClientEnvironment* env) :
	m_env(env)
{}

ParticleManager::~ParticleManager()
{
	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;
			m_particle_spawners.erase(i++);
		}
		else
		{
			i->second->step(dtime, m_env);
			++i;
		}
	}
}

void ParticleManager::stepParticles (float dtime)
{
	MutexAutoLock lock(m_particle_list_lock);
	for(std::vector<Particle*>::iterator i = m_particles.begin();
			i != m_particles.end();)
	{
		if ((*i)->get_expired())
		{
			(*i)->remove();
			delete *i;
			i = m_particles.erase(i);
		}
		else
		{
			(*i)->step(dtime);
			++i;
		}
	}
}

void ParticleManager::clearAll ()
{
	MutexAutoLock lock(m_spawner_list_lock);
	MutexAutoLock lock2(m_particle_list_lock);
	for(std::map<u32, ParticleSpawner*>::iterator i =
			m_particle_spawners.begin();
			i != m_particle_spawners.end();)
	{
		delete i->second;
		m_particle_spawners.erase(i++);
	}

	for(std::vector<Particle*>::iterator i =
			m_particles.begin();
			i != m_particles.end();)
	{
		(*i)->remove();
		delete *i;
		i = m_particles.erase(i);
	}
}

void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
	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, 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, 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,
	LocalPlayer *player, v3s16 pos, const MapNode &n, const ContentFeatures &f)
{
	// No particles for "airlike" nodes
	if (f.drawtype == NDT_AIRLIKE)
		return;

	// set the amount of particles here
	for (u16 j = 0; j < 32; j++) {
		addNodeParticle(gamedef, player, pos, n, f);
	}
}

void ParticleManager::addNodeParticle(IGameDef* gamedef,
	LocalPlayer *player, v3s16 pos, const MapNode &n, const ContentFeatures &f)
{
	// No particles for "airlike" nodes
	if (f.drawtype == NDT_AIRLIKE)
		return;

	// Texture
	u8 texid = myrand_range(0, 5);
	const TileLayer &tile = f.tiles[texid].layers[0];
	video::ITexture *texture;
	struct TileAnimationParams anim;
	anim.type = TAT_NONE;

	// Only use first frame of animated texture
	if (tile.material_flags & MATERIAL_FLAG_ANIMATION)
		texture = (*tile.frames)[0].texture;
	else
		texture = tile.texture;

	float size = rand() % 64 / 512.;
	float visual_size = BS * size;
	if (tile.scale)
		size /= tile.scale;
	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
	);

	video::SColor color;
	if (tile.has_color)
		color = tile.color;
	else
		n.getColor(f, &color);

	Particle* toadd = new Particle(
		gamedef,
		player,
		m_env,
		particlepos,
		velocity,
		acceleration,
		rand() % 100 / 100., // expiration time
		visual_size,
		true,
		false,
		false,
		texture,
		texpos,
		texsize,
		anim,
		0,
		color);

	addParticle(toadd);
}

void ParticleManager::addParticle(Particle* toadd)
{
	MutexAutoLock lock(m_particle_list_lock);
	m_particles.push_back(toadd);
}