PageRenderTime 25ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/resources/sprite/spritedef.cpp

https://gitlab.com/Alige/manaplus
C++ | 694 lines | 589 code | 74 blank | 31 comment | 156 complexity | 8f30a8c10e89add47ac20b75b48bca47 MD5 | raw file
  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-2017 The ManaPlus Developers
  6. *
  7. * This file is part of The ManaPlus Client.
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. #include "resources/sprite/spritedef.h"
  23. #include "configuration.h"
  24. #include "settings.h"
  25. #include "const/resources/spriteaction.h"
  26. #include "const/resources/map/map.h"
  27. #include "utils/checkutils.h"
  28. #include "utils/foreach.h"
  29. #include "resources/action.h"
  30. #include "resources/imageset.h"
  31. #include "resources/animation/animation.h"
  32. #include "resources/dye/dye.h"
  33. #include "resources/loaders/imagesetloader.h"
  34. #include "resources/loaders/xmlloader.h"
  35. #include "resources/sprite/spritereference.h"
  36. #include "debug.h"
  37. SpriteReference *SpriteReference::Empty = nullptr;
  38. const Action *SpriteDef::getAction(const std::string &action,
  39. const unsigned num) const
  40. {
  41. Actions::const_iterator i = mActions.find(num);
  42. if (i == mActions.end() && num != 100)
  43. i = mActions.find(100);
  44. if (i == mActions.end() || ((*i).second == nullptr))
  45. return nullptr;
  46. const ActionMap *const actMap = (*i).second;
  47. if (actMap == nullptr)
  48. return nullptr;
  49. const ActionMap::const_iterator it = actMap->find(action);
  50. if (it == actMap->end())
  51. {
  52. logger->log("Warning: no action \"%s\" defined!", action.c_str());
  53. return nullptr;
  54. }
  55. return (*it).second;
  56. }
  57. unsigned SpriteDef::findNumber(const unsigned num) const
  58. {
  59. unsigned min = 101;
  60. FOR_EACH (Actions::const_iterator, it, mActions)
  61. {
  62. const unsigned n = (*it).first;
  63. if (n >= num && n < min)
  64. min = n;
  65. }
  66. if (min == 101)
  67. return 0;
  68. return min;
  69. }
  70. SpriteDef *SpriteDef::load(const std::string &animationFile,
  71. const int variant, const bool prot)
  72. {
  73. BLOCK_START("SpriteDef::load")
  74. const size_t pos = animationFile.find('|');
  75. std::string palettes;
  76. if (pos != std::string::npos)
  77. palettes = animationFile.substr(pos + 1);
  78. XML::Document *const doc = Loader::getXml(animationFile.substr(0, pos),
  79. UseVirtFs_true,
  80. SkipError_false);
  81. if (doc == nullptr)
  82. return nullptr;
  83. XmlNodePtrConst rootNode = doc->rootNode();
  84. if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "sprite"))
  85. {
  86. reportAlways("Error, failed to parse sprite %s",
  87. animationFile.c_str());
  88. const std::string errorFile = pathJoin(paths.getStringValue("sprites"),
  89. paths.getStringValue("spriteErrorFile"));
  90. BLOCK_END("SpriteDef::load")
  91. doc->decRef();
  92. if (animationFile != errorFile)
  93. return load(errorFile, 0, prot);
  94. return nullptr;
  95. }
  96. SpriteDef *const def = new SpriteDef;
  97. def->mSource = animationFile;
  98. def->mProcessedFiles.insert(animationFile);
  99. def->loadSprite(rootNode, variant, palettes);
  100. def->substituteActions();
  101. if (settings.fixDeadAnimation)
  102. def->fixDeadAction();
  103. if (prot)
  104. {
  105. def->incRef();
  106. def->mProtected = true;
  107. }
  108. doc->decRef();
  109. BLOCK_END("SpriteDef::load")
  110. return def;
  111. }
  112. void SpriteDef::fixDeadAction()
  113. {
  114. FOR_EACH (ActionsIter, it, mActions)
  115. {
  116. ActionMap *const d = (*it).second;
  117. if (d == nullptr)
  118. continue;
  119. const ActionMap::iterator i = d->find(SpriteAction::DEAD);
  120. const ActionMap::iterator i2 = d->find(SpriteAction::STAND);
  121. // search dead action and check what it not same with stand action
  122. if (i != d->end() &&
  123. i->second != nullptr &&
  124. (i2 == d->end() || i->second != i2->second))
  125. {
  126. (i->second)->setLastFrameDelay(0);
  127. }
  128. }
  129. }
  130. void SpriteDef::substituteAction(const std::string &restrict complete,
  131. const std::string &restrict with)
  132. {
  133. FOR_EACH (ActionsConstIter, it, mActions)
  134. {
  135. ActionMap *const d = (*it).second;
  136. if (reportTrue(d == nullptr))
  137. continue;
  138. if (d->find(complete) == d->end())
  139. {
  140. const ActionMap::iterator i = d->find(with);
  141. if (i != d->end())
  142. (*d)[complete] = i->second;
  143. }
  144. }
  145. }
  146. void SpriteDef::substituteActions()
  147. {
  148. substituteAction(SpriteAction::STAND, SpriteAction::DEFAULT);
  149. substituteAction(SpriteAction::MOVE, SpriteAction::STAND);
  150. substituteAction(SpriteAction::ATTACK, SpriteAction::STAND);
  151. substituteAction(SpriteAction::CAST, SpriteAction::ATTACK);
  152. substituteAction(SpriteAction::SIT, SpriteAction::STAND);
  153. substituteAction(SpriteAction::SITTOP, SpriteAction::SIT);
  154. substituteAction(SpriteAction::DEAD, SpriteAction::STAND);
  155. substituteAction(SpriteAction::SPAWN, SpriteAction::STAND);
  156. substituteAction(SpriteAction::FLY, SpriteAction::MOVE);
  157. substituteAction(SpriteAction::SWIM, SpriteAction::MOVE);
  158. substituteAction(SpriteAction::RIDE, SpriteAction::MOVE);
  159. substituteAction(SpriteAction::STANDSKY, SpriteAction::STAND);
  160. substituteAction(SpriteAction::STANDWATER, SpriteAction::STAND);
  161. substituteAction(SpriteAction::STANDRIDE, SpriteAction::STAND);
  162. substituteAction(SpriteAction::SITSKY, SpriteAction::SIT);
  163. substituteAction(SpriteAction::SITWATER, SpriteAction::SIT);
  164. substituteAction(SpriteAction::SITRIDE, SpriteAction::SIT);
  165. substituteAction(SpriteAction::ATTACKSKY, SpriteAction::ATTACK);
  166. substituteAction(SpriteAction::ATTACKWATER, SpriteAction::ATTACK);
  167. substituteAction(SpriteAction::ATTACKRIDE, SpriteAction::ATTACK);
  168. substituteAction(SpriteAction::CASTSKY, SpriteAction::CAST);
  169. substituteAction(SpriteAction::CASTWATER, SpriteAction::CAST);
  170. substituteAction(SpriteAction::CASTRIDE, SpriteAction::CAST);
  171. substituteAction(SpriteAction::SPAWNSKY, SpriteAction::SPAWN);
  172. substituteAction(SpriteAction::SPAWNWATER, SpriteAction::SPAWN);
  173. substituteAction(SpriteAction::SPAWNRIDE, SpriteAction::SPAWN);
  174. substituteAction(SpriteAction::DEADSKY, SpriteAction::DEAD);
  175. substituteAction(SpriteAction::DEADWATER, SpriteAction::DEAD);
  176. substituteAction(SpriteAction::DEADRIDE, SpriteAction::DEAD);
  177. }
  178. void SpriteDef::loadSprite(XmlNodeConstPtr spriteNode,
  179. const int variant,
  180. const std::string &palettes)
  181. {
  182. BLOCK_START("SpriteDef::loadSprite")
  183. if (spriteNode == nullptr)
  184. {
  185. BLOCK_END("SpriteDef::loadSprite")
  186. return;
  187. }
  188. // Get the variant
  189. const int variantCount = XML::getProperty(spriteNode, "variants", 0);
  190. int variant_offset = 0;
  191. if (variantCount > 0 && variant < variantCount)
  192. {
  193. variant_offset = variant * XML::getProperty(spriteNode,
  194. "variant_offset",
  195. 0);
  196. }
  197. for_each_xml_child_node(node, spriteNode)
  198. {
  199. if (xmlNameEqual(node, "imageset"))
  200. loadImageSet(node, palettes);
  201. else if (xmlNameEqual(node, "action"))
  202. loadAction(node, variant_offset);
  203. else if (xmlNameEqual(node, "include"))
  204. includeSprite(node, variant);
  205. }
  206. BLOCK_END("SpriteDef::loadSprite")
  207. }
  208. void SpriteDef::loadImageSet(XmlNodeConstPtr node,
  209. const std::string &palettes)
  210. {
  211. const std::string name = XML::getProperty(node, "name", "");
  212. // We don't allow redefining image sets. This way, an included sprite
  213. // definition will use the already loaded image set with the same name.
  214. if (mImageSets.find(name) != mImageSets.end())
  215. return;
  216. const int width = XML::getProperty(node, "width", 0);
  217. const int height = XML::getProperty(node, "height", 0);
  218. std::string imageSrc = XML::getProperty(node, "src", "");
  219. Dye::instantiate(imageSrc, palettes);
  220. ImageSet *const imageSet = Loader::getImageSet(imageSrc,
  221. width, height);
  222. if (imageSet == nullptr)
  223. {
  224. reportAlways("%s: Couldn't load imageset: %s",
  225. mSource.c_str(),
  226. imageSrc.c_str());
  227. return;
  228. }
  229. imageSet->setOffsetX(XML::getProperty(node, "offsetX", 0));
  230. imageSet->setOffsetY(XML::getProperty(node, "offsetY", 0));
  231. mImageSets[name] = imageSet;
  232. }
  233. const ImageSet *SpriteDef::getImageSet(const std::string &imageSetName) const
  234. {
  235. const ImageSetCIterator si = mImageSets.find(imageSetName);
  236. if (si == mImageSets.end())
  237. {
  238. reportAlways("%s: Imageset \"%s\" not defined in %s",
  239. mSource.c_str(),
  240. imageSetName.c_str(),
  241. mIdPath.c_str());
  242. return nullptr;
  243. }
  244. return si->second;
  245. }
  246. void SpriteDef::loadAction(XmlNodeConstPtr node,
  247. const int variant_offset)
  248. {
  249. if (node == nullptr)
  250. return;
  251. const std::string actionName = XML::getProperty(node, "name", "");
  252. const std::string imageSetName = XML::getProperty(node, "imageset", "");
  253. const unsigned hp = XML::getProperty(node, "hp", 100);
  254. const ImageSet *const imageSet = getImageSet(imageSetName);
  255. if (actionName == SpriteAction::INVALID)
  256. {
  257. reportAlways("%s: Unknown action \"%s\" defined in %s",
  258. mSource.c_str(),
  259. actionName.c_str(),
  260. mIdPath.c_str());
  261. return;
  262. }
  263. Action *const action = new Action(actionName);
  264. action->setNumber(hp);
  265. addAction(hp, actionName, action);
  266. // dirty hack to fix bad resources in tmw server
  267. if (actionName == "attack_stab")
  268. {
  269. reportAlways("Found legacy attribute attack_stab in animation");
  270. addAction(hp, "attack", action);
  271. }
  272. // When first action, set it as default direction.
  273. // i here always correct, because hp was added above.
  274. const Actions::const_iterator i = mActions.find(hp);
  275. if ((*i).second->size() == 1)
  276. addAction(hp, SpriteAction::DEFAULT, action);
  277. // Load animations
  278. for_each_xml_child_node(animationNode, node)
  279. {
  280. if (xmlNameEqual(animationNode, "animation"))
  281. loadAnimation(animationNode, action, imageSet, variant_offset);
  282. }
  283. }
  284. void SpriteDef::loadAnimation(XmlNodeConstPtr animationNode,
  285. Action *const action,
  286. const ImageSet *const imageSet0,
  287. const int variant_offset) const
  288. {
  289. if (action == nullptr ||
  290. imageSet0 == nullptr ||
  291. animationNode == nullptr)
  292. {
  293. return;
  294. }
  295. const std::string directionName =
  296. XML::getProperty(animationNode, "direction", "");
  297. const SpriteDirection::Type directionType
  298. = makeSpriteDirection(directionName);
  299. if (directionType == SpriteDirection::INVALID)
  300. {
  301. reportAlways("%s: Unknown direction \"%s\" used in %s",
  302. mSource.c_str(),
  303. directionName.c_str(),
  304. mIdPath.c_str());
  305. return;
  306. }
  307. Animation *const animation = new Animation(directionName);
  308. action->setAnimation(directionType, animation);
  309. // Get animation frames
  310. for_each_xml_child_node(frameNode, animationNode)
  311. {
  312. const int delay = XML::getIntProperty(
  313. frameNode, "delay", 0, 0, 100000);
  314. const std::string imageSetName = XML::getProperty(frameNode,
  315. "imageset",
  316. "");
  317. const ImageSet *imageSet = imageSet0;
  318. if (!imageSetName.empty())
  319. {
  320. imageSet = getImageSet(imageSetName);
  321. if (imageSet == nullptr)
  322. imageSet = imageSet0;
  323. }
  324. const int offsetX = XML::getProperty(frameNode, "offsetX", 0)
  325. + imageSet->getOffsetX() - imageSet->getWidth() / 2
  326. + mapTileSize / 2;
  327. const int offsetY = XML::getProperty(frameNode, "offsetY", 0)
  328. + imageSet->getOffsetY() - imageSet->getHeight() + mapTileSize;
  329. const int rand = XML::getIntProperty(frameNode, "rand", 100, 0, 100);
  330. if (xmlNameEqual(frameNode, "frame"))
  331. {
  332. const int index = XML::getProperty(frameNode, "index", -1);
  333. if (index < 0)
  334. {
  335. reportAlways(
  336. "%s: No valid value for 'index' at direction '%s'",
  337. mSource.c_str(),
  338. directionName.c_str());
  339. continue;
  340. }
  341. Image *const img = imageSet->get(index + variant_offset);
  342. if (img == nullptr)
  343. {
  344. reportAlways("%s: No image at index %d at direction '%s'",
  345. mSource.c_str(),
  346. index + variant_offset,
  347. directionName.c_str());
  348. continue;
  349. }
  350. animation->addFrame(img, delay, offsetX, offsetY, rand);
  351. }
  352. else if (xmlNameEqual(frameNode, "sequence"))
  353. {
  354. const int start = XML::getProperty(frameNode, "start", -1);
  355. const int end = XML::getProperty(frameNode, "end", -1);
  356. const std::string value = XML::getProperty(frameNode, "value", "");
  357. const int repeat = XML::getIntProperty(
  358. frameNode, "repeat", 1, 0, 100);
  359. if (repeat < 1)
  360. {
  361. reportAlways("%s: No valid value for 'repeat' at direction %s",
  362. mSource.c_str(),
  363. directionName.c_str());
  364. continue;
  365. }
  366. if (value.empty())
  367. {
  368. if (addSequence(start, end, delay, offsetX, offsetY,
  369. variant_offset, repeat, rand, imageSet, animation))
  370. {
  371. continue;
  372. }
  373. }
  374. else
  375. {
  376. StringVect vals;
  377. splitToStringVector(vals, value, ',');
  378. FOR_EACH (StringVectCIter, it, vals)
  379. {
  380. const std::string str = *it;
  381. const size_t idx = str.find('-');
  382. if (str == "p")
  383. {
  384. animation->addPause(delay, rand);
  385. }
  386. else if (idx != std::string::npos)
  387. {
  388. const int v1 = atoi(str.substr(0, idx).c_str());
  389. const int v2 = atoi(str.substr(idx + 1).c_str());
  390. addSequence(v1, v2, delay, offsetX, offsetY,
  391. variant_offset, repeat, rand, imageSet, animation);
  392. }
  393. else
  394. {
  395. Image *const img = imageSet->get(atoi(
  396. str.c_str()) + variant_offset);
  397. if (img != nullptr)
  398. {
  399. animation->addFrame(img, delay,
  400. offsetX, offsetY, rand);
  401. }
  402. }
  403. }
  404. }
  405. }
  406. else if (xmlNameEqual(frameNode, "pause"))
  407. {
  408. animation->addPause(delay, rand);
  409. }
  410. else if (xmlNameEqual(frameNode, "end"))
  411. {
  412. animation->addTerminator(rand);
  413. }
  414. else if (xmlNameEqual(frameNode, "jump"))
  415. {
  416. animation->addJump(XML::getProperty(
  417. frameNode, "action", ""), rand);
  418. }
  419. else if (xmlNameEqual(frameNode, "label"))
  420. {
  421. const std::string name = XML::getProperty(frameNode, "name", "");
  422. if (!name.empty())
  423. animation->addLabel(name);
  424. }
  425. else if (xmlNameEqual(frameNode, "goto"))
  426. {
  427. const std::string name = XML::getProperty(frameNode, "label", "");
  428. if (!name.empty())
  429. animation->addGoto(name, rand);
  430. }
  431. } // for frameNode
  432. }
  433. void SpriteDef::includeSprite(XmlNodeConstPtr includeNode, const int variant)
  434. {
  435. std::string filename = XML::getProperty(includeNode, "file", "");
  436. if (filename.empty())
  437. return;
  438. filename = pathJoin(paths.getStringValue("sprites"), filename);
  439. if (mProcessedFiles.find(filename) != mProcessedFiles.end())
  440. {
  441. reportAlways("%s: Tried to include %s which already is included.",
  442. mSource.c_str(),
  443. filename.c_str());
  444. return;
  445. }
  446. mProcessedFiles.insert(filename);
  447. XML::Document *const doc = Loader::getXml(filename,
  448. UseVirtFs_true,
  449. SkipError_false);
  450. if (doc == nullptr)
  451. return;
  452. XmlNodeConstPtr rootNode = doc->rootNode();
  453. if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "sprite"))
  454. {
  455. reportAlways("%s: No sprite root node in %s",
  456. mSource.c_str(),
  457. filename.c_str());
  458. doc->decRef();
  459. return;
  460. }
  461. loadSprite(rootNode, variant);
  462. doc->decRef();
  463. }
  464. SpriteDef::~SpriteDef()
  465. {
  466. // Actions are shared, so ensure they are deleted only once.
  467. std::set<Action*> actions;
  468. FOR_EACH (Actions::iterator, i, mActions)
  469. {
  470. FOR_EACHP (ActionMap::iterator, it, (*i).second)
  471. actions.insert(it->second);
  472. delete (*i).second;
  473. }
  474. FOR_EACH (std::set<Action*>::const_iterator, i, actions)
  475. delete *i;
  476. mActions.clear();
  477. FOR_EACH (ImageSetIterator, i, mImageSets)
  478. {
  479. if (i->second != nullptr)
  480. {
  481. i->second->decRef();
  482. i->second = nullptr;
  483. }
  484. }
  485. mImageSets.clear();
  486. }
  487. SpriteDirection::Type SpriteDef::makeSpriteDirection(const std::string
  488. &direction)
  489. {
  490. if (direction.empty() || direction == "default")
  491. return SpriteDirection::DEFAULT;
  492. else if (direction == "up")
  493. return SpriteDirection::UP;
  494. else if (direction == "left")
  495. return SpriteDirection::LEFT;
  496. else if (direction == "right")
  497. return SpriteDirection::RIGHT;
  498. else if (direction == "down")
  499. return SpriteDirection::DOWN;
  500. else if (direction == "upleft")
  501. return SpriteDirection::UPLEFT;
  502. else if (direction == "upright")
  503. return SpriteDirection::UPRIGHT;
  504. else if (direction == "downleft")
  505. return SpriteDirection::DOWNLEFT;
  506. else if (direction == "downright")
  507. return SpriteDirection::DOWNRIGHT;
  508. else
  509. return SpriteDirection::INVALID;
  510. }
  511. void SpriteDef::addAction(const unsigned hp, const std::string &name,
  512. Action *const action)
  513. {
  514. const Actions::const_iterator i = mActions.find(hp);
  515. if (i == mActions.end())
  516. mActions[hp] = new ActionMap;
  517. (*mActions[hp])[name] = action;
  518. }
  519. bool SpriteDef::addSequence(const int start,
  520. const int end,
  521. const int delay,
  522. const int offsetX,
  523. const int offsetY,
  524. const int variant_offset,
  525. int repeat,
  526. const int rand,
  527. const ImageSet *const imageSet,
  528. Animation *const animation) const
  529. {
  530. if ((imageSet == nullptr) || (animation == nullptr))
  531. return true;
  532. if (start < 0 || end < 0)
  533. {
  534. reportAlways("%s: No valid value for 'start' or 'end'",
  535. mSource.c_str());
  536. return true;
  537. }
  538. if (start <= end)
  539. {
  540. while (repeat > 0)
  541. {
  542. int pos = start;
  543. while (end >= pos)
  544. {
  545. Image *const img = imageSet->get(pos + variant_offset);
  546. if (img == nullptr)
  547. {
  548. reportAlways("%s: No image at index %d",
  549. mSource.c_str(),
  550. pos + variant_offset);
  551. pos ++;
  552. continue;
  553. }
  554. animation->addFrame(img, delay,
  555. offsetX, offsetY, rand);
  556. pos ++;
  557. }
  558. repeat --;
  559. }
  560. }
  561. else
  562. {
  563. while (repeat > 0)
  564. {
  565. int pos = start;
  566. while (end <= pos)
  567. {
  568. Image *const img = imageSet->get(pos + variant_offset);
  569. if (img == nullptr)
  570. {
  571. reportAlways("%s: No image at index %d",
  572. mSource.c_str(),
  573. pos + variant_offset);
  574. pos ++;
  575. continue;
  576. }
  577. animation->addFrame(img, delay,
  578. offsetX, offsetY, rand);
  579. pos --;
  580. }
  581. repeat --;
  582. }
  583. }
  584. return false;
  585. }
  586. int SpriteDef::calcMemoryLocal() const
  587. {
  588. int sz = static_cast<int>(sizeof(SpriteDef) +
  589. sizeof(ImageSets) +
  590. sizeof(Actions) +
  591. sizeof(std::set<std::string>)) +
  592. Resource::calcMemoryLocal();
  593. FOR_EACH (std::set<std::string>::const_iterator, it, mProcessedFiles)
  594. {
  595. sz += static_cast<int>((*it).capacity());
  596. }
  597. return sz;
  598. }
  599. int SpriteDef::calcMemoryChilds(const int level) const
  600. {
  601. int sz = 0;
  602. FOR_EACH (ImageSets::const_iterator, it, mImageSets)
  603. {
  604. sz += static_cast<int>((*it).first.capacity());
  605. const ImageSet *const imageSet = (*it).second;
  606. sz += imageSet->calcMemory(level + 1);
  607. }
  608. FOR_EACH (ActionsCIter, it, mActions)
  609. {
  610. sz += sizeof(unsigned);
  611. const ActionMap *const actionMap = (*it).second;
  612. FOR_EACHP (ActionMap::const_iterator, it2, actionMap)
  613. {
  614. sz += static_cast<int>((*it2).first.capacity());
  615. const Action *const action = (*it2).second;
  616. sz += action->calcMemory(level + 1);
  617. }
  618. }
  619. return sz;
  620. }