diff options
Diffstat (limited to 'src')
58 files changed, 1542 insertions, 1057 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e6ee7405..e6b9d3b1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,6 +49,8 @@ MARK_AS_ADVANCED(SDL_INCLUDE_DIR) MARK_AS_ADVANCED(SDL_LIBRARY) SET(SRCS + gui/widgets/dropdown.cpp + gui/widgets/dropdown.h gui/widgets/resizegrip.cpp gui/widgets/resizegrip.h gui/box.cpp @@ -159,6 +161,8 @@ SET(SRCS gui/skill.h gui/slider.cpp gui/slider.h + gui/speechbubble.cpp + gui/speechbubble.h gui/status.cpp gui/status.h gui/tabbedcontainer.cpp diff --git a/src/Makefile.am b/src/Makefile.am index d2fd5efb..f6d06536 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -117,6 +117,8 @@ aethyra_SOURCES = gui/widgets/dropdown.cpp \ gui/skill.h \ gui/slider.cpp \ gui/slider.h \ + gui/speechbubble.cpp \ + gui/speechbubble.h \ gui/status.cpp \ gui/status.h \ gui/tabbedcontainer.cpp \ diff --git a/src/being.cpp b/src/being.cpp index fd1b67fe..1d7f5ee7 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -22,8 +22,8 @@ */ #include "being.h" -#include <algorithm> #include <cassert> +#include <cmath> #include "animatedsprite.h" #include "equipment.h" @@ -32,7 +32,7 @@ #include "log.h" #include "map.h" #include "particle.h" -#include "text.h" +#include "sound.h" #include "localplayer.h" #include "resources/resourcemanager.h" @@ -40,20 +40,22 @@ #include "resources/iteminfo.h" #include "gui/gui.h" +#include "gui/speechbubble.h" #include "utils/dtor.h" #include "utils/tostring.h" +#include "utils/xml.h" + +#define BEING_EFFECTS_FILE "effects.xml" + int Being::instances = 0; ImageSet *Being::emotionSet = NULL; -static const int X_SPEECH_OFFSET = 18; -static const int Y_SPEECH_OFFSET = 60; - Being::Being(int id, int job, Map *map): mJob(job), mX(0), mY(0), - mAction(0), + mAction(STAND), mWalkTime(0), mEmotion(0), mEmotionTime(0), mAttackSpeed(350), @@ -73,6 +75,8 @@ Being::Being(int id, int job, Map *map): { setMap(map); + mSpeechBubble = new SpeechBubble(); + if (instances == 0) { // Load the emotion set @@ -82,13 +86,13 @@ Being::Being(int id, int job, Map *map): } instances++; - mSpeech = 0; + mSpeech = ""; mIsGM = false; } Being::~Being() { - std::for_each(mSprites.begin(), mSprites.end(), make_dtor(mSprites)); + delete_all(mSprites); clearPath(); for ( std::list<Particle *>::iterator i = mChildParticleEffects.begin(); @@ -107,14 +111,11 @@ Being::~Being() emotionSet->decRef(); emotionSet = NULL; } - if (mSpeech) - { - delete mSpeech; - } + + delete mSpeechBubble; } -void -Being::setDestination(Uint16 destX, Uint16 destY) +void Being::setDestination(Uint16 destX, Uint16 destY) { if (mMap) { @@ -122,14 +123,12 @@ Being::setDestination(Uint16 destX, Uint16 destY) } } -void -Being::clearPath() +void Being::clearPath() { mPath.clear(); } -void -Being::setPath(const Path &path) +void Being::setPath(const Path &path) { mPath = path; @@ -140,36 +139,26 @@ Being::setPath(const Path &path) } } -void -Being::setHairStyle(int style, int color) +void Being::setHairStyle(int style, int color) { mHairStyle = style < 0 ? mHairStyle : style % NR_HAIR_STYLES; mHairColor = color < 0 ? mHairColor : color % NR_HAIR_COLORS; } -void -Being::setSprite(int slot, int id, std::string color) +void Being::setSprite(int slot, int id, std::string color) { assert(slot >= BASE_SPRITE && slot < VECTOREND_SPRITE); mSpriteIDs[slot] = id; mSpriteColors[slot] = color; } -void -Being::setSpeech(const std::string &text, Uint32 time) +void Being::setSpeech(const std::string &text, Uint32 time) { - if (mSpeech) // don't introduce a memory leak - { - delete mSpeech; - } - mSpeech = new Text(text, mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET, - gcn::Graphics::CENTER, speechFont, - gcn::Color(255, 255, 255)); + mSpeech = text; mSpeechTime = 500; } -void -Being::takeDamage(int amount) +void Being::takeDamage(int amount) { gcn::Font *font; std::string damage = amount ? toString(amount) : "miss"; @@ -200,16 +189,14 @@ Being::takeDamage(int amount) mPx + 16, mPy + 16); } -void -Being::handleAttack(Being *victim, int damage) +void Being::handleAttack(Being *victim, int damage) { setAction(Being::ATTACK); mFrame = 0; mWalkTime = tick_time; } -void -Being::setMap(Map *map) +void Being::setMap(Map *map) { // Remove sprite from potential previous map if (mMap) @@ -229,8 +216,7 @@ Being::setMap(Map *map) mChildParticleEffects.clear(); } -void -Being::controlParticle(Particle *particle) +void Being::controlParticle(Particle *particle) { if (particle) { @@ -240,8 +226,7 @@ Being::controlParticle(Particle *particle) } } -void -Being::setAction(Uint8 action) +void Being::setAction(Action action) { SpriteAction currentAction = ACTION_INVALID; switch (action) @@ -294,10 +279,11 @@ Being::setAction(Uint8 action) } } - -void -Being::setDirection(Uint8 direction) +void Being::setDirection(Uint8 direction) { + if (mDirection == direction) + return; + mDirection = direction; SpriteDirection dir = getSpriteDirection(); @@ -308,8 +294,7 @@ Being::setDirection(Uint8 direction) } } -SpriteDirection -Being::getSpriteDirection() const +SpriteDirection Being::getSpriteDirection() const { SpriteDirection dir; @@ -325,15 +310,15 @@ Being::getSpriteDirection() const { dir = DIRECTION_RIGHT; } - else { - dir = DIRECTION_LEFT; + else + { + dir = DIRECTION_LEFT; } - + return dir; } -void -Being::nextStep() +void Being::nextStep() { if (mPath.empty()) { @@ -368,32 +353,23 @@ Being::nextStep() mWalkTime += mWalkSpeed / 10; } -void -Being::logic() +void Being::logic() { // Reduce the time that speech is still displayed - if (mSpeechTime > 0 && mSpeech) - { - if (--mSpeechTime == 0) - { - delete mSpeech; - mSpeech = 0; - } - } + if (mSpeechTime > 0) + mSpeechTime--; int oldPx = mPx; int oldPy = mPy; // Update pixel coordinates mPx = mX * 32 + getXOffset(); mPy = mY * 32 + getYOffset(); + if (mPx != oldPx || mPy != oldPy) { - if (mSpeech) - { - mSpeech->adviseXY(mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET); - } updateCoords(); } + if (mEmotion != 0) { mEmotionTime--; @@ -412,25 +388,23 @@ Being::logic() } //Update particle effects - for ( std::list<Particle *>::iterator i = mChildParticleEffects.begin(); - i != mChildParticleEffects.end(); - - ) + for (std::list<Particle *>::iterator i = mChildParticleEffects.begin(); + i != mChildParticleEffects.end();) { (*i)->setPosition((float)mPx + 16.0f, (float)mPy + 32.0f); - if (!(*i)->isAlive()) + if ((*i)->isExtinct()) { (*i)->kill(); i = mChildParticleEffects.erase(i); } - else { + else + { i++; } } } -void -Being::draw(Graphics *graphics, int offsetX, int offsetY) const +void Being::draw(Graphics *graphics, int offsetX, int offsetY) const { int px = mPx + offsetX; int py = mPy + offsetY; @@ -444,8 +418,7 @@ Being::draw(Graphics *graphics, int offsetX, int offsetY) const } } -void -Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) +void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) { if (!mEmotion) return; @@ -458,17 +431,39 @@ Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) graphics->drawImage(emotionSet->get(emotionIndex), px, py); } -Being::Type -Being::getType() const +void Being::drawSpeech(Graphics *graphics, int offsetX, int offsetY) +{ + int px = mPx + offsetX; + int py = mPy + offsetY; + + // Draw speech above this being + if (mSpeechTime > 0) + { + mSpeechBubble->setCaption(mName); + mSpeechBubble->setWindowName(mName); + // Not quite centered, but close enough. However, it's not too important to get + // it right right now, as it doesn't take bubble collision into account yet. + mSpeechBubble->setPosition(px - (mSpeechBubble->getWidth() * 4 / 11), py - 70 - + (mSpeechBubble->getNumRows()*14)); + mSpeechBubble->setText( mSpeech ); + mSpeechBubble->setVisible(true); + } + else if (mSpeechTime == 0) + { + mSpeechBubble->setVisible(false); + } +} + +Being::Type Being::getType() const { return UNKNOWN; } -int -Being::getOffset(char pos, char neg) const +int Being::getOffset(char pos, char neg) const { // Check whether we're walking in the requested direction - if (mAction != WALK || !(mDirection & (pos | neg))) { + if (mAction != WALK || !(mDirection & (pos | neg))) + { return 0; } @@ -476,40 +471,132 @@ Being::getOffset(char pos, char neg) const // We calculate the offset _from_ the _target_ location offset -= 32; - if (offset > 0) { + if (offset > 0) + { offset = 0; } // Going into negative direction? Invert the offset. - if (mDirection & pos) { + if (mDirection & pos) + { offset = -offset; } return offset; } - -int -Being::getWidth() const +int Being::getWidth() const { if (mSprites[BASE_SPRITE]) { return mSprites[BASE_SPRITE]->getWidth(); } - else { + else + { return 0; } } -int -Being::getHeight() const +int Being::getHeight() const { if (mSprites[BASE_SPRITE]) { return mSprites[BASE_SPRITE]->getHeight(); } - else { + else + { return 0; } } + +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]; + + if (!ed) + return default_effect; + else + return ed; +} + +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 != "") { + Particle *selfFX; + + selfFX = particleEngine->addEffect(ed->mGFXEffect, 0, 0); + controlParticle(selfFX); + } + + if (sfx && ed->mSFXEffect != "") { + sound.playSfx(ed->mSFXEffect); + } +} diff --git a/src/being.h b/src/being.h index f196f66c..b7f85c63 100644 --- a/src/being.h +++ b/src/being.h @@ -25,7 +25,6 @@ #define _TMW_BEING_H #include <list> -#include <memory> #include <string> #include <SDL_types.h> #include <vector> @@ -34,6 +33,8 @@ #include "map.h" #include "animatedsprite.h" +#include "gui/speechbubble.h" + #define NR_HAIR_STYLES 10 #define NR_HAIR_COLORS 16 @@ -47,7 +48,7 @@ class Map; class Graphics; class ImageSet; class Particle; -class Text; +class SpeechBubble; /** * A position along a being's path. @@ -116,20 +117,17 @@ class Being : public Sprite /** * Directions, to be used as bitmask values */ - static const char DOWN = 1; - static const char LEFT = 2; - static const char UP = 4; - static const char RIGHT = 8; + enum { DOWN = 1, LEFT = 2, UP = 4, RIGHT = 8 }; Uint16 mJob; /**< Job (player job, npc, monster, ) */ Uint16 mX, mY; /**< Tile coordinates */ - Uint8 mAction; /**< Action the being is performing */ - Uint8 mFrame; + Action mAction; /**< Action the being is performing */ + Uint16 mFrame; Uint16 mWalkTime; Uint8 mEmotion; /**< Currently showing emotion */ Uint8 mEmotionTime; /**< Time until emotion disappears */ - Uint16 mAttackSpeed; /**< Attack speed */ + Uint16 mAttackSpeed; /**< Attack speed */ /** * Constructor. @@ -165,8 +163,7 @@ class Being : public Sprite * * @param amount The amount of damage. */ - virtual void - takeDamage(int amount); + virtual void takeDamage(int amount); /** * Handles an attack of another being by this being. @@ -174,22 +171,19 @@ class Being : public Sprite * @param victim The attacked being. * @param damage The amount of damage dealt (0 means miss). */ - virtual void - handleAttack(Being *victim, int damage); + virtual void handleAttack(Being *victim, int damage); /** * Returns the name of the being. */ - const std::string& - getName() const { return mName; } + const std::string& getName() const { return mName; } /** * Sets the name for the being. * * @param name The name that should appear. */ - virtual void - setName(const std::string &name) { mName = name; } + virtual void setName(const std::string &name) { mName = name; } /** * Gets the hair color for this being. @@ -206,47 +200,44 @@ class Being : public Sprite /** * Sets the hair style and color for this being. */ - virtual void - setHairStyle(int style, int color); + virtual void setHairStyle(int style, int color); /** * Sets visible equipments for this being. */ - virtual void - setSprite(int slot, int id, std::string color = ""); + virtual void setSprite(int slot, int id, std::string color = ""); /** * Sets the gender of this being. */ - virtual void - setGender(int gender) { mGender = gender; } + virtual void setGender(int gender) { mGender = gender; } /** * Gets the gender of this being. */ - int - getGender() const { return mGender; } + int getGender() const { return mGender; } /** * Makes this being take the next step of his path. */ - virtual void - nextStep(); + virtual void nextStep(); - virtual void - setGM() { mIsGM = true; } + virtual void setGM() { mIsGM = true; } /** * Performs being logic. */ - virtual void - logic(); + virtual void logic(); + + /** + * Draws the speech text above the being. + */ + void drawSpeech(Graphics *graphics, int offsetX, int offsetY); /** * Draws the emotion picture above the being. */ - void - drawEmotion(Graphics *graphics, int offsetX, int offsetY); + void drawEmotion(Graphics *graphics, int offsetX, int offsetY); /** * Returns the type of the being. @@ -256,26 +247,22 @@ class Being : public Sprite /** * Gets the walk speed. */ - Uint16 - getWalkSpeed() const { return mWalkSpeed; } + Uint16 getWalkSpeed() const { return mWalkSpeed; } /** * Sets the walk speed. */ - void - setWalkSpeed(Uint16 speed) { mWalkSpeed = speed; } + void setWalkSpeed(Uint16 speed) { mWalkSpeed = speed; } /** * Gets the sprite id. */ - Uint32 - getId() const { return mId; } + Uint32 getId() const { return mId; } /** * Sets the sprite id. */ - void - setId(Uint32 id) { mId = id; } + void setId(Uint32 id) { mId = id; } /** * Sets the map the being is on @@ -285,8 +272,7 @@ class Being : public Sprite /** * Sets the current action. */ - virtual void - setAction(Uint8 action); + virtual void setAction(Action action); /** * Returns the current direction. @@ -299,50 +285,53 @@ class Being : public Sprite void setDirection(Uint8 direction); /** + * Gets the current action. + */ + int getWalkTime() { return mWalkTime; } + + /** + * Returns the direction the being is facing. + */ + SpriteDirection getSpriteDirection() const; + + /** * Draws this being to the given graphics context. * * @see Sprite::draw(Graphics, int, int) */ - virtual void - draw(Graphics *graphics, int offsetX, int offsetY) const; + virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; /** * Returns the pixel X coordinate. */ - int - getPixelX() const { return mPx; } + int getPixelX() const { return mPx; } /** * Returns the pixel Y coordinate. * * @see Sprite::getPixelY() */ - int - getPixelY() const { return mPy; } + int getPixelY() const { return mPy; } /** * Get the current X pixel offset. */ - int - getXOffset() const { return getOffset(LEFT, RIGHT); } + int getXOffset() const { return getOffset(LEFT, RIGHT); } /** * Get the current Y pixel offset. */ - int - getYOffset() const { return getOffset(UP, DOWN); } + int getYOffset() const { return getOffset(UP, DOWN); } /** * Returns the horizontal size of the current base sprite of the being */ - virtual int - getWidth() const; + virtual int getWidth() const; /** * Returns the vertical size of the current base sprite of the being */ - virtual int - getHeight() const; + virtual int getHeight() const; /** * Returns the required size of a target cursor for this being. @@ -361,6 +350,15 @@ class Being : public Sprite mEmotionTime = emote_time; } + /** + * 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); } + const std::auto_ptr<Equipment> mEquipment; protected: @@ -375,9 +373,13 @@ class Being : public Sprite virtual void updateCoords() {} /** - * Returns the sprite direction of this being. + * 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 */ - SpriteDirection getSpriteDirection() const; + void internalTriggerEffect(int effectId, bool sfx, bool gfx); Uint32 mId; /**< Unique sprite id */ Uint16 mWalkSpeed; /**< Walking speed */ @@ -391,7 +393,7 @@ class Being : public Sprite const ItemInfo* mEquippedWeapon; Path mPath; - Text *mSpeech; + std::string mSpeech; Uint16 mHairStyle, mHairColor; Uint8 mGender; Uint32 mSpeechTime; @@ -409,6 +411,9 @@ class Being : public Sprite */ int getOffset(char pos, char neg) const; + // Speech Bubble components + SpeechBubble *mSpeechBubble; + static int instances; /**< Number of Being instances */ static ImageSet *emotionSet; /**< Emoticons used by beings */ }; diff --git a/src/beingmanager.cpp b/src/beingmanager.cpp index 47729a92..f0ce9bd3 100644 --- a/src/beingmanager.cpp +++ b/src/beingmanager.cpp @@ -21,7 +21,7 @@ * $Id: beingmanager.cpp 4237 2008-05-14 18:57:32Z b_lindeijer $ */ -#include <algorithm> +#include <cassert> #include "beingmanager.h" @@ -124,7 +124,7 @@ Being* BeingManager::findBeing(Uint16 x, Uint16 y, Being::Type type) return (i == mBeings.end()) ? NULL : *i; } -/*Being* BeingManager::findBeingByPixel(Uint16 x, Uint16 y) +Being* BeingManager::findBeingByPixel(Uint16 x, Uint16 y) { BeingIterator itr = mBeings.begin(); BeingIterator itr_end = mBeings.end(); @@ -144,7 +144,7 @@ Being* BeingManager::findBeing(Uint16 x, Uint16 y, Being::Type type) } return NULL; -}*/ +} Being* BeingManager::findBeingByName(std::string name, Being::Type type) { @@ -159,8 +159,6 @@ Being* BeingManager::findBeingByName(std::string name, Being::Type type) return NULL; } - - Beings& BeingManager::getAll() { return mBeings; @@ -193,7 +191,7 @@ void BeingManager::clear() mBeings.remove(player_node); } - for_each(mBeings.begin(), mBeings.end(), make_dtor(mBeings)); + delete_all(mBeings); mBeings.clear(); if (player_node) @@ -208,9 +206,12 @@ Being* BeingManager::findNearestLivingBeing(Uint16 x, Uint16 y, int maxdist, Being *closestBeing = NULL; int dist = 0; - for (BeingIterator i = mBeings.begin(); i != mBeings.end(); i++) + BeingIterator itr = mBeings.begin(); + BeingIterator itr_end = mBeings.end(); + + for (; itr != itr_end; ++itr) { - Being *being = (*i); + Being *being = (*itr); int d = abs(being->mX - x) + abs(being->mY - y); if ((being->getType() == type || type == Being::UNKNOWN) diff --git a/src/beingmanager.h b/src/beingmanager.h index f8391855..6942ff54 100644 --- a/src/beingmanager.h +++ b/src/beingmanager.h @@ -51,7 +51,7 @@ class BeingManager /** * Create a being and add it to the list of beings. */ - Being* createBeing(Uint32 id, Uint16 job); + Being *createBeing(Uint32 id, Uint16 job); /** * Remove a Being. @@ -62,33 +62,36 @@ class BeingManager * Return a specific id Being. */ Being* findBeing(Uint32 id); + Being* findBeingByPixel(Uint16 x, Uint16 y); /** - * Return a being at specific coordinates. + * Returns a being at specific coordinates. */ Being* findBeing(Uint16 x, Uint16 y, Being::Type type = Being::UNKNOWN); - //Being* findBeingByPixel(Uint16 x, Uint16 y); - /** - * Return a being nearest to specific coordinates. - * - * \param maxdist maximal distance. If minimal distance is larger, - * no being is returned - */ + /** + * Returns a being nearest to specific coordinates. + * + * @param x X coordinate. + * @param y Y coordinate. + * @param maxdist Maximal distance. If minimal distance is larger, + * no being is returned. + * @param type The type of being to look for. + */ Being* findNearestLivingBeing(Uint16 x, Uint16 y, int maxdist, Being::Type type = Being::UNKNOWN); - /** - * Finds a being by name and (optionally) by type. - */ + /** + * Finds a being by name and (optionally) by type. + */ Being* findBeingByName(std::string name, Being::Type type = Being::UNKNOWN); - /** - * Return a being nearest to another being. - * - * \param maxdist maximal distance. If minimal distance is larger, - * no being is returned - */ + /** + * Returns a being nearest to another being. + * + * \param maxdist maximal distance. If minimal distance is larger, + * no being is returned + */ Being* findNearestLivingBeing(Being *aroundBeing, int maxdist, Being::Type type = Being::UNKNOWN); diff --git a/src/engine.cpp b/src/engine.cpp index d1912b0d..42054ffb 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -96,6 +96,17 @@ void Engine::changeMap(const std::string &mapPath) if (newMap->hasProperty("minimap")) { mapImage = resman->getImage(newMap->getProperty("minimap")); + + // Set the title for the Minimap + if (newMap->hasProperty("mapname")) + { + minimap->setCaption(newMap->getProperty("mapname")); + } + else + { + minimap->setCaption("Unknown"); + logger->log("WARNING: Map file '%s' defines a minimap image but does not define a 'mapname' property", map_path.c_str()); + } } minimap->setMapImage(mapImage); beingManager->setMap(newMap); @@ -120,6 +131,7 @@ void Engine::changeMap(const std::string &mapPath) } mCurrentMap = newMap; + mMapName = mapPath; // Send "map loaded" MessageOut outMsg(mNetwork); diff --git a/src/engine.h b/src/engine.h index ed76a595..52f1e63a 100644 --- a/src/engine.h +++ b/src/engine.h @@ -18,13 +18,14 @@ * 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: engine.h 4000 2008-03-23 11:47:52Z b_lindeijer $ + * $Id$ */ #ifndef _ENGINE_H #define _ENGINE_H #include <iosfwd> +#include <string> class Map; class Network; @@ -51,6 +52,9 @@ class Engine */ Map *getCurrentMap() { return mCurrentMap; } + const std::string &getCurrentMapName() { return mMapName; } + + /** * Sets the currently active map. */ @@ -64,6 +68,7 @@ class Engine private: Map *mCurrentMap; Network *mNetwork; + std::string mMapName; }; extern Engine *engine; diff --git a/src/equipment.cpp b/src/equipment.cpp index 08e6fad1..491aff0c 100644 --- a/src/equipment.cpp +++ b/src/equipment.cpp @@ -41,12 +41,10 @@ Equipment::removeEquipment(Item *item) if (i != mEquipment + EQUIPMENT_SIZE) { *i = 0; } - item->setEquipped(false); } void Equipment::removeEquipment(int index) { - mEquipment[index]->setEquipped(false); mEquipment[index] = 0; } diff --git a/src/equipment.h b/src/equipment.h index fd7a9c1e..e497a156 100644 --- a/src/equipment.h +++ b/src/equipment.h @@ -24,6 +24,8 @@ #ifndef _TMW_EQUIPMENT_H_ #define _TMW_EQUIPMENT_H_ +#include <guichan/actionlistener.hpp> + class Item; #define EQUIPMENT_SIZE 10 diff --git a/src/flooritemmanager.cpp b/src/flooritemmanager.cpp index f6ad3442..fe64779b 100644 --- a/src/flooritemmanager.cpp +++ b/src/flooritemmanager.cpp @@ -21,8 +21,6 @@ * $Id: flooritemmanager.cpp 3754 2007-11-20 15:19:50Z b_lindeijer $ */ -#include <algorithm> - #include "flooritemmanager.h" #include "floor_item.h" @@ -50,7 +48,7 @@ void FloorItemManager::destroy(FloorItem *item) void FloorItemManager::clear() { - for_each(mFloorItems.begin(), mFloorItems.end(), make_dtor(mFloorItems)); + delete_all(mFloorItems); mFloorItems.clear(); } diff --git a/src/game.cpp b/src/game.cpp index 0562b75e..5e7ee1e8 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -68,6 +68,7 @@ #include "gui/status.h" #include "gui/trade.h" #include "gui/viewport.h" + #include "net/protocol.h" #include "net/beinghandler.h" #include "net/buysellhandler.h" @@ -132,19 +133,19 @@ Particle* particleEngine = NULL; const int MAX_TIME = 10000; /** - * Listener used for exitting handling. + * Listener used for exiting handling. */ namespace { struct ExitListener : public gcn::ActionListener { - void action(const gcn::ActionEvent &event) - { - if (event.getId() == "yes" || event.getId() == "ok") { - done = true; + void action(const gcn::ActionEvent &event) + { + if (event.getId() == "yes" || event.getId() == "ok") { + done = true; + } + exitConfirm = NULL; + disconnectedDialog = NULL; } - exitConfirm = NULL; - disconnectedDialog = NULL; - } } exitListener; } @@ -171,11 +172,13 @@ Uint32 nextSecond(Uint32 interval, void *param) int get_elapsed_time(int start_time) { - if (start_time <= tick_time) { - return (tick_time - start_time) * 10; + if (start_time <= tick_time) + { + return (tick_time - start_time) * 10; } - else { - return (tick_time + (MAX_TIME - start_time)) * 10; + else + { + return (tick_time + (MAX_TIME - start_time)) * 10; } } @@ -218,8 +221,9 @@ void createGuiWindows(Network *network) chatWindow->setVisible((bool) config.getValue( chatWindow->getWindowName() + "Visible", true)); miniStatusWindow->setVisible((bool) config.getValue( - miniStatusWindow->getWindowName() + "Visible", - true)); + miniStatusWindow->getWindowName() + "Visible", true)); + buyDialog->setVisible(false); + sellDialog->setVisible(false); menuWindow->setVisible((bool) config.getValue( menuWindow->getWindowName() + "Visible", true)); itemShortcutWindow->setVisible((bool) config.getValue( @@ -227,7 +231,7 @@ void createGuiWindows(Network *network) if (config.getValue("logToChat", 0)) { - logger->setChatWindow(chatWindow); + logger->setChatWindow(chatWindow); } } @@ -369,15 +373,15 @@ bool saveScreenshot(SDL_Surface *screenshot) if (ImageWriter::writePNG(screenshot, filename.str())) { - std::stringstream chatlogentry; - chatlogentry << "Screenshot saved to " << filename.str().c_str(); - chatWindow->chatLog(chatlogentry.str(), BY_SERVER); - return true; + std::stringstream chatlogentry; + chatlogentry << "Screenshot saved to " << filename.str().c_str(); + chatWindow->chatLog(chatlogentry.str(), BY_SERVER); + return true; } else { - chatWindow->chatLog("Saving screenshot failed!", BY_SERVER); - return false; + chatWindow->chatLog("Saving screenshot failed!", BY_SERVER); + return false; } } @@ -420,18 +424,18 @@ void Game::logic() if (!mMinFrameTime || get_elapsed_time(mDrawTime / 10) > mMinFrameTime) { - frame++; - gui->draw(); - graphics->updateScreen(); - mDrawTime += mMinFrameTime; - - // Make sure to wrap mDrawTime, since tick_time will wrap. - if (mDrawTime > MAX_TIME * 10) - mDrawTime -= MAX_TIME * 10; + frame++; + gui->draw(); + graphics->updateScreen(); + mDrawTime += mMinFrameTime; + + // Make sure to wrap mDrawTime, since tick_time will wrap. + if (mDrawTime > MAX_TIME * 10) + mDrawTime -= MAX_TIME * 10; } else { - SDL_Delay(10); + SDL_Delay(10); } } else @@ -448,12 +452,11 @@ void Game::logic() { if (!disconnectedDialog) { - disconnectedDialog = new - OkDialog("Network Error", - "The connection to the server was lost, the " - "program will now quit"); - disconnectedDialog->addActionListener(&exitListener); - disconnectedDialog->requestMoveToTop(); + disconnectedDialog = new + OkDialog("Network Error", + "The connection to the server was lost, the program will now quit"); + disconnectedDialog->addActionListener(&exitListener); + disconnectedDialog->requestMoveToTop(); } } } @@ -463,273 +466,268 @@ void Game::handleInput() { if (joystick != NULL) { - joystick->update(); + joystick->update(); } // Events SDL_Event event; while (SDL_PollEvent(&event)) { - bool used = false; - - // Keyboard events (for discontinuous keys) - if (event.type == SDL_KEYDOWN) - { - gcn::Window *requestedWindow = NULL; + bool used = false; - if (setupWindow->isVisible() && - keyboard.getNewKeyIndex() > keyboard.KEY_NO_VALUE) + // Keyboard events (for discontinuous keys) + if (event.type == SDL_KEYDOWN) { - keyboard.setNewKey((int) event.key.keysym.sym); - keyboard.callbackNewKey(); - keyboard.setNewKeyIndex(keyboard.KEY_NO_VALUE); - return; - } - // Keys pressed together with Alt/Meta - // Emotions and some internal gui windows - #ifndef __APPLE__ - if (event.key.keysym.mod & KMOD_ALT) - #else - if (event.key.keysym.mod & KMOD_LMETA) - #endif - { - switch (event.key.keysym.sym) - { - case SDLK_p: - // Screenshot (picture, hence the p) + gcn::Window *requestedWindow = NULL; + + if (setupWindow->isVisible() && + keyboard.getNewKeyIndex() > keyboard.KEY_NO_VALUE) { - SDL_Surface *screenshot = - graphics->getScreenshot(); - if (!saveScreenshot(screenshot)) - { - logger-> - log("Error: could not save Screenshot."); - } - SDL_FreeSurface(screenshot); + keyboard.setNewKey((int) event.key.keysym.sym); + keyboard.callbackNewKey(); + keyboard.setNewKeyIndex(keyboard.KEY_NO_VALUE); + return; } - used = true; - break; - - default: - break; - - case SDLK_f: - // Find path to mouse (debug purpose) - viewport->toggleDebugPath(); - used = true; - break; - - case SDLK_t: - // Toggle accepting of incoming trade requests + // Keys pressed together with Alt/Meta + // Emotions and some internal gui windows + #ifndef __APPLE__ + if (event.key.keysym.mod & KMOD_ALT) + #else + if (event.key.keysym.mod & KMOD_LMETA) + #endif { - unsigned int deflt = player_relations.getDefault(); - if (deflt & PlayerRelation::TRADE) { - chatWindow-> - chatLog("Ignoring incoming trade requests", - BY_SERVER); - deflt &= ~PlayerRelation::TRADE; - } else { - chatWindow->chatLog("Accepting incoming trade " - "requests", BY_SERVER); - deflt |= PlayerRelation::TRADE; - } - - player_relations.setDefault(deflt); + switch (event.key.keysym.sym) + { + case SDLK_p: + // Screenshot (picture, hence the p) + { + SDL_Surface *screenshot = graphics->getScreenshot(); + if (!saveScreenshot(screenshot)) + { + logger->log("Error: could not save Screenshot."); + } + SDL_FreeSurface(screenshot); + } + used = true; + break; + + default: + break; + + case SDLK_f: + // Find path to mouse (debug purpose) + viewport->toggleDebugPath(); + used = true; + break; + + case SDLK_t: + // Toggle accepting of incoming trade requests + { + unsigned int deflt = player_relations.getDefault(); + if (deflt & PlayerRelation::TRADE) { + chatWindow->chatLog("Ignoring incoming trade requests", BY_SERVER); + deflt &= ~PlayerRelation::TRADE; + } else { + chatWindow->chatLog("Accepting incoming trade requests", BY_SERVER); + deflt |= PlayerRelation::TRADE; + } + + player_relations.setDefault(deflt); + } + used = true; + break; } - used = true; - break; - } - - // Emotions - Uint8 emotion; - switch (event.key.keysym.sym) - { - case SDLK_1: emotion = 1; break; - case SDLK_2: emotion = 2; break; - case SDLK_3: emotion = 3; break; - case SDLK_4: emotion = 4; break; - case SDLK_5: emotion = 5; break; - case SDLK_6: emotion = 6; break; - case SDLK_7: emotion = 7; break; - case SDLK_8: emotion = 8; break; - case SDLK_9: emotion = 9; break; - case SDLK_0: emotion = 10; break; - case SDLK_MINUS: emotion = 11; break; - case SDLK_EQUALS: emotion = 12; break; - default: emotion = 0; break; } - if (emotion) - { - player_node->emote(emotion); - used = true; - } - } - switch (event.key.keysym.sym) + // Smilie + if (keyboard.isKeyActive(keyboard.KEY_SMILIE)) { - case SDLK_PAGEUP: - if (chatWindow->isVisible()) + // Emotions + Uint8 emotion; + switch (event.key.keysym.sym) { - chatWindow->scroll(-DEFAULT_CHAT_WINDOW_SCROLL); - used = true; + case SDLK_1: emotion = 1; break; + case SDLK_2: emotion = 2; break; + case SDLK_3: emotion = 3; break; + case SDLK_4: emotion = 4; break; + case SDLK_5: emotion = 5; break; + case SDLK_6: emotion = 6; break; + case SDLK_7: emotion = 7; break; + case SDLK_8: emotion = 8; break; + case SDLK_9: emotion = 9; break; + case SDLK_0: emotion = 10; break; + case SDLK_MINUS: emotion = 11; break; + case SDLK_EQUALS: emotion = 12; break; + default: emotion = 0; break; } - break; - case SDLK_PAGEDOWN: - if (chatWindow->isVisible()) + if (emotion) { - chatWindow->scroll(DEFAULT_CHAT_WINDOW_SCROLL); - used = true; + player_node->emote(emotion); + used = true; } - break; + } + switch (event.key.keysym.sym) + { + case SDLK_PAGEUP: + if (chatWindow->isVisible()) + { + chatWindow->scroll(-DEFAULT_CHAT_WINDOW_SCROLL); + used = true; + } + break; - case SDLK_F1: - // In-game Help - if (helpWindow->isVisible()) - { - helpWindow->setVisible(false); - } - else - { - helpWindow->loadHelp("index"); - helpWindow->requestMoveToTop(); - } - used = true; - break; + case SDLK_PAGEDOWN: + if (chatWindow->isVisible()) + { + chatWindow->scroll(DEFAULT_CHAT_WINDOW_SCROLL); + used = true; + } + break; - case SDLK_F2: requestedWindow = statusWindow; break; - case SDLK_F3: requestedWindow = inventoryWindow; break; - case SDLK_F4: requestedWindow = equipmentWindow; break; - case SDLK_F5: requestedWindow = skillDialog; break; - case SDLK_F6: requestedWindow = minimap; break; - case SDLK_F7: requestedWindow = chatWindow; break; - case SDLK_F8: requestedWindow = itemShortcutWindow; break; - case SDLK_F9: requestedWindow = setupWindow; break; - case SDLK_F10: requestedWindow = debugWindow; break; - //case SDLK_F11: requestedWindow = newSkillWindow; break; - - case SDLK_RETURN: - // Input chat window - if (chatWindow->isInputFocused() || - deathNotice != NULL || - weightNotice != NULL) - { - break; - } + case SDLK_F1: + // In-game Help + if (helpWindow->isVisible()) + { + helpWindow->setVisible(false); + } + else + { + helpWindow->loadHelp("index"); + helpWindow->requestMoveToTop(); + } + used = true; + break; + + case SDLK_F2: requestedWindow = statusWindow; break; + case SDLK_F3: requestedWindow = inventoryWindow; break; + case SDLK_F4: requestedWindow = equipmentWindow; break; + case SDLK_F5: requestedWindow = skillDialog; break; + case SDLK_F6: requestedWindow = minimap; break; + case SDLK_F7: requestedWindow = chatWindow; break; + case SDLK_F8: requestedWindow = itemShortcutWindow; break; + case SDLK_F9: requestedWindow = setupWindow; break; + case SDLK_F10: requestedWindow = debugWindow; break; + //case SDLK_F11: requestedWindow = newSkillWindow; break; + + case SDLK_RETURN: + // Input chat window + if (chatWindow->isInputFocused() || + deathNotice != NULL || + weightNotice != NULL) + { + break; + } - // Quit by pressing Enter if the exit confirm is there - if (exitConfirm) - { - done = true; - } - // Close the Browser if opened - else if (helpWindow->isVisible()) - { - helpWindow->setVisible(false); - } - // Close the config window, cancelling changes if opened - else if (setupWindow->isVisible()) - { - setupWindow->action(gcn::ActionEvent(NULL, "cancel")); - } - // Else, open the chat edit box - else - { - chatWindow->requestChatFocus(); - used = true; - } - break; - // Quitting confirmation dialog - case SDLK_ESCAPE: - if (!exitConfirm) { - exitConfirm = new ConfirmDialog( - "Quit", "Are you sure you want to quit?"); - exitConfirm->addActionListener(&exitListener); - exitConfirm->requestMoveToTop(); - } - else - { - exitConfirm->action(gcn::ActionEvent(NULL, "no")); - } - break; + // Quit by pressing Enter if the exit confirm is there + if (exitConfirm) + { + done = true; + } + // Close the Browser if opened + else if (helpWindow->isVisible()) + { + helpWindow->setVisible(false); + } + // Close the config window, cancelling changes if opened + else if (setupWindow->isVisible()) + { + setupWindow->action(gcn::ActionEvent(NULL, "cancel")); + } + // Else, open the chat edit box + else + { + chatWindow->requestChatFocus(); + used = true; + } + break; + // Quitting confirmation dialog + case SDLK_ESCAPE: + if (!exitConfirm) { + exitConfirm = new ConfirmDialog( + "Quit", "Are you sure you want to quit?"); + exitConfirm->addActionListener(&exitListener); + exitConfirm->requestMoveToTop(); + } + else + { + exitConfirm->action(gcn::ActionEvent(NULL, "no")); + } + break; - default: - break; + default: + break; } if (keyboard.isEnabled() && !chatWindow->isInputFocused()) { - const int tKey = keyboard.getKeyIndex(event.key.keysym.sym); - // Checks if any item shortcut is pressed. - for (int i = KeyboardConfig::KEY_SHORTCUT_0; - i <= KeyboardConfig::KEY_SHORTCUT_9; - i++) - { - if (tKey == i && !used) { - itemShortcut->useItem( - i - KeyboardConfig::KEY_SHORTCUT_0); - break; + const int tKey = keyboard.getKeyIndex(event.key.keysym.sym); + // Checks if any item shortcut is pressed. + for (int i = KeyboardConfig::KEY_SHORTCUT_0; + i <= KeyboardConfig::KEY_SHORTCUT_9; + i++) + { + if (tKey == i && !used) { + itemShortcut->useItem(i - KeyboardConfig::KEY_SHORTCUT_0); + break; } } switch (tKey) { case KeyboardConfig::KEY_PICKUP: { - FloorItem *item = - floorItemManager->findByCoordinates( - player_node->mX, player_node->mY); + FloorItem *item = floorItemManager->findByCoordinates( + player_node->mX, player_node->mY); // If none below the player, try the tile in front // of the player if (!item) { - Uint16 x = player_node->mX; - Uint16 y = player_node->mY; - if (player_node->getDirection() & Being::UP) - y--; - if (player_node->getDirection() & Being::DOWN) - y++; - if (player_node->getDirection() & Being::LEFT) - x--; - if (player_node->getDirection() & Being::RIGHT) - x++; - - item = floorItemManager-> - findByCoordinates(x, y); + Uint16 x = player_node->mX; + Uint16 y = player_node->mY; + switch (player_node->getSpriteDirection()) + { + case DIRECTION_UP : --y; break; + case DIRECTION_DOWN : ++y; break; + case DIRECTION_LEFT : --x; break; + case DIRECTION_RIGHT: ++x; break; + default: break; + } + item = floorItemManager->findByCoordinates(x, y); } if (item) - player_node->pickUp(item); + player_node->pickUp(item); used = true; } break; case KeyboardConfig::KEY_SIT: - // Player sit action - player_node->toggleSit(); - used = true; - break; + // Player sit action + player_node->toggleSit(); + used = true; + break; case KeyboardConfig::KEY_HIDE_WINDOWS: - // Hide certain windows - if (!chatWindow->isInputFocused()) - { - statusWindow->setVisible(false); - inventoryWindow->setVisible(false); - skillDialog->setVisible(false); - setupWindow->setVisible(false); - equipmentWindow->setVisible(false); - helpWindow->setVisible(false); - debugWindow->setVisible(false); + // Hide certain windows + if (!chatWindow->isInputFocused()) + { + statusWindow->setVisible(false); + inventoryWindow->setVisible(false); + skillDialog->setVisible(false); + setupWindow->setVisible(false); + equipmentWindow->setVisible(false); + helpWindow->setVisible(false); + debugWindow->setVisible(false); + } + break; } - break; - } } if (requestedWindow) { - requestedWindow->setVisible(!requestedWindow->isVisible()); - if (requestedWindow->isVisible()) - { - requestedWindow->requestMoveToTop(); - } - used = true; + requestedWindow->setVisible(!requestedWindow->isVisible()); + if (requestedWindow->isVisible()) + { + requestedWindow->requestMoveToTop(); + } + used = true; } } @@ -745,12 +743,12 @@ void Game::handleInput() { try { - guiInput->pushInput(event); + guiInput->pushInput(event); } catch (gcn::Exception e) { - const char* err = e.getMessage().c_str(); - logger->log("Warning: guichan input exception: %s", err); + const char* err = e.getMessage().c_str(); + logger->log("Warning: guichan input exception: %s", err); } } @@ -762,112 +760,106 @@ void Game::handleInput() } // Moving player around if (player_node->mAction != Being::DEAD && - current_npc == 0 && - !chatWindow->isInputFocused()) + current_npc == 0 && + !chatWindow->isInputFocused()) { - // Get the state of the keyboard keys - keyboard.refreshActiveKeys(); + // Get the state of the keyboard keys + keyboard.refreshActiveKeys(); - const Uint16 x = player_node->mX; - const Uint16 y = player_node->mY; - unsigned char direction = 0; + const Uint16 x = player_node->mX; + const Uint16 y = player_node->mY; + unsigned char direction = 0; - // Translate pressed keys to movement and direction - if (keyboard.isKeyActive(keyboard.KEY_MOVE_UP) || - (joystick && joystick->isUp())) - { - direction |= Being::UP; - } - else if (keyboard.isKeyActive(keyboard.KEY_MOVE_DOWN) || - (joystick && joystick->isDown())) - { - direction |= Being::DOWN; - } + // Translate pressed keys to movement and direction + if (keyboard.isKeyActive(keyboard.KEY_MOVE_UP) || + (joystick && joystick->isUp())) + { + direction |= Being::UP; + } + else if (keyboard.isKeyActive(keyboard.KEY_MOVE_DOWN) || + (joystick && joystick->isDown())) + { + direction |= Being::DOWN; + } - if (keyboard.isKeyActive(keyboard.KEY_MOVE_LEFT) || - (joystick && joystick->isLeft())) - { - direction |= Being::LEFT; - } - else if (keyboard.isKeyActive(keyboard.KEY_MOVE_RIGHT) || - (joystick && joystick->isRight())) - { - direction |= Being::RIGHT; - } + if (keyboard.isKeyActive(keyboard.KEY_MOVE_LEFT) || + (joystick && joystick->isLeft())) + { + direction |= Being::LEFT; + } + else if (keyboard.isKeyActive(keyboard.KEY_MOVE_RIGHT) || + (joystick && joystick->isRight())) + { + direction |= Being::RIGHT; + } - player_node->setWalkingDir(direction); + player_node->setWalkingDir(direction); - // Attacking monsters - if (keyboard.isKeyActive(keyboard.KEY_ATTACK) || - (joystick && joystick->buttonPressed(0))) - { - Being *target = NULL; - bool newTarget = keyboard.isKeyActive(keyboard.KEY_TARGET); - // A set target has highest priority - if (newTarget || !player_node->getTarget()) + // Attacking monsters + if (keyboard.isKeyActive(keyboard.KEY_ATTACK) || + (joystick && joystick->buttonPressed(0))) { - Uint16 targetX = x, targetY = y; - - if (player_node->getDirection() & Being::UP) - targetY--; - if (player_node->getDirection() & Being::DOWN) - targetY++; - if (player_node->getDirection() & Being::LEFT) - targetX--; - if (player_node->getDirection() & Being::RIGHT) - targetX++; - - // Attack priorioty is: Monster, Player, auto target - target = beingManager->findBeing( - targetX, targetY, Being::MONSTER); - if (!target) - target = beingManager->findBeing( - targetX, targetY, Being::PLAYER); - } + Being *target = beingManager->findNearestLivingBeing(x, y, 20, Being::MONSTER); - player_node->attack(target, newTarget); - } + bool newTarget = keyboard.isKeyActive(keyboard.KEY_TARGET); + // A set target has highest priority + if (newTarget || !player_node->getTarget()) + { + Uint16 targetX = x, targetY = y; - // Target the nearest player if 'q' is pressed - if ( keyboard.isKeyActive(keyboard.KEY_TARGET_PLAYER) ) - //if (keys[SDLK_q]) - { - Being *target = - beingManager-> - findNearestLivingBeing(player_node, 20, Being::PLAYER); + switch (player_node->getSpriteDirection()) + { + case DIRECTION_UP : --targetY; break; + case DIRECTION_DOWN : ++targetY; break; + case DIRECTION_LEFT : --targetX; break; + case DIRECTION_RIGHT: ++targetX; break; + default: break; + } - if (target) - { - player_node->setTarget(target); - } - } + // Attack priorioty is: Monster, Player, auto target + target = beingManager->findBeing(targetX, targetY, Being::MONSTER); + if (!target) + target = beingManager->findBeing(targetX, targetY, Being::PLAYER); + } - // Target the nearest monster if 'a' pressed - if ( keyboard.isKeyActive(keyboard.KEY_TARGET_CLOSEST) ) - //if (keys[SDLK_a]) - { - Being *target = - beingManager->findNearestLivingBeing(x, y, 20, Being::MONSTER); + player_node->attack(target, newTarget); + } - if (target) + // Target the nearest player if 'q' is pressed + if ( keyboard.isKeyActive(keyboard.KEY_TARGET_PLAYER) ) { - player_node->setTarget(target); + Being *target = beingManager->findNearestLivingBeing(player_node, 20, Being::PLAYER); + + if (target) + { + player_node->setTarget(target); + } } - } - if (joystick) - { - if (joystick->buttonPressed(1)) + // Target the nearest monster if 'a' pressed + if ( keyboard.isKeyActive(keyboard.KEY_TARGET_CLOSEST) ) { - FloorItem *item = floorItemManager->findByCoordinates(x, y); + Being *target = beingManager->findNearestLivingBeing(x, y, 20, Being::MONSTER); - if (item) - player_node->pickUp(item); + if (target) + { + player_node->setTarget(target); + } } - else if (joystick->buttonPressed(2)) + + if (joystick) { - player_node->toggleSit(); + if (joystick->buttonPressed(1)) + { + FloorItem *item = floorItemManager->findByCoordinates(x, y); + + if (item) + player_node->pickUp(item); + } + else if (joystick->buttonPressed(2)) + { + player_node->toggleSit(); + } } } - } } diff --git a/src/gui/setup.cpp b/src/gui/setup.cpp index 9c562b68..8ec3bfd8 100644 --- a/src/gui/setup.cpp +++ b/src/gui/setup.cpp @@ -102,7 +102,7 @@ Setup::Setup(): Setup::~Setup() { - for_each(mTabs.begin(), mTabs.end(), make_dtor(mTabs)); + delete_all(mTabs); } void Setup::action(const gcn::ActionEvent &event) diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index 0e85138c..dd56ee2f 100644 --- a/src/gui/setup_video.cpp +++ b/src/gui/setup_video.cpp @@ -280,15 +280,17 @@ void Setup_Video::apply() bool fullscreen = mFsCheckBox->isSelected(); if (fullscreen != (config.getValue("screen", 0) == 1)) { + + /* Commented out the openGL test because + * the fullscreen mode change works fine, but + * will need to test it on windows so not + * deleting entirely until then --kraant*/ - /*Commented out the openGL test because - * the fullscreen mode change works fine, but - * will need to test it on windows so not - * deleting entirely until then --kraant*/ - +#ifdef WIN32 // checks for opengl usage - /*if (!(config.getValue("opengl", 0) == 1)) - {*/ + if (!(config.getValue("opengl", 0) == 1)) + { +#endif if (!graphics->setFullscreen(fullscreen)) { fullscreen = !fullscreen; @@ -302,10 +304,12 @@ void Setup_Video::apply() logger->error(error.str()); } } - /*} else { +#ifdef WIN32 + } else { new OkDialog("Switching to full screen", "Restart needed for changes to take effect."); - }*/ + } +#endif config.setValue("screen", fullscreen ? 1 : 0); } diff --git a/src/gui/shop.cpp b/src/gui/shop.cpp index 015e70d5..e7619547 100644 --- a/src/gui/shop.cpp +++ b/src/gui/shop.cpp @@ -25,8 +25,6 @@ #include "../utils/dtor.h" -#include <algorithm> - ShopItems::~ShopItems() { clear(); @@ -61,7 +59,7 @@ ShopItem* ShopItems::at(int i) const void ShopItems::clear() { - std::for_each(mShopItems.begin(), mShopItems.end(), make_dtor(mShopItems)); + delete_all(mShopItems); mShopItems.clear(); } diff --git a/src/gui/skill.cpp b/src/gui/skill.cpp index e70fb3ef..8cf1e06d 100644 --- a/src/gui/skill.cpp +++ b/src/gui/skill.cpp @@ -21,8 +21,6 @@ * $Id: skill.cpp 3754 2007-11-20 15:19:50Z b_lindeijer $ */ -#include <algorithm> - #include <guichan/widgets/label.hpp> #include "skill.h" @@ -186,6 +184,6 @@ void SkillDialog::setSkill(int id, int lvl, int mp) void SkillDialog::cleanList() { - for_each(mSkillList.begin(), mSkillList.end(), make_dtor(mSkillList)); + delete_all(mSkillList); mSkillList.clear(); } diff --git a/src/gui/textbox.cpp b/src/gui/textbox.cpp index 743d88b6..4976549f 100644 --- a/src/gui/textbox.cpp +++ b/src/gui/textbox.cpp @@ -60,6 +60,8 @@ void TextBox::setTextWrapped(const std::string &text) text.substr(lastNewlinePos, newlinePos - lastNewlinePos); std::string::size_type spacePos, lastSpacePos = 0; int xpos = 0; + mMinWidth = getWidth(); + bool longWord = false; do { @@ -75,22 +77,28 @@ void TextBox::setTextWrapped(const std::string &text) int width = getFont()->getWidth(word); - if (xpos != 0 && xpos + width < getWidth()) + if (xpos != 0 && xpos + width + getFont()->getWidth(" ") < getWidth()) { xpos += width + getFont()->getWidth(" "); wrappedStream << " " << word; } else if (lastSpacePos == 0) { + if (xpos > mMinWidth) + { + longWord = true; + mMinWidth = xpos; + } xpos += width; wrappedStream << word; } else { + if ((xpos < mMinWidth) && !longWord && spacePos != line.size()) + mMinWidth = xpos; xpos = width; wrappedStream << "\n" << word; } - lastSpacePos = spacePos + 1; } while (spacePos != line.size()); diff --git a/src/gui/textbox.h b/src/gui/textbox.h index 7df30fd9..09819360 100644 --- a/src/gui/textbox.h +++ b/src/gui/textbox.h @@ -44,6 +44,19 @@ class TextBox : public gcn::TextBox { * Sets the text after wrapping it to the current width of the widget. */ void setTextWrapped(const std::string &text); + + /** + * Get the minimum text width for the text box. + */ + int getMinWidth() { return mMinWidth; } + + /** + * Set the minimum text width for the text box. + */ + void setMinWidth(int width) { mMinWidth = width; } + + private: + int mMinWidth; }; #endif diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index 22e361c5..b052e4b6 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -115,7 +115,7 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, mBrowserBox = new BrowserBox(); mScrollArea = new ScrollArea(mBrowserBox); mLabel = new gcn::Label("Connecting..."); - mProgressBar = new ProgressBar(0.0, w - 10, 20, 37, 70, 200); + mProgressBar = new ProgressBar(0.0, w - 10, 20, 168, 116, 31); mCancelButton = new Button("Cancel", "cancel", this); mPlayButton = new Button("Play", "play", this); diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index 568c1ea3..95e0d2b6 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -33,6 +33,7 @@ #include "../configuration.h" #include "../flooritemmanager.h" #include "../graphics.h" +#include "../keyboardconfig.h" #include "../localplayer.h" #include "../map.h" #include "../monster.h" @@ -49,6 +50,8 @@ #include <cassert> +extern volatile int tick_time; + Viewport::Viewport(): mMap(0), mPixelViewX(0.0f), @@ -112,7 +115,8 @@ Viewport::loadTargetCursor(std::string filename, int width, int height, mOutRangeImages[size] = currentImageSet; mTargetCursorOutRange[size] = currentCursor; } - else { + else + { mInRangeImages[size] = currentImageSet; mTargetCursorInRange[size] = currentCursor; } @@ -223,52 +227,46 @@ Viewport::draw(gcn::Graphics *gcnGraphics) // Draw tiles and sprites if (mMap) { - mMap->draw(graphics, (int) mPixelViewX, (int) mPixelViewY, 0); - drawTargetCursor(graphics); - mMap->draw(graphics, (int) mPixelViewX, (int) mPixelViewY, 1); - mMap->draw(graphics, (int) mPixelViewX, (int) mPixelViewY, 2); - mMap->drawOverlay(graphics, mPixelViewX, mPixelViewY, - (int) config.getValue("OverlayDetail", 2)); - } + mMap->draw(graphics, (int) mPixelViewX, (int) mPixelViewY); + drawTargetCursor(graphics); // TODO: Draw the cursor with the sprite - // Find a path from the player to the mouse, and draw it. This is for debug - // purposes. - if (mShowDebugPath && mMap) - { - // Get the current mouse position - int mouseX, mouseY; - SDL_GetMouseState(&mouseX, &mouseY); - int mouseTileX = mouseX / 32 + mTileViewX; - int mouseTileY = mouseY / 32 + mTileViewY; + // Find a path from the player to the mouse, and draw it. This is for debug + // purposes. + if (mShowDebugPath) + { + // Get the current mouse position + int mouseX, mouseY; + SDL_GetMouseState(&mouseX, &mouseY); - Path debugPath = mMap->findPath( - player_node->mX, player_node->mY, - mouseTileX, mouseTileY); + int mouseTileX = mouseX / 32 + mTileViewX; + int mouseTileY = mouseY / 32 + mTileViewY; - graphics->setColor(gcn::Color(255, 0, 0)); - for (PathIterator i = debugPath.begin(); i != debugPath.end(); i++) - { - int squareX = i->x * 32 - (int) mPixelViewX + 12; - int squareY = i->y * 32 - (int) mPixelViewY + 12; + Path debugPath = mMap->findPath(player_node->mX, player_node->mY, mouseTileX, mouseTileY); - graphics->fillRectangle(gcn::Rectangle(squareX, squareY, 8, 8)); - graphics->drawText( - toString(mMap->getMetaTile(i->x, i->y)->Gcost), - squareX + 4, squareY + 12, gcn::Graphics::CENTER); + graphics->setColor(gcn::Color(255, 0, 0)); + for (PathIterator i = debugPath.begin(); i != debugPath.end(); i++) + { + int squareX = i->x * 32 - (int) mPixelViewX + 12; + int squareY = i->y * 32 - (int) mPixelViewY + 12; + + graphics->fillRectangle(gcn::Rectangle(squareX, squareY, 8, 8)); + graphics->drawText(toString(mMap->getMetaTile(i->x, i->y)->Gcost), squareX + 4, squareY + 12, gcn::Graphics::CENTER); + } } } - // Draw text + // Draw names if (textManager) { textManager->draw(graphics, mPixelViewX, mPixelViewY); } - // Draw player nickname, speech, and emotion sprite as needed + // Draw player speech, and emotion sprite as needed Beings &beings = beingManager->getAll(); for (BeingIterator i = beings.begin(); i != beings.end(); i++) { + (*i)->drawSpeech(graphics, -(int) mPixelViewX, -(int) mPixelViewY); (*i)->drawEmotion(graphics, -(int) mPixelViewX, -(int) mPixelViewY); } @@ -276,8 +274,7 @@ Viewport::draw(gcn::Graphics *gcnGraphics) WindowContainer::draw(gcnGraphics); } -void -Viewport::logic() +void Viewport::logic() { WindowContainer::logic(); @@ -295,15 +292,14 @@ Viewport::logic() mWalkTime = player_node->mWalkTime; } - for (int i = 0; i < 3; i++) + for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++) { mTargetCursorInRange[i]->update(10); mTargetCursorOutRange[i]->update(10); } } -void -Viewport::drawTargetCursor(Graphics *graphics) +void Viewport::drawTargetCursor(Graphics *graphics) { // Draw target marker if needed Being *target = player_node->getTarget(); @@ -323,7 +319,8 @@ Viewport::drawTargetCursor(Graphics *graphics) { targetCursor = mTargetCursorOutRange[cursorSize]->getCurrentImage(); } - else { + else + { targetCursor = mTargetCursorInRange[cursorSize]->getCurrentImage(); } @@ -332,11 +329,10 @@ Viewport::drawTargetCursor(Graphics *graphics) int posY = target->getPixelY() + 16 - targetCursor->getHeight() / 2 - (int) mPixelViewY; graphics->drawImage(targetCursor, posX, posY); - } + } } -void -Viewport::mousePressed(gcn::MouseEvent &event) +void Viewport::mousePressed(gcn::MouseEvent &event) { // Check if we are alive and kickin' if (!mMap || !player_node || player_node->mAction == Being::DEAD) @@ -348,8 +344,10 @@ Viewport::mousePressed(gcn::MouseEvent &event) mPlayerFollowMouse = false; - int tilex = event.getX() / 32 + mTileViewX; - int tiley = event.getY() / 32 + mTileViewY; + const int tilex = event.getX() / 32 + mTileViewX; + const int tiley = event.getY() / 32 + mTileViewY; + const int x = (int)((float) event.getX() + mPixelViewX); + const int y = (int)((float) event.getY() + mPixelViewY); // Right click might open a popup if (event.getButton() == gcn::MouseEvent::RIGHT) @@ -357,11 +355,11 @@ Viewport::mousePressed(gcn::MouseEvent &event) Being *being; FloorItem *floorItem; - if ((being = beingManager->findBeing(tilex, tiley)) && - being != player_node) + if ((being = beingManager->findBeingByPixel(x, y)) && + being != player_node) { - mPopupMenu->showPopup(event.getX(), event.getY(), being); - return; + mPopupMenu->showPopup(event.getX(), event.getY(), being); + return; } else if((floorItem = floorItemManager->findByCoordinates(tilex, tiley))) { @@ -382,12 +380,10 @@ Viewport::mousePressed(gcn::MouseEvent &event) { Being *being; FloorItem *item; - + // Interact with some being -// int x = (int)((float) event.getX() + mPixelViewX); -// int y = (int)((float) event.getY() + mPixelViewY); -// if ((being = beingManager->findBeingByPixel(x, y))) - if ((being = beingManager->findBeing(tilex, tiley))) +// if ((being = beingManager->findBeing(tilex, tiley))) + if ((being = beingManager->findBeingByPixel(x, y))) { switch (being->getType()) { @@ -400,59 +396,49 @@ Viewport::mousePressed(gcn::MouseEvent &event) if (being->mAction == Being::DEAD) break; - if (player_node->withinAttackRange(being)) + if (keyboard.isKeyActive(keyboard.KEY_TARGET) || player_node->withinAttackRange(being)) { + player_node->stopAttack(); + player_node->setGotoTarget(being); player_node->attack(being, true); } else { - Uint8 *keys = SDL_GetKeyState(NULL); - if (!(keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT])) - { - player_node->stopAttack(); - player_node->setGotoTarget(being); - } + player_node->setDestination(tilex, tiley); + player_node->stopAttack(); } break; default: break; - } + } } // Pick up some item else if ((item = floorItemManager->findByCoordinates(tilex, tiley))) { - player_node->pickUp(item); + player_node->pickUp(item); } // Just walk around else { - // XXX XXX XXX REALLY UGLY! - Uint8 *keys = SDL_GetKeyState(NULL); - if (!(keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT])) - { - player_node->setDestination(tilex, tiley); - player_node->stopAttack(); - } + player_node->stopAttack(); + player_node->setDestination(tilex, tiley); mPlayerFollowMouse = true; } } else if (event.getButton() == gcn::MouseEvent::MIDDLE) { // Find the being nearest to the clicked position - Being *target = beingManager->findNearestLivingBeing( - tilex, tiley, - 20, Being::MONSTER); - + Being *target = beingManager->findBeingByPixel(x, y); + if (target) { - player_node->setTarget(target); + player_node->setTarget(target); } } } -void -Viewport::mouseDragged(gcn::MouseEvent &event) +void Viewport::mouseDragged(gcn::MouseEvent &event) { if (!mMap || !player_node) return; @@ -465,20 +451,17 @@ Viewport::mouseDragged(gcn::MouseEvent &event) } } -void -Viewport::mouseReleased(gcn::MouseEvent &event) +void Viewport::mouseReleased(gcn::MouseEvent &event) { mPlayerFollowMouse = false; } -void -Viewport::showPopup(int x, int y, Item *item) +void Viewport::showPopup(int x, int y, Item *item) { mPopupMenu->showPopup(x, y, item); } -void -Viewport::optionChanged(const std::string &name) +void Viewport::optionChanged(const std::string &name) { mScrollLaziness = (int) config.getValue("ScrollLaziness", 32); mScrollRadius = (int) config.getValue("ScrollRadius", 32); diff --git a/src/gui/viewport.h b/src/gui/viewport.h index 44a877a6..325a7221 100644 --- a/src/gui/viewport.h +++ b/src/gui/viewport.h @@ -18,7 +18,7 @@ * 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: viewport.h 4247 2008-05-19 10:48:18Z b_lindeijer $ + * $Id$ */ #ifndef _TMW_VIEWPORT_H_ @@ -117,16 +117,16 @@ class Viewport : public WindowContainer, public gcn::MouseListener, optionChanged(const std::string &name); /** - * Returns camera x offset in tiles. + * Returns camera x offset in pixels. */ int - getCameraX() { return mTileViewX; } + getCameraX() const { return (int) mPixelViewX; } /** - * Returns camera y offset in tiles. + * Returns camera y offset in pixels. */ int - getCameraY() { return mTileViewY; } + getCameraY() const { return (int) mPixelViewY; } /** * Changes viewpoint by relative pixel coordinates. @@ -138,22 +138,13 @@ class Viewport : public WindowContainer, public gcn::MouseListener, /** * Helper function for loading target cursors */ - void - loadTargetCursor(std::string filename, int width, int height, - bool outRange, Being::TargetCursorSize size); + void loadTargetCursor(std::string filename, int width, int height, + bool outRange, Being::TargetCursorSize size); /** * Draws range based target cursor */ - void - drawTargetCursor(Graphics *graphics); - - /** - * Draws target name - */ - void - drawTargetName(Graphics *graphics); - + void drawTargetCursor(Graphics *graphics); Map *mMap; /**< The current map. */ diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp index b33a55cf..9bf7452d 100644 --- a/src/gui/widgets/dropdown.cpp +++ b/src/gui/widgets/dropdown.cpp @@ -165,5 +165,5 @@ void DropDown::drawButton(gcn::Graphics *graphics) int height = mDroppedDown ? mFoldedUpHeight : getHeight(); static_cast<Graphics*>(graphics)-> - drawImage(buttons[mDroppedDown][mPushed], getWidth() - height, 1); + drawImage(buttons[mDroppedDown][mPushed], getWidth() - height + 2, 1); } diff --git a/src/gui/window.cpp b/src/gui/window.cpp index 55dac5e0..1a6f1ac6 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -18,11 +18,12 @@ * 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: window.cpp 4207 2008-04-29 09:10:43Z b_lindeijer $ + * $Id$ */ #include <algorithm> #include <climits> +#include <cassert> #include <guichan/exception.hpp> #include <guichan/widgets/icon.hpp> @@ -43,24 +44,28 @@ #include "../resources/image.h" #include "../resources/resourcemanager.h" +#include "../utils/xml.h" + ConfigListener *Window::windowConfigListener = 0; WindowContainer *Window::windowContainer = 0; int Window::instances = 0; int Window::mouseResize = 0; ImageRect Window::border; Image *Window::closeImage = NULL; +bool Window::mAlphaChanged = false; class WindowConfigListener : public ConfigListener { void optionChanged(const std::string &) { + Window::mAlphaChanged = true; for_each(Window::border.grid, Window::border.grid + 9, std::bind2nd(std::mem_fun(&Image::setAlpha), config.getValue("guialpha", 0.8))); } }; -Window::Window(const std::string& caption, bool modal, Window *parent): +Window::Window(const std::string& caption, bool modal, Window *parent, const std::string& skin): gcn::Window(caption), mGrip(0), mParent(parent), @@ -72,7 +77,8 @@ Window::Window(const std::string& caption, bool modal, Window *parent): mMinWinWidth(100), mMinWinHeight(40), mMaxWinWidth(INT_MAX), - mMaxWinHeight(INT_MAX) + mMaxWinHeight(INT_MAX), + mSkin(skin) { logger->log("Window::Window(\"%s\")", caption.c_str()); @@ -80,23 +86,13 @@ Window::Window(const std::string& caption, bool modal, Window *parent): throw GCN_EXCEPTION("Window::Window. no windowContainer set"); } + // Loads the skin + loadSkin(mSkin); + + setGuiAlpha(); + if (instances == 0) { - // Load static resources - ResourceManager *resman = ResourceManager::getInstance(); - Image *dBorders = resman->getImage("graphics/gui/vscroll_grey.png"); - border.grid[0] = dBorders->getSubImage(0, 0, 4, 4); - border.grid[1] = dBorders->getSubImage(4, 0, 3, 4); - border.grid[2] = dBorders->getSubImage(7, 0, 4, 4); - border.grid[3] = dBorders->getSubImage(0, 4, 4, 10); - border.grid[4] = resman->getImage("graphics/gui/bg_quad_dis.png"); - border.grid[5] = dBorders->getSubImage(7, 4, 4, 10); - border.grid[6] = dBorders->getSubImage(0, 15, 4, 4); - border.grid[7] = dBorders->getSubImage(4, 15, 3, 4); - border.grid[8] = dBorders->getSubImage(7, 15, 4, 4); - dBorders->decRef(); - closeImage = resman->getImage("graphics/gui/close_button.png"); - windowConfigListener = new WindowConfigListener(); // Send GUI alpha changed for initialization windowConfigListener->optionChanged("guialpha"); @@ -135,14 +131,15 @@ Window::~Window() const std::string &name = mWindowName; // Saving X, Y and Width and Height for resizables in the config - config.setValue(name + "WinX", getX()); - config.setValue(name + "WinY", getY()); - config.setValue(name + "Visible", isVisible()); - - if (mGrip) - { - config.setValue(name + "WinWidth", getWidth()); - config.setValue(name + "WinHeight", getHeight()); + if (!name.empty()) { + config.setValue(name + "WinX", getX()); + config.setValue(name + "WinY", getY()); + config.setValue(name + "Visible", isVisible()); + + if (mGrip) { + config.setValue(name + "WinWidth", getWidth()); + config.setValue(name + "WinHeight", getHeight()); + } } instances--; @@ -154,15 +151,11 @@ Window::~Window() windowConfigListener = NULL; // Clean up static resources - delete border.grid[0]; - delete border.grid[1]; - delete border.grid[2]; - delete border.grid[3]; - border.grid[4]->decRef(); - delete border.grid[5]; - delete border.grid[6]; - delete border.grid[7]; - delete border.grid[8]; + for( int i = 0; i < 9; i++ ) + { + delete border.grid[i]; + border.grid[i] = NULL; + } closeImage->decRef(); } @@ -474,6 +467,7 @@ void Window::loadWindowState() { const std::string &name = mWindowName; + assert(!name.empty()); setPosition((int) config.getValue(name + "WinX", mDefaultX), (int) config.getValue(name + "WinY", mDefaultY)); @@ -536,3 +530,185 @@ int Window::getResizeHandles(gcn::MouseEvent &event) return resizeHandles; } + +void Window::setGuiAlpha() +{ + //logger->log("Window::setGuiAlpha: Alpha Value %f", config.getValue("guialpha", 0.8)); + for(int i = 0; i < 9; i++) + { + //logger->log("Window::setGuiAlpha: Border Image (%i)", i); + border.grid[i]->setAlpha(config.getValue("guialpha", 0.8)); + } + + mAlphaChanged = false; +} + +void Window::loadSkin(const std::string filename) +{ + const std::string windowId = Window::getId(); + + ResourceManager *resman = ResourceManager::getInstance(); + + logger->log("Loading Window Skin '%s'.", filename.c_str()); + logger->log("Loading Window ID '%s'.", windowId.c_str()); + + + if(filename == "") + logger->error("Window::loadSkin(): Invalid File Name."); + + // TODO: + // If there is an error loading the specified file, we should try to revert + // to a 'default' skin file. Only if the 'default' skin file can't be loaded + // should we have a terminating error. + XML::Document doc(filename); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "skinset")) + { + logger->error("Widget Skinning error"); + } + + std::string skinSetImage; + skinSetImage = XML::getProperty(rootNode, "image", ""); + Image *dBorders = NULL; + if(skinSetImage != "") + { + logger->log("Window::loadSkin(): <skinset> defines '%s' as a skin image.", skinSetImage.c_str()); + dBorders = resman->getImage("graphics/gui/" + skinSetImage);//"graphics/gui/speech_bubble.png"); + } + else + { + logger->error("Window::loadSkin(): Skinset does not define an image!"); + } + + //iterate <widget>'s + for_each_xml_child_node(widgetNode, rootNode) + { + if (!xmlStrEqual(widgetNode->name, BAD_CAST "widget")) + continue; + + std::string widgetType; + widgetType = XML::getProperty(widgetNode, "type", "unknown"); + if (widgetType == "Window") + { + // Iterate through <part>'s + // LEEOR / TODO: + // We need to make provisions to load in a CloseButton image. For now it + // can just be hard-coded. + for_each_xml_child_node(partNode, widgetNode) + { + if (!xmlStrEqual(partNode->name, BAD_CAST "part")) + { + continue; + } + + std::string partType; + partType = XML::getProperty(partNode, "type", "unknown"); + // TOP ROW + if(partType == "top-left-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[0] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "top-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[1] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "top-right-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[2] = dBorders->getSubImage(xPos, yPos, width, height); + } + + // MIDDLE ROW + else if(partType == "left-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[3] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "bg-quad") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[4] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "right-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[5] = dBorders->getSubImage(xPos, yPos, width, height); + } + + // BOTTOM ROW + else if(partType == "bottom-left-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[6] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "bottom-edge") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[7] = dBorders->getSubImage(xPos, yPos, width, height); + } + else if(partType == "bottom-right-corner") + { + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + border.grid[8] = dBorders->getSubImage(xPos, yPos, width, height); + } + + // Part is of an uknown type. + else + { + logger->log("Window::loadSkin(): Unknown Part Type '%s'", partType.c_str()); + } + } + } + // Widget is of an uknown type. + else + { + logger->log("Window::loadSkin(): Unknown Widget Type '%s'", widgetType.c_str()); + } + } + dBorders->decRef(); + + logger->log("Finished loading Window Skin."); + + // Hard-coded for now until we update the above code to look for window buttons. + closeImage = resman->getImage("graphics/gui/close_button.png"); +} + diff --git a/src/gui/window.h b/src/gui/window.h index 8f288991..228cc37b 100644 --- a/src/gui/window.h +++ b/src/gui/window.h @@ -18,7 +18,7 @@ * 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: window.h 4207 2008-04-29 09:10:43Z b_lindeijer $ + * $Id$ */ #ifndef _TMW_WINDOW_H__ @@ -56,9 +56,10 @@ class Window : public gcn::Window, gcn::WidgetListener * @param parent The parent window. This is the window standing above * this one in the window hiearchy. When reordering, * a window will never go below its parent window. + * @param skin The location where the window's skin XML can be found. */ Window(const std::string &caption = "Window", bool modal = false, - Window *parent = NULL); + Window *parent = NULL, const std::string &skin = "graphics/gui/gui.xml"); /** * Destructor. @@ -237,6 +238,11 @@ class Window : public gcn::Window, gcn::WidgetListener */ virtual void resetToDefaultSize(); + /** + * Loads a window skin + */ + void loadSkin(const std::string filename); + enum ResizeHandles { TOP = 0x01, @@ -255,6 +261,8 @@ class Window : public gcn::Window, gcn::WidgetListener */ int getResizeHandles(gcn::MouseEvent &event); + void setGuiAlpha(); + GCContainer *mChrome; /**< Contained container */ ResizeGrip *mGrip; /**< Resize grip */ Window *mParent; /**< The parent window */ @@ -263,6 +271,7 @@ class Window : public gcn::Window, gcn::WidgetListener bool mModal; /**< Window is modal */ bool mCloseButton; /**< Window has a close button */ bool mSticky; /**< Window resists minimization */ + static bool mAlphaChanged; /**< Whether the alpha percent was changed */ int mMinWinWidth; /**< Minimum window width */ int mMinWinHeight; /**< Minimum window height */ int mMaxWinWidth; /**< Maximum window width */ @@ -271,6 +280,7 @@ class Window : public gcn::Window, gcn::WidgetListener int mDefaultY; /**< Default window Y position */ int mDefaultWidth; /**< Default window width */ int mDefaultHeight; /**< Default window height */ + std::string mSkin; /**< Name of the skin to use */ /** The window container windows add themselves to. */ static WindowContainer *windowContainer; diff --git a/src/gui/windowcontainer.cpp b/src/gui/windowcontainer.cpp index 0a0a0a55..05c2b5e9 100644 --- a/src/gui/windowcontainer.cpp +++ b/src/gui/windowcontainer.cpp @@ -21,15 +21,13 @@ * $Id: windowcontainer.cpp 3754 2007-11-20 15:19:50Z b_lindeijer $ */ -#include <algorithm> - #include "windowcontainer.h" #include "../utils/dtor.h" void WindowContainer::logic() { - for_each(mDeathList.begin(), mDeathList.end(), make_dtor(mDeathList)); + delete_all(mDeathList); mDeathList.clear(); gcn::Container::logic(); diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp index d4ff8f02..965434b0 100644 --- a/src/imageparticle.cpp +++ b/src/imageparticle.cpp @@ -18,7 +18,7 @@ * 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: imageparticle.cpp 4360 2008-06-23 14:44:20Z crush_tmw $ + * $Id$ */ #include "imageparticle.h" diff --git a/src/keyboardconfig.cpp b/src/keyboardconfig.cpp index 5534b96e..9f558883 100644 --- a/src/keyboardconfig.cpp +++ b/src/keyboardconfig.cpp @@ -18,7 +18,7 @@ * 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: keyboardconfig.cpp 4255 2008-05-21 21:44:27Z crush_tmw $ + * $Id$ */ #include "keyboardconfig.h" @@ -43,6 +43,7 @@ static KeyData const keyData[KeyboardConfig::KEY_TOTAL] = { {"keyMoveLeft", SDLK_LEFT, "Move Left"}, {"keyMoveRight", SDLK_RIGHT, "Move Right"}, {"keyAttack", SDLK_LCTRL, "Attack"}, + {"keySmilie", SDLK_LALT, "Smilie"}, {"keyTarget", SDLK_LSHIFT, "Target"}, {"keyTargetClosest", SDLK_a, "Target Closest"}, {"keyTargetPlayer", SDLK_q, "Target Player"}, diff --git a/src/keyboardconfig.h b/src/keyboardconfig.h index b98fcb7a..b57136dc 100644 --- a/src/keyboardconfig.h +++ b/src/keyboardconfig.h @@ -18,7 +18,7 @@ * 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: keyboardconfig.h 4255 2008-05-21 21:44:27Z crush_tmw $ + * $Id$ */ #ifndef _TMW_KEYBOARDCONFIG_H @@ -151,6 +151,7 @@ class KeyboardConfig KEY_MOVE_LEFT, KEY_MOVE_RIGHT, KEY_ATTACK, + KEY_SMILIE, KEY_TARGET, KEY_TARGET_CLOSEST, KEY_TARGET_PLAYER, diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 554c52a6..66d37ddf 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -18,7 +18,7 @@ * 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: localplayer.cpp 4347 2008-06-12 09:06:01Z b_lindeijer $ + * $Id$ */ #include "localplayer.h" @@ -48,8 +48,8 @@ LocalPlayer::LocalPlayer(Uint32 id, Uint16 job, Map *map): mAttackRange(0), mXp(0), mNetwork(0), mTarget(NULL), mPickUpTarget(NULL), - mTrading(false), mInStorage(false), - mGoingToTarget(false), mLastAction(-1), + mTrading(false), mGoingToTarget(false), + mLastAction(-1), mWalkingDir(0), mDestX(0), mDestY(0), mInventory(new Inventory(INVENTORY_SIZE)), mStorage(new Inventory(STORAGE_SIZE)) @@ -65,6 +65,18 @@ LocalPlayer::~LocalPlayer() void LocalPlayer::logic() { switch (mAction) { + case STAND: + break; + + case SIT: + break; + + case DEAD: + break; + + case HURT: + break; + case WALK: mFrame = (get_elapsed_time(mWalkTime) * 6) / mWalkSpeed; if (mFrame >= 6) { @@ -144,6 +156,9 @@ void LocalPlayer::unequipItem(Item *item) MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_PLAYER_UNEQUIP); outMsg.writeInt16(item->getInvIndex()); + + // Tidy equipment directly to avoid weapon still shown bug, for instance + mEquipment->removeEquipment(item); } void LocalPlayer::useItem(Item *item) @@ -183,7 +198,6 @@ void LocalPlayer::pickUp(FloorItem *item) void LocalPlayer::walk(unsigned char dir) { - if (!mMap || !dir) return; diff --git a/src/localplayer.h b/src/localplayer.h index 1d865824..757c70eb 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -18,7 +18,7 @@ * 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: localplayer.h 4347 2008-06-12 09:06:01Z b_lindeijer $ + * $Id$ */ #ifndef _TMW_LOCALPLAYER_H diff --git a/src/map.cpp b/src/map.cpp index d9d3a319..ef622ad0 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -23,10 +23,11 @@ #include "map.h" -#include <algorithm> #include <queue> +#include <cassert> #include "beingmanager.h" +#include "configuration.h" #include "game.h" #include "graphics.h" #include "particle.h" @@ -40,6 +41,8 @@ #include "utils/dtor.h" #include "utils/tostring.h" +extern volatile int tick_time; + /** * A location on a tile map. Used for pathfinding, open list. */ @@ -62,6 +65,80 @@ struct Location MetaTile *tile; }; +MapLayer::MapLayer(int x, int y, int width, int height, bool isFringeLayer): + mX(x), mY(y), + mWidth(width), mHeight(height), + mIsFringeLayer(isFringeLayer) +{ + const int size = mWidth * mHeight; + mTiles = new Image*[size]; + std::fill_n(mTiles, size, (Image*) 0); +} + +MapLayer::~MapLayer() +{ + delete[] mTiles; +} + +void MapLayer::setTile(int x, int y, Image *img) +{ + mTiles[x + y * mWidth] = img; +} + +Image* MapLayer::getTile(int x, int y) const +{ + return mTiles[x + y * mWidth]; +} + +void MapLayer::draw(Graphics *graphics, + int startX, int startY, + int endX, int endY, + int scrollX, int scrollY, + const Sprites &sprites) const +{ + startX -= mX; + startY -= mY; + endX -= mX; + endY -= mY; + + if (startX < 0) startX = 0; + if (startY < 0) startY = 0; + if (endX > mWidth) endX = mWidth; + if (endY > mHeight) endY = mHeight; + + Sprites::const_iterator si = sprites.begin(); + + for (int y = startY; y < endY; y++) + { + // If drawing the fringe layer, make sure all sprites above this row of + // tiles have been drawn + if (mIsFringeLayer) { + while (si != sprites.end() && (*si)->getPixelY() <= y * 32 - 32) { + (*si)->draw(graphics, -scrollX, -scrollY); + si++; + } + } + + for (int x = startX; x < endX; x++) + { + Image *img = getTile(x, y); + if (img) { + const int px = (x + mX) * 32 - scrollX; + const int py = (y + mY) * 32 - scrollY + 32 - img->getHeight(); + graphics->drawImage(img, px, py); + } + } + } + + // Draw any remaining sprites + if (mIsFringeLayer) { + while (si != sprites.end()) { + (*si)->draw(graphics, -scrollX, -scrollY); + si++; + } + } +} + Map::Map(int width, int height, int tileWidth, int tileHeight): mWidth(width), mHeight(height), mTileWidth(tileWidth), mTileHeight(tileHeight), @@ -69,23 +146,18 @@ Map::Map(int width, int height, int tileWidth, int tileHeight): mOnClosedList(1), mOnOpenList(2), mLastScrollX(0.0f), mLastScrollY(0.0f) { - int size = mWidth * mHeight; + const int size = mWidth * mHeight; mMetaTiles = new MetaTile[size]; - mTiles = new Image*[size * 3]; - std::fill_n(mTiles, size * 3, (Image*)0); } Map::~Map() { - // clean up map data + // delete metadata, layers, tilesets and overlays delete[] mMetaTiles; - delete[] mTiles; - // clean up tilesets - for_each(mTilesets.begin(), mTilesets.end(), make_dtor(mTilesets)); - mTilesets.clear(); - // clean up overlays - for_each(mOverlays.begin(), mOverlays.end(), make_dtor(mOverlays)); + delete_all(mLayers); + delete_all(mTilesets); + delete_all(mOverlays); } void Map::initializeOverlays() @@ -99,9 +171,9 @@ void Map::initializeOverlays() const std::string name = "overlay" + toString(i); Image *img = resman->getImage(getProperty(name + "image")); - float speedX = getFloatProperty(name + "scrollX"); - float speedY = getFloatProperty(name + "scrollY"); - float parallax = getFloatProperty(name + "parallax"); + const float speedX = getFloatProperty(name + "scrollX"); + const float speedY = getFloatProperty(name + "scrollY"); + const float parallax = getFloatProperty(name + "parallax"); if (img) { @@ -114,6 +186,11 @@ void Map::initializeOverlays() } } +void Map::addLayer(MapLayer *layer) +{ + mLayers.push_back(layer); +} + void Map::addTileset(Tileset *tileset) { mTilesets.push_back(tileset); @@ -127,63 +204,32 @@ bool spriteCompare(const Sprite *a, const Sprite *b) return a->getPixelY() < b->getPixelY(); } -void Map::draw(Graphics *graphics, int scrollX, int scrollY, int layer) +void Map::draw(Graphics *graphics, int scrollX, int scrollY) { int endPixelY = graphics->getHeight() + scrollY + mTileHeight - 1; - // If drawing the fringe layer, make sure sprites are sorted - SpriteIterator si; - if (layer == 1) - { - mSprites.sort(spriteCompare); - si = mSprites.begin(); - endPixelY += mMaxTileHeight - mTileHeight; - } + // TODO: Do this per-layer + endPixelY += mMaxTileHeight - mTileHeight; int startX = scrollX / mTileWidth; int startY = scrollY / mTileHeight; int endX = (graphics->getWidth() + scrollX + mTileWidth - 1) / mTileWidth; int endY = endPixelY / mTileHeight; - if (startX < 0) startX = 0; - if (startY < 0) startY = 0; - if (endX > mWidth) endX = mWidth; - if (endY > mHeight) endY = mHeight; + // Make sure sprites are sorted + mSprites.sort(spriteCompare); - for (int y = startY; y < endY; y++) + Layers::const_iterator layeri = mLayers.begin(); + for (; layeri != mLayers.end(); ++layeri) { - // If drawing the fringe layer, make sure all sprites above this row of - // tiles have been drawn - if (layer == 1) - { - while (si != mSprites.end() && (*si)->getPixelY() <= y * 32 - 32) - { - (*si)->draw(graphics, -scrollX, -scrollY); - si++; - } - } - - for (int x = startX; x < endX; x++) - { - Image *img = getTile(x, y, layer); - if (img) { - graphics->drawImage(img, - x * mTileWidth - scrollX, - y * mTileHeight - scrollY + - mTileHeight - img->getHeight()); - } - } + (*layeri)->draw(graphics, + startX, startY, endX, endY, + scrollX, scrollY, + mSprites); } - // Draw any remaining sprites - if (layer == 1) - { - while (si != mSprites.end()) - { - (*si)->draw(graphics, -scrollX, -scrollY); - si++; - } - } + drawOverlay(graphics, scrollX, scrollY, + (int) config.getValue("OverlayDetail", 2)); } void Map::drawOverlay(Graphics *graphics, @@ -226,23 +272,10 @@ void Map::drawOverlay(Graphics *graphics, }; } -void Map::setTileWithGid(int x, int y, int layer, int gid) -{ - if (layer == 3) - { - Tileset *set = getTilesetWithGid(gid); - setWalk(x, y, (!set || (gid - set->getFirstGid() == 0))); - } - else if (layer < 3) - { - setTile(x, y, layer, getTileWithGid(gid)); - } -} - class ContainsGidFunctor { public: - bool operator() (Tileset* set) + bool operator() (Tileset* set) const { return (set->getFirstGid() <= gid && gid - set->getFirstGid() < (int)set->size()); @@ -255,27 +288,16 @@ Tileset* Map::getTilesetWithGid(int gid) const containsGid.gid = gid; Tilesets::const_iterator i = find_if(mTilesets.begin(), mTilesets.end(), - containsGid); + containsGid); return (i == mTilesets.end()) ? NULL : *i; } -Image* Map::getTileWithGid(int gid) const -{ - Tileset *set = getTilesetWithGid(gid); - - if (set) { - return set->get(gid - set->getFirstGid()); - } - - return NULL; -} - void Map::setWalk(int x, int y, bool walkable) { mMetaTiles[x + y * mWidth].walkable = walkable; } - + bool Map::occupied(int x, int y) const { Beings &beings = beingManager->getAll(); @@ -293,7 +315,7 @@ bool Map::occupied(int x, int y) const bool Map::tileCollides(int x, int y) const { - return !(contains(x, y) && mMetaTiles[x + y * mWidth].walkable); + return !(contains(x, y) && mMetaTiles[x + y * mWidth].walkable); } bool Map::contains(int x, int y) const @@ -301,16 +323,6 @@ bool Map::contains(int x, int y) const return x >= 0 && y >= 0 && x < mWidth && y < mHeight; } -void Map::setTile(int x, int y, int layer, Image *img) -{ - mTiles[x + y * mWidth + layer * (mWidth * mHeight)] = img; -} - -Image* Map::getTile(int x, int y, int layer) -{ - return mTiles[x + y * mWidth + layer * (mWidth * mHeight)]; -} - MetaTile* Map::getMetaTile(int x, int y) { return &mMetaTiles[x + y * mWidth]; @@ -327,6 +339,8 @@ void Map::removeSprite(SpriteIterator iterator) mSprites.erase(iterator); } +static int const basicCost = 100; + Path Map::findPath(int startX, int startY, int destX, int destY) { // Path to be built up (empty by default) @@ -367,8 +381,8 @@ Path Map::findPath(int startX, int startY, int destX, int destY) for (int dx = -1; dx <= 1; dx++) { // Calculate location of tile to check - int x = curr.x + dx; - int y = curr.y + dy; + const int x = curr.x + dx; + const int y = curr.y + dy; // Skip if if we're checking the same tile we're leaving from, // or if the new location falls outside of the map boundaries @@ -379,7 +393,8 @@ Path Map::findPath(int startX, int startY, int destX, int destY) MetaTile *newTile = getMetaTile(x, y); - // Skip if the tile is on the closed list or collides unless its the destination tile + // Skip if the tile is on the closed list or collides unless + // its the destination tile if (newTile->whichList == mOnClosedList || (tileCollides(x, y) && !(x == destX && y == destY))) { @@ -404,13 +419,6 @@ Path Map::findPath(int startX, int startY, int destX, int destY) // 14 for moving diagonal (sqrt(200) = 14.1421...) int Gcost = curr.tile->Gcost + ((dx == 0 || dy == 0) ? 10 : 14); - // It costs extra to walk through a being (needs to be enough - // to make it more attractive to walk around). - if (occupied(x, y)) - { - Gcost += 30; - } - // Skip if Gcost becomes too much // Warning: probably not entirely accurate if (Gcost > 200) @@ -430,7 +438,7 @@ Path Map::findPath(int startX, int startY, int destX, int destY) // Update Gcost and Fcost of new tile newTile->Gcost = Gcost; - newTile->Fcost = newTile->Gcost + newTile->Hcost; + newTile->Fcost = Gcost + newTile->Hcost; if (x != destX || y != destY) { // Add this tile to the open list @@ -447,7 +455,7 @@ Path Map::findPath(int startX, int startY, int destX, int destY) // Found a shorter route. // Update Gcost and Fcost of the new tile newTile->Gcost = Gcost; - newTile->Fcost = newTile->Gcost + newTile->Hcost; + newTile->Fcost = Gcost + newTile->Hcost; // Set the current tile as the parent of the new tile newTile->parentX = curr.x; @@ -488,7 +496,7 @@ Path Map::findPath(int startX, int startY, int destX, int destY) return path; } -void Map::addParticleEffect (std::string effectFile, int x, int y) +void Map::addParticleEffect(const std::string &effectFile, int x, int y) { ParticleEffectData newEffect; newEffect.file = effectFile; @@ -18,7 +18,7 @@ * 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: map.h 3887 2008-02-12 19:49:37Z umperio $ + * $Id$ */ #ifndef _TMW_MAP_H_ @@ -32,6 +32,7 @@ class AmbientOverlay; class Graphics; class Image; +class MapLayer; class Particle; class Sprite; class Tileset; @@ -41,8 +42,7 @@ struct PATH_NODE; typedef std::vector<Tileset*> Tilesets; typedef std::list<Sprite*> Sprites; typedef Sprites::iterator SpriteIterator; - -extern volatile int tick_time; +typedef std::vector<MapLayer*> Layers; /** * A meta tile stores additional information about a location on a tile map. @@ -67,6 +67,56 @@ struct MetaTile }; /** + * A map layer. Stores a grid of tiles and their offset, and implements layer + * rendering. + */ +class MapLayer +{ + public: + /** + * Constructor, taking layer origin, size and whether this layer is the + * fringe layer. The fringe layer is the layer that draws the sprites. + * There can be only one fringe layer per map. + */ + MapLayer(int x, int y, int width, int height, bool isFringeLayer); + + /** + * Destructor. + */ + ~MapLayer(); + + /** + * Set tile image, with x and y in layer coordinates. + */ + void setTile(int x, int y, Image *img); + + /** + * Get tile image, with x and y in layer coordinates. + */ + Image *getTile(int x, int y) const; + + /** + * Draws this layer to the given graphics context. The coordinates are + * expected to be in map range and will be translated to local layer + * coordinates and clipped to the layer's dimensions. + * + * The given sprites are only drawn when this layer is the fringe + * layer. + */ + void draw(Graphics *graphics, + int startX, int startY, + int endX, int endY, + int scrollX, int scrollY, + const Sprites &sprites) const; + + private: + int mX, mY; + int mWidth, mHeight; + bool mIsFringeLayer; /**< Whether the sprites are drawn. */ + Image **mTiles; +}; + +/** * A tile map. */ class Map : public Properties @@ -89,39 +139,30 @@ class Map : public Properties void initializeOverlays(); /** - * Draws a map layer to the given graphics output. - */ - void draw(Graphics *graphics, int scrollX, int scrollY, int layer); - - /** - * Draws the overlay graphic to the given graphics output. - */ - void - drawOverlay(Graphics *graphics, float scrollX, float scrollY, - int detail); - - /** - * Adds a tileset to this map. + * Draws the map to the given graphics output. This method draws all + * layers, sprites and overlay effects. + * + * TODO: For efficiency reasons, this method could take into account + * the clipping rectangle set on the Graphics object. However, + * currently the map is always drawn full-screen. */ - void - addTileset(Tileset *tileset); + void draw(Graphics *graphics, int scrollX, int scrollY); /** - * Sets a tile using a global tile id. Used by the layer loading - * routine. + * Adds a layer to this map. The map takes ownership of the layer. */ - void - setTileWithGid(int x, int y, int layer, int gid); + void addLayer(MapLayer *layer); /** - * Set tile ID. + * Adds a tileset to this map. The map takes ownership of the tileset. */ - void setTile(int x, int y, int layer, Image *img); + void addTileset(Tileset *tileset); /** - * Get tile ID. + * Finds the tile set that a tile with the given global id is part of. */ - Image *getTile(int x, int y, int layer); + Tileset* + getTilesetWithGid(int gid) const; /** * Get tile reference. @@ -183,7 +224,7 @@ class Map : public Properties /** * Adds a particle effect */ - void addParticleEffect (std::string effectFile, int x, int y); + void addParticleEffect(const std::string &effectFile, int x, int y); /** * Initializes all added particle effects @@ -193,15 +234,11 @@ class Map : public Properties private: /** - * Converts a global tile id to the Image* pointing to the associated - * tile image. - */ - Image* getTileWithGid(int gid) const; - - /** - * Finds the tile set that a tile with the given global id is part of. + * Draws the overlay graphic to the given graphics output. */ - Tileset* getTilesetWithGid(int gid) const; + void + drawOverlay(Graphics *graphics, float scrollX, float scrollY, + int detail); /** * Tells whether a tile is occupied by a being. @@ -217,15 +254,14 @@ class Map : public Properties int mTileWidth, mTileHeight; int mMaxTileHeight; MetaTile *mMetaTiles; - Image **mTiles; - + Layers mLayers; Tilesets mTilesets; Sprites mSprites; // Pathfinding members int mOnClosedList, mOnOpenList; - // Overlay Data + // Overlay data std::list<AmbientOverlay*> mOverlays; float mLastScrollX; float mLastScrollY; diff --git a/src/monster.cpp b/src/monster.cpp index d3e52520..2b6fadce 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -74,15 +74,13 @@ Monster::Monster(Uint32 id, Uint16 job, Map *map): } } -Monster::~Monster() { - +Monster::~Monster() +{ if (mText) delete mText; - } -void -Monster::logic() +void Monster::logic() { if (mAction != STAND) { @@ -97,14 +95,12 @@ Monster::logic() Being::logic(); } -Being::Type -Monster::getType() const +Being::Type Monster::getType() const { return MONSTER; } -void -Monster::setAction(Uint8 action) +void Monster::setAction(Action action) { SpriteAction currentAction = ACTION_INVALID; @@ -122,11 +118,14 @@ Monster::setAction(Uint8 action) mSprites[BASE_SPRITE]->reset(); break; case STAND: - currentAction = ACTION_STAND; - break; + currentAction = ACTION_STAND; + break; case HURT: - // Not implemented yet - break; + // Not implemented yet + break; + case SIT: + // Also not implemented yet + break; } if (currentAction != ACTION_INVALID) @@ -142,8 +141,7 @@ Monster::setAction(Uint8 action) } } -void -Monster::handleAttack(Being *victim, int damage) +void Monster::handleAttack(Being *victim, int damage) { Being::handleAttack(victim, damage); @@ -151,21 +149,18 @@ Monster::handleAttack(Being *victim, int damage) sound.playSfx(mi.getSound((damage > 0) ? MONSTER_EVENT_HIT : MONSTER_EVENT_MISS)); } -void -Monster::takeDamage(int amount) +void Monster::takeDamage(int amount) { if (amount > 0) sound.playSfx(getInfo().getSound(MONSTER_EVENT_HURT)); Being::takeDamage(amount); } -Being::TargetCursorSize -Monster::getTargetCursorSize() const +Being::TargetCursorSize Monster::getTargetCursorSize() const { return getInfo().getTargetCursorSize(); } -const MonsterInfo& -Monster::getInfo() const +const MonsterInfo& Monster::getInfo() const { return MonsterDB::get(mJob - 1002); } diff --git a/src/monster.h b/src/monster.h index b613e96d..4839966a 100644 --- a/src/monster.h +++ b/src/monster.h @@ -38,7 +38,7 @@ class Monster : public Being virtual void logic(); - virtual void setAction(Uint8 action); + virtual void setAction(Action action); virtual Type getType() const; diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp index fbef02bd..7c9b8afd 100644 --- a/src/net/beinghandler.cpp +++ b/src/net/beinghandler.cpp @@ -23,6 +23,7 @@ #include "beinghandler.h" +#include <iostream> #include <SDL_types.h> #include "messagein.h" @@ -37,6 +38,7 @@ #include "../particle.h" #include "../sound.h" #include "../player_relations.h" +#include "../npc.h" const int EMOTION_TIME = 150; /**< Duration of emotion icon */ @@ -203,8 +205,13 @@ void BeingHandler::handleMessage(MessageIn *msg) break; // If this is player's current target, clear it. - if (player_node->getTarget() == dstBeing) + if (dstBeing == player_node->getTarget()) + { player_node->stopAttack(); + } + + if (dstBeing == current_npc) + current_npc = NULL; if (msg->readInt8() == 1) { @@ -464,7 +471,7 @@ void BeingHandler::handleMessage(MessageIn *msg) msg->readInt8(); // unknown dstBeing->mWalkTime = tick_time; - dstBeing->mFrame = 0; + //dstBeing->mFrame = 0; break; case SMSG_PLAYER_STOP: diff --git a/src/net/equipmenthandler.cpp b/src/net/equipmenthandler.cpp index cc5d016c..0fc98175 100644 --- a/src/net/equipmenthandler.cpp +++ b/src/net/equipmenthandler.cpp @@ -156,6 +156,12 @@ void EquipmentHandler::handleMessage(MessageIn *msg) mask <<= 1; position++; } + + item = inventory->getItem(index); + if (!item) + break; + + item->setEquipped(false); player_node->mEquipment->removeEquipment(position); } logger->log("Unequipping: %i %i(%i) %i", @@ -174,6 +180,7 @@ void EquipmentHandler::handleMessage(MessageIn *msg) item = inventory->getItem(index); if (item) { + item->setEquipped(true); player_node->mEquipment->setArrows(item); logger->log("Arrows equipped: %i", index); } diff --git a/src/net/loginhandler.cpp b/src/net/loginhandler.cpp index 6505e39f..737d1762 100644 --- a/src/net/loginhandler.cpp +++ b/src/net/loginhandler.cpp @@ -113,7 +113,7 @@ void LoginHandler::handleMessage(MessageIn *msg) errorMessage = "You have been blocked by the GM Team"; break; case 6: - errorMessage = "You have been banned for 5 minutes"; + errorMessage = "You have been temporarily banned from the game. Please contact the GM team"; break; case 9: errorMessage = "This user name is already taken"; diff --git a/src/net/playerhandler.cpp b/src/net/playerhandler.cpp index e871b670..61e68295 100644 --- a/src/net/playerhandler.cpp +++ b/src/net/playerhandler.cpp @@ -52,6 +52,9 @@ extern BuyDialog *buyDialog; extern SellDialog *sellDialog; extern Window *buySellDialog; +static const int MAP_TELEPORT_SCROLL_DISTANCE = 8; /* Max. distance we are willing to scroll after a teleport; + * everything beyond will reset the port hard. */ + /** * Listener used for handling the overweigth message. */ @@ -113,17 +116,13 @@ void PlayerHandler::handleMessage(MessageIn *msg) * This client assumes that all walk messages succeed, * and that the server will send a correction notice * otherwise. - * - * Note that this packet is also used by eAthena to notify - * the client of a server-generated auto-move. A patch has - * been submitted to Mantis to eliminate these auto moves, - * since they're inconsistent with the client design. */ break; case SMSG_PLAYER_WARP: { std::string mapPath = msg->readString(16); + bool nearby; Uint16 x = msg->readInt16(); Uint16 y = msg->readInt16(); @@ -135,13 +134,23 @@ void PlayerHandler::handleMessage(MessageIn *msg) */ player_node->stopAttack(); + nearby = (engine->getCurrentMapName() == mapPath); // Switch the actual map, deleting the previous one engine->changeMap(mapPath); current_npc = 0; - float scrollOffsetX = (x - player_node->mX) * 32; - float scrollOffsetY = (y - player_node->mY) * 32; + float scrollOffsetX = 0.0f; + float scrollOffsetY = 0.0f; + + /* Scroll if neccessary */ + if (!nearby + || (abs(x - player_node->mX) > MAP_TELEPORT_SCROLL_DISTANCE) + || (abs(y - player_node->mY) > MAP_TELEPORT_SCROLL_DISTANCE)) + { + scrollOffsetX = (x - player_node->mX) * 32; + scrollOffsetY = (y - player_node->mY) * 32; + } player_node->setAction(Being::STAND); player_node->mFrame = 0; @@ -204,8 +213,39 @@ void PlayerHandler::handleMessage(MessageIn *msg) if (player_node->mHp == 0 && deathNotice == NULL) { - deathNotice = new OkDialog("Message", - "You're now dead, press ok to restart"); + static char const *const deadMsg[] = + { + "You are dead.", + "We regret to inform you that your character was killed in battle.", + "You are not that alive anymore.", + "The cold hands of the grim reaper are grabbing for your soul.", + "Game Over!", + "Insert coin to continue", + "No, kids. Your character did not really die. It... err... went to a better place.", + "Your plan of breaking your enemies weapon by bashing it with your throat failed.", + "I guess this did not run too well.", + "Do you want your possessions identified?", // Nethack reference + "Sadly, no trace of you was ever found...", // Secret of Mana reference + "Annihilated.", // Final Fantasy VI reference + "Looks like you got your head handed to you.", //Earthbound reference + "You screwed up again, dump your body down the tubes and get you another one.", // Leisure Suit Larry 1 Reference + "You're not dead yet. You're just resting.", // Monty Python reference from a couple of skits + "You are no more.", // Monty Python reference from the dead parrot sketch starting now + "You have ceased to be.", + "You've expired and gone to meet your maker.", + "You're a stiff.", + "Bereft of life, you rest in peace.", + "If you weren't so animated, you'd be pushing up the daisies.", + "Your metabolic processes are now history.", + "You're off the twig.", + "You've kicked the bucket.", + "You've shuffled off your mortal coil, run down the curtain and joined the bleedin' choir invisibile.", + "You are an ex-player.", + "You're pining for the fjords." // Monty Python reference from the dead parrot sketch + }; + std::string message(deadMsg[rand()%27]); + + deathNotice = new OkDialog("Message", message); deathNotice->addActionListener(&deathListener); player_node->setAction(Being::DEAD); } @@ -288,7 +328,7 @@ void PlayerHandler::handleMessage(MessageIn *msg) } break; - // Updates stats and status points + // Updates stats and status points case SMSG_PLAYER_STAT_UPDATE_5: player_node->mStatsPointsToAttribute = msg->readInt16(); player_node->mAttr[LocalPlayer::STR] = msg->readInt8(); diff --git a/src/npc.cpp b/src/npc.cpp index 1615ecd6..fe0b4598 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -86,7 +86,8 @@ void NPC::setName(const std::string &name) mName = new Text(name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, gcn::Graphics::CENTER, npcNameFont, gcn::Color(200, 200, 255)); - } + Being::setName(name + " (NPC)"); +} Being::Type NPC::getType() const diff --git a/src/particle.cpp b/src/particle.cpp index 651fae41..8591838f 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -18,7 +18,7 @@ * 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: particle.cpp 4362 2008-06-24 12:29:33Z crush_tmw $ + * $Id$ */ #include <algorithm> @@ -61,7 +61,7 @@ Particle::Particle(Map *map): mAutoDelete(true), mMap(map), mGravity(0.0f), - mRandomnes(0), + mRandomness(0), mBounce(0.0f), mFollow(false), mTarget(NULL), @@ -139,11 +139,11 @@ Particle::update() } } - if (mRandomnes > 0) + if (mRandomness > 0) { - mVelocity.x += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f; - mVelocity.y += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f; - mVelocity.z += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f; + mVelocity.x += (rand()%mRandomness - rand()%mRandomness) / 1000.0f; + mVelocity.y += (rand()%mRandomness - rand()%mRandomness) / 1000.0f; + mVelocity.z += (rand()%mRandomness - rand()%mRandomness) / 1000.0f; } mVelocity.z -= mGravity; diff --git a/src/particle.h b/src/particle.h index d4a671c7..f281864d 100644 --- a/src/particle.h +++ b/src/particle.h @@ -18,7 +18,7 @@ * 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: particle.h 4362 2008-06-24 12:29:33Z crush_tmw $ + * $Id$ */ #ifndef _PARTICLE_H @@ -161,6 +161,9 @@ class Particle : public Sprite { mPos.x += x; mPos.y += y; mPos.z += z; } void + moveChildren(Vector change); + + void moveBy (Vector change) { mPos += change; } @@ -227,8 +230,8 @@ class Particle : public Sprite * Sets the ammount of random vector changes */ void - setRandomnes(int r) - { mRandomnes = r; } + setRandomness(int r) + { mRandomness = r; } /** * Sets the ammount of velocity particles retain after @@ -271,6 +274,12 @@ class Particle : public Sprite { return mAlive; } /** + * Determines whether the particle and its children are all dead + */ + bool isExtinct() + { return !isAlive() && mChildParticles.empty(); } + + /** * Manually marks the particle for deletion. */ void kill() @@ -303,7 +312,7 @@ class Particle : public Sprite // dynamic particle Vector mVelocity; /**< Speed in pixels per game-tick. */ float mGravity; /**< Downward acceleration in pixels per game-tick. */ - int mRandomnes; /**< Ammount of random vector change */ + int mRandomness; /**< Ammount of random vector change */ float mBounce; /**< How much the particle bounces off when hitting the ground */ bool mFollow; /**< is this particle moved when its parent particle moves? */ diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp index 996e3396..3e0a3d75 100644 --- a/src/particleemitter.cpp +++ b/src/particleemitter.cpp @@ -18,7 +18,7 @@ * 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: particleemitter.cpp 4362 2008-06-24 12:29:33Z crush_tmw $ + * $Id$ */ #include "particleemitter.h" @@ -39,6 +39,7 @@ #define DEG_RAD_FACTOR 0.017453293f ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map *map): + mOutputPauseLeft(0), mParticleImage(0) { mMap = map; @@ -52,7 +53,7 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * mParticleAngleVertical.set(0.0f); mParticlePower.set(0.0f); mParticleGravity.set(0.0f); - mParticleRandomnes.set(0); + mParticleRandomness.set(0); mParticleBounce.set(0.0f); mParticleFollow = false; mParticleAcceleration.set(0.0f); @@ -62,6 +63,7 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * mParticleFadeOut.set(0); mParticleFadeIn.set(0); mOutput.set(1); + mOutputPause.set(0); mParticleAlpha.set(1.0f); for_each_xml_child_node(propertyNode, emitterNode) @@ -117,9 +119,9 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * { mParticleGravity = readMinMax(propertyNode, 0.0f); } - else if (name == "randomnes") + else if (name == "randomnes" || name == "randomness") // legacy bug { - mParticleRandomnes = readMinMax(propertyNode, 0); + mParticleRandomness = readMinMax(propertyNode, 0); } else if (name == "bounce") { @@ -135,6 +137,11 @@ ParticleEmitter::ParticleEmitter(xmlNodePtr emitterNode, Particle *target, Map * mOutput = readMinMax(propertyNode, 0); mOutput.maxVal +=1; } + else if (name == "output-pause") + { + mOutputPause = readMinMax(propertyNode, 0); + mOutputPauseLeft = mOutputPause.value(); + } else if (name == "acceleration") { mParticleAcceleration = readMinMax(propertyNode, 0.0f); @@ -261,7 +268,7 @@ ParticleEmitter & ParticleEmitter::operator=(const ParticleEmitter &o) mParticleAngleVertical = o.mParticleAngleVertical; mParticlePower = o.mParticlePower; mParticleGravity = o.mParticleGravity; - mParticleRandomnes = o.mParticleRandomnes; + mParticleRandomness = o.mParticleRandomness; mParticleBounce = o.mParticleBounce; mParticleFollow = o.mParticleFollow; mParticleTarget = o.mParticleTarget; @@ -274,10 +281,13 @@ ParticleEmitter & ParticleEmitter::operator=(const ParticleEmitter &o) mParticleAlpha = o.mParticleAlpha; mMap = o.mMap; mOutput = o.mOutput; + mOutputPause = o.mOutputPause; mParticleImage = o.mParticleImage; mParticleAnimation = o.mParticleAnimation; mParticleChildEmitters = o.mParticleChildEmitters; + mOutputPauseLeft = 0; + if (mParticleImage) mParticleImage->incRef(); return *this; @@ -308,6 +318,13 @@ ParticleEmitter::createParticles() { std::list<Particle *> newParticles; + if (mOutputPauseLeft > 0) + { + mOutputPauseLeft--; + return newParticles; + } + mOutputPauseLeft = mOutputPause.value(); + for (int i = mOutput.value(); i > 0; i--) { // Limit maximum particles @@ -342,7 +359,7 @@ ParticleEmitter::createParticles() sin(angleH) * cos(angleV) * power, sin(angleV) * power); - newParticle->setRandomnes(mParticleRandomnes.value()); + newParticle->setRandomness(mParticleRandomness.value()); newParticle->setGravity(mParticleGravity.value()); newParticle->setBounce(mParticleBounce.value()); newParticle->setFollow(mParticleFollow); diff --git a/src/particleemitter.h b/src/particleemitter.h index 481d4b44..5cf3fd46 100644 --- a/src/particleemitter.h +++ b/src/particleemitter.h @@ -18,7 +18,7 @@ * 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: particleemitter.h 4362 2008-06-24 12:29:33Z crush_tmw $ + * $Id$ */ #ifndef _PARTICLEEMITTER_H @@ -97,7 +97,7 @@ class ParticleEmitter * Vector changing of particles: */ MinMax<float> mParticleGravity; - MinMax<int> mParticleRandomnes; + MinMax<int> mParticleRandomness; MinMax<float> mParticleBounce; bool mParticleFollow; @@ -119,6 +119,8 @@ class ParticleEmitter Map *mMap; /**< Map the particles are spawned on */ MinMax<int> mOutput; /**< Number of particles spawned per update */ + MinMax<int> mOutputPause; /**< Pause in frames between two spawns */ + int mOutputPauseLeft; /* * Graphical representation of the particle diff --git a/src/player.cpp b/src/player.cpp index 125407de..8380fdfe 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -75,6 +75,18 @@ void Player::logic() { switch (mAction) { + case STAND: + break; + + case SIT: + break; + + case DEAD: + break; + + case HURT: + break; + case WALK: mFrame = (get_elapsed_time(mWalkTime) * 6) / mWalkSpeed; if (mFrame >= 6) { diff --git a/src/resources/action.cpp b/src/resources/action.cpp index facd23fb..8ed099ea 100644 --- a/src/resources/action.cpp +++ b/src/resources/action.cpp @@ -23,8 +23,6 @@ #include "action.h" -#include <algorithm> - #include "animation.h" #include "../utils/dtor.h" @@ -36,8 +34,7 @@ Action::Action() Action::~Action() { - std::for_each(mAnimations.begin(), mAnimations.end(), - make_dtor(mAnimations)); + delete_all(mAnimations); } Animation* diff --git a/src/resources/imageset.cpp b/src/resources/imageset.cpp index 6228f4e4..b7263ec3 100644 --- a/src/resources/imageset.cpp +++ b/src/resources/imageset.cpp @@ -21,8 +21,6 @@ * $Id: imageset.cpp 4209 2008-04-29 12:58:21Z b_lindeijer $ */ -#include <algorithm> - #include "imageset.h" #include "../log.h" @@ -46,11 +44,10 @@ ImageSet::ImageSet(Image *img, int width, int height) ImageSet::~ImageSet() { - for_each(mImages.begin(), mImages.end(), make_dtor(mImages)); + delete_all(mImages); } -Image* -ImageSet::get(size_type i) +Image* ImageSet::get(size_type i) const { if (i >= mImages.size()) { diff --git a/src/resources/imageset.h b/src/resources/imageset.h index 719e9769..e276dd06 100644 --- a/src/resources/imageset.h +++ b/src/resources/imageset.h @@ -47,12 +47,18 @@ class ImageSet : public Resource */ ~ImageSet(); + /** + * Returns the width of the images in the image set. + */ int getWidth() { return mWidth; }; + /** + * Returns the height of the images in the image set. + */ int getHeight() { return mHeight; }; typedef std::vector<Image*>::size_type size_type; - Image* get(size_type i); + Image* get(size_type i) const; size_type size() { return mImages.size(); } diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp index 6ae7b499..8999f651 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -21,7 +21,6 @@ * $Id: itemdb.cpp 4347 2008-06-12 09:06:01Z b_lindeijer $ */ -#include <algorithm> #include <cassert> #include <libxml/tree.h> @@ -146,7 +145,7 @@ void ItemDB::unload() delete mUnknown; mUnknown = NULL; - for_each(mItemInfos.begin(), mItemInfos.end(), make_dtor(mItemInfos)); + delete_all(mItemInfos); mItemInfos.clear(); mLoaded = false; } @@ -177,8 +176,7 @@ void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node) { itemInfo->setSprite(filename, 0); } - - if (gender == "female" || gender == "unisex") + else { itemInfo->setSprite(filename, 1); } diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp index e2b47e66..ab3b0cae 100644 --- a/src/resources/mapreader.cpp +++ b/src/resources/mapreader.cpp @@ -145,8 +145,7 @@ inflateMemory(unsigned char *in, unsigned int inLength, return outLength; } -Map* -MapReader::readMap(const std::string &filename) +Map* MapReader::readMap(const std::string &filename) { // Load the file through resource manager ResourceManager *resman = ResourceManager::getInstance(); @@ -206,19 +205,13 @@ MapReader::readMap(const std::string &filename) Map* MapReader::readMap(xmlNodePtr node, const std::string &path) { - xmlChar *prop; - // Take the filename off the path - std::string pathDir = path.substr(0, path.rfind("/") + 1); - - prop = xmlGetProp(node, BAD_CAST "version"); - xmlFree(prop); + const std::string pathDir = path.substr(0, path.rfind("/") + 1); - int w = XML::getProperty(node, "width", 0); - int h = XML::getProperty(node, "height", 0); - int tilew = XML::getProperty(node, "tilewidth", DEFAULT_TILE_WIDTH); - int tileh = XML::getProperty(node, "tileheight", DEFAULT_TILE_HEIGHT); - int layerNr = 0; + const int w = XML::getProperty(node, "width", 0); + const int h = XML::getProperty(node, "height", 0); + const int tilew = XML::getProperty(node, "tilewidth", DEFAULT_TILE_WIDTH); + const int tileh = XML::getProperty(node, "tileheight", DEFAULT_TILE_HEIGHT); Map *map = new Map(w, h, tilew, tileh); for_each_xml_child_node(childNode, node) @@ -232,9 +225,7 @@ MapReader::readMap(xmlNodePtr node, const std::string &path) } else if (xmlStrEqual(childNode->name, BAD_CAST "layer")) { - logger->log("- Loading layer %d", layerNr); - readLayer(childNode, map, layerNr); - layerNr++; + readLayer(childNode, map); } else if (xmlStrEqual(childNode->name, BAD_CAST "properties")) { @@ -246,15 +237,29 @@ MapReader::readMap(xmlNodePtr node, const std::string &path) { if (xmlStrEqual(objectNode->name, BAD_CAST "object")) { - std::string objName = XML::getProperty(objectNode, "name", ""); - std::string objType = XML::getProperty(objectNode, "type", ""); - int objX = XML::getProperty(objectNode, "x", 0); - int objY = XML::getProperty(objectNode, "y", 0); + const std::string objType = XML::getProperty(objectNode, "type", ""); + + if (objType == "WARP" || objType == "NPC" || + objType == "SCRIPT" || objType == "SPAWN") + { + // Silently skip server-side objects. + continue; + } + + const std::string objName = XML::getProperty(objectNode, "name", ""); + const int objX = XML::getProperty(objectNode, "x", 0); + const int objY = XML::getProperty(objectNode, "y", 0); logger->log("- Loading object name: %s type: %s at %d:%d", objName.c_str(), objType.c_str(), objX, objY); + if (objType == "PARTICLE_EFFECT") { + if (objName.empty()) { + logger->log(" Warning: No particle file given"); + continue; + } + map->addParticleEffect(objName, objX, objY); } else @@ -271,8 +276,7 @@ MapReader::readMap(xmlNodePtr node, const std::string &path) return map; } -void -MapReader::readProperties(xmlNodePtr node, Properties* props) +void MapReader::readProperties(xmlNodePtr node, Properties* props) { for_each_xml_child_node(childNode, node) { @@ -280,43 +284,65 @@ MapReader::readProperties(xmlNodePtr node, Properties* props) continue; // Example: <property name="name" value="value"/> - xmlChar *name = xmlGetProp(childNode, BAD_CAST "name"); - xmlChar *value = xmlGetProp(childNode, BAD_CAST "value"); + const std::string name = XML::getProperty(childNode, "name", ""); + const std::string value = XML::getProperty(childNode, "value", ""); - if (name && value) { - props->setProperty((const char*)name, (const char*)value); - } - - if (name) xmlFree(name); - if (value) xmlFree(value); + if (!name.empty() && !value.empty()) + props->setProperty(name, value); } } -void -MapReader::readLayer(xmlNodePtr node, Map *map, int layer) +static void setTile(Map *map, MapLayer *layer, int x, int y, int gid) +{ + const Tileset * const set = map->getTilesetWithGid(gid); + if (layer) { + // Set regular tile on a layer + Image * const img = set ? set->get(gid - set->getFirstGid()) : 0; + layer->setTile(x, y, img); + } else { + // Set collision tile + map->setWalk(x, y, (!set || (gid - set->getFirstGid() == 0))); + } +} + +void MapReader::readLayer(xmlNodePtr node, Map *map) { - int h = map->getHeight(); - int w = map->getWidth(); + // Layers are not necessarily the same size as the map + const int w = XML::getProperty(node, "width", map->getWidth()); + const int h = XML::getProperty(node, "height", map->getHeight()); + const int offsetX = XML::getProperty(node, "xoffset", 0); + const int offsetY = XML::getProperty(node, "yoffset", 0); + const std::string name = XML::getProperty(node, "name", ""); + + const bool isFringeLayer = (name.substr(0,6) == "Fringe"); + const bool isCollisionLayer = (name.substr(0,9) == "Collision"); + + MapLayer *layer = 0; + + if (!isCollisionLayer) { + layer = new MapLayer(offsetX, offsetY, w, h, isFringeLayer); + map->addLayer(layer); + } + + logger->log("- Loading layer \"%s\"", name.c_str()); int x = 0; int y = 0; - - // Load the tile data. Layers are assumed to be map size, with (0,0) as - // origin. + + // Load the tile data for_each_xml_child_node(childNode, node) { if (!xmlStrEqual(childNode->name, BAD_CAST "data")) - continue; - - xmlChar *encoding = xmlGetProp(childNode, BAD_CAST "encoding"); - xmlChar *compression = xmlGetProp(childNode, BAD_CAST "compression"); - - if (encoding && xmlStrEqual(encoding, BAD_CAST "base64")) + continue; + + const std::string encoding = + XML::getProperty(childNode, "encoding", ""); + const std::string compression = + XML::getProperty(childNode, "compression", ""); + + if (encoding == "base64") { - xmlFree(encoding); - - if (compression && !xmlStrEqual(compression, BAD_CAST "gzip")) { + if (!compression.empty() && compression != "gzip") { logger->log("Warning: only gzip layer compression supported!"); - xmlFree(compression); return; } @@ -324,15 +350,15 @@ MapReader::readLayer(xmlNodePtr node, Map *map, int layer) xmlNodePtr dataChild = childNode->xmlChildrenNode; if (!dataChild) continue; - + int len = strlen((const char*)dataChild->content) + 1; unsigned char *charData = new unsigned char[len + 1]; const char *charStart = (const char*)dataChild->content; unsigned char *charIndex = charData; - + while (*charStart) { if (*charStart != ' ' && *charStart != '\t' && - *charStart != '\n') + *charStart != '\n') { *charIndex = *charStart; charIndex++; @@ -348,40 +374,34 @@ MapReader::readLayer(xmlNodePtr node, Map *map, int layer) delete[] charData; if (binData) { - if (compression) { - if (xmlStrEqual(compression, BAD_CAST "gzip")) { - // Inflate the gzipped layer data - unsigned char *inflated; - unsigned int inflatedSize = - inflateMemory(binData, binLen, inflated); - - free(binData); - binData = inflated; - binLen = inflatedSize; - - if (inflated == NULL) - { - logger->log("Error: Could not decompress layer!"); - xmlFree(compression); - return; - } + if (compression == "gzip") { + // Inflate the gzipped layer data + unsigned char *inflated; + unsigned int inflatedSize = + inflateMemory(binData, binLen, inflated); + + free(binData); + binData = inflated; + binLen = inflatedSize; + + if (!inflated) { + logger->log("Error: Could not decompress layer!"); + return; } - xmlFree(compression); } for (int i = 0; i < binLen - 3; i += 4) { - int gid = binData[i] | + const int gid = binData[i] | binData[i + 1] << 8 | binData[i + 2] << 16 | binData[i + 3] << 24; - map->setTileWithGid(x, y, layer, gid); - + setTile(map, layer, x, y, gid); + x++; - if (x == w) { + if (x == w) + { x = 0; y++; - - // When we're done, don't crash on too much data if (y == h) break; } @@ -390,29 +410,29 @@ MapReader::readLayer(xmlNodePtr node, Map *map, int layer) } } else { - // Read plain XML map file - for_each_xml_child_node(childNode2, childNode) - { - if (!xmlStrEqual(childNode2->name, BAD_CAST "tile")) + // Read plain XML map file + for_each_xml_child_node(childNode2, childNode) + { + if (!xmlStrEqual(childNode2->name, BAD_CAST "tile")) continue; - - int gid = XML::getProperty(childNode2, "gid", -1); - map->setTileWithGid(x, y, layer, gid); - - x++; - if (x == w) { - x = 0; y++; - if (y >= h) - break; - } - } + + const int gid = XML::getProperty(childNode2, "gid", -1); + setTile(map, layer, x, y, gid); + + x++; + if (x == w) { + x = 0; y++; + if (y >= h) + break; + } + } } - + if (y < h) std::cerr << "TOO SMALL!\n"; if (x) std::cerr << "TOO SMALL!\n"; - + // There can be only one data element break; } @@ -429,20 +449,20 @@ MapReader::readTileset(xmlNodePtr node, return NULL; } - int firstGid = XML::getProperty(node, "firstgid", 0); - int tw = XML::getProperty(node, "tilewidth", map->getTileWidth()); - int th = XML::getProperty(node, "tileheight", map->getTileHeight()); + const int firstGid = XML::getProperty(node, "firstgid", 0); + const int tw = XML::getProperty(node, "tilewidth", map->getTileWidth()); + const int th = XML::getProperty(node, "tileheight", map->getTileHeight()); for_each_xml_child_node(childNode, node) { if (!xmlStrEqual(childNode->name, BAD_CAST "image")) continue; - xmlChar* source = xmlGetProp(childNode, BAD_CAST "source"); + const std::string source = XML::getProperty(childNode, "source", ""); - if (source) + if (!source.empty()) { - std::string sourceStr = std::string((const char*)source); + std::string sourceStr = source; sourceStr.erase(0, 3); // Remove "../" ResourceManager *resman = ResourceManager::getInstance(); @@ -452,14 +472,14 @@ MapReader::readTileset(xmlNodePtr node, { Tileset *set = new Tileset(tilebmp, tw, th, firstGid); tilebmp->decRef(); - xmlFree(source); return set; } else { - logger->log("Warning: Failed to load tileset (%s)", source); + logger->log("Warning: Failed to load tileset (%s)", source.c_str()); } } + // Only one image element expected break; } diff --git a/src/resources/mapreader.h b/src/resources/mapreader.h index 0d59fc9f..eb0d4873 100644 --- a/src/resources/mapreader.h +++ b/src/resources/mapreader.h @@ -63,10 +63,10 @@ class MapReader readProperties(xmlNodePtr node, Properties* props); /** - * Reads a map layer. + * Reads a map layer and adds it to the given map. */ static void - readLayer(xmlNodePtr node, Map *map, int layer); + readLayer(xmlNodePtr node, Map *map); /** * Reads a tile set. diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp index 847b99fe..73e9d666 100644 --- a/src/resources/monsterdb.cpp +++ b/src/resources/monsterdb.cpp @@ -21,8 +21,6 @@ * $Id: monsterdb.cpp 4255 2008-05-21 21:44:27Z crush_tmw $ */ -#include <algorithm> - #include "monsterdb.h" #include "resourcemanager.h" @@ -143,8 +141,7 @@ MonsterDB::load() void MonsterDB::unload() { - for_each(mMonsterInfos.begin(), mMonsterInfos.end(), - make_dtor(mMonsterInfos)); + delete_all(mMonsterInfos); mMonsterInfos.clear(); mLoaded = false; diff --git a/src/resources/monsterinfo.cpp b/src/resources/monsterinfo.cpp index e492ccd3..19f2990b 100644 --- a/src/resources/monsterinfo.cpp +++ b/src/resources/monsterinfo.cpp @@ -21,8 +21,6 @@ * $Id: monsterinfo.cpp 2650 2006-09-03 15:00:47Z b_lindeijer $ */ -#include <algorithm> - #include "monsterinfo.h" #include "../utils/dtor.h" @@ -35,8 +33,7 @@ MonsterInfo::MonsterInfo() MonsterInfo::~MonsterInfo() { // kill vectors in mSoundEffects - for_each (mSounds.begin(), mSounds.end(), - make_dtor(mSounds)); + delete_all(mSounds); mSounds.clear(); } diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp index 550b2d1c..ebc60240 100644 --- a/src/resources/spritedef.cpp +++ b/src/resources/spritedef.cpp @@ -18,7 +18,7 @@ * 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: spritedef.cpp 4332 2008-06-05 07:33:12Z b_lindeijer $ + * $Id$ */ #include <set> @@ -70,33 +70,8 @@ SpriteDef *SpriteDef::load(std::string const &animationFile, int variant) } } - // Get the variant - int variant_num = XML::getProperty(rootNode, "variants", 0); - int variant_offset = 0; - - if (variant_num > 0 && variant < variant_num) - { - variant_offset = variant * XML::getProperty(rootNode, "variant_offset", 0); - } - SpriteDef *def = new SpriteDef; - - for_each_xml_child_node(node, rootNode) - { - if (xmlStrEqual(node->name, BAD_CAST "imageset")) - { - def->loadImageSet(node, palettes); - } - else if (xmlStrEqual(node->name, BAD_CAST "action")) - { - def->loadAction(node, variant_offset); - } - else if (xmlStrEqual(node->name, BAD_CAST "include")) - { - def->includeSprite(node); - } - } - + def->loadSprite(rootNode, variant, palettes); def->substituteActions(); return def; } @@ -119,11 +94,47 @@ void SpriteDef::substituteActions() substituteAction(ACTION_DEAD, ACTION_HURT); } +void SpriteDef::loadSprite(xmlNodePtr spriteNode, int variant, + const std::string &palettes) +{ + // Get the variant + const int variantCount = XML::getProperty(spriteNode, "variants", 0); + int variant_offset = 0; + + if (variantCount > 0 && variant < variantCount) + { + variant_offset = + variant * XML::getProperty(spriteNode, "variant_offset", 0); + } + + for_each_xml_child_node(node, spriteNode) + { + if (xmlStrEqual(node->name, BAD_CAST "imageset")) + { + loadImageSet(node, palettes); + } + else if (xmlStrEqual(node->name, BAD_CAST "action")) + { + loadAction(node, variant_offset); + } + else if (xmlStrEqual(node->name, BAD_CAST "include")) + { + includeSprite(node); + } + } +} + void SpriteDef::loadImageSet(xmlNodePtr node, std::string const &palettes) { - int width = XML::getProperty(node, "width", 0); - int height = XML::getProperty(node, "height", 0); - std::string name = XML::getProperty(node, "name", ""); + const std::string name = XML::getProperty(node, "name", ""); + + // We don't allow redefining image sets. This way, an included sprite + // definition will use the already loaded image set with the same name. + if (mImageSets.find(name) != mImageSets.end()) + return; + + const int width = XML::getProperty(node, "width", 0); + const int height = XML::getProperty(node, "height", 0); std::string imageSrc = XML::getProperty(node, "src", ""); Dye::instantiate(imageSrc, palettes); @@ -184,9 +195,9 @@ SpriteDef::loadAnimation(xmlNodePtr animationNode, Action *action, ImageSet *imageSet, int variant_offset) { - std::string directionName = + const std::string directionName = XML::getProperty(animationNode, "direction", ""); - SpriteDirection directionType = makeSpriteDirection(directionName); + const SpriteDirection directionType = makeSpriteDirection(directionName); if (directionType == DIRECTION_INVALID) { @@ -201,7 +212,7 @@ SpriteDef::loadAnimation(xmlNodePtr animationNode, // Get animation frames for_each_xml_child_node(frameNode, animationNode) { - int delay = XML::getProperty(frameNode, "delay", 0); + const int delay = XML::getProperty(frameNode, "delay", 0); int offsetX = XML::getProperty(frameNode, "offsetX", 0); int offsetY = XML::getProperty(frameNode, "offsetY", 0); offsetY -= imageSet->getHeight() - 32; @@ -209,7 +220,7 @@ SpriteDef::loadAnimation(xmlNodePtr animationNode, if (xmlStrEqual(frameNode->name, BAD_CAST "frame")) { - int index = XML::getProperty(frameNode, "index", -1); + const int index = XML::getProperty(frameNode, "index", -1); if (index < 0) { @@ -230,7 +241,7 @@ SpriteDef::loadAnimation(xmlNodePtr animationNode, else if (xmlStrEqual(frameNode->name, BAD_CAST "sequence")) { int start = XML::getProperty(frameNode, "start", -1); - int end = XML::getProperty(frameNode, "end", -1); + const int end = XML::getProperty(frameNode, "end", -1); if (start < 0 || end < 0) { @@ -263,12 +274,23 @@ SpriteDef::loadAnimation(xmlNodePtr animationNode, void SpriteDef::includeSprite(xmlNodePtr includeNode) { + // TODO: Perform circular dependency check, since it's easy to crash the + // client this way. const std::string filename = XML::getProperty(includeNode, "file", ""); - ResourceManager *resman = ResourceManager::getInstance(); - SpriteDef *sprite = resman->getSprite("graphics/sprites/" + filename); - // TODO: Somehow implement actually including it - sprite->decRef(); + if (filename.empty()) + return; + + XML::Document doc("graphics/sprites/" + filename); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "sprite")) + { + logger->log("Error, no sprite root node in %s", filename.c_str()); + return; + } + + loadSprite(rootNode, 0); } void diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index 2872af06..72c2566f 100644 --- a/src/resources/spritedef.h +++ b/src/resources/spritedef.h @@ -18,7 +18,7 @@ * 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: spritedef.h 4255 2008-05-21 21:44:27Z crush_tmw $ + * $Id$ */ #ifndef _TMW_SPRITEDEF_H @@ -92,6 +92,12 @@ class SpriteDef : public Resource ~SpriteDef(); /** + * Loads a sprite element. + */ + void loadSprite(xmlNodePtr spriteNode, int variant, + const std::string &palettes = ""); + + /** * Loads an imageset element. */ void loadImageSet(xmlNodePtr node, std::string const &palettes); diff --git a/src/sound.h b/src/sound.h index 038f299e..ebcd6442 100644 --- a/src/sound.h +++ b/src/sound.h @@ -18,7 +18,7 @@ * 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: sound.h 3606 2007-09-27 14:54:09Z b_lindeijer $ + * $Id$ */ #ifndef _TMW_SOUND_H diff --git a/src/textparticle.cpp b/src/textparticle.cpp index c4b432f3..89466006 100644 --- a/src/textparticle.cpp +++ b/src/textparticle.cpp @@ -18,7 +18,7 @@ * 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: textparticle.cpp 4360 2008-06-23 14:44:20Z crush_tmw $ + * $Id$ */ #include "textparticle.h" diff --git a/src/tileset.h b/src/tileset.h index 6272d64a..fb5593ef 100644 --- a/src/tileset.h +++ b/src/tileset.h @@ -44,8 +44,7 @@ class Tileset : public ImageSet /** * Returns the first gid. */ - int - getFirstGid() + int getFirstGid() const { return mFirstGid; } diff --git a/src/utils/dtor.h b/src/utils/dtor.h index 3b8aeb0e..29cde178 100644 --- a/src/utils/dtor.h +++ b/src/utils/dtor.h @@ -24,6 +24,7 @@ #ifndef _TMW_UTILS_DTOR_H #define _TMW_UTILS_DTOR_H +#include <algorithm> #include <functional> #include <utility> @@ -46,4 +47,11 @@ inline dtor<typename Cont::value_type> make_dtor(Cont const&) return dtor<typename Cont::value_type>(); } +template<typename Container> +inline void delete_all(Container &c) +{ + std::for_each(c.begin(), c.end(), make_dtor(c)); +} + + #endif |