ManaPlus
actorsprite.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2010 The Mana Developers
4  * Copyright (C) 2011-2019 The ManaPlus Developers
5  * Copyright (C) 2019-2021 Andrei Karas
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 "being/actorsprite.h"
24 
25 #include "configuration.h"
26 #include "statuseffect.h"
27 
28 #include "being/localplayer.h"
29 
30 #include "const/utils/timer.h"
31 
32 #include "gui/theme.h"
33 
35 
36 #include "particle/particle.h"
37 
39 
41 
45 
46 #include "utils/checkutils.h"
47 #include "utils/delete2.h"
48 #include "utils/foreach.h"
49 #include "utils/timer.h"
50 
51 #include "debug.h"
52 
53 #define for_each_cursors() \
54  for (int size = CAST_S32(TargetCursorSize::SMALL); \
55  size < CAST_S32(TargetCursorSize::NUM_TC); \
56  size ++) \
57  { \
58  for (int type = CAST_S32(TargetCursorType::NORMAL); \
59  type < CAST_S32(TargetCursorType::NUM_TCT); \
60  type ++) \
61 
62 #define end_foreach }
63 
67 bool ActorSprite::loaded = false;
68 
71  Actor(),
72  mStatusEffects(),
73  mStatusParticleEffects(nullptr, true),
74  mChildParticleEffects(&mStatusParticleEffects, false),
75  mHorseId(0),
76  mId(id),
77  mUsedTargetCursor(nullptr),
78  mActorSpriteListeners(),
79  mCursorPaddingX(0),
80  mCursorPaddingY(0),
81  mMustResetParticles(false),
82  mPoison(false),
83  mHaveCart(false),
84  mTrickDead(false)
85 {
86 }
87 
89 {
91  mMustResetParticles = true;
92 
93  mUsedTargetCursor = nullptr;
94 
95  if (localPlayer != nullptr &&
96  localPlayer != this &&
97  localPlayer->getTarget() == this)
98  {
99  localPlayer->setTarget(nullptr);
100  }
101 
102  // Notify listeners of the destruction.
104  {
105  if (reportFalse(*iter))
106  (*iter)->actorSpriteDestroyed(*this);
107  }
108 }
109 
111 {
112  BLOCK_START("ActorSprite::logic")
113  // Update sprite animations
115 
116  // Restart status/particle effects, if needed
118  {
119  mMustResetParticles = false;
120  FOR_EACH (std::set<int32_t>::const_iterator, it, mStatusEffects)
121  {
122  const StatusEffect *const effect
124  if (effect != nullptr &&
125  effect->mIsPersistent)
126  {
127  updateStatusEffect(*it,
128  Enable_true,
129  IsStart_false);
130  }
131  }
132  }
133 
134  // Update particle effects
136  BLOCK_END("ActorSprite::logic")
137 }
138 
139 void ActorSprite::setMap(Map *const map)
140 {
141  Actor::setMap(map);
142 
143  // Clear particle effect list because child particles became invalid
145  mMustResetParticles = true; // Reset status particles on next redraw
146 }
147 
149 {
150  if (particle != nullptr)
151  {
152  particle->setActor(mId);
154  }
155 }
156 
158 {
159  if (particle != nullptr)
160  {
161  // The effect may not die without the beings permission or we segfault
162  particle->disableAutoDelete();
164  }
165 }
166 
168 {
169  if (particle != nullptr)
171 }
172 
174 {
175  if (type == TargetCursorType::NONE)
176  {
177  untarget();
178  }
179  else
180  {
181  const size_t sz = CAST_SIZE(getTargetCursorSize());
183  if (mUsedTargetCursor != nullptr)
184  {
185  static const int targetWidths[CAST_SIZE(
187  = {0, 0, 0};
188  static const int targetHeights[CAST_SIZE(
190  = {-mapTileSize / 2, -mapTileSize / 2, -mapTileSize};
191 
192  mCursorPaddingX = CAST_S32(targetWidths[sz]);
193  mCursorPaddingY = CAST_S32(targetHeights[sz]);
194  }
195  }
196 }
197 
198 void ActorSprite::setStatusEffect(const int32_t index,
199  const Enable active,
200  const IsStart start)
201 {
202  const Enable wasActive = fromBool(
203  mStatusEffects.find(index) != mStatusEffects.end(), Enable);
204 
205  if (active != wasActive)
206  {
207  updateStatusEffect(index, active, start);
208  if (active == Enable_true)
209  {
210  mStatusEffects.insert(index);
211  }
212  else
213  {
214  mStatusEffects.erase(index);
215  }
216  }
217 }
218 
219 static void applyEffectByOption(ActorSprite *const actor,
220  uint32_t option,
221  const char *const name,
222  const OptionsMap& options)
223 {
224  FOR_EACH (OptionsMapCIter, it, options)
225  {
226  const uint32_t opt = (*it).first;
227  const int32_t id = (*it).second;
228  const Enable enable = (opt & option) != 0 ? Enable_true : Enable_false;
229  option |= opt;
230  option ^= opt;
231  actor->setStatusEffect(id,
232  enable,
233  IsStart_false);
234  }
235  if (option != 0U &&
236  config.getBoolValue("unimplimentedLog"))
237  {
238  const std::string str = strprintf(
239  "Error: unknown effect by %s. "
240  "Left value: %u",
241  name,
242  option);
243  logger->log(str);
245  }
246 }
247 
248 static void applyEffectByOption1(ActorSprite *const actor,
249  uint32_t option,
250  const char *const name,
251  const OptionsMap& options)
252 {
253  FOR_EACH (OptionsMapCIter, it, options)
254  {
255  const uint32_t opt = (*it).first;
256  const int32_t id = (*it).second;
257  if (opt == option)
258  {
259  actor->setStatusEffect(id,
260  Enable_true,
261  IsStart_false);
262  option = 0U;
263  }
264  else
265  {
266  actor->setStatusEffect(id,
267  Enable_false,
268  IsStart_false);
269  }
270  }
271  if (option != 0 &&
272  config.getBoolValue("unimplimentedLog"))
273  {
274  const std::string str = strprintf(
275  "Error: unknown effect by %s. "
276  "Left value: %u",
277  name,
278  option);
279  logger->log(str);
281  }
282 }
283 
284 void ActorSprite::setStatusEffectOpitons(const uint32_t option,
285  const uint32_t opt1,
286  const uint32_t opt2,
287  const uint32_t opt3)
288 {
289  applyEffectByOption(this, option, "option",
291  applyEffectByOption1(this, opt1, "opt1",
293  applyEffectByOption(this, opt2, "opt2",
295  applyEffectByOption(this, opt3, "opt3",
297 }
298 
299 void ActorSprite::setStatusEffectOpitons(const uint32_t option,
300  const uint32_t opt1,
301  const uint32_t opt2)
302 {
303  applyEffectByOption(this, option, "option",
305  applyEffectByOption1(this, opt1, "opt1",
307  applyEffectByOption(this, opt2, "opt2",
309 }
310 
311 void ActorSprite::setStatusEffectOpiton0(const uint32_t option)
312 {
313  applyEffectByOption(this, option, "option",
315 }
316 
317 void ActorSprite::updateStatusEffect(const int32_t index,
318  const Enable newStatus,
319  const IsStart start)
320 {
322  index, newStatus);
323  if (effect == nullptr)
324  return;
325  if (effect->mIsPoison && getType() == ActorType::Player)
326  setPoison(newStatus == Enable_true);
327  else if (effect->mIsCart && localPlayer == this)
328  setHaveCart(newStatus == Enable_true);
329  else if (effect->mIsRiding)
330  setRiding(newStatus == Enable_true);
331  else if (effect->mIsTrickDead)
332  setTrickDead(newStatus == Enable_true);
333  else if (effect->mIsPostDelay)
334  stopCast(newStatus == Enable_true);
335  handleStatusEffect(effect, index, newStatus, start);
336 }
337 
339  const int32_t effectId,
340  const Enable newStatus,
341  const IsStart start)
342 {
343  if (effect == nullptr)
344  return;
345 
346  if (newStatus == Enable_true)
347  {
348  if (effectId >= 0)
349  {
350  Particle *particle = nullptr;
351  if (start == IsStart_true)
352  particle = effect->getStartParticle();
353  if (particle == nullptr)
354  particle = effect->getParticle();
355  if (particle != nullptr)
356  mStatusParticleEffects.setLocally(effectId, particle);
357  }
358  }
359  else
360  {
362  }
363 }
364 
366  const ForceDisplay forceDisplay,
367  const DisplayTypeT displayType,
368  const std::string &color)
369 {
370  clear();
371 
372  FOR_EACH (SpriteRefs, it, display.sprites)
373  {
374  if (*it == nullptr)
375  continue;
376  const std::string file = pathJoin(paths.getStringValue("sprites"),
377  combineDye3((*it)->sprite, color));
378 
379  const int variant = (*it)->variant;
380  addSprite(AnimatedSprite::delayedLoad(file, variant));
381  }
382 
383  // Ensure that something is shown, if desired
384  if (mSprites.empty() && forceDisplay == ForceDisplay_true)
385  {
386  if (display.image.empty())
387  {
389  paths.getStringValue("sprites"),
390  paths.getStringValue("spriteErrorFile")),
391  0));
392  }
393  else
394  {
395  std::string imagePath;
396  switch (displayType)
397  {
398  case DisplayType::Item:
399  default:
400  imagePath = pathJoin(paths.getStringValue("itemIcons"),
401  display.image);
402  break;
403  case DisplayType::Floor:
404  imagePath = pathJoin(paths.getStringValue("itemIcons"),
405  display.floor);
406  break;
407  }
408  imagePath = combineDye2(imagePath, color);
409  Image *img = Loader::getImage(imagePath);
410 
411  if (img == nullptr)
412  img = Theme::getImageFromTheme("unknown-item.png");
413 
414  addSprite(new ImageSprite(img));
415  if (img != nullptr)
416  img->decRef();
417  }
418  }
419 
421 
422  // setup particle effects
423  if (ParticleEngine::enabled && (particleEngine != nullptr))
424  {
425  FOR_EACH (StringVectCIter, itr, display.particles)
426  {
427  Particle *const p = particleEngine->addEffect(*itr, 0, 0, 0);
429  }
430  }
431 
432  mMustResetParticles = true;
433 }
434 
436 {
437  if (loaded)
438  unload();
439 
441 
442  loaded = true;
443 }
444 
446 {
447  if (reportTrue(!loaded))
448  return;
449 
451  loaded = false;
452 }
453 
455 {
456  mActorSpriteListeners.push_front(listener);
457 }
458 
460  listener)
461 {
463 }
464 
465 static const char *cursorType(const TargetCursorTypeT type)
466 {
467  switch (type)
468  {
470  return "in-range";
471  default:
475  return "normal";
476  }
477 }
478 
479 static const char *cursorSize(const TargetCursorSizeT size)
480 {
481  switch (size)
482  {
484  return "l";
486  return "m";
487  default:
490  return "s";
491  }
492 }
493 
495 {
496  static const std::string targetCursorFile("target-cursor-%s-%s.xml");
497 
498  // Load target cursors
500  {
503  targetCursorFile.c_str(),
504  cursorType(static_cast<TargetCursorTypeT>(type)),
505  cursorSize(static_cast<TargetCursorSizeT>(size)))),
506  0);
507  }
509 }
510 
512 {
514  {
515  delete2(targetCursor[type][size])
516  }
518 }
519 
521 {
522  std::string effectsStr;
523  if (!mStatusEffects.empty())
524  {
525  FOR_EACH (std::set<int32_t>::const_iterator, it, mStatusEffects)
526  {
527  const StatusEffect *const effect =
529  *it,
530  Enable_true);
531  if (effect == nullptr)
532  continue;
533  if (!effectsStr.empty())
534  effectsStr.append(", ");
535  effectsStr.append(effect->mName);
536  }
537  }
538  return effectsStr;
539 }
static void applyEffectByOption1(ActorSprite *const actor, uint32_t option, const char *const name, const OptionsMap &options)
static void applyEffectByOption(ActorSprite *const actor, uint32_t option, const char *const name, const OptionsMap &options)
#define end_foreach
Definition: actorsprite.cpp:62
#define for_each_cursors()
Definition: actorsprite.cpp:53
static const char * cursorType(const TargetCursorTypeT type)
static const char * cursorSize(const TargetCursorSizeT size)
int BeingId
Definition: beingid.h:30
#define fromBool(val, name)
Definition: booldefines.h:49
#define CAST_S32
Definition: cast.h:30
#define CAST_SIZE
Definition: cast.h:34
#define reportTrue(val)
Definition: checkutils.h:252
#define reportFalse(val)
Definition: checkutils.h:251
virtual void logic()
AnimatedSprite * mUsedTargetCursor
Definition: actorsprite.h:250
int mCursorPaddingY
Definition: actorsprite.h:257
ParticleList mChildParticleEffects
Definition: actorsprite.h:245
void setStatusEffectOpitons(const uint32_t option, const uint32_t opt1, const uint32_t opt2, const uint32_t opt3)
ActorSpriteListeners mActorSpriteListeners
Definition: actorsprite.h:254
void setMap(Map *const map)
static void initTargetCursor()
void setupSpriteDisplay(const SpriteDisplay &display, const ForceDisplay forceDisplay, const DisplayTypeT displayType, const std::string &color)
void setPoison(const bool b)
Definition: actorsprite.h:166
virtual TargetCursorSizeT getTargetCursorSize() const
Definition: actorsprite.h:99
BeingId mId
Definition: actorsprite.h:247
ActorSprite(const BeingId id)
Definition: actorsprite.cpp:69
void setStatusEffectOpiton0(const uint32_t option)
virtual void setRiding(const bool b)
Definition: actorsprite.h:178
virtual ActorTypeT getType() const
Definition: actorsprite.h:73
ParticleVector mStatusParticleEffects
Definition: actorsprite.h:244
static void cleanupTargetCursors()
static AnimatedSprite * targetCursor[static_cast< size_t >(TargetCursorType::NUM_TCT)][static_cast< size_t >(TargetCursorSize::NUM_TC)]
Definition: actorsprite.h:237
virtual void stopCast(const bool b)
Definition: actorsprite.h:192
bool mMustResetParticles
Definition: actorsprite.h:260
ActorSpriteListeners::iterator ActorSpriteListenerIterator
Definition: actorsprite.h:253
virtual void setTrickDead(const bool b)
Definition: actorsprite.h:181
void untarget()
Definition: actorsprite.h:116
static bool loaded
Definition: actorsprite.h:239
void controlParticleDeleted(const Particle *const particle)
std::set< int32_t > mStatusEffects
Definition: actorsprite.h:242
static void load()
void setTargetType(const TargetCursorTypeT type)
void controlCustomParticle(Particle *const particle)
void controlAutoParticle(Particle *const particle)
static void unload()
int mCursorPaddingX
Definition: actorsprite.h:256
void setStatusEffect(const int32_t index, const Enable active, const IsStart start)
virtual void updateStatusEffect(const int32_t index, const Enable newStatus, const IsStart start)
void removeActorSpriteListener(ActorSpriteListener *const listener)
std::string getStatusEffectsString() const
void addActorSpriteListener(ActorSpriteListener *const listener)
virtual void handleStatusEffect(const StatusEffect *const effect, const int32_t effectId, const Enable newStatus, const IsStart start)
void setHaveCart(const bool b)
Definition: actorsprite.h:172
Definition: actor.h:42
virtual void setMap(Map *const map)
Definition: actor.cpp:48
Vector mPos
Definition: actor.h:140
static AnimatedSprite * delayedLoad(const std::string &filename, const int variant)
static AnimatedSprite * load(const std::string &filename, const int variant)
std::vector< Sprite * > mSprites
bool update(const int time)
void addSprite(Sprite *const sprite)
bool getBoolValue(const std::string &key) const
std::string getStringValue(const std::string &key) const
static void distributeEvent(const std::string &msg)
void setTarget(Being *const target)
Being * getTarget() const
void log(const char *const log_text,...)
Definition: logger.cpp:269
Definition: map.h:75
Particle * addEffect(const std::string &particleEffectFile, const int pixelX, const int pixelY, const int rotation)
static bool enabled
void addLocally(Particle *const particle)
void moveTo(const float x, const float y)
void removeLocally(const Particle *const particle)
void setLocally(const int index, Particle *const particle)
void delLocally(const int index)
void setActor(const BeingId actor)
Definition: particle.h:244
void disableAutoDelete()
Definition: particle.h:227
Particle * getParticle() const
bool mIsPostDelay
Definition: statuseffect.h:89
bool mIsTrickDead
Definition: statuseffect.h:88
std::string mName
Definition: statuseffect.h:83
bool mIsPersistent
Definition: statuseffect.h:84
Particle * getStartParticle() const
static std::string resolveThemePath(const std::string &path)
Definition: theme.cpp:627
static Image * getImageFromTheme(const std::string &path)
Definition: theme.cpp:655
float y
Definition: vector.h:209
float x
Definition: vector.h:209
Configuration config
Configuration paths
static const int mapTileSize
Definition: map.h:27
static const int MILLISECONDS_IN_A_TICK
Definition: timer.h:30
#define delete2(var)
Definition: delete2.h:25
DisplayType ::T DisplayTypeT
Definition: displaytype.h:32
bool Enable
Definition: enable.h:30
const bool Enable_true
Definition: enable.h:30
const bool Enable_false
Definition: enable.h:30
const bool ForceDisplay_true
Definition: forcedisplay.h:30
bool ForceDisplay
Definition: forcedisplay.h:30
#define FOR_EACH(type, iter, array)
Definition: foreach.h:25
const bool IsStart_true
Definition: isstart.h:30
const bool IsStart_false
Definition: isstart.h:30
bool IsStart
Definition: isstart.h:30
#define nullptr
Definition: localconsts.h:45
LocalPlayer * localPlayer
Logger * logger
Definition: logger.cpp:89
volatile int tick_time
Definition: timer.cpp:53
int size()
Definition: emotedb.cpp:306
Image * getImage(const std::string &idPath)
Definition: imageloader.cpp:86
const OptionsMap & getOpt3Map()
const OptionsMap & getOpt2Map()
StatusEffect * getStatusEffect(const int index, const Enable enabling)
const OptionsMap & getOpt1Map()
const OptionsMap & getOptionMap()
ParticleEngine * particleEngine
#define BLOCK_END(name)
Definition: perfomance.h:80
#define BLOCK_START(name)
Definition: perfomance.h:79
std::vector< SpriteReference * >::const_iterator SpriteRefs
OptionsMap::const_iterator OptionsMapCIter
std::map< uint32_t, uint32_t > OptionsMap
std::string combineDye3(std::string file, const std::string &dye)
std::string strprintf(const char *const format,...)
std::string combineDye2(std::string file, const std::string &dye)
std::string pathJoin(std::string str1, const std::string &str2)
StringVect::const_iterator StringVectCIter
Definition: stringvector.h:31
StringVect particles
Definition: spritedisplay.h:48
std::string image
Definition: spritedisplay.h:45
std::string floor
Definition: spritedisplay.h:46
std::vector< SpriteReference * > sprites
Definition: spritedisplay.h:47
TargetCursorSize ::T TargetCursorSizeT
TargetCursorType ::T TargetCursorTypeT