diff options
author | Jared Adams <jaxad0127@gmail.com> | 2010-05-02 14:43:40 -0600 |
---|---|---|
committer | Jared Adams <jaxad0127@gmail.com> | 2010-05-06 22:21:57 -0600 |
commit | 9baedc27191c82bbf1fedee2a7e738bc5b267c0e (patch) | |
tree | f0986c6839d1e79b402d5d4ec36e4994741955ea /src | |
parent | 844e9a7a72faca6a212e788a3adc45e17f41dca6 (diff) | |
download | mana-client-9baedc27191c82bbf1fedee2a7e738bc5b267c0e.tar.gz mana-client-9baedc27191c82bbf1fedee2a7e738bc5b267c0e.tar.bz2 mana-client-9baedc27191c82bbf1fedee2a7e738bc5b267c0e.tar.xz mana-client-9baedc27191c82bbf1fedee2a7e738bc5b267c0e.zip |
Add support for floor item sprites
This commit adds a sprite hierarchy (Sprite->ImageSprite,AnimatedSprite,CompundSprite;
CompoundSprite,Actor->ActorSprite;ActorSprite->Being,FloorItem) to collect common
functionailty into new base classes which will make other Mantis tickets easier to do.
Also allows monsters to use particle effects.
Reviewed-by: Bertram
Diffstat (limited to 'src')
44 files changed, 1144 insertions, 720 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 433a1d3d..6ccdd4e2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -402,6 +402,8 @@ SET(SRCS utils/xml.h actor.cpp actor.h + actorsprite.cpp + actorsprite.h animatedsprite.cpp animatedsprite.h animationparticle.cpp @@ -422,6 +424,8 @@ SET(SRCS channelmanager.h commandhandler.cpp commandhandler.h + compoundsprite.cpp + compoundsprite.h configlistener.h configuration.cpp configuration.h @@ -443,6 +447,8 @@ SET(SRCS guild.h imageparticle.cpp imageparticle.h + imagesprite.cpp + imagesprite.h inventory.cpp inventory.h item.cpp @@ -491,6 +497,7 @@ SET(SRCS simpleanimation.h sound.cpp sound.h + sprite.h statuseffect.cpp statuseffect.h text.cpp diff --git a/src/Makefile.am b/src/Makefile.am index a479f70c..d0ee9948 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -303,6 +303,8 @@ mana_SOURCES = gui/widgets/avatarlistbox.cpp \ utils/xml.h \ actor.cpp \ actor.h \ + actorsprite.cpp \ + actorsprite.h \ animatedsprite.cpp \ animatedsprite.h \ animationparticle.cpp \ @@ -323,6 +325,8 @@ mana_SOURCES = gui/widgets/avatarlistbox.cpp \ channelmanager.h \ commandhandler.cpp \ commandhandler.h \ + compoundsprite.cpp \ + compoundsprite.h \ configlistener.h \ configuration.cpp \ configuration.h \ @@ -344,6 +348,8 @@ mana_SOURCES = gui/widgets/avatarlistbox.cpp \ guild.h \ imageparticle.cpp \ imageparticle.h \ + imagesprite.cpp \ + imagesprite.h \ inventory.cpp \ inventory.h \ item.cpp \ @@ -392,6 +398,7 @@ mana_SOURCES = gui/widgets/avatarlistbox.cpp \ simpleanimation.h \ sound.cpp \ sound.h \ + sprite.h \ statuseffect.cpp \ statuseffect.h \ text.cpp \ diff --git a/src/actor.cpp b/src/actor.cpp index 4fdce5f1..44a89600 100644 --- a/src/actor.cpp +++ b/src/actor.cpp @@ -26,8 +26,7 @@ #include "resources/resourcemanager.h" Actor::Actor(): - mMap(NULL), - mAlpha(1.0f) + mMap(NULL) {} Actor::~Actor() diff --git a/src/actor.h b/src/actor.h index 59e29b88..367bcd75 100644 --- a/src/actor.h +++ b/src/actor.h @@ -46,7 +46,7 @@ public: * would support setting a translation offset. It already does this * partly with the clipping rectangle support. */ - virtual void draw(Graphics *graphics, int offsetX, int offsetY) const = 0; + virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const = 0; /** * Returns the horizontal size of the actors graphical representation @@ -105,14 +105,12 @@ public: /** * Returns the current alpha value used to draw the actor. */ - virtual float getAlpha() const - { return mAlpha; } + virtual float getAlpha() const = 0; /** * Sets the alpha value used to draw the actor. */ - virtual void setAlpha(float alpha) - { mAlpha = alpha; } + virtual void setAlpha(float alpha) = 0; void setMap(Map *map); @@ -122,7 +120,6 @@ public: protected: Map *mMap; Vector mPos; /**< Position in pixels relative to map. */ - float mAlpha; private: Actors::iterator mMapActor; diff --git a/src/actorsprite.cpp b/src/actorsprite.cpp new file mode 100644 index 00000000..ad703bc2 --- /dev/null +++ b/src/actorsprite.cpp @@ -0,0 +1,326 @@ +/* + * The Mana Client + * Copyright (C) 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 "actorsprite.h" + +#include "client.h" +#include "effectmanager.h" +#include "imagesprite.h" +#include "localplayer.h" +#include "log.h" +#include "simpleanimation.h" +#include "sound.h" +#include "statuseffect.h" + +#include "gui/theme.h" + +#include "net/net.h" + +#include "resources/image.h" +#include "resources/resourcemanager.h" + +#define EFFECTS_FILE "effects.xml" + +ActorSprite::ActorSprite(int id): + mId(id), + mStunMode(0), + mStatusParticleEffects(&mStunParticleEffects, false), + mChildParticleEffects(&mStatusParticleEffects, false), + mMustResetParticles(false), + mUsedTargetCursor(NULL) +{ + // +} + +ActorSprite::~ActorSprite() +{ + setMap(NULL); + + mUsedTargetCursor = NULL; + + if (player_node && player_node->getTarget() == this) + player_node->setTarget(NULL); +} + +bool ActorSprite::draw(Graphics *graphics, int offsetX, int offsetY) const +{ + // TODO: Eventually, we probably should fix all sprite offsets so that + // these translations aren't necessary anymore. The sprites know + // best where their base point should be. + const int px = getPixelX() + offsetX - 16; + // Temporary fix to the Y offset. + const int py = getPixelY() + offsetY - + ((Net::getNetworkType() == ServerInfo::MANASERV) ? 15 : 32); + + if (mUsedTargetCursor) + mUsedTargetCursor->draw(graphics, px, py); + + return drawSpriteAt(graphics, px, py); +} + +bool ActorSprite::drawSpriteAt(Graphics *graphics, int x, int y) const +{ + return CompoundSprite::draw(graphics, x, y); +} + +void ActorSprite::logic() +{ + // Update sprite animations + if (mUsedTargetCursor) + mUsedTargetCursor->update(tick_time * MILLISECONDS_IN_A_TICK); + + update(tick_time * MILLISECONDS_IN_A_TICK); + + // Restart status/particle effects, if needed + if (mMustResetParticles) + { + mMustResetParticles = false; + for (std::set<int>::iterator it = mStatusEffects.begin(); + it != mStatusEffects.end(); it++) + { + const StatusEffect *effect = StatusEffect::getStatusEffect(*it, true); + if (effect && effect->particleEffectIsPersistent()) + updateStatusEffect(*it, true); + } + } + + // Update particle effects + mChildParticleEffects.moveTo(mPos.x, mPos.y); +} + +void ActorSprite::setMap(Map* map) +{ + Actor::setMap(map); + + // Clear particle effect list because child particles became invalid + mChildParticleEffects.clear(); + mMustResetParticles = true; // Reset status particles on next redraw +} + +void ActorSprite::controlParticle(Particle *particle) +{ + mChildParticleEffects.addLocally(particle); +} + +void ActorSprite::setTargetAnimation(SimpleAnimation *animation) +{ + mUsedTargetCursor = animation; + mUsedTargetCursor->reset(); +} + +struct EffectDescription { + std::string mGFXEffect; + std::string mSFXEffect; +}; + +static EffectDescription *default_effect = NULL; +static std::map<int, EffectDescription *> effects; +static bool effects_initialized = false; + +static EffectDescription *getEffectDescription(xmlNodePtr node, int *id) +{ + EffectDescription *ed = new EffectDescription; + + *id = atoi(XML::getProperty(node, "id", "-1").c_str()); + ed->mSFXEffect = XML::getProperty(node, "audio", ""); + ed->mGFXEffect = XML::getProperty(node, "particle", ""); + + return ed; +} + +static EffectDescription *getEffectDescription(int effectId) +{ + if (!effects_initialized) + { + XML::Document doc(EFFECTS_FILE); + xmlNodePtr root = doc.rootNode(); + + if (!root || !xmlStrEqual(root->name, BAD_CAST "being-effects")) + { + logger->log("Error loading being effects file: " + EFFECTS_FILE); + return NULL; + } + + for_each_xml_child_node(node, root) + { + int id; + + if (xmlStrEqual(node->name, BAD_CAST "effect")) + { + EffectDescription *EffectDescription = + getEffectDescription(node, &id); + effects[id] = EffectDescription; + } + else if (xmlStrEqual(node->name, BAD_CAST "default")) + { + EffectDescription *effectDescription = + getEffectDescription(node, &id); + + if (default_effect) + delete default_effect; + + default_effect = effectDescription; + } + } + + effects_initialized = true; + } // done initializing + + EffectDescription *ed = effects[effectId]; + + return ed ? ed : default_effect; +} + +void ActorSprite::setStatusEffect(int index, bool active) +{ + const bool wasActive = mStatusEffects.find(index) != mStatusEffects.end(); + + if (active != wasActive) + { + updateStatusEffect(index, active); + if (active) + mStatusEffects.insert(index); + else + mStatusEffects.erase(index); + } +} + +void ActorSprite::setStatusEffectBlock(int offset, Uint16 newEffects) +{ + for (int i = 0; i < STATUS_EFFECTS; i++) + { + int index = StatusEffect::blockEffectIndexToEffectIndex(offset + i); + + if (index != -1) + setStatusEffect(index, (newEffects & (1 << i)) > 0); + } +} + +void ActorSprite::internalTriggerEffect(int effectId, bool sfx, bool gfx) +{ + logger->log("Special effect #%d on %s", effectId, + getId() == player_node->getId() ? "self" : "other"); + + EffectDescription *ed = getEffectDescription(effectId); + + if (!ed) + { + logger->log("Unknown special effect and no default recorded"); + return; + } + + if (gfx && !ed->mGFXEffect.empty()) + { + Particle *selfFX; + + selfFX = particleEngine->addEffect(ed->mGFXEffect, 0, 0); + controlParticle(selfFX); + } + + if (sfx && !ed->mSFXEffect.empty()) + sound.playSfx(ed->mSFXEffect); +} + +void ActorSprite::updateStunMode(int oldMode, int newMode) +{ + handleStatusEffect(StatusEffect::getStatusEffect(oldMode, false), -1); + handleStatusEffect(StatusEffect::getStatusEffect(newMode, true), -1); +} + +void ActorSprite::updateStatusEffect(int index, bool newStatus) +{ + handleStatusEffect(StatusEffect::getStatusEffect(index, newStatus), index); +} + +void ActorSprite::handleStatusEffect(StatusEffect *effect, int effectId) +{ + if (!effect) + return; + + // TODO: Find out how this is meant to be used + // (SpriteAction != Being::Action) + //SpriteAction action = effect->getAction(); + //if (action != ACTION_INVALID) + // setAction(action); + + Particle *particle = effect->getParticle(); + + if (effectId >= 0) + { + mStatusParticleEffects.setLocally(effectId, particle); + } + else + { + mStunParticleEffects.clearLocally(); + if (particle) + mStunParticleEffects.addLocally(particle); + } +} + +void ActorSprite::setupSpriteDisplay(const SpriteDisplay &display, + bool forceDisplay) +{ + clear(); + + SpriteRefs it, it_end; + + for (it = display.sprites.begin(), it_end = display.sprites.end(); + it != it_end; it++) + { + std::string file = "graphics/sprites/" + (*it)->sprite; + int variant = (*it)->variant; + push_back(AnimatedSprite::load(file, variant)); + } + + // Ensure that something is shown, if desired + if (size() == 0 && forceDisplay) + { + if (display.image.empty()) + push_back(AnimatedSprite::load("graphics/sprites/error.xml")); + else + { + ResourceManager *resman = ResourceManager::getInstance(); + std::string imagePath = "graphics/items/" + display.image; + Image *img = resman->getImage(imagePath); + + if (!img) + img = Theme::getImageFromTheme("unknown-item.png"); + + push_back(new ImageSprite(img)); + } + } + + mChildParticleEffects.clear(); + + //setup particle effects + if (Particle::enabled) + { + std::list<std::string>::const_iterator it, it_end; + for (it = display.particles.begin(), it_end = display.particles.end(); + it != it_end; it++) + { + Particle *p = particleEngine->addEffect(*it, 0, 0); + controlParticle(p); + } + } + + mMustResetParticles = true; +} diff --git a/src/actorsprite.h b/src/actorsprite.h new file mode 100644 index 00000000..b5c402eb --- /dev/null +++ b/src/actorsprite.h @@ -0,0 +1,192 @@ +/* + * The Mana Client + * Copyright (C) 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/>. + */ + +#ifndef ACTORSPRITE_H +#define ACTORSPRITE_H + +#include "actor.h" +#include "compoundsprite.h" +#include "map.h" +#include "particlecontainer.h" + +#include <SDL_types.h> + +#include <set> + +class SimpleAnimation; +class StatusEffect; + +class ActorSprite : public CompoundSprite, public Actor +{ +public: + enum Type + { + UNKNOWN, + PLAYER, + NPC, + MONSTER, + FLOOR_ITEM + }; + + enum TargetCursorSize + { + TC_SMALL = 0, + TC_MEDIUM, + TC_LARGE, + NUM_TC + }; + + ActorSprite(int id); + + ~ActorSprite(); + + int getId() const + { return mId; } + + void setId(int id) { mId = id; } + + /** + * Returns the type of the ActorSprite. + */ + virtual Type getType() const { return UNKNOWN; } + + virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const; + + virtual bool drawSpriteAt(Graphics *graphics, int x, int y) const; + + virtual void logic(); + + void setMap(Map* map); + + /** + * Gets the way the object blocks pathfinding for other objects + */ + virtual Map::BlockType getBlockType() const + { return Map::BLOCKTYPE_NONE; } + + /** + * Take control of a particle. + */ + void controlParticle(Particle *particle); + + /** + * Returns the required size of a target cursor for this being. + */ + virtual TargetCursorSize getTargetCursorSize() const + { return TC_MEDIUM; } + + /** + * Sets the target animation for this actor. + */ + void setTargetAnimation(SimpleAnimation *animation); + + /** + * Untargets the actor. + */ + void untarget() { mUsedTargetCursor = NULL; } + + /** + * Triggers a visual effect, such as `level up'. Only draws the visual + * effect, does not play sound effects. + * + * \param effectId ID of the effect to trigger + */ + virtual void triggerEffect(int effectId) + { + internalTriggerEffect(effectId, false, true); + } + + /** + * Sets the actor's stun mode. If zero, the being is `normal', otherwise it + * is `stunned' in some fashion. + */ + void setStunMode(int stunMode) + { + if (mStunMode != stunMode) + updateStunMode(mStunMode, stunMode); + mStunMode = stunMode; + } + + void setStatusEffect(int index, bool active); + + /** + * A status effect block is a 16 bit mask of status effects. We assign each + * such flag a block ID of offset + bitnr. + * + * These are NOT the same as the status effect indices. + */ + void setStatusEffectBlock(int offset, Uint16 flags); + + virtual void setAlpha(float alpha) + { CompoundSprite::setAlpha(alpha); } + + virtual float getAlpha() const + { return CompoundSprite::getAlpha(); } + +protected: + /** + * Trigger visual effect, with components + * + * \param effectId ID of the effect to trigger + * \param sfx Whether to trigger sound effects + * \param gfx Whether to trigger graphical effects + */ + void internalTriggerEffect(int effectId, bool sfx, bool gfx); + + /** + * Notify self that the stun mode has been updated. Invoked by + * setStunMode if something changed. + */ + virtual void updateStunMode(int oldMode, int newMode); + + /** + * Notify self that a status effect has flipped. + * The new flag is passed. + */ + virtual void updateStatusEffect(int index, bool newStatus); + + /** + * Handle an update to a status or stun effect + * + * \param The StatusEffect to effect + * \param effectId -1 for stun, otherwise the effect index + */ + virtual void handleStatusEffect(StatusEffect *effect, int effectId); + + void setupSpriteDisplay(const SpriteDisplay &display, + bool forceDisplay = true); + + int mId; + Uint16 mStunMode; /**< Stun mode; zero if not stunned */ + std::set<int> mStatusEffects; /**< set of active status effects */ + + ParticleList mStunParticleEffects; + ParticleVector mStatusParticleEffects; + ParticleList mChildParticleEffects; + +private: + /** Reset particle status effects on next redraw? */ + bool mMustResetParticles; + + /** Target cursor being used */ + SimpleAnimation *mUsedTargetCursor; +}; + +#endif // ACTORSPRITE_H diff --git a/src/animatedsprite.cpp b/src/animatedsprite.cpp index 59bf2f88..bc595f79 100644 --- a/src/animatedsprite.cpp +++ b/src/animatedsprite.cpp @@ -41,11 +41,12 @@ AnimatedSprite::AnimatedSprite(SpriteDef *sprite): mSprite(sprite), mAction(0), mAnimation(0), - mFrame(0), - mAlpha(1.0f) + mFrame(0) { assert(mSprite); + mAlpha = 1.0f; + // Take possession of the sprite mSprite->incRef(); @@ -193,3 +194,8 @@ int AnimatedSprite::getHeight() const else return 0; } + +Image* AnimatedSprite::getImage() const +{ + return mFrame ? mFrame->image : 0; +} diff --git a/src/animatedsprite.h b/src/animatedsprite.h index 54b63cc0..833a07a3 100644 --- a/src/animatedsprite.h +++ b/src/animatedsprite.h @@ -22,19 +22,18 @@ #ifndef ANIMATEDSPRITE_H #define ANIMATEDSPRITE_H -#include "resources/spritedef.h" +#include "sprite.h" #include <map> #include <string> class Animation; -class Graphics; struct Frame; /** * Animates a sprite by adding playback state. */ -class AnimatedSprite +class AnimatedSprite : public Sprite { public: /** @@ -53,59 +52,26 @@ class AnimatedSprite static AnimatedSprite *load(const std::string &filename, int variant = 0); - /** - * Destructor. - */ virtual ~AnimatedSprite(); - /** - * Resets the animated sprite. - */ void reset(); - /** - * Plays an action using the current direction - */ void play(SpriteAction action); - /** - * Inform the animation of the passed time so that it can output the - * correct animation frame. - */ void update(int time); - /** - * Draw the current animation frame at the coordinates given in screen - * pixels. - */ bool draw(Graphics* graphics, int posX, int posY) const; - /** - * gets the width in pixels of the image of the current frame - */ int getWidth() const; - /** - * gets the height in pixels of the image of the current frame - */ int getHeight() const; - /** - * Sets the direction. - */ - void setDirection(SpriteDirection direction); + Image* getImage() const; - /** - * Sets the alpha value of the animated sprite - */ - void setAlpha(float alpha) - { mAlpha = alpha; } + void setDirection(SpriteDirection direction); - /** - * Returns the current alpha opacity of the animated sprite. - */ - virtual float getAlpha() const - { return mAlpha; } + int getNumberOfLayers() + { return 1; } private: bool updateCurrentAnimation(unsigned int dt); @@ -120,7 +86,6 @@ class AnimatedSprite Action *mAction; /**< The currently active action. */ Animation *mAnimation; /**< The currently active animation. */ Frame *mFrame; /**< The currently active frame. */ - float mAlpha; /**< The alpha opacity used to draw */ }; #endif diff --git a/src/being.cpp b/src/being.cpp index 40a6befd..2ce34f94 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -32,6 +32,7 @@ #include "particle.h" #include "simpleanimation.h" #include "sound.h" +#include "sprite.h" #include "text.h" #include "statuseffect.h" @@ -47,17 +48,16 @@ #include "resources/iteminfo.h" #include "resources/resourcemanager.h" +#include "net/net.h" +#include "net/playerhandler.h" #include "utils/dtor.h" #include "utils/stringutils.h" #include "utils/xml.h" -#include "net/net.h" -#include "net/playerhandler.h" #include <cassert> #include <cmath> -#define BEING_EFFECTS_FILE "effects.xml" #define HAIR_FILE "hair.xml" static const int DEFAULT_BEING_WIDTH = 32; @@ -68,6 +68,7 @@ int Being::mNumberOfHairstyles = 1; // TODO: mWalkTime used by eAthena only Being::Being(int id, int subtype, Map *map): + ActorSprite(id), mFrame(0), mWalkTime(0), mEmotion(0), mEmotionTime(0), @@ -75,20 +76,14 @@ Being::Being(int id, int subtype, Map *map): mAttackSpeed(350), mAction(STAND), mSubType(subtype), - mId(id), mDirection(DOWN), mSpriteDirection(DIRECTION_DOWN), mDispName(0), mShowName(false), mEquippedWeapon(NULL), mText(0), - mStunMode(0), - mStatusParticleEffects(&mStunParticleEffects, false), - mChildParticleEffects(&mStatusParticleEffects, false), - mMustResetParticles(false), mX(0), mY(0), - mDamageTaken(0), - mUsedTargetCursor(NULL) + mDamageTaken(0) { setMap(map); @@ -101,12 +96,6 @@ Being::Being(int id, int subtype, Map *map): Being::~Being() { - mUsedTargetCursor = NULL; - delete_all(mSprites); - - if (player_node && player_node->getTarget() == this) - player_node->setTarget(NULL); - delete mSpeechBubble; delete mDispName; delete mText; @@ -356,20 +345,6 @@ void Being::setGuildPos(const std::string &pos) logger->log("Got guild position \"%s\" for being %s(%i)", pos.c_str(), mName.c_str(), mId); } -void Being::setMap(Map *map) -{ - Actor::setMap(map); - - // Clear particle effect list because child particles became invalid - mChildParticleEffects.clear(); - mMustResetParticles = true; // Reset status particles on next redraw -} - -void Being::controlParticle(Particle *particle) -{ - mChildParticleEffects.addLocally(particle); -} - void Being::fireMissile(Being *victim, const std::string &particle) { if (!victim || particle.empty()) @@ -408,9 +383,7 @@ void Being::setAction(Action action, int attackType) else currentAction = ACTION_ATTACK; - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->reset(); + reset(); break; case HURT: //currentAction = ACTION_HURT; // Buggy: makes the player stop @@ -427,9 +400,7 @@ void Being::setAction(Action action, int attackType) if (currentAction != ACTION_INVALID) { - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->play(currentAction); + play(currentAction); mAction = action; } } @@ -457,9 +428,7 @@ void Being::setDirection(Uint8 direction) dir = DIRECTION_LEFT; mSpriteDirection = dir; - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->setDirection(dir); + CompoundSprite::setDirection(dir); } /** TODO: Used by eAthena only */ @@ -607,69 +576,7 @@ void Being::logic() mEmotion = 0; } - // Update sprite animations - if (mUsedTargetCursor) - mUsedTargetCursor->update(tick_time * MILLISECONDS_IN_A_TICK); - - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->update(tick_time * MILLISECONDS_IN_A_TICK); - - // Restart status/particle effects, if needed - if (mMustResetParticles) - { - mMustResetParticles = false; - for (std::set<int>::iterator it = mStatusEffects.begin(); - it != mStatusEffects.end(); it++) - { - const StatusEffect *effect = StatusEffect::getStatusEffect(*it, true); - if (effect && effect->particleEffectIsPersistent()) - updateStatusEffect(*it, true); - } - } - - // Update particle effects - mChildParticleEffects.moveTo(mPos.x, mPos.y); -} - -void Being::draw(Graphics *graphics, int offsetX, int offsetY) const -{ - // TODO: Eventually, we probably should fix all sprite offsets so that - // these translations aren't necessary anymore. The sprites know - // best where their base point should be. - const int px = getPixelX() + offsetX - 16; - // Temporary fix to the Y offset. - const int py = getPixelY() + offsetY - - ((Net::getNetworkType() == ServerInfo::MANASERV) ? 15 : 32); - - if (mUsedTargetCursor) - mUsedTargetCursor->draw(graphics, px, py); - - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) - { - if (*it) - { - if ((*it)->getAlpha() != mAlpha) - (*it)->setAlpha(mAlpha); - (*it)->draw(graphics, px, py); - } - } -} - -void Being::drawSpriteAt(Graphics *graphics, int x, int y) const -{ - const int px = x - 16; - const int py = y - 32; - - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) - { - if (*it) - { - if ((*it)->getAlpha() != mAlpha) - (*it)->setAlpha(mAlpha); - (*it)->draw(graphics, px, py); - } - } + ActorSprite::logic(); } void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) @@ -739,67 +646,6 @@ void Being::drawSpeech(int offsetX, int offsetY) } } -void Being::setStatusEffectBlock(int offset, Uint16 newEffects) -{ - for (int i = 0; i < STATUS_EFFECTS; i++) - { - int index = StatusEffect::blockEffectIndexToEffectIndex(offset + i); - - if (index != -1) - setStatusEffect(index, (newEffects & (1 << i)) > 0); - } -} - -void Being::handleStatusEffect(StatusEffect *effect, int effectId) -{ - if (!effect) - return; - - // TODO: Find out how this is meant to be used - // (SpriteAction != Being::Action) - //SpriteAction action = effect->getAction(); - //if (action != ACTION_INVALID) - // setAction(action); - - Particle *particle = effect->getParticle(); - - if (effectId >= 0) - { - mStatusParticleEffects.setLocally(effectId, particle); - } - else - { - mStunParticleEffects.clearLocally(); - if (particle) - mStunParticleEffects.addLocally(particle); - } -} - -void Being::updateStunMode(int oldMode, int newMode) -{ - handleStatusEffect(StatusEffect::getStatusEffect(oldMode, false), -1); - handleStatusEffect(StatusEffect::getStatusEffect(newMode, true), -1); -} - -void Being::updateStatusEffect(int index, bool newStatus) -{ - handleStatusEffect(StatusEffect::getStatusEffect(index, newStatus), index); -} - -void Being::setStatusEffect(int index, bool active) -{ - const bool wasActive = mStatusEffects.find(index) != mStatusEffects.end(); - - if (active != wasActive) - { - updateStatusEffect(index, active); - if (active) - mStatusEffects.insert(index); - else - mStatusEffects.erase(index); - } -} - /** TODO: eAthena only */ int Being::getOffset(char pos, char neg) const { @@ -832,125 +678,12 @@ int Being::getOffset(char pos, char neg) const int Being::getWidth() const { - AnimatedSprite *base = NULL; - - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) - if ((base = (*it))) - break; - - if (base) - return std::max(base->getWidth(), DEFAULT_BEING_WIDTH); - - return DEFAULT_BEING_WIDTH; + return std::max(CompoundSprite::getWidth(), DEFAULT_BEING_WIDTH); } int Being::getHeight() const { - AnimatedSprite *base = NULL; - - for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) - if ((base = (*it))) - break; - - if (base) - return std::max(base->getHeight(), DEFAULT_BEING_HEIGHT); - - return DEFAULT_BEING_HEIGHT; -} - -void Being::setTargetAnimation(SimpleAnimation *animation) -{ - mUsedTargetCursor = animation; - mUsedTargetCursor->reset(); -} - -struct EffectDescription { - std::string mGFXEffect; - std::string mSFXEffect; -}; - -static EffectDescription *default_effect = NULL; -static std::map<int, EffectDescription *> effects; -static bool effects_initialized = false; - -static EffectDescription *getEffectDescription(xmlNodePtr node, int *id) -{ - EffectDescription *ed = new EffectDescription; - - *id = atoi(XML::getProperty(node, "id", "-1").c_str()); - ed->mSFXEffect = XML::getProperty(node, "audio", ""); - ed->mGFXEffect = XML::getProperty(node, "particle", ""); - - return ed; -} - -static EffectDescription *getEffectDescription(int effectId) -{ - if (!effects_initialized) - { - XML::Document doc(BEING_EFFECTS_FILE); - xmlNodePtr root = doc.rootNode(); - - if (!root || !xmlStrEqual(root->name, BAD_CAST "being-effects")) - { - logger->log("Error loading being effects file: " - BEING_EFFECTS_FILE); - return NULL; - } - - for_each_xml_child_node(node, root) - { - int id; - - if (xmlStrEqual(node->name, BAD_CAST "effect")) - { - EffectDescription *EffectDescription = - getEffectDescription(node, &id); - effects[id] = EffectDescription; - } - else if (xmlStrEqual(node->name, BAD_CAST "default")) - { - EffectDescription *effectDescription = - getEffectDescription(node, &id); - - if (default_effect) - delete default_effect; - - default_effect = effectDescription; - } - } - - effects_initialized = true; - } // done initializing - - EffectDescription *ed = effects[effectId]; - - return ed ? ed : default_effect; -} - -void Being::internalTriggerEffect(int effectId, bool sfx, bool gfx) -{ - logger->log("Special effect #%d on %s", effectId, - getId() == player_node->getId() ? "self" : "other"); - - EffectDescription *ed = getEffectDescription(effectId); - - if (!ed) - { - logger->log("Unknown special effect and no default recorded"); - return; - } - - if (gfx && !ed->mGFXEffect.empty()) - { - Particle *selfFX; - - selfFX = particleEngine->addEffect(ed->mGFXEffect, 0, 0); - controlParticle(selfFX); - } - - if (sfx && !ed->mSFXEffect.empty()) - sound.playSfx(ed->mSFXEffect); + return std::max(CompoundSprite::getHeight(), DEFAULT_BEING_HEIGHT); } void Being::updateCoords() @@ -1001,7 +734,7 @@ void Being::showName() int Being::getNumberOfLayers() const { - return mSprites.size(); + return size(); } void Being::load() diff --git a/src/being.h b/src/being.h index 6264d56d..95f8c802 100644 --- a/src/being.h +++ b/src/being.h @@ -22,6 +22,7 @@ #ifndef BEING_H #define BEING_H +#include "actorsprite.h" #include "configlistener.h" #include "map.h" #include "particlecontainer.h" @@ -44,31 +45,17 @@ #define SPEECH_TIME 500 #define SPEECH_MAX_TIME 1000 -class AnimatedSprite; class FlashText; -class Graphics; -class Image; class ItemInfo; class Item; class Particle; class Position; -class SimpleAnimation; class SpeechBubble; class Text; -class StatusEffect; - -class Being : public Actor, public ConfigListener +class Being : public ActorSprite, public ConfigListener { public: - enum Type - { - UNKNOWN, - PLAYER, - NPC, - MONSTER - }; - /** * Action the being is currently performing * WARNING: Has to be in sync with the same enum in the Being class @@ -84,14 +71,6 @@ class Being : public Actor, public ConfigListener HURT }; - enum TargetCursorSize - { - TC_SMALL = 0, - TC_MEDIUM, - TC_LARGE, - NUM_TC - }; - enum Speech { NO_SPEECH = 0, @@ -275,11 +254,6 @@ class Being : public Actor, public ConfigListener */ void drawEmotion(Graphics *graphics, int offsetX, int offsetY); - /** - * Returns the type of the being. - */ - virtual Type getType() const { return UNKNOWN; } - /** * Return Being's current Job (player job, npc, monster, creature ) */ @@ -317,18 +291,6 @@ class Being : public Actor, public ConfigListener int getAttackSpeed() const { return mAttackSpeed; } /** - * Sets the sprite id. - */ - void setId(int id) { mId = id; } - - int getId() const { return mId; } - - /** - * Sets the map the being is on - */ - void setMap(Map *map); - - /** * Sets the current action. */ virtual void setAction(Action action, int attackType = 0); @@ -369,21 +331,6 @@ class Being : public Actor, public ConfigListener SpriteDirection getSpriteDirection() const { return SpriteDirection(mSpriteDirection); } - /** - * Draws this being to the given graphics context. - * - * @see Actor::draw(Graphics, int, int) - * - * TODO: The following two functions should be combined. - * at some point draw(), was changed to use mPx and mPy, with arugements - * only for the offset, drawSpriteAt() takes x, and y and draws the sprite - * exactly at those coords (though it does do some computing to work how the - * old draw() worked). - */ - virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; - - virtual void drawSpriteAt(Graphics *graphics, int x, int y) const; - void setPosition(const Vector &pos); /** @@ -412,17 +359,6 @@ class Being : public Actor, public ConfigListener virtual int getCollisionRadius() const; /** - * Returns the required size of a target cursor for this being. - */ - virtual Being::TargetCursorSize getTargetCursorSize() const - { return TC_MEDIUM; } - - /** - * Take control of a particle. - */ - void controlParticle(Particle *particle); - - /** * Shoots a missile particle from this being, to target being */ void fireMissile(Being *target, const std::string &particle); @@ -439,16 +375,6 @@ class Being : public Actor, public ConfigListener */ const Path &getPath() const { return mPath; } - /** - * Sets the target animation for this being. - */ - void setTargetAnimation(SimpleAnimation *animation); - - /** - * Untargets the being - */ - void untarget() { mUsedTargetCursor = NULL; } - /** * Set the Emoticon type and time displayed above @@ -466,42 +392,6 @@ class Being : public Actor, public ConfigListener */ Uint8 getEmotion() const { return mEmotion; } - /** - * Sets the being's stun mode. If zero, the being is `normal', - * otherwise it is `stunned' in some fashion. - */ - void setStunMode(int stunMode) - { - if (mStunMode != stunMode) - updateStunMode(mStunMode, stunMode); - mStunMode = stunMode; - }; - - void setStatusEffect(int index, bool active); - - /** - * A status effect block is a 16 bit mask of status effects. - * We assign each such flag a block ID of offset + bitnr. - * - * These are NOT the same as the status effect indices. - */ - void setStatusEffectBlock(int offset, Uint16 flags); - - /** - * Triggers a visual effect, such as `level up' - * - * Only draws the visual effect, does not play sound effects - * - * \param effectId ID of the effect to trigger - */ - virtual void triggerEffect(int effectId) - { - internalTriggerEffect(effectId, false, true); - } - - virtual AnimatedSprite *getSprite(int index) const - { return mSprites[index]; } - static void load(); virtual void optionChanged(const std::string &value) {} @@ -524,41 +414,6 @@ class Being : public Actor, public ConfigListener */ virtual void updateCoords(); - /** - * Gets the way the object blocks pathfinding for other objects - */ - virtual Map::BlockType getBlockType() const - { return Map::BLOCKTYPE_NONE; } - - /** - * Trigger visual effect, with components - * - * \param effectId ID of the effect to trigger - * \param sfx Whether to trigger sound effects - * \param gfx Whether to trigger graphical effects - */ - void internalTriggerEffect(int effectId, bool sfx, bool gfx); - - /** - * Notify self that the stun mode has been updated. Invoked by - * setStunMode if something changed. - */ - virtual void updateStunMode(int oldMode, int newMode); - - /** - * Notify self that a status effect has flipped. - * The new flag is passed. - */ - virtual void updateStatusEffect(int index, bool newStatus); - - /** - * Handle an update to a status or stun effect - * - * \param The StatusEffect to effect - * \param effectId -1 for stun, otherwise the effect index - */ - virtual void handleStatusEffect(StatusEffect *effect, int effectId); - virtual void showName(); /** The current sprite Frame number to be displayed */ @@ -578,7 +433,6 @@ class Being : public Actor, public ConfigListener Action mAction; /**< Action the being is performing */ Uint16 mSubType; /**< Subtype (graphical view, basically) */ - int mId; /**< Unique sprite id */ Uint8 mDirection; /**< Facing direction */ Uint8 mSpriteDirection; /**< Facing direction */ std::string mName; /**< Name of character */ @@ -600,17 +454,6 @@ class Being : public Actor, public ConfigListener std::string mSpeech; Text *mText; const gcn::Color *mTextColor; - Uint16 mStunMode; /**< Stun mode; zero if not stunned */ - std::set<int> mStatusEffects; /**< set of active status effects */ - - typedef std::vector<AnimatedSprite*> Sprites; - typedef Sprites::iterator SpriteIterator; - typedef Sprites::const_iterator SpriteConstIterator; - Sprites mSprites; - - ParticleList mStunParticleEffects; - ParticleVector mStatusParticleEffects; - ParticleList mChildParticleEffects; Vector mDest; /**< destination coordinates. */ @@ -623,9 +466,6 @@ class Being : public Actor, public ConfigListener */ int getOffset(char pos, char neg) const; - /** Reset particle status effects on next redraw? */ - bool mMustResetParticles; - /** Speech Bubble components */ SpeechBubble *mSpeechBubble; @@ -640,9 +480,6 @@ class Being : public Actor, public ConfigListener int mX, mY; /**< Position in tile */ int mDamageTaken; - - /** Target cursor being used */ - SimpleAnimation *mUsedTargetCursor; }; #endif diff --git a/src/compoundsprite.cpp b/src/compoundsprite.cpp new file mode 100644 index 00000000..a9a521ef --- /dev/null +++ b/src/compoundsprite.cpp @@ -0,0 +1,121 @@ +/* + * The Mana Client + * Copyright (C) 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 "compoundsprite.h" + +#include "utils/dtor.h" + +CompoundSprite::CompoundSprite() +{ + mAlpha = 1.0f; +} + +CompoundSprite::~CompoundSprite() +{ + SpriteIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + delete (*it); + + clear(); +} + +void CompoundSprite::reset() +{ + SpriteIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if (*it) + (*it)->reset(); +} + +void CompoundSprite::play(SpriteAction action) +{ + SpriteIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if (*it) + (*it)->play(action); +} + +void CompoundSprite::update(int time) +{ + SpriteIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if (*it) + (*it)->update(time); +} + +bool CompoundSprite::draw(Graphics* graphics, int posX, int posY) const +{ + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + { + if (*it) + { + if ((*it)->getAlpha() != mAlpha) + (*it)->setAlpha(mAlpha); + (*it)->draw(graphics, posX, posY); + } + } + + return true; +} + +int CompoundSprite::getWidth() const +{ + Sprite *base = NULL; + + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if ((base = (*it))) + break; + + if (base) + return base->getWidth(); + + return 0; +} + +int CompoundSprite::getHeight() const +{ + Sprite *base = NULL; + + SpriteConstIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if ((base = (*it))) + break; + + if (base) + return base->getHeight(); + + return 0; +} + +Image* CompoundSprite::getImage() const +{ + // TODO http://bugs.manasource.org/view.php?id=24 + return NULL; +} + +void CompoundSprite::setDirection(SpriteDirection direction) +{ + SpriteIterator it, it_end; + for (it = begin(), it_end = end(); it != it_end; it++) + if (*it) + (*it)->setDirection(direction); +} diff --git a/src/compoundsprite.h b/src/compoundsprite.h new file mode 100644 index 00000000..b3925216 --- /dev/null +++ b/src/compoundsprite.h @@ -0,0 +1,67 @@ +/* + * The Mana Client + * Copyright (C) 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/>. + */ + +#ifndef COMPOUNDSPRITE_H +#define COMPOUNDSPRITE_H + +#include "sprite.h" + +#include <vector> + +class CompoundSprite : public Sprite, public std::vector<Sprite*> +{ +public: + CompoundSprite(); + + ~CompoundSprite(); + + virtual void reset(); + + virtual void play(SpriteAction action); + + virtual void update(int time); + + virtual bool draw(Graphics* graphics, int posX, int posY) const; + + /** + * Gets the width in pixels of the first sprite in the list. + */ + virtual int getWidth() const; + + /** + * Gets the height in pixels of the first sprite in the list. + */ + virtual int getHeight() const; + + virtual Image* getImage() const; + + virtual void setDirection(SpriteDirection direction); + + virtual Sprite *getSprite(int index) const + { return at(index); } + + int getNumberOfLayers() + { return size(); } +}; + +typedef CompoundSprite::iterator SpriteIterator; +typedef CompoundSprite::const_iterator SpriteConstIterator; + +#endif // COMPOUNDSPRITE_H diff --git a/src/flooritem.cpp b/src/flooritem.cpp index e5a9d215..84e07f35 100644 --- a/src/flooritem.cpp +++ b/src/flooritem.cpp @@ -21,52 +21,34 @@ #include "flooritem.h" -#include "graphics.h" -#include "item.h" -#include "map.h" +#include "net/net.h" -#include "resources/image.h" +#include "resources/itemdb.h" +#include "resources/iteminfo.h" FloorItem::FloorItem(int id, int itemId, int x, int y, Map *map): - mId(id) + ActorSprite(id), + mItemId(itemId), + mX(x), + mY(y) { setMap(map); - mPos.x = x * map->getTileWidth(); - mPos.y = y * map->getTileHeight(); - // Create a corresponding item instance - mItem = new Item(itemId); -} -FloorItem::~FloorItem() -{ - delete mItem; -} + // TODO: Eventually, we probably should fix all sprite offsets so that + // these translations aren't necessary anymore. The sprites know + // best where their base point should be. + mPos.x = x * map->getTileWidth() + 16; + mPos.y = y * map->getTileHeight() + + ((Net::getNetworkType() == ServerInfo::MANASERV) ? 15 : 32); -int FloorItem::getItemId() const -{ - return mItem->getId(); + setupSpriteDisplay(ItemDB::get(itemId).getDisplay()); } -Item *FloorItem::getItem() const +const ItemInfo &FloorItem::getInfo() const { - return mItem; -} - -void FloorItem::draw(Graphics *graphics, int offsetX, int offsetY) const -{ - if (mItem) - { - Image *image = mItem->getDrawImage(); - - if (image) - if (mAlpha != image->getAlpha()) - image->setAlpha(mAlpha); - - graphics->drawImage(image, getPixelX() + offsetX, - getPixelY() + offsetY); - } + return ItemDB::get(mId); } diff --git a/src/flooritem.h b/src/flooritem.h index 17b7b54d..e599c939 100644 --- a/src/flooritem.h +++ b/src/flooritem.h @@ -22,16 +22,14 @@ #ifndef FLOORITEM_H #define FLOORITEM_H -#include "map.h" +#include "actorsprite.h" -class Graphics; -class Image; -class Item; +class ItemInfo; /** * An item lying on the floor. */ -class FloorItem : public Actor +class FloorItem : public ActorSprite { public: /** @@ -49,39 +47,29 @@ class FloorItem : public Actor int y, Map *map); - ~FloorItem(); - - /** - * Returns instance ID of this item. - */ - int getId() const - { return mId; } + Type getType() const { return FLOOR_ITEM; } /** * Returns the item ID. */ - int getItemId() const; + int getItemId() const + { return mItemId; } /** - * Returns the item object. Useful for adding an item link for the - * floor item to chat. + * Returns the item info for this floor item. Useful for adding an item + * link for the floor item to chat. */ - Item *getItem() const; + const ItemInfo &getInfo() const; - /** - * Draws this floor item to the given graphics context. - * - * @see Actor::draw(Graphics, int, int) - */ - void draw(Graphics *graphics, int offsetX, int offsetY) const; + virtual int getTileX() const + { return mX; } - /** We consider flooritems (at least for now) to be one layer-sprites */ - virtual int getNumberOfLayers() const - { return 1; } + virtual int getTileY() const + { return mY; } private: - int mId; - Item *mItem; + int mItemId; + int mX, mY; }; #endif diff --git a/src/flooritemmanager.cpp b/src/flooritemmanager.cpp index 9115b704..b4471db8 100644 --- a/src/flooritemmanager.cpp +++ b/src/flooritemmanager.cpp @@ -78,3 +78,12 @@ FloorItem *FloorItemManager::findByCoordinates(int x, int y) const return NULL; } + +void FloorItemManager::logic() +{ + FloorItemIterator it, it_end; + + for (it = mFloorItems.begin(), it_end = mFloorItems.end(); it != it_end; + it++) + (*it)->logic(); +} diff --git a/src/flooritemmanager.h b/src/flooritemmanager.h index 62ca8dc2..7df6288e 100644 --- a/src/flooritemmanager.h +++ b/src/flooritemmanager.h @@ -41,6 +41,11 @@ class FloorItemManager FloorItem *findById(int id) const; FloorItem *findByCoordinates(int x, int y) const; + /** + * Performs floor item logic. + */ + void logic(); + private: typedef std::list<FloorItem*> FloorItems; typedef FloorItems::iterator FloorItemIterator; diff --git a/src/game.cpp b/src/game.cpp index 79f21863..74997b90 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -335,6 +335,7 @@ void Game::logic() // Handle all necessary game logic beingManager->logic(); + floorItemManager->logic(); particleEngine->update(); if (mCurrentMap) mCurrentMap->update(); diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 520b4005..77e919a9 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -182,11 +182,11 @@ void PopupMenu::showPopup(int x, int y, Being *being) void PopupMenu::showPopup(int x, int y, FloorItem *floorItem) { mFloorItem = floorItem; - mItem = floorItem->getItem(); + ItemInfo info = floorItem->getInfo(); mBrowserBox->clearRows(); // Floor item can be picked up (single option, candidate for removal) - std::string name = ItemDB::get(mFloorItem->getItemId()).getName(); + std::string name = info.getName(); mBrowserBox->addRow(strprintf("@@pickup|%s@@", strprintf(_("Pick up %s"), name.c_str()).c_str())); mBrowserBox->addRow(strprintf("@@chat|%s@@", _("Add to chat"))); @@ -283,7 +283,10 @@ void PopupMenu::handleLink(const std::string &link) else if (link == "chat") { - chatWindow->addItemText(mItem->getInfo().getName()); + if (mItem) + chatWindow->addItemText(mItem->getInfo().getName()); + else if (mFloorItem) + chatWindow->addItemText(mFloorItem->getInfo().getName()); } else if (link == "split") diff --git a/src/gui/widgets/playerbox.cpp b/src/gui/widgets/playerbox.cpp index 57cbec6f..a953f474 100644 --- a/src/gui/widgets/playerbox.cpp +++ b/src/gui/widgets/playerbox.cpp @@ -86,8 +86,8 @@ void PlayerBox::draw(gcn::Graphics *graphics) { // Draw character const int bs = getFrameSize(); - const int x = getWidth() / 2 + bs; - const int y = getHeight() - bs; + const int x = getWidth() / 2 + bs - 16; + const int y = getHeight() - bs - 32; mPlayer->drawSpriteAt(static_cast<Graphics*>(graphics), x, y); } diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp index feb9548d..76c05d64 100644 --- a/src/imageparticle.cpp +++ b/src/imageparticle.cpp @@ -39,10 +39,10 @@ ImageParticle::~ImageParticle() mImage->decRef(); } -void ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const +bool ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const { if (!mAlive || !mImage) - return; + return false; int screenX = (int) mPos.x + offsetX - mImage->getWidth() / 2; int screenY = (int) mPos.y - (int)mPos.z + offsetY - mImage->getHeight()/2; @@ -52,9 +52,7 @@ void ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const screenX > graphics->getWidth() || screenY + mImage->getHeight() < 0 || screenY > graphics->getHeight()) - { - return; - } + return false; float alphafactor = mAlpha; @@ -65,5 +63,5 @@ void ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const alphafactor *= (float) mLifetimePast / (float) mFadeIn; mImage->setAlpha(alphafactor); - graphics->drawImage(mImage, screenX, screenY); + return graphics->drawImage(mImage, screenX, screenY); } diff --git a/src/imageparticle.h b/src/imageparticle.h index bc32400d..23909fa3 100644 --- a/src/imageparticle.h +++ b/src/imageparticle.h @@ -49,7 +49,7 @@ class ImageParticle : public Particle /** * Draws the particle image */ - virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; + virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const; protected: Image *mImage; /**< The image used for this particle. */ diff --git a/src/imagesprite.cpp b/src/imagesprite.cpp new file mode 100644 index 00000000..504aba96 --- /dev/null +++ b/src/imagesprite.cpp @@ -0,0 +1,44 @@ +/* + * The Mana Client + * Copyright (C) 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 "imagesprite.h" + +#include "graphics.h" + +ImageSprite::ImageSprite(Image *image): + mImage(image) +{ + mAlpha = mImage->getAlpha(); + + mImage->incRef(); +} + +ImageSprite::~ImageSprite() +{ + mImage->decRef(); +} + +bool ImageSprite::draw(Graphics* graphics, int posX, int posY) const +{ + if (mImage->getAlpha() != mAlpha) + mImage->setAlpha(mAlpha); + + return graphics->drawImage(mImage, posX, posY); +} diff --git a/src/imagesprite.h b/src/imagesprite.h new file mode 100644 index 00000000..83fcfa12 --- /dev/null +++ b/src/imagesprite.h @@ -0,0 +1,63 @@ +/* + * The Mana Client + * Copyright (C) 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/>. + */ + +#ifndef IMAGESPRITE_H +#define IMAGESPRITE_H + +#include "sprite.h" + +#include "resources/image.h" + +class Graphics; + +class ImageSprite : public Sprite +{ +public: + ImageSprite(Image *image); + + ~ImageSprite(); + + virtual void reset() {} + + virtual void play(SpriteAction action) {} + + virtual void update(int time) {} + + virtual bool draw(Graphics* graphics, int posX, int posY) const; + + virtual int getWidth() const + { return mImage->getWidth(); } + + virtual int getHeight() const + { return mImage->getHeight(); } + + virtual Image* getImage() const + { return mImage; } + + virtual void setDirection(SpriteDirection direction) {} + + int getNumberOfLayers() + { return 1; } + +private: + Image *mImage; +}; + +#endif // IMAGESPRITE_H diff --git a/src/item.cpp b/src/item.cpp index 79ccd2c3..19bb7c23 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -57,7 +57,8 @@ void Item::setId(int id) mDrawImage->decRef(); ResourceManager *resman = ResourceManager::getInstance(); - std::string imagePath = "graphics/items/" + getInfo().getImageName(); + SpriteDisplay display = getInfo().getDisplay(); + std::string imagePath = "graphics/items/" + display.image; mImage = resman->getImage(imagePath); mDrawImage = resman->getImage(imagePath); diff --git a/src/monster.cpp b/src/monster.cpp index d25c6c90..922bfa7a 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -79,8 +79,7 @@ void Monster::setAction(Action action, int attackType) case ATTACK: mAttackType = attackType; currentAction = getInfo().getAttackAction(attackType); - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - (*it)->reset(); + reset(); //attack particle effect particleEffect = getInfo().getAttackParticleEffect(attackType); @@ -112,9 +111,7 @@ void Monster::setAction(Action action, int attackType) if (currentAction != ACTION_INVALID) { - for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) - if (*it) - (*it)->play(currentAction); + play(currentAction); mAction = action; } } @@ -125,32 +122,7 @@ void Monster::setSubtype(Uint16 subtype) const MonsterInfo &info = getInfo(); - // Setup Monster sprites - const std::list<std::string> &sprites = info.getSprites(); - - mSprites.clear(); - for (std::list<std::string>::const_iterator i = sprites.begin(); - i != sprites.end(); i++) - { - std::string file = "graphics/sprites/" + *i; - mSprites.push_back(AnimatedSprite::load(file)); - } - - // Ensure that something is shown - if (mSprites.size() == 0) - { - mSprites.push_back(AnimatedSprite::load("graphics/sprites/error.xml")); - } - - if (Particle::enabled) - { - const std::list<std::string> &particleEffects = info.getParticleEffects(); - for (std::list<std::string>::const_iterator i = particleEffects.begin(); - i != particleEffects.end(); i++) - { - controlParticle(particleEngine->addEffect((*i), 0, 0)); - } - } + setupSpriteDisplay(info.getDisplay()); } void Monster::handleAttack(Being *victim, int damage, AttackType type) diff --git a/src/monster.h b/src/monster.h index 9bb8e3b9..843c3fc8 100644 --- a/src/monster.h +++ b/src/monster.h @@ -40,8 +40,7 @@ class Monster : public Being virtual void setSubtype(Uint16 subtype); - virtual TargetCursorSize - getTargetCursorSize() const; + virtual TargetCursorSize getTargetCursorSize() const; /** * Handles an attack of another being by this monster. Plays a hit or diff --git a/src/npc.cpp b/src/npc.cpp index cdfe5193..bdbcfb76 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -38,7 +38,7 @@ #include "resources/npcdb.h" NPC::NPC(int id, int subtype, Map *map): - Player(id, subtype, map, true) + Being(id, subtype, map) { setSubtype(subtype); @@ -60,32 +60,7 @@ void NPC::setSubtype(Uint16 subtype) { Being::setSubtype(subtype); - NPCInfo info = NPCDB::get(subtype); - - mSprites.clear(); - // Setup NPC sprites - for (std::list<NPCsprite*>::const_iterator i = info.sprites.begin(); - i != info.sprites.end(); - i++) - { - std::string file = "graphics/sprites/" + (*i)->sprite; - int variant = (*i)->variant; - mSprites.push_back(AnimatedSprite::load(file, variant)); - mSpriteIDs.push_back(0); - mSpriteColors.push_back(""); - } - - if (Particle::enabled) - { - //setup particle effects - for (std::list<std::string>::const_iterator i = info.particles.begin(); - i != info.particles.end(); - i++) - { - Particle *p = particleEngine->addEffect(*i, 0, 0); - this->controlParticle(p); - } - } + setupSpriteDisplay(NPCDB::get(subtype), false); } void NPC::talk() @@ -93,11 +68,6 @@ void NPC::talk() Net::getNpcHandler()->talk(mId); } -void NPC::setSprite(unsigned int slot, int id, const std::string &color) -{ - // Do nothing -} - bool NPC::isTalking() { return NpcDialog::isActive() || BuyDialog::isActive() || @@ -22,12 +22,12 @@ #ifndef NPC_H #define NPC_H -#include "player.h" +#include "being.h" class Graphics; class Text; -class NPC : public Player +class NPC : public Being { public: NPC(int id, int subtype, Map *map); @@ -40,9 +40,6 @@ class NPC : public Player void talk(); - void setSprite(unsigned int slot, int id, - const std::string &color = ""); - /** * Gets the way an NPC is blocked by other things on the map */ @@ -65,9 +62,6 @@ class NPC : public Player */ virtual Map::BlockType getBlockType() const { return Map::BLOCKTYPE_CHARACTER; } //blocks like a player character - - // Colors don't change for NPCs - virtual void updateColors() {} }; #endif diff --git a/src/particle.cpp b/src/particle.cpp index 398e6a9c..6a3fd7da 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -61,6 +61,7 @@ Particle::Particle(Map *map): mLifetimePast(0), mFadeOut(0), mFadeIn(0), + mAlpha(1.0f), mAutoDelete(true), mAllowSizeAdjust(false), mGravity(0.0f), @@ -93,8 +94,9 @@ void Particle::setupEngine() logger->log("Particle engine set up"); } -void Particle::draw(Graphics *, int, int) const +bool Particle::draw(Graphics *, int, int) const { + return false; } bool Particle::update() diff --git a/src/particle.h b/src/particle.h index 64221514..2be169c1 100644 --- a/src/particle.h +++ b/src/particle.h @@ -83,7 +83,7 @@ class Particle : public Actor /** * Draws the particle image. */ - virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; + virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const; /** * Necessary for sorting with the other sprites. @@ -247,12 +247,18 @@ class Particle : public Actor virtual int getNumberOfLayers() const { return 1; } + virtual float getAlpha() const + { return 1.0f; } + + virtual void setAlpha(float alpha) {} + protected: bool mAlive; /**< Is the particle supposed to be drawn and updated?*/ int mLifetimeLeft; /**< Lifetime left in game ticks*/ int mLifetimePast; /**< Age of the particle in game ticks*/ int mFadeOut; /**< Lifetime in game ticks left where fading out begins*/ int mFadeIn; /**< Age in game ticks where fading in is finished*/ + float mAlpha; /**< Opacity of the graphical representation of the particle */ // generic properties bool mAutoDelete; /**< May the particle request its deletion by the parent particle? */ diff --git a/src/player.cpp b/src/player.cpp index 1f706cb8..10c4c500 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -43,23 +43,20 @@ #include "utils/stringutils.h" -Player::Player(int id, int subtype, Map *map, bool isNPC): +Player::Player(int id, int subtype, Map *map): Being(id, subtype, map), mGender(GENDER_UNSPECIFIED), mParty(NULL), mIsGM(false) { - if (!isNPC) + for (int i = 0; i < Net::getCharHandler()->maxSprite(); i++) { - for (int i = 0; i < Net::getCharHandler()->maxSprite(); i++) - { - mSprites.push_back(NULL); - mSpriteIDs.push_back(0); - mSpriteColors.push_back(""); - } - - setSubtype(subtype); + push_back(NULL); + mSpriteIDs.push_back(0); + mSpriteColors.push_back(""); } + + setSubtype(subtype); mShowName = config.getValue("visiblenames", 1); config.addListener("visiblenames", this); @@ -151,7 +148,7 @@ void Player::setGender(Gender gender) mGender = gender; // Reload all subsprites - for (unsigned int i = 0; i < mSprites.size(); i++) + for (unsigned int i = 0; i < size(); i++) { if (mSpriteIDs.at(i) != 0) setSprite(i, mSpriteIDs.at(i), mSpriteColors.at(i)); @@ -169,16 +166,13 @@ void Player::setGM(bool gm) void Player::setSprite(int slot, int id, const std::string &color, bool isWeapon) { - if (getType() == NPC) - return; - assert(slot < Net::getCharHandler()->maxSprite()); // id = 0 means unequip if (id == 0) { - delete mSprites[slot]; - mSprites[slot] = NULL; + delete at(slot); + at(slot) = NULL; if (isWeapon) mEquippedWeapon = NULL; @@ -200,10 +194,10 @@ void Player::setSprite(int slot, int id, const std::string &color, if (equipmentSprite) equipmentSprite->setDirection(getSpriteDirection()); - if (mSprites[slot]) - delete mSprites[slot]; + if (at(slot)) + delete at(slot); - mSprites[slot] = equipmentSprite; + at(slot) = equipmentSprite; if (isWeapon) mEquippedWeapon = &ItemDB::get(id); diff --git a/src/player.h b/src/player.h index e75870a0..0bb59428 100644 --- a/src/player.h +++ b/src/player.h @@ -47,7 +47,7 @@ class Player : public Being /** * Constructor. */ - Player(int id, int subtype, Map *map, bool isNPC = false); + Player(int id, int subtype, Map *map); ~Player(); diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp index 4638f00e..57df6143 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -46,6 +46,7 @@ namespace // Forward declarations static void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node); static void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node); +static void loadFloorSprite(SpriteDisplay *display, xmlNodePtr node); static char const *const fields[][2] = { @@ -114,7 +115,7 @@ void ItemDB::load() mUnknown = new ItemInfo; mUnknown->setName(_("Unknown item")); - mUnknown->setImageName(""); + mUnknown->setDisplay(SpriteDisplay()); mUnknown->setSprite("error.xml", GENDER_MALE); mUnknown->setSprite("error.xml", GENDER_FEMALE); @@ -154,9 +155,11 @@ void ItemDB::load() int attackRange = XML::getProperty(node, "attack-range", 0); std::string missileParticle = XML::getProperty(node, "missile-particle", ""); + SpriteDisplay display; + display.image = image; + ItemInfo *itemInfo = new ItemInfo; itemInfo->setId(id); - itemInfo->setImageName(image); itemInfo->setName(name.empty() ? _("unnamed") : name); itemInfo->setDescription(description); itemInfo->setType(itemTypeFromString(typeStr)); @@ -202,8 +205,14 @@ void ItemDB::load() { loadSoundRef(itemInfo, itemChild); } + else if (xmlStrEqual(itemChild->name, BAD_CAST "floor")) + { + loadFloorSprite(&display, itemChild); + } } + itemInfo->setDisplay(display); + mItemInfos[id] = itemInfo; if (!name.empty()) { @@ -334,3 +343,22 @@ void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node) event.c_str()); } } + +void loadFloorSprite(SpriteDisplay *display, xmlNodePtr floorNode) +{ + for_each_xml_child_node(spriteNode, floorNode) + { + if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) + { + SpriteReference *currentSprite = new SpriteReference; + currentSprite->sprite = (const char*)spriteNode->xmlChildrenNode->content; + currentSprite->variant = XML::getProperty(spriteNode, "variant", 0); + display->sprites.push_back(currentSprite); + } + else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx")) + { + std::string particlefx = (const char*)spriteNode->xmlChildrenNode->content; + display->particles.push_back(particlefx); + } + } +} diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h index a7c0ddca..f42ac8e6 100644 --- a/src/resources/iteminfo.h +++ b/src/resources/iteminfo.h @@ -143,11 +143,11 @@ class ItemInfo std::string getParticleEffect() const { return mParticle; } - void setImageName(const std::string &imageName) - { mImageName = imageName; } + void setDisplay(SpriteDisplay display) + { mDisplay = display; } - const std::string &getImageName() const - { return mImageName; } + const SpriteDisplay &getDisplay() const + { return mDisplay; } void setDescription(const std::string &description) { mDescription = description; } @@ -201,7 +201,7 @@ class ItemInfo const std::string &getSound(EquipmentSoundEvent event) const; protected: - std::string mImageName; /**< The filename of the icon image. */ + SpriteDisplay mDisplay; /**< Display info (like icon) */ std::string mName; std::string mDescription; /**< Short description. */ std::string mEffect; /**< Description of effects. */ diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp index e9afa7ef..658321f1 100644 --- a/src/resources/monsterdb.cpp +++ b/src/resources/monsterdb.cpp @@ -45,7 +45,16 @@ void MonsterDB::load() if (mLoaded) return; - mUnknown.addSprite("error.xml"); + { + SpriteReference *unknownSprite = new SpriteReference; + unknownSprite->sprite = "error.xml"; + unknownSprite->variant = 0; + + SpriteDisplay display; + display.sprites.push_front(unknownSprite); + + mUnknown.setDisplay(display); + } logger->log("Initializing monster database..."); @@ -94,13 +103,17 @@ void MonsterDB::load() currentInfo->setTargetCursorSize(Being::TC_MEDIUM); } + SpriteDisplay display; + //iterate <sprite>s and <sound>s for_each_xml_child_node(spriteNode, monsterNode) { if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) { - currentInfo->addSprite( - (const char*) spriteNode->xmlChildrenNode->content); + SpriteReference *currentSprite = new SpriteReference; + currentSprite->sprite = (const char*)spriteNode->xmlChildrenNode->content; + currentSprite->variant = XML::getProperty(spriteNode, "variant", 0); + display.sprites.push_back(currentSprite); } else if (xmlStrEqual(spriteNode->name, BAD_CAST "sound")) { @@ -145,10 +158,12 @@ void MonsterDB::load() } else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx")) { - currentInfo->addParticleEffect( + display.particles.push_back( (const char*) spriteNode->xmlChildrenNode->content); } } + currentInfo->setDisplay(display); + mMonsterInfos[XML::getProperty(monsterNode, "id", 0) + offset] = currentInfo; } diff --git a/src/resources/monsterinfo.cpp b/src/resources/monsterinfo.cpp index 9104c721..41ce5b6b 100644 --- a/src/resources/monsterinfo.cpp +++ b/src/resources/monsterinfo.cpp @@ -91,8 +91,3 @@ void MonsterInfo::addMonsterAttack(int id, a->action = action; mMonsterAttacks[id] = a; } - -void MonsterInfo::addParticleEffect(const std::string &filename) -{ - mParticleEffects.push_back(filename); -} diff --git a/src/resources/monsterinfo.h b/src/resources/monsterinfo.h index f074254a..7741f762 100644 --- a/src/resources/monsterinfo.h +++ b/src/resources/monsterinfo.h @@ -24,6 +24,8 @@ #include "being.h" +#include "resources/spritedef.h" + #include <list> #include <map> #include <string> @@ -59,23 +61,21 @@ class MonsterInfo void setName(const std::string &name) { mName = name; } - void addSprite(const std::string &filename) - { mSprites.push_back(filename); } + void setDisplay(SpriteDisplay display) + { mDisplay = display; } + + const SpriteDisplay &getDisplay() const + { return mDisplay; } void setTargetCursorSize(Being::TargetCursorSize targetCursorSize) { mTargetCursorSize = targetCursorSize; } void addSound(MonsterSoundEvent event, const std::string &filename); - void addParticleEffect(const std::string &filename); - const std::string &getName() const { return mName; } - const std::list<std::string>& getSprites() const - { return mSprites; } - - Being::TargetCursorSize getTargetCursorSize() const + ActorSprite::TargetCursorSize getTargetCursorSize() const { return mTargetCursorSize; } const std::string &getSound(MonsterSoundEvent event) const; @@ -91,16 +91,12 @@ class MonsterInfo SpriteAction getAttackAction(int attackType) const; - const std::list<std::string>& getParticleEffects() const - { return mParticleEffects; } - private: + SpriteDisplay mDisplay; std::string mName; - std::list<std::string> mSprites; Being::TargetCursorSize mTargetCursorSize; std::map<MonsterSoundEvent, std::vector<std::string>* > mSounds; std::map<int, MonsterAttack*> mMonsterAttacks; - std::list<std::string> mParticleEffects; }; #endif // MONSTERINFO_H diff --git a/src/resources/npcdb.cpp b/src/resources/npcdb.cpp index 4bc5a6e4..e2628257 100644 --- a/src/resources/npcdb.cpp +++ b/src/resources/npcdb.cpp @@ -28,7 +28,7 @@ namespace { NPCInfos mNPCInfos; - NPCInfo mUnknown; + SpriteDisplay mUnknown; bool mLoaded = false; } @@ -37,10 +37,12 @@ void NPCDB::load() if (mLoaded) return; - NPCsprite *unknownSprite = new NPCsprite; - unknownSprite->sprite = "error.xml"; - unknownSprite->variant = 0; - mUnknown.sprites.push_back(unknownSprite); + { + SpriteReference *unknownSprite = new SpriteReference; + unknownSprite->sprite = "error.xml"; + unknownSprite->variant = 0; + mUnknown.sprites.push_back(unknownSprite); + } logger->log("Initializing NPC database..."); @@ -65,13 +67,13 @@ void NPCDB::load() continue; } - NPCInfo *currentInfo = new NPCInfo; + SpriteDisplay *currentInfo = new SpriteDisplay; for_each_xml_child_node(spriteNode, npcNode) { if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) { - NPCsprite *currentSprite = new NPCsprite; + SpriteReference *currentSprite = new SpriteReference; currentSprite->sprite = (const char*)spriteNode->xmlChildrenNode->content; currentSprite->variant = XML::getProperty(spriteNode, "variant", 0); currentInfo->sprites.push_back(currentSprite); @@ -113,7 +115,7 @@ void NPCDB::unload() mLoaded = false; } -const NPCInfo& NPCDB::get(int id) +const SpriteDisplay& NPCDB::get(int id) { NPCInfosIterator i = mNPCInfos.find(id); diff --git a/src/resources/npcdb.h b/src/resources/npcdb.h index 9da873e4..84ae4e24 100644 --- a/src/resources/npcdb.h +++ b/src/resources/npcdb.h @@ -22,23 +22,11 @@ #ifndef NPC_DB_H #define NPC_DB_H -#include <list> -#include <map> -#include <string> - -struct NPCsprite -{ - std::string sprite; - int variant; -}; +#include "resources/spritedef.h" -struct NPCInfo -{ - std::list<NPCsprite*> sprites; - std::list<std::string> particles; -}; +#include <map> -typedef std::map<int, NPCInfo*> NPCInfos; +typedef std::map<int, SpriteDisplay*> NPCInfos; /** * NPC information database. @@ -49,7 +37,7 @@ namespace NPCDB void unload(); - const NPCInfo& get(int id); + const SpriteDisplay& get(int id); typedef NPCInfos::iterator NPCInfosIterator; } diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index 24f346f7..3d23edcd 100644 --- a/src/resources/resourcemanager.cpp +++ b/src/resources/resourcemanager.cpp @@ -164,6 +164,8 @@ void ResourceManager::searchAndAddArchives(const std::string &path, const char *dirSep = PHYSFS_getDirSeparator(); char **list = PHYSFS_enumerateFiles(path.c_str()); + printf("Path: %s -> %s\n", path.c_str(), PHYSFS_getRealDir(path.c_str())); + for (char **i = list; *i; i++) { size_t len = strlen(*i); diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index 5bb6078e..d5db542c 100644 --- a/src/resources/spritedef.h +++ b/src/resources/spritedef.h @@ -26,12 +26,28 @@ #include <libxml/tree.h> +#include <list> #include <map> #include <string> class Action; class ImageSet; +struct SpriteReference +{ + std::string sprite; + int variant; +}; + +struct SpriteDisplay +{ + std::string image; + std::list<SpriteReference*> sprites; + std::list<std::string> particles; +}; + +typedef std::list<SpriteReference*>::const_iterator SpriteRefs; + enum SpriteAction { ACTION_DEFAULT = 0, diff --git a/src/sprite.h b/src/sprite.h new file mode 100644 index 00000000..d8345ab8 --- /dev/null +++ b/src/sprite.h @@ -0,0 +1,92 @@ +/* + * The Mana Client + * Copyright (C) 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/>. + */ + +#ifndef SPRITE_H +#define SPRITE_H + +#include "resources/spritedef.h" + +class Graphics; +class Image; + +class Sprite +{ + public: + virtual ~Sprite() {} + + /** + * Resets the sprite. + */ + virtual void reset() = 0; + + /** + * Plays an action using the current direction + */ + virtual void play(SpriteAction action) = 0; + + /** + * Inform the animation of the passed time so that it can output the + * correct animation frame. + */ + virtual void update(int time) = 0; + + /** + * Draw the current animation frame at the coordinates given in screen + * pixels. + */ + virtual bool draw(Graphics* graphics, int posX, int posY) const = 0; + + /** + * Gets the width in pixels of the image of the current frame + */ + virtual int getWidth() const = 0; + + /** + * Gets the height in pixels of the image of the current frame + */ + virtual int getHeight() const = 0; + + /** + * Returns a reference to the current image being drawn. + */ + virtual Image* getImage() const = 0; + + /** + * Sets the direction. + */ + virtual void setDirection(SpriteDirection direction) = 0; + + /** + * Sets the alpha value of the animated sprite + */ + virtual void setAlpha(float alpha) + { mAlpha = alpha; } + + /** + * Returns the current alpha opacity of the animated sprite. + */ + virtual float getAlpha() const + { return mAlpha; } + + protected: + float mAlpha; /**< The alpha opacity used to draw */ +}; + +#endif // SPRITE_H diff --git a/src/textparticle.cpp b/src/textparticle.cpp index e6226449..827343fa 100644 --- a/src/textparticle.cpp +++ b/src/textparticle.cpp @@ -36,10 +36,10 @@ TextParticle::TextParticle(Map *map, const std::string &text, { } -void TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const +bool TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const { if (!mAlive) - return; + return false; int screenX = (int) mPos.x + offsetX; int screenY = (int) mPos.y - (int) mPos.z + offsetY; @@ -58,4 +58,6 @@ void TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const TextRenderer::renderText(graphics, mText, screenX, screenY, gcn::Graphics::CENTER, color, mTextFont, mOutline, false); + + return true; } diff --git a/src/textparticle.h b/src/textparticle.h index a61bf8d9..7d99a057 100644 --- a/src/textparticle.h +++ b/src/textparticle.h @@ -38,7 +38,7 @@ class TextParticle : public Particle /** * Draws the particle image. */ - virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; + virtual bool draw(Graphics *graphics, int offsetX, int offsetY) const; // hack to improve text visibility virtual int getPixelY() const |