PageRenderTime 35ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/resources/sprite/spritedef.cpp

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