GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/particle/particleengine.cpp Lines: 1 137 0.7 %
Date: 2017-11-29 Branches: 0 176 0.0 %

Line Branch Exec Source
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-2017  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 "configuration.h"
24
25
#include "gui/viewport.h"
26
27
#include "particle/animationparticle.h"
28
#include "particle/particleemitter.h"
29
#include "particle/rotationalparticle.h"
30
#include "particle/textparticle.h"
31
32
#include "resources/dye/dye.h"
33
34
#include "resources/loaders/imageloader.h"
35
#include "resources/loaders/xmlloader.h"
36
37
#include "utils/dtor.h"
38
39
#include "debug.h"
40
41
ParticleEngine *particleEngine = nullptr;
42
43
class Image;
44
45
int ParticleEngine::particleCount = 0;
46
int ParticleEngine::maxCount = 0;
47
ParticlePhysicsT ParticleEngine::fastPhysics = ParticlePhysics::Best;
48
int ParticleEngine::emitterSkip = 1;
49
bool ParticleEngine::enabled = true;
50
const float ParticleEngine::PARTICLE_SKY = 800.0F;
51
52
ParticleEngine::ParticleEngine() :
53
    mChildParticles(),
54
    mChildMoveParticles(),
55
    mMap(nullptr)
56
{
57
    ParticleEngine::particleCount++;
58
}
59
60
ParticleEngine::~ParticleEngine()
61
{
62
    // Delete child emitters and child particles
63
    clear();
64
    ParticleEngine::particleCount--;
65
}
66
67
void ParticleEngine::setupEngine() restrict2
68
{
69
    ParticleEngine::maxCount = config.getIntValue("particleMaxCount");
70
    ParticleEngine::fastPhysics = fromInt(config.getIntValue(
71
        "particleFastPhysics"),
72
        ParticlePhysicsT);
73
    ParticleEngine::emitterSkip =
74
        config.getIntValue("particleEmitterSkip") + 1;
75
    if (ParticleEngine::emitterSkip == 0)
76
        ParticleEngine::emitterSkip = 1;
77
    ParticleEngine::enabled = config.getBoolValue("particleeffects");
78
    logger->log1("Particle engine set up");
79
}
80
81
bool ParticleEngine::update() restrict2
82
{
83
    if (mChildParticles.empty() || (mMap == nullptr))
84
        return true;
85
86
    // Update child particles
87
88
    const int cameraX = viewport->getCameraX();
89
    const int cameraY = viewport->getCameraY();
90
    const float x1 = static_cast<float>(cameraX - 3000);
91
    const float y1 = static_cast<float>(cameraY - 2000);
92
    const float x2 = static_cast<float>(cameraX + 3000);
93
    const float y2 = static_cast<float>(cameraY + 2000);
94
95
    for (ParticleIterator p = mChildParticles.begin(),
96
         fp2 = mChildParticles.end(); p != fp2; )
97
    {
98
        Particle *restrict const particle = *p;
99
        const float posX = particle->mPos.x;
100
        const float posY = particle->mPos.y;
101
        if (posX < x1 || posX > x2 || posY < y1 || posY > y2)
102
        {
103
            ++p;
104
            continue;
105
        }
106
        // update particle
107
        if (particle->update())
108
        {
109
            ++p;
110
        }
111
        else
112
        {
113
            mChildMoveParticles.remove(*p);
114
            delete particle;
115
            p = mChildParticles.erase(p);
116
        }
117
    }
118
    return true;
119
}
120
121
Particle *ParticleEngine::createChild() restrict2
122
{
123
    Particle *const newParticle = new Particle;
124
    newParticle->setMap(mMap);
125
    mChildParticles.push_back(newParticle);
126
    return newParticle;
127
}
128
129
Particle *ParticleEngine::addEffect(const std::string &restrict
130
                                    particleEffectFile,
131
                                    const int pixelX,
132
                                    const int pixelY,
133
                                    const int rotation) restrict2
134
{
135
    Particle *newParticle = nullptr;
136
137
    const size_t pos = particleEffectFile.find('|');
138
    const std::string dyePalettes = (pos != std::string::npos)
139
        ? particleEffectFile.substr(pos + 1) : "";
140
    XML::Document *doc = Loader::getXml(
141
        particleEffectFile.substr(0, pos),
142
        UseVirtFs_true,
143
        SkipError_false);
144
    if (doc == nullptr)
145
        return nullptr;
146
147
    XmlNodeConstPtrConst rootNode = doc->rootNode();
148
149
    if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "effect"))
150
    {
151
        logger->log("Error loading particle: %s", particleEffectFile.c_str());
152
        doc->decRef();
153
        return nullptr;
154
    }
155
156
    // Parse particles
157
    for_each_xml_child_node(effectChildNode, rootNode)
158
    {
159
        // We're only interested in particles
160
        if (!xmlNameEqual(effectChildNode, "particle"))
161
            continue;
162
163
        // Determine the exact particle type
164
        XmlNodePtr node;
165
166
        // Animation
167
        if ((node = XML::findFirstChildByName(effectChildNode, "animation")) !=
168
            nullptr)
169
        {
170
            newParticle = new AnimationParticle(node, dyePalettes);
171
            newParticle->setMap(mMap);
172
        }
173
        // Rotational
174
        else if ((node = XML::findFirstChildByName(
175
                 effectChildNode, "rotation")) != nullptr)
176
        {
177
            newParticle = new RotationalParticle(node, dyePalettes);
178
            newParticle->setMap(mMap);
179
        }
180
        // Image
181
        else if ((node = XML::findFirstChildByName(effectChildNode,
182
                 "image")) != nullptr)
183
        {
184
            std::string imageSrc;
185
            if (XmlHaveChildContent(node))
186
                imageSrc = XmlChildContent(node);
187
            if (!imageSrc.empty() && !dyePalettes.empty())
188
                Dye::instantiate(imageSrc, dyePalettes);
189
            Image *const img = Loader::getImage(imageSrc);
190
191
            newParticle = new ImageParticle(img);
192
            newParticle->setMap(mMap);
193
        }
194
        // Other
195
        else
196
        {
197
            newParticle = new Particle;
198
            newParticle->setMap(mMap);
199
        }
200
201
        // Read and set the basic properties of the particle
202
        const float offsetX = XML::getFloatProperty(
203
            effectChildNode, "position-x", 0);
204
        const float offsetY = XML::getFloatProperty(
205
            effectChildNode, "position-y", 0);
206
        const float offsetZ = XML::getFloatProperty(
207
            effectChildNode, "position-z", 0);
208
        const Vector position(static_cast<float>(pixelX) + offsetX,
209
            static_cast<float>(pixelY) + offsetY,
210
            offsetZ);
211
        newParticle->moveTo(position);
212
213
        const int lifetime = XML::getProperty(effectChildNode, "lifetime", -1);
214
        newParticle->setLifetime(lifetime);
215
        const bool resizeable = "false" != XML::getProperty(effectChildNode,
216
            "size-adjustable", "false");
217
218
        newParticle->setAllowSizeAdjust(resizeable);
219
220
        // Look for additional emitters for this particle
221
        for_each_xml_child_node(emitterNode, effectChildNode)
222
        {
223
            if (xmlNameEqual(emitterNode, "emitter"))
224
            {
225
                ParticleEmitter *restrict const newEmitter =
226
                    new ParticleEmitter(
227
                    emitterNode,
228
                    newParticle,
229
                    mMap,
230
                    rotation,
231
                    dyePalettes);
232
                newParticle->addEmitter(newEmitter);
233
            }
234
            else if (xmlNameEqual(emitterNode, "deatheffect"))
235
            {
236
                std::string deathEffect;
237
                if ((node != nullptr) && XmlHaveChildContent(node))
238
                    deathEffect = XmlChildContent(emitterNode);
239
240
                char deathEffectConditions = 0x00;
241
                if (XML::getBoolProperty(emitterNode, "on-floor", true))
242
                {
243
                    deathEffectConditions += CAST_S8(
244
                        AliveStatus::DEAD_FLOOR);
245
                }
246
                if (XML::getBoolProperty(emitterNode, "on-sky", true))
247
                {
248
                    deathEffectConditions += CAST_S8(
249
                        AliveStatus::DEAD_SKY);
250
                }
251
                if (XML::getBoolProperty(emitterNode, "on-other", false))
252
                {
253
                    deathEffectConditions += CAST_S8(
254
                        AliveStatus::DEAD_OTHER);
255
                }
256
                if (XML::getBoolProperty(emitterNode, "on-impact", true))
257
                {
258
                    deathEffectConditions += CAST_S8(
259
                        AliveStatus::DEAD_IMPACT);
260
                }
261
                if (XML::getBoolProperty(emitterNode, "on-timeout", true))
262
                {
263
                    deathEffectConditions += CAST_S8(
264
                        AliveStatus::DEAD_TIMEOUT);
265
                }
266
                newParticle->setDeathEffect(
267
                    deathEffect, deathEffectConditions);
268
            }
269
        }
270
271
        mChildParticles.push_back(newParticle);
272
    }
273
274
    doc->decRef();
275
    return newParticle;
276
}
277
278
Particle *ParticleEngine::addTextSplashEffect(const std::string &restrict text,
279
                                              const int x,
280
                                              const int y,
281
                                              const Color *restrict const
282
                                              color,
283
                                              Font *restrict const font,
284
                                              const bool outline) restrict2
285
{
286
    Particle *const newParticle = new TextParticle(
287
        text,
288
        color,
289
        font,
290
        outline);
291
    newParticle->setMap(mMap);
292
    newParticle->moveTo(static_cast<float>(x),
293
        static_cast<float>(y));
294
    newParticle->setVelocity(
295
        static_cast<float>((rand() % 100) - 50) / 200.0F,       // X
296
        static_cast<float>((rand() % 100) - 50) / 200.0F,       // Y
297
        (static_cast<float>((rand() % 100)) / 200.0F) + 4.0F);  // Z
298
299
    newParticle->setGravity(0.1F);
300
    newParticle->setBounce(0.5F);
301
    newParticle->setLifetime(200);
302
    newParticle->setFadeOut(100);
303
304
    mChildParticles.push_back(newParticle);
305
306
    return newParticle;
307
}
308
309
Particle *ParticleEngine::addTextRiseFadeOutEffect(const std::string &restrict
310
                                                   text,
311
                                                   const int x,
312
                                                   const int y,
313
                                                   const Color *restrict const
314
                                                   color,
315
                                                   Font *restrict const font,
316
                                                   const bool outline)
317
                                                   restrict2
318
{
319
    Particle *const newParticle = new TextParticle(
320
        text,
321
        color,
322
        font,
323
        outline);
324
    newParticle->setMap(mMap);
325
    newParticle->moveTo(static_cast<float>(x),
326
        static_cast<float>(y));
327
    newParticle->setVelocity(0.0F, 0.0F, 0.5F);
328
    newParticle->setGravity(0.0015F);
329
    newParticle->setLifetime(300);
330
    newParticle->setFadeOut(100);
331
    newParticle->setFadeIn(0);
332
333
    mChildParticles.push_back(newParticle);
334
335
    return newParticle;
336
}
337
338
void ParticleEngine::clear() restrict2
339
{
340
    delete_all(mChildParticles);
341
    mChildParticles.clear();
342
    mChildMoveParticles.clear();
343
4
}