From 1b21442b5eb6dcc41a585715d07c73ed3ad59a1b Mon Sep 17 00:00:00 2001 From: Philipp Sehmisch Date: Sun, 17 Oct 2010 17:15:21 +0200 Subject: Added death effects to particle engine. Every particle can now have a death effect. This is an effect which is created when the particle dies. Which death reasons (timeout, touching floor, touching sky, reaching target or deleted by external call) trigger the effect can also be specified. This is useful for exploding projectiles and many other effects. Reviewed-by: Bertram --- src/being.cpp | 9 +++--- src/imageparticle.cpp | 2 +- src/particle.cpp | 74 ++++++++++++++++++++++++++++++++++++++----------- src/particle.h | 30 +++++++++++++++----- src/particleemitter.cpp | 29 +++++++++++++++++++ src/particleemitter.h | 8 +++++- src/textparticle.cpp | 2 +- 7 files changed, 124 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/being.cpp b/src/being.cpp index 138e5c5a..20225f47 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -530,19 +530,20 @@ void Being::fireMissile(Being *victim, const std::string &particle) if (!victim || particle.empty()) return; - Particle *target = particleEngine->createChild(); - Particle *missile = target->addEffect(particle, getPixelX(), getPixelY()); + Particle *missile = particleEngine->addEffect(particle, getPixelX(), getPixelY()); if (missile) { - target->setLifetime(2000); + Particle *target = particleEngine->createChild(); target->moveBy(Vector(0.0f, 0.0f, 32.0f)); + target->setLifetime(1000); victim->controlParticle(target); missile->setDestination(target, 7, 0); missile->setDieDistance(8); missile->setLifetime(900); } + } void Being::setAction(Action action, int attackType) @@ -582,7 +583,7 @@ void Being::setAction(Action action, int attackType) { switch (mSpriteDirection) { - case DIRECTION_DOWN: rotation = 0; break; + case DIRECTION_DOWN: rotation = 0; break; case DIRECTION_LEFT: rotation = 90; break; case DIRECTION_UP: rotation = 180; break; case DIRECTION_RIGHT: rotation = 270; break; diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp index 76c05d64..2900239f 100644 --- a/src/imageparticle.cpp +++ b/src/imageparticle.cpp @@ -41,7 +41,7 @@ ImageParticle::~ImageParticle() bool ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const { - if (!mAlive || !mImage) + if (!isAlive() || !mImage) return false; int screenX = (int) mPos.x + offsetX - mImage->getWidth() / 2; diff --git a/src/particle.cpp b/src/particle.cpp index e3c45a9f..cb2faa77 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -56,14 +56,15 @@ bool Particle::enabled = true; const float Particle::PARTICLE_SKY = 800.0f; Particle::Particle(Map *map): - mAlive(true), + mAlpha(1.0f), mLifetimeLeft(-1), mLifetimePast(0), mFadeOut(0), mFadeIn(0), - mAlpha(1.0f), + mAlive(ALIVE), mAutoDelete(true), mAllowSizeAdjust(false), + mDeathEffectConditions(0x00), mGravity(0.0f), mRandomness(0), mBounce(0.0f), @@ -81,6 +82,7 @@ Particle::~Particle() { // Delete child emitters and child particles clear(); + //update particle count Particle::particleCount--; } @@ -104,12 +106,12 @@ bool Particle::update() if (!mMap) return false; - if (mLifetimeLeft == 0) - mAlive = false; + if (mLifetimeLeft == 0 && mAlive == ALIVE) + mAlive = DEAD_TIMEOUT; Vector oldPos = mPos; - if (mAlive) + if (mAlive == ALIVE) { //calculate particle movement if (mMomentum != 1.0f) @@ -143,7 +145,7 @@ bool Particle::update() { if (mInvDieDistance > 0.0f && invHypotenuse > mInvDieDistance) { - mAlive = false; + mAlive = DEAD_IMPACT; } float accFactor = invHypotenuse * mAcceleration; mVelocity -= dist * accFactor; @@ -171,7 +173,7 @@ bool Particle::update() } mLifetimePast++; - if (mPos.z > PARTICLE_SKY || mPos.z < 0.0f) + if (mPos.z < 0.0f) { if (mBounce > 0.0f) { @@ -181,8 +183,11 @@ bool Particle::update() } else { - mAlive = false; + mAlive = DEAD_FLOOR; } + } else if (mPos.z > PARTICLE_SKY) + { + mAlive = DEAD_SKY; } // Update child emitters @@ -202,6 +207,17 @@ bool Particle::update() } } + // create death effect when the particle died + if (mAlive != ALIVE && mAlive != DEAD_LONG_AGO) + { + if ((mAlive & mDeathEffectConditions) > 0x00 && !mDeathEffect.empty()) + { + Particle* deathEffect = particleEngine->addEffect(mDeathEffect, 0, 0); + deathEffect->moveBy(mPos); + } + mAlive = DEAD_LONG_AGO; + } + Vector change = mPos - oldPos; // Update child particles @@ -225,7 +241,7 @@ bool Particle::update() p = mChildParticles.erase(p); } } - if (!mAlive && mChildParticles.empty() && mAutoDelete) + if (mAlive != ALIVE && mChildParticles.empty() && mAutoDelete) { return false; } @@ -325,13 +341,39 @@ Particle *Particle::addEffect(const std::string &particleEffectFile, // Look for additional emitters for this particle for_each_xml_child_node(emitterNode, effectChildNode) { - if (!xmlStrEqual(emitterNode->name, BAD_CAST "emitter")) - continue; - - ParticleEmitter *newEmitter; - newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap, - rotation); - newParticle->addEmitter(newEmitter); + if (xmlStrEqual(emitterNode->name, BAD_CAST "emitter")) + { + ParticleEmitter *newEmitter; + newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap, + rotation); + newParticle->addEmitter(newEmitter); + } + else if (xmlStrEqual(emitterNode->name, BAD_CAST "deatheffect")) + { + std::string deathEffect = (const char*)emitterNode->xmlChildrenNode->content; + char deathEffectConditions = 0x00; + if (XML::getBoolProperty(emitterNode, "on-floor", true)) + { + deathEffectConditions += Particle::DEAD_FLOOR; + } + if (XML::getBoolProperty(emitterNode, "on-sky", true)) + { + deathEffectConditions += Particle::DEAD_SKY; + } + if (XML::getBoolProperty(emitterNode, "on-other", false)) + { + deathEffectConditions += Particle::DEAD_OTHER; + } + if (XML::getBoolProperty(emitterNode, "on-impact", true)) + { + deathEffectConditions += Particle::DEAD_IMPACT; + } + if (XML::getBoolProperty(emitterNode, "on-timeout", true)) + { + deathEffectConditions += Particle::DEAD_TIMEOUT; + } + newParticle->setDeathEffect(deathEffect, deathEffectConditions); + } } mChildParticles.push_back(newParticle); diff --git a/src/particle.h b/src/particle.h index 2be169c1..8aa9e5f2 100644 --- a/src/particle.h +++ b/src/particle.h @@ -44,6 +44,16 @@ typedef Emitters::iterator EmitterIterator; class Particle : public Actor { public: + enum AliveStatus + { + ALIVE = 0, + DEAD_TIMEOUT = 1, + DEAD_FLOOR = 2, + DEAD_SKY = 4, + DEAD_IMPACT = 8, + DEAD_OTHER = 16, + DEAD_LONG_AGO = 128 + }; static const float PARTICLE_SKY; /**< Maximum Z position of particles */ static int fastPhysics; /**< Mode of squareroot calculation */ static int particleCount; /**< Current number of particles */ @@ -221,20 +231,20 @@ class Particle : public Actor void setAllowSizeAdjust(bool adjust) { mAllowSizeAdjust = adjust; } - bool isAlive() - { return mAlive; } + bool isAlive() const + { return mAlive == ALIVE; } /** * Determines whether the particle and its children are all dead */ - bool isExtinct() + bool isExtinct() const { return !isAlive() && mChildParticles.empty(); } /** * Manually marks the particle for deletion. */ void kill() - { mAlive = false; mAutoDelete = true; } + { mAlive = DEAD_OTHER; mAutoDelete = true; } /** * After calling this function the particle will only request @@ -252,22 +262,28 @@ class Particle : public Actor virtual void setAlpha(float alpha) {} + virtual void setDeathEffect(const std::string &effectFile, char conditions) + { mDeathEffect = effectFile; mDeathEffectConditions = conditions; } + protected: - bool mAlive; /**< Is the particle supposed to be drawn and updated?*/ + float mAlpha; /**< Opacity of the graphical representation of the particle */ int mLifetimeLeft; /**< Lifetime left in game ticks*/ int mLifetimePast; /**< Age of the particle in game ticks*/ int mFadeOut; /**< Lifetime in game ticks left where fading out begins*/ int mFadeIn; /**< Age in game ticks where fading in is finished*/ - float mAlpha; /**< Opacity of the graphical representation of the particle */ + Vector mVelocity; /**< Speed in pixels per game-tick. */ + private: + AliveStatus mAlive; /**< Is the particle supposed to be drawn and updated?*/ // generic properties bool mAutoDelete; /**< May the particle request its deletion by the parent particle? */ Emitters mChildEmitters; /**< List of child emitters. */ Particles mChildParticles; /**< List of particles controlled by this particle */ bool mAllowSizeAdjust; /**< Can the effect size be adjusted by the object props in the map file? */ + std::string mDeathEffect; /**< Particle effect file to be spawned when the particle dies */ + char mDeathEffectConditions;/**< Bitfield of death conditions which trigger spawning of the death particle */ // dynamic particle - Vector mVelocity; /**< Speed in pixels per game-tick. */ float mGravity; /**< Downward acceleration in pixels per game-tick. */ int mRandomness; /**< Ammount of random vector change */ float mBounce; /**< How much the particle bounces off when hitting the ground */ diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp index dc9931a5..b9855c10 100644 --- a/src/particleemitter.cpp +++ b/src/particleemitter.cpp @@ -320,6 +320,30 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * mParticleAnimation.addTerminator(); } } // for frameNode + } else if (xmlStrEqual(propertyNode->name, BAD_CAST "deatheffect")) + { + mDeathEffect = (const char*)propertyNode->xmlChildrenNode->content; + mDeathEffectConditions = 0x00; + if (XML::getBoolProperty(propertyNode, "on-floor", true)) + { + mDeathEffectConditions += Particle::DEAD_FLOOR; + } + if (XML::getBoolProperty(propertyNode, "on-sky", true)) + { + mDeathEffectConditions += Particle::DEAD_SKY; + } + if (XML::getBoolProperty(propertyNode, "on-other", false)) + { + mDeathEffectConditions += Particle::DEAD_OTHER; + } + if (XML::getBoolProperty(propertyNode, "on-impact", true)) + { + mDeathEffectConditions += Particle::DEAD_IMPACT; + } + if (XML::getBoolProperty(propertyNode, "on-timeout", true)) + { + mDeathEffectConditions += Particle::DEAD_TIMEOUT; + } } } } @@ -469,6 +493,11 @@ std::list ParticleEmitter::createParticles(int tick) newParticle->addEmitter(new ParticleEmitter(*i)); } + if (!mDeathEffect.empty()) + { + newParticle->setDeathEffect(mDeathEffect, mDeathEffectConditions); + } + newParticles.push_back(newParticle); } diff --git a/src/particleemitter.h b/src/particleemitter.h index cc073c1c..9baaa73c 100644 --- a/src/particleemitter.h +++ b/src/particleemitter.h @@ -127,13 +127,19 @@ class ParticleEmitter int mOutputPauseLeft; /* - * Graphical representation of the particle + * Graphical representation of the particles */ Image *mParticleImage; /**< Particle image, if used */ Animation mParticleAnimation; /**< Filename of particle animation file */ Animation mParticleRotation; /**< Filename of particle rotation file */ ParticleEmitterProp mParticleAlpha; /**< Opacity of the graphical representation of the particles */ + /* + * Death effect of the particles + */ + std::string mDeathEffect; + char mDeathEffectConditions; + /** List of emitters the spawned particles are equipped with */ std::list mParticleChildEmitters; }; diff --git a/src/textparticle.cpp b/src/textparticle.cpp index 827343fa..e8d99dca 100644 --- a/src/textparticle.cpp +++ b/src/textparticle.cpp @@ -38,7 +38,7 @@ TextParticle::TextParticle(Map *map, const std::string &text, bool TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const { - if (!mAlive) + if (!isAlive()) return false; int screenX = (int) mPos.x + offsetX; -- cgit v1.2.3-70-g09d2