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