From 2a506c72f0690b1b73034ce1d6911a1abba74029 Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Sun, 20 Dec 2015 17:55:49 +0300 Subject: Move spritedef into sprite directory. --- src/CMakeLists.txt | 8 +- src/Makefile.am | 4 +- src/resources/resourcemanager.cpp | 3 +- src/resources/sprite/spritedef.cpp | 591 +++++++++++++++++++++++++++++++++++++ src/resources/sprite/spritedef.h | 159 ++++++++++ src/resources/spritedef.cpp | 591 ------------------------------------- src/resources/spritedef.h | 159 ---------- src/sprite.h | 2 +- 8 files changed, 759 insertions(+), 758 deletions(-) create mode 100644 src/resources/sprite/spritedef.cpp create mode 100644 src/resources/sprite/spritedef.h delete mode 100644 src/resources/spritedef.cpp delete mode 100644 src/resources/spritedef.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 89e3cb267..fbff965d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -735,8 +735,8 @@ SET(SRCS resources/soundeffect.h resources/soundinfo.h resources/spriteaction.h - resources/spritedef.h - resources/spritedef.cpp + resources/sprite/spritedef.h + resources/sprite/spritedef.cpp resources/spritedirection.h resources/spritedisplay.h resources/spritereference.h @@ -1373,8 +1373,8 @@ SET(DYE_CMD_SRCS resources/surfaceimagehelper.h resources/textureatlas.h resources/updatefile.h - resources/spritedef.cpp - resources/spritedef.h + resources/sprite/spritedef.cpp + resources/sprite/spritedef.h resources/spritedisplay.h resources/spritereference.h utils/files.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 8a26916fb..7b446fb73 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -384,8 +384,8 @@ SRC += events/actionevent.h \ resources/soundeffect.h \ resources/soundinfo.h \ resources/spriteaction.h \ - resources/spritedef.cpp \ - resources/spritedef.h \ + resources/sprite/spritedef.cpp \ + resources/sprite/spritedef.h \ resources/spritedirection.h \ resources/subimage.cpp \ resources/subimage.h \ diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index bb566d9f4..8d8e843df 100644 --- a/src/resources/resourcemanager.cpp +++ b/src/resources/resourcemanager.cpp @@ -38,7 +38,8 @@ #include "resources/imageset.h" #include "resources/sdlmusic.h" #include "resources/soundeffect.h" -#include "resources/spritedef.h" + +#include "resources/sprite/spritedef.h" #include "utils/delete2.h" #include "utils/physfscheckutils.h" diff --git a/src/resources/sprite/spritedef.cpp b/src/resources/sprite/spritedef.cpp new file mode 100644 index 000000000..cb0fc777c --- /dev/null +++ b/src/resources/sprite/spritedef.cpp @@ -0,0 +1,591 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "resources/sprite/spritedef.h" + +#include "configuration.h" +#include "logger.h" +#include "settings.h" + +#include "const/resources/map/map.h" + +#include "resources/action.h" +#include "resources/animation.h" +#include "resources/dye.h" +#include "resources/imageset.h" +#include "resources/resourcemanager.h" +#include "resources/spriteaction.h" +#include "resources/spritereference.h" + +#include "debug.h" + +SpriteReference *SpriteReference::Empty = nullptr; + +const Action *SpriteDef::getAction(const std::string &action, + const unsigned num) const +{ + Actions::const_iterator i = mActions.find(num); + if (i == mActions.end() && num != 100) + i = mActions.find(100); + + if (i == mActions.end() || !(*i).second) + return nullptr; + + const ActionMap *const actMap = (*i).second; + if (!actMap) + return nullptr; + const ActionMap::const_iterator it = actMap->find(action); + + if (it == actMap->end()) + { + logger->log("Warning: no action \"%s\" defined!", action.c_str()); + return nullptr; + } + + return (*it).second; +} + +unsigned SpriteDef::findNumber(const unsigned num) const +{ + unsigned min = 101; + FOR_EACH (Actions::const_iterator, it, mActions) + { + const unsigned n = (*it).first; + if (n >= num && n < min) + min = n; + } + if (min == 101) + return 0; + return min; +} + +SpriteDef *SpriteDef::load(const std::string &animationFile, + const int variant, const bool prot) +{ + BLOCK_START("SpriteDef::load") + const size_t pos = animationFile.find('|'); + std::string palettes; + if (pos != std::string::npos) + palettes = animationFile.substr(pos + 1); + + XML::Document doc(animationFile.substr(0, pos), + UseResman_true, + SkipError_false); + XmlNodePtrConst rootNode = doc.rootNode(); + + if (!rootNode || !xmlNameEqual(rootNode, "sprite")) + { + logger->log("Error, failed to parse %s", animationFile.c_str()); + + const std::string errorFile = paths.getStringValue("sprites").append( + paths.getStringValue("spriteErrorFile")); + BLOCK_END("SpriteDef::load") + if (animationFile != errorFile) + return load(errorFile, 0, prot); + else + return nullptr; + } + + SpriteDef *const def = new SpriteDef; + def->mProcessedFiles.insert(animationFile); + def->loadSprite(rootNode, variant, palettes); + def->substituteActions(); + if (settings.fixDeadAnimation) + def->fixDeadAction(); + if (prot) + { + def->incRef(); + def->setProtected(true); + } + BLOCK_END("SpriteDef::load") + return def; +} + +void SpriteDef::fixDeadAction() +{ + FOR_EACH (ActionsIter, it, mActions) + { + ActionMap *const d = (*it).second; + if (!d) + continue; + const ActionMap::iterator i = d->find(SpriteAction::DEAD); + const ActionMap::iterator i2 = d->find(SpriteAction::STAND); + // search dead action and check what it not same with stand action + if (i != d->end() && i->second && i->second != i2->second) + (i->second)->setLastFrameDelay(0); + } +} + +void SpriteDef::substituteAction(const std::string &restrict complete, + const std::string &restrict with) +{ + FOR_EACH (ActionsConstIter, it, mActions) + { + ActionMap *const d = (*it).second; + if (!d) + continue; + if (d->find(complete) == d->end()) + { + const ActionMap::iterator i = d->find(with); + if (i != d->end()) + (*d)[complete] = i->second; + } + } +} + +void SpriteDef::substituteActions() +{ + substituteAction(SpriteAction::STAND, SpriteAction::DEFAULT); + substituteAction(SpriteAction::MOVE, SpriteAction::STAND); + substituteAction(SpriteAction::ATTACK, SpriteAction::STAND); + substituteAction(SpriteAction::CAST, SpriteAction::ATTACK); + substituteAction(SpriteAction::SIT, SpriteAction::STAND); + substituteAction(SpriteAction::SITTOP, SpriteAction::SIT); + substituteAction(SpriteAction::DEAD, SpriteAction::STAND); + substituteAction(SpriteAction::SPAWN, SpriteAction::STAND); + substituteAction(SpriteAction::FLY, SpriteAction::MOVE); + substituteAction(SpriteAction::SWIM, SpriteAction::MOVE); + substituteAction(SpriteAction::RIDE, SpriteAction::MOVE); + substituteAction(SpriteAction::STANDSKY, SpriteAction::STAND); + substituteAction(SpriteAction::STANDWATER, SpriteAction::STAND); + substituteAction(SpriteAction::STANDRIDE, SpriteAction::STAND); + substituteAction(SpriteAction::SITSKY, SpriteAction::SIT); + substituteAction(SpriteAction::SITWATER, SpriteAction::SIT); + substituteAction(SpriteAction::SITRIDE, SpriteAction::SIT); + substituteAction(SpriteAction::ATTACKSKY, SpriteAction::ATTACK); + substituteAction(SpriteAction::ATTACKWATER, SpriteAction::ATTACK); + substituteAction(SpriteAction::ATTACKRIDE, SpriteAction::ATTACK); + substituteAction(SpriteAction::CASTSKY, SpriteAction::CAST); + substituteAction(SpriteAction::CASTWATER, SpriteAction::CAST); + substituteAction(SpriteAction::CASTRIDE, SpriteAction::CAST); + substituteAction(SpriteAction::SPAWNSKY, SpriteAction::SPAWN); + substituteAction(SpriteAction::SPAWNWATER, SpriteAction::SPAWN); + substituteAction(SpriteAction::SPAWNRIDE, SpriteAction::SPAWN); + substituteAction(SpriteAction::DEADSKY, SpriteAction::DEAD); + substituteAction(SpriteAction::DEADWATER, SpriteAction::DEAD); + substituteAction(SpriteAction::DEADRIDE, SpriteAction::DEAD); +} + +void SpriteDef::loadSprite(const XmlNodePtr spriteNode, + const int variant, + const std::string &palettes) +{ + BLOCK_START("SpriteDef::loadSprite") + if (!spriteNode) + { + BLOCK_END("SpriteDef::loadSprite") + return; + } + // Get the variant + const int variantCount = XML::getProperty(spriteNode, "variants", 0); + int variant_offset = 0; + + if (variantCount > 0 && variant < variantCount) + { + variant_offset = + variant * XML::getProperty(spriteNode, "variant_offset", 0); + } + + for_each_xml_child_node(node, spriteNode) + { + if (xmlNameEqual(node, "imageset")) + loadImageSet(node, palettes); + else if (xmlNameEqual(node, "action")) + loadAction(node, variant_offset); + else if (xmlNameEqual(node, "include")) + includeSprite(node, variant); + } + BLOCK_END("SpriteDef::loadSprite") +} + +void SpriteDef::loadImageSet(const XmlNodePtr node, + const std::string &palettes) +{ + const std::string name = XML::getProperty(node, "name", ""); + + // We don't allow redefining image sets. This way, an included sprite + // definition will use the already loaded image set with the same name. + if (mImageSets.find(name) != mImageSets.end()) + return; + + const int width = XML::getProperty(node, "width", 0); + const int height = XML::getProperty(node, "height", 0); + std::string imageSrc = XML::getProperty(node, "src", ""); + Dye::instantiate(imageSrc, palettes); + + ImageSet *const imageSet = resourceManager->getImageSet(imageSrc, + width, height); + + if (!imageSet) + { + logger->log1("Couldn't load imageset!"); + return; + } + + imageSet->setOffsetX(XML::getProperty(node, "offsetX", 0)); + imageSet->setOffsetY(XML::getProperty(node, "offsetY", 0)); + mImageSets[name] = imageSet; +} + +void SpriteDef::loadAction(const XmlNodePtr node, + const int variant_offset) +{ + if (!node) + return; + + const std::string actionName = XML::getProperty(node, "name", ""); + const std::string imageSetName = XML::getProperty(node, "imageset", ""); + const unsigned hp = XML::getProperty(node, "hp", 100); + + const ImageSetIterator si = mImageSets.find(imageSetName); + if (si == mImageSets.end()) + { + logger->log("Warning: imageset \"%s\" not defined in %s", + imageSetName.c_str(), getIdPath().c_str()); + return; + } + const ImageSet *const imageSet = si->second; + + if (actionName == SpriteAction::INVALID) + { + logger->log("Warning: Unknown action \"%s\" defined in %s", + actionName.c_str(), getIdPath().c_str()); + return; + } + Action *const action = new Action; + action->setNumber(hp); + addAction(hp, actionName, action); + + // dirty hack to fix bad resources in tmw server + if (actionName == "attack_stab") + addAction(hp, "attack", action); + + // When first action set it as default direction + const Actions::const_iterator i = mActions.find(hp); + if ((*i).second->size() == 1) + addAction(hp, SpriteAction::DEFAULT, action); + + // Load animations + for_each_xml_child_node(animationNode, node) + { + if (xmlNameEqual(animationNode, "animation")) + loadAnimation(animationNode, action, imageSet, variant_offset); + } +} + +void SpriteDef::loadAnimation(const XmlNodePtr animationNode, + Action *const action, + const ImageSet *const imageSet, + const int variant_offset) const +{ + if (!action || !imageSet || !animationNode) + return; + + const std::string directionName = + XML::getProperty(animationNode, "direction", ""); + const SpriteDirection::Type directionType + = makeSpriteDirection(directionName); + + if (directionType == SpriteDirection::INVALID) + { + logger->log("Warning: Unknown direction \"%s\" used in %s", + directionName.c_str(), getIdPath().c_str()); + return; + } + + Animation *const animation = new Animation; + action->setAnimation(directionType, animation); + + // Get animation frames + for_each_xml_child_node(frameNode, animationNode) + { + const int delay = XML::getIntProperty( + frameNode, "delay", 0, 0, 100000); + const int offsetX = XML::getProperty(frameNode, "offsetX", 0) + + imageSet->getOffsetX() - imageSet->getWidth() / 2 + + mapTileSize / 2; + const int offsetY = XML::getProperty(frameNode, "offsetY", 0) + + imageSet->getOffsetY() - imageSet->getHeight() + mapTileSize; + const int rand = XML::getIntProperty(frameNode, "rand", 100, 0, 100); + + if (xmlNameEqual(frameNode, "frame")) + { + const int index = XML::getProperty(frameNode, "index", -1); + + if (index < 0) + { + logger->log1("No valid value for 'index'"); + continue; + } + + Image *const img = imageSet->get(index + variant_offset); + + if (!img) + { + logger->log("No image at index %d", index + variant_offset); + continue; + } + + animation->addFrame(img, delay, offsetX, offsetY, rand); + } + else if (xmlNameEqual(frameNode, "sequence")) + { + const int start = XML::getProperty(frameNode, "start", -1); + const int end = XML::getProperty(frameNode, "end", -1); + const std::string value = XML::getProperty(frameNode, "value", ""); + const int repeat = XML::getIntProperty( + frameNode, "repeat", 1, 0, 100); + + if (repeat < 1) + { + logger->log1("No valid value for 'repeat'"); + continue; + } + + if (value.empty()) + { + if (addSequence(start, end, delay, offsetX, offsetY, + variant_offset, repeat, rand, imageSet, animation)) + { + continue; + } + } + else + { + StringVect vals; + splitToStringVector(vals, value, ','); + FOR_EACH (StringVectCIter, it, vals) + { + const std::string str = *it; + const size_t idx = str.find("-"); + if (str == "p") + { + animation->addPause(delay, rand); + } + else if (idx != std::string::npos) + { + const int v1 = atoi(str.substr(0, idx).c_str()); + const int v2 = atoi(str.substr(idx + 1).c_str()); + addSequence(v1, v2, delay, offsetX, offsetY, + variant_offset, repeat, rand, imageSet, animation); + } + else + { + Image *const img = imageSet->get(atoi( + str.c_str()) + variant_offset); + if (img) + { + animation->addFrame(img, delay, + offsetX, offsetY, rand); + } + } + } + } + } + else if (xmlNameEqual(frameNode, "pause")) + { + animation->addPause(delay, rand); + } + else if (xmlNameEqual(frameNode, "end")) + { + animation->addTerminator(rand); + } + else if (xmlNameEqual(frameNode, "jump")) + { + animation->addJump(XML::getProperty( + frameNode, "action", ""), rand); + } + else if (xmlNameEqual(frameNode, "label")) + { + const std::string name = XML::getProperty(frameNode, "name", ""); + if (!name.empty()) + animation->addLabel(name); + } + else if (xmlNameEqual(frameNode, "goto")) + { + const std::string name = XML::getProperty(frameNode, "label", ""); + if (!name.empty()) + animation->addGoto(name, rand); + } + } // for frameNode +} + +void SpriteDef::includeSprite(const XmlNodePtr includeNode, const int variant) +{ + std::string filename = XML::getProperty(includeNode, "file", ""); + + if (filename.empty()) + return; + filename = paths.getStringValue("sprites").append(filename); + + if (mProcessedFiles.find(filename) != mProcessedFiles.end()) + { + logger->log("Error, Tried to include %s which already is included.", + filename.c_str()); + return; + } + mProcessedFiles.insert(filename); + + XML::Document doc(filename, UseResman_true, SkipError_false); + const XmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlNameEqual(rootNode, "sprite")) + { + logger->log("Error, no sprite root node in %s", filename.c_str()); + return; + } + + loadSprite(rootNode, variant); +} + +SpriteDef::~SpriteDef() +{ + // Actions are shared, so ensure they are deleted only once. + std::set actions; + FOR_EACH (Actions::iterator, i, mActions) + { + FOR_EACHP (ActionMap::iterator, it, (*i).second) + actions.insert(it->second); + delete (*i).second; + } + + FOR_EACH (std::set::const_iterator, i, actions) + delete *i; + + mActions.clear(); + + FOR_EACH (ImageSetIterator, i, mImageSets) + { + if (i->second) + { + i->second->decRef(); + i->second = nullptr; + } + } + mImageSets.clear(); +} + +SpriteDirection::Type SpriteDef::makeSpriteDirection(const std::string + &direction) +{ + if (direction.empty() || direction == "default") + return SpriteDirection::DEFAULT; + else if (direction == "up") + return SpriteDirection::UP; + else if (direction == "left") + return SpriteDirection::LEFT; + else if (direction == "right") + return SpriteDirection::RIGHT; + else if (direction == "down") + return SpriteDirection::DOWN; + else if (direction == "upleft") + return SpriteDirection::UPLEFT; + else if (direction == "upright") + return SpriteDirection::UPRIGHT; + else if (direction == "downleft") + return SpriteDirection::DOWNLEFT; + else if (direction == "downright") + return SpriteDirection::DOWNRIGHT; + else + return SpriteDirection::INVALID; +} + +void SpriteDef::addAction(const unsigned hp, const std::string &name, + Action *const action) +{ + const Actions::const_iterator i = mActions.find(hp); + if (i == mActions.end()) + mActions[hp] = new ActionMap(); + + (*mActions[hp])[name] = action; +} + +bool SpriteDef::addSequence(const int start, + const int end, + const int delay, + const int offsetX, + const int offsetY, + const int variant_offset, + int repeat, + const int rand, + const ImageSet *const imageSet, + Animation *const animation) +{ + if (!imageSet || !animation) + return true; + + if (start < 0 || end < 0) + { + logger->log1("No valid value for 'start' or 'end'"); + return true; + } + + if (start <= end) + { + while (repeat > 0) + { + int pos = start; + while (end >= pos) + { + Image *const img = imageSet->get(pos + variant_offset); + + if (!img) + { + logger->log("No image at index %d", + pos + variant_offset); + pos ++; + continue; + } + + animation->addFrame(img, delay, + offsetX, offsetY, rand); + pos ++; + } + repeat --; + } + } + else + { + while (repeat > 0) + { + int pos = start; + while (end <= pos) + { + Image *const img = imageSet->get(pos + variant_offset); + + if (!img) + { + logger->log("No image at index %d", + pos + variant_offset); + pos ++; + continue; + } + + animation->addFrame(img, delay, + offsetX, offsetY, rand); + pos --; + } + repeat --; + } + } + return false; +} diff --git a/src/resources/sprite/spritedef.h b/src/resources/sprite/spritedef.h new file mode 100644 index 000000000..02ce98ff9 --- /dev/null +++ b/src/resources/sprite/spritedef.h @@ -0,0 +1,159 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef RESOURCES_SPRITE_SPRITEDEF_H +#define RESOURCES_SPRITE_SPRITEDEF_H + +#include "resources/resource.h" + +#include "resources/spritedirection.h" + +#include "utils/xml.h" + +#include +#include + +class Action; +class Animation; +class ImageSet; + +/** + * Defines a class to load an animation. + */ +class SpriteDef final : public Resource +{ + public: + A_DELETE_COPY(SpriteDef) + + /** + * Loads a sprite definition file. + */ + static SpriteDef *load(const std::string &file, + const int variant, + const bool prot) A_WARN_UNUSED; + + /** + * Returns the specified action. + */ + const Action *getAction(const std::string &action, + const unsigned num) const A_WARN_UNUSED; + + unsigned findNumber(const unsigned num) const A_WARN_UNUSED; + + /** + * Converts a string into a SpriteDirection enum. + */ + static SpriteDirection::Type + makeSpriteDirection(const std::string &direction) A_WARN_UNUSED; + + void addAction(const unsigned hp, const std::string &name, + Action *const action); + + static bool addSequence(const int start, + const int end, + const int delay, + const int offsetX, + const int offsetY, + const int variant_offset, + int repeat, + const int rand, + const ImageSet *const imageSet, + Animation *const animation); + + private: + /** + * Constructor. + */ + SpriteDef() : + Resource(), + mImageSets(), + mActions(), + mProcessedFiles() + { } + + /** + * Destructor. + */ + ~SpriteDef(); + + /** + * Loads a sprite element. + */ + void loadSprite(const XmlNodePtr spriteNode, + const int variant, + const std::string &palettes = ""); + + /** + * Loads an imageset element. + */ + void loadImageSet(const XmlNodePtr node, + const std::string &palettes); + + /** + * Loads an action element. + */ + void loadAction(const XmlNodePtr node, + const int variant_offset); + + /** + * Loads an animation element. + */ + void loadAnimation(const XmlNodePtr animationNode, + Action *const action, + const ImageSet *const imageSet, + const int variant_offset) const; + + /** + * Include another sprite into this one. + */ + void includeSprite(const XmlNodePtr includeNode, const int variant); + + /** + * Complete missing actions by copying existing ones. + */ + void substituteActions(); + + /** + * Fix bad timeout in last dead action frame + */ + void fixDeadAction(); + + /** + * When there are no animations defined for the action "complete", its + * animations become a copy of those of the action "with". + */ + void substituteAction(const std::string &restrict complete, + const std::string &restrict with); + + typedef std::map ImageSets; + typedef ImageSets::iterator ImageSetIterator; + typedef std::map ActionMap; + typedef std::map Actions; + typedef Actions::const_iterator ActionsConstIter; + typedef Actions::iterator ActionsIter; + + ImageSets mImageSets; + Actions mActions; + std::set mProcessedFiles; +}; + +#endif // RESOURCES_SPRITE_SPRITEDEF_H diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp deleted file mode 100644 index 53b975a9f..000000000 --- a/src/resources/spritedef.cpp +++ /dev/null @@ -1,591 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2015 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "resources/spritedef.h" - -#include "configuration.h" -#include "logger.h" -#include "settings.h" - -#include "const/resources/map/map.h" - -#include "resources/action.h" -#include "resources/animation.h" -#include "resources/dye.h" -#include "resources/imageset.h" -#include "resources/resourcemanager.h" -#include "resources/spriteaction.h" -#include "resources/spritereference.h" - -#include "debug.h" - -SpriteReference *SpriteReference::Empty = nullptr; - -const Action *SpriteDef::getAction(const std::string &action, - const unsigned num) const -{ - Actions::const_iterator i = mActions.find(num); - if (i == mActions.end() && num != 100) - i = mActions.find(100); - - if (i == mActions.end() || !(*i).second) - return nullptr; - - const ActionMap *const actMap = (*i).second; - if (!actMap) - return nullptr; - const ActionMap::const_iterator it = actMap->find(action); - - if (it == actMap->end()) - { - logger->log("Warning: no action \"%s\" defined!", action.c_str()); - return nullptr; - } - - return (*it).second; -} - -unsigned SpriteDef::findNumber(const unsigned num) const -{ - unsigned min = 101; - FOR_EACH (Actions::const_iterator, it, mActions) - { - const unsigned n = (*it).first; - if (n >= num && n < min) - min = n; - } - if (min == 101) - return 0; - return min; -} - -SpriteDef *SpriteDef::load(const std::string &animationFile, - const int variant, const bool prot) -{ - BLOCK_START("SpriteDef::load") - const size_t pos = animationFile.find('|'); - std::string palettes; - if (pos != std::string::npos) - palettes = animationFile.substr(pos + 1); - - XML::Document doc(animationFile.substr(0, pos), - UseResman_true, - SkipError_false); - XmlNodePtrConst rootNode = doc.rootNode(); - - if (!rootNode || !xmlNameEqual(rootNode, "sprite")) - { - logger->log("Error, failed to parse %s", animationFile.c_str()); - - const std::string errorFile = paths.getStringValue("sprites").append( - paths.getStringValue("spriteErrorFile")); - BLOCK_END("SpriteDef::load") - if (animationFile != errorFile) - return load(errorFile, 0, prot); - else - return nullptr; - } - - SpriteDef *const def = new SpriteDef; - def->mProcessedFiles.insert(animationFile); - def->loadSprite(rootNode, variant, palettes); - def->substituteActions(); - if (settings.fixDeadAnimation) - def->fixDeadAction(); - if (prot) - { - def->incRef(); - def->setProtected(true); - } - BLOCK_END("SpriteDef::load") - return def; -} - -void SpriteDef::fixDeadAction() -{ - FOR_EACH (ActionsIter, it, mActions) - { - ActionMap *const d = (*it).second; - if (!d) - continue; - const ActionMap::iterator i = d->find(SpriteAction::DEAD); - const ActionMap::iterator i2 = d->find(SpriteAction::STAND); - // search dead action and check what it not same with stand action - if (i != d->end() && i->second && i->second != i2->second) - (i->second)->setLastFrameDelay(0); - } -} - -void SpriteDef::substituteAction(const std::string &restrict complete, - const std::string &restrict with) -{ - FOR_EACH (ActionsConstIter, it, mActions) - { - ActionMap *const d = (*it).second; - if (!d) - continue; - if (d->find(complete) == d->end()) - { - const ActionMap::iterator i = d->find(with); - if (i != d->end()) - (*d)[complete] = i->second; - } - } -} - -void SpriteDef::substituteActions() -{ - substituteAction(SpriteAction::STAND, SpriteAction::DEFAULT); - substituteAction(SpriteAction::MOVE, SpriteAction::STAND); - substituteAction(SpriteAction::ATTACK, SpriteAction::STAND); - substituteAction(SpriteAction::CAST, SpriteAction::ATTACK); - substituteAction(SpriteAction::SIT, SpriteAction::STAND); - substituteAction(SpriteAction::SITTOP, SpriteAction::SIT); - substituteAction(SpriteAction::DEAD, SpriteAction::STAND); - substituteAction(SpriteAction::SPAWN, SpriteAction::STAND); - substituteAction(SpriteAction::FLY, SpriteAction::MOVE); - substituteAction(SpriteAction::SWIM, SpriteAction::MOVE); - substituteAction(SpriteAction::RIDE, SpriteAction::MOVE); - substituteAction(SpriteAction::STANDSKY, SpriteAction::STAND); - substituteAction(SpriteAction::STANDWATER, SpriteAction::STAND); - substituteAction(SpriteAction::STANDRIDE, SpriteAction::STAND); - substituteAction(SpriteAction::SITSKY, SpriteAction::SIT); - substituteAction(SpriteAction::SITWATER, SpriteAction::SIT); - substituteAction(SpriteAction::SITRIDE, SpriteAction::SIT); - substituteAction(SpriteAction::ATTACKSKY, SpriteAction::ATTACK); - substituteAction(SpriteAction::ATTACKWATER, SpriteAction::ATTACK); - substituteAction(SpriteAction::ATTACKRIDE, SpriteAction::ATTACK); - substituteAction(SpriteAction::CASTSKY, SpriteAction::CAST); - substituteAction(SpriteAction::CASTWATER, SpriteAction::CAST); - substituteAction(SpriteAction::CASTRIDE, SpriteAction::CAST); - substituteAction(SpriteAction::SPAWNSKY, SpriteAction::SPAWN); - substituteAction(SpriteAction::SPAWNWATER, SpriteAction::SPAWN); - substituteAction(SpriteAction::SPAWNRIDE, SpriteAction::SPAWN); - substituteAction(SpriteAction::DEADSKY, SpriteAction::DEAD); - substituteAction(SpriteAction::DEADWATER, SpriteAction::DEAD); - substituteAction(SpriteAction::DEADRIDE, SpriteAction::DEAD); -} - -void SpriteDef::loadSprite(const XmlNodePtr spriteNode, - const int variant, - const std::string &palettes) -{ - BLOCK_START("SpriteDef::loadSprite") - if (!spriteNode) - { - BLOCK_END("SpriteDef::loadSprite") - return; - } - // Get the variant - const int variantCount = XML::getProperty(spriteNode, "variants", 0); - int variant_offset = 0; - - if (variantCount > 0 && variant < variantCount) - { - variant_offset = - variant * XML::getProperty(spriteNode, "variant_offset", 0); - } - - for_each_xml_child_node(node, spriteNode) - { - if (xmlNameEqual(node, "imageset")) - loadImageSet(node, palettes); - else if (xmlNameEqual(node, "action")) - loadAction(node, variant_offset); - else if (xmlNameEqual(node, "include")) - includeSprite(node, variant); - } - BLOCK_END("SpriteDef::loadSprite") -} - -void SpriteDef::loadImageSet(const XmlNodePtr node, - const std::string &palettes) -{ - const std::string name = XML::getProperty(node, "name", ""); - - // We don't allow redefining image sets. This way, an included sprite - // definition will use the already loaded image set with the same name. - if (mImageSets.find(name) != mImageSets.end()) - return; - - const int width = XML::getProperty(node, "width", 0); - const int height = XML::getProperty(node, "height", 0); - std::string imageSrc = XML::getProperty(node, "src", ""); - Dye::instantiate(imageSrc, palettes); - - ImageSet *const imageSet = resourceManager->getImageSet(imageSrc, - width, height); - - if (!imageSet) - { - logger->log1("Couldn't load imageset!"); - return; - } - - imageSet->setOffsetX(XML::getProperty(node, "offsetX", 0)); - imageSet->setOffsetY(XML::getProperty(node, "offsetY", 0)); - mImageSets[name] = imageSet; -} - -void SpriteDef::loadAction(const XmlNodePtr node, - const int variant_offset) -{ - if (!node) - return; - - const std::string actionName = XML::getProperty(node, "name", ""); - const std::string imageSetName = XML::getProperty(node, "imageset", ""); - const unsigned hp = XML::getProperty(node, "hp", 100); - - const ImageSetIterator si = mImageSets.find(imageSetName); - if (si == mImageSets.end()) - { - logger->log("Warning: imageset \"%s\" not defined in %s", - imageSetName.c_str(), getIdPath().c_str()); - return; - } - const ImageSet *const imageSet = si->second; - - if (actionName == SpriteAction::INVALID) - { - logger->log("Warning: Unknown action \"%s\" defined in %s", - actionName.c_str(), getIdPath().c_str()); - return; - } - Action *const action = new Action; - action->setNumber(hp); - addAction(hp, actionName, action); - - // dirty hack to fix bad resources in tmw server - if (actionName == "attack_stab") - addAction(hp, "attack", action); - - // When first action set it as default direction - const Actions::const_iterator i = mActions.find(hp); - if ((*i).second->size() == 1) - addAction(hp, SpriteAction::DEFAULT, action); - - // Load animations - for_each_xml_child_node(animationNode, node) - { - if (xmlNameEqual(animationNode, "animation")) - loadAnimation(animationNode, action, imageSet, variant_offset); - } -} - -void SpriteDef::loadAnimation(const XmlNodePtr animationNode, - Action *const action, - const ImageSet *const imageSet, - const int variant_offset) const -{ - if (!action || !imageSet || !animationNode) - return; - - const std::string directionName = - XML::getProperty(animationNode, "direction", ""); - const SpriteDirection::Type directionType - = makeSpriteDirection(directionName); - - if (directionType == SpriteDirection::INVALID) - { - logger->log("Warning: Unknown direction \"%s\" used in %s", - directionName.c_str(), getIdPath().c_str()); - return; - } - - Animation *const animation = new Animation; - action->setAnimation(directionType, animation); - - // Get animation frames - for_each_xml_child_node(frameNode, animationNode) - { - const int delay = XML::getIntProperty( - frameNode, "delay", 0, 0, 100000); - const int offsetX = XML::getProperty(frameNode, "offsetX", 0) - + imageSet->getOffsetX() - imageSet->getWidth() / 2 - + mapTileSize / 2; - const int offsetY = XML::getProperty(frameNode, "offsetY", 0) - + imageSet->getOffsetY() - imageSet->getHeight() + mapTileSize; - const int rand = XML::getIntProperty(frameNode, "rand", 100, 0, 100); - - if (xmlNameEqual(frameNode, "frame")) - { - const int index = XML::getProperty(frameNode, "index", -1); - - if (index < 0) - { - logger->log1("No valid value for 'index'"); - continue; - } - - Image *const img = imageSet->get(index + variant_offset); - - if (!img) - { - logger->log("No image at index %d", index + variant_offset); - continue; - } - - animation->addFrame(img, delay, offsetX, offsetY, rand); - } - else if (xmlNameEqual(frameNode, "sequence")) - { - const int start = XML::getProperty(frameNode, "start", -1); - const int end = XML::getProperty(frameNode, "end", -1); - const std::string value = XML::getProperty(frameNode, "value", ""); - const int repeat = XML::getIntProperty( - frameNode, "repeat", 1, 0, 100); - - if (repeat < 1) - { - logger->log1("No valid value for 'repeat'"); - continue; - } - - if (value.empty()) - { - if (addSequence(start, end, delay, offsetX, offsetY, - variant_offset, repeat, rand, imageSet, animation)) - { - continue; - } - } - else - { - StringVect vals; - splitToStringVector(vals, value, ','); - FOR_EACH (StringVectCIter, it, vals) - { - const std::string str = *it; - const size_t idx = str.find("-"); - if (str == "p") - { - animation->addPause(delay, rand); - } - else if (idx != std::string::npos) - { - const int v1 = atoi(str.substr(0, idx).c_str()); - const int v2 = atoi(str.substr(idx + 1).c_str()); - addSequence(v1, v2, delay, offsetX, offsetY, - variant_offset, repeat, rand, imageSet, animation); - } - else - { - Image *const img = imageSet->get(atoi( - str.c_str()) + variant_offset); - if (img) - { - animation->addFrame(img, delay, - offsetX, offsetY, rand); - } - } - } - } - } - else if (xmlNameEqual(frameNode, "pause")) - { - animation->addPause(delay, rand); - } - else if (xmlNameEqual(frameNode, "end")) - { - animation->addTerminator(rand); - } - else if (xmlNameEqual(frameNode, "jump")) - { - animation->addJump(XML::getProperty( - frameNode, "action", ""), rand); - } - else if (xmlNameEqual(frameNode, "label")) - { - const std::string name = XML::getProperty(frameNode, "name", ""); - if (!name.empty()) - animation->addLabel(name); - } - else if (xmlNameEqual(frameNode, "goto")) - { - const std::string name = XML::getProperty(frameNode, "label", ""); - if (!name.empty()) - animation->addGoto(name, rand); - } - } // for frameNode -} - -void SpriteDef::includeSprite(const XmlNodePtr includeNode, const int variant) -{ - std::string filename = XML::getProperty(includeNode, "file", ""); - - if (filename.empty()) - return; - filename = paths.getStringValue("sprites").append(filename); - - if (mProcessedFiles.find(filename) != mProcessedFiles.end()) - { - logger->log("Error, Tried to include %s which already is included.", - filename.c_str()); - return; - } - mProcessedFiles.insert(filename); - - XML::Document doc(filename, UseResman_true, SkipError_false); - const XmlNodePtr rootNode = doc.rootNode(); - - if (!rootNode || !xmlNameEqual(rootNode, "sprite")) - { - logger->log("Error, no sprite root node in %s", filename.c_str()); - return; - } - - loadSprite(rootNode, variant); -} - -SpriteDef::~SpriteDef() -{ - // Actions are shared, so ensure they are deleted only once. - std::set actions; - FOR_EACH (Actions::iterator, i, mActions) - { - FOR_EACHP (ActionMap::iterator, it, (*i).second) - actions.insert(it->second); - delete (*i).second; - } - - FOR_EACH (std::set::const_iterator, i, actions) - delete *i; - - mActions.clear(); - - FOR_EACH (ImageSetIterator, i, mImageSets) - { - if (i->second) - { - i->second->decRef(); - i->second = nullptr; - } - } - mImageSets.clear(); -} - -SpriteDirection::Type SpriteDef::makeSpriteDirection(const std::string - &direction) -{ - if (direction.empty() || direction == "default") - return SpriteDirection::DEFAULT; - else if (direction == "up") - return SpriteDirection::UP; - else if (direction == "left") - return SpriteDirection::LEFT; - else if (direction == "right") - return SpriteDirection::RIGHT; - else if (direction == "down") - return SpriteDirection::DOWN; - else if (direction == "upleft") - return SpriteDirection::UPLEFT; - else if (direction == "upright") - return SpriteDirection::UPRIGHT; - else if (direction == "downleft") - return SpriteDirection::DOWNLEFT; - else if (direction == "downright") - return SpriteDirection::DOWNRIGHT; - else - return SpriteDirection::INVALID; -} - -void SpriteDef::addAction(const unsigned hp, const std::string &name, - Action *const action) -{ - const Actions::const_iterator i = mActions.find(hp); - if (i == mActions.end()) - mActions[hp] = new ActionMap(); - - (*mActions[hp])[name] = action; -} - -bool SpriteDef::addSequence(const int start, - const int end, - const int delay, - const int offsetX, - const int offsetY, - const int variant_offset, - int repeat, - const int rand, - const ImageSet *const imageSet, - Animation *const animation) -{ - if (!imageSet || !animation) - return true; - - if (start < 0 || end < 0) - { - logger->log1("No valid value for 'start' or 'end'"); - return true; - } - - if (start <= end) - { - while (repeat > 0) - { - int pos = start; - while (end >= pos) - { - Image *const img = imageSet->get(pos + variant_offset); - - if (!img) - { - logger->log("No image at index %d", - pos + variant_offset); - pos ++; - continue; - } - - animation->addFrame(img, delay, - offsetX, offsetY, rand); - pos ++; - } - repeat --; - } - } - else - { - while (repeat > 0) - { - int pos = start; - while (end <= pos) - { - Image *const img = imageSet->get(pos + variant_offset); - - if (!img) - { - logger->log("No image at index %d", - pos + variant_offset); - pos ++; - continue; - } - - animation->addFrame(img, delay, - offsetX, offsetY, rand); - pos --; - } - repeat --; - } - } - return false; -} diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h deleted file mode 100644 index d24837aec..000000000 --- a/src/resources/spritedef.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * The ManaPlus Client - * Copyright (C) 2004-2009 The Mana World Development Team - * Copyright (C) 2009-2010 The Mana Developers - * Copyright (C) 2011-2015 The ManaPlus Developers - * - * This file is part of The ManaPlus Client. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef RESOURCES_SPRITEDEF_H -#define RESOURCES_SPRITEDEF_H - -#include "resources/resource.h" - -#include "resources/spritedirection.h" - -#include "utils/xml.h" - -#include -#include - -class Action; -class Animation; -class ImageSet; - -/** - * Defines a class to load an animation. - */ -class SpriteDef final : public Resource -{ - public: - A_DELETE_COPY(SpriteDef) - - /** - * Loads a sprite definition file. - */ - static SpriteDef *load(const std::string &file, - const int variant, - const bool prot) A_WARN_UNUSED; - - /** - * Returns the specified action. - */ - const Action *getAction(const std::string &action, - const unsigned num) const A_WARN_UNUSED; - - unsigned findNumber(const unsigned num) const A_WARN_UNUSED; - - /** - * Converts a string into a SpriteDirection enum. - */ - static SpriteDirection::Type - makeSpriteDirection(const std::string &direction) A_WARN_UNUSED; - - void addAction(const unsigned hp, const std::string &name, - Action *const action); - - static bool addSequence(const int start, - const int end, - const int delay, - const int offsetX, - const int offsetY, - const int variant_offset, - int repeat, - const int rand, - const ImageSet *const imageSet, - Animation *const animation); - - private: - /** - * Constructor. - */ - SpriteDef() : - Resource(), - mImageSets(), - mActions(), - mProcessedFiles() - { } - - /** - * Destructor. - */ - ~SpriteDef(); - - /** - * Loads a sprite element. - */ - void loadSprite(const XmlNodePtr spriteNode, - const int variant, - const std::string &palettes = ""); - - /** - * Loads an imageset element. - */ - void loadImageSet(const XmlNodePtr node, - const std::string &palettes); - - /** - * Loads an action element. - */ - void loadAction(const XmlNodePtr node, - const int variant_offset); - - /** - * Loads an animation element. - */ - void loadAnimation(const XmlNodePtr animationNode, - Action *const action, - const ImageSet *const imageSet, - const int variant_offset) const; - - /** - * Include another sprite into this one. - */ - void includeSprite(const XmlNodePtr includeNode, const int variant); - - /** - * Complete missing actions by copying existing ones. - */ - void substituteActions(); - - /** - * Fix bad timeout in last dead action frame - */ - void fixDeadAction(); - - /** - * When there are no animations defined for the action "complete", its - * animations become a copy of those of the action "with". - */ - void substituteAction(const std::string &restrict complete, - const std::string &restrict with); - - typedef std::map ImageSets; - typedef ImageSets::iterator ImageSetIterator; - typedef std::map ActionMap; - typedef std::map Actions; - typedef Actions::const_iterator ActionsConstIter; - typedef Actions::iterator ActionsIter; - - ImageSets mImageSets; - Actions mActions; - std::set mProcessedFiles; -}; - -#endif // RESOURCES_SPRITEDEF_H diff --git a/src/sprite.h b/src/sprite.h index 1062615c9..3575eea2f 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -22,7 +22,7 @@ #ifndef SPRITE_H #define SPRITE_H -#include "resources/spritedef.h" +#include "resources/sprite/spritedef.h" #include "localconsts.h" -- cgit v1.2.3-70-g09d2