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-2017 The ManaPlus Developers
5  *
6  * This file is part of The ManaPlus Client.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "being/actorsprite.h"
23 
24 #include "configuration.h"
25 #include "settings.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 
64 static const unsigned int STATUS_EFFECTS = 32;
65 
69 bool ActorSprite::loaded = false;
70 
73  Actor(),
74  mStatusEffects(),
75  mStatusParticleEffects(),
76  mChildParticleEffects(&mStatusParticleEffects, false),
77  mHorseId(0),
78  mId(id),
79  mUsedTargetCursor(nullptr),
80  mActorSpriteListeners(),
81  mCursorPaddingX(0),
82  mCursorPaddingY(0),
83  mMustResetParticles(false),
84  mPoison(false),
85  mHaveCart(false),
86  mTrickDead(false)
87 {
88 }
89 
91 {
93  mMustResetParticles = true;
94 
95  mUsedTargetCursor = nullptr;
96 
97  if (localPlayer != nullptr &&
98  localPlayer != this &&
99  localPlayer->getTarget() == this)
100  {
101  localPlayer->setTarget(nullptr);
102  }
103 
104  // Notify listeners of the destruction.
106  {
107  if (reportFalse(*iter))
108  (*iter)->actorSpriteDestroyed(*this);
109  }
110 }
111 
113 {
114  BLOCK_START("ActorSprite::logic")
115  // Update sprite animations
117 
118  // Restart status/particle effects, if needed
120  {
121  mMustResetParticles = false;
122  FOR_EACH (std::set<int32_t>::const_iterator, it, mStatusEffects)
123  {
124  const StatusEffect *const effect
126  if (effect != nullptr &&
127  effect->mIsPersistent)
128  {
129  updateStatusEffect(*it,
130  Enable_true,
131  IsStart_false);
132  }
133  }
134  }
135 
136  // Update particle effects
138  BLOCK_END("ActorSprite::logic")
139 }
140 
141 void ActorSprite::setMap(Map *const map)
142 {
143  Actor::setMap(map);
144 
145  // Clear particle effect list because child particles became invalid
147  mMustResetParticles = true; // Reset status particles on next redraw
148 }
149 
151 {
152  if (particle != nullptr)
153  {
154  particle->setActor(mId);
156  }
157 }
158 
160 {
161  if (particle != nullptr)
162  {
163  // The effect may not die without the beings permission or we segfault
164  particle->disableAutoDelete();
166  }
167 }
168 
170 {
171  if (particle != nullptr)
173 }
174 
176 {
177  if (type == TargetCursorType::NONE)
178  {
179  untarget();
180  }
181  else
182  {
183  const size_t sz = CAST_SIZE(getTargetCursorSize());
185  if (mUsedTargetCursor != nullptr)
186  {
187  static const int targetWidths[CAST_SIZE(
189  = {0, 0, 0};
190  static const int targetHeights[CAST_SIZE(
192  = {-mapTileSize / 2, -mapTileSize / 2, -mapTileSize};
193 
194  mCursorPaddingX = CAST_S32(targetWidths[sz]);
195  mCursorPaddingY = CAST_S32(targetHeights[sz]);
196  }
197  }
198 }
199 
200 void ActorSprite::setStatusEffect(const int32_t index,
201  const Enable active,
202  const IsStart start)
203 {
204  const Enable wasActive = fromBool(
205  mStatusEffects.find(index) != mStatusEffects.end(), Enable);
206 
207  if (active != wasActive)
208  {
209  updateStatusEffect(index, active, start);
210  if (active == Enable_true)
211  {
212  mStatusEffects.insert(index);
213  }
214  else
215  {
216  mStatusEffects.erase(index);
217  }
218  }
219 }
220 
221 // function for legacy configs only. Must be removed in future
222 void ActorSprite::setStatusEffectBlock(const int offset,
223  const uint16_t newEffects)
224 {
225  for (unsigned i = 0; i < STATUS_EFFECTS; i++)
226  {
227  const bool val = (newEffects & (1 << i)) > 0;
228  const int32_t index = StatusEffectDB::blockIdToId(
229  offset + i); // block-id (offset + i) to id (index)
230 
231  if (index != -1)
232  {
233  setStatusEffect(index,
234  fromBool(val, Enable),
235  IsStart_true);
236  }
237  else if (val && config.getBoolValue("unimplimentedLog"))
238  {
239  const std::string str = strprintf(
240  "Error: unknown effect by block-index. "
241  "Offset: %d, effect int: %d, i: %u",
242  offset, CAST_S32(newEffects), i);
243  logger->log(str);
245  }
246  }
247 }
248 
249 static void applyEffectByOption(ActorSprite *const actor,
250  uint32_t option,
251  const char *const name,
252  const OptionsMap& options)
253 {
254  FOR_EACH (OptionsMapCIter, it, options)
255  {
256  const uint32_t opt = (*it).first;
257  const int32_t id = (*it).second;
258  const Enable enable = (opt & option) != 0 ? Enable_true : Enable_false;
259  option |= opt;
260  option ^= opt;
261  actor->setStatusEffect(id,
262  enable,
263  IsStart_false);
264  }
265  if (option != 0U &&
266  config.getBoolValue("unimplimentedLog"))
267  {
268  const std::string str = strprintf(
269  "Error: unknown effect by %s. "
270  "Left value: %u",
271  name,
272  option);
273  logger->log(str);
275  }
276 }
277 
278 static void applyEffectByOption1(ActorSprite *const actor,
279  uint32_t option,
280  const char *const name,
281  const OptionsMap& options)
282 {
283  FOR_EACH (OptionsMapCIter, it, options)
284  {
285  const uint32_t opt = (*it).first;
286  const int32_t id = (*it).second;
287  if (opt == option)
288  {
289  actor->setStatusEffect(id,
290  Enable_true,
291  IsStart_false);
292  option = 0U;
293  }
294  else
295  {
296  actor->setStatusEffect(id,
297  Enable_false,
298  IsStart_false);
299  }
300  }
301  if (option != 0 &&
302  config.getBoolValue("unimplimentedLog"))
303  {
304  const std::string str = strprintf(
305  "Error: unknown effect by %s. "
306  "Left value: %u",
307  name,
308  option);
309  logger->log(str);
311  }
312 }
313 
315  const uint32_t opt1,
316  const uint32_t opt2,
317  const uint32_t opt3)
318 {
319  if (settings.legacyEffects == false)
320  {
321  applyEffectByOption(this, option, "option",
323  applyEffectByOption1(this, opt1, "opt1",
325  applyEffectByOption(this, opt2, "opt2",
327  applyEffectByOption(this, opt3, "opt3",
329  }
330  else
331  {
332  uint32_t statusEffects = opt2;
333  statusEffects |= option << 16;
335  CAST_U16((statusEffects >> 16) & 0xffffU));
337  CAST_U16(statusEffects & 0xffffU));
339  CAST_U16(opt3));
340  }
341 }
342 
344  const uint32_t opt1,
345  const uint32_t opt2)
346 {
347  if (settings.legacyEffects == false)
348  {
349  applyEffectByOption(this, option, "option",
351  applyEffectByOption1(this, opt1, "opt1",
353  applyEffectByOption(this, opt2, "opt2",
355  }
356  else
357  {
358  uint32_t statusEffects = opt2;
359  statusEffects |= option << 16;
361  CAST_U16((statusEffects >> 16) & 0xffffU));
363  CAST_U16(statusEffects & 0xffffU));
364  }
365 }
366 
368 {
369  if (settings.legacyEffects == false)
370  {
371  applyEffectByOption(this, option, "option",
373  }
374  else
375  {
376  const uint32_t statusEffects = option << 16;
378  CAST_U16((statusEffects >> 16) & 0xffff));
379  }
380 }
381 
382 void ActorSprite::updateStatusEffect(const int32_t index,
383  const Enable newStatus,
384  const IsStart start)
385 {
387  index, newStatus);
388  if (effect == nullptr)
389  return;
390  if (effect->mIsPoison && getType() == ActorType::Player)
391  setPoison(newStatus == Enable_true);
392  else if (effect->mIsCart && localPlayer == this)
393  setHaveCart(newStatus == Enable_true);
394  else if (effect->mIsRiding)
395  setRiding(newStatus == Enable_true);
396  else if (effect->mIsTrickDead)
397  setTrickDead(newStatus == Enable_true);
398  else if (effect->mIsPostDelay)
399  stopCast(newStatus == Enable_true);
400  handleStatusEffect(effect, index, newStatus, start);
401 }
402 
404  const int32_t effectId,
405  const Enable newStatus,
406  const IsStart start)
407 {
408  if (effect == nullptr)
409  return;
410 
411  if (newStatus == Enable_true)
412  {
413  if (effectId >= 0)
414  {
415  Particle *particle = nullptr;
416  if (start == IsStart_true)
417  particle = effect->getStartParticle();
418  if (particle == nullptr)
419  particle = effect->getParticle();
420  if (particle != nullptr)
421  mStatusParticleEffects.setLocally(effectId, particle);
422  }
423  }
424  else
425  {
427  }
428 }
429 
431  const ForceDisplay forceDisplay,
432  const DisplayTypeT displayType,
433  const std::string &color)
434 {
435  clear();
436 
437  FOR_EACH (SpriteRefs, it, display.sprites)
438  {
439  if (*it == nullptr)
440  continue;
441  const std::string file = pathJoin(paths.getStringValue("sprites"),
442  combineDye3((*it)->sprite, color));
443 
444  const int variant = (*it)->variant;
445  addSprite(AnimatedSprite::delayedLoad(file, variant));
446  }
447 
448  // Ensure that something is shown, if desired
449  if (mSprites.empty() && forceDisplay == ForceDisplay_true)
450  {
451  if (display.image.empty())
452  {
454  paths.getStringValue("sprites"),
455  paths.getStringValue("spriteErrorFile"))));
456  }
457  else
458  {
459  std::string imagePath;
460  switch (displayType)
461  {
462  case DisplayType::Item:
463  default:
464  imagePath = pathJoin(paths.getStringValue("itemIcons"),
465  display.image);
466  break;
467  case DisplayType::Floor:
468  imagePath = pathJoin(paths.getStringValue("itemIcons"),
469  display.floor);
470  break;
471  }
472  imagePath = combineDye2(imagePath, color);
473  Image *img = Loader::getImage(imagePath);
474 
475  if (img == nullptr)
476  img = Theme::getImageFromTheme("unknown-item.png");
477 
478  addSprite(new ImageSprite(img));
479  if (img != nullptr)
480  img->decRef();
481  }
482  }
483 
485 
486  // setup particle effects
487  if (ParticleEngine::enabled && (particleEngine != nullptr))
488  {
489  FOR_EACH (StringVectCIter, itr, display.particles)
490  {
491  Particle *const p = particleEngine->addEffect(*itr, 0, 0);
493  }
494  }
495 
496  mMustResetParticles = true;
497 }
498 
500 {
501  if (loaded)
502  unload();
503 
505 
506  loaded = true;
507 }
508 
510 {
511  if (reportTrue(!loaded))
512  return;
513 
515  loaded = false;
516 }
517 
519 {
520  mActorSpriteListeners.push_front(listener);
521 }
522 
524  listener)
525 {
526  mActorSpriteListeners.remove(listener);
527 }
528 
529 static const char *cursorType(const TargetCursorTypeT type)
530 {
531  switch (type)
532  {
534  return "in-range";
535  default:
539  return "normal";
540  }
541 }
542 
543 static const char *cursorSize(const TargetCursorSizeT size)
544 {
545  switch (size)
546  {
548  return "l";
550  return "m";
551  default:
554  return "s";
555  }
556 }
557 
559 {
560  static const std::string targetCursorFile("target-cursor-%s-%s.xml");
561 
562  // Load target cursors
564  {
567  targetCursorFile.c_str(),
568  cursorType(static_cast<TargetCursorTypeT>(type)),
569  cursorSize(static_cast<TargetCursorSizeT>(size)))));
570  }
572 }
573 
575 {
577  {
578  delete2(targetCursor[type][size])
579  }
581 }
582 
584 {
585  std::string effectsStr;
586  if (!mStatusEffects.empty())
587  {
588  FOR_EACH (std::set<int32_t>::const_iterator, it, mStatusEffects)
589  {
590  const StatusEffect *const effect =
592  *it,
593  Enable_true);
594  if (effect == nullptr)
595  continue;
596  if (!effectsStr.empty())
597  effectsStr.append(", ");
598  effectsStr.append(effect->mName);
599  }
600  }
601  return effectsStr;
602 }
void addLocally(Particle *const particle)
#define FOR_EACH(type, iter, array)
Definition: foreach.h:24
const OptionsMap & getOpt2Map()
ParticleVector mStatusParticleEffects
Definition: actorsprite.h:252
static bool enabled
std::string getStringValue(const std::string &key) const
void controlParticleDeleted(const Particle *const particle)
static bool loaded
Definition: actorsprite.h:247
void addActorSpriteListener(ActorSpriteListener *const listener)
void setupSpriteDisplay(const SpriteDisplay &display, const ForceDisplay forceDisplay, const DisplayTypeT displayType, const std::string &color)
bool update(const int time)
std::vector< SpriteReference * > sprites
Definition: spritedisplay.h:46
StringVect::const_iterator StringVectCIter
Definition: stringvector.h:30
volatile int tick_time
Definition: timer.cpp:52
ParticleList mChildParticleEffects
Definition: actorsprite.h:253
const OptionsMap & getOpt1Map()
static AnimatedSprite * targetCursor[static_cast< size_t >(TargetCursorType::NUM_TCT)][static_cast< size_t >(TargetCursorSize::NUM_TC)]
Definition: actorsprite.h:245
std::string combineDye3(std::string file, const std::string &dye)
std::set< int32_t > mStatusEffects
Definition: actorsprite.h:250
void removeActorSpriteListener(ActorSpriteListener *const listener)
TargetCursorType ::T TargetCursorTypeT
std::string pathJoin(std::string str1, const std::string &str2)
ActorSpriteListeners::iterator ActorSpriteListenerIterator
Definition: actorsprite.h:261
void addSprite(Sprite *const sprite)
ActorSprite(const BeingId id)
Definition: actorsprite.cpp:71
#define BLOCK_START(name)
Definition: perfomance.h:78
std::string getStatusEffectsString() const
int mCursorPaddingX
Definition: actorsprite.h:264
Vector mPos
Definition: actor.h:139
ParticleEngine * particleEngine
Configuration config
int BeingId
Definition: beingid.h:29
virtual void updateStatusEffect(const int32_t index, const Enable newStatus, const IsStart start)
const OptionsMap & getOptionMap()
Particle * addEffect(const std::string &particleEffectFile, const int pixelX, const int pixelY, const int rotation=0)
int blockIdToId(const int blocKIndex)
static const char * cursorSize(const TargetCursorSizeT size)
#define reportFalse(val)
Definition: checkutils.h:250
#define BLOCK_END(name)
Definition: perfomance.h:79
void controlAutoParticle(Particle *const particle)
#define delete2(var)
Definition: delete2.h:24
std::string mName
Definition: statuseffect.h:82
#define end_foreach
Definition: actorsprite.cpp:62
const bool Enable_true
Definition: enable.h:29
virtual TargetCursorSizeT getTargetCursorSize() const
Definition: actorsprite.h:98
static AnimatedSprite * load(const std::string &filename, const int variant=0)
void setLocally(const int index, Particle *const particle)
bool mMustResetParticles
Definition: actorsprite.h:268
const bool ForceDisplay_true
Definition: forcedisplay.h:29
Logger * logger
Definition: logger.cpp:95
DisplayType ::T DisplayTypeT
Definition: displaytype.h:31
void controlCustomParticle(Particle *const particle)
Settings settings
Definition: settings.cpp:31
void setStatusEffectBlock(const int offset, const uint16_t newEffects)
virtual void setTrickDead(const bool b)
Definition: actorsprite.h:180
#define CAST_U16
Definition: cast.h:28
bool getBoolValue(const std::string &key) const
static AnimatedSprite * delayedLoad(const std::string &filename, const int variant=0)
#define CAST_S32
Definition: cast.h:29
virtual void stopCast(const bool b)
Definition: actorsprite.h:191
OptionsMap::const_iterator OptionsMapCIter
std::string strprintf(const char *const format,...)
Definition: stringutils.cpp:99
void setStatusEffectOpiton0(const uint32_t option)
Image * getImage(const std::string &idPath)
Definition: imageloader.cpp:85
#define fromBool(val, name)
Definition: booldefines.h:48
const bool IsStart_true
Definition: isstart.h:29
StringVect particles
Definition: spritedisplay.h:47
static const char * cursorType(const TargetCursorTypeT type)
AnimatedSprite * mUsedTargetCursor
Definition: actorsprite.h:258
void setTargetType(const TargetCursorTypeT type)
LocalPlayer * localPlayer
static Image * getImageFromTheme(const std::string &path)
Definition: theme.cpp:654
std::vector< Sprite * > mSprites
#define for_each_cursors()
Definition: actorsprite.cpp:53
#define nullptr
Definition: localconsts.h:44
static void load()
static const int MILLISECONDS_IN_A_TICK
Definition: timer.h:29
virtual void setMap(Map *const map)
Definition: actor.cpp:47
bool Enable
Definition: enable.h:29
void setHaveCart(const bool b)
Definition: actorsprite.h:171
void setPoison(const bool b)
Definition: actorsprite.h:165
static void applyEffectByOption1(ActorSprite *const actor, uint32_t option, const char *const name, const OptionsMap &options)
const bool IsStart_false
Definition: isstart.h:29
int mCursorPaddingY
Definition: actorsprite.h:265
bool mIsPersistent
Definition: statuseffect.h:83
Definition: map.h:68
std::string floor
Definition: spritedisplay.h:45
const bool Enable_false
Definition: enable.h:29
bool mIsTrickDead
Definition: statuseffect.h:87
virtual void setRiding(const bool b)
Definition: actorsprite.h:177
float x
Definition: vector.h:208
virtual void logic()
Configuration paths
void setActor(const BeingId actor)
Definition: particle.h:243
ActorSpriteListeners mActorSpriteListeners
Definition: actorsprite.h:262
bool mIsPostDelay
Definition: statuseffect.h:88
const OptionsMap & getOpt3Map()
static void cleanupTargetCursors()
void moveTo(const float x, const float y)
static void applyEffectByOption(ActorSprite *const actor, uint32_t option, const char *const name, const OptionsMap &options)
void setStatusEffectOpitons(const uint32_t option, const uint32_t opt1, const uint32_t opt2, const uint32_t opt3)
virtual ~ActorSprite()
Definition: actorsprite.cpp:90
bool legacyEffects
Definition: settings.h:159
void setTarget(Being *const target)
float y
Definition: vector.h:208
Definition: actor.h:40
Definition: image.h:61
StatusEffect * getStatusEffect(const int index, const Enable enabling)
static const int mapTileSize
Definition: map.h:26
#define reportTrue(val)
Definition: checkutils.h:251
std::string combineDye2(std::string file, const std::string &dye)
static const unsigned int STATUS_EFFECTS
Definition: actorsprite.cpp:64
#define CAST_SIZE
Definition: cast.h:33
static void distributeEvent(const std::string &msg)
void setMap(Map *const map)
void log(const char *const log_text,...)
Definition: logger.cpp:243
void removeLocally(const Particle *const particle)
void untarget()
Definition: actorsprite.h:115
void setStatusEffect(const int32_t index, const Enable active, const IsStart start)
Particle * getParticle() const
void disableAutoDelete()
Definition: particle.h:226
void decRef()
Definition: image.cpp:521
BeingId mId
Definition: actorsprite.h:255
static std::string resolveThemePath(const std::string &path)
Definition: theme.cpp:626
TargetCursorSize ::T TargetCursorSizeT
static void unload()
std::map< uint32_t, uint32_t > OptionsMap
static void initTargetCursor()
virtual void handleStatusEffect(const StatusEffect *const effect, const int32_t effectId, const Enable newStatus, const IsStart start)
std::string image
Definition: spritedisplay.h:44
Being * getTarget() const
virtual ActorTypeT getType() const
Definition: actorsprite.h:72
std::vector< SpriteReference * >::const_iterator SpriteRefs
Particle * getStartParticle() const
void delLocally(const int index)