GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/map/map.cpp Lines: 57 760 7.5 %
Date: 2018-09-20 Branches: 53 970 5.5 %

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
47
Map::Map(const std::string &name,
97
         const int width,
98
         const int height,
99
         const int tileWidth,
100
47
         const int tileHeight) :
101
    Properties(),
102
    mWidth(width), mHeight(height),
103
    mTileWidth(tileWidth), mTileHeight(tileHeight),
104
    mMaxTileHeight(height),
105

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

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

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

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

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

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

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

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

1316
    mDrawOnlyFringe(false)
167
{
168

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

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

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

188
        mBeingOpacity = config.getBoolValue("beingopacity");
174
    else
175
        mBeingOpacity = false;
176
47
}
177
178
752
Map::~Map()
179
{
180
47
    config.removeListeners(this);
181
    CHECKLISTENERS
182
183
47
    if (mWalkLayer != nullptr)
184
    {
185
        mWalkLayer->decRef();
186
        mWalkLayer = nullptr;
187
    }
188
47
    mFringeLayer = nullptr;
189
94
    delete_all(mLayers);
190
94
    delete_all(mTilesets);
191
94
    delete_all(mForegrounds);
192
94
    delete_all(mBackgrounds);
193
94
    delete_all(mTileAnimations);
194
47
    delete2(mSpecialLayer);
195
47
    delete2(mTempLayer);
196
47
    delete2(mObjects);
197
94
    delete_all(mMapPortals);
198
#ifdef USE_OPENGL
199
47
    if (mAtlas != nullptr)
200
    {
201
        mAtlas->decRef();
202
        mAtlas = nullptr;
203
    }
204
#endif  // USE_OPENGL
205
47
    delete2(mHeights);
206
47
    delete [] mMetaTiles;
207
94
}
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
45
void Map::addLayer(MapLayer *const layer) restrict2
312
{
313
45
    mLayers.push_back(layer);
314

90
    if (layer->isFringeLayer() && (mFringeLayer == nullptr))
315
30
        mFringeLayer = layer;
316
45
}
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
20010
void Map::addBlockMask(const int x, const int y,
703
                       const BlockTypeT type) restrict2
704
{
705

40016
    if (type == BlockType::NONE || !contains(x, y))
706
        return;
707
708
20006
    const int tileNum = x + y * mWidth;
709
710


20006
    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
        case BlockType::WATER:
719
20006
            mMetaTiles[tileNum].blockmask |= BlockMask::WATER;
720
20006
            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




20006
    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
2
Actors::iterator Map::addActor(Actor *const actor) restrict2
818
{
819
4
    mActors.push_front(actor);
820
//    mSpritesUpdated = true;
821
4
    return mActors.begin();
822
}
823
824
2
void Map::removeActor(const Actors::iterator &restrict iterator) restrict2
825
{
826
4
    mActors.erase(iterator);
827
//    mSpritesUpdated = true;
828
2
}
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
                        CAST_S32(static_cast<float>(std::min(dx1, dy1)) *
1013
                        (basicCostF));
1014
1015
                    // Set the current tile as the parent of the new tile
1016
                    newTile->parentX = curr.x;
1017
                    newTile->parentY = curr.y;
1018
1019
                    // Update Gcost and Fcost of new tile
1020
                    newTile->Gcost = Gcost;
1021
                    newTile->Fcost = Gcost + newTile->Hcost;
1022
1023
                    if (x != destX || y != destY)
1024
                    {
1025
                        // Add this tile to the open list
1026
                        newTile->whichList = mOnOpenList;
1027
                        openList.push(Location(x, y, newTile));
1028
                    }
1029
                    else
1030
                    {
1031
                        // Target location was found
1032
                        foundPath = true;
1033
                    }
1034
                }
1035
                else if (Gcost < newTile->Gcost)
1036
                {
1037
                    // Found a shorter route.
1038
                    // Update Gcost and Fcost of the new tile
1039
                    newTile->Gcost = Gcost;
1040
                    newTile->Fcost = Gcost + newTile->Hcost;
1041
1042
                    // Set the current tile as the parent of the new tile
1043
                    newTile->parentX = curr.x;
1044
                    newTile->parentY = curr.y;
1045
1046
                    // Add this tile to the open list (it's already
1047
                    // there, but this instance has a lower F score)
1048
                    openList.push(Location(x, y, newTile));
1049
                }
1050
            }
1051
        }
1052
    }
1053
1054
    // Two new values to indicate whether a tile is on the open or closed list,
1055
    // this way we don't have to clear all the values between each pathfinding.
1056
    if (mOnOpenList > UINT_MAX - 2)
1057
    {
1058
        // We reset the list memebers value.
1059
        mOnClosedList = 1;
1060
        mOnOpenList = 2;
1061
1062
        // Clean up the metaTiles
1063
        const int size = mWidth * mHeight;
1064
        for (int i = 0; i < size; ++i)
1065
            mMetaTiles[i].whichList = 0;
1066
    }
1067
    else
1068
    {
1069
        mOnClosedList += 2;
1070
        mOnOpenList += 2;
1071
    }
1072
1073
    // If a path has been found, iterate backwards using the parent locations
1074
    // to extract it.
1075
    if (foundPath)
1076
    {
1077
        int pathX = destX;
1078
        int pathY = destY;
1079
1080
        while (pathX != startX || pathY != startY)
1081
        {
1082
            // Add the new path node to the start of the path list
1083
            path.push_front(Position(pathX, pathY));
1084
1085
            // Find out the next parent
1086
            const MetaTile *const tile = &mMetaTiles[pathX + pathY * mWidth];
1087
            pathX = tile->parentX;
1088
            pathY = tile->parentY;
1089
        }
1090
    }
1091
1092
    BLOCK_END("Map::findPath")
1093
    return path;
1094
}
1095
1096
void Map::addParticleEffect(const std::string &effectFile,
1097
                            const int x, const int y,
1098
                            const int w, const int h) restrict2
1099
{
1100
    ParticleEffectData newEffect(effectFile, x, y, w, h);
1101
    mParticleEffects.push_back(newEffect);
1102
}
1103
1104
void Map::initializeParticleEffects() const restrict2
1105
{
1106
    BLOCK_START("Map::initializeParticleEffects")
1107
    if (particleEngine == nullptr)
1108
    {
1109
        BLOCK_END("Map::initializeParticleEffects")
1110
        return;
1111
    }
1112
1113
    if (config.getBoolValue("particleeffects"))
1114
    {
1115
        for (STD_VECTOR<ParticleEffectData>::const_iterator
1116
             i = mParticleEffects.begin();
1117
             i != mParticleEffects.end();
1118
             ++i)
1119
        {
1120
            // +++ add z for map particle effects?
1121
            Particle *const p = particleEngine->addEffect(
1122
                i->file,
1123
                i->x,
1124
                i->y,
1125
                0);
1126
            if ((p != nullptr) &&
1127
                i->w > 0 &&
1128
                i->h > 0)
1129
            {
1130
                p->adjustEmitterSize(i->w, i->h);
1131
            }
1132
        }
1133
    }
1134
     BLOCK_END("Map::initializeParticleEffects")
1135
}
1136
1137
void Map::addExtraLayer() restrict2
1138
{
1139
    BLOCK_START("Map::addExtraLayer")
1140
    if (mSpecialLayer == nullptr)
1141
    {
1142
        logger->log1("No special layer");
1143
        BLOCK_END("Map::addExtraLayer")
1144
        return;
1145
    }
1146
    const std::string mapFileName = pathJoin(getUserMapDirectory(),
1147
        "extralayer.txt");
1148
    logger->log("loading extra layer: " + mapFileName);
1149
    struct stat statbuf;
1150
    if (stat(mapFileName.c_str(), &statbuf) == 0 &&
1151
        S_ISREG(statbuf.st_mode))
1152
    {
1153
        std::ifstream mapFile;
1154
        mapFile.open(mapFileName.c_str(), std::ios::in);
1155
        if (!mapFile.is_open())
1156
        {
1157
            mapFile.close();
1158
            BLOCK_END("Map::addExtraLayer")
1159
            return;
1160
        }
1161
        char line[201];
1162
1163
        while (mapFile.getline(line, 200))
1164
        {
1165
            std::string buf;
1166
            std::string str = line;
1167
            if (!str.empty())
1168
            {
1169
                std::string x;
1170
                std::string y;
1171
                std::string type1;
1172
                std::string comment;
1173
                std::stringstream ss(str);
1174
                ss >> x;
1175
                ss >> y;
1176
                ss >> type1;
1177
                ss >> comment;
1178
                while (ss >> buf)
1179
                    comment.append(" ").append(buf);
1180
1181
                const int type = atoi(type1.c_str());
1182
1183
                if (comment.empty())
1184
                {
1185
                    if (type < MapItemType::ARROW_UP
1186
                        || type > MapItemType::ARROW_RIGHT)
1187
                    {
1188
                        comment = "unknown";
1189
                    }
1190
                }
1191
                if (type == MapItemType::PORTAL)
1192
                {
1193
                    updatePortalTile(comment,
1194
                        type,
1195
                        atoi(x.c_str()),
1196
                        atoi(y.c_str()),
1197
                        false);
1198
                }
1199
                else if (type == MapItemType::HOME)
1200
                {
1201
                    updatePortalTile(comment,
1202
                        type,
1203
                        atoi(x.c_str()),
1204
                        atoi(y.c_str()),
1205
                        true);
1206
                }
1207
                else
1208
                {
1209
                    addPortalTile(comment,
1210
                        type,
1211
                        atoi(x.c_str()),
1212
                        atoi(y.c_str()));
1213
                }
1214
            }
1215
        }
1216
        mapFile.close();
1217
    }
1218
    BLOCK_END("Map::addExtraLayer")
1219
}
1220
1221
void Map::saveExtraLayer() const restrict2
1222
{
1223
    if (mSpecialLayer == nullptr)
1224
    {
1225
        logger->log1("No special layer");
1226
        return;
1227
    }
1228
    const std::string mapFileName = pathJoin(getUserMapDirectory(),
1229
        "extralayer.txt");
1230
    logger->log("saving extra layer: " + mapFileName);
1231
1232
    if (mkdir_r(getUserMapDirectory().c_str()) != 0)
1233
    {
1234
        logger->log(strprintf("%s doesn't exist and can't be created! "
1235
                              "Exiting.", getUserMapDirectory().c_str()));
1236
        return;
1237
    }
1238
1239
    std::ofstream mapFile;
1240
    mapFile.open(mapFileName.c_str(), std::ios::binary);
1241
    if (!mapFile.is_open())
1242
    {
1243
        reportAlways("Error opening file for writing: %s",
1244
            mapFileName.c_str());
1245
        return;
1246
    }
1247
1248
    const int width = mSpecialLayer->mWidth;
1249
    const int height = mSpecialLayer->mHeight;
1250
1251
    for (int x = 0; x < width; x ++)
1252
    {
1253
        for (int y = 0; y < height; y ++)
1254
        {
1255
            const MapItem *restrict const item = mSpecialLayer->getTile(x, y);
1256
            if ((item != nullptr) && item->mType != MapItemType::EMPTY
1257
                && item->mType != MapItemType::HOME)
1258
            {
1259
                mapFile << x << " " << y << " "
1260
                    << CAST_S32(item->mType) << " "
1261
                    << item->mComment << std::endl;
1262
            }
1263
        }
1264
    }
1265
    mapFile.close();
1266
}
1267
1268
std::string Map::getUserMapDirectory() const restrict2
1269
{
1270
    return pathJoin(settings.serverConfigDir,
1271
        getProperty("_realfilename", std::string()));
1272
}
1273
1274
void Map::addRange(const std::string &restrict name,
1275
                   const int type,
1276
                   const int x, const int y,
1277
                   const int dx, const int dy) restrict2
1278
{
1279
    if (mObjects == nullptr)
1280
        return;
1281
1282
    mObjects->addObject(name, type, x / mapTileSize, y / mapTileSize,
1283
        dx / mapTileSize, dy / mapTileSize);
1284
}
1285
1286
void Map::addPortal(const std::string &restrict name,
1287
                    const int type,
1288
                    const int x, const int y,
1289
                    const int dx, const int dy) restrict2
1290
{
1291
    addPortalTile(name, type, (x / mapTileSize) + (dx / mapTileSize / 2),
1292
        (y / mapTileSize) + (dy / mapTileSize / 2));
1293
}
1294
1295
void Map::addPortalTile(const std::string &restrict name,
1296
                        const int type,
1297
                        const int x, const int y) restrict2
1298
{
1299
    if (mSpecialLayer != nullptr)
1300
    {
1301
        mSpecialLayer->setTile(x, y, new MapItem(type, name, x, y));
1302
        mSpecialLayer->updateCache();
1303
    }
1304
1305
    mMapPortals.push_back(new MapItem(type, name, x, y));
1306
}
1307
1308
void Map::updatePortalTile(const std::string &restrict name,
1309
                           const int type,
1310
                           const int x, const int y,
1311
                           const bool addNew) restrict2
1312
{
1313
    MapItem *restrict item = findPortalXY(x, y);
1314
    if (item != nullptr)
1315
    {
1316
        item->mComment = name;
1317
        item->setType(type);
1318
        item->mX = x;
1319
        item->mY = y;
1320
        if (mSpecialLayer != nullptr)
1321
        {
1322
            item = new MapItem(type, name, x, y);
1323
            mSpecialLayer->setTile(x, y, item);
1324
            mSpecialLayer->updateCache();
1325
        }
1326
    }
1327
    else if (addNew)
1328
    {
1329
        addPortalTile(name, type, x, y);
1330
    }
1331
}
1332
1333
MapItem *Map::findPortalXY(const int x, const int y) const restrict2
1334
{
1335
    FOR_EACH (STD_VECTOR<MapItem*>::const_iterator, it, mMapPortals)
1336
    {
1337
        if (*it == nullptr)
1338
            continue;
1339
1340
        MapItem *restrict const item = *it;
1341
        if (item->mX == x && item->mY == y)
1342
            return item;
1343
    }
1344
    return nullptr;
1345
}
1346
1347
const TileAnimation *Map::getAnimationForGid(const int gid) const restrict2
1348
{
1349
    if (mTileAnimations.empty())
1350
        return nullptr;
1351
1352
    const TileAnimationMapCIter i = mTileAnimations.find(gid);
1353
    return (i == mTileAnimations.end()) ? nullptr : i->second;
1354
}
1355
1356
void Map::setPvpMode(const int mode) restrict2
1357
{
1358
    const int oldMode = mPvp;
1359
1360
    if (mode == 0)
1361
        mPvp = 0;
1362
    else
1363
        mPvp |= mode;
1364
1365
    if (mPvp != oldMode && (localPlayer != nullptr))
1366
    {
1367
        switch (mPvp)
1368
        {
1369
            case 0:
1370
                NotifyManager::notify(NotifyTypes::PVP_OFF_GVG_OFF);
1371
                break;
1372
            case 1:
1373
                NotifyManager::notify(NotifyTypes::PVP_ON);
1374
                break;
1375
            case 2:
1376
                NotifyManager::notify(NotifyTypes::GVG_ON);
1377
                break;
1378
            case 3:
1379
                NotifyManager::notify(NotifyTypes::PVP_ON_GVG_ON);
1380
                break;
1381
            default:
1382
                NotifyManager::notify(NotifyTypes::PVP_UNKNOWN);
1383
                break;
1384
        }
1385
    }
1386
}
1387
1388
std::string Map::getObjectData(const unsigned x, const unsigned y,
1389
                               const int type) const restrict2
1390
{
1391
    if (mObjects == nullptr)
1392
        return "";
1393
1394
    MapObjectList *restrict const list = mObjects->getAt(x, y);
1395
    if (list == nullptr)
1396
        return "";
1397
1398
    STD_VECTOR<MapObject>::const_iterator it = list->objects.begin();
1399
    const STD_VECTOR<MapObject>::const_iterator it_end = list->objects.end();
1400
    while (it != it_end)
1401
    {
1402
        if ((*it).type == type)
1403
            return (*it).data;
1404
        ++ it;
1405
    }
1406
1407
    return "";
1408
}
1409
1410
void Map::indexTilesets() restrict2
1411
{
1412
    if (mTilesetsIndexed)
1413
        return;
1414
1415
    mTilesetsIndexed = true;
1416
1417
    const Tileset *restrict s = nullptr;
1418
    size_t sSz = 0;
1419
    FOR_EACH (Tilesets::const_iterator, it, mTilesets)
1420
    {
1421
        const size_t sz = (*it)->size();
1422
        if ((s == nullptr) || CAST_SIZE(s->getFirstGid()) + sSz
1423
            < CAST_SIZE((*it)->getFirstGid()) + sz)
1424
        {
1425
            s = *it;
1426
            sSz = sz;
1427
        }
1428
    }
1429
    if (s == nullptr)
1430
    {
1431
        mIndexedTilesetsSize = 0;
1432
        mIndexedTilesets = nullptr;
1433
        return;
1434
    }
1435
1436
    const int size = CAST_S32(s->getFirstGid())
1437
        + CAST_S32(s->size());
1438
    mIndexedTilesetsSize = size;
1439
    mIndexedTilesets = new Tileset*[CAST_SIZE(size)];
1440
    std::fill_n(mIndexedTilesets, size, static_cast<Tileset*>(nullptr));
1441
1442
    FOR_EACH (Tilesets::const_iterator, it, mTilesets)
1443
    {
1444
        Tileset *restrict const s2 = *it;
1445
        if (s2 != nullptr)
1446
        {
1447
            const int start = s2->getFirstGid();
1448
            const int end = start + CAST_S32(s2->size());
1449
            for (int f = start; f < end; f ++)
1450
            {
1451
                if (f < size)
1452
                    mIndexedTilesets[f] = s2;
1453
            }
1454
        }
1455
    }
1456
}
1457
1458
void Map::clearIndexedTilesets() restrict2
1459
{
1460
    if (!mTilesetsIndexed)
1461
        return;
1462
1463
    mTilesetsIndexed = false;
1464
    delete [] mIndexedTilesets;
1465
    mIndexedTilesetsSize = 0;
1466
}
1467
1468
void Map::reduce() restrict2
1469
{
1470
#ifndef USE_SDL2
1471
    if ((mFringeLayer == nullptr) ||
1472
        mOpenGL != RENDER_SOFTWARE ||
1473
        !config.getBoolValue("enableMapReduce"))
1474
    {
1475
        return;
1476
    }
1477
1478
    int cnt = 0;
1479
    for (int x = 0; x < mWidth; x ++)
1480
    {
1481
        for (int y = 0; y < mHeight; y ++)
1482
        {
1483
            bool correct(true);
1484
            bool dontHaveAlpha(false);
1485
1486
            FOR_EACH (LayersCIter, layeri, mLayers)
1487
            {
1488
                const MapLayer *restrict const layer = *layeri;
1489
                if (x >= layer->mWidth || y >= layer->mHeight)
1490
                    continue;
1491
1492
                // skip layers with flags
1493
                if (layer->mTileCondition != -1 || layer->mMask != 1)
1494
                    continue;
1495
1496
                Image *restrict const img =
1497
                    layer->mTiles[x + y * layer->mWidth].image;
1498
                if (img != nullptr)
1499
                {
1500
                    if (img->hasAlphaChannel() && img->isAlphaCalculated())
1501
                    {
1502
                        if (!img->isAlphaVisible())
1503
                        {
1504
                            dontHaveAlpha = true;
1505
                            img->setAlphaVisible(false);
1506
                        }
1507
                    }
1508
                    else if (img->mBounds.w > mapTileSize
1509
                             || img->mBounds.h > mapTileSize)
1510
                    {
1511
                        correct = false;
1512
                        img->setAlphaVisible(true);
1513
                        break;
1514
                    }
1515
                    else if (!img->isHasAlphaChannel())
1516
                    {
1517
                        dontHaveAlpha = true;
1518
                        img->setAlphaVisible(false);
1519
                    }
1520
                    else if (img->hasAlphaChannel())
1521
                    {
1522
                        const uint8_t *restrict const arr =
1523
                            img->SDLgetAlphaChannel();
1524
                        if (arr == nullptr)
1525
                            continue;
1526
1527
                        bool bad(false);
1528
                        bool stop(false);
1529
                        int width;
1530
                        const SubImage *restrict const subImg
1531
                            = dynamic_cast<SubImage*>(img);
1532
                        if (subImg != nullptr)
1533
                            width = subImg->mInternalBounds.w;
1534
                        else
1535
                            width = img->mBounds.w;
1536
1537
                        for (int f = img->mBounds.x;
1538
                             f < img->mBounds.x + img->mBounds.w; f ++)
1539
                        {
1540
                            for (int d = img->mBounds.y;
1541
                                 d < img->mBounds.y + img->mBounds.h; d ++)
1542
                            {
1543
                                const uint8_t chan = arr[f + d * width];
1544
                                if (chan != 255)
1545
                                {
1546
                                    bad = true;
1547
                                    stop = true;
1548
                                    break;
1549
                                }
1550
                            }
1551
                            if (stop)
1552
                                break;
1553
                        }
1554
                        if (!bad)
1555
                        {
1556
                            dontHaveAlpha = true;
1557
                            img->setAlphaVisible(false);
1558
                        }
1559
                        else
1560
                        {
1561
                            img->setAlphaVisible(true);
1562
                        }
1563
                    }
1564
                    img->setAlphaCalculated(true);
1565
                }
1566
            }
1567
            if (!correct || !dontHaveAlpha)
1568
                continue;
1569
1570
            Layers::reverse_iterator ri = mLayers.rbegin();
1571
            while (ri != mLayers.rend())
1572
            {
1573
                const MapLayer *restrict const layer = *ri;
1574
                if (x >= layer->mWidth || y >= layer->mHeight)
1575
                {
1576
                    ++ ri;
1577
                    continue;
1578
                }
1579
1580
                // skip layers with flags
1581
                if (layer->mTileCondition != -1 || layer->mMask != 1)
1582
                {
1583
                    ++ ri;
1584
                    continue;
1585
                }
1586
1587
                const Image *restrict img =
1588
                    layer->mTiles[x + y * layer->mWidth].image;
1589
                if ((img != nullptr) && !img->isAlphaVisible())
1590
                {   // removing all down tiles
1591
                    ++ ri;
1592
                    while (ri != mLayers.rend())
1593
                    {
1594
                        MapLayer *restrict const layer2 = *ri;
1595
                        // skip layers with flags
1596
                        if (layer2->mTileCondition != -1 || layer2->mMask != 1)
1597
                        {
1598
                            ++ ri;
1599
                            continue;
1600
                        }
1601
                        const size_t pos = CAST_SIZE(
1602
                            x + y * layer2->mWidth);
1603
                        img = layer2->mTiles[pos].image;
1604
                        if (img != nullptr)
1605
                        {
1606
                            layer2->mTiles[pos].image = nullptr;
1607
                            cnt ++;
1608
                        }
1609
                        ++ ri;
1610
                    }
1611
                    break;
1612
                }
1613
                ++ ri;
1614
            }
1615
        }
1616
    }
1617
    logger->log("tiles reduced: %d", cnt);
1618
#endif  // USE_SDL2
1619
}
1620
1621
void Map::addHeights(const MapHeights *restrict const heights) restrict2
1622
{
1623
    delete mHeights;
1624
    mHeights = heights;
1625
}
1626
1627
2
uint8_t Map::getHeightOffset(const int x, const int y) const restrict2
1628
{
1629
2
    if (mHeights == nullptr)
1630
        return 0;
1631
    return mHeights->getHeight(x, y);
1632
}
1633
1634
void Map::updateDrawLayersList() restrict2
1635
{
1636
    mDrawUnderLayers.clear();
1637
    mDrawOverLayers.clear();
1638
1639
    if (mDrawOnlyFringe)
1640
        return;
1641
1642
    LayersCIter layers = mLayers.begin();
1643
    const LayersCIter layers_end = mLayers.end();
1644
    for (; layers != layers_end; ++ layers)
1645
    {
1646
        MapLayer *const layer = *layers;
1647
        if ((layer->mMask & mMask) == 0)
1648
            continue;
1649
1650
        if (layer->isFringeLayer())
1651
        {
1652
            ++ layers;
1653
            break;
1654
        }
1655
1656
        mDrawUnderLayers.push_back(layer);
1657
    }
1658
1659
    if (mDrawLayersFlags == MapType::SPECIAL2)
1660
        return;
1661
1662
    for (; layers != layers_end; ++ layers)
1663
    {
1664
        MapLayer *const layer = *layers;
1665
        if ((layer->mMask & mMask) == 0)
1666
            continue;
1667
1668
        mDrawOverLayers.push_back(layer);
1669
    }
1670
}
1671
1672
void Map::setMask(const int mask) restrict2
1673
{
1674
    if (mask != mMask)
1675
    {
1676
        mRedrawMap = true;
1677
        mMask = mask;
1678
        updateDrawLayersList();
1679
    }
1680
}
1681
1682
void Map::setMusicFile(const std::string &restrict file) restrict2
1683
{
1684
    setProperty("music", file);
1685
}
1686
1687
void Map::addAnimation(const int gid,
1688
                       TileAnimation *restrict const animation) restrict2
1689
{
1690
    std::map<int, TileAnimation*>::iterator it = mTileAnimations.find(gid);
1691
    if (it != mTileAnimations.end())
1692
    {
1693
        logger->log("duplicate map animation with gid = %d", gid);
1694
        delete (*it).second;
1695
    }
1696
    mTileAnimations[gid] = animation;
1697
}
1698
1699
void Map::setDrawLayersFlags(const MapTypeT &restrict n) restrict2
1700
{
1701
    mDrawLayersFlags = n;
1702
    if (mDrawLayersFlags == MapType::SPECIAL3 ||
1703
        mDrawLayersFlags == MapType::SPECIAL4 ||
1704
        mDrawLayersFlags == MapType::BLACKWHITE)
1705
    {
1706
        mDrawOnlyFringe = true;
1707
    }
1708
    else
1709
    {
1710
        mDrawOnlyFringe = false;
1711
    }
1712
    updateDrawLayersList();
1713
    FOR_EACH (Layers::iterator, it, mLayers)
1714
    {
1715
        MapLayer *restrict const layer = *it;
1716
        layer->setDrawLayerFlags(mDrawLayersFlags);
1717
    }
1718
}
1719
1720
void Map::setActorsFix(const int x, const int y) restrict2
1721
{
1722
    mActorFixX = x;
1723
    mActorFixY = y;
1724
    if (mFringeLayer != nullptr)
1725
        mFringeLayer->setActorsFix(y);
1726
}
1727
1728
void Map::updateConditionLayers() restrict2
1729
{
1730
    mRedrawMap = true;
1731
1732
    FOR_EACH (LayersCIter, it, mLayers)
1733
    {
1734
        MapLayer *restrict const layer = *it;
1735
        if ((layer == nullptr) || layer->mTileCondition == -1)
1736
            continue;
1737
        layer->updateConditionTiles(mMetaTiles,
1738
            mWidth, mHeight);
1739
    }
1740
}
1741
1742
void Map::preCacheLayers() restrict2
1743
{
1744
    FOR_EACH (LayersCIter, it, mLayers)
1745
    {
1746
        MapLayer *restrict const layer = *it;
1747
        if (layer != nullptr)
1748
            layer->updateCache(mWidth, mHeight);
1749
    }
1750
}
1751
1752
int Map::calcMemoryLocal() const
1753
{
1754
    return static_cast<int>(sizeof(Map) +
1755
        mName.capacity() +
1756
        sizeof(MetaTile) * mWidth * mHeight +
1757
        sizeof(MapLayer*) * (mLayers.capacity() +
1758
        mDrawUnderLayers.capacity() +
1759
        mDrawOverLayers.capacity()) +
1760
        sizeof(Tileset*) * mTilesets.capacity() +
1761
        sizeof(Actor*) * mActors.size() +
1762
        sizeof(AmbientLayer*) * (mBackgrounds.capacity()
1763
        + mForegrounds.capacity()) +
1764
        sizeof(ParticleEffectData) * mParticleEffects.capacity() +
1765
        sizeof(MapItem) * mMapPortals.capacity() +
1766
        (sizeof(TileAnimation) + sizeof(int)) * mTileAnimations.size() +
1767
        sizeof(Tileset*) * mIndexedTilesetsSize);
1768
}
1769
1770
int Map::calcMemoryChilds(const int level) const
1771
{
1772
    int sz = 0;
1773
1774
    if (mWalkLayer != nullptr)
1775
        sz += mWalkLayer->calcMemory(level + 1);
1776
    FOR_EACH (LayersCIter, it, mLayers)
1777
    {
1778
        sz += (*it)->calcMemory(level + 1);
1779
    }
1780
    FOR_EACH (Tilesets::const_iterator, it, mTilesets)
1781
    {
1782
        sz += (*it)->calcMemory(level + 1);
1783
    }
1784
    FOR_EACH (AmbientLayerVectorCIter, it, mBackgrounds)
1785
    {
1786
        sz += (*it)->calcMemory(level + 1);
1787
    }
1788
    FOR_EACH (AmbientLayerVectorCIter, it, mForegrounds)
1789
    {
1790
        sz += (*it)->calcMemory(level + 1);
1791
    }
1792
    if (mSpecialLayer != nullptr)
1793
        mSpecialLayer->calcMemory(level + 1);
1794
    if (mTempLayer != nullptr)
1795
        mTempLayer->calcMemory(level + 1);
1796
    if (mObjects != nullptr)
1797
        mObjects->calcMemory(level + 1);
1798
    if (mHeights != nullptr)
1799
        mHeights->calcMemory(level + 1);
1800
    return sz;
1801
}
1802
1803
#ifdef USE_OPENGL
1804
int Map::getAtlasCount() const restrict2
1805
{
1806
    if (mAtlas == nullptr)
1807
        return 0;
1808
    return CAST_S32(mAtlas->atlases.size());
1809
2
}
1810
#endif  // USE_OPENGL