/*
* The Mana World
* Copyright 2004 The Mana World Development Team
*
* This file is part of The Mana World.
*
* The Mana World 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.
*
* The Mana World 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 The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id$
*/
#include "animation.h"
#include <libxml/tree.h>
#include "log.h"
#include "graphic/spriteset.h"
#include "resources/image.h"
#include "resources/resourcemanager.h"
#define READ_PROP(node, prop, name, target, cast) \
prop = xmlGetProp(node, BAD_CAST name); \
if (prop) { \
target = cast((const char*)prop); \
xmlFree(prop); \
}
Animation::Animation():
mTime(0)
{
iCurrentPhase = mAnimationPhases.begin();
}
void
Animation::update(unsigned int time)
{
mTime += time;
if (!mAnimationPhases.empty())
{
while ((mTime > (*iCurrentPhase).delay) &&
(*iCurrentPhase).delay > 0)
{
mTime -= (*iCurrentPhase).delay;
iCurrentPhase++;
if (iCurrentPhase == mAnimationPhases.end())
{
iCurrentPhase = mAnimationPhases.begin();
}
}
}
}
int
Animation::getCurrentPhase()
{
if (mAnimationPhases.empty())
{
return -1;
}
else
{
return (*iCurrentPhase).image;
}
}
void
Animation::addPhase (int image, unsigned int delay, int offsetX, int offsetY)
{
//add new phase to animation list
AnimationPhase newPhase;
newPhase.image = image;
newPhase.delay = delay;
newPhase.offsetX = offsetX;
newPhase.offsetY = offsetY;
mAnimationPhases.push_back(newPhase);
//reset animation circle
iCurrentPhase = mAnimationPhases.begin();
}
Action::Action()
:mImageset("")
{
//NOOP
}
Action::~Action()
{
for (AnimationIterator i = mAnimations.begin(); i != mAnimations.end(); i++)
{
delete i->second;
}
mAnimations.clear();
}
Animation *
Action::getAnimation(std::string direction)
{
Animation *animation = NULL;
AnimationIterator i = mAnimations.find(direction);
if (i == mAnimations.end())
{
//when the direction isn't defined just use another one
animation = mAnimations["default"];
}
else
{
animation = mAnimations[direction];
}
return animation;
}
void
Action::setAnimation(std::string direction, Animation *animation)
{
//set first direction as default direction
if (mAnimations.empty())
{
mAnimations["default"] = animation;
}
mAnimations[direction] = animation;
}
AnimatedSprite::AnimatedSprite(std::string animationFile, int variant):
mAction("stand"), mDirection("down"), mLastTime(0)
{
int variant_num = 0;
int variant_offset = 0;
int size;
ResourceManager *resman = ResourceManager::getInstance();
char *data = (char*)resman->loadFile(
animationFile.c_str(), size);
if (!data) {
logger->error("Animation: Could not find " + animationFile + " !");
}
xmlDocPtr doc = xmlParseMemory(data, size);
free(data);
if (!doc)
{
logger->error("Animation: Error while parsing animation definition file!");
return;
}
xmlNodePtr node = xmlDocGetRootElement(doc);
if (!node || !xmlStrEqual(node->name, BAD_CAST "sprite"))
{
logger->error("Animation: this is not a valid animation definition file!");
return;
}
//get the variant
xmlChar *prop = NULL;
READ_PROP(node, prop, "variants", variant_num, atoi);
READ_PROP(node, prop, "variant_offset", variant_offset, atoi);
if (variant_num > 0 && variant < variant_num )
{
variant_offset *= variant;
}
else
{
variant_offset = 0;
}
for (node = node->xmlChildrenNode; node != NULL; node = node->next)
{
if (xmlStrEqual(node->name, BAD_CAST "imageset"))
{
int width = 0, height = 0;
std::string name = "", imageSrc = "";
xmlChar *prop = NULL;
READ_PROP(node, prop, "name", name, );
READ_PROP(node, prop, "src", imageSrc, );
READ_PROP(node, prop, "width", width, atoi);
READ_PROP(node, prop, "height", height, atoi);
Spriteset *spriteset = resman->createSpriteset(
imageSrc, width, height);
if (!spriteset)
{
logger->error("Couldn't load spriteset!");
}
else
{
mSpritesets[name] = spriteset;
}
}
// get action
else if (xmlStrEqual(node->name, BAD_CAST "action"))
{
std::string name = "", imageset = "";
xmlChar *prop = NULL;
READ_PROP(node, prop, "name", name, );
READ_PROP(node, prop, "imageset", imageset, );
Action *action = new Action();
mActions[name] = action;
action->setImageset(imageset);
//get animations
for ( xmlNodePtr animationNode = node->xmlChildrenNode;
animationNode != NULL;
animationNode = animationNode->next)
{
if (xmlStrEqual(animationNode->name, BAD_CAST "animation"))
{
std::string direction = "";
Animation *animation = new Animation();
READ_PROP(animationNode, prop, "direction", direction, );
//get animation phases
for ( xmlNodePtr phaseNode = animationNode->xmlChildrenNode;
phaseNode != NULL;
phaseNode = phaseNode->next)
{
int index = -1;
int start = 0;
int end = 0;
int delay = 0;
int offsetX = 0;
int offsetY = 0;
if (xmlStrEqual(phaseNode->name, BAD_CAST "frame"))
{
READ_PROP(phaseNode, prop, "index", index, atoi);
READ_PROP(phaseNode, prop, "delay", delay, atoi);
READ_PROP(phaseNode, prop, "offsetX", offsetX, atoi);
READ_PROP(phaseNode, prop, "offsetY", offsetY, atoi);
offsetY = offsetY - mSpritesets[imageset]->getHeight() + 32;
offsetX = offsetX - mSpritesets[imageset]->getWidth() / 2 + 16;
animation->addPhase(index + variant_offset, delay, offsetX, offsetY);
}
if (xmlStrEqual(phaseNode->name, BAD_CAST "sequence"))
{
READ_PROP(phaseNode, prop, "start", start, atoi);
READ_PROP(phaseNode, prop, "end", end, atoi);
READ_PROP(phaseNode, prop, "delay", delay, atoi);
offsetY = 0 - mSpritesets[imageset]->getHeight() + 32;
offsetX = 0 - mSpritesets[imageset]->getWidth() / 2 + 16;
while (end >= start)
{
animation->addPhase(start + variant_offset, delay, offsetX, offsetY);
start++;
}
}
} // for phaseNode
action->setAnimation(direction, animation);
} // if "<animation>"
} // for animationNode
} // if "<imageset>" else if "<action>"
} // for node
//complete missing actions
substituteAction("stand", "");
substituteAction("walk", "stand");
substituteAction("walk", "run");
substituteAction("attack", "stand");
substituteAction("attack_swing", "attack");
substituteAction("attack_stab", "attack_swing");
substituteAction("attack_bow", "attack_stab");
substituteAction("attack_throw", "attack_swing");
substituteAction("cast_magic", "attack_swing");
substituteAction("use_item", "cast_magic");
substituteAction("sit", "stand");
substituteAction("sleeping", "sit");
substituteAction("hurt", "stand");
substituteAction("dead", "hurt");
xmlFreeDoc(doc);
}
void
AnimatedSprite::substituteAction(std::string complete, std::string with)
{
if (mActions.find(complete) == mActions.end())
{
mActions[complete] = mActions[with];
}
}
AnimatedSprite::~AnimatedSprite()
{
for (SpritesetIterator i = mSpritesets.begin(); i != mSpritesets.end(); i++)
{
delete i->second;
}
mSpritesets.clear();
}
void
AnimatedSprite::play(std::string action)
{
if (mAction != action)
{
mAction = action;
}
mLastTime = 0;
}
void
AnimatedSprite::play(std::string action, std::string direction)
{
play(action);
mDirection = direction;
}
void
AnimatedSprite::update(int time)
{
//avoid freaking out at first frame or when tick_time overflows
if (time < mLastTime || mLastTime == 0) mLastTime = time;
Action *action = mActions[mAction];
action->getAnimation(mDirection)->update(time - mLastTime);
mLastTime = time;
}
bool
AnimatedSprite::draw(Graphics * graphics, Sint32 posX, Sint32 posY)
{
Sint32 offsetX, offsetY;
Action *action = mActions[mAction];
Spriteset *spriteset = mSpritesets[action->getImageset()];
Animation *animation = action->getAnimation(mDirection);
if (animation->getCurrentPhase() >= 0)
{
Image *image = spriteset->get(animation->getCurrentPhase());
offsetX = animation->getOffsetX();
offsetY = animation->getOffsetY();
return graphics->drawImage(image, posX + offsetX, posY + offsetY);
}
else
{
return false;
}
}
Image *
AnimatedSprite::getCurrentFrame()
{
Action *action = mActions[mAction];
Spriteset *spriteset = mSpritesets[action->getImageset()];
Animation *animation = action->getAnimation(mDirection);
if (animation->getCurrentPhase() >= 0)
{
return spriteset->get(animation->getCurrentPhase());
}
else
{
return NULL;
}
}
int
AnimatedSprite::getWidth()
{
Action *action = mActions[mAction];
Spriteset *spriteset = mSpritesets[action->getImageset()];
return spriteset->getWidth();
}
int
AnimatedSprite::getHeight()
{
Action *action = mActions[mAction];
Spriteset *spriteset = mSpritesets[action->getImageset()];
return spriteset->getHeight();
}