ManaPlus
localplayer.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-2018 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/localplayer.h"
24 
25 #include "actormanager.h"
26 #include "configuration.h"
27 #include "gamemodifiers.h"
28 #include "guild.h"
29 #include "party.h"
30 #include "settings.h"
31 #include "soundmanager.h"
32 #include "statuseffect.h"
33 
34 #include "being/beingflag.h"
35 #include "being/crazymoves.h"
36 #include "being/playerinfo.h"
37 #include "being/playerrelations.h"
38 
39 #include "const/sound.h"
40 
41 #include "enums/equipslot.h"
42 
44 
47 
49 
50 #include "input/keyboardconfig.h"
51 
52 #include "gui/gui.h"
53 #include "gui/userpalette.h"
54 #include "gui/popupmanager.h"
55 
56 #include "gui/windows/chatwindow.h"
58 #include "gui/windows/okdialog.h"
60 #include "gui/windows/shopwindow.h"
64 
66 
67 #include "listeners/awaylistener.h"
68 
69 #include "net/beinghandler.h"
70 #include "net/chathandler.h"
71 #include "net/inventoryhandler.h"
72 #include "net/net.h"
73 #include "net/packetlimiter.h"
74 #include "net/playerhandler.h"
75 #include "net/serverfeatures.h"
76 
77 #include "resources/iteminfo.h"
78 
79 #include "resources/db/weaponsdb.h"
80 
81 #include "resources/item/item.h"
82 
83 #include "resources/map/map.h"
84 #include "resources/map/mapitem.h"
87 
89 
90 #include "utils/delete2.h"
91 #include "utils/foreach.h"
92 #include "utils/gettext.h"
93 #include "utils/timer.h"
94 
95 #ifdef USE_MUMBLE
96 #include "mumblemanager.h"
97 #endif // USE_MUMBLE
98 
99 #include <climits>
100 
101 #include "debug.h"
102 
103 static const int16_t awayLimitTimer = 60;
104 static const int MAX_TICK_VALUE = INT_MAX / 2;
105 
106 typedef std::map<int, Guild*>::const_iterator GuildMapCIter;
107 
109 
110 extern OkDialog *weightNotice;
111 extern time_t weightNoticeTime;
112 
114  const BeingTypeId subType) :
115  Being(id, ActorType::Player),
119  mMoveState(0),
120  mLastTargetX(0),
121  mLastTargetY(0),
122  mHomes(),
123  mTarget(nullptr),
124  mPlayerFollowed(),
125  mPlayerImitated(),
126  mNextDestX(0),
127  mNextDestY(0),
128  mPickUpTarget(nullptr),
129  mLastAction(-1),
130  mStatusEffectIcons(),
131  mMessages(),
132  mMessageTime(0),
133  mAwayListener(new AwayListener),
134  mAwayDialog(nullptr),
135  mPingSendTick(0),
136  mPingTime(0),
137  mAfkTime(0),
138  mActivityTime(0),
139  mNavigateX(0),
140  mNavigateY(0),
141  mNavigateId(BeingId_zero),
142  mCrossX(0),
143  mCrossY(0),
144  mOldX(0),
145  mOldY(0),
146  mOldTileX(0),
147  mOldTileY(0),
148  mNavigatePath(),
149  mLastHitFrom(),
150  mWaitFor(),
151  mAdvertTime(0),
152  mTestParticle(nullptr),
153  mTestParticleName(),
154  mTestParticleTime(0),
155  mTestParticleHash(0L),
156  mSyncPlayerMoveDistance(config.getIntValue("syncPlayerMoveDistance")),
157  mUnfreezeTime(0),
158  mWalkingDir(0),
159  mUpdateName(true),
160  mBlockAdvert(false),
161  mTargetDeadPlayers(config.getBoolValue("targetDeadPlayers")),
162  mServerAttack(fromBool(config.getBoolValue("serverAttack"), Keep)),
163  mVisibleNames(static_cast<VisibleName::Type>(
164  config.getIntValue("visiblenames"))),
165  mEnableAdvert(config.getBoolValue("enableAdvert")),
166  mTradebot(config.getBoolValue("tradebot")),
167  mTargetOnlyReachable(config.getBoolValue("targetOnlyReachable")),
168  mIsServerBuggy(serverConfig.getValueBool("enableBuggyServers", true)),
169  mSyncPlayerMove(config.getBoolValue("syncPlayerMove")),
170  mDrawPath(config.getBoolValue("drawPath")),
171  mAttackMoving(config.getBoolValue("attackMoving")),
172  mAttackNext(config.getBoolValue("attackNext")),
173  mShowJobExp(config.getBoolValue("showJobExp")),
174  mShowServerPos(config.getBoolValue("showserverpos")),
175  mNextStep(false),
176  mGoingToTarget(false),
177  mKeepAttacking(false),
178  mPathSetByMouse(false),
179  mWaitPing(false),
180  mShowNavigePath(false),
181  mAllowRename(false),
182  mFreezed(false)
183 {
184  logger->log1("LocalPlayer::LocalPlayer");
185 
186  postInit(subType, nullptr);
187  mAttackRange = 0;
188  mLevel = 1;
189  mAdvanced = true;
191  if (userPalette != nullptr)
193  else
194  mNameColor = nullptr;
195 
197  getWalkSpeed(),
198  Notify_true);
200  0,
201  Notify_true);
202 
203  loadHomes();
204 
205  config.addListener("showownname", this);
206  config.addListener("targetDeadPlayers", this);
207  serverConfig.addListener("enableBuggyServers", this);
208  config.addListener("syncPlayerMove", this);
209  config.addListener("syncPlayerMoveDistance", this);
210  config.addListener("drawPath", this);
211  config.addListener("serverAttack", this);
212  config.addListener("attackMoving", this);
213  config.addListener("attackNext", this);
214  config.addListener("showJobExp", this);
215  config.addListener("enableAdvert", this);
216  config.addListener("tradebot", this);
217  config.addListener("targetOnlyReachable", this);
218  config.addListener("showserverpos", this);
219  config.addListener("visiblenames", this);
220  setShowName(config.getBoolValue("showownname"));
221 }
222 
224 {
225  logger->log1("LocalPlayer::~LocalPlayer");
226 
227  config.removeListeners(this);
228  serverConfig.removeListener("enableBuggyServers", this);
229 
230  navigateClean();
231  mCrossX = 0;
232  mCrossY = 0;
233 
235 
236  if (mAwayDialog != nullptr)
237  {
240  }
242 }
243 
245 {
246  BLOCK_START("LocalPlayer::logic")
247 #ifdef USE_MUMBLE
248  if (mumbleManager)
249  mumbleManager->setPos(mX, mY, mDirection);
250 #endif // USE_MUMBLE
251 
252  // Actions are allowed once per second
253  if (get_elapsed_time(mLastAction) >= 1000)
254  mLastAction = -1;
255 
256  if (mActivityTime == 0 || mLastAction != -1)
258 
259  if (mUnfreezeTime > 0 &&
261  {
262  mUnfreezeTime = 0;
263  mFreezed = false;
264  }
265 
266  if ((mAction != BeingAction::MOVE || mNextStep) && !mNavigatePath.empty())
267  {
268  mNextStep = false;
269  int dist = 5;
270  if (!mSyncPlayerMove)
271  dist = 20;
272 
273  if (((mNavigateX != 0) || (mNavigateY != 0)) &&
274  ((mCrossX + dist >= mX && mCrossX <= mX + dist
275  && mCrossY + dist >= mY && mCrossY <= mY + dist)
276  || ((mCrossX == 0) && (mCrossY == 0))))
277  {
278  const Path::const_iterator i = mNavigatePath.begin();
279  if ((*i).x == mX && (*i).y == mY)
280  mNavigatePath.pop_front();
281  else
282  setDestination((*i).x, (*i).y);
283  }
284  }
285 
286  // Show XP messages
287  if (!mMessages.empty())
288  {
289  if (mMessageTime == 0)
290  {
291  const MessagePair info = mMessages.front();
292 
293  if ((particleEngine != nullptr) && (gui != nullptr))
294  {
296  info.first,
297  mPixelX,
298  mPixelY - 48,
299  &userPalette->getColor(info.second, 255U),
301  true);
302  }
303 
304  mMessages.pop_front();
305  mMessageTime = 30;
306  }
307  mMessageTime--;
308  }
309 
310  if (mTarget != nullptr)
311  {
312  if (mTarget->getType() == ActorType::Npc)
313  {
314  // NPCs are always in range
316  }
317  else
318  {
319  // Find whether target is in range
320  const int rangeX = CAST_S32(
321  abs(mTarget->mX - mX));
322  const int rangeY = CAST_S32(
323  abs(mTarget->mY - mY));
324  const int attackRange = getAttackRange();
325  const TargetCursorTypeT targetType
326  = rangeX > attackRange || rangeY > attackRange
328  mTarget->setTargetType(targetType);
329 
330  if (!mTarget->isAlive() && (!mTargetDeadPlayers
332  {
333  stopAttack(true);
334  }
335 
336  if (mKeepAttacking && (mTarget != nullptr))
337  attack(mTarget, true, false);
338  }
339  }
340 
341  Being::logic();
342  BLOCK_END("LocalPlayer::logic")
343 }
344 
346 {
347  BLOCK_START("LocalPlayer::slowLogic")
348  const time_t time = cur_time;
349  if ((weightNotice != nullptr) && weightNoticeTime < time)
350  {
351  weightNotice->scheduleDelete();
352  weightNotice = nullptr;
353  weightNoticeTime = 0;
354  }
355 
356  if ((serverFeatures != nullptr) &&
358  mEnableAdvert &&
359  !mBlockAdvert &&
361  {
362  uint8_t smile = BeingFlag::SPECIAL;
363  if (mTradebot &&
364  shopWindow != nullptr &&
366  {
367  smile |= BeingFlag::SHOP;
368  }
369 
371  smile |= BeingFlag::AWAY;
372 
373  if (mInactive)
374  smile |= BeingFlag::INACTIVE;
375 
376  if (emote(smile))
377  mAdvertTime = time + 60;
378  else
379  mAdvertTime = time + 30;
380  }
381 
382  if (mTestParticleTime != time && !mTestParticleName.empty())
383  {
384  const unsigned long hash = UpdaterWindow::getFileHash(
386  if (hash != mTestParticleHash)
387  {
389  mTestParticleHash = hash;
390  }
392  }
393 
394  BLOCK_END("LocalPlayer::slowLogic")
395 }
396 
398  const int attackId)
399 {
400  if (action == BeingAction::DEAD)
401  {
402  if (!mLastHitFrom.empty() &&
404  {
405  // TRANSLATORS: chat message after death
406  debugMsg(strprintf(_("You were killed by %s."),
407  mLastHitFrom.c_str()));
408  mLastHitFrom.clear();
409  }
410  setTarget(nullptr);
411  }
412 
413  Being::setAction(action,
414  attackId);
415 #ifdef USE_MUMBLE
416  if (mumbleManager)
417  mumbleManager->setAction(CAST_S32(action));
418 #endif // USE_MUMBLE
419 }
420 
421 void LocalPlayer::setGroupId(const int id)
422 {
423  Being::setGroupId(id);
424 
425  if (mIsGM != 0)
426  {
427  if (chatWindow != nullptr)
428  {
431  }
432  }
433  if (statusWindow != nullptr)
435 }
436 
438 {
439  const Party *const party = Party::getParty(1);
440  if (party != nullptr)
441  {
442  PartyMember *const pm = party->getMember(mName);
443  if (pm != nullptr)
444  {
445  pm->setX(mX);
446  pm->setY(mY);
447  }
448  }
449 
450  if (mPath.empty())
451  {
452  if (mPickUpTarget != nullptr)
454 
455  if (mWalkingDir != 0u)
457  }
458  else if (mPath.size() == 1)
459  {
460  if (mPickUpTarget != nullptr)
462  }
463 
464  if (mGoingToTarget &&
465  mTarget != nullptr &&
466  withinAttackRange(mTarget, false, 0))
467  {
469  attack(mTarget, true, false);
470  mGoingToTarget = false;
471  mPath.clear();
472  return;
473  }
474  else if (mGoingToTarget && (mTarget == nullptr))
475  {
476  mGoingToTarget = false;
477  mPath.clear();
478  }
479 
480  if (mPath.empty())
481  {
482  if (mNavigatePath.empty() || mAction != BeingAction::MOVE)
484  else
485  mNextStep = true;
486  }
487  else
488  {
489  Being::nextTile();
490  }
491 }
492 
494 {
495  if (item == nullptr)
496  return false;
497 
499  return false;
500 
501  const int dx = item->getTileX() - mX;
502  const int dy = item->getTileY() - mY;
503  int dist = 6;
504 
505  const unsigned int pickUpType = settings.pickUpType;
506  if (pickUpType >= 4 && pickUpType <= 6)
507  dist = 4;
508 
509  if (dx * dx + dy * dy < dist)
510  {
511  if ((actorManager != nullptr) && actorManager->checkForPickup(item))
512  {
514  mPickUpTarget = nullptr;
515  }
516  }
517  else if (pickUpType >= 4 && pickUpType <= 6)
518  {
519  const Path debugPath = mMap->findPath(
520  (mPixelX - mapTileSize / 2) / mapTileSize,
522  item->getTileX(),
523  item->getTileY(),
525  0);
526  if (!debugPath.empty())
527  navigateTo(item->getTileX(), item->getTileY());
528  else
529  setDestination(item->getTileX(), item->getTileY());
530 
532  mPickUpTarget->addActorSpriteListener(this);
533  }
534  return true;
535 }
536 
538 {
539  if (mPickUpTarget == &actorSprite)
540  mPickUpTarget = nullptr;
541 }
542 
544 {
545  return mTarget;
546 }
547 
548 void LocalPlayer::setTarget(Being *const target)
549 {
550  if (target == this && (target != nullptr))
551  return;
552 
553  if (target == mTarget)
554  return;
555 
556  Being *oldTarget = nullptr;
557  if (mTarget != nullptr)
558  {
559  mTarget->untarget();
560  oldTarget = mTarget;
561  }
562 
563  if (mTarget != nullptr)
564  {
566  mTarget->setShowName(false);
567  }
568 
569  mTarget = target;
570 
571  if (oldTarget != nullptr)
572  oldTarget->updateName();
573 
574  if (target != nullptr)
575  {
576  mLastTargetX = target->mX;
577  mLastTargetY = target->mY;
578  target->updateName();
580  target->setShowName(true);
581  }
582  if (oldTarget != nullptr && mVisibleNames == VisibleName::ShowOnSelection)
583  oldTarget->setShowName(false);
584  if (target != nullptr && target->getType() == ActorType::Monster)
585  target->setShowName(true);
586 }
587 
589  const AllowSort allowSort)
590 {
591  if (actorManager != nullptr)
592  {
593  Being *const target = actorManager->findNearestLivingBeing(
594  localPlayer, 20, type, allowSort);
595 
596  if ((target != nullptr) && target != mTarget)
597  setTarget(target);
598 
599  return target;
600  }
601  return nullptr;
602 }
603 
604 void LocalPlayer::setDestination(const int x, const int y)
605 {
607 
608  if (settings.attackType == 0 || !mAttackMoving)
609  mKeepAttacking = false;
610 
611  // Only send a new message to the server when destination changes
612  if (x != mDest.x || y != mDest.y)
613  {
614  if (settings.moveType != 1)
615  {
617  Being::setDestination(x, y);
618  }
619  else
620  {
621  uint8_t newDir = 0;
622  if ((mDirection & BeingDirection::UP) != 0)
623  newDir |= BeingDirection::DOWN;
624  if ((mDirection & BeingDirection::LEFT) != 0)
625  newDir |= BeingDirection::RIGHT;
626  if ((mDirection & BeingDirection::DOWN) != 0)
627  newDir |= BeingDirection::UP;
628  if ((mDirection & BeingDirection::RIGHT) != 0)
629  newDir |= BeingDirection::LEFT;
630 
631  playerHandler->setDestination(x, y, newDir);
632 
633 // if (PacketLimiter::limitPackets(PacketType::PACKET_DIRECTION))
634  {
635  setDirection(newDir);
636  playerHandler->setDirection(newDir);
637  }
638 
639  Being::setDestination(x, y);
641  }
642  }
643 }
644 
645 void LocalPlayer::setWalkingDir(const unsigned char dir)
646 {
647  // This function is called by Game::handleInput()
648  mWalkingDir = dir;
649 
650  // If we're not already walking, start walking.
651  if (mAction != BeingAction::MOVE && (dir != 0u))
652  startWalking(dir);
653 }
654 
655 void LocalPlayer::startWalking(const unsigned char dir)
656 {
657  // This function is called by setWalkingDir(),
658  // but also by nextTile() for TMW-Athena...
659  if ((mMap == nullptr) || (dir == 0u))
660  return;
661 
662  mPickUpTarget = nullptr;
663  if (mAction == BeingAction::MOVE && !mPath.empty())
664  {
665  // Just finish the current action, otherwise we get out of sync
667  return;
668  }
669 
670  int dx = 0, dy = 0;
671  if ((dir & BeingDirection::UP) != 0)
672  dy--;
673  if ((dir & BeingDirection::DOWN) != 0)
674  dy++;
675  if ((dir & BeingDirection::LEFT) != 0)
676  dx--;
677  if ((dir & BeingDirection::RIGHT) != 0)
678  dx++;
679 
680  const unsigned char blockWalkMask = getBlockWalkMask();
681  // Prevent skipping corners over colliding tiles
682  if ((dx != 0) && !mMap->getWalk(mX + dx, mY, blockWalkMask))
683  dx = 0;
684  if ((dy != 0) && !mMap->getWalk(mX, mY + dy, blockWalkMask))
685  dy = 0;
686 
687  // Choose a straight direction when diagonal target is blocked
688  if (dx != 0 && dy != 0 && !mMap->getWalk(mX + dx, mY + dy, blockWalkMask))
689  dx = 0;
690 
691  // Walk to where the player can actually go
692  if ((dx != 0 || dy != 0) && mMap->getWalk(mX + dx, mY + dy, blockWalkMask))
693  {
694  setDestination(mX + dx, mY + dy);
695  }
696  else if (dir != mDirection)
697  {
698  // If the being can't move, just change direction
699 
700 // if (PacketLimiter::limitPackets(PacketType::PACKET_DIRECTION))
701  {
703  setDirection(dir);
704  }
705  }
706 }
707 
708 void LocalPlayer::stopWalking(const bool sendToServer)
709 {
710  if (mAction == BeingAction::MOVE && (mWalkingDir != 0u))
711  {
712  mWalkingDir = 0;
713  mPickUpTarget = nullptr;
715  mPixelY);
716  if (sendToServer)
717  {
719  mPixelX,
720  mPixelY,
721  -1);
722  }
724  }
725 
726  // No path set anymore, so we reset the path by mouse flag
727  mPathSetByMouse = false;
728 
729  clearPath();
730  navigateClean();
731 }
732 
734 {
736  return false;
737 
738  BeingActionT newAction;
739  switch (mAction)
740  {
741  case BeingAction::STAND:
743  case BeingAction::SPAWN:
744  newAction = BeingAction::SIT;
745  break;
746  case BeingAction::SIT:
747  newAction = BeingAction::STAND;
748  break;
749  case BeingAction::MOVE:
750  case BeingAction::ATTACK:
751  case BeingAction::DEAD:
752  case BeingAction::HURT:
753  case BeingAction::CAST:
754  default:
755  return true;
756  }
757 
758  playerHandler->changeAction(newAction);
759  return true;
760 }
761 
763 {
765  return false;
766 
768  return true;
769 }
770 
771 bool LocalPlayer::emote(const uint8_t emotion)
772 {
774  return false;
775 
776  playerHandler->emote(emotion);
777  return true;
778 }
779 
780 void LocalPlayer::attack(Being *const target,
781  const bool keep,
782  const bool dontChangeEquipment)
783 {
784  mKeepAttacking = keep;
785 
786  if ((target == nullptr) || target->getType() == ActorType::Npc)
787  return;
788 
789  if (mTarget != target)
790  setTarget(target);
791 
792  // Must be standing or sitting or casting to attack
793  if (mAction != BeingAction::STAND &&
796  {
797  return;
798  }
799 
800 #ifdef TMWA_SUPPORT
801  const int dist_x = target->mX - mX;
802  const int dist_y = target->mY - mY;
803 
805  {
806  if (abs(dist_y) >= abs(dist_x))
807  {
808  if (dist_y > 0)
810  else
812  }
813  else
814  {
815  if (dist_x > 0)
817  else
819  }
820  }
821 #endif // TMWA_SUPPORT
822 
824 
825  if (target->getType() != ActorType::Player
826  || checAttackPermissions(target))
827  {
829 
831  return;
832 
833  if (!dontChangeEquipment)
835 
836  const BeingId targetId = target->getId();
837  playerHandler->attack(targetId, mServerAttack);
839  }
840 
841  if (!keep)
842  stopAttack(false);
843 }
844 
845 void LocalPlayer::stopAttack(const bool keepAttack)
846 {
848  return;
849 
852 
853  untarget();
854  if (!keepAttack || !mAttackNext)
855  mKeepAttacking = false;
856 }
857 
859 {
862 
863  if (mTarget != nullptr)
864  setTarget(nullptr);
865 }
866 
868  const int amount,
869  const ItemColor color,
870  const BeingId floorItemId,
871  const PickupT fail)
872 {
873  if (fail != Pickup::OKAY)
874  {
875  if ((actorManager != nullptr) && floorItemId != BeingId_zero)
876  {
877  FloorItem *const item = actorManager->findItem(floorItemId);
878  if (item != nullptr)
879  {
880  if (!item->getShowMsg())
881  return;
882  item->setShowMsg(false);
883  }
884  }
885  const char* msg = nullptr;
886  switch (fail)
887  {
888  case Pickup::BAD_ITEM:
889  // TRANSLATORS: pickup error message
890  msg = N_("Tried to pick up nonexistent item.");
891  break;
892  case Pickup::TOO_HEAVY:
893  // TRANSLATORS: pickup error message
894  msg = N_("Item is too heavy.");
895  break;
896  case Pickup::TOO_FAR:
897  // TRANSLATORS: pickup error message
898  msg = N_("Item is too far away.");
899  break;
900  case Pickup::INV_FULL:
901  // TRANSLATORS: pickup error message
902  msg = N_("Inventory is full.");
903  break;
904  case Pickup::STACK_FULL:
905  // TRANSLATORS: pickup error message
906  msg = N_("Stack is too big.");
907  break;
908  case Pickup::DROP_STEAL:
909  // TRANSLATORS: pickup error message
910  msg = N_("Item belongs to someone else.");
911  break;
912  case Pickup::MAX_AMOUNT:
913  // TRANSLATORS: pickup error message
914  msg = N_("You can't pickup this amount of items.");
915  break;
917  // TRANSLATORS: pickup error message
918  msg = N_("Your item stack has max amount.");
919  break;
920  case Pickup::OKAY:
921  break;
922  default:
923  case Pickup::UNKNOWN:
924  // TRANSLATORS: pickup error message
925  msg = N_("Unknown problem picking up item.");
926  break;
927  }
928  if (localChatTab != nullptr &&
929  config.getBoolValue("showpickupchat"))
930  {
931  localChatTab->chatLog(gettext(msg),
935  }
936 
937  if ((mMap != nullptr) && config.getBoolValue("showpickupparticle"))
938  {
939  // Show pickup notification
941  }
942  }
943  else
944  {
945  std::string str;
946 #ifdef TMWA_SUPPORT
948  {
949  str = itemInfo.getName();
950  }
951  else
952 #endif // TMWA_SUPPORT
953  {
954  str = itemInfo.getName(color);
955  }
956 
957  if (config.getBoolValue("showpickupchat") && (localChatTab != nullptr))
958  {
959  // TRANSLATORS: %d is number,
960  // [@@%d|%[email protected]@] - here player can see link to item
961  localChatTab->chatLog(strprintf(ngettext("You picked up %d "
962  "[@@%d|%[email protected]@].", "You picked up %d [@@%d|%[email protected]@].", amount),
963  amount, itemInfo.getId(), str.c_str()),
967  }
968 
969  if ((mMap != nullptr) && config.getBoolValue("showpickupparticle"))
970  {
971  // Show pickup notification
972  if (amount > 1)
973  {
974  addMessageToQueue(strprintf("%d x %s", amount,
975  str.c_str()), UserColorId::PICKUP_INFO);
976  }
977  else
978  {
980  }
981  }
982  }
983 }
984 
986 {
987  if (mAttackRange > -1)
988  {
989  return mAttackRange;
990  }
991 
992  const Item *const weapon = PlayerInfo::getEquipment(
994  if (weapon != nullptr)
995  {
996  const ItemInfo &info = weapon->getInfo();
997  return info.getAttackRange();
998  }
999  return 48; // unarmed range
1000 }
1001 
1002 bool LocalPlayer::withinAttackRange(const Being *const target,
1003  const bool fixDistance,
1004  const int addRange) const
1005 {
1006  if (target == nullptr)
1007  return false;
1008 
1009  int range = getAttackRange() + addRange;
1010  int dx;
1011  int dy;
1012 
1013  if (fixDistance && range == 1)
1014  range = 2;
1015 
1016  dx = CAST_S32(abs(target->mX - mX));
1017  dy = CAST_S32(abs(target->mY - mY));
1018  return !(dx > range || dy > range);
1019 }
1020 
1022 {
1023  if (target == nullptr)
1024  return;
1025 
1026  mPickUpTarget = nullptr;
1027  setTarget(target);
1028  mGoingToTarget = true;
1029  navigateTo(target->mX,
1030  target->mY);
1031 }
1032 
1034  const int32_t effectId,
1035  const Enable newStatus,
1036  const IsStart start)
1037 {
1039  effectId,
1040  newStatus,
1041  start);
1042 
1043  if (effect != nullptr)
1044  {
1045  effect->deliverMessage();
1046  effect->playSFX();
1047 
1048  AnimatedSprite *const sprite = effect->getIcon();
1049 
1050  if (sprite == nullptr)
1051  {
1052  // delete sprite, if necessary
1053  for (size_t i = 0; i < mStatusEffectIcons.size(); )
1054  {
1055  if (mStatusEffectIcons[i] == effectId)
1056  {
1057  mStatusEffectIcons.erase(mStatusEffectIcons.begin() + i);
1058  if (miniStatusWindow != nullptr)
1060  }
1061  else
1062  {
1063  i++;
1064  }
1065  }
1066  }
1067  else
1068  {
1069  // replace sprite or append
1070  bool found = false;
1071  const size_t sz = mStatusEffectIcons.size();
1072  for (size_t i = 0; i < sz; i++)
1073  {
1074  if (mStatusEffectIcons[i] == effectId)
1075  {
1076  if (miniStatusWindow != nullptr)
1077  miniStatusWindow->setIcon(CAST_S32(i), sprite);
1078  found = true;
1079  break;
1080  }
1081  }
1082 
1083  if (!found)
1084  { // add new
1085  const int offset = CAST_S32(mStatusEffectIcons.size());
1086  if (miniStatusWindow != nullptr)
1087  miniStatusWindow->setIcon(offset, sprite);
1088  mStatusEffectIcons.push_back(effectId);
1089  }
1090  }
1091  }
1092 }
1093 
1094 void LocalPlayer::addMessageToQueue(const std::string &message,
1095  const UserColorIdT color)
1096 {
1097  if (mMessages.size() < 20)
1098  mMessages.push_back(MessagePair(message, color));
1099 }
1100 
1101 void LocalPlayer::optionChanged(const std::string &value)
1102 {
1103  if (value == "showownname")
1104  {
1105  setShowName(config.getBoolValue("showownname"));
1106  }
1107  else if (value == "targetDeadPlayers")
1108  {
1109  mTargetDeadPlayers = config.getBoolValue("targetDeadPlayers");
1110  }
1111  else if (value == "enableBuggyServers")
1112  {
1113  mIsServerBuggy = serverConfig.getBoolValue("enableBuggyServers");
1114  }
1115  else if (value == "syncPlayerMove")
1116  {
1117  mSyncPlayerMove = config.getBoolValue("syncPlayerMove");
1118  }
1119  else if (value == "syncPlayerMoveDistance")
1120  {
1121  mSyncPlayerMoveDistance = config.getIntValue("syncPlayerMoveDistance");
1122  }
1123  else if (value == "drawPath")
1124  {
1125  mDrawPath = config.getBoolValue("drawPath");
1126  }
1127  else if (value == "serverAttack")
1128  {
1129  mServerAttack = fromBool(config.getBoolValue("serverAttack"), Keep);
1130  }
1131  else if (value == "attackMoving")
1132  {
1133  mAttackMoving = config.getBoolValue("attackMoving");
1134  }
1135  else if (value == "attackNext")
1136  {
1137  mAttackNext = config.getBoolValue("attackNext");
1138  }
1139  else if (value == "showJobExp")
1140  {
1141  mShowJobExp = config.getBoolValue("showJobExp");
1142  }
1143  else if (value == "enableAdvert")
1144  {
1145  mEnableAdvert = config.getBoolValue("enableAdvert");
1146  }
1147  else if (value == "tradebot")
1148  {
1149  mTradebot = config.getBoolValue("tradebot");
1150  }
1151  else if (value == "targetOnlyReachable")
1152  {
1153  mTargetOnlyReachable = config.getBoolValue("targetOnlyReachable");
1154  }
1155  else if (value == "showserverpos")
1156  {
1157  mShowServerPos = config.getBoolValue("showserverpos");
1158  }
1159  else if (value == "visiblenames")
1160  {
1161  mVisibleNames = static_cast<VisibleName::Type>(
1162  config.getIntValue("visiblenames"));
1163  }
1164 }
1165 
1166 void LocalPlayer::addJobMessage(const int64_t change)
1167 {
1168  if (change != 0 && mMessages.size() < 20)
1169  {
1170  const std::string xpStr = toString(CAST_U64(change));
1171  if (!mMessages.empty())
1172  {
1173  MessagePair pair = mMessages.back();
1174  // TRANSLATORS: this is normal experience
1175  if (pair.first.find(strprintf(" %s", _("xp"))) ==
1176  // TRANSLATORS: this is normal experience
1177  pair.first.size() - strlen(_("xp")) - 1)
1178  {
1179  mMessages.pop_back();
1180  pair.first.append(strprintf(", %s %s",
1181  xpStr.c_str(),
1182  // TRANSLATORS: this is job experience
1183  _("job")));
1184  mMessages.push_back(pair);
1185  }
1186  else
1187  {
1188  addMessageToQueue(strprintf("%s %s",
1189  xpStr.c_str(),
1190  // TRANSLATORS: this is job experience
1191  _("job")),
1193  }
1194  }
1195  else
1196  {
1197  addMessageToQueue(strprintf("%s %s",
1198  xpStr.c_str(),
1199  // TRANSLATORS: this is job experience
1200  _("job")),
1202  }
1203  }
1204 }
1205 
1206 void LocalPlayer::addXpMessage(const int64_t change)
1207 {
1208  if (change != 0 && mMessages.size() < 20)
1209  {
1210  addMessageToQueue(strprintf("%s %s",
1211  toString(CAST_U64(change)).c_str(),
1212  // TRANSLATORS: get xp message
1213  _("xp")),
1215  }
1216 }
1217 
1218 void LocalPlayer::addHomunXpMessage(const int change)
1219 {
1220  if (change != 0 && mMessages.size() < 20)
1221  {
1222  addMessageToQueue(strprintf("%s %d %s",
1223  // TRANSLATORS: get homunculus xp message
1224  _("Homun"),
1225  change,
1226  // TRANSLATORS: get xp message
1227  _("xp")),
1229  }
1230 }
1231 
1232 void LocalPlayer::addHpMessage(const int change)
1233 {
1234  if (change != 0 && mMessages.size() < 20)
1235  {
1236  // TRANSLATORS: get hp message
1237  addMessageToQueue(strprintf("%d %s", change, _("hp")),
1239  }
1240 }
1241 
1242 void LocalPlayer::addSpMessage(const int change)
1243 {
1244  if (change != 0 && mMessages.size() < 20)
1245  {
1246  // TRANSLATORS: get hp message
1247  addMessageToQueue(strprintf("%d %s", change, _("mana")),
1249  }
1250 }
1251 
1253  const int64_t oldVal,
1254  const int64_t newVal)
1255 {
1256  PRAGMA45(GCC diagnostic push)
1257  PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
1258  switch (id)
1259  {
1261  {
1263  break;
1264  if (oldVal > newVal)
1265  break;
1266 
1267  const int change = CAST_S32(newVal - oldVal);
1268  addXpMessage(change);
1269  break;
1270  }
1272  mLevel = CAST_S32(newVal);
1273  break;
1274  case Attributes::PLAYER_HP:
1275  if (oldVal != 0 && newVal == 0)
1277  break;
1279  {
1280  if (!mShowJobExp ||
1282  {
1283  return;
1284  }
1285  if (oldVal > newVal ||
1288  {
1289  return;
1290  }
1291  const int32_t change = CAST_S32(newVal - oldVal);
1292  addJobMessage(change);
1293  break;
1294  }
1295  default:
1296  break;
1297  }
1298  PRAGMA45(GCC diagnostic pop)
1299 }
1300 
1301 void LocalPlayer::move(const int dX, const int dY)
1302 {
1303  mPickUpTarget = nullptr;
1304  setDestination(mX + dX, mY + dY);
1305 }
1306 
1308 {
1309  bool gotPos(false);
1310  Path debugPath;
1311 
1312  size_t limit(0);
1313 
1314  if (dist == -1)
1315  {
1316  dist = settings.moveToTargetType;
1317  if (dist != 0)
1318  {
1319  const bool broken = (Net::getNetworkType() ==
1321  switch (dist)
1322  {
1323  case 10:
1324  dist = mAttackRange;
1325  if (dist == 1 && broken)
1326  dist = 2;
1327  break;
1328  case 11:
1329  dist = mAttackRange - 1;
1330  if (dist < 1)
1331  dist = 1;
1332  if (dist == 1 && broken)
1333  dist = 2;
1334  break;
1335  default:
1336  break;
1337  }
1338  }
1339  }
1340 
1341  if (mTarget != nullptr)
1342  {
1343  if (mMap != nullptr)
1344  {
1345  debugPath = mMap->findPath(
1346  (mPixelX - mapTileSize / 2) / mapTileSize,
1348  mTarget->mX,
1349  mTarget->mY,
1350  getBlockWalkMask(),
1351  0);
1352  }
1353 
1354  const size_t sz = debugPath.size();
1355  if (sz < CAST_SIZE(dist))
1356  return;
1357  limit = CAST_S32(sz) - dist;
1358  gotPos = true;
1359  }
1360  else if ((mNavigateX != 0) || (mNavigateY != 0))
1361  {
1362  debugPath = mNavigatePath;
1363  limit = dist;
1364  gotPos = true;
1365  }
1366 
1367  if (gotPos)
1368  {
1369  if (dist == 0)
1370  {
1371  if (mTarget != nullptr)
1373  }
1374  else
1375  {
1376  Position pos(0, 0);
1377  size_t f = 0;
1378 
1379  for (Path::const_iterator i = debugPath.begin(),
1380  i_fend = debugPath.end();
1381  i != i_fend && f < limit; ++i, f++)
1382  {
1383  pos = (*i);
1384  }
1385  navigateTo(pos.x, pos.y);
1386  }
1387  }
1388  else if ((mLastTargetX != 0) || (mLastTargetY != 0))
1389  {
1391  }
1392 }
1393 
1395 {
1396  mPickUpTarget = nullptr;
1397  if ((mX != mCrossX || mY != mCrossY) && (mCrossX != 0) && (mCrossY != 0))
1398  {
1400  }
1401  else if (mMap != nullptr)
1402  {
1403  const std::map<std::string, Vector>::const_iterator iter =
1404  mHomes.find(mMap->getProperty("_realfilename", std::string()));
1405 
1406  if (iter != mHomes.end())
1407  {
1408  const Vector pos = mHomes[(*iter).first];
1409  if (mX == pos.x && mY == pos.y)
1410  {
1412  CAST_S32(pos.x),
1413  CAST_S32(pos.y),
1414  CAST_S32(mDirection));
1415  }
1416  else
1417  {
1418  navigateTo(CAST_S32(pos.x), CAST_S32(pos.y));
1419  }
1420  }
1421  }
1422 }
1423 
1424 void LocalPlayer::changeEquipmentBeforeAttack(const Being *const target) const
1425 {
1426  if (settings.attackWeaponType == 1
1427  || (target == nullptr)
1428  || (PlayerInfo::getInventory() == nullptr))
1429  {
1430  return;
1431  }
1432 
1433  bool allowSword = false;
1434  const int dx = target->mX - mX;
1435  const int dy = target->mY - mY;
1436  const Item *item = nullptr;
1437 
1438  if (dx * dx + dy * dy > 80)
1439  return;
1440 
1441  if (dx * dx + dy * dy < 8)
1442  allowSword = true;
1443 
1444  const Inventory *const inv = PlayerInfo::getInventory();
1445  if (inv == nullptr)
1446  return;
1447 
1448  // if attack distance for sword
1449  if (allowSword)
1450  {
1451  // searching swords
1452  const WeaponsInfos &swords = WeaponsDB::getSwords();
1453  FOR_EACH (WeaponsInfosIter, it, swords)
1454  {
1455  item = inv->findItem(*it, ItemColor_zero);
1456  if (item != nullptr)
1457  break;
1458  }
1459 
1460  // no swords
1461  if (item == nullptr)
1462  return;
1463 
1464  // if sword not equiped
1465  if (item->isEquipped() == Equipped_false)
1467 
1468  // if need equip shield too
1469  if (settings.attackWeaponType == 3)
1470  {
1471  // searching shield
1472  const WeaponsInfos &shields = WeaponsDB::getShields();
1473  FOR_EACH (WeaponsInfosIter, it, shields)
1474  {
1475  item = inv->findItem(*it, ItemColor_zero);
1476  if (item != nullptr)
1477  break;
1478  }
1479  if ((item != nullptr) && item->isEquipped() == Equipped_false)
1481  }
1482  }
1483  // big distance. allowed only bow
1484  else
1485  {
1486  // searching bow
1487  const WeaponsInfos &bows = WeaponsDB::getBows();
1488  FOR_EACH (WeaponsInfosIter, it, bows)
1489  {
1490  item = inv->findItem(*it, ItemColor_zero);
1491  if (item != nullptr)
1492  break;
1493  }
1494 
1495  // no bow
1496  if (item == nullptr)
1497  return;
1498 
1499  if (item->isEquipped() == Equipped_false)
1501  }
1502 }
1503 
1505  const int maxCost)
1506 {
1507  if ((being == nullptr) || (mMap == nullptr))
1508  return false;
1509 
1510  if (being->getReachable() == Reachable::REACH_NO)
1511  return false;
1512 
1513  if (being->mX == mX &&
1514  being->mY == mY)
1515  {
1516  being->setDistance(0);
1518  return true;
1519  }
1520  else if (being->mX - 1 <= mX &&
1521  being->mX + 1 >= mX &&
1522  being->mY - 1 <= mY &&
1523  being->mY + 1 >= mY)
1524  {
1525  being->setDistance(1);
1527  return true;
1528  }
1529 
1530  const Path debugPath = mMap->findPath(
1531  (mPixelX - mapTileSize / 2) / mapTileSize,
1533  being->mX,
1534  being->mY,
1535  getBlockWalkMask(),
1536  maxCost);
1537 
1538  being->setDistance(CAST_S32(debugPath.size()));
1539  if (!debugPath.empty())
1540  {
1542  return true;
1543  }
1545  return false;
1546 }
1547 
1548 bool LocalPlayer::isReachable(const int x, const int y,
1549  const bool allowCollision) const
1550 {
1551  const WalkLayer *const walk = mMap->getWalkLayer();
1552  if (walk == nullptr)
1553  return false;
1554  int num = walk->getDataAt(x, y);
1555  if (allowCollision && num < 0)
1556  num = -num;
1557 
1558  return walk->getDataAt(mX, mY) == num;
1559 }
1560 
1561 bool LocalPlayer::pickUpItems(int pickUpType)
1562 {
1563  if (actorManager == nullptr)
1564  return false;
1565 
1566  bool status = false;
1567  int x = mX;
1568  int y = mY;
1569 
1570  // first pick up item on player position
1571  FloorItem *item =
1572  actorManager->findItem(x, y);
1573  if (item != nullptr)
1574  status = pickUp(item);
1575 
1576  if (pickUpType == 0)
1577  pickUpType = settings.pickUpType;
1578 
1579  if (pickUpType == 0)
1580  return status;
1581 
1582  int x1, y1, x2, y2;
1583  switch (pickUpType)
1584  {
1585  case 1:
1586  switch (mDirection)
1587  {
1588  case BeingDirection::UP : --y; break;
1589  case BeingDirection::DOWN : ++y; break;
1590  case BeingDirection::LEFT : --x; break;
1591  case BeingDirection::RIGHT: ++x; break;
1592  default: break;
1593  }
1594  item = actorManager->findItem(x, y);
1595  if (item != nullptr)
1596  status = pickUp(item);
1597  break;
1598  case 2:
1599  switch (mDirection)
1600  {
1601  case BeingDirection::UP:
1602  x1 = x - 1; y1 = y - 1; x2 = x + 1; y2 = y; break;
1603  case BeingDirection::DOWN:
1604  x1 = x - 1; y1 = y; x2 = x + 1; y2 = y + 1; break;
1605  case BeingDirection::LEFT:
1606  x1 = x - 1; y1 = y - 1; x2 = x; y2 = y + 1; break;
1607  case BeingDirection::RIGHT:
1608  x1 = x; y1 = y - 1; x2 = x + 1; y2 = y + 1; break;
1609  default:
1610  x1 = x; x2 = x; y1 = y; y2 = y; break;
1611  }
1612  if (actorManager->pickUpAll(x1, y1, x2, y2, false))
1613  status = true;
1614  break;
1615  case 3:
1616  if (actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1617  status = true;
1618  break;
1619 
1620  case 4:
1621  if (!actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1622  {
1623  if (actorManager->pickUpNearest(x, y, 4))
1624  status = true;
1625  }
1626  else
1627  {
1628  status = true;
1629  }
1630  break;
1631 
1632  case 5:
1633  if (!actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1634  {
1635  if (actorManager->pickUpNearest(x, y, 8))
1636  status = true;
1637  }
1638  else
1639  {
1640  status = true;
1641  }
1642  break;
1643 
1644  case 6:
1645  if (!actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1646  {
1647  if (actorManager->pickUpNearest(x, y, 90))
1648  status = true;
1649  }
1650  else
1651  {
1652  status = true;
1653  }
1654  break;
1655 
1656  default:
1657  break;
1658  }
1659  return status;
1660 }
1661 
1662 
1663 void LocalPlayer::moveByDirection(const unsigned char dir)
1664 {
1665  int dx = 0, dy = 0;
1666  if ((dir & BeingDirection::UP) != 0)
1667  dy--;
1668  if ((dir & BeingDirection::DOWN) != 0)
1669  dy++;
1670  if ((dir & BeingDirection::LEFT) != 0)
1671  dx--;
1672  if ((dir & BeingDirection::RIGHT) != 0)
1673  dx++;
1674  move(dx, dy);
1675 }
1676 
1677 void LocalPlayer::specialMove(const unsigned char direction)
1678 {
1679  if ((direction != 0u) && ((mNavigateX != 0) || (mNavigateY != 0)))
1680  navigateClean();
1681 
1682  if ((direction != 0u) && (settings.moveType >= 2
1683  && settings.moveType <= 4))
1684  {
1685  if (mAction == BeingAction::MOVE)
1686  return;
1687 
1688  unsigned int max;
1689 
1690  if (settings.moveType == 2)
1691  max = 5;
1692  else if (settings.moveType == 4)
1693  max = 1;
1694  else
1695  max = 3;
1696 
1697  if (getMoveState() < max)
1698  {
1699  moveByDirection(direction);
1700  mMoveState ++;
1701  }
1702  else
1703  {
1704  mMoveState = 0;
1705  crazyMoves->crazyMove();
1706  }
1707  }
1708  else
1709  {
1710  setWalkingDir(direction);
1711  }
1712 }
1713 
1714 #ifdef TMWA_SUPPORT
1716 {
1718  return;
1719  if (chatWindow == nullptr ||
1720  !isAlive() ||
1722  {
1723  return;
1724  }
1725 
1726  switch (settings.magicAttackType)
1727  {
1728  // flar W00
1729  case 0:
1730  tryMagic("#flar", 1, 0, 10);
1731  break;
1732  // chiza W01
1733  case 1:
1734  tryMagic("#chiza", 1, 0, 9);
1735  break;
1736  // ingrav W10
1737  case 2:
1738  tryMagic("#ingrav", 2, 2, 20);
1739  break;
1740  // frillyar W11
1741  case 3:
1742  tryMagic("#frillyar", 2, 2, 25);
1743  break;
1744  // upmarmu W12
1745  case 4:
1746  tryMagic("#upmarmu", 2, 2, 20);
1747  break;
1748  default:
1749  break;
1750  }
1751 }
1752 
1753 void LocalPlayer::tryMagic(const std::string &spell, const int baseMagic,
1754  const int schoolMagic, const int mana)
1755 {
1756  if (chatWindow == nullptr)
1757  return;
1758 
1759  if (PlayerInfo::getSkillLevel(340) >= baseMagic
1760  && PlayerInfo::getSkillLevel(342) >= schoolMagic)
1761  {
1763  {
1765  return;
1766 
1767  chatWindow->localChatInput(spell);
1768  }
1769  }
1770 }
1771 #endif // TMWA_SUPPORT
1772 
1774 {
1775  std::string buf;
1776  std::stringstream ss(serverConfig.getValue("playerHomes", ""));
1777 
1778  while (ss >> buf)
1779  {
1780  Vector pos;
1781  ss >> pos.x;
1782  ss >> pos.y;
1783  mHomes[buf] = pos;
1784  }
1785 }
1786 
1787 void LocalPlayer::setMap(Map *const map)
1788 {
1789  BLOCK_START("LocalPlayer::setMap")
1790  if (map != nullptr)
1791  {
1792  if (socialWindow != nullptr)
1794  }
1795  navigateClean();
1796  mCrossX = 0;
1797  mCrossY = 0;
1798 
1799  Being::setMap(map);
1801  BLOCK_END("LocalPlayer::setMap")
1802 }
1803 
1805 {
1806  if ((mMap == nullptr) || (socialWindow == nullptr))
1807  return;
1808 
1809  SpecialLayer *const specialLayer = mMap->getSpecialLayer();
1810 
1811  if (specialLayer == nullptr)
1812  return;
1813 
1814  const std::string key = mMap->getProperty("_realfilename", std::string());
1815  Vector pos = mHomes[key];
1816 
1817  if (mAction == BeingAction::SIT)
1818  {
1819  const std::map<std::string, Vector>::const_iterator
1820  iter = mHomes.find(key);
1821 
1822  if (iter != mHomes.end())
1823  {
1825  CAST_S32(pos.y));
1826  }
1827 
1828  if (iter != mHomes.end() && mX == CAST_S32(pos.x)
1829  && mY == CAST_S32(pos.y))
1830  {
1831  mMap->updatePortalTile("",
1833  CAST_S32(pos.x),
1834  CAST_S32(pos.y),
1835  true);
1836 
1837  mHomes.erase(key);
1839  CAST_S32(pos.y));
1840  }
1841  else
1842  {
1843  if (iter != mHomes.end())
1844  {
1845  specialLayer->setTile(CAST_S32(pos.x),
1846  CAST_S32(pos.y), MapItemType::EMPTY);
1847  specialLayer->updateCache();
1848  }
1849 
1850  pos.x = static_cast<float>(mX);
1851  pos.y = static_cast<float>(mY);
1852  mHomes[key] = pos;
1853  mMap->updatePortalTile("home",
1855  mX,
1856  mY,
1857  true);
1859  }
1860  MapItem *const mapItem = specialLayer->getTile(mX, mY);
1861  if (mapItem != nullptr)
1862  {
1863  const int idx = socialWindow->getPortalIndex(mX, mY);
1865  OutfitWindow::keyName(idx)));
1866  }
1867  saveHomes();
1868  }
1869  else
1870  {
1871  MapItem *mapItem = specialLayer->getTile(mX, mY);
1872  int type = 0;
1873 
1874  const std::map<std::string, Vector>::iterator iter = mHomes.find(key);
1875  if (iter != mHomes.end() && mX == pos.x && mY == pos.y)
1876  {
1877  mHomes.erase(key);
1878  saveHomes();
1879  }
1880 
1881  if ((mapItem == nullptr) || mapItem->getType() == MapItemType::EMPTY)
1882  {
1883  if ((mDirection & BeingDirection::UP) != 0)
1884  type = MapItemType::ARROW_UP;
1885  else if ((mDirection & BeingDirection::LEFT) != 0)
1886  type = MapItemType::ARROW_LEFT;
1887  else if ((mDirection & BeingDirection::DOWN) != 0)
1888  type = MapItemType::ARROW_DOWN;
1889  else if ((mDirection & BeingDirection::RIGHT) != 0)
1890  type = MapItemType::ARROW_RIGHT;
1891  }
1892  else
1893  {
1894  type = MapItemType::EMPTY;
1895  }
1896  mMap->updatePortalTile("",
1897  type,
1898  mX,
1899  mY,
1900  true);
1901 
1902  if (type != MapItemType::EMPTY)
1903  {
1905  mapItem = specialLayer->getTile(mX, mY);
1906  if (mapItem != nullptr)
1907  {
1908  const int idx = socialWindow->getPortalIndex(mX, mY);
1910  OutfitWindow::keyName(idx)));
1911  }
1912  }
1913  else
1914  {
1915  specialLayer->setTile(mX, mY, MapItemType::EMPTY);
1916  specialLayer->updateCache();
1918  }
1919  }
1920 }
1921 
1923 {
1924  std::stringstream ss;
1925 
1926  for (std::map<std::string, Vector>::const_iterator iter = mHomes.begin(),
1927  iter_fend = mHomes.end();
1928  iter != iter_fend;
1929  ++iter)
1930  {
1931  const Vector &pos = (*iter).second;
1932 
1933  if (iter != mHomes.begin())
1934  ss << " ";
1935  ss << (*iter).first << " " << pos.x << " " << pos.y;
1936  }
1937 
1938  serverConfig.setValue("playerHomes", ss.str());
1939 }
1940 
1942 {
1943  const int time = tick_time;
1944  if (mWaitPing == true && mPingSendTick != 0)
1945  {
1946  if (time >= mPingSendTick && (time - mPingSendTick) > 1000)
1947  return;
1948  }
1949 
1950  mPingSendTick = time;
1951  mWaitPing = true;
1953 }
1954 
1955 std::string LocalPlayer::getPingTime() const
1956 {
1957  std::string str;
1958  if (!mWaitPing)
1959  {
1960  if (mPingTime == 0)
1961  str = "?";
1962  else
1963  str = toString(CAST_S32(mPingTime));
1964  }
1965  else
1966  {
1967  time_t time = tick_time;
1968  if (time > mPingSendTick)
1969  time -= mPingSendTick;
1970  else
1971  time += MAX_TICK_VALUE - mPingSendTick;
1972  if (time <= mPingTime)
1973  time = mPingTime;
1974  if (mPingTime != time)
1975  str = strprintf("%d (%d)", CAST_S32(mPingTime), CAST_S32(time));
1976  else
1977  str = toString(CAST_S32(time));
1978  }
1979  return str;
1980 }
1981 
1983 {
1984  if (mWaitPing == true && mPingSendTick > 0)
1985  {
1986  mWaitPing = false;
1987  const int time = tick_time;
1988  if (time < mPingSendTick)
1989  {
1990  mPingSendTick = 0;
1991  mPingTime = 0;
1992  }
1993  else
1994  {
1995  mPingTime = (time - mPingSendTick) * 10;
1996  }
1997  }
1998 }
1999 
2001 {
2002  if (mPingSendTick == 0 || tick_time < mPingSendTick
2003  || (tick_time - mPingSendTick) > 200)
2004  {
2005  pingRequest();
2006  }
2007 }
2008 
2009 
2010 void LocalPlayer::setAway(const std::string &message) const
2011 {
2012  setAfkMessage(message);
2014  updateStatus();
2015 }
2016 
2017 void LocalPlayer::setAfkMessage(std::string message)
2018 {
2019  if (!message.empty())
2020  {
2021  if (message.size() > 4 && message.substr(0, 4) == "/me ")
2022  {
2023  message = message.substr(4);
2024  config.setValue("afkFormat", 1);
2025  }
2026  else
2027  {
2028  config.setValue("afkFormat", 0);
2029  }
2030  serverConfig.setValue("afkMessage", message);
2031  }
2032 }
2033 
2034 void LocalPlayer::setPseudoAway(const std::string &message)
2035 {
2036  setAfkMessage(message);
2038 }
2039 
2040 void LocalPlayer::afkRespond(ChatTab *const tab, const std::string &nick)
2041 {
2042  if (settings.awayMode)
2043  {
2044  const time_t time = cur_time;
2045  if (mAfkTime == 0 || time < mAfkTime
2046  || time - mAfkTime > awayLimitTimer)
2047  {
2048  std::string str(serverConfig.getValue("afkMessage",
2049  "I am away from keyboard."));
2050  if (str.find("'NAME'") != std::string::npos)
2051  replaceAll(str, "'NAME'", nick);
2052 
2053  std::string msg("*AFK*: " + str);
2054 
2055  if (config.getIntValue("afkFormat") == 1)
2056  msg = "*" + msg + "*";
2057 
2058  if (tab == nullptr)
2059  {
2060  chatHandler->privateMessage(nick, msg);
2061  if (localChatTab != nullptr)
2062  {
2063  localChatTab->chatLog(std::string(mName).append(
2064  " : ").append(msg),
2068  }
2069  }
2070  else
2071  {
2072  if (tab->getNoAway())
2073  return;
2074  chatHandler->privateMessage(nick, msg);
2075  tab->chatLog(mName, msg);
2076  }
2077  mAfkTime = time;
2078  }
2079  }
2080 }
2081 
2082 bool LocalPlayer::navigateTo(const int x, const int y)
2083 {
2084  if (mMap == nullptr)
2085  return false;
2086 
2087  SpecialLayer *const tmpLayer = mMap->getTempLayer();
2088  if (tmpLayer == nullptr)
2089  return false;
2090 
2091  mShowNavigePath = true;
2092  mOldX = mPixelX;
2093  mOldY = mPixelY;
2094  mOldTileX = mX;
2095  mOldTileY = mY;
2096  mNavigateX = x;
2097  mNavigateY = y;
2099 
2101  (mPixelX - mapTileSize / 2) / mapTileSize,
2103  x,
2104  y,
2105  getBlockWalkMask(),
2106  0);
2107 
2108  if (mDrawPath)
2109  tmpLayer->addRoad(mNavigatePath);
2110  return !mNavigatePath.empty();
2111 }
2112 
2114 {
2115  if (mMap == nullptr)
2116  return;
2117 
2118  mShowNavigePath = false;
2119  mOldX = 0;
2120  mOldY = 0;
2121  mOldTileX = 0;
2122  mOldTileY = 0;
2123  mNavigateX = 0;
2124  mNavigateY = 0;
2126 
2127  mNavigatePath.clear();
2128 
2129  SpecialLayer *const tmpLayer = mMap->getTempLayer();
2130  if (tmpLayer == nullptr)
2131  return;
2132 
2133  tmpLayer->clean();
2134 }
2135 
2137 {
2138  if (mMap != nullptr)
2139  {
2140  std::string str = mMap->getObjectData(mX, mY, MapItemType::MUSIC);
2141  if (str.empty())
2142  str = mMap->getMusicFile();
2143  if (str != soundManager.getCurrentMusicFile())
2144  {
2145  if (str.empty())
2146  soundManager.fadeOutMusic(1000);
2147  else
2148  soundManager.fadeOutAndPlayMusic(str, 1000);
2149  }
2150  }
2151 }
2152 
2154 {
2156 
2157  // probably map not loaded.
2158  if ((mPixelX == 0) || (mPixelY == 0))
2159  return;
2160 
2161  if (mX != mOldTileX || mY != mOldTileY)
2162  {
2163  if (socialWindow != nullptr)
2166  updateMusic();
2167  }
2168 
2169  if ((mMap != nullptr) && (mX != mOldTileX || mY != mOldTileY))
2170  {
2171  SpecialLayer *const tmpLayer = mMap->getTempLayer();
2172  if (tmpLayer == nullptr)
2173  return;
2174 
2175  const int x = (mPixelX - mapTileSize / 2) / mapTileSize;
2176  const int y = (mPixelY - mapTileSize) / mapTileSize;
2177  if (mNavigateId != BeingId_zero)
2178  {
2179  if (actorManager == nullptr)
2180  {
2181  navigateClean();
2182  return;
2183  }
2184 
2185  const Being *const being = actorManager
2187  if (being == nullptr)
2188  {
2189  navigateClean();
2190  return;
2191  }
2192  mNavigateX = being->mX;
2193  mNavigateY = being->mY;
2194  }
2195 
2196  if (mNavigateX == x && mNavigateY == y)
2197  {
2198  navigateClean();
2199  return;
2200  }
2201  for (Path::const_iterator i = mNavigatePath.begin(),
2202  i_fend = mNavigatePath.end();
2203  i != i_fend;
2204  ++i)
2205  {
2206  if ((*i).x == mX && (*i).y == mY)
2207  {
2208  mNavigatePath.pop_front();
2209  fixPos();
2210  break;
2211  }
2212  }
2213  if (mDrawPath && mShowNavigePath)
2214  {
2215  tmpLayer->clean();
2216  tmpLayer->addRoad(mNavigatePath);
2217  }
2218  }
2219  mOldX = mPixelX;
2220  mOldY = mPixelY;
2221  mOldTileX = mX;
2222  mOldTileY = mY;
2223 }
2224 
2226 {
2227 /*
2228  if (mKeepAttacking)
2229  {
2230  if (mTarget && mServerAttack == Keep_true)
2231  {
2232  logger->log("LocalPlayer::targetMoved0");
2233  if (!PacketLimiter::limitPackets(PacketType::PACKET_ATTACK))
2234  return;
2235  logger->log("LocalPlayer::targetMoved");
2236  playerHandler->attack(mTarget->getId(), mServerAttack);
2237  }
2238  }
2239 */
2240 }
2241 
2242 int LocalPlayer::getPathLength(const Being *const being) const
2243 {
2244  if ((mMap == nullptr) || (being == nullptr))
2245  return 0;
2246 
2247  if (being->mX == mX && being->mY == mY)
2248  return 0;
2249 
2250  if (being->mX - 1 <= mX &&
2251  being->mX + 1 >= mX &&
2252  being->mY - 1 <= mY &&
2253  being->mY + 1 >= mY)
2254  {
2255  return 1;
2256  }
2257 
2259  {
2260  const Path debugPath = mMap->findPath(
2261  (mPixelX - mapTileSize / 2) / mapTileSize,
2263  being->mX,
2264  being->mY,
2265  getBlockWalkMask(),
2266  0);
2267  return CAST_S32(debugPath.size());
2268  }
2269 
2270  const int dx = CAST_S32(abs(being->mX - mX));
2271  const int dy = CAST_S32(abs(being->mY - mY));
2272  if (dx > dy)
2273  return dx;
2274  return dy;
2275 }
2276 
2278 {
2279  int range = getAttackRange();
2280  if (range == 1)
2281  range = 2;
2282  return range;
2283 }
2284 
2285 void LocalPlayer::attack2(Being *const target,
2286  const bool keep,
2287  const bool dontChangeEquipment)
2288 {
2289  if (!dontChangeEquipment && (target != nullptr))
2291 
2292  const bool broken = (Net::getNetworkType() == ServerType::TMWATHENA);
2293 
2294  // probably need cache getPathLength(target)
2295  if ((target == nullptr ||
2296  settings.attackType == 0 ||
2297  settings.attackType == 3) ||
2298  (withinAttackRange(target, broken, broken ? 1 : 0) &&
2299  getPathLength(target) <= getAttackRange2()))
2300  {
2301  attack(target, keep, false);
2302  if (settings.attackType == 2)
2303  {
2304  if (target == nullptr)
2305  {
2306  if (pickUpItems(0))
2307  return;
2308  }
2309  else
2310  {
2311  pickUpItems(3);
2312  }
2313  }
2314  }
2315  else if (mPickUpTarget == nullptr)
2316  {
2317  if (settings.attackType == 2)
2318  {
2319  if (pickUpItems(0))
2320  return;
2321  }
2322  setTarget(target);
2323  if (target->getType() != ActorType::Npc)
2324  {
2325  mKeepAttacking = true;
2326  moveToTarget(-1);
2327  }
2328  }
2329 }
2330 
2331 void LocalPlayer::setFollow(const std::string &player)
2332 {
2333  mPlayerFollowed = player;
2334  if (!mPlayerFollowed.empty())
2335  {
2336  // TRANSLATORS: follow command message
2337  std::string msg = strprintf(_("Follow: %s"), player.c_str());
2338  debugMsg(msg);
2339  }
2340  else
2341  {
2342  // TRANSLATORS: follow command message
2343  debugMsg(_("Follow canceled"));
2344  }
2345 }
2346 
2347 void LocalPlayer::setImitate(const std::string &player)
2348 {
2349  mPlayerImitated = player;
2350  if (!mPlayerImitated.empty())
2351  {
2352  // TRANSLATORS: imitate command message
2353  std::string msg = strprintf(_("Imitation: %s"), player.c_str());
2354  debugMsg(msg);
2355  }
2356  else
2357  {
2358  // TRANSLATORS: imitate command message
2359  debugMsg(_("Imitation canceled"));
2360  }
2361 }
2362 
2364 {
2365  if (!mPlayerFollowed.empty())
2366  {
2367  // TRANSLATORS: cancel follow message
2368  debugMsg(_("Follow canceled"));
2369  }
2370  if (!mPlayerImitated.empty())
2371  {
2372  // TRANSLATORS: cancel follow message
2373  debugMsg(_("Imitation canceled"));
2374  }
2375  mPlayerFollowed.clear();
2376  mPlayerImitated.clear();
2377 }
2378 
2379 void LocalPlayer::imitateEmote(const Being *const being,
2380  const unsigned char action) const
2381 {
2382  if (being == nullptr)
2383  return;
2384 
2385  std::string player_imitated = getImitate();
2386  if (!player_imitated.empty() && being->mName == player_imitated)
2387  emote(action);
2388 }
2389 
2390 void LocalPlayer::imitateAction(const Being *const being,
2391  const BeingActionT &action)
2392 {
2393  if (being == nullptr)
2394  return;
2395 
2396  if (!mPlayerImitated.empty() && being->mName == mPlayerImitated)
2397  {
2398  setAction(action, 0);
2399  playerHandler->changeAction(action);
2400  }
2401 }
2402 
2403 void LocalPlayer::imitateDirection(const Being *const being,
2404  const unsigned char dir)
2405 {
2406  if (being == nullptr)
2407  return;
2408 
2409  if (!mPlayerImitated.empty() && being->mName == mPlayerImitated)
2410  {
2412  return;
2413 
2414  if (settings.followMode == 2)
2415  {
2416  uint8_t dir2 = 0;
2417  if ((dir & BeingDirection::LEFT) != 0)
2418  dir2 |= BeingDirection::RIGHT;
2419  else if ((dir & BeingDirection::RIGHT) != 0)
2420  dir2 |= BeingDirection::LEFT;
2421  if ((dir & BeingDirection::UP) != 0)
2422  dir2 |= BeingDirection::DOWN;
2423  else if ((dir & BeingDirection::DOWN) != 0)
2424  dir2 |= BeingDirection::UP;
2425 
2426  setDirection(dir2);
2427  playerHandler->setDirection(dir2);
2428  }
2429  else
2430  {
2431  setDirection(dir);
2433  }
2434  }
2435 }
2436 
2437 void LocalPlayer::imitateOutfit(const Being *const player,
2438  const int sprite) const
2439 {
2440  if (player == nullptr)
2441  return;
2442 
2443  if (settings.imitationMode == 1 &&
2444  !mPlayerImitated.empty() &&
2445  player->mName == mPlayerImitated)
2446  {
2447  if (sprite < 0 || sprite >= player->getNumberOfLayers())
2448  return;
2449 
2450  const AnimatedSprite *const equipmentSprite
2451  = dynamic_cast<const AnimatedSprite *>(
2452  player->mSprites[sprite]);
2453 
2454  if (equipmentSprite != nullptr)
2455  {
2456 // logger->log("have equipmentSprite");
2457  const Inventory *const inv = PlayerInfo::getInventory();
2458  if (inv == nullptr)
2459  return;
2460 
2461  const std::string &path = equipmentSprite->getIdPath();
2462  if (path.empty())
2463  return;
2464 
2465 // logger->log("idPath: " + path);
2466  const Item *const item = inv->findItemBySprite(path,
2467  player->getGender(), player->getSubType());
2468  if ((item != nullptr) && item->isEquipped() == Equipped_false)
2470  }
2471  else
2472  {
2473 // logger->log("have unequip %d", sprite);
2474  const int equipmentSlot = inventoryHandler
2475  ->convertFromServerSlot(sprite);
2476 // logger->log("equipmentSlot: " + toString(equipmentSlot));
2477  if (equipmentSlot == inventoryHandler->getProjectileSlot())
2478  return;
2479 
2480  const Item *const item = PlayerInfo::getEquipment(equipmentSlot);
2481  if (item != nullptr)
2482  {
2483 // logger->log("unequiping");
2485  }
2486  }
2487  }
2488 }
2489 
2490 void LocalPlayer::followMoveTo(const Being *const being,
2491  const int x, const int y)
2492 {
2493  if ((being != nullptr) &&
2494  !mPlayerFollowed.empty() &&
2495  being->mName == mPlayerFollowed)
2496  {
2497  mPickUpTarget = nullptr;
2498  navigateTo(x, y);
2499  }
2500 }
2501 
2502 void LocalPlayer::followMoveTo(const Being *const being,
2503  const int x1, const int y1,
2504  const int x2, const int y2)
2505 {
2506  if (being == nullptr)
2507  return;
2508 
2509  mPickUpTarget = nullptr;
2510  if (!mPlayerFollowed.empty() &&
2511  being->mName == mPlayerFollowed)
2512  {
2513  switch (settings.followMode)
2514  {
2515  case 0:
2516  navigateTo(x1, y1);
2517  setNextDest(x2, y2);
2518  break;
2519  case 1:
2520  if (x1 != x2 || y1 != y2)
2521  {
2522  navigateTo(mX + x2 - x1, mY + y2 - y1);
2523  setNextDest(mX + x2 - x1, mY + y2 - y1);
2524  }
2525  break;
2526  case 2:
2527  if (x1 != x2 || y1 != y2)
2528  {
2529  navigateTo(mX + x1 - x2, mY + y1 - y2);
2530  setNextDest(mX + x1 - x2, mY + y1 - y2);
2531  }
2532  break;
2533  case 3:
2534  if (mTarget == nullptr ||
2536  {
2537  if (actorManager != nullptr)
2538  {
2539  Being *const b = actorManager->findBeingByName(
2541  setTarget(b);
2542  }
2543  }
2544  moveToTarget(-1);
2545  setNextDest(x2, y2);
2546  break;
2547  default:
2548  break;
2549  }
2550  }
2551 }
2552 
2553 void LocalPlayer::setNextDest(const int x, const int y)
2554 {
2555  mNextDestX = x;
2556  mNextDestY = y;
2557 }
2558 
2560 {
2561  if (mIsServerBuggy)
2562  {
2563  if (mLastAction != -1)
2564  return false;
2566  }
2567  return true;
2568 }
2569 
2571 {
2572  if ((mCrossX == 0) && (mCrossY == 0))
2573  return;
2574 
2575  const int dx = abs(mX - mCrossX);
2576  const int dy = abs(mY - mCrossY);
2577  const int dist = dx > dy ? dx : dy;
2578  const time_t time = cur_time;
2579  const int maxDist = mSyncPlayerMove ? mSyncPlayerMoveDistance : 7;
2580 
2581  if (dist > maxDist)
2582  {
2583  mActivityTime = time;
2585 // alternative way to fix, move to real position
2586 // setDestination(mCrossX, mCrossY);
2587  }
2588 }
2589 
2590 void LocalPlayer::setRealPos(const int x, const int y)
2591 {
2592  if (mMap == nullptr)
2593  return;
2594 
2595  SpecialLayer *const layer = mMap->getTempLayer();
2596  if (layer != nullptr)
2597  {
2598  bool cacheUpdated(false);
2599  if (((mCrossX != 0) || (mCrossY != 0)) &&
2600  (layer->getTile(mCrossX, mCrossY) != nullptr) &&
2602  {
2604  layer->updateCache();
2605  cacheUpdated = true;
2606  }
2607 
2608  if (mShowServerPos)
2609  {
2610  const MapItem *const mapItem = layer->getTile(x, y);
2611 
2612  if (mapItem == nullptr ||
2613  mapItem->getType() == MapItemType::EMPTY)
2614  {
2615  if (mX != x && mY != y)
2616  {
2617  layer->setTile(x, y, MapItemType::CROSS);
2618  if (cacheUpdated == false)
2619  layer->updateCache();
2620  }
2621  }
2622  }
2623 
2624  if (mCrossX != x || mCrossY != y)
2625  {
2626  mCrossX = x;
2627  mCrossY = y;
2628  }
2629  }
2630  if (mMap->isCustom())
2631  mMap->setWalk(x, y);
2632 }
2634 {
2635  if ((mMap == nullptr) || (mTarget == nullptr))
2636  return;
2637 
2638  if (settings.moveToTargetType == 11 || (settings.attackType == 0u)
2639  || !config.getBoolValue("autofixPos"))
2640  {
2641  return;
2642  }
2643 
2644  const Path debugPath = mMap->findPath(
2645  (mPixelX - mapTileSize / 2) / mapTileSize,
2647  mTarget->mX,
2648  mTarget->mY,
2649  getBlockWalkMask(),
2650  0);
2651 
2652  if (!debugPath.empty())
2653  {
2654  const Path::const_iterator i = debugPath.begin();
2655  setDestination((*i).x, (*i).y);
2656  }
2657 }
2658 
2660 {
2661  navigateClean();
2662 }
2663 
2665 {
2667 }
2668 
2670 {
2671  if (mMap != nullptr)
2672  {
2673  const std::map<std::string, Vector>::const_iterator iter =
2674  mHomes.find(mMap->getProperty("_realfilename", std::string()));
2675 
2676  if (iter != mHomes.end())
2677  {
2678  const Vector &pos = mHomes[(*iter).first];
2679  if ((pos.x != 0.0f) && (pos.y != 0.0f))
2680  {
2682  CAST_S32(pos.x), CAST_S32(pos.y));
2683  }
2684  }
2685  }
2686 }
2687 
2689  const int y A_UNUSED)
2690 {
2691 }
2692 
2693 void LocalPlayer::waitFor(const std::string &nick)
2694 {
2695  mWaitFor = nick;
2696 }
2697 
2699 {
2700  if (being == nullptr)
2701  return;
2702 
2703  const std::string &nick = being->mName;
2704  if (being->getType() == ActorType::Player)
2705  {
2706  const Guild *const guild = getGuild();
2707  if (guild != nullptr)
2708  {
2709  const GuildMember *const gm = guild->getMember(nick);
2710  if (gm != nullptr)
2711  {
2712  const int level = gm->getLevel();
2713  if (level > 1 && being->getLevel() != level)
2714  {
2715  being->setLevel(level);
2716  being->updateName();
2717  }
2718  }
2719  }
2720  if (chatWindow != nullptr)
2721  {
2722  WhisperTab *const tab = chatWindow->getWhisperTab(nick);
2723  if (tab != nullptr)
2724  tab->setWhisperTabColors();
2725  }
2726  }
2727 
2728  if (!mWaitFor.empty() && mWaitFor == nick)
2729  {
2730  // TRANSLATORS: wait player/monster message
2731  debugMsg(strprintf(_("You see %s"), mWaitFor.c_str()));
2733  mWaitFor.clear();
2734  }
2735 }
2736 
2737 unsigned char LocalPlayer::getBlockWalkMask() const
2738 {
2739  // for now blocking all types of collisions
2740  return BlockMask::WALL |
2741  BlockMask::AIR |
2744 }
2745 
2747 {
2748  if (mMap == nullptr)
2749  return;
2750 
2751  const std::string key = mMap->getProperty("_realfilename", std::string());
2752  const std::map<std::string, Vector>::iterator iter = mHomes.find(key);
2753 
2754  if (iter != mHomes.end())
2755  mHomes.erase(key);
2756 }
2757 
2759 {
2760  mBlockAdvert = true;
2761 }
2762 
2764 {
2765  if (target == nullptr)
2766  return false;
2767 
2768  switch (settings.pvpAttackType)
2769  {
2770  case 0:
2771  return true;
2772  case 1:
2773  return !(playerRelations.getRelation(target->mName)
2774  == Relation::FRIEND);
2775  case 2:
2776  return playerRelations.checkBadRelation(target->mName);
2777  default:
2778  case 3:
2779  return false;
2780  }
2781 }
2782 
2784 {
2786  {
2787  uint8_t status = 0;
2789  {
2790  if (mTradebot &&
2791  shopWindow != nullptr &&
2792  !shopWindow->isShopEmpty())
2793  {
2794  status |= BeingFlag::SHOP;
2795  }
2796  }
2798  status |= BeingFlag::AWAY;
2799 
2800  if (mInactive)
2801  status |= BeingFlag::INACTIVE;
2802 
2803  playerHandler->updateStatus(status);
2804  }
2805 }
2806 
2807 void LocalPlayer::setTestParticle(const std::string &fileName,
2808  const bool updateHash)
2809 {
2812  if (mTestParticle != nullptr)
2813  {
2815  mTestParticle = nullptr;
2816  }
2817  if (!fileName.empty())
2818  {
2819  mTestParticle = particleEngine->addEffect(fileName, 0, 0, 0);
2821  if (updateHash)
2823  }
2824 }
2825 
2827 {
2828  if (mAction != BeingAction::DEAD)
2829  {
2832  }
2833 }
2834 
2836 {
2837  return !mFreezed &&
2841 }
2842 
2843 void LocalPlayer::freezeMoving(const int timeWaitTicks)
2844 {
2845  if (timeWaitTicks <= 0)
2846  return;
2847  const int nextTime = tick_time + timeWaitTicks;
2848  if (mUnfreezeTime < nextTime)
2849  mUnfreezeTime = nextTime;
2850  if (mUnfreezeTime > 0)
2851  mFreezed = true;
2852 }
Net::PlayerHandler * playerHandler
Definition: net.cpp:92
void setDistance(const int n)
Definition: being.h:554
Pickup ::T PickupT
Definition: pickup.h:43
void imitateEmote(const Being *const being, const unsigned char action) const
virtual void emote(const uint8_t emoteId) const =0
int getTileY() const
Definition: flooritem.h:95
ChatWindow * chatWindow
Definition: chatwindow.cpp:89
#define FOR_EACH(type, iter, array)
Definition: foreach.h:24
time_t mActivityTime
Definition: localplayer.h:484
std::string mTestParticleName
Definition: localplayer.h:500
void updatePortalTile(const std::string &name, const int type, const int x, const int y, const bool addNew)
Definition: map.cpp:1307
void updateActiveList()
#define _(s)
Definition: gettext.h:34
const Color & getColor(UserColorIdT type, const unsigned int alpha)
Definition: userpalette.h:159
void move(const int dX, const int dY)
virtual void updateStatus(const uint8_t status) const =0
std::vector< std::string > mMessages
Definition: deaddb.cpp:35
void log1(const char *const log_text)
Definition: logger.cpp:233
void localChatInput(const std::string &msg) const
Definition: chatwindow.cpp:686
void updateCoords()
Being * setNewTarget(const ActorTypeT type, const AllowSort allowSort)
time_t mAfkTime
Definition: localplayer.h:483
bool mAttackMoving
Definition: localplayer.h:518
int getNumberOfLayers() const
Definition: being.h:376
void setStatMod(const AttributesT id, const int value, const Notify notify)
Definition: playerinfo.cpp:157
BeingAction ::T BeingActionT
Definition: beingaction.h:40
void showGMTab()
void setShowName(const bool doShowName)
Definition: being.cpp:1165
unsigned int attackType
Definition: settings.h:135
Definition: party.h:61
void setDestination(const int x, const int y)
volatile int tick_time
Definition: timer.cpp:52
Particle * addEffect(const std::string &particleEffectFile, const int pixelX, const int pixelY, const int rotation)
ChatTab * localChatTab
Definition: chattab.cpp:61
FloorItem * findItem(const BeingId id) const
Gui * gui
Definition: gui.cpp:110
void setWalkingDir(const unsigned char dir)
void equipItem(const Item *const item, const Sfx sfx)
Definition: playerinfo.cpp:235
virtual bool haveKillerId() const =0
void attack(Being *const target, const bool keep, const bool dontChangeEquipment)
std::pair< std::string, UserColorIdT > MessagePair
Definition: localplayer.h:474
void stopAdvert()
void setDestination(const int dstX, const int dstY)
Definition: being.cpp:539
unsigned int getMoveState() const
Definition: localplayer.h:192
unsigned int followMode
Definition: settings.h:133
void loadGMCommands()
Definition: chatwindow.cpp:259
RelationT getRelation(const std::string &name) const
bool mNextStep
Definition: localplayer.h:522
void setStatBase(const AttributesT id, const int value, const Notify notify)
Definition: playerinfo.cpp:141
std::map< std::string, Vector > mHomes
Definition: localplayer.h:458
#define N_(s)
Definition: gettext.h:35
ParticleList mChildParticleEffects
Definition: actorsprite.h:244
PRAGMA45(GCC diagnostic push) PRAGMA45(GCC diagnostic ignored "-Wunused-result") int TestLauncher
Equipment * getEquipment()
Definition: playerinfo.cpp:217
void setTileCoords(const int x, const int y)
Definition: being.cpp:5010
void tryPingRequest()
std::string fileName
Definition: testmain.cpp:38
uint8_t mDirection
Definition: being.h:1234
const bool Keep_true
Definition: keep.h:29
static const int16_t awayLimitTimer
virtual void attack(const BeingId id, const Keep keep) const =0
bool pickUpAll(const int x1, const int y1, const int x2, const int y2, const bool serverBuggy) const
int getId() const
Definition: iteminfo.h:67
int mLastTargetX
Definition: localplayer.h:455
int y
Definition: position.h:45
Item * findItem(const int itemId, const ItemColor color) const
Definition: inventory.cpp:93
GenderT getGender() const
Definition: being.h:625
void setMap(Map *const map)
const Color * mNameColor
Definition: being.h:1195
Equipped isEquipped() const
Definition: item.h:128
bool pickUpNearest(const int x, const int y, int maxdist) const
Definition: vector.h:38
BeingId getId() const
Definition: actorsprite.h:63
unsigned int attackWeaponType
Definition: settings.h:134
void addHomunXpMessage(const int change)
int getTileX() const
Definition: flooritem.h:92
void deliverMessage() const
void setRealPos(const int x, const int y)
TargetCursorType ::T TargetCursorTypeT
bool mAttackNext
Definition: localplayer.h:519
const std::string getMusicFile() const
Definition: map.cpp:830
void saveHomes()
int getAttackRange2() const
int getLevel() const
virtual bool canUseMagic() const =0
void updateStatus() const
#define CAST_U64
Definition: cast.h:32
Inventory * getInventory()
Definition: playerinfo.cpp:192
void stopWalking(const bool sendToServer)
const bool Sfx_false
Definition: sfx.h:29
bool mIsGM
Definition: being.h:1238
void setTestParticle(const std::string &fileName, const bool updateHash)
int mUnfreezeTime
Definition: localplayer.h:504
bool mGoingToTarget
Definition: localplayer.h:524
int mPingSendTick
Definition: localplayer.h:481
#define BLOCK_START(name)
Definition: perfomance.h:78
unsigned char getBlockWalkMask() const A_CONST
const std::string getProperty(const std::string &name, const std::string &def) const
Definition: properties.h:58
SoundManager soundManager
void loadHomes()
ParticleEngine * particleEngine
Configuration config
Path mPath
Definition: being.h:1200
Particle * mTestParticle
Definition: localplayer.h:499
const bool Sfx_true
Definition: sfx.h:29
void setFollow(const std::string &player)
int BeingId
Definition: beingid.h:29
int mLevel
Definition: being.h:1223
void setGotoTarget(Being *const target)
std::string & replaceAll(std::string &context, const std::string &from, const std::string &to)
void fadeOutMusic(const int ms)
void setMap(Map *const map)
Definition: being.cpp:5023
void setName(const std::string &name)
Definition: mapitem.h:76
bool limitPackets(const PacketTypeT type)
void cancelFollow()
bool mWaitPing
Definition: localplayer.h:529
OkDialog * weightNotice
unsigned long mTestParticleHash
Definition: localplayer.h:502
Font * getInfoParticleFont() const
Definition: gui.h:184
StatusWindow * statusWindow
void setImitate(const std::string &player)
#define BLOCK_END(name)
Definition: perfomance.h:79
const bool TryRemoveColors_true
void chatLog(std::string line, ChatMsgTypeT own, const IgnoreRecord ignoreRecord, const TryRemoveColors tryRemoveColors)
Definition: chattab.cpp:110
AnimatedSprite * getIcon() const
ActorType ::T ActorTypeT
Definition: actortype.h:42
uint16_t ItemColor
Definition: itemcolor.h:29
bool msg(InputEvent &event)
Definition: chat.cpp:38
const ItemColor ItemColor_zero
Definition: itemcolor.h:29
int getIntValue(const std::string &key) const
void updateMusic() const
const Color * mTextColor
Definition: being.h:1202
void setWalk(const int x, const int y)
Definition: map.cpp:802
void pickUpItem(const FloorItem *const item, const Sfx sfx)
Definition: playerinfo.cpp:363
void addPortal(const int x, const int y)
Guild * getGuild() const
Definition: being.cpp:1279
const std::string & getName() const
Definition: iteminfo.h:73
#define delete2(var)
Definition: delete2.h:24
virtual void nextTile()
Definition: being.cpp:1781
const WalkLayer * getWalkLayer() const
Definition: map.h:346
Configuration serverConfig
void setGroupId(const int id)
void slowLogic()
int getLevel() const
Definition: avatar.h:113
bool mSyncPlayerMove
Definition: localplayer.h:516
void setY(const int y)
Definition: avatar.h:134
uint32_t party
Being * mTarget
Definition: localplayer.h:460
void setTile(const int x, const int y, MapItem *const item)
Particle * addTextRiseFadeOutEffect(const std::string &text, const int x, const int y, const Color *const color, Font *const font, const bool outline)
WhisperTab * getWhisperTab(const std::string &nick) const
void moveToTarget(int dist)
virtual void updateCoords()
Definition: being.cpp:2503
void pickedUp(const ItemInfo &itemInfo, const int amount, const ItemColor color, const BeingId floorItemId, const PickupT fail)
void addPortalTile(const std::string &name, const int type, const int x, const int y)
Definition: map.cpp:1294
bool mBlockAdvert
Definition: localplayer.h:508
Vector mDest
Definition: being.h:1204
const bool Equipped_false
Definition: equipped.h:29
void addListener(const std::string &key, ConfigListener *const listener)
bool mIsServerBuggy
Definition: localplayer.h:515
UserPalette * userPalette
Definition: userpalette.cpp:33
static bool checAttackPermissions(const Being *const target)
GuildMember * getMember(const BeingId id) const
Definition: guild.cpp:139
bool mTargetOnlyReachable
Definition: localplayer.h:514
void addXpMessage(const int64_t change)
std::string getCurrentMusicFile() const
Definition: soundmanager.h:134
void addSpMessage(const int change)
std::vector< int > WeaponsInfos
Definition: weaponsdb.h:28
std::string mLastHitFrom
Definition: localplayer.h:496
void removeHome()
unsigned int moveToTargetType
Definition: settings.h:132
bool pickUpItems(int pickUpType)
Logger * logger
Definition: logger.cpp:88
LocalPlayer(const BeingId id, const BeingTypeId subType)
unsigned int imitationMode
Definition: settings.h:140
int get_elapsed_time(const int startTime)
Definition: timer.cpp:93
void freezeMoving(const int timeWaitTicks)
std::string mName
Definition: being.h:1183
bool Keep
Definition: keep.h:29
virtual bool haveMoveWhileSit() const =0
std::string getObjectData(const unsigned x, const unsigned y, const int type) const
Definition: map.cpp:1387
void controlCustomParticle(Particle *const particle)
virtual void setDirection(const uint8_t direction)
Definition: being.cpp:1689
#define new
Definition: debug_new.h:147
Settings settings
Definition: settings.cpp:31
void addMessageToQueue(const std::string &message, const UserColorIdT color)
void setAway(const std::string &message) const
ReachableT getReachable() const
Definition: being.h:609
static std::string getKeyShortString(const std::string &key)
std::list< MessagePair > mMessages
Definition: localplayer.h:476
bool mInactive
Definition: being.h:1386
MiniStatusWindow * miniStatusWindow
std::string mPlayerImitated
Definition: localplayer.h:464
Net::ServerFeatures * serverFeatures
Definition: net.cpp:97
bool getBoolValue(const std::string &key) const
int mPixelX
Definition: actor.h:132
virtual bool havePlayerStatusUpdate() const =0
void attack2(Being *const target, const bool keep, const bool dontChangeEquipment)
void optionChanged(const std::string &value)
const WeaponsInfos & getBows()
Definition: weaponsdb.cpp:72
void attributeChanged(const AttributesT id, const int64_t oldVal, const int64_t newVal)
virtual void privateMessage(const std::string &recipient, const std::string &text) const =0
virtual void setDirection(const unsigned char direction) const =0
void updateCache()
#define CAST_S32
Definition: cast.h:29
bool isReachable(Being *const being, const int maxCost)
const Color & getColor(const ThemeColorIdT type, const unsigned int alpha)
Definition: theme.h:135
void afkRespond(ChatTab *const tab, const std::string &nick)
int getType() const
Definition: mapitem.h:54
std::string strprintf(const char *const format,...)
Definition: stringutils.cpp:99
void setWhisperTabColors()
Definition: whispertab.cpp:122
virtual int getProjectileSlot() const =0
int getPortalIndex(const int x, const int y)
#define fromBool(val, name)
Definition: booldefines.h:48
int mX
Definition: being.h:1317
bool info(InputEvent &event)
Definition: commands.cpp:56
void eraseIcon(const int index)
int mMessageTime
Definition: localplayer.h:477
void navigateClean()
CrazyMoves * crazyMoves
Definition: crazymoves.cpp:46
int getAttackRange() const
Definition: iteminfo.h:197
void pingResponse()
void updateAttackAi(const BeingId targetId, const Keep keep)
Definition: playerinfo.cpp:614
static Party * getParty(const int16_t id)
Definition: party.cpp:312
void targetMoved() const
void setTargetType(const TargetCursorTypeT type)
LocalPlayer * localPlayer
SpecialLayer * getSpecialLayer() const
Definition: map.h:240
time_t mPingTime
Definition: localplayer.h:482
void addJobMessage(const int64_t change)
bool getShowMsg() const
Definition: flooritem.h:107
MapItem * getTile(const int x, const int y) const
bool isAlive() const
Definition: being.h:487
std::vector< Sprite * > mSprites
const bool IgnoreRecord_false
Definition: ignorerecord.h:29
virtual void changeAction(const BeingActionT &action) const =0
std::string getIdPath() const
Being * findBeing(const BeingId id) const
static void setAfkMessage(std::string message)
Definition: item.h:48
void moveByDirection(const unsigned char dir)
Net::BeingHandler * beingHandler
Definition: net.cpp:95
void stopAttack(const bool keepAttack)
unsigned int pvpAttackType
Definition: settings.h:139
PartyMember * getMember(const BeingId id) const
Definition: party.cpp:98
#define nullptr
Definition: localconsts.h:44
time_t weightNoticeTime
virtual void setDestination(const int x, const int y, const int direction) const =0
const std::string & getImitate() const
Definition: localplayer.h:338
ActorTypeT getType() const
Definition: being.h:115
void magicAttack() const
unsigned int moveType
Definition: settings.h:130
void setShowMsg(const bool n)
Definition: flooritem.h:110
Path mNavigatePath
Definition: localplayer.h:494
bool checkForPickup(const FloorItem *const item) const
void playerDeath()
void postInit(const BeingTypeId subType, Map *const map)
Definition: being.cpp:288
bool navigateTo(const int x, const int y)
void setReachable(const ReachableT n)
Definition: being.h:606
void imitateOutfit(const Being *const player, const int sprite) const
void updateNavigateList()
unsigned char mWalkingDir
Definition: localplayer.h:505
const Item * findItemBySprite(std::string spritePath, const GenderT gender, const BeingTypeId race) const
Definition: inventory.cpp:317
const ItemInfo & getInfo() const
Definition: item.h:170
static bool emote(const uint8_t emotion)
int mLastTargetY
Definition: localplayer.h:456
void failMove(const int x, const int y)
void playSFX() const
virtual void setAction(const BeingActionT &action, const int attackId)
Definition: being.cpp:1553
volatile time_t cur_time
Definition: timer.cpp:57
void untarget()
int mPixelY
Definition: actor.h:133
bool mTargetDeadPlayers
Definition: localplayer.h:509
bool mShowNavigePath
Definition: localplayer.h:530
SocialWindow * socialWindow
time_t mTestParticleTime
Definition: localplayer.h:501
bool getWalk(const int x, const int y, const unsigned char blockWalkMask) const
Definition: map.cpp:780
void imitateDirection(const Being *const being, const unsigned char dir)
int mActionTime
Definition: being.h:1216
Net::InventoryHandler * inventoryHandler
Definition: net.cpp:85
void addRoad(const Path &road)
Definition: map.h:71
bool updateSit() const
virtual void scheduleDelete()
Definition: window.cpp:826
void checkNewName(Being *const being)
float x
Definition: vector.h:208
OkDialog * mAwayDialog
Definition: localplayer.h:479
int mSyncPlayerMoveDistance
Definition: localplayer.h:503
bool mShowJobExp
Definition: localplayer.h:520
int getDataAt(const int x, const int y) const
Definition: walklayer.cpp:39
void waitFor(const std::string &nick)
ShopWindow * shopWindow
Definition: shopwindow.cpp:100
int getPathLength(const Being *const being) const
ServerTypeT getNetworkType()
Definition: net.cpp:182
WeaponsInfos::const_iterator WeaponsInfosIter
Definition: weaponsdb.h:29
SpecialLayer * getTempLayer() const
Definition: map.h:237
void updateName()
Definition: being.cpp:3425
void fixAttackTarget()
void imitateAction(const Being *const being, const BeingActionT &action)
virtual void stopAttack() const =0
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:1774
bool allowAction()
void setNextDest(const int x, const int y)
void nextTile()
std::string getValue(const std::string &key, const std::string &deflt) const
static std::string keyName(const int number)
void startWalking(const unsigned char dir)
Theme * theme
Definition: theme.cpp:61
#define A_UNUSED
Definition: localconsts.h:171
static void hideBeingPopup()
int x
Definition: position.h:44
AwayListener * mAwayListener
Definition: localplayer.h:478
void unequipItem(const Item *const item, const Sfx sfx)
Definition: playerinfo.cpp:243
BeingId mNavigateId
Definition: localplayer.h:487
bool mTradebot
Definition: localplayer.h:513
bool canMove() const
void pingRequest()
bool mEnableAdvert
Definition: localplayer.h:512
Net::ChatHandler * chatHandler
Definition: net.cpp:82
void removeListeners(ConfigListener *const listener)
int getSkillLevel(const int id)
Definition: playerinfo.cpp:118
std::string mWaitFor
Definition: localplayer.h:497
void followMoveTo(const Being *const being, const int x, const int y)
std::vector< int32_t > mStatusEffectIcons
Definition: localplayer.h:472
bool isShopEmpty() const
virtual int convertFromServerSlot(const int eAthenaSlot) const =0
bool getNoAway() const
Definition: chattab.h:169
int mY
Definition: being.h:1318
bool mPathSetByMouse
Definition: localplayer.h:528
void logic()
Definition: being.cpp:1833
Path findPath(const int startX, const int startY, const int destX, const int destY, const unsigned char blockWalkmask, const int maxCost)
Definition: map.cpp:858
bool gm(InputEvent &event)
Definition: commands.cpp:74
int getWalkSpeed() const
Definition: being.h:457
void changeEquipmentBeforeAttack(const Being *const target) const
void setTarget(Being *const target)
Attributes ::T AttributesT
Definition: attributes.h:117
bool awayMode
Definition: settings.h:154
float y
Definition: vector.h:208
Keep mServerAttack
Definition: localplayer.h:510
void updateLevelLabel()
void actorSpriteDestroyed(const ActorSprite &actorSprite)
std::list< Position > Path
Definition: position.h:48
static void setPseudoAway(const std::string &message)
void moveToHome()
static const int mapTileSize
Definition: map.h:26
void recalcSpritesOrder()
Definition: being.cpp:4359
bool toggleSit() const
void addHpMessage(const int change)
virtual void requestNameById(const BeingId id) const =0
#define debugMsg(str)
Definition: chattab.h:41
Being * findBeingByName(const std::string &name, const ActorTypeT type) const
#define CAST_SIZE
Definition: cast.h:33
bool pseudoAwayMode
Definition: settings.h:155
const BeingId BeingId_zero
Definition: beingid.h:29
bool checkBadRelation(const std::string &name) const
unsigned int magicAttackType
Definition: settings.h:138
virtual int getLevel() const
Definition: being.h:603
const WeaponsInfos & getSwords()
Definition: weaponsdb.cpp:77
void updatePortals()
static void changeAwayMode(const bool forward)
void crazyMove()
Definition: crazymoves.cpp:54
int mAttackRange
Definition: being.h:1225
std::string getPingTime() const
static const std::string SOUND_INFO
Definition: sound.h:26
virtual void setGroupId(const int id)
Definition: being.cpp:5587
void removeLocally(const Particle *const particle)
static unsigned long getFileHash(const std::string &filePath)
UserColorId ::T UserColorIdT
Definition: usercolorid.h:98
void untarget()
Definition: actorsprite.h:115
void fadeOutAndPlayMusic(const std::string &fileName, const int ms)
int32_t getAttribute(const AttributesT id)
Definition: playerinfo.cpp:100
Being * findNearestLivingBeing(const int x, const int y, int maxTileDist, const ActorTypeT type, const Being *const excluded) const
void handleStatusEffect(const StatusEffect *const effect, const int32_t effectId, const Enable newStatus, const IsStart start)
uint32_t guild
int BeingTypeId
Definition: beingtypeid.h:29
Definition: being.h:93
bool mDrawPath
Definition: localplayer.h:517
PlayerRelationsManager playerRelations
Definition: guild.h:68
bool withinAttackRange(const Being *const target, const bool fixDistance, const int addRange) const
const WeaponsInfos & getShields()
Definition: weaponsdb.cpp:82
void setValue(const std::string &key, const std::string &value)
BeingTypeId getSubType() const
Definition: being.h:399
BeingActionT mAction
Definition: being.h:1232
void setIcon(const int index, AnimatedSprite *const sprite)
time_t mAdvertTime
Definition: localplayer.h:498
FloorItem * mPickUpTarget
Definition: localplayer.h:468
static const int MAX_TICK_VALUE
void specialMove(const unsigned char direction)
bool mAdvanced
Definition: being.h:1383
static void tryMagic(const std::string &spell, const int baseMagic, const int schoolMagic, const int mana)
void setAction(const BeingActionT &action, const int attackId)
std::string mPlayerFollowed
Definition: localplayer.h:463
std::map< int, Guild * >::const_iterator GuildMapCIter
void removeListener(const std::string &key, ConfigListener *const listener)
void setX(const int x)
Definition: avatar.h:128
const bool Notify_true
Definition: notify.h:29
ActorManager * actorManager
void removePortal(const int x, const int y)
unsigned int pickUpType
Definition: settings.h:137
bool itemInfo(InputEvent &event)
Definition: commands.cpp:104
int getAttackRange() const
virtual void handleStatusEffect(const StatusEffect *const effect, const int32_t effectId, const Enable newStatus, const IsStart start)
void setLevel(const int n)
Definition: being.h:600
void playGuiSound(const std::string &name)
Being * getTarget() const
unsigned int mMoveState
Definition: localplayer.h:453
bool mShowServerPos
Definition: localplayer.h:521
void clearPath()
Definition: being.cpp:553
VisibleName::Type mVisibleNames
Definition: localplayer.h:511
bool isCustom() const
Definition: map.h:326
bool pickUp(FloorItem *const item)
void volumeRestore() const
bool mKeepAttacking
Definition: localplayer.h:526
Map * mMap
Definition: actor.h:138