GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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 |
} |
Generated by: GCOVR (Version 3.3) |