GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/minimap.cpp Lines: 35 267 13.1 %
Date: 2017-11-29 Branches: 22 264 8.3 %

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


22
    mAutoResize(config.getBoolValue("autoresizeminimaps"))
74
{
75
10
    setWindowName("Minimap");
76

4
    mShow = config.getValueBool(getWindowName() + "Show", true);
77
78

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

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

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