GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/map/map.cpp Lines: 58 788 7.4 %
Date: 2017-11-29 Branches: 48 970 4.9 %

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 "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
#include "resources/image/subimage.h"
46
47
#include "resources/loaders/imageloader.h"
48
49
#include "resources/map/location.h"
50
#include "resources/map/mapheights.h"
51
#include "resources/map/mapobjectlist.h"
52
#include "resources/map/maplayer.h"
53
#include "resources/map/mapitem.h"
54
#include "resources/map/objectslayer.h"
55
#include "resources/map/speciallayer.h"
56
#include "resources/map/tileanimation.h"
57
#include "resources/map/tileset.h"
58
#include "resources/map/walklayer.h"
59
60
61
#ifdef USE_OPENGL
62
#include "render/renderers.h"
63
#endif  // USE_OPENGL
64
65
#include "utils/checkutils.h"
66
#include "utils/delete2.h"
67
#include "utils/dtor.h"
68
#include "utils/foreach.h"
69
#include "utils/timer.h"
70
71
#include <queue>
72
73
#include <sys/stat.h>
74
75
#include <climits>
76
77
#include "debug.h"
78
79
class ActorFunctuator final
80
{
81
    public:
82
        A_DEFAULT_COPY(ActorFunctuator)
83
84
        bool operator()(const Actor *const a,
85
                        const Actor *const b) const
86
        {
87
            if ((a == nullptr) || (b == nullptr))
88
                return false;
89
            return a->getSortPixelY() < b->getSortPixelY();
90
        }
91
} actorCompare;
92
93
94
Map::Map(const std::string &name,
94
         const int width,
95
         const int height,
96
         const int tileWidth,
97
94
         const int tileHeight) :
98
    Properties(),
99
    mWidth(width), mHeight(height),
100
    mTileWidth(tileWidth), mTileHeight(tileHeight),
101
    mMaxTileHeight(height),
102

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

376
    mOverlayDetail(config.getIntValue("OverlayDetail")),
122

376
    mOpacity(config.getFloatValue("guialpha")),
123
#ifdef USE_OPENGL
124

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

94
    mSpecialLayer(new SpecialLayer("special layer", width, height)),
136

94
    mTempLayer(new SpecialLayer("temp layer", width, height)),
137

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

188
        mOpenGL == RENDER_GLES2_OPENGL ||
156
        mOpenGL == RENDER_MODERN_OPENGL),
157
#else  // USE_OPENGL
158
    mCachedDraw(false),
159
#endif  // USE_OPENGL
160
    mCustom(false),
161

2726
    mDrawOnlyFringe(false)
162
{
163

376
    config.addListener("OverlayDetail", this);
164

376
    config.addListener("guialpha", this);
165

376
    config.addListener("beingopacity", this);
166
167
94
    if (mOpacity != 1.0F)
168

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

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


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






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