ManaPlus
particleengine.cpp
Go to the documentation of this file.
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 
31 #include "particle/textparticle.h"
32 
33 #include "resources/dye/dye.h"
34 
37 
38 #include "utils/dtor.h"
39 
40 #include "debug.h"
41 
43 
44 class Image;
45 
50 bool ParticleEngine::enabled = true;
51 const float ParticleEngine::PARTICLE_SKY = 800.0F;
52 
54  mChildParticles(),
55  mChildMoveParticles(),
56  mMap(nullptr)
57 {
59 }
60 
62 {
63  // Delete child emitters and child particles
64  clear();
66 }
67 
69 {
70  ParticleEngine::maxCount = config.getIntValue("particleMaxCount");
72  "particleFastPhysics"),
75  config.getIntValue("particleEmitterSkip") + 1;
78  ParticleEngine::enabled = config.getBoolValue("particleeffects");
79  logger->log1("Particle engine set up");
80 }
81 
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 
123 {
124  Particle *const newParticle = new Particle;
125  newParticle->setMap(mMap);
126  mChildParticles.push_back(newParticle);
127  return newParticle;
128 }
129 
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) : "";
142  particleEffectFile.substr(0, pos),
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(
246  }
247  if (XML::getBoolProperty(emitterNode, "on-sky", true))
248  {
249  deathEffectConditions += CAST_S8(
251  }
252  if (XML::getBoolProperty(emitterNode, "on-other", false))
253  {
254  deathEffectConditions += CAST_S8(
256  }
257  if (XML::getBoolProperty(emitterNode, "on-impact", true))
258  {
259  deathEffectConditions += CAST_S8(
261  }
262  if (XML::getBoolProperty(emitterNode, "on-timeout", true))
263  {
264  deathEffectConditions += CAST_S8(
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 
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 
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 
340 {
342  mChildParticles.clear();
343  mChildMoveParticles.clear();
344 }
#define CAST_S8
Definition: cast.h:26
virtual void setMap(Map *const map)
Definition: actor.cpp:48
Definition: color.h:76
bool getBoolValue(const std::string &key) const
int getIntValue(const std::string &key) const
static void instantiate(std::string &target, const std::string &palettes)
Definition: dye.cpp:97
Definition: font.h:90
void log(const char *const log_text,...)
Definition: logger.cpp:269
void log1(const char *const log_text)
Definition: logger.cpp:238
static int particleCount
static const float PARTICLE_SKY
Particle * createChild()
static int maxCount
Particle * addEffect(const std::string &particleEffectFile, const int pixelX, const int pixelY, const int rotation)
Particle * addTextRiseFadeOutEffect(const std::string &text, const int x, const int y, const Color *const color, Font *const font, const bool outline)
static ParticlePhysicsT fastPhysics
Particles mChildMoveParticles
static bool enabled
Particle * addTextSplashEffect(const std::string &text, const int x, const int y, const Color *const color, Font *const font, const bool outline)
static int emitterSkip
static void setupEngine()
Particles mChildParticles
void setLifetime(const int lifetime)
Definition: particle.h:123
void setGravity(const float gravity)
Definition: particle.h:151
void setBounce(const float bouncieness)
Definition: particle.h:164
void setAllowSizeAdjust(const bool adjust)
Definition: particle.h:203
virtual void setDeathEffect(const std::string &effectFile, const signed char conditions)
Definition: particle.h:240
void addEmitter(ParticleEmitter *const emitter)
Definition: particle.h:101
void setFadeIn(const int fadeIn)
Definition: particle.h:137
void setFadeOut(const int fadeOut)
Definition: particle.h:130
void moveTo(const Vector &pos)
Definition: particle.h:107
void setVelocity(const float x, const float y, const float z)
Definition: particle.h:143
virtual void decRef()
Definition: resource.cpp:50
Definition: vector.h:40
int getCameraY() const
Definition: viewport.h:121
int getCameraX() const
Definition: viewport.h:115
xmlNodePtr rootNode()
Definition: libxml.cpp:169
Configuration config
void delete_all(Container &c)
Definition: dtor.h:56
Viewport * viewport
Definition: viewport.cpp:36
#define fromInt(val, name)
Definition: intdefines.h:46
#define for_each_xml_child_node(var, parent)
Definition: libxml.h:161
#define restrict
Definition: localconsts.h:165
#define restrict2
Definition: localconsts.h:166
#define nullptr
Definition: localconsts.h:45
Logger * logger
Definition: logger.cpp:89
std::string mMap
Definition: gamerecv.cpp:46
XML::Document * getXml(const std::string &idPath, const UseVirtFs useResman, const SkipError skipError)
Definition: xmlloader.cpp:56
Image * getImage(const std::string &idPath)
Definition: imageloader.cpp:86
float getFloatProperty(const xmlNodePtr node, const char *const name, float def)
Definition: libxml.cpp:211
bool getBoolProperty(const xmlNodePtr node, const char *const name, const bool def)
Definition: libxml.cpp:269
int getProperty(const xmlNodePtr node, const char *const name, int def)
Definition: libxml.cpp:174
xmlNodePtr findFirstChildByName(const xmlNode *const parent, const char *const name)
Definition: libxml.cpp:289
ParticleEngine * particleEngine
Particles::iterator ParticleIterator
ParticlePhysics ::T ParticlePhysicsT
const bool SkipError_false
Definition: skiperror.h:30
const bool UseVirtFs_true
Definition: usevirtfs.h:30