GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/progs/manaplus/gui/viewport.cpp Lines: 1 530 0.2 %
Date: 2018-06-18 21:15:20 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-2018  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "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, 255));
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, 0.0F);
321
    const int mousePosX = mMouseX + mPixelViewX;
322
    const int mousePosY = mMouseY + mPixelViewY;
323
    Vector mouseDestination(mousePosX, mousePosY, 0.0F);
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
                        false, 0) ||
472
                        inputManager.isActionActive(InputAction::ATTACK))
473
                    {
474
                        validateSpeed();
475
                        if (!mStatsReUpdated && localPlayer != mHoverBeing)
476
                        {
477
                            localPlayer->attack(mHoverBeing,
478
                                !inputManager.isActionActive(
479
                                InputAction::STOP_ATTACK),
480
                                false);
481
                            return true;
482
                        }
483
                    }
484
                    else if (!inputManager.isActionActive(
485
                             InputAction::ATTACK))
486
                    {
487
                        validateSpeed();
488
                        if (!mStatsReUpdated && localPlayer != mHoverBeing)
489
                        {
490
                            localPlayer->setGotoTarget(mHoverBeing);
491
                            return true;
492
                        }
493
                    }
494
                }
495
                break;
496
            case ActorType::FloorItem:
497
            case ActorType::Portal:
498
            case ActorType::Pet:
499
            case ActorType::Mercenary:
500
            case ActorType::Homunculus:
501
            case ActorType::Elemental:
502
                break;
503
            case ActorType::Unknown:
504
            case ActorType::Avatar:
505
            default:
506
                reportAlways("Left click on unknown actor type: %d",
507
                    CAST_S32(type));
508
                break;
509
        }
510
    }
511
    // Picks up a item if we clicked on one
512
    if (mHoverItem != nullptr)
513
    {
514
        validateSpeed();
515
        localPlayer->pickUp(mHoverItem);
516
    }
517
    else if (stopAttack)
518
    {
519
        if (mMap != nullptr)
520
        {
521
            const int mouseTileX = (mMouseX + mPixelViewX)
522
                / mMap->getTileWidth();
523
            const int mouseTileY = (mMouseY + mPixelViewY)
524
                / mMap->getTileHeight();
525
            inputManager.executeChatCommand(InputAction::PET_MOVE,
526
                strprintf("%d %d", mouseTileX, mouseTileY),
527
                nullptr);
528
        }
529
        return true;
530
    }
531
    // Just walk around
532
    else if (!inputManager.isActionActive(InputAction::ATTACK) &&
533
             localPlayer->canMove())
534
    {
535
        validateSpeed();
536
        localPlayer->stopAttack(false);
537
        localPlayer->cancelFollow();
538
        mPlayerFollowMouse = mAllowMoveByMouse;
539
        if (mPlayerFollowMouse)
540
        {
541
            // Make the player go to the mouse position
542
            followMouse();
543
        }
544
    }
545
    return false;
546
}
547
548
void Viewport::mousePressed(MouseEvent &event)
549
{
550
    if (event.getSource() != this || event.isConsumed())
551
        return;
552
553
    // Check if we are alive and kickin'
554
    if ((mMap == nullptr) || (localPlayer == nullptr))
555
        return;
556
557
    // Check if we are busy
558
    // if commented, allow context menu if npc dialog open
559
    if (PlayerInfo::isTalking())
560
    {
561
        mMouseClicked = false;
562
        return;
563
    }
564
565
    mMouseClicked = true;
566
567
    mMousePressX = event.getX();
568
    mMousePressY = event.getY();
569
    const MouseButtonT eventButton = event.getButton();
570
    const int pixelX = mMousePressX + mPixelViewX;
571
    const int pixelY = mMousePressY + mPixelViewY;
572
573
    // Right click might open a popup
574
    if (eventButton == MouseButton::RIGHT)
575
    {
576
        if (openContextMenu(event))
577
            return;
578
    }
579
580
    // If a popup is active, just remove it
581
    if (PopupManager::isPopupMenuVisible())
582
    {
583
        mPlayerFollowMouse = false;
584
        PopupManager::hidePopupMenu();
585
        return;
586
    }
587
588
    // Left click can cause different actions
589
    if (!mLongMouseClick && eventButton == MouseButton::LEFT)
590
    {
591
        if (leftMouseAction())
592
        {
593
            mPlayerFollowMouse = false;
594
            return;
595
        }
596
    }
597
    else if (eventButton == MouseButton::MIDDLE)
598
    {
599
        mPlayerFollowMouse = false;
600
        validateSpeed();
601
        // Find the being nearest to the clicked position
602
        if (actorManager != nullptr)
603
        {
604
            Being *const target = actorManager->findNearestLivingBeing(
605
                pixelX, pixelY, 20, ActorType::Monster, nullptr);
606
607
            if (target != nullptr)
608
                localPlayer->setTarget(target);
609
        }
610
    }
611
}
612
613
void Viewport::getMouseTile(int &destX, int &destY) const
614
{
615
    getMouseTile(mMouseX, mMouseY, destX, destY);
616
}
617
618
void Viewport::getMouseTile(const int x, const int y,
619
                            int &destX, int &destY) const
620
{
621
    if (mMap == nullptr)
622
        return;
623
    const int tw = mMap->getTileWidth();
624
    const int th = mMap->getTileHeight();
625
    destX = CAST_S32(x + mPixelViewX)
626
        / static_cast<float>(tw);
627
628
    if (mMap->isHeightsPresent())
629
    {
630
        const int th2 = th / 2;
631
        const int clickY = y + mPixelViewY - th2;
632
        destY = y + mPixelViewY;
633
        int newDiffY = 1000000;
634
        const int heightTiles = mainGraphics->mHeight / th;
635
        const int tileViewY = mPixelViewY / th;
636
        for (int f = tileViewY; f < tileViewY + heightTiles; f ++)
637
        {
638
            if (!mMap->getWalk(destX,
639
                f,
640
                BlockMask::WALL |
641
                BlockMask::AIR |
642
                BlockMask::WATER |
643
                BlockMask::PLAYERWALL))
644
            {
645
                continue;
646
            }
647
648
            const int offset = mMap->getHeightOffset(
649
                destX, f) * th2;
650
            const int pixelF = f * th;
651
            const int diff = abs(clickY + offset - pixelF);
652
            if (diff < newDiffY)
653
            {
654
                destY = pixelF;
655
                newDiffY = diff;
656
            }
657
        }
658
        destY /= 32;
659
    }
660
    else
661
    {
662
        destY = CAST_S32((y + mPixelViewY) / static_cast<float>(th));
663
    }
664
}
665
666
void Viewport::walkByMouse(const MouseEvent &event)
667
{
668
    if ((mMap == nullptr) || (localPlayer == nullptr))
669
        return;
670
    if (mPlayerFollowMouse
671
        && !inputManager.isActionActive(InputAction::STOP_ATTACK)
672
        && !inputManager.isActionActive(InputAction::UNTARGET))
673
    {
674
        if (!mMouseDirectionMove)
675
            mPlayerFollowMouse = false;
676
        if (mLocalWalkTime != localPlayer->getActionTime())
677
        {
678
            mLocalWalkTime = cur_time;
679
            localPlayer->unSetPickUpTarget();
680
            int playerX = localPlayer->getTileX();
681
            int playerY = localPlayer->getTileY();
682
            if (mMouseDirectionMove)
683
            {
684
                const int width = mainGraphics->mWidth / 2;
685
                const int height = mainGraphics->mHeight / 2;
686
                const float wh = static_cast<float>(width)
687
                    / static_cast<float>(height);
688
                int x = event.getX() - width;
689
                int y = event.getY() - height;
690
                if ((x == 0) && (y == 0))
691
                    return;
692
                const int x2 = abs(x);
693
                const int y2 = abs(y);
694
                const float diff = 2;
695
                int dx = 0;
696
                int dy = 0;
697
                if (x2 > y2)
698
                {
699
                    if (y2 != 0 &&
700
                        static_cast<float>(x2) / static_cast<float>(y2) /
701
                        wh > diff)
702
                    {
703
                        y = 0;
704
                    }
705
                }
706
                else
707
                {
708
                    if ((x2 != 0) && y2 * wh / x2 > diff)
709
                        x = 0;
710
                }
711
                if (x > 0)
712
                    dx = 1;
713
                else if (x < 0)
714
                    dx = -1;
715
                if (y > 0)
716
                    dy = 1;
717
                else if (y < 0)
718
                    dy = -1;
719
720
                if (mMap->getWalk(playerX + dx,
721
                    playerY + dy,
722
                    BlockMask::WALL |
723
                    BlockMask::AIR |
724
                    BlockMask::WATER |
725
                    BlockMask::PLAYERWALL))
726
                {
727
                    localPlayer->navigateTo(playerX + dx, playerY + dy);
728
                }
729
                else
730
                {
731
                    if ((dx != 0) && (dy != 0))
732
                    {
733
                        // try avoid diagonal collision
734
                        if (x2 > y2)
735
                        {
736
                            if (mMap->getWalk(playerX + dx,
737
                                playerY,
738
                                BlockMask::WALL |
739
                                BlockMask::AIR |
740
                                BlockMask::WATER |
741
                                BlockMask::PLAYERWALL))
742
                            {
743
                                dy = 0;
744
                            }
745
                            else
746
                            {
747
                                dx = 0;
748
                            }
749
                        }
750
                        else
751
                        {
752
                            if (mMap->getWalk(playerX,
753
                                playerY + dy,
754
                                BlockMask::WALL |
755
                                BlockMask::AIR |
756
                                BlockMask::WATER |
757
                                BlockMask::PLAYERWALL))
758
                            {
759
                                dx = 0;
760
                            }
761
                            else
762
                            {
763
                                dy = 0;
764
                            }
765
                        }
766
                    }
767
                    else
768
                    {
769
                        // try avoid vertical or horisontal collision
770
                        if (dx == 0)
771
                        {
772
                            if (mMap->getWalk(playerX + 1,
773
                                playerY + dy,
774
                                BlockMask::WALL |
775
                                BlockMask::AIR |
776
                                BlockMask::WATER |
777
                                BlockMask::PLAYERWALL))
778
                            {
779
                                dx = 1;
780
                            }
781
                            if (mMap->getWalk(playerX - 1,
782
                                playerY + dy,
783
                                BlockMask::WALL |
784
                                BlockMask::AIR |
785
                                BlockMask::WATER |
786
                                BlockMask::PLAYERWALL))
787
                            {
788
                                dx = -1;
789
                            }
790
                        }
791
                        if (dy == 0)
792
                        {
793
                            if (mMap->getWalk(playerX + dx,
794
                                playerY + 1,
795
                                BlockMask::WALL |
796
                                BlockMask::AIR |
797
                                BlockMask::WATER |
798
                                BlockMask::PLAYERWALL))
799
                            {
800
                                dy = 1;
801
                            }
802
                            if (mMap->getWalk(playerX + dx,
803
                                playerY - 1,
804
                                BlockMask::WALL |
805
                                BlockMask::AIR |
806
                                BlockMask::WATER |
807
                                BlockMask::PLAYERWALL))
808
                            {
809
                                dy = -1;
810
                            }
811
                        }
812
                    }
813
                    localPlayer->navigateTo(playerX + dx, playerY + dy);
814
                }
815
            }
816
            else
817
            {
818
                int destX;
819
                int destY;
820
                getMouseTile(event.getX(), event.getY(),
821
                    destX, destY);
822
                if (playerX != destX || playerY != destY)
823
                {
824
                    if (!localPlayer->navigateTo(destX, destY))
825
                    {
826
                        if (playerX > destX)
827
                            playerX --;
828
                        else if (playerX < destX)
829
                            playerX ++;
830
                        if (playerY > destY)
831
                            playerY --;
832
                        else if (playerY < destY)
833
                            playerY ++;
834
                        if (mMap->getWalk(playerX, playerY, 0))
835
                            localPlayer->navigateTo(playerX, playerY);
836
                    }
837
                }
838
            }
839
        }
840
    }
841
}
842
843
void Viewport::mouseDragged(MouseEvent &event)
844
{
845
    if (event.getSource() != this || event.isConsumed())
846
    {
847
        mPlayerFollowMouse = false;
848
        return;
849
    }
850
    if (mAllowMoveByMouse &&
851
        mMouseClicked &&
852
        (localPlayer != nullptr) &&
853
        localPlayer->canMove())
854
    {
855
        if (abs(event.getX() - mMousePressX) > 32
856
            || abs(event.getY() - mMousePressY) > 32)
857
        {
858
            mPlayerFollowMouse = true;
859
        }
860
861
        walkByMouse(event);
862
    }
863
}
864
865
void Viewport::mouseReleased(MouseEvent &event)
866
{
867
    mPlayerFollowMouse = false;
868
    mLocalWalkTime = -1;
869
    if (mLongMouseClick && mMouseClicked)
870
    {
871
        mMouseClicked = false;
872
        if (event.getSource() != this || event.isConsumed())
873
            return;
874
        const MouseButtonT eventButton = event.getButton();
875
        if (eventButton == MouseButton::LEFT)
876
        {
877
            // long button press
878
            if ((gui != nullptr) && gui->isLongPress())
879
            {
880
                if (openContextMenu(event))
881
                {
882
                    gui->resetClickCount();
883
                    return;
884
                }
885
            }
886
            else
887
            {
888
                if (leftMouseAction())
889
                    return;
890
            }
891
            walkByMouse(event);
892
        }
893
    }
894
}
895
896
void Viewport::optionChanged(const std::string &name)
897
{
898
    if (name == "ScrollLaziness")
899
        mScrollLaziness = config.getIntValue("ScrollLaziness");
900
    else if (name == "ScrollRadius")
901
        mScrollRadius = config.getIntValue("ScrollRadius");
902
    else if (name == "showBeingPopup")
903
        mShowBeingPopup = config.getBoolValue("showBeingPopup");
904
    else if (name == "selfMouseHeal")
905
        mSelfMouseHeal = config.getBoolValue("selfMouseHeal");
906
    else if (name == "enableLazyScrolling")
907
        mEnableLazyScrolling = config.getBoolValue("enableLazyScrolling");
908
    else if (name == "mouseDirectionMove")
909
        mMouseDirectionMove = config.getBoolValue("mouseDirectionMove");
910
    else if (name == "longmouseclick")
911
        mLongMouseClick = config.getBoolValue("longmouseclick");
912
    else if (name == "allowMoveByMouse")
913
        mAllowMoveByMouse = config.getBoolValue("allowMoveByMouse");
914
}
915
916
void Viewport::mouseMoved(MouseEvent &event)
917
{
918
    // Check if we are on the map
919
    if (mMap == nullptr ||
920
        localPlayer == nullptr ||
921
        actorManager == nullptr)
922
    {
923
        return;
924
    }
925
926
    if (mMouseDirectionMove)
927
        mPlayerFollowMouse = false;
928
929
    const int x = mMouseX + mPixelViewX;
930
    const int y = mMouseY + mPixelViewY;
931
932
    ActorTypeT type = ActorType::Unknown;
933
    mHoverBeing = actorManager->findBeingByPixel(x, y, AllPlayers_true);
934
    if (mHoverBeing != nullptr)
935
        type = mHoverBeing->getType();
936
    if ((mHoverBeing != nullptr)
937
        && (type == ActorType::Player
938
        || type == ActorType::Npc
939
        || type == ActorType::Homunculus
940
        || type == ActorType::Mercenary
941
        || type == ActorType::Pet))
942
    {
943
        PopupManager::hideTextPopup();
944
        if (mShowBeingPopup && (beingPopup != nullptr))
945
            beingPopup->show(mMouseX, mMouseY, mHoverBeing);
946
    }
947
    else
948
    {
949
        PopupManager::hideBeingPopup();
950
    }
951
952
    mHoverItem = actorManager->findItem(x / mMap->getTileWidth(),
953
        y / mMap->getTileHeight());
954
955
    if ((mHoverBeing == nullptr) && (mHoverItem == nullptr))
956
    {
957
        const SpecialLayer *const specialLayer = mMap->getSpecialLayer();
958
        if (specialLayer != nullptr)
959
        {
960
            const int mouseTileX = (mMouseX + mPixelViewX)
961
                / mMap->getTileWidth();
962
            const int mouseTileY = (mMouseY + mPixelViewY)
963
                / mMap->getTileHeight();
964
965
            mHoverSign = specialLayer->getTile(mouseTileX, mouseTileY);
966
            if (mHoverSign != nullptr &&
967
                mHoverSign->getType() != MapItemType::EMPTY)
968
            {
969
                if (!mHoverSign->getComment().empty())
970
                {
971
                    PopupManager::hideBeingPopup();
972
                    if (textPopup != nullptr)
973
                    {
974
                        textPopup->show(mMouseX, mMouseY,
975
                            mHoverSign->getComment());
976
                    }
977
                }
978
                else
979
                {
980
                    if (PopupManager::isTextPopupVisible())
981
                        PopupManager::hideTextPopup();
982
                }
983
                gui->setCursorType(Cursor::CURSOR_UP);
984
                return;
985
            }
986
        }
987
    }
988
    if (!event.isConsumed() &&
989
        PopupManager::isTextPopupVisible())
990
    {
991
        PopupManager::hideTextPopup();
992
    }
993
994
    if (mHoverBeing != nullptr)
995
    {
996
        switch (type)
997
        {
998
            case ActorType::Npc:
999
            case ActorType::Monster:
1000
            case ActorType::Portal:
1001
            case ActorType::Pet:
1002
            case ActorType::Mercenary:
1003
            case ActorType::Homunculus:
1004
            case ActorType::SkillUnit:
1005
            case ActorType::Elemental:
1006
                gui->setCursorType(mHoverBeing->getHoverCursor());
1007
                break;
1008
1009
            case ActorType::Avatar:
1010
            case ActorType::FloorItem:
1011
            case ActorType::Unknown:
1012
            case ActorType::Player:
1013
            default:
1014
                gui->setCursorType(Cursor::CURSOR_POINTER);
1015
                break;
1016
        }
1017
    }
1018
    // Item mouseover
1019
    else if (mHoverItem != nullptr)
1020
    {
1021
        gui->setCursorType(mHoverItem->getHoverCursor());
1022
    }
1023
    else
1024
    {
1025
        gui->setCursorType(Cursor::CURSOR_POINTER);
1026
    }
1027
}
1028
1029
void Viewport::toggleMapDrawType()
1030
{
1031
    settings.mapDrawType = static_cast<MapTypeT>(
1032
        CAST_S32(settings.mapDrawType) + 1);
1033
    if (settings.mapDrawType > MapType::BLACKWHITE)
1034
        settings.mapDrawType = MapType::NORMAL;
1035
    if (mMap != nullptr)
1036
        mMap->setDrawLayersFlags(settings.mapDrawType);
1037
}
1038
1039
void Viewport::toggleCameraMode()
1040
{
1041
    settings.cameraMode ++;
1042
    if (settings.cameraMode > 1)
1043
        settings.cameraMode = 0;
1044
    if (settings.cameraMode == 0u)
1045
    {
1046
        mCameraRelativeX = 0;
1047
        mCameraRelativeY = 0;
1048
        updateMidVars();
1049
    }
1050
    UpdateStatusListener::distributeEvent();
1051
}
1052
1053
void Viewport::clearHover(const ActorSprite *const actor)
1054
{
1055
    if (mHoverBeing == actor)
1056
        mHoverBeing = nullptr;
1057
1058
    if (mHoverItem == actor)
1059
        mHoverItem = nullptr;
1060
}
1061
1062
void Viewport::cleanHoverItems()
1063
{
1064
    mHoverBeing = nullptr;
1065
    mHoverItem = nullptr;
1066
    mHoverSign = nullptr;
1067
}
1068
1069
void Viewport::moveCamera(const int dx, const int dy)
1070
{
1071
    mCameraRelativeX += dx;
1072
    mCameraRelativeY += dy;
1073
    updateMidVars();
1074
}
1075
1076
void Viewport::moveCameraToActor(const BeingId actorId,
1077
                                 const int x, const int y)
1078
{
1079
    if ((localPlayer == nullptr) || (actorManager == nullptr))
1080
        return;
1081
1082
    const Actor *const actor = actorManager->findBeing(actorId);
1083
    if (actor == nullptr)
1084
        return;
1085
    settings.cameraMode = 1;
1086
    mCameraRelativeX = actor->mPixelX - localPlayer->mPixelX + x;
1087
    mCameraRelativeY = actor->mPixelY - localPlayer->mPixelY + y;
1088
    updateMidVars();
1089
}
1090
1091
void Viewport::moveCameraToPosition(const int x, const int y)
1092
{
1093
    if (localPlayer == nullptr)
1094
        return;
1095
1096
    settings.cameraMode = 1;
1097
    mCameraRelativeX = x - localPlayer->mPixelX;
1098
    mCameraRelativeY = y - localPlayer->mPixelY;
1099
    updateMidVars();
1100
}
1101
1102
void Viewport::moveCameraRelative(const int x, const int y)
1103
{
1104
    settings.cameraMode = 1;
1105
    mCameraRelativeX += x;
1106
    mCameraRelativeY += y;
1107
    updateMidVars();
1108
}
1109
1110
void Viewport::returnCamera()
1111
{
1112
    settings.cameraMode = 0;
1113
    mCameraRelativeX = 0;
1114
    mCameraRelativeY = 0;
1115
    updateMidVars();
1116
}
1117
1118
void Viewport::validateSpeed()
1119
{
1120
    if (!inputManager.isActionActive(InputAction::TARGET_ATTACK) &&
1121
        !inputManager.isActionActive(InputAction::ATTACK))
1122
    {
1123
        if (Game::instance() != nullptr)
1124
            Game::instance()->setValidSpeed();
1125
    }
1126
}
1127
1128
void Viewport::updateMidVars()
1129
{
1130
    mMidTileX = (mainGraphics->mWidth + mScrollCenterOffsetX) / 2
1131
        - mCameraRelativeX;
1132
    mMidTileY = (mainGraphics->mHeight + mScrollCenterOffsetY) / 2
1133
        - mCameraRelativeY;
1134
}
1135
1136
void Viewport::updateMaxVars()
1137
{
1138
    if (mMap == nullptr)
1139
        return;
1140
    mViewXmax = mMap->getWidth() * mMap->getTileWidth()
1141
        - mainGraphics->mWidth;
1142
    mViewYmax = mMap->getHeight() * mMap->getTileHeight()
1143
        - mainGraphics->mHeight;
1144
}
1145
1146
void Viewport::videoResized()
1147
{
1148
    updateMidVars();
1149
    updateMaxVars();
1150
4
}