GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/minimap.cpp Lines: 35 255 13.7 %
Date: 2021-03-17 Branches: 21 268 7.8 %

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 "gui/windows/minimap.h"
25
26
#include "actormanager.h"
27
#include "configuration.h"
28
#include "party.h"
29
#include "settings.h"
30
31
#include "being/localplayer.h"
32
33
#include "enums/resources/map/blockmask.h"
34
35
#include "fs/virtfs/fs.h"
36
37
#include "gui/popupmanager.h"
38
#include "gui/viewport.h"
39
#include "gui/userpalette.h"
40
41
#include "gui/popups/popupmenu.h"
42
#include "gui/popups/textpopup.h"
43
44
#include "gui/windows/setupwindow.h"
45
46
#include "resources/imagehelper.h"
47
48
#include "resources/image/image.h"
49
50
#include "resources/map/map.h"
51
#include "resources/map/metatile.h"
52
53
#include "resources/loaders/imageloader.h"
54
55
#include "utils/gettext.h"
56
#include "utils/foreach.h"
57
#include "utils/sdlcheckutils.h"
58
#include "utils/stdmove.h"
59
60
#include "debug.h"
61
62
Minimap *minimap = nullptr;
63
bool Minimap::mShow = true;
64
65
1
Minimap::Minimap() :
66
    // TRANSLATORS: mini map window name
67
1
    Window(_("Map"), Modal_false, nullptr, "map.xml"),
68
    mWidthProportion(0.5),
69
    mHeightProportion(0.5),
70
    mMapImage(nullptr),
71
    mMapOriginX(0),
72
    mMapOriginY(0),
73
    mCustomMapImage(false),
74


11
    mAutoResize(config.getBoolValue("autoresizeminimaps"))
75
{
76
5
    setWindowName("Minimap");
77

2
    mShow = config.getValueBool(getWindowName() + "Show", true);
78
79

4
    config.addListener("autoresizeminimaps", this);
80
81
1
    setDefaultSize(5, 25, 100, 100);
82
    // set this to false as the minimap window size is changed
83
    // depending on the map size
84
1
    setResizable(true);
85
1
    if (setupWindow != nullptr)
86
        setupWindow->registerWindowForReset(this);
87
88
2
    setDefaultVisible(true);
89
2
    setSaveVisible(true);
90
91
1
    setStickyButton(true);
92
1
    setSticky(false);
93
94
1
    loadWindowState();
95

2
    setVisible(fromBool(mShow, Visible), isSticky());
96
2
    enableVisibleSound(true);
97
1
}
98
99
4
Minimap::~Minimap()
100
{
101
4
    config.setValue(getWindowName() + "Show", mShow);
102
1
    config.removeListeners(this);
103
    CHECKLISTENERS
104
1
    deleteMapImage();
105
2
}
106
107
1
void Minimap::deleteMapImage()
108
{
109
1
    if (mMapImage != nullptr)
110
    {
111
        if (mCustomMapImage)
112
            delete mMapImage;
113
        else
114
            mMapImage->decRef();
115
        mMapImage = nullptr;
116
    }
117
1
}
118
119
void Minimap::setMap(const Map *const map)
120
{
121
    BLOCK_START("Minimap::setMap")
122
    std::string caption;
123
124
    if (map != nullptr)
125
        caption = map->getName();
126
127
    if (caption.empty())
128
    {
129
        // TRANSLATORS: mini map window name
130
        caption = _("Map");
131
    }
132
133
    setCaption(caption);
134
    deleteMapImage();
135
136
    if (map != nullptr)
137
    {
138
        if (config.getBoolValue("showExtMinimaps"))
139
        {
140
            SDL_Surface *const surface = MSDL_CreateRGBSurface(SDL_SWSURFACE,
141
                map->getWidth(), map->getHeight(), 32,
142
                0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
143
            if (surface == nullptr)
144
            {
145
                if (!isSticky())
146
                    setVisible(Visible_false);
147
                BLOCK_END("Minimap::setMap")
148
                return;
149
            }
150
151
            // I'm not sure if the locks are necessary since it's a SWSURFACE
152
            SDL_LockSurface(surface);
153
            int* data = static_cast<int*>(surface->pixels);
154
            if (data == nullptr)
155
            {
156
                if (!isSticky())
157
                    setVisible(Visible_false);
158
                BLOCK_END("Minimap::setMap")
159
                return;
160
            }
161
            const int size = surface->h * surface->w;
162
            const int mask = (BlockMask::WALL |
163
                BlockMask::AIR |
164
                BlockMask::WATER |
165
                BlockMask::PLAYERWALL);
166
167
            for (int ptr = 0; ptr < size; ptr ++)
168
            {
169
                *(data ++) = (map->mMetaTiles[ptr].blockmask & mask) != 0 ?
170
                    0x0 : 0x00ffffff;
171
            }
172
173
            SDL_UnlockSurface(surface);
174
175
            mMapImage = imageHelper->loadSurface(surface);
176
            mMapImage->setAlpha(settings.guiAlpha);
177
            mCustomMapImage = true;
178
            MSDL_FreeSurface(surface);
179
        }
180
        else
181
        {
182
            std::string tempname = pathJoin(paths.getStringValue("minimaps"),
183
                map->getFilename()).append(".png");
184
185
            std::string minimapName = map->getProperty("minimap",
186
                std::string());
187
188
            if (minimapName.empty() && VirtFs::exists(tempname))
189
                minimapName = tempname;
190
191
            if (minimapName.empty())
192
            {
193
                tempname = pathJoin("graphics/minimaps",
194
                    map->getFilename()).append(".png");
195
                if (VirtFs::exists(tempname))
196
                    minimapName = STD_MOVE(tempname);
197
            }
198
199
            if (!minimapName.empty())
200
                mMapImage = Loader::getImage(minimapName);
201
            else
202
                mMapImage = nullptr;
203
            mCustomMapImage = false;
204
        }
205
    }
206
207
    if ((mMapImage != nullptr) && (map != nullptr))
208
    {
209
        const int width = mMapImage->mBounds.w + 2 * getPadding();
210
        const int height = mMapImage->mBounds.h
211
            + getTitleBarHeight() + getPadding();
212
        const int mapWidth = mMapImage->mBounds.w < 100 ? width : 100;
213
        const int mapHeight = mMapImage->mBounds.h < 100 ? height : 100;
214
        const int minWidth = mapWidth > 310 ? 310 : mapWidth;
215
        const int minHeight = mapHeight > 220 ? 220 : mapHeight;
216
217
        setMinWidth(minWidth);
218
        setMinHeight(minHeight);
219
220
        mWidthProportion = static_cast<float>(
221
                mMapImage->mBounds.w) / static_cast<float>(map->getWidth());
222
        mHeightProportion = static_cast<float>(
223
                mMapImage->mBounds.h) / static_cast<float>(map->getHeight());
224
225
        setMaxWidth(width);
226
        setMaxHeight(height);
227
        if (mAutoResize)
228
        {
229
            setWidth(width);
230
            setHeight(height);
231
        }
232
233
        const Rect &rect = mDimension;
234
        setDefaultSize(rect.x, rect.y, rect.width, rect.height);
235
        resetToDefaultSize();
236
237
        if (mShow)
238
            setVisible(Visible_true);
239
    }
240
    else
241
    {
242
        if (!isSticky())
243
            setVisible(Visible_false);
244
    }
245
    BLOCK_END("Minimap::setMap")
246
}
247
248
void Minimap::toggle()
249
{
250
    setVisible(fromBool(!isWindowVisible(), Visible), isSticky());
251
    mShow = isWindowVisible();
252
}
253
254
1
void Minimap::draw(Graphics *const graphics)
255
{
256
    BLOCK_START("Minimap::draw")
257
258
1
    Window::draw(graphics);
259
1
    draw2(graphics);
260
1
}
261
262
void Minimap::safeDraw(Graphics *const graphics)
263
{
264
    BLOCK_START("Minimap::draw")
265
266
    Window::safeDraw(graphics);
267
    draw2(graphics);
268
}
269
270
1
void Minimap::draw2(Graphics *const graphics)
271
{
272

2
    if (userPalette == nullptr ||
273
2
        localPlayer == nullptr ||
274
1
        viewport == nullptr)
275
    {
276
        BLOCK_END("Minimap::draw")
277
1
        return;
278
    }
279
280
    const Rect a = getChildrenArea();
281
282
    graphics->pushClipArea(a);
283
284
    if (actorManager == nullptr)
285
    {
286
        BLOCK_END("Minimap::draw")
287
        return;
288
    }
289
290
    mMapOriginX = 0;
291
    mMapOriginY = 0;
292
293
    if (mMapImage != nullptr)
294
    {
295
        const SDL_Rect &rect = mMapImage->mBounds;
296
        const int w = rect.w;
297
        const int h = rect.h;
298
        if (w > a.width || h > a.height)
299
        {
300
            mMapOriginX = (a.width / 2) - (localPlayer->mPixelX +
301
                viewport->getCameraRelativeX() * mWidthProportion) / 32;
302
303
            mMapOriginY = (a.height / 2) - (localPlayer->mPixelY +
304
                viewport->getCameraRelativeY() * mHeightProportion) / 32;
305
306
            const int minOriginX = a.width - w;
307
            const int minOriginY = a.height - h;
308
309
            if (mMapOriginX < minOriginX)
310
                mMapOriginX = minOriginX;
311
            if (mMapOriginY < minOriginY)
312
                mMapOriginY = minOriginY;
313
            if (mMapOriginX > 0)
314
                mMapOriginX = 0;
315
            if (mMapOriginY > 0)
316
                mMapOriginY = 0;
317
        }
318
319
        graphics->drawImage(mMapImage, mMapOriginX, mMapOriginY);
320
    }
321
322
    const ActorSprites &actors = actorManager->getAll();
323
    FOR_EACH (ActorSpritesConstIterator, it, actors)
324
    {
325
        if (((*it) == nullptr) || (*it)->getType() == ActorType::FloorItem)
326
            continue;
327
328
        const Being *const being = static_cast<const Being *>(*it);
329
        if (being == nullptr)
330
            continue;
331
332
        int dotSize = 2;
333
        UserColorIdT type = UserColorId::PC;
334
335
        if (being == localPlayer)
336
        {
337
            type = UserColorId::SELF;
338
            dotSize = 3;
339
        }
340
        else if (being->isGM())
341
        {
342
            type = UserColorId::GM;
343
        }
344
        else if (being->getGuild() == localPlayer->getGuild()
345
                 || being->getGuildName() == localPlayer->getGuildName())
346
        {
347
            type = UserColorId::GUILD;
348
        }
349
        else
350
        {
351
            switch (being->getType())
352
            {
353
                case ActorType::Monster:
354
                    type = UserColorId::MONSTER;
355
                    break;
356
357
                case ActorType::Npc:
358
                    type = UserColorId::NPC;
359
                    break;
360
361
                case ActorType::Portal:
362
                    type = UserColorId::PORTAL_HIGHLIGHT;
363
                    break;
364
365
                case ActorType::Pet:
366
                    type = UserColorId::PET;
367
                    break;
368
                case ActorType::Mercenary:
369
                    type = UserColorId::MERCENARY;
370
                    break;
371
372
                case ActorType::Homunculus:
373
                    type = UserColorId::HOMUNCULUS;
374
                    break;
375
376
                case ActorType::SkillUnit:
377
                    type = UserColorId::SKILLUNIT;
378
                    break;
379
                case ActorType::Avatar:
380
                case ActorType::Unknown:
381
                case ActorType::Player:
382
                case ActorType::FloorItem:
383
                case ActorType::Elemental:
384
                default:
385
                    continue;
386
            }
387
        }
388
389
        if (userPalette != nullptr)
390
            graphics->setColor(userPalette->getColor(type, 255U));
391
392
        const int offsetHeight = CAST_S32(static_cast<float>(
393
                dotSize - 1) * mHeightProportion);
394
        const int offsetWidth = CAST_S32(static_cast<float>(
395
                dotSize - 1) * mWidthProportion);
396
        graphics->fillRectangle(Rect(
397
            (being->mPixelX * mWidthProportion) / 32
398
            + mMapOriginX - offsetWidth,
399
            (being->mPixelY * mHeightProportion) / 32
400
            + mMapOriginY - offsetHeight, dotSize, dotSize));
401
    }
402
403
    if (localPlayer->isInParty())
404
    {
405
        const Party *const party = localPlayer->getParty();
406
        if (party != nullptr)
407
        {
408
            const PartyMember *const m = party->getMember(
409
                localPlayer->getName());
410
            const Party::MemberList *const members = party->getMembers();
411
            if (m != nullptr)
412
            {
413
                const std::string curMap = m->getMap();
414
                Party::MemberList::const_iterator it = members->begin();
415
                const Party::MemberList::const_iterator
416
                    it_end = members->end();
417
                while (it != it_end)
418
                {
419
                    const PartyMember *const member = *it;
420
                    if ((member != nullptr) && member->getMap() == curMap
421
                        && member->getOnline() && member != m)
422
                    {
423
                        if (userPalette != nullptr)
424
                        {
425
                            graphics->setColor(userPalette->getColor(
426
                                UserColorId::PARTY, 255U));
427
                        }
428
429
                        const int offsetHeight = CAST_S32(
430
                            mHeightProportion);
431
                        const int offsetWidth = CAST_S32(
432
                            mWidthProportion);
433
434
                        graphics->fillRectangle(Rect(
435
                            CAST_S32(member->getX()
436
                            * mWidthProportion) + mMapOriginX - offsetWidth,
437
                            CAST_S32(member->getY()
438
                            * mHeightProportion) + mMapOriginY - offsetHeight,
439
                            2, 2));
440
                    }
441
                    ++ it;
442
                }
443
            }
444
        }
445
    }
446
447
    const int gw = graphics->getWidth();
448
    const int gh = graphics->getHeight();
449
    int x = (localPlayer->mPixelX - (gw / 2)
450
        + viewport->getCameraRelativeX())
451
        * mWidthProportion / 32 + mMapOriginX;
452
    int y = (localPlayer->mPixelY - (gh / 2)
453
        + viewport->getCameraRelativeY())
454
        * mHeightProportion / 32 + mMapOriginY;
455
456
    const int w = CAST_S32(static_cast<float>(
457
        gw) * mWidthProportion / 32);
458
    const int h = CAST_S32(static_cast<float>(
459
        gh) * mHeightProportion / 32);
460
461
    if (w <= a.width)
462
    {
463
        if (x < 0 && (w != 0))
464
            x = 0;
465
        if (x + w > a.width)
466
            x = a.width - w;
467
    }
468
    if (h <= a.height)
469
    {
470
        if (y < 0 && (h != 0))
471
            y = 0;
472
        if (y + h > a.height)
473
            y = a.height - h;
474
    }
475
476
    graphics->setColor(userPalette->getColor(UserColorId::PC, 255U));
477
    graphics->drawRectangle(Rect(x, y, w, h));
478
    graphics->popClipArea();
479
    BLOCK_END("Minimap::draw")
480
}
481
482
void Minimap::mousePressed(MouseEvent &event)
483
{
484
    if (event.getButton() == MouseButton::RIGHT)
485
        return;
486
    Window::mousePressed(event);
487
}
488
489
void Minimap::mouseReleased(MouseEvent &event)
490
{
491
    Window::mouseReleased(event);
492
493
    if ((localPlayer == nullptr) || (popupManager == nullptr))
494
        return;
495
496
    if (event.getButton() == MouseButton::LEFT)
497
    {
498
        int x = event.getX();
499
        int y = event.getY();
500
        screenToMap(x, y);
501
502
        localPlayer->navigateTo(x, y);
503
    }
504
    else if (event.getButton() == MouseButton::RIGHT)
505
    {
506
        int x = event.getX();
507
        int y = event.getY();
508
        screenToMap(x, y);
509
        popupMenu->showMapPopup(viewport->mMouseX,
510
            viewport->mMouseY,
511
            x, y,
512
            true);
513
    }
514
}
515
516
void Minimap::mouseMoved(MouseEvent &event)
517
{
518
    Window::mouseMoved(event);
519
    const int x = event.getX();
520
    const int y = event.getY();
521
    const Rect &rect = mDimension;
522
    textPopup->show(x + rect.x, y + rect.y, mCaption);
523
}
524
525
void Minimap::mouseExited(MouseEvent &event)
526
{
527
    Window::mouseExited(event);
528
    textPopup->hide();
529
}
530
531
void Minimap::screenToMap(int &x, int &y)
532
{
533
    const Rect a = getChildrenArea();
534
    x = (x - a.x - mMapOriginX + mWidthProportion) / mWidthProportion;
535
    y = (y - a.y - mMapOriginY + mHeightProportion) / mHeightProportion;
536
}
537
538
void Minimap::optionChanged(const std::string &name)
539
{
540
    if (name == "autoresizeminimaps")
541
        mAutoResize = config.getBoolValue("autoresizeminimaps");
542
2
}