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;
671  int dy = 0;
672  if ((dir & BeingDirection::UP) != 0)
673  dy--;
674  if ((dir & BeingDirection::DOWN) != 0)
675  dy++;
676  if ((dir & BeingDirection::LEFT) != 0)
677  dx--;
678  if ((dir & BeingDirection::RIGHT) != 0)
679  dx++;
680 
681  const unsigned char blockWalkMask = getBlockWalkMask();
682  // Prevent skipping corners over colliding tiles
683  if ((dx != 0) && !mMap->getWalk(mX + dx, mY, blockWalkMask))
684  dx = 0;
685  if ((dy != 0) && !mMap->getWalk(mX, mY + dy, blockWalkMask))
686  dy = 0;
687 
688  // Choose a straight direction when diagonal target is blocked
689  if (dx != 0 && dy != 0 && !mMap->getWalk(mX + dx, mY + dy, blockWalkMask))
690  dx = 0;
691 
692  // Walk to where the player can actually go
693  if ((dx != 0 || dy != 0) && mMap->getWalk(mX + dx, mY + dy, blockWalkMask))
694  {
695  setDestination(mX + dx, mY + dy);
696  }
697  else if (dir != mDirection)
698  {
699  // If the being can't move, just change direction
700 
701 // if (PacketLimiter::limitPackets(PacketType::PACKET_DIRECTION))
702  {
704  setDirection(dir);
705  }
706  }
707 }
708 
709 void LocalPlayer::stopWalking(const bool sendToServer)
710 {
711  if (mAction == BeingAction::MOVE && (mWalkingDir != 0U))
712  {
713  mWalkingDir = 0;
714  mPickUpTarget = nullptr;
716  mPixelY);
717  if (sendToServer)
718  {
720  mPixelX,
721  mPixelY,
722  -1);
723  }
725  }
726 
727  // No path set anymore, so we reset the path by mouse flag
728  mPathSetByMouse = false;
729 
730  clearPath();
731  navigateClean();
732 }
733 
735 {
737  return false;
738 
739  BeingActionT newAction;
740  switch (mAction)
741  {
742  case BeingAction::STAND:
744  case BeingAction::SPAWN:
745  newAction = BeingAction::SIT;
746  break;
747  case BeingAction::SIT:
748  newAction = BeingAction::STAND;
749  break;
750  case BeingAction::MOVE:
751  case BeingAction::ATTACK:
752  case BeingAction::DEAD:
753  case BeingAction::HURT:
754  case BeingAction::CAST:
755  default:
756  return true;
757  }
758 
759  playerHandler->changeAction(newAction);
760  return true;
761 }
762 
764 {
766  return false;
767 
769  return true;
770 }
771 
772 bool LocalPlayer::emote(const uint8_t emotion)
773 {
775  return false;
776 
777  playerHandler->emote(emotion);
778  return true;
779 }
780 
781 void LocalPlayer::attack(Being *const target,
782  const bool keep,
783  const bool dontChangeEquipment)
784 {
785  mKeepAttacking = keep;
786 
787  if ((target == nullptr) || target->getType() == ActorType::Npc)
788  return;
789 
790  if (mTarget != target)
791  setTarget(target);
792 
793  // Must be standing or sitting or casting to attack
794  if (mAction != BeingAction::STAND &&
797  {
798  return;
799  }
800 
801 #ifdef TMWA_SUPPORT
802  const int dist_x = target->mX - mX;
803  const int dist_y = target->mY - mY;
804 
806  {
807  if (abs(dist_y) >= abs(dist_x))
808  {
809  if (dist_y > 0)
811  else
813  }
814  else
815  {
816  if (dist_x > 0)
818  else
820  }
821  }
822 #endif // TMWA_SUPPORT
823 
825 
826  if (target->getType() != ActorType::Player
827  || checAttackPermissions(target))
828  {
830 
832  return;
833 
834  if (!dontChangeEquipment)
836 
837  const BeingId targetId = target->getId();
838  playerHandler->attack(targetId, mServerAttack);
840  }
841 
842  if (!keep)
843  stopAttack(false);
844 }
845 
846 void LocalPlayer::stopAttack(const bool keepAttack)
847 {
849  return;
850 
853 
854  untarget();
855  if (!keepAttack || !mAttackNext)
856  mKeepAttacking = false;
857 }
858 
860 {
863 
864  if (mTarget != nullptr)
865  setTarget(nullptr);
866 }
867 
869  const int amount,
870  const ItemColor color,
871  const BeingId floorItemId,
872  const PickupT fail)
873 {
874  if (fail != Pickup::OKAY)
875  {
876  if ((actorManager != nullptr) && floorItemId != BeingId_zero)
877  {
878  FloorItem *const item = actorManager->findItem(floorItemId);
879  if (item != nullptr)
880  {
881  if (!item->getShowMsg())
882  return;
883  item->setShowMsg(false);
884  }
885  }
886  const char* msg = nullptr;
887  switch (fail)
888  {
889  case Pickup::BAD_ITEM:
890  // TRANSLATORS: pickup error message
891  msg = N_("Tried to pick up nonexistent item.");
892  break;
893  case Pickup::TOO_HEAVY:
894  // TRANSLATORS: pickup error message
895  msg = N_("Item is too heavy.");
896  break;
897  case Pickup::TOO_FAR:
898  // TRANSLATORS: pickup error message
899  msg = N_("Item is too far away.");
900  break;
901  case Pickup::INV_FULL:
902  // TRANSLATORS: pickup error message
903  msg = N_("Inventory is full.");
904  break;
905  case Pickup::STACK_FULL:
906  // TRANSLATORS: pickup error message
907  msg = N_("Stack is too big.");
908  break;
909  case Pickup::DROP_STEAL:
910  // TRANSLATORS: pickup error message
911  msg = N_("Item belongs to someone else.");
912  break;
913  case Pickup::MAX_AMOUNT:
914  // TRANSLATORS: pickup error message
915  msg = N_("You can't pickup this amount of items.");
916  break;
918  // TRANSLATORS: pickup error message
919  msg = N_("Your item stack has max amount.");
920  break;
921  case Pickup::OKAY:
922  break;
923  default:
924  case Pickup::UNKNOWN:
925  // TRANSLATORS: pickup error message
926  msg = N_("Unknown problem picking up item.");
927  break;
928  }
929  if (localChatTab != nullptr &&
930  config.getBoolValue("showpickupchat"))
931  {
932  localChatTab->chatLog(gettext(msg),
936  }
937 
938  if ((mMap != nullptr) && config.getBoolValue("showpickupparticle"))
939  {
940  // Show pickup notification
942  }
943  }
944  else
945  {
946  std::string str;
947 #ifdef TMWA_SUPPORT
949  {
950  str = itemInfo.getName();
951  }
952  else
953 #endif // TMWA_SUPPORT
954  {
955  str = itemInfo.getName(color);
956  }
957 
958  if (config.getBoolValue("showpickupchat") && (localChatTab != nullptr))
959  {
960  // TRANSLATORS: %d is number,
961  // [@@%d|%[email protected]@] - here player can see link to item
962  localChatTab->chatLog(strprintf(ngettext("You picked up %d "
963  "[@@%d|%[email protected]@].", "You picked up %d [@@%d|%[email protected]@].", amount),
964  amount, itemInfo.getId(), str.c_str()),
968  }
969 
970  if ((mMap != nullptr) && config.getBoolValue("showpickupparticle"))
971  {
972  // Show pickup notification
973  if (amount > 1)
974  {
975  addMessageToQueue(strprintf("%d x %s", amount,
976  str.c_str()), UserColorId::PICKUP_INFO);
977  }
978  else
979  {
981  }
982  }
983  }
984 }
985 
987 {
988  if (mAttackRange > -1)
989  {
990  return mAttackRange;
991  }
992 
993  const Item *const weapon = PlayerInfo::getEquipment(
995  if (weapon != nullptr)
996  {
997  const ItemInfo &info = weapon->getInfo();
998  return info.getAttackRange();
999  }
1000  return 48; // unarmed range
1001 }
1002 
1003 bool LocalPlayer::withinAttackRange(const Being *const target,
1004  const bool fixDistance,
1005  const int addRange) const
1006 {
1007  if (target == nullptr)
1008  return false;
1009 
1010  int range = getAttackRange() + addRange;
1011  int dx;
1012  int dy;
1013 
1014  if (fixDistance && range == 1)
1015  range = 2;
1016 
1017  dx = CAST_S32(abs(target->mX - mX));
1018  dy = CAST_S32(abs(target->mY - mY));
1019  return !(dx > range || dy > range);
1020 }
1021 
1023 {
1024  if (target == nullptr)
1025  return;
1026 
1027  mPickUpTarget = nullptr;
1028  setTarget(target);
1029  mGoingToTarget = true;
1030  navigateTo(target->mX,
1031  target->mY);
1032 }
1033 
1035  const int32_t effectId,
1036  const Enable newStatus,
1037  const IsStart start)
1038 {
1040  effectId,
1041  newStatus,
1042  start);
1043 
1044  if (effect != nullptr)
1045  {
1046  effect->deliverMessage();
1047  effect->playSFX();
1048 
1049  AnimatedSprite *const sprite = effect->getIcon();
1050 
1051  if (sprite == nullptr)
1052  {
1053  // delete sprite, if necessary
1054  for (size_t i = 0; i < mStatusEffectIcons.size(); )
1055  {
1056  if (mStatusEffectIcons[i] == effectId)
1057  {
1058  mStatusEffectIcons.erase(mStatusEffectIcons.begin() + i);
1059  if (miniStatusWindow != nullptr)
1061  }
1062  else
1063  {
1064  i++;
1065  }
1066  }
1067  }
1068  else
1069  {
1070  // replace sprite or append
1071  bool found = false;
1072  const size_t sz = mStatusEffectIcons.size();
1073  for (size_t i = 0; i < sz; i++)
1074  {
1075  if (mStatusEffectIcons[i] == effectId)
1076  {
1077  if (miniStatusWindow != nullptr)
1078  miniStatusWindow->setIcon(CAST_S32(i), sprite);
1079  found = true;
1080  break;
1081  }
1082  }
1083 
1084  if (!found)
1085  { // add new
1086  const int offset = CAST_S32(mStatusEffectIcons.size());
1087  if (miniStatusWindow != nullptr)
1088  miniStatusWindow->setIcon(offset, sprite);
1089  mStatusEffectIcons.push_back(effectId);
1090  }
1091  }
1092  }
1093 }
1094 
1095 void LocalPlayer::addMessageToQueue(const std::string &message,
1096  const UserColorIdT color)
1097 {
1098  if (mMessages.size() < 20)
1099  mMessages.push_back(MessagePair(message, color));
1100 }
1101 
1102 void LocalPlayer::optionChanged(const std::string &value)
1103 {
1104  if (value == "showownname")
1105  {
1106  setShowName(config.getBoolValue("showownname"));
1107  }
1108  else if (value == "targetDeadPlayers")
1109  {
1110  mTargetDeadPlayers = config.getBoolValue("targetDeadPlayers");
1111  }
1112  else if (value == "enableBuggyServers")
1113  {
1114  mIsServerBuggy = serverConfig.getBoolValue("enableBuggyServers");
1115  }
1116  else if (value == "syncPlayerMove")
1117  {
1118  mSyncPlayerMove = config.getBoolValue("syncPlayerMove");
1119  }
1120  else if (value == "syncPlayerMoveDistance")
1121  {
1122  mSyncPlayerMoveDistance = config.getIntValue("syncPlayerMoveDistance");
1123  }
1124  else if (value == "drawPath")
1125  {
1126  mDrawPath = config.getBoolValue("drawPath");
1127  }
1128  else if (value == "serverAttack")
1129  {
1130  mServerAttack = fromBool(config.getBoolValue("serverAttack"), Keep);
1131  }
1132  else if (value == "attackMoving")
1133  {
1134  mAttackMoving = config.getBoolValue("attackMoving");
1135  }
1136  else if (value == "attackNext")
1137  {
1138  mAttackNext = config.getBoolValue("attackNext");
1139  }
1140  else if (value == "showJobExp")
1141  {
1142  mShowJobExp = config.getBoolValue("showJobExp");
1143  }
1144  else if (value == "enableAdvert")
1145  {
1146  mEnableAdvert = config.getBoolValue("enableAdvert");
1147  }
1148  else if (value == "tradebot")
1149  {
1150  mTradebot = config.getBoolValue("tradebot");
1151  }
1152  else if (value == "targetOnlyReachable")
1153  {
1154  mTargetOnlyReachable = config.getBoolValue("targetOnlyReachable");
1155  }
1156  else if (value == "showserverpos")
1157  {
1158  mShowServerPos = config.getBoolValue("showserverpos");
1159  }
1160  else if (value == "visiblenames")
1161  {
1162  mVisibleNames = static_cast<VisibleName::Type>(
1163  config.getIntValue("visiblenames"));
1164  }
1165 }
1166 
1167 void LocalPlayer::addJobMessage(const int64_t change)
1168 {
1169  if (change != 0 && mMessages.size() < 20)
1170  {
1171  const std::string xpStr = toString(CAST_U64(change));
1172  if (!mMessages.empty())
1173  {
1174  MessagePair pair = mMessages.back();
1175  // TRANSLATORS: this is normal experience
1176  if (pair.first.find(strprintf(" %s", _("xp"))) ==
1177  // TRANSLATORS: this is normal experience
1178  pair.first.size() - strlen(_("xp")) - 1)
1179  {
1180  mMessages.pop_back();
1181  pair.first.append(strprintf(", %s %s",
1182  xpStr.c_str(),
1183  // TRANSLATORS: this is job experience
1184  _("job")));
1185  mMessages.push_back(pair);
1186  }
1187  else
1188  {
1189  addMessageToQueue(strprintf("%s %s",
1190  xpStr.c_str(),
1191  // TRANSLATORS: this is job experience
1192  _("job")),
1194  }
1195  }
1196  else
1197  {
1198  addMessageToQueue(strprintf("%s %s",
1199  xpStr.c_str(),
1200  // TRANSLATORS: this is job experience
1201  _("job")),
1203  }
1204  }
1205 }
1206 
1207 void LocalPlayer::addXpMessage(const int64_t change)
1208 {
1209  if (change != 0 && mMessages.size() < 20)
1210  {
1211  addMessageToQueue(strprintf("%s %s",
1212  toString(CAST_U64(change)).c_str(),
1213  // TRANSLATORS: get xp message
1214  _("xp")),
1216  }
1217 }
1218 
1219 void LocalPlayer::addHomunXpMessage(const int change)
1220 {
1221  if (change != 0 && mMessages.size() < 20)
1222  {
1223  addMessageToQueue(strprintf("%s %d %s",
1224  // TRANSLATORS: get homunculus xp message
1225  _("Homun"),
1226  change,
1227  // TRANSLATORS: get xp message
1228  _("xp")),
1230  }
1231 }
1232 
1233 void LocalPlayer::addHpMessage(const int change)
1234 {
1235  if (change != 0 && mMessages.size() < 20)
1236  {
1237  // TRANSLATORS: get hp message
1238  addMessageToQueue(strprintf("%d %s", change, _("hp")),
1240  }
1241 }
1242 
1243 void LocalPlayer::addSpMessage(const int change)
1244 {
1245  if (change != 0 && mMessages.size() < 20)
1246  {
1247  // TRANSLATORS: get hp message
1248  addMessageToQueue(strprintf("%d %s", change, _("mana")),
1250  }
1251 }
1252 
1254  const int64_t oldVal,
1255  const int64_t newVal)
1256 {
1257  PRAGMA45(GCC diagnostic push)
1258  PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
1259  switch (id)
1260  {
1262  {
1264  break;
1265  if (oldVal > newVal)
1266  break;
1267 
1268  const int change = CAST_S32(newVal - oldVal);
1269  addXpMessage(change);
1270  break;
1271  }
1273  mLevel = CAST_S32(newVal);
1274  break;
1275  case Attributes::PLAYER_HP:
1276  if (oldVal != 0 && newVal == 0)
1278  break;
1280  {
1281  if (!mShowJobExp ||
1283  {
1284  return;
1285  }
1286  if (oldVal > newVal ||
1289  {
1290  return;
1291  }
1292  const int32_t change = CAST_S32(newVal - oldVal);
1293  addJobMessage(change);
1294  break;
1295  }
1296  default:
1297  break;
1298  }
1299  PRAGMA45(GCC diagnostic pop)
1300 }
1301 
1302 void LocalPlayer::move(const int dX, const int dY)
1303 {
1304  mPickUpTarget = nullptr;
1305  setDestination(mX + dX, mY + dY);
1306 }
1307 
1309 {
1310  bool gotPos(false);
1311  Path debugPath;
1312 
1313  size_t limit(0);
1314 
1315  if (dist == -1)
1316  {
1317  dist = settings.moveToTargetType;
1318  if (dist != 0)
1319  {
1320  const bool broken = (Net::getNetworkType() ==
1322  switch (dist)
1323  {
1324  case 10:
1325  dist = mAttackRange;
1326  if (dist == 1 && broken)
1327  dist = 2;
1328  break;
1329  case 11:
1330  dist = mAttackRange - 1;
1331  if (dist < 1)
1332  dist = 1;
1333  if (dist == 1 && broken)
1334  dist = 2;
1335  break;
1336  default:
1337  break;
1338  }
1339  }
1340  }
1341 
1342  if (mTarget != nullptr)
1343  {
1344  if (mMap != nullptr)
1345  {
1346  debugPath = mMap->findPath(
1347  (mPixelX - mapTileSize / 2) / mapTileSize,
1349  mTarget->mX,
1350  mTarget->mY,
1351  getBlockWalkMask(),
1352  0);
1353  }
1354 
1355  const size_t sz = debugPath.size();
1356  if (sz < CAST_SIZE(dist))
1357  return;
1358  limit = CAST_S32(sz) - dist;
1359  gotPos = true;
1360  }
1361  else if ((mNavigateX != 0) || (mNavigateY != 0))
1362  {
1363  debugPath = mNavigatePath;
1364  limit = dist;
1365  gotPos = true;
1366  }
1367 
1368  if (gotPos)
1369  {
1370  if (dist == 0)
1371  {
1372  if (mTarget != nullptr)
1374  }
1375  else
1376  {
1377  Position pos(0, 0);
1378  size_t f = 0;
1379 
1380  for (Path::const_iterator i = debugPath.begin(),
1381  i_fend = debugPath.end();
1382  i != i_fend && f < limit; ++i, f++)
1383  {
1384  pos = (*i);
1385  }
1386  navigateTo(pos.x, pos.y);
1387  }
1388  }
1389  else if ((mLastTargetX != 0) || (mLastTargetY != 0))
1390  {
1392  }
1393 }
1394 
1396 {
1397  mPickUpTarget = nullptr;
1398  if ((mX != mCrossX || mY != mCrossY) && (mCrossX != 0) && (mCrossY != 0))
1399  {
1401  }
1402  else if (mMap != nullptr)
1403  {
1404  const std::map<std::string, Vector>::const_iterator iter =
1405  mHomes.find(mMap->getProperty("_realfilename", std::string()));
1406 
1407  if (iter != mHomes.end())
1408  {
1409  const Vector pos = mHomes[(*iter).first];
1410  if (mX == pos.x && mY == pos.y)
1411  {
1413  CAST_S32(pos.x),
1414  CAST_S32(pos.y),
1415  CAST_S32(mDirection));
1416  }
1417  else
1418  {
1419  navigateTo(CAST_S32(pos.x), CAST_S32(pos.y));
1420  }
1421  }
1422  }
1423 }
1424 
1425 void LocalPlayer::changeEquipmentBeforeAttack(const Being *const target) const
1426 {
1427  if (settings.attackWeaponType == 1
1428  || (target == nullptr)
1429  || (PlayerInfo::getInventory() == nullptr))
1430  {
1431  return;
1432  }
1433 
1434  bool allowSword = false;
1435  const int dx = target->mX - mX;
1436  const int dy = target->mY - mY;
1437  const Item *item = nullptr;
1438 
1439  if (dx * dx + dy * dy > 80)
1440  return;
1441 
1442  if (dx * dx + dy * dy < 8)
1443  allowSword = true;
1444 
1445  const Inventory *const inv = PlayerInfo::getInventory();
1446  if (inv == nullptr)
1447  return;
1448 
1449  // if attack distance for sword
1450  if (allowSword)
1451  {
1452  // searching swords
1453  const WeaponsInfos &swords = WeaponsDB::getSwords();
1454  FOR_EACH (WeaponsInfosIter, it, swords)
1455  {
1456  item = inv->findItem(*it, ItemColor_zero);
1457  if (item != nullptr)
1458  break;
1459  }
1460 
1461  // no swords
1462  if (item == nullptr)
1463  return;
1464 
1465  // if sword not equiped
1466  if (item->isEquipped() == Equipped_false)
1468 
1469  // if need equip shield too
1470  if (settings.attackWeaponType == 3)
1471  {
1472  // searching shield
1473  const WeaponsInfos &shields = WeaponsDB::getShields();
1474  FOR_EACH (WeaponsInfosIter, it, shields)
1475  {
1476  item = inv->findItem(*it, ItemColor_zero);
1477  if (item != nullptr)
1478  break;
1479  }
1480  if ((item != nullptr) && item->isEquipped() == Equipped_false)
1482  }
1483  }
1484  // big distance. allowed only bow
1485  else
1486  {
1487  // searching bow
1488  const WeaponsInfos &bows = WeaponsDB::getBows();
1489  FOR_EACH (WeaponsInfosIter, it, bows)
1490  {
1491  item = inv->findItem(*it, ItemColor_zero);
1492  if (item != nullptr)
1493  break;
1494  }
1495 
1496  // no bow
1497  if (item == nullptr)
1498  return;
1499 
1500  if (item->isEquipped() == Equipped_false)
1502  }
1503 }
1504 
1506  const int maxCost)
1507 {
1508  if ((being == nullptr) || (mMap == nullptr))
1509  return false;
1510 
1511  if (being->getReachable() == Reachable::REACH_NO)
1512  return false;
1513 
1514  if (being->mX == mX &&
1515  being->mY == mY)
1516  {
1517  being->setDistance(0);
1519  return true;
1520  }
1521  else if (being->mX - 1 <= mX &&
1522  being->mX + 1 >= mX &&
1523  being->mY - 1 <= mY &&
1524  being->mY + 1 >= mY)
1525  {
1526  being->setDistance(1);
1528  return true;
1529  }
1530 
1531  const Path debugPath = mMap->findPath(
1532  (mPixelX - mapTileSize / 2) / mapTileSize,
1534  being->mX,
1535  being->mY,
1536  getBlockWalkMask(),
1537  maxCost);
1538 
1539  being->setDistance(CAST_S32(debugPath.size()));
1540  if (!debugPath.empty())
1541  {
1543  return true;
1544  }
1546  return false;
1547 }
1548 
1549 bool LocalPlayer::isReachable(const int x, const int y,
1550  const bool allowCollision) const
1551 {
1552  const WalkLayer *const walk = mMap->getWalkLayer();
1553  if (walk == nullptr)
1554  return false;
1555  int num = walk->getDataAt(x, y);
1556  if (allowCollision && num < 0)
1557  num = -num;
1558 
1559  return walk->getDataAt(mX, mY) == num;
1560 }
1561 
1562 bool LocalPlayer::pickUpItems(int pickUpType)
1563 {
1564  if (actorManager == nullptr)
1565  return false;
1566 
1567  bool status = false;
1568  int x = mX;
1569  int y = mY;
1570 
1571  // first pick up item on player position
1572  FloorItem *item =
1573  actorManager->findItem(x, y);
1574  if (item != nullptr)
1575  status = pickUp(item);
1576 
1577  if (pickUpType == 0)
1578  pickUpType = settings.pickUpType;
1579 
1580  if (pickUpType == 0)
1581  return status;
1582 
1583  int x1;
1584  int y1;
1585  int x2;
1586  int y2;
1587  switch (pickUpType)
1588  {
1589  case 1:
1590  switch (mDirection)
1591  {
1592  case BeingDirection::UP : --y; break;
1593  case BeingDirection::DOWN : ++y; break;
1594  case BeingDirection::LEFT : --x; break;
1595  case BeingDirection::RIGHT: ++x; break;
1596  default: break;
1597  }
1598  item = actorManager->findItem(x, y);
1599  if (item != nullptr)
1600  status = pickUp(item);
1601  break;
1602  case 2:
1603  switch (mDirection)
1604  {
1605  case BeingDirection::UP:
1606  x1 = x - 1; y1 = y - 1; x2 = x + 1; y2 = y; break;
1607  case BeingDirection::DOWN:
1608  x1 = x - 1; y1 = y; x2 = x + 1; y2 = y + 1; break;
1609  case BeingDirection::LEFT:
1610  x1 = x - 1; y1 = y - 1; x2 = x; y2 = y + 1; break;
1611  case BeingDirection::RIGHT:
1612  x1 = x; y1 = y - 1; x2 = x + 1; y2 = y + 1; break;
1613  default:
1614  x1 = x; x2 = x; y1 = y; y2 = y; break;
1615  }
1616  if (actorManager->pickUpAll(x1, y1, x2, y2, false))
1617  status = true;
1618  break;
1619  case 3:
1620  if (actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1621  status = true;
1622  break;
1623 
1624  case 4:
1625  if (!actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1626  {
1627  if (actorManager->pickUpNearest(x, y, 4))
1628  status = true;
1629  }
1630  else
1631  {
1632  status = true;
1633  }
1634  break;
1635 
1636  case 5:
1637  if (!actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1638  {
1639  if (actorManager->pickUpNearest(x, y, 8))
1640  status = true;
1641  }
1642  else
1643  {
1644  status = true;
1645  }
1646  break;
1647 
1648  case 6:
1649  if (!actorManager->pickUpAll(x - 1, y - 1, x + 1, y + 1, false))
1650  {
1651  if (actorManager->pickUpNearest(x, y, 90))
1652  status = true;
1653  }
1654  else
1655  {
1656  status = true;
1657  }
1658  break;
1659 
1660  default:
1661  break;
1662  }
1663  return status;
1664 }
1665 
1666 
1667 void LocalPlayer::moveByDirection(const unsigned char dir)
1668 {
1669  int dx = 0;
1670  int 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:93
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:1308
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: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:236
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:134
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:218
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: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: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:193
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:364
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:1295
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:133
bool pickUpItems(int pickUpType)
Logger * logger
Definition: logger.cpp:88
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: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:1388
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:98
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:615
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:96
void stopAttack(const bool keepAttack)
unsigned int pvpAttackType
Definition: settings.h:140
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: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: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:86
void addRoad(const Path &road)
Definition: map.h:71
bool updateSit() const
virtual void scheduleDelete()
Definition: window.cpp:830
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:184
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:159
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:244
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:83
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: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: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: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: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: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: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