From 5919cdc663d5f60a8c5cc7e50ad0c43a18cf9829 Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Sat, 31 Aug 2013 21:54:52 +0300 Subject: move particles into particle dir. --- src/CMakeLists.txt | 30 +- src/Makefile.am | 30 +- src/actorsprite.h | 3 +- src/animationparticle.cpp | 61 ---- src/animationparticle.h | 52 --- src/being.cpp | 3 +- src/effectmanager.cpp | 3 +- src/game.cpp | 3 +- src/gui/debugwindow.cpp | 3 +- src/imageparticle.cpp | 110 ------ src/imageparticle.h | 68 ---- src/localplayer.cpp | 3 +- src/map.cpp | 3 +- src/net/ea/beinghandler.cpp | 3 +- src/particle.cpp | 477 ------------------------- src/particle.h | 366 -------------------- src/particle/animationparticle.cpp | 61 ++++ src/particle/animationparticle.h | 52 +++ src/particle/imageparticle.cpp | 110 ++++++ src/particle/imageparticle.h | 68 ++++ src/particle/particle.cpp | 478 +++++++++++++++++++++++++ src/particle/particle.h | 366 ++++++++++++++++++++ src/particle/particlecontainer.cpp | 196 +++++++++++ src/particle/particlecontainer.h | 138 ++++++++ src/particle/particleemitter.cpp | 674 ++++++++++++++++++++++++++++++++++++ src/particle/particleemitter.h | 167 +++++++++ src/particle/particleemitterprop.h | 132 +++++++ src/particle/rotationalparticle.cpp | 96 +++++ src/particle/rotationalparticle.h | 52 +++ src/particle/textparticle.cpp | 89 +++++ src/particle/textparticle.h | 62 ++++ src/particlecontainer.cpp | 196 ----------- src/particlecontainer.h | 138 -------- src/particleemitter.cpp | 673 ----------------------------------- src/particleemitter.h | 167 --------- src/particleemitterprop.h | 132 ------- src/rotationalparticle.cpp | 96 ----- src/rotationalparticle.h | 52 --- src/statuseffect.h | 3 +- src/textparticle.cpp | 89 ----- src/textparticle.h | 62 ---- 41 files changed, 2789 insertions(+), 2778 deletions(-) delete mode 100644 src/animationparticle.cpp delete mode 100644 src/animationparticle.h delete mode 100644 src/imageparticle.cpp delete mode 100644 src/imageparticle.h delete mode 100644 src/particle.cpp delete mode 100644 src/particle.h create mode 100644 src/particle/animationparticle.cpp create mode 100644 src/particle/animationparticle.h create mode 100644 src/particle/imageparticle.cpp create mode 100644 src/particle/imageparticle.h create mode 100644 src/particle/particle.cpp create mode 100644 src/particle/particle.h create mode 100644 src/particle/particlecontainer.cpp create mode 100644 src/particle/particlecontainer.h create mode 100644 src/particle/particleemitter.cpp create mode 100644 src/particle/particleemitter.h create mode 100644 src/particle/particleemitterprop.h create mode 100644 src/particle/rotationalparticle.cpp create mode 100644 src/particle/rotationalparticle.h create mode 100644 src/particle/textparticle.cpp create mode 100644 src/particle/textparticle.h delete mode 100644 src/particlecontainer.cpp delete mode 100644 src/particlecontainer.h delete mode 100644 src/particleemitter.cpp delete mode 100644 src/particleemitter.h delete mode 100644 src/particleemitterprop.h delete mode 100644 src/rotationalparticle.cpp delete mode 100644 src/rotationalparticle.h delete mode 100644 src/textparticle.cpp delete mode 100644 src/textparticle.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b2e943111..813e23672 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -569,8 +569,8 @@ SET(SRCS animatedsprite.h animationdelayload.cpp animationdelayload.h - animationparticle.cpp - animationparticle.h + particle/animationparticle.cpp + particle/animationparticle.h auctionmanager.cpp auctionmanager.h avatar.cpp @@ -622,8 +622,8 @@ SET(SRCS guild.h guildmanager.cpp guildmanager.h - imageparticle.cpp - imageparticle.h + particle/imageparticle.cpp + particle/imageparticle.h imagesprite.cpp imagesprite.h inventory.cpp @@ -680,13 +680,13 @@ SET(SRCS notifymanager.h render/nullopenglgraphics.cpp render/nullopenglgraphics.h - particle.cpp - particle.h - particlecontainer.cpp - particlecontainer.h - particleemitter.cpp - particleemitter.h - particleemitterprop.h + particle/particle.cpp + particle/particle.h + particle/particlecontainer.cpp + particle/particlecontainer.h + particle/particleemitter.cpp + particle/particleemitter.h + particle/particleemitterprop.h party.cpp party.h playerinfo.cpp @@ -696,8 +696,8 @@ SET(SRCS position.cpp position.h properties.h - rotationalparticle.cpp - rotationalparticle.h + particle/rotationalparticle.cpp + particle/rotationalparticle.h render/safeopenglgraphics.cpp render/safeopenglgraphics.h render/sdl2graphics.cpp @@ -721,8 +721,8 @@ SET(SRCS text.h textmanager.cpp textmanager.h - textparticle.cpp - textparticle.h + particle/textparticle.cpp + particle/textparticle.h tileset.h touchactions.cpp touchactions.h diff --git a/src/Makefile.am b/src/Makefile.am index d4c6779f4..252aa05f7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -580,8 +580,8 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ animatedsprite.h \ animationdelayload.cpp \ animationdelayload.h \ - animationparticle.cpp \ - animationparticle.h \ + particle/animationparticle.cpp \ + particle/animationparticle.h \ auctionmanager.cpp \ auctionmanager.h \ avatar.cpp \ @@ -633,8 +633,8 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ guild.h \ guildmanager.cpp \ guildmanager.h \ - imageparticle.cpp \ - imageparticle.h \ + particle/imageparticle.cpp \ + particle/imageparticle.h \ imagesprite.cpp \ imagesprite.h \ inventory.cpp \ @@ -689,13 +689,13 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ notifymanager.h \ render/nullopenglgraphics.cpp \ render/nullopenglgraphics.h \ - particle.cpp \ - particle.h \ - particlecontainer.cpp \ - particlecontainer.h \ - particleemitter.cpp \ - particleemitter.h \ - particleemitterprop.h \ + particle/particle.cpp \ + particle/particle.h \ + particle/particlecontainer.cpp \ + particle/particlecontainer.h \ + particle/particleemitter.cpp \ + particle/particleemitter.h \ + particle/particleemitterprop.h \ party.cpp \ party.h \ playerinfo.cpp \ @@ -705,8 +705,8 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ position.cpp \ position.h \ properties.h \ - rotationalparticle.cpp \ - rotationalparticle.h \ + particle/rotationalparticle.cpp \ + particle/rotationalparticle.h \ render/safeopenglgraphics.cpp\ render/safeopenglgraphics.h \ render/sdl2graphics.cpp \ @@ -730,8 +730,8 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ text.h \ textmanager.cpp \ textmanager.h \ - textparticle.cpp \ - textparticle.h \ + particle/textparticle.cpp \ + particle/textparticle.h \ tileset.h \ touchactions.cpp \ touchactions.h \ diff --git a/src/actorsprite.h b/src/actorsprite.h index 4b2b07c52..c8b1e03df 100644 --- a/src/actorsprite.h +++ b/src/actorsprite.h @@ -26,7 +26,8 @@ #include "compoundsprite.h" #include "localconsts.h" #include "map.h" -#include "particlecontainer.h" + +#include "particle/particlecontainer.h" #include diff --git a/src/animationparticle.cpp b/src/animationparticle.cpp deleted file mode 100644 index 2c4936518..000000000 --- a/src/animationparticle.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#include "animationparticle.h" - -#include "simpleanimation.h" - -#include "render/graphics.h" - -#include "debug.h" - -AnimationParticle::AnimationParticle(Map *const map, - Animation *const animation) : - ImageParticle(map, nullptr), - mAnimation(new SimpleAnimation(animation)) -{ -} - -AnimationParticle::AnimationParticle(Map *const map, - XmlNodePtr const animationNode, - const std::string& dyePalettes): - ImageParticle(map, nullptr), - mAnimation(new SimpleAnimation(animationNode, dyePalettes)) -{ -} - -AnimationParticle::~AnimationParticle() -{ - delete mAnimation; - mAnimation = nullptr; - mImage = nullptr; -} - -bool AnimationParticle::update() -{ - if (mAnimation) - { - mAnimation->update(10); // particle engine is updated every 10ms - mImage = mAnimation->getCurrentImage(); - } - return Particle::update(); -} diff --git a/src/animationparticle.h b/src/animationparticle.h deleted file mode 100644 index 02c383fea..000000000 --- a/src/animationparticle.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#ifndef ANIMATIONPARTICLE_H -#define ANIMATIONPARTICLE_H - -#include "imageparticle.h" - -#include "utils/xml.h" - -class Animation; -class Map; -class SimpleAnimation; - -class AnimationParticle final : public ImageParticle -{ - public: - AnimationParticle(Map *const map, Animation *const animation); - - AnimationParticle(Map *const map, XmlNodePtr const animationNode, - const std::string& dyePalettes = std::string()); - - A_DELETE_COPY(AnimationParticle) - - ~AnimationParticle(); - - virtual bool update() override; - - private: - SimpleAnimation *mAnimation; /**< Used animation for this particle */ -}; - -#endif // ANIMATIONPARTICLE_H diff --git a/src/being.cpp b/src/being.cpp index 8fc8003ff..535c0c0e3 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -29,13 +29,14 @@ #include "client.h" #include "effectmanager.h" #include "guild.h" -#include "particle.h" #include "party.h" #include "playerrelations.h" #include "soundconsts.h" #include "soundmanager.h" #include "text.h" +#include "particle/particle.h" + #include "gui/equipmentwindow.h" #include "gui/socialwindow.h" #include "gui/speechbubble.h" diff --git a/src/effectmanager.cpp b/src/effectmanager.cpp index f76f3ebd0..9d41ac297 100644 --- a/src/effectmanager.cpp +++ b/src/effectmanager.cpp @@ -25,9 +25,10 @@ #include "being.h" #include "configuration.h" #include "logger.h" -#include "particle.h" #include "soundmanager.h" +#include "particle/particle.h" + #include "debug.h" EffectManager::EffectManager() : diff --git a/src/game.cpp b/src/game.cpp index 258ce6e60..a900eb6ed 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -34,12 +34,13 @@ #include "guildmanager.h" #include "itemshortcut.h" #include "localplayer.h" -#include "particle.h" #include "playerinfo.h" #include "soundmanager.h" #include "spellshortcut.h" #include "touchmanager.h" +#include "particle/particle.h" + #include "input/inputmanager.h" #include "input/joystick.h" #include "input/keyboardconfig.h" diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp index b154a59bc..2cb633589 100644 --- a/src/gui/debugwindow.cpp +++ b/src/gui/debugwindow.cpp @@ -26,7 +26,8 @@ #include "game.h" #include "localplayer.h" #include "main.h" -#include "particle.h" + +#include "particle/particle.h" #include "gui/setup.h" #include "gui/setup_video.h" diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp deleted file mode 100644 index 52d66178c..000000000 --- a/src/imageparticle.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#include "imageparticle.h" - -#include "logger.h" - -#include "render/graphics.h" - -#include "resources/image.h" - -#include "debug.h" - -std::map ImageParticle::imageParticleCountByName; - -ImageParticle::ImageParticle(Map *const map, Image *const image): - Particle(map), - mImage(image) -{ - if (mImage) - { - mImage->incRef(); - - const std::string &name = mImage->getIdPath(); - std::map::iterator it - = ImageParticle::imageParticleCountByName.find(name); - if (it == ImageParticle::imageParticleCountByName.end()) - ImageParticle::imageParticleCountByName[name] = 1; - else - (*it).second ++; - } -} - -ImageParticle::~ImageParticle() -{ - if (mImage) - { - const std::string &name = mImage->getIdPath(); - std::map::iterator it - = ImageParticle::imageParticleCountByName.find(name); - if (it != ImageParticle::imageParticleCountByName.end()) - { - int &cnt = (*it).second; - if (cnt > 0) - cnt --; - } - - mImage->decRef(); - mImage = nullptr; - } - setMap(nullptr); -} - -bool ImageParticle::draw(Graphics *const graphics, - const int offsetX, const int offsetY) const -{ - FUNC_BLOCK("ImageParticle::draw", 1) - if (mAlive != ALIVE || !mImage) - return false; - - const int screenX = static_cast(mPos.x) - + offsetX - mImage->mBounds.w / 2; - const int screenY = static_cast(mPos.y) - static_cast(mPos.z) - + offsetY - mImage->mBounds.h / 2; - - // Check if on screen - if (screenX + mImage->mBounds.w < 0 || - screenX > graphics->mWidth || - screenY + mImage->mBounds.h < 0 || - screenY > graphics->mHeight) - { - return false; - } - - float alphafactor = mAlpha; - - if (mFadeOut && mLifetimeLeft > -1 && mLifetimeLeft < mFadeOut) - { - alphafactor *= static_cast(mLifetimeLeft) - / static_cast(mFadeOut); - } - - if (mFadeIn && mLifetimePast < mFadeIn) - { - alphafactor *= static_cast(mLifetimePast) - / static_cast(mFadeIn); - } - - mImage->setAlpha(alphafactor); - return graphics->drawImage(mImage, screenX, screenY); -} diff --git a/src/imageparticle.h b/src/imageparticle.h deleted file mode 100644 index 160567930..000000000 --- a/src/imageparticle.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#ifndef IMAGEPARTICLE_H -#define IMAGEPARTICLE_H - -#include "particle.h" - -#include - -class Image; -class Map; - -/** - * A particle that uses an image for its visualization. - */ -class ImageParticle : public Particle -{ - public: - /** - * Constructor. The image is reference counted by this particle. - * - * @param map the map this particle appears on - * @param image an Image instance, may not be NULL - */ - ImageParticle(Map *const map, Image *const image); - - A_DELETE_COPY(ImageParticle) - - /** - * Destructor. - */ - ~ImageParticle(); - - /** - * Draws the particle image - */ - virtual bool draw(Graphics *const graphics, - const int offsetX, const int offsetY) const override; - - virtual void setAlpha(const float alpha) override - { mAlpha = alpha; } - - static std::map imageParticleCountByName; - protected: - Image *mImage; /**< The image used for this particle. */ -}; - -#endif // IMAGEPARTICLE_H diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 409d9de4f..02c1d6ed9 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -31,7 +31,6 @@ #include "item.h" #include "maplayer.h" #include "party.h" -#include "particle.h" #include "playerinfo.h" #include "playerrelations.h" #include "simpleanimation.h" @@ -40,6 +39,8 @@ #include "statuseffect.h" #include "walklayer.h" +#include "particle/particle.h" + #include "input/keyboardconfig.h" #include "gui/chatwindow.h" diff --git a/src/map.cpp b/src/map.cpp index f7082383b..30508e07a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -27,11 +27,12 @@ #include "localplayer.h" #include "maplayer.h" #include "notifymanager.h" -#include "particle.h" #include "simpleanimation.h" #include "tileset.h" #include "walklayer.h" +#include "particle/particle.h" + #include "resources/ambientlayer.h" #include "resources/image.h" #include "resources/resourcemanager.h" diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp index 83576d9a2..3dee60dfa 100644 --- a/src/net/ea/beinghandler.cpp +++ b/src/net/ea/beinghandler.cpp @@ -31,11 +31,12 @@ #include "guild.h" #include "guildmanager.h" #include "localplayer.h" -#include "particle.h" #include "party.h" #include "playerrelations.h" #include "configuration.h" +#include "particle/particle.h" + #include "input/inputmanager.h" #include "input/keyboardconfig.h" diff --git a/src/particle.cpp b/src/particle.cpp deleted file mode 100644 index dcd9422a1..000000000 --- a/src/particle.cpp +++ /dev/null @@ -1,477 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#include "particle.h" - -#include "animationparticle.h" -#include "configuration.h" -#include "resources/dye.h" -#include "logger.h" -#include "map.h" -#include "particleemitter.h" -#include "rotationalparticle.h" -#include "textparticle.h" - -#include "resources/resourcemanager.h" - -#include "gui/sdlfont.h" - -#include "utils/dtor.h" -#include "utils/mathutils.h" - -#include - -#include -#include - -#include "debug.h" - -static const float SIN45 = 0.707106781f; - -class Graphics; -class Image; - -int Particle::particleCount = 0; -int Particle::maxCount = 0; -int Particle::fastPhysics = 0; -int Particle::emitterSkip = 1; -bool Particle::enabled = true; -const float Particle::PARTICLE_SKY = 800.0f; - -Particle::Particle(Map *const map) : - Actor(), - mAlpha(1.0f), - mLifetimeLeft(-1), - mLifetimePast(0), - mFadeOut(0), - mFadeIn(0), - mVelocity(), - mAlive(ALIVE), - mChildEmitters(), - mChildParticles(), - mDeathEffect(), - mGravity(0.0f), - mBounce(0.0f), - mAcceleration(0.0f), - mInvDieDistance(-1.0f), - mMomentum(1.0f), - mTarget(nullptr), - mRandomness(0), - mDeathEffectConditions(0x00), - mAutoDelete(true), - mAllowSizeAdjust(false), - mFollow(false) -{ - setMap(map); - Particle::particleCount++; -} - -Particle::~Particle() -{ - // Delete child emitters and child particles - clear(); - Particle::particleCount--; -} - -void Particle::setupEngine() -{ - Particle::maxCount = config.getIntValue("particleMaxCount"); - Particle::fastPhysics = config.getIntValue("particleFastPhysics"); - Particle::emitterSkip = config.getIntValue("particleEmitterSkip") + 1; - if (!Particle::emitterSkip) - Particle::emitterSkip = 1; - Particle::enabled = config.getBoolValue("particleeffects"); - disableAutoDelete(); - logger->log1("Particle engine set up"); -} - -bool Particle::draw(Graphics *const, const int, const int) const -{ - return false; -} - -bool Particle::update() -{ - if (!mMap) - return false; - - if (mLifetimeLeft == 0 && mAlive == ALIVE) - mAlive = DEAD_TIMEOUT; - - const Vector oldPos = mPos; - - if (mAlive == ALIVE) - { - // calculate particle movement - if (mMomentum != 1.0f) - mVelocity *= mMomentum; - - if (mTarget && mAcceleration != 0.0f) - { - Vector dist = mPos - mTarget->mPos; - dist.x *= SIN45; - float invHypotenuse; - - switch (Particle::fastPhysics) - { - case 1: - invHypotenuse = fastInvSqrt( - dist.x * dist.x + dist.y * dist.y + dist.z * dist.z); - break; - case 2: - if (!dist.x) - { - invHypotenuse = 0; - break; - } - - invHypotenuse = 2.0f / (static_cast(fabs(dist.x)) - + static_cast(fabs(dist.y)) - + static_cast(fabs(dist.z))); - break; - default: - invHypotenuse = 1.0f / static_cast(sqrt( - dist.x * dist.x + dist.y * dist.y + dist.z * dist.z)); - break; - } - - if (invHypotenuse) - { - if (mInvDieDistance > 0.0f && invHypotenuse > mInvDieDistance) - mAlive = DEAD_IMPACT; - const float accFactor = invHypotenuse * mAcceleration; - mVelocity -= dist * accFactor; - } - } - - if (mRandomness > 0) - { - mVelocity.x += static_cast((rand() % mRandomness - rand() - % mRandomness)) / 1000.0f; - mVelocity.y += static_cast((rand() % mRandomness - rand() - % mRandomness)) / 1000.0f; - mVelocity.z += static_cast((rand() % mRandomness - rand() - % mRandomness)) / 1000.0f; - } - - mVelocity.z -= mGravity; - - // Update position - mPos.x += mVelocity.x; - mPos.y += mVelocity.y * SIN45; - mPos.z += mVelocity.z * SIN45; - - // Update other stuff - if (mLifetimeLeft > 0) - mLifetimeLeft--; - - mLifetimePast++; - - if (mPos.z < 0.0f) - { - if (mBounce > 0.0f) - { - mPos.z *= -mBounce; - mVelocity *= mBounce; - mVelocity.z = -mVelocity.z; - } - else - { - mAlive = DEAD_FLOOR; - } - } - else if (mPos.z > PARTICLE_SKY) - { - mAlive = DEAD_SKY; - } - - // Update child emitters - if (Particle::emitterSkip && (mLifetimePast - 1) - % Particle::emitterSkip == 0) - { - FOR_EACH (EmitterConstIterator, e, mChildEmitters) - { - Particles newParticles = (*e)->createParticles(mLifetimePast); - FOR_EACH (ParticleConstIterator, it, newParticles) - { - Particle *const p = *it; - p->moveBy(mPos); - mChildParticles.push_back(p); - } - } - } - } - - // create death effect when the particle died - if (mAlive != ALIVE && mAlive != DEAD_LONG_AGO) - { - if ((mAlive & mDeathEffectConditions) > 0x00 && !mDeathEffect.empty()) - { - Particle *const deathEffect = particleEngine->addEffect( - mDeathEffect, 0, 0); - if (deathEffect) - deathEffect->moveBy(mPos); - } - mAlive = DEAD_LONG_AGO; - } - - const Vector change = mPos - oldPos; - - // Update child particles - - for (ParticleIterator p = mChildParticles.begin(), - p2 = mChildParticles.end(); p != p2; ) - { - Particle *const particle = *p; - // move particle with its parent if desired - if (particle->mFollow) - particle->moveBy(change); - - // update particle - if (particle->update()) - { - ++p; - } - else - { - delete particle; - p = mChildParticles.erase(p); - } - } - if (mAlive != ALIVE && mChildParticles.empty() && mAutoDelete) - return false; - - return true; -} - -void Particle::moveBy(const Vector &change) -{ - mPos += change; - FOR_EACH (ParticleConstIterator, p, mChildParticles) - { - Particle *const particle = *p; - if (particle->mFollow) - particle->moveBy(change); - } -} - -void Particle::moveTo(const float x, const float y) -{ - moveTo(Vector(x, y, mPos.z)); -} - -Particle *Particle::createChild() -{ - Particle *const newParticle = new Particle(mMap); - mChildParticles.push_back(newParticle); - return newParticle; -} - -Particle *Particle::addEffect(const std::string &particleEffectFile, - const int pixelX, const int pixelY, - const int rotation) -{ - Particle *newParticle = nullptr; - - const size_t pos = particleEffectFile.find('|'); - const std::string dyePalettes = (pos != std::string::npos) - ? particleEffectFile.substr(pos + 1) : ""; - XML::Document doc(particleEffectFile.substr(0, pos)); - const XmlNodePtr rootNode = doc.rootNode(); - - if (!rootNode || !xmlNameEqual(rootNode, "effect")) - { - logger->log("Error loading particle: %s", particleEffectFile.c_str()); - return nullptr; - } - - ResourceManager *const resman = ResourceManager::getInstance(); - - // Parse particles - for_each_xml_child_node(effectChildNode, rootNode) - { - // We're only interested in particles - if (!xmlNameEqual(effectChildNode, "particle")) - continue; - - // Determine the exact particle type - XmlNodePtr node; - - // Animation - if ((node = XML::findFirstChildByName(effectChildNode, "animation"))) - { - newParticle = new AnimationParticle(mMap, node, dyePalettes); - } - // Rotational - else if ((node = XML::findFirstChildByName( - effectChildNode, "rotation"))) - { - newParticle = new RotationalParticle(mMap, node, dyePalettes); - } - // Image - else if ((node = XML::findFirstChildByName(effectChildNode, "image"))) - { - std::string imageSrc = reinterpret_cast( - node->xmlChildrenNode->content); - if (!imageSrc.empty() && !dyePalettes.empty()) - Dye::instantiate(imageSrc, dyePalettes); - Image *const img = resman->getImage(imageSrc); - - newParticle = new ImageParticle(mMap, img); - } - // Other - else - { - newParticle = new Particle(mMap); - } - - // Read and set the basic properties of the particle - const float offsetX = static_cast(XML::getFloatProperty( - effectChildNode, "position-x", 0)); - const float offsetY = static_cast(XML::getFloatProperty( - effectChildNode, "position-y", 0)); - const float offsetZ = static_cast(XML::getFloatProperty( - effectChildNode, "position-z", 0)); - const Vector position(mPos.x + static_cast(pixelX) + offsetX, - mPos.y + static_cast(pixelY) + offsetY, - mPos.z + offsetZ); - newParticle->moveTo(position); - - const int lifetime = XML::getProperty(effectChildNode, "lifetime", -1); - newParticle->setLifetime(lifetime); - const bool resizeable = "false" != XML::getProperty(effectChildNode, - "size-adjustable", "false"); - - newParticle->setAllowSizeAdjust(resizeable); - - // Look for additional emitters for this particle - for_each_xml_child_node(emitterNode, effectChildNode) - { - if (xmlNameEqual(emitterNode, "emitter")) - { - ParticleEmitter *const newEmitter = new ParticleEmitter( - emitterNode, newParticle, mMap, rotation, dyePalettes); - newParticle->addEmitter(newEmitter); - } - else if (xmlNameEqual(emitterNode, "deatheffect")) - { - const std::string deathEffect = reinterpret_cast( - emitterNode->xmlChildrenNode->content); - - char deathEffectConditions = 0x00; - if (XML::getBoolProperty(emitterNode, "on-floor", true)) - { - deathEffectConditions += static_cast( - Particle::DEAD_FLOOR); - } - if (XML::getBoolProperty(emitterNode, "on-sky", true)) - { - deathEffectConditions += static_cast( - Particle::DEAD_SKY); - } - if (XML::getBoolProperty(emitterNode, "on-other", false)) - { - deathEffectConditions += static_cast( - Particle::DEAD_OTHER); - } - if (XML::getBoolProperty(emitterNode, "on-impact", true)) - { - deathEffectConditions += static_cast( - Particle::DEAD_IMPACT); - } - if (XML::getBoolProperty(emitterNode, "on-timeout", true)) - { - deathEffectConditions += static_cast( - Particle::DEAD_TIMEOUT); - } - newParticle->setDeathEffect( - deathEffect, deathEffectConditions); - } - } - - mChildParticles.push_back(newParticle); - } - - return newParticle; -} - -Particle *Particle::addTextSplashEffect(const std::string &text, - const int x, const int y, - const gcn::Color *const color, - gcn::Font *const font, - const bool outline) -{ - Particle *const newParticle = new TextParticle( - mMap, text, color, font, outline); - newParticle->moveTo(static_cast(x), static_cast(y)); - newParticle->setVelocity( - static_cast((rand() % 100) - 50) / 200.0f, // X - static_cast((rand() % 100) - 50) / 200.0f, // Y - (static_cast((rand() % 100)) / 200.0f) + 4.0f); // Z - - newParticle->setGravity(0.1f); - newParticle->setBounce(0.5f); - newParticle->setLifetime(200); - newParticle->setFadeOut(100); - - mChildParticles.push_back(newParticle); - - return newParticle; -} - -Particle *Particle::addTextRiseFadeOutEffect(const std::string &text, - const int x, const int y, - const gcn::Color *const color, - gcn::Font *const font, - const bool outline) -{ - Particle *const newParticle = new TextParticle( - mMap, text, color, font, outline); - newParticle->moveTo(static_cast(x), static_cast(y)); - newParticle->setVelocity(0.0f, 0.0f, 0.5f); - newParticle->setGravity(0.0015f); - newParticle->setLifetime(300); - newParticle->setFadeOut(100); - newParticle->setFadeIn(0); - - mChildParticles.push_back(newParticle); - - return newParticle; -} - -void Particle::adjustEmitterSize(const int w, const int h) -{ - if (mAllowSizeAdjust) - { - FOR_EACH (EmitterConstIterator, e, mChildEmitters) - (*e)->adjustSize(w, h); - } -} - -void Particle::clear() -{ - delete_all(mChildEmitters); - mChildEmitters.clear(); - - delete_all(mChildParticles); - mChildParticles.clear(); -} diff --git a/src/particle.h b/src/particle.h deleted file mode 100644 index 61e231d20..000000000 --- a/src/particle.h +++ /dev/null @@ -1,366 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#ifndef PARTICLE_H -#define PARTICLE_H - -#include "actor.h" -#include "localconsts.h" - -#include -#include - -class Map; -class Particle; -class ParticleEmitter; -class SDLFont; - -namespace gcn -{ - class Color; - class Font; -} - -typedef std::list Particles; -typedef Particles::iterator ParticleIterator; -typedef Particles::const_iterator ParticleConstIterator; -typedef std::list Emitters; -typedef Emitters::iterator EmitterIterator; -typedef Emitters::const_iterator EmitterConstIterator; - -/** - * A particle spawned by a ParticleEmitter. - */ -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 - static int maxCount; // Maximum number of particles - static int emitterSkip; // Duration of pause between two - // emitter updates in ticks - static bool enabled; // true when non-crucial particle effects - // are disabled - - /** - * Constructor. - * - * @param map the map this particle will add itself to, may be nullptr - */ - explicit Particle(Map *const map); - - A_DELETE_COPY(Particle) - - /** - * Destructor. - */ - ~Particle(); - - /** - * Deletes all child particles and emitters. - */ - void clear(); - - /** - * Gives a particle the properties of an engine root particle and loads - * the particle-related config settings. - */ - void setupEngine(); - - /** - * Updates particle position, returns false when the particle should - * be deleted. - */ - virtual bool update(); - - /** - * Draws the particle image. - */ - virtual bool draw(Graphics *const graphics, - const int offsetX, const int offsetY) const override; - - /** - * Necessary for sorting with the other sprites. - */ - virtual int getPixelY() const override A_WARN_UNUSED - { return static_cast(mPos.y) - 16; } - - /** - * Necessary for sorting with the other sprites for sorting only. - */ - virtual int getSortPixelY() const override A_WARN_UNUSED - { return static_cast(mPos.y) - 16; } - - /** - * Creates a blank particle as a child of the current particle - * Useful for creating target particles - */ - Particle *createChild(); - - /** - * Creates a child particle that hosts some emitters described in the - * particleEffectFile. - */ - Particle *addEffect(const std::string &particleEffectFile, - const int pixelX, const int pixelY, - const int rotation = 0); - - /** - * Creates a standalone text particle. - */ - Particle *addTextSplashEffect(const std::string &text, - const int x, const int y, - const gcn::Color *const color, - gcn::Font *const font, - const bool outline = false); - - /** - * Creates a standalone text particle. - */ - Particle *addTextRiseFadeOutEffect(const std::string &text, - const int x, const int y, - const gcn::Color *const color, - gcn::Font *const font, - const bool outline = false); - - /** - * Adds an emitter to the particle. - */ - void addEmitter(ParticleEmitter *const emitter) - { mChildEmitters.push_back(emitter); } - - /** - * Sets the position in 3 dimensional space in pixels relative to map. - */ - void moveTo(const Vector &pos) - { moveBy(pos - mPos); } - - /** - * Sets the position in 2 dimensional space in pixels relative to map. - */ - void moveTo(const float x, const float y); - - /** - * Changes the particle position relative - */ - void moveBy(const Vector &change); - - /** - * Sets the time in game ticks until the particle is destroyed. - */ - void setLifetime(const int lifetime) - { mLifetimeLeft = lifetime; mLifetimePast = 0; } - - /** - * Sets the age of the pixel in game ticks where the particle has - * faded in completely. - */ - void setFadeOut(const int fadeOut) - { mFadeOut = fadeOut; } - - /** - * Sets the remaining particle lifetime where the particle starts to - * fade out. - */ - void setFadeIn(const int fadeIn) - { mFadeIn = fadeIn; } - - /** - * Sets the current velocity in 3 dimensional space. - */ - void setVelocity(const float x, const float y, const float z) - { mVelocity.x = x; mVelocity.y = y; mVelocity.z = z; } - - /** - * Sets the downward acceleration. - */ - void setGravity(const float gravity) - { mGravity = gravity; } - - /** - * Sets the ammount of random vector changes - */ - void setRandomness(const int r) - { mRandomness = r; } - - /** - * Sets the ammount of velocity particles retain after - * hitting the ground. - */ - void setBounce(const float bouncieness) - { mBounce = bouncieness; } - - /** - * Sets the flag if the particle is supposed to be moved by its parent - */ - void setFollow(const bool follow) - { mFollow = follow; } - - /** - * Gets the flag if the particle is supposed to be moved by its parent - */ - bool doesFollow() const A_WARN_UNUSED - { return mFollow; } - - /** - * Makes the particle move toward another particle with a - * given acceleration and momentum - */ - void setDestination(Particle *const target, - const float accel, const float moment) - { mTarget = target; mAcceleration = accel; mMomentum = moment; } - - /** - * Sets the distance in pixel the particle can come near the target - * particle before it is destroyed. Does only make sense after a target - * particle has been set using setDestination. - */ - void setDieDistance(const float dist) - { mInvDieDistance = 1.0f / dist; } - - /** - * Changes the size of the emitters so that the effect fills a - * rectangle of this size - */ - void adjustEmitterSize(const int w, const int h); - - void setAllowSizeAdjust(const bool adjust) - { mAllowSizeAdjust = adjust; } - - bool isAlive() const A_WARN_UNUSED - { return mAlive == ALIVE; } - - /** - * Determines whether the particle and its children are all dead - */ - bool isExtinct() const A_WARN_UNUSED - { return !isAlive() && mChildParticles.empty(); } - - /** - * Manually marks the particle for deletion. - */ - void kill() - { mAlive = DEAD_OTHER; mAutoDelete = true; } - - /** - * After calling this function the particle will only request - * deletion when kill() is called - */ - void disableAutoDelete() - { mAutoDelete = false; } - - /** We consider particles (at least for now) to be one layer-sprites */ - virtual int getNumberOfLayers() const - { return 1; } - - virtual float getAlpha() const - { return 1.0f; } - - virtual void setAlpha(const float alpha A_UNUSED) override - { } - - virtual void setDeathEffect(const std::string &effectFile, - const signed char conditions) - { mDeathEffect = effectFile; mDeathEffectConditions = conditions; } - - protected: - // Opacity of the graphical representation of the particle - float mAlpha; - - // Lifetime left in game ticks - int mLifetimeLeft; - - // Age of the particle in game ticks - int mLifetimePast; - - // Lifetime in game ticks left where fading out begins - int mFadeOut; - - // Age in game ticks where fading in is finished - int mFadeIn; - - // Speed in pixels per game-tick. - Vector mVelocity; - - // Is the particle supposed to be drawn and updated? - AliveStatus mAlive; - private: - // List of child emitters. - Emitters mChildEmitters; - - // List of particles controlled by this particle - Particles mChildParticles; - - // Particle effect file to be spawned when the particle dies - std::string mDeathEffect; - - // dynamic particle - // Downward acceleration in pixels per game-tick. - float mGravity; - - // How much the particle bounces off when hitting the ground - float mBounce; - - // Acceleration towards the target particle in pixels per game-tick - float mAcceleration; - - // Distance in pixels from the target particle that causes - // the destruction of the particle - float mInvDieDistance; - - // How much speed the particle retains after each game tick - float mMomentum; - - // The particle that attracts this particle - Particle *mTarget; - - // Ammount of random vector change - int mRandomness; - - // Bitfield of death conditions which trigger spawning - // of the death particle - signed char mDeathEffectConditions; - - // May the particle request its deletion by the parent particle? - bool mAutoDelete; - - // Can the effect size be adjusted by the object props in the map file? - bool mAllowSizeAdjust; - - // is this particle moved when its parent particle moves? - bool mFollow; -}; - -extern Particle *particleEngine; - -#endif // PARTICLE_H diff --git a/src/particle/animationparticle.cpp b/src/particle/animationparticle.cpp new file mode 100644 index 000000000..bc88e5d95 --- /dev/null +++ b/src/particle/animationparticle.cpp @@ -0,0 +1,61 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#include "particle/animationparticle.h" + +#include "simpleanimation.h" + +#include "render/graphics.h" + +#include "debug.h" + +AnimationParticle::AnimationParticle(Map *const map, + Animation *const animation) : + ImageParticle(map, nullptr), + mAnimation(new SimpleAnimation(animation)) +{ +} + +AnimationParticle::AnimationParticle(Map *const map, + XmlNodePtr const animationNode, + const std::string& dyePalettes): + ImageParticle(map, nullptr), + mAnimation(new SimpleAnimation(animationNode, dyePalettes)) +{ +} + +AnimationParticle::~AnimationParticle() +{ + delete mAnimation; + mAnimation = nullptr; + mImage = nullptr; +} + +bool AnimationParticle::update() +{ + if (mAnimation) + { + mAnimation->update(10); // particle engine is updated every 10ms + mImage = mAnimation->getCurrentImage(); + } + return Particle::update(); +} diff --git a/src/particle/animationparticle.h b/src/particle/animationparticle.h new file mode 100644 index 000000000..182bfa177 --- /dev/null +++ b/src/particle/animationparticle.h @@ -0,0 +1,52 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#ifndef PARTICLE_ANIMATIONPARTICLE_H +#define PARTICLE_ANIMATIONPARTICLE_H + +#include "particle/imageparticle.h" + +#include "utils/xml.h" + +class Animation; +class Map; +class SimpleAnimation; + +class AnimationParticle final : public ImageParticle +{ + public: + AnimationParticle(Map *const map, Animation *const animation); + + AnimationParticle(Map *const map, XmlNodePtr const animationNode, + const std::string& dyePalettes = std::string()); + + A_DELETE_COPY(AnimationParticle) + + ~AnimationParticle(); + + virtual bool update() override; + + private: + SimpleAnimation *mAnimation; /**< Used animation for this particle */ +}; + +#endif // PARTICLE_ANIMATIONPARTICLE_H diff --git a/src/particle/imageparticle.cpp b/src/particle/imageparticle.cpp new file mode 100644 index 000000000..b8fd5bc94 --- /dev/null +++ b/src/particle/imageparticle.cpp @@ -0,0 +1,110 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#include "particle/imageparticle.h" + +#include "logger.h" + +#include "render/graphics.h" + +#include "resources/image.h" + +#include "debug.h" + +std::map ImageParticle::imageParticleCountByName; + +ImageParticle::ImageParticle(Map *const map, Image *const image): + Particle(map), + mImage(image) +{ + if (mImage) + { + mImage->incRef(); + + const std::string &name = mImage->getIdPath(); + std::map::iterator it + = ImageParticle::imageParticleCountByName.find(name); + if (it == ImageParticle::imageParticleCountByName.end()) + ImageParticle::imageParticleCountByName[name] = 1; + else + (*it).second ++; + } +} + +ImageParticle::~ImageParticle() +{ + if (mImage) + { + const std::string &name = mImage->getIdPath(); + std::map::iterator it + = ImageParticle::imageParticleCountByName.find(name); + if (it != ImageParticle::imageParticleCountByName.end()) + { + int &cnt = (*it).second; + if (cnt > 0) + cnt --; + } + + mImage->decRef(); + mImage = nullptr; + } + setMap(nullptr); +} + +bool ImageParticle::draw(Graphics *const graphics, + const int offsetX, const int offsetY) const +{ + FUNC_BLOCK("ImageParticle::draw", 1) + if (mAlive != ALIVE || !mImage) + return false; + + const int screenX = static_cast(mPos.x) + + offsetX - mImage->mBounds.w / 2; + const int screenY = static_cast(mPos.y) - static_cast(mPos.z) + + offsetY - mImage->mBounds.h / 2; + + // Check if on screen + if (screenX + mImage->mBounds.w < 0 || + screenX > graphics->mWidth || + screenY + mImage->mBounds.h < 0 || + screenY > graphics->mHeight) + { + return false; + } + + float alphafactor = mAlpha; + + if (mFadeOut && mLifetimeLeft > -1 && mLifetimeLeft < mFadeOut) + { + alphafactor *= static_cast(mLifetimeLeft) + / static_cast(mFadeOut); + } + + if (mFadeIn && mLifetimePast < mFadeIn) + { + alphafactor *= static_cast(mLifetimePast) + / static_cast(mFadeIn); + } + + mImage->setAlpha(alphafactor); + return graphics->drawImage(mImage, screenX, screenY); +} diff --git a/src/particle/imageparticle.h b/src/particle/imageparticle.h new file mode 100644 index 000000000..9217417fd --- /dev/null +++ b/src/particle/imageparticle.h @@ -0,0 +1,68 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#ifndef PARTICLE_IMAGEPARTICLE_H +#define PARTICLE_IMAGEPARTICLE_H + +#include "particle/particle.h" + +#include + +class Image; +class Map; + +/** + * A particle that uses an image for its visualization. + */ +class ImageParticle : public Particle +{ + public: + /** + * Constructor. The image is reference counted by this particle. + * + * @param map the map this particle appears on + * @param image an Image instance, may not be NULL + */ + ImageParticle(Map *const map, Image *const image); + + A_DELETE_COPY(ImageParticle) + + /** + * Destructor. + */ + ~ImageParticle(); + + /** + * Draws the particle image + */ + virtual bool draw(Graphics *const graphics, + const int offsetX, const int offsetY) const override; + + virtual void setAlpha(const float alpha) override + { mAlpha = alpha; } + + static std::map imageParticleCountByName; + protected: + Image *mImage; /**< The image used for this particle. */ +}; + +#endif // PARTICLE_IMAGEPARTICLE_H diff --git a/src/particle/particle.cpp b/src/particle/particle.cpp new file mode 100644 index 000000000..fa07fb235 --- /dev/null +++ b/src/particle/particle.cpp @@ -0,0 +1,478 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#include "particle/particle.h" + +#include "configuration.h" +#include "resources/dye.h" +#include "logger.h" +#include "map.h" + +#include "particle/animationparticle.h" +#include "particle/particleemitter.h" +#include "particle/rotationalparticle.h" +#include "particle/textparticle.h" + +#include "resources/resourcemanager.h" + +#include "gui/sdlfont.h" + +#include "utils/dtor.h" +#include "utils/mathutils.h" + +#include + +#include +#include + +#include "debug.h" + +static const float SIN45 = 0.707106781f; + +class Graphics; +class Image; + +int Particle::particleCount = 0; +int Particle::maxCount = 0; +int Particle::fastPhysics = 0; +int Particle::emitterSkip = 1; +bool Particle::enabled = true; +const float Particle::PARTICLE_SKY = 800.0f; + +Particle::Particle(Map *const map) : + Actor(), + mAlpha(1.0f), + mLifetimeLeft(-1), + mLifetimePast(0), + mFadeOut(0), + mFadeIn(0), + mVelocity(), + mAlive(ALIVE), + mChildEmitters(), + mChildParticles(), + mDeathEffect(), + mGravity(0.0f), + mBounce(0.0f), + mAcceleration(0.0f), + mInvDieDistance(-1.0f), + mMomentum(1.0f), + mTarget(nullptr), + mRandomness(0), + mDeathEffectConditions(0x00), + mAutoDelete(true), + mAllowSizeAdjust(false), + mFollow(false) +{ + setMap(map); + Particle::particleCount++; +} + +Particle::~Particle() +{ + // Delete child emitters and child particles + clear(); + Particle::particleCount--; +} + +void Particle::setupEngine() +{ + Particle::maxCount = config.getIntValue("particleMaxCount"); + Particle::fastPhysics = config.getIntValue("particleFastPhysics"); + Particle::emitterSkip = config.getIntValue("particleEmitterSkip") + 1; + if (!Particle::emitterSkip) + Particle::emitterSkip = 1; + Particle::enabled = config.getBoolValue("particleeffects"); + disableAutoDelete(); + logger->log1("Particle engine set up"); +} + +bool Particle::draw(Graphics *const, const int, const int) const +{ + return false; +} + +bool Particle::update() +{ + if (!mMap) + return false; + + if (mLifetimeLeft == 0 && mAlive == ALIVE) + mAlive = DEAD_TIMEOUT; + + const Vector oldPos = mPos; + + if (mAlive == ALIVE) + { + // calculate particle movement + if (mMomentum != 1.0f) + mVelocity *= mMomentum; + + if (mTarget && mAcceleration != 0.0f) + { + Vector dist = mPos - mTarget->mPos; + dist.x *= SIN45; + float invHypotenuse; + + switch (Particle::fastPhysics) + { + case 1: + invHypotenuse = fastInvSqrt( + dist.x * dist.x + dist.y * dist.y + dist.z * dist.z); + break; + case 2: + if (!dist.x) + { + invHypotenuse = 0; + break; + } + + invHypotenuse = 2.0f / (static_cast(fabs(dist.x)) + + static_cast(fabs(dist.y)) + + static_cast(fabs(dist.z))); + break; + default: + invHypotenuse = 1.0f / static_cast(sqrt( + dist.x * dist.x + dist.y * dist.y + dist.z * dist.z)); + break; + } + + if (invHypotenuse) + { + if (mInvDieDistance > 0.0f && invHypotenuse > mInvDieDistance) + mAlive = DEAD_IMPACT; + const float accFactor = invHypotenuse * mAcceleration; + mVelocity -= dist * accFactor; + } + } + + if (mRandomness > 0) + { + mVelocity.x += static_cast((rand() % mRandomness - rand() + % mRandomness)) / 1000.0f; + mVelocity.y += static_cast((rand() % mRandomness - rand() + % mRandomness)) / 1000.0f; + mVelocity.z += static_cast((rand() % mRandomness - rand() + % mRandomness)) / 1000.0f; + } + + mVelocity.z -= mGravity; + + // Update position + mPos.x += mVelocity.x; + mPos.y += mVelocity.y * SIN45; + mPos.z += mVelocity.z * SIN45; + + // Update other stuff + if (mLifetimeLeft > 0) + mLifetimeLeft--; + + mLifetimePast++; + + if (mPos.z < 0.0f) + { + if (mBounce > 0.0f) + { + mPos.z *= -mBounce; + mVelocity *= mBounce; + mVelocity.z = -mVelocity.z; + } + else + { + mAlive = DEAD_FLOOR; + } + } + else if (mPos.z > PARTICLE_SKY) + { + mAlive = DEAD_SKY; + } + + // Update child emitters + if (Particle::emitterSkip && (mLifetimePast - 1) + % Particle::emitterSkip == 0) + { + FOR_EACH (EmitterConstIterator, e, mChildEmitters) + { + Particles newParticles = (*e)->createParticles(mLifetimePast); + FOR_EACH (ParticleConstIterator, it, newParticles) + { + Particle *const p = *it; + p->moveBy(mPos); + mChildParticles.push_back(p); + } + } + } + } + + // create death effect when the particle died + if (mAlive != ALIVE && mAlive != DEAD_LONG_AGO) + { + if ((mAlive & mDeathEffectConditions) > 0x00 && !mDeathEffect.empty()) + { + Particle *const deathEffect = particleEngine->addEffect( + mDeathEffect, 0, 0); + if (deathEffect) + deathEffect->moveBy(mPos); + } + mAlive = DEAD_LONG_AGO; + } + + const Vector change = mPos - oldPos; + + // Update child particles + + for (ParticleIterator p = mChildParticles.begin(), + p2 = mChildParticles.end(); p != p2; ) + { + Particle *const particle = *p; + // move particle with its parent if desired + if (particle->mFollow) + particle->moveBy(change); + + // update particle + if (particle->update()) + { + ++p; + } + else + { + delete particle; + p = mChildParticles.erase(p); + } + } + if (mAlive != ALIVE && mChildParticles.empty() && mAutoDelete) + return false; + + return true; +} + +void Particle::moveBy(const Vector &change) +{ + mPos += change; + FOR_EACH (ParticleConstIterator, p, mChildParticles) + { + Particle *const particle = *p; + if (particle->mFollow) + particle->moveBy(change); + } +} + +void Particle::moveTo(const float x, const float y) +{ + moveTo(Vector(x, y, mPos.z)); +} + +Particle *Particle::createChild() +{ + Particle *const newParticle = new Particle(mMap); + mChildParticles.push_back(newParticle); + return newParticle; +} + +Particle *Particle::addEffect(const std::string &particleEffectFile, + const int pixelX, const int pixelY, + const int rotation) +{ + Particle *newParticle = nullptr; + + const size_t pos = particleEffectFile.find('|'); + const std::string dyePalettes = (pos != std::string::npos) + ? particleEffectFile.substr(pos + 1) : ""; + XML::Document doc(particleEffectFile.substr(0, pos)); + const XmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlNameEqual(rootNode, "effect")) + { + logger->log("Error loading particle: %s", particleEffectFile.c_str()); + return nullptr; + } + + ResourceManager *const resman = ResourceManager::getInstance(); + + // Parse particles + for_each_xml_child_node(effectChildNode, rootNode) + { + // We're only interested in particles + if (!xmlNameEqual(effectChildNode, "particle")) + continue; + + // Determine the exact particle type + XmlNodePtr node; + + // Animation + if ((node = XML::findFirstChildByName(effectChildNode, "animation"))) + { + newParticle = new AnimationParticle(mMap, node, dyePalettes); + } + // Rotational + else if ((node = XML::findFirstChildByName( + effectChildNode, "rotation"))) + { + newParticle = new RotationalParticle(mMap, node, dyePalettes); + } + // Image + else if ((node = XML::findFirstChildByName(effectChildNode, "image"))) + { + std::string imageSrc = reinterpret_cast( + node->xmlChildrenNode->content); + if (!imageSrc.empty() && !dyePalettes.empty()) + Dye::instantiate(imageSrc, dyePalettes); + Image *const img = resman->getImage(imageSrc); + + newParticle = new ImageParticle(mMap, img); + } + // Other + else + { + newParticle = new Particle(mMap); + } + + // Read and set the basic properties of the particle + const float offsetX = static_cast(XML::getFloatProperty( + effectChildNode, "position-x", 0)); + const float offsetY = static_cast(XML::getFloatProperty( + effectChildNode, "position-y", 0)); + const float offsetZ = static_cast(XML::getFloatProperty( + effectChildNode, "position-z", 0)); + const Vector position(mPos.x + static_cast(pixelX) + offsetX, + mPos.y + static_cast(pixelY) + offsetY, + mPos.z + offsetZ); + newParticle->moveTo(position); + + const int lifetime = XML::getProperty(effectChildNode, "lifetime", -1); + newParticle->setLifetime(lifetime); + const bool resizeable = "false" != XML::getProperty(effectChildNode, + "size-adjustable", "false"); + + newParticle->setAllowSizeAdjust(resizeable); + + // Look for additional emitters for this particle + for_each_xml_child_node(emitterNode, effectChildNode) + { + if (xmlNameEqual(emitterNode, "emitter")) + { + ParticleEmitter *const newEmitter = new ParticleEmitter( + emitterNode, newParticle, mMap, rotation, dyePalettes); + newParticle->addEmitter(newEmitter); + } + else if (xmlNameEqual(emitterNode, "deatheffect")) + { + const std::string deathEffect = reinterpret_cast( + emitterNode->xmlChildrenNode->content); + + char deathEffectConditions = 0x00; + if (XML::getBoolProperty(emitterNode, "on-floor", true)) + { + deathEffectConditions += static_cast( + Particle::DEAD_FLOOR); + } + if (XML::getBoolProperty(emitterNode, "on-sky", true)) + { + deathEffectConditions += static_cast( + Particle::DEAD_SKY); + } + if (XML::getBoolProperty(emitterNode, "on-other", false)) + { + deathEffectConditions += static_cast( + Particle::DEAD_OTHER); + } + if (XML::getBoolProperty(emitterNode, "on-impact", true)) + { + deathEffectConditions += static_cast( + Particle::DEAD_IMPACT); + } + if (XML::getBoolProperty(emitterNode, "on-timeout", true)) + { + deathEffectConditions += static_cast( + Particle::DEAD_TIMEOUT); + } + newParticle->setDeathEffect( + deathEffect, deathEffectConditions); + } + } + + mChildParticles.push_back(newParticle); + } + + return newParticle; +} + +Particle *Particle::addTextSplashEffect(const std::string &text, + const int x, const int y, + const gcn::Color *const color, + gcn::Font *const font, + const bool outline) +{ + Particle *const newParticle = new TextParticle( + mMap, text, color, font, outline); + newParticle->moveTo(static_cast(x), static_cast(y)); + newParticle->setVelocity( + static_cast((rand() % 100) - 50) / 200.0f, // X + static_cast((rand() % 100) - 50) / 200.0f, // Y + (static_cast((rand() % 100)) / 200.0f) + 4.0f); // Z + + newParticle->setGravity(0.1f); + newParticle->setBounce(0.5f); + newParticle->setLifetime(200); + newParticle->setFadeOut(100); + + mChildParticles.push_back(newParticle); + + return newParticle; +} + +Particle *Particle::addTextRiseFadeOutEffect(const std::string &text, + const int x, const int y, + const gcn::Color *const color, + gcn::Font *const font, + const bool outline) +{ + Particle *const newParticle = new TextParticle( + mMap, text, color, font, outline); + newParticle->moveTo(static_cast(x), static_cast(y)); + newParticle->setVelocity(0.0f, 0.0f, 0.5f); + newParticle->setGravity(0.0015f); + newParticle->setLifetime(300); + newParticle->setFadeOut(100); + newParticle->setFadeIn(0); + + mChildParticles.push_back(newParticle); + + return newParticle; +} + +void Particle::adjustEmitterSize(const int w, const int h) +{ + if (mAllowSizeAdjust) + { + FOR_EACH (EmitterConstIterator, e, mChildEmitters) + (*e)->adjustSize(w, h); + } +} + +void Particle::clear() +{ + delete_all(mChildEmitters); + mChildEmitters.clear(); + + delete_all(mChildParticles); + mChildParticles.clear(); +} diff --git a/src/particle/particle.h b/src/particle/particle.h new file mode 100644 index 000000000..29cc7c0b4 --- /dev/null +++ b/src/particle/particle.h @@ -0,0 +1,366 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#ifndef PARTICLE_PARTICLE_H +#define PARTICLE_PARTICLE_H + +#include "actor.h" +#include "localconsts.h" + +#include +#include + +class Map; +class Particle; +class ParticleEmitter; +class SDLFont; + +namespace gcn +{ + class Color; + class Font; +} + +typedef std::list Particles; +typedef Particles::iterator ParticleIterator; +typedef Particles::const_iterator ParticleConstIterator; +typedef std::list Emitters; +typedef Emitters::iterator EmitterIterator; +typedef Emitters::const_iterator EmitterConstIterator; + +/** + * A particle spawned by a ParticleEmitter. + */ +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 + static int maxCount; // Maximum number of particles + static int emitterSkip; // Duration of pause between two + // emitter updates in ticks + static bool enabled; // true when non-crucial particle effects + // are disabled + + /** + * Constructor. + * + * @param map the map this particle will add itself to, may be nullptr + */ + explicit Particle(Map *const map); + + A_DELETE_COPY(Particle) + + /** + * Destructor. + */ + ~Particle(); + + /** + * Deletes all child particles and emitters. + */ + void clear(); + + /** + * Gives a particle the properties of an engine root particle and loads + * the particle-related config settings. + */ + void setupEngine(); + + /** + * Updates particle position, returns false when the particle should + * be deleted. + */ + virtual bool update(); + + /** + * Draws the particle image. + */ + virtual bool draw(Graphics *const graphics, + const int offsetX, const int offsetY) const override; + + /** + * Necessary for sorting with the other sprites. + */ + virtual int getPixelY() const override A_WARN_UNUSED + { return static_cast(mPos.y) - 16; } + + /** + * Necessary for sorting with the other sprites for sorting only. + */ + virtual int getSortPixelY() const override A_WARN_UNUSED + { return static_cast(mPos.y) - 16; } + + /** + * Creates a blank particle as a child of the current particle + * Useful for creating target particles + */ + Particle *createChild(); + + /** + * Creates a child particle that hosts some emitters described in the + * particleEffectFile. + */ + Particle *addEffect(const std::string &particleEffectFile, + const int pixelX, const int pixelY, + const int rotation = 0); + + /** + * Creates a standalone text particle. + */ + Particle *addTextSplashEffect(const std::string &text, + const int x, const int y, + const gcn::Color *const color, + gcn::Font *const font, + const bool outline = false); + + /** + * Creates a standalone text particle. + */ + Particle *addTextRiseFadeOutEffect(const std::string &text, + const int x, const int y, + const gcn::Color *const color, + gcn::Font *const font, + const bool outline = false); + + /** + * Adds an emitter to the particle. + */ + void addEmitter(ParticleEmitter *const emitter) + { mChildEmitters.push_back(emitter); } + + /** + * Sets the position in 3 dimensional space in pixels relative to map. + */ + void moveTo(const Vector &pos) + { moveBy(pos - mPos); } + + /** + * Sets the position in 2 dimensional space in pixels relative to map. + */ + void moveTo(const float x, const float y); + + /** + * Changes the particle position relative + */ + void moveBy(const Vector &change); + + /** + * Sets the time in game ticks until the particle is destroyed. + */ + void setLifetime(const int lifetime) + { mLifetimeLeft = lifetime; mLifetimePast = 0; } + + /** + * Sets the age of the pixel in game ticks where the particle has + * faded in completely. + */ + void setFadeOut(const int fadeOut) + { mFadeOut = fadeOut; } + + /** + * Sets the remaining particle lifetime where the particle starts to + * fade out. + */ + void setFadeIn(const int fadeIn) + { mFadeIn = fadeIn; } + + /** + * Sets the current velocity in 3 dimensional space. + */ + void setVelocity(const float x, const float y, const float z) + { mVelocity.x = x; mVelocity.y = y; mVelocity.z = z; } + + /** + * Sets the downward acceleration. + */ + void setGravity(const float gravity) + { mGravity = gravity; } + + /** + * Sets the ammount of random vector changes + */ + void setRandomness(const int r) + { mRandomness = r; } + + /** + * Sets the ammount of velocity particles retain after + * hitting the ground. + */ + void setBounce(const float bouncieness) + { mBounce = bouncieness; } + + /** + * Sets the flag if the particle is supposed to be moved by its parent + */ + void setFollow(const bool follow) + { mFollow = follow; } + + /** + * Gets the flag if the particle is supposed to be moved by its parent + */ + bool doesFollow() const A_WARN_UNUSED + { return mFollow; } + + /** + * Makes the particle move toward another particle with a + * given acceleration and momentum + */ + void setDestination(Particle *const target, + const float accel, const float moment) + { mTarget = target; mAcceleration = accel; mMomentum = moment; } + + /** + * Sets the distance in pixel the particle can come near the target + * particle before it is destroyed. Does only make sense after a target + * particle has been set using setDestination. + */ + void setDieDistance(const float dist) + { mInvDieDistance = 1.0f / dist; } + + /** + * Changes the size of the emitters so that the effect fills a + * rectangle of this size + */ + void adjustEmitterSize(const int w, const int h); + + void setAllowSizeAdjust(const bool adjust) + { mAllowSizeAdjust = adjust; } + + bool isAlive() const A_WARN_UNUSED + { return mAlive == ALIVE; } + + /** + * Determines whether the particle and its children are all dead + */ + bool isExtinct() const A_WARN_UNUSED + { return !isAlive() && mChildParticles.empty(); } + + /** + * Manually marks the particle for deletion. + */ + void kill() + { mAlive = DEAD_OTHER; mAutoDelete = true; } + + /** + * After calling this function the particle will only request + * deletion when kill() is called + */ + void disableAutoDelete() + { mAutoDelete = false; } + + /** We consider particles (at least for now) to be one layer-sprites */ + virtual int getNumberOfLayers() const + { return 1; } + + virtual float getAlpha() const + { return 1.0f; } + + virtual void setAlpha(const float alpha A_UNUSED) override + { } + + virtual void setDeathEffect(const std::string &effectFile, + const signed char conditions) + { mDeathEffect = effectFile; mDeathEffectConditions = conditions; } + + protected: + // Opacity of the graphical representation of the particle + float mAlpha; + + // Lifetime left in game ticks + int mLifetimeLeft; + + // Age of the particle in game ticks + int mLifetimePast; + + // Lifetime in game ticks left where fading out begins + int mFadeOut; + + // Age in game ticks where fading in is finished + int mFadeIn; + + // Speed in pixels per game-tick. + Vector mVelocity; + + // Is the particle supposed to be drawn and updated? + AliveStatus mAlive; + private: + // List of child emitters. + Emitters mChildEmitters; + + // List of particles controlled by this particle + Particles mChildParticles; + + // Particle effect file to be spawned when the particle dies + std::string mDeathEffect; + + // dynamic particle + // Downward acceleration in pixels per game-tick. + float mGravity; + + // How much the particle bounces off when hitting the ground + float mBounce; + + // Acceleration towards the target particle in pixels per game-tick + float mAcceleration; + + // Distance in pixels from the target particle that causes + // the destruction of the particle + float mInvDieDistance; + + // How much speed the particle retains after each game tick + float mMomentum; + + // The particle that attracts this particle + Particle *mTarget; + + // Ammount of random vector change + int mRandomness; + + // Bitfield of death conditions which trigger spawning + // of the death particle + signed char mDeathEffectConditions; + + // May the particle request its deletion by the parent particle? + bool mAutoDelete; + + // Can the effect size be adjusted by the object props in the map file? + bool mAllowSizeAdjust; + + // is this particle moved when its parent particle moves? + bool mFollow; +}; + +extern Particle *particleEngine; + +#endif // PARTICLE_PARTICLE_H diff --git a/src/particle/particlecontainer.cpp b/src/particle/particlecontainer.cpp new file mode 100644 index 000000000..1695d55f2 --- /dev/null +++ b/src/particle/particlecontainer.cpp @@ -0,0 +1,196 @@ +/* + * The ManaPlus Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#include "particle/particle.h" +#include "particle/particlecontainer.h" + +#include "debug.h" + +typedef std::list::iterator ParticleListIter; +typedef std::list::const_iterator ParticleListCIter; + +ParticleContainer::ParticleContainer(ParticleContainer *const parent, + const bool delParent): + mNext(parent), + mDelParent(delParent) +{ +} + +ParticleContainer::~ParticleContainer() +{ + clearLocally(); + if (mDelParent) + { + delete mNext; + mNext = nullptr; + } +} + +void ParticleContainer::clear() +{ + clearLocally(); + if (mNext) + mNext->clear(); +} + +void ParticleContainer::moveTo(const float x, const float y) +{ + if (mNext) + mNext->moveTo(x, y); +} + +// -- particle list ---------------------------------------- + +ParticleList::ParticleList(ParticleContainer *const parent, + const bool delParent) : + ParticleContainer(parent, delParent), + mElements() +{} + +ParticleList::~ParticleList() +{} + +void ParticleList::addLocally(Particle *const particle) +{ + if (particle) + { + // The effect may not die without the beings permission or we segfault + particle->disableAutoDelete(); + mElements.push_back(particle); + } +} + +void ParticleList::removeLocally(const Particle *const particle) +{ + for (std::list::iterator it = mElements.begin(); + it != mElements.end(); ) + { + Particle *const p = *it; + if (p == particle) + { + p->kill(); + it = mElements.erase(it); + } + else + { + ++it; + } + } +} + +void ParticleList::clearLocally() +{ + FOR_EACH (ParticleListCIter, it, mElements) + (*it)->kill(); + + mElements.clear(); +} + +void ParticleList::moveTo(const float x, const float y) +{ + ParticleContainer::moveTo(x, y); + + for (std::list::iterator it = mElements.begin(); + it != mElements.end(); ) + { + Particle *const p = *it; + p->moveTo(x, y); + if (p->isExtinct()) + { + p->kill(); + it = mElements.erase(it); + } + else + { + ++it; + } + } +} + +// -- particle vector ---------------------------------------- + +ParticleVector::ParticleVector(ParticleContainer *const parent, + const bool delParent) : + ParticleContainer(parent, delParent), + mIndexedElements() +{} + +ParticleVector::~ParticleVector() +{} + +void ParticleVector::setLocally(const int index, Particle *const particle) +{ + if (index < 0) + return; + + delLocally(index); + + if (mIndexedElements.size() <= static_cast(index)) + mIndexedElements.resize(index + 1, nullptr); + + if (particle) + particle->disableAutoDelete(); + mIndexedElements[index] = particle; +} + +void ParticleVector::delLocally(const int index) +{ + if (index < 0) + return; + + if (mIndexedElements.size() <= static_cast(index)) + return; + + Particle *const p = mIndexedElements[index]; + if (p) + { + mIndexedElements[index] = nullptr; + p->kill(); + } +} + +void ParticleVector::clearLocally() +{ + for (unsigned int i = 0; i < mIndexedElements.size(); i++) + delLocally(i); +} + +void ParticleVector::moveTo(const float x, const float y) +{ + ParticleContainer::moveTo(x, y); + + for (std::vector::iterator it = mIndexedElements.begin(); + it != mIndexedElements.end(); ++it) + { + Particle *const p = *it; + if (p) + { + p->moveTo(x, y); + + if (p->isExtinct()) + { + p->kill(); + *it = nullptr; + } + } + } +} diff --git a/src/particle/particlecontainer.h b/src/particle/particlecontainer.h new file mode 100644 index 000000000..a348f4cef --- /dev/null +++ b/src/particle/particlecontainer.h @@ -0,0 +1,138 @@ +/* + * The ManaPlus Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#ifndef PARTICLE_PARTICLECONTAINER_H +#define PARTICLE_PARTICLECONTAINER_H + +#include +#include + +#include "localconsts.h" + +class Particle; + +/** + * Set of particle effects. May be stacked with other ParticleContainers. All + * operations herein affect such stacked containers, unless the operations end + * in `Locally'. + */ +class ParticleContainer +{ +public: + /** + * Constructs a new particle container and assumes responsibility for + * its parent (for all operations defined herein, except when ending in `Locally') + * + * delParent means that the destructor should also free the parent. + */ + explicit ParticleContainer(ParticleContainer *const parent = nullptr, + const bool delParent = true); + + A_DELETE_COPY(ParticleContainer) + + virtual ~ParticleContainer(); + + /** + * Kills and removes all particle effects + */ + void clear(); + + /** + * Kills and removes all particle effects (only in this container) + */ + virtual void clearLocally() + { } + + /** + * Sets the positions of all elements + */ + virtual void moveTo(const float x, const float y); + +protected: + ParticleContainer *mNext; /**< Contained container, if any */ + bool mDelParent; /**< Delete mNext in destructor */ +}; + +/** + * Linked list of particle effects. + */ +class ParticleList final : public ParticleContainer +{ +public: + explicit ParticleList(ParticleContainer *const parent = nullptr, + const bool delParent = true); + + A_DELETE_COPY(ParticleList) + + virtual ~ParticleList(); + + /** + * Takes control of and adds a particle + */ + void addLocally(Particle *const particle); + + /** + * `kills' and removes a particle + */ + void removeLocally(const Particle *const particle); + + virtual void clearLocally() override; + + virtual void moveTo(const float x, const float y) override; + +protected: + std::list mElements; /**< Contained particle effects */ +}; + +/** + * Particle container with indexing facilities + */ +class ParticleVector final : public ParticleContainer +{ +public: + explicit ParticleVector(ParticleContainer *const parent = nullptr, + const bool delParent = true); + + A_DELETE_COPY(ParticleVector) + + virtual ~ParticleVector(); + + /** + * Sets a particle at a specified index. Kills the previous particle + * there, if needed. + */ + virtual void setLocally(const int index, Particle *const particle); + + /** + * Removes a particle at a specified index + */ + virtual void delLocally(const int index); + + virtual void clearLocally() override; + + virtual void moveTo(const float x, const float y) override; + +protected: + std::vector mIndexedElements; +}; + +#endif // PARTICLE_PARTICLECONTAINER_H diff --git a/src/particle/particleemitter.cpp b/src/particle/particleemitter.cpp new file mode 100644 index 000000000..9e273e4a1 --- /dev/null +++ b/src/particle/particleemitter.cpp @@ -0,0 +1,674 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#include "particle/particleemitter.h" + +#include "logger.h" + +#include "particle/animationparticle.h" +#include "particle/rotationalparticle.h" + +#include "resources/dye.h" +#include "resources/image.h" +#include "resources/imageset.h" +#include "resources/resourcemanager.h" + +#include + +#include "debug.h" + +static const float SIN45 = 0.707106781f; +static const float DEG_RAD_FACTOR = 0.017453293f; + +typedef std::vector::const_iterator ImageSetVectorCIter; +typedef std::list::const_iterator ParticleEmitterListCIter; + +ParticleEmitter::ParticleEmitter(const XmlNodePtr emitterNode, + Particle *const target, + Map *const map, const int rotation, + const std::string& dyePalettes) : + mParticleTarget(target), + mMap(map), + mParticleImage(nullptr), + mOutputPauseLeft(0), + mDeathEffectConditions(0), + mParticleFollow(false) +{ + // Initializing default values + mParticlePosX.set(0.0f); + mParticlePosY.set(0.0f); + mParticlePosZ.set(0.0f); + mParticleAngleHorizontal.set(0.0f); + mParticleAngleVertical.set(0.0f); + mParticlePower.set(0.0f); + mParticleGravity.set(0.0f); + mParticleRandomness.set(0); + mParticleBounce.set(0.0f); + mParticleAcceleration.set(0.0f); + mParticleDieDistance.set(-1.0f); + mParticleMomentum.set(1.0f); + mParticleLifetime.set(-1); + mParticleFadeOut.set(0); + mParticleFadeIn.set(0); + mOutput.set(1); + mOutputPause.set(0); + mParticleAlpha.set(1.0f); + + for_each_xml_child_node(propertyNode, emitterNode) + { + if (xmlNameEqual(propertyNode, "property")) + { + const std::string name = XML::getProperty( + propertyNode, "name", ""); + + if (name == "position-x") + { + mParticlePosX = readParticleEmitterProp(propertyNode, 0.0f); + } + else if (name == "position-y") + { + mParticlePosY = readParticleEmitterProp(propertyNode, 0.0f); + mParticlePosY.minVal *= SIN45; + mParticlePosY.maxVal *= SIN45; + mParticlePosY.changeAmplitude *= SIN45; + } + else if (name == "position-z") + { + mParticlePosZ = readParticleEmitterProp(propertyNode, 0.0f); + mParticlePosZ.minVal *= SIN45; + mParticlePosZ.maxVal *= SIN45; + mParticlePosZ.changeAmplitude *= SIN45; + } + else if (name == "image") + { + std::string image = XML::getProperty( + propertyNode, "value", ""); + // Don't leak when multiple images are defined + if (!image.empty() && !mParticleImage) + { + if (!dyePalettes.empty()) + Dye::instantiate(image, dyePalettes); + + ResourceManager *const resman + = ResourceManager::getInstance(); + mParticleImage = resman->getImage(image); + } + } + else if (name == "subimage") + { + std::string image = XML::getProperty( + propertyNode, "value", ""); + // Don't leak when multiple images are defined + if (!image.empty() && !mParticleImage) + { + if (!dyePalettes.empty()) + Dye::instantiate(image, dyePalettes); + + ResourceManager *const resman + = ResourceManager::getInstance(); + Image *img = resman->getImage(image); + if (img) + { + mParticleImage = resman->getSubImage(img, + XML::getProperty(propertyNode, "x", 0), + XML::getProperty(propertyNode, "y", 0), + XML::getProperty(propertyNode, "width", 0), + XML::getProperty(propertyNode, "height", 0)); + img->decRef(); + } + else + { + mParticleImage = nullptr; + } + } + } + else if (name == "horizontal-angle") + { + mParticleAngleHorizontal = + readParticleEmitterProp(propertyNode, 0.0f); + mParticleAngleHorizontal.minVal + += static_cast(rotation); + mParticleAngleHorizontal.minVal *= DEG_RAD_FACTOR; + mParticleAngleHorizontal.maxVal + += static_cast(rotation); + mParticleAngleHorizontal.maxVal *= DEG_RAD_FACTOR; + mParticleAngleHorizontal.changeAmplitude *= DEG_RAD_FACTOR; + } + else if (name == "vertical-angle") + { + mParticleAngleVertical = + readParticleEmitterProp(propertyNode, 0.0f); + mParticleAngleVertical.minVal *= DEG_RAD_FACTOR; + mParticleAngleVertical.maxVal *= DEG_RAD_FACTOR; + mParticleAngleVertical.changeAmplitude *= DEG_RAD_FACTOR; + } + else if (name == "power") + { + mParticlePower = readParticleEmitterProp(propertyNode, 0.0f); + } + else if (name == "gravity") + { + mParticleGravity = readParticleEmitterProp(propertyNode, 0.0f); + } + else if (name == "randomnes" + || name == "randomness") // legacy bug + { + mParticleRandomness = readParticleEmitterProp(propertyNode, 0); + } + else if (name == "bounce") + { + mParticleBounce = readParticleEmitterProp(propertyNode, 0.0f); + } + else if (name == "lifetime") + { + mParticleLifetime = readParticleEmitterProp(propertyNode, 0); + mParticleLifetime.minVal += 1; + } + else if (name == "output") + { + mOutput = readParticleEmitterProp(propertyNode, 0); + mOutput.maxVal += 1; + } + else if (name == "output-pause") + { + mOutputPause = readParticleEmitterProp(propertyNode, 0); + mOutputPauseLeft = mOutputPause.value(0); + } + else if (name == "acceleration") + { + mParticleAcceleration = readParticleEmitterProp( + propertyNode, 0.0f); + } + else if (name == "die-distance") + { + mParticleDieDistance = readParticleEmitterProp( + propertyNode, 0.0f); + } + else if (name == "momentum") + { + mParticleMomentum = readParticleEmitterProp( + propertyNode, 1.0f); + } + else if (name == "fade-out") + { + mParticleFadeOut = readParticleEmitterProp(propertyNode, 0); + } + else if (name == "fade-in") + { + mParticleFadeIn = readParticleEmitterProp(propertyNode, 0); + } + else if (name == "alpha") + { + mParticleAlpha = readParticleEmitterProp(propertyNode, 1.0f); + } + else if (name == "follow-parent") + { + mParticleFollow = true; + } + else + { + logger->log("Particle Engine: Warning, " + "unknown emitter property \"%s\"", + name.c_str()); + } + } + else if (xmlNameEqual(propertyNode, "emitter")) + { + ParticleEmitter newEmitter(propertyNode, mParticleTarget, map, + rotation, dyePalettes); + mParticleChildEmitters.push_back(newEmitter); + } + else if (xmlNameEqual(propertyNode, "rotation")) + { + ImageSet *const imageset = getImageSet(propertyNode); + if (!imageset) + { + logger->log1("Error: no valid imageset"); + continue; + } + mTempSets.push_back(imageset); + + // Get animation frames + for_each_xml_child_node(frameNode, propertyNode) + { + const int delay = XML::getIntProperty( + frameNode, "delay", 0, 0, 100000); + const int offsetX = XML::getProperty(frameNode, "offsetX", 0) + - imageset->getWidth() / 2 + 16; + const int offsetY = XML::getProperty(frameNode, "offsetY", 0) + - imageset->getHeight() + 32; + const int rand = XML::getIntProperty( + frameNode, "rand", 100, 0, 100); + + if (xmlNameEqual(frameNode, "frame")) + { + const int index = XML::getProperty(frameNode, "index", -1); + + if (index < 0) + { + logger->log1("No valid value for 'index'"); + continue; + } + + Image *const img = imageset->get(index); + + if (!img) + { + logger->log("No image at index %d", index); + continue; + } + + mParticleRotation.addFrame(img, delay, + offsetX, offsetY, rand); + } + else if (xmlNameEqual(frameNode, "sequence")) + { + int start = XML::getProperty(frameNode, "start", -1); + const int end = XML::getProperty(frameNode, "end", -1); + + if (start < 0 || end < 0) + { + logger->log1("No valid value for 'start' or 'end'"); + continue; + } + + while (end >= start) + { + Image *const img = imageset->get(start); + if (!img) + { + logger->log("No image at index %d", start); + continue; + } + + mParticleRotation.addFrame(img, delay, + offsetX, offsetY, rand); + start ++; + } + } + else if (xmlNameEqual(frameNode, "end")) + { + mParticleRotation.addTerminator(rand); + } + } // for frameNode + } + else if (xmlNameEqual(propertyNode, "animation")) + { + ImageSet *const imageset = getImageSet(propertyNode); + if (!imageset) + { + logger->log1("Error: no valid imageset"); + continue; + } + mTempSets.push_back(imageset); + + // Get animation frames + for_each_xml_child_node(frameNode, propertyNode) + { + const int delay = XML::getIntProperty( + frameNode, "delay", 0, 0, 100000); + const int offsetX = XML::getProperty(frameNode, "offsetX", 0) + - imageset->getWidth() / 2 + 16; + const int offsetY = XML::getProperty(frameNode, "offsetY", 0) + - imageset->getHeight() + 32; + const int rand = XML::getIntProperty( + frameNode, "rand", 100, 0, 100); + + if (xmlNameEqual(frameNode, "frame")) + { + const int index = XML::getProperty(frameNode, "index", -1); + if (index < 0) + { + logger->log1("No valid value for 'index'"); + continue; + } + + Image *const img = imageset->get(index); + + if (!img) + { + logger->log("No image at index %d", index); + continue; + } + + mParticleAnimation.addFrame(img, delay, + offsetX, offsetY, rand); + } + else if (xmlNameEqual(frameNode, "sequence")) + { + int start = XML::getProperty(frameNode, "start", -1); + const int end = XML::getProperty(frameNode, "end", -1); + + if (start < 0 || end < 0) + { + logger->log1("No valid value for 'start' or 'end'"); + continue; + } + + while (end >= start) + { + Image *const img = imageset->get(start); + + if (!img) + { + logger->log("No image at index %d", start); + continue; + } + + mParticleAnimation.addFrame(img, delay, + offsetX, offsetY, rand); + start++; + } + } + else if (xmlNameEqual(frameNode, "end")) + { + mParticleAnimation.addTerminator(rand); + } + } // for frameNode + } + else if (xmlNameEqual(propertyNode, "deatheffect")) + { + mDeathEffect = reinterpret_cast( + propertyNode->xmlChildrenNode->content); + mDeathEffectConditions = 0x00; + if (XML::getBoolProperty(propertyNode, "on-floor", true)) + { + mDeathEffectConditions += static_cast( + Particle::DEAD_FLOOR); + } + if (XML::getBoolProperty(propertyNode, "on-sky", true)) + { + mDeathEffectConditions += static_cast( + Particle::DEAD_SKY); + } + if (XML::getBoolProperty(propertyNode, "on-other", false)) + { + mDeathEffectConditions += static_cast( + Particle::DEAD_OTHER); + } + if (XML::getBoolProperty(propertyNode, "on-impact", true)) + { + mDeathEffectConditions += static_cast( + Particle::DEAD_IMPACT); + } + if (XML::getBoolProperty(propertyNode, "on-timeout", true)) + { + mDeathEffectConditions += static_cast( + Particle::DEAD_TIMEOUT); + } + } + } +} + +ParticleEmitter::ParticleEmitter(const ParticleEmitter &o) +{ + *this = o; +} + +ImageSet *ParticleEmitter::getImageSet(XmlNodePtr node) +{ + ResourceManager *const resman = ResourceManager::getInstance(); + ImageSet *imageset = nullptr; + const int subX = XML::getProperty(node, "subX", -1); + if (subX != -1) + { + Image *const img = resman->getImage(XML::getProperty( + node, "imageset", "")); + if (!img) + return nullptr; + + Image *const img2 = resman->getSubImage(img, subX, + XML::getProperty(node, "subY", 0), + XML::getProperty(node, "subWidth", 0), + XML::getProperty(node, "subHeight", 0)); + if (!img2) + { + img->decRef(); + return nullptr; + } + + imageset = resman->getSubImageSet(img2, + XML::getProperty(node, "width", 0), + XML::getProperty(node, "height", 0)); + img2->decRef(); + img->decRef(); + } + else + { + imageset = resman->getImageSet( + XML::getProperty(node, "imageset", ""), + XML::getProperty(node, "width", 0), + XML::getProperty(node, "height", 0)); + } + return imageset; +} + +ParticleEmitter & ParticleEmitter::operator=(const ParticleEmitter &o) +{ + mParticlePosX = o.mParticlePosX; + mParticlePosY = o.mParticlePosY; + mParticlePosZ = o.mParticlePosZ; + mParticleAngleHorizontal = o.mParticleAngleHorizontal; + mParticleAngleVertical = o.mParticleAngleVertical; + mParticlePower = o.mParticlePower; + mParticleGravity = o.mParticleGravity; + mParticleRandomness = o.mParticleRandomness; + mParticleBounce = o.mParticleBounce; + mParticleFollow = o.mParticleFollow; + mParticleTarget = o.mParticleTarget; + mParticleAcceleration = o.mParticleAcceleration; + mParticleDieDistance = o.mParticleDieDistance; + mParticleMomentum = o.mParticleMomentum; + mParticleLifetime = o.mParticleLifetime; + mParticleFadeOut = o.mParticleFadeOut; + mParticleFadeIn = o.mParticleFadeIn; + mParticleAlpha = o.mParticleAlpha; + mMap = o.mMap; + mOutput = o.mOutput; + mOutputPause = o.mOutputPause; + mParticleImage = o.mParticleImage; + mParticleAnimation = o.mParticleAnimation; + mParticleRotation = o.mParticleRotation; + mParticleChildEmitters = o.mParticleChildEmitters; + mDeathEffectConditions = o.mDeathEffectConditions; + mDeathEffect = o.mDeathEffect; + mTempSets = o.mTempSets; + + FOR_EACH (ImageSetVectorCIter, i, mTempSets) + { + if (*i) + (*i)->incRef(); + } + + mOutputPauseLeft = 0; + + if (mParticleImage) + mParticleImage->incRef(); + + return *this; +} + +ParticleEmitter::~ParticleEmitter() +{ + FOR_EACH (ImageSetVectorCIter, i, mTempSets) + { + if (*i) + (*i)->decRef(); + } + mTempSets.clear(); + + if (mParticleImage) + { + mParticleImage->decRef(); + mParticleImage = nullptr; + } +} + +template ParticleEmitterProp +ParticleEmitter::readParticleEmitterProp(XmlNodePtr propertyNode, T def) +{ + ParticleEmitterProp retval; + + def = static_cast(XML::getFloatProperty(propertyNode, "value", + static_cast(def))); + retval.set(static_cast(XML::getFloatProperty(propertyNode, "min", + static_cast(def))), static_cast(XML::getFloatProperty( + propertyNode, "max", static_cast(def)))); + + const std::string change = XML::getProperty( + propertyNode, "change-func", "none"); + T amplitude = static_cast(XML::getFloatProperty(propertyNode, + "change-amplitude", 0.0)); + + const int period = XML::getProperty(propertyNode, "change-period", 0); + const int phase = XML::getProperty(propertyNode, "change-phase", 0); + if (change == "saw" || change == "sawtooth") + retval.setFunction(FUNC_SAW, amplitude, period, phase); + else if (change == "sine" || change == "sinewave") + retval.setFunction(FUNC_SINE, amplitude, period, phase); + else if (change == "triangle") + retval.setFunction(FUNC_TRIANGLE, amplitude, period, phase); + else if (change == "square") + retval.setFunction(FUNC_SQUARE, amplitude, period, phase); + + return retval; +} + +std::list ParticleEmitter::createParticles(const int tick) +{ + std::list newParticles; + + if (mOutputPauseLeft > 0) + { + mOutputPauseLeft --; + return newParticles; + } + mOutputPauseLeft = mOutputPause.value(tick); + + for (int i = mOutput.value(tick); i > 0; i--) + { + // Limit maximum particles + if (Particle::particleCount > Particle::maxCount) + break; + + Particle *newParticle; + if (mParticleImage) + { + const std::string name = mParticleImage->getIdPath(); + if (ImageParticle::imageParticleCountByName.find(name) == + ImageParticle::imageParticleCountByName.end()) + { + ImageParticle::imageParticleCountByName[name] = 0; + } + + if (ImageParticle::imageParticleCountByName[name] > 200) + break; + + newParticle = new ImageParticle(mMap, mParticleImage); + } + else if (!mParticleRotation.mFrames.empty()) + { + Animation *const newAnimation = new Animation(mParticleRotation); + newParticle = new RotationalParticle(mMap, newAnimation); + } + else if (!mParticleAnimation.mFrames.empty()) + { + Animation *const newAnimation = new Animation(mParticleAnimation); + newParticle = new AnimationParticle(mMap, newAnimation); + } + else + { + newParticle = new Particle(mMap); + } + + const Vector position(mParticlePosX.value(tick), + mParticlePosY.value(tick), + mParticlePosZ.value(tick)); + newParticle->moveTo(position); + + const float angleH = mParticleAngleHorizontal.value(tick); + const float cosAngleH = cos(angleH); + const float sinAngleH = sin(angleH); + const float angleV = mParticleAngleVertical.value(tick); + const float cosAngleV = cos(angleV); + const float sinAngleV = sin(angleV); + const float power = mParticlePower.value(tick); + newParticle->setVelocity(cosAngleH * cosAngleV * power, + sinAngleH * cosAngleV * power, + sinAngleV * power); + + newParticle->setRandomness(mParticleRandomness.value(tick)); + newParticle->setGravity(mParticleGravity.value(tick)); + newParticle->setBounce(mParticleBounce.value(tick)); + newParticle->setFollow(mParticleFollow); + + newParticle->setDestination(mParticleTarget, + mParticleAcceleration.value(tick), + mParticleMomentum.value(tick)); + + newParticle->setDieDistance(mParticleDieDistance.value(tick)); + + newParticle->setLifetime(mParticleLifetime.value(tick)); + newParticle->setFadeOut(mParticleFadeOut.value(tick)); + newParticle->setFadeIn(mParticleFadeIn.value(tick)); + newParticle->setAlpha(mParticleAlpha.value(tick)); + + FOR_EACH (ParticleEmitterListCIter, it, mParticleChildEmitters) + newParticle->addEmitter(new ParticleEmitter(*it)); + + if (!mDeathEffect.empty()) + newParticle->setDeathEffect(mDeathEffect, mDeathEffectConditions); + + newParticles.push_back(newParticle); + } + + return newParticles; +} + +void ParticleEmitter::adjustSize(const int w, const int h) +{ + if (w == 0 || h == 0) + return; // new dimensions are illegal + + // calculate the old rectangle + const int oldArea = static_cast( + mParticlePosX.maxVal - mParticlePosX.minVal) * static_cast( + mParticlePosX.maxVal - mParticlePosY.minVal); + if (oldArea == 0) + { + // when the effect has no dimension it is + // not designed to be resizeable + return; + } + + // set the new dimensions + mParticlePosX.set(0, static_cast(w)); + mParticlePosY.set(0, static_cast(h)); + const int newArea = w * h; + // adjust the output so that the particle density stays the same + const float outputFactor = static_cast(newArea) + / static_cast(oldArea); + mOutput.minVal = static_cast(static_cast( + mOutput.minVal) * outputFactor); + mOutput.maxVal = static_cast(static_cast( + mOutput.maxVal) * outputFactor); +} diff --git a/src/particle/particleemitter.h b/src/particle/particleemitter.h new file mode 100644 index 000000000..415c10f37 --- /dev/null +++ b/src/particle/particleemitter.h @@ -0,0 +1,167 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#ifndef PARTICLE_PARTICLEEMITTER_H +#define PARTICLE_PARTICLEEMITTER_H + +#include "particle/particleemitterprop.h" + +#include "resources/animation.h" + +#include "utils/xml.h" + +#include + +class Image; +class ImageSet; +class Map; +class Particle; + +/** + * Every Particle can have one or more particle emitters that create new + * particles when they are updated + */ +class ParticleEmitter final +{ + public: + ParticleEmitter(const XmlNodePtr emitterNode, Particle *const target, + Map *const map, const int rotation = 0, + const std::string& dyePalettes = std::string()); + + /** + * Copy Constructor (necessary for reference counting of particle images) + */ + ParticleEmitter(const ParticleEmitter &o); + + /** + * Assignment operator that calls the copy constructor + */ + ParticleEmitter & operator=(const ParticleEmitter &o); + + /** + * Destructor. + */ + ~ParticleEmitter(); + + /** + * Spawns new particles + * @return: a list of created particles + */ + std::list createParticles(const int tick); + + /** + * Sets the target of the particles that are created + */ + void setTarget(Particle *const target) + { mParticleTarget = target; } + + /** + * Changes the size of the emitter so that the effect fills a + * rectangle of this size + */ + void adjustSize(const int w, const int h); + + private: + template ParticleEmitterProp + readParticleEmitterProp(XmlNodePtr propertyNode, T def); + + ImageSet *getImageSet(XmlNodePtr node); + + /** + * initial position of particles: + */ + ParticleEmitterProp mParticlePosX, mParticlePosY, mParticlePosZ; + + /** + * initial vector of particles: + */ + ParticleEmitterProp mParticleAngleHorizontal, + mParticleAngleVertical; + + /** + * Initial velocity of particles + */ + ParticleEmitterProp mParticlePower; + + /* + * Vector changing of particles: + */ + ParticleEmitterProp mParticleGravity; + ParticleEmitterProp mParticleRandomness; + ParticleEmitterProp mParticleBounce; + + /* + * Properties of targeting particles: + */ + Particle *mParticleTarget; + ParticleEmitterProp mParticleAcceleration; + ParticleEmitterProp mParticleDieDistance; + ParticleEmitterProp mParticleMomentum; + + /* + * Behavior over time of the particles: + */ + ParticleEmitterProp mParticleLifetime; + ParticleEmitterProp mParticleFadeOut; + ParticleEmitterProp mParticleFadeIn; + + // Map the particles are spawned on + Map *mMap; + + // Number of particles spawned per update + ParticleEmitterProp mOutput; + + // Pause in frames between two spawns + ParticleEmitterProp mOutputPause; + + /* + * Graphical representation of the particles + */ + // Particle image, if used + Image *mParticleImage; + + // Filename of particle animation file + Animation mParticleAnimation; + + // Filename of particle rotation file + Animation mParticleRotation; + + // Opacity of the graphical representation of the particles + ParticleEmitterProp mParticleAlpha; + + /* + * Death effect of the particles + */ + std::string mDeathEffect; + + // List of emitters the spawned particles are equipped with + std::list mParticleChildEmitters; + + std::vector mTempSets; + + int mOutputPauseLeft; + + signed char mDeathEffectConditions; + + bool mParticleFollow; +}; +#endif // PARTICLE_PARTICLEEMITTER_H diff --git a/src/particle/particleemitterprop.h b/src/particle/particleemitterprop.h new file mode 100644 index 000000000..ea241e740 --- /dev/null +++ b/src/particle/particleemitterprop.h @@ -0,0 +1,132 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#ifndef PARTICLE_PARTICLEEMITTERPROP_H +#define PARTICLE_PARTICLEEMITTERPROP_H + +#include +#include + +#include "localconsts.h" + +/** + * Returns a random numeric value that is larger than or equal min and smaller + * than max + */ + +enum ChangeFunc +{ + FUNC_NONE = 0, + FUNC_SINE, + FUNC_SAW, + FUNC_TRIANGLE, + FUNC_SQUARE +}; + +template struct ParticleEmitterProp final +{ + ParticleEmitterProp(): + minVal(0), maxVal(0), changeFunc(FUNC_NONE), + changeAmplitude(0), changePeriod(0), changePhase(0) + { + } + + void set(const T min, const T max) + { + minVal = min; + maxVal = max; + } + + void set(const T val) + { + set(val, val); + } + + void setFunction(ChangeFunc func, T amplitude, + const int period, const int phase) + { + changeFunc = func; + changeAmplitude = amplitude; + changePeriod = period; + if (!changePeriod) + changePeriod = 1; + changePhase = phase; + } + + T value(int tick) const + { + tick += changePhase; + T val = static_cast(minVal + (maxVal - minVal) + * (rand() / (static_cast(RAND_MAX) + 1))); + + switch (changeFunc) + { + case FUNC_SINE: + val += static_cast(std::sin(M_PI * 2 * (static_cast( + tick % changePeriod) / static_cast( + changePeriod)))) * changeAmplitude; + break; + case FUNC_SAW: + val += static_cast(changeAmplitude * (static_cast( + tick % changePeriod) / static_cast( + changePeriod))) * 2 - changeAmplitude; + break; + case FUNC_TRIANGLE: + if ((tick % changePeriod) * 2 < changePeriod) + { + val += changeAmplitude - static_cast(( + tick % changePeriod) / static_cast( + changePeriod)) * changeAmplitude * 4; + } + else + { + val += changeAmplitude * -3 + static_cast(( + tick % changePeriod) / static_cast( + changePeriod)) * changeAmplitude * 4; + // I have no idea why this works but it does + } + break; + case FUNC_SQUARE: + if ((tick % changePeriod) * 2 < changePeriod) + val += changeAmplitude; + else + val -= changeAmplitude; + break; + case FUNC_NONE: + default: + // nothing + break; + } + + return val; + } + + T minVal; + T maxVal; + + ChangeFunc changeFunc; + T changeAmplitude; + int changePeriod; + int changePhase; +}; + +#endif // PARTICLE_PARTICLEEMITTERPROP_H diff --git a/src/particle/rotationalparticle.cpp b/src/particle/rotationalparticle.cpp new file mode 100644 index 000000000..b72a94b3e --- /dev/null +++ b/src/particle/rotationalparticle.cpp @@ -0,0 +1,96 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#include "particle/rotationalparticle.h" + +#include "simpleanimation.h" + +#include "render/graphics.h" + +#include + +#include "debug.h" + +static const double PI = M_PI; +static const float PI2 = 2 * M_PI; + +RotationalParticle::RotationalParticle(Map *const map, + Animation *const animation) : + ImageParticle(map, nullptr), + mAnimation(new SimpleAnimation(animation)) +{ +} + +RotationalParticle::RotationalParticle(Map *const map, + const XmlNodePtr animationNode, + const std::string& dyePalettes): + ImageParticle(map, nullptr), + mAnimation(new SimpleAnimation(animationNode, dyePalettes)) +{ +} + +RotationalParticle::~RotationalParticle() +{ + delete mAnimation; + mAnimation = nullptr; + mImage = nullptr; +} + +bool RotationalParticle::update() +{ + if (!mAnimation) + return false; + + // TODO: cache velocities to avoid spamming atan2() + + const int size = mAnimation->getLength(); + if (!size) + return false; + + float rad = static_cast(atan2(mVelocity.x, mVelocity.y)); + if (rad < 0) + rad = PI2 + rad; + + const float range = static_cast(PI / size); + + // Determines which frame the particle should play + if (rad < range || rad > PI2 - range) + { + mAnimation->setFrame(0); + } + else + { + for (int c = 1; c < size; c++) + { + if (((static_cast(c) * (2 * range)) - range) < rad + && rad < ((static_cast(c) * (2 * range)) + range)) + { + mAnimation->setFrame(c); + break; + } + } + } + + mImage = mAnimation->getCurrentImage(); + + return Particle::update(); +} diff --git a/src/particle/rotationalparticle.h b/src/particle/rotationalparticle.h new file mode 100644 index 000000000..33289d8c3 --- /dev/null +++ b/src/particle/rotationalparticle.h @@ -0,0 +1,52 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#ifndef PARTICLE_ROTATIONALPARTICLE_H +#define PARTICLE_ROTATIONALPARTICLE_H + +#include "particle/imageparticle.h" + +#include "utils/xml.h" + +class Animation; +class Map; +class SimpleAnimation; + +class RotationalParticle final : public ImageParticle +{ + public: + RotationalParticle(Map *const map, Animation *const animation); + + RotationalParticle(Map *const map, const XmlNodePtr animationNode, + const std::string& dyePalettes = std::string()); + + A_DELETE_COPY(RotationalParticle) + + ~RotationalParticle(); + + virtual bool update() override; + + private: + SimpleAnimation *mAnimation; /**< Used animation for this particle */ +}; + +#endif // PARTICLE_ROTATIONALPARTICLE_H diff --git a/src/particle/textparticle.cpp b/src/particle/textparticle.cpp new file mode 100644 index 000000000..ecab98068 --- /dev/null +++ b/src/particle/textparticle.cpp @@ -0,0 +1,89 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#include "particle/textparticle.h" + +#include "render/graphics.h" + +#include "gui/theme.h" + +#include +#include + +#include "debug.h" + +TextParticle::TextParticle(Map *const map, const std::string &text, + const gcn::Color *const color, + gcn::Font *const font, const bool outline) : + Particle(map), + mText(text), + mTextFont(font), + mColor(color), + mTextWidth(mTextFont ? mTextFont->getWidth(mText) / 2 : 1), + mOutline(outline) +{ +} + +bool TextParticle::draw(Graphics *const graphics, + const int offsetX, const int offsetY) const +{ + if (!mColor || !mTextFont) + return false; + + BLOCK_START("TextParticle::draw") + if (!isAlive()) + { + BLOCK_END("TextParticle::draw") + return false; + } + + const int screenX = static_cast(mPos.x) + offsetX; + const int screenY = static_cast(mPos.y) - static_cast(mPos.z) + + offsetY; + + float alpha = mAlpha * 255.0f; + + if (mFadeOut && mLifetimeLeft > -1 && mLifetimeLeft < mFadeOut) + { + alpha *= static_cast(mLifetimeLeft) + / static_cast(mFadeOut); + } + + if (mFadeIn && mLifetimePast < mFadeIn) + { + alpha *= static_cast(mLifetimePast) + / static_cast(mFadeIn); + } + + gcn::Color color = *mColor; + color.a = static_cast(alpha); + + graphics->setColor(color); + if (mOutline) + { + graphics->setColor2(Theme::getThemeColor( + Theme::OUTLINE, static_cast(alpha))); + } + mTextFont->drawString(graphics, mText, screenX - mTextWidth, screenY); + BLOCK_END("TextParticle::draw") + return true; +} diff --git a/src/particle/textparticle.h b/src/particle/textparticle.h new file mode 100644 index 000000000..a243e32e0 --- /dev/null +++ b/src/particle/textparticle.h @@ -0,0 +1,62 @@ +/* + * The ManaPlus Client + * Copyright (C) 2006-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program 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 + * 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, see . + */ + +#ifndef PARTICLE_TEXTPARTICLE_H +#define PARTICLE_TEXTPARTICLE_H + +#include "particle/particle.h" + +class TextParticle final : public Particle +{ + public: + /** + * Constructor. + */ + TextParticle(Map *const map, const std::string &text, + const gcn::Color *const color, + gcn::Font *const font, const bool outline = false); + + A_DELETE_COPY(TextParticle) + + /** + * Draws the particle image. + */ + virtual bool draw(Graphics *const graphics, + const int offsetX, const int offsetY) const override; + + // hack to improve text visibility + virtual int getPixelY() const override A_WARN_UNUSED + { return static_cast(mPos.y + mPos.z); } + + // hack to improve text visibility (for sorting only) + virtual int getSortPixelY() const override A_WARN_UNUSED + { return static_cast(mPos.y + mPos.z); } + + private: + std::string mText; /**< Text of the particle. */ + gcn::Font *mTextFont; /**< Font used for drawing the text. */ + const gcn::Color *mColor; /**< Color used for drawing the text. */ + int mTextWidth; + bool mOutline; /**< Make the text better readable */ +}; + +#endif // PARTICLE_TEXTPARTICLE_H diff --git a/src/particlecontainer.cpp b/src/particlecontainer.cpp deleted file mode 100644 index a890f0d9c..000000000 --- a/src/particlecontainer.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2008-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#include "particle.h" -#include "particlecontainer.h" - -#include "debug.h" - -typedef std::list::iterator ParticleListIter; -typedef std::list::const_iterator ParticleListCIter; - -ParticleContainer::ParticleContainer(ParticleContainer *const parent, - const bool delParent): - mNext(parent), - mDelParent(delParent) -{ -} - -ParticleContainer::~ParticleContainer() -{ - clearLocally(); - if (mDelParent) - { - delete mNext; - mNext = nullptr; - } -} - -void ParticleContainer::clear() -{ - clearLocally(); - if (mNext) - mNext->clear(); -} - -void ParticleContainer::moveTo(const float x, const float y) -{ - if (mNext) - mNext->moveTo(x, y); -} - -// -- particle list ---------------------------------------- - -ParticleList::ParticleList(ParticleContainer *const parent, - const bool delParent) : - ParticleContainer(parent, delParent), - mElements() -{} - -ParticleList::~ParticleList() -{} - -void ParticleList::addLocally(Particle *const particle) -{ - if (particle) - { - // The effect may not die without the beings permission or we segfault - particle->disableAutoDelete(); - mElements.push_back(particle); - } -} - -void ParticleList::removeLocally(const Particle *const particle) -{ - for (std::list::iterator it = mElements.begin(); - it != mElements.end(); ) - { - Particle *const p = *it; - if (p == particle) - { - p->kill(); - it = mElements.erase(it); - } - else - { - ++it; - } - } -} - -void ParticleList::clearLocally() -{ - FOR_EACH (ParticleListCIter, it, mElements) - (*it)->kill(); - - mElements.clear(); -} - -void ParticleList::moveTo(const float x, const float y) -{ - ParticleContainer::moveTo(x, y); - - for (std::list::iterator it = mElements.begin(); - it != mElements.end(); ) - { - Particle *const p = *it; - p->moveTo(x, y); - if (p->isExtinct()) - { - p->kill(); - it = mElements.erase(it); - } - else - { - ++it; - } - } -} - -// -- particle vector ---------------------------------------- - -ParticleVector::ParticleVector(ParticleContainer *const parent, - const bool delParent) : - ParticleContainer(parent, delParent), - mIndexedElements() -{} - -ParticleVector::~ParticleVector() -{} - -void ParticleVector::setLocally(const int index, Particle *const particle) -{ - if (index < 0) - return; - - delLocally(index); - - if (mIndexedElements.size() <= static_cast(index)) - mIndexedElements.resize(index + 1, nullptr); - - if (particle) - particle->disableAutoDelete(); - mIndexedElements[index] = particle; -} - -void ParticleVector::delLocally(const int index) -{ - if (index < 0) - return; - - if (mIndexedElements.size() <= static_cast(index)) - return; - - Particle *const p = mIndexedElements[index]; - if (p) - { - mIndexedElements[index] = nullptr; - p->kill(); - } -} - -void ParticleVector::clearLocally() -{ - for (unsigned int i = 0; i < mIndexedElements.size(); i++) - delLocally(i); -} - -void ParticleVector::moveTo(const float x, const float y) -{ - ParticleContainer::moveTo(x, y); - - for (std::vector::iterator it = mIndexedElements.begin(); - it != mIndexedElements.end(); ++it) - { - Particle *const p = *it; - if (p) - { - p->moveTo(x, y); - - if (p->isExtinct()) - { - p->kill(); - *it = nullptr; - } - } - } -} diff --git a/src/particlecontainer.h b/src/particlecontainer.h deleted file mode 100644 index c78c0dbb9..000000000 --- a/src/particlecontainer.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2008-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#ifndef PARTICLECONTAINER_H -#define PARTICLECONTAINER_H - -#include -#include - -#include "localconsts.h" - -class Particle; - -/** - * Set of particle effects. May be stacked with other ParticleContainers. All - * operations herein affect such stacked containers, unless the operations end - * in `Locally'. - */ -class ParticleContainer -{ -public: - /** - * Constructs a new particle container and assumes responsibility for - * its parent (for all operations defined herein, except when ending in `Locally') - * - * delParent means that the destructor should also free the parent. - */ - explicit ParticleContainer(ParticleContainer *const parent = nullptr, - const bool delParent = true); - - A_DELETE_COPY(ParticleContainer) - - virtual ~ParticleContainer(); - - /** - * Kills and removes all particle effects - */ - void clear(); - - /** - * Kills and removes all particle effects (only in this container) - */ - virtual void clearLocally() - { } - - /** - * Sets the positions of all elements - */ - virtual void moveTo(const float x, const float y); - -protected: - ParticleContainer *mNext; /**< Contained container, if any */ - bool mDelParent; /**< Delete mNext in destructor */ -}; - -/** - * Linked list of particle effects. - */ -class ParticleList final : public ParticleContainer -{ -public: - explicit ParticleList(ParticleContainer *const parent = nullptr, - const bool delParent = true); - - A_DELETE_COPY(ParticleList) - - virtual ~ParticleList(); - - /** - * Takes control of and adds a particle - */ - void addLocally(Particle *const particle); - - /** - * `kills' and removes a particle - */ - void removeLocally(const Particle *const particle); - - virtual void clearLocally() override; - - virtual void moveTo(const float x, const float y) override; - -protected: - std::list mElements; /**< Contained particle effects */ -}; - -/** - * Particle container with indexing facilities - */ -class ParticleVector final : public ParticleContainer -{ -public: - explicit ParticleVector(ParticleContainer *const parent = nullptr, - const bool delParent = true); - - A_DELETE_COPY(ParticleVector) - - virtual ~ParticleVector(); - - /** - * Sets a particle at a specified index. Kills the previous particle - * there, if needed. - */ - virtual void setLocally(const int index, Particle *const particle); - - /** - * Removes a particle at a specified index - */ - virtual void delLocally(const int index); - - virtual void clearLocally() override; - - virtual void moveTo(const float x, const float y) override; - -protected: - std::vector mIndexedElements; -}; - -#endif // PARTICLECONTAINER_H diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp deleted file mode 100644 index 924f2094d..000000000 --- a/src/particleemitter.cpp +++ /dev/null @@ -1,673 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#include "particleemitter.h" - -#include "animationparticle.h" -#include "logger.h" -#include "rotationalparticle.h" - -#include "resources/dye.h" -#include "resources/image.h" -#include "resources/imageset.h" -#include "resources/resourcemanager.h" - -#include - -#include "debug.h" - -static const float SIN45 = 0.707106781f; -static const float DEG_RAD_FACTOR = 0.017453293f; - -typedef std::vector::const_iterator ImageSetVectorCIter; -typedef std::list::const_iterator ParticleEmitterListCIter; - -ParticleEmitter::ParticleEmitter(const XmlNodePtr emitterNode, - Particle *const target, - Map *const map, const int rotation, - const std::string& dyePalettes) : - mParticleTarget(target), - mMap(map), - mParticleImage(nullptr), - mOutputPauseLeft(0), - mDeathEffectConditions(0), - mParticleFollow(false) -{ - // Initializing default values - mParticlePosX.set(0.0f); - mParticlePosY.set(0.0f); - mParticlePosZ.set(0.0f); - mParticleAngleHorizontal.set(0.0f); - mParticleAngleVertical.set(0.0f); - mParticlePower.set(0.0f); - mParticleGravity.set(0.0f); - mParticleRandomness.set(0); - mParticleBounce.set(0.0f); - mParticleAcceleration.set(0.0f); - mParticleDieDistance.set(-1.0f); - mParticleMomentum.set(1.0f); - mParticleLifetime.set(-1); - mParticleFadeOut.set(0); - mParticleFadeIn.set(0); - mOutput.set(1); - mOutputPause.set(0); - mParticleAlpha.set(1.0f); - - for_each_xml_child_node(propertyNode, emitterNode) - { - if (xmlNameEqual(propertyNode, "property")) - { - const std::string name = XML::getProperty( - propertyNode, "name", ""); - - if (name == "position-x") - { - mParticlePosX = readParticleEmitterProp(propertyNode, 0.0f); - } - else if (name == "position-y") - { - mParticlePosY = readParticleEmitterProp(propertyNode, 0.0f); - mParticlePosY.minVal *= SIN45; - mParticlePosY.maxVal *= SIN45; - mParticlePosY.changeAmplitude *= SIN45; - } - else if (name == "position-z") - { - mParticlePosZ = readParticleEmitterProp(propertyNode, 0.0f); - mParticlePosZ.minVal *= SIN45; - mParticlePosZ.maxVal *= SIN45; - mParticlePosZ.changeAmplitude *= SIN45; - } - else if (name == "image") - { - std::string image = XML::getProperty( - propertyNode, "value", ""); - // Don't leak when multiple images are defined - if (!image.empty() && !mParticleImage) - { - if (!dyePalettes.empty()) - Dye::instantiate(image, dyePalettes); - - ResourceManager *const resman - = ResourceManager::getInstance(); - mParticleImage = resman->getImage(image); - } - } - else if (name == "subimage") - { - std::string image = XML::getProperty( - propertyNode, "value", ""); - // Don't leak when multiple images are defined - if (!image.empty() && !mParticleImage) - { - if (!dyePalettes.empty()) - Dye::instantiate(image, dyePalettes); - - ResourceManager *const resman - = ResourceManager::getInstance(); - Image *img = resman->getImage(image); - if (img) - { - mParticleImage = resman->getSubImage(img, - XML::getProperty(propertyNode, "x", 0), - XML::getProperty(propertyNode, "y", 0), - XML::getProperty(propertyNode, "width", 0), - XML::getProperty(propertyNode, "height", 0)); - img->decRef(); - } - else - { - mParticleImage = nullptr; - } - } - } - else if (name == "horizontal-angle") - { - mParticleAngleHorizontal = - readParticleEmitterProp(propertyNode, 0.0f); - mParticleAngleHorizontal.minVal - += static_cast(rotation); - mParticleAngleHorizontal.minVal *= DEG_RAD_FACTOR; - mParticleAngleHorizontal.maxVal - += static_cast(rotation); - mParticleAngleHorizontal.maxVal *= DEG_RAD_FACTOR; - mParticleAngleHorizontal.changeAmplitude *= DEG_RAD_FACTOR; - } - else if (name == "vertical-angle") - { - mParticleAngleVertical = - readParticleEmitterProp(propertyNode, 0.0f); - mParticleAngleVertical.minVal *= DEG_RAD_FACTOR; - mParticleAngleVertical.maxVal *= DEG_RAD_FACTOR; - mParticleAngleVertical.changeAmplitude *= DEG_RAD_FACTOR; - } - else if (name == "power") - { - mParticlePower = readParticleEmitterProp(propertyNode, 0.0f); - } - else if (name == "gravity") - { - mParticleGravity = readParticleEmitterProp(propertyNode, 0.0f); - } - else if (name == "randomnes" - || name == "randomness") // legacy bug - { - mParticleRandomness = readParticleEmitterProp(propertyNode, 0); - } - else if (name == "bounce") - { - mParticleBounce = readParticleEmitterProp(propertyNode, 0.0f); - } - else if (name == "lifetime") - { - mParticleLifetime = readParticleEmitterProp(propertyNode, 0); - mParticleLifetime.minVal += 1; - } - else if (name == "output") - { - mOutput = readParticleEmitterProp(propertyNode, 0); - mOutput.maxVal += 1; - } - else if (name == "output-pause") - { - mOutputPause = readParticleEmitterProp(propertyNode, 0); - mOutputPauseLeft = mOutputPause.value(0); - } - else if (name == "acceleration") - { - mParticleAcceleration = readParticleEmitterProp( - propertyNode, 0.0f); - } - else if (name == "die-distance") - { - mParticleDieDistance = readParticleEmitterProp( - propertyNode, 0.0f); - } - else if (name == "momentum") - { - mParticleMomentum = readParticleEmitterProp( - propertyNode, 1.0f); - } - else if (name == "fade-out") - { - mParticleFadeOut = readParticleEmitterProp(propertyNode, 0); - } - else if (name == "fade-in") - { - mParticleFadeIn = readParticleEmitterProp(propertyNode, 0); - } - else if (name == "alpha") - { - mParticleAlpha = readParticleEmitterProp(propertyNode, 1.0f); - } - else if (name == "follow-parent") - { - mParticleFollow = true; - } - else - { - logger->log("Particle Engine: Warning, " - "unknown emitter property \"%s\"", - name.c_str()); - } - } - else if (xmlNameEqual(propertyNode, "emitter")) - { - ParticleEmitter newEmitter(propertyNode, mParticleTarget, map, - rotation, dyePalettes); - mParticleChildEmitters.push_back(newEmitter); - } - else if (xmlNameEqual(propertyNode, "rotation")) - { - ImageSet *const imageset = getImageSet(propertyNode); - if (!imageset) - { - logger->log1("Error: no valid imageset"); - continue; - } - mTempSets.push_back(imageset); - - // Get animation frames - for_each_xml_child_node(frameNode, propertyNode) - { - const int delay = XML::getIntProperty( - frameNode, "delay", 0, 0, 100000); - const int offsetX = XML::getProperty(frameNode, "offsetX", 0) - - imageset->getWidth() / 2 + 16; - const int offsetY = XML::getProperty(frameNode, "offsetY", 0) - - imageset->getHeight() + 32; - const int rand = XML::getIntProperty( - frameNode, "rand", 100, 0, 100); - - if (xmlNameEqual(frameNode, "frame")) - { - const int index = XML::getProperty(frameNode, "index", -1); - - if (index < 0) - { - logger->log1("No valid value for 'index'"); - continue; - } - - Image *const img = imageset->get(index); - - if (!img) - { - logger->log("No image at index %d", index); - continue; - } - - mParticleRotation.addFrame(img, delay, - offsetX, offsetY, rand); - } - else if (xmlNameEqual(frameNode, "sequence")) - { - int start = XML::getProperty(frameNode, "start", -1); - const int end = XML::getProperty(frameNode, "end", -1); - - if (start < 0 || end < 0) - { - logger->log1("No valid value for 'start' or 'end'"); - continue; - } - - while (end >= start) - { - Image *const img = imageset->get(start); - if (!img) - { - logger->log("No image at index %d", start); - continue; - } - - mParticleRotation.addFrame(img, delay, - offsetX, offsetY, rand); - start ++; - } - } - else if (xmlNameEqual(frameNode, "end")) - { - mParticleRotation.addTerminator(rand); - } - } // for frameNode - } - else if (xmlNameEqual(propertyNode, "animation")) - { - ImageSet *const imageset = getImageSet(propertyNode); - if (!imageset) - { - logger->log1("Error: no valid imageset"); - continue; - } - mTempSets.push_back(imageset); - - // Get animation frames - for_each_xml_child_node(frameNode, propertyNode) - { - const int delay = XML::getIntProperty( - frameNode, "delay", 0, 0, 100000); - const int offsetX = XML::getProperty(frameNode, "offsetX", 0) - - imageset->getWidth() / 2 + 16; - const int offsetY = XML::getProperty(frameNode, "offsetY", 0) - - imageset->getHeight() + 32; - const int rand = XML::getIntProperty( - frameNode, "rand", 100, 0, 100); - - if (xmlNameEqual(frameNode, "frame")) - { - const int index = XML::getProperty(frameNode, "index", -1); - if (index < 0) - { - logger->log1("No valid value for 'index'"); - continue; - } - - Image *const img = imageset->get(index); - - if (!img) - { - logger->log("No image at index %d", index); - continue; - } - - mParticleAnimation.addFrame(img, delay, - offsetX, offsetY, rand); - } - else if (xmlNameEqual(frameNode, "sequence")) - { - int start = XML::getProperty(frameNode, "start", -1); - const int end = XML::getProperty(frameNode, "end", -1); - - if (start < 0 || end < 0) - { - logger->log1("No valid value for 'start' or 'end'"); - continue; - } - - while (end >= start) - { - Image *const img = imageset->get(start); - - if (!img) - { - logger->log("No image at index %d", start); - continue; - } - - mParticleAnimation.addFrame(img, delay, - offsetX, offsetY, rand); - start++; - } - } - else if (xmlNameEqual(frameNode, "end")) - { - mParticleAnimation.addTerminator(rand); - } - } // for frameNode - } - else if (xmlNameEqual(propertyNode, "deatheffect")) - { - mDeathEffect = reinterpret_cast( - propertyNode->xmlChildrenNode->content); - mDeathEffectConditions = 0x00; - if (XML::getBoolProperty(propertyNode, "on-floor", true)) - { - mDeathEffectConditions += static_cast( - Particle::DEAD_FLOOR); - } - if (XML::getBoolProperty(propertyNode, "on-sky", true)) - { - mDeathEffectConditions += static_cast( - Particle::DEAD_SKY); - } - if (XML::getBoolProperty(propertyNode, "on-other", false)) - { - mDeathEffectConditions += static_cast( - Particle::DEAD_OTHER); - } - if (XML::getBoolProperty(propertyNode, "on-impact", true)) - { - mDeathEffectConditions += static_cast( - Particle::DEAD_IMPACT); - } - if (XML::getBoolProperty(propertyNode, "on-timeout", true)) - { - mDeathEffectConditions += static_cast( - Particle::DEAD_TIMEOUT); - } - } - } -} - -ParticleEmitter::ParticleEmitter(const ParticleEmitter &o) -{ - *this = o; -} - -ImageSet *ParticleEmitter::getImageSet(XmlNodePtr node) -{ - ResourceManager *const resman = ResourceManager::getInstance(); - ImageSet *imageset = nullptr; - const int subX = XML::getProperty(node, "subX", -1); - if (subX != -1) - { - Image *const img = resman->getImage(XML::getProperty( - node, "imageset", "")); - if (!img) - return nullptr; - - Image *const img2 = resman->getSubImage(img, subX, - XML::getProperty(node, "subY", 0), - XML::getProperty(node, "subWidth", 0), - XML::getProperty(node, "subHeight", 0)); - if (!img2) - { - img->decRef(); - return nullptr; - } - - imageset = resman->getSubImageSet(img2, - XML::getProperty(node, "width", 0), - XML::getProperty(node, "height", 0)); - img2->decRef(); - img->decRef(); - } - else - { - imageset = resman->getImageSet( - XML::getProperty(node, "imageset", ""), - XML::getProperty(node, "width", 0), - XML::getProperty(node, "height", 0)); - } - return imageset; -} - -ParticleEmitter & ParticleEmitter::operator=(const ParticleEmitter &o) -{ - mParticlePosX = o.mParticlePosX; - mParticlePosY = o.mParticlePosY; - mParticlePosZ = o.mParticlePosZ; - mParticleAngleHorizontal = o.mParticleAngleHorizontal; - mParticleAngleVertical = o.mParticleAngleVertical; - mParticlePower = o.mParticlePower; - mParticleGravity = o.mParticleGravity; - mParticleRandomness = o.mParticleRandomness; - mParticleBounce = o.mParticleBounce; - mParticleFollow = o.mParticleFollow; - mParticleTarget = o.mParticleTarget; - mParticleAcceleration = o.mParticleAcceleration; - mParticleDieDistance = o.mParticleDieDistance; - mParticleMomentum = o.mParticleMomentum; - mParticleLifetime = o.mParticleLifetime; - mParticleFadeOut = o.mParticleFadeOut; - mParticleFadeIn = o.mParticleFadeIn; - mParticleAlpha = o.mParticleAlpha; - mMap = o.mMap; - mOutput = o.mOutput; - mOutputPause = o.mOutputPause; - mParticleImage = o.mParticleImage; - mParticleAnimation = o.mParticleAnimation; - mParticleRotation = o.mParticleRotation; - mParticleChildEmitters = o.mParticleChildEmitters; - mDeathEffectConditions = o.mDeathEffectConditions; - mDeathEffect = o.mDeathEffect; - mTempSets = o.mTempSets; - - FOR_EACH (ImageSetVectorCIter, i, mTempSets) - { - if (*i) - (*i)->incRef(); - } - - mOutputPauseLeft = 0; - - if (mParticleImage) - mParticleImage->incRef(); - - return *this; -} - -ParticleEmitter::~ParticleEmitter() -{ - FOR_EACH (ImageSetVectorCIter, i, mTempSets) - { - if (*i) - (*i)->decRef(); - } - mTempSets.clear(); - - if (mParticleImage) - { - mParticleImage->decRef(); - mParticleImage = nullptr; - } -} - -template ParticleEmitterProp -ParticleEmitter::readParticleEmitterProp(XmlNodePtr propertyNode, T def) -{ - ParticleEmitterProp retval; - - def = static_cast(XML::getFloatProperty(propertyNode, "value", - static_cast(def))); - retval.set(static_cast(XML::getFloatProperty(propertyNode, "min", - static_cast(def))), static_cast(XML::getFloatProperty( - propertyNode, "max", static_cast(def)))); - - const std::string change = XML::getProperty( - propertyNode, "change-func", "none"); - T amplitude = static_cast(XML::getFloatProperty(propertyNode, - "change-amplitude", 0.0)); - - const int period = XML::getProperty(propertyNode, "change-period", 0); - const int phase = XML::getProperty(propertyNode, "change-phase", 0); - if (change == "saw" || change == "sawtooth") - retval.setFunction(FUNC_SAW, amplitude, period, phase); - else if (change == "sine" || change == "sinewave") - retval.setFunction(FUNC_SINE, amplitude, period, phase); - else if (change == "triangle") - retval.setFunction(FUNC_TRIANGLE, amplitude, period, phase); - else if (change == "square") - retval.setFunction(FUNC_SQUARE, amplitude, period, phase); - - return retval; -} - -std::list ParticleEmitter::createParticles(const int tick) -{ - std::list newParticles; - - if (mOutputPauseLeft > 0) - { - mOutputPauseLeft --; - return newParticles; - } - mOutputPauseLeft = mOutputPause.value(tick); - - for (int i = mOutput.value(tick); i > 0; i--) - { - // Limit maximum particles - if (Particle::particleCount > Particle::maxCount) - break; - - Particle *newParticle; - if (mParticleImage) - { - const std::string name = mParticleImage->getIdPath(); - if (ImageParticle::imageParticleCountByName.find(name) == - ImageParticle::imageParticleCountByName.end()) - { - ImageParticle::imageParticleCountByName[name] = 0; - } - - if (ImageParticle::imageParticleCountByName[name] > 200) - break; - - newParticle = new ImageParticle(mMap, mParticleImage); - } - else if (!mParticleRotation.mFrames.empty()) - { - Animation *const newAnimation = new Animation(mParticleRotation); - newParticle = new RotationalParticle(mMap, newAnimation); - } - else if (!mParticleAnimation.mFrames.empty()) - { - Animation *const newAnimation = new Animation(mParticleAnimation); - newParticle = new AnimationParticle(mMap, newAnimation); - } - else - { - newParticle = new Particle(mMap); - } - - const Vector position(mParticlePosX.value(tick), - mParticlePosY.value(tick), - mParticlePosZ.value(tick)); - newParticle->moveTo(position); - - const float angleH = mParticleAngleHorizontal.value(tick); - const float cosAngleH = cos(angleH); - const float sinAngleH = sin(angleH); - const float angleV = mParticleAngleVertical.value(tick); - const float cosAngleV = cos(angleV); - const float sinAngleV = sin(angleV); - const float power = mParticlePower.value(tick); - newParticle->setVelocity(cosAngleH * cosAngleV * power, - sinAngleH * cosAngleV * power, - sinAngleV * power); - - newParticle->setRandomness(mParticleRandomness.value(tick)); - newParticle->setGravity(mParticleGravity.value(tick)); - newParticle->setBounce(mParticleBounce.value(tick)); - newParticle->setFollow(mParticleFollow); - - newParticle->setDestination(mParticleTarget, - mParticleAcceleration.value(tick), - mParticleMomentum.value(tick)); - - newParticle->setDieDistance(mParticleDieDistance.value(tick)); - - newParticle->setLifetime(mParticleLifetime.value(tick)); - newParticle->setFadeOut(mParticleFadeOut.value(tick)); - newParticle->setFadeIn(mParticleFadeIn.value(tick)); - newParticle->setAlpha(mParticleAlpha.value(tick)); - - FOR_EACH (ParticleEmitterListCIter, it, mParticleChildEmitters) - newParticle->addEmitter(new ParticleEmitter(*it)); - - if (!mDeathEffect.empty()) - newParticle->setDeathEffect(mDeathEffect, mDeathEffectConditions); - - newParticles.push_back(newParticle); - } - - return newParticles; -} - -void ParticleEmitter::adjustSize(const int w, const int h) -{ - if (w == 0 || h == 0) - return; // new dimensions are illegal - - // calculate the old rectangle - const int oldArea = static_cast( - mParticlePosX.maxVal - mParticlePosX.minVal) * static_cast( - mParticlePosX.maxVal - mParticlePosY.minVal); - if (oldArea == 0) - { - // when the effect has no dimension it is - // not designed to be resizeable - return; - } - - // set the new dimensions - mParticlePosX.set(0, static_cast(w)); - mParticlePosY.set(0, static_cast(h)); - const int newArea = w * h; - // adjust the output so that the particle density stays the same - const float outputFactor = static_cast(newArea) - / static_cast(oldArea); - mOutput.minVal = static_cast(static_cast( - mOutput.minVal) * outputFactor); - mOutput.maxVal = static_cast(static_cast( - mOutput.maxVal) * outputFactor); -} diff --git a/src/particleemitter.h b/src/particleemitter.h deleted file mode 100644 index 28b894254..000000000 --- a/src/particleemitter.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#ifndef PARTICLEEMITTER_H -#define PARTICLEEMITTER_H - -#include "particleemitterprop.h" - -#include "resources/animation.h" - -#include "utils/xml.h" - -#include - -class Image; -class ImageSet; -class Map; -class Particle; - -/** - * Every Particle can have one or more particle emitters that create new - * particles when they are updated - */ -class ParticleEmitter final -{ - public: - ParticleEmitter(const XmlNodePtr emitterNode, Particle *const target, - Map *const map, const int rotation = 0, - const std::string& dyePalettes = std::string()); - - /** - * Copy Constructor (necessary for reference counting of particle images) - */ - ParticleEmitter(const ParticleEmitter &o); - - /** - * Assignment operator that calls the copy constructor - */ - ParticleEmitter & operator=(const ParticleEmitter &o); - - /** - * Destructor. - */ - ~ParticleEmitter(); - - /** - * Spawns new particles - * @return: a list of created particles - */ - std::list createParticles(const int tick); - - /** - * Sets the target of the particles that are created - */ - void setTarget(Particle *const target) - { mParticleTarget = target; } - - /** - * Changes the size of the emitter so that the effect fills a - * rectangle of this size - */ - void adjustSize(const int w, const int h); - - private: - template ParticleEmitterProp - readParticleEmitterProp(XmlNodePtr propertyNode, T def); - - ImageSet *getImageSet(XmlNodePtr node); - - /** - * initial position of particles: - */ - ParticleEmitterProp mParticlePosX, mParticlePosY, mParticlePosZ; - - /** - * initial vector of particles: - */ - ParticleEmitterProp mParticleAngleHorizontal, - mParticleAngleVertical; - - /** - * Initial velocity of particles - */ - ParticleEmitterProp mParticlePower; - - /* - * Vector changing of particles: - */ - ParticleEmitterProp mParticleGravity; - ParticleEmitterProp mParticleRandomness; - ParticleEmitterProp mParticleBounce; - - /* - * Properties of targeting particles: - */ - Particle *mParticleTarget; - ParticleEmitterProp mParticleAcceleration; - ParticleEmitterProp mParticleDieDistance; - ParticleEmitterProp mParticleMomentum; - - /* - * Behavior over time of the particles: - */ - ParticleEmitterProp mParticleLifetime; - ParticleEmitterProp mParticleFadeOut; - ParticleEmitterProp mParticleFadeIn; - - // Map the particles are spawned on - Map *mMap; - - // Number of particles spawned per update - ParticleEmitterProp mOutput; - - // Pause in frames between two spawns - ParticleEmitterProp mOutputPause; - - /* - * Graphical representation of the particles - */ - // Particle image, if used - Image *mParticleImage; - - // Filename of particle animation file - Animation mParticleAnimation; - - // Filename of particle rotation file - Animation mParticleRotation; - - // Opacity of the graphical representation of the particles - ParticleEmitterProp mParticleAlpha; - - /* - * Death effect of the particles - */ - std::string mDeathEffect; - - // List of emitters the spawned particles are equipped with - std::list mParticleChildEmitters; - - std::vector mTempSets; - - int mOutputPauseLeft; - - signed char mDeathEffectConditions; - - bool mParticleFollow; -}; -#endif // PARTICLEEMITTER_H diff --git a/src/particleemitterprop.h b/src/particleemitterprop.h deleted file mode 100644 index 6196a1eca..000000000 --- a/src/particleemitterprop.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#ifndef PARTICLEEMITTERPROP_H -#define PARTICLEEMITTERPROP_H - -#include -#include - -#include "localconsts.h" - -/** - * Returns a random numeric value that is larger than or equal min and smaller - * than max - */ - -enum ChangeFunc -{ - FUNC_NONE = 0, - FUNC_SINE, - FUNC_SAW, - FUNC_TRIANGLE, - FUNC_SQUARE -}; - -template struct ParticleEmitterProp final -{ - ParticleEmitterProp(): - minVal(0), maxVal(0), changeFunc(FUNC_NONE), - changeAmplitude(0), changePeriod(0), changePhase(0) - { - } - - void set(const T min, const T max) - { - minVal = min; - maxVal = max; - } - - void set(const T val) - { - set(val, val); - } - - void setFunction(ChangeFunc func, T amplitude, - const int period, const int phase) - { - changeFunc = func; - changeAmplitude = amplitude; - changePeriod = period; - if (!changePeriod) - changePeriod = 1; - changePhase = phase; - } - - T value(int tick) const - { - tick += changePhase; - T val = static_cast(minVal + (maxVal - minVal) - * (rand() / (static_cast(RAND_MAX) + 1))); - - switch (changeFunc) - { - case FUNC_SINE: - val += static_cast(std::sin(M_PI * 2 * (static_cast( - tick % changePeriod) / static_cast( - changePeriod)))) * changeAmplitude; - break; - case FUNC_SAW: - val += static_cast(changeAmplitude * (static_cast( - tick % changePeriod) / static_cast( - changePeriod))) * 2 - changeAmplitude; - break; - case FUNC_TRIANGLE: - if ((tick % changePeriod) * 2 < changePeriod) - { - val += changeAmplitude - static_cast(( - tick % changePeriod) / static_cast( - changePeriod)) * changeAmplitude * 4; - } - else - { - val += changeAmplitude * -3 + static_cast(( - tick % changePeriod) / static_cast( - changePeriod)) * changeAmplitude * 4; - // I have no idea why this works but it does - } - break; - case FUNC_SQUARE: - if ((tick % changePeriod) * 2 < changePeriod) - val += changeAmplitude; - else - val -= changeAmplitude; - break; - case FUNC_NONE: - default: - // nothing - break; - } - - return val; - } - - T minVal; - T maxVal; - - ChangeFunc changeFunc; - T changeAmplitude; - int changePeriod; - int changePhase; -}; - -#endif // PARTICLEEMITTERPROP_H diff --git a/src/rotationalparticle.cpp b/src/rotationalparticle.cpp deleted file mode 100644 index af04df1e7..000000000 --- a/src/rotationalparticle.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#include "rotationalparticle.h" - -#include "simpleanimation.h" - -#include "render/graphics.h" - -#include - -#include "debug.h" - -static const double PI = M_PI; -static const float PI2 = 2 * M_PI; - -RotationalParticle::RotationalParticle(Map *const map, - Animation *const animation) : - ImageParticle(map, nullptr), - mAnimation(new SimpleAnimation(animation)) -{ -} - -RotationalParticle::RotationalParticle(Map *const map, - const XmlNodePtr animationNode, - const std::string& dyePalettes): - ImageParticle(map, nullptr), - mAnimation(new SimpleAnimation(animationNode, dyePalettes)) -{ -} - -RotationalParticle::~RotationalParticle() -{ - delete mAnimation; - mAnimation = nullptr; - mImage = nullptr; -} - -bool RotationalParticle::update() -{ - if (!mAnimation) - return false; - - // TODO: cache velocities to avoid spamming atan2() - - const int size = mAnimation->getLength(); - if (!size) - return false; - - float rad = static_cast(atan2(mVelocity.x, mVelocity.y)); - if (rad < 0) - rad = PI2 + rad; - - const float range = static_cast(PI / size); - - // Determines which frame the particle should play - if (rad < range || rad > PI2 - range) - { - mAnimation->setFrame(0); - } - else - { - for (int c = 1; c < size; c++) - { - if (((static_cast(c) * (2 * range)) - range) < rad - && rad < ((static_cast(c) * (2 * range)) + range)) - { - mAnimation->setFrame(c); - break; - } - } - } - - mImage = mAnimation->getCurrentImage(); - - return Particle::update(); -} diff --git a/src/rotationalparticle.h b/src/rotationalparticle.h deleted file mode 100644 index d0108e530..000000000 --- a/src/rotationalparticle.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#ifndef ROTATIONALPARTICLE_H -#define ROTATIONALPARTICLE_H - -#include "imageparticle.h" - -#include "utils/xml.h" - -class Animation; -class Map; -class SimpleAnimation; - -class RotationalParticle final : public ImageParticle -{ - public: - RotationalParticle(Map *const map, Animation *const animation); - - RotationalParticle(Map *const map, const XmlNodePtr animationNode, - const std::string& dyePalettes = std::string()); - - A_DELETE_COPY(RotationalParticle) - - ~RotationalParticle(); - - virtual bool update() override; - - private: - SimpleAnimation *mAnimation; /**< Used animation for this particle */ -}; - -#endif // ROTATIONALPARTICLE_H diff --git a/src/statuseffect.h b/src/statuseffect.h index 15b0bd1e1..0a38e54f6 100644 --- a/src/statuseffect.h +++ b/src/statuseffect.h @@ -23,9 +23,10 @@ #ifndef STATUSEFFECT_H #define STATUSEFFECT_H -#include "particle.h" #include "animatedsprite.h" +#include "particle/particle.h" + #include "resources/animation.h" class StatusEffect final diff --git a/src/textparticle.cpp b/src/textparticle.cpp deleted file mode 100644 index c573ca567..000000000 --- a/src/textparticle.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#include "textparticle.h" - -#include "render/graphics.h" - -#include "gui/theme.h" - -#include -#include - -#include "debug.h" - -TextParticle::TextParticle(Map *const map, const std::string &text, - const gcn::Color *const color, - gcn::Font *const font, const bool outline) : - Particle(map), - mText(text), - mTextFont(font), - mColor(color), - mTextWidth(mTextFont ? mTextFont->getWidth(mText) / 2 : 1), - mOutline(outline) -{ -} - -bool TextParticle::draw(Graphics *const graphics, - const int offsetX, const int offsetY) const -{ - if (!mColor || !mTextFont) - return false; - - BLOCK_START("TextParticle::draw") - if (!isAlive()) - { - BLOCK_END("TextParticle::draw") - return false; - } - - const int screenX = static_cast(mPos.x) + offsetX; - const int screenY = static_cast(mPos.y) - static_cast(mPos.z) - + offsetY; - - float alpha = mAlpha * 255.0f; - - if (mFadeOut && mLifetimeLeft > -1 && mLifetimeLeft < mFadeOut) - { - alpha *= static_cast(mLifetimeLeft) - / static_cast(mFadeOut); - } - - if (mFadeIn && mLifetimePast < mFadeIn) - { - alpha *= static_cast(mLifetimePast) - / static_cast(mFadeIn); - } - - gcn::Color color = *mColor; - color.a = static_cast(alpha); - - graphics->setColor(color); - if (mOutline) - { - graphics->setColor2(Theme::getThemeColor( - Theme::OUTLINE, static_cast(alpha))); - } - mTextFont->drawString(graphics, mText, screenX - mTextWidth, screenY); - BLOCK_END("TextParticle::draw") - return true; -} diff --git a/src/textparticle.h b/src/textparticle.h deleted file mode 100644 index f48a57196..000000000 --- a/src/textparticle.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2006-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2013 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program 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 - * 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, see . - */ - -#ifndef TEXTPARTICLE_H -#define TEXTPARTICLE_H - -#include "particle.h" - -class TextParticle final : public Particle -{ - public: - /** - * Constructor. - */ - TextParticle(Map *const map, const std::string &text, - const gcn::Color *const color, - gcn::Font *const font, const bool outline = false); - - A_DELETE_COPY(TextParticle) - - /** - * Draws the particle image. - */ - virtual bool draw(Graphics *const graphics, - const int offsetX, const int offsetY) const override; - - // hack to improve text visibility - virtual int getPixelY() const override A_WARN_UNUSED - { return static_cast(mPos.y + mPos.z); } - - // hack to improve text visibility (for sorting only) - virtual int getSortPixelY() const override A_WARN_UNUSED - { return static_cast(mPos.y + mPos.z); } - - private: - std::string mText; /**< Text of the particle. */ - gcn::Font *mTextFont; /**< Font used for drawing the text. */ - const gcn::Color *mColor; /**< Color used for drawing the text. */ - int mTextWidth; - bool mOutline; /**< Make the text better readable */ -}; - -#endif // TEXTPARTICLE_H -- cgit v1.2.3-60-g2f50