00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <algorithm>
00023 #include <cmath>
00024
00025 #include <guichan/color.hpp>
00026
00027 #include "animationparticle.h"
00028 #include "configuration.h"
00029 #include "imageparticle.h"
00030 #include "log.h"
00031 #include "map.h"
00032 #include "particle.h"
00033 #include "particleemitter.h"
00034 #include "textparticle.h"
00035
00036 #include "resources/resourcemanager.h"
00037
00038 #include "utils/dtor.h"
00039 #include "utils/mathutils.h"
00040 #include "utils/xml.h"
00041
00042 #define SIN45 0.707106781f
00043
00044 class Graphics;
00045 class Image;
00046
00047 int Particle::particleCount = 0;
00048 int Particle::maxCount = 0;
00049 int Particle::fastPhysics = 0;
00050 int Particle::emitterSkip = 1;
00051 const float Particle::PARTICLE_SKY = 800.0f;
00052
00053 Particle::Particle(Map *map):
00054 mAlive(true),
00055 mLifetimeLeft(-1),
00056 mLifetimePast(0),
00057 mFadeOut(0),
00058 mFadeIn(0),
00059 mAlpha(1.0f),
00060 mAutoDelete(true),
00061 mMap(map),
00062 mGravity(0.0f),
00063 mRandomness(0),
00064 mBounce(0.0f),
00065 mFollow(false),
00066 mTarget(NULL),
00067 mAcceleration(0.0f),
00068 mInvDieDistance(-1.0f),
00069 mMomentum(1.0f)
00070 {
00071 Particle::particleCount++;
00072 if (mMap)
00073 setSpriteIterator(mMap->addSprite(this));
00074 }
00075
00076 Particle::~Particle()
00077 {
00078
00079 if (mMap)
00080 mMap->removeSprite(mSpriteIterator);
00081
00082 clear();
00083 Particle::particleCount--;
00084 }
00085
00086 void Particle::setupEngine()
00087 {
00088 Particle::maxCount = (int)config.getValue("particleMaxCount", 3000);
00089 Particle::fastPhysics = (int)config.getValue("particleFastPhysics", 0);
00090 Particle::emitterSkip = (int)config.getValue("particleEmitterSkip", 1) + 1;
00091 disableAutoDelete();
00092 logger->log("Particle engine set up");
00093 }
00094
00095 void Particle::draw(Graphics *, int, int) const
00096 {
00097 }
00098
00099 bool Particle::update()
00100 {
00101 if (!mMap)
00102 return false;
00103
00104 if (mLifetimeLeft == 0)
00105 mAlive = false;
00106
00107 Vector oldPos = mPos;
00108
00109 if (mAlive)
00110 {
00111
00112 if (mMomentum != 1.0f)
00113 {
00114 mVelocity *= mMomentum;
00115 }
00116
00117 if (mTarget && mAcceleration != 0.0f)
00118 {
00119 Vector dist = mPos - mTarget->getPosition();
00120 dist.x *= SIN45;
00121 float invHypotenuse;
00122
00123 switch (Particle::fastPhysics)
00124 {
00125 case 1:
00126 invHypotenuse = fastInvSqrt(
00127 dist.x * dist.x + dist.y * dist.y + dist.z * dist.z);
00128 break;
00129 case 2:
00130 invHypotenuse = 2.0f /
00131 fabs(dist.x) + fabs(dist.y) + fabs(dist.z);
00132 break;
00133 default:
00134 invHypotenuse = 1.0f / sqrt(
00135 dist.x * dist.x + dist.y * dist.y + dist.z * dist.z);
00136 break;
00137 }
00138
00139 if (invHypotenuse)
00140 {
00141 if (mInvDieDistance > 0.0f && invHypotenuse > mInvDieDistance)
00142 {
00143 mAlive = false;
00144 }
00145 float accFactor = invHypotenuse * mAcceleration;
00146 mVelocity -= dist * accFactor;
00147 }
00148 }
00149
00150 if (mRandomness > 0)
00151 {
00152 mVelocity.x += (rand()%mRandomness - rand()%mRandomness) / 1000.0f;
00153 mVelocity.y += (rand()%mRandomness - rand()%mRandomness) / 1000.0f;
00154 mVelocity.z += (rand()%mRandomness - rand()%mRandomness) / 1000.0f;
00155 }
00156
00157 mVelocity.z -= mGravity;
00158
00159
00160 mPos.x += mVelocity.x;
00161 mPos.y += mVelocity.y * SIN45;
00162 mPos.z += mVelocity.z * SIN45;
00163
00164
00165 if (mLifetimeLeft > 0)
00166 {
00167 mLifetimeLeft--;
00168 }
00169 mLifetimePast++;
00170
00171 if (mPos.z > PARTICLE_SKY || mPos.z < 0.0f)
00172 {
00173 if (mBounce > 0.0f)
00174 {
00175 mPos.z *= -mBounce;
00176 mVelocity *= mBounce;
00177 mVelocity.z = -mVelocity.z;
00178 }
00179 else
00180 {
00181 mAlive = false;
00182 }
00183 }
00184
00185
00186 if ((mLifetimePast-1)%Particle::emitterSkip == 0)
00187 {
00188 for (EmitterIterator e = mChildEmitters.begin();
00189 e != mChildEmitters.end(); e++)
00190 {
00191 Particles newParticles = (*e)->createParticles(mLifetimePast);
00192 for (ParticleIterator p = newParticles.begin();
00193 p != newParticles.end(); p++)
00194 {
00195 (*p)->moveBy(mPos);
00196 mChildParticles.push_back (*p);
00197 }
00198 }
00199 }
00200 }
00201
00202 Vector change = mPos - oldPos;
00203
00204
00205
00206 for (ParticleIterator p = mChildParticles.begin();
00207 p != mChildParticles.end();)
00208 {
00209
00210 if ((*p)->doesFollow())
00211 {
00212 (*p)->moveBy(change);
00213 }
00214
00215 if ((*p)->update())
00216 {
00217 p++;
00218 }
00219 else
00220 {
00221 delete (*p);
00222 p = mChildParticles.erase(p);
00223 }
00224 }
00225
00226 if (!mAlive && mChildParticles.empty() && mAutoDelete)
00227 {
00228 return false;
00229 }
00230
00231 return true;
00232 }
00233
00234 void Particle::moveBy(const Vector &change)
00235 {
00236 mPos += change;
00237 for (ParticleIterator p = mChildParticles.begin();
00238 p != mChildParticles.end(); p++)
00239 {
00240 if ((*p)->doesFollow())
00241 {
00242 (*p)->moveBy(change);
00243 }
00244 }
00245 }
00246
00247 void Particle::moveTo(float x, float y)
00248 {
00249 moveTo(Vector(x, y, mPos.z));
00250 }
00251
00252 Particle *Particle::addEffect(const std::string &particleEffectFile,
00253 int pixelX, int pixelY, int rotation)
00254 {
00255 Particle *newParticle = NULL;
00256
00257 XML::Document doc(particleEffectFile);
00258 xmlNodePtr rootNode = doc.rootNode();
00259
00260 if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "effect"))
00261 {
00262 logger->log("Error loading particle: %s", particleEffectFile.c_str());
00263 return NULL;
00264 }
00265
00266 ResourceManager *resman = ResourceManager::getInstance();
00267
00268
00269 for_each_xml_child_node(effectChildNode, rootNode)
00270 {
00271
00272 if (!xmlStrEqual(effectChildNode->name, BAD_CAST "particle"))
00273 continue;
00274
00275
00276 xmlNodePtr node;
00277
00278
00279 if ((node = XML::findFirstChildByName(effectChildNode, "animation")))
00280 {
00281 newParticle = new AnimationParticle(mMap, node);
00282 }
00283
00284 else if ((node = XML::findFirstChildByName(effectChildNode, "image")))
00285 {
00286 Image *img= resman->getImage((const char*)
00287 node->xmlChildrenNode->content);
00288
00289 newParticle = new ImageParticle(mMap, img);
00290 }
00291
00292 else
00293 {
00294 newParticle = new Particle(mMap);
00295 }
00296
00297
00298 float offsetX = XML::getFloatProperty(effectChildNode, "position-x", 0);
00299 float offsetY = XML::getFloatProperty(effectChildNode, "position-y", 0);
00300 float offsetZ = XML::getFloatProperty(effectChildNode, "position-z", 0);
00301 Vector position (mPos.x + (float)pixelX + offsetX,
00302 mPos.y + (float)pixelY + offsetY,
00303 mPos.z + offsetZ);
00304 newParticle->moveTo(position);
00305
00306 int lifetime = XML::getProperty(effectChildNode, "lifetime", -1);
00307 newParticle->setLifetime(lifetime);
00308
00309
00310 for_each_xml_child_node(emitterNode, effectChildNode)
00311 {
00312 if (!xmlStrEqual(emitterNode->name, BAD_CAST "emitter"))
00313 continue;
00314
00315 ParticleEmitter *newEmitter;
00316 newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap,
00317 rotation);
00318 newParticle->addEmitter(newEmitter);
00319 }
00320
00321 mChildParticles.push_back(newParticle);
00322 }
00323
00324 return newParticle;
00325 }
00326
00327 Particle *Particle::addTextSplashEffect(const std::string &text, int x, int y,
00328 const gcn::Color *color,
00329 gcn::Font *font, bool outline)
00330 {
00331 Particle *newParticle = new TextParticle(mMap, text, color, font, outline);
00332 newParticle->moveTo(x, y);
00333 newParticle->setVelocity(((rand() % 100) - 50) / 200.0f,
00334 ((rand() % 100) - 50) / 200.0f,
00335 ((rand() % 100) / 200.0f) + 4.0f);
00336 newParticle->setGravity(0.1f);
00337 newParticle->setBounce(0.5f);
00338 newParticle->setLifetime(200);
00339 newParticle->setFadeOut(100);
00340
00341 mChildParticles.push_back(newParticle);
00342
00343 return newParticle;
00344 }
00345
00346 Particle *Particle::addTextRiseFadeOutEffect(const std::string &text,
00347 int x, int y,
00348 const gcn::Color *color,
00349 gcn::Font *font, bool outline)
00350 {
00351 Particle *newParticle = new TextParticle(mMap, text, color, font, outline);
00352 newParticle->moveTo(x, y);
00353 newParticle->setVelocity(0.0f, 0.0f, 0.5f);
00354 newParticle->setGravity(0.0015f);
00355 newParticle->setLifetime(300);
00356 newParticle->setFadeOut(50);
00357 newParticle->setFadeIn(200);
00358
00359 mChildParticles.push_back(newParticle);
00360
00361 return newParticle;
00362 }
00363
00364 void Particle::setMap(Map *map)
00365 {
00366 mMap = map;
00367 if (mMap)
00368 setSpriteIterator(mMap->addSprite(this));
00369 }
00370
00371 void Particle::clear()
00372 {
00373 delete_all(mChildEmitters);
00374 mChildEmitters.clear();
00375
00376 delete_all(mChildParticles);
00377 mChildParticles.clear();
00378 }