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