ManaPlus
being.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2004-2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2019 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 "being/being.h"
24 
25 #include "actormanager.h"
26 #include "beingequipbackend.h"
27 #include "configuration.h"
28 #include "effectmanager.h"
29 #include "guild.h"
30 #include "itemcolormanager.h"
31 #include "party.h"
32 #include "settings.h"
33 #include "soundmanager.h"
34 #include "text.h"
35 
36 #include "being/beingcacheentry.h"
37 #include "being/beingflag.h"
38 #include "being/beingspeech.h"
39 #include "being/castingeffect.h"
40 #include "being/localplayer.h"
41 #include "being/playerinfo.h"
42 #include "being/playerrelations.h"
43 #include "being/homunculusinfo.h"
44 #include "being/mercenaryinfo.h"
45 
46 #include "const/utils/timer.h"
47 
49 
51 
53 
54 #include "fs/files.h"
55 
56 #include "gui/gui.h"
57 #include "gui/userpalette.h"
58 
59 #include "gui/fonts/font.h"
60 
62 
63 #include "gui/windows/chatwindow.h"
67 
68 #include "net/charserverhandler.h"
69 #include "net/gamehandler.h"
70 #include "net/homunculushandler.h"
71 #include "net/mercenaryhandler.h"
72 #include "net/net.h"
73 #include "net/npchandler.h"
74 #include "net/packetlimiter.h"
75 #include "net/playerhandler.h"
76 #include "net/serverfeatures.h"
77 
78 #include "particle/particleinfo.h"
79 
80 #include "resources/attack.h"
81 #include "resources/chatobject.h"
82 #include "resources/emoteinfo.h"
83 #include "resources/emotesprite.h"
84 #include "resources/horseinfo.h"
85 #include "resources/iteminfo.h"
86 
87 #include "resources/db/avatardb.h"
88 #include "resources/db/badgesdb.h"
89 #include "resources/db/groupdb.h"
91 #include "resources/db/emotedb.h"
93 #include "resources/db/horsedb.h"
96 #include "resources/db/monsterdb.h"
97 #include "resources/db/npcdb.h"
98 #include "resources/db/petdb.h"
100 
101 #include "resources/image/image.h"
102 
103 #include "resources/item/item.h"
104 
105 #include "resources/map/map.h"
106 
109 
111 
113 
114 #include "utils/checkutils.h"
115 #include "utils/delete2.h"
116 #include "utils/foreach.h"
117 #include "utils/gettext.h"
118 #include "utils/likely.h"
119 #include "utils/stdmove.h"
120 #include "utils/timer.h"
121 
122 #include "debug.h"
123 
124 const unsigned int CACHE_SIZE = 50;
125 
126 time_t Being::mUpdateConfigTime = 0;
127 unsigned int Being::mConfLineLim = 0;
128 int Being::mSpeechType = 0;
129 bool Being::mHighlightMapPortals = false;
131 bool Being::mLowTraffic = true;
132 bool Being::mDrawHotKeys = true;
133 bool Being::mShowBattleEvents = false;
134 bool Being::mShowMobHP = false;
135 bool Being::mShowOwnHP = false;
136 bool Being::mShowGender = false;
137 bool Being::mShowLevel = false;
138 bool Being::mShowPlayersStatus = false;
139 bool Being::mEnableReorderSprites = true;
140 bool Being::mHideErased = false;
142 bool Being::mUseDiagonal = true;
144 int Being::mAwayEffect = -1;
146 
147 std::list<BeingCacheEntry*> beingInfoCache;
148 typedef std::map<int, Guild*>::const_iterator GuildsMapCIter;
149 typedef std::map<int, int>::const_iterator IntMapCIter;
150 
151 static const unsigned int SPEECH_TIME = 500;
152 static const unsigned int SPEECH_MIN_TIME = 200;
153 static const unsigned int SPEECH_MAX_TIME = 800;
154 
155 #define for_each_badges() \
156  for (int f = 0; f < BadgeIndex::BadgeIndexSize; f++)
157 
158 #define for_each_horses(name) \
159  FOR_EACH (STD_VECTOR<AnimatedSprite*>::const_iterator, it, name)
160 
162  const ActorTypeT type) :
163  ActorSprite(id),
164  mNextSound(),
165  mInfo(BeingInfo::unknown),
166  mEmotionSprite(nullptr),
167  mAnimationEffect(nullptr),
168  mCastingEffect(nullptr),
169  mBadges(),
170  mSpriteAction(SpriteAction::STAND),
171  mName(),
172  mExtName(),
173  mRaceName(),
174  mPartyName(),
175  mGuildName(),
176  mClanName(),
177  mSpeech(),
178  mDispName(nullptr),
179  mNameColor(nullptr),
180  mEquippedWeapon(nullptr),
181  mPath(),
182  mText(nullptr),
183  mTextColor(nullptr),
184  mDest(),
185  mSlots(),
186  mSpriteParticles(),
187  mGuilds(),
188  mParty(nullptr),
189  mActionTime(0),
190  mEmotionTime(0),
191  mSpeechTime(0),
192  mAttackSpeed(350),
193  mLevel(0),
194  mGroupId(0),
195  mAttackRange(1),
196  mLastAttackX(0),
197  mLastAttackY(0),
198  mPreStandTime(0),
199  mGender(Gender::UNSPECIFIED),
200  mAction(BeingAction::STAND),
201  mSubType(fromInt(0xFFFF, BeingTypeId)),
202  mDirection(BeingDirection::DOWN),
203  mDirectionDelayed(0),
204  mSpriteDirection(SpriteDirection::DOWN),
205  mShowName(false),
206  mIsGM(false),
207  mType(type),
208  mSpeechBubble(nullptr),
209  mWalkSpeed(playerHandler != nullptr ?
210  playerHandler->getDefaultWalkSpeed() : 1),
211  mSpeed(playerHandler != nullptr ?
212  playerHandler->getDefaultWalkSpeed() : 1),
213  mIp(),
214  mSpriteRemap(new int[20]),
215  mSpriteHide(new int[20]),
216  mSpriteDraw(new int[20]),
217  mComment(),
218  mBuyBoard(),
219  mSellBoard(),
220  mOwner(nullptr),
221  mSpecialParticle(nullptr),
222  mChat(nullptr),
223  mHorseInfo(nullptr),
224  mDownHorseSprites(),
225  mUpHorseSprites(),
226  mSpiritParticles(),
227  mX(0),
228  mY(0),
229  mCachedX(0),
230  mCachedY(0),
231  mSortOffsetY(0),
232  mPixelOffsetY(0),
233  mFixedOffsetY(0),
234  mOldHeight(0),
235  mDamageTaken(0),
236  mHP(0),
237  mMaxHP(0),
238  mDistance(0),
239  mReachable(Reachable::REACH_UNKNOWN),
240  mGoodStatus(-1),
241  mMoveTime(0),
242  mAttackTime(0),
243  mTalkTime(0),
244  mOtherTime(0),
245  mTestTime(cur_time),
246  mAttackDelay(0),
247  mMinHit(0),
248  mMaxHit(0),
249  mCriticalHit(0),
250  mPvpRank(0),
251  mNumber(100),
252  mSpiritBalls(0U),
253  mUsageCounter(1),
254  mKarma(0),
255  mManner(0),
256  mAreaSize(11),
257  mCastEndTime(0),
258  mLanguageId(-1),
259  mBadgesX(0),
260  mBadgesY(0),
261  mCreatorId(BeingId_zero),
262  mTeamId(0U),
263  mLook(0U),
264  mBadgesCount(0U),
265  mHairColor(ItemColor_zero),
266  mErased(false),
267  mEnemy(false),
268  mGotComment(false),
269  mAdvanced(false),
270  mShop(false),
271  mAway(false),
272  mInactive(false),
273  mNeedPosUpdate(true),
274  mBotAi(true),
275  mAllowNpcEquipment(false)
276 {
277  for (int f = 0; f < 20; f ++)
278  {
279  mSpriteRemap[f] = f;
280  mSpriteHide[f] = 0;
281  mSpriteDraw[f] = 0;
282  }
283 
285  mBadges[f] = nullptr;
286 }
287 
288 void Being::postInit(const BeingTypeId subtype,
289  Map *const map)
290 {
291  setMap(map);
292  setSubtype(subtype, 0);
293 
295 
296  switch (mType)
297  {
298  case ActorType::Player:
300  case ActorType::Pet:
303  showName1 = static_cast<VisibleName::Type>(
304  config.getIntValue("visiblenames"));
305  break;
306  case ActorType::Portal:
308  showName1 = VisibleName::Hide;
309  break;
310  default:
311  case ActorType::Unknown:
312  case ActorType::Npc:
313  case ActorType::Monster:
315  case ActorType::Avatar:
316  break;
317  }
318 
319  if (mType != ActorType::Npc)
320  mGotComment = true;
321 
322  config.addListener("visiblenames", this);
323 
324  reReadConfig();
325 
326  if (mType == ActorType::Npc ||
327  showName1 == VisibleName::Show)
328  {
329  setShowName(true);
330  }
331 
332  updateColors();
333  updatePercentHP();
334 }
335 
337 {
338  config.removeListener("visiblenames", this);
340 
341  delete [] mSpriteRemap;
342  mSpriteRemap = nullptr;
343  delete [] mSpriteHide;
344  mSpriteHide = nullptr;
345  delete [] mSpriteDraw;
346  mSpriteDraw = nullptr;
347 
349  delete2(mBadges[f])
350 
353  delete2(mText)
357  mBadgesCount = 0;
358  delete2(mChat)
359  removeHorse();
360 
362  mSpiritParticles.clear();
363 }
364 
366 {
368 }
369 
370 void Being::setSubtype(const BeingTypeId subtype,
371  const uint16_t look) restrict2
372 {
373  if (mInfo == nullptr)
374  return;
375 
376  if (subtype == mSubType && mLook == look)
377  return;
378 
379  mSubType = subtype;
380  mLook = look;
381 
382  switch (mType)
383  {
384  case ActorType::Monster:
385  mInfo = MonsterDB::get(mSubType);
386  if (mInfo != nullptr)
387  {
388  setName(mInfo->getName());
389  setupSpriteDisplay(mInfo->getDisplay(),
392  mInfo->getColor(fromInt(mLook, ItemColor)));
393  mYDiff = mInfo->getSortOffsetY();
394  }
395  break;
396  case ActorType::Pet:
397  mInfo = PETDB::get(mSubType);
398  if (mInfo != nullptr)
399  {
400  setName(mInfo->getName());
401  setupSpriteDisplay(mInfo->getDisplay(),
404  mInfo->getColor(fromInt(mLook, ItemColor)));
405  mYDiff = mInfo->getSortOffsetY();
406  }
407  break;
409  mInfo = MercenaryDB::get(mSubType);
410  if (mInfo != nullptr)
411  {
412  setName(mInfo->getName());
413  setupSpriteDisplay(mInfo->getDisplay(),
416  mInfo->getColor(fromInt(mLook, ItemColor)));
417  mYDiff = mInfo->getSortOffsetY();
418  }
419  break;
421  mInfo = HomunculusDB::get(mSubType);
422  if (mInfo != nullptr)
423  {
424  setName(mInfo->getName());
425  setupSpriteDisplay(mInfo->getDisplay(),
428  mInfo->getColor(fromInt(mLook, ItemColor)));
429  mYDiff = mInfo->getSortOffsetY();
430  }
431  break;
433  mInfo = SkillUnitDb::get(mSubType);
434  if (mInfo != nullptr)
435  {
436  setName(mInfo->getName());
437  setupSpriteDisplay(mInfo->getDisplay(),
440  mInfo->getColor(fromInt(mLook, ItemColor)));
441  mYDiff = mInfo->getSortOffsetY();
442  }
443  break;
445  mInfo = ElementalDb::get(mSubType);
446  if (mInfo != nullptr)
447  {
448  setName(mInfo->getName());
449  setupSpriteDisplay(mInfo->getDisplay(),
452  mInfo->getColor(fromInt(mLook, ItemColor)));
453  mYDiff = mInfo->getSortOffsetY();
454  }
455  break;
456  case ActorType::Npc:
457  mInfo = NPCDB::get(mSubType);
458  if (mInfo != nullptr)
459  {
460  setupSpriteDisplay(mInfo->getDisplay(),
463  std::string());
464  mYDiff = mInfo->getSortOffsetY();
465  mAllowNpcEquipment = mInfo->getAllowEquipment();
466  }
467  break;
468  case ActorType::Avatar:
469  mInfo = AvatarDB::get(mSubType);
470  if (mInfo != nullptr)
471  {
472  setupSpriteDisplay(mInfo->getDisplay(),
475  std::string());
476  }
477  break;
478  case ActorType::Player:
479  {
480  int id = -100 - toInt(subtype, int);
481  // Prevent showing errors when sprite doesn't exist
482  if (!ItemDB::exists(id))
483  {
484  id = -100;
485  // TRANSLATORS: default race name
486  setRaceName(_("Human"));
487  if (charServerHandler != nullptr)
488  {
489  setSpriteId(charServerHandler->baseSprite(),
490  id);
491  }
492  }
493  else
494  {
495  const ItemInfo &restrict info = ItemDB::get(id);
496  setRaceName(info.getName());
497  if (charServerHandler != nullptr)
498  {
499  setSpriteColor(charServerHandler->baseSprite(),
500  id,
501  info.getColor(fromInt(mLook, ItemColor)));
502  }
503  }
504  break;
505  }
506  case ActorType::Portal:
507  break;
508  case ActorType::Unknown:
510  default:
511  reportAlways("Wrong being type %d in setSubType",
512  CAST_S32(mType))
513  break;
514  }
515 }
516 
518 {
519  if (mInfo == nullptr)
521 
522  return mInfo->getTargetCursorSize();
523 }
524 
526 {
528 
529  updateCoords();
530 
531  if (mText != nullptr)
532  {
533  mText->adviseXY(CAST_S32(pos.x),
534  CAST_S32(pos.y) - getHeight() - mText->getHeight() - 9,
535  mMoveNames);
536  }
537 }
538 
539 void Being::setDestination(const int dstX,
540  const int dstY) restrict2
541 {
542  if (mMap == nullptr)
543  return;
544 
545  setPath(mMap->findPath(mX,
546  mY,
547  dstX,
548  dstY,
549  getBlockWalkMask(),
550  20));
551 }
552 
554 {
555  mPath.clear();
556 }
557 
559 {
560  mPath = path;
561  if (mPath.empty())
562  return;
563 
564  if (mAction != BeingAction::MOVE && mAction != BeingAction::DEAD)
565  {
566  nextTile();
567  mActionTime = tick_time;
568  }
569 }
570 
571 void Being::setSpeech(const std::string &restrict text) restrict2
572 {
573  if (userPalette == nullptr)
574  return;
575 
576  // Remove colors
577  mSpeech = removeColors(text);
578 
579  // Trim whitespace
580  trim(mSpeech);
581 
582  const unsigned int lineLim = mConfLineLim;
583  if (lineLim > 0 && mSpeech.length() > lineLim)
584  mSpeech = mSpeech.substr(0, lineLim);
585 
586  trim(mSpeech);
587  if (mSpeech.empty())
588  return;
589 
590  const size_t sz = mSpeech.size();
591  int time = 0;
592  if (sz < 200)
593  time = CAST_S32(SPEECH_TIME - 300 + (3 * sz));
594 
595  if (time < CAST_S32(SPEECH_MIN_TIME))
596  time = CAST_S32(SPEECH_MIN_TIME);
597 
598  // Check for links
599  size_t start = mSpeech.find('[');
600  size_t e = mSpeech.find(']', start);
601 
602  while (start != std::string::npos && e != std::string::npos)
603  {
604  // Catch multiple embeds and ignore them so it doesn't crash the client.
605  while ((mSpeech.find('[', start + 1) != std::string::npos) &&
606  (mSpeech.find('[', start + 1) < e))
607  {
608  start = mSpeech.find('[', start + 1);
609  }
610 
611  size_t position = mSpeech.find('|');
612  if (mSpeech[start + 1] == '@' && mSpeech[start + 2] == '@')
613  {
614  mSpeech.erase(e, 1);
615  mSpeech.erase(start, (position - start) + 1);
616  }
617  position = mSpeech.find("@@");
618 
619  while (position != std::string::npos)
620  {
621  mSpeech.erase(position, 2);
622  position = mSpeech.find('@');
623  }
624 
625  start = mSpeech.find('[', start + 1);
626  e = mSpeech.find(']', start);
627  }
628 
629  if (!mSpeech.empty())
630  {
631  mSpeechTime = time <= CAST_S32(SPEECH_MAX_TIME)
632  ? time : CAST_S32(SPEECH_MAX_TIME);
633  }
634 
635  const int speech = mSpeechType;
636  if (speech == BeingSpeech::TEXT_OVERHEAD)
637  {
638  delete mText;
639  mText = nullptr;
640  mText = new Text(mSpeech,
641  mPixelX,
642  mPixelY - getHeight(),
645  Speech_true,
646  nullptr);
647  mText->adviseXY(mPixelX,
648  (mY + 1) * mapTileSize - getHeight() - mText->getHeight() - 9,
649  mMoveNames);
650  }
651  else
652  {
653  if (mSpeechBubble == nullptr)
654  createSpeechBubble();
655  if (mSpeechBubble != nullptr)
656  {
657  const bool isShowName = (speech == BeingSpeech::NAME_IN_BUBBLE);
658  mSpeechBubble->setCaption(isShowName ? mName : "",
659  &theme->getColor(ThemeColorId::BUBBLE_NAME, 255),
660  &theme->getColor(ThemeColorId::BUBBLE_NAME_OUTLINE, 255));
661  mSpeechBubble->setText(mSpeech, isShowName);
662  }
663  }
664 }
665 
666 void Being::takeDamage(Being *restrict const attacker,
667  const int amount,
668  const AttackTypeT type,
669  const int attackId,
670  const int level) restrict2
671 {
672  if (userPalette == nullptr || attacker == nullptr)
673  return;
674 
675  BLOCK_START("Being::takeDamage1")
676 
677  Font *font = nullptr;
678  const Color *color;
679 
680  if (gui != nullptr)
681  font = gui->getInfoParticleFont();
682 
683  // Selecting the right color
684  if (type == AttackType::CRITICAL || type == AttackType::FLEE)
685  {
686  if (type == AttackType::CRITICAL)
687  attacker->setCriticalHit(amount);
688 
689  if (attacker == localPlayer)
690  {
691  color = &userPalette->getColor(
693  255U);
694  }
695  else
696  {
698  255U);
699  }
700  }
701  else if (amount == 0)
702  {
703  if (attacker == localPlayer)
704  {
705  // This is intended to be the wrong direction to visually
706  // differentiate between hits and misses
708  255U);
709  }
710  else
711  {
713  255U);
714  }
715  }
716  else if (mType == ActorType::Monster ||
717  mType == ActorType::Mercenary ||
718  mType == ActorType::Pet ||
719  mType == ActorType::Homunculus ||
720  mType == ActorType::SkillUnit)
721  {
722  if (attacker == localPlayer)
723  {
724  color = &userPalette->getColor(
726  255U);
727  }
728  else
729  {
730  color = &userPalette->getColor(
732  255U);
733  }
734  }
735  else if (mType == ActorType::Player &&
736  attacker != localPlayer &&
737  this == localPlayer)
738  {
739  // here player was attacked by other player. mark him as enemy.
741  255U);
742  attacker->setEnemy(true);
743  attacker->updateColors();
744  }
745  else
746  {
748  255U);
749  }
750 
751  if (chatWindow != nullptr && mShowBattleEvents)
752  {
753  if (this == localPlayer)
754  {
755  if (attacker->mType == ActorType::Player || (amount != 0))
756  {
757  ChatWindow::battleChatLog(strprintf("%s : Hit you -%d",
758  attacker->getName().c_str(), amount),
762  }
763  }
764  else if (attacker == localPlayer && (amount != 0))
765  {
766  ChatWindow::battleChatLog(strprintf("%s : You hit %s -%d",
767  attacker->mName.c_str(),
768  mName.c_str(),
769  amount),
773  }
774  }
775  if (font != nullptr && particleEngine != nullptr)
776  {
777  const std::string damage = amount != 0 ? toString(amount) :
778  // TRANSLATORS: dodge or miss message in attacks
779  type == AttackType::FLEE ? _("dodge") : _("miss");
780  // Show damage number
782  mPixelX,
783  mPixelY - 16,
784  color,
785  font,
786  true);
787  }
788  BLOCK_END("Being::takeDamage1")
789  BLOCK_START("Being::takeDamage2")
790 
791  if (type != AttackType::SKILL)
792  attacker->updateHit(amount);
793 
794  if (amount > 0)
795  {
796  if ((localPlayer != nullptr) && localPlayer == this)
797  localPlayer->setLastHitFrom(attacker->mName);
798 
799  mDamageTaken += amount;
800  if (mInfo != nullptr)
801  {
802  playSfx(mInfo->getSound(ItemSoundEvent::HURT),
803  this,
804  false,
805  mX,
806  mY);
807 
808  if (!mInfo->isStaticMaxHP())
809  {
810  if ((mHP == 0) && mInfo->getMaxHP() < mDamageTaken)
811  mInfo->setMaxHP(mDamageTaken);
812  }
813  }
814  if ((mHP != 0) && isAlive())
815  {
816  mHP -= amount;
817  if (mHP < 0)
818  mHP = 0;
819  }
820 
821  if (mType == ActorType::Monster)
822  {
823  updatePercentHP();
824  updateName();
825  }
826  else if (mType == ActorType::Player &&
827  (socialWindow != nullptr) &&
828  !mName.empty())
829  {
830  socialWindow->updateAvatar(mName);
831  }
832 
833  if (effectManager != nullptr)
834  {
835  const int hitEffectId = getHitEffect(attacker,
836  type,
837  attackId,
838  level);
839  if (hitEffectId >= 0)
840  effectManager->trigger(hitEffectId, this, 0);
841  }
842  }
843  else
844  {
845  if (effectManager != nullptr)
846  {
847  int hitEffectId = -1;
848  if (type == AttackType::SKILL)
849  {
850  hitEffectId = getHitEffect(attacker,
852  attackId,
853  level);
854  }
855  else
856  {
857  hitEffectId = getHitEffect(attacker,
859  attackId,
860  level);
861  }
862  if (hitEffectId >= 0)
863  effectManager->trigger(hitEffectId, this, 0);
864  }
865  }
866  BLOCK_END("Being::takeDamage2")
867 }
868 
869 int Being::getHitEffect(const Being *restrict const attacker,
870  const AttackTypeT type,
871  const int attackId,
872  const int level)
873 {
874  if (effectManager == nullptr)
875  return 0;
876 
877  BLOCK_START("Being::getHitEffect")
878  // Init the particle effect path based on current
879  // weapon or default.
880  int hitEffectId = 0;
881  if (type == AttackType::SKILL || type == AttackType::SKILLMISS)
882  {
883  const SkillData *restrict const data =
884  skillDialog->getSkillDataByLevel(attackId, level);
885  if (data == nullptr)
886  return -1;
887  if (type == AttackType::SKILL)
888  {
889  hitEffectId = data->hitEffectId;
890  if (hitEffectId == -1)
891  hitEffectId = paths.getIntValue("skillHitEffectId");
892  }
893  else
894  {
895  hitEffectId = data->missEffectId;
896  if (hitEffectId == -1)
897  hitEffectId = paths.getIntValue("skillMissEffectId");
898  }
899  }
900  else
901  {
902  if (attacker != nullptr)
903  {
904  const ItemInfo *restrict const attackerWeapon
905  = attacker->getEquippedWeapon();
906  if (attackerWeapon != nullptr &&
907  attacker->getType() == ActorType::Player)
908  {
909  if (type == AttackType::MISS)
910  hitEffectId = attackerWeapon->getMissEffectId();
911  else if (type != AttackType::CRITICAL)
912  hitEffectId = attackerWeapon->getHitEffectId();
913  else
914  hitEffectId = attackerWeapon->getCriticalHitEffectId();
915  }
916  else if (attacker->getType() == ActorType::Monster)
917  {
918  const BeingInfo *restrict const info = attacker->getInfo();
919  if (info != nullptr)
920  {
921  const Attack *restrict const atk =
922  info->getAttack(attackId);
923  if (atk != nullptr)
924  {
925  if (type == AttackType::MISS)
926  hitEffectId = atk->mMissEffectId;
927  else if (type != AttackType::CRITICAL)
928  hitEffectId = atk->mHitEffectId;
929  else
930  hitEffectId = atk->mCriticalHitEffectId;
931  }
932  else
933  {
934  hitEffectId = getDefaultEffectId(type);
935  }
936  }
937  }
938  else
939  {
940  hitEffectId = getDefaultEffectId(type);
941  }
942  }
943  else
944  {
945  hitEffectId = getDefaultEffectId(type);
946  }
947  }
948  BLOCK_END("Being::getHitEffect")
949  return hitEffectId;
950 }
951 
953 {
954  if (type == AttackType::MISS)
955  return paths.getIntValue("missEffectId");
956  else if (type != AttackType::CRITICAL)
957  return paths.getIntValue("hitEffectId");
958  else
959  return paths.getIntValue("criticalHitEffectId");
960 }
961 
962 void Being::handleAttack(Being *restrict const victim,
963  const int damage,
964  const int attackId) restrict2
965 {
966  if ((victim == nullptr) || (mInfo == nullptr))
967  return;
968 
969  BLOCK_START("Being::handleAttack")
970 
971  if (this != localPlayer)
972  setAction(BeingAction::ATTACK, attackId);
973 
974  mLastAttackX = victim->mX;
975  mLastAttackY = victim->mY;
976 
977  if (mType == ActorType::Player && (mEquippedWeapon != nullptr))
978  fireMissile(victim, mEquippedWeapon->getMissileConst());
979  else if (mInfo->getAttack(attackId) != nullptr)
980  fireMissile(victim, mInfo->getAttack(attackId)->mMissile);
981 
982  reset();
983  mActionTime = tick_time;
984 
986  this != localPlayer)
987  {
988  const uint8_t dir = calcDirection(victim->mX,
989  victim->mY);
990  if (dir != 0U)
991  setDirection(dir);
992  }
993 
994  if ((damage != 0) && victim->mType == ActorType::Player
995  && victim->mAction == BeingAction::SIT)
996  {
997  victim->setAction(BeingAction::STAND, 0);
998  }
999 
1000  if (mType == ActorType::Player)
1001  {
1002  if (mSlots.size() >= 10)
1003  {
1004  // here 10 is weapon slot
1005  int weaponId = mSlots[10].spriteId;
1006  if (weaponId == 0)
1007  weaponId = -100 - toInt(mSubType, int);
1008  const ItemInfo &info = ItemDB::get(weaponId);
1009  playSfx(info.getSound(
1010  (damage > 0) ? ItemSoundEvent::HIT : ItemSoundEvent::MISS),
1011  victim,
1012  true,
1013  mX, mY);
1014  }
1015  }
1016  else
1017  {
1018  playSfx(mInfo->getSound((damage > 0) ?
1019  ItemSoundEvent::HIT : ItemSoundEvent::MISS), victim, true, mX, mY);
1020  }
1021  BLOCK_END("Being::handleAttack")
1022 }
1023 
1025  const int skillId,
1026  const int skillLevel) restrict2
1027 {
1028  if ((victim == nullptr) || (mInfo == nullptr) || (skillDialog == nullptr))
1029  return;
1030 
1031  setAction(BeingAction::CAST, skillId);
1032 
1034  skillId,
1035  skillLevel);
1036 
1037  if (data != nullptr)
1038  {
1039  effectManager->triggerDefault(data->castingSrcEffectId,
1040  this,
1041  paths.getIntValue("skillCastingSrcEffectId"));
1042  effectManager->triggerDefault(data->castingDstEffectId,
1043  victim,
1044  paths.getIntValue("skillCastingDstEffectId"));
1045  fireMissile(victim, data->castingMissile);
1046  }
1047 }
1048 
1049 void Being::handleSkill(Being *restrict const victim,
1050  const int damage,
1051  const int skillId,
1052  const int skillLevel) restrict2
1053 {
1054  if ((victim == nullptr) || (mInfo == nullptr) || (skillDialog == nullptr))
1055  return;
1056 
1057  const SkillInfo *restrict const skill = skillDialog->getSkill(skillId);
1058  const SkillData *restrict const data = skill != nullptr
1059  ? skill->getData1(skillLevel) : nullptr;
1060  if (data != nullptr)
1061  {
1062  effectManager->triggerDefault(data->srcEffectId,
1063  this,
1064  paths.getIntValue("skillSrcEffectId"));
1065  effectManager->triggerDefault(data->dstEffectId,
1066  victim,
1067  paths.getIntValue("skillDstEffectId"));
1068  fireMissile(victim, data->missile);
1069  }
1070 
1071  if (this != localPlayer && (skill != nullptr))
1072  {
1073  const SkillType::SkillType type = skill->type;
1074  if ((type & SkillType::Attack) != 0 ||
1075  (type & SkillType::Ground) != 0)
1076  {
1077  setAction(BeingAction::ATTACK, 1);
1078  }
1079  else
1080  {
1081  setAction(BeingAction::STAND, 1);
1082  }
1083  }
1084 
1085  reset();
1086  mActionTime = tick_time;
1087 
1089  this != localPlayer)
1090  {
1091  const uint8_t dir = calcDirection(victim->mX,
1092  victim->mY);
1093  if (dir != 0U)
1094  setDirection(dir);
1095  }
1096  if ((damage != 0) && victim->mType == ActorType::Player
1097  && victim->mAction == BeingAction::SIT)
1098  {
1099  victim->setAction(BeingAction::STAND, 0);
1100  }
1101  if (data != nullptr)
1102  {
1103  if (damage > 0)
1104  playSfx(data->soundHit, victim, true, mX, mY);
1105  else
1106  playSfx(data->soundMiss, victim, true, mX, mY);
1107  }
1108  else
1109  {
1110  playSfx(mInfo->getSound((damage > 0) ?
1112  victim,
1113  true,
1114  mX, mY);
1115  }
1116 }
1117 
1118 void Being::showNameBadge(const bool show) restrict2
1119 {
1120  delete2(mBadges[BadgeIndex::Name])
1121  if (show &&
1122  !mName.empty() &&
1123  mShowBadges != BadgeDrawType::Hide)
1124  {
1125  const std::string badge = BadgesDB::getNameBadge(mName);
1126  if (!badge.empty())
1127  {
1129  paths.getStringValue("badges") + badge,
1130  0);
1131  }
1132  }
1133 }
1134 
1135 void Being::setName(const std::string &restrict name) restrict2
1136 {
1137  mExtName = name;
1138  if (mType == ActorType::Npc)
1139  {
1140  mName = name.substr(0, name.find('#', 0));
1141  showName();
1142  }
1143  else if (mType == ActorType::Player)
1144  {
1145  if (mName != name)
1146  {
1147  mName = name;
1148  showNameBadge(!mName.empty());
1149  }
1150  if (getShowName())
1151  showName();
1152  }
1153  else
1154  {
1155  if (mType == ActorType::Portal)
1156  mName = name.substr(0, name.find('#', 0));
1157  else
1158  mName = name;
1159 
1160  if (getShowName())
1161  showName();
1162  }
1163 }
1164 
1165 void Being::setShowName(const bool doShowName) restrict2
1166 {
1167  if (mShowName == doShowName)
1168  return;
1169 
1170  mShowName = doShowName;
1171 
1172  if (doShowName)
1173  showName();
1174  else
1175  delete2(mDispName)
1176 }
1177 
1178 void Being::showGuildBadge(const bool show) restrict2
1179 {
1180  delete2(mBadges[BadgeIndex::Guild])
1181  if (show &&
1182  !mGuildName.empty() &&
1183  mShowBadges != BadgeDrawType::Hide)
1184  {
1185  const std::string badge = BadgesDB::getGuildBadge(mGuildName);
1186  if (!badge.empty())
1187  {
1189  paths.getStringValue("badges") + badge,
1190  0);
1191  }
1192  }
1193 }
1194 
1195 void Being::setGuildName(const std::string &restrict name) restrict2
1196 {
1197  if (mGuildName != name)
1198  {
1199  mGuildName = name;
1200  showGuildBadge(!mGuildName.empty());
1201  updateBadgesCount();
1202  }
1203 }
1204 
1205 void Being::showClanBadge(const bool show) restrict2
1206 {
1207  delete2(mBadges[BadgeIndex::Clan])
1208  if (show &&
1209  !mClanName.empty() &&
1210  mShowBadges != BadgeDrawType::Hide)
1211  {
1212  const std::string badge = BadgesDB::getClanBadge(mClanName);
1213  if (!badge.empty())
1214  {
1216  paths.getStringValue("badges") + badge,
1217  0);
1218  }
1219  }
1220 }
1221 
1222 void Being::setClanName(const std::string &restrict name) restrict2
1223 {
1224  if (mClanName != name)
1225  {
1226  mClanName = name;
1227  showClanBadge(!mClanName.empty());
1228  updateBadgesCount();
1229  }
1230 }
1231 
1232 void Being::setGuildPos(const std::string &restrict pos A_UNUSED) restrict2
1233 {
1234 }
1235 
1237 {
1238  if (guild == nullptr)
1239  return;
1240 
1241  mGuilds[guild->getId()] = guild;
1242 
1243  if (this == localPlayer && (socialWindow != nullptr))
1245 }
1246 
1247 void Being::removeGuild(const int id) restrict2
1248 {
1249  if (this == localPlayer && (socialWindow != nullptr))
1251 
1252  if (mGuilds[id] != nullptr)
1253  mGuilds[id]->removeMember(mName);
1254  mGuilds.erase(id);
1255 }
1256 
1257 const Guild *Being::getGuild(const std::string &restrict guildName) const
1258  restrict2
1259 {
1261  {
1262  const Guild *restrict const guild = itr->second;
1263  if ((guild != nullptr) && guild->getName() == guildName)
1264  return guild;
1265  }
1266 
1267  return nullptr;
1268 }
1269 
1270 const Guild *Being::getGuild(const int id) const restrict2
1271 {
1272  const std::map<int, Guild*>::const_iterator itr = mGuilds.find(id);
1273  if (itr != mGuilds.end())
1274  return itr->second;
1275 
1276  return nullptr;
1277 }
1278 
1280 {
1281  const std::map<int, Guild*>::const_iterator itr = mGuilds.begin();
1282  if (itr != mGuilds.end())
1283  return itr->second;
1284 
1285  return nullptr;
1286 }
1287 
1289 {
1291  {
1292  Guild *const guild = itr->second;
1293 
1294  if (guild != nullptr)
1295  {
1296  if (this == localPlayer && (socialWindow != nullptr))
1298 
1299  guild->removeMember(mId);
1300  }
1301  }
1302 
1303  mGuilds.clear();
1304 }
1305 
1307 {
1308  if (party == mParty)
1309  return;
1310 
1311  Party *const old = mParty;
1312  mParty = party;
1313 
1314  if (old != nullptr)
1315  old->removeMember(mId);
1316 
1317  if (party != nullptr)
1318  party->addMember(mId, mName);
1319 
1320  updateColors();
1321 
1322  if (this == localPlayer && (socialWindow != nullptr))
1323  {
1324  if (old != nullptr)
1325  socialWindow->removeTab(old);
1326 
1327  if (party != nullptr)
1329  }
1330 }
1331 
1333 {
1334  if (localPlayer == nullptr)
1335  return;
1336 
1337  Guild *restrict const guild = localPlayer->getGuild();
1338  if (guild == nullptr)
1339  {
1340  clearGuilds();
1341  updateColors();
1342  return;
1343  }
1344  if (guild->getMember(mName) != nullptr)
1345  {
1346  setGuild(guild);
1347  if (!guild->getName().empty())
1348  setGuildName(guild->getName());
1349  }
1350  updateColors();
1351 }
1352 
1354 {
1355  Guild *restrict const old = getGuild();
1356  if (guild == old)
1357  return;
1358 
1359  clearGuilds();
1360  addGuild(guild);
1361 
1362  if (old != nullptr)
1363  old->removeMember(mName);
1364 
1365  updateColors();
1366 
1367  if (this == localPlayer && (socialWindow != nullptr))
1368  {
1369  if (old != nullptr)
1370  socialWindow->removeTab(old);
1371 
1372  if (guild != nullptr)
1374  }
1375 }
1376 
1377 void Being::fireMissile(Being *restrict const victim,
1378  const MissileInfo &restrict missile) const restrict2
1379 {
1380  BLOCK_START("Being::fireMissile")
1381 
1382  if (victim == nullptr ||
1383  missile.particle.empty() ||
1384  particleEngine == nullptr)
1385  {
1386  BLOCK_END("Being::fireMissile")
1387  return;
1388  }
1389 
1390  Particle *restrict const target = particleEngine->createChild();
1391 
1392  if (target == nullptr)
1393  {
1394  BLOCK_END("Being::fireMissile")
1395  return;
1396  }
1397 
1398  // +++ add z particle position?
1399  Particle *restrict const missileParticle = target->addEffect(
1400  missile.particle,
1401  mPixelX,
1402  mPixelY,
1403  0);
1404 
1405  target->moveBy(Vector(0.0F, 0.0F, missile.z));
1406  target->setLifetime(missile.lifeTime);
1407  victim->controlAutoParticle(target);
1408 
1409  if (missileParticle != nullptr)
1410  {
1411  missileParticle->setDestination(target, missile.speed, 0.0F);
1412  missileParticle->setDieDistance(missile.dieDistance);
1413  missileParticle->setLifetime(missile.lifeTime);
1414  }
1415  BLOCK_END("Being::fireMissile")
1416 }
1417 
1418 std::string Being::getSitAction() const restrict2
1419 {
1420  if (mHorseId != 0)
1421  return SpriteAction::SITRIDE;
1422  if (mMap != nullptr)
1423  {
1424  const unsigned char mask = mMap->getBlockMask(mX, mY);
1425  if ((mask & BlockMask::GROUNDTOP) != 0)
1426  return SpriteAction::SITTOP;
1427  else if ((mask & BlockMask::AIR) != 0)
1428  return SpriteAction::SITSKY;
1429  else if ((mask & BlockMask::WATER) != 0)
1430  return SpriteAction::SITWATER;
1431  }
1432  return SpriteAction::SIT;
1433 }
1434 
1435 
1436 std::string Being::getMoveAction() const restrict2
1437 {
1438  if (mHorseId != 0)
1439  return SpriteAction::RIDE;
1440  if (mMap != nullptr)
1441  {
1442  const unsigned char mask = mMap->getBlockMask(mX, mY);
1443  if ((mask & BlockMask::AIR) != 0)
1444  return SpriteAction::FLY;
1445  else if ((mask & BlockMask::WATER) != 0)
1446  return SpriteAction::SWIM;
1447  }
1448  return SpriteAction::MOVE;
1449 }
1450 
1451 std::string Being::getWeaponAttackAction(const ItemInfo *restrict const weapon)
1452  const restrict2
1453 {
1454  if (weapon == nullptr)
1455  return getAttackAction();
1456 
1457  if (mHorseId != 0)
1458  return weapon->getRideAttackAction();
1459  if (mMap != nullptr)
1460  {
1461  const unsigned char mask = mMap->getBlockMask(mX, mY);
1462  if ((mask & BlockMask::AIR) != 0)
1463  return weapon->getSkyAttackAction();
1464  else if ((mask & BlockMask::WATER) != 0)
1465  return weapon->getWaterAttackAction();
1466  }
1467  return weapon->getAttackAction();
1468 }
1469 
1470 std::string Being::getAttackAction(const Attack *restrict const attack1) const
1471  restrict2
1472 {
1473  if (attack1 == nullptr)
1474  return getAttackAction();
1475 
1476  if (mHorseId != 0)
1477  return attack1->mRideAction;
1478  if (mMap != nullptr)
1479  {
1480  const unsigned char mask = mMap->getBlockMask(mX, mY);
1481  if ((mask & BlockMask::AIR) != 0)
1482  return attack1->mSkyAction;
1483  else if ((mask & BlockMask::WATER) != 0)
1484  return attack1->mWaterAction;
1485  }
1486  return attack1->mAction;
1487 }
1488 
1489 std::string Being::getCastAction(const SkillInfo *restrict const skill) const
1490  restrict2
1491 {
1492  if (skill == nullptr)
1493  return getCastAction();
1494 
1495  if (mHorseId != 0)
1496  return skill->castingRideAction;
1497  if (mMap != nullptr)
1498  {
1499  const unsigned char mask = mMap->getBlockMask(mX, mY);
1500  if ((mask & BlockMask::AIR) != 0)
1501  return skill->castingSkyAction;
1502  else if ((mask & BlockMask::WATER) != 0)
1503  return skill->castingWaterAction;
1504  }
1505  return skill->castingAction;
1506 }
1507 
1508 #define getSpriteAction(func, action) \
1509  std::string Being::get##func##Action() const restrict2\
1510 { \
1511  if (mHorseId != 0) \
1512  return SpriteAction::action##RIDE; \
1513  if (mMap) \
1514  { \
1515  const unsigned char mask = mMap->getBlockMask(mX, mY); \
1516  if (mask & BlockMask::AIR) \
1517  return SpriteAction::action##SKY; \
1518  else if (mask & BlockMask::WATER) \
1519  return SpriteAction::action##WATER; \
1520  } \
1521  return SpriteAction::action; \
1522 }
1523 
1528 
1529 std::string Being::getStandAction() const restrict2
1530 {
1531  if (mHorseId != 0)
1532  return SpriteAction::STANDRIDE;
1533  if (mMap != nullptr)
1534  {
1535  const unsigned char mask = mMap->getBlockMask(mX, mY);
1536  if (mTrickDead)
1537  {
1538  if ((mask & BlockMask::AIR) != 0)
1539  return SpriteAction::DEADSKY;
1540  else if ((mask & BlockMask::WATER) != 0)
1541  return SpriteAction::DEADWATER;
1542  else
1543  return SpriteAction::DEAD;
1544  }
1545  if ((mask & BlockMask::AIR) != 0)
1546  return SpriteAction::STANDSKY;
1547  else if ((mask & BlockMask::WATER) != 0)
1548  return SpriteAction::STANDWATER;
1549  }
1550  return SpriteAction::STAND;
1551 }
1552 
1554  const int attackId) restrict2
1555 {
1556  std::string currentAction = SpriteAction::INVALID;
1557 
1558  switch (action)
1559  {
1560  case BeingAction::MOVE:
1561  if (mInfo != nullptr)
1562  {
1563  playSfx(mInfo->getSound(
1564  ItemSoundEvent::MOVE), nullptr, true, mX, mY);
1565  }
1566  currentAction = getMoveAction();
1567  // Note: When adding a run action,
1568  // Differentiate walk and run with action name,
1569  // while using only the ACTION_MOVE.
1570  break;
1571  case BeingAction::SIT:
1572  currentAction = getSitAction();
1573  if (mInfo != nullptr)
1574  {
1575  ItemSoundEvent::Type event;
1576  if (currentAction == SpriteAction::SITTOP)
1577  event = ItemSoundEvent::SITTOP;
1578  else
1579  event = ItemSoundEvent::SIT;
1580  playSfx(mInfo->getSound(event), nullptr, true, mX, mY);
1581  }
1582  break;
1583  case BeingAction::ATTACK:
1584  if (mEquippedWeapon != nullptr)
1585  {
1586  currentAction = getWeaponAttackAction(mEquippedWeapon);
1587  reset();
1588  }
1589  else
1590  {
1591  if (mInfo == nullptr || mInfo->getAttack(attackId) == nullptr)
1592  break;
1593 
1594  currentAction = getAttackAction(mInfo->getAttack(attackId));
1595  reset();
1596 
1597  // attack particle effect
1598  if (ParticleEngine::enabled && (effectManager != nullptr))
1599  {
1600  const int effectId = mInfo->getAttack(attackId)->mEffectId;
1601  if (effectId >= 0)
1602  {
1603  effectManager->triggerDirection(effectId,
1604  this,
1605  mSpriteDirection);
1606  }
1607  }
1608  }
1609  break;
1610  case BeingAction::CAST:
1611  if (skillDialog != nullptr)
1612  {
1613  const SkillInfo *restrict const info =
1614  skillDialog->getSkill(attackId);
1615  currentAction = getCastAction(info);
1616  }
1617  break;
1618  case BeingAction::HURT:
1619  if (mInfo != nullptr)
1620  {
1621  playSfx(mInfo->getSound(ItemSoundEvent::HURT),
1622  this, false, mX, mY);
1623  }
1624  break;
1625  case BeingAction::DEAD:
1626  currentAction = getDeadAction();
1627  if (mInfo != nullptr)
1628  {
1629  playSfx(mInfo->getSound(ItemSoundEvent::DIE),
1630  this,
1631  false,
1632  mX, mY);
1633  if (mType == ActorType::Monster ||
1634  mType == ActorType::Npc ||
1635  mType == ActorType::SkillUnit)
1636  {
1637  mYDiff = mInfo->getDeadSortOffsetY();
1638  }
1639  }
1640  break;
1641  case BeingAction::STAND:
1642  currentAction = getStandAction();
1643  break;
1644  case BeingAction::SPAWN:
1645  if (mInfo != nullptr)
1646  {
1647  playSfx(mInfo->getSound(ItemSoundEvent::SPAWN),
1648  nullptr, true, mX, mY);
1649  }
1650  currentAction = getSpawnAction();
1651  break;
1652  case BeingAction::PRESTAND:
1653  break;
1654  default:
1655  logger->log("Being::setAction unknown action: "
1656  + toString(CAST_U32(action)));
1657  break;
1658  }
1659 
1660  if (currentAction != SpriteAction::INVALID)
1661  {
1662  mSpriteAction = currentAction;
1663  play(currentAction);
1664  if (mEmotionSprite != nullptr)
1665  mEmotionSprite->play(currentAction);
1666  if (mAnimationEffect != nullptr)
1667  mAnimationEffect->play(currentAction);
1668  for_each_badges()
1669  {
1670  AnimatedSprite *const sprite = mBadges[f];
1671  if (sprite != nullptr)
1672  sprite->play(currentAction);
1673  }
1674  for_each_horses(mDownHorseSprites)
1675  (*it)->play(currentAction);
1676  for_each_horses(mUpHorseSprites)
1677  (*it)->play(currentAction);
1678  mAction = action;
1679  }
1680 
1681  if (currentAction != SpriteAction::MOVE
1682  && currentAction != SpriteAction::FLY
1683  && currentAction != SpriteAction::SWIM)
1684  {
1685  mActionTime = tick_time;
1686  }
1687 }
1688 
1689 void Being::setDirection(const uint8_t direction) restrict2
1690 {
1691  if (mDirection == direction)
1692  return;
1693 
1694  mDirection = direction;
1695 
1696  mDirectionDelayed = 0;
1697 
1698  // if the direction does not change much, keep the common component
1699  int mFaceDirection = mDirection & direction;
1700  if (mFaceDirection == 0)
1701  mFaceDirection = direction;
1702 
1704  if ((mFaceDirection & BeingDirection::UP) != 0)
1705  {
1706  if ((mFaceDirection & BeingDirection::LEFT) != 0)
1708  else if ((mFaceDirection & BeingDirection::RIGHT) != 0)
1710  else
1711  dir = SpriteDirection::UP;
1712  }
1713  else if ((mFaceDirection & BeingDirection::DOWN) != 0)
1714  {
1715  if ((mFaceDirection & BeingDirection::LEFT) != 0)
1717  else if ((mFaceDirection & BeingDirection::RIGHT) != 0)
1719  else
1720  dir = SpriteDirection::DOWN;
1721  }
1722  else if ((mFaceDirection & BeingDirection::RIGHT) != 0)
1723  {
1724  dir = SpriteDirection::RIGHT;
1725  }
1726  else
1727  {
1728  dir = SpriteDirection::LEFT;
1729  }
1730  mSpriteDirection = dir;
1731 
1733  if (mEmotionSprite != nullptr)
1734  mEmotionSprite->setSpriteDirection(dir);
1735  if (mAnimationEffect != nullptr)
1736  mAnimationEffect->setSpriteDirection(dir);
1737 
1738  for_each_badges()
1739  {
1740  AnimatedSprite *const sprite = mBadges[f];
1741  if (sprite != nullptr)
1742  sprite->setSpriteDirection(dir);
1743  }
1744 
1745  for_each_horses(mDownHorseSprites)
1746  (*it)->setSpriteDirection(dir);
1747  for_each_horses(mUpHorseSprites)
1748  (*it)->setSpriteDirection(dir);
1749  recalcSpritesOrder();
1750 }
1751 
1753 {
1754  uint8_t dir = 0;
1755  if (mDest.x > mX)
1756  dir |= BeingDirection::RIGHT;
1757  else if (mDest.x < mX)
1758  dir |= BeingDirection::LEFT;
1759  if (mDest.y > mY)
1760  dir |= BeingDirection::DOWN;
1761  else if (mDest.y < mY)
1762  dir |= BeingDirection::UP;
1763  return dir;
1764 }
1765 
1766 uint8_t Being::calcDirection(const int dstX,
1767  const int dstY) const restrict2
1768 {
1769  uint8_t dir = 0;
1770  if (dstX > mX)
1771  dir |= BeingDirection::RIGHT;
1772  else if (dstX < mX)
1773  dir |= BeingDirection::LEFT;
1774  if (dstY > mY)
1775  dir |= BeingDirection::DOWN;
1776  else if (dstY < mY)
1777  dir |= BeingDirection::UP;
1778  return dir;
1779 }
1780 
1782 {
1783  if (mPath.empty())
1784  {
1787  return;
1788  }
1789 
1790  const Position pos = mPath.front();
1791  mPath.pop_front();
1792 
1793  const uint8_t dir = calcDirection(pos.x, pos.y);
1794  if (dir != 0U)
1795  setDirection(dir);
1796 
1797  if (mMap == nullptr ||
1798  !mMap->getWalk(pos.x, pos.y, getBlockWalkMask()))
1799  {
1801  return;
1802  }
1803 
1804  mActionTime += mSpeed / 10;
1806  && mX != pos.x && mY != pos.y)
1807  {
1808  mSpeed = mWalkSpeed * 14 / 10;
1809  }
1810  else
1811  {
1812  mSpeed = mWalkSpeed;
1813  }
1814 
1815  if (mX != pos.x || mY != pos.y)
1816  {
1819  mMap->getBlockMask(mX, mY) != mMap->getBlockMask(pos.x, pos.y))
1820  {
1822  }
1823  }
1824  mX = pos.x;
1825  mY = pos.y;
1826  const uint8_t height = mMap->getHeightOffset(mX, mY);
1827  mPixelOffsetY = height - mOldHeight;
1828  mFixedOffsetY = height;
1829  mNeedPosUpdate = true;
1831 }
1832 
1834 {
1835  BLOCK_START("Being::logic")
1836  if (A_UNLIKELY(mSpeechTime != 0))
1837  {
1838  mSpeechTime--;
1839  if (mSpeechTime == 0 && mText != nullptr)
1840  delete2(mText)
1841  }
1842 
1843  if (A_UNLIKELY(mOwner != nullptr))
1844  {
1845  if (mType == ActorType::Homunculus ||
1847  {
1848  botLogic();
1849  }
1850  }
1851 
1852  const int time = tick_time * MILLISECONDS_IN_A_TICK;
1853  if (mEmotionSprite != nullptr)
1854  mEmotionSprite->update(time);
1856  (*it)->update(time);
1858  (*it)->update(time);
1859 
1861  {
1862  mCastEndTime = 0;
1864  }
1865 
1867  {
1868  mAnimationEffect->update(time);
1871  }
1873  {
1874  mCastingEffect->update(time);
1877  }
1878  for_each_badges()
1879  {
1880  AnimatedSprite *restrict const sprite = mBadges[f];
1881  if (sprite != nullptr)
1882  sprite->update(time);
1883  }
1884 
1885  int frameCount = CAST_S32(getFrameCount());
1886 
1887  switch (mAction)
1888  {
1889  case BeingAction::STAND:
1890  case BeingAction::SIT:
1891  case BeingAction::DEAD:
1892  case BeingAction::HURT:
1893  case BeingAction::SPAWN:
1894  case BeingAction::CAST:
1895  default:
1896  break;
1897 
1898  case BeingAction::MOVE:
1899  {
1901  nextTile();
1902  break;
1903  }
1904 
1905  case BeingAction::ATTACK:
1906  {
1907  if (mActionTime == 0)
1908  break;
1909 
1910  int curFrame = 0;
1911  if (mAttackSpeed != 0)
1912  {
1913  curFrame = (get_elapsed_time(mActionTime) * frameCount)
1914  / mAttackSpeed;
1915  }
1916 
1917  if (this == localPlayer && curFrame >= frameCount)
1918  nextTile();
1919 
1920  break;
1921  }
1922 
1923  case BeingAction::PRESTAND:
1924  {
1925  if (get_elapsed_time1(mPreStandTime) > 10)
1927  break;
1928  }
1929  }
1930 
1932  {
1933  const int xOffset = getOffset<BeingDirection::LEFT,
1935  const int yOffset = getOffset<BeingDirection::UP,
1937  int offset = xOffset;
1938  if (offset == 0)
1939  offset = yOffset;
1940 
1941  if ((xOffset == 0) && (yOffset == 0))
1942  mNeedPosUpdate = false;
1943 
1944  const int halfTile = mapTileSize / 2;
1945  const float offset2 = static_cast<float>(
1946  mPixelOffsetY * abs(offset)) / 2;
1947 // mSortOffsetY = (mOldHeight - mFixedOffsetY + mPixelOffsetY)
1948 // * halfTile - offset2;
1949  mSortOffsetY = 0;
1950  const float yOffset3 = (mY + 1) * mapTileSize + yOffset
1951  - (mOldHeight + mPixelOffsetY) * halfTile + offset2;
1952 
1953  // Update pixel coordinates
1954  setPixelPositionF(static_cast<float>(mX * mapTileSize
1955  + mapTileSize / 2 + xOffset),
1956  yOffset3,
1957  0.0F);
1958  }
1959 
1961  {
1962  mEmotionTime--;
1963  if (mEmotionTime == 0)
1965  }
1966 
1968 
1969  if (frameCount < 10)
1970  frameCount = 10;
1971 
1972  if (A_UNLIKELY(!isAlive() &&
1973  mSpeed != 0 &&
1975  get_elapsed_time(mActionTime) / mSpeed >= frameCount))
1976  {
1977  if (mType != ActorType::Player && (actorManager != nullptr))
1978  actorManager->destroy(this);
1979  }
1980 
1981  const SoundInfo *restrict const sound = mNextSound.sound;
1982  if (A_UNLIKELY(sound))
1983  {
1984  const int time2 = tick_time;
1985  if (time2 > mNextSound.time)
1986  {
1987  soundManager.playSfx(sound->sound,
1988  mNextSound.x,
1989  mNextSound.y);
1990  mNextSound.sound = nullptr;
1991  mNextSound.time = time2 + sound->delay;
1992  }
1993  }
1994 
1995  BLOCK_END("Being::logic")
1996 }
1997 
1999 {
2000  if ((mOwner == nullptr) || (mMap == nullptr) || (mInfo == nullptr))
2001  return;
2002 
2003  const int time = tick_time;
2004  const int thinkTime = mInfo->getThinkTime();
2005  if (abs(CAST_S32(mMoveTime) - time) < thinkTime)
2006  return;
2007 
2008  mMoveTime = time;
2009 
2010  int dstX = mOwner->mX;
2011  int dstY = mOwner->mY;
2012  const int warpDist = mInfo->getWarpDist();
2013  const int divX = abs(dstX - mX);
2014  const int divY = abs(dstY - mY);
2015 
2016  if (divX >= warpDist || divY >= warpDist)
2017  {
2020  else
2022  mBotAi = true;
2023  return;
2024  }
2025  if (!mBotAi)
2026  return;
2027  if (mAction == BeingAction::MOVE)
2028  {
2030  {
2031  updateBotFollow(dstX, dstY,
2032  divX, divY);
2033  }
2034  return;
2035  }
2036 
2037  switch (mOwner->mAction)
2038  {
2039  case BeingAction::MOVE:
2040  case BeingAction::PRESTAND:
2041  updateBotFollow(dstX, dstY,
2042  divX, divY);
2043  break;
2044  case BeingAction::STAND:
2045  case BeingAction::SPAWN:
2046  botFixOffset(dstX, dstY);
2047  moveBotTo(dstX, dstY);
2048  break;
2049  case BeingAction::ATTACK:
2050  {
2051  const Being *const target = localPlayer->getTarget();
2052  if (target == nullptr)
2053  return;
2054  const BeingId targetId = target->getId();
2056  {
2057  homunculusHandler->attack(targetId,
2058  Keep_true);
2059  }
2060  else
2061  {
2062  mercenaryHandler->attack(targetId,
2063  Keep_true);
2064  }
2065  break;
2066  }
2067  case BeingAction::SIT:
2068  case BeingAction::DEAD:
2069  botFixOffset(dstX, dstY);
2070  moveBotTo(dstX, dstY);
2071  break;
2072  case BeingAction::CAST:
2073  case BeingAction::HURT:
2074  default:
2075  break;
2076  }
2077 }
2078 
2080  int &restrict dstY) const
2081 {
2082  if ((mInfo == nullptr) || (mOwner == nullptr))
2083  return;
2084 
2085  int offsetX1;
2086  int offsetY1;
2087  switch (mOwner->getCurrentAction())
2088  {
2089  case BeingAction::SIT:
2090  offsetX1 = mInfo->getSitOffsetX();
2091  offsetY1 = mInfo->getSitOffsetY();
2092  break;
2093 
2094  case BeingAction::MOVE:
2095  offsetX1 = mInfo->getMoveOffsetX();
2096  offsetY1 = mInfo->getMoveOffsetY();
2097  break;
2098 
2099  case BeingAction::DEAD:
2100  offsetX1 = mInfo->getDeadOffsetX();
2101  offsetY1 = mInfo->getDeadOffsetY();
2102  break;
2103 
2104  case BeingAction::ATTACK:
2105  offsetX1 = mInfo->getAttackOffsetX();
2106  offsetY1 = mInfo->getAttackOffsetY();
2107  break;
2108 
2109  case BeingAction::SPAWN:
2110  case BeingAction::HURT:
2111  case BeingAction::STAND:
2112  case BeingAction::PRESTAND:
2113  case BeingAction::CAST:
2114  default:
2115  offsetX1 = mInfo->getTargetOffsetX();
2116  offsetY1 = mInfo->getTargetOffsetY();
2117  break;
2118  }
2119 
2120  int offsetX = offsetX1;
2121  int offsetY = offsetY1;
2122  switch (mOwner->mDirection)
2123  {
2124  case BeingDirection::LEFT:
2125  offsetX = -offsetY1;
2126  offsetY = offsetX1;
2127  break;
2128  case BeingDirection::RIGHT:
2129  offsetX = offsetY1;
2130  offsetY = -offsetX1;
2131  break;
2132  case BeingDirection::UP:
2133  offsetY = -offsetY;
2134  offsetX = -offsetX;
2135  break;
2136  default:
2137  case BeingDirection::DOWN:
2138  break;
2139  }
2140  dstX += offsetX;
2141  dstY += offsetY;
2142  if (mMap != nullptr)
2143  {
2144  if (!mMap->getWalk(dstX, dstY, getBlockWalkMask()))
2145  {
2146  dstX = mOwner->mX;
2147  dstY = mOwner->mY;
2148  }
2149  }
2150 }
2151 
2153  int dstY,
2154  const int divX,
2155  const int divY)
2156 {
2157  const int followDist = mInfo->getStartFollowDist();
2158  const int dist = mInfo->getFollowDist();
2159  if (divX > followDist || divY > followDist)
2160  {
2161  if (dist > 0)
2162  {
2163  if (divX > followDist)
2164  {
2165  if (dstX > mX + dist)
2166  dstX -= dist;
2167  else if (dstX + dist <= mX)
2168  dstX += dist;
2169  }
2170  else
2171  {
2172  dstX = mX;
2173  }
2174  if (divY > followDist)
2175  {
2176  if (dstY > mY + dist)
2177  dstY -= dist;
2178  else if (dstX + dist <= mX)
2179  dstY += dist;
2180  }
2181  else
2182  {
2183  dstY = mY;
2184  }
2185  }
2186  botFixOffset(dstX, dstY);
2187  moveBotTo(dstX, dstY);
2188  }
2189 }
2190 
2191 void Being::moveBotTo(int dstX,
2192  int dstY)
2193 {
2194  const int dstX0 = mOwner->mX;
2195  const int dstY0 = mOwner->mY;
2196  const unsigned char blockWalkMask = getBlockWalkMask();
2197  if (!mMap->getWalk(dstX, dstY, blockWalkMask))
2198  {
2199  if (dstX != dstX0)
2200  {
2201  dstX = dstX0;
2202  if (!mMap->getWalk(dstX, dstY, blockWalkMask))
2203  dstY = dstY0;
2204  }
2205  else if (dstY != dstY0)
2206  {
2207  dstY = dstY0;
2208  if (!mMap->getWalk(dstX, dstY, blockWalkMask))
2209  dstX = dstX0;
2210  }
2211  }
2212  if (mX != dstX || mY != dstY)
2213  {
2215  homunculusHandler->move(dstX, dstY);
2216  else
2217  mercenaryHandler->move(dstX, dstY);
2218  return;
2219  }
2220  updateBotDirection(dstX, dstY);
2221 }
2222 
2223 void Being::updateBotDirection(const int dstX,
2224  const int dstY)
2225 {
2226  int directionType = 0;
2227  switch (mOwner->getCurrentAction())
2228  {
2229  case BeingAction::STAND:
2230  case BeingAction::MOVE:
2231  case BeingAction::HURT:
2232  case BeingAction::SPAWN:
2233  case BeingAction::CAST:
2234  case BeingAction::PRESTAND:
2235  default:
2236  directionType = mInfo->getDirectionType();
2237  break;
2238  case BeingAction::SIT:
2239  directionType = mInfo->getSitDirectionType();
2240  break;
2241  case BeingAction::DEAD:
2242  directionType = mInfo->getDeadDirectionType();
2243  break;
2244  case BeingAction::ATTACK:
2245  directionType = mInfo->getAttackDirectionType();
2246  break;
2247  }
2248 
2249  uint8_t newDir = 0;
2250  switch (directionType)
2251  {
2252  case 0:
2253  default:
2254  return;
2255 
2256  case 1:
2257  newDir = mOwner->mDirection;
2258  break;
2259 
2260  case 2:
2261  {
2262  const int dstX0 = mOwner->mX;
2263  const int dstY0 = mOwner->mY;
2264  if (dstX > dstX0)
2265  newDir |= BeingDirection::LEFT;
2266  else if (dstX < dstX0)
2267  newDir |= BeingDirection::RIGHT;
2268  if (dstY > dstY0)
2269  newDir |= BeingDirection::UP;
2270  else if (dstY < dstY0)
2271  newDir |= BeingDirection::DOWN;
2272  break;
2273  }
2274  case 3:
2275  {
2276  const int dstX0 = mOwner->mX;
2277  const int dstY0 = mOwner->mY;
2278  if (dstX > dstX0)
2279  newDir |= BeingDirection::RIGHT;
2280  else if (dstX < dstX0)
2281  newDir |= BeingDirection::LEFT;
2282  if (dstY > dstY0)
2283  newDir |= BeingDirection::DOWN;
2284  else if (dstY < dstY0)
2285  newDir |= BeingDirection::UP;
2286  break;
2287  }
2288  case 4:
2289  {
2290  const int dstX2 = mOwner->getLastAttackX();
2291  const int dstY2 = mOwner->getLastAttackY();
2292  if (dstX > dstX2)
2293  newDir |= BeingDirection::LEFT;
2294  else if (dstX < dstX2)
2295  newDir |= BeingDirection::RIGHT;
2296  if (dstY > dstY2)
2297  newDir |= BeingDirection::UP;
2298  else if (dstY < dstY2)
2299  newDir |= BeingDirection::DOWN;
2300  break;
2301  }
2302  }
2303  if ((newDir != 0U) && newDir != mDirection)
2304  {
2307  else
2308  mercenaryHandler->setDirection(newDir);
2309  }
2310 }
2311 
2313 {
2314  const int px = mPixelX - mapTileSize / 2;
2315  const int py = mPixelY - mapTileSize * 2 - mapTileSize;
2317  mBadgesCount != 0U)
2318  {
2319  if (mDispName != nullptr &&
2320  gui != nullptr)
2321  {
2323  {
2324  const Font *restrict const font = gui->getFont();
2326  mBadgesY = mDispName->getY() - font->getHeight();
2327  }
2328  else if (mShowBadges == BadgeDrawType::Bottom)
2329  {
2330  mBadgesX = px + 8 - mBadgesCount * 8;
2332  {
2333  mBadgesY = mDispName->getY();
2334  }
2335  else
2336  {
2337  mBadgesY = py + settings.playerNameOffset + 16;
2338  }
2339  }
2340  else
2341  {
2342  mBadgesX = px + 8 - mBadgesCount * 8;
2344  mBadgesY = py - mDispName->getHeight();
2345  else
2346  mBadgesY = py;
2347  }
2348  }
2349  else
2350  {
2352  {
2354  mBadgesY = py;
2355  }
2356  else if (mShowBadges == BadgeDrawType::Bottom)
2357  {
2358  mBadgesX = px + 8 - mBadgesCount * 8;
2359  const int height = settings.playerNameOffset;
2361  mBadgesY = py + height;
2362  else
2363  mBadgesY = py + height + 16;
2364  }
2365  else
2366  {
2367  mBadgesX = px + 8 - mBadgesCount * 8;
2368  mBadgesY = py;
2369  }
2370  }
2371  }
2372 }
2373 
2374 void Being::drawEmotion(Graphics *restrict const graphics,
2375  const int offsetX,
2376  const int offsetY) const restrict2
2377 {
2378  if (mErased)
2379  return;
2380 
2381  const int px = mPixelX - offsetX - mapTileSize / 2;
2382  const int py = mPixelY - offsetY - mapTileSize * 2 - mapTileSize;
2383  if (mAnimationEffect != nullptr)
2384  mAnimationEffect->draw(graphics, px, py);
2385  if (mShowBadges != BadgeDrawType::Hide &&
2386  mBadgesCount != 0U)
2387  {
2388  int x = mBadgesX - offsetX;
2389  const int y = mBadgesY - offsetY;
2390  for_each_badges()
2391  {
2392  const AnimatedSprite *restrict const sprite = mBadges[f];
2393  if (sprite != nullptr)
2394  {
2395  sprite->draw(graphics, x, y);
2396  x += 16;
2397  }
2398  }
2399  }
2400  if (mEmotionSprite != nullptr)
2401  mEmotionSprite->draw(graphics, px, py);
2402 }
2403 
2404 void Being::drawSpeech(const int offsetX,
2405  const int offsetY) restrict2
2406 {
2407  if (mErased)
2408  return;
2409  if (mSpeech.empty())
2410  return;
2411 
2412  const int px = mPixelX - offsetX;
2413  const int py = mPixelY - offsetY;
2414  const int speech = mSpeechType;
2415 
2416  // Draw speech above this being
2417  if (mSpeechTime == 0)
2418  {
2419  if (mSpeechBubble != nullptr &&
2420  mSpeechBubble->mVisible == Visible_true)
2421  {
2422  mSpeechBubble->setVisible(Visible_false);
2423  }
2424  mSpeech.clear();
2425  }
2426  else if (mSpeechTime > 0 && (speech == BeingSpeech::NAME_IN_BUBBLE ||
2427  speech == BeingSpeech::NO_NAME_IN_BUBBLE))
2428  {
2429  delete2(mText)
2430 
2431  if (mSpeechBubble != nullptr)
2432  {
2433  mSpeechBubble->setPosition(px - (mSpeechBubble->getWidth() / 2),
2434  py - getHeight() - (mSpeechBubble->getHeight()));
2435  mSpeechBubble->setVisible(Visible_true);
2436  }
2437  }
2438  else if (mSpeechTime > 0 && speech == BeingSpeech::TEXT_OVERHEAD)
2439  {
2440  if (mSpeechBubble != nullptr)
2441  mSpeechBubble->setVisible(Visible_false);
2442 
2443  if ((mText == nullptr) && (userPalette != nullptr))
2444  {
2445  mText = new Text(mSpeech,
2446  mPixelX,
2447  mPixelY - getHeight(),
2449  &theme->getColor(ThemeColorId::BUBBLE_TEXT, 255),
2450  Speech_true,
2451  nullptr);
2452  mText->adviseXY(mPixelX,
2453  (mY + 1) * mapTileSize - getHeight() - mText->getHeight() - 9,
2454  mMoveNames);
2455  }
2456  }
2457  else if (speech == BeingSpeech::NO_SPEECH)
2458  {
2459  if (mSpeechBubble != nullptr)
2460  mSpeechBubble->setVisible(Visible_false);
2461  delete2(mText)
2462  }
2463 }
2464 
2465 template<signed char pos, signed char neg>
2467 {
2468  // Check whether we're walking in the requested direction
2469  if (mAction != BeingAction::MOVE || !(mDirection & (pos | neg)))
2470  return 0;
2471 
2472  int offset = 0;
2473 
2474  if (mMap && mSpeed)
2475  {
2476  const int time = get_elapsed_time(mActionTime);
2477  offset = (pos == BeingDirection::LEFT &&
2478  neg == BeingDirection::RIGHT) ?
2479  (time * mMap->getTileWidth() / mSpeed)
2480  : (time * mMap->getTileHeight() / mSpeed);
2481  }
2482 
2483  // We calculate the offset _from_ the _target_ location
2484  offset -= mapTileSize;
2485  if (offset > 0)
2486  offset = 0;
2487 
2488  // Going into negative direction? Invert the offset.
2489  if (mDirection & pos)
2490  offset = -offset;
2491 
2492  if (offset > mapTileSize)
2493  offset = mapTileSize;
2494  if (offset < -mapTileSize)
2495  offset = -mapTileSize;
2496 
2497  return offset;
2498 }
2499 
2501 {
2502  if (mDispName != nullptr)
2503  {
2504  int offsetX = mPixelX;
2505  int offsetY = mPixelY;
2506  if (mInfo != nullptr)
2507  {
2508  offsetX += mInfo->getNameOffsetX();
2509  offsetY += mInfo->getNameOffsetY();
2510  }
2511  // Monster names show above the sprite instead of below it
2512  if (mType == ActorType::Monster ||
2514  {
2515  offsetY += - settings.playerNameOffset - mDispName->getHeight();
2516  }
2517  mDispName->adviseXY(offsetX, offsetY, mMoveNames);
2518  }
2520 }
2521 
2522 void Being::optionChanged(const std::string &restrict value) restrict2
2523 {
2524  if (mType == ActorType::Player && value == "visiblenames")
2525  {
2526  setShowName(config.getIntValue("visiblenames") == VisibleName::Show);
2527  updateBadgesPosition();
2528  }
2529 }
2530 
2531 void Being::flashName(const int time) restrict2
2532 {
2533  if (mDispName != nullptr)
2534  mDispName->flash(time);
2535 }
2536 
2538 {
2539  const std::string &restrict str = getGenderSign();
2540  if (str.empty())
2541  return str;
2542  else
2543  return std::string(" ").append(str);
2544 }
2545 
2546 std::string Being::getGenderSign() const restrict2
2547 {
2548  std::string str;
2549  if (mShowGender)
2550  {
2551  if (getGender() == Gender::FEMALE)
2552  str = "\u2640";
2553  else if (getGender() == Gender::MALE)
2554  str = "\u2642";
2555  }
2556  if (mShowPlayersStatus &&
2558  {
2559  if (mShop)
2560  str.append("$");
2561  if (mAway)
2562  {
2563  // TRANSLATORS: this away status writed in player nick
2564  str.append(_("A"));
2565  }
2566  else if (mInactive)
2567  {
2568  // TRANSLATORS: this inactive status writed in player nick
2569  str.append(_("I"));
2570  }
2571  }
2572  return str;
2573 }
2574 
2576 {
2577  if (mName.empty())
2578  return;
2579 
2581 
2583  return;
2584 
2585  std::string displayName(mName);
2586 
2588  {
2589  displayName.append(" ");
2590  if (mShowLevel && getLevel() != 0)
2591  displayName.append(toString(getLevel()));
2592 
2593  displayName.append(getGenderSign());
2594  }
2595 
2596  if (mType == ActorType::Monster)
2597  {
2598  if (config.getBoolValue("showMonstersTakedDamage"))
2599  displayName.append(", ").append(toString(getDamageTaken()));
2600  }
2601 
2602  Font *font = nullptr;
2603  if ((localPlayer != nullptr) && localPlayer->getTarget() == this
2604  && mType != ActorType::Monster)
2605  {
2606  font = boldFont;
2607  }
2608  else if (mType == ActorType::Player
2609  && !playerRelations.isGoodName(this) && (gui != nullptr))
2610  {
2611  font = gui->getSecureFont();
2612  }
2613 
2614  if (mInfo != nullptr)
2615  {
2616  mDispName = new FlashText(displayName,
2620  mNameColor,
2621  font);
2622  }
2623  else
2624  {
2625  mDispName = new FlashText(displayName,
2626  mPixelX,
2627  mPixelY,
2629  mNameColor,
2630  font);
2631  }
2632 
2633  updateCoords();
2634 }
2635 
2637 {
2638  switch (mTeamId)
2639  {
2640  case 0:
2641  default:
2642  mNameColor = &userPalette->getColor(defaultColor,
2643  255U);
2644  break;
2645  case 1:
2646  mNameColor = &userPalette->getColor(UserColorId::TEAM1,
2647  255U);
2648  break;
2649  case 2:
2650  mNameColor = &userPalette->getColor(UserColorId::TEAM2,
2651  255U);
2652  break;
2653  case 3:
2654  mNameColor = &userPalette->getColor(UserColorId::TEAM3,
2655  255U);
2656  break;
2657  }
2658 }
2659 
2661 {
2662  if (userPalette != nullptr)
2663  {
2664  if (mType == ActorType::Monster)
2665  {
2668  255U);
2669  }
2670  else if (mType == ActorType::Npc)
2671  {
2674  255U);
2675  }
2676  else if (mType == ActorType::Pet)
2677  {
2680  255U);
2681  }
2682  else if (mType == ActorType::Homunculus)
2683  {
2686  255U);
2687  }
2688  else if (mType == ActorType::SkillUnit)
2689  {
2692  255U);
2693  }
2694  else if (this == localPlayer)
2695  {
2697  mTextColor = &theme->getColor(ThemeColorId::PLAYER, 255);
2698  }
2699  else
2700  {
2701  mTextColor = &theme->getColor(ThemeColorId::PLAYER, 255);
2702 
2704  mErased = false;
2705  else
2706  mErased = true;
2707 
2708  if (mIsGM)
2709  {
2711  255U);
2713  255U);
2714  }
2715  else if (mEnemy)
2716  {
2718  255U);
2719  }
2720  else if ((mParty != nullptr) && (localPlayer != nullptr)
2721  && mParty == localPlayer->getParty())
2722  {
2724  255U);
2725  }
2726  else if ((localPlayer != nullptr) && (getGuild() != nullptr)
2727  && getGuild() == localPlayer->getGuild())
2728  {
2730  255U);
2731  }
2733  {
2735  255U);
2736  }
2737  else if (playerRelations.getRelation(mName) ==
2741  {
2743  255U);
2744  }
2746  == Relation::IGNORED ||
2748  {
2750  255U);
2751  }
2753  {
2755  255U);
2756  }
2757  else
2758  {
2760  }
2761  }
2762 
2763  if (mDispName != nullptr)
2765  }
2766 }
2767 
2768 void Being::updateSprite(const unsigned int slot,
2769  const int id,
2770  const std::string &restrict color) restrict2
2771 {
2772  if (charServerHandler == nullptr || slot >= charServerHandler->maxSprite())
2773  return;
2774 
2775  if (slot >= CAST_U32(mSlots.size()))
2776  mSlots.resize(slot + 1, BeingSlot());
2777 
2778  if ((slot != 0U) && mSlots[slot].spriteId == id)
2779  return;
2780  setSpriteColor(slot,
2781  id,
2782  color);
2783 }
2784 
2785 // set sprite id, reset colors, reset cards
2786 void Being::setSpriteId(const unsigned int slot,
2787  const int id) restrict2
2788 {
2789  if (charServerHandler == nullptr || slot >= charServerHandler->maxSprite())
2790  return;
2791 
2792  if (slot >= CAST_U32(mSprites.size()))
2793  ensureSize(slot + 1);
2794 
2795  if (slot >= CAST_U32(mSlots.size()))
2796  mSlots.resize(slot + 1, BeingSlot());
2797 
2798  // id = 0 means unequip
2799  if (id == 0)
2800  {
2801  removeSprite(slot);
2802  mSpriteDraw[slot] = 0;
2803 
2804  const int id1 = mSlots[slot].spriteId;
2805  if (id1 != 0)
2806  removeItemParticles(id1);
2807  }
2808  else
2809  {
2810  const ItemInfo &info = ItemDB::get(id);
2811  const std::string &restrict filename = info.getSprite(
2812  mGender, mSubType);
2813  int lastTime = 0;
2814  int startTime = 0;
2815  AnimatedSprite *restrict equipmentSprite = nullptr;
2816 
2817  if (!filename.empty())
2818  {
2819  equipmentSprite = AnimatedSprite::delayedLoad(
2820  pathJoin(paths.getStringValue("sprites"), filename),
2821  0);
2822  }
2823 
2824  if (equipmentSprite != nullptr)
2825  {
2826  equipmentSprite->setSpriteDirection(getSpriteDirection());
2827  startTime = getStartTime();
2828  lastTime = getLastTime();
2829  }
2830 
2831  CompoundSprite::setSprite(slot, equipmentSprite);
2832  mSpriteDraw[slot] = id;
2833 
2834  addItemParticles(id, info.getDisplay());
2835 
2836  setAction(mAction, 0);
2837  if (equipmentSprite != nullptr)
2838  {
2839  if (lastTime > 0)
2840  {
2841  equipmentSprite->setLastTime(startTime);
2842  equipmentSprite->update(lastTime);
2843  }
2844  }
2845  }
2846 
2847  BeingSlot &beingSlot = mSlots[slot];
2848  beingSlot.spriteId = id;
2849  beingSlot.color.clear();
2850  beingSlot.colorId = ItemColor_one;
2851  beingSlot.cardsId = CardsList(nullptr);
2852  recalcSpritesOrder();
2853  if (beingEquipmentWindow != nullptr)
2855 }
2856 
2857 // reset sprite id, reset colors, reset cards
2858 void Being::unSetSprite(const unsigned int slot) restrict2
2859 {
2860  if (charServerHandler == nullptr || slot >= charServerHandler->maxSprite())
2861  return;
2862 
2863  if (slot >= CAST_U32(mSprites.size()))
2864  ensureSize(slot + 1);
2865 
2866  if (slot >= CAST_U32(mSlots.size()))
2867  mSlots.resize(slot + 1, BeingSlot());
2868 
2869  removeSprite(slot);
2870  mSpriteDraw[slot] = 0;
2871 
2872  BeingSlot &beingSlot = mSlots[slot];
2873  const int id1 = beingSlot.spriteId;
2874  if (id1 != 0)
2875  removeItemParticles(id1);
2876 
2877  beingSlot.spriteId = 0;
2878  beingSlot.color.clear();
2879  beingSlot.colorId = ItemColor_one;
2880  beingSlot.cardsId = CardsList(nullptr);
2881  recalcSpritesOrder();
2882  if (beingEquipmentWindow != nullptr)
2884 }
2885 
2886 // set sprite id, use color string, reset cards
2887 void Being::setSpriteColor(const unsigned int slot,
2888  const int id,
2889  const std::string &color) restrict2
2890 {
2891  if (charServerHandler == nullptr || slot >= charServerHandler->maxSprite())
2892  return;
2893 
2894  if (slot >= CAST_U32(mSprites.size()))
2895  ensureSize(slot + 1);
2896 
2897  if (slot >= CAST_U32(mSlots.size()))
2898  mSlots.resize(slot + 1, BeingSlot());
2899 
2900  // disabled for now, because it may broke replace/reorder sprites logic
2901 // if (slot && mSlots[slot].spriteId == id)
2902 // return;
2903 
2904  // id = 0 means unequip
2905  if (id == 0)
2906  {
2907  removeSprite(slot);
2908  mSpriteDraw[slot] = 0;
2909 
2910  const int id1 = mSlots[slot].spriteId;
2911  if (id1 != 0)
2912  removeItemParticles(id1);
2913  }
2914  else
2915  {
2916  const ItemInfo &info = ItemDB::get(id);
2917  const std::string &restrict filename = info.getSprite(
2918  mGender, mSubType);
2919  int lastTime = 0;
2920  int startTime = 0;
2921  AnimatedSprite *restrict equipmentSprite = nullptr;
2922 
2923  if (!filename.empty())
2924  {
2925  equipmentSprite = AnimatedSprite::delayedLoad(
2926  pathJoin(paths.getStringValue("sprites"),
2927  combineDye(filename, color)),
2928  0);
2929  }
2930 
2931  if (equipmentSprite != nullptr)
2932  {
2933  equipmentSprite->setSpriteDirection(getSpriteDirection());
2934  startTime = getStartTime();
2935  lastTime = getLastTime();
2936  }
2937 
2938  CompoundSprite::setSprite(slot, equipmentSprite);
2939  mSpriteDraw[slot] = id;
2940  addItemParticles(id, info.getDisplay());
2941 
2942  setAction(mAction, 0);
2943  if (equipmentSprite != nullptr)
2944  {
2945  if (lastTime > 0)
2946  {
2947  equipmentSprite->setLastTime(startTime);
2948  equipmentSprite->update(lastTime);
2949  }
2950  }
2951  }
2952 
2953  BeingSlot &beingSlot = mSlots[slot];
2954  beingSlot.spriteId = id;
2955  beingSlot.color = color;
2956  beingSlot.colorId = ItemColor_one;
2957  beingSlot.cardsId = CardsList(nullptr);
2958  recalcSpritesOrder();
2959  if (beingEquipmentWindow != nullptr)
2961 }
2962 
2963 // set sprite id, use color id, reset cards
2964 void Being::setSpriteColorId(const unsigned int slot,
2965  const int id,
2966  ItemColor colorId) restrict2
2967 {
2968  if (charServerHandler == nullptr || slot >= charServerHandler->maxSprite())
2969  return;
2970 
2971  if (slot >= CAST_U32(mSprites.size()))
2972  ensureSize(slot + 1);
2973 
2974  if (slot >= CAST_U32(mSlots.size()))
2975  mSlots.resize(slot + 1, BeingSlot());
2976 
2977  // disabled for now, because it may broke replace/reorder sprites logic
2978 // if (slot && mSlots[slot].spriteId == id)
2979 // return;
2980 
2981  std::string color;
2982 
2983  // id = 0 means unequip
2984  if (id == 0)
2985  {
2986  removeSprite(slot);
2987  mSpriteDraw[slot] = 0;
2988 
2989  const int id1 = mSlots[slot].spriteId;
2990  if (id1 != 0)
2991  removeItemParticles(id1);
2992  }
2993  else
2994  {
2995  const ItemInfo &info = ItemDB::get(id);
2996  const std::string &restrict filename = info.getSprite(
2997  mGender, mSubType);
2998  int lastTime = 0;
2999  int startTime = 0;
3000  AnimatedSprite *restrict equipmentSprite = nullptr;
3001 
3002  if (!filename.empty())
3003  {
3004  color = info.getDyeColorsString(colorId);
3005  equipmentSprite = AnimatedSprite::delayedLoad(
3006  pathJoin(paths.getStringValue("sprites"),
3007  combineDye(filename, color)),
3008  0);
3009  }
3010 
3011  if (equipmentSprite != nullptr)
3012  {
3013  equipmentSprite->setSpriteDirection(getSpriteDirection());
3014  startTime = getStartTime();
3015  lastTime = getLastTime();
3016  }
3017 
3018  CompoundSprite::setSprite(slot, equipmentSprite);
3019  mSpriteDraw[slot] = id;
3020 
3021  addItemParticles(id, info.getDisplay());
3022 
3023  setAction(mAction, 0);
3024  if (equipmentSprite != nullptr)
3025  {
3026  if (lastTime > 0)
3027  {
3028  equipmentSprite->setLastTime(startTime);
3029  equipmentSprite->update(lastTime);
3030  }
3031  }
3032  }
3033 
3034  BeingSlot &beingSlot = mSlots[slot];
3035  beingSlot.spriteId = id;
3036  beingSlot.color = STD_MOVE(color);
3037  beingSlot.colorId = colorId;
3038  beingSlot.cardsId = CardsList(nullptr);
3039  recalcSpritesOrder();
3040  if (beingEquipmentWindow != nullptr)
3042 }
3043 
3044 // set sprite id, colors from cards, cards
3045 void Being::setSpriteCards(const unsigned int slot,
3046  const int id,
3047  const CardsList &cards) restrict2
3048 {
3049  if (charServerHandler == nullptr || slot >= charServerHandler->maxSprite())
3050  return;
3051 
3052  if (slot >= CAST_U32(mSprites.size()))
3053  ensureSize(slot + 1);
3054 
3055  if (slot >= CAST_U32(mSlots.size()))
3056  mSlots.resize(slot + 1, BeingSlot());
3057 
3058  // disabled for now, because it may broke replace/reorder sprites logic
3059 // if (slot && mSlots[slot].spriteId == id)
3060 // return;
3061 
3062  ItemColor colorId = ItemColor_one;
3063  std::string color;
3064 
3065  // id = 0 means unequip
3066  if (id == 0)
3067  {
3068  removeSprite(slot);
3069  mSpriteDraw[slot] = 0;
3070 
3071  const int id1 = mSlots[slot].spriteId;
3072  if (id1 != 0)
3073  removeItemParticles(id1);
3074  }
3075  else
3076  {
3077  const ItemInfo &info = ItemDB::get(id);
3078  const std::string &restrict filename = info.getSprite(
3079  mGender, mSubType);
3080  int lastTime = 0;
3081  int startTime = 0;
3082  AnimatedSprite *restrict equipmentSprite = nullptr;
3083 
3084  if (!cards.isEmpty())
3085  colorId = ItemColorManager::getColorFromCards(cards);
3086 
3087  if (!filename.empty())
3088  {
3089  color = info.getDyeColorsString(colorId);
3090 
3091  equipmentSprite = AnimatedSprite::delayedLoad(
3092  pathJoin(paths.getStringValue("sprites"),
3093  combineDye(filename, color)),
3094  0);
3095  }
3096 
3097  if (equipmentSprite != nullptr)
3098  {
3099  equipmentSprite->setSpriteDirection(getSpriteDirection());
3100  startTime = getStartTime();
3101  lastTime = getLastTime();
3102  }
3103 
3104  CompoundSprite::setSprite(slot, equipmentSprite);
3105  mSpriteDraw[slot] = id;
3106 
3107  addItemParticlesCards(id,
3108  info.getDisplay(),
3109  cards);
3110 
3111  setAction(mAction, 0);
3112  if (equipmentSprite != nullptr)
3113  {
3114  if (lastTime > 0)
3115  {
3116  equipmentSprite->setLastTime(startTime);
3117  equipmentSprite->update(lastTime);
3118  }
3119  }
3120  }
3121 
3122  BeingSlot &beingSlot = mSlots[slot];
3123  beingSlot.spriteId = id;
3124  beingSlot.color = STD_MOVE(color);
3125  beingSlot.colorId = colorId;
3126  beingSlot.cardsId = CardsList(cards);
3127  recalcSpritesOrder();
3128  if (beingEquipmentWindow != nullptr)
3130 }
3131 
3132 void Being::setWeaponId(const int id) restrict2
3133 {
3134  if (id == 0)
3135  mEquippedWeapon = nullptr;
3136  else
3137  mEquippedWeapon = &ItemDB::get(id);
3138 }
3139 
3140 void Being::setTempSprite(const unsigned int slot,
3141  const int id) restrict2
3142 {
3143  if (charServerHandler == nullptr || slot >= charServerHandler->maxSprite())
3144  return;
3145 
3146  if (slot >= CAST_U32(mSprites.size()))
3147  ensureSize(slot + 1);
3148 
3149  if (slot >= CAST_U32(mSlots.size()))
3150  mSlots.resize(slot + 1, BeingSlot());
3151 
3152  BeingSlot &beingSlot = mSlots[slot];
3153 
3154  // id = 0 means unequip
3155  if (id == 0)
3156  {
3157  removeSprite(slot);
3158  mSpriteDraw[slot] = 0;
3159 
3160  const int id1 = beingSlot.spriteId;
3161  if (id1 != 0)
3162  removeItemParticles(id1);
3163  }
3164  else
3165  {
3166  const ItemInfo &info = ItemDB::get(id);
3167  const std::string &restrict filename = info.getSprite(
3168  mGender, mSubType);
3169  int lastTime = 0;
3170  int startTime = 0;
3171 
3172  AnimatedSprite *restrict equipmentSprite = nullptr;
3173 
3174  if (!filename.empty())
3175  {
3176  ItemColor colorId = ItemColor_one;
3177  const CardsList &cards = beingSlot.cardsId;
3178  if (!cards.isEmpty())
3179  colorId = ItemColorManager::getColorFromCards(cards);
3180  std::string color = beingSlot.color;
3181  if (color.empty())
3182  color = info.getDyeColorsString(colorId);
3183 
3184  equipmentSprite = AnimatedSprite::delayedLoad(
3185  pathJoin(paths.getStringValue("sprites"),
3186  combineDye(filename, color)),
3187  0);
3188  }
3189 
3190  if (equipmentSprite != nullptr)
3191  {
3192  equipmentSprite->setSpriteDirection(getSpriteDirection());
3193  startTime = getStartTime();
3194  lastTime = getLastTime();
3195  }
3196 
3197  CompoundSprite::setSprite(slot, equipmentSprite);
3198  mSpriteDraw[slot] = id;
3199 
3200  // +++ here probably need use existing cards
3201  addItemParticles(id, info.getDisplay());
3202 
3203  setAction(mAction, 0);
3204  if (equipmentSprite != nullptr)
3205  {
3206  if (lastTime > 0)
3207  {
3208  equipmentSprite->setLastTime(startTime);
3209  equipmentSprite->update(lastTime);
3210  }
3211  }
3212  }
3213 }
3214 
3215 void Being::setHairTempSprite(const unsigned int slot,
3216  const int id) restrict2
3217 {
3218  if (charServerHandler == nullptr || slot >= charServerHandler->maxSprite())
3219  return;
3220 
3221  if (slot >= CAST_U32(mSprites.size()))
3222  ensureSize(slot + 1);
3223 
3224  if (slot >= CAST_U32(mSlots.size()))
3225  mSlots.resize(slot + 1, BeingSlot());
3226 
3227  const CardsList &cards = mSlots[slot].cardsId;
3228 
3229  // id = 0 means unequip
3230  if (id == 0)
3231  {
3232  removeSprite(slot);
3233  mSpriteDraw[slot] = 0;
3234 
3235  const int id1 = mSlots[slot].spriteId;
3236  if (id1 != 0)
3237  removeItemParticles(id1);
3238  }
3239  else
3240  {
3241  const ItemInfo &info = ItemDB::get(id);
3242  const std::string &restrict filename = info.getSprite(
3243  mGender, mSubType);
3244  int lastTime = 0;
3245  int startTime = 0;
3246  AnimatedSprite *restrict equipmentSprite = nullptr;
3247 
3248  if (!filename.empty())
3249  {
3250  ItemColor colorId = ItemColor_one;
3251  if (!cards.isEmpty())
3252  colorId = ItemColorManager::getColorFromCards(cards);
3253 
3254  std::string color = info.getDyeColorsString(mHairColor);
3255  if (color.empty())
3256  color = info.getDyeColorsString(colorId);
3257 
3258  equipmentSprite = AnimatedSprite::delayedLoad(
3259  pathJoin(paths.getStringValue("sprites"),
3260  combineDye(filename, color)),
3261  0);
3262  }
3263 
3264  if (equipmentSprite != nullptr)
3265  {
3266  equipmentSprite->setSpriteDirection(getSpriteDirection());
3267  startTime = getStartTime();
3268  lastTime = getLastTime();
3269  }
3270 
3271  CompoundSprite::setSprite(slot, equipmentSprite);
3272  mSpriteDraw[slot] = id;
3273 
3274  addItemParticles(id, info.getDisplay());
3275 
3276  setAction(mAction, 0);
3277  if (equipmentSprite != nullptr)
3278  {
3279  if (lastTime > 0)
3280  {
3281  equipmentSprite->setLastTime(startTime);
3282  equipmentSprite->update(lastTime);
3283  }
3284  }
3285  }
3286 }
3287 
3288 void Being::setHairColorSpriteID(const unsigned int slot,
3289  const int id) restrict2
3290 {
3291  if (charServerHandler == nullptr || slot >= charServerHandler->maxSprite())
3292  return;
3293 
3294  if (slot >= CAST_U32(mSprites.size()))
3295  ensureSize(slot + 1);
3296 
3297  if (slot >= CAST_U32(mSlots.size()))
3298  mSlots.resize(slot + 1, BeingSlot());
3299 
3300  BeingSlot &beingSlot = mSlots[slot];
3301  setSpriteColor(slot,
3302  id,
3303  beingSlot.color);
3304 }
3305 
3306 void Being::setSpriteColor(const unsigned int slot,
3307  const std::string &restrict color) restrict2
3308 {
3309  if (charServerHandler == nullptr || slot >= charServerHandler->maxSprite())
3310  return;
3311 
3312  if (slot >= CAST_U32(mSprites.size()))
3313  ensureSize(slot + 1);
3314 
3315  if (slot >= CAST_U32(mSlots.size()))
3316  mSlots.resize(slot + 1, BeingSlot());
3317 
3318  // disabled for now, because it may broke replace/reorder sprites logic
3319 // if (slot && mSlots[slot].spriteId == id)
3320 // return;
3321 
3322  BeingSlot &beingSlot = mSlots[slot];
3323  const int id = beingSlot.spriteId;
3324 
3325  // id = 0 means unequip
3326  if (id != 0)
3327  {
3328  const ItemInfo &info = ItemDB::get(id);
3329  const std::string &restrict filename = info.getSprite(
3330  mGender, mSubType);
3331  int lastTime = 0;
3332  int startTime = 0;
3333  AnimatedSprite *restrict equipmentSprite = nullptr;
3334 
3335  if (!filename.empty())
3336  {
3337  equipmentSprite = AnimatedSprite::delayedLoad(
3338  pathJoin(paths.getStringValue("sprites"),
3339  combineDye(filename, color)),
3340  0);
3341  }
3342 
3343  if (equipmentSprite != nullptr)
3344  {
3345  equipmentSprite->setSpriteDirection(getSpriteDirection());
3346  startTime = getStartTime();
3347  lastTime = getLastTime();
3348  }
3349 
3350  CompoundSprite::setSprite(slot, equipmentSprite);
3351 
3352  setAction(mAction, 0);
3353  if (equipmentSprite != nullptr)
3354  {
3355  if (lastTime > 0)
3356  {
3357  equipmentSprite->setLastTime(startTime);
3358  equipmentSprite->update(lastTime);
3359  }
3360  }
3361  }
3362 
3363  beingSlot.color = color;
3364  beingSlot.colorId = ItemColor_one;
3365  if (beingEquipmentWindow != nullptr)
3367 }
3368 
3369 void Being::setHairStyle(const unsigned int slot,
3370  const int id) restrict2
3371 {
3372  if (id != 0)
3373  {
3374  setSpriteColor(slot,
3375  id,
3376  ItemDB::get(id).getDyeColorsString(mHairColor));
3377  }
3378  else
3379  {
3380  setSpriteColor(slot,
3381  0,
3382  std::string());
3383  }
3384 }
3385 
3386 void Being::setHairColor(const unsigned int slot,
3387  const ItemColor color) restrict2
3388 {
3389  mHairColor = color;
3390  BeingSlot &beingSlot = mSlots[slot];
3391  const int id = beingSlot.spriteId;
3392  if (id != 0)
3393  {
3394  setSpriteColor(slot,
3395  id,
3396  ItemDB::get(id).getDyeColorsString(color));
3397  }
3398 }
3399 
3400 void Being::setSpriteSlot(const unsigned int slot,
3401  const BeingSlot &beingSlot)
3402 {
3403  mSlots[slot] = beingSlot;
3404 }
3405 
3407 {
3408  STD_VECTOR<BeingSlot>::const_iterator it1 = mSlots.begin();
3409  const STD_VECTOR<BeingSlot>::const_iterator it1_end = mSlots.end();
3410 
3411  logger->log("sprites");
3412  for (; it1 != it1_end;
3413  ++ it1)
3414  {
3415  logger->log("%d,%s,%d",
3416  (*it1).spriteId,
3417  (*it1).color.c_str(),
3418  toInt((*it1).colorId, int));
3419  }
3420 }
3421 
3423 {
3424  if (mShowName)
3425  showName();
3426 }
3427 
3429 {
3430  BLOCK_START("Being::reReadConfig")
3431  if (mUpdateConfigTime + 1 < cur_time)
3432  {
3433  mAwayEffect = paths.getIntValue("afkEffectId");
3434  mHighlightMapPortals = config.getBoolValue("highlightMapPortals");
3435  mConfLineLim = config.getIntValue("chatMaxCharLimit");
3436  mSpeechType = config.getIntValue("speech");
3438  config.getBoolValue("highlightMonsterAttackRange");
3439  mLowTraffic = config.getBoolValue("lowTraffic");
3440  mDrawHotKeys = config.getBoolValue("drawHotKeys");
3441  mShowBattleEvents = config.getBoolValue("showBattleEvents");
3442  mShowMobHP = config.getBoolValue("showMobHP");
3443  mShowOwnHP = config.getBoolValue("showOwnHP");
3444  mShowGender = config.getBoolValue("showgender");
3445  mShowLevel = config.getBoolValue("showlevel");
3446  mShowPlayersStatus = config.getBoolValue("showPlayersStatus");
3447  mEnableReorderSprites = config.getBoolValue("enableReorderSprites");
3448  mHideErased = config.getBoolValue("hideErased");
3449  mMoveNames = fromBool(config.getBoolValue("moveNames"), Move);
3450  mUseDiagonal = config.getBoolValue("useDiagonalSpeed");
3451  mShowBadges = static_cast<BadgeDrawType::Type>(
3452  config.getIntValue("showBadges"));
3453  mVisibleNamePos = static_cast<VisibleNamePos::Type>(
3454  config.getIntValue("visiblenamespos"));
3455 
3457  }
3458  BLOCK_END("Being::reReadConfig")
3459 }
3460 
3462 {
3463  const BeingCacheEntry *restrict const entry =
3465 
3466  if ((entry != nullptr) && entry->getTime() + 120 >= cur_time)
3467  {
3468  if (!entry->getName().empty())
3469  setName(entry->getName());
3470  setPartyName(entry->getPartyName());
3471  setGuildName(entry->getGuildName());
3472  setLevel(entry->getLevel());
3473  setPvpRank(entry->getPvpRank());
3474  setIp(entry->getIp());
3475  setTeamId(entry->getTeamId());
3476 
3477  mAdvanced = entry->isAdvanced();
3478  if (mAdvanced)
3479  {
3480  const int flags = entry->getFlags();
3481  if ((serverFeatures != nullptr) &&
3483  {
3484  mShop = ((flags & BeingFlag::SHOP) != 0);
3485  }
3486  mAway = ((flags & BeingFlag::AWAY) != 0);
3487  mInactive = ((flags & BeingFlag::INACTIVE) != 0);
3488  if (mShop || mAway || mInactive)
3489  updateName();
3490  }
3491  else
3492  {
3494  mShop = false;
3495  mAway = false;
3496  mInactive = false;
3497  }
3498 
3502  updateAwayEffect();
3503  if (mType == ActorType::Player || (mTeamId != 0U))
3504  updateColors();
3505  return true;
3506  }
3507  return false;
3508 }
3509 
3511 {
3512  if (localPlayer == this)
3513  return;
3514 
3516  if (entry == nullptr)
3517  {
3518  entry = new BeingCacheEntry(getId());
3519  beingInfoCache.push_front(entry);
3520 
3521  if (beingInfoCache.size() >= CACHE_SIZE)
3522  {
3523  delete beingInfoCache.back();
3524  beingInfoCache.pop_back();
3525  }
3526  }
3527  if (!mLowTraffic)
3528  return;
3529 
3530  entry->setName(mName);
3531  entry->setLevel(getLevel());
3532  entry->setPartyName(getPartyName());
3533  entry->setGuildName(getGuildName());
3534  entry->setTime(cur_time);
3535  entry->setPvpRank(getPvpRank());
3536  entry->setIp(getIp());
3537  entry->setAdvanced(isAdvanced());
3538  entry->setTeamId(getTeamId());
3539  if (isAdvanced())
3540  {
3541  int flags = 0;
3543  flags += BeingFlag::SHOP;
3544  if (mAway)
3545  flags += BeingFlag::AWAY;
3546  if (mInactive)
3547  flags += BeingFlag::INACTIVE;
3548  entry->setFlags(flags);
3549  }
3550  else
3551  {
3552  entry->setFlags(0);
3553  }
3554 }
3555 
3557 {
3558  FOR_EACH (std::list<BeingCacheEntry*>::iterator, i, beingInfoCache)
3559  {
3560  if (*i == nullptr)
3561  continue;
3562 
3563  if (id == (*i)->getId())
3564  {
3565  // Raise priority: move it to front
3566  if ((*i)->getTime() + 120 < cur_time)
3567  {
3568  beingInfoCache.splice(beingInfoCache.begin(),
3569  beingInfoCache, i);
3570  }
3571  return *i;
3572  }
3573  }
3574  return nullptr;
3575 }
3576 
3577 
3579 {
3580  if (charServerHandler == nullptr)
3581  return;
3582 
3583  if (gender != mGender)
3584  {
3585  mGender = gender;
3586 
3587  const unsigned int sz = CAST_U32(mSlots.size());
3588 
3589  if (sz > CAST_U32(mSprites.size()))
3590  ensureSize(sz);
3591 
3592  // Reload all subsprites
3593  for (unsigned int i = 0;
3594  i < sz;
3595  i++)
3596  {
3597  BeingSlot &beingSlot = mSlots[i];
3598  const int id = beingSlot.spriteId;
3599  if (id != 0)
3600  {
3601  const ItemInfo &info = ItemDB::get(id);
3602  const std::string &restrict filename = info.getSprite(
3603  mGender, mSubType);
3604  int lastTime = 0;
3605  int startTime = 0;
3606  AnimatedSprite *restrict equipmentSprite = nullptr;
3607 
3608  if (!filename.empty())
3609  {
3610  equipmentSprite = AnimatedSprite::delayedLoad(
3611  pathJoin(paths.getStringValue("sprites"),
3612  combineDye(filename, beingSlot.color)),
3613  0);
3614  }
3615 
3616  if (equipmentSprite != nullptr)
3617  {
3618  equipmentSprite->setSpriteDirection(getSpriteDirection());
3619  startTime = getStartTime();
3620  lastTime = getLastTime();
3621  }
3622 
3623  CompoundSprite::setSprite(i, equipmentSprite);
3624  setAction(mAction, 0);
3625  if (equipmentSprite != nullptr)
3626  {
3627  if (lastTime > 0)
3628  {
3629  equipmentSprite->setLastTime(startTime);
3630  equipmentSprite->update(lastTime);
3631  }
3632  }
3633 
3634  if (beingEquipmentWindow != nullptr)
3636  }
3637  }
3638 
3639  updateName();
3640  }
3641 }
3642 
3643 void Being::showGmBadge(const bool show) restrict2
3644 {
3645  delete2(mBadges[BadgeIndex::Gm])
3646  if (show &&
3647  mIsGM &&
3648  mShowBadges != BadgeDrawType::Hide &&
3649  GroupDb::getShowBadge(mGroupId))
3650  {
3651  const std::string &gmBadge = GroupDb::getBadge(mGroupId);
3652  if (!gmBadge.empty())
3653  {
3655  paths.getStringValue("badges") + gmBadge,
3656  0);
3657  }
3658  }
3659  updateBadgesCount();
3660  updateBadgesPosition();
3661 }
3662 
3663 void Being::setGM(const bool gm) restrict2
3664 {
3665  if (mIsGM != gm)
3666  {
3667  mIsGM = gm;
3668  updateColors();
3669  }
3670 }
3671 
3673 {
3674  if (npcHandler == nullptr)
3675  return;
3676 
3678  {
3679  // using workaround...
3680  if ((playerHandler != nullptr) &&
3682  {
3684  }
3685  return;
3686  }
3687 
3688  npcHandler->talk(this);
3689 }
3690 
3691 void Being::drawPlayer(Graphics *restrict const graphics,
3692  const int offsetX,
3693  const int offsetY) const restrict2
3694 {
3695  if (!mErased)
3696  {
3697  // getActorX() + offsetX;
3698  const int px = mPixelX - mapTileSize / 2 + offsetX;
3699  // getActorY() + offsetY;
3700  const int py = mPixelY - mapTileSize + offsetY;
3701  if (mHorseInfo != nullptr)
3702  {
3703  HorseOffset &offset = mHorseInfo->offsets[mSpriteDirection];
3704  for_each_horses(mDownHorseSprites)
3705  {
3706  (*it)->draw(graphics,
3707  px + offset.downOffsetX,
3708  py + offset.downOffsetY);
3709  }
3710 
3711  drawBeingCursor(graphics, px, py);
3712  drawPlayerSpriteAt(graphics,
3713  px + offset.riderOffsetX,
3714  py + offset.riderOffsetY);
3715 
3716  for_each_horses(mUpHorseSprites)
3717  {
3718  (*it)->draw(graphics,
3719  px + offset.upOffsetX,
3720  py + offset.upOffsetY);
3721  }
3722  }
3723  else
3724  {
3725  drawBeingCursor(graphics, px, py);
3726  drawPlayerSpriteAt(graphics, px, py);
3727  }
3728  }
3729 }
3730 
3731 void Being::drawBeingCursor(Graphics *const graphics,
3732  const int offsetX,
3733  const int offsetY) const
3734 {
3735  if (mUsedTargetCursor != nullptr)
3736  {
3738  if (mInfo == nullptr)
3739  {
3740  mUsedTargetCursor->draw(graphics,
3741  offsetX - mCursorPaddingX,
3742  offsetY - mCursorPaddingY);
3743  }
3744  else
3745  {
3746  mUsedTargetCursor->draw(graphics,
3747  offsetX + mInfo->getTargetOffsetX() - mCursorPaddingX,
3748  offsetY + mInfo->getTargetOffsetY() - mCursorPaddingY);
3749  }
3750  }
3751 }
3752 
3753 void Being::drawOther(Graphics *restrict const graphics,
3754  const int offsetX,
3755  const int offsetY) const restrict2
3756 {
3757  // getActorX() + offsetX;
3758  const int px = mPixelX - mapTileSize / 2 + offsetX;
3759  // getActorY() + offsetY;
3760  const int py = mPixelY - mapTileSize + offsetY;
3761  drawBeingCursor(graphics, px, py);
3762  drawOtherSpriteAt(graphics, px, py);
3763 }
3764 
3765 void Being::drawNpc(Graphics *restrict const graphics,
3766  const int offsetX,
3767  const int offsetY) const restrict2
3768 {
3769  // getActorX() + offsetX;
3770  const int px = mPixelX - mapTileSize / 2 + offsetX;
3771  // getActorY() + offsetY;
3772  const int py = mPixelY - mapTileSize + offsetY;
3773  drawBeingCursor(graphics, px, py);
3774  drawNpcSpriteAt(graphics, px, py);
3775 }
3776 
3777 void Being::drawMonster(Graphics *restrict const graphics,
3778  const int offsetX,
3779  const int offsetY) const restrict2
3780 {
3781  // getActorX() + offsetX;
3782  const int px = mPixelX - mapTileSize / 2 + offsetX;
3783  // getActorY() + offsetY;
3784  const int py = mPixelY - mapTileSize + offsetY;
3785  drawBeingCursor(graphics, px, py);
3786  drawMonsterSpriteAt(graphics, px, py);
3787 }
3788 
3790  const int offsetX,
3791  const int offsetY) const restrict2
3792 {
3793  // getActorX() + offsetX;
3794  const int px = mPixelX - mapTileSize / 2 + offsetX;
3795  // getActorY() + offsetY;
3796  const int py = mPixelY - mapTileSize + offsetY;
3797  drawBeingCursor(graphics, px, py);
3798  drawHomunculusSpriteAt(graphics, px, py);
3799 }
3800 
3802  const int offsetX,
3803  const int offsetY) const restrict2
3804 {
3805  // getActorX() + offsetX;
3806  const int px = mPixelX - mapTileSize / 2 + offsetX;
3807  // getActorY() + offsetY;
3808  const int py = mPixelY - mapTileSize + offsetY;
3809  drawBeingCursor(graphics, px, py);
3810  drawMercenarySpriteAt(graphics, px, py);
3811 }
3812 
3814  const int offsetX,
3815  const int offsetY) const restrict2
3816 {
3817  // getActorX() + offsetX;
3818  const int px = mPixelX - mapTileSize / 2 + offsetX;
3819  // getActorY() + offsetY;
3820  const int py = mPixelY - mapTileSize + offsetY;
3821  drawBeingCursor(graphics, px, py);
3822  drawElementalSpriteAt(graphics, px, py);
3823 }
3824 
3825 void Being::drawPortal(Graphics *restrict const graphics,
3826  const int offsetX,
3827  const int offsetY) const restrict2
3828 {
3829  // getActorX() + offsetX;
3830  const int px = mPixelX - mapTileSize / 2 + offsetX;
3831  // getActorY() + offsetY;
3832  const int py = mPixelY - mapTileSize + offsetY;
3833  drawPortalSpriteAt(graphics, px, py);
3834 }
3835 
3836 void Being::draw(Graphics *restrict const graphics,
3837  const int offsetX,
3838  const int offsetY) const restrict2
3839 {
3840  switch (mType)
3841  {
3842  case ActorType::Player:
3843  drawPlayer(graphics,
3844  offsetX,
3845  offsetY);
3846  break;
3847  case ActorType::Portal:
3848  drawPortal(graphics,
3849  offsetX,
3850  offsetY);
3851  break;
3852  case ActorType::Homunculus:
3853  drawHomunculus(graphics,
3854  offsetX,
3855  offsetY);
3856  break;
3857  case ActorType::Mercenary:
3858  drawMercenary(graphics,
3859  offsetX,
3860  offsetY);
3861  break;
3862  case ActorType::Elemental:
3863  drawElemental(graphics,
3864  offsetX,
3865  offsetY);
3866  break;
3867  case ActorType::Monster:
3868  drawMonster(graphics,
3869  offsetX,
3870  offsetY);
3871  break;
3872  case ActorType::Npc:
3873  drawNpc(graphics,
3874  offsetX,
3875  offsetY);
3876  break;
3877  case ActorType::Pet:
3878  case ActorType::SkillUnit:
3879  case ActorType::Unknown:
3880  case ActorType::FloorItem:
3881  case ActorType::Avatar:
3882  default:
3883  drawOther(graphics,
3884  offsetX,
3885  offsetY);
3886  break;
3887  }
3888 }
3889 
3891  const int posX,
3892  const int posY) const restrict2
3893 {
3894  const int sz = CompoundSprite::getNumberOfLayers();
3895  for (int f = 0; f < sz; f ++)
3896  {
3897  const int rSprite = mSpriteHide[mSpriteRemap[f]];
3898  if (rSprite == 1)
3899  continue;
3900 
3901  Sprite *restrict const sprite = mSprites[mSpriteRemap[f]];
3902  if (sprite != nullptr)
3903  {
3904  sprite->setAlpha(mAlpha);
3905  sprite->draw(graphics, posX, posY);
3906  }
3907  }
3908 }
3909 
3911  const int posX,
3912  const int posY) const restrict2
3913 {
3914  const size_t sz = mSprites.size();
3915  for (size_t f = 0; f < sz; f ++)
3916  {
3917  const int rSprite = mSpriteHide[mSpriteRemap[f]];
3918  if (rSprite == 1)
3919  continue;
3920 
3921  const Sprite *restrict const sprite = mSprites[mSpriteRemap[f]];
3922  if (sprite != nullptr)
3923  sprite->draw(graphics, posX, posY);
3924  }
3925 }
3926 
3927 void Being::drawBasic(Graphics *restrict const graphics,
3928  const int x,
3929  const int y) const restrict2
3930 {
3931  drawCompound(graphics, x, y);
3932 }
3933 
3934 void Being::drawCompound(Graphics *const graphics,
3935  const int posX,
3936  const int posY) const
3937 {
3938  FUNC_BLOCK("CompoundSprite::draw", 1)
3939  if (mNeedsRedraw)
3940  updateImages();
3941 
3942  if (mSprites.empty()) // Nothing to draw
3943  return;
3944 
3945  if (mAlpha == 1.0F && (mImage != nullptr))
3946  {
3947  graphics->drawImage(mImage,
3948  posX + mOffsetX,
3949  posY + mOffsetY);
3950  }
3951  else if ((mAlpha != 0.0F) && (mAlphaImage != nullptr))
3952  {
3954  graphics->drawImage(mAlphaImage,
3955  posX + mOffsetX,
3956  posY + mOffsetY);
3957  }
3958  else
3959  {
3960  Being::drawPlayerSprites(graphics, posX, posY);
3961  }
3962 }
3963 
3965  const int x,
3966  const int y) const restrict2
3967 {
3968  drawCompound(graphics, x, y);
3969 
3970  if (mShowOwnHP &&
3971  (mInfo != nullptr) &&
3972  localPlayer == this &&
3973  mAction != BeingAction::DEAD)
3974  {
3975  drawHpBar(graphics,
3978  0,
3981  x - 50 + mapTileSize / 2 + mInfo->getHpBarOffsetX(),
3982  y + mapTileSize - 6 + mInfo->getHpBarOffsetY(),
3983  2 * 50,
3984  4);
3985  }
3986 }
3987 
3989  const int x,
3990  const int y) const restrict2
3991 {
3992  CompoundSprite::drawSimple(graphics, x, y);
3993 }
3994 
3996  const int x,
3997  const int y) const restrict2
3998 {
3999  drawCompound(graphics, x, y);
4000 }
4001 
4003  const int x,
4004  const int y) const restrict2
4005 {
4006  if (mHighlightMonsterAttackRange &&
4007  mType == ActorType::Monster &&
4008  mAction != BeingAction::DEAD)
4009  {
4010  if (userPalette == nullptr)
4011  {
4012  CompoundSprite::drawSimple(graphics, x, y);
4013  return;
4014  }
4015 
4016  int attackRange;
4017  if (mAttackRange != 0)
4018  attackRange = mapTileSize * mAttackRange;
4019  else
4020  attackRange = mapTileSize;
4021 
4022  graphics->setColor(userPalette->getColorWithAlpha(
4024 
4025  graphics->fillRectangle(Rect(
4026  x - attackRange, y - attackRange,
4027  2 * attackRange + mapTileSize, 2 * attackRange + mapTileSize));
4028  }
4029 
4030  CompoundSprite::drawSimple(graphics, x, y);
4031 
4032  if (mShowMobHP &&
4033  (mInfo != nullptr) &&
4034  (localPlayer != nullptr) &&
4035  localPlayer->getTarget() == this &&
4036  mType == ActorType::Monster)
4037  {
4038  // show hp bar here
4039  int maxHP = mMaxHP;
4040  if (maxHP == 0)
4041  maxHP = mInfo->getMaxHP();
4042 
4043  drawHpBar(graphics,
4044  maxHP,
4045  mHP,
4046  mDamageTaken,
4049  x - 50 + mapTileSize / 2 + mInfo->getHpBarOffsetX(),
4050  y + mapTileSize - 6 + mInfo->getHpBarOffsetY(),
4051  2 * 50,
4052  4);
4053  }
4054 }
4055 
4057  const int x,
4058  const int y) const restrict2
4059 {
4060  if (mHighlightMonsterAttackRange &&
4061  mAction != BeingAction::DEAD)
4062  {
4063  if (userPalette == nullptr)
4064  {
4065  CompoundSprite::drawSimple(graphics, x, y);
4066  return;
4067  }
4068 
4069  int attackRange;
4070  if (mAttackRange != 0)
4071  attackRange = mapTileSize * mAttackRange;
4072  else
4073  attackRange = mapTileSize;
4074 
4075  graphics->setColor(userPalette->getColorWithAlpha(
4077 
4078  graphics->fillRectangle(Rect(
4079  x - attackRange, y - attackRange,
4080  2 * attackRange + mapTileSize, 2 * attackRange + mapTileSize));
4081  }
4082 
4083  CompoundSprite::drawSimple(graphics, x, y);
4084 
4085  if (mShowMobHP &&
4086  (mInfo != nullptr))
4087  {
4089  if ((info != nullptr) &&
4090  mId == info->id)
4091  {
4092  // show hp bar here
4094  if (maxHP == 0)
4095  maxHP = mInfo->getMaxHP();
4096 
4097  drawHpBar(graphics,
4098  maxHP,
4100  mDamageTaken,
4103  x - 50 + mapTileSize / 2 + mInfo->getHpBarOffsetX(),
4104  y + mapTileSize - 6 + mInfo->getHpBarOffsetY(),
4105  2 * 50,
4106  4);
4107  }
4108  }
4109 }
4110 
4112  const int x,
4113  const int y) const restrict2
4114 {
4115  if (mHighlightMonsterAttackRange &&
4116  mAction != BeingAction::DEAD)
4117  {
4118  if (userPalette == nullptr)
4119  {
4120  CompoundSprite::drawSimple(graphics, x, y);
4121  return;
4122  }
4123 
4124  int attackRange;
4125  if (mAttackRange != 0)
4126  attackRange = mapTileSize * mAttackRange;
4127  else
4128  attackRange = mapTileSize;
4129 
4130  graphics->setColor(userPalette->getColorWithAlpha(
4132 
4133  graphics->fillRectangle(Rect(
4134  x - attackRange, y - attackRange,
4135  2 * attackRange + mapTileSize, 2 * attackRange + mapTileSize));
4136  }
4137 
4138  CompoundSprite::drawSimple(graphics, x, y);
4139 
4140  if (mShowMobHP &&
4141  (mInfo != nullptr))
4142  {
4143  const MercenaryInfo *const info = PlayerInfo::getMercenary();
4144  if ((info != nullptr) &&
4145  mId == info->id)
4146  {
4147  // show hp bar here
4149  if (maxHP == 0)
4150  maxHP = mInfo->getMaxHP();
4151 
4152  drawHpBar(graphics,
4153  maxHP,
4155  mDamageTaken,
4158  x - 50 + mapTileSize / 2 + mInfo->getHpBarOffsetX(),
4159  y + mapTileSize - 6 + mInfo->getHpBarOffsetY(),
4160  2 * 50,
4161  4);
4162  }
4163  }
4164 }
4165 
4167  const int x,
4168  const int y) const restrict2
4169 {
4170  if (mHighlightMonsterAttackRange &&
4171  mAction != BeingAction::DEAD)
4172  {
4173  if (userPalette == nullptr)
4174  {
4175  CompoundSprite::drawSimple(graphics, x, y);
4176  return;
4177  }
4178 
4179  int attackRange;
4180  if (mAttackRange != 0)
4181  attackRange = mapTileSize * mAttackRange;
4182  else
4183  attackRange = mapTileSize;
4184 
4185  graphics->setColor(userPalette->getColorWithAlpha(
4187 
4188  graphics->fillRectangle(Rect(
4189  x - attackRange, y - attackRange,
4190  2 * attackRange + mapTileSize, 2 * attackRange + mapTileSize));
4191  }
4192 
4193  CompoundSprite::drawSimple(graphics, x, y);
4194 
4195  if (mShowMobHP &&
4196  (mInfo != nullptr))
4197  {
4198  if (mId == PlayerInfo::getElementalId())
4199  {
4200  // show hp bar here
4202  if (maxHP == 0)
4203  maxHP = mInfo->getMaxHP();
4204 
4205  drawHpBar(graphics,
4206  maxHP,
4208  mDamageTaken,
4211  x - 50 + mapTileSize / 2 + mInfo->getHpBarOffsetX(),
4212  y + mapTileSize - 6 + mInfo->getHpBarOffsetY(),
4213  2 * 50,
4214  4);
4215  }
4216  }
4217 }
4218 
4220  const int x,
4221  const int y) const restrict2
4222 {
4223  if (mHighlightMapPortals &&
4224  (mMap != nullptr) &&
4225  !mMap->getHasWarps())
4226  {
4227  if (userPalette == nullptr)
4228  {
4229  CompoundSprite::drawSimple(graphics, x, y);
4230  return;
4231  }
4232 
4233  graphics->setColor(userPalette->
4234  getColorWithAlpha(UserColorId::PORTAL_HIGHLIGHT));
4235 
4236  graphics->fillRectangle(Rect(x, y,
4238 
4239  if (mDrawHotKeys && !mName.empty())
4240  {
4241  const Color &color = userPalette->getColor(UserColorId::BEING,
4242  255U);
4243  gui->getFont()->drawString(graphics, color, color, mName, x, y);
4244  }
4245  }
4246 
4247  CompoundSprite::drawSimple(graphics, x, y);
4248 }
4249 
4250 void Being::drawHpBar(Graphics *restrict const graphics,
4251  const int maxHP,
4252  const int hp,
4253  const int damage,
4254  const UserColorIdT color1,
4255  const UserColorIdT color2,
4256  const int x,
4257  const int y,
4258  const int width,
4259  const int height) const restrict2
4260 {
4261  if (maxHP <= 0 || (userPalette == nullptr))
4262  return;
4263 
4264  float p;
4265 
4266  if (hp != 0)
4267  {
4268  p = static_cast<float>(maxHP) / static_cast<float>(hp);
4269  }
4270  else if (maxHP != damage)
4271  {
4272  p = static_cast<float>(maxHP)
4273  / static_cast<float>(maxHP - damage);
4274  }
4275  else
4276  {
4277  p = 1;
4278  }
4279 
4280  if (p <= 0 || p > width)
4281  return;
4282 
4283  const int dx = static_cast<int>(static_cast<float>(width) / p);
4284 
4285 #ifdef TMWA_SUPPORT
4286  if (!serverFeatures->haveServerHp())
4287  { // old servers
4288  if ((damage == 0 && (this != localPlayer || hp == maxHP))
4289  || (hp == 0 && maxHP == damage))
4290  {
4291  graphics->setColor(userPalette->getColorWithAlpha(color1));
4292  graphics->fillRectangle(Rect(
4293  x, y, dx, height));
4294  return;
4295  }
4296  else if (width - dx <= 0)
4297  {
4298  graphics->setColor(userPalette->getColorWithAlpha(color2));
4299  graphics->fillRectangle(Rect(
4300  x, y, width, height));
4301  return;
4302  }
4303  }
4304  else
4305 #endif // TMWA_SUPPORT
4306  {
4307  if (hp == maxHP)
4308  {
4309  graphics->setColor(userPalette->getColorWithAlpha(color1));
4310  graphics->fillRectangle(Rect(
4311  x, y, dx, height));
4312  return;
4313  }
4314  else if (width - dx <= 0)
4315  {
4316  graphics->setColor(userPalette->getColorWithAlpha(color2));
4317  graphics->fillRectangle(Rect(
4318  x, y, width, height));
4319  return;
4320  }
4321  }
4322 
4323  graphics->setColor(userPalette->getColorWithAlpha(color1));
4324  graphics->fillRectangle(Rect(
4325  x, y, dx, height));
4326 
4327  graphics->setColor(userPalette->getColorWithAlpha(color2));
4328  graphics->fillRectangle(Rect(x + dx, y, width - dx, height));
4329 }
4330 
4331 void Being::setHP(const int hp) restrict2
4332 {
4333  mHP = hp;
4334  if (mMaxHP < mHP)
4335  mMaxHP = mHP;
4336  if (mType == ActorType::Monster)
4337  updatePercentHP();
4338 }
4339 
4340 void Being::setMaxHP(const int hp) restrict2
4341 {
4342  mMaxHP = hp;
4343  if (mMaxHP < mHP)
4344  mMaxHP = mHP;
4345 }
4346 
4348 {
4349  mMoveTime = 0;
4350  mAttackTime = 0;
4351  mTalkTime = 0;
4352  mOtherTime = 0;
4353  mTestTime = cur_time;
4354 }
4355 
4357 {
4358  if (!mEnableReorderSprites)
4359  return;
4360 
4361 // logger->log("recalcSpritesOrder");
4362  const size_t sz = mSprites.size();
4363  if (sz < 1)
4364  return;
4365 
4366  STD_VECTOR<int> slotRemap;
4367  IntMap itemSlotRemap;
4368 
4369  STD_VECTOR<int>::iterator it;
4370  int oldHide[20];
4371  bool updatedSprite[20];
4372  int dir = mSpriteDirection;
4373  if (dir < 0 || dir >= 9)
4374  dir = 0;
4375  // hack for allow different logic in dead player
4376  if (mAction == BeingAction::DEAD)
4377  dir = 9;
4378 
4379  const unsigned int hairSlot = charServerHandler->hairSprite();
4380 
4381  for (size_t slot = sz; slot < 20; slot ++)
4382  {
4383  oldHide[slot] = 0;
4384  updatedSprite[slot] = false;
4385  }
4386 
4387  for (size_t slot = 0; slot < sz; slot ++)
4388  {
4389  oldHide[slot] = mSpriteHide[slot];
4390  mSpriteHide[slot] = 0;
4391  updatedSprite[slot] = false;
4392  }
4393 
4394  size_t spriteIdSize = mSlots.size();
4395  if (reportTrue(spriteIdSize > 20))
4396  spriteIdSize = 20;
4397 
4398  for (size_t slot = 0; slot < sz; slot ++)
4399  {
4400  slotRemap.push_back(CAST_S32(slot));
4401 
4402  if (spriteIdSize <= slot)
4403  continue;
4404 
4405  const int id = mSlots[slot].spriteId;
4406  if (id == 0)
4407  continue;
4408 
4409  const ItemInfo &info = ItemDB::get(id);
4410 
4411  if (info.isRemoveSprites())
4412  {
4413  const SpriteToItemMap *restrict const spriteToItems
4414  = info.getSpriteToItemReplaceMap(dir);
4415 
4416  if (spriteToItems != nullptr)
4417  {
4418  FOR_EACHP (SpriteToItemMapCIter, itr, spriteToItems)
4419  {
4420  const int remSlot = itr->first;
4421  const IntMap &restrict itemReplacer = itr->second;
4422  if (remSlot >= 0)
4423  { // slot known
4424  if (CAST_U32(remSlot) >= spriteIdSize)
4425  continue;
4426  if (itemReplacer.empty())
4427  {
4428  mSpriteHide[remSlot] = 1;
4429  }
4430  else if (mSpriteHide[remSlot] != 1)
4431  {
4432  IntMapCIter repIt = itemReplacer.find(
4433  mSlots[remSlot].spriteId);
4434  if (repIt == itemReplacer.end())
4435  {
4436  repIt = itemReplacer.find(0);
4437  if (repIt == itemReplacer.end() ||
4438  repIt->second == 0)
4439  {
4440  repIt = itemReplacer.end();
4441  }
4442  }
4443  if (repIt != itemReplacer.end())
4444  {
4445  mSpriteHide[remSlot] = repIt->second;
4446  if (repIt->second != 1)
4447  {
4448  if (CAST_U32(remSlot)
4449  != hairSlot)
4450  {
4451  setTempSprite(remSlot,
4452  repIt->second);
4453  }
4454  else
4455  {
4456  setHairTempSprite(remSlot,
4457  repIt->second);
4458  }
4459  updatedSprite[remSlot] = true;
4460  }
4461  }
4462  }
4463  }
4464  else
4465  { // slot unknown. Search for real slot, this can be slow
4466  FOR_EACH (IntMapCIter, repIt, itemReplacer)
4467  {
4468  for (unsigned int slot2 = 0; slot2 < sz; slot2 ++)
4469  {
4470  if (mSlots[slot2].spriteId == repIt->first)
4471  {
4472  mSpriteHide[slot2] = repIt->second;
4473  if (repIt->second != 1)
4474  {
4475  if (slot2 != hairSlot)
4476  {
4477  setTempSprite(slot2,
4478  repIt->second);
4479  }
4480  else
4481  {
4482  setHairTempSprite(slot2,
4483  repIt->second);
4484  }
4485  updatedSprite[slot2] = true;
4486  }
4487  }
4488  }
4489  }
4490  }
4491  }
4492  }
4493  }
4494 
4495  if (info.mDrawBefore[dir] > 0)
4496  {
4497  const int id2 = mSlots[info.mDrawBefore[dir]].spriteId;
4498  if (itemSlotRemap.find(id2) != itemSlotRemap.end())
4499  {
4500 // logger->log("found duplicate (before)");
4501  const ItemInfo &info2 = ItemDB::get(id2);
4502  if (info.mDrawPriority[dir] < info2.mDrawPriority[dir])
4503  {
4504 // logger->log("old more priority");
4505  continue;
4506  }
4507  else
4508  {
4509 // logger->log("new more priority");
4510  itemSlotRemap.erase(id2);
4511  }
4512  }
4513 
4514  itemSlotRemap[id] = -info.mDrawBefore[dir];
4515  }
4516  else if (info.mDrawAfter[dir] > 0)
4517  {
4518  const int id2 = mSlots[info.mDrawAfter[dir]].spriteId;
4519  if (itemSlotRemap.find(id2) != itemSlotRemap.end())
4520  {
4521  const ItemInfo &info2 = ItemDB::get(id2);
4522  if (info.mDrawPriority[dir] < info2.mDrawPriority[dir])
4523  {
4524 // logger->log("old more priority");
4525  continue;
4526  }
4527  else
4528  {
4529 // logger->log("new more priority");
4530  itemSlotRemap.erase(id2);
4531  }
4532  }
4533 
4534  itemSlotRemap[id] = info.mDrawAfter[dir];
4535 // logger->log("item slot->slot %d %d->%d", id, slot, itemSlotRemap[id]);
4536  }
4537  }
4538 // logger->log("preparation end");
4539 
4540  int lastRemap = 0;
4541  unsigned cnt = 0;
4542 
4543  while (cnt < 15 && lastRemap >= 0)
4544  {
4545  lastRemap = -1;
4546  cnt ++;
4547 // logger->log("iteration");
4548 
4549  for (unsigned int slot0 = 0; slot0 < sz; slot0 ++)
4550  {
4551  const int slot = searchSlotValue(slotRemap, slot0);
4552  const int val = slotRemap.at(slot);
4553  int id = 0;
4554 
4555  if (CAST_S32(spriteIdSize) > val)
4556  id = mSlots[val].spriteId;
4557 
4558  int idx = -1;
4559  int idx1 = -1;
4560 // logger->log("item %d, id=%d", slot, id);
4561  int reorder = 0;
4562  const IntMapCIter orderIt = itemSlotRemap.find(id);
4563  if (orderIt != itemSlotRemap.end())
4564  reorder = orderIt->second;
4565 
4566  if (reorder < 0)
4567  {
4568 // logger->log("move item %d before %d", slot, -reorder);
4569  searchSlotValueItr(it, idx, slotRemap, -reorder);
4570  if (it == slotRemap.end())
4571  return;
4572  searchSlotValueItr(it, idx1, slotRemap, val);
4573  if (it == slotRemap.end())
4574  return;
4575  lastRemap = idx1;
4576  if (idx1 + 1 != idx)
4577  {
4578  slotRemap.erase(it);
4579  searchSlotValueItr(it, idx, slotRemap, -reorder);
4580  slotRemap.insert(it, val);
4581  }
4582  }
4583  else if (reorder > 0)
4584  {
4585 // logger->log("move item %d after %d", slot, reorder);
4586  searchSlotValueItr(it, idx, slotRemap, reorder);
4587  searchSlotValueItr(it, idx1, slotRemap, val);
4588  if (it == slotRemap.end())
4589  return;
4590  lastRemap = idx1;
4591  if (idx1 != idx + 1)
4592  {
4593  slotRemap.erase(it);
4594  searchSlotValueItr(it, idx, slotRemap, reorder);
4595  if (it != slotRemap.end())
4596  {
4597  ++ it;
4598  if (it != slotRemap.end())
4599  slotRemap.insert(it, val);
4600  else
4601  slotRemap.push_back(val);
4602  }
4603  else
4604  {
4605  slotRemap.push_back(val);
4606  }
4607  }
4608  }
4609  }
4610  }
4611 
4612 // logger->log("after remap");
4613  for (unsigned int slot = 0; slot < sz; slot ++)
4614  {
4615  mSpriteRemap[slot] = slotRemap[slot];
4616  if (mSpriteHide[slot] == 0)
4617  {
4618  if (oldHide[slot] != 0 && oldHide[slot] != 1)
4619  {
4620  const BeingSlot &beingSlot = mSlots[slot];
4621  const int id = beingSlot.spriteId;
4622  if (id == 0)
4623  continue;
4624 
4625  updatedSprite[slot] = true;
4626  setTempSprite(slot,
4627  id);
4628  }
4629  }
4630  }
4631  for (size_t slot = 0; slot < spriteIdSize; slot ++)
4632  {
4633  if (mSpriteHide[slot] == 0)
4634  {
4635  const BeingSlot &beingSlot = mSlots[slot];
4636  const int id = beingSlot.spriteId;
4637  if (updatedSprite[slot] == false &&
4638  mSpriteDraw[slot] != id)
4639  {
4640  setTempSprite(static_cast<unsigned int>(slot),
4641  id);
4642  }
4643  }
4644  }
4645 }
4646 
4647 int Being::searchSlotValue(const STD_VECTOR<int> &restrict slotRemap,
4648  const int val) const restrict2
4649 {
4650  const size_t sz = mSprites.size();
4651  for (size_t slot = 0; slot < sz; slot ++)
4652  {
4653  if (slotRemap[slot] == val)
4654  return CAST_S32(slot);
4655  }
4656  return CompoundSprite::getNumberOfLayers() - 1;
4657 }
4658 
4659 void Being::searchSlotValueItr(STD_VECTOR<int>::iterator &restrict it,
4660  int &restrict idx,
4661  STD_VECTOR<int> &restrict slotRemap,
4662  const int val)
4663 {
4664 // logger->log("searching %d", val);
4665  it = slotRemap.begin();
4666  const STD_VECTOR<int>::iterator it_end = slotRemap.end();
4667  idx = 0;
4668  while (it != it_end)
4669  {
4670 // logger->log("testing %d", *it);
4671  if (*it == val)
4672  {
4673 // logger->log("found at %d", idx);
4674  return;
4675  }
4676  ++ it;
4677  idx ++;
4678  }
4679 // logger->log("not found");
4680  idx = -1;
4681 }
4682 
4683 void Being::updateHit(const int amount) restrict2
4684 {
4685  if (amount > 0)
4686  {
4687  if ((mMinHit == 0) || amount < mMinHit)
4688  mMinHit = amount;
4689  if (amount != mCriticalHit && ((mMaxHit == 0) || amount > mMaxHit))
4690  mMaxHit = amount;
4691  }
4692 }
4693 
4695 {
4696  Equipment *restrict const eq = new Equipment;
4697  Equipment::Backend *restrict const bk = new BeingEquipBackend(this);
4698  eq->setBackend(bk);
4699  return eq;
4700 }
4701 
4703 {
4704  const size_t sz = mSlots.size();
4705 
4706  for (size_t f = 0; f < sz; f ++)
4707  {
4708  if (id == mSlots[f].spriteId)
4709  {
4710  unSetSprite(CAST_U32(f));
4711  break;
4712  }
4713  }
4714 }
4715 
4717 {
4719  beingInfoCache.clear();
4720 }
4721 
4723 {
4724  if (mGotComment || mName.empty())
4725  return;
4726 
4727  mGotComment = true;
4729 }
4730 
4731 std::string Being::loadComment(const std::string &restrict name,
4732  const ActorTypeT &restrict type)
4733 {
4734  std::string str;
4735  switch (type)
4736  {
4737  case ActorType::Player:
4738  str = settings.usersDir;
4739  break;
4740  case ActorType::Npc:
4741  str = settings.npcsDir;
4742  break;
4743  case ActorType::Unknown:
4744  case ActorType::Monster:
4745  case ActorType::FloorItem:
4746  case ActorType::Portal:
4747  case ActorType::Avatar:
4748  case ActorType::Mercenary:
4749  case ActorType::Homunculus:
4750  case ActorType::Pet:
4751  case ActorType::SkillUnit:
4752  case ActorType::Elemental:
4753  default:
4754  return "";
4755  }
4756 
4757  str = pathJoin(str, stringToHexPath(name), "comment.txt");
4758  if (Files::existsLocal(str))
4759  {
4760  StringVect lines;
4761  Files::loadTextFileLocal(str, lines);
4762  if (lines.size() >= 2)
4763  return lines[1];
4764  }
4765  return std::string();
4766 }
4767 
4768 void Being::saveComment(const std::string &restrict name,
4769  const std::string &restrict comment,
4770  const ActorTypeT &restrict type)
4771 {
4772  std::string dir;
4773  switch (type)
4774  {
4775  case ActorType::Player:
4776  dir = settings.usersDir;
4777  break;
4778  case ActorType::Npc:
4779  dir = settings.npcsDir;
4780  break;
4781  case ActorType::Monster:
4782  case ActorType::FloorItem:
4783  case ActorType::Portal:
4784  case ActorType::Avatar:
4785  case ActorType::Unknown:
4786  case ActorType::Pet:
4787  case ActorType::Mercenary:
4788  case ActorType::Homunculus:
4789  case ActorType::SkillUnit:
4790  case ActorType::Elemental:
4791  default:
4792  return;
4793  }
4794  dir = pathJoin(dir, stringToHexPath(name));
4795  Files::saveTextFile(dir,
4796  "comment.txt",
4797  (name + "\n").append(comment));
4798 }
4799 
4800 void Being::setState(const uint8_t state) restrict2
4801 {
4802  const bool shop = ((state & BeingFlag::SHOP) != 0);
4803  const bool away = ((state & BeingFlag::AWAY) != 0);
4804  const bool inactive = ((state & BeingFlag::INACTIVE) != 0);
4805  const bool needUpdate = (shop != mShop || away != mAway
4806  || inactive != mInactive);
4807 
4809  mShop = shop;
4810  mAway = away;
4811  mInactive = inactive;
4812  updateAwayEffect();
4813  showShopBadge(mShop);
4814  showInactiveBadge(mInactive);
4815  showAwayBadge(mAway);
4816 
4817  if (needUpdate)
4818  {
4819  if (shop || away || inactive)
4820  mAdvanced = true;
4821  updateName();
4822  addToCache();
4823  }
4824 }
4825 
4826 void Being::setEmote(const uint8_t emotion,
4827  const int emote_time) restrict2
4828 {
4829  if ((emotion & BeingFlag::SPECIAL) == BeingFlag::SPECIAL)
4830  {
4831  setState(emotion);
4832  mAdvanced = true;
4833  }
4834  else
4835  {
4836  const int emotionIndex = emotion - 1;
4837  if (emotionIndex >= 0 && emotionIndex <= EmoteDB::getLast())
4838  {
4839  delete2(mEmotionSprite)
4840  const EmoteInfo *const info = EmoteDB::get2(emotionIndex, true);
4841  if (info != nullptr)
4842  {
4843  const EmoteSprite *restrict const sprite =
4844  info->sprites.front();
4845  if (sprite != nullptr)
4846  {
4847  mEmotionSprite = AnimatedSprite::clone(sprite->sprite);
4848  if (mEmotionSprite != nullptr)
4849  mEmotionTime = info->time;
4850  else
4851  mEmotionTime = emote_time;
4852  }
4853  const int effectId = info->effectId;
4854  if (effectId >= 0)
4855  {
4856  effectManager->trigger(effectId, this, 0);
4857  }
4858  }
4859  }
4860 
4861  if (mEmotionSprite != nullptr)
4862  {
4863  mEmotionSprite->play(mSpriteAction);
4864  mEmotionSprite->setSpriteDirection(mSpriteDirection);
4865  }
4866  else
4867  {
4868  mEmotionTime = 0;
4869  }
4870  }
4871 }
4872 
4874 {
4875  if (mMaxHP == 0)
4876  return;
4877  BLOCK_START("Being::updatePercentHP")
4878  if (mHP != 0)
4879  {
4880  const unsigned num = mHP * 100 / mMaxHP;
4881  if (num != mNumber)
4882  {
4883  mNumber = num;
4884  if (updateNumber(mNumber))
4885  setAction(mAction, 0);
4886  }
4887  }
4888  BLOCK_END("Being::updatePercentHP")
4889 }
4890 
4891 int Being::getSpriteID(const int slot) const restrict2
4892 {
4893  if (slot < 0 || CAST_SIZE(slot) >= mSlots.size())
4894  return -1;
4895 
4896  return mSlots[slot].spriteId;
4897 }
4898 
4899 const BeingSlot &Being::getSpriteSlot(const int slot) const restrict2
4900 {
4901  if (slot < 0 || CAST_SIZE(slot) >= mSlots.size())
4902  return *emptyBeingSlot;
4903 
4904  return mSlots[slot];
4905 }
4906 
4908 {
4909  if (slot < 0 || CAST_SIZE(slot) >= mSlots.size())
4910  return ItemColor_one;
4911 
4912  return mSlots[slot].colorId;
4913 }
4914 
4916 {
4918 }
4919 
4921 {
4923 }
4924 
4925 void Being::addSpecialEffect(const int effect) restrict2
4926 {
4927  if ((effectManager != nullptr) &&
4929  (mSpecialParticle == nullptr) &&
4930  effect != -1)
4931  {
4932  mSpecialParticle = effectManager->triggerReturn(effect,
4933  this,
4934  0);
4935  }
4936 }
4937 
4939 {
4940  if ((effectManager != nullptr) && (mSpecialParticle != nullptr))
4941  {
4943  mSpecialParticle = nullptr;
4944  }
4946 }
4947 
4949 {
4950  if (mAway)
4951  addAfkEffect();
4952  else
4953  removeAfkEffect();
4954 }
4955 
4956 void Being::addEffect(const std::string &restrict name) restrict2
4957 {
4958  delete mAnimationEffect;
4959  mAnimationEffect = AnimatedSprite::load(
4960  paths.getStringValue("sprites") + name,
4961  0);
4962 }
4963 
4964 void Being::playSfx(const SoundInfo &sound,
4965  Being *const being,
4966  const bool main,
4967  const int x, const int y) const restrict2
4968 {
4969  BLOCK_START("Being::playSfx")
4970 
4971  if (being != nullptr)
4972  {
4973  // here need add timer and delay sound
4974  const int time = tick_time;
4975  if (main)
4976  {
4977  being->mNextSound.sound = nullptr;
4978  being->mNextSound.time = time + sound.delay;
4979  soundManager.playSfx(sound.sound, x, y);
4980  }
4981  else if (mNextSound.time <= time)
4982  { // old event sound time is gone. we can play new sound
4983  being->mNextSound.sound = nullptr;
4984  being->mNextSound.time = time + sound.delay;
4985  soundManager.playSfx(sound.sound, x, y);
4986  }
4987  else
4988  { // old event sound in progress. need save sound and wait
4989  being->mNextSound.sound = &sound;
4990  being->mNextSound.x = x;
4991  being->mNextSound.y = y;
4992  }
4993  }
4994  else
4995  {
4996  soundManager.playSfx(sound.sound, x, y);
4997  }
4998  BLOCK_END("Being::playSfx")
4999 }
5000 
5001 void Being::setLook(const uint16_t look) restrict2
5002 {
5003  if (mType == ActorType::Player)
5004  setSubtype(mSubType, look);
5005 }
5006 
5007 void Being::setTileCoords(const int x, const int y) restrict2
5008 {
5009  mX = x;
5010  mY = y;
5011  if (mMap != nullptr)
5012  {
5013  mPixelOffsetY = 0;
5014  mFixedOffsetY = mPixelOffsetY;
5015  mOldHeight = mMap->getHeightOffset(mX, mY);
5016  mNeedPosUpdate = true;
5017  }
5018 }
5019 
5021 {
5022  mCastEndTime = 0;
5023  delete2(mCastingEffect)
5024  ActorSprite::setMap(map);
5025  if (mMap != nullptr)
5026  {
5027  mPixelOffsetY = mMap->getHeightOffset(mX, mY);
5028  mFixedOffsetY = mPixelOffsetY;
5029  mOldHeight = 0;
5030  mNeedPosUpdate = true;
5031  }
5032 }
5033 
5035 {
5037  delete (*it).second;
5038  mSpriteParticles.clear();
5039 }
5040 
5041 void Being::addItemParticles(const int id,
5042  const SpriteDisplay &restrict display) restrict2
5043 {
5044  const SpriteParticleInfoIter it = mSpriteParticles.find(id);
5045  ParticleInfo *restrict pi = nullptr;
5046  if (it == mSpriteParticles.end())
5047  {
5048  pi = new ParticleInfo;
5049  mSpriteParticles[id] = pi;
5050  }
5051  else
5052  {
5053  pi = (*it).second;
5054  }
5055 
5056  if ((pi == nullptr) || !pi->particles.empty())
5057  return;
5058 
5059  // setup particle effects
5061  (particleEngine != nullptr))
5062  {
5063  FOR_EACH (StringVectCIter, itr, display.particles)
5064  {
5065  Particle *const p = particleEngine->addEffect(*itr, 0, 0, 0);
5066  controlCustomParticle(p);
5067  pi->files.push_back(*itr);
5068  pi->particles.push_back(p);
5069  }
5070  }
5071  else
5072  {
5073  FOR_EACH (StringVectCIter, itr, display.particles)
5074  pi->files.push_back(*itr);
5075  }
5076 }
5077 
5079  const SpriteDisplay &restrict display,
5080  const CardsList &cards) restrict2
5081 {
5082  const SpriteParticleInfoIter it = mSpriteParticles.find(id);
5083  ParticleInfo *restrict pi = nullptr;
5084  if (it == mSpriteParticles.end())
5085  {
5086  pi = new ParticleInfo;
5087  mSpriteParticles[id] = pi;
5088  }
5089  else
5090  {
5091  pi = (*it).second;
5092  }
5093 
5094  if ((pi == nullptr) || !pi->particles.empty())
5095  return;
5096 
5097  // setup particle effects
5099  (particleEngine != nullptr))
5100  {
5101  FOR_EACH (StringVectCIter, itr, display.particles)
5102  {
5103  Particle *const p = particleEngine->addEffect(*itr, 0, 0, 0);
5104  controlCustomParticle(p);
5105  pi->files.push_back(*itr);
5106  pi->particles.push_back(p);
5107  }
5108  for (int f = 0; f < maxCards; f ++)
5109  {
5110  const int cardId = cards.cards[f];
5111  if (!Item::isItem(cardId))
5112  continue;
5113  const ItemInfo &info = ItemDB::get(cardId);
5114  const SpriteDisplay &restrict display2 = info.getDisplay();
5115  FOR_EACH (StringVectCIter, itr, display2.particles)
5116  {
5117  Particle *const p = particleEngine->addEffect(*itr, 0, 0, 0);
5118  controlCustomParticle(p);
5119  pi->files.push_back(*itr);
5120  pi->particles.push_back(p);
5121  }
5122  }
5123  }
5124  else
5125  {
5126  FOR_EACH (StringVectCIter, itr, display.particles)
5127  {
5128  pi->files.push_back(*itr);
5129  }
5130  for (int f = 0; f < maxCards; f ++)
5131  {
5132  const int cardId = cards.cards[f];
5133  if (!Item::isItem(cardId))
5134  continue;
5135  const ItemInfo &info = ItemDB::get(cardId);
5136  const SpriteDisplay &restrict display2 = info.getDisplay();
5137  FOR_EACH (StringVectCIter, itr, display2.particles)
5138  {
5139  pi->files.push_back(*itr);
5140  }
5141  }
5142  }
5143 }
5144 
5146 {
5147  const SpriteParticleInfoIter it = mSpriteParticles.find(id);
5148  if (it == mSpriteParticles.end())
5149  return;
5150  ParticleInfo *restrict const pi = (*it).second;
5151  if (pi != nullptr)
5152  {
5153  FOR_EACH (STD_VECTOR<Particle*>::const_iterator, itp, pi->particles)
5154  mChildParticleEffects.removeLocally(*itp);
5155  delete pi;
5156  }
5157  mSpriteParticles.erase(it);
5158 }
5159 
5161 {
5163  {
5164  ParticleInfo *restrict const pi = (*it).second;
5165  if ((pi != nullptr) && !pi->files.empty())
5166  {
5167  FOR_EACH (STD_VECTOR<Particle*>::const_iterator,
5168  itp, pi->particles)
5169  {
5171  }
5172 
5173  FOR_EACH (STD_VECTOR<std::string>::const_iterator, str, pi->files)
5174  {
5175  Particle *const p = particleEngine->addEffect(
5176  *str, 0, 0, 0);
5178  pi->particles.push_back(p);
5179  }
5180  }
5181  }
5182 }
5183 
5184 void Being::setTeamId(const uint16_t teamId) restrict2
5185 {
5186  if (mTeamId != teamId)
5187  {
5188  mTeamId = teamId;
5189  showTeamBadge(mTeamId != 0);
5190  updateColors();
5191  }
5192 }
5193 
5194 void Being::showTeamBadge(const bool show) restrict2
5195 {
5196  delete2(mBadges[BadgeIndex::Team])
5197  if (show &&
5198  mTeamId != 0U &&
5199  mShowBadges != BadgeDrawType::Hide)
5200  {
5201  const std::string name = paths.getStringValue("badges") +
5202  paths.getStringValue(strprintf("team%dbadge",
5203  mTeamId));
5204  if (!name.empty())
5205  mBadges[BadgeIndex::Team] = AnimatedSprite::load(name, 0);
5206  }
5207  updateBadgesCount();
5208  updateBadgesPosition();
5209 }
5210 
5211 void Being::showBadges(const bool show) restrict2
5212 {
5213  showTeamBadge(show);
5214  showGuildBadge(show);
5215  showGmBadge(show);
5216  showPartyBadge(show);
5217  showNameBadge(show);
5218  showShopBadge(show);
5219  showInactiveBadge(show);
5220  showAwayBadge(show);
5221 }
5222 
5223 void Being::showPartyBadge(const bool show) restrict2
5224 {
5225  delete2(mBadges[BadgeIndex::Party])
5226  if (show &&
5227  !mPartyName.empty() &&
5228  mShowBadges != BadgeDrawType::Hide)
5229  {
5230  const std::string badge = BadgesDB::getPartyBadge(mPartyName);
5231  if (!badge.empty())
5232  {
5234  paths.getStringValue("badges") + badge,
5235  0);
5236  }
5237  }
5238  updateBadgesCount();
5239  updateBadgesPosition();
5240 }
5241 
5242 
5243 void Being::setPartyName(const std::string &restrict name) restrict2
5244 {
5245  if (mPartyName != name)
5246  {
5247  mPartyName = name;
5248  showPartyBadge(!mPartyName.empty());
5249  }
5250 }
5251 
5252 void Being::showShopBadge(const bool show) restrict2
5253 {
5254  delete2(mBadges[BadgeIndex::Shop])
5255  if (show &&
5256  mShop &&
5257  mShowBadges != BadgeDrawType::Hide)
5258  {
5259  const std::string badge = paths.getStringValue("shopbadge");
5260  if (!badge.empty())
5261  {
5263  paths.getStringValue("badges") + badge,
5264  0);
5265  }
5266  }
5267  updateBadgesCount();
5268  updateBadgesPosition();
5269 }
5270 
5272 {
5273  delete2(mBadges[BadgeIndex::Inactive])
5274  if (show &&
5275  mInactive &&
5276  mShowBadges != BadgeDrawType::Hide)
5277  {
5278  const std::string badge = paths.getStringValue("inactivebadge");
5279  if (!badge.empty())
5280  {
5282  paths.getStringValue("badges") + badge,
5283  0);
5284  }
5285  }
5286  updateBadgesCount();
5287  updateBadgesPosition();
5288 }
5289 
5290 void Being::showAwayBadge(const bool show) restrict2
5291 {
5292  delete2(mBadges[BadgeIndex::Away])
5293  if (show &&
5294  mAway &&
5295  mShowBadges != BadgeDrawType::Hide)
5296  {
5297  const std::string badge = paths.getStringValue("awaybadge");
5298  if (!badge.empty())
5299  {
5301  paths.getStringValue("badges") + badge,
5302  0);
5303  }
5304  }
5305  updateBadgesCount();
5306  updateBadgesPosition();
5307 }
5308 
5310 {
5311  mBadgesCount = 0;
5312  for_each_badges()
5313  {
5314  if (mBadges[f] != nullptr)
5315  mBadgesCount ++;
5316  }
5317 }
5318 
5320 {
5321  delete mChat;
5322  mChat = obj;
5323 }
5324 
5325 void Being::setSellBoard(const std::string &restrict text) restrict2
5326 {
5327  mShop = !text.empty() || !mBuyBoard.empty();
5328  mSellBoard = text;
5329  updateName();
5330  showShopBadge(mShop);
5331 }
5332 
5333 void Being::setBuyBoard(const std::string &restrict text) restrict2
5334 {
5335  mShop = !text.empty() || !mSellBoard.empty();
5336  mBuyBoard = text;
5337  updateName();
5338  showShopBadge(mShop);
5339 }
5340 
5341 void Being::enableShop(const bool b) restrict2
5342 {
5343  mShop = b;
5344  updateName();
5345  showShopBadge(mShop);
5346 }
5347 
5349 {
5351  !mBuyBoard.empty());
5352 }
5353 
5355 {
5357  !mSellBoard.empty());
5358 }
5359 
5361 {
5362  // remove some flags what can survive player remove and next visible
5363  mTrickDead = false;
5364 }
5365 
5366 void Being::addCast(const int dstX,
5367  const int dstY,
5368  const int skillId,
5369  const int skillLevel,
5370  const int range,
5371  const int waitTimeTicks)
5372 {
5373  if (waitTimeTicks <= 0)
5374  {
5375  mCastEndTime = 0;
5376  return;
5377  }
5378  mCastEndTime = tick_time + waitTimeTicks;
5380  skillId,
5381  skillLevel);
5383  if (data != nullptr)
5384  {
5385  const std::string castingAnimation = data->castingAnimation;
5386  mCastingEffect = new CastingEffect(skillId,
5387  skillLevel,
5388  castingAnimation,
5389  dstX,
5390  dstY,
5391  range);
5393  }
5394  else
5395  {
5396  reportAlways("Want to draw casting for unknown skill %d",
5397  skillId)
5398  }
5399 }
5400 
5402 {
5404  mUpHorseSprites.clear();
5406  mDownHorseSprites.clear();
5407 }
5408 
5409 void Being::setRiding(const bool b) restrict2
5410 {
5412  return;
5413 
5414  if (b == (mHorseId != 0))
5415  return;
5416  if (b)
5417  setHorse(1);
5418  else
5419  setHorse(0);
5420 }
5421 
5422 void Being::setHorse(const int horseId) restrict2
5423 {
5424  if (mHorseId == horseId)
5425  return;
5426  mHorseId = horseId;
5427  setAction(mAction, 0);
5428  removeHorse();
5429  if (mHorseId != 0)
5430  {
5431  mHorseInfo = HorseDB::get(horseId, false);
5432  if (mHorseInfo != nullptr)
5433  {
5434  FOR_EACH (SpriteRefs, it, mHorseInfo->downSprites)
5435  {
5436  const SpriteReference *restrict const ref = *it;
5437  AnimatedSprite *const sprite = AnimatedSprite::load(
5438  ref->sprite,
5439  ref->variant);
5440  mDownHorseSprites.push_back(sprite);
5441  sprite->play(mSpriteAction);
5442  sprite->setSpriteDirection(mSpriteDirection);
5443  }
5444  FOR_EACH (SpriteRefs, it, mHorseInfo->upSprites)
5445  {
5446  const SpriteReference *restrict const ref = *it;
5447  AnimatedSprite *const sprite = AnimatedSprite::load(
5448  ref->sprite,
5449  ref->variant);
5450  mUpHorseSprites.push_back(sprite);
5451  sprite->play(mSpriteAction);
5452  sprite->setSpriteDirection(mSpriteDirection);
5453  }
5454  }
5455  }
5456  else
5457  {
5458  mHorseInfo = nullptr;
5459  }
5460 }
5461 
5462 void Being::setTrickDead(const bool b) restrict2
5463 {
5464  if (b != mTrickDead)
5465  {
5466  mTrickDead = b;
5467  setAction(mAction, 0);
5468  }
5469 }
5470 
5471 void Being::setSpiritBalls(const unsigned int balls) restrict2
5472 {
5474  {
5475  mSpiritBalls = balls;
5476  return;
5477  }
5478 
5479  if (balls > mSpiritBalls)
5480  {
5481  const int effectId = paths.getIntValue("spiritEffectId");
5482  if (effectId != -1)
5483  addSpiritBalls(balls - mSpiritBalls, effectId);
5484  }
5485  else if (balls < mSpiritBalls)
5486  {
5487  removeSpiritBalls(mSpiritBalls - balls);
5488  }
5489  mSpiritBalls = balls;
5490 }
5491 
5492 void Being::addSpiritBalls(const unsigned int balls,
5493  const int effectId) restrict2
5494 {
5495  if (effectManager == nullptr)
5496  return;
5497  for (unsigned int f = 0; f < balls; f ++)
5498  {
5499  Particle *const particle = effectManager->triggerReturn(
5500  effectId,
5501  this,
5502  0);
5503  mSpiritParticles.push_back(particle);
5504  }
5505 }
5506 
5507 void Being::removeSpiritBalls(const unsigned int balls) restrict2
5508 {
5509  if (particleEngine == nullptr)
5510  return;
5511  for (unsigned int f = 0; f < balls && !mSpiritParticles.empty(); f ++)
5512  {
5513  const Particle *restrict const particle = mSpiritParticles.back();
5514  mChildParticleEffects.removeLocally(particle);
5515  mSpiritParticles.pop_back();
5516  }
5517 }
5518 
5519 void Being::stopCast(const bool b)
5520 {
5521  if (b && mAction == BeingAction::CAST)
5523 }
5524 
5525 void Being::fixDirectionOffsets(int &offsetX,
5526  int &offsetY) const
5527 {
5528  const uint8_t dir = mDirection;
5529  if ((dir & BeingDirection::DOWN) != 0)
5530  {
5531  // do nothing
5532  }
5533  else if ((dir & BeingDirection::UP) != 0)
5534  {
5535  offsetX = -offsetX;
5536  offsetY = -offsetY;
5537  }
5538  else if ((dir & BeingDirection::LEFT) != 0)
5539  {
5540  const int tmp = offsetY;
5541  offsetY = offsetX;
5542  offsetX = -tmp;
5543  }
5544  else if ((dir & BeingDirection::RIGHT) != 0)
5545  {
5546  const int tmp = offsetY;
5547  offsetY = -offsetX;
5548  offsetX = tmp;
5549  }
5550 }
5551 
5553 {
5554  if (lang != mLanguageId)
5555  {
5556  delete2(mBadges[BadgeIndex::Lang])
5557  const std::string &badge = LanguageDb::getIcon(lang);
5558  if (!badge.empty())
5559  {
5561  paths.getStringValue("languageIcons"),
5562  badge),
5563  0);
5564  }
5565 
5566  mLanguageId = lang;
5567  }
5568  updateBadgesCount();
5569  updateBadgesPosition();
5570 }
5571 
5573  const ActorTypeT type,
5574  const BeingTypeId subType,
5575  Map *const map)
5576 {
5577  Being *const being = new Being(id,
5578  type);
5579  being->postInit(subType,
5580  map);
5581  return being;
5582 }
5583 
5584 void Being::setGroupId(const int id)
5585 {
5586  if (mGroupId != id)
5587  {
5588  mGroupId = id;
5589  const bool gm = GroupDb::getHighlightName(mGroupId);
5590  if (mIsGM != gm)
5591  {
5592  mIsGM = gm;
5593  updateColors();
5594  }
5595  showGmBadge(id != 0);
5596  }
5597 }
Net::ServerFeatures::haveExtendedRiding
virtual bool haveExtendedRiding() const =0
PlayerRelationsManager::isGoodName
bool isGoodName(Being *const being) const
Definition: playerrelations.cpp:611
itemcolormanager.h
BeingDirection::DOWN
@ DOWN
Definition: beingdirection.h:33
ForceDisplay_true
const bool ForceDisplay_true
Definition: forcedisplay.h:29
Being::fireMissile
void fireMissile(Being *const victim, const MissileInfo &missile) const
Definition: being.cpp:1377
Being::mEnemy
bool mEnemy
Definition: being.h:1381
Being::mDownHorseSprites
std::vector< AnimatedSprite * > mDownHorseSprites
Definition: being.h:1313
reportTrue
#define reportTrue(val)
Definition: checkutils.h:251
PacketType::PACKET_ATTACK
@ PACKET_ATTACK
Definition: packettype.h:37
VisibleName::Hide
@ Hide
Definition: visiblename.h:28
Being::getAttackAction
std::string getAttackAction() const
Definition: being.cpp:1524
SpriteAction::STANDWATER
static const std::string STANDWATER("standwater")
BeingFlag::SPECIAL
@ SPECIAL
Definition: beingflag.h:34
Files::existsLocal
bool existsLocal(const std::string &path)
Definition: files.cpp:208
ActorType::Unknown
@ Unknown
Definition: actortype.h:29
Sprite::mAlpha
float mAlpha
Definition: sprite.h:129
Reachable::REACH_NO
@ REACH_NO
Definition: reachable.h:32
ItemInfo::mDrawPriority
int mDrawPriority[10]
Definition: iteminfo.h:334
SpriteAction::SITSKY
static const std::string SITSKY("sitsky")
Being::mSpriteDirection
SpriteDirection::Type mSpriteDirection
Definition: being.h:1236
ActorType::Elemental
@ Elemental
Definition: actortype.h:40
horseinfo.h
reportAlways
#define reportAlways(...)
Definition: checkutils.h:252
Catch::trim
std::string trim(std::string const &str)
SpriteAction::FLY
static const std::string FLY("fly")
VisibleNamePos::Type
Type
Definition: visiblenamepos.h:26
AnimatedSprite::play
bool play(const std::string &spriteAction)
Definition: animatedsprite.cpp:158
Being::setLanguageId
void setLanguageId(const int lang)
Definition: being.cpp:5552
stringToHexPath
std::string stringToHexPath(const std::string &str)
Definition: stringutils.cpp:609
CHECKLISTENERS
#define CHECKLISTENERS
Definition: localconsts.h:276
BeingAction::PRESTAND
@ PRESTAND
Definition: beingaction.h:38
Being::setGroupId
virtual void setGroupId(const int id)
Definition: being.cpp:5584
playerhandler.h
SpriteAction::INVALID
static const std::string INVALID
Definition: spriteaction.h:71
BeingInfo::getAttackOffsetY
int getAttackOffsetY() const
Definition: beinginfo.h:284
AnimatedSprite::update
bool update(const int time)
Definition: animatedsprite.cpp:188
SpriteRefs
std::vector< SpriteReference * >::const_iterator SpriteRefs
Definition: spritereference.h:51
BeingSlot::colorId
ItemColor colorId
Definition: beingslot.h:46
UserColorId::TEAM3
@ TEAM3
Definition: usercolorid.h:52
homunculusinfo.h
Being::showName
void showName()
Definition: being.cpp:2575
Being::addSpiritBalls
void addSpiritBalls(const unsigned int balls, const int effectId)
Definition: being.cpp:5492
Being::setChat
void setChat(ChatObject *const obj)
Definition: being.cpp:5319
Being::setGuildPos
void setGuildPos(const std::string &pos)
Definition: being.cpp:1232
Being::showBadges
void showBadges(const bool show)
Definition: being.cpp:5211
skillunitdb.h
Being::handleAttack
void handleAttack(Being *const victim, const int damage, const int attackId)
Definition: being.cpp:962
Position::x
int x
Definition: position.h:44
emotedb.h
UserColorId::MERC_HP
@ MERC_HP
Definition: usercolorid.h:64
Being::mPath
Path mPath
Definition: being.h:1200
SpriteDirection::DOWN
@ DOWN
Definition: spritedirection.h:32
SkillUnitDb::get
BeingInfo * get(const BeingTypeId id)
Definition: skillunitdb.cpp:149
Being::getSpriteID
int getSpriteID(const int slot) const
Definition: being.cpp:4891
Being::addEffect
void addEffect(const std::string &name)
Definition: being.cpp:4956
Being::updateFromCache
bool updateFromCache()
Definition: being.cpp:3461
Being::getHitEffect
static int getHitEffect(const Being *const attacker, const AttackTypeT type, const int attackId, const int level)
Definition: being.cpp:869
Being::mSpeed
int mSpeed
Definition: being.h:1301
EffectManager::triggerDirection
bool triggerDirection(const int id, Being *const being, const SpriteDirection::Type &direction)
Definition: effectmanager.cpp:87
TargetCursorSize::SMALL
@ SMALL
Definition: targetcursorsize.h:29
BeingId
int BeingId
Definition: beingid.h:29
Being::mBadgesCount
uint16_t mBadgesCount
Definition: being.h:1378
Being::drawHomunculusSpriteAt
void drawHomunculusSpriteAt(Graphics *const graphics, const int x, const int y) const
Definition: being.cpp:4056
SpriteAction::STANDRIDE
static const std::string STANDRIDE("standride")
ActorSprite::mChildParticleEffects
ParticleList mChildParticleEffects
Definition: actorsprite.h:244
Being::mInfo
BeingInfo * mInfo
Definition: being.h:1176
Being::mAction
BeingActionT mAction
Definition: being.h:1232
restrict2
#define restrict2
Definition: localconsts.h:165
BadgeIndex::Away
@ Away
Definition: badgeindex.h:29
ActorType::Pet
@ Pet
Definition: actortype.h:36
EquipmentWindow::updateBeing
void updateBeing(Being *const being)
Definition: equipmentwindow.cpp:616
Net::CharServerHandler::baseSprite
virtual unsigned int baseSprite() const =0
Being::updateBadgesCount
void updateBadgesCount()
Definition: being.cpp:5309
CAST_SIZE
#define CAST_SIZE
Definition: cast.h:33
Being::mSpeechTime
int mSpeechTime
Definition: being.h:1220
horsedb.h
Being::setHairTempSprite
void setHairTempSprite(const unsigned int slot, const int id)
Definition: being.cpp:3215
ItemSoundEvent::HURT
@ HURT
Definition: itemsoundevent.h:30
BeingSlot::color
std::string color
Definition: beingslot.h:47
Being::mAnimationEffect
AnimatedSprite * mAnimationEffect
Definition: being.h:1178
SpriteAction::SIT
static const std::string SIT("sit")
BadgeDrawType::Hide
@ Hide
Definition: badgedrawtype.h:28
EmoteDB::getLast
const int & getLast() A_CONST
Definition: emotedb.cpp:300
Being::showAwayBadge
void showAwayBadge(const bool show)
Definition: being.cpp:5290
BeingInfo::getTargetCursorSize
TargetCursorSizeT getTargetCursorSize() const
Definition: beinginfo.h:89
Being::getSitAction
std::string getSitAction() const
Definition: being.cpp:1418
BeingCacheEntry::setFlags
void setFlags(const int flags)
Definition: beingcacheentry.h:115
Being::setDefaultNameColor
void setDefaultNameColor(const UserColorIdT defaultColor)
Definition: being.cpp:2636
Being::unSetSprite
void unSetSprite(const unsigned int slot)
Definition: being.cpp:2858
VisibleName::Show
@ Show
Definition: visiblename.h:29
Being::getOffset
int getOffset() const
Definition: being.cpp:2466
Being::mAdvanced
bool mAdvanced
Definition: being.h:1383
skilldata.h
FOR_EACHP
#define FOR_EACHP(type, iter, array)
Definition: foreach.h:30
anonymous_namespace{stringutils.cpp}::start
unsigned int start
Definition: stringutils.cpp:227
Being::drawOther
void drawOther(Graphics *const graphics, const int offsetX, const int offsetY) const
Definition: being.cpp:3753
Being::playSfx
void playSfx(const SoundInfo &sound, Being *const being, const bool main, const int x, const int y) const
Definition: being.cpp:4964
DragDropSource::Equipment
@ Equipment
Definition: dragdropsource.h:39
ChatMsgType::BY_OTHER
@ BY_OTHER
Definition: chatmsgtype.h:33
Being::mChat
ChatObject * mChat
Definition: being.h:1311
SkillData
Definition: skilldata.h:31
settings.h
Being::getGender
GenderT getGender() const
Definition: being.h:625
Being::mLowTraffic
static bool mLowTraffic
Definition: being.h:1339
Being::mShowBattleEvents
static bool mShowBattleEvents
Definition: being.h:1341
Being::showInactiveBadge
void showInactiveBadge(const bool show)
Definition: being.cpp:5271
Being::mAttackTime
time_t mAttackTime
Definition: being.h:1356
PETDB::get
BeingInfo * get(const BeingTypeId id)
Definition: petdb.cpp:156
Being::getParty
Party * getParty() const
Definition: being.h:329
LanguageDb::getIcon
const std::string & getIcon(const int id)
Definition: languagedb.cpp:110
Net::ServerFeatures::haveServerHp
virtual bool haveServerHp() const =0
Being::mEmotionTime
int mEmotionTime
Definition: being.h:1217
BadgeIndex::Shop
@ Shop
Definition: badgeindex.h:32
chatwindow.h
Relation::FRIEND
@ FRIEND
Definition: relation.h:31
Vector::x
float x
Definition: vector.h:208
PlayerInfo::getElementalId
BeingId getElementalId()
Definition: playerinfo.cpp:543
BeingInfo::getTargetOffsetX
int getTargetOffsetX() const
Definition: beinginfo.h:134
VisibleName::Type
Type
Definition: visiblename.h:26
Particle
Definition: particle.h:44
BadgeDrawType::Right
@ Right
Definition: badgedrawtype.h:30
BeingInfo
Definition: beinginfo.h:52
UserPalette::getColorWithAlpha
const Color & getColorWithAlpha(const UserColorIdT type)
Definition: userpalette.h:199
Being::mType
const ActorTypeT mType
Definition: being.h:1290
ItemSoundEvent::DIE
@ DIE
Definition: itemsoundevent.h:31
Item::isItem
static bool isItem(const int id)
Definition: item.h:237
BeingDirection::UP
@ UP
Definition: beingdirection.h:35
Being::drawMonster
void drawMonster(Graphics *const graphics, const int offsetX, const int offsetY) const
Definition: being.cpp:3777
Being::mIsGM
bool mIsGM
Definition: being.h:1238
UserColorId::MONSTER_HP2
@ MONSTER_HP2
Definition: usercolorid.h:61
Being::nextTile
virtual void nextTile()
Definition: being.cpp:1781
SpriteDirection::UPLEFT
@ UPLEFT
Definition: spritedirection.h:35
Being::drawMercenarySpriteAt
void drawMercenarySpriteAt(Graphics *const graphics, const int x, const int y) const
Definition: being.cpp:4111
BeingSpeech::NO_SPEECH
@ NO_SPEECH
Definition: beingspeech.h:30
Attributes::MERC_MAX_HP
@ MERC_MAX_HP
Definition: attributes.h:91
emptyBeingSlot
BeingSlot * emptyBeingSlot
Definition: beingslot.cpp:25
BeingCacheEntry
Definition: beingcacheentry.h:30
Actor::mMap
Map * mMap
Definition: actor.h:138
Net::HomunculusHandler::setDirection
virtual void setDirection(const unsigned char type) const =0
Being::mText
Text * mText
Definition: being.h:1201
stdmove.h
Files::loadTextFileLocal
bool loadTextFileLocal(const std::string &fileName, StringVect &lines)
Definition: files.cpp:228
Being::mSellBoard
std::string mSellBoard
Definition: being.h:1308
AttackType::STAND
@ STAND
Definition: attacktype.h:33
IntMapCIter
std::map< int, int >::const_iterator IntMapCIter
Definition: being.cpp:149
Net::MercenaryHandler::move
virtual void move(const int x, const int y) const =0
Equipment
Definition: equipment.h:30
doctest::detail::binaryAssertComparison::eq
@ eq
Definition: doctest.h:1303
Net::CharServerHandler::maxSprite
virtual unsigned int maxSprite() const =0
being.h
SpriteAction::DEADSKY
static const std::string DEADSKY("deadsky")
UserColorId::IGNORED
@ IGNORED
Definition: usercolorid.h:36
Being::mSpriteParticles
SpriteParticleInfo mSpriteParticles
Definition: being.h:1210
SPEECH_MAX_TIME
static const unsigned int SPEECH_MAX_TIME
Definition: being.cpp:153
SpriteAction::SITRIDE
static const std::string SITRIDE("sitride")
ParticleEngine::addEffect
Particle * addEffect(const std::string &particleEffectFile, const int pixelX, const int pixelY, const int rotation)
Definition: particleengine.cpp:129
Being::SpriteParticleInfoIter
SpriteParticleInfo::iterator SpriteParticleInfoIter
Definition: being.h:1207
effectManager
EffectManager * effectManager
Definition: effectmanager.cpp:38
Net::HomunculusHandler::attack
virtual void attack(const BeingId targetId, const Keep keep) const =0
BeingAction::DEAD
@ DEAD
Definition: beingaction.h:35
BeingInfo::getFollowDist
int getFollowDist() const
Definition: beinginfo.h:224
Being::resetCounters
void resetCounters()
Definition: being.cpp:4347
chatobject.h
Being::saveComment
static void saveComment(const std::string &name, const std::string &comment, const ActorTypeT &type)
Definition: being.cpp:4768
Being::setMap
void setMap(Map *const map)
Definition: being.cpp:5020
UserColorId::PARTICLE
@ PARTICLE
Definition: usercolorid.h:54
IntMap
std::map< int, int > IntMap
Definition: intmap.h:26
Being::setTileCoords
virtual void setTileCoords(const int x, const int y)
Definition: being.cpp:5007
PlayerInfo::getAttribute
int32_t getAttribute(const AttributesT id)
Definition: playerinfo.cpp:101
Being::drawNpc
void drawNpc(Graphics *const graphics, const int offsetX, const int offsetY) const
Definition: being.cpp:3765
ActorType::Homunculus
@ Homunculus
Definition: actortype.h:38
Attributes::ELEMENTAL_HP
@ ELEMENTAL_HP
Definition: attributes.h:110
Keep_false
const bool Keep_false
Definition: keep.h:29
Reachable::REACH_UNKNOWN