GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/map/map.cpp Lines: 57 767 7.4 %
Date: 2021-03-17 Branches: 53 978 5.4 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2019  The ManaPlus Developers
6
 *  Copyright (C) 2019-2021  Andrei Karas
7
 *
8
 *  This file is part of The ManaPlus Client.
9
 *
10
 *  This program is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU General Public License as published by
12
 *  the Free Software Foundation; either version 2 of the License, or
13
 *  any later version.
14
 *
15
 *  This program is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU General Public License
21
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
#include "resources/map/map.h"
25
26
#include "configuration.h"
27
#include "render/graphics.h"
28
#include "notifymanager.h"
29
#include "settings.h"
30
31
#include "being/localplayer.h"
32
33
#include "enums/resources/notifytypes.h"
34
35
#include "enums/resources/map/blockmask.h"
36
#include "enums/resources/map/mapitemtype.h"
37
38
#include "fs/mkdir.h"
39
40
#include "gui/userpalette.h"
41
42
#include "particle/particle.h"
43
44
#include "resources/ambientlayer.h"
45
46
#ifdef USE_OPENGL
47
#include "resources/atlas/atlasresource.h"
48
#endif  // USE_OPENGL
49
50
#include "resources/image/subimage.h"
51
52
#include "resources/loaders/imageloader.h"
53
54
#include "resources/map/location.h"
55
#include "resources/map/mapheights.h"
56
#include "resources/map/mapobjectlist.h"
57
#include "resources/map/maplayer.h"
58
#include "resources/map/mapitem.h"
59
#include "resources/map/objectslayer.h"
60
#include "resources/map/speciallayer.h"
61
#include "resources/map/tileanimation.h"
62
#include "resources/map/tileset.h"
63
#include "resources/map/walklayer.h"
64
65
#ifdef USE_OPENGL
66
#include "render/renderers.h"
67
#endif  // USE_OPENGL
68
69
#include "utils/checkutils.h"
70
#include "utils/delete2.h"
71
#include "utils/dtor.h"
72
#include "utils/foreach.h"
73
#include "utils/timer.h"
74
75
#include <sys/stat.h>
76
77
#include <climits>
78
#include <fstream>
79
#include <queue>
80
81
#include "debug.h"
82
83
class ActorFunctuator final
84
{
85
    public:
86
        A_DEFAULT_COPY(ActorFunctuator)
87
88
        bool operator()(const Actor *const a,
89
                        const Actor *const b) const
90
        {
91
            if ((a == nullptr) || (b == nullptr))
92
                return false;
93
            return a->getSortPixelY() < b->getSortPixelY();
94
        }
95
} actorCompare;
96
97
47
Map::Map(const std::string &name,
98
         const int width,
99
         const int height,
100
         const int tileWidth,
101
47
         const int tileHeight) :
102
    Properties(),
103
    mWidth(width), mHeight(height),
104
    mTileWidth(tileWidth), mTileHeight(tileHeight),
105
    mMaxTileHeight(height),
106

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

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

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

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

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

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

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

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

1363
    mClear(false)
169
{
170

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

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

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

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

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

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


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




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