GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/minimap.cpp Lines: 35 267 13.1 %
Date: 2018-06-18 21:15:20 Branches: 22 262 8.4 %

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 "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
                std::string());
186
187
            if (minimapName.empty() && VirtFs::exists(tempname))
188
                minimapName = tempname;
189
190
            if (minimapName.empty())
191
            {
192
                tempname = pathJoin("graphics/minimaps",
193
                    map->getFilename()).append(".png");
194
                if (VirtFs::exists(tempname))
195
                    minimapName = STD_MOVE(tempname);
196
            }
197
198
            if (!minimapName.empty())
199
                mMapImage = Loader::getImage(minimapName);
200
            else
201
                mMapImage = nullptr;
202
            mCustomMapImage = false;
203
        }
204
    }
205
206
    if ((mMapImage != nullptr) && (map != nullptr))
207
    {
208
        const int width = mMapImage->mBounds.w + 2 * getPadding();
209
        const int height = mMapImage->mBounds.h
210
            + getTitleBarHeight() + getPadding();
211
        const int mapWidth = mMapImage->mBounds.w < 100 ? width : 100;
212
        const int mapHeight = mMapImage->mBounds.h < 100 ? height : 100;
213
        const int minWidth = mapWidth > 310 ? 310 : mapWidth;
214
        const int minHeight = mapHeight > 220 ? 220 : mapHeight;
215
216
        setMinWidth(minWidth);
217
        setMinHeight(minHeight);
218
219
        mWidthProportion = static_cast<float>(
220
                mMapImage->mBounds.w) / static_cast<float>(map->getWidth());
221
        mHeightProportion = static_cast<float>(
222
                mMapImage->mBounds.h) / static_cast<float>(map->getHeight());
223
224
        setMaxWidth(width);
225
        setMaxHeight(height);
226
        if (mAutoResize)
227
        {
228
            setWidth(width);
229
            setHeight(height);
230
        }
231
232
        const Rect &rect = mDimension;
233
        setDefaultSize(rect.x, rect.y, rect.width, rect.height);
234
        resetToDefaultSize();
235
236
        if (mShow)
237
            setVisible(Visible_true);
238
    }
239
    else
240
    {
241
        if (!isSticky())
242
            setVisible(Visible_false);
243
    }
244
    BLOCK_END("Minimap::setMap")
245
}
246
247
void Minimap::toggle()
248
{
249
    setVisible(fromBool(!isWindowVisible(), Visible), isSticky());
250
    mShow = isWindowVisible();
251
}
252
253
2
void Minimap::draw(Graphics *const graphics)
254
{
255
    BLOCK_START("Minimap::draw")
256
257
2
    Window::draw(graphics);
258
2
    draw2(graphics);
259
2
}
260
261
void Minimap::safeDraw(Graphics *const graphics)
262
{
263
    BLOCK_START("Minimap::draw")
264
265
    Window::safeDraw(graphics);
266
    draw2(graphics);
267
}
268
269
2
void Minimap::draw2(Graphics *const graphics)
270
{
271

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