~ubuntu-branches/ubuntu/trusty/manaplus/trusty-proposed

« back to all changes in this revision

Viewing changes to src/particle.cpp

  • Committer: Package Import Robot
  • Author(s): Patrick Matthäi
  • Date: 2013-09-17 10:35:51 UTC
  • mfrom: (1.1.10)
  • Revision ID: package-import@ubuntu.com-20130917103551-az7p3nz9jgxwqjfn
Tags: 1.3.9.15-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 *  The ManaPlus Client
3
 
 *  Copyright (C) 2006-2009  The Mana World Development Team
4
 
 *  Copyright (C) 2009-2010  The Mana Developers
5
 
 *  Copyright (C) 2011-2013  The ManaPlus Developers
6
 
 *
7
 
 *  This file is part of The ManaPlus Client.
8
 
 *
9
 
 *  This program is free software; you can redistribute it and/or modify
10
 
 *  it under the terms of the GNU General Public License as published by
11
 
 *  the Free Software Foundation; either version 2 of the License, or
12
 
 *  any later version.
13
 
 *
14
 
 *  This program is distributed in the hope that it will be useful,
15
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 
 *  GNU General Public License for more details.
18
 
 *
19
 
 *  You should have received a copy of the GNU General Public License
20
 
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 
 */
22
 
 
23
 
#include "particle.h"
24
 
 
25
 
#include "animationparticle.h"
26
 
#include "configuration.h"
27
 
#include "resources/dye.h"
28
 
#include "logger.h"
29
 
#include "map.h"
30
 
#include "particleemitter.h"
31
 
#include "rotationalparticle.h"
32
 
#include "textparticle.h"
33
 
 
34
 
#include "resources/resourcemanager.h"
35
 
 
36
 
#include "gui/sdlfont.h"
37
 
 
38
 
#include "utils/dtor.h"
39
 
#include "utils/mathutils.h"
40
 
 
41
 
#include <guichan/color.hpp>
42
 
 
43
 
#include <algorithm>
44
 
#include <cmath>
45
 
 
46
 
#include "debug.h"
47
 
 
48
 
static const float SIN45 = 0.707106781f;
49
 
 
50
 
class Graphics;
51
 
class Image;
52
 
 
53
 
int Particle::particleCount = 0;
54
 
int Particle::maxCount = 0;
55
 
int Particle::fastPhysics = 0;
56
 
int Particle::emitterSkip = 1;
57
 
bool Particle::enabled = true;
58
 
const float Particle::PARTICLE_SKY = 800.0f;
59
 
 
60
 
Particle::Particle(Map *const map) :
61
 
    Actor(),
62
 
    mAlpha(1.0f),
63
 
    mLifetimeLeft(-1),
64
 
    mLifetimePast(0),
65
 
    mFadeOut(0),
66
 
    mFadeIn(0),
67
 
    mVelocity(),
68
 
    mAlive(ALIVE),
69
 
    mChildEmitters(),
70
 
    mChildParticles(),
71
 
    mDeathEffect(),
72
 
    mGravity(0.0f),
73
 
    mBounce(0.0f),
74
 
    mAcceleration(0.0f),
75
 
    mInvDieDistance(-1.0f),
76
 
    mMomentum(1.0f),
77
 
    mTarget(nullptr),
78
 
    mRandomness(0),
79
 
    mDeathEffectConditions(0x00),
80
 
    mAutoDelete(true),
81
 
    mAllowSizeAdjust(false),
82
 
    mFollow(false)
83
 
{
84
 
    setMap(map);
85
 
    Particle::particleCount++;
86
 
}
87
 
 
88
 
Particle::~Particle()
89
 
{
90
 
    // Delete child emitters and child particles
91
 
    clear();
92
 
    Particle::particleCount--;
93
 
}
94
 
 
95
 
void Particle::setupEngine()
96
 
{
97
 
    Particle::maxCount = config.getIntValue("particleMaxCount");
98
 
    Particle::fastPhysics = config.getIntValue("particleFastPhysics");
99
 
    Particle::emitterSkip = config.getIntValue("particleEmitterSkip") + 1;
100
 
    if (!Particle::emitterSkip)
101
 
        Particle::emitterSkip = 1;
102
 
    Particle::enabled = config.getBoolValue("particleeffects");
103
 
    disableAutoDelete();
104
 
    logger->log1("Particle engine set up");
105
 
}
106
 
 
107
 
bool Particle::draw(Graphics *const, const int, const int) const
108
 
{
109
 
    return false;
110
 
}
111
 
 
112
 
bool Particle::update()
113
 
{
114
 
    if (!mMap)
115
 
        return false;
116
 
 
117
 
    if (mLifetimeLeft == 0 && mAlive == ALIVE)
118
 
        mAlive = DEAD_TIMEOUT;
119
 
 
120
 
    const Vector oldPos = mPos;
121
 
 
122
 
    if (mAlive == ALIVE)
123
 
    {
124
 
        // calculate particle movement
125
 
        if (mMomentum != 1.0f)
126
 
            mVelocity *= mMomentum;
127
 
 
128
 
        if (mTarget && mAcceleration != 0.0f)
129
 
        {
130
 
            Vector dist = mPos - mTarget->mPos;
131
 
            dist.x *= SIN45;
132
 
            float invHypotenuse;
133
 
 
134
 
            switch (Particle::fastPhysics)
135
 
            {
136
 
                case 1:
137
 
                    invHypotenuse = fastInvSqrt(
138
 
                        dist.x * dist.x + dist.y * dist.y + dist.z * dist.z);
139
 
                    break;
140
 
                case 2:
141
 
                    if (!dist.x)
142
 
                    {
143
 
                        invHypotenuse = 0;
144
 
                        break;
145
 
                    }
146
 
 
147
 
                    invHypotenuse = 2.0f / (static_cast<float>(fabs(dist.x))
148
 
                                    + static_cast<float>(fabs(dist.y))
149
 
                                    + static_cast<float>(fabs(dist.z)));
150
 
                    break;
151
 
                default:
152
 
                    invHypotenuse = 1.0f / static_cast<float>(sqrt(
153
 
                        dist.x * dist.x + dist.y * dist.y + dist.z * dist.z));
154
 
                    break;
155
 
            }
156
 
 
157
 
            if (invHypotenuse)
158
 
            {
159
 
                if (mInvDieDistance > 0.0f && invHypotenuse > mInvDieDistance)
160
 
                    mAlive = DEAD_IMPACT;
161
 
                const float accFactor = invHypotenuse * mAcceleration;
162
 
                mVelocity -= dist * accFactor;
163
 
            }
164
 
        }
165
 
 
166
 
        if (mRandomness > 0)
167
 
        {
168
 
            mVelocity.x += static_cast<float>((rand() % mRandomness - rand()
169
 
                           % mRandomness)) / 1000.0f;
170
 
            mVelocity.y += static_cast<float>((rand() % mRandomness - rand()
171
 
                           % mRandomness)) / 1000.0f;
172
 
            mVelocity.z += static_cast<float>((rand() % mRandomness - rand()
173
 
                           % mRandomness)) / 1000.0f;
174
 
        }
175
 
 
176
 
        mVelocity.z -= mGravity;
177
 
 
178
 
        // Update position
179
 
        mPos.x += mVelocity.x;
180
 
        mPos.y += mVelocity.y * SIN45;
181
 
        mPos.z += mVelocity.z * SIN45;
182
 
 
183
 
        // Update other stuff
184
 
        if (mLifetimeLeft > 0)
185
 
            mLifetimeLeft--;
186
 
 
187
 
        mLifetimePast++;
188
 
 
189
 
        if (mPos.z < 0.0f)
190
 
        {
191
 
            if (mBounce > 0.0f)
192
 
            {
193
 
                mPos.z *= -mBounce;
194
 
                mVelocity *= mBounce;
195
 
                mVelocity.z = -mVelocity.z;
196
 
            }
197
 
            else
198
 
            {
199
 
                mAlive = DEAD_FLOOR;
200
 
            }
201
 
        }
202
 
        else if (mPos.z > PARTICLE_SKY)
203
 
        {
204
 
            mAlive = DEAD_SKY;
205
 
        }
206
 
 
207
 
        // Update child emitters
208
 
        if (Particle::emitterSkip && (mLifetimePast - 1)
209
 
            % Particle::emitterSkip == 0)
210
 
        {
211
 
            FOR_EACH (EmitterConstIterator, e, mChildEmitters)
212
 
            {
213
 
                Particles newParticles = (*e)->createParticles(mLifetimePast);
214
 
                FOR_EACH (ParticleConstIterator, it, newParticles)
215
 
                {
216
 
                    Particle *const p = *it;
217
 
                    p->moveBy(mPos);
218
 
                    mChildParticles.push_back(p);
219
 
                }
220
 
            }
221
 
        }
222
 
    }
223
 
 
224
 
    // create death effect when the particle died
225
 
    if (mAlive != ALIVE && mAlive != DEAD_LONG_AGO)
226
 
    {
227
 
        if ((mAlive & mDeathEffectConditions) > 0x00  && !mDeathEffect.empty())
228
 
        {
229
 
            Particle *const deathEffect = particleEngine->addEffect(
230
 
                mDeathEffect, 0, 0);
231
 
            if (deathEffect)
232
 
                deathEffect->moveBy(mPos);
233
 
        }
234
 
        mAlive = DEAD_LONG_AGO;
235
 
    }
236
 
 
237
 
    const Vector change = mPos - oldPos;
238
 
 
239
 
    // Update child particles
240
 
 
241
 
    for (ParticleIterator p = mChildParticles.begin(),
242
 
         p2 = mChildParticles.end(); p != p2; )
243
 
    {
244
 
        Particle *const particle = *p;
245
 
        // move particle with its parent if desired
246
 
        if (particle->mFollow)
247
 
            particle->moveBy(change);
248
 
 
249
 
        // update particle
250
 
        if (particle->update())
251
 
        {
252
 
            ++p;
253
 
        }
254
 
        else
255
 
        {
256
 
            delete particle;
257
 
            p = mChildParticles.erase(p);
258
 
        }
259
 
    }
260
 
    if (mAlive != ALIVE && mChildParticles.empty() && mAutoDelete)
261
 
        return false;
262
 
 
263
 
    return true;
264
 
}
265
 
 
266
 
void Particle::moveBy(const Vector &change)
267
 
{
268
 
    mPos += change;
269
 
    FOR_EACH (ParticleConstIterator, p, mChildParticles)
270
 
    {
271
 
        Particle *const particle = *p;
272
 
        if (particle->mFollow)
273
 
            particle->moveBy(change);
274
 
    }
275
 
}
276
 
 
277
 
void Particle::moveTo(const float x, const float y)
278
 
{
279
 
    moveTo(Vector(x, y, mPos.z));
280
 
}
281
 
 
282
 
Particle *Particle::createChild()
283
 
{
284
 
    Particle *const newParticle = new Particle(mMap);
285
 
    mChildParticles.push_back(newParticle);
286
 
    return newParticle;
287
 
}
288
 
 
289
 
Particle *Particle::addEffect(const std::string &particleEffectFile,
290
 
                              const int pixelX, const int pixelY,
291
 
                              const int rotation)
292
 
{
293
 
    Particle *newParticle = nullptr;
294
 
 
295
 
    const size_t pos = particleEffectFile.find('|');
296
 
    const std::string dyePalettes = (pos != std::string::npos)
297
 
        ? particleEffectFile.substr(pos + 1) : "";
298
 
    XML::Document doc(particleEffectFile.substr(0, pos));
299
 
    const XmlNodePtr rootNode = doc.rootNode();
300
 
 
301
 
    if (!rootNode || !xmlNameEqual(rootNode, "effect"))
302
 
    {
303
 
        logger->log("Error loading particle: %s", particleEffectFile.c_str());
304
 
        return nullptr;
305
 
    }
306
 
 
307
 
    ResourceManager *const resman = ResourceManager::getInstance();
308
 
 
309
 
    // Parse particles
310
 
    for_each_xml_child_node(effectChildNode, rootNode)
311
 
    {
312
 
        // We're only interested in particles
313
 
        if (!xmlNameEqual(effectChildNode, "particle"))
314
 
            continue;
315
 
 
316
 
        // Determine the exact particle type
317
 
        XmlNodePtr node;
318
 
 
319
 
        // Animation
320
 
        if ((node = XML::findFirstChildByName(effectChildNode, "animation")))
321
 
        {
322
 
            newParticle = new AnimationParticle(mMap, node, dyePalettes);
323
 
        }
324
 
        // Rotational
325
 
        else if ((node = XML::findFirstChildByName(
326
 
                 effectChildNode, "rotation")))
327
 
        {
328
 
            newParticle = new RotationalParticle(mMap, node, dyePalettes);
329
 
        }
330
 
        // Image
331
 
        else if ((node = XML::findFirstChildByName(effectChildNode, "image")))
332
 
        {
333
 
            std::string imageSrc = reinterpret_cast<const char*>(
334
 
                node->xmlChildrenNode->content);
335
 
            if (!imageSrc.empty() && !dyePalettes.empty())
336
 
                Dye::instantiate(imageSrc, dyePalettes);
337
 
            Image *const img = resman->getImage(imageSrc);
338
 
 
339
 
            newParticle = new ImageParticle(mMap, img);
340
 
        }
341
 
        // Other
342
 
        else
343
 
        {
344
 
            newParticle = new Particle(mMap);
345
 
        }
346
 
 
347
 
        // Read and set the basic properties of the particle
348
 
        const float offsetX = static_cast<float>(XML::getFloatProperty(
349
 
            effectChildNode, "position-x", 0));
350
 
        const float offsetY = static_cast<float>(XML::getFloatProperty(
351
 
            effectChildNode, "position-y", 0));
352
 
        const float offsetZ = static_cast<float>(XML::getFloatProperty(
353
 
            effectChildNode, "position-z", 0));
354
 
        const Vector position(mPos.x + static_cast<float>(pixelX) + offsetX,
355
 
            mPos.y + static_cast<float>(pixelY) + offsetY,
356
 
            mPos.z + offsetZ);
357
 
        newParticle->moveTo(position);
358
 
 
359
 
        const int lifetime = XML::getProperty(effectChildNode, "lifetime", -1);
360
 
        newParticle->setLifetime(lifetime);
361
 
        const bool resizeable = "false" != XML::getProperty(effectChildNode,
362
 
            "size-adjustable", "false");
363
 
 
364
 
        newParticle->setAllowSizeAdjust(resizeable);
365
 
 
366
 
        // Look for additional emitters for this particle
367
 
        for_each_xml_child_node(emitterNode, effectChildNode)
368
 
        {
369
 
            if (xmlNameEqual(emitterNode, "emitter"))
370
 
            {
371
 
                ParticleEmitter *const newEmitter = new ParticleEmitter(
372
 
                    emitterNode, newParticle, mMap, rotation, dyePalettes);
373
 
                newParticle->addEmitter(newEmitter);
374
 
            }
375
 
            else if (xmlNameEqual(emitterNode, "deatheffect"))
376
 
            {
377
 
                const std::string deathEffect = reinterpret_cast<const char*>(
378
 
                    emitterNode->xmlChildrenNode->content);
379
 
 
380
 
                char deathEffectConditions = 0x00;
381
 
                if (XML::getBoolProperty(emitterNode, "on-floor", true))
382
 
                {
383
 
                    deathEffectConditions += static_cast<signed char>(
384
 
                        Particle::DEAD_FLOOR);
385
 
                }
386
 
                if (XML::getBoolProperty(emitterNode, "on-sky", true))
387
 
                {
388
 
                    deathEffectConditions += static_cast<signed char>(
389
 
                        Particle::DEAD_SKY);
390
 
                }
391
 
                if (XML::getBoolProperty(emitterNode, "on-other", false))
392
 
                {
393
 
                    deathEffectConditions += static_cast<signed char>(
394
 
                        Particle::DEAD_OTHER);
395
 
                }
396
 
                if (XML::getBoolProperty(emitterNode, "on-impact", true))
397
 
                {
398
 
                    deathEffectConditions += static_cast<signed char>(
399
 
                        Particle::DEAD_IMPACT);
400
 
                }
401
 
                if (XML::getBoolProperty(emitterNode, "on-timeout", true))
402
 
                {
403
 
                    deathEffectConditions += static_cast<signed char>(
404
 
                        Particle::DEAD_TIMEOUT);
405
 
                }
406
 
                newParticle->setDeathEffect(
407
 
                    deathEffect, deathEffectConditions);
408
 
            }
409
 
        }
410
 
 
411
 
        mChildParticles.push_back(newParticle);
412
 
    }
413
 
 
414
 
    return newParticle;
415
 
}
416
 
 
417
 
Particle *Particle::addTextSplashEffect(const std::string &text,
418
 
                                        const int x, const int y,
419
 
                                        const gcn::Color *const color,
420
 
                                        gcn::Font *const font,
421
 
                                        const bool outline)
422
 
{
423
 
    Particle *const newParticle = new TextParticle(
424
 
        mMap, text, color, font, outline);
425
 
    newParticle->moveTo(static_cast<float>(x), static_cast<float>(y));
426
 
    newParticle->setVelocity(
427
 
        static_cast<float>((rand() % 100) - 50) / 200.0f,       // X
428
 
        static_cast<float>((rand() % 100) - 50) / 200.0f,       // Y
429
 
        (static_cast<float>((rand() % 100)) / 200.0f) + 4.0f);  // Z
430
 
 
431
 
    newParticle->setGravity(0.1f);
432
 
    newParticle->setBounce(0.5f);
433
 
    newParticle->setLifetime(200);
434
 
    newParticle->setFadeOut(100);
435
 
 
436
 
    mChildParticles.push_back(newParticle);
437
 
 
438
 
    return newParticle;
439
 
}
440
 
 
441
 
Particle *Particle::addTextRiseFadeOutEffect(const std::string &text,
442
 
                                             const int x, const int y,
443
 
                                             const gcn::Color *const color,
444
 
                                             gcn::Font *const font,
445
 
                                             const bool outline)
446
 
{
447
 
    Particle *const newParticle = new TextParticle(
448
 
        mMap, text, color, font, outline);
449
 
    newParticle->moveTo(static_cast<float>(x), static_cast<float>(y));
450
 
    newParticle->setVelocity(0.0f, 0.0f, 0.5f);
451
 
    newParticle->setGravity(0.0015f);
452
 
    newParticle->setLifetime(300);
453
 
    newParticle->setFadeOut(100);
454
 
    newParticle->setFadeIn(0);
455
 
 
456
 
    mChildParticles.push_back(newParticle);
457
 
 
458
 
    return newParticle;
459
 
}
460
 
 
461
 
void Particle::adjustEmitterSize(const int w, const int h)
462
 
{
463
 
    if (mAllowSizeAdjust)
464
 
    {
465
 
        FOR_EACH (EmitterConstIterator, e, mChildEmitters)
466
 
            (*e)->adjustSize(w, h);
467
 
    }
468
 
}
469
 
 
470
 
void Particle::clear()
471
 
{
472
 
    delete_all(mChildEmitters);
473
 
    mChildEmitters.clear();
474
 
 
475
 
    delete_all(mChildParticles);
476
 
    mChildParticles.clear();
477
 
}