From 7ac7d0e030464f744546b4e6183a7242f640d5d3 Mon Sep 17 00:00:00 2001 From: Bjørn Lindeijer Date: Sun, 19 Nov 2006 21:24:36 +0000 Subject: Separated sprite definition from playback. --- src/CMakeLists.txt | 2 + src/Makefile.am | 2 + src/action.cpp | 10 - src/action.h | 9 - src/animatedsprite.cpp | 408 +++++++++----------------------------- src/animatedsprite.h | 115 +++-------- src/animation.cpp | 56 +----- src/animation.h | 47 ++--- src/being.cpp | 14 -- src/being.h | 2 +- src/game.cpp | 14 +- src/localplayer.cpp | 39 +++- src/localplayer.h | 10 +- src/monster.cpp | 3 +- src/player.cpp | 16 -- src/player.h | 8 - src/resources/resourcemanager.cpp | 47 ++++- src/resources/resourcemanager.h | 17 +- src/resources/spritedef.cpp | 329 ++++++++++++++++++++++++++++++ src/resources/spritedef.h | 130 ++++++++++++ src/resources/spriteset.h | 6 +- 21 files changed, 718 insertions(+), 566 deletions(-) create mode 100644 src/resources/spritedef.cpp create mode 100644 src/resources/spritedef.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3981ee58..51b68935 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -221,6 +221,8 @@ SET(SRCS resources/sdlimageloader.cpp resources/soundeffect.h resources/soundeffect.cpp + resources/spritedef.h + resources/spritedef.cpp resources/spriteset.h resources/spriteset.cpp resources/buddylist.h diff --git a/src/Makefile.am b/src/Makefile.am index 5f29bebc..8715bac6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -183,6 +183,8 @@ tmw_SOURCES = graphic/imagerect.h \ resources/sdlimageloader.cpp \ resources/soundeffect.h \ resources/soundeffect.cpp \ + resources/spritedef.h \ + resources/spritedef.cpp \ resources/spriteset.h \ resources/spriteset.cpp \ resources/buddylist.h \ diff --git a/src/action.cpp b/src/action.cpp index 0a382af3..148ea105 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -64,13 +64,3 @@ Action::setAnimation(int direction, Animation *animation) mAnimations[direction] = animation; } - -void -Action::reset() -{ - for (AnimationIterator i = mAnimations.begin(); - i != mAnimations.end(); ++i) - { - i->second->reset(); - } -} diff --git a/src/action.h b/src/action.h index 3dcc02f7..8d5e8d11 100644 --- a/src/action.h +++ b/src/action.h @@ -28,9 +28,6 @@ #include -class Image; - -struct AnimationPhase; class Animation; /** @@ -52,12 +49,6 @@ class Action void setAnimation(int direction, Animation *animation); - /** - * Resets all animations associated with this action. - */ - void - reset(); - Animation* getAnimation(int direction) const; diff --git a/src/animatedsprite.cpp b/src/animatedsprite.cpp index c2f7f133..46369c80 100644 --- a/src/animatedsprite.cpp +++ b/src/animatedsprite.cpp @@ -34,381 +34,167 @@ #include "utils/xml.h" -AnimatedSprite::AnimatedSprite(const std::string& animationFile, int variant): - mAction(NULL), +#include + +AnimatedSprite::AnimatedSprite(SpriteDef *sprite): mDirection(DIRECTION_DOWN), - mLastTime(0) + mLastTime(0), + mFrameIndex(0), + mFrameTime(0), + mSprite(sprite), + mAction(0), + mAnimation(0), + mFrame(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!"); - } - - xmlNodePtr node = xmlDocGetRootElement(doc); - if (!node || !xmlStrEqual(node->name, BAD_CAST "sprite")) { - logger->error( - "Animation: this is not a valid animation definition file!"); - } - - // Get the variant - int variant_num = XML::getProperty(node, "variants", 0); - int variant_offset = XML::getProperty(node, "variant_offset", 0); - - if (variant_num > 0 && variant < variant_num ) { - variant_offset *= variant; - } else { - variant_offset = 0; - } + assert(mSprite); - for (node = node->xmlChildrenNode; node != NULL; node = node->next) - { - if (xmlStrEqual(node->name, BAD_CAST "imageset")) - { - int width = XML::getProperty(node, "width", 0); - int height = XML::getProperty(node, "height", 0); - std::string name = XML::getProperty(node, "name", ""); - std::string imageSrc = XML::getProperty(node, "src", ""); - - Spriteset *spriteset = - resman->getSpriteset(imageSrc, width, height); - - if (!spriteset) { - logger->error("Couldn't load spriteset!"); - } - - mSpritesets[name] = spriteset; - } - // get action - else if (xmlStrEqual(node->name, BAD_CAST "action")) - { - std::string actionName = XML::getProperty(node, "name", ""); - std::string imagesetName = XML::getProperty(node, "imageset", ""); - - SpritesetIterator si = mSpritesets.find(imagesetName); - if (si == mSpritesets.end()) { - logger->log("Warning: imageset \"%s\" not defined in %s", - imagesetName.c_str(), - animationFile.c_str()); - - // skip loading animations - continue; - } - Spriteset *imageset = si->second; - - SpriteAction actionType = makeSpriteAction(actionName); - if (actionType == ACTION_INVALID) - { - logger->log("Warning: Unknown action \"%s\" defined in %s", - actionName.c_str(), - animationFile.c_str()); - continue; - } - Action *action = new Action(); - mActions[actionType] = action; - - // When first action set it as default direction - if (mActions.empty()) - { - mActions[ACTION_DEFAULT] = action; - } - - - // get animations - for (xmlNodePtr animationNode = node->xmlChildrenNode; - animationNode != NULL; - animationNode = animationNode->next) - { - // We're only interested in animations - if (!xmlStrEqual(animationNode->name, BAD_CAST "animation")) - continue; - - std::string directionName = - XML::getProperty(animationNode, "direction", ""); - SpriteDirection directionType = - makeSpriteDirection(directionName); - - if (directionType == DIRECTION_INVALID) - { - logger->log("Warning: Unknown direction \"%s\" defined " - "for action %s in %s", - directionName.c_str(), - actionName.c_str(), - animationFile.c_str()); - continue; - } - - Animation *animation = new Animation(); - action->setAnimation(directionType, animation); - - // Get animation phases - for (xmlNodePtr phaseNode = animationNode->xmlChildrenNode; - phaseNode != NULL; - phaseNode = phaseNode->next) - { - int delay = XML::getProperty(phaseNode, "delay", 0); - - if (xmlStrEqual(phaseNode->name, BAD_CAST "frame")) - { - int index = XML::getProperty(phaseNode, "index", -1); - int offsetX = XML::getProperty(phaseNode, "offsetX", 0); - int offsetY = XML::getProperty(phaseNode, "offsetY", 0); - - offsetY -= imageset->getHeight() - 32; - offsetX -= imageset->getWidth() / 2 - 16; - Image *img = imageset->get(index + variant_offset); - animation->addPhase(img, delay, offsetX, offsetY); - } - else if (xmlStrEqual(phaseNode->name, BAD_CAST "sequence")) - { - int start = XML::getProperty(phaseNode, "start", 0); - int end = XML::getProperty(phaseNode, "end", 0); - int offsetY = -imageset->getHeight() + 32; - int offsetX = -imageset->getWidth() / 2 + 16; - while (end >= start) - { - Image *img = imageset->get(start + variant_offset); - animation->addPhase(img, delay, offsetX, offsetY); - start++; - } - } - else if (xmlStrEqual(phaseNode->name, BAD_CAST "end")) - { - animation->addTerminator(); - } - } // for phaseNode - } // for animationNode - } // if "" else if "" - } // for node - - // Complete missing actions - substituteAction(ACTION_STAND, ACTION_DEFAULT); - substituteAction(ACTION_WALK, ACTION_STAND); - substituteAction(ACTION_WALK, ACTION_RUN); - substituteAction(ACTION_ATTACK, ACTION_STAND); - substituteAction(ACTION_ATTACK_SWING, ACTION_ATTACK); - substituteAction(ACTION_ATTACK_STAB, ACTION_ATTACK_SWING); - substituteAction(ACTION_ATTACK_BOW, ACTION_ATTACK_STAB); - substituteAction(ACTION_ATTACK_THROW, ACTION_ATTACK_SWING); - substituteAction(ACTION_CAST_MAGIC, ACTION_ATTACK_SWING); - substituteAction(ACTION_USE_ITEM, ACTION_CAST_MAGIC); - substituteAction(ACTION_SIT, ACTION_STAND); - substituteAction(ACTION_SLEEP, ACTION_SIT); - substituteAction(ACTION_HURT, ACTION_STAND); - substituteAction(ACTION_DEAD, ACTION_HURT); + // Take possession of the sprite + mSprite->incRef(); // Play the stand animation by default play(ACTION_STAND); - - xmlFreeDoc(doc); } -void -AnimatedSprite::substituteAction(SpriteAction complete, - SpriteAction with) +AnimatedSprite::AnimatedSprite(const std::string& filename, int variant): + mDirection(DIRECTION_DOWN), + mLastTime(0), + mFrameIndex(0), + mFrameTime(0), + mAnimation(0), + mFrame(0) { - if (mActions.find(complete) == mActions.end()) - { - ActionIterator i = mActions.find(with); - if (i != mActions.end()) { - mActions[complete] = i->second; - } - } + ResourceManager *resman = ResourceManager::getInstance(); + mSprite = resman->getSprite(filename, variant); + assert(mSprite); + + // Play the stand animation by default + play(ACTION_STAND); } AnimatedSprite::~AnimatedSprite() { - for (SpritesetIterator i = mSpritesets.begin(); i != mSpritesets.end(); ++i) - { - i->second->decRef(); - } - mSpritesets.clear(); + mSprite->decRef(); } void AnimatedSprite::reset() { - // Reset all defined actions (because of aliases some will be resetted - // multiple times, but this doesn't matter) - for (ActionIterator i = mActions.begin(); i != mActions.end(); ++i) - { - if (i->second) - { - i->second->reset(); - } - } + mFrameIndex = 0; + mFrameTime = 0; + mLastTime = 0; } void -AnimatedSprite::play(SpriteAction action) +AnimatedSprite::play(SpriteAction spriteAction) { - ActionIterator i = mActions.find(action); - - if (i == mActions.end()) + Action *action = mSprite->getAction(spriteAction); + if (!action) { - logger->log("Warning: no action \"%u\" defined!", action); - mAction = NULL; return; } - if (mAction != i->second) + mAction = action; + Animation *animation = mAction->getAnimation(mDirection); + + if (animation && animation != mAnimation && animation->getLength() > 0) { - mAction = i->second; - //mAction->reset(); + mAnimation = animation; + mFrame = mAnimation->getFrame(0); + + reset(); } } void AnimatedSprite::update(int time) { - bool finished = false; // Avoid freaking out at first frame or when tick_time overflows if (time < mLastTime || mLastTime == 0) + { mLastTime = time; + } // If not enough time has passed yet, do nothing - if (time > mLastTime && mAction) + if (time <= mLastTime || !mAnimation) { - Animation *animation = mAction->getAnimation(mDirection); - if (animation != NULL) { - finished = !animation->update((unsigned int)(time - mLastTime)); - } - mLastTime = time; + return; } - if (finished) + unsigned int dt = time - mLastTime; + mLastTime = time; + + if (!updateCurrentAnimation(dt)) { + // Animation finished, reset to default play(ACTION_STAND); } } bool -AnimatedSprite::draw(Graphics* graphics, Sint32 posX, Sint32 posY) const +AnimatedSprite::updateCurrentAnimation(unsigned int time) { - const AnimationPhase *phase = getCurrentPhase(); - if (!phase || !phase->image) + if (!mFrame || Animation::isTerminator(*mFrame)) { return false; } - Sint32 offsetX = phase->offsetX; - Sint32 offsetY = phase->offsetY; - return graphics->drawImage(phase->image, posX + offsetX, posY + offsetY); -} + mFrameTime += time; -int -AnimatedSprite::getWidth() const -{ - const AnimationPhase *phase = getCurrentPhase(); - return (phase && phase->image) ? phase->image->getWidth() : 0; -} + while (mFrameTime > mFrame->delay && mFrame->delay > 0) + { + mFrameTime -= mFrame->delay; + mFrameIndex++; -int -AnimatedSprite::getHeight() const -{ - const AnimationPhase *phase = getCurrentPhase(); - return (phase && phase->image) ? phase->image->getHeight() : 0; -} + if (mFrameIndex == mAnimation->getLength()) + { + mFrameIndex = 0; + } -const AnimationPhase* -AnimatedSprite::getCurrentPhase() const -{ - if (!mAction) - { - return NULL; - } + mFrame = mAnimation->getFrame(mFrameIndex); - Animation *animation = mAction->getAnimation(mDirection); - if (animation == NULL) - { - return NULL; + if (Animation::isTerminator(*mFrame)) + { + mAnimation = 0; + mFrame = 0; + return false; + } } - return animation->getCurrentPhase(); + return true; } -SpriteAction -AnimatedSprite::makeSpriteAction(const std::string& action) +bool +AnimatedSprite::draw(Graphics* graphics, int posX, int posY) const { - if (action == "" || action == "default") { - return ACTION_DEFAULT; - } - if (action == "stand") { - return ACTION_STAND; - } - else if (action == "walk") { - return ACTION_WALK; - } - else if (action == "run") { - return ACTION_RUN; - } - else if (action == "attack") { - return ACTION_ATTACK; - } - else if (action == "attack_swing") { - return ACTION_ATTACK_SWING; - } - else if (action == "attack_stab") { - return ACTION_ATTACK_STAB; - } - else if (action == "attack_bow") { - return ACTION_ATTACK_BOW; - } - else if (action == "attack_throw") { - return ACTION_ATTACK_THROW; - } - else if (action == "cast_magic") { - return ACTION_CAST_MAGIC; - } - else if (action == "use_item") { - return ACTION_USE_ITEM; - } - else if (action == "sit") { - return ACTION_SIT; - } - else if (action == "sleep") { - return ACTION_SLEEP; - } - else if (action == "hurt") { - return ACTION_HURT; - } - else if (action == "dead") { - return ACTION_DEAD; - } - else { - return ACTION_INVALID; + if (!mFrame || !mFrame->image) + { + return false; } + + return graphics->drawImage(mFrame->image, + posX + mFrame->offsetX, + posY + mFrame->offsetY); } -SpriteDirection -AnimatedSprite::makeSpriteDirection(const std::string& direction) +void +AnimatedSprite::setDirection(SpriteDirection direction) { - if (direction == "" || 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; + if (mDirection != direction) + { + mDirection = direction; + + if (!mAction) + { + return; + } + + Animation *animation = mAction->getAnimation(mDirection); + + if (animation && animation != mAnimation && animation->getLength() > 0) + { + mAnimation = animation; + mFrame = mAnimation->getFrame(0); + reset(); + } } - else { - return DIRECTION_INVALID; - }; } diff --git a/src/animatedsprite.h b/src/animatedsprite.h index 721a2824..e5a435f9 100644 --- a/src/animatedsprite.h +++ b/src/animatedsprite.h @@ -24,55 +24,34 @@ #ifndef _TMW_ANIMATEDSPRITE_H #define _TMW_ANIMATEDSPRITE_H +#include "resources/spritedef.h" + #include #include -#include -class Action; class Graphics; -class Spriteset; struct AnimationPhase; -enum SpriteAction -{ - ACTION_DEFAULT = 0, - ACTION_STAND, - ACTION_WALK, - ACTION_RUN, - ACTION_ATTACK, - ACTION_ATTACK_SWING, - ACTION_ATTACK_STAB, - ACTION_ATTACK_BOW, - ACTION_ATTACK_THROW, - ACTION_CAST_MAGIC, - ACTION_USE_ITEM, - ACTION_SIT, - ACTION_SLEEP, - ACTION_HURT, - ACTION_DEAD, - ACTION_INVALID -}; - -enum SpriteDirection -{ - DIRECTION_DEFAULT = 0, - DIRECTION_DOWN, - DIRECTION_UP, - DIRECTION_LEFT, - DIRECTION_RIGHT, - DIRECTION_INVALID -}; - /** - * Defines a class to load an animation. + * Animates a sprite by adding playback state. */ class AnimatedSprite { public: /** * Constructor. + * @param sprite the sprite to animate */ - AnimatedSprite(const std::string& animationFile, int variant); + AnimatedSprite(SpriteDef *sprite); + + /** + * A convenience constructor, which will request the sprite to animate + * from the resource manager. + * + * @param filename the file of the sprite to animate + * @param variant the sprite variant + */ + AnimatedSprite(const std::string& filename, int variant); /** * Destructor. @@ -80,8 +59,7 @@ class AnimatedSprite ~AnimatedSprite(); /** - * Resets the animated sprite. This is used to synchronize several - * animated sprites. + * Resets the animated sprite. */ void reset(); @@ -104,67 +82,28 @@ class AnimatedSprite * pixels. */ bool - draw(Graphics* graphics, Sint32 posX, Sint32 posY) const; - - /** - * Returns the width in pixels of the current animation phase. - */ - int - getWidth() const; - - /** - * Returns the height in pixels of the current animation phase. - */ - int - getHeight() const; + draw(Graphics* graphics, int posX, int posY) const; /** * Sets the direction. */ void - setDirection(SpriteDirection direction) - { - mDirection = direction; - } + setDirection(SpriteDirection direction); private: - /** - * When there are no animations defined for the action "complete", its - * animations become a copy of those of the action "with". - */ - void - substituteAction(SpriteAction complete, SpriteAction with); - - /** - * Returns the current animation frame. - */ - const AnimationPhase* - getCurrentPhase() const; - - /** - * Converts a string into a SpriteAction enum. - */ - static SpriteAction - makeSpriteAction(const std::string &action); - - /** - * Converts a string into a SpriteDirection enum. - */ - static SpriteDirection - makeSpriteDirection(const std::string &direction); - + bool + updateCurrentAnimation(unsigned int dt); - typedef std::map Spritesets; - typedef Spritesets::iterator SpritesetIterator; + SpriteDirection mDirection; /**< The sprite direction. */ + int mLastTime; /**< The last time update was called. */ - typedef std::map Actions; - typedef Actions::iterator ActionIterator; + unsigned int mFrameIndex; /**< The index of the current frame. */ + unsigned int mFrameTime; /**< The time since start of frame. */ - Spritesets mSpritesets; - Actions mActions; - Action *mAction; - SpriteDirection mDirection; - int mLastTime; + SpriteDef *mSprite; /**< The sprite definition. */ + Action *mAction; /**< The currently active action. */ + Animation *mAnimation; /**< The currently active animation. */ + AnimationPhase *mFrame; /**< The currently active frame. */ }; #endif diff --git a/src/animation.cpp b/src/animation.cpp index a91bdf8d..67fdae11 100644 --- a/src/animation.cpp +++ b/src/animation.cpp @@ -28,74 +28,28 @@ #include "utils/dtor.h" Animation::Animation(): - mLength(0) + mDuration(0) { - reset(); -} - -void -Animation::reset() -{ - mTime = 0; - iCurrentPhase = mAnimationPhases.begin(); -} - -bool -Animation::update(unsigned int time) -{ - mTime += time; - if (mAnimationPhases.empty()) - return true; - if (isTerminator(*iCurrentPhase)) - return false; - - unsigned int delay = iCurrentPhase->delay; - - while (mTime > delay) - { - if (!delay) - return true; - mTime -= delay; - iCurrentPhase++; - if (iCurrentPhase == mAnimationPhases.end()) - { - iCurrentPhase = mAnimationPhases.begin(); - } - if (isTerminator(*iCurrentPhase)) - return false; - delay = iCurrentPhase->delay; - } - return true; -} - -const AnimationPhase* -Animation::getCurrentPhase() const -{ - return mAnimationPhases.empty() ? NULL : &(*iCurrentPhase); } void Animation::addPhase(Image *image, unsigned int delay, int offsetX, int offsetY) { // Add new phase to animation list - AnimationPhase newPhase = { image, delay, offsetX, offsetY}; + AnimationPhase newPhase = { image, delay, offsetX, offsetY }; mAnimationPhases.push_back(newPhase); - mLength += delay; - // Reset animation circle - iCurrentPhase = mAnimationPhases.begin(); + mDuration += delay; } void Animation::addTerminator() { - AnimationPhase terminator = { NULL, 0, 0, 0}; - mAnimationPhases.push_back(terminator); - iCurrentPhase = mAnimationPhases.begin(); + addPhase(NULL, 0, 0, 0); } bool -Animation::isTerminator(AnimationPhase candidate) +Animation::isTerminator(const AnimationPhase candidate) { return (candidate.image == NULL); } diff --git a/src/animation.h b/src/animation.h index 0230e820..85e950d7 100644 --- a/src/animation.h +++ b/src/animation.h @@ -24,8 +24,7 @@ #ifndef _TMW_ANIMATION_H #define _TMW_ANIMATION_H -#include -#include +#include #include @@ -34,6 +33,8 @@ class Spriteset; /** * A single frame in an animation, with a delay and an offset. + * + * TODO: Rename this struct to Frame */ struct AnimationPhase { @@ -55,12 +56,6 @@ class Animation */ Animation(); - /** - * Restarts the animation from the first frame. - */ - void - reset(); - /** * Appends a new animation at the end of the sequence */ @@ -69,46 +64,38 @@ class Animation /** * Appends an animation terminator that states that the animation - * should not loop + * should not loop. */ void addTerminator(); /** - * Updates animation phase. - * true indicates a still running animation while false indicates a - * finished animation + * Returns the frame at the specified index. */ - bool - update(unsigned int time); - - const AnimationPhase* - getCurrentPhase() const; + AnimationPhase* + getFrame(int index) { return &(mAnimationPhases[index]); } /** - * Returns the x offset of the current frame. + * Returns the length of this animation in frames. */ - int - getOffsetX() const { return iCurrentPhase->offsetX; }; + unsigned int + getLength() const { return mAnimationPhases.size(); } /** - * Returns the y offset of the current frame. + * Returns the duration of this animation. */ int - getOffsetY() const { return iCurrentPhase->offsetY; }; + getDuration() const { return mDuration; } /** - * Returns the length of this animation. + * Determines whether the given animation frame is a terminator. */ - int - getLength() const { return mLength; } + static bool + isTerminator(const AnimationPhase phase); protected: - static bool isTerminator(AnimationPhase phase); - std::list mAnimationPhases; - std::list::iterator iCurrentPhase; - unsigned int mTime; - int mLength; + std::vector mAnimationPhases; + int mDuration; }; #endif diff --git a/src/being.cpp b/src/being.cpp index 47f9965c..14d2df00 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -193,24 +193,10 @@ Being::setAction(Uint8 action) currentAction = ACTION_ATTACK; break; } - for (int i = 0; i < VECTOREND_SPRITE; i++) - { - if (mSprites[i]) - { - mSprites[i]->reset(); - } - } }; break; case MONSTER_ATTACK: currentAction = ACTION_ATTACK; - for (int i = 0; i < VECTOREND_SPRITE; i++) - { - if (mSprites[i]) - { - mSprites[i]->reset(); - } - } break; case DEAD: currentAction = ACTION_DEAD; diff --git a/src/being.h b/src/being.h index 42f37058..362fa393 100644 --- a/src/being.h +++ b/src/being.h @@ -206,7 +206,7 @@ class Being : public Sprite /** * Makes this being take the next step of his path. */ - void + virtual void nextStep(); /** diff --git a/src/game.cpp b/src/game.cpp index ba65298f..7da80069 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -86,7 +86,7 @@ bool done = false; volatile int tick_time; volatile int fps = 0, frame = 0; Engine *engine = NULL; -Joystick *joystick; +Joystick *joystick = NULL; extern Window *weightNotice; extern Window *deathNotice; @@ -650,35 +650,35 @@ void Game::handleInput() { Uint16 x = player_node->mX; Uint16 y = player_node->mY; - unsigned char Direction = 0; + unsigned char direction = 0; // Translate pressed keys to movement and direction if (keys[SDLK_UP] || keys[SDLK_KP8] || keys[SDLK_KP7] || keys[SDLK_KP9] || joystick && joystick->isUp()) { - Direction |= Being::UP; + direction |= Being::UP; } else if (keys[SDLK_DOWN] || keys[SDLK_KP2] || keys[SDLK_KP1] || keys[SDLK_KP3] || joystick && joystick->isDown()) { - Direction |= Being::DOWN; + direction |= Being::DOWN; } if (keys[SDLK_LEFT] || keys[SDLK_KP4] || keys[SDLK_KP1] || keys[SDLK_KP7] || joystick && joystick->isLeft()) { - Direction |= Being::LEFT; + direction |= Being::LEFT; } else if (keys[SDLK_RIGHT] || keys[SDLK_KP6] || keys[SDLK_KP3] || keys[SDLK_KP9] || joystick && joystick->isRight()) { - Direction |= Being::RIGHT; + direction |= Being::RIGHT; } - player_node->walk(Direction); + player_node->walk(direction); // Attacking monsters if (keys[SDLK_LCTRL] || keys[SDLK_RCTRL] || diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 802da92f..46348efc 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -82,9 +82,19 @@ void LocalPlayer::logic() void LocalPlayer::nextStep() { - if (mPath.empty() && mPickUpTarget) { - pickUp(mPickUpTarget); + if (mPath.empty()) + { + if (mPickUpTarget) + { + pickUp(mPickUpTarget); + } + + if (mWalkingDir) + { + walk(mWalkingDir); + } } + Player::nextStep(); } @@ -171,10 +181,15 @@ void LocalPlayer::pickUp(FloorItem *item) void LocalPlayer::walk(unsigned char dir) { + if (mWalkingDir != dir) + { + mWalkingDir = dir; + } + if (!mMap || !dir) return; - if (mAction == WALK) + if (mAction == WALK && !mPath.empty()) { // Just finish the current action, otherwise we get out of sync Being::setDestination(mX, mY); @@ -218,14 +233,20 @@ void LocalPlayer::walk(unsigned char dir) void LocalPlayer::setDestination(Uint16 x, Uint16 y) { - char temp[3]; - MessageOut outMsg(mNetwork); - set_coordinates(temp, x, y, mDirection); - outMsg.writeInt16(0x0085); - outMsg.writeString(temp, 3); + // Only send a new message to the server when destination changes + if (x != destX || y != destY) + { + destX = x; + destY = y; - mPickUpTarget = NULL; + char temp[3]; + MessageOut outMsg(mNetwork); + set_coordinates(temp, x, y, mDirection); + outMsg.writeInt16(0x0085); + outMsg.writeString(temp, 3); + } + mPickUpTarget = NULL; Being::setDestination(x, y); } diff --git a/src/localplayer.h b/src/localplayer.h index a3fe91f7..04477af2 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -50,6 +50,11 @@ class LocalPlayer : public Player void setNetwork(Network *network) { mNetwork = network; } virtual void logic(); + + /** + * Adds a new step when walking before calling super. Also, when + * specified it picks up an item at the end of a path. + */ virtual void nextStep(); /** @@ -155,7 +160,10 @@ class LocalPlayer : public Player FloorItem *mPickUpTarget; bool mTrading; - int mLastAction; /**< Time stamp of the last action, -1 if none */ + int mLastAction; /**< Time stamp of the last action, -1 if none. */ + int mWalkingDir; /**< The direction the player is walking in. */ + int destX; /**< X coordinate of destination. */ + int destY; /**< Y coordinate of destination. */ }; extern LocalPlayer *player_node; diff --git a/src/monster.cpp b/src/monster.cpp index 8a7e2f32..ce073a04 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -32,7 +32,8 @@ Monster::Monster(Uint32 id, Uint16 job, Map *map): Being(id, job, map) { - mSprites[BASE_SPRITE] = new AnimatedSprite("graphics/sprites/monster" + toString(job - 1002) + ".xml", 0); + mSprites[BASE_SPRITE] = new AnimatedSprite( + "graphics/sprites/monster" + toString(job - 1002) + ".xml", 0); } void diff --git a/src/player.cpp b/src/player.cpp index 3f0ebfc4..df9b74f6 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -98,7 +98,6 @@ Player::setSex(Uint8 sex) mSprites[BASE_SPRITE] = new AnimatedSprite( "graphics/sprites/player_female_base.xml", 0); } - resetAnimations(); } Being::setSex(sex); } @@ -144,7 +143,6 @@ Player::setHairColor(Uint16 color) delete mSprites[HAIR_SPRITE]; mSprites[HAIR_SPRITE] = newHairSprite; - resetAnimations(); setAction(mAction); } @@ -164,7 +162,6 @@ Player::setHairStyle(Uint16 style) delete mSprites[HAIR_SPRITE]; mSprites[HAIR_SPRITE] = newHairSprite; - resetAnimations(); setAction(mAction); } @@ -206,22 +203,9 @@ Player::setVisibleEquipment(Uint8 slot, Uint8 id) delete mSprites[position]; mSprites[position] = equipmentSprite; - resetAnimations(); setAction(mAction); } Being::setVisibleEquipment(slot, id); } - -void -Player::resetAnimations() -{ - for (int i = 0; i < VECTOREND_SPRITE; i++) - { - if (mSprites[i] != NULL) - { - mSprites[i]->reset(); - } - } -} diff --git a/src/player.h b/src/player.h index e23110be..dab081f8 100644 --- a/src/player.h +++ b/src/player.h @@ -60,14 +60,6 @@ class Player : public Being virtual void setWeapon(Uint16 weapon); - - private: - /** - * Resets all animations associated with this player. This is used to - * synchronize the animations after a new one has been added. - */ - void - resetAnimations(); }; #endif diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index e729ecfc..f9a57e87 100644 --- a/src/resources/resourcemanager.cpp +++ b/src/resources/resourcemanager.cpp @@ -32,6 +32,7 @@ #include "music.h" #include "soundeffect.h" #include "spriteset.h" +#include "spritedef.h" #include "../log.h" @@ -44,11 +45,28 @@ ResourceManager::ResourceManager() ResourceManager::~ResourceManager() { - // Release any remaining spritesets first because they depend on images + // Release any remaining spritedefs first because they depend on spritesets ResourceIterator iter = mResources.begin(); while (iter != mResources.end()) { - if (dynamic_cast(iter->second) != NULL) + if (dynamic_cast(iter->second) != 0) + { + cleanUp(iter->second); + ResourceIterator toErase = iter; + ++iter; + mResources.erase(toErase); + } + else + { + ++iter; + } + } + + // Release any remaining spritesets first because they depend on images + iter = mResources.begin(); + while (iter != mResources.end()) + { + if (dynamic_cast(iter->second) != 0) { cleanUp(iter->second); ResourceIterator toErase = iter; @@ -80,7 +98,7 @@ ResourceManager::cleanUp(Resource *res) bool ResourceManager::setWriteDir(const std::string &path) { - return (bool)PHYSFS_setWriteDir(path.c_str()); + return (bool) PHYSFS_setWriteDir(path.c_str()); } void @@ -93,7 +111,7 @@ ResourceManager::addToSearchPath(const std::string &path, bool append) bool ResourceManager::mkdir(const std::string &path) { - return (bool)PHYSFS_mkdir(path.c_str()); + return (bool) PHYSFS_mkdir(path.c_str()); } bool @@ -212,6 +230,27 @@ ResourceManager::getSpriteset(const std::string &imagePath, int w, int h) return spriteset; } +SpriteDef* +ResourceManager::getSprite(const std::string &path, int variant) +{ + std::stringstream ss; + ss << path << "[" << variant << "]"; + const std::string idPath = ss.str(); + + ResourceIterator resIter = mResources.find(idPath); + + if (resIter != mResources.end()) { + resIter->second->incRef(); + return dynamic_cast(resIter->second); + } + + SpriteDef *sprite = new SpriteDef(idPath, path, variant); + sprite->incRef(); + mResources[idPath] = sprite; + + return sprite; +} + void ResourceManager::release(const std::string &idPath) { diff --git a/src/resources/resourcemanager.h b/src/resources/resourcemanager.h index 0086b167..4367cd5e 100644 --- a/src/resources/resourcemanager.h +++ b/src/resources/resourcemanager.h @@ -34,6 +34,7 @@ class Image; class Music; class SoundEffect; class Spriteset; +class SpriteDef; /** * A class for loading and managing resources. @@ -113,21 +114,21 @@ class ResourceManager get(const E_RESOURCE_TYPE &type, const std::string &idPath); /** - * Convenience wrapper around ResourceManager::create for loading + * Convenience wrapper around ResourceManager::get for loading * images. */ Image* getImage(const std::string &idPath); /** - * Convenience wrapper around ResourceManager::create for loading + * Convenience wrapper around ResourceManager::get for loading * songs. */ Music* getMusic(const std::string &idPath); /** - * Convenience wrapper around ResourceManager::create for loading + * Convenience wrapper around ResourceManager::get for loading * samples. */ SoundEffect* @@ -137,7 +138,15 @@ class ResourceManager * Creates a spriteset based on the image referenced by the given * path and the supplied sprite sizes */ - Spriteset* getSpriteset(const std::string &imagePath, int w, int h); + Spriteset* + getSpriteset(const std::string &imagePath, int w, int h); + + /** + * Creates a sprite definition based on a given path and the supplied + * variant. + */ + SpriteDef* + getSprite(const std::string &path, int variant); /** * Releases a resource, removing it from the set of loaded resources. diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp new file mode 100644 index 00000000..21eae6e0 --- /dev/null +++ b/src/resources/spritedef.cpp @@ -0,0 +1,329 @@ +/* + * 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 "spritedef.h" + +#include "../animation.h" +#include "../action.h" +#include "../graphics.h" +#include "../log.h" + +#include "resourcemanager.h" +#include "spriteset.h" +#include "image.h" + +#include "../utils/xml.h" + +SpriteDef::SpriteDef(const std::string &idPath, + const std::string &file, int variant): + Resource(idPath), + mAction(NULL), + mDirection(DIRECTION_DOWN), + mLastTime(0) +{ + load(file, variant); +} + +Action* +SpriteDef::getAction(SpriteAction action) const +{ + Actions::const_iterator i = mActions.find(action); + + if (i == mActions.end()) + { + logger->log("Warning: no action \"%u\" defined!", action); + return NULL; + } + + return i->second; +} + +void +SpriteDef::load(const std::string &animationFile, int variant) +{ + 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!"); + } + + xmlNodePtr node = xmlDocGetRootElement(doc); + if (!node || !xmlStrEqual(node->name, BAD_CAST "sprite")) { + logger->error( + "Animation: this is not a valid animation definition file!"); + } + + // Get the variant + int variant_num = XML::getProperty(node, "variants", 0); + int variant_offset = XML::getProperty(node, "variant_offset", 0); + + 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 = XML::getProperty(node, "width", 0); + int height = XML::getProperty(node, "height", 0); + std::string name = XML::getProperty(node, "name", ""); + std::string imageSrc = XML::getProperty(node, "src", ""); + + Spriteset *spriteset = + resman->getSpriteset(imageSrc, width, height); + + if (!spriteset) { + logger->error("Couldn't load spriteset!"); + } + + mSpritesets[name] = spriteset; + } + // get action + else if (xmlStrEqual(node->name, BAD_CAST "action")) + { + std::string actionName = XML::getProperty(node, "name", ""); + std::string imagesetName = XML::getProperty(node, "imageset", ""); + + SpritesetIterator si = mSpritesets.find(imagesetName); + if (si == mSpritesets.end()) { + logger->log("Warning: imageset \"%s\" not defined in %s", + imagesetName.c_str(), + animationFile.c_str()); + + // skip loading animations + continue; + } + Spriteset *imageset = si->second; + + SpriteAction actionType = makeSpriteAction(actionName); + if (actionType == ACTION_INVALID) + { + logger->log("Warning: Unknown action \"%s\" defined in %s", + actionName.c_str(), + animationFile.c_str()); + continue; + } + Action *action = new Action(); + mActions[actionType] = action; + + // When first action set it as default direction + if (mActions.empty()) + { + mActions[ACTION_DEFAULT] = action; + } + + + // get animations + for (xmlNodePtr animationNode = node->xmlChildrenNode; + animationNode != NULL; + animationNode = animationNode->next) + { + // We're only interested in animations + if (!xmlStrEqual(animationNode->name, BAD_CAST "animation")) + continue; + + std::string directionName = + XML::getProperty(animationNode, "direction", ""); + SpriteDirection directionType = + makeSpriteDirection(directionName); + + if (directionType == DIRECTION_INVALID) + { + logger->log("Warning: Unknown direction \"%s\" defined " + "for action %s in %s", + directionName.c_str(), + actionName.c_str(), + animationFile.c_str()); + continue; + } + + Animation *animation = new Animation(); + action->setAnimation(directionType, animation); + + // Get animation phases + for (xmlNodePtr phaseNode = animationNode->xmlChildrenNode; + phaseNode != NULL; + phaseNode = phaseNode->next) + { + int delay = XML::getProperty(phaseNode, "delay", 0); + + if (xmlStrEqual(phaseNode->name, BAD_CAST "frame")) + { + int index = XML::getProperty(phaseNode, "index", -1); + int offsetX = XML::getProperty(phaseNode, "offsetX", 0); + int offsetY = XML::getProperty(phaseNode, "offsetY", 0); + + offsetY -= imageset->getHeight() - 32; + offsetX -= imageset->getWidth() / 2 - 16; + Image *img = imageset->get(index + variant_offset); + animation->addPhase(img, delay, offsetX, offsetY); + } + else if (xmlStrEqual(phaseNode->name, BAD_CAST "sequence")) + { + int start = XML::getProperty(phaseNode, "start", 0); + int end = XML::getProperty(phaseNode, "end", 0); + int offsetY = -imageset->getHeight() + 32; + int offsetX = -imageset->getWidth() / 2 + 16; + while (end >= start) + { + Image *img = imageset->get(start + variant_offset); + animation->addPhase(img, delay, offsetX, offsetY); + start++; + } + } + else if (xmlStrEqual(phaseNode->name, BAD_CAST "end")) + { + animation->addTerminator(); + } + } // for phaseNode + } // for animationNode + } // if "" else if "" + } // for node + + // Complete missing actions + substituteAction(ACTION_STAND, ACTION_DEFAULT); + substituteAction(ACTION_WALK, ACTION_STAND); + substituteAction(ACTION_WALK, ACTION_RUN); + substituteAction(ACTION_ATTACK, ACTION_STAND); + substituteAction(ACTION_ATTACK_SWING, ACTION_ATTACK); + substituteAction(ACTION_ATTACK_STAB, ACTION_ATTACK_SWING); + substituteAction(ACTION_ATTACK_BOW, ACTION_ATTACK_STAB); + substituteAction(ACTION_ATTACK_THROW, ACTION_ATTACK_SWING); + substituteAction(ACTION_CAST_MAGIC, ACTION_ATTACK_SWING); + substituteAction(ACTION_USE_ITEM, ACTION_CAST_MAGIC); + substituteAction(ACTION_SIT, ACTION_STAND); + substituteAction(ACTION_SLEEP, ACTION_SIT); + substituteAction(ACTION_HURT, ACTION_STAND); + substituteAction(ACTION_DEAD, ACTION_HURT); + + xmlFreeDoc(doc); +} + +void +SpriteDef::substituteAction(SpriteAction complete, SpriteAction with) +{ + if (mActions.find(complete) == mActions.end()) + { + Actions::iterator i = mActions.find(with); + if (i != mActions.end()) { + mActions[complete] = i->second; + } + } +} + +SpriteDef::~SpriteDef() +{ + for (SpritesetIterator i = mSpritesets.begin(); i != mSpritesets.end(); ++i) + { + i->second->decRef(); + } +} + +SpriteAction +SpriteDef::makeSpriteAction(const std::string& action) +{ + if (action == "" || action == "default") { + return ACTION_DEFAULT; + } + if (action == "stand") { + return ACTION_STAND; + } + else if (action == "walk") { + return ACTION_WALK; + } + else if (action == "run") { + return ACTION_RUN; + } + else if (action == "attack") { + return ACTION_ATTACK; + } + else if (action == "attack_swing") { + return ACTION_ATTACK_SWING; + } + else if (action == "attack_stab") { + return ACTION_ATTACK_STAB; + } + else if (action == "attack_bow") { + return ACTION_ATTACK_BOW; + } + else if (action == "attack_throw") { + return ACTION_ATTACK_THROW; + } + else if (action == "cast_magic") { + return ACTION_CAST_MAGIC; + } + else if (action == "use_item") { + return ACTION_USE_ITEM; + } + else if (action == "sit") { + return ACTION_SIT; + } + else if (action == "sleep") { + return ACTION_SLEEP; + } + else if (action == "hurt") { + return ACTION_HURT; + } + else if (action == "dead") { + return ACTION_DEAD; + } + else { + return ACTION_INVALID; + } +} + +SpriteDirection +SpriteDef::makeSpriteDirection(const std::string& direction) +{ + if (direction == "" || 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; + }; +} diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h new file mode 100644 index 00000000..51c47a24 --- /dev/null +++ b/src/resources/spritedef.h @@ -0,0 +1,130 @@ +/* + * 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$ + */ + +#ifndef _TMW_SPRITEDEF_H +#define _TMW_SPRITEDEF_H + +#include "resource.h" + +#include +#include + +class Action; +class Graphics; +class Spriteset; +struct AnimationPhase; +class Animation; + +enum SpriteAction +{ + ACTION_DEFAULT = 0, + ACTION_STAND, + ACTION_WALK, + ACTION_RUN, + ACTION_ATTACK, + ACTION_ATTACK_SWING, + ACTION_ATTACK_STAB, + ACTION_ATTACK_BOW, + ACTION_ATTACK_THROW, + ACTION_CAST_MAGIC, + ACTION_USE_ITEM, + ACTION_SIT, + ACTION_SLEEP, + ACTION_HURT, + ACTION_DEAD, + ACTION_INVALID +}; + +enum SpriteDirection +{ + DIRECTION_DEFAULT = 0, + DIRECTION_DOWN, + DIRECTION_UP, + DIRECTION_LEFT, + DIRECTION_RIGHT, + DIRECTION_INVALID +}; + +/** + * Defines a class to load an animation. + */ +class SpriteDef : public Resource +{ + public: + /** + * Constructor. + */ + SpriteDef(const std::string &idPath, + const std::string &file, int variant); + + /** + * Destructor. + */ + ~SpriteDef(); + + /** + * Returns the specified action. + */ + Action* + getAction(SpriteAction action) const; + + private: + /** + * Loads a sprite definition file. + */ + void + load(const std::string &file, int variant); + + /** + * When there are no animations defined for the action "complete", its + * animations become a copy of those of the action "with". + */ + void + substituteAction(SpriteAction complete, SpriteAction with); + + /** + * Converts a string into a SpriteAction enum. + */ + static SpriteAction + makeSpriteAction(const std::string &action); + + /** + * Converts a string into a SpriteDirection enum. + */ + static SpriteDirection + makeSpriteDirection(const std::string &direction); + + + typedef std::map Spritesets; + typedef Spritesets::iterator SpritesetIterator; + + typedef std::map Actions; + + Spritesets mSpritesets; + Actions mActions; + Action *mAction; + SpriteDirection mDirection; + int mLastTime; +}; + +#endif diff --git a/src/resources/spriteset.h b/src/resources/spriteset.h index c51e6a75..7f6b42df 100644 --- a/src/resources/spriteset.h +++ b/src/resources/spriteset.h @@ -32,7 +32,9 @@ class Image; /** - * Stores a complete set of sprites. + * Stores a set of subimages originating from a single image. + * + * TODO: Should probably be renamed to ImageSet or TileSet. */ class Spriteset : public Resource { @@ -40,7 +42,7 @@ class Spriteset : public Resource /* * Cuts the passed image in a grid of sub images. */ - Spriteset(const std::string& idPath, Image *img, int w, int h); + Spriteset(const std::string &idPath, Image *img, int w, int h); /** * Destructor. -- cgit v1.2.3-70-g09d2