GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/sprite/animatedsprite.cpp Lines: 82 197 41.6 %
Date: 2021-03-17 Branches: 70 210 33.3 %

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/animatedsprite.h"
25
26
#include "const/resources/spriteaction.h"
27
28
#include "render/graphics.h"
29
30
#include "resources/action.h"
31
#include "resources/delayedmanager.h"
32
33
#include "resources/animation/animation.h"
34
35
#include "resources/image/image.h"
36
37
#include "resources/loaders/spritedefloader.h"
38
39
#include "resources/resourcemanager/resourcemanager.h"
40
41
#include "resources/sprite/animationdelayload.h"
42
43
#include "utils/delete2.h"
44
#include "utils/likely.h"
45
#include "utils/mrand.h"
46
47
#include "debug.h"
48
49
bool AnimatedSprite::mEnableCache = false;
50
51
1293
AnimatedSprite::AnimatedSprite(SpriteDef *restrict const sprite) :
52
    mDirection(SpriteDirection::DOWN),
53
    mLastTime(0),
54
    mFrameIndex(0),
55
    mFrameTime(0),
56
    mSprite(sprite),
57
    mAction(nullptr),
58
    mAnimation(nullptr),
59
    mFrame(nullptr),
60
    mNumber(100),
61
    mNumber1(100),
62
    mDelayLoad(nullptr),
63
2586
    mTerminated(false)
64
{
65
1293
    mAlpha = 1.0F;
66
67
    // Take possession of the sprite
68
    if (mSprite != nullptr)
69

1293
        mSprite->incRef();
70
}
71
72
1293
AnimatedSprite *AnimatedSprite::load(const std::string &restrict filename,
73
                                     const int variant)
74
{
75
    SpriteDef *restrict const s = Loader::getSprite(
76
1293
        filename, variant);
77
1293
    if (s == nullptr)
78
        return nullptr;
79
2586
    AnimatedSprite *restrict const as = new AnimatedSprite(s);
80
#ifdef DEBUG_ANIMATIONS
81
    as->setSpriteName(filename);
82
#endif  // DEBUG_ANIMATIONS
83
84
1293
    as->play(SpriteAction::STAND);
85
1293
    s->decRef();
86
1293
    return as;
87
}
88
89
AnimatedSprite *AnimatedSprite::delayedLoad(const std::string &restrict
90
                                            filename,
91
                                            const int variant)
92
{
93
    if (!mEnableCache)
94
        return load(filename, variant);
95
    Resource *restrict const res = ResourceManager::getFromCache(
96
        filename, variant);
97
    if (res != nullptr)
98
    {
99
        res->decRef();
100
        return load(filename, variant);
101
    }
102
103
    AnimatedSprite *restrict const as = new AnimatedSprite(nullptr);
104
#ifdef DEBUG_ANIMATIONS
105
    as->setSpriteName(filename);
106
#endif  // DEBUG_ANIMATIONS
107
108
    as->play(SpriteAction::STAND);
109
    as->setDelayLoad(filename, variant);
110
    return as;
111
}
112
113
AnimatedSprite *AnimatedSprite::clone(const AnimatedSprite *restrict const
114
                                      anim)
115
{
116
    if (anim == nullptr)
117
        return nullptr;
118
    AnimatedSprite *restrict const sprite = new AnimatedSprite(anim->mSprite);
119
#ifdef DEBUG_ANIMATIONS
120
    sprite->setSpriteName(anim->getSpriteName());
121
#endif  // DEBUG_ANIMATIONS
122
123
    sprite->play(SpriteAction::STAND);
124
    return sprite;
125
}
126
127
3879
AnimatedSprite::~AnimatedSprite()
128
{
129
1293
    if (mSprite != nullptr)
130
    {
131
1293
        mSprite->decRef();
132
1293
        mSprite = nullptr;
133
    }
134
1293
    if (mDelayLoad != nullptr)
135
    {
136
        mDelayLoad->clearSprite();
137
        DelayedManager::removeDelayLoad(mDelayLoad);
138
        delete2(mDelayLoad)
139
    }
140
2586
}
141
142
bool AnimatedSprite::reset() restrict2
143
{
144
    const bool ret = mFrameIndex !=0 ||
145
        mFrameTime != 0 ||
146
1294
        mLastTime != 0;
147
148
1294
    mFrameIndex = 0;
149
1294
    mFrameTime = 0;
150
1294
    mLastTime = 0;
151
152

1294
    if (mAnimation != nullptr)
153
1294
        mFrame = &mAnimation->mFrames[0];
154
    else
155
        mFrame = nullptr;
156
    return ret;
157
}
158
159
1296
bool AnimatedSprite::play(const std::string &restrict spriteAction) restrict2
160
{
161
1296
    if (mSprite == nullptr)
162
    {
163
        if (mDelayLoad == nullptr)
164
            return false;
165
        mDelayLoad->setAction(spriteAction);
166
        return true;
167
    }
168
169
1296
    const Action *const action = mSprite->getAction(spriteAction, mNumber);
170
1296
    if (action == nullptr)
171
        return false;
172
173
1296
    mAction = action;
174
1296
    const Animation *const animation = mAction->getAnimation(mDirection);
175
176

2592
    if ((animation != nullptr) &&
177

2590
        animation != mAnimation &&
178
1294
        animation->getLength() > 0)
179
    {
180
1294
        mAnimation = animation;
181
        reset();
182
183
        return true;
184
    }
185
186
    return false;
187
}
188
189
16
bool AnimatedSprite::update(const int time) restrict2
190
{
191
    // Avoid freaking out at first frame or when tick_time overflows
192

16
    if (A_UNLIKELY(time < mLastTime || mLastTime == 0))
193
3
        mLastTime = time;
194
195
    // If not enough time has passed yet, do nothing
196

16
    if (time <= mLastTime || (mAnimation == nullptr))
197
        return false;
198
199
12
    const unsigned int dt = time - mLastTime;
200
12
    mLastTime = time;
201
202
12
    const Animation *restrict const animation = mAnimation;
203
12
    const Frame *restrict const frame = mFrame;
204
205
12
    if (A_UNLIKELY(!updateCurrentAnimation(dt)))
206
    {
207
        // Animation finished, reset to default
208
        play(SpriteAction::STAND);
209
        mTerminated = true;
210
    }
211
212
    // Make sure something actually changed
213

12
    return animation != mAnimation || frame != mFrame;
214
}
215
216
12
bool AnimatedSprite::updateCurrentAnimation(const unsigned int time) restrict2
217
{
218
    // move code from Animation::isTerminator(*mFrame)
219

24
    if (mFrame == nullptr ||
220
24
        mAnimation == nullptr ||
221
12
        (mFrame->image == nullptr && mFrame->type == FrameType::ANIMATION))
222
    {
223
        return false;
224
    }
225
226
12
    mFrameTime += time;
227
228
35
    while ((mFrameTime > CAST_U32(mFrame->delay) &&
229

38
           mFrame->delay > 0) ||
230
15
           (mFrame->type != FrameType::ANIMATION &&
231
           mFrame->type != FrameType::PAUSE))
232
    {
233
11
        bool fail(true);
234
11
        mFrameTime -= CAST_U32(mFrame->delay);
235
11
        mFrameIndex++;
236
237
22
        if (mFrameIndex >= CAST_U32(mAnimation->getLength()))
238
            mFrameIndex = 0;
239
240
22
        mFrame = &mAnimation->mFrames[mFrameIndex];
241

12
        if (mFrame->type == FrameType::LABEL &&
242
2
            !mFrame->nextAction.empty())
243
        {
244
            fail = false;
245
        }
246

12
        else if (mFrame->type == FrameType::GOTO &&
247
4
                 !mFrame->nextAction.empty())
248
        {
249
2
            const int rand = mFrame->rand;
250

4
            if (rand == 100 ||
251
                ((rand != 0) && rand >= mrand() % 100))
252
            {
253
11
                for (size_t i = 0; i < mAnimation->getLength(); i ++)
254
                {
255
                    const Frame *restrict const frame =
256
8
                        &mAnimation->mFrames[i];
257

5
                    if (frame->type == FrameType::LABEL &&
258
1
                        mFrame->nextAction == frame->nextAction)
259
                    {
260
1
                        mFrameIndex = CAST_U32(i);
261
1
                        if (mFrameIndex >= CAST_U32(
262
2
                            mAnimation->getLength()))
263
                        {
264
                            mFrameIndex = 0;
265
                        }
266
267
2
                        mFrame = &mAnimation->mFrames[mFrameIndex];
268
269
1
                        fail = false;
270
1
                        break;
271
                    }
272
                }
273
            }
274
            else
275
            {
276
                fail = false;
277
            }
278
        }
279

8
        else if (mFrame->type == FrameType::JUMP &&
280
                 !mFrame->nextAction.empty())
281
        {
282
            const int rand = mFrame->rand;
283
            if (rand == 100 ||
284
                ((rand != 0) && rand >= mrand() % 100))
285
            {
286
                play(mFrame->nextAction);
287
                return true;
288
            }
289
        }
290
        // copy code from Animation::isTerminator(*mFrame)
291

8
        else if ((mFrame->image == nullptr) &&
292
                 mFrame->type == FrameType::ANIMATION)
293
        {
294
            const int rand = mFrame->rand;
295
            if (rand == 100 ||
296
                ((rand != 0) && rand >= mrand() % 100))
297
            {
298
                mAnimation = nullptr;
299
                mFrame = nullptr;
300
                return false;
301
            }
302
        }
303
        else
304
        {
305
8
            const int rand = mFrame->rand;
306

9
            if (rand == 100 ||
307
2
                mFrameIndex >= CAST_U32(mAnimation->getLength()))
308
            {
309
                fail = false;
310
            }
311
            else
312
            {
313

1
                if ((rand != 0) && mrand() % 100 <= rand)
314
                    fail = false;
315
            }
316
        }
317
11
        if (fail)
318
        {
319
1
            if (mFrame != nullptr)
320
1
                mFrameTime = mFrame->delay + 1;
321
            else
322
                mFrameTime ++;
323
        }
324
    }
325
    return true;
326
}
327
328
void AnimatedSprite::draw(Graphics *restrict const graphics,
329
                          const int posX,
330
                          const int posY) const restrict2
331
{
332
    FUNC_BLOCK("AnimatedSprite::draw", 1)
333
    if ((mFrame == nullptr) || (mFrame->image == nullptr))
334
        return;
335
336
    Image *restrict const image = mFrame->image;
337
    image->setAlpha(mAlpha);
338
    graphics->drawImage(image,
339
        posX + mFrame->offsetX, posY + mFrame->offsetY);
340
}
341
342
void AnimatedSprite::drawRescaled(Graphics *restrict const graphics,
343
                                  const int posX,
344
                                  const int posY,
345
                                  const int dx,
346
                                  const int dy) const restrict2
347
{
348
    if (mFrame == nullptr ||
349
        mFrame->image == nullptr)
350
    {
351
        return;
352
    }
353
354
    Image *restrict const image = mFrame->image;
355
    image->setAlpha(mAlpha);
356
    graphics->drawRescaledImage(image,
357
        posX + mFrame->offsetX,
358
        posY + mFrame->offsetY,
359
        dx,
360
        dy);
361
}
362
363
void AnimatedSprite::drawRaw(Graphics *restrict const graphics,
364
                             const int posX,
365
                             const int posY) const restrict2
366
{
367
    if ((mFrame == nullptr) || (mFrame->image == nullptr))
368
        return;
369
370
    Image *restrict const image = mFrame->image;
371
    image->setAlpha(mAlpha);
372
    graphics->drawImage(image,
373
        posX,
374
        posY);
375
}
376
377
bool AnimatedSprite::setSpriteDirection(const SpriteDirection::Type direction)
378
                                        restrict2
379
{
380
    if (mDirection != direction)
381
    {
382
        mDirection = direction;
383
384
        if (mAction == nullptr)
385
            return false;
386
387
        const Animation *restrict const animation =
388
            mAction->getAnimation(mDirection);
389
390
        if ((animation != nullptr) &&
391
            animation != mAnimation &&
392
            animation->getLength() > 0)
393
        {
394
            mAnimation = animation;
395
            reset();
396
        }
397
398
        return true;
399
    }
400
401
    return false;
402
}
403
404
unsigned int AnimatedSprite::getFrameCount() const restrict2
405
{
406
    if (mAnimation != nullptr)
407
        return CAST_U32(mAnimation->getLength());
408
    return 0;
409
}
410
411
int AnimatedSprite::getWidth() const restrict2
412
{
413
    if ((mFrame != nullptr) && (mFrame->image != nullptr))
414
        return mFrame->image->mBounds.w;
415
    return 0;
416
}
417
418
int AnimatedSprite::getHeight() const restrict2
419
{
420
    if ((mFrame != nullptr) && (mFrame->image != nullptr))
421
        return mFrame->image->mBounds.h;
422
    return 0;
423
}
424
425
std::string AnimatedSprite::getIdPath() const restrict2
426
{
427
    if (mSprite == nullptr)
428
        return "";
429
    return mSprite->mIdPath;
430
}
431
432
const Image* AnimatedSprite::getImage() const restrict2 noexcept2
433
{
434
    return mFrame != nullptr ? mFrame->image : nullptr;
435
}
436
437
void AnimatedSprite::setAlpha(float alpha) restrict2
438
{
439
    mAlpha = alpha;
440
441
    if (mFrame != nullptr)
442
    {
443
        Image *restrict const image = mFrame->image;
444
        if (image != nullptr)
445
            image->setAlpha(mAlpha);
446
    }
447
}
448
449
const void *AnimatedSprite::getHash() const restrict2
450
{
451
    if (mFrame != nullptr)
452
        return mFrame;
453
    return this;
454
}
455
456
bool AnimatedSprite::updateNumber(const unsigned num) restrict2
457
{
458
    if (mSprite == nullptr)
459
        return false;
460
461
    if (mNumber1 != num)
462
    {
463
        mNumber1 = num;
464
        mNumber = mSprite->findNumber(num);
465
        if (mNumber == 0U)
466
        {
467
            mNumber = 100;
468
            return false;
469
        }
470
        return true;
471
    }
472
    return false;
473
}
474
475
void AnimatedSprite::setDelayLoad(const std::string &restrict filename,
476
                                  const int variant) restrict2
477
{
478
    if (mDelayLoad != nullptr)
479
    {
480
        mDelayLoad->clearSprite();
481
        DelayedManager::removeDelayLoad(mDelayLoad);
482
        delete mDelayLoad;
483
    }
484
    mDelayLoad = new AnimationDelayLoad(filename, variant, this);
485
    DelayedManager::addDelayedAnimation(mDelayLoad);
486

3
}