GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/progs/manaplus/gui/viewport.cpp Lines: 1 529 0.2 %
Date: 2017-11-29 Branches: 0 538 0.0 %

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-2017  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "progs/manaplus/gui/viewport.h"
24
25
#include "actormanager.h"
26
#include "configuration.h"
27
#include "game.h"
28
#include "settings.h"
29
#include "sdlshared.h"
30
#include "textmanager.h"
31
32
#include "being/flooritem.h"
33
#include "being/localplayer.h"
34
#include "being/playerinfo.h"
35
36
#include "enums/resources/map/blockmask.h"
37
#include "enums/resources/map/mapitemtype.h"
38
39
#include "gui/gui.h"
40
#include "gui/popupmanager.h"
41
#include "gui/userpalette.h"
42
43
#include "gui/fonts/font.h"
44
45
#include "gui/popups/beingpopup.h"
46
#include "gui/popups/popupmenu.h"
47
#include "gui/popups/textpopup.h"
48
49
#include "gui/windows/ministatuswindow.h"
50
51
#include "input/inputmanager.h"
52
53
#include "utils/checkutils.h"
54
#include "utils/foreach.h"
55
56
#include "resources/map/map.h"
57
#include "resources/map/mapitem.h"
58
#include "resources/map/speciallayer.h"
59
60
#include "debug.h"
61
62
Viewport *viewport = nullptr;
63
64
extern volatile int tick_time;
65
66
Viewport::Viewport() :
67
    WindowContainer(nullptr),
68
    MouseListener(),
69
    ConfigListener(),
70
    mMouseX(0),
71
    mMouseY(0),
72
    mMap(nullptr),
73
    mHoverBeing(nullptr),
74
    mHoverItem(nullptr),
75
    mHoverSign(nullptr),
76
    mScrollRadius(config.getIntValue("ScrollRadius")),
77
    mScrollLaziness(config.getIntValue("ScrollLaziness")),
78
    mScrollCenterOffsetX(config.getIntValue("ScrollCenterOffsetX")),
79
    mScrollCenterOffsetY(config.getIntValue("ScrollCenterOffsetY")),
80
    mMousePressX(0),
81
    mMousePressY(0),
82
    mPixelViewX(0),
83
    mPixelViewY(0),
84
    mMidTileX(0),
85
    mMidTileY(0),
86
    mViewXmax(0),
87
    mViewYmax(0),
88
    mLocalWalkTime(-1),
89
    mCameraRelativeX(0),
90
    mCameraRelativeY(0),
91
    mShowBeingPopup(config.getBoolValue("showBeingPopup")),
92
    mSelfMouseHeal(config.getBoolValue("selfMouseHeal")),
93
    mEnableLazyScrolling(config.getBoolValue("enableLazyScrolling")),
94
    mMouseDirectionMove(config.getBoolValue("mouseDirectionMove")),
95
    mLongMouseClick(config.getBoolValue("longmouseclick")),
96
    mAllowMoveByMouse(config.getBoolValue("allowMoveByMouse")),
97
    mMouseClicked(false),
98
    mPlayerFollowMouse(false)
99
{
100
    setOpaque(Opaque_false);
101
    addMouseListener(this);
102
103
    config.addListener("ScrollLaziness", this);
104
    config.addListener("ScrollRadius", this);
105
    config.addListener("showBeingPopup", this);
106
    config.addListener("selfMouseHeal", this);
107
    config.addListener("enableLazyScrolling", this);
108
    config.addListener("mouseDirectionMove", this);
109
    config.addListener("longmouseclick", this);
110
    config.addListener("allowMoveByMouse", this);
111
112
    setFocusable(true);
113
    updateMidVars();
114
}
115
116
Viewport::~Viewport()
117
{
118
    config.removeListeners(this);
119
    CHECKLISTENERS
120
}
121
122
void Viewport::setMap(Map *const map)
123
{
124
    if ((mMap != nullptr) && (map != nullptr))
125
        map->setDrawLayersFlags(mMap->getDrawLayersFlags());
126
    mMap = map;
127
    updateMaxVars();
128
}
129
130
void Viewport::draw(Graphics *const graphics)
131
{
132
    BLOCK_START("Viewport::draw 1")
133
    static int lastTick = tick_time;
134
135
    if ((mMap == nullptr) || (localPlayer == nullptr))
136
    {
137
        graphics->setColor(Color(64, 64, 64));
138
        graphics->fillRectangle(
139
                Rect(0, 0, getWidth(), getHeight()));
140
        BLOCK_END("Viewport::draw 1")
141
        return;
142
    }
143
144
    // Avoid freaking out when tick_time overflows
145
    if (tick_time < lastTick)
146
        lastTick = tick_time;
147
148
    // Calculate viewpoint
149
150
    const int player_x = localPlayer->mPixelX - mMidTileX;
151
    const int player_y = localPlayer->mPixelY - mMidTileY;
152
153
    if (mScrollLaziness < 1)
154
        mScrollLaziness = 1;  // Avoids division by zero
155
156
    if (mEnableLazyScrolling)
157
    {
158
        int cnt = 0;
159
160
        // Apply lazy scrolling
161
        while (lastTick < tick_time && cnt < mapTileSize)
162
        {
163
            if (player_x > mPixelViewX + mScrollRadius)
164
            {
165
                mPixelViewX += CAST_S32(
166
                    static_cast<float>(player_x
167
                    - mPixelViewX - mScrollRadius) /
168
                    static_cast<float>(mScrollLaziness));
169
            }
170
            if (player_x < mPixelViewX - mScrollRadius)
171
            {
172
                mPixelViewX += CAST_S32(
173
                    static_cast<float>(player_x
174
                    - mPixelViewX + mScrollRadius) /
175
                    static_cast<float>(mScrollLaziness));
176
            }
177
            if (player_y > mPixelViewY + mScrollRadius)
178
            {
179
                mPixelViewY += CAST_S32(
180
                    static_cast<float>(player_y
181
                    - mPixelViewY - mScrollRadius) /
182
                    static_cast<float>(mScrollLaziness));
183
            }
184
            if (player_y < mPixelViewY - mScrollRadius)
185
            {
186
                mPixelViewY += CAST_S32(
187
                    static_cast<float>(player_y
188
                    - mPixelViewY + mScrollRadius) /
189
                    static_cast<float>(mScrollLaziness));
190
            }
191
            lastTick ++;
192
            cnt ++;
193
        }
194
195
        // Auto center when player is off screen
196
        if (cnt > 30 || player_x - mPixelViewX
197
            > graphics->mWidth / 2 || mPixelViewX
198
            - player_x > graphics->mWidth / 2 || mPixelViewY
199
            - player_y > graphics->getHeight() / 2 ||  player_y
200
            - mPixelViewY > graphics->getHeight() / 2)
201
        {
202
            if (player_x <= 0 || player_y <= 0)
203
            {
204
                logger->log("incorrect player position: %d, %d, %d, %d",
205
                    player_x, player_y, mPixelViewX, mPixelViewY);
206
                logger->log("tile position: %d, %d",
207
                    localPlayer->getTileX(), localPlayer->getTileY());
208
            }
209
            mPixelViewX = player_x;
210
            mPixelViewY = player_y;
211
        }
212
    }
213
    else
214
    {
215
        mPixelViewX = player_x;
216
        mPixelViewY = player_y;
217
    }
218
219
    if (mPixelViewX < 0)
220
        mPixelViewX = 0;
221
    if (mPixelViewY < 0)
222
        mPixelViewY = 0;
223
    if (mPixelViewX > mViewXmax)
224
        mPixelViewX = mViewXmax;
225
    if (mPixelViewY > mViewYmax)
226
        mPixelViewY = mViewYmax;
227
228
    // Draw tiles and sprites
229
    mMap->draw(graphics, mPixelViewX, mPixelViewY);
230
231
    const MapTypeT drawType = settings.mapDrawType;
232
    if (drawType != MapType::NORMAL)
233
    {
234
        if (drawType != MapType::SPECIAL4)
235
        {
236
            mMap->drawCollision(graphics, mPixelViewX,
237
                mPixelViewY, drawType);
238
        }
239
        if (drawType == MapType::DEBUGTYPE)
240
            drawDebugPath(graphics);
241
    }
242
243
    if (localPlayer->getCheckNameSetting())
244
    {
245
        localPlayer->setCheckNameSetting(false);
246
        localPlayer->setName(localPlayer->getName());
247
    }
248
249
    // Draw text
250
    if (textManager != nullptr)
251
        textManager->draw(graphics, mPixelViewX, mPixelViewY);
252
253
    // Draw player names, speech, and emotion sprite as needed
254
    const ActorSprites &actors = actorManager->getAll();
255
    FOR_EACH (ActorSpritesIterator, it, actors)
256
    {
257
        if ((*it)->getType() == ActorType::FloorItem)
258
            continue;
259
        Being *const b = static_cast<Being*>(*it);
260
        b->drawEmotion(graphics, mPixelViewX, mPixelViewY);
261
        b->drawSpeech(mPixelViewX, mPixelViewY);
262
    }
263
264
    if (miniStatusWindow != nullptr)
265
        miniStatusWindow->drawIcons(graphics);
266
267
    // Draw contained widgets
268
    WindowContainer::draw(graphics);
269
    BLOCK_END("Viewport::draw 1")
270
}
271
272
void Viewport::safeDraw(Graphics *const graphics)
273
{
274
    Viewport::draw(graphics);
275
}
276
277
void Viewport::logic()
278
{
279
    BLOCK_START("Viewport::logic")
280
    // Make the player follow the mouse position
281
    // if the mouse is dragged elsewhere than in a window.
282
    Gui::getMouseState(mMouseX, mMouseY);
283
    BLOCK_END("Viewport::logic")
284
}
285
286
void Viewport::followMouse()
287
{
288
    if (gui == nullptr)
289
        return;
290
    const MouseStateType button = Gui::getMouseState(mMouseX, mMouseY);
291
    // If the left button is dragged
292
    if (mPlayerFollowMouse && ((button & SDL_BUTTON(1)) != 0))
293
    {
294
        // We create a mouse event and send it to mouseDragged.
295
        const MouseEvent event(nullptr,
296
            MouseEventType::DRAGGED,
297
            MouseButton::LEFT,
298
            mMouseX,
299
            mMouseY,
300
            0);
301
302
        walkByMouse(event);
303
    }
304
}
305
306
void Viewport::drawDebugPath(Graphics *const graphics)
307
{
308
    if (localPlayer == nullptr ||
309
        userPalette == nullptr ||
310
        actorManager == nullptr ||
311
        mMap == nullptr ||
312
        gui == nullptr)
313
    {
314
        return;
315
    }
316
317
    Gui::getMouseState(mMouseX, mMouseY);
318
319
    static Path debugPath;
320
    static Vector lastMouseDestination = Vector(0.0F, 0.0F);
321
    const int mousePosX = mMouseX + mPixelViewX;
322
    const int mousePosY = mMouseY + mPixelViewY;
323
    Vector mouseDestination(mousePosX, mousePosY);
324
325
    if (mouseDestination.x != lastMouseDestination.x
326
        || mouseDestination.y != lastMouseDestination.y)
327
    {
328
        debugPath = mMap->findPath(
329
            CAST_S32(localPlayer->mPixelX - mapTileSize / 2) / mapTileSize,
330
            CAST_S32(localPlayer->mPixelY - mapTileSize) / mapTileSize,
331
            mousePosX / mapTileSize,
332
            mousePosY / mapTileSize,
333
            localPlayer->getBlockWalkMask(),
334
            500);
335
        lastMouseDestination = mouseDestination;
336
    }
337
    drawPath(graphics, debugPath, userPalette->getColorWithAlpha(
338
        UserColorId::ROAD_POINT));
339
340
    const ActorSprites &actors = actorManager->getAll();
341
    FOR_EACH (ActorSpritesConstIterator, it, actors)
342
    {
343
        const Being *const being = dynamic_cast<const Being*>(*it);
344
        if ((being != nullptr) && being != localPlayer)
345
        {
346
            const Path &beingPath = being->getPath();
347
            drawPath(graphics, beingPath, userPalette->getColorWithAlpha(
348
                UserColorId::ROAD_POINT));
349
        }
350
    }
351
}
352
353
void Viewport::drawPath(Graphics *const graphics,
354
                        const Path &path,
355
                        const Color &color) const
356
{
357
    graphics->setColor(color);
358
    Font *const font = getFont();
359
360
    int cnt = 1;
361
    FOR_EACH (Path::const_iterator, i, path)
362
    {
363
        const int squareX = i->x * mapTileSize - mPixelViewX + 12;
364
        const int squareY = i->y * mapTileSize - mPixelViewY + 12;
365
366
        graphics->fillRectangle(Rect(squareX, squareY, 8, 8));
367
        if (mMap != nullptr)
368
        {
369
            const std::string str = toString(cnt);
370
            font->drawString(graphics,
371
                color, color,
372
                str,
373
                squareX + 4 - font->getWidth(str) / 2,
374
                squareY + 12);
375
        }
376
        cnt ++;
377
    }
378
}
379
380
bool Viewport::openContextMenu(const MouseEvent &event)
381
{
382
    mPlayerFollowMouse = false;
383
    const int eventX = event.getX();
384
    const int eventY = event.getY();
385
    if (popupMenu == nullptr)
386
        return false;
387
    if (mHoverBeing != nullptr)
388
    {
389
        validateSpeed();
390
        if (actorManager != nullptr)
391
        {
392
            STD_VECTOR<ActorSprite*> beings;
393
            const int x = mMouseX + mPixelViewX;
394
            const int y = mMouseY + mPixelViewY;
395
            actorManager->findBeingsByPixel(beings, x, y, AllPlayers_true);
396
            if (beings.size() > 1)
397
                popupMenu->showPopup(eventX, eventY, beings);
398
            else
399
                popupMenu->showPopup(eventX, eventY, mHoverBeing);
400
            return true;
401
        }
402
    }
403
    else if (mHoverItem != nullptr)
404
    {
405
        validateSpeed();
406
        popupMenu->showPopup(eventX, eventY, mHoverItem);
407
        return true;
408
    }
409
    else if (mHoverSign != nullptr)
410
    {
411
        validateSpeed();
412
        popupMenu->showPopup(eventX, eventY, mHoverSign);
413
        return true;
414
    }
415
    else if (settings.cameraMode != 0u)
416
    {
417
        if (mMap == nullptr)
418
            return false;
419
        popupMenu->showMapPopup(eventX, eventY,
420
            (mMouseX + mPixelViewX) / mMap->getTileWidth(),
421
            (mMouseY + mPixelViewY) / mMap->getTileHeight(),
422
            false);
423
        return true;
424
    }
425
    return false;
426
}
427
428
bool Viewport::leftMouseAction()
429
{
430
    const bool stopAttack = inputManager.isActionActive(
431
        InputAction::STOP_ATTACK);
432
    // Interact with some being
433
    if (mHoverBeing != nullptr)
434
    {
435
        if (!mHoverBeing->isAlive())
436
            return true;
437
438
        if (mHoverBeing->canTalk())
439
        {
440
            validateSpeed();
441
            mHoverBeing->talkTo();
442
            return true;
443
        }
444
445
        const ActorTypeT type = mHoverBeing->getType();
446
        switch (type)
447
        {
448
            case ActorType::Player:
449
                validateSpeed();
450
                if (actorManager != nullptr)
451
                {
452
#ifdef TMWA_SUPPORT
453
                    if (localPlayer != mHoverBeing || mSelfMouseHeal)
454
                        actorManager->heal(mHoverBeing);
455
#endif  // TMWA_SUPPORT
456
457
                    if (localPlayer == mHoverBeing &&
458
                        mHoverItem != nullptr)
459
                    {
460
                        localPlayer->pickUp(mHoverItem);
461
                    }
462
                    return true;
463
                }
464
                break;
465
            case ActorType::Monster:
466
            case ActorType::Npc:
467
            case ActorType::SkillUnit:
468
                if (!stopAttack)
469
                {
470
                    if (localPlayer->withinAttackRange(mHoverBeing) ||
471
                        inputManager.isActionActive(InputAction::ATTACK))
472
                    {
473
                        validateSpeed();
474
                        if (!mStatsReUpdated && localPlayer != mHoverBeing)
475
                        {
476
                            localPlayer->attack(mHoverBeing,
477
                                !inputManager.isActionActive(
478
                                InputAction::STOP_ATTACK));
479
                            return true;
480
                        }
481
                    }
482
                    else if (!inputManager.isActionActive(
483
                             InputAction::ATTACK))
484
                    {
485
                        validateSpeed();
486
                        if (!mStatsReUpdated && localPlayer != mHoverBeing)
487
                        {
488
                            localPlayer->setGotoTarget(mHoverBeing);
489
                            return true;
490
                        }
491
                    }
492
                }
493
                break;
494
            case ActorType::FloorItem:
495
            case ActorType::Portal:
496
            case ActorType::Pet:
497
            case ActorType::Mercenary:
498
            case ActorType::Homunculus:
499
            case ActorType::Elemental:
500
                break;
501
            case ActorType::Unknown:
502
            case ActorType::Avatar:
503
            default:
504
                reportAlways("Left click on unknown actor type: %d",
505
                    CAST_S32(type));
506
                break;
507
        }
508
    }
509
    // Picks up a item if we clicked on one
510
    if (mHoverItem != nullptr)
511
    {
512
        validateSpeed();
513
        localPlayer->pickUp(mHoverItem);
514
    }
515
    else if (stopAttack)
516
    {
517
        if (mMap != nullptr)
518
        {
519
            const int mouseTileX = (mMouseX + mPixelViewX)
520
                / mMap->getTileWidth();
521
            const int mouseTileY = (mMouseY + mPixelViewY)
522
                / mMap->getTileHeight();
523
            inputManager.executeChatCommand(InputAction::PET_MOVE,
524
                strprintf("%d %d", mouseTileX, mouseTileY),
525
                nullptr);
526
        }
527
        return true;
528
    }
529
    // Just walk around
530
    else if (!inputManager.isActionActive(InputAction::ATTACK) &&
531
             localPlayer->canMove())
532
    {
533
        validateSpeed();
534
        localPlayer->stopAttack();
535
        localPlayer->cancelFollow();
536
        mPlayerFollowMouse = mAllowMoveByMouse;
537
        if (mPlayerFollowMouse)
538
        {
539
            // Make the player go to the mouse position
540
            followMouse();
541
        }
542
    }
543
    return false;
544
}
545
546
void Viewport::mousePressed(MouseEvent &event)
547
{
548
    if (event.getSource() != this || event.isConsumed())
549
        return;
550
551
    // Check if we are alive and kickin'
552
    if ((mMap == nullptr) || (localPlayer == nullptr))
553
        return;
554
555
    // Check if we are busy
556
    // if commented, allow context menu if npc dialog open
557
    if (PlayerInfo::isTalking())
558
    {
559
        mMouseClicked = false;
560
        return;
561
    }
562
563
    mMouseClicked = true;
564
565
    mMousePressX = event.getX();
566
    mMousePressY = event.getY();
567
    const MouseButtonT eventButton = event.getButton();
568
    const int pixelX = mMousePressX + mPixelViewX;
569
    const int pixelY = mMousePressY + mPixelViewY;
570
571
    // Right click might open a popup
572
    if (eventButton == MouseButton::RIGHT)
573
    {
574
        if (openContextMenu(event))
575
            return;
576
    }
577
578
    // If a popup is active, just remove it
579
    if (PopupManager::isPopupMenuVisible())
580
    {
581
        mPlayerFollowMouse = false;
582
        PopupManager::hidePopupMenu();
583
        return;
584
    }
585
586
    // Left click can cause different actions
587
    if (!mLongMouseClick && eventButton == MouseButton::LEFT)
588
    {
589
        if (leftMouseAction())
590
        {
591
            mPlayerFollowMouse = false;
592
            return;
593
        }
594
    }
595
    else if (eventButton == MouseButton::MIDDLE)
596
    {
597
        mPlayerFollowMouse = false;
598
        validateSpeed();
599
        // Find the being nearest to the clicked position
600
        if (actorManager != nullptr)
601
        {
602
            Being *const target = actorManager->findNearestLivingBeing(
603
                pixelX, pixelY, 20, ActorType::Monster, nullptr);
604
605
            if (target != nullptr)
606
                localPlayer->setTarget(target);
607
        }
608
    }
609
}
610
611
void Viewport::getMouseTile(int &destX, int &destY) const
612
{
613
    getMouseTile(mMouseX, mMouseY, destX, destY);
614
}
615
616
void Viewport::getMouseTile(const int x, const int y,
617
                            int &destX, int &destY) const
618
{
619
    if (mMap == nullptr)
620
        return;
621
    const int tw = mMap->getTileWidth();
622
    const int th = mMap->getTileHeight();
623
    destX = CAST_S32(x + mPixelViewX)
624
        / static_cast<float>(tw);
625
626
    if (mMap->isHeightsPresent())
627
    {
628
        const int th2 = th / 2;
629
        const int clickY = y + mPixelViewY - th2;
630
        destY = y + mPixelViewY;
631
        int newDiffY = 1000000;
632
        const int heightTiles = mainGraphics->mHeight / th;
633
        const int tileViewY = mPixelViewY / th;
634
        for (int f = tileViewY; f < tileViewY + heightTiles; f ++)
635
        {
636
            if (!mMap->getWalk(destX,
637
                f,
638
                BlockMask::WALL |
639
                BlockMask::AIR |
640
                BlockMask::WATER |
641
                BlockMask::PLAYERWALL))
642
            {
643
                continue;
644
            }
645
646
            const int offset = mMap->getHeightOffset(
647
                destX, f) * th2;
648
            const int pixelF = f * th;
649
            const int diff = abs(clickY + offset - pixelF);
650
            if (diff < newDiffY)
651
            {
652
                destY = pixelF;
653
                newDiffY = diff;
654
            }
655
        }
656
        destY /= 32;
657
    }
658
    else
659
    {
660
        destY = CAST_S32((y + mPixelViewY) / static_cast<float>(th));
661
    }
662
}
663
664
void Viewport::walkByMouse(const MouseEvent &event)
665
{
666
    if ((mMap == nullptr) || (localPlayer == nullptr))
667
        return;
668
    if (mPlayerFollowMouse
669
        && !inputManager.isActionActive(InputAction::STOP_ATTACK)
670
        && !inputManager.isActionActive(InputAction::UNTARGET))
671
    {
672
        if (!mMouseDirectionMove)
673
            mPlayerFollowMouse = false;
674
        if (mLocalWalkTime != localPlayer->getActionTime())
675
        {
676
            mLocalWalkTime = cur_time;
677
            localPlayer->unSetPickUpTarget();
678
            int playerX = localPlayer->getTileX();
679
            int playerY = localPlayer->getTileY();
680
            if (mMouseDirectionMove)
681
            {
682
                const int width = mainGraphics->mWidth / 2;
683
                const int height = mainGraphics->mHeight / 2;
684
                const float wh = static_cast<float>(width)
685
                    / static_cast<float>(height);
686
                int x = event.getX() - width;
687
                int y = event.getY() - height;
688
                if ((x == 0) && (y == 0))
689
                    return;
690
                const int x2 = abs(x);
691
                const int y2 = abs(y);
692
                const float diff = 2;
693
                int dx = 0;
694
                int dy = 0;
695
                if (x2 > y2)
696
                {
697
                    if (y2 != 0 &&
698
                        static_cast<float>(x2) / static_cast<float>(y2) /
699
                        wh > diff)
700
                    {
701
                        y = 0;
702
                    }
703
                }
704
                else
705
                {
706
                    if ((x2 != 0) && y2 * wh / x2 > diff)
707
                        x = 0;
708
                }
709
                if (x > 0)
710
                    dx = 1;
711
                else if (x < 0)
712
                    dx = -1;
713
                if (y > 0)
714
                    dy = 1;
715
                else if (y < 0)
716
                    dy = -1;
717
718
                if (mMap->getWalk(playerX + dx,
719
                    playerY + dy,
720
                    BlockMask::WALL |
721
                    BlockMask::AIR |
722
                    BlockMask::WATER |
723
                    BlockMask::PLAYERWALL))
724
                {
725
                    localPlayer->navigateTo(playerX + dx, playerY + dy);
726
                }
727
                else
728
                {
729
                    if ((dx != 0) && (dy != 0))
730
                    {
731
                        // try avoid diagonal collision
732
                        if (x2 > y2)
733
                        {
734
                            if (mMap->getWalk(playerX + dx,
735
                                playerY,
736
                                BlockMask::WALL |
737
                                BlockMask::AIR |
738
                                BlockMask::WATER |
739
                                BlockMask::PLAYERWALL))
740
                            {
741
                                dy = 0;
742
                            }
743
                            else
744
                            {
745
                                dx = 0;
746
                            }
747
                        }
748
                        else
749
                        {
750
                            if (mMap->getWalk(playerX,
751
                                playerY + dy,
752
                                BlockMask::WALL |
753
                                BlockMask::AIR |
754
                                BlockMask::WATER |
755
                                BlockMask::PLAYERWALL))
756
                            {
757
                                dx = 0;
758
                            }
759
                            else
760
                            {
761
                                dy = 0;
762
                            }
763
                        }
764
                    }
765
                    else
766
                    {
767
                        // try avoid vertical or horisontal collision
768
                        if (dx == 0)
769
                        {
770
                            if (mMap->getWalk(playerX + 1,
771
                                playerY + dy,
772
                                BlockMask::WALL |
773
                                BlockMask::AIR |
774
                                BlockMask::WATER |
775
                                BlockMask::PLAYERWALL))
776
                            {
777
                                dx = 1;
778
                            }
779
                            if (mMap->getWalk(playerX - 1,
780
                                playerY + dy,
781
                                BlockMask::WALL |
782
                                BlockMask::AIR |
783
                                BlockMask::WATER |
784
                                BlockMask::PLAYERWALL))
785
                            {
786
                                dx = -1;
787
                            }
788
                        }
789
                        if (dy == 0)
790
                        {
791
                            if (mMap->getWalk(playerX + dx,
792
                                playerY + 1,
793
                                BlockMask::WALL |
794
                                BlockMask::AIR |
795
                                BlockMask::WATER |
796
                                BlockMask::PLAYERWALL))
797
                            {
798
                                dy = 1;
799
                            }
800
                            if (mMap->getWalk(playerX + dx,
801
                                playerY - 1,
802
                                BlockMask::WALL |
803
                                BlockMask::AIR |
804
                                BlockMask::WATER |
805
                                BlockMask::PLAYERWALL))
806
                            {
807
                                dy = -1;
808
                            }
809
                        }
810
                    }
811
                    localPlayer->navigateTo(playerX + dx, playerY + dy);
812
                }
813
            }
814
            else
815
            {
816
                int destX;
817
                int destY;
818
                getMouseTile(event.getX(), event.getY(),
819
                    destX, destY);
820
                if (playerX != destX || playerY != destY)
821
                {
822
                    if (!localPlayer->navigateTo(destX, destY))
823
                    {
824
                        if (playerX > destX)
825
                            playerX --;
826
                        else if (playerX < destX)
827
                            playerX ++;
828
                        if (playerY > destY)
829
                            playerY --;
830
                        else if (playerY < destY)
831
                            playerY ++;
832
                        if (mMap->getWalk(playerX, playerY, 0))
833
                            localPlayer->navigateTo(playerX, playerY);
834
                    }
835
                }
836
            }
837
        }
838
    }
839
}
840
841
void Viewport::mouseDragged(MouseEvent &event)
842
{
843
    if (event.getSource() != this || event.isConsumed())
844
    {
845
        mPlayerFollowMouse = false;
846
        return;
847
    }
848
    if (mAllowMoveByMouse &&
849
        mMouseClicked &&
850
        (localPlayer != nullptr) &&
851
        localPlayer->canMove())
852
    {
853
        if (abs(event.getX() - mMousePressX) > 32
854
            || abs(event.getY() - mMousePressY) > 32)
855
        {
856
            mPlayerFollowMouse = true;
857
        }
858
859
        walkByMouse(event);
860
    }
861
}
862
863
void Viewport::mouseReleased(MouseEvent &event)
864
{
865
    mPlayerFollowMouse = false;
866
    mLocalWalkTime = -1;
867
    if (mLongMouseClick && mMouseClicked)
868
    {
869
        mMouseClicked = false;
870
        if (event.getSource() != this || event.isConsumed())
871
            return;
872
        const MouseButtonT eventButton = event.getButton();
873
        if (eventButton == MouseButton::LEFT)
874
        {
875
            // long button press
876
            if ((gui != nullptr) && gui->isLongPress())
877
            {
878
                if (openContextMenu(event))
879
                {
880
                    gui->resetClickCount();
881
                    return;
882
                }
883
            }
884
            else
885
            {
886
                if (leftMouseAction())
887
                    return;
888
            }
889
            walkByMouse(event);
890
        }
891
    }
892
}
893
894
void Viewport::optionChanged(const std::string &name)
895
{
896
    if (name == "ScrollLaziness")
897
        mScrollLaziness = config.getIntValue("ScrollLaziness");
898
    else if (name == "ScrollRadius")
899
        mScrollRadius = config.getIntValue("ScrollRadius");
900
    else if (name == "showBeingPopup")
901
        mShowBeingPopup = config.getBoolValue("showBeingPopup");
902
    else if (name == "selfMouseHeal")
903
        mSelfMouseHeal = config.getBoolValue("selfMouseHeal");
904
    else if (name == "enableLazyScrolling")
905
        mEnableLazyScrolling = config.getBoolValue("enableLazyScrolling");
906
    else if (name == "mouseDirectionMove")
907
        mMouseDirectionMove = config.getBoolValue("mouseDirectionMove");
908
    else if (name == "longmouseclick")
909
        mLongMouseClick = config.getBoolValue("longmouseclick");
910
    else if (name == "allowMoveByMouse")
911
        mAllowMoveByMouse = config.getBoolValue("allowMoveByMouse");
912
}
913
914
void Viewport::mouseMoved(MouseEvent &event)
915
{
916
    // Check if we are on the map
917
    if (mMap == nullptr ||
918
        localPlayer == nullptr ||
919
        actorManager == nullptr)
920
    {
921
        return;
922
    }
923
924
    if (mMouseDirectionMove)
925
        mPlayerFollowMouse = false;
926
927
    const int x = mMouseX + mPixelViewX;
928
    const int y = mMouseY + mPixelViewY;
929
930
    ActorTypeT type = ActorType::Unknown;
931
    mHoverBeing = actorManager->findBeingByPixel(x, y, AllPlayers_true);
932
    if (mHoverBeing != nullptr)
933
        type = mHoverBeing->getType();
934
    if ((mHoverBeing != nullptr)
935
        && (type == ActorType::Player
936
        || type == ActorType::Npc
937
        || type == ActorType::Homunculus
938
        || type == ActorType::Mercenary
939
        || type == ActorType::Pet))
940
    {
941
        PopupManager::hideTextPopup();
942
        if (mShowBeingPopup && (beingPopup != nullptr))
943
            beingPopup->show(mMouseX, mMouseY, mHoverBeing);
944
    }
945
    else
946
    {
947
        PopupManager::hideBeingPopup();
948
    }
949
950
    mHoverItem = actorManager->findItem(x / mMap->getTileWidth(),
951
        y / mMap->getTileHeight());
952
953
    if ((mHoverBeing == nullptr) && (mHoverItem == nullptr))
954
    {
955
        const SpecialLayer *const specialLayer = mMap->getSpecialLayer();
956
        if (specialLayer != nullptr)
957
        {
958
            const int mouseTileX = (mMouseX + mPixelViewX)
959
                / mMap->getTileWidth();
960
            const int mouseTileY = (mMouseY + mPixelViewY)
961
                / mMap->getTileHeight();
962
963
            mHoverSign = specialLayer->getTile(mouseTileX, mouseTileY);
964
            if (mHoverSign != nullptr &&
965
                mHoverSign->getType() != MapItemType::EMPTY)
966
            {
967
                if (!mHoverSign->getComment().empty())
968
                {
969
                    PopupManager::hideBeingPopup();
970
                    if (textPopup != nullptr)
971
                    {
972
                        textPopup->show(mMouseX, mMouseY,
973
                            mHoverSign->getComment());
974
                    }
975
                }
976
                else
977
                {
978
                    if (PopupManager::isTextPopupVisible())
979
                        PopupManager::hideTextPopup();
980
                }
981
                gui->setCursorType(Cursor::CURSOR_UP);
982
                return;
983
            }
984
        }
985
    }
986
    if (!event.isConsumed() &&
987
        PopupManager::isTextPopupVisible())
988
    {
989
        PopupManager::hideTextPopup();
990
    }
991
992
    if (mHoverBeing != nullptr)
993
    {
994
        switch (type)
995
        {
996
            case ActorType::Npc:
997
            case ActorType::Monster:
998
            case ActorType::Portal:
999
            case ActorType::Pet:
1000
            case ActorType::Mercenary:
1001
            case ActorType::Homunculus:
1002
            case ActorType::SkillUnit:
1003
            case ActorType::Elemental:
1004
                gui->setCursorType(mHoverBeing->getHoverCursor());
1005
                break;
1006
1007
            case ActorType::Avatar:
1008
            case ActorType::FloorItem:
1009
            case ActorType::Unknown:
1010
            case ActorType::Player:
1011
            default:
1012
                gui->setCursorType(Cursor::CURSOR_POINTER);
1013
                break;
1014
        }
1015
    }
1016
    // Item mouseover
1017
    else if (mHoverItem != nullptr)
1018
    {
1019
        gui->setCursorType(mHoverItem->getHoverCursor());
1020
    }
1021
    else
1022
    {
1023
        gui->setCursorType(Cursor::CURSOR_POINTER);
1024
    }
1025
}
1026
1027
void Viewport::toggleMapDrawType()
1028
{
1029
    settings.mapDrawType = static_cast<MapTypeT>(
1030
        CAST_S32(settings.mapDrawType) + 1);
1031
    if (settings.mapDrawType > MapType::BLACKWHITE)
1032
        settings.mapDrawType = MapType::NORMAL;
1033
    if (mMap != nullptr)
1034
        mMap->setDrawLayersFlags(settings.mapDrawType);
1035
}
1036
1037
void Viewport::toggleCameraMode()
1038
{
1039
    settings.cameraMode ++;
1040
    if (settings.cameraMode > 1)
1041
        settings.cameraMode = 0;
1042
    if (settings.cameraMode == 0u)
1043
    {
1044
        mCameraRelativeX = 0;
1045
        mCameraRelativeY = 0;
1046
        updateMidVars();
1047
    }
1048
    UpdateStatusListener::distributeEvent();
1049
}
1050
1051
void Viewport::clearHover(const ActorSprite *const actor)
1052
{
1053
    if (mHoverBeing == actor)
1054
        mHoverBeing = nullptr;
1055
1056
    if (mHoverItem == actor)
1057
        mHoverItem = nullptr;
1058
}
1059
1060
void Viewport::cleanHoverItems()
1061
{
1062
    mHoverBeing = nullptr;
1063
    mHoverItem = nullptr;
1064
    mHoverSign = nullptr;
1065
}
1066
1067
void Viewport::moveCamera(const int dx, const int dy)
1068
{
1069
    mCameraRelativeX += dx;
1070
    mCameraRelativeY += dy;
1071
    updateMidVars();
1072
}
1073
1074
void Viewport::moveCameraToActor(const BeingId actorId,
1075
                                 const int x, const int y)
1076
{
1077
    if ((localPlayer == nullptr) || (actorManager == nullptr))
1078
        return;
1079
1080
    const Actor *const actor = actorManager->findBeing(actorId);
1081
    if (actor == nullptr)
1082
        return;
1083
    settings.cameraMode = 1;
1084
    mCameraRelativeX = actor->mPixelX - localPlayer->mPixelX + x;
1085
    mCameraRelativeY = actor->mPixelY - localPlayer->mPixelY + y;
1086
    updateMidVars();
1087
}
1088
1089
void Viewport::moveCameraToPosition(const int x, const int y)
1090
{
1091
    if (localPlayer == nullptr)
1092
        return;
1093
1094
    settings.cameraMode = 1;
1095
    mCameraRelativeX = x - localPlayer->mPixelX;
1096
    mCameraRelativeY = y - localPlayer->mPixelY;
1097
    updateMidVars();
1098
}
1099
1100
void Viewport::moveCameraRelative(const int x, const int y)
1101
{
1102
    settings.cameraMode = 1;
1103
    mCameraRelativeX += x;
1104
    mCameraRelativeY += y;
1105
    updateMidVars();
1106
}
1107
1108
void Viewport::returnCamera()
1109
{
1110
    settings.cameraMode = 0;
1111
    mCameraRelativeX = 0;
1112
    mCameraRelativeY = 0;
1113
    updateMidVars();
1114
}
1115
1116
void Viewport::validateSpeed()
1117
{
1118
    if (!inputManager.isActionActive(InputAction::TARGET_ATTACK) &&
1119
        !inputManager.isActionActive(InputAction::ATTACK))
1120
    {
1121
        if (Game::instance() != nullptr)
1122
            Game::instance()->setValidSpeed();
1123
    }
1124
}
1125
1126
void Viewport::updateMidVars()
1127
{
1128
    mMidTileX = (mainGraphics->mWidth + mScrollCenterOffsetX) / 2
1129
        - mCameraRelativeX;
1130
    mMidTileY = (mainGraphics->mHeight + mScrollCenterOffsetY) / 2
1131
        - mCameraRelativeY;
1132
}
1133
1134
void Viewport::updateMaxVars()
1135
{
1136
    if (mMap == nullptr)
1137
        return;
1138
    mViewXmax = mMap->getWidth() * mMap->getTileWidth()
1139
        - mainGraphics->mWidth;
1140
    mViewYmax = mMap->getHeight() * mMap->getTileHeight()
1141
        - mainGraphics->mHeight;
1142
}
1143
1144
void Viewport::videoResized()
1145
{
1146
    updateMidVars();
1147
    updateMaxVars();
1148
4
}