GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/sprite/spritedef.cpp Lines: 200 304 65.8 %
Date: 2021-03-17 Branches: 156 458 34.1 %

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/sprite/spritedef.h"
25
26
#include "configuration.h"
27
#include "settings.h"
28
29
#include "const/resources/spriteaction.h"
30
31
#include "const/resources/map/map.h"
32
33
#include "utils/checkutils.h"
34
#include "utils/foreach.h"
35
36
#include "resources/action.h"
37
#include "resources/imageset.h"
38
39
#include "resources/animation/animation.h"
40
41
#include "resources/dye/dye.h"
42
43
#include "resources/loaders/imagesetloader.h"
44
#include "resources/loaders/xmlloader.h"
45
46
#include "resources/sprite/spritereference.h"
47
48
#include "debug.h"
49
50
SpriteReference *SpriteReference::Empty = nullptr;
51
52
1296
const Action *SpriteDef::getAction(const std::string &action,
53
                                   const unsigned num) const
54
{
55
2592
    Actions::const_iterator i = mActions.find(num);
56

2592
    if (i == mActions.end() && num != 100)
57
        i = mActions.find(100);
58
59

3888
    if (i == mActions.end() || ((*i).second == nullptr))
60
        return nullptr;
61
62
1296
    const ActionMap *const actMap = (*i).second;
63
1296
    if (actMap == nullptr)
64
        return nullptr;
65
1296
    const ActionMap::const_iterator it = actMap->find(action);
66
67
1296
    if (it == actMap->end())
68
    {
69
        logger->log("Warning: no action \"%s\" defined!", action.c_str());
70
        return nullptr;
71
    }
72
73
1296
    return (*it).second;
74
}
75
76
unsigned SpriteDef::findNumber(const unsigned num) const
77
{
78
    unsigned min = 101;
79
    FOR_EACH (Actions::const_iterator, it, mActions)
80
    {
81
        const unsigned n = (*it).first;
82
        if (n >= num && n < min)
83
            min = n;
84
    }
85
    if (min == 101)
86
        return 0;
87
    return min;
88
}
89
90
1293
SpriteDef *SpriteDef::load(const std::string &animationFile,
91
                           const int variant, const bool prot)
92
{
93
    BLOCK_START("SpriteDef::load")
94
1293
    const size_t pos = animationFile.find('|');
95
2586
    std::string palettes;
96
1293
    if (pos != std::string::npos)
97
        palettes = animationFile.substr(pos + 1);
98
99
2586
    XML::Document *const doc = Loader::getXml(animationFile.substr(0, pos),
100
        UseVirtFs_true,
101
1293
        SkipError_false);
102
1293
    if (doc == nullptr)
103
        return nullptr;
104
1293
    XmlNodePtrConst rootNode = doc->rootNode();
105
106


1293
    if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "sprite"))
107
    {
108
        reportAlways("Error, failed to parse sprite %s",
109
            animationFile.c_str())
110
        const std::string errorFile = pathJoin(paths.getStringValue("sprites"),
111
            paths.getStringValue("spriteErrorFile"));
112
        BLOCK_END("SpriteDef::load")
113
        doc->decRef();
114
        if (animationFile != errorFile)
115
            return load(errorFile, 0, prot);
116
        return nullptr;
117
    }
118
119

1293
    SpriteDef *const def = new SpriteDef;
120
2586
    def->mSource = animationFile;
121
2586
    def->mProcessedFiles.insert(animationFile);
122
1293
    def->loadSprite(rootNode, variant, palettes);
123
1293
    def->substituteActions();
124
1293
    if (settings.fixDeadAnimation)
125
1293
        def->fixDeadAction();
126
1293
    if (prot)
127
    {
128
        def->incRef();
129
        def->mProtected = true;
130
    }
131
1293
    doc->decRef();
132
    BLOCK_END("SpriteDef::load")
133
    return def;
134
}
135
136
1293
void SpriteDef::fixDeadAction()
137
{
138
3879
    FOR_EACH (ActionsIter, it, mActions)
139
    {
140
1293
        ActionMap *const d = (*it).second;
141
1293
        if (d == nullptr)
142
            continue;
143
1293
        const ActionMap::iterator i = d->find(SpriteAction::DEAD);
144
1293
        const ActionMap::iterator i2 = d->find(SpriteAction::STAND);
145
        // search dead action and check what it not same with stand action
146

5172
        if (i != d->end() &&
147

7758
            i->second != nullptr &&
148
5172
            (i2 == d->end() || i->second != i2->second))
149
        {
150
            (i->second)->setLastFrameDelay(0);
151
        }
152
    }
153
1293
}
154
155
37497
void SpriteDef::substituteAction(const std::string &restrict complete,
156
                                 const std::string &restrict with)
157
{
158
187485
    FOR_EACH (ActionsConstIter, it, mActions)
159
    {
160
37497
        ActionMap *const d = (*it).second;
161

37497
        if (reportTrue(d == nullptr))
162
            continue;
163
74994
        if (d->find(complete) == d->end())
164
        {
165
36202
            const ActionMap::iterator i = d->find(with);
166
36202
            if (i != d->end())
167
72404
                (*d)[complete] = i->second;
168
        }
169
    }
170
37497
}
171
172
1293
void SpriteDef::substituteActions()
173
{
174
1293
    substituteAction(SpriteAction::STAND, SpriteAction::DEFAULT);
175
1293
    substituteAction(SpriteAction::MOVE, SpriteAction::STAND);
176
1293
    substituteAction(SpriteAction::ATTACK, SpriteAction::STAND);
177
1293
    substituteAction(SpriteAction::CAST, SpriteAction::ATTACK);
178
1293
    substituteAction(SpriteAction::SIT, SpriteAction::STAND);
179
1293
    substituteAction(SpriteAction::SITTOP, SpriteAction::SIT);
180
1293
    substituteAction(SpriteAction::DEAD, SpriteAction::STAND);
181
1293
    substituteAction(SpriteAction::SPAWN, SpriteAction::STAND);
182
1293
    substituteAction(SpriteAction::FLY, SpriteAction::MOVE);
183
1293
    substituteAction(SpriteAction::SWIM, SpriteAction::MOVE);
184
1293
    substituteAction(SpriteAction::RIDE, SpriteAction::MOVE);
185
1293
    substituteAction(SpriteAction::STANDSKY, SpriteAction::STAND);
186
1293
    substituteAction(SpriteAction::STANDWATER, SpriteAction::STAND);
187
1293
    substituteAction(SpriteAction::STANDRIDE, SpriteAction::STAND);
188
1293
    substituteAction(SpriteAction::SITSKY, SpriteAction::SIT);
189
1293
    substituteAction(SpriteAction::SITWATER, SpriteAction::SIT);
190
1293
    substituteAction(SpriteAction::SITRIDE, SpriteAction::SIT);
191
1293
    substituteAction(SpriteAction::ATTACKSKY, SpriteAction::ATTACK);
192
1293
    substituteAction(SpriteAction::ATTACKWATER, SpriteAction::ATTACK);
193
1293
    substituteAction(SpriteAction::ATTACKRIDE, SpriteAction::ATTACK);
194
1293
    substituteAction(SpriteAction::CASTSKY, SpriteAction::CAST);
195
1293
    substituteAction(SpriteAction::CASTWATER, SpriteAction::CAST);
196
1293
    substituteAction(SpriteAction::CASTRIDE, SpriteAction::CAST);
197
1293
    substituteAction(SpriteAction::SPAWNSKY, SpriteAction::SPAWN);
198
1293
    substituteAction(SpriteAction::SPAWNWATER, SpriteAction::SPAWN);
199
1293
    substituteAction(SpriteAction::SPAWNRIDE, SpriteAction::SPAWN);
200
1293
    substituteAction(SpriteAction::DEADSKY, SpriteAction::DEAD);
201
1293
    substituteAction(SpriteAction::DEADWATER, SpriteAction::DEAD);
202
1293
    substituteAction(SpriteAction::DEADRIDE, SpriteAction::DEAD);
203
1293
}
204
205
1293
void SpriteDef::loadSprite(XmlNodeConstPtr spriteNode,
206
                           const int variant,
207
                           const std::string &palettes)
208
{
209
    BLOCK_START("SpriteDef::loadSprite")
210
1293
    if (spriteNode == nullptr)
211
    {
212
        BLOCK_END("SpriteDef::loadSprite")
213
        return;
214
    }
215
    // Get the variant
216
1293
    const int variantCount = XML::getProperty(spriteNode, "variants", 0);
217
1293
    int variant_offset = 0;
218
219
1293
    if (variantCount > 0 && variant < variantCount)
220
    {
221
        variant_offset = variant * XML::getProperty(spriteNode,
222
            "variant_offset",
223
            0);
224
    }
225
226
7766
    for_each_xml_child_node(node, spriteNode)
227
    {
228
6473
        if (xmlNameEqual(node, "imageset"))
229
1293
            loadImageSet(node, palettes);
230
5180
        else if (xmlNameEqual(node, "action"))
231
1297
            loadAction(node, variant_offset);
232
3883
        else if (xmlNameEqual(node, "include"))
233
            includeSprite(node, variant);
234
    }
235
    BLOCK_END("SpriteDef::loadSprite")
236
}
237
238
1293
void SpriteDef::loadImageSet(XmlNodeConstPtr node,
239
                             const std::string &palettes)
240
{
241
6465
    const std::string name = XML::getProperty(node, "name", "");
242
243
    // We don't allow redefining image sets. This way, an included sprite
244
    // definition will use the already loaded image set with the same name.
245
3879
    if (mImageSets.find(name) != mImageSets.end())
246
        return;
247
248
1293
    const int width = XML::getProperty(node, "width", 0);
249
1293
    const int height = XML::getProperty(node, "height", 0);
250

6465
    std::string imageSrc = XML::getProperty(node, "src", "");
251
1293
    Dye::instantiate(imageSrc, palettes);
252
253
    ImageSet *const imageSet = Loader::getImageSet(imageSrc,
254
1293
        width, height);
255
256
1293
    if (imageSet == nullptr)
257
    {
258
        reportAlways("%s: Couldn't load imageset: %s",
259
            mSource.c_str(),
260
            imageSrc.c_str())
261
        return;
262
    }
263
264
2586
    imageSet->setOffsetX(XML::getProperty(node, "offsetX", 0));
265
2586
    imageSet->setOffsetY(XML::getProperty(node, "offsetY", 0));
266
1293
    mImageSets[name] = imageSet;
267
}
268
269
1297
const ImageSet *SpriteDef::getImageSet(const std::string &imageSetName) const
270
{
271
2594
    const ImageSetCIterator si = mImageSets.find(imageSetName);
272
2594
    if (si == mImageSets.end())
273
    {
274
        reportAlways("%s: Imageset \"%s\" not defined in %s",
275
            mSource.c_str(),
276
            imageSetName.c_str(),
277
            mIdPath.c_str())
278
        return nullptr;
279
    }
280
1297
    return si->second;
281
}
282
283
1297
void SpriteDef::loadAction(XmlNodeConstPtr node,
284
                           const int variant_offset)
285
{
286
1297
    if (node == nullptr)
287
        return;
288
289
6485
    const std::string actionName = XML::getProperty(node, "name", "");
290

6485
    const std::string imageSetName = XML::getProperty(node, "imageset", "");
291
1297
    const unsigned hp = XML::getProperty(node, "hp", 100);
292
1297
    const ImageSet *const imageSet = getImageSet(imageSetName);
293
294
1297
    if (actionName == SpriteAction::INVALID)
295
    {
296
        reportAlways("%s: Unknown action \"%s\" defined in %s",
297
            mSource.c_str(),
298
            actionName.c_str(),
299
            mIdPath.c_str())
300
        return;
301
    }
302
1297
    Action *const action = new Action(actionName);
303
2594
    action->setNumber(hp);
304
1297
    addAction(hp, actionName, action);
305
306
    // dirty hack to fix bad resources in tmw server
307
1297
    if (actionName == "attack_stab")
308
    {
309
        reportAlways("Found legacy attribute attack_stab in animation")
310
        addAction(hp, "attack", action);
311
    }
312
313
    // When first action, set it as default direction.
314
    // i here always correct, because hp was added above.
315
3891
    const Actions::const_iterator i = mActions.find(hp);
316
2594
    if ((*i).second->size() == 1)
317
1293
        addAction(hp, SpriteAction::DEFAULT, action);
318
319
    // Load animations
320
5188
    for_each_xml_child_node(animationNode, node)
321
    {
322

3891
        if (xmlNameEqual(animationNode, "animation"))
323
1297
            loadAnimation(animationNode, action, imageSet, variant_offset);
324
    }
325
}
326
327
1297
void SpriteDef::loadAnimation(XmlNodeConstPtr animationNode,
328
                              Action *const action,
329
                              const ImageSet *const imageSet0,
330
                              const int variant_offset) const
331
{
332
2594
    if (action == nullptr ||
333
2594
        imageSet0 == nullptr ||
334
        animationNode == nullptr)
335
    {
336
        return;
337
    }
338
339
    const std::string directionName =
340
6485
        XML::getProperty(animationNode, "direction", "");
341
    const SpriteDirection::Type directionType
342
1297
        = makeSpriteDirection(directionName);
343
344
1297
    if (directionType == SpriteDirection::INVALID)
345
    {
346
        reportAlways("%s: Unknown direction \"%s\" used in %s",
347
            mSource.c_str(),
348
            directionName.c_str(),
349
            mIdPath.c_str())
350
        return;
351
    }
352
353
1297
    Animation *const animation = new Animation(directionName);
354
1297
    action->setAnimation(directionType, animation);
355
356
    // Get animation frames
357
5228
    for_each_xml_child_node(frameNode, animationNode)
358
    {
359
        const int delay = XML::getIntProperty(
360
3931
            frameNode, "delay", 0, 0, 100000);
361
        const std::string imageSetName = XML::getProperty(frameNode,
362
            "imageset",
363

19655
            "");
364
3931
        const ImageSet *imageSet = imageSet0;
365
3931
        if (!imageSetName.empty())
366
        {
367
            imageSet = getImageSet(imageSetName);
368
            if (imageSet == nullptr)
369
                imageSet = imageSet0;
370
        }
371
3931
        const int offsetX = XML::getProperty(frameNode, "offsetX", 0)
372
7862
            + imageSet->getOffsetX() - imageSet->getWidth() / 2
373
3931
            + mapTileSize / 2;
374
3931
        const int offsetY = XML::getProperty(frameNode, "offsetY", 0)
375
7862
            + imageSet->getOffsetY() - imageSet->getHeight() + mapTileSize;
376
3931
        const int rand = XML::getIntProperty(frameNode, "rand", 100, 0, 100);
377
378

3931
        if (xmlNameEqual(frameNode, "frame"))
379
        {
380
21
            const int index = XML::getProperty(frameNode, "index", -1);
381
382
21
            if (index < 0)
383
            {
384
                reportAlways(
385
                    "%s: No valid value for 'index' at direction '%s'",
386
                    mSource.c_str(),
387
                    directionName.c_str())
388
                continue;
389
            }
390
391
21
            Image *const img = imageSet->get(index + variant_offset);
392
21
            if (img == nullptr)
393
            {
394
                reportAlways("%s: No image at index %d at direction '%s'",
395
                    mSource.c_str(),
396
                    index + variant_offset,
397
                    directionName.c_str())
398
                continue;
399
            }
400
401
21
            animation->addFrame(img, delay, offsetX, offsetY, rand);
402
        }
403

3910
        else if (xmlNameEqual(frameNode, "sequence"))
404
        {
405
1290
            const int start = XML::getProperty(frameNode, "start", -1);
406
1290
            const int end = XML::getProperty(frameNode, "end", -1);
407

6450
            const std::string value = XML::getProperty(frameNode, "value", "");
408
            const int repeat = XML::getIntProperty(
409
1290
                frameNode, "repeat", 1, 0, 100);
410
411
1290
            if (repeat < 1)
412
            {
413
                reportAlways("%s: No valid value for 'repeat' at direction %s",
414
                    mSource.c_str(),
415
                    directionName.c_str())
416
                continue;
417
            }
418
419
1290
            if (value.empty())
420
            {
421

1290
                if (addSequence(start, end, delay, offsetX, offsetY,
422
                    variant_offset, repeat, rand, imageSet, animation))
423
                {
424
                    continue;
425
                }
426
            }
427
            else
428
            {
429
                StringVect vals;
430
                splitToStringVector(vals, value, ',');
431
                FOR_EACH (StringVectCIter, it, vals)
432
                {
433
                    const std::string str = *it;
434
                    const size_t idx = str.find('-');
435
                    if (str == "p")
436
                    {
437
                        animation->addPause(delay, rand);
438
                    }
439
                    else if (idx != std::string::npos)
440
                    {
441
                        const int v1 = atoi(str.substr(0, idx).c_str());
442
                        const int v2 = atoi(str.substr(idx + 1).c_str());
443
                        addSequence(v1, v2, delay, offsetX, offsetY,
444
                            variant_offset, repeat, rand, imageSet, animation);
445
                    }
446
                    else
447
                    {
448
                        Image *const img = imageSet->get(atoi(
449
                            str.c_str()) + variant_offset);
450
                        if (img != nullptr)
451
                        {
452
                            animation->addFrame(img, delay,
453
                                offsetX, offsetY, rand);
454
                        }
455
                    }
456
                }
457
            }
458
        }
459

2620
        else if (xmlNameEqual(frameNode, "pause"))
460
        {
461
            animation->addPause(delay, rand);
462
        }
463

2620
        else if (xmlNameEqual(frameNode, "end"))
464
        {
465
            animation->addTerminator(rand);
466
        }
467

2620
        else if (xmlNameEqual(frameNode, "jump"))
468
        {
469
            animation->addJump(XML::getProperty(
470
                frameNode, "action", ""), rand);
471
        }
472

2620
        else if (xmlNameEqual(frameNode, "label"))
473
        {
474

10
            const std::string name = XML::getProperty(frameNode, "name", "");
475
2
            if (!name.empty())
476
2
                animation->addLabel(name);
477
        }
478

2618
        else if (xmlNameEqual(frameNode, "goto"))
479
        {
480

20
            const std::string name = XML::getProperty(frameNode, "label", "");
481
4
            if (!name.empty())
482
4
                animation->addGoto(name, rand);
483
        }
484
    }  // for frameNode
485
}
486
487
void SpriteDef::includeSprite(XmlNodeConstPtr includeNode, const int variant)
488
{
489
    std::string filename = XML::getProperty(includeNode, "file", "");
490
491
    if (filename.empty())
492
        return;
493
    filename = pathJoin(paths.getStringValue("sprites"), filename);
494
495
    if (mProcessedFiles.find(filename) != mProcessedFiles.end())
496
    {
497
        reportAlways("%s: Tried to include %s which already is included.",
498
            mSource.c_str(),
499
            filename.c_str())
500
        return;
501
    }
502
    mProcessedFiles.insert(filename);
503
504
    XML::Document *const doc = Loader::getXml(filename,
505
        UseVirtFs_true,
506
        SkipError_false);
507
    if (doc == nullptr)
508
        return;
509
    XmlNodeConstPtr rootNode = doc->rootNode();
510
511
    if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "sprite"))
512
    {
513
        reportAlways("%s: No sprite root node in %s",
514
            mSource.c_str(),
515
            filename.c_str())
516
        doc->decRef();
517
        return;
518
    }
519
520
    loadSprite(rootNode, variant, std::string());
521
    doc->decRef();
522
}
523
524
7758
SpriteDef::~SpriteDef()
525
{
526
    // Actions are shared, so ensure they are deleted only once.
527
2586
    std::set<Action*> actions;
528
3879
    FOR_EACH (Actions::iterator, i, mActions)
529
    {
530
5172
        FOR_EACHP (ActionMap::iterator, it, (*i).second)
531
77580
            actions.insert(it->second);
532
2586
        delete (*i).second;
533
    }
534
535
2586
    FOR_EACH (std::set<Action*>::const_iterator, i, actions)
536
1297
        delete *i;
537
538
2586
    mActions.clear();
539
540
3879
    FOR_EACH (ImageSetIterator, i, mImageSets)
541
    {
542
1293
        if (i->second != nullptr)
543
        {
544
1293
            i->second->decRef();
545
1293
            i->second = nullptr;
546
        }
547
    }
548
2586
    mImageSets.clear();
549
2586
}
550
551
1297
SpriteDirection::Type SpriteDef::makeSpriteDirection(const std::string
552
                                                     &direction)
553
{
554

2594
    if (direction.empty() || direction == "default")
555
        return SpriteDirection::DEFAULT;
556
    else if (direction == "up")
557
        return SpriteDirection::UP;
558
    else if (direction == "left")
559
        return SpriteDirection::LEFT;
560
    else if (direction == "right")
561
        return SpriteDirection::RIGHT;
562
    else if (direction == "down")
563
        return SpriteDirection::DOWN;
564
    else if (direction == "upleft")
565
        return SpriteDirection::UPLEFT;
566
    else if (direction == "upright")
567
        return SpriteDirection::UPRIGHT;
568
    else if (direction == "downleft")
569
        return SpriteDirection::DOWNLEFT;
570
    else if (direction == "downright")
571
        return SpriteDirection::DOWNRIGHT;
572
    else
573
        return SpriteDirection::INVALID;
574
}
575
576
2590
void SpriteDef::addAction(const unsigned hp, const std::string &name,
577
                          Action *const action)
578
{
579
7770
    const Actions::const_iterator i = mActions.find(hp);
580
5180
    if (i == mActions.end())
581
2586
        mActions[hp] = new ActionMap;
582
583
2590
    (*mActions[hp])[name] = action;
584
2590
}
585
586
1290
bool SpriteDef::addSequence(const int start,
587
                            const int end,
588
                            const int delay,
589
                            const int offsetX,
590
                            const int offsetY,
591
                            const int variant_offset,
592
                            int repeat,
593
                            const int rand,
594
                            const ImageSet *const imageSet,
595
                            Animation *const animation) const
596
{
597
1290
    if ((imageSet == nullptr) || (animation == nullptr))
598
        return true;
599
600
1290
    if (start < 0 || end < 0)
601
    {
602
        reportAlways("%s: No valid value for 'start' or 'end'",
603
            mSource.c_str())
604
        return true;
605
    }
606
607
1290
    if (start <= end)
608
    {
609
3870
        while (repeat > 0)
610
        {
611
            int pos = start;
612
44290
            while (end >= pos)
613
            {
614
21500
                Image *const img = imageSet->get(pos + variant_offset);
615
616
21500
                if (img == nullptr)
617
                {
618
                    reportAlways("%s: No image at index %d",
619
                        mSource.c_str(),
620
                        pos + variant_offset)
621
                    pos ++;
622
                    continue;
623
                }
624
625
                animation->addFrame(img, delay,
626
21500
                    offsetX, offsetY, rand);
627
21500
                pos ++;
628
            }
629
1290
            repeat --;
630
        }
631
    }
632
    else
633
    {
634
        while (repeat > 0)
635
        {
636
            int pos = start;
637
            while (end <= pos)
638
            {
639
                Image *const img = imageSet->get(pos + variant_offset);
640
641
                if (img == nullptr)
642
                {
643
                    reportAlways("%s: No image at index %d",
644
                        mSource.c_str(),
645
                        pos + variant_offset)
646
                    pos ++;
647
                    continue;
648
                }
649
650
                animation->addFrame(img, delay,
651
                    offsetX, offsetY, rand);
652
                pos --;
653
            }
654
            repeat --;
655
        }
656
    }
657
    return false;
658
}
659
660
int SpriteDef::calcMemoryLocal() const
661
{
662
    int sz = static_cast<int>(sizeof(SpriteDef) +
663
        sizeof(ImageSets) +
664
        sizeof(Actions) +
665
        sizeof(std::set<std::string>)) +
666
        Resource::calcMemoryLocal();
667
    FOR_EACH (std::set<std::string>::const_iterator, it, mProcessedFiles)
668
    {
669
        sz += static_cast<int>((*it).capacity());
670
    }
671
    return sz;
672
}
673
674
int SpriteDef::calcMemoryChilds(const int level) const
675
{
676
    int sz = 0;
677
    FOR_EACH (ImageSets::const_iterator, it, mImageSets)
678
    {
679
        sz += static_cast<int>((*it).first.capacity());
680
        const ImageSet *const imageSet = (*it).second;
681
        sz += imageSet->calcMemory(level + 1);
682
    }
683
    FOR_EACH (ActionsCIter, it, mActions)
684
    {
685
        sz += sizeof(unsigned);
686
        const ActionMap *const actionMap = (*it).second;
687
        FOR_EACHP (ActionMap::const_iterator, it2, actionMap)
688
        {
689
            sz += static_cast<int>((*it2).first.capacity());
690
            const Action *const action = (*it2).second;
691
            sz += action->calcMemory(level + 1);
692
        }
693
    }
694
    return sz;
695

3
}