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 (id > 0)
426  {
427  setGM(true);
428  if (chatWindow != nullptr)
429  {
432  }
433  }
434  else
435  {
436  setGM(false);
437  }
438  if (statusWindow != nullptr)
440 }
441 
443 {
444  const Party *const party = Party::getParty(1);
445  if (party != nullptr)
446  {
447  PartyMember *const pm = party->getMember(mName);
448  if (pm != nullptr)
449  {
450  pm->setX(mX);
451  pm->setY(mY);
452  }
453  }
454 
455  if (mPath.empty())
456  {
457  if (mPickUpTarget != nullptr)
459 
460  if (mWalkingDir != 0u)
462  }
463  else if (mPath.size() == 1)
464  {
465  if (mPickUpTarget != nullptr)
467  }
468 
469  if (mGoingToTarget &&
470  mTarget != nullptr &&
471  withinAttackRange(mTarget, false, 0))
472  {
474  attack(mTarget, true, false);
475  mGoingToTarget = false;
476  mPath.clear();
477  return;
478  }
479  else if (mGoingToTarget && (mTarget == nullptr))
480  {
481  mGoingToTarget = false;
482  mPath.clear();
483  }
484 
485  if (mPath.empty())
486  {
487  if (mNavigatePath.empty() || mAction != BeingAction::MOVE)
489  else
490  mNextStep = true;
491  }
492  else
493  {
494  Being::nextTile();
495  }
496 }
497 
499 {
500  if (item == nullptr)
501  return false;
502 
504  return false;
505 
506  const int dx = item->getTileX() - mX;
507  const int dy = item->getTileY() - mY;
508  int dist = 6;
509 
510  const unsigned int pickUpType = settings.pickUpType;
511  if (pickUpType >= 4 && pickUpType <= 6)
512  dist = 4;
513 
514  if (dx * dx + dy * dy < dist)
515  {
516  if ((actorManager != nullptr) && actorManager->checkForPickup(item))
517  {
519  mPickUpTarget = nullptr;
520  }
521  }
522  else if (pickUpType >= 4 && pickUpType <= 6)
523  {
524  const Path debugPath = mMap->findPath(
525  (mPixelX - mapTileSize / 2) / mapTileSize,
527  item->getTileX(),
528  item->getTileY(),
530  0);
531  if (!debugPath.empty())
532  navigateTo(item->getTileX(), item->getTileY());
533  else
534  setDestination(item->getTileX(), item->getTileY());
535 
537  mPickUpTarget->addActorSpriteListener(this);
538  }
539  return true;
540 }
541 
543 {
544  if (mPickUpTarget == &actorSprite)
545  mPickUpTarget = nullptr;
546 }
547 
549 {
550  return mTarget;
551 }
552 
553 void LocalPlayer::setTarget(Being *const target)
554 {
555  if (target == this && (target != nullptr))
556  return;
557 
558  if (target == mTarget)
559  return;
560 
561  Being *oldTarget = nullptr;
562  if (mTarget != nullptr)
563  {
564  mTarget->untarget();
565  oldTarget = mTarget;
566  }
567 
568  if (mTarget != nullptr)
569  {
571  mTarget->setShowName(false);
572  }
573 
574  mTarget = target;
575 
576  if (oldTarget != nullptr)
577  oldTarget->updateName();
578 
579  if (target != nullptr)
580  {
581  mLastTargetX = target->mX;
582  mLastTargetY = target->mY;
583  target->updateName();
585  target->setShowName(true);
586  }
587  if (oldTarget != nullptr && mVisibleNames == VisibleName::ShowOnSelection)
588  oldTarget->setShowName(false);
589  if (target != nullptr && target->getType() == ActorType::Monster)
590  target->setShowName(true);
591 }
592 
594  const AllowSort allowSort)
595 {
596  if (actorManager != nullptr)
597  {
598  Being *const target = actorManager->findNearestLivingBeing(
599  localPlayer, 20, type, allowSort);
600 
601  if ((target != nullptr) && target != mTarget)
602  setTarget(target);
603 
604  return target;
605  }
606  return nullptr;
607 }
608 
609 void LocalPlayer::setDestination(const int x, const int y)
610 {
612 
613  if (settings.attackType == 0 || !mAttackMoving)
614  mKeepAttacking = false;
615 
616  // Only send a new message to the server when destination changes
617  if (x != mDest.x || y != mDest.y)
618  {
619  if (settings.moveType != 1)
620  {
622  Being::setDestination(x, y);
623  }
624  else
625  {
626  uint8_t newDir = 0;
627  if ((mDirection & BeingDirection::UP) != 0)
628  newDir |= BeingDirection::DOWN;
629  if ((mDirection & BeingDirection::LEFT) != 0)
630  newDir |= BeingDirection::RIGHT;
631  if ((mDirection & BeingDirection::DOWN) != 0)
632  newDir |= BeingDirection::UP;
633  if ((mDirection & BeingDirection::RIGHT) != 0)
634  newDir |= BeingDirection::LEFT;
635 
636  playerHandler->setDestination(x, y, newDir);
637 
638 // if (PacketLimiter::limitPackets(PacketType::PACKET_DIRECTION))
639  {
640  setDirection(newDir);
641  playerHandler->setDirection(newDir);
642  }
643 
644  Being::setDestination(x, y);
646  }
647  }
648 }
649 
650 void LocalPlayer::setWalkingDir(const unsigned char dir)
651 {
652  // This function is called by Game::handleInput()
653  mWalkingDir = dir;
654 
655  // If we're not already walking, start walking.
656  if (mAction != BeingAction::MOVE && (dir != 0u))
657  startWalking(dir);
658 }
659 
660 void LocalPlayer::startWalking(const unsigned char dir)
661 {
662  // This function is called by setWalkingDir(),
663  // but also by nextTile() for TMW-Athena...
664  if ((mMap == nullptr) || (dir == 0u))
665  return;
666 
667  mPickUpTarget = nullptr;
668  if (mAction == BeingAction::MOVE && !mPath.empty())
669  {
670  // Just finish the current action, otherwise we get out of sync
672  return;
673  }
674 
675  int dx = 0, dy = 0;
676  if ((dir & BeingDirection::UP) != 0)
677  dy--;
678  if ((dir & BeingDirection::DOWN) != 0)
679  dy++;
680  if ((dir & BeingDirection::LEFT) != 0)
681  dx--;
682  if ((dir & BeingDirection::RIGHT) != 0)
683  dx++;
684 
685  const unsigned char blockWalkMask = getBlockWalkMask();
686  // Prevent skipping corners over colliding tiles
687  if ((dx != 0) && !mMap->getWalk(mX + dx, mY, blockWalkMask))
688  dx = 0;
689  if ((dy != 0) && !mMap->getWalk(mX, mY + dy, blockWalkMask))
690  dy = 0;
691 
692  // Choose a straight direction when diagonal target is blocked
693  if (dx != 0 && dy != 0 && !mMap->getWalk(mX + dx, mY + dy, blockWalkMask))
694  dx = 0;
695 
696  // Walk to where the player can actually go
697  if ((dx != 0 || dy != 0) && mMap->getWalk(mX + dx, mY + dy, blockWalkMask))
698  {
699  setDestination(mX + dx, mY + dy);
700  }
701  else if (dir != mDirection)
702  {
703  // If the being can't move, just change direction
704 
705 // if (PacketLimiter::limitPackets(PacketType::PACKET_DIRECTION))
706  {
708  setDirection(dir);
709  }
710  }
711 }
712 
713 void LocalPlayer::stopWalking(const bool sendToServer)
714 {
715  if (mAction == BeingAction::MOVE && (mWalkingDir != 0u))
716  {
717  mWalkingDir = 0;
718  mPickUpTarget = nullptr;
720  mPixelY);
721  if (sendToServer)
722  {
724  mPixelX,
725  mPixelY,
726  -1);
727  }
729  }
730 
731  // No path set anymore, so we reset the path by mouse flag
732  mPathSetByMouse = false;
733 
734  clearPath();
735  navigateClean();
736 }
737 
739 {
741  return false;
742 
743  BeingActionT newAction;
744  switch (mAction)
745  {
746  case BeingAction::STAND:
748  case BeingAction::SPAWN:
749  newAction = BeingAction::SIT;
750  break;
751  case BeingAction::SIT:
752  newAction = BeingAction::STAND;
753  break;
754  case BeingAction::MOVE:
755  case BeingAction::ATTACK:
756  case BeingAction::DEAD:
757  case BeingAction::HURT:
758  case BeingAction::CAST:
759  default:
760  return true;
761  }
762 
763  playerHandler->changeAction(newAction);
764  return true;
765 }
766 
768 {
770  return false;
771 
773  return true;
774 }
775 
776 bool LocalPlayer::emote(const uint8_t emotion)
777 {
779  return false;
780 
781  playerHandler->emote(emotion);
782  return true;
783 }
784 
785 void LocalPlayer::attack(Being *const target,
786  const bool keep,
787  const bool dontChangeEquipment)
788 {
789  mKeepAttacking = keep;
790 
791  if ((target == nullptr) || target->getType() == ActorType::Npc)
792  return;
793 
794  if (mTarget != target)
795  setTarget(target);
796 
797  // Must be standing or sitting or casting to attack
798  if (mAction != BeingAction::STAND &&
801  {
802  return;
803  }
804 
805 #ifdef TMWA_SUPPORT
806  const int dist_x = target->mX - mX;
807  const int dist_y = target->mY - mY;
808 
810  {
811  if (abs(dist_y) >= abs(dist_x))
812  {
813  if (dist_y > 0)
815  else
817  }
818  else
819  {
820  if (dist_x > 0)
822  else
824  }
825  }
826 #endif // TMWA_SUPPORT
827 
829 
830  if (target->getType() != ActorType::Player
831  || checAttackPermissions(target))
832  {
834 
836  return;
837 
838  if (!dontChangeEquipment)
840 
841  const BeingId targetId = target->getId();
842  playerHandler->attack(targetId, mServerAttack);
844  }
845 
846  if (!keep)
847  stopAttack(false);
848 }
849 
850 void LocalPlayer::stopAttack(const bool keepAttack)
851 {
853  return;
854 
857 
858  untarget();
859  if (!keepAttack || !mAttackNext)
860  mKeepAttacking = false;
861 }
862 
864 {
867 
868  if (mTarget != nullptr)
869  setTarget(nullptr);
870 }
871 
873  const int amount,
874  const ItemColor color,
875  const BeingId floorItemId,
876  const PickupT fail)
877 {
878  if (fail != Pickup::OKAY)
879  {
880  if ((actorManager != nullptr) && floorItemId != BeingId_zero)
881  {
882  FloorItem *const item = actorManager->findItem(floorItemId);
883  if (item != nullptr)
884  {
885  if (!item->getShowMsg())
886  return;
887  item->setShowMsg(false);
888  }
889  }
890  const char* msg = nullptr;
891  switch (fail)
892  {
893  case Pickup::BAD_ITEM:
894  // TRANSLATORS: pickup error message
895  msg = N_("Tried to pick up nonexistent item.");
896  break;
897  case Pickup::TOO_HEAVY:
898  // TRANSLATORS: pickup error message
899  msg = N_("Item is too heavy.");
900  break;
901  case Pickup::TOO_FAR:
902  // TRANSLATORS: pickup error message
903  msg = N_("Item is too far away.");
904  break;
905  case Pickup::INV_FULL:
906  // TRANSLATORS: pickup error message
907  msg = N_("Inventory is full.");
908  break;
909  case Pickup::STACK_FULL:
910  // TRANSLATORS: pickup error message
911  msg = N_("Stack is too big.");
912  break;
913  case Pickup::DROP_STEAL:
914  // TRANSLATORS: pickup error message
915  msg = N_("Item belongs to someone else.");
916  break;
917  case Pickup::MAX_AMOUNT:
918  // TRANSLATORS: pickup error message
919  msg = N_("You can't pickup this amount of items.");
920  break;
922  // TRANSLATORS: pickup error message
923  msg = N_("Your item stack has max amount.");
924  break;
925  case Pickup::OKAY:
926  break;
927  default:
928  case Pickup::UNKNOWN:
929  // TRANSLATORS: pickup error message
930  msg = N_("Unknown problem picking up item.");
931  break;
932  }
933  if (localChatTab != nullptr &&
934  config.getBoolValue("showpickupchat"))
935  {
936  localChatTab->chatLog(gettext(msg),
940  }
941 
942  if ((mMap != nullptr) && config.getBoolValue("showpickupparticle"))
943  {
944  // Show pickup notification
946  }
947  }
948  else
949  {
950  std::string str;
951 #ifdef TMWA_SUPPORT
953  {
954  str = itemInfo.getName();
955  }
956  else
957 #endif // TMWA_SUPPORT
958  {
959  str = itemInfo.getName(color);
960  }
961 
962  if (config.getBoolValue("showpickupchat") && (localChatTab != nullptr))
963  {
964  // TRANSLATORS: %d is number,
965  // [@@%d|%[email protected]@] - here player can see link to item
966  localChatTab->chatLog(strprintf(ngettext("You picked up %d "
967  "[@@%d|%[email protected]@].", "You picked up %d [@@%d|%[email protected]@].", amount),
968  amount, itemInfo.getId(), str.c_str()),
972  }
973 
974  if ((mMap != nullptr) && config.getBoolValue("showpickupparticle"))
975  {
976  // Show pickup notification
977  if (amount > 1)
978  {
979  addMessageToQueue(strprintf("%d x %s", amount,
980  str.c_str()), UserColorId::PICKUP_INFO);
981  }
982  else
983  {
985  }
986  }
987  }
988 }
989 
991 {
992  if (mAttackRange > -1)
993  {
994  return mAttackRange;
995  }
996 
997  const Item *const weapon = PlayerInfo::getEquipment(
999  if (weapon != nullptr)
1000  {
1001  const ItemInfo &info = weapon->getInfo();
1002  return info.getAttackRange();
1003  }
1004  return 48; // unarmed range
1005 }
1006 
1007 bool LocalPlayer::withinAttackRange(const Being *const target,
1008  const bool fixDistance,
1009  const int addRange) const
1010 {
1011  if (target == nullptr)
1012  return false;
1013 
1014  int range = getAttackRange() + addRange;
1015  int dx;
1016  int dy;
1017 
1018  if (fixDistance && range == 1)
1019  range = 2;
1020 
1021  dx = CAST_S32(abs(target->mX - mX));
1022  dy = CAST_S32(abs(target->mY - mY));
1023  return !(dx > range || dy > range);
1024 }
1025 
1027 {
1028  if (target == nullptr)
1029  return;
1030 
1031  mPickUpTarget = nullptr;
1032  setTarget(target);
1033  mGoingToTarget = true;
1034  navigateTo(target->mX,
1035  target->mY);
1036 }
1037 
1039  const int32_t effectId,
1040  const Enable newStatus,
1041  const IsStart start)
1042 {
1044  effectId,
1045  newStatus,
1046  start);
1047 
1048  if (effect != nullptr)
1049  {
1050  effect->deliverMessage();
1051  effect->playSFX();
1052 
1053  AnimatedSprite *const sprite = effect->getIcon();
1054 
1055  if (sprite == nullptr)
1056  {
1057  // delete sprite, if necessary
1058  for (size_t i = 0; i < mStatusEffectIcons.size(); )
1059  {
1060  if (mStatusEffectIcons[i] == effectId)
1061  {
1062  mStatusEffectIcons.erase(mStatusEffectIcons.begin() + i);
1063  if (miniStatusWindow != nullptr)
1065  }
1066  else
1067  {
1068  i++;
1069  }
1070  }
1071  }
1072  else
1073  {
1074  // replace sprite or append
1075  bool found = false;
1076  const size_t sz = mStatusEffectIcons.size();
1077  for (size_t i = 0; i < sz; i++)
1078  {
1079  if (mStatusEffectIcons[i] == effectId)
1080  {
1081  if (miniStatusWindow != nullptr)
1082  miniStatusWindow->setIcon(CAST_S32(i), sprite);
1083  found = true;
1084  break;
1085  }
1086  }
1087 
1088  if (!found)
1089  { // add new
1090  const int offset = CAST_S32(mStatusEffectIcons.size());
1091  if (miniStatusWindow != nullptr)
1092  miniStatusWindow->setIcon(offset, sprite);
1093  mStatusEffectIcons.push_back(effectId);
1094  }
1095  }
1096  }
1097 }
1098 
1099 void LocalPlayer::addMessageToQueue(const std::string &message,
1100  const UserColorIdT color)
1101 {
1102  if (mMessages.size() < 20)
1103  mMessages.push_back(MessagePair(message, color));
1104 }
1105 
1106 void LocalPlayer::optionChanged(const std::string &value)
1107 {
1108  if (value == "showownname")
1109  {
1110  setShowName(config.getBoolValue("showownname"));
1111  }
1112  else if (value == "targetDeadPlayers")
1113  {
1114  mTargetDeadPlayers = config.getBoolValue("targetDeadPlayers");
1115  }
1116  else if (value == "enableBuggyServers")
1117  {
1118  mIsServerBuggy = serverConfig.getBoolValue("enableBuggyServers");
1119  }
1120  else if (value == "syncPlayerMove")
1121  {
1122  mSyncPlayerMove = config.getBoolValue("syncPlayerMove");
1123  }
1124  else if (value == "syncPlayerMoveDistance")
1125  {
1126  mSyncPlayerMoveDistance = config.getIntValue("syncPlayerMoveDistance");
1127  }
1128  else if (value == "drawPath")
1129  {
1130  mDrawPath = config.getBoolValue("drawPath");
1131  }
1132  else if (value == "serverAttack")
1133  {
1134  mServerAttack = fromBool(config.getBoolValue("serverAttack"), Keep);
1135  }
1136  else if (value == "attackMoving")
1137  {
1138  mAttackMoving = config.getBoolValue("attackMoving");
1139  }
1140  else if (value == "attackNext")
1141  {
1142  mAttackNext = config.getBoolValue("attackNext");
1143  }
1144  else if (value == "showJobExp")
1145  {
1146  mShowJobExp = config.getBoolValue("showJobExp");
1147  }
1148  else if (value == "enableAdvert")
1149  {
1150  mEnableAdvert = config.getBoolValue("enableAdvert");
1151  }
1152  else if (value == "tradebot")
1153  {
1154  mTradebot = config.getBoolValue("tradebot");
1155  }
1156  else if (value == "targetOnlyReachable")
1157  {
1158  mTargetOnlyReachable = config.getBoolValue("targetOnlyReachable");
1159  }
1160  else if (value == "showserverpos")
1161  {
1162  mShowServerPos = config.getBoolValue("showserverpos");
1163  }
1164  else if (value == "visiblenames")
1165  {
1166  mVisibleNames = static_cast<VisibleName::Type>(
1167  config.getIntValue("visiblenames"));
1168  }
1169 }
1170 
1171 void LocalPlayer::addJobMessage(const int64_t change)
1172 {
1173  if (change != 0 && mMessages.size() < 20)
1174  {
1175  const std::string xpStr = toString(CAST_U64(change));
1176  if (!mMessages.empty())
1177  {
1178  MessagePair pair = mMessages.back();
1179  // TRANSLATORS: this is normal experience
1180  if (pair.first.find(strprintf(" %s", _("xp"))) ==
1181  // TRANSLATORS: this is normal experience
1182  pair.first.size() - strlen(_("xp")) - 1)
1183  {
1184  mMessages.pop_back();
1185  pair.first.append(strprintf(", %s %s",
1186  xpStr.c_str(),
1187  // TRANSLATORS: this is job experience
1188  _("job")));
1189  mMessages.push_back(pair);
1190  }
1191  else
1192  {
1193  addMessageToQueue(strprintf("%s %s",
1194  xpStr.c_str(),
1195  // TRANSLATORS: this is job experience
1196  _("job")),
1198  }
1199  }
1200  else
1201  {
1202  addMessageToQueue(strprintf("%s %s",
1203  xpStr.c_str(),
1204  // TRANSLATORS: this is job experience
1205  _("job")),
1207  }
1208  }
1209 }
1210 
1211 void LocalPlayer::addXpMessage(const int64_t change)
1212 {
1213  if (change != 0 && mMessages.size() < 20)
1214  {
1215  addMessageToQueue(strprintf("%s %s",
1216  toString(CAST_U64(change)).c_str(),
1217  // TRANSLATORS: get xp message
1218  _("xp")),
1220  }
1221 }
1222 
1223 void LocalPlayer::addHomunXpMessage(const int change)
1224 {
1225  if (change != 0 && mMessages.size() < 20)
1226  {
1227  addMessageToQueue(strprintf("%s %d %s",
1228  // TRANSLATORS: get homunculus xp message
1229  _("Homun"),
1230  change,
1231  // TRANSLATORS: get xp message
1232  _("xp")),
1234  }
1235 }
1236 
1237 void LocalPlayer::addHpMessage(const int change)
1238 {
1239  if (change != 0 && mMessages.size() < 20)
1240  {
1241  // TRANSLATORS: get hp message
1242  addMessageToQueue(strprintf("%d %s", change, _("hp")),
1244  }
1245 }
1246 
1247 void LocalPlayer::addSpMessage(const int change)
1248 {
1249  if (change != 0 && mMessages.size() < 20)
1250  {
1251  // TRANSLATORS: get hp message
1252  addMessageToQueue(strprintf("%d %s", change, _("mana")),
1254  }
1255 }
1256 
1258  const int64_t oldVal,
1259  const int64_t newVal)
1260 {
1261  PRAGMA45(GCC diagnostic push)
1262  PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
1263  switch (id)
1264  {
1266  {
1268  break;
1269  if (oldVal > newVal)
1270  break;
1271 
1272  const int change = CAST_S32(newVal - oldVal);
1273  addXpMessage(change);
1274  break;
1275  }
1277  mLevel = CAST_S32(newVal);
1278  break;
1279  case Attributes::PLAYER_HP:
1280  if (oldVal != 0 && newVal == 0)
1282  break;
1284  {
1285  if (!mShowJobExp ||
1287  {
1288  return;
1289  }
1290  if (oldVal > newVal ||
1293  {
1294  return;
1295  }
1296  const int32_t change = CAST_S32(newVal - oldVal);
1297  addJobMessage(change);
1298  break;
1299  }
1300  default:
1301  break;
1302  }
1303  PRAGMA45(GCC diagnostic pop)
1304 }
1305 
1306 void LocalPlayer::move(const int dX, const int dY)
1307 {
1308  mPickUpTarget = nullptr;
1309  setDestination(mX + dX, mY + dY);
1310 }
1311 
1313 {
1314  bool gotPos(false);
1315  Path debugPath;
1316 
1317  size_t limit(0);
1318 
1319  if (dist == -1)
1320  {
1321  dist = settings.moveToTargetType;
1322  if (dist != 0)
1323  {
1324  const bool broken = (Net::getNetworkType() ==
1326  switch (dist)
1327  {
1328  case 10:
1329  dist = mAttackRange;
1330  if (dist == 1 && broken)
1331  dist = 2;
1332  break;
1333  case 11:
1334  dist = mAttackRange - 1;
1335  if (dist < 1)
1336  dist = 1;
1337  if (dist == 1 && broken)
1338  dist = 2;
1339  break;
1340  default:
1341  break;
1342  }
1343  }
1344  }
1345 
1346  if (mTarget != nullptr)
1347  {
1348  if (mMap != nullptr)
1349  {
1350  debugPath = mMap->findPath(
1351  (mPixelX - mapTileSize / 2) / mapTileSize,
1353  mTarget->mX,
1354  mTarget->mY,
1355  getBlockWalkMask(),
1356  0);
1357  }
1358 
1359  const size_t sz = debugPath.size();
1360  if (sz < CAST_SIZE(dist))
1361  return;
1362  limit = CAST_S32(sz) - dist;
1363  gotPos = true;
1364  }
1365  else if ((mNavigateX != 0) || (mNavigateY != 0))
1366  {
1367  debugPath = mNavigatePath;
1368  limit = dist;
1369  gotPos = true;
1370  }
1371 
1372  if (gotPos)
1373  {
1374  if (dist == 0)
1375  {
1376  if (mTarget != nullptr)
1378  }
1379  else
1380  {
1381  Position pos(0, 0);
1382  size_t f = 0;
1383 
1384  for (Path::const_iterator i = debugPath.begin(),
1385  i_fend = debugPath.end();
1386  i != i_fend && f < limit; ++i, f++)
1387  {
1388  pos = (*i);
1389  }
1390  navigateTo(pos.x, pos.y);
1391  }
1392  }
1393  else if ((mLastTargetX != 0) || (mLastTargetY != 0))
1394  {
1396  }
1397 }
1398 
1400 {
1401  mPickUpTarget = nullptr;
1402  if ((mX != mCrossX || mY != mCrossY) && (mCrossX != 0) && (mCrossY != 0))
1403  {
1405  }
1406  else if (mMap != nullptr)
1407  {
1408  const std::map<std::string, Vector>::const_iterator iter =
1409  mHomes.find(mMap->getProperty("_realfilename", std::string()));
1410 
1411  if (iter != mHomes.end())
1412  {
1413  const Vector pos = mHomes[(*iter).first];
1414  if (mX == pos.x && mY == pos.y)
1415  {
1417  CAST_S32(pos.x),
1418  CAST_S32(pos.y),
1419  CAST_S32(mDirection));
1420  }
1421  else
1422  {
1423  navigateTo(CAST_S32(pos.x), CAST_S32(pos.y));
1424  }
1425  }
1426  }
1427 }
1428 
1429 void LocalPlayer::changeEquipmentBeforeAttack(const Being *const target) const
1430 {
1431  if (settings.attackWeaponType == 1
1432  || (target == nullptr)
1433  || (PlayerInfo::getInventory() == nullptr))
1434  {
1435  return;
1436  }
1437 
1438  bool allowSword = false;
1439  const int dx = target->mX - mX;
1440  const int dy = target->mY - mY;
1441  const Item *item = nullptr;
1442 
1443  if (dx * dx + dy * dy > 80)
1444  return;
1445 
1446  if (dx * dx + dy * dy < 8)
1447  allowSword = true;
1448 
1449  const Inventory *const inv = PlayerInfo::getInventory();
1450  if (inv == nullptr)
1451  return;
1452 
1453  // if attack distance for sword
1454  if (allowSword)
1455  {
1456  // searching swords
1457  const WeaponsInfos &swords = WeaponsDB::getSwords();
1458  FOR_EACH (WeaponsInfosIter, it, swords)
1459  {
1460  item = inv->findItem(*it, ItemColor_zero);
1461  if (item != nullptr)
1462  break;
1463  }
1464 
1465  // no swords
1466  if (item == nullptr)
1467  return;
1468 
1469  // if sword not equiped
1470  if (item->isEquipped() == Equipped_false)
1472 
1473  // if need equip shield too
1474  if (settings.attackWeaponType == 3)
1475  {
1476  // searching shield
1477  const WeaponsInfos &shields = WeaponsDB::getShields();
1478  FOR_EACH (WeaponsInfosIter, it, shields)
1479  {
1480  item = inv->findItem(*it, ItemColor_zero);
1481  if (item != nullptr)
1482  break;
1483  }
1484  if ((item != nullptr) && item->isEquipped() == Equipped_false)
1486  }
1487  }
1488  // big distance. allowed only bow
1489  else
1490  {
1491  // searching bow
1492  const WeaponsInfos &bows = WeaponsDB::getBows();
1493  FOR_EACH (WeaponsInfosIter, it, bows)
1494  {
1495  item = inv->findItem(*it, ItemColor_zero);
1496  if (item != nullptr)
1497  break;
1498  }
1499 
1500  // no bow
1501  if (item == nullptr)
1502  return;
1503 
1504  if (item->isEquipped() == Equipped_false)
1506  }
1507 }
1508 
1510  const int maxCost)
1511 {
1512  if ((being == nullptr) || (mMap == nullptr))
1513  return false;
1514 
1515  if (being->getReachable() == Reachable::REACH_NO)
1516  return false;
1517 
1518  if (being->mX == mX &&
1519  being->mY == mY)
1520  {
1521  being->setDistance(0);
1523  return true;
1524  }
1525  else if (being->mX - 1 <= mX &&
1526  being->mX + 1 >= mX &&
1527  being->mY - 1 <= mY &&
1528  being->mY + 1 >= mY)
1529  {
1530  being->setDistance(1);
1532  return true;
1533  }
1534 
1535  const Path debugPath = mMap->findPath(
1536  (mPixelX - mapTileSize / 2) / mapTileSize,
1538  being->mX,
1539  being->mY,
1540  getBlockWalkMask(),
1541  maxCost);
1542 
1543  being->setDistance(CAST_S32(debugPath.size()));
1544  if (!debugPath.empty())
1545  {
1547  return true;
1548  }
1550  return false;
1551 }
1552 
1553 bool LocalPlayer::isReachable(const int x, const int y,
1554  const bool allowCollision) const
1555 {
1556  const WalkLayer *const walk = mMap->getWalkLayer();
1557  if (walk == nullptr)
1558  return false;
1559  int num = walk->getDataAt(x, y);
1560  if (allowCollision && num < 0)
1561  num = -num;
1562 
1563  return walk->getDataAt(mX, mY) == num;
1564 }
1565 
1566 bool LocalPlayer::pickUpItems(int pickUpType)
1567 {
1568  if (actorManager == nullptr)
1569  return false;
1570 
1571  bool status = false;
1572  int x = mX;
1573  int y = mY;
1574 
1575  // first pick up item on player position
1576  FloorItem *item =
1577  actorManager->findItem(x, y);
1578  if (item != nullptr)
1579  status = pickUp(item);
1580 
1581  if (pickUpType == 0)
1582  pickUpType = settings.pickUpType;
1583 
1584  if (pickUpType == 0)
1585  return status;
1586 
1587  int x1, y1, x2, y2;
1588  switch (pickUpType)
1589  {
1590  case 1:
1591  switch (mDirection)
1592  {
1593  case BeingDirection::UP : --y; break;
1594  case BeingDirection::DOWN : ++y; break;
1595  case BeingDirection::LEFT : --x; break;
1596  case BeingDirection::RIGHT: ++x; break;
1597  default: break;
1598  }
1599  item = actorManager->findItem(x, y);
1600  if (item != nullptr)
1601  status = pickUp(item);
1602  break;
1603  case 2:
1604  switch (mDirection)
1605  {
1606  case BeingDirection::UP:
1607  x1 = x - 1; y1 = y - 1; x2 = x + 1; y2 = y; break;
1608  case BeingDirection::DOWN:
1609  x1 = x - 1; y1 = y; x2 = x + 1; y2 = y + 1; break;
1610  case BeingDirection::LEFT:
1611  x1 = x - 1; y1 = y - 1; x2 = x; y2 = y + 1; break;
1612  case BeingDirection::RIGHT:
1613  x1 = x; y1 = y - 1; x2 = x + 1; y2 = y + 1; break;
1614  default:
1615  x1 = x; x2 = x; y1 = y; y2 = y; break;
1616  }
1617  if (actorManager->pickUpAll(x1, y1, x2, y2, false))
1618  status = true;
1619  break;
1620  case 3:
1621  if (actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1622  status = true;
1623  break;
1624 
1625  case 4:
1626  if (!actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1627  {
1628  if (actorManager->pickUpNearest(x, y, 4))
1629  status = true;
1630  }
1631  else
1632  {
1633  status = true;
1634  }
1635  break;
1636 
1637  case 5:
1638  if (!actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1639  {
1640  if (actorManager->pickUpNearest(x, y, 8))
1641  status = true;
1642  }
1643  else
1644  {
1645  status = true;
1646  }
1647  break;
1648 
1649  case 6:
1650  if (!actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1651  {
1652  if (actorManager->pickUpNearest(x, y, 90))
1653  status = true;
1654  }
1655  else
1656  {
1657  status = true;
1658  }
1659  break;
1660 
1661  default:
1662  break;
1663  }
1664  return status;
1665 }
1666 
1667 
1668 void LocalPlayer::moveByDirection(const unsigned char dir)
1669 {
1670  int dx = 0, dy = 0;
1671  if ((dir & BeingDirection::UP) != 0)
1672  dy--;
1673  if ((dir & BeingDirection::DOWN) != 0)
1674  dy++;
1675  if ((dir & BeingDirection::LEFT) != 0)
1676  dx--;
1677  if ((dir & BeingDirection::RIGHT) != 0)
1678  dx++;
1679  move(dx, dy);
1680 }
1681 
1682 void LocalPlayer::specialMove(const unsigned char direction)
1683 {
1684  if ((direction != 0u) && ((mNavigateX != 0) || (mNavigateY != 0)))
1685  navigateClean();
1686 
1687  if ((direction != 0u) && (settings.moveType >= 2
1688  && settings.moveType <= 4))
1689  {
1690  if (mAction == BeingAction::MOVE)
1691  return;
1692 
1693  unsigned int max;
1694 
1695  if (settings.moveType == 2)
1696  max = 5;
1697  else if (settings.moveType == 4)
1698  max = 1;
1699  else
1700  max = 3;
1701 
1702  if (getMoveState() < max)
1703  {
1704  moveByDirection(direction);
1705  mMoveState ++;
1706  }
1707  else
1708  {
1709  mMoveState = 0;
1710  crazyMoves->crazyMove();
1711  }
1712  }
1713  else
1714  {
1715  setWalkingDir(direction);
1716  }
1717 }
1718 
1719 #ifdef TMWA_SUPPORT
1721 {
1723  return;
1724  if (chatWindow == nullptr ||
1725  !isAlive() ||
1727  {
1728  return;
1729  }
1730 
1731  switch (settings.magicAttackType)
1732  {
1733  // flar W00
1734  case 0:
1735  tryMagic("#flar", 1, 0, 10);
1736  break;
1737  // chiza W01
1738  case 1:
1739  tryMagic("#chiza", 1, 0, 9);
1740  break;
1741  // ingrav W10
1742  case 2:
1743  tryMagic("#ingrav", 2, 2, 20);
1744  break;
1745  // frillyar W11
1746  case 3:
1747  tryMagic("#frillyar", 2, 2, 25);
1748  break;
1749  // upmarmu W12
1750  case 4:
1751  tryMagic("#upmarmu", 2, 2, 20);
1752  break;
1753  default:
1754  break;
1755  }
1756 }
1757 
1758 void LocalPlayer::tryMagic(const std::string &spell, const int baseMagic,
1759  const int schoolMagic, const int mana)
1760 {
1761  if (chatWindow == nullptr)
1762  return;
1763 
1764  if (PlayerInfo::getSkillLevel(340) >= baseMagic
1765  && PlayerInfo::getSkillLevel(342) >= schoolMagic)
1766  {
1768  {
1770  return;
1771 
1772  chatWindow->localChatInput(spell);
1773  }
1774  }
1775 }
1776 #endif // TMWA_SUPPORT
1777 
1779 {
1780  std::string buf;
1781  std::stringstream ss(serverConfig.getValue("playerHomes", ""));
1782 
1783  while (ss >> buf)
1784  {
1785  Vector pos;
1786  ss >> pos.x;
1787  ss >> pos.y;
1788  mHomes[buf] = pos;
1789  }
1790 }
1791 
1792 void LocalPlayer::setMap(Map *const map)
1793 {
1794  BLOCK_START("LocalPlayer::setMap")
1795  if (map != nullptr)
1796  {
1797  if (socialWindow != nullptr)
1799  }
1800  navigateClean();
1801  mCrossX = 0;
1802  mCrossY = 0;
1803 
1804  Being::setMap(map);
1806  BLOCK_END("LocalPlayer::setMap")
1807 }
1808 
1810 {
1811  if ((mMap == nullptr) || (socialWindow == nullptr))
1812  return;
1813 
1814  SpecialLayer *const specialLayer = mMap->getSpecialLayer();
1815 
1816  if (specialLayer == nullptr)
1817  return;
1818 
1819  const std::string key = mMap->getProperty("_realfilename", std::string());
1820  Vector pos = mHomes[key];
1821 
1822  if (mAction == BeingAction::SIT)
1823  {
1824  const std::map<std::string, Vector>::const_iterator
1825  iter = mHomes.find(key);
1826 
1827  if (iter != mHomes.end())
1828  {
1830  CAST_S32(pos.y));
1831  }
1832 
1833  if (iter != mHomes.end() && mX == CAST_S32(pos.x)
1834  && mY == CAST_S32(pos.y))
1835  {
1836  mMap->updatePortalTile("",
1838  CAST_S32(pos.x),
1839  CAST_S32(pos.y),
1840  true);
1841 
1842  mHomes.erase(key);
1844  CAST_S32(pos.y));
1845  }
1846  else
1847  {
1848  if (iter != mHomes.end())
1849  {
1850  specialLayer->setTile(CAST_S32(pos.x),
1851  CAST_S32(pos.y), MapItemType::EMPTY);
1852  specialLayer->updateCache();
1853  }
1854 
1855  pos.x = static_cast<float>(mX);
1856  pos.y = static_cast<float>(mY);
1857  mHomes[key] = pos;
1858  mMap->updatePortalTile("home",
1860  mX,
1861  mY,
1862  true);
1864  }
1865  MapItem *const mapItem = specialLayer->getTile(mX, mY);
1866  if (mapItem != nullptr)
1867  {
1868  const int idx = socialWindow->getPortalIndex(mX, mY);
1870  OutfitWindow::keyName(idx)));
1871  }
1872  saveHomes();
1873  }
1874  else
1875  {
1876  MapItem *mapItem = specialLayer->getTile(mX, mY);
1877  int type = 0;
1878 
1879  const std::map<std::string, Vector>::iterator iter = mHomes.find(key);
1880  if (iter != mHomes.end() && mX == pos.x && mY == pos.y)
1881  {
1882  mHomes.erase(key);
1883  saveHomes();
1884  }
1885 
1886  if ((mapItem == nullptr) || mapItem->getType() == MapItemType::EMPTY)
1887  {
1888  if ((mDirection & BeingDirection::UP) != 0)
1889  type = MapItemType::ARROW_UP;
1890  else if ((mDirection & BeingDirection::LEFT) != 0)
1891  type = MapItemType::ARROW_LEFT;
1892  else if ((mDirection & BeingDirection::DOWN) != 0)
1893  type = MapItemType::ARROW_DOWN;
1894  else if ((mDirection & BeingDirection::RIGHT) != 0)
1895  type = MapItemType::ARROW_RIGHT;
1896  }
1897  else
1898  {
1899  type = MapItemType::EMPTY;
1900  }
1901  mMap->updatePortalTile("",
1902  type,
1903  mX,
1904  mY,
1905  true);
1906 
1907  if (type != MapItemType::EMPTY)
1908  {
1910  mapItem = specialLayer->getTile(mX, mY);
1911  if (mapItem != nullptr)
1912  {
1913  const int idx = socialWindow->getPortalIndex(mX, mY);
1915  OutfitWindow::keyName(idx)));
1916  }
1917  }
1918  else
1919  {
1920  specialLayer->setTile(mX, mY, MapItemType::EMPTY);
1921  specialLayer->updateCache();
1923  }
1924  }
1925 }
1926 
1928 {
1929  std::stringstream ss;
1930 
1931  for (std::map<std::string, Vector>::const_iterator iter = mHomes.begin(),
1932  iter_fend = mHomes.end();
1933  iter != iter_fend;
1934  ++iter)
1935  {
1936  const Vector &pos = (*iter).second;
1937 
1938  if (iter != mHomes.begin())
1939  ss << " ";
1940  ss << (*iter).first << " " << pos.x << " " << pos.y;
1941  }
1942 
1943  serverConfig.setValue("playerHomes", ss.str());
1944 }
1945 
1947 {
1948  const int time = tick_time;
1949  if (mWaitPing == true && mPingSendTick != 0)
1950  {
1951  if (time >= mPingSendTick && (time - mPingSendTick) > 1000)
1952  return;
1953  }
1954 
1955  mPingSendTick = time;
1956  mWaitPing = true;
1958 }
1959 
1960 std::string LocalPlayer::getPingTime() const
1961 {
1962  std::string str;
1963  if (!mWaitPing)
1964  {
1965  if (mPingTime == 0)
1966  str = "?";
1967  else
1968  str = toString(CAST_S32(mPingTime));
1969  }
1970  else
1971  {
1972  time_t time = tick_time;
1973  if (time > mPingSendTick)
1974  time -= mPingSendTick;
1975  else
1976  time += MAX_TICK_VALUE - mPingSendTick;
1977  if (time <= mPingTime)
1978  time = mPingTime;
1979  if (mPingTime != time)
1980  str = strprintf("%d (%d)", CAST_S32(mPingTime), CAST_S32(time));
1981  else
1982  str = toString(CAST_S32(time));
1983  }
1984  return str;
1985 }
1986 
1988 {
1989  if (mWaitPing == true && mPingSendTick > 0)
1990  {
1991  mWaitPing = false;
1992  const int time = tick_time;
1993  if (time < mPingSendTick)
1994  {
1995  mPingSendTick = 0;
1996  mPingTime = 0;
1997  }
1998  else
1999  {
2000  mPingTime = (time - mPingSendTick) * 10;
2001  }
2002  }
2003 }
2004 
2006 {
2007  if (mPingSendTick == 0 || tick_time < mPingSendTick
2008  || (tick_time - mPingSendTick) > 200)
2009  {
2010  pingRequest();
2011  }
2012 }
2013 
2014 
2015 void LocalPlayer::setAway(const std::string &message) const
2016 {
2017  setAfkMessage(message);
2019  updateStatus();
2020 }
2021 
2022 void LocalPlayer::setAfkMessage(std::string message)
2023 {
2024  if (!message.empty())
2025  {
2026  if (message.size() > 4 && message.substr(0, 4) == "/me ")
2027  {
2028  message = message.substr(4);
2029  config.setValue("afkFormat", 1);
2030  }
2031  else
2032  {
2033  config.setValue("afkFormat", 0);
2034  }
2035  serverConfig.setValue("afkMessage", message);
2036  }
2037 }
2038 
2039 void LocalPlayer::setPseudoAway(const std::string &message)
2040 {
2041  setAfkMessage(message);
2043 }
2044 
2045 void LocalPlayer::afkRespond(ChatTab *const tab, const std::string &nick)
2046 {
2047  if (settings.awayMode)
2048  {
2049  const time_t time = cur_time;
2050  if (mAfkTime == 0 || time < mAfkTime
2051  || time - mAfkTime > awayLimitTimer)
2052  {
2053  std::string str(serverConfig.getValue("afkMessage",
2054  "I am away from keyboard."));
2055  if (str.find("'NAME'") != std::string::npos)
2056  replaceAll(str, "'NAME'", nick);
2057 
2058  std::string msg("*AFK*: " + str);
2059 
2060  if (config.getIntValue("afkFormat") == 1)
2061  msg = "*" + msg + "*";
2062 
2063  if (tab == nullptr)
2064  {
2065  chatHandler->privateMessage(nick, msg);
2066  if (localChatTab != nullptr)
2067  {
2068  localChatTab->chatLog(std::string(mName).append(
2069  " : ").append(msg),
2073  }
2074  }
2075  else
2076  {
2077  if (tab->getNoAway())
2078  return;
2079  chatHandler->privateMessage(nick, msg);
2080  tab->chatLog(mName, msg);
2081  }
2082  mAfkTime = time;
2083  }
2084  }
2085 }
2086 
2087 bool LocalPlayer::navigateTo(const int x, const int y)
2088 {
2089  if (mMap == nullptr)
2090  return false;
2091 
2092  SpecialLayer *const tmpLayer = mMap->getTempLayer();
2093  if (tmpLayer == nullptr)
2094  return false;
2095 
2096  mShowNavigePath = true;
2097  mOldX = mPixelX;
2098  mOldY = mPixelY;
2099  mOldTileX = mX;
2100  mOldTileY = mY;
2101  mNavigateX = x;
2102  mNavigateY = y;
2104 
2106  (mPixelX - mapTileSize / 2) / mapTileSize,
2108  x,
2109  y,
2110  getBlockWalkMask(),
2111  0);
2112 
2113  if (mDrawPath)
2114  tmpLayer->addRoad(mNavigatePath);
2115  return !mNavigatePath.empty();
2116 }
2117 
2119 {
2120  if (mMap == nullptr)
2121  return;
2122 
2123  mShowNavigePath = false;
2124  mOldX = 0;
2125  mOldY = 0;
2126  mOldTileX = 0;
2127  mOldTileY = 0;
2128  mNavigateX = 0;
2129  mNavigateY = 0;
2131 
2132  mNavigatePath.clear();
2133 
2134  SpecialLayer *const tmpLayer = mMap->getTempLayer();
2135  if (tmpLayer == nullptr)
2136  return;
2137 
2138  tmpLayer->clean();
2139 }
2140 
2142 {
2143  if (mMap != nullptr)
2144  {
2145  std::string str = mMap->getObjectData(mX, mY, MapItemType::MUSIC);
2146  if (str.empty())
2147  str = mMap->getMusicFile();
2148  if (str != soundManager.getCurrentMusicFile())
2149  {
2150  if (str.empty())
2151  soundManager.fadeOutMusic(1000);
2152  else
2153  soundManager.fadeOutAndPlayMusic(str, 1000);
2154  }
2155  }
2156 }
2157 
2159 {
2161 
2162  // probably map not loaded.
2163  if ((mPixelX == 0) || (mPixelY == 0))
2164  return;
2165 
2166  if (mX != mOldTileX || mY != mOldTileY)
2167  {
2168  if (socialWindow != nullptr)
2171  updateMusic();
2172  }
2173 
2174  if ((mMap != nullptr) && (mX != mOldTileX || mY != mOldTileY))
2175  {
2176  SpecialLayer *const tmpLayer = mMap->getTempLayer();
2177  if (tmpLayer == nullptr)
2178  return;
2179 
2180  const int x = (mPixelX - mapTileSize / 2) / mapTileSize;
2181  const int y = (mPixelY - mapTileSize) / mapTileSize;
2182  if (mNavigateId != BeingId_zero)
2183  {
2184  if (actorManager == nullptr)
2185  {
2186  navigateClean();
2187  return;
2188  }
2189 
2190  const Being *const being = actorManager
2192  if (being == nullptr)
2193  {
2194  navigateClean();
2195  return;
2196  }
2197  mNavigateX = being->mX;
2198  mNavigateY = being->mY;
2199  }
2200 
2201  if (mNavigateX == x && mNavigateY == y)
2202  {
2203  navigateClean();
2204  return;
2205  }
2206  for (Path::const_iterator i = mNavigatePath.begin(),
2207  i_fend = mNavigatePath.end();
2208  i != i_fend;
2209  ++i)
2210  {
2211  if ((*i).x == mX && (*i).y == mY)
2212  {
2213  mNavigatePath.pop_front();
2214  fixPos();
2215  break;
2216  }
2217  }
2218  if (mDrawPath && mShowNavigePath)
2219  {
2220  tmpLayer->clean();
2221  tmpLayer->addRoad(mNavigatePath);
2222  }
2223  }
2224  mOldX = mPixelX;
2225  mOldY = mPixelY;
2226  mOldTileX = mX;
2227  mOldTileY = mY;
2228 }
2229 
2231 {
2232 /*
2233  if (mKeepAttacking)
2234  {
2235  if (mTarget && mServerAttack == Keep_true)
2236  {
2237  logger->log("LocalPlayer::targetMoved0");
2238  if (!PacketLimiter::limitPackets(PacketType::PACKET_ATTACK))
2239  return;
2240  logger->log("LocalPlayer::targetMoved");
2241  playerHandler->attack(mTarget->getId(), mServerAttack);
2242  }
2243  }
2244 */
2245 }
2246 
2247 int LocalPlayer::getPathLength(const Being *const being) const
2248 {
2249  if ((mMap == nullptr) || (being == nullptr))
2250  return 0;
2251 
2252  if (being->mX == mX && being->mY == mY)
2253  return 0;
2254 
2255  if (being->mX - 1 <= mX &&
2256  being->mX + 1 >= mX &&
2257  being->mY - 1 <= mY &&
2258  being->mY + 1 >= mY)
2259  {
2260  return 1;
2261  }
2262 
2264  {
2265  const Path debugPath = mMap->findPath(
2266  (mPixelX - mapTileSize / 2) / mapTileSize,
2268  being->mX,
2269  being->mY,
2270  getBlockWalkMask(),
2271  0);
2272  return CAST_S32(debugPath.size());
2273  }
2274 
2275  const int dx = CAST_S32(abs(being->mX - mX));
2276  const int dy = CAST_S32(abs(being->mY - mY));
2277  if (dx > dy)
2278  return dx;
2279  return dy;
2280 }
2281 
2283 {
2284  int range = getAttackRange();
2285  if (range == 1)
2286  range = 2;
2287  return range;
2288 }
2289 
2290 void LocalPlayer::attack2(Being *const target,
2291  const bool keep,
2292  const bool dontChangeEquipment)
2293 {
2294  if (!dontChangeEquipment && (target != nullptr))
2296 
2297  const bool broken = (Net::getNetworkType() == ServerType::TMWATHENA);
2298 
2299  // probably need cache getPathLength(target)
2300  if ((target == nullptr ||
2301  settings.attackType == 0 ||
2302  settings.attackType == 3) ||
2303  (withinAttackRange(target, broken, broken ? 1 : 0) &&
2304  getPathLength(target) <= getAttackRange2()))
2305  {
2306  attack(target, keep, false);
2307  if (settings.attackType == 2)
2308  {
2309  if (target == nullptr)
2310  {
2311  if (pickUpItems(0))
2312  return;
2313  }
2314  else
2315  {
2316  pickUpItems(3);
2317  }
2318  }
2319  }
2320  else if (mPickUpTarget == nullptr)
2321  {
2322  if (settings.attackType == 2)
2323  {
2324  if (pickUpItems(0))
2325  return;
2326  }
2327  setTarget(target);
2328  if (target->getType() != ActorType::Npc)
2329  {
2330  mKeepAttacking = true;
2331  moveToTarget(-1);
2332  }
2333  }
2334 }
2335 
2336 void LocalPlayer::setFollow(const std::string &player)
2337 {
2338  mPlayerFollowed = player;
2339  if (!mPlayerFollowed.empty())
2340  {
2341  // TRANSLATORS: follow command message
2342  std::string msg = strprintf(_("Follow: %s"), player.c_str());
2343  debugMsg(msg);
2344  }
2345  else
2346  {
2347  // TRANSLATORS: follow command message
2348  debugMsg(_("Follow canceled"));
2349  }
2350 }
2351 
2352 void LocalPlayer::setImitate(const std::string &player)
2353 {
2354  mPlayerImitated = player;
2355  if (!mPlayerImitated.empty())
2356  {
2357  // TRANSLATORS: imitate command message
2358  std::string msg = strprintf(_("Imitation: %s"), player.c_str());
2359  debugMsg(msg);
2360  }
2361  else
2362  {
2363  // TRANSLATORS: imitate command message
2364  debugMsg(_("Imitation canceled"));
2365  }
2366 }
2367 
2369 {
2370  if (!mPlayerFollowed.empty())
2371  {
2372  // TRANSLATORS: cancel follow message
2373  debugMsg(_("Follow canceled"));
2374  }
2375  if (!mPlayerImitated.empty())
2376  {
2377  // TRANSLATORS: cancel follow message
2378  debugMsg(_("Imitation canceled"));
2379  }
2380  mPlayerFollowed.clear();
2381  mPlayerImitated.clear();
2382 }
2383 
2384 void LocalPlayer::imitateEmote(const Being *const being,
2385  const unsigned char action) const
2386 {
2387  if (being == nullptr)
2388  return;
2389 
2390  std::string player_imitated = getImitate();
2391  if (!player_imitated.empty() && being->mName == player_imitated)
2392  emote(action);
2393 }
2394 
2395 void LocalPlayer::imitateAction(const Being *const being,
2396  const BeingActionT &action)
2397 {
2398  if (being == nullptr)
2399  return;
2400 
2401  if (!mPlayerImitated.empty() && being->mName == mPlayerImitated)
2402  {
2403  setAction(action, 0);
2404  playerHandler->changeAction(action);
2405  }
2406 }
2407 
2408 void LocalPlayer::imitateDirection(const Being *const being,
2409  const unsigned char dir)
2410 {
2411  if (being == nullptr)
2412  return;
2413 
2414  if (!mPlayerImitated.empty() && being->mName == mPlayerImitated)
2415  {
2417  return;
2418 
2419  if (settings.followMode == 2)
2420  {
2421  uint8_t dir2 = 0;
2422  if ((dir & BeingDirection::LEFT) != 0)
2423  dir2 |= BeingDirection::RIGHT;
2424  else if ((dir & BeingDirection::RIGHT) != 0)
2425  dir2 |= BeingDirection::LEFT;
2426  if ((dir & BeingDirection::UP) != 0)
2427  dir2 |= BeingDirection::DOWN;
2428  else if ((dir & BeingDirection::DOWN) != 0)
2429  dir2 |= BeingDirection::UP;
2430 
2431  setDirection(dir2);
2432  playerHandler->setDirection(dir2);
2433  }
2434  else
2435  {
2436  setDirection(dir);
2438  }
2439  }
2440 }
2441 
2442 void LocalPlayer::imitateOutfit(const Being *const player,
2443  const int sprite) const
2444 {
2445  if (player == nullptr)
2446  return;
2447 
2448  if (settings.imitationMode == 1 &&
2449  !mPlayerImitated.empty() &&
2450  player->mName == mPlayerImitated)
2451  {
2452  if (sprite < 0 || sprite >= player->getNumberOfLayers())
2453  return;
2454 
2455  const AnimatedSprite *const equipmentSprite
2456  = dynamic_cast<const AnimatedSprite *>(
2457  player->mSprites[sprite]);
2458 
2459  if (equipmentSprite != nullptr)
2460  {
2461 // logger->log("have equipmentSprite");
2462  const Inventory *const inv = PlayerInfo::getInventory();
2463  if (inv == nullptr)
2464  return;
2465 
2466  const std::string &path = equipmentSprite->getIdPath();
2467  if (path.empty())
2468  return;
2469 
2470 // logger->log("idPath: " + path);
2471  const Item *const item = inv->findItemBySprite(path,
2472  player->getGender(), player->getSubType());
2473  if ((item != nullptr) && item->isEquipped() == Equipped_false)
2475  }
2476  else
2477  {
2478 // logger->log("have unequip %d", sprite);
2479  const int equipmentSlot = inventoryHandler
2480  ->convertFromServerSlot(sprite);
2481 // logger->log("equipmentSlot: " + toString(equipmentSlot));
2482  if (equipmentSlot == inventoryHandler->getProjectileSlot())
2483  return;
2484 
2485  const Item *const item = PlayerInfo::getEquipment(equipmentSlot);
2486  if (item != nullptr)
2487  {
2488 // logger->log("unequiping");
2490  }
2491  }
2492  }
2493 }
2494 
2495 void LocalPlayer::followMoveTo(const Being *const being,
2496  const int x, const int y)
2497 {
2498  if ((being != nullptr) &&
2499  !mPlayerFollowed.empty() &&
2500  being->mName == mPlayerFollowed)
2501  {
2502  mPickUpTarget = nullptr;
2503  navigateTo(x, y);
2504  }
2505 }
2506 
2507 void LocalPlayer::followMoveTo(const Being *const being,
2508  const int x1, const int y1,
2509  const int x2, const int y2)
2510 {
2511  if (being == nullptr)
2512  return;
2513 
2514  mPickUpTarget = nullptr;
2515  if (!mPlayerFollowed.empty() &&
2516  being->mName == mPlayerFollowed)
2517  {
2518  switch (settings.followMode)
2519  {
2520  case 0:
2521  navigateTo(x1, y1);
2522  setNextDest(x2, y2);
2523  break;
2524  case 1:
2525  if (x1 != x2 || y1 != y2)
2526  {
2527  navigateTo(mX + x2 - x1, mY + y2 - y1);
2528  setNextDest(mX + x2 - x1, mY + y2 - y1);
2529  }
2530  break;
2531  case 2:
2532  if (x1 != x2 || y1 != y2)
2533  {
2534  navigateTo(mX + x1 - x2, mY + y1 - y2);
2535  setNextDest(mX + x1 - x2, mY + y1 - y2);
2536  }
2537  break;
2538  case 3:
2539  if (mTarget == nullptr ||
2541  {
2542  if (actorManager != nullptr)
2543  {
2544  Being *const b = actorManager->findBeingByName(
2546  setTarget(b);
2547  }
2548  }
2549  moveToTarget(-1);
2550  setNextDest(x2, y2);
2551  break;
2552  default:
2553  break;
2554  }
2555  }
2556 }
2557 
2558 void LocalPlayer::setNextDest(const int x, const int y)
2559 {
2560  mNextDestX = x;
2561  mNextDestY = y;
2562 }
2563 
2565 {
2566  if (mIsServerBuggy)
2567  {
2568  if (mLastAction != -1)
2569  return false;
2571  }
2572  return true;
2573 }
2574 
2576 {
2577  if ((mCrossX == 0) && (mCrossY == 0))
2578  return;
2579 
2580  const int dx = abs(mX - mCrossX);
2581  const int dy = abs(mY - mCrossY);
2582  const int dist = dx > dy ? dx : dy;
2583  const time_t time = cur_time;
2584  const int maxDist = mSyncPlayerMove ? mSyncPlayerMoveDistance : 7;
2585 
2586  if (dist > maxDist)
2587  {
2588  mActivityTime = time;
2590 // alternative way to fix, move to real position
2591 // setDestination(mCrossX, mCrossY);
2592  }
2593 }
2594 
2595 void LocalPlayer::setRealPos(const int x, const int y)
2596 {
2597  if (mMap == nullptr)
2598  return;
2599 
2600  SpecialLayer *const layer = mMap->getTempLayer();
2601  if (layer != nullptr)
2602  {
2603  bool cacheUpdated(false);
2604  if (((mCrossX != 0) || (mCrossY != 0)) &&
2605  (layer->getTile(mCrossX, mCrossY) != nullptr) &&
2607  {
2609  layer->updateCache();
2610  cacheUpdated = true;
2611  }
2612 
2613  if (mShowServerPos)
2614  {
2615  const MapItem *const mapItem = layer->getTile(x, y);
2616 
2617  if (mapItem == nullptr ||
2618  mapItem->getType() == MapItemType::EMPTY)
2619  {
2620  if (mX != x && mY != y)
2621  {
2622  layer->setTile(x, y, MapItemType::CROSS);
2623  if (cacheUpdated == false)
2624  layer->updateCache();
2625  }
2626  }
2627  }
2628 
2629  if (mCrossX != x || mCrossY != y)
2630  {
2631  mCrossX = x;
2632  mCrossY = y;
2633  }
2634  }
2635  if (mMap->isCustom())
2636  mMap->setWalk(x, y);
2637 }
2639 {
2640  if ((mMap == nullptr) || (mTarget == nullptr))
2641  return;
2642 
2643  if (settings.moveToTargetType == 11 || (settings.attackType == 0u)
2644  || !config.getBoolValue("autofixPos"))
2645  {
2646  return;
2647  }
2648 
2649  const Path debugPath = mMap->findPath(
2650  (mPixelX - mapTileSize / 2) / mapTileSize,
2652  mTarget->mX,
2653  mTarget->mY,
2654  getBlockWalkMask(),
2655  0);
2656 
2657  if (!debugPath.empty())
2658  {
2659  const Path::const_iterator i = debugPath.begin();
2660  setDestination((*i).x, (*i).y);
2661  }
2662 }
2663 
2665 {
2666  navigateClean();
2667 }
2668 
2670 {
2672 }
2673 
2675 {
2676  if (mMap != nullptr)
2677  {
2678  const std::map<std::string, Vector>::const_iterator iter =
2679  mHomes.find(mMap->getProperty("_realfilename", std::string()));
2680 
2681  if (iter != mHomes.end())
2682  {
2683  const Vector &pos = mHomes[(*iter).first];
2684  if ((pos.x != 0.0f) && (pos.y != 0.0f))
2685  {
2687  CAST_S32(pos.x), CAST_S32(pos.y));
2688  }
2689  }
2690  }
2691 }
2692 
2694  const int y A_UNUSED)
2695 {
2696 }
2697 
2698 void LocalPlayer::waitFor(const std::string &nick)
2699 {
2700  mWaitFor = nick;
2701 }
2702 
2704 {
2705  if (being == nullptr)
2706  return;
2707 
2708  const std::string &nick = being->mName;
2709  if (being->getType() == ActorType::Player)
2710  {
2711  const Guild *const guild = getGuild();
2712  if (guild != nullptr)
2713  {
2714  const GuildMember *const gm = guild->getMember(nick);
2715  if (gm != nullptr)
2716  {
2717  const int level = gm->getLevel();
2718  if (level > 1 && being->getLevel() != level)
2719  {
2720  being->setLevel(level);
2721  being->updateName();
2722  }
2723  }
2724  }
2725  if (chatWindow != nullptr)
2726  {
2727  WhisperTab *const tab = chatWindow->getWhisperTab(nick);
2728  if (tab != nullptr)
2729  tab->setWhisperTabColors();
2730  }
2731  }
2732 
2733  if (!mWaitFor.empty() && mWaitFor == nick)
2734  {
2735  // TRANSLATORS: wait player/monster message
2736  debugMsg(strprintf(_("You see %s"), mWaitFor.c_str()));
2738  mWaitFor.clear();
2739  }
2740 }
2741 
2742 unsigned char LocalPlayer::getBlockWalkMask() const
2743 {
2744  // for now blocking all types of collisions
2745  return BlockMask::WALL |
2746  BlockMask::AIR |
2749 }
2750 
2752 {
2753  if (mMap == nullptr)
2754  return;
2755 
2756  const std::string key = mMap->getProperty("_realfilename", std::string());
2757  const std::map<std::string, Vector>::iterator iter = mHomes.find(key);
2758 
2759  if (iter != mHomes.end())
2760  mHomes.erase(key);
2761 }
2762 
2764 {
2765  mBlockAdvert = true;
2766 }
2767 
2769 {
2770  if (target == nullptr)
2771  return false;
2772 
2773  switch (settings.pvpAttackType)
2774  {
2775  case 0:
2776  return true;
2777  case 1:
2778  return !(playerRelations.getRelation(target->mName)
2779  == Relation::FRIEND);
2780  case 2:
2781  return playerRelations.checkBadRelation(target->mName);
2782  default:
2783  case 3:
2784  return false;
2785  }
2786 }
2787 
2789 {
2791  {
2792  uint8_t status = 0;
2794  {
2795  if (mTradebot &&
2796  shopWindow != nullptr &&
2797  !shopWindow->isShopEmpty())
2798  {
2799  status |= BeingFlag::SHOP;
2800  }
2801  }
2803  status |= BeingFlag::AWAY;
2804 
2805  if (mInactive)
2806  status |= BeingFlag::INACTIVE;
2807 
2808  playerHandler->updateStatus(status);
2809  }
2810 }
2811 
2812 void LocalPlayer::setTestParticle(const std::string &fileName,
2813  const bool updateHash)
2814 {
2817  if (mTestParticle != nullptr)
2818  {
2820  mTestParticle = nullptr;
2821  }
2822  if (!fileName.empty())
2823  {
2824  mTestParticle = particleEngine->addEffect(fileName, 0, 0, 0);
2826  if (updateHash)
2828  }
2829 }
2830 
2832 {
2833  if (mAction != BeingAction::DEAD)
2834  {
2837  }
2838 }
2839 
2841 {
2842  return !mFreezed &&
2846 }
2847 
2848 void LocalPlayer::freezeMoving(const int timeWaitTicks)
2849 {
2850  if (timeWaitTicks <= 0)
2851  return;
2852  const int nextTime = tick_time + timeWaitTicks;
2853  if (mUnfreezeTime < nextTime)
2854  mUnfreezeTime = nextTime;
2855  if (mUnfreezeTime > 0)
2856  mFreezed = true;
2857 }
Net::PlayerHandler * playerHandler
Definition: net.cpp:89
void setDistance(const int n)
Definition: being.h:548
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:88
#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:1300
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:222
void localChatInput(const std::string &msg) const
Definition: chatwindow.cpp:685
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:370
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:1164
unsigned int attackType
Definition: settings.h:136
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:538
unsigned int getMoveState() const
Definition: localplayer.h:192
unsigned int followMode
Definition: settings.h:134
void loadGMCommands()
Definition: chatwindow.cpp:258
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:253
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:4980
void tryPingRequest()
std::string fileName
Definition: testmain.cpp:36
uint8_t mDirection
Definition: being.h:1226
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:619
void setMap(Map *const map)
const Color * mNameColor
Definition: being.h:1187
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:135
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:823
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
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:1192
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:1215
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:4993
virtual void setGroupId(const int id)
Definition: being.h:1080
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:1194
void setWalk(const int x, const int y)
Definition: map.cpp:795
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:1251
const std::string & getName() const
Definition: iteminfo.h:73
#define delete2(var)
Definition: delete2.h:24
virtual void nextTile()
Definition: being.cpp:1753
const WalkLayer * getWalkLayer() const
Definition: map.h:336
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:2476
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:1287
bool mBlockAdvert
Definition: localplayer.h:508
Vector mDest
Definition: being.h:1196
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:133
bool pickUpItems(int pickUpType)
Logger * logger
Definition: logger.cpp:95
LocalPlayer(const BeingId id, const BeingTypeId subType)
unsigned int imitationMode
Definition: settings.h:141
int get_elapsed_time(const int startTime)
Definition: timer.cpp:93
void freezeMoving(const int timeWaitTicks)
std::string mName
Definition: being.h:1176
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:1380
void controlCustomParticle(Particle *const particle)
virtual void setDirection(const uint8_t direction)
Definition: being.cpp:1661
#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:603
static std::string getKeyShortString(const std::string &key)
std::list< MessagePair > mMessages
Definition: localplayer.h:476
bool mInactive
Definition: being.h:1378
MiniStatusWindow * miniStatusWindow
std::string mPlayerImitated
Definition: localplayer.h:464
Net::ServerFeatures * serverFeatures
Definition: net.cpp:94
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)
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:1309
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:313
void targetMoved() const
void setTargetType(const TargetCursorTypeT type)
LocalPlayer * localPlayer
SpecialLayer * getSpecialLayer() const
Definition: map.h:237
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:481
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:92
void stopAttack(const bool keepAttack)
unsigned int pvpAttackType
Definition: settings.h:140
PartyMember * getMember(const BeingId id) const
Definition: party.cpp:99
#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:131
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:287
bool navigateTo(const int x, const int y)
void setReachable(const ReachableT n)
Definition: being.h:600
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)
void setGM(const bool gm)
Definition: being.cpp:3639
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:1525
volatile time_t cur_time
Definition: timer.cpp:57
void untarget()
int mPixelY
Definition: actor.h:133
const Color & getColor(ThemeColorIdT type, const unsigned int alpha)
Definition: theme.h:135
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:773
void imitateDirection(const Being *const being, const unsigned char dir)
int mActionTime
Definition: being.h:1208
Net::InventoryHandler * inventoryHandler
Definition: net.cpp:82
void addRoad(const Path &road)
Definition: map.h:68
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:99
int getPathLength(const Being *const being) const
ServerTypeT getNetworkType()
Definition: net.cpp:178
WeaponsInfos::const_iterator WeaponsInfosIter
Definition: weaponsdb.h:29
SpecialLayer * getTempLayer() const
Definition: map.h:234
void updateName()
Definition: being.cpp:3398
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:80
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:1310
bool mPathSetByMouse
Definition: localplayer.h:528
void logic()
Definition: being.cpp:1805
Path findPath(const int startX, const int startY, const int destX, const int destY, const unsigned char blockWalkmask, const int maxCost)
Definition: map.cpp:851
bool gm(InputEvent &event)
Definition: commands.cpp:74
int getWalkSpeed() const
Definition: being.h:451
void changeEquipmentBeforeAttack(const Being *const target) const
void setTarget(Being *const target)
Attributes ::T AttributesT
Definition: attributes.h:117
bool awayMode
Definition: settings.h:155
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:4334
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:156
const BeingId BeingId_zero
Definition: beingid.h:29
bool checkBadRelation(const std::string &name) const
unsigned int magicAttackType
Definition: settings.h:139
virtual int getLevel() const
Definition: being.h:597
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:1217
std::string getPingTime() const
static const std::string SOUND_INFO
Definition: sound.h:26
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:393
BeingActionT mAction
Definition: being.h:1224
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:1375
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:138
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:594
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:552
VisibleName::Type mVisibleNames
Definition: localplayer.h:511
bool isCustom() const
Definition: map.h:323
bool pickUp(FloorItem *const item)
void volumeRestore() const
bool mKeepAttacking
Definition: localplayer.h:526
Map * mMap
Definition: actor.h:138