Mana
Loading...
Searching...
No Matches
particle.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2006-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "particle.h"
23
24#include "animationparticle.h"
25#include "configuration.h"
26#include "imageparticle.h"
27#include "log.h"
28#include "map.h"
29#include "particleemitter.h"
30#include "rotationalparticle.h"
31#include "textparticle.h"
32
33#include "resources/dye.h"
34#include "resources/image.h"
36
37#include "utils/dtor.h"
38#include "utils/mathutils.h"
39#include "utils/xml.h"
40
41#include <guichan/color.hpp>
42
43#include <cmath>
44
45#define SIN45 0.707106781f
46
47class Graphics;
48class Image;
49
51int Particle::maxCount = 0;
54bool Particle::enabled = true;
55const float Particle::PARTICLE_SKY = 800.0f;
56
62
64{
65 // Delete child emitters and child particles
66 clear();
67 //update particle count
69}
70
79
80bool Particle::draw(Graphics *, int, int) const
81{
82 return false;
83}
84
86{
87 if (!mMap)
88 return false;
89
90 if (mLifetimeLeft == 0 && mAlive == ALIVE)
92
93 Vector oldPos = mPos;
94
95 if (mAlive == ALIVE)
96 {
97 //calculate particle movement
98 if (mMomentum != 1.0f)
99 {
101 }
102
103 if (mTarget && mAcceleration != 0.0f)
104 {
105 Vector dist = mPos - mTarget->getPosition();
106 dist.x *= SIN45;
107 float invHypotenuse;
108
109 switch (Particle::fastPhysics)
110 {
111 case 1:
112 invHypotenuse = fastInvSqrt(
113 dist.x * dist.x + dist.y * dist.y + dist.z * dist.z);
114 break;
115 case 2:
116 invHypotenuse = 2.0f /
117 fabs(dist.x) + fabs(dist.y) + fabs(dist.z);
118 break;
119 default:
120 invHypotenuse = 1.0f / sqrt(
121 dist.x * dist.x + dist.y * dist.y + dist.z * dist.z);
122 break;
123 }
124
125 if (invHypotenuse)
126 {
127 if (mInvDieDistance > 0.0f && invHypotenuse > mInvDieDistance)
128 {
130 }
131 float accFactor = invHypotenuse * mAcceleration;
132 mVelocity -= dist * accFactor;
133 }
134 }
135
136 if (mRandomness > 0)
137 {
138 const int rand2 = mRandomness * 2;
139 mVelocity.x += (rand() % rand2 - mRandomness) / 1000.0f;
140 mVelocity.y += (rand() % rand2 - mRandomness) / 1000.0f;
141 mVelocity.z += (rand() % rand2 - mRandomness) / 1000.0f;
142 }
143
145
146 // Update position
147 mPos.x += mVelocity.x;
148 mPos.y += mVelocity.y * SIN45;
149 mPos.z += mVelocity.z * SIN45;
150
151 // Update other stuff
152 if (mLifetimeLeft > 0)
153 {
155 }
157
158 if (mPos.z < 0.0f)
159 {
160 if (mBounce > 0.0f)
161 {
162 mPos.z *= -mBounce;
165 }
166 else
167 {
169 }
170 }
171 else if (mPos.z > PARTICLE_SKY)
172 {
174 }
175
176 // Update child emitters
178 {
179 for (auto &childEmitter : mChildEmitters)
180 {
181 Particles newParticles = childEmitter->createParticles(mLifetimePast);
182 for (auto &newParticle : newParticles)
183 {
184 newParticle->moveBy(mPos);
185 mChildParticles.push_back(newParticle);
186 }
187 }
188 }
189 }
190
191 // create death effect when the particle died
192 if (mAlive != ALIVE && mAlive != DEAD_LONG_AGO)
193 {
194 if ((mAlive & mDeathEffectConditions) > 0x00 && !mDeathEffect.empty())
195 {
196 Particle* deathEffect = particleEngine->addEffect(mDeathEffect, 0, 0);
197 deathEffect->moveBy(mPos);
198 }
200 }
201
202 Vector change = mPos - oldPos;
203
204 // Update child particles
205
206 for (auto p = mChildParticles.begin(); p != mChildParticles.end(); )
207 {
208 auto particle = *p;
209 //move particle with its parent if desired
210 if (particle->doesFollow())
211 {
212 particle->moveBy(change);
213 }
214 if (particle->update())
215 {
216 p++;
217 }
218 else
219 {
220 delete particle;
221 p = mChildParticles.erase(p);
222 }
223 }
224
225 return isAlive() || !mChildParticles.empty() || !mAutoDelete;
226}
227
228void Particle::moveBy(const Vector &change)
229{
230 mPos += change;
231
232 for (auto &childParticle : mChildParticles)
233 if (childParticle->doesFollow())
234 childParticle->moveBy(change);
235}
236
237void Particle::moveTo(float x, float y)
238{
239 moveTo(Vector(x, y, mPos.z));
240}
241
243{
244 auto *newParticle = new Particle(mMap);
245 mChildParticles.push_back(newParticle);
246 return newParticle;
247}
248
249Particle *Particle::addEffect(const std::string &particleEffectFile,
250 int pixelX, int pixelY, int rotation)
251{
252 Particle *newParticle = nullptr;
253
254 std::string::size_type pos = particleEffectFile.find('|');
255 std::string dyePalettes;
256 if (pos != std::string::npos)
257 dyePalettes = particleEffectFile.substr(pos + 1);
258
259 XML::Document doc(particleEffectFile.substr(0, pos));
260 XML::Node rootNode = doc.rootNode();
261
262 if (!rootNode || rootNode.name() != "effect")
263 {
264 Log::info("Error loading particle: %s", particleEffectFile.c_str());
265 return nullptr;
266 }
267
269
270 // Parse particles
271 for (auto effectChildNode : rootNode.children())
272 {
273 // We're only interested in particles
274 if (effectChildNode.name() != "particle")
275 continue;
276
277 // Determine the exact particle type
278 XML::Node node;
279
280 // Animation
281 if ((node = effectChildNode.findFirstChildByName("animation")))
282 {
283 newParticle = new AnimationParticle(mMap, node, dyePalettes);
284 }
285 // Rotational
286 else if ((node = effectChildNode.findFirstChildByName("rotation")))
287 {
288 newParticle = new RotationalParticle(mMap, node, dyePalettes);
289 }
290 // Image
291 else if ((node = effectChildNode.findFirstChildByName("image")))
292 {
293 std::string imageSrc { node.textContent() };
294 if (!imageSrc.empty() && !dyePalettes.empty())
295 Dye::instantiate(imageSrc, dyePalettes);
296
297 auto img = resman->getImage(imageSrc);
298 newParticle = new ImageParticle(mMap, img);
299 }
300 // Other
301 else
302 {
303 newParticle = new Particle(mMap);
304 }
305
306 // Read and set the basic properties of the particle
307 float offsetX = effectChildNode.getFloatProperty("position-x", 0);
308 float offsetY = effectChildNode.getFloatProperty("position-y", 0);
309 float offsetZ = effectChildNode.getFloatProperty("position-z", 0);
310 Vector position(mPos.x + (float)pixelX + offsetX,
311 mPos.y + (float)pixelY + offsetY,
312 mPos.z + offsetZ);
313 newParticle->moveTo(position);
314
315 int lifetime = effectChildNode.getProperty("lifetime", -1);
316 newParticle->setLifetime(lifetime);
317 bool resizeable = "false" != effectChildNode.getProperty("size-adjustable", "false");
318 newParticle->setAllowSizeAdjust(resizeable);
319
320 // Look for additional emitters for this particle
321 for (auto emitterNode : effectChildNode.children())
322 {
323 if (emitterNode.name() == "emitter")
324 {
325 ParticleEmitter *newEmitter;
326 newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap,
327 rotation, dyePalettes);
328 newParticle->addEmitter(newEmitter);
329 }
330 else if (emitterNode.name() == "deatheffect")
331 {
332 std::string deathEffect { emitterNode.textContent() };
333 char deathEffectConditions = 0x00;
334 if (emitterNode.getBoolProperty("on-floor", true))
335 {
336 deathEffectConditions += Particle::DEAD_FLOOR;
337 }
338 if (emitterNode.getBoolProperty("on-sky", true))
339 {
340 deathEffectConditions += Particle::DEAD_SKY;
341 }
342 if (emitterNode.getBoolProperty("on-other", false))
343 {
344 deathEffectConditions += Particle::DEAD_OTHER;
345 }
346 if (emitterNode.getBoolProperty("on-impact", true))
347 {
348 deathEffectConditions += Particle::DEAD_IMPACT;
349 }
350 if (emitterNode.getBoolProperty("on-timeout", true))
351 {
352 deathEffectConditions += Particle::DEAD_TIMEOUT;
353 }
354 newParticle->setDeathEffect(deathEffect, deathEffectConditions);
355 }
356 }
357
358 mChildParticles.push_back(newParticle);
359 }
360
361 return newParticle;
362}
363
364Particle *Particle::addTextSplashEffect(const std::string &text, int x, int y,
365 const gcn::Color *color,
366 gcn::Font *font, bool outline)
367{
368 Particle *newParticle = new TextParticle(mMap, text, color, font, outline);
369 newParticle->moveTo(x, y);
370 newParticle->setVelocity(((rand() % 100) - 50) / 200.0f, // X
371 ((rand() % 100) - 50) / 200.0f, // Y
372 ((rand() % 100) / 200.0f) + 4.0f); // Z
373 newParticle->setGravity(0.1f);
374 newParticle->setBounce(0.5f);
375 newParticle->setLifetime(200);
376 newParticle->setFadeOut(100);
377
378 mChildParticles.push_back(newParticle);
379
380 return newParticle;
381}
382
384 int x, int y,
385 const gcn::Color *color,
386 gcn::Font *font, bool outline)
387{
388 Particle *newParticle = new TextParticle(mMap, text, color, font, outline);
389 newParticle->moveTo(x, y);
390 newParticle->setVelocity(0.0f, 0.0f, 0.5f);
391 newParticle->setGravity(0.0015f);
392 newParticle->setLifetime(300);
393 newParticle->setFadeOut(50);
394 newParticle->setFadeIn(200);
395
396 mChildParticles.push_back(newParticle);
397
398 return newParticle;
399}
400
402{
403 if (!mAllowSizeAdjust)
404 return;
405
406 for (auto &childEmitter : mChildEmitters)
407 childEmitter->adjustSize(w, h);
408}
409
411{
412 float alpha = mAlpha;
413
415 alpha *= (float)mLifetimeLeft / (float)mFadeOut;
416
418 alpha *= (float)mLifetimePast / (float)mFadeIn;
419
420 return alpha;
421}
422
424{
426 mChildEmitters.clear();
427
429 mChildParticles.clear();
430}
virtual void setMap(Map *map)
Definition actor.cpp:32
Vector mPos
Position in pixels relative to map.
Definition actor.h:114
const Vector & getPosition() const
Returns the pixel position of this actor.
Definition actor.h:53
Map * mMap
Definition actor.h:113
static void instantiate(std::string &target, const std::string &palettes)
Fills the blank in a dye placeholder with some palette names.
Definition dye.cpp:252
A central point of control for graphics.
Definition graphics.h:78
A particle that uses an image for its visualization.
Defines a class for loading and storing images.
Definition image.h:45
A tile map.
Definition map.h:147
Every Particle can have one or more particle emitters that create new particles when they are updated...
A particle spawned by a ParticleEmitter.
Definition particle.h:42
Particles mChildParticles
List of particles controlled by this particle.
Definition particle.h:276
float mBounce
How much the particle bounces off when hitting the ground.
Definition particle.h:284
~Particle() override
Definition particle.cpp:63
float getCurrentAlpha() const
Calculates the current alpha transparency taking current fade status into account.
Definition particle.cpp:410
void setAllowSizeAdjust(bool adjust)
Definition particle.h:225
void setVelocity(float x, float y, float z)
Sets the current velocity in 3 dimensional space.
Definition particle.h:170
float mMomentum
How much speed the particle retains after each game tick.
Definition particle.h:291
float mGravity
Downward acceleration in pixels per game-tick.
Definition particle.h:282
virtual void setDeathEffect(const std::string &effectFile, char conditions)
Definition particle.h:255
AliveStatus mAlive
Is the particle supposed to be drawn and updated?
Definition particle.h:272
int mRandomness
Ammount of random vector change.
Definition particle.h:283
static void setupEngine()
Gives a particle the properties of an engine root particle and loads the particle-related config sett...
Definition particle.cpp:71
static const float PARTICLE_SKY
Maximum Z position of particles.
Definition particle.h:54
static int particleCount
Current number of particles.
Definition particle.h:56
static int maxCount
Maximum number of particles.
Definition particle.h:57
void moveBy(const Vector &change)
Changes the particle position relative.
Definition particle.cpp:228
void addEmitter(ParticleEmitter *emitter)
Adds an emitter to the particle.
Definition particle.h:128
int mFadeIn
Age in game ticks where fading in is finished.
Definition particle.h:268
void setFadeIn(int fadeIn)
Sets the remaining particle lifetime where the particle starts to fade out.
Definition particle.h:164
float mAcceleration
Acceleration towards the target particle in pixels per game-tick.
Definition particle.h:289
bool draw(Graphics *graphics, int offsetX, int offsetY) const override
Draws the particle image.
Definition particle.cpp:80
@ DEAD_TIMEOUT
Definition particle.h:47
@ DEAD_IMPACT
Definition particle.h:50
@ DEAD_LONG_AGO
Definition particle.h:52
@ DEAD_OTHER
Definition particle.h:51
@ DEAD_SKY
Definition particle.h:49
@ DEAD_FLOOR
Definition particle.h:48
Particle(Map *map)
Constructor.
Definition particle.cpp:57
Particle * mTarget
The particle that attracts this particle.
Definition particle.h:288
bool mAutoDelete
May the particle request its deletion by the parent particle?
Definition particle.h:274
Particle * createChild()
Creates a blank particle as a child of the current particle Useful for creating target particles.
Definition particle.cpp:242
Particle * addTextRiseFadeOutEffect(const std::string &text, int x, int y, const gcn::Color *color, gcn::Font *font, bool outline=false)
Creates a standalone text particle.
Definition particle.cpp:383
std::string mDeathEffect
Particle effect file to be spawned when the particle dies.
Definition particle.h:278
float mInvDieDistance
Distance in pixels from the target particle that causes the destruction of the particle.
Definition particle.h:290
int mLifetimePast
Age of the particle in game ticks.
Definition particle.h:266
float mAlpha
Opacity of the graphical representation of the particle.
Definition particle.h:260
void setGravity(float gravity)
Sets the downward acceleration.
Definition particle.h:176
int mLifetimeLeft
Lifetime left in game ticks.
Definition particle.h:265
static int fastPhysics
Mode of squareroot calculation.
Definition particle.h:55
virtual bool update()
Updates particle position, returns false when the particle should be deleted.
Definition particle.cpp:85
void moveTo(const Vector &pos)
Sets the position in 3 dimensional space in pixels relative to map.
Definition particle.h:134
static int emitterSkip
Duration of pause between two emitter updates in ticks.
Definition particle.h:58
void setLifetime(int lifetime)
Sets the time in game ticks until the particle is destroyed.
Definition particle.h:150
void clear()
Deletes all child particles and emitters.
Definition particle.cpp:423
bool mAllowSizeAdjust
Can the effect size be adjusted by the object props in the map file?
Definition particle.h:277
char mDeathEffectConditions
Bitfield of death conditions which trigger spawning of the death particle.
Definition particle.h:279
int mFadeOut
Lifetime in game ticks left where fading out begins.
Definition particle.h:267
Vector mVelocity
Speed in pixels per game-tick.
Definition particle.h:269
void setBounce(float bouncieness)
Sets the ammount of velocity particles retain after hitting the ground.
Definition particle.h:189
bool isAlive() const
Definition particle.h:228
Particle * addTextSplashEffect(const std::string &text, int x, int y, const gcn::Color *color, gcn::Font *font, bool outline=false)
Creates a standalone text particle.
Definition particle.cpp:364
Emitters mChildEmitters
List of child emitters.
Definition particle.h:275
void setFadeOut(int fadeOut)
Sets the age of the pixel in game ticks where the particle has faded in completely.
Definition particle.h:157
Particle * addEffect(const std::string &particleEffectFile, int pixelX, int pixelY, int rotation=0)
Creates a child particle that hosts some emitters described in the particleEffectFile.
Definition particle.cpp:249
void adjustEmitterSize(int w, int h)
Changes the size of the emitters so that the effect fills a rectangle of this size.
Definition particle.cpp:401
static bool enabled
true when non-crucial particle effects are disabled
Definition particle.h:59
A class for loading and managing resources.
static ResourceManager * getInstance()
Returns an instance of the class, creating one if it does not already exist.
ResourceRef< Image > getImage(const std::string &idPath)
Loads the Image resource found at the given identifier path.
Vector class.
Definition vector.h:33
float z
Definition vector.h:173
float y
Definition vector.h:172
float x
Definition vector.h:171
A helper class for parsing an XML document, which also cleans it up again (RAII).
Definition xml.h:190
Node rootNode() const
Returns the root node of the document (or NULL if there was a load error).
Definition xml.h:213
std::string_view name() const
Definition xml.h:46
Node findFirstChildByName(const char *name) const
Definition xml.h:176
Children children() const
Definition xml.h:97
std::string_view textContent() const
Definition xml.h:105
Config config
Global settings (config.xml)
Definition client.cpp:97
void delete_all(Container &c)
Definition dtor.h:46
float fastInvSqrt(float x)
Definition mathutils.h:33
void info(const char *log_text,...) LOG_PRINTF_ATTR
#define SIN45
Definition particle.cpp:45
Particle * particleEngine
Definition game.cpp:113
std::list< Particle * > Particles
Definition particle.h:35
bool particleEffects
int particleFastPhysics
int particleEmitterSkip
int particleMaxCount