GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/map/map.cpp Lines: 58 792 7.3 %
Date: 2018-05-19 03:07:18 Branches: 48 952 5.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2018  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "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
94
Map::Map(const std::string &name,
97
         const int width,
98
         const int height,
99
         const int tileWidth,
100
94
         const int tileHeight) :
101
    Properties(),
102
    mWidth(width), mHeight(height),
103
    mTileWidth(tileWidth), mTileHeight(tileHeight),
104
    mMaxTileHeight(height),
105

320440
    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

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

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

376
    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

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

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

94
    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
94
    mCachedDraw(mOpenGL == RENDER_NORMAL_OPENGL ||
159
94
        mOpenGL == RENDER_GLES_OPENGL ||
160

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

2726
    mDrawOnlyFringe(false)
167
{
168

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

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

376
    config.addListener("beingopacity", this);
171
172
94
    if (mOpacity != 1.0F)
173

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

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


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






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