summaryrefslogtreecommitdiff
path: root/src/resources/spritedef.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/resources/spritedef.cpp')
-rw-r--r--src/resources/spritedef.cpp339
1 files changed, 339 insertions, 0 deletions
diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp
new file mode 100644
index 000000000..dddee575f
--- /dev/null
+++ b/src/resources/spritedef.cpp
@@ -0,0 +1,339 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "resources/spritedef.h"
+
+#include "log.h"
+
+#include "resources/action.h"
+#include "resources/animation.h"
+#include "resources/dye.h"
+#include "resources/image.h"
+#include "resources/imageset.h"
+#include "resources/resourcemanager.h"
+
+#include "configuration.h"
+
+#include "utils/xml.h"
+
+#include <set>
+
+SpriteReference *SpriteReference::Empty = new SpriteReference(
+ paths.getStringValue("spriteErrorFile"), 0);
+
+Action *SpriteDef::getAction(std::string action) const
+{
+ Actions::const_iterator i = mActions.find(action);
+
+ if (i == mActions.end())
+ {
+ logger->log("Warning: no action \"%s\" defined!", action.c_str());
+ return NULL;
+ }
+
+ return i->second;
+}
+
+SpriteDef *SpriteDef::load(const std::string &animationFile, int variant)
+{
+ std::string::size_type pos = animationFile.find('|');
+ std::string palettes;
+ if (pos != std::string::npos)
+ palettes = animationFile.substr(pos + 1);
+
+ XML::Document doc(animationFile.substr(0, pos));
+ xmlNodePtr rootNode = doc.rootNode();
+
+ if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "sprite"))
+ {
+ logger->log("Error, failed to parse %s", animationFile.c_str());
+
+ std::string errorFile = paths.getStringValue("sprites")
+ + paths.getStringValue("spriteErrorFile");
+ if (animationFile != errorFile)
+ return load(errorFile, 0);
+ else
+ return NULL;
+ }
+
+ SpriteDef *def = new SpriteDef;
+ def->loadSprite(rootNode, variant, palettes);
+ def->substituteActions();
+ return def;
+}
+
+void SpriteDef::substituteAction(std::string complete, std::string with)
+{
+ if (mActions.find(complete) == mActions.end())
+ {
+ Actions::iterator i = mActions.find(with);
+ if (i != mActions.end())
+ mActions[complete] = i->second;
+ }
+}
+
+void SpriteDef::substituteActions()
+{
+ substituteAction(SpriteAction::STAND, SpriteAction::DEFAULT);
+ substituteAction(SpriteAction::MOVE, SpriteAction::STAND);
+ substituteAction(SpriteAction::ATTACK, SpriteAction::STAND);
+ substituteAction(SpriteAction::CAST_MAGIC, SpriteAction::ATTACK);
+ substituteAction(SpriteAction::USE_ITEM, SpriteAction::CAST_MAGIC);
+ substituteAction(SpriteAction::SIT, SpriteAction::STAND);
+ substituteAction(SpriteAction::SLEEP, SpriteAction::SIT);
+ substituteAction(SpriteAction::HURT, SpriteAction::STAND);
+ substituteAction(SpriteAction::DEAD, SpriteAction::HURT);
+}
+
+void SpriteDef::loadSprite(xmlNodePtr spriteNode, int variant,
+ const std::string &palettes)
+{
+ // 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 (xmlStrEqual(node->name, BAD_CAST "imageset"))
+ loadImageSet(node, palettes);
+ else if (xmlStrEqual(node->name, BAD_CAST "action"))
+ loadAction(node, variant_offset);
+ else if (xmlStrEqual(node->name, BAD_CAST "include"))
+ includeSprite(node);
+ }
+}
+
+void SpriteDef::loadImageSet(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);
+
+ ResourceManager *resman = ResourceManager::getInstance();
+ ImageSet *imageSet = resman->getImageSet(imageSrc, width, height);
+
+ if (!imageSet)
+ {
+ logger->log1("Couldn't load imageset!");
+ return;
+ }
+
+ mImageSets[name] = imageSet;
+}
+
+void SpriteDef::loadAction(xmlNodePtr node, int variant_offset)
+{
+ const std::string actionName = XML::getProperty(node, "name", "");
+ const std::string imageSetName = XML::getProperty(node, "imageset", "");
+
+ 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;
+ }
+ ImageSet *imageSet = si->second;
+
+ if (actionName == SpriteAction::INVALID)
+ {
+ logger->log("Warning: Unknown action \"%s\" defined in %s",
+ actionName.c_str(), getIdPath().c_str());
+ return;
+ }
+ Action *action = new Action;
+ mActions[actionName] = action;
+
+ // dirty hack to fix bad resources in tmw server
+ if (actionName == "attack_stab")
+ mActions["attack"] = action;
+
+ // When first action set it as default direction
+ if (mActions.size() == 1)
+ mActions[SpriteAction::DEFAULT] = action;
+
+ // Load animations
+ for_each_xml_child_node(animationNode, node)
+ {
+ if (xmlStrEqual(animationNode->name, BAD_CAST "animation"))
+ loadAnimation(animationNode, action, imageSet, variant_offset);
+ }
+}
+
+void SpriteDef::loadAnimation(xmlNodePtr animationNode,
+ Action *action, ImageSet *imageSet,
+ int variant_offset)
+{
+ if (!action || !imageSet)
+ return;
+
+ const std::string directionName =
+ XML::getProperty(animationNode, "direction", "");
+ const SpriteDirection directionType = makeSpriteDirection(directionName);
+
+ if (directionType == DIRECTION_INVALID)
+ {
+ logger->log("Warning: Unknown direction \"%s\" used in %s",
+ directionName.c_str(), getIdPath().c_str());
+ return;
+ }
+
+ Animation *animation = new Animation;
+ action->setAnimation(directionType, animation);
+
+ // Get animation frames
+ for_each_xml_child_node(frameNode, animationNode)
+ {
+ const int delay = XML::getProperty(frameNode, "delay", 0);
+ int offsetX = XML::getProperty(frameNode, "offsetX", 0);
+ int offsetY = XML::getProperty(frameNode, "offsetY", 0);
+ offsetY -= imageSet->getHeight() - 32;
+ offsetX -= imageSet->getWidth() / 2 - 16;
+
+ if (xmlStrEqual(frameNode->name, BAD_CAST "frame"))
+ {
+ const int index = XML::getProperty(frameNode, "index", -1);
+
+ if (index < 0)
+ {
+ logger->log1("No valid value for 'index'");
+ continue;
+ }
+
+ Image *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);
+ }
+ else if (xmlStrEqual(frameNode->name, BAD_CAST "sequence"))
+ {
+ int start = XML::getProperty(frameNode, "start", -1);
+ const int end = XML::getProperty(frameNode, "end", -1);
+
+ if (start < 0 || end < 0)
+ {
+ logger->log1("No valid value for 'start' or 'end'");
+ continue;
+ }
+
+ while (end >= start)
+ {
+ Image *img = imageSet->get(start + variant_offset);
+
+ if (!img)
+ {
+ logger->log("No image at index %d",
+ start + variant_offset);
+ start++;
+ continue;
+ }
+
+ animation->addFrame(img, delay, offsetX, offsetY);
+ start++;
+ }
+ }
+ else if (xmlStrEqual(frameNode->name, BAD_CAST "end"))
+ {
+ animation->addTerminator();
+ }
+ } // for frameNode
+}
+
+void SpriteDef::includeSprite(xmlNodePtr includeNode)
+{
+ // TODO: Perform circular dependency check, since it's easy to crash the
+ // client this way.
+ const std::string filename = XML::getProperty(includeNode, "file", "");
+
+ if (filename.empty())
+ return;
+
+ XML::Document doc(paths.getStringValue("sprites") + filename);
+ xmlNodePtr rootNode = doc.rootNode();
+
+ if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "sprite"))
+ {
+ logger->log("Error, no sprite root node in %s", filename.c_str());
+ return;
+ }
+
+ loadSprite(rootNode, 0);
+}
+
+SpriteDef::~SpriteDef()
+{
+ // Actions are shared, so ensure they are deleted only once.
+ std::set< Action * > actions;
+ for (Actions::const_iterator i = mActions.begin(),
+ i_end = mActions.end(); i != i_end; ++i)
+ {
+ actions.insert(i->second);
+ }
+
+ for (std::set< Action * >::const_iterator i = actions.begin(),
+ i_end = actions.end(); i != i_end; ++i)
+ {
+ delete *i;
+ }
+
+//need actions.clear?
+
+ for (ImageSetIterator i = mImageSets.begin();
+ i != mImageSets.end(); ++i)
+ {
+ i->second->decRef();
+ }
+}
+
+SpriteDirection SpriteDef::makeSpriteDirection(const std::string &direction)
+{
+ if (direction.empty() || direction == "default")
+ return DIRECTION_DEFAULT;
+ else if (direction == "up")
+ return DIRECTION_UP;
+ else if (direction == "left")
+ return DIRECTION_LEFT;
+ else if (direction == "right")
+ return DIRECTION_RIGHT;
+ else if (direction == "down")
+ return DIRECTION_DOWN;
+ else
+ return DIRECTION_INVALID;
+}