/* Copyright (C) 2007, 2010 - Bit-Blot This file is part of Aquaria. Aquaria is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "Particles.h" #include "SimpleIStringStream.h" ParticleEffect::ParticleEffect() : RenderObject() { addType(SCO_PARTICLE_EFFECT); running = false; waitForParticles = true; effectLife = -1; die = false; effectLifeCounter = -1; cull = false; } void ParticleEffect::setDie(bool v) { die = v; } void ParticleEffect::load(const std::string &name) { BBGE_PROF(ParticleEffect_load); particleManager->loadParticleEffectFromBank(name, this); } void ParticleEffect::transfer(ParticleEffect *pe) { pe->effectLife = this->effectLife; pe->clearEmitters(); pe->name = this->name; for (Emitters::iterator i = emitters.begin(); i != emitters.end(); i++) { Emitter *e = pe->addNewEmitter(); e->data = (*i)->data; e->setTexture(e->data.texture); /* if (e->data.flipH) { if (!e->isfh()) e->flipHorizontal(); } else { if (e->isfh()) e->flipHorizontal(); } */ } } Emitter *ParticleEffect::addNewEmitter() { Emitter *e = new Emitter(this); emitters.push_back(e); addChild(e, PM_POINTER); return e; } void ParticleEffect::clearEmitters() { for (Emitters::iterator i = emitters.begin(); i != emitters.end(); i++) { (*i)->destroy(); delete *i; } emitters.clear(); children.clear(); } void ParticleEffect::bankLoad(const std::string &file, const std::string &path) { std::string usef = path + file + ".txt"; name = file; stringToLower(name); clearEmitters(); usef = core->adjustFilenameCase(usef); debugLog(usef); char *buffer = readFile(usef); if (!buffer) { debugLog("Can't read " + usef); return; } SimpleIStringStream inf(buffer, SimpleIStringStream::TAKE_OVER); std::string token, tmp; int state=0; Emitter *currentEmitter = 0; while (inf >> token) { //debugLog("Token: " + token); if (token == "[Emitter]") { state = 1; currentEmitter = addNewEmitter(); continue; } if (token == "[Color]") { state = 2; continue; } if (token == "[Number]") { state = 3; continue; } if (token == "[Alpha]") { state = 4; continue; } if (token == "[Rotation]") { state = 5; continue; } if (token == "[Scale]") { state = 6; continue; } if (state == 2 && currentEmitter) { float t, x, y, z; SimpleIStringStream is(token); is >> t; inf >> x >> y >> z; currentEmitter->data.color.ensureData(); currentEmitter->data.color.data->path.addPathNode(Vector(x,y,z), t); currentEmitter->data.color.startPath(currentEmitter->data.life); /* std::ostringstream os; os << "colorNode: " << t << ", " << x << ", " << y << ", " << z; debugLog(os.str()); */ } if (state == 3 && currentEmitter) { float t, num; SimpleIStringStream is(token); is >> t; inf >> num; currentEmitter->data.number.ensureData(); currentEmitter->data.number.data->path.addPathNode(num, t); currentEmitter->data.number.startPath(currentEmitter->data.life); /* std::ostringstream os; os << "numberNode: " << t << ", " << num; debugLog(os.str()); */ } if (state == 4 && currentEmitter) { float t, num; SimpleIStringStream is(token); is >> t; inf >> num; currentEmitter->data.alpha.ensureData(); currentEmitter->data.alpha.data->path.addPathNode(num, t); currentEmitter->data.alpha.startPath(currentEmitter->data.life); /* std::ostringstream os; os << "alphaNode: " << t << ", " << num; debugLog(os.str()); */ } if (state == 5 && currentEmitter) { float t, num; SimpleIStringStream is(token); is >> t; inf >> num; currentEmitter->data.rotation.ensureData(); currentEmitter->data.rotation.data->path.addPathNode(Vector(0,0,num), t); currentEmitter->data.rotation.startPath(currentEmitter->data.life); /* std::ostringstream os; os << "rotationNode: " << t << ", " << num; debugLog(os.str()); */ } if (state == 6 && currentEmitter) { float t, sx, sy; SimpleIStringStream is(token); is >> t; inf >> sx >> sy; currentEmitter->data.scale.ensureData(); currentEmitter->data.scale.data->path.addPathNode(Vector(sx, sy), t); currentEmitter->data.scale.startPath(currentEmitter->data.life); /* std::ostringstream os; os << "scaleNode: " << t << ", " << sx << ", " << sy; debugLog(os.str()); */ } if (token == "EmitterLife") { inf >> tmp; inf >> effectLife; continue; } if (token == "EmitterScale") { inf >> tmp; inf >> scale.x; scale.y = scale.x; continue; } if (token == "EmitterUpdateCull") { inf >> tmp; inf >> updateCull; continue; } // subs if (currentEmitter) { if (token == "SpawnLocal") { inf >> tmp; inf >> currentEmitter->data.spawnLocal; continue; } else if (token == "Texture") { inf >> tmp; inf >> currentEmitter->data.texture; } else if (token == "AvatarVelocity") { inf >> tmp; inf >> currentEmitter->data.avatarVelocity; } else if (token == "AlphaModTimesVel") { inf >> tmp; inf >> currentEmitter->data.alphaModTimesVel; } else if (token == "RandomScale") { inf >> tmp; inf >> currentEmitter->data.randomScale1 >> currentEmitter->data.randomScale2; } else if (token == "RandomAlphaMod") { inf >> tmp; inf >> currentEmitter->data.randomAlphaMod1 >> currentEmitter->data.randomAlphaMod2; } else if (token == "RandomSpawnRadius") { inf >> tmp; inf >> currentEmitter->data.randomSpawnRadius; } else if (token == "RandomSpawnMod") { inf >> tmp; inf >> currentEmitter->data.randomSpawnMod.x >> currentEmitter->data.randomSpawnMod.y; } else if (token == "RandomSpawnRadiusRange") { inf >> tmp; inf >> currentEmitter->data.randomSpawnRadiusRange; } else if (token == "RandomVelocityMagnitude") { inf >> tmp; inf >> currentEmitter->data.randomVelocityMagnitude; } else if (token == "CopyParentRotation") { inf >> tmp; inf >> currentEmitter->data.copyParentRotation; } else if (token == "CopyParentFlip") { inf >> tmp; inf >> currentEmitter->data.copyParentFlip; } else if (token == "JustOne") { inf >> tmp; inf >> currentEmitter->data.justOne; } else if (token == "SpawnTimeOffset") { inf >> tmp; inf >> currentEmitter->data.spawnTimeOffset; } else if (token == "RandomRotationRange") { inf >> tmp; inf >> currentEmitter->data.randomRotationRange; } else if (token == "InitialVelocity") { inf >> tmp; inf >> currentEmitter->data.initialVelocity.x >> currentEmitter->data.initialVelocity.y; } else if (token == "Influenced") { inf >> tmp; inf >> currentEmitter->data.influenced; } else if (token == "DeathPrt") { inf >> tmp; inf >> currentEmitter->data.deathPrt; } else if (token == "Gravity") { inf >> tmp; inf >> currentEmitter->data.gravity.x >> currentEmitter->data.gravity.y; } else if (token == "PauseLevel") { inf >> tmp; inf >> currentEmitter->data.pauseLevel; //errorLog("read in pauseLevel"); } else if (token == "FlipH") { inf >> tmp; inf >> currentEmitter->data.flipH; } else if (token == "FlipV") { inf >> tmp; inf >> currentEmitter->data.flipV; } else if (token == "Blend") { inf >> tmp; std::string blendType; inf >> blendType; if (blendType == "Add") currentEmitter->data.blendType = BLEND_ADD; else if (blendType == "Sub") currentEmitter->data.blendType = RenderObject::BLEND_SUB; } else if (token == "Width") { inf >> tmp; inf >> currentEmitter->data.width; } else if (token == "Height") { inf >> tmp; inf >> currentEmitter->data.height; } else if (token == "Life") { inf >> tmp; inf >> currentEmitter->data.life; } else if (token == "GroupRender") { inf >> tmp; inf >> currentEmitter->data.groupRender; } else if (token == "Shape") { inf >> tmp; inf >> tmp; } else if (token == "Suck") { inf >> tmp; inf >> currentEmitter->data.suckIndex >> currentEmitter->data.suckStr; } } } } void ParticleEffect::onUpdate(float dt) { RenderObject::onUpdate(dt); /* for (Emitters::iterator i = emitters.begin(); i != emitters.end(); i++) { (*i)->update(dt); } */ if (effectLifeCounter == 0) { if (waitForParticles) { // extra loop, could be combined above later int c=0,e=0; for (Emitters::iterator i = emitters.begin(); i != emitters.end(); i++) { if ((*i)->isEmpty()) { e++; } c++; } if (c == e) { if (die) safeKill(); } } else { if (die) safeKill(); } } if (effectLifeCounter != -1 && running) { effectLifeCounter -= dt; if (effectLifeCounter <= 0) { effectLifeCounter = 0; stop(); } } } // stop the particle effect, let the particles all die off before we delete ourselves void ParticleEffect::killParticleEffect() { effectLifeCounter = 0.0001; die = true; //stop(); } void ParticleEffect::start() { effectLifeCounter = effectLife; running = true; for (Emitters::iterator i = emitters.begin(); i != emitters.end(); i++) { (*i)->start(); } } void ParticleEffect::stop() { running = false; for (Emitters::iterator i = emitters.begin(); i != emitters.end(); i++) { (*i)->stop(); } } void ParticleEffect::onRender() { BBGE_PROF(ParticleEffect_onRender); RenderObject::onRender(); }