GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/map/map.cpp Lines: 57 767 7.4 %
Date: 2019-08-19 Branches: 53 978 5.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-2019  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 "resources/map/map.h"
24
25
#include "configuration.h"
26
#include "render/graphics.h"
27
#include "notifymanager.h"
28
#include "settings.h"
29
30
#include "being/localplayer.h"
31
32
#include "enums/resources/notifytypes.h"
33
34
#include "enums/resources/map/blockmask.h"
35
#include "enums/resources/map/mapitemtype.h"
36
37
#include "fs/mkdir.h"
38
39
#include "gui/userpalette.h"
40
41
#include "particle/particle.h"
42
43
#include "resources/ambientlayer.h"
44
45
#ifdef USE_OPENGL
46
#include "resources/atlas/atlasresource.h"
47
#endif  // USE_OPENGL
48
49
#include "resources/image/subimage.h"
50
51
#include "resources/loaders/imageloader.h"
52
53
#include "resources/map/location.h"
54
#include "resources/map/mapheights.h"
55
#include "resources/map/mapobjectlist.h"
56
#include "resources/map/maplayer.h"
57
#include "resources/map/mapitem.h"
58
#include "resources/map/objectslayer.h"
59
#include "resources/map/speciallayer.h"
60
#include "resources/map/tileanimation.h"
61
#include "resources/map/tileset.h"
62
#include "resources/map/walklayer.h"
63
64
#ifdef USE_OPENGL
65
#include "render/renderers.h"
66
#endif  // USE_OPENGL
67
68
#include "utils/checkutils.h"
69
#include "utils/delete2.h"
70
#include "utils/dtor.h"
71
#include "utils/foreach.h"
72
#include "utils/timer.h"
73
74
#include <sys/stat.h>
75
76
#include <climits>
77
#include <fstream>
78
#include <queue>
79
80
#include "debug.h"
81
82
class ActorFunctuator final
83
{
84
    public:
85
        A_DEFAULT_COPY(ActorFunctuator)
86
87
        bool operator()(const Actor *const a,
88
                        const Actor *const b) const
89
        {
90
            if ((a == nullptr) || (b == nullptr))
91
                return false;
92
            return a->getSortPixelY() < b->getSortPixelY();
93
        }
94
} actorCompare;
95
96
47
Map::Map(const std::string &name,
97
         const int width,
98
         const int height,
99
         const int tileWidth,
100
47
         const int tileHeight) :
101
    Properties(),
102
    mWidth(width), mHeight(height),
103
    mTileWidth(tileWidth), mTileHeight(tileHeight),
104
    mMaxTileHeight(height),
105

160220
    mMetaTiles(new MetaTile[mWidth * mHeight]),
106
    mWalkLayer(nullptr),
107
    mLayers(),
108
    mDrawUnderLayers(),
109
    mDrawOverLayers(),
110
    mTilesets(),
111
    mActors(),
112
    mHasWarps(false),
113
    mDrawLayersFlags(MapType::NORMAL),
114
    mOnClosedList(1),
115
    mOnOpenList(2),
116
    mBackgrounds(),
117
    mForegrounds(),
118
    mLastAScrollX(0.0F),
119
    mLastAScrollY(0.0F),
120
    mParticleEffects(),
121
    mMapPortals(),
122
    mTileAnimations(),
123
    mName(name),
124

188
    mOverlayDetail(config.getIntValue("OverlayDetail")),
125

188
    mOpacity(config.getFloatValue("guialpha")),
126
#ifdef USE_OPENGL
127

188
    mOpenGL(intToRenderType(config.getIntValue("opengl"))),
128
#else  // USE_OPENGL
129
    mOpenGL(RENDER_SOFTWARE),
130
#endif  // USE_OPENGL
131
    mPvp(0),
132
    mTilesetsIndexed(false),
133
    mIndexedTilesets(nullptr),
134
    mIndexedTilesetsSize(0),
135
    mActorFixX(0),
136
    mActorFixY(0),
137
    mVersion(0),
138

47
    mSpecialLayer(new SpecialLayer("special layer", width, height)),
139

47
    mTempLayer(new SpecialLayer("temp layer", width, height)),
140

47
    mObjects(new ObjectsLayer(width, height)),
141
    mFringeLayer(nullptr),
142
    mLastX(-1),
143
    mLastY(-1),
144
    mLastScrollX(-1),
145
    mLastScrollY(-1),
146
    mDrawX(-1),
147
    mDrawY(-1),
148
    mDrawScrollX(-1),
149
    mDrawScrollY(-1),
150
    mMask(1),
151
#ifdef USE_OPENGL
152
    mAtlas(nullptr),
153
#endif  // USE_OPENGL
154
    mHeights(nullptr),
155
    mRedrawMap(true),
156
    mBeingOpacity(false),
157
#ifdef USE_OPENGL
158
47
    mCachedDraw(mOpenGL == RENDER_NORMAL_OPENGL ||
159
47
        mOpenGL == RENDER_GLES_OPENGL ||
160

94
        mOpenGL == RENDER_GLES2_OPENGL ||
161
        mOpenGL == RENDER_MODERN_OPENGL),
162
#else  // USE_OPENGL
163
    mCachedDraw(false),
164
#endif  // USE_OPENGL
165
    mCustom(false),
166
    mDrawOnlyFringe(false),
167

1363
    mClear(false)
168
{
169

188
    config.addListener("OverlayDetail", this);
170

188
    config.addListener("guialpha", this);
171

188
    config.addListener("beingopacity", this);
172
173
47
    if (mOpacity != 1.0F)
174

188
        mBeingOpacity = config.getBoolValue("beingopacity");
175
    else
176
        mBeingOpacity = false;
177
47
}
178
179
752
Map::~Map()
180
{
181
47
    config.removeListeners(this);
182
    CHECKLISTENERS
183
184
47
    if (mWalkLayer != nullptr)
185
    {
186
        mWalkLayer->decRef();
187
        mWalkLayer = nullptr;
188
    }
189
47
    mFringeLayer = nullptr;
190
94
    delete_all(mLayers);
191
94
    delete_all(mTilesets);
192
94
    delete_all(mForegrounds);
193
94
    delete_all(mBackgrounds);
194
94
    delete_all(mTileAnimations);
195
47
    delete2(mSpecialLayer)
196
47
    delete2(mTempLayer)
197
47
    delete2(mObjects)
198
94
    delete_all(mMapPortals);
199
#ifdef USE_OPENGL
200
47
    if (mAtlas != nullptr)
201
    {
202
        mAtlas->decRef();
203
        mAtlas = nullptr;
204
    }
205
#endif  // USE_OPENGL
206
47
    delete2(mHeights)
207
47
    delete [] mMetaTiles;
208
94
}
209
210
void Map::optionChanged(const std::string &restrict value) restrict2
211
{
212
    if (value == "OverlayDetail")
213
    {
214
        mOverlayDetail = config.getIntValue("OverlayDetail");
215
    }
216
    else if (value == "guialpha")
217
    {
218
        mOpacity = config.getFloatValue("guialpha");
219
        if (mOpacity != 1.0F)
220
            mBeingOpacity = config.getBoolValue("beingopacity");
221
        else
222
            mBeingOpacity = false;
223
    }
224
    else if (value == "beingopacity")
225
    {
226
        if (mOpacity != 1.0F)
227
            mBeingOpacity = config.getBoolValue("beingopacity");
228
        else
229
            mBeingOpacity = false;
230
    }
231
}
232
233
void Map::initializeAmbientLayers() restrict2
234
{
235
    // search for "foreground*" or "overlay*" (old term) in map properties
236
    for (int i = 0; /* terminated by a break */; i++)
237
    {
238
        std::string name;
239
        if (hasProperty(std::string("foreground").append(
240
            toString(i)).append("image")))
241
        {
242
            name = "foreground" + toString(i);
243
        }
244
        else if (hasProperty(std::string("overlay").append(
245
                 toString(i)).append("image")))
246
        {
247
            name = "overlay" + toString(i);
248
        }
249
        else
250
        {
251
            break;  // the FOR loop
252
        }
253
254
        Image *restrict const img = Loader::getImage(
255
            getProperty(name + "image", std::string()));
256
        if (img != nullptr)
257
        {
258
            int mask = atoi(getProperty(name + "mask", std::string()).c_str());
259
            if (mask == 0)
260
                mask = 1;
261
            const float parallax = getFloatProperty(name + "parallax", 0.0F);
262
            mForegrounds.push_back(new AmbientLayer(
263
                name,
264
                img,
265
                getFloatProperty(name + "parallaxX", parallax),
266
                getFloatProperty(name + "parallaxY", parallax),
267
                getFloatProperty(name + "posX", 0.0F),
268
                getFloatProperty(name + "posY", 0.0F),
269
                getFloatProperty(name + "scrollX", 0.0F),
270
                getFloatProperty(name + "scrollY", 0.0F),
271
                getBoolProperty(name + "keepratio", false),
272
                mask));
273
274
            // The AmbientLayer takes control over the image.
275
            img->decRef();
276
        }
277
    }
278
279
    // search for "background*" in map properties
280
    for (int i = 0; hasProperty(std::string("background").append(
281
         toString(i)).append("image")); i ++)
282
    {
283
        const std::string name("background" + toString(i));
284
        Image *restrict const img = Loader::getImage(
285
            getProperty(name + "image", std::string()));
286
287
        if (img != nullptr)
288
        {
289
            int mask = atoi(getProperty(name + "mask", std::string()).c_str());
290
            if (mask == 0)
291
                mask = 1;
292
293
            const float parallax = getFloatProperty(name + "parallax", 0.0F);
294
            mBackgrounds.push_back(new AmbientLayer(
295
                name,
296
                img,
297
                getFloatProperty(name + "parallaxX", parallax),
298
                getFloatProperty(name + "parallaxY", parallax),
299
                getFloatProperty(name + "posX", 0.0F),
300
                getFloatProperty(name + "posY", 0.0F),
301
                getFloatProperty(name + "scrollX", 0.0F),
302
                getFloatProperty(name + "scrollY", 0.0F),
303
                getBoolProperty(name + "keepratio", false),
304
                mask));
305
306
            // The AmbientLayer takes control over the image.
307
            img->decRef();
308
        }
309
    }
310
}
311
312
45
void Map::addLayer(MapLayer *const layer) restrict2
313
{
314
45
    mLayers.push_back(layer);
315

90
    if (layer->isFringeLayer() && (mFringeLayer == nullptr))
316
30
        mFringeLayer = layer;
317
45
}
318
319
void Map::addTileset(Tileset *const tileset) restrict2
320
{
321
    mTilesets.push_back(tileset);
322
    const int height = tileset->getHeight();
323
    if (height > mMaxTileHeight)
324
        mMaxTileHeight = height;
325
}
326
327
void Map::update(const int ticks) restrict2
328
{
329
    // Update animated tiles
330
    FOR_EACH (TileAnimationMapCIter, iAni, mTileAnimations)
331
    {
332
        TileAnimation *restrict const tileAni = iAni->second;
333
        if ((tileAni != nullptr) && tileAni->update(ticks))
334
            mRedrawMap = true;
335
    }
336
}
337
338
void Map::draw(Graphics *restrict const graphics,
339
               int scrollX, int scrollY) restrict2
340
{
341
    if (localPlayer == nullptr)
342
        return;
343
344
    if (mClear)
345
        mainGraphics->clearScreen();
346
347
    BLOCK_START("Map::draw")
348
    // Calculate range of tiles which are on-screen
349
    const int endPixelY = graphics->mHeight + scrollY + mTileHeight - 1
350
        + mMaxTileHeight - mTileHeight;
351
    const int startX = scrollX / mTileWidth - 2;
352
    const int startY = scrollY / mTileHeight;
353
    const int endX = (graphics->mWidth + scrollX + mTileWidth - 1)
354
        / mTileWidth + 1;
355
    const int endY = endPixelY / mTileHeight + 1;
356
357
    // Make sure actors are sorted ascending by Y-coordinate
358
    // so that they overlap correctly
359
    BLOCK_START("Map::draw sort")
360
    mActors.sort(actorCompare);
361
    BLOCK_END("Map::draw sort")
362
363
    // update scrolling of all ambient layers
364
    updateAmbientLayers(static_cast<float>(scrollX),
365
                        static_cast<float>(scrollY));
366
367
    // Draw backgrounds
368
    drawAmbientLayers(graphics,
369
        MapLayerPosition::BACKGROUND_LAYERS,
370
        mOverlayDetail);
371
372
    if (mDrawLayersFlags == MapType::BLACKWHITE && (userPalette != nullptr))
373
    {
374
        graphics->setColor(userPalette->getColorWithAlpha(
375
            UserColorId::WALKABLE_HIGHLIGHT));
376
377
        graphics->fillRectangle(Rect(0, 0,
378
            graphics->mWidth, graphics->mHeight));
379
    }
380
381
#ifdef USE_OPENGL
382
    int updateFlag = 0;
383
384
    if (mCachedDraw)
385
    {
386
        if (mLastX != startX || mLastY != startY || mLastScrollX != scrollX
387
            || mLastScrollY != scrollY)
388
        {   // player moving
389
            mLastX = startX;
390
            mLastY = startY;
391
            mLastScrollX = scrollX;
392
            mLastScrollY = scrollY;
393
            updateFlag = 2;
394
        }
395
        else if (mRedrawMap || startX != mDrawX || startY != mDrawY ||
396
                 scrollX != mDrawScrollX || scrollY != mDrawScrollY)
397
        {   // player mode to new position
398
            mRedrawMap = false;
399
            mDrawX = startX;
400
            mDrawY = startY;
401
            mDrawScrollX = scrollX;
402
            mDrawScrollY = scrollY;
403
            updateFlag = 1;
404
        }
405
    }
406
#endif  // USE_OPENGL
407
408
    if (mDrawOnlyFringe)
409
    {
410
        if (mFringeLayer != nullptr)
411
        {
412
            mFringeLayer->setSpecialLayer(mSpecialLayer);
413
            mFringeLayer->setTempLayer(mTempLayer);
414
            mFringeLayer->drawFringe(graphics,
415
                startX, startY,
416
                endX, endY,
417
                scrollX, scrollY,
418
                mActors);
419
        }
420
    }
421
    else
422
    {
423
#ifdef USE_OPENGL
424
        if (mCachedDraw)
425
        {
426
            if (updateFlag != 0)
427
            {
428
                FOR_EACH (Layers::iterator, it, mDrawUnderLayers)
429
                {
430
                    (*it)->updateOGL(graphics,
431
                        startX, startY,
432
                        endX, endY,
433
                        scrollX, scrollY);
434
                }
435
                FOR_EACH (Layers::iterator, it, mDrawOverLayers)
436
                {
437
                    (*it)->updateOGL(graphics,
438
                        startX, startY,
439
                        endX, endY,
440
                        scrollX, scrollY);
441
                }
442
            }
443
444
            FOR_EACH (Layers::iterator, it, mDrawUnderLayers)
445
                (*it)->drawOGL(graphics);
446
447
            if (mFringeLayer != nullptr)
448
            {
449
                mFringeLayer->setSpecialLayer(mSpecialLayer);
450
                mFringeLayer->setTempLayer(mTempLayer);
451
                mFringeLayer->drawFringe(graphics,
452
                    startX, startY,
453
                    endX, endY,
454
                    scrollX, scrollY,
455
                    mActors);
456
            }
457
458
            FOR_EACH (Layers::iterator, it, mDrawOverLayers)
459
                (*it)->drawOGL(graphics);
460
        }
461
        else
462
#endif  // USE_OPENGL
463
        {
464
            FOR_EACH (Layers::iterator, it, mDrawUnderLayers)
465
            {
466
                (*it)->draw(graphics,
467
                    startX, startY,
468
                    endX, endY,
469
                    scrollX, scrollY);
470
            }
471
472
            if (mFringeLayer != nullptr)
473
            {
474
                mFringeLayer->setSpecialLayer(mSpecialLayer);
475
                mFringeLayer->setTempLayer(mTempLayer);
476
                mFringeLayer->drawFringe(graphics,
477
                    startX, startY,
478
                    endX, endY,
479
                    scrollX, scrollY,
480
                    mActors);
481
            }
482
483
            FOR_EACH (Layers::iterator, it, mDrawOverLayers)
484
            {
485
                (*it)->draw(graphics, startX, startY,
486
                    endX, endY,
487
                    scrollX, scrollY);
488
            }
489
        }
490
    }
491
492
    // Don't draw if gui opacity == 1
493
    if (mBeingOpacity)
494
    {
495
        // Draws beings with a lower opacity to make them visible
496
        // even when covered by a wall or some other elements...
497
        ActorsCIter ai = mActors.begin();
498
        const ActorsCIter ai_end = mActors.end();
499
500
        if (mOpenGL == RENDER_SOFTWARE)
501
        {
502
            while (ai != ai_end)
503
            {
504
                if (Actor *restrict const actor = *ai)
505
                {
506
                    const int x = actor->getTileX();
507
                    const int y = actor->getTileY();
508
                    if (x < startX || x > endX || y < startY || y > endY)
509
                    {
510
                        ++ai;
511
                        continue;
512
                    }
513
                    // For now, just draw actors with only one layer.
514
                    if (actor->getNumberOfLayers() == 1)
515
                    {
516
                        actor->setAlpha(0.3F);
517
                        actor->draw(graphics, -scrollX, -scrollY);
518
                        actor->setAlpha(1.0F);
519
                    }
520
                }
521
                ++ai;
522
            }
523
        }
524
        else
525
        {
526
            while (ai != ai_end)
527
            {
528
                if (Actor *const actor = *ai)
529
                {
530
                    actor->setAlpha(0.3F);
531
                    actor->draw(graphics, -scrollX, -scrollY);
532
                    actor->setAlpha(1.0F);
533
                }
534
                ++ai;
535
            }
536
        }
537
    }
538
539
    drawAmbientLayers(graphics,
540
        MapLayerPosition::FOREGROUND_LAYERS,
541
        mOverlayDetail);
542
    BLOCK_END("Map::draw")
543
}
544
545
#define fillCollision(collision, color) \
546
    if (x < endX && mMetaTiles[tilePtr].blockmask & (collision))\
547
    {\
548
        width = mapTileSize;\
549
        for (int x2 = tilePtr + 1; x < endX; x2 ++)\
550
        {\
551
            if (!(mMetaTiles[x2].blockmask & (collision)))\
552
                break;\
553
            width += mapTileSize;\
554
            x ++;\
555
            tilePtr ++;\
556
        }\
557
        if (width && userPalette)\
558
        {\
559
            graphics->setColor(userPalette->getColorWithAlpha(\
560
                UserColorId::color));\
561
            graphics->fillRectangle(Rect(\
562
                x0 * mTileWidth - scrollX, \
563
                y * mTileHeight - scrollY, \
564
                width, mapTileSize));\
565
        }\
566
    }\
567
568
void Map::drawCollision(Graphics *restrict const graphics,
569
                        const int scrollX,
570
                        const int scrollY,
571
                        const MapTypeT drawFlags) const restrict2
572
{
573
    const int endPixelY = graphics->mHeight + scrollY + mTileHeight - 1;
574
    int startX = scrollX / mTileWidth;
575
    int startY = scrollY / mTileHeight;
576
    int endX = (graphics->mWidth + scrollX + mTileWidth - 1) / mTileWidth;
577
    int endY = endPixelY / mTileHeight;
578
579
    if (startX < 0)
580
        startX = 0;
581
    if (startY < 0)
582
        startY = 0;
583
    if (endX > mWidth)
584
        endX = mWidth;
585
    if (endY > mHeight)
586
        endY = mHeight;
587
588
    if (drawFlags < MapType::SPECIAL)
589
    {
590
        graphics->setColor(userPalette->getColorWithAlpha(UserColorId::NET));
591
        graphics->drawNet(
592
            startX * mTileWidth - scrollX,
593
            startY * mTileHeight - scrollY,
594
            endX * mTileWidth - scrollX,
595
            endY * mTileHeight - scrollY,
596
            mapTileSize, mapTileSize);
597
    }
598
599
    for (int y = startY; y < endY; y++)
600
    {
601
        const int yWidth = y * mWidth;
602
        int tilePtr = startX + yWidth;
603
        for (int x = startX; x < endX; x++, tilePtr++)
604
        {
605
            int width = 0;
606
            const int x0 = x;
607
608
            fillCollision(BlockMask::WALL, COLLISION_HIGHLIGHT)
609
            fillCollision(BlockMask::AIR, AIR_COLLISION_HIGHLIGHT)
610
            fillCollision(BlockMask::WATER, WATER_COLLISION_HIGHLIGHT)
611
            fillCollision(BlockMask::GROUNDTOP, GROUNDTOP_COLLISION_HIGHLIGHT)
612
            fillCollision(BlockMask::PLAYERWALL, COLLISION_HIGHLIGHT)
613
            fillCollision(BlockMask::MONSTERWALL, MONSTER_COLLISION_HIGHLIGHT)
614
        }
615
    }
616
}
617
618
void Map::updateAmbientLayers(const float scrollX,
619
                              const float scrollY) restrict2
620
{
621
    BLOCK_START("Map::updateAmbientLayers")
622
    static int lastTick = tick_time;
623
624
    if (mLastAScrollX == 0.0F && mLastAScrollY == 0.0F)
625
    {
626
        // First call - initialisation
627
        mLastAScrollX = scrollX;
628
        mLastAScrollY = scrollY;
629
    }
630
631
    // Update Overlays
632
    const float dx = scrollX - mLastAScrollX;
633
    const float dy = scrollY - mLastAScrollY;
634
    const int timePassed = get_elapsed_time(lastTick);
635
636
    // need check mask to update or not to update
637
638
    FOR_EACH (AmbientLayerVectorIter, i, mBackgrounds)
639
    {
640
        AmbientLayer *const layer = *i;
641
        if ((layer != nullptr) && ((layer->mMask & mMask) != 0))
642
            layer->update(timePassed, dx, dy);
643
    }
644
645
    FOR_EACH (AmbientLayerVectorIter, i, mForegrounds)
646
    {
647
        AmbientLayer *const layer = *i;
648
        if ((layer != nullptr) && ((layer->mMask & mMask) != 0))
649
            layer->update(timePassed, dx, dy);
650
    }
651
652
    mLastAScrollX = scrollX;
653
    mLastAScrollY = scrollY;
654
    lastTick = tick_time;
655
    BLOCK_END("Map::updateAmbientLayers")
656
}
657
658
void Map::drawAmbientLayers(Graphics *restrict const graphics,
659
                            const MapLayerPositionT type,
660
                            const int detail) const restrict2
661
{
662
    BLOCK_START("Map::drawAmbientLayers")
663
    // Detail 0 = no ambient effects except background image
664
    if (detail <= 0 && type != MapLayerPosition::BACKGROUND_LAYERS)
665
    {
666
        BLOCK_END("Map::drawAmbientLayers")
667
        return;
668
    }
669
670
    // find out which layer list to draw
671
    const AmbientLayerVector *restrict layers = nullptr;
672
    switch (type)
673
    {
674
        case MapLayerPosition::FOREGROUND_LAYERS:
675
            layers = &mForegrounds;
676
            break;
677
        case MapLayerPosition::BACKGROUND_LAYERS:
678
            layers = &mBackgrounds;
679
            break;
680
        default:
681
            return;
682
    }
683
684
    // Draw overlays
685
    FOR_EACHP (AmbientLayerVectorCIter, i, layers)
686
    {
687
        const AmbientLayer *restrict const layer = *i;
688
        // need check mask to draw or not to draw
689
        if ((layer != nullptr) && ((layer->mMask & mMask) != 0))
690
            (layer)->draw(graphics, graphics->mWidth, graphics->mHeight);
691
692
        // Detail 1: only one overlay, higher: all overlays
693
        if (detail == 1)
694
            break;
695
    }
696
    BLOCK_END("Map::drawAmbientLayers")
697
}
698
699
const Tileset *Map::getTilesetWithGid(const int gid) const restrict2
700
{
701
    if (gid >= 0 && gid < mIndexedTilesetsSize)
702
        return mIndexedTilesets[gid];
703
    return nullptr;
704
}
705
706
20010
void Map::addBlockMask(const int x, const int y,
707
                       const BlockTypeT type) restrict2
708
{
709

40016
    if (type == BlockType::NONE || !contains(x, y))
710
        return;
711
712
20006
    const int tileNum = x + y * mWidth;
713
714


20006
    switch (type)
715
    {
716
        case BlockType::WALL:
717
            mMetaTiles[tileNum].blockmask |= BlockMask::WALL;
718
            break;
719
        case BlockType::AIR:
720
            mMetaTiles[tileNum].blockmask |= BlockMask::AIR;
721
            break;
722
        case BlockType::WATER:
723
20006
            mMetaTiles[tileNum].blockmask |= BlockMask::WATER;
724
20006
            break;
725
        case BlockType::GROUND:
726
            mMetaTiles[tileNum].blockmask |= BlockMask::GROUND;
727
            break;
728
        case BlockType::GROUNDTOP:
729
            mMetaTiles[tileNum].blockmask |= BlockMask::GROUNDTOP;
730
            break;
731
        case BlockType::PLAYERWALL:
732
            mMetaTiles[tileNum].blockmask |= BlockMask::PLAYERWALL;
733
            break;
734
        case BlockType::MONSTERWALL:
735
            mMetaTiles[tileNum].blockmask |= BlockMask::MONSTERWALL;
736
            break;
737
        default:
738
        case BlockType::NONE:
739
        case BlockType::NB_BLOCKTYPES:
740
            // Do nothing.
741
            break;
742
    }
743
}
744
745
void Map::setBlockMask(const int x, const int y,
746
                       const BlockTypeT type) restrict2
747
{
748
    if (type == BlockType::NONE || !contains(x, y))
749
        return;
750
751
    const int tileNum = x + y * mWidth;
752
753
    switch (type)
754
    {
755
        case BlockType::WALL:
756
            mMetaTiles[tileNum].blockmask = BlockMask::WALL;
757
            break;
758
        case BlockType::AIR:
759
            mMetaTiles[tileNum].blockmask = BlockMask::AIR;
760
            break;
761
        case BlockType::WATER:
762
            mMetaTiles[tileNum].blockmask = BlockMask::WATER;
763
            break;
764
        case BlockType::GROUND:
765
            mMetaTiles[tileNum].blockmask = BlockMask::GROUND;
766
            break;
767
        case BlockType::GROUNDTOP:
768
            mMetaTiles[tileNum].blockmask = BlockMask::GROUNDTOP;
769
            break;
770
        case BlockType::PLAYERWALL:
771
            mMetaTiles[tileNum].blockmask = BlockMask::PLAYERWALL;
772
            break;
773
        case BlockType::MONSTERWALL:
774
            mMetaTiles[tileNum].blockmask = BlockMask::MONSTERWALL;
775
            break;
776
        default:
777
        case BlockType::NONE:
778
        case BlockType::NB_BLOCKTYPES:
779
            // Do nothing.
780
            break;
781
    }
782
}
783
784
bool Map::getWalk(const int x, const int y,
785
                  const unsigned char blockWalkMask) const restrict2
786
{
787
    // You can't walk outside of the map
788
    if (x < 0 || y < 0 || x >= mWidth || y >= mHeight)
789
        return false;
790
791
    // Check if the tile is walkable
792
    return (mMetaTiles[x + y * mWidth].blockmask & blockWalkMask) == 0;
793
}
794
795
unsigned char Map::getBlockMask(const int x,
796
                                const int y) const restrict2
797
{
798
    // You can't walk outside of the map
799
    if (x < 0 || y < 0 || x >= mWidth || y >= mHeight)
800
        return 0;
801
802
    // Check if the tile is walkable
803
    return mMetaTiles[x + y * mWidth].blockmask;
804
}
805
806
void Map::setWalk(const int x, const int y) restrict2
807
{
808
    addBlockMask(x, y, BlockType::GROUNDTOP);
809
}
810
811
bool Map::contains(const int x, const int y) const restrict2
812
{
813




20006
    return x >= 0 && y >= 0 && x < mWidth && y < mHeight;
814
}
815
816
const MetaTile *Map::getMetaTile(const int x, const int y) const restrict2
817
{
818
    return &mMetaTiles[x + y * mWidth];
819
}
820
821
2
Actors::iterator Map::addActor(Actor *const actor) restrict2
822
{
823
4
    mActors.push_front(actor);
824
//    mSpritesUpdated = true;
825
4
    return mActors.begin();
826
}
827
828
2
void Map::removeActor(const Actors::iterator &restrict iterator) restrict2
829
{
830
4
    mActors.erase(iterator);
831
//    mSpritesUpdated = true;
832
2
}
833
834
const std::string Map::getMusicFile() const restrict2
835
{
836
    return getProperty("music", std::string());
837
}
838
839
const std::string Map::getName() const restrict2
840
{
841
    if (hasProperty("name"))
842
        return getProperty("name", std::string());
843
844
    return getProperty("mapname", std::string());
845
}
846
847
const std::string Map::getFilename() const restrict2
848
{
849
    const std::string fileName = getProperty("_filename", std::string());
850
    const size_t lastSlash = fileName.rfind('/') + 1;
851
    return fileName.substr(lastSlash, fileName.rfind('.') - lastSlash);
852
}
853
854
const std::string Map::getGatName() const restrict2
855
{
856
    const std::string fileName = getProperty("_filename", std::string());
857
    const size_t lastSlash = rfindSepatator(fileName) + 1;
858
    return fileName.substr(lastSlash,
859
        fileName.rfind('.') - lastSlash).append(".gat");
860
}
861
862
Path Map::findPath(const int startX, const int startY,
863
                   const int destX, const int destY,
864
                   const unsigned char blockWalkMask,
865
                   const int maxCost) restrict2
866
{
867
    BLOCK_START("Map::findPath")
868
    // The basic walking cost of a tile.
869
    static const int basicCost = 100;
870
    const int basicCost2 = 100 * 362 / 256;
871
    const float basicCostF = 100.0 * 362 / 256;
872
873
    // Path to be built up (empty by default)
874
    Path path;
875
876
    if (startX >= mWidth || startY >= mHeight || startX < 0 || startY < 0)
877
    {
878
        BLOCK_END("Map::findPath")
879
        return path;
880
    }
881
882
    // Return when destination not walkable
883
    if (!getWalk(destX, destY, blockWalkMask))
884
    {
885
        BLOCK_END("Map::findPath")
886
        return path;
887
    }
888
889
    // Reset starting tile's G cost to 0
890
    MetaTile *const startTile = &mMetaTiles[startX + startY * mWidth];
891
    if (startTile == nullptr)
892
    {
893
        BLOCK_END("Map::findPath")
894
        return path;
895
    }
896
897
    startTile->Gcost = 0;
898
899
    // Declare open list, a list with open tiles sorted on F cost
900
    std::priority_queue<Location> openList;
901
902
    // Add the start point to the open list
903
    openList.push(Location(startX, startY, startTile));
904
905
    bool foundPath = false;
906
907
    // Keep trying new open tiles until no more tiles to try or target found
908
    while (!openList.empty() && !foundPath)
909
    {
910
        // Take the location with the lowest F cost from the open list.
911
        const Location curr = openList.top();
912
        openList.pop();
913
914
        const MetaTile *const tile = curr.tile;
915
916
        // If the tile is already on the closed list, this means it has already
917
        // been processed with a shorter path to the start point (lower G cost)
918
        if (tile->whichList == mOnClosedList)
919
            continue;
920
921
        // Put the current tile on the closed list
922
        curr.tile->whichList = mOnClosedList;
923
924
        const int curWidth = curr.y * mWidth;
925
        const int tileGcost = tile->Gcost;
926
927
        // Check the adjacent tiles
928
        for (int dy = -1; dy <= 1; dy++)
929
        {
930
            const int y = curr.y + dy;
931
            if (y < 0 || y >= mHeight)
932
                continue;
933
934
            const int yWidth = y * mWidth;
935
            const int dy1 = std::abs(y - destY);
936
937
            for (int dx = -1; dx <= 1; dx++)
938
            {
939
                // Calculate location of tile to check
940
                const int x = curr.x + dx;
941
942
                // Skip if if we're checking the same tile we're leaving from,
943
                // or if the new location falls outside of the map boundaries
944
                if ((dx == 0 && dy == 0) || x < 0 || x >= mWidth)
945
                    continue;
946
947
                MetaTile *const newTile = &mMetaTiles[x + yWidth];
948
949
                // Skip if the tile is on the closed list or is not walkable
950
                // unless its the destination tile
951
                // +++ probably here "newTile->blockmask & BlockMask::WALL"
952
                // can be removed. It left here only for protect from
953
                // walk on wall in any case
954
                if (newTile->whichList == mOnClosedList ||
955
                    (((newTile->blockmask & blockWalkMask) != 0)
956
                    && !(x == destX && y == destY))
957
                    || ((newTile->blockmask & BlockMask::WALL) != 0))
958
                {
959
                    continue;
960
                }
961
962
                // When taking a diagonal step, verify that we can skip the
963
                // corner.
964
                if (dx != 0 && dy != 0)
965
                {
966
                    const MetaTile *const t1 = &mMetaTiles[curr.x +
967
                        (curr.y + dy) * mWidth];
968
                    const MetaTile *const t2 = &mMetaTiles[curr.x +
969
                        dx + curWidth];
970
971
                    // on player abilities.
972
                    if (((t1->blockmask | t2->blockmask) & blockWalkMask) != 0)
973
                        continue;
974
                }
975
976
                // Calculate G cost for this route, ~sqrt(2) for moving diagonal
977
                int Gcost = tileGcost + (dx == 0 || dy == 0
978
                    ? basicCost : basicCost2);
979
980
                /* Demote an arbitrary direction to speed pathfinding by
981
                   adding a defect
982
                   Important: as long as the total defect along any path is
983
                   less than the basicCost, the pathfinder will still find one
984
                   of the shortest paths! */
985
                if (dx == 0 || dy == 0)
986
                {
987
                    // Demote horizontal and vertical directions, so that two
988
                    // consecutive directions cannot have the same Fcost.
989
                    ++Gcost;
990
                }
991
992
/*
993
                // It costs extra to walk through a being (needs to be enough
994
                // to make it more attractive to walk around).
995
                if (occupied(x, y))
996
                {
997
                    Gcost += 3 * basicCost;
998
                }
999
*/
1000
1001
                // Skip if Gcost becomes too much
1002
                // Warning: probably not entirely accurate
1003
                if (maxCost > 0 && Gcost > maxCost * basicCost)
1004
                    continue;
1005
1006
                if (newTile->whichList != mOnOpenList)
1007
                {
1008
                    // Found a new tile (not on open nor on closed list)
1009
1010
                    /* Update Hcost of the new tile. The pathfinder does not
1011
                       work reliably if the heuristic cost is higher than the
1012
                       real cost. In particular, using Manhattan distance is
1013
                       forbidden here. */
1014
                    const int dx1 = std::abs(x - destX);
1015
                    newTile->Hcost = std::abs(dx1 - dy1) * basicCost +
1016
                        CAST_S32(static_cast<float>(std::min(dx1, dy1)) *
1017
                        (basicCostF));
1018
1019
                    // Set the current tile as the parent of the new tile
1020
                    newTile->parentX = curr.x;
1021
                    newTile->parentY = curr.y;
1022
1023
                    // Update Gcost and Fcost of new tile
1024
                    newTile->Gcost = Gcost;
1025
                    newTile->Fcost = Gcost + newTile->Hcost;
1026
1027
                    if (x != destX || y != destY)
1028
                    {
1029
                        // Add this tile to the open list
1030
                        newTile->whichList = mOnOpenList;
1031
                        openList.push(Location(x, y, newTile));
1032
                    }
1033
                    else
1034
                    {
1035
                        // Target location was found
1036
                        foundPath = true;
1037
                    }
1038
                }
1039
                else if (Gcost < newTile->Gcost)
1040
                {
1041
                    // Found a shorter route.
1042
                    // Update Gcost and Fcost of the new tile
1043
                    newTile->Gcost = Gcost;
1044
                    newTile->Fcost = Gcost + newTile->Hcost;
1045
1046
                    // Set the current tile as the parent of the new tile
1047
                    newTile->parentX = curr.x;
1048
                    newTile->parentY = curr.y;
1049
1050
                    // Add this tile to the open list (it's already
1051
                    // there, but this instance has a lower F score)
1052
                    openList.push(Location(x, y, newTile));
1053
                }
1054
            }
1055
        }
1056
    }
1057
1058
    // Two new values to indicate whether a tile is on the open or closed list,
1059
    // this way we don't have to clear all the values between each pathfinding.
1060
    if (mOnOpenList > UINT_MAX - 2)
1061
    {
1062
        // We reset the list memebers value.
1063
        mOnClosedList = 1;
1064
        mOnOpenList = 2;
1065
1066
        // Clean up the metaTiles
1067
        const int size = mWidth * mHeight;
1068
        for (int i = 0; i < size; ++i)
1069
            mMetaTiles[i].whichList = 0;
1070
    }
1071
    else
1072
    {
1073
        mOnClosedList += 2;
1074
        mOnOpenList += 2;
1075
    }
1076
1077
    // If a path has been found, iterate backwards using the parent locations
1078
    // to extract it.
1079
    if (foundPath)
1080
    {
1081
        int pathX = destX;
1082
        int pathY = destY;
1083
1084
        while (pathX != startX || pathY != startY)
1085
        {
1086
            // Add the new path node to the start of the path list
1087
            path.push_front(Position(pathX, pathY));
1088
1089
            // Find out the next parent
1090
            const MetaTile *const tile = &mMetaTiles[pathX + pathY * mWidth];
1091
            pathX = tile->parentX;
1092
            pathY = tile->parentY;
1093
        }
1094
    }
1095
1096
    BLOCK_END("Map::findPath")
1097
    return path;
1098
}
1099
1100
void Map::addParticleEffect(const std::string &effectFile,
1101
                            const int x, const int y,
1102
                            const int w, const int h) restrict2
1103
{
1104
    ParticleEffectData newEffect(effectFile, x, y, w, h);
1105
    mParticleEffects.push_back(newEffect);
1106
}
1107
1108
void Map::initializeParticleEffects() const restrict2
1109
{
1110
    BLOCK_START("Map::initializeParticleEffects")
1111
    if (particleEngine == nullptr)
1112
    {
1113
        BLOCK_END("Map::initializeParticleEffects")
1114
        return;
1115
    }
1116
1117
    if (config.getBoolValue("particleeffects"))
1118
    {
1119
        for (STD_VECTOR<ParticleEffectData>::const_iterator
1120
             i = mParticleEffects.begin();
1121
             i != mParticleEffects.end();
1122
             ++i)
1123
        {
1124
            // +++ add z for map particle effects?
1125
            Particle *const p = particleEngine->addEffect(
1126
                i->file,
1127
                i->x,
1128
                i->y,
1129
                0);
1130
            if ((p != nullptr) &&
1131
                i->w > 0 &&
1132
                i->h > 0)
1133
            {
1134
                p->adjustEmitterSize(i->w, i->h);
1135
            }
1136
        }
1137
    }
1138
     BLOCK_END("Map::initializeParticleEffects")
1139
}
1140
1141
void Map::addExtraLayer() restrict2
1142
{
1143
    BLOCK_START("Map::addExtraLayer")
1144
    if (mSpecialLayer == nullptr)
1145
    {
1146
        logger->log1("No special layer");
1147
        BLOCK_END("Map::addExtraLayer")
1148
        return;
1149
    }
1150
    const std::string mapFileName = pathJoin(getUserMapDirectory(),
1151
        "extralayer.txt");
1152
    logger->log("loading extra layer: " + mapFileName);
1153
    struct stat statbuf;
1154
    if (stat(mapFileName.c_str(), &statbuf) == 0 &&
1155
        S_ISREG(statbuf.st_mode))
1156
    {
1157
        std::ifstream mapFile;
1158
        mapFile.open(mapFileName.c_str(), std::ios::in);
1159
        if (!mapFile.is_open())
1160
        {
1161
            mapFile.close();
1162
            BLOCK_END("Map::addExtraLayer")
1163
            return;
1164
        }
1165
        char line[201];
1166
1167
        while (mapFile.getline(line, 200))
1168
        {
1169
            std::string buf;
1170
            std::string str = line;
1171
            if (!str.empty())
1172
            {
1173
                std::string x;
1174
                std::string y;
1175
                std::string type1;
1176
                std::string comment;
1177
                std::stringstream ss(str);
1178
                ss >> x;
1179
                ss >> y;
1180
                ss >> type1;
1181
                ss >> comment;
1182
                while (ss >> buf)
1183
                    comment.append(" ").append(buf);
1184
1185
                const int type = atoi(type1.c_str());
1186
1187
                if (comment.empty())
1188
                {
1189
                    if (type < MapItemType::ARROW_UP
1190
                        || type > MapItemType::ARROW_RIGHT)
1191
                    {
1192
                        comment = "unknown";
1193
                    }
1194
                }
1195
                if (type == MapItemType::PORTAL)
1196
                {
1197
                    updatePortalTile(comment,
1198
                        type,
1199
                        atoi(x.c_str()),
1200
                        atoi(y.c_str()),
1201
                        false);
1202
                }
1203
                else if (type == MapItemType::HOME)
1204
                {
1205
                    updatePortalTile(comment,
1206
                        type,
1207
                        atoi(x.c_str()),
1208
                        atoi(y.c_str()),
1209
                        true);
1210
                }
1211
                else
1212
                {
1213
                    addPortalTile(comment,
1214
                        type,
1215
                        atoi(x.c_str()),
1216
                        atoi(y.c_str()));
1217
                }
1218
            }
1219
        }
1220
        mapFile.close();
1221
    }
1222
    BLOCK_END("Map::addExtraLayer")
1223
}
1224
1225
void Map::saveExtraLayer() const restrict2
1226
{
1227
    if (mSpecialLayer == nullptr)
1228
    {
1229
        logger->log1("No special layer");
1230
        return;
1231
    }
1232
    const std::string mapFileName = pathJoin(getUserMapDirectory(),
1233
        "extralayer.txt");
1234
    logger->log("saving extra layer: " + mapFileName);
1235
1236
    if (mkdir_r(getUserMapDirectory().c_str()) != 0)
1237
    {
1238
        logger->log(strprintf("%s doesn't exist and can't be created! "
1239
                              "Exiting.", getUserMapDirectory().c_str()));
1240
        return;
1241
    }
1242
1243
    std::ofstream mapFile;
1244
    mapFile.open(mapFileName.c_str(), std::ios::binary);
1245
    if (!mapFile.is_open())
1246
    {
1247
        reportAlways("Error opening file for writing: %s",
1248
            mapFileName.c_str())
1249
        return;
1250
    }
1251
1252
    const int width = mSpecialLayer->mWidth;
1253
    const int height = mSpecialLayer->mHeight;
1254
1255
    for (int x = 0; x < width; x ++)
1256
    {
1257
        for (int y = 0; y < height; y ++)
1258
        {
1259
            const MapItem *restrict const item = mSpecialLayer->getTile(x, y);
1260
            if ((item != nullptr) && item->mType != MapItemType::EMPTY
1261
                && item->mType != MapItemType::HOME)
1262
            {
1263
                mapFile << x << " " << y << " "
1264
                    << CAST_S32(item->mType) << " "
1265
                    << item->mComment << std::endl;
1266
            }
1267
        }
1268
    }
1269
    mapFile.close();
1270
}
1271
1272
std::string Map::getUserMapDirectory() const restrict2
1273
{
1274
    return pathJoin(settings.serverConfigDir,
1275
        getProperty("_realfilename", std::string()));
1276
}
1277
1278
void Map::addRange(const std::string &restrict name,
1279
                   const int type,
1280
                   const int x, const int y,
1281
                   const int dx, const int dy) restrict2
1282
{
1283
    if (mObjects == nullptr)
1284
        return;
1285
1286
    mObjects->addObject(name, type, x / mapTileSize, y / mapTileSize,
1287
        dx / mapTileSize, dy / mapTileSize);
1288
}
1289
1290
void Map::addPortal(const std::string &restrict name,
1291
                    const int type,
1292
                    const int x, const int y,
1293
                    const int dx, const int dy) restrict2
1294
{
1295
    addPortalTile(name, type, (x / mapTileSize) + (dx / mapTileSize / 2),
1296
        (y / mapTileSize) + (dy / mapTileSize / 2));
1297
}
1298
1299
void Map::addPortalTile(const std::string &restrict name,
1300
                        const int type,
1301
                        const int x, const int y) restrict2
1302
{
1303
    if (mSpecialLayer != nullptr)
1304
    {
1305
        mSpecialLayer->setTile(x, y, new MapItem(type, name, x, y));
1306
        mSpecialLayer->updateCache();
1307
    }
1308
1309
    mMapPortals.push_back(new MapItem(type, name, x, y));
1310
}
1311
1312
void Map::updatePortalTile(const std::string &restrict name,
1313
                           const int type,
1314
                           const int x, const int y,
1315
                           const bool addNew) restrict2
1316
{
1317
    MapItem *restrict item = findPortalXY(x, y);
1318
    if (item != nullptr)
1319
    {
1320
        item->mComment = name;
1321
        item->setType(type);
1322
        item->mX = x;
1323
        item->mY = y;
1324
        if (mSpecialLayer != nullptr)
1325
        {
1326
            item = new MapItem(type, name, x, y);
1327
            mSpecialLayer->setTile(x, y, item);
1328
            mSpecialLayer->updateCache();
1329
        }
1330
    }
1331
    else if (addNew)
1332
    {
1333
        addPortalTile(name, type, x, y);
1334
    }
1335
}
1336
1337
MapItem *Map::findPortalXY(const int x, const int y) const restrict2
1338
{
1339
    FOR_EACH (STD_VECTOR<MapItem*>::const_iterator, it, mMapPortals)
1340
    {
1341
        if (*it == nullptr)
1342
            continue;
1343
1344
        MapItem *restrict const item = *it;
1345
        if (item->mX == x && item->mY == y)
1346
            return item;
1347
    }
1348
    return nullptr;
1349
}
1350
1351
const TileAnimation *Map::getAnimationForGid(const int gid) const restrict2
1352
{
1353
    if (mTileAnimations.empty())
1354
        return nullptr;
1355
1356
    const TileAnimationMapCIter i = mTileAnimations.find(gid);
1357
    return (i == mTileAnimations.end()) ? nullptr : i->second;
1358
}
1359
1360
void Map::setPvpMode(const int mode) restrict2
1361
{
1362
    const int oldMode = mPvp;
1363
1364
    if (mode == 0)
1365
        mPvp = 0;
1366
    else
1367
        mPvp |= mode;
1368
1369
    if (mPvp != oldMode && (localPlayer != nullptr))
1370
    {
1371
        switch (mPvp)
1372
        {
1373
            case 0:
1374
                NotifyManager::notify(NotifyTypes::PVP_OFF_GVG_OFF);
1375
                break;
1376
            case 1:
1377
                NotifyManager::notify(NotifyTypes::PVP_ON);
1378
                break;
1379
            case 2:
1380
                NotifyManager::notify(NotifyTypes::GVG_ON);
1381
                break;
1382
            case 3:
1383
                NotifyManager::notify(NotifyTypes::PVP_ON_GVG_ON);
1384
                break;
1385
            default:
1386
                NotifyManager::notify(NotifyTypes::PVP_UNKNOWN);
1387
                break;
1388
        }
1389
    }
1390
}
1391
1392
std::string Map::getObjectData(const unsigned x, const unsigned y,
1393
                               const int type) const restrict2
1394
{
1395
    if (mObjects == nullptr)
1396
        return "";
1397
1398
    MapObjectList *restrict const list = mObjects->getAt(x, y);
1399
    if (list == nullptr)
1400
        return "";
1401
1402
    STD_VECTOR<MapObject>::const_iterator it = list->objects.begin();
1403
    const STD_VECTOR<MapObject>::const_iterator it_end = list->objects.end();
1404
    while (it != it_end)
1405
    {
1406
        if ((*it).type == type)
1407
            return (*it).data;
1408
        ++ it;
1409
    }
1410
1411
    return "";
1412
}
1413
1414
void Map::indexTilesets() restrict2
1415
{
1416
    if (mTilesetsIndexed)
1417
        return;
1418
1419
    mTilesetsIndexed = true;
1420
1421
    const Tileset *restrict s = nullptr;
1422
    size_t sSz = 0;
1423
    FOR_EACH (Tilesets::const_iterator, it, mTilesets)
1424
    {
1425
        const size_t sz = (*it)->size();
1426
        if ((s == nullptr) || CAST_SIZE(s->getFirstGid()) + sSz
1427
            < CAST_SIZE((*it)->getFirstGid()) + sz)
1428
        {
1429
            s = *it;
1430
            sSz = sz;
1431
        }
1432
    }
1433
    if (s == nullptr)
1434
    {
1435
        mIndexedTilesetsSize = 0;
1436
        mIndexedTilesets = nullptr;
1437
        return;
1438
    }
1439
1440
    const int size = CAST_S32(s->getFirstGid())
1441
        + CAST_S32(s->size());
1442
    mIndexedTilesetsSize = size;
1443
    mIndexedTilesets = new Tileset*[CAST_SIZE(size)];
1444
    std::fill_n(mIndexedTilesets, size, static_cast<Tileset*>(nullptr));
1445
1446
    FOR_EACH (Tilesets::const_iterator, it, mTilesets)
1447
    {
1448
        Tileset *restrict const s2 = *it;
1449
        if (s2 != nullptr)
1450
        {
1451
            const int start = s2->getFirstGid();
1452
            const int end = start + CAST_S32(s2->size());
1453
            for (int f = start; f < end; f ++)
1454
            {
1455
                if (f < size)
1456
                    mIndexedTilesets[f] = s2;
1457
            }
1458
        }
1459
    }
1460
}
1461
1462
void Map::clearIndexedTilesets() restrict2
1463
{
1464
    if (!mTilesetsIndexed)
1465
        return;
1466
1467
    mTilesetsIndexed = false;
1468
    delete [] mIndexedTilesets;
1469
    mIndexedTilesetsSize = 0;
1470
}
1471
1472
void Map::reduce() restrict2
1473
{
1474
#ifndef USE_SDL2
1475
    if ((mFringeLayer == nullptr) ||
1476
        mOpenGL != RENDER_SOFTWARE ||
1477
        !config.getBoolValue("enableMapReduce"))
1478
    {
1479
        return;
1480
    }
1481
1482
    int cnt = 0;
1483
    for (int x = 0; x < mWidth; x ++)
1484
    {
1485
        for (int y = 0; y < mHeight; y ++)
1486
        {
1487
            bool correct(true);
1488
            bool dontHaveAlpha(false);
1489
1490
            FOR_EACH (LayersCIter, layeri, mLayers)
1491
            {
1492
                const MapLayer *restrict const layer = *layeri;
1493
                if (x >= layer->mWidth || y >= layer->mHeight)
1494
                    continue;
1495
1496
                // skip layers with flags
1497
                if (layer->mTileCondition != -1 || layer->mMask != 1)
1498
                    continue;
1499
1500
                Image *restrict const img =
1501
                    layer->mTiles[x + y * layer->mWidth].image;
1502
                if (img != nullptr)
1503
                {
1504
                    if (img->hasAlphaChannel() && img->isAlphaCalculated())
1505
                    {
1506
                        if (!img->isAlphaVisible())
1507
                        {
1508
                            dontHaveAlpha = true;
1509
                            img->setAlphaVisible(false);
1510
                        }
1511
                    }
1512
                    else if (img->mBounds.w > mapTileSize
1513
                             || img->mBounds.h > mapTileSize)
1514
                    {
1515
                        correct = false;
1516
                        img->setAlphaVisible(true);
1517
                        break;
1518
                    }
1519
                    else if (!img->isHasAlphaChannel())
1520
                    {
1521
                        dontHaveAlpha = true;
1522
                        img->setAlphaVisible(false);
1523
                    }
1524
                    else if (img->hasAlphaChannel())
1525
                    {
1526
                        const uint8_t *restrict const arr =
1527
                            img->SDLgetAlphaChannel();
1528
                        if (arr == nullptr)
1529
                            continue;
1530
1531
                        bool bad(false);
1532
                        bool stop(false);
1533
                        int width;
1534
                        const SubImage *restrict const subImg
1535
                            = dynamic_cast<SubImage*>(img);
1536
                        if (subImg != nullptr)
1537
                            width = subImg->mInternalBounds.w;
1538
                        else
1539
                            width = img->mBounds.w;
1540
1541
                        for (int f = img->mBounds.x;
1542
                             f < img->mBounds.x + img->mBounds.w; f ++)
1543
                        {
1544
                            for (int d = img->mBounds.y;
1545
                                 d < img->mBounds.y + img->mBounds.h; d ++)
1546
                            {
1547
                                const uint8_t chan = arr[f + d * width];
1548
                                if (chan != 255)
1549
                                {
1550
                                    bad = true;
1551
                                    stop = true;
1552
                                    break;
1553
                                }
1554
                            }
1555
                            if (stop)
1556
                                break;
1557
                        }
1558
                        if (!bad)
1559
                        {
1560
                            dontHaveAlpha = true;
1561
                            img->setAlphaVisible(false);
1562
                        }
1563
                        else
1564
                        {
1565
                            img->setAlphaVisible(true);
1566
                        }
1567
                    }
1568
                    img->setAlphaCalculated(true);
1569
                }
1570
            }
1571
            if (!correct || !dontHaveAlpha)
1572
                continue;
1573
1574
            Layers::reverse_iterator ri = mLayers.rbegin();
1575
            while (ri != mLayers.rend())
1576
            {
1577
                const MapLayer *restrict const layer = *ri;
1578
                if (x >= layer->mWidth || y >= layer->mHeight)
1579
                {
1580
                    ++ ri;
1581
                    continue;
1582
                }
1583
1584
                // skip layers with flags
1585
                if (layer->mTileCondition != -1 || layer->mMask != 1)
1586
                {
1587
                    ++ ri;
1588
                    continue;
1589
                }
1590
1591
                const Image *restrict img =
1592
                    layer->mTiles[x + y * layer->mWidth].image;
1593
                if ((img != nullptr) && !img->isAlphaVisible())
1594
                {   // removing all down tiles
1595
                    ++ ri;
1596
                    while (ri != mLayers.rend())
1597
                    {
1598
                        MapLayer *restrict const layer2 = *ri;
1599
                        // skip layers with flags
1600
                        if (layer2->mTileCondition != -1 || layer2->mMask != 1)
1601
                        {
1602
                            ++ ri;
1603
                            continue;
1604
                        }
1605
                        const size_t pos = x + y * CAST_SIZE(layer2->mWidth);
1606
                        img = layer2->mTiles[pos].image;
1607
                        if (img != nullptr)
1608
                        {
1609
                            layer2->mTiles[pos].image = nullptr;
1610
                            cnt ++;
1611
                        }
1612
                        ++ ri;
1613
                    }
1614
                    break;
1615
                }
1616
                ++ ri;
1617
            }
1618
        }
1619
    }
1620
    logger->log("tiles reduced: %d", cnt);
1621
#endif  // USE_SDL2
1622
}
1623
1624
void Map::addHeights(const MapHeights *restrict const heights) restrict2
1625
{
1626
    delete mHeights;
1627
    mHeights = heights;
1628
}
1629
1630
2
uint8_t Map::getHeightOffset(const int x, const int y) const restrict2
1631
{
1632
2
    if (mHeights == nullptr)
1633
        return 0;
1634
    return mHeights->getHeight(x, y);
1635
}
1636
1637
void Map::updateDrawLayersList() restrict2
1638
{
1639
    mDrawUnderLayers.clear();
1640
    mDrawOverLayers.clear();
1641
1642
    if (mDrawOnlyFringe)
1643
        return;
1644
1645
    LayersCIter layers = mLayers.begin();
1646
    const LayersCIter layers_end = mLayers.end();
1647
    for (; layers != layers_end; ++ layers)
1648
    {
1649
        MapLayer *const layer = *layers;
1650
        if ((layer->mMask & mMask) == 0)
1651
            continue;
1652
1653
        if (layer->isFringeLayer())
1654
        {
1655
            ++ layers;
1656
            break;
1657
        }
1658
1659
        mDrawUnderLayers.push_back(layer);
1660
    }
1661
1662
    if (mDrawLayersFlags == MapType::SPECIAL2)
1663
        return;
1664
1665
    for (; layers != layers_end; ++ layers)
1666
    {
1667
        MapLayer *const layer = *layers;
1668
        if ((layer->mMask & mMask) == 0)
1669
            continue;
1670
1671
        mDrawOverLayers.push_back(layer);
1672
    }
1673
}
1674
1675
void Map::setMask(const int mask) restrict2
1676
{
1677
    if (mask != mMask)
1678
    {
1679
        mRedrawMap = true;
1680
        mMask = mask;
1681
        updateDrawLayersList();
1682
    }
1683
}
1684
1685
void Map::setMusicFile(const std::string &restrict file) restrict2
1686
{
1687
    setProperty("music", file);
1688
}
1689
1690
void Map::addAnimation(const int gid,
1691
                       TileAnimation *restrict const animation) restrict2
1692
{
1693
    std::map<int, TileAnimation*>::iterator it = mTileAnimations.find(gid);
1694
    if (it != mTileAnimations.end())
1695
    {
1696
        logger->log("duplicate map animation with gid = %d", gid);
1697
        delete (*it).second;
1698
    }
1699
    mTileAnimations[gid] = animation;
1700
}
1701
1702
void Map::setDrawLayersFlags(const MapTypeT &restrict n) restrict2
1703
{
1704
    mDrawLayersFlags = n;
1705
    if (mDrawLayersFlags == MapType::SPECIAL3 ||
1706
        mDrawLayersFlags == MapType::SPECIAL4 ||
1707
        mDrawLayersFlags == MapType::BLACKWHITE)
1708
    {
1709
        mDrawOnlyFringe = true;
1710
    }
1711
    else
1712
    {
1713
        mDrawOnlyFringe = false;
1714
    }
1715
    updateDrawLayersList();
1716
    FOR_EACH (Layers::iterator, it, mLayers)
1717
    {
1718
        MapLayer *restrict const layer = *it;
1719
        layer->setDrawLayerFlags(mDrawLayersFlags);
1720
    }
1721
}
1722
1723
void Map::setActorsFix(const int x, const int y) restrict2
1724
{
1725
    mActorFixX = x;
1726
    mActorFixY = y;
1727
    if (mFringeLayer != nullptr)
1728
        mFringeLayer->setActorsFix(y);
1729
}
1730
1731
void Map::updateConditionLayers() restrict2
1732
{
1733
    mRedrawMap = true;
1734
1735
    FOR_EACH (LayersCIter, it, mLayers)
1736
    {
1737
        MapLayer *restrict const layer = *it;
1738
        if ((layer == nullptr) || layer->mTileCondition == -1)
1739
            continue;
1740
        layer->updateConditionTiles(mMetaTiles,
1741
            mWidth, mHeight);
1742
    }
1743
}
1744
1745
void Map::preCacheLayers() restrict2
1746
{
1747
    FOR_EACH (LayersCIter, it, mLayers)
1748
    {
1749
        MapLayer *restrict const layer = *it;
1750
        if (layer != nullptr)
1751
            layer->updateCache(mWidth, mHeight);
1752
    }
1753
}
1754
1755
int Map::calcMemoryLocal() const
1756
{
1757
    return static_cast<int>(sizeof(Map) +
1758
        mName.capacity() +
1759
        sizeof(MetaTile) * mWidth * mHeight +
1760
        sizeof(MapLayer*) * (mLayers.capacity() +
1761
        mDrawUnderLayers.capacity() +
1762
        mDrawOverLayers.capacity()) +
1763
        sizeof(Tileset*) * mTilesets.capacity() +
1764
        sizeof(Actor*) * mActors.size() +
1765
        sizeof(AmbientLayer*) * (mBackgrounds.capacity()
1766
        + mForegrounds.capacity()) +
1767
        sizeof(ParticleEffectData) * mParticleEffects.capacity() +
1768
        sizeof(MapItem) * mMapPortals.capacity() +
1769
        (sizeof(TileAnimation) + sizeof(int)) * mTileAnimations.size() +
1770
        sizeof(Tileset*) * mIndexedTilesetsSize);
1771
}
1772
1773
int Map::calcMemoryChilds(const int level) const
1774
{
1775
    int sz = 0;
1776
1777
    if (mWalkLayer != nullptr)
1778
        sz += mWalkLayer->calcMemory(level + 1);
1779
    FOR_EACH (LayersCIter, it, mLayers)
1780
    {
1781
        sz += (*it)->calcMemory(level + 1);
1782
    }
1783
    FOR_EACH (Tilesets::const_iterator, it, mTilesets)
1784
    {
1785
        sz += (*it)->calcMemory(level + 1);
1786
    }
1787
    FOR_EACH (AmbientLayerVectorCIter, it, mBackgrounds)
1788
    {
1789
        sz += (*it)->calcMemory(level + 1);
1790
    }
1791
    FOR_EACH (AmbientLayerVectorCIter, it, mForegrounds)
1792
    {
1793
        sz += (*it)->calcMemory(level + 1);
1794
    }
1795
    if (mSpecialLayer != nullptr)
1796
        mSpecialLayer->calcMemory(level + 1);
1797
    if (mTempLayer != nullptr)
1798
        mTempLayer->calcMemory(level + 1);
1799
    if (mObjects != nullptr)
1800
        mObjects->calcMemory(level + 1);
1801
    if (mHeights != nullptr)
1802
        mHeights->calcMemory(level + 1);
1803
    return sz;
1804
}
1805
1806
void Map::screenResized()
1807
{
1808
    if (mWidth * mapTileSize < mainGraphics->mWidth ||
1809
        mHeight * mapTileSize < mainGraphics->mHeight)
1810
    {
1811
        mClear = true;
1812
    }
1813
    else
1814
    {
1815
        mClear = false;
1816
    }
1817
}
1818
1819
#ifdef USE_OPENGL
1820
int Map::getAtlasCount() const restrict2
1821
{
1822
    if (mAtlas == nullptr)
1823
        return 0;
1824
    return CAST_S32(mAtlas->atlases.size());
1825
2
}
1826
#endif  // USE_OPENGL