diff options
author | Philipp Sehmisch <crush@themanaworld.org> | 2009-08-16 18:25:55 +0200 |
---|---|---|
committer | Philipp Sehmisch <crush@themanaworld.org> | 2009-08-16 18:25:55 +0200 |
commit | 4263962e947b4914de8d1750f21cc7cc82567b4d (patch) | |
tree | 32340098913c302fdb33f88ffea65fb36920db75 /src | |
parent | dd0844b6ac01293e7afd0dd85e695b8d77b8e4be (diff) | |
parent | 7e2673a3b9460fdcd646e23c3bd9039de854565f (diff) | |
download | mana-4263962e947b4914de8d1750f21cc7cc82567b4d.tar.gz mana-4263962e947b4914de8d1750f21cc7cc82567b4d.tar.bz2 mana-4263962e947b4914de8d1750f21cc7cc82567b4d.tar.xz mana-4263962e947b4914de8d1750f21cc7cc82567b4d.zip |
More merges and conflict resolves
Diffstat (limited to 'src')
53 files changed, 1329 insertions, 997 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 43726f9d..eeaa7459 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,6 +74,8 @@ SET(SRCS gui/widgets/desktop.h gui/widgets/dropdown.cpp gui/widgets/dropdown.h + gui/widgets/flowcontainer.cpp + gui/widgets/flowcontainer.h gui/widgets/icon.cpp gui/widgets/icon.h gui/widgets/inttextfield.cpp @@ -221,6 +223,8 @@ SET(SRCS gui/skin.h gui/speechbubble.cpp gui/speechbubble.h + gui/specialswindow.cpp + gui/specialswindow.h gui/statuswindow.cpp gui/statuswindow.h gui/storagewindow.cpp @@ -476,8 +480,6 @@ SET(SRCS_TMW gui/guildlistbox.h gui/guildwindow.cpp gui/guildwindow.h - gui/magic.cpp - gui/magic.h gui/quitdialog.cpp gui/quitdialog.h gui/serverdialog.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 25fdb140..ff35cdd5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,6 +23,8 @@ tmw_SOURCES = gui/widgets/avatar.cpp \ gui/widgets/desktop.h \ gui/widgets/dropdown.cpp \ gui/widgets/dropdown.h \ + gui/widgets/flowcontainer.cpp \ + gui/widgets/flowcontainer.h \ gui/widgets/icon.cpp \ gui/widgets/icon.h \ gui/widgets/inttextfield.cpp \ @@ -170,6 +172,8 @@ tmw_SOURCES = gui/widgets/avatar.cpp \ gui/skin.h \ gui/speechbubble.cpp \ gui/speechbubble.h \ + gui/specialswindow.cpp \ + gui/specialswindow.h \ gui/statuswindow.cpp \ gui/statuswindow.h \ gui/storagewindow.cpp \ @@ -384,8 +388,6 @@ tmw_SOURCES += \ gui/guildlistbox.h \ gui/guildwindow.cpp \ gui/guildwindow.h \ - gui/magic.cpp \ - gui/magic.h \ gui/quitdialog.cpp \ gui/quitdialog.h \ gui/serverdialog.cpp \ diff --git a/src/being.cpp b/src/being.cpp index 23b87e6c..37ae2200 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -77,18 +77,11 @@ Being::Being(int id, int job, Map *map): mSpriteDirection(DIRECTION_DOWN), mMap(NULL), mParticleEffects(config.getValue("particleeffects", 1)), + mDispName(0), + mShowName(false), mEquippedWeapon(NULL), -#ifdef TMWSERV_SUPPORT - mHairStyle(0), -#else - mHairStyle(1), -#endif - mHairColor(0), - mGender(GENDER_UNSPECIFIED), + mText(0), mStunMode(0), - mSprites(VECTOREND_SPRITE, NULL), - mSpriteIDs(VECTOREND_SPRITE, 0), - mSpriteColors(VECTOREND_SPRITE, ""), mStatusParticleEffects(&mStunParticleEffects, false), mChildParticleEffects(&mStatusParticleEffects, false), mMustResetParticles(false), @@ -105,8 +98,8 @@ Being::Being(int id, int job, Map *map): mSpeechBubble = new SpeechBubble; - mNameColor = &guiPalette->getColor(Palette::CHAT); - mText = 0; + mNameColor = &guiPalette->getColor(Palette::NPC); + mTextColor = &guiPalette->getColor(Palette::CHAT); } Being::~Being() @@ -120,6 +113,7 @@ Being::~Being() setMap(NULL); delete mSpeechBubble; + delete mDispName; delete mText; } @@ -218,19 +212,6 @@ void Being::setPath(const Path &path) #endif } -void Being::setHairStyle(int style, int color) -{ - mHairStyle = style < 0 ? mHairStyle : style % mNumberOfHairstyles; - mHairColor = color < 0 ? mHairColor : color % ColorDB::size(); -} - -void Being::setSprite(int slot, int id, const std::string &color) -{ - assert(slot >= BASE_SPRITE && slot < VECTOREND_SPRITE); - mSpriteIDs[slot] = id; - mSpriteColors[slot] = color; -} - void Being::setSpeech(const std::string &text, int time) { mSpeech = text; @@ -368,17 +349,42 @@ void Being::handleAttack(Being *victim, int damage, AttackType type) #endif } +void Being::setName(const std::string &name) +{ + mName = name; + + if (getShowName()) + showName(); +} + +void Being::setShowName(bool doShowName) +{ + bool oldShow = mShowName; + mShowName = doShowName; + + if (doShowName != oldShow) + { + if (doShowName) + showName(); + else + { + delete mDispName; + mDispName = 0; + } + } +} + void Being::setMap(Map *map) { // Remove sprite from potential previous map if (mMap) - mMap->removeSprite(mSpriteIterator); + mMap->removeSprite(mMapSprite); mMap = map; // Add sprite to potential new map if (mMap) - mSpriteIterator = mMap->addSprite(this); + mMapSprite = mMap->addSprite(this); // Clear particle effect list because child particles became invalid mChildParticleEffects.clear(); @@ -408,11 +414,9 @@ void Being::setAction(Action action, int attackType) else currentAction = ACTION_ATTACK; - for (int i = 0; i < VECTOREND_SPRITE; i++) - { - if (mSprites[i]) - mSprites[i]->reset(); - } + for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) + if (*it) + (*it)->reset(); break; case HURT: //currentAction = ACTION_HURT; // Buggy: makes the player stop @@ -429,11 +433,9 @@ void Being::setAction(Action action, int attackType) if (currentAction != ACTION_INVALID) { - for (int i = 0; i < VECTOREND_SPRITE; i++) - { - if (mSprites[i]) - mSprites[i]->play(currentAction); - } + for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) + if (*it) + (*it)->play(currentAction); mAction = action; } } @@ -461,11 +463,9 @@ void Being::setDirection(Uint8 direction) dir = DIRECTION_LEFT; mSpriteDirection = dir; - for (int i = 0; i < VECTOREND_SPRITE; i++) - { - if (mSprites[i]) - mSprites[i]->setDirection(dir); - } + for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) + if (*it) + (*it)->setDirection(dir); } #ifdef EATHENA_SUPPORT @@ -573,11 +573,9 @@ void Being::logic() if (mUsedTargetCursor) mUsedTargetCursor->update(tick_time * 10); - for (int i = 0; i < VECTOREND_SPRITE; i++) - { - if (mSprites[i]) - mSprites[i]->update(tick_time * 10); - } + for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) + if (*it) + (*it)->update(tick_time * 10); // Restart status/particle effects, if needed if (mMustResetParticles) { @@ -605,13 +603,9 @@ void Being::draw(Graphics *graphics, int offsetX, int offsetY) const if (mUsedTargetCursor) mUsedTargetCursor->draw(graphics, px, py); - for (int i = 0; i < VECTOREND_SPRITE; i++) - { - if (mSprites[i]) - { - mSprites[i]->draw(graphics, px, py); - } - } + for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) + if (*it) + (*it)->draw(graphics, px, py); } void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) @@ -650,7 +644,7 @@ void Being::drawSpeech(int offsetX, int offsetY) mText = NULL; } - mSpeechBubble->setCaption(showName ? mName : "", mNameColor); + mSpeechBubble->setCaption(showName ? mName : "", mTextColor); mSpeechBubble->setText(mSpeech, showName); mSpeechBubble->setPosition(px - (mSpeechBubble->getWidth() / 2), @@ -680,11 +674,6 @@ void Being::drawSpeech(int offsetX, int offsetY) } } -Being::Type Being::getType() const -{ - return UNKNOWN; -} - void Being::setStatusEffectBlock(int offset, Uint16 newEffects) { for (int i = 0; i < STATUS_EFFECTS; i++) { @@ -765,7 +754,13 @@ int Being::getOffset(char pos, char neg) const int Being::getWidth() const { - if (AnimatedSprite *base = mSprites[BASE_SPRITE]) + AnimatedSprite *base = NULL; + + for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) + if ((base = (*it))) + break; + + if (base) return std::max(base->getWidth(), DEFAULT_WIDTH); else return DEFAULT_WIDTH; @@ -773,7 +768,13 @@ int Being::getWidth() const int Being::getHeight() const { - if (AnimatedSprite *base = mSprites[BASE_SPRITE]) + AnimatedSprite *base = NULL; + + for (SpriteConstIterator it = mSprites.begin(); it != mSprites.end(); it++) + if ((base = (*it))) + break; + + if (base) return std::max(base->getHeight(), DEFAULT_HEIGHT); else return DEFAULT_HEIGHT; @@ -875,9 +876,27 @@ void Being::internalTriggerEffect(int effectId, bool sfx, bool gfx) } } -int Being::getHairStyleCount() +void Being::updateCoords() { - return mNumberOfHairstyles; + if (mDispName) + { + mDispName->adviseXY(getPixelX(), getPixelY()); + } +} + +void Being::flashName(int time) +{ + if (mDispName) + mDispName->flash(time); +} + +void Being::showName() +{ + delete mDispName; + mDispName = 0; + + mDispName = new FlashText(mName, getPixelX(), getPixelY(), + gcn::Graphics::CENTER, mNameColor); } void Being::load() diff --git a/src/being.h b/src/being.h index 6e90b39d..ad7f3459 100644 --- a/src/being.h +++ b/src/being.h @@ -46,11 +46,11 @@ #define SPEECH_MAX_TIME 1000 class AnimatedSprite; +class FlashText; +class Graphics; class Image; class ItemInfo; class Item; -class Map; -class Graphics; class Particle; class Position; class SimpleAnimation; @@ -59,16 +59,6 @@ class Text; class StatusEffect; -typedef std::list<Sprite*> Sprites; -typedef Sprites::iterator SpriteIterator; - -enum Gender -{ - GENDER_MALE = 0, - GENDER_FEMALE = 1, - GENDER_UNSPECIFIED = 2 -}; - class Being : public Sprite, public ConfigListener { public: @@ -95,29 +85,6 @@ class Being : public Sprite, public ConfigListener HURT }; - enum Sprite - { - BASE_SPRITE = 0, - SHOE_SPRITE, - BOTTOMCLOTHES_SPRITE, - TOPCLOTHES_SPRITE, -#ifdef EATHENA_SUPPORT - MISC1_SPRITE, - MISC2_SPRITE, -#endif - HAIR_SPRITE, - HAT_SPRITE, -#ifdef EATHENA_SUPPORT - CAPE_SPRITE, - GLOVES_SPRITE, -#endif - WEAPON_SPRITE, -#ifdef EATHENA_SUPPORT - SHIELD_SPRITE, -#endif - VECTOREND_SPRITE - }; - enum TargetCursorSize { TC_SMALL = 0, @@ -246,42 +213,17 @@ class Being : public Sprite, public ConfigListener * * @param name The name that should appear. */ - virtual void setName(const std::string &name) - { mName = name; } + virtual void setName(const std::string &name); - /** - * Gets the hair color for this being. - */ - int getHairColor() const { return mHairColor; } + const bool getShowName() const { return mShowName; } - /** - * Gets the hair style for this being. - */ - int getHairStyle() const { return mHairStyle; } + virtual void setShowName(bool doShowName); /** * Get the number of hairstyles implemented */ static int getNumOfHairstyles() { return mNumberOfHairstyles; } - /** - * Sets the hair style and color for this being. - */ - virtual void setHairStyle(int style, int color); - - /** - * Sets visible equipments for this being. - */ - virtual void setSprite(int slot, int id, - const std::string &color = ""); - - /** - * Sets the gender of this being. - */ - virtual void setGender(Gender gender) { mGender = gender; } - - Gender getGender() const { return mGender; } - #ifdef EATHENA_SUPPORT /** * Makes this being take the next step of his path. @@ -307,7 +249,7 @@ class Being : public Sprite, public ConfigListener /** * Returns the type of the being. */ - virtual Type getType() const; + virtual Type getType() const { return UNKNOWN; } /** * Sets the walk speed (in pixels per second). @@ -496,14 +438,14 @@ class Being : public Sprite, public ConfigListener internalTriggerEffect(effectId, false, true); } - static int getHairStyleCount(); - virtual AnimatedSprite *getSprite(int index) const { return mSprites[index]; } static void load(); - void optionChanged(const std::string &value) {} + virtual void optionChanged(const std::string &value) {} + + void flashName(int time); protected: /** @@ -512,9 +454,9 @@ class Being : public Sprite, public ConfigListener void setPath(const Path &path); /** - * Let the sub-classes react to a replacement. + * Updates name's location. */ - virtual void updateCoords() {} + virtual void updateCoords(); /** * Gets the way the object blocks pathfinding for other objects @@ -551,14 +493,23 @@ class Being : public Sprite, public ConfigListener */ virtual void handleStatusEffect(StatusEffect *effect, int effectId); + virtual void showName(); + int mId; /**< Unique sprite id */ Uint8 mDirection; /**< Facing direction */ Uint8 mSpriteDirection; /**< Facing direction */ Map *mMap; /**< Map on which this being resides */ std::string mName; /**< Name of character */ - SpriteIterator mSpriteIterator; + MapSprite mMapSprite; bool mParticleEffects; /**< Whether to display particles or not */ + /** + * Holds a text object when the being displays it's name, 0 otherwise + */ + FlashText *mDispName; + const gcn::Color *mNameColor; + bool mShowName; + /** Engine-related infos about weapon. */ const ItemInfo *mEquippedWeapon; @@ -567,17 +518,14 @@ class Being : public Sprite, public ConfigListener Path mPath; std::string mSpeech; Text *mText; - int mHairStyle; - int mHairColor; - Gender mGender; + const gcn::Color *mTextColor; Uint16 mStunMode; /**< Stun mode; zero if not stunned */ std::set<int> mStatusEffects; /**< set of active status effects */ - const gcn::Color *mNameColor; - - std::vector<AnimatedSprite*> mSprites; - std::vector<int> mSpriteIDs; - std::vector<std::string> mSpriteColors; + typedef std::vector<AnimatedSprite*> Sprites; + typedef Sprites::iterator SpriteIterator; + typedef Sprites::const_iterator SpriteConstIterator; + Sprites mSprites; ParticleList mStunParticleEffects; ParticleVector mStatusParticleEffects; ParticleList mChildParticleEffects; diff --git a/src/flooritem.cpp b/src/flooritem.cpp index b08258c1..9376dd73 100644 --- a/src/flooritem.cpp +++ b/src/flooritem.cpp @@ -41,13 +41,13 @@ FloorItem::FloorItem(int id, mItem = new Item(itemId); // Add ourselves to the map - mSpriteIterator = mMap->addSprite(this); + mMapSprite = mMap->addSprite(this); } FloorItem::~FloorItem() { // Remove ourselves from the map - mMap->removeSprite(mSpriteIterator); + mMap->removeSprite(mMapSprite); delete mItem; } diff --git a/src/flooritem.h b/src/flooritem.h index 99074943..61e88a70 100644 --- a/src/flooritem.h +++ b/src/flooritem.h @@ -24,14 +24,12 @@ #include <list> +#include "map.h" #include "sprite.h" class Graphics; class Image; class Item; -class Map; - -typedef std::list<Sprite*> Sprites; /** * An item lying on the floor. @@ -100,7 +98,7 @@ class FloorItem : public Sprite int mId; int mX, mY; Item *mItem; - Sprites::iterator mSpriteIterator; + MapSprite mMapSprite; Map *mMap; }; diff --git a/src/game.cpp b/src/game.cpp index ff0d84f7..2acb8bc2 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -71,10 +71,10 @@ #ifdef TMWSERV_SUPPORT #include "gui/buddywindow.h" #include "gui/guildwindow.h" -#include "gui/magic.h" #include "gui/quitdialog.h" #endif #include "gui/npcpostdialog.h" +#include "gui/specialswindow.h" #include "gui/storagewindow.h" #include "net/generalhandler.h" @@ -128,7 +128,6 @@ PartyWindow *partyWindow; #ifdef TMWSERV_SUPPORT BuddyWindow *buddyWindow; GuildWindow *guildWindow; -MagicDialog *magicDialog; #endif NpcDialog *npcDialog; NpcPostDialog *npcPostDialog; @@ -141,6 +140,7 @@ DebugWindow *debugWindow; ShortcutWindow *itemShortcutWindow; ShortcutWindow *emoteShortcutWindow; OutfitWindow *outfitWindow; +SpecialsWindow *specialsWindow; BeingManager *beingManager = NULL; FloorItemManager *floorItemManager = NULL; @@ -215,7 +215,6 @@ static void createGuiWindows() tradeWindow = new TradeWindow; partyWindow = new PartyWindow; #ifdef TMWSERV_SUPPORT - magicDialog = new MagicDialog; buddyWindow = new BuddyWindow; guildWindow = new GuildWindow; #else @@ -237,6 +236,7 @@ static void createGuiWindows() emoteShortcutWindow = new ShortcutWindow("EmoteShortcut", new EmoteShortcutContainer); outfitWindow = new OutfitWindow(); + specialsWindow = new SpecialsWindow(); localChatTab = new ChatTab(_("General")); @@ -269,7 +269,6 @@ static void destroyGuiWindows() delete npcDialog; delete npcPostDialog; #ifdef TMWSERV_SUPPORT - delete magicDialog; delete buddyWindow; delete guildWindow; #endif @@ -283,6 +282,7 @@ static void destroyGuiWindows() delete emoteShortcutWindow; delete storageWindow; delete outfitWindow; + delete specialsWindow; } Game::Game(): diff --git a/src/graphics.cpp b/src/graphics.cpp index 75db11f4..14a2b852 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -117,7 +117,10 @@ int Graphics::getHeight() const bool Graphics::drawImage(Image *image, int x, int y) { - return drawImage(image, 0, 0, x, y, image->mBounds.w, image->mBounds.h); + if (image) + return drawImage(image, 0, 0, x, y, image->mBounds.w, image->mBounds.h); + else + return false; } bool Graphics::drawRescaledImage(Image *image, int srcX, int srcY, @@ -128,12 +131,13 @@ bool Graphics::drawRescaledImage(Image *image, int srcX, int srcY, { // Check that preconditions for blitting are met. if (!mScreen || !image) return false; - if (!image->mImage) return false; + if (!image->mSDLSurface) return false; Image *tmpImage = image->SDLgetScaledImage(desiredWidth, desiredHeight); bool returnValue = false; + if (!tmpImage) return false; - if (!tmpImage->mImage) return false; + if (!tmpImage->mSDLSurface) return false; dstX += mClipStack.top().xOffset; dstY += mClipStack.top().yOffset; @@ -148,7 +152,7 @@ bool Graphics::drawRescaledImage(Image *image, int srcX, int srcY, srcRect.w = width; srcRect.h = height; - returnValue = !(SDL_BlitSurface(tmpImage->mImage, &srcRect, mScreen, &dstRect) < 0); + returnValue = !(SDL_BlitSurface(tmpImage->mSDLSurface, &srcRect, mScreen, &dstRect) < 0); delete tmpImage; @@ -160,7 +164,7 @@ bool Graphics::drawImage(Image *image, int srcX, int srcY, int dstX, int dstY, { // Check that preconditions for blitting are met. if (!mScreen || !image) return false; - if (!image->mImage) return false; + if (!image->mSDLSurface) return false; dstX += mClipStack.top().xOffset; dstY += mClipStack.top().yOffset; @@ -175,7 +179,7 @@ bool Graphics::drawImage(Image *image, int srcX, int srcY, int dstX, int dstY, srcRect.w = width; srcRect.h = height; - return !(SDL_BlitSurface(image->mImage, &srcRect, mScreen, &dstRect) < 0); + return !(SDL_BlitSurface(image->mSDLSurface, &srcRect, mScreen, &dstRect) < 0); } void Graphics::drawImage(gcn::Image const *image, int srcX, int srcY, @@ -191,12 +195,12 @@ void Graphics::drawImagePattern(Image *image, int x, int y, int w, int h) { // Check that preconditions for blitting are met. if (!mScreen || !image) return; - if (!image->mImage) return; + if (!image->mSDLSurface) return; const int iw = image->getWidth(); const int ih = image->getHeight(); - - if (iw == 0 || ih == 0) return; + + if (iw == 0 || ih == 0) return; for (int py = 0; py < h; py += ih) // Y position on pattern plane { @@ -204,7 +208,7 @@ void Graphics::drawImagePattern(Image *image, int x, int y, int w, int h) int srcY = image->mBounds.y; int dstY = y + py + mClipStack.top().yOffset; - for (int px = 0; px < w; px += iw) // X position on pattern plane + for (int px = 0; px < w; px += iw) // X position on pattern plane { int dw = (px + iw >= w) ? w - px : iw; int srcX = image->mBounds.x; @@ -216,7 +220,7 @@ void Graphics::drawImagePattern(Image *image, int x, int y, int w, int h) srcRect.x = srcX; srcRect.y = srcY; srcRect.w = dw; srcRect.h = dh; - SDL_BlitSurface(image->mImage, &srcRect, mScreen, &dstRect); + SDL_BlitSurface(image->mSDLSurface, &srcRect, mScreen, &dstRect); } } } @@ -226,7 +230,7 @@ void Graphics::drawRescaledImagePattern(Image *image, int x, int y, { // Check that preconditions for blitting are met. if (!mScreen || !image) return; - if (!image->mImage) return; + if (!image->mSDLSurface) return; if (scaledHeight == 0 || scaledWidth == 0) return; @@ -235,8 +239,8 @@ void Graphics::drawRescaledImagePattern(Image *image, int x, int y, const int iw = tmpImage->getWidth(); const int ih = tmpImage->getHeight(); - - if (iw == 0 || ih == 0) return; + + if (iw == 0 || ih == 0) return; for (int py = 0; py < h; py += ih) // Y position on pattern plane { @@ -244,7 +248,7 @@ void Graphics::drawRescaledImagePattern(Image *image, int x, int y, int srcY = tmpImage->mBounds.y; int dstY = y + py + mClipStack.top().yOffset; - for (int px = 0; px < w; px += iw) // X position on pattern plane + for (int px = 0; px < w; px += iw) // X position on pattern plane { int dw = (px + iw >= w) ? w - px : iw; int srcX = tmpImage->mBounds.x; @@ -256,7 +260,7 @@ void Graphics::drawRescaledImagePattern(Image *image, int x, int y, srcRect.x = srcX; srcRect.y = srcY; srcRect.w = dw; srcRect.h = dh; - SDL_BlitSurface(tmpImage->mImage, &srcRect, mScreen, &dstRect); + SDL_BlitSurface(tmpImage->mSDLSurface, &srcRect, mScreen, &dstRect); } } diff --git a/src/gui/charcreatedialog.cpp b/src/gui/charcreatedialog.cpp index e09eee55..7c2b0ed9 100644 --- a/src/gui/charcreatedialog.cpp +++ b/src/gui/charcreatedialog.cpp @@ -59,8 +59,9 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot): int numberOfHairColors = ColorDB::size(); - mPlayer->setHairStyle(rand() % mPlayer->getNumOfHairstyles(), - rand() % numberOfHairColors); + mHairStyle = rand() % mPlayer->getNumOfHairstyles(); + mHairColor = rand() % numberOfHairColors; + updateHair(); mNameField = new TextField(""); mNameLabel = new Label(_("Name:")); @@ -153,7 +154,6 @@ CharCreateDialog::~CharCreateDialog() void CharCreateDialog::action(const gcn::ActionEvent &event) { - int numberOfColors = ColorDB::size(); if (event.getId() == "create") { if (getName().length() >= 4) @@ -168,8 +168,8 @@ void CharCreateDialog::action(const gcn::ActionEvent &event) } Net::getCharHandler()->newCharacter(getName(), mSlot, - mFemale->isSelected(), mPlayer->getHairStyle(), - mPlayer->getHairColor(), atts); + mFemale->isSelected(), mHairStyle, + mHairColor, atts); } else { @@ -181,19 +181,25 @@ void CharCreateDialog::action(const gcn::ActionEvent &event) else if (event.getId() == "cancel") scheduleDelete(); else if (event.getId() == "nextcolor") - mPlayer->setHairStyle(mPlayer->getHairStyle(), - (mPlayer->getHairColor() + 1) % numberOfColors); + { + mHairColor++; + updateHair(); + } else if (event.getId() == "prevcolor") - mPlayer->setHairStyle(mPlayer->getHairStyle(), - (mPlayer->getHairColor() + numberOfColors - 1) % - numberOfColors); + { + mHairColor--; + updateHair(); + } else if (event.getId() == "nextstyle") - mPlayer->setHairStyle(mPlayer->getHairStyle() + 1, - mPlayer->getHairColor()); + { + mHairStyle++; + updateHair(); + } else if (event.getId() == "prevstyle") - mPlayer->setHairStyle(mPlayer->getHairStyle() + - mPlayer->getNumOfHairstyles() - 1, - mPlayer->getHairColor()); + { + mHairStyle--; + updateHair(); + } else if (event.getId() == "statslider") { updateSliders(); } @@ -334,3 +340,13 @@ void CharCreateDialog::setFixedGender(bool fixed, Gender gender) mFemale->setEnabled(false); } } + +void CharCreateDialog::updateHair() +{ + mHairStyle %= Being::getNumOfHairstyles(); + + mHairColor %= ColorDB::size(); + + mPlayer->setSprite(Player::HAIR_SPRITE, + mHairStyle * -1, ColorDB::get(mHairColor)); +} diff --git a/src/gui/charcreatedialog.h b/src/gui/charcreatedialog.h index 5dbc6050..a30aadd3 100644 --- a/src/gui/charcreatedialog.h +++ b/src/gui/charcreatedialog.h @@ -22,7 +22,7 @@ #ifndef CHAR_CREATE_H #define CHAR_CREATE_H -#include "being.h" +#include "player.h" #include "guichanfwd.h" #include "lockedarray.h" @@ -34,7 +34,6 @@ #include <vector> class LocalPlayer; -class Player; class PlayerBox; /** @@ -82,6 +81,8 @@ class CharCreateDialog : public Window, public gcn::ActionListener */ void attemptCharCreate(); + void updateHair(); + gcn::TextField *mNameField; gcn::Label *mNameLabel; gcn::Button *mNextHairColorButton; @@ -108,6 +109,9 @@ class CharCreateDialog : public Window, public gcn::ActionListener Player *mPlayer; PlayerBox *mPlayerBox; + int mHairStyle; + int mHairColor; + int mSlot; }; diff --git a/src/gui/charselectdialog.h b/src/gui/charselectdialog.h index 06ce4b55..4427017e 100644 --- a/src/gui/charselectdialog.h +++ b/src/gui/charselectdialog.h @@ -24,7 +24,7 @@ #include "gui/widgets/window.h" -#include "being.h" +#include "player.h" #include "guichanfwd.h" #include "lockedarray.h" @@ -32,7 +32,6 @@ class LocalPlayer; class LoginData; -class Player; class PlayerBox; /** diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp index c337d33b..f1814d93 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -98,8 +98,8 @@ ChatWindow::ChatWindow(): mChatTabs = new TabbedArea; - add(mChatTabs); - add(mChatInput); + place(0, 0, mChatTabs, 3, 3); + place(0, 3, mChatInput, 3); loadWindowState(); @@ -126,40 +126,6 @@ void ChatWindow::resetToDefaultSize() Window::resetToDefaultSize(); } -void ChatWindow::adjustTabSize() -{ - const gcn::Rectangle area = getChildrenArea(); - - mChatInput->setPosition(mChatInput->getFrameSize(), - area.height - mChatInput->getHeight() - - mChatInput->getFrameSize()); - mChatInput->setWidth(area.width - 2 * mChatInput->getFrameSize()); - - mChatTabs->setWidth(area.width - 2 * mChatTabs->getFrameSize()); - mChatTabs->setHeight(area.height - 2 * mChatTabs->getFrameSize() - - (mChatInput->getHeight() + mChatInput->getFrameSize() * 2)); - - ChatTab *tab = getFocused(); - if (tab) { - gcn::Widget *content = tab->mScrollArea; - bool scrollLock = false; - if(tab->mScrollArea->getVerticalMaxScroll() == tab->mScrollArea->getVerticalScrollAmount()) - scrollLock = true; - content->setSize(mChatTabs->getWidth() - 2 * content->getFrameSize(), - mChatTabs->getContainerHeight() - 2 * content->getFrameSize()); - content->logic(); - if(scrollLock) - tab->mScrollArea->setVerticalScrollAmount(tab->mScrollArea->getVerticalMaxScroll()); - } -} - -void ChatWindow::widgetResized(const gcn::Event &event) -{ - Window::widgetResized(event); - - adjustTabSize(); -} - void ChatWindow::logic() { Window::logic(); @@ -167,7 +133,6 @@ void ChatWindow::logic() Tab *tab = getFocused(); if (tab != mCurrentTab) { mCurrentTab = tab; - adjustTabSize(); } } @@ -291,10 +256,6 @@ void ChatWindow::addTab(ChatTab *tab) mChatTabs->addTab(tab, tab->mScrollArea); - // Fix for layout issues when adding the first tab - if (tab == localChatTab) - adjustTabSize(); - // Update UI logic(); } diff --git a/src/gui/chat.h b/src/gui/chat.h index 2de3a634..af5f760b 100644 --- a/src/gui/chat.h +++ b/src/gui/chat.h @@ -74,12 +74,6 @@ class ChatWindow : public Window, */ ~ChatWindow(); - /** - * Called when the widget changes size. Used for adapting the size of - * the tabbed area. - */ - void widgetResized(const gcn::Event &event); - void logic(); /** @@ -193,8 +187,6 @@ class ChatWindow : public Window, void removeWhisper(const std::string &nick); - void adjustTabSize(); - /** Used for showing item popup on clicking links **/ ItemLinkHandler *mItemLinkHandler; Recorder *mRecorder; diff --git a/src/gui/magic.cpp b/src/gui/magic.cpp deleted file mode 100644 index 6e314656..00000000 --- a/src/gui/magic.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * The Mana World - * Copyright (C) 2004 The Mana World Development Team - * - * This file is part of The Mana World. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "gui/magic.h" - -#include "gui/widgets/button.h" -#include "gui/setup.h" - -#include "localplayer.h" - -#include "utils/dtor.h" -#include "utils/gettext.h" - -MagicDialog::MagicDialog(): - Window(_("Magic")) -{ - setWindowName("Magic"); - setCloseButton(true); - setSaveVisible(true); - setDefaultSize(255, 30, 175, 225); - setupWindow->registerWindowForReset(this); - - mSpellButtons.resize(4); - - mSpellButtons[1] = new Button(_("Spell 1"), "spell_1", this); - mSpellButtons[2] = new Button(_("Spell 2"), "spell_2", this); - mSpellButtons[3] = new Button(_("Spell 3"), "spell_3", this); - - mSpellButtons[1]->setPosition(10, 60); - mSpellButtons[2]->setPosition(10, 90); - mSpellButtons[3]->setPosition(10, 120); - - add(mSpellButtons[1]); - add(mSpellButtons[2]); - add(mSpellButtons[3]); - - update(); - - setLocationRelativeTo(getParent()); - loadWindowState(); -} - -MagicDialog::~MagicDialog() -{ -} - -void MagicDialog::action(const gcn::ActionEvent &event) -{ - if (event.getId() == "spell_1") - { - player_node->useSpecial(1); - } - if (event.getId() == "spell_2") - { - player_node->useSpecial(2); - } - if (event.getId() == "spell_3") - { - player_node->useSpecial(3); - } - else if (event.getId() == "close") - { - setVisible(false); - } -} - -void MagicDialog::draw(gcn::Graphics *g) -{ - update(); - - Window::draw(g); -} - -void MagicDialog::update() -{ - std::map<int, Special> specials = player_node->getSpecialStatus(); - - for (size_t i = 1; i < mSpellButtons.size(); i++) - { - if (specials.find(i) != specials.end()) - { - std::stringstream s; - s << - "Spell" << - i << - " (" << - specials[i].currentMana << - "/" << - specials[i].neededMana << - ")"; - mSpellButtons[i]->setCaption(s.str()); - mSpellButtons[i]->adjustSize(); - } - } -} diff --git a/src/gui/playerbox.cpp b/src/gui/playerbox.cpp index d00194bd..9a1f4805 100644 --- a/src/gui/playerbox.cpp +++ b/src/gui/playerbox.cpp @@ -82,11 +82,11 @@ void PlayerBox::draw(gcn::Graphics *graphics) { // Draw character const int bs = getFrameSize(); -#ifdef TMWSERV_SUPPORT +//#ifdef TMWSERV_SUPPORT const int x = getWidth() / 2 + bs; const int y = getHeight() - bs - 8; mPlayer->draw(static_cast<Graphics*>(graphics), x, y); -#else +/*#else const int x = getWidth() / 2 - 16 + bs; const int y = getHeight() / 2 + bs; for (int i = 0; i < Being::VECTOREND_SPRITE; i++) @@ -96,7 +96,7 @@ void PlayerBox::draw(gcn::Graphics *graphics) mPlayer->getSprite(i)->draw(static_cast<Graphics*>(graphics), x, y); } } -#endif +#endif*/ } if (config.getValue("guialpha", 0.8) != mAlpha) diff --git a/src/gui/skilldialog.cpp b/src/gui/skilldialog.cpp index 14e0e5a4..6e7b3e70 100644 --- a/src/gui/skilldialog.cpp +++ b/src/gui/skilldialog.cpp @@ -47,7 +47,6 @@ #include "utils/xml.h" #include <string> -#include <vector> class SkillEntry; @@ -96,7 +95,7 @@ SkillDialog::SkillDialog(): mPointsLabel = new Label("0"); place(0, 0, mTabs, 5, 5); - place(0, 5, mPointsLabel); + place(0, 5, mPointsLabel, 2); setLocationRelativeTo(getParent()); loadWindowState(); @@ -104,7 +103,8 @@ SkillDialog::SkillDialog(): SkillDialog::~SkillDialog() { - //delete_all(mTabs); + // Clear gui + loadSkills(""); } void SkillDialog::action(const gcn::ActionEvent &event) @@ -122,36 +122,6 @@ void SkillDialog::action(const gcn::ActionEvent &event) } } -void SkillDialog::adjustTabSize() -{ - gcn::Widget *content = mTabs->getCurrentWidget(); - if (content) { - int width = mTabs->getWidth() - 2 * content->getFrameSize() - 2 * mTabs->getFrameSize(); - int height = mTabs->getContainerHeight() - 2 * content->getFrameSize(); - content->setSize(width, height); - content->setVisible(true); - content->logic(); - } -} - -void SkillDialog::widgetResized(const gcn::Event &event) -{ - Window::widgetResized(event); - - adjustTabSize(); -} - -void SkillDialog::logic() -{ - Window::logic(); - - Tab *tab = dynamic_cast<Tab*>(mTabs->getSelectedTab()); - if (tab != mCurrentTab) { - mCurrentTab = tab; - adjustTabSize(); - } -} - std::string SkillDialog::update(int id) { SkillMap::iterator i = mSkills.find(id); @@ -182,9 +152,21 @@ void SkillDialog::update() void SkillDialog::loadSkills(const std::string &file) { // TODO: mTabs->clear(); + while (mTabs->getSelectedTabIndex() != -1) + { + mTabs->removeTabWithIndex(mTabs->getSelectedTabIndex()); + } + + for (SkillMap::iterator it = mSkills.begin(); it != mSkills.end(); it++) + { + delete (*it).second->display; + } delete_all(mSkills); mSkills.clear(); + if (file.length() == 0) + return; + XML::Document doc(file); xmlNodePtr root = doc.rootNode(); @@ -236,8 +218,6 @@ void SkillDialog::loadSkills(const std::string &file) } } } - - adjustTabSize(); update(); } diff --git a/src/gui/skilldialog.h b/src/gui/skilldialog.h index ce8f091a..12cb3581 100644 --- a/src/gui/skilldialog.h +++ b/src/gui/skilldialog.h @@ -28,7 +28,6 @@ #include <guichan/actionlistener.hpp> -#include <list> #include <map> class Label; @@ -56,14 +55,6 @@ class SkillDialog : public Window, public gcn::ActionListener void action(const gcn::ActionEvent &event); /** - * Called when the widget changes size. Used for adapting the size of - * the tabbed area. - */ - void widgetResized(const gcn::Event &event); - - void logic(); - - /** * Update the given skill's display */ std::string update(int id); @@ -78,11 +69,8 @@ class SkillDialog : public Window, public gcn::ActionListener void setModifiable(int id, bool modifiable); private: - void adjustTabSize(); - typedef std::map<int, SkillInfo*> SkillMap; SkillMap mSkills; - Tab *mCurrentTab; TabbedArea *mTabs; Label *mPointsLabel; }; diff --git a/src/gui/specialswindow.cpp b/src/gui/specialswindow.cpp new file mode 100644 index 00000000..35839b39 --- /dev/null +++ b/src/gui/specialswindow.cpp @@ -0,0 +1,244 @@ +/* + * The Mana World + * Copyright (C) 2009 The Mana World Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "gui/specialswindow.h" + +#include "gui/widgets/button.h" +#include "gui/widgets/container.h" +#include "gui/widgets/icon.h" +#include "gui/widgets/label.h" +#include "gui/widgets/layouthelper.h" +#include "gui/widgets/listbox.h" +#include "gui/widgets/progressbar.h" +#include "gui/widgets/scrollarea.h" +#include "gui/widgets/tab.h" +#include "gui/widgets/tabbedarea.h" +#include "gui/widgets/flowcontainer.h" +#include "gui/widgets/windowcontainer.h" +#include "gui/setup.h" + +#include "localplayer.h" +#include "log.h" + +#include "net/net.h" +#include "net/specialhandler.h" + +#include "utils/dtor.h" +#include "utils/gettext.h" +#include "utils/stringutils.h" +#include "utils/xml.h" + +#include <string> + +#define SPECIALS_WIDTH 200 +#define SPECIALS_HEIGHT 32 + +class SpecialEntry; + +struct SpecialInfo +{ + unsigned short id; + std::string name; + std::string icon; + SpecialEntry *display; +}; + +class SpecialEntry : public Container +{ + public: + SpecialEntry(SpecialInfo *info); + + void update(); + + protected: + friend class SpecialsWindow; + SpecialInfo *mInfo; + + private: + Icon *mIcon; + Label *mNameLabel; + Label *mLevelLabel; + Label *mTechLabel; + Button *mUse; +}; + +SpecialsWindow::SpecialsWindow(): + Window(_("Specials")) +{ + setWindowName("Specials"); + setCloseButton(true); + setResizable(true); + setSaveVisible(true); + setDefaultSize(windowContainer->getWidth() - 280, 30, 275, 425); + setupWindow->registerWindowForReset(this); + + mTabs = new TabbedArea(); + + place(0, 0, mTabs, 5, 5); + + setLocationRelativeTo(getParent()); + loadWindowState(); +} + +SpecialsWindow::~SpecialsWindow() +{ + // Clear gui + loadSpecials(""); +} + +void SpecialsWindow::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "use") + { + SpecialEntry *disp = dynamic_cast<SpecialEntry*>(event.getSource()->getParent()); + + if (disp) + { + /*Being *target = player_node->getTarget(); + + if (target) + Net::getSpecialHandler()->use(disp->mInfo->id, 1, target->getId()); + else*/ + Net::getSpecialHandler()->use(disp->mInfo->id); + } + } + else if (event.getId() == "close") + { + setVisible(false); + } +} + +std::string SpecialsWindow::update(int id) +{ + // TODO + + return std::string(); +} + +void SpecialsWindow::loadSpecials(const std::string &file) +{ + // TODO: mTabs->clear(); + while (mTabs->getSelectedTabIndex() != -1) + { + mTabs->removeTabWithIndex(mTabs->getSelectedTabIndex()); + } + + for (SpecialMap::iterator it = mSpecials.begin(); it != mSpecials.end(); it++) + { + delete (*it).second->display; + } + delete_all(mSpecials); + mSpecials.clear(); + + if (file.length() == 0) + return; + + XML::Document doc(file); + xmlNodePtr root = doc.rootNode(); + + if (!root || !xmlStrEqual(root->name, BAD_CAST "specials")) + { + logger->log("Error loading specials file: %s", file.c_str()); + return; + } + + int setCount = 0; + std::string setName; + ScrollArea *scroll; + FlowContainer *container; + + for_each_xml_child_node(set, root) + { + if (xmlStrEqual(set->name, BAD_CAST "set")) + { + setCount++; + setName = XML::getProperty(set, "name", strprintf(_("Specials Set %d"), setCount)); + + container = new FlowContainer(SPECIALS_WIDTH, SPECIALS_HEIGHT); + container->setOpaque(false); + scroll = new ScrollArea(container); + scroll->setOpaque(false); + scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER); + scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS); + + mTabs->addTab(setName, scroll); + for_each_xml_child_node(node, set) + { + if (xmlStrEqual(node->name, BAD_CAST "special")) + { + int id = atoi(XML::getProperty(node, "id", "-1").c_str()); + if (id == -1) + continue; + std::string name = XML::getProperty(node, "name", strprintf(_("Special %d"), id)); + std::string icon = XML::getProperty(node, "icon", ""); + + SpecialInfo *special = new SpecialInfo; + special->id = id; + special->name = name; + special->icon = icon; + special->display = new SpecialEntry(special); + + container->add(special->display); + + mSpecials[id] = special; + } + } + } + } +} + +SpecialEntry::SpecialEntry(SpecialInfo *info) : + mInfo(info), + mIcon(NULL), + mNameLabel(new Label(info->name)), + mLevelLabel(new Label("999")), + mUse(new Button("Use", "use", specialsWindow)) +{ + setFrameSize(1); + setOpaque(false); + setSize(SPECIALS_WIDTH, SPECIALS_HEIGHT); + + if (!info->icon.empty()) + mIcon = new Icon(info->icon); + else + mIcon = new Icon("graphics/gui/unknown-item.png"); + + mIcon->setPosition(1, 0); + add(mIcon); + + mNameLabel->setPosition(35, 0); + add(mNameLabel); + + mLevelLabel->setPosition(getWidth() - mLevelLabel->getWidth(), 0); + add(mLevelLabel); + + mNameLabel->setWidth(mLevelLabel->getX() - mNameLabel->getX() - 1); + + mUse->setPosition(getWidth() - mUse->getWidth(), 13); + add(mUse); + + update(); +} + +void SpecialEntry::update() +{ + // TODO +} diff --git a/src/gui/magic.h b/src/gui/specialswindow.h index 44a1a6fc..cd92c065 100644 --- a/src/gui/magic.h +++ b/src/gui/specialswindow.h @@ -1,6 +1,6 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright (C) 2009 The Mana World Development Team * * This file is part of The Mana World. * @@ -19,8 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MAGIC_H -#define MAGIC_H +#ifndef SPECIALSWINDOW_H +#define SPECIALSWINDOW_H #include <vector> @@ -30,43 +30,41 @@ #include <guichan/actionlistener.hpp> -/** - * The magic interface. - * - * This window is hacked together quickly to test the spell - * recharge netcode. - * It does in no way represent how the interface is going to - * look in the final version. Optimization / cleanup is - * pointless, as it will be redesigned from scratch. - * - * \ingroup Interface - */ -class MagicDialog : public Window, public gcn::ActionListener -{ + +#include <map> + +class Label; +class ScrollArea; +class Tab; +class TabbedArea; + +struct SpecialInfo; + +class SpecialsWindow : public Window, public gcn::ActionListener { public: - MagicDialog(); + SpecialsWindow(); - ~MagicDialog(); + ~SpecialsWindow(); /** * Called when receiving actions from widget. */ - void action(const gcn::ActionEvent &event); + void action(const gcn::ActionEvent &actionEvent); /** - * Update the tabs in this dialog + * Update the given special's display */ - void update(); + std::string update(int id); - /** - * Draw this window. - */ - void draw(gcn::Graphics *g); + void loadSpecials(const std::string &file); private: std::vector<gcn::Button *> mSpellButtons; + typedef std::map<int, SpecialInfo*> SpecialMap; + SpecialMap mSpecials; + TabbedArea *mTabs; }; -extern MagicDialog *magicDialog; +extern SpecialsWindow *specialsWindow; -#endif +#endif // SPECIALSWINDOW_H diff --git a/src/gui/widgets/flowcontainer.cpp b/src/gui/widgets/flowcontainer.cpp new file mode 100644 index 00000000..6ce4284b --- /dev/null +++ b/src/gui/widgets/flowcontainer.cpp @@ -0,0 +1,78 @@ +/* + * The Mana World + * Copyright (C) 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "flowcontainer.h" + +FlowContainer::FlowContainer(int boxWidth, int boxHeight): + mBoxWidth(boxWidth), mBoxHeight(boxHeight), + mGridWidth(1), mGridHeight(1) +{ + addWidgetListener(this); +} + +void FlowContainer::widgetResized(const gcn::Event &event) +{ + if (getWidth() < mBoxWidth) + { + setWidth(mBoxWidth); + return; + } + + int itemCount = mWidgets.size(); + + mGridWidth = getWidth() / mBoxWidth; + + if (mGridWidth < 1) + mGridWidth = 1; + + mGridHeight = itemCount / mGridWidth; + + if (itemCount % mGridWidth != 0 || mGridHeight < 1) + ++mGridHeight; + + int height = mGridHeight * mBoxHeight; + + if (getHeight() != height) + { + setHeight(height); + return; + } + + int i = 0; + height = 0; + for (WidgetList::iterator it = mWidgets.begin(); it != mWidgets.end(); it++) + { + int x = i % mGridWidth * mBoxWidth; + (*it)->setPosition(x, height); + + i++; + + if (i % mGridWidth == 0) + height += mBoxHeight; + } +} + +void FlowContainer::add(gcn::Widget *widget) +{ + Container::add(widget); + widget->setSize(mBoxWidth, mBoxHeight); + widgetResized(NULL); +} diff --git a/src/gui/widgets/flowcontainer.h b/src/gui/widgets/flowcontainer.h new file mode 100644 index 00000000..afecde25 --- /dev/null +++ b/src/gui/widgets/flowcontainer.h @@ -0,0 +1,68 @@ +/* + * The Mana World + * Copyright (C) 2009 The Mana World Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef FLOWCONTAINER_H +#define FLOWCONTAINER_H + +#include "container.h" + +#include <guichan/widgetlistener.hpp> + +/** + * A container that arranges its contents like words on a page. + * + * \ingroup GUI + */ +class FlowContainer : public Container, + public gcn::WidgetListener +{ + public: + /** + * Constructor. Initializes the shortcut container. + */ + FlowContainer(int boxWidth, int boxHeight); + + /** + * Destructor. + */ + ~FlowContainer() {} + + /** + * Invoked when a widget changes its size. This is used to determine + * the new height of the container. + */ + void widgetResized(const gcn::Event &event); + + int getBoxWidth() const + { return mBoxWidth; } + + int getBoxHeight() const + { return mBoxHeight; } + + void add(gcn::Widget *widget); + + private: + int mBoxWidth; + int mBoxHeight; + int mGridWidth, mGridHeight; +}; + +#endif diff --git a/src/gui/widgets/scrollarea.cpp b/src/gui/widgets/scrollarea.cpp index 52322b05..6cf27bb6 100644 --- a/src/gui/widgets/scrollarea.cpp +++ b/src/gui/widgets/scrollarea.cpp @@ -42,6 +42,7 @@ ScrollArea::ScrollArea(): mY(0), mOpaque(true) { + addWidgetListener(this); init(); } @@ -346,3 +347,7 @@ void ScrollArea::mouseExited(gcn::MouseEvent& event) mHasMouse = false; } +void ScrollArea::widgetResized(const gcn::Event &event) +{ + getContent()->setSize(getWidth() - 2 * getFrameSize(), getHeight() - 2 * getFrameSize()); +} diff --git a/src/gui/widgets/scrollarea.h b/src/gui/widgets/scrollarea.h index 8fd92b5f..69e99b1f 100644 --- a/src/gui/widgets/scrollarea.h +++ b/src/gui/widgets/scrollarea.h @@ -23,6 +23,7 @@ #define SCROLLAREA_H #include <guichan/widgets/scrollarea.hpp> +#include <guichan/widgetlistener.hpp> class Image; class ImageRect; @@ -36,7 +37,7 @@ class ImageRect; * * \ingroup GUI */ -class ScrollArea : public gcn::ScrollArea +class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener { public: /** @@ -98,6 +99,8 @@ class ScrollArea : public gcn::ScrollArea */ void mouseExited(gcn::MouseEvent& event); + void widgetResized(const gcn::Event &event); + protected: enum BUTTON_DIR { UP, diff --git a/src/gui/widgets/tabbedarea.cpp b/src/gui/widgets/tabbedarea.cpp index bb5ae9a4..13bb884b 100644 --- a/src/gui/widgets/tabbedarea.cpp +++ b/src/gui/widgets/tabbedarea.cpp @@ -27,6 +27,7 @@ TabbedArea::TabbedArea() : gcn::TabbedArea() { mWidgetContainer->setOpaque(false); + addWidgetListener(this); } int TabbedArea::getNumberOfTabs() const @@ -79,6 +80,15 @@ gcn::Widget *TabbedArea::getCurrentWidget() return NULL; } +void TabbedArea::addTab(gcn::Tab* tab, gcn::Widget* widget) +{ + gcn::TabbedArea::addTab(tab, widget); + + int width = getWidth() - 2 * getFrameSize(); + int height = getHeight() - 2 * getFrameSize() - mTabContainer->getHeight(); + widget->setSize(width, height); +} + void TabbedArea::addTab(const std::string &caption, gcn::Widget *widget) { Tab *tab = new Tab; @@ -152,3 +162,13 @@ void TabbedArea::setSelectedTab(gcn::Tab *tab) if (newTab) newTab->setCurrent(); } + +void TabbedArea::widgetResized(const gcn::Event &event) +{ + int width = getWidth() - 2 * getFrameSize(); + int height = getHeight() - 2 * getFrameSize() - mTabContainer->getHeight(); + mWidgetContainer->setSize(width, height); + gcn::Widget *w = getCurrentWidget(); + if (w) + w->setSize(width, height); +} diff --git a/src/gui/widgets/tabbedarea.h b/src/gui/widgets/tabbedarea.h index 29ba2f76..a64d855f 100644 --- a/src/gui/widgets/tabbedarea.h +++ b/src/gui/widgets/tabbedarea.h @@ -23,6 +23,7 @@ #define TABBEDAREA_H #include <guichan/widget.hpp> +#include <guichan/widgetlistener.hpp> #include <guichan/widgets/container.hpp> #include <guichan/widgets/tabbedarea.hpp> @@ -33,7 +34,7 @@ class Tab; /** * A tabbed area, the same as the guichan tabbed area in 0.8, but extended */ -class TabbedArea : public gcn::TabbedArea +class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener { public: /** @@ -71,6 +72,14 @@ class TabbedArea : public gcn::TabbedArea using gcn::TabbedArea::addTab; /** + * Add a tab. Overridden since it needs to size the widget. + * + * @param tab The tab widget for the tab. + * @param widget The widget to view when the tab is selected. + */ + void addTab(gcn::Tab* tab, gcn::Widget* widget); + + /** * Add a tab. Overridden since it needs to create an instance of Tab * instead of gcn::Tab. * @@ -97,6 +106,8 @@ class TabbedArea : public gcn::TabbedArea void setSelectedTab(gcn::Tab *tab); + void widgetResized(const gcn::Event &event); + private: typedef std::vector< std::pair<gcn::Tab*, gcn::Widget*> > TabContainer; }; diff --git a/src/gui/windowmenu.cpp b/src/gui/windowmenu.cpp index 96776617..b73558c0 100644 --- a/src/gui/windowmenu.cpp +++ b/src/gui/windowmenu.cpp @@ -41,11 +41,11 @@ extern Window *inventoryWindow; extern Window *itemShortcutWindow; extern Window *setupWindow; extern Window *skillDialog; +extern Window *specialsWindow; extern Window *statusWindow; #ifdef TMWSERV_SUPPORT extern Window *buddyWindow; extern Window *guildWindow; -extern Window *magicDialog; #endif WindowMenu::WindowMenu(): @@ -59,8 +59,8 @@ WindowMenu::WindowMenu(): N_("Equipment"), N_("Inventory"), N_("Skills"), + N_("Specials"), #ifdef TMWSERV_SUPPORT - N_("Magic"), N_("Guilds"), N_("Buddies"), #endif @@ -129,11 +129,11 @@ void WindowMenu::action(const gcn::ActionEvent &event) { window = skillDialog; } -#ifdef TMWSERV_SUPPORT - else if (event.getId() == "Magic") + else if (event.getId() == "Specials") { - window = magicDialog; + window = specialsWindow; } +#ifdef TMWSERV_SUPPORT else if (event.getId() == "Guilds") { window = guildWindow; diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 1cc0ae34..68b70db4 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -119,7 +119,12 @@ LocalPlayer::LocalPlayer(int id, int job, Map *map): mUpdateName = true; + mTextColor = &guiPalette->getColor(Palette::PLAYER); + mNameColor = &guiPalette->getColor(Palette::SELF); + initTargetCursor(); + + config.addListener("showownname", this); } LocalPlayer::~LocalPlayer() @@ -129,6 +134,8 @@ LocalPlayer::~LocalPlayer() delete mStorage; #endif + config.removeListener("showownname", this); + for (int i = Being::TC_SMALL; i < Being::NUM_TC; i++) { delete mTargetCursor[0][i]; @@ -258,20 +265,6 @@ void LocalPlayer::setGMLevel(int level) setGM(true); } -void LocalPlayer::setName(const std::string &name) -{ - if (mName) - { - delete mName; - mName = 0; - } - - if (config.getValue("showownname", false) && mMapInitialized) - Player::setName(name); - else - Being::setName(name); -} - void LocalPlayer::nextStep() { // TODO: Fix picking up when reaching target (this method is obsolete) @@ -1113,3 +1106,11 @@ void LocalPlayer::addMessageToQueue(const std::string &message, { mMessages.push_back(MessagePair(message, color)); } + +void LocalPlayer::optionChanged(const std::string &value) +{ + if (value == "showownname") + { + setShowName(config.getValue("showownname", 1)); + } +} diff --git a/src/localplayer.h b/src/localplayer.h index 0b41ca82..691307ba 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -118,8 +118,6 @@ class LocalPlayer : public Player */ ~LocalPlayer(); - virtual void setName(const std::string &name); - virtual void logic(); virtual void setAction(Action action, int attackType = 0); @@ -375,9 +373,17 @@ class LocalPlayer : public Player void addMessageToQueue(const std::string &message, Palette::ColorType color = Palette::EXP_INFO); + /** + * Called when a option (set with config.addListener()) is changed + */ + void optionChanged(const std::string &value); + protected: virtual void handleStatusEffect(StatusEffect *effect, int effectId); + // Colors don't change for local player + virtual void updateColors() {} + void walk(unsigned char dir); bool mInStorage; /**< Whether storage is currently accessible */ diff --git a/src/map.cpp b/src/map.cpp index dbecda3d..5f6433c2 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -123,7 +123,7 @@ Image* MapLayer::getTile(int x, int y) const void MapLayer::draw(Graphics *graphics, int startX, int startY, int endX, int endY, int scrollX, int scrollY, - const Sprites &sprites) const + const MapSprites &sprites) const { startX -= mX; startY -= mY; @@ -135,7 +135,7 @@ void MapLayer::draw(Graphics *graphics, int startX, int startY, if (endX > mWidth) endX = mWidth; if (endY > mHeight) endY = mHeight; - Sprites::const_iterator si = sprites.begin(); + MapSprites::const_iterator si = sprites.begin(); for (int y = startY; y < endY; y++) { @@ -467,13 +467,13 @@ MetaTile *Map::getMetaTile(int x, int y) const return &mMetaTiles[x + y * mWidth]; } -SpriteIterator Map::addSprite(Sprite *sprite) +MapSprite Map::addSprite(Sprite *sprite) { mSprites.push_front(sprite); return mSprites.begin(); } -void Map::removeSprite(SpriteIterator iterator) +void Map::removeSprite(MapSprite iterator) { mSprites.erase(iterator); } @@ -39,8 +39,8 @@ class Sprite; class Tileset; typedef std::vector<Tileset*> Tilesets; -typedef std::list<Sprite*> Sprites; -typedef Sprites::iterator SpriteIterator; +typedef std::list<Sprite*> MapSprites; +typedef MapSprites::iterator MapSprite; typedef std::vector<MapLayer*> Layers; /** @@ -128,7 +128,7 @@ class MapLayer int startX, int startY, int endX, int endY, int scrollX, int scrollY, - const Sprites &sprites) const; + const MapSprites &sprites) const; private: int mX, mY; @@ -266,12 +266,12 @@ class Map : public Properties /** * Adds a sprite to the map. */ - SpriteIterator addSprite(Sprite *sprite); + MapSprite addSprite(Sprite *sprite); /** * Removes a sprite from the map. */ - void removeSprite(SpriteIterator iterator); + void removeSprite(MapSprite iterator); /** * Adds a particle effect @@ -317,7 +317,7 @@ class Map : public Properties MetaTile *mMetaTiles; Layers mLayers; Tilesets mTilesets; - Sprites mSprites; + MapSprites mSprites; // Pathfinding members int mOnClosedList, mOnOpenList; diff --git a/src/monster.cpp b/src/monster.cpp index 3bdf1a62..cc2285fe 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -33,29 +33,24 @@ #include "resources/monsterinfo.h" Monster::Monster(int id, int job, Map *map): - Being(id, job, map), - mText(0) + Being(id, job, map) { const MonsterInfo& info = getInfo(); // Setup Monster sprites - int c = BASE_SPRITE; const std::list<std::string> &sprites = info.getSprites(); for (std::list<std::string>::const_iterator i = sprites.begin(); i != sprites.end(); i++) { - if (c == VECTOREND_SPRITE) break; - std::string file = "graphics/sprites/" + *i; - mSprites[c] = AnimatedSprite::load(file); - c++; + mSprites.push_back(AnimatedSprite::load(file)); } // Ensure that something is shown - if (c == BASE_SPRITE) + if (mSprites.size() == 0) { - mSprites[c] = AnimatedSprite::load("graphics/sprites/error.xml"); + mSprites.push_back(AnimatedSprite::load("graphics/sprites/error.xml")); } if (mParticleEffects) @@ -69,15 +64,11 @@ Monster::Monster(int id, int job, Map *map): } mNameColor = &guiPalette->getColor(Palette::MONSTER); + mTextColor = &guiPalette->getColor(Palette::MONSTER); Being::setName(getInfo().getName()); } -Monster::~Monster() -{ - delete mText; -} - #ifdef EATHENA_SUPPORT void Monster::logic() { @@ -93,11 +84,6 @@ void Monster::logic() } #endif -Being::Type Monster::getType() const -{ - return MONSTER; -} - void Monster::setAction(Action action, int attackType) { SpriteAction currentAction = ACTION_INVALID; @@ -115,7 +101,8 @@ void Monster::setAction(Action action, int attackType) break; case ATTACK: currentAction = getInfo().getAttackAction(attackType); - mSprites[BASE_SPRITE]->reset(); + for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) + (*it)->reset(); //attack particle effect particleEffect = getInfo().getAttackParticleEffect(attackType); @@ -147,11 +134,9 @@ void Monster::setAction(Action action, int attackType) if (currentAction != ACTION_INVALID) { - for (int i = 0; i < VECTOREND_SPRITE; i++) - { - if (mSprites[i]) - mSprites[i]->play(currentAction); - } + for (SpriteIterator it = mSprites.begin(); it != mSprites.end(); it++) + if (*it) + (*it)->play(currentAction); mAction = action; } } @@ -183,29 +168,18 @@ const MonsterInfo &Monster::getInfo() const return MonsterDB::get(mJob); } -void Monster::setShowName(bool show) +void Monster::updateCoords() { - delete mText; - - if (show) - { - mText = new Text(getInfo().getName(), - getPixelX(), - getPixelY() - getHeight(), - gcn::Graphics::CENTER, - &guiPalette->getColor(Palette::MONSTER)); - } - else + if (mDispName) { - mText = 0; + mDispName->adviseXY(getPixelX(), + getPixelY() - getHeight() - mDispName->getHeight()); } } -void Monster::updateCoords() +void Monster::showName() { - if (mText) - { - mText->adviseXY(getPixelX(), - getPixelY() - getHeight() - mText->getHeight()); - } + Being::showName(); + + updateCoords(); } diff --git a/src/monster.h b/src/monster.h index 29b04eab..1cfb8e93 100644 --- a/src/monster.h +++ b/src/monster.h @@ -32,15 +32,13 @@ class Monster : public Being public: Monster(int id, int job, Map *map); - ~Monster(); - #ifdef EATHENA_SUPPORT virtual void logic(); #endif virtual void setAction(Action action, int attackType = 0); - virtual Type getType() const; + virtual Type getType() const { return MONSTER; } virtual TargetCursorSize getTargetCursorSize() const; @@ -70,11 +68,6 @@ class Monster : public Being const MonsterInfo& getInfo() const; /** - * Determine whether the mob should show it's name - */ - void setShowName(bool show); - - /** * Gets the way the monster is blocked by other objects */ virtual unsigned char getWalkMask() const @@ -96,11 +89,7 @@ class Monster : public Being */ void updateCoords(); - private: - /** - * holds a text object when the mod displays it's name, 0 otherwise - */ - Text *mText; + void showName(); }; #endif diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp index 1d780a60..29c10900 100644 --- a/src/net/ea/beinghandler.cpp +++ b/src/net/ea/beinghandler.cpp @@ -37,6 +37,8 @@ #include "gui/partywindow.h" +#include "resources/colordb.h" + #include <iostream> namespace EAthena { @@ -98,7 +100,7 @@ Being *createBeing(int id, short job) void BeingHandler::handleMessage(MessageIn &msg) { int id; - short job, speed; + short job, speed, gender; Uint16 headTop, headMid, headBottom; Uint16 shoes, gloves; Uint16 weapon, shield; @@ -109,6 +111,7 @@ void BeingHandler::handleMessage(MessageIn &msg) int type; Uint16 status; Being *srcBeing, *dstBeing; + Player *player; int hairStyle, hairColor, flag; switch (msg.getId()) @@ -137,6 +140,8 @@ void BeingHandler::handleMessage(MessageIn &msg) dstBeing = createBeing(id, job); } + player = dynamic_cast<Player*>(dstBeing); + // Fix monster jobs if (dstBeing->getType() == Being::MONSTER) { @@ -158,7 +163,7 @@ void BeingHandler::handleMessage(MessageIn &msg) dstBeing->setWalkSpeed(speed); dstBeing->mJob = job; hairStyle = msg.readInt16(); - dstBeing->setSprite(Being::WEAPON_SPRITE, msg.readInt16()); + weapon = msg.readInt16(); headBottom = msg.readInt16(); if (msg.getId() == SMSG_BEING_MOVE) @@ -166,7 +171,7 @@ void BeingHandler::handleMessage(MessageIn &msg) msg.readInt32(); // server tick } - dstBeing->setSprite(Being::SHIELD_SPRITE, msg.readInt16()); + shield = msg.readInt16(); headTop = msg.readInt16(); headMid = msg.readInt16(); hairColor = msg.readInt16(); @@ -178,16 +183,22 @@ void BeingHandler::handleMessage(MessageIn &msg) msg.readInt16(); // manner dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3 msg.readInt8(); // karma - dstBeing->setGender( - (msg.readInt8() == 0) ? GENDER_FEMALE : GENDER_MALE); + gender = msg.readInt8(); - // Set these after the gender, as the sprites may be gender-specific - dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, headBottom); - dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid); - dstBeing->setSprite(Being::HAT_SPRITE, headTop); - dstBeing->setSprite(Being::SHOE_SPRITE, shoes); - dstBeing->setSprite(Being::GLOVES_SPRITE, gloves); - dstBeing->setHairStyle(hairStyle, hairColor); + if (player) + { + player->setGender((gender == 0) + ? GENDER_FEMALE : GENDER_MALE); + // Set these after the gender, as the sprites may be gender-specific + player->setSprite(Player::HAIR_SPRITE, hairStyle * -1, ColorDB::get(hairColor)); + player->setSprite(Player::BOTTOMCLOTHES_SPRITE, headBottom); + player->setSprite(Player::TOPCLOTHES_SPRITE, headMid); + player->setSprite(Player::HAT_SPRITE, headTop); + player->setSprite(Player::SHOE_SPRITE, shoes); + player->setSprite(Player::GLOVES_SPRITE, gloves); + player->setSprite(Player::WEAPON_SPRITE, weapon); + player->setSprite(Player::SHIELD_SPRITE, shield); + } if (msg.getId() == SMSG_BEING_MOVE) { @@ -371,6 +382,8 @@ void BeingHandler::handleMessage(MessageIn &msg) break; } + player = dynamic_cast<Player*>(dstBeing); + int type = msg.readInt8(); int id = 0; int id2 = 0; @@ -384,41 +397,41 @@ void BeingHandler::handleMessage(MessageIn &msg) switch (type) { case 1: // eAthena LOOK_HAIR - dstBeing->setHairStyle(id, -1); + player->setSpriteID(Player::HAIR_SPRITE, id *-1); break; case 2: // Weapon ID in id, Shield ID in id2 - dstBeing->setSprite(Being::WEAPON_SPRITE, id); - dstBeing->setSprite(Being::SHIELD_SPRITE, id2); + player->setSprite(Player::WEAPON_SPRITE, id); + player->setSprite(Player::SHIELD_SPRITE, id2); break; case 3: // Change lower headgear for eAthena, pants for us - dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, id); + player->setSprite(Player::BOTTOMCLOTHES_SPRITE, id); break; case 4: // Change upper headgear for eAthena, hat for us - dstBeing->setSprite(Being::HAT_SPRITE, id); + player->setSprite(Player::HAT_SPRITE, id); break; case 5: // Change middle headgear for eathena, armor for us - dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, id); + player->setSprite(Player::TOPCLOTHES_SPRITE, id); break; case 6: // eAthena LOOK_HAIR_COLOR - dstBeing->setHairStyle(-1, id); + player->setSpriteColor(Player::HAIR_SPRITE, ColorDB::get(id)); break; case 8: // eAthena LOOK_SHIELD - dstBeing->setSprite(Being::SHIELD_SPRITE, id); + player->setSprite(Player::SHIELD_SPRITE, id); break; case 9: // eAthena LOOK_SHOES - dstBeing->setSprite(Being::SHOE_SPRITE, id); + player->setSprite(Player::SHOE_SPRITE, id); break; case 10: // LOOK_GLOVES - dstBeing->setSprite(Being::GLOVES_SPRITE, id); + player->setSprite(Player::GLOVES_SPRITE, id); break; case 11: // LOOK_CAPE - dstBeing->setSprite(Being::CAPE_SPRITE, id); + player->setSprite(Player::CAPE_SPRITE, id); break; case 12: - dstBeing->setSprite(Being::MISC1_SPRITE, id); + player->setSprite(Player::MISC1_SPRITE, id); break; case 13: - dstBeing->setSprite(Being::MISC2_SPRITE, id); + player->setSprite(Player::MISC2_SPRITE, id); break; default: logger->log("SMSG_BEING_CHANGE_LOOKS: unsupported type: " @@ -466,15 +479,16 @@ void BeingHandler::handleMessage(MessageIn &msg) dstBeing = createBeing(id, job); } + player = dynamic_cast<Player*>(dstBeing); + { PartyMember *member = partyWindow->findMember(id); if (member && member->online) { - dynamic_cast<Player*>(dstBeing)->setInParty(true); + player->setInParty(true); } } - dstBeing->setWalkSpeed(speed); dstBeing->mJob = job; hairStyle = msg.readInt16(); @@ -497,19 +511,19 @@ void BeingHandler::handleMessage(MessageIn &msg) msg.readInt16(); // manner dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3 msg.readInt8(); // karma - dstBeing->setGender( - (msg.readInt8() == 0) ? GENDER_FEMALE : GENDER_MALE); + player->setGender((msg.readInt8() == 0) + ? GENDER_FEMALE : GENDER_MALE); // Set these after the gender, as the sprites may be gender-specific - dstBeing->setSprite(Being::WEAPON_SPRITE, weapon); - dstBeing->setSprite(Being::SHIELD_SPRITE, shield); - dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, headBottom); - dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid); - dstBeing->setSprite(Being::HAT_SPRITE, headTop); - //dstBeing->setSprite(Being::CAPE_SPRITE, cape); - //dstBeing->setSprite(Being::MISC1_SPRITE, misc1); - //dstBeing->setSprite(Being::MISC2_SPRITE, misc2); - dstBeing->setHairStyle(hairStyle, hairColor); + player->setSprite(Player::WEAPON_SPRITE, weapon); + player->setSprite(Player::SHIELD_SPRITE, shield); + player->setSprite(Player::BOTTOMCLOTHES_SPRITE, headBottom); + player->setSprite(Player::TOPCLOTHES_SPRITE, headMid); + player->setSprite(Player::HAT_SPRITE, headTop); + //player->setSprite(Player::CAPE_SPRITE, cape); + //player->setSprite(Player::MISC1_SPRITE, misc1); + //player->setSprite(Player::MISC2_SPRITE, misc2); + player->setSprite(Player::HAIR_SPRITE, hairStyle * -1, ColorDB::get(hairColor)); if (msg.getId() == SMSG_PLAYER_MOVE) { diff --git a/src/net/ea/charserverhandler.cpp b/src/net/ea/charserverhandler.cpp index 6fae1864..47d454a8 100644 --- a/src/net/ea/charserverhandler.cpp +++ b/src/net/ea/charserverhandler.cpp @@ -35,6 +35,8 @@ #include "gui/charcreatedialog.h" #include "gui/okdialog.h" +#include "resources/colordb.h" + #include "utils/gettext.h" #include "utils/stringutils.h" @@ -176,10 +178,10 @@ LocalPlayer *CharServerHandler::readPlayerData(MessageIn &msg, int &slot) int temp = msg.readInt32(); tempPlayer->setAttributeBase(JOB, temp); tempPlayer->setAttributeEffective(JOB, temp); - tempPlayer->setSprite(Being::SHOE_SPRITE, msg.readInt16()); - tempPlayer->setSprite(Being::GLOVES_SPRITE, msg.readInt16()); - tempPlayer->setSprite(Being::CAPE_SPRITE, msg.readInt16()); - tempPlayer->setSprite(Being::MISC1_SPRITE, msg.readInt16()); + tempPlayer->setSprite(Player::SHOE_SPRITE, msg.readInt16()); + tempPlayer->setSprite(Player::GLOVES_SPRITE, msg.readInt16()); + tempPlayer->setSprite(Player::CAPE_SPRITE, msg.readInt16()); + tempPlayer->setSprite(Player::MISC1_SPRITE, msg.readInt16()); msg.readInt32(); // option msg.readInt32(); // karma msg.readInt32(); // manner @@ -192,16 +194,15 @@ LocalPlayer *CharServerHandler::readPlayerData(MessageIn &msg, int &slot) msg.readInt16(); // class int hairStyle = msg.readInt16(); Uint16 weapon = msg.readInt16(); - tempPlayer->setSprite(Being::WEAPON_SPRITE, weapon); + tempPlayer->setSprite(Player::WEAPON_SPRITE, weapon); tempPlayer->setLevel(msg.readInt16()); msg.readInt16(); // skill point - tempPlayer->setSprite(Being::BOTTOMCLOTHES_SPRITE, msg.readInt16()); // head bottom - tempPlayer->setSprite(Being::SHIELD_SPRITE, msg.readInt16()); - tempPlayer->setSprite(Being::HAT_SPRITE, msg.readInt16()); // head option top - tempPlayer->setSprite(Being::TOPCLOTHES_SPRITE, msg.readInt16()); // head option mid - int hairColor = msg.readInt16(); - tempPlayer->setHairStyle(hairStyle, hairColor); - tempPlayer->setSprite(Being::MISC2_SPRITE, msg.readInt16()); + tempPlayer->setSprite(Player::BOTTOMCLOTHES_SPRITE, msg.readInt16()); // head bottom + tempPlayer->setSprite(Player::SHIELD_SPRITE, msg.readInt16()); + tempPlayer->setSprite(Player::HAT_SPRITE, msg.readInt16()); // head option top + tempPlayer->setSprite(Player::TOPCLOTHES_SPRITE, msg.readInt16()); // head option mid + tempPlayer->setSprite(Player::HAIR_SPRITE, hairStyle * -1, ColorDB::get(msg.readInt16())); + tempPlayer->setSprite(Player::MISC2_SPRITE, msg.readInt16()); tempPlayer->setName(msg.readString(24)); for (int i = 0; i < 6; i++) { tempPlayer->setAttributeBase(i + STR, msg.readInt8()); diff --git a/src/net/ea/partyhandler.cpp b/src/net/ea/partyhandler.cpp index a4a84b07..f514c3b6 100644 --- a/src/net/ea/partyhandler.cpp +++ b/src/net/ea/partyhandler.cpp @@ -139,7 +139,6 @@ void PartyHandler::handleMessage(MessageIn &msg) break; } std::string nick; - int gender = 0; std::string partyName = ""; if (being->getType() != Being::PLAYER) { @@ -148,7 +147,6 @@ void PartyHandler::handleMessage(MessageIn &msg) else { nick = being->getName(); - gender = being->getGender(); partyName = msg.readString(24); } partyWindow->showPartyInvite(nick, partyName); diff --git a/src/net/logindata.h b/src/net/logindata.h index 4cf989cb..db7aafff 100644 --- a/src/net/logindata.h +++ b/src/net/logindata.h @@ -24,7 +24,7 @@ #include <string> -#include "being.h" +#include "player.h" struct LoginData { diff --git a/src/net/tmwserv/beinghandler.cpp b/src/net/tmwserv/beinghandler.cpp index acd6b62c..d5092782 100644 --- a/src/net/tmwserv/beinghandler.cpp +++ b/src/net/tmwserv/beinghandler.cpp @@ -37,6 +37,8 @@ #include "gui/okdialog.h" +#include "resources/colordb.h" + #include "utils/gettext.h" #include "net/tmwserv/gameserver/player.h" @@ -95,8 +97,8 @@ static void handleLooks(Player *being, MessageIn &msg) // Order of sent slots. Has to be in sync with the server code. static int const nb_slots = 4; static int const slots[nb_slots] = - { Being::WEAPON_SPRITE, Being::HAT_SPRITE, Being::TOPCLOTHES_SPRITE, - Being::BOTTOMCLOTHES_SPRITE }; + { Player::WEAPON_SPRITE, Player::HAT_SPRITE, Player::TOPCLOTHES_SPRITE, + Player::BOTTOMCLOTHES_SPRITE }; int mask = msg.readInt8(); @@ -144,7 +146,7 @@ void BeingHandler::handleBeingEnterMessage(MessageIn &msg) } Player *p = static_cast< Player * >(being); int hs = msg.readInt8(), hc = msg.readInt8(); - p->setHairStyle(hs, hc); + p->setSprite(Player::HAIR_SPRITE, hs * -1, ColorDB::get(hc)); p->setGender(msg.readInt8() == GENDER_MALE ? GENDER_MALE : GENDER_FEMALE); handleLooks(p, msg); @@ -304,8 +306,7 @@ void BeingHandler::handleBeingLooksChangeMessage(MessageIn &msg) { int style = msg.readInt16(); int color = msg.readInt16(); - player->setHairStyle(style, color); - player->setGender((Gender)msg.readInt16()); + player->setSprite(Player::HAIR_SPRITE, style * -1, ColorDB::get(color)); } } diff --git a/src/net/tmwserv/charserverhandler.cpp b/src/net/tmwserv/charserverhandler.cpp index 0146babb..93181a93 100644 --- a/src/net/tmwserv/charserverhandler.cpp +++ b/src/net/tmwserv/charserverhandler.cpp @@ -38,6 +38,8 @@ #include "gui/charcreatedialog.h" #include "gui/okdialog.h" +#include "resources/colordb.h" + #include "utils/gettext.h" extern Net::Connection *gameServerConnection; @@ -229,7 +231,7 @@ LocalPlayer* CharServerHandler::readPlayerData(MessageIn &msg, int &slot) tempPlayer->setName(msg.readString()); tempPlayer->setGender(msg.readInt8() == GENDER_MALE ? GENDER_MALE : GENDER_FEMALE); int hs = msg.readInt8(), hc = msg.readInt8(); - tempPlayer->setHairStyle(hs, hc); + tempPlayer->setSprite(Player::HAIR_SPRITE, hs * -1, ColorDB::get(hc)); tempPlayer->setLevel(msg.readInt16()); tempPlayer->setCharacterPoints(msg.readInt16()); tempPlayer->setCorrectionPoints(msg.readInt16()); diff --git a/src/net/tmwserv/chathandler.cpp b/src/net/tmwserv/chathandler.cpp index ad3ae49b..c95f6ac5 100644 --- a/src/net/tmwserv/chathandler.cpp +++ b/src/net/tmwserv/chathandler.cpp @@ -298,7 +298,7 @@ void ChatHandler::handleChannelEvent(MessageIn &msg) std::string user1 = line.substr(0, first); std::string user2 = line.substr(first+1, line.length()); channel->getTab()->chatLog(strprintf(_("%s has kicked %s."), - user1, user2), BY_CHANNEL); + user1.c_str(), user2.c_str()), BY_CHANNEL); } break; default: diff --git a/src/net/tmwserv/generalhandler.cpp b/src/net/tmwserv/generalhandler.cpp index d643586b..011433fe 100644 --- a/src/net/tmwserv/generalhandler.cpp +++ b/src/net/tmwserv/generalhandler.cpp @@ -22,6 +22,7 @@ #include "gui/inventorywindow.h" #include "gui/partywindow.h" #include "gui/skilldialog.h" +#include "gui/specialswindow.h" #include "gui/statuswindow.h" #include "net/tmwserv/generalhandler.h" @@ -43,6 +44,7 @@ #include "net/tmwserv/npchandler.h" #include "net/tmwserv/partyhandler.h" #include "net/tmwserv/playerhandler.h" +#include "net/tmwserv/specialhandler.h" #include "net/tmwserv/tradehandler.h" #include "utils/gettext.h" @@ -72,7 +74,8 @@ GeneralHandler::GeneralHandler(): mNpcHandler(new NpcHandler), mPartyHandler(new PartyHandler), mPlayerHandler(new PlayerHandler), - mTradeHandler(new TradeHandler) + mTradeHandler(new TradeHandler), + mSpecialHandler(new SpecialHandler) { accountServerConnection = Net::getConnection(); gameServerConnection = Net::getConnection(); @@ -149,6 +152,7 @@ void GeneralHandler::guiWindowsLoaded() inventoryWindow->setSplitAllowed(true); partyWindow->clearPartyName(); skillDialog->loadSkills("tmw-skills.xml"); + specialsWindow->loadSpecials("specials.xml"); player_node->setExpNeeded(100); diff --git a/src/net/tmwserv/generalhandler.h b/src/net/tmwserv/generalhandler.h index 08e18850..40166ca0 100644 --- a/src/net/tmwserv/generalhandler.h +++ b/src/net/tmwserv/generalhandler.h @@ -63,6 +63,7 @@ class GeneralHandler : public Net::GeneralHandler MessageHandlerPtr mPartyHandler; MessageHandlerPtr mPlayerHandler; MessageHandlerPtr mTradeHandler; + MessageHandlerPtr mSpecialHandler; }; } // namespace TmwServ diff --git a/src/net/tmwserv/specialhandler.cpp b/src/net/tmwserv/specialhandler.cpp index f259e77a..2e4ff1bb 100644 --- a/src/net/tmwserv/specialhandler.cpp +++ b/src/net/tmwserv/specialhandler.cpp @@ -37,6 +37,11 @@ SpecialHandler::SpecialHandler() specialHandler = this; } +void SpecialHandler::handleMessage(MessageIn &msg) +{ + // TODO +} + void SpecialHandler::use(int id) { MessageOut msg(PGMSG_USE_SPECIAL); diff --git a/src/net/tmwserv/specialhandler.h b/src/net/tmwserv/specialhandler.h index c7ebd6a2..b8f0ce90 100644 --- a/src/net/tmwserv/specialhandler.h +++ b/src/net/tmwserv/specialhandler.h @@ -22,15 +22,18 @@ #ifndef NET_TMWSERV_SKILLHANDLER_H #define NET_TMWSERV_SKILLHANDLER_H +#include "net/messagehandler.h" #include "net/specialhandler.h" namespace TmwServ { -class SpecialHandler : public Net::SpecialHandler +class SpecialHandler : public MessageHandler, public Net::SpecialHandler { public: SpecialHandler(); + void handleMessage(MessageIn &msg); + void use(int id); void use(int id, int level, int beingId); diff --git a/src/npc.cpp b/src/npc.cpp index a49ef406..882f1b42 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -36,23 +36,20 @@ bool NPC::isTalking = false; int current_npc = 0; NPC::NPC(int id, int job, Map *map): - Player(id, job, map) + Player(id, job, map, true) { NPCInfo info = NPCDB::get(job); // Setup NPC sprites - int c = BASE_SPRITE; for (std::list<NPCsprite*>::const_iterator i = info.sprites.begin(); i != info.sprites.end(); i++) { - if (c == VECTOREND_SPRITE) - break; - std::string file = "graphics/sprites/" + (*i)->sprite; int variant = (*i)->variant; - mSprites[c] = AnimatedSprite::load(file, variant); - c++; + mSprites.push_back(AnimatedSprite::load(file, variant)); + mSpriteIDs.push_back(0); + mSpriteColors.push_back(""); } if (mParticleEffects) @@ -66,43 +63,15 @@ NPC::NPC(int id, int job, Map *map): this->controlParticle(p); } } - mName = 0; - - mNameColor = &guiPalette->getColor(Palette::NPC); -} -NPC::~NPC() -{ - delete mName; + setShowName(true); } void NPC::setName(const std::string &name) { const std::string displayName = name.substr(0, name.find('#', 0)); - delete mName; - mName = new Text(displayName, - getPixelX(), - getPixelY(), - gcn::Graphics::CENTER, - &guiPalette->getColor(Palette::NPC)); - Being::setName(displayName + " (NPC)"); -} - -void NPC::setGender(Gender gender) -{ - Being::setGender(gender); -} - -void NPC::setSprite(int slot, int id, std::string color) -{ - // Fix this later should it not be adequate enough. - Being::setSprite(slot, id, color); -} - -Being::Type NPC::getType() const -{ - return Being::NPC; + Being::setName(displayName); } void NPC::talk() @@ -115,10 +84,7 @@ void NPC::talk() Net::getNpcHandler()->talk(mId); } -void NPC::updateCoords() +void NPC::setSprite(unsigned int slot, int id, const std::string &color) { - if (mName) - { - mName->adviseXY(getPixelX(), getPixelY()); - } + // Do nothing } @@ -32,16 +32,15 @@ class NPC : public Player public: NPC(int id, int job, Map *map); - ~NPC(); - void setName(const std::string &name); - void setGender(Gender gender); - void setSprite(int slot, int id, std::string color); - virtual Type getType() const; + virtual Type getType() const { return Being::NPC; } void talk(); + void setSprite(unsigned int slot, int id, + const std::string &color = ""); + /** * Gets the way an NPC is blocked by other things on the map */ @@ -61,10 +60,8 @@ class NPC : public Player virtual Map::BlockType getBlockType() const { return Map::BLOCKTYPE_CHARACTER; } //blocks like a player character - void updateCoords(); - - private: - Text *mName; + // Colors don't change for NPCs + virtual void updateColors() {} }; extern int current_npc; diff --git a/src/player.cpp b/src/player.cpp index fd7cd0d6..288c565d 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -38,55 +38,36 @@ #include "utils/stringutils.h" -Player::Player(int id, int job, Map *map): +Player::Player(int id, int job, Map *map, bool isNPC): Being(id, job, map), - mName(0), + mGender(GENDER_UNSPECIFIED), mIsGM(false), mInParty(false) { + if (!isNPC) + { + for (int i = 0; i < VECTOREND_SPRITE; i++) + { + mSprites.push_back(NULL); + mSpriteIDs.push_back(0); + mSpriteColors.push_back(""); + } + + /* Human base sprite. When implementing different races remove this + * line and set the base sprite when setting the race of the player + * character. + */ + setSprite(BASE_SPRITE, -100); + } mShowName = config.getValue("visiblenames", 1); config.addListener("visiblenames", this); + + updateColors(); } Player::~Player() { config.removeListener("visiblenames", this); - delete mName; -} - -void Player::setName(const std::string &name) -{ - if (!mName && mShowName) - { - mNameColor = &guiPalette->getColor(Palette::PLAYER); - - const gcn::Color *color; - if (this == player_node) - { - color = &guiPalette->getColor(Palette::SELF); - } - else if (mIsGM) - { - mNameColor = &guiPalette->getColor(Palette::GM); - color = &guiPalette->getColor(Palette::GM_NAME); - } - else if (mInParty) - { - color = &guiPalette->getColor(Palette::PARTY); - } - else - { - color = &guiPalette->getColor(Palette::PC); - } - - mName = new FlashText(name, - getPixelX(), - getPixelY(), - gcn::Graphics::CENTER, - color); - } - - Being::setName(name); } #ifdef EATHENA_SUPPORT @@ -149,31 +130,14 @@ void Player::logic() } #endif -Being::Type Player::getType() const -{ - return PLAYER; -} - -void Player::flash(int time) -{ - if (mName) - mName->flash(time); -} - void Player::setGender(Gender gender) { if (gender != mGender) { - Being::setGender(gender); - - /* Human base sprite. When implementing different races remove this - * line and set the base sprite when setting the race of the player - * character. - */ - setSprite(Being::BASE_SPRITE, -100); + mGender = gender; // Reload all subsprites - for (int i = 1; i < VECTOREND_SPRITE; i++) + for (unsigned int i = 0; i < mSprites.size(); i++) { if (mSpriteIDs.at(i) != 0) setSprite(i, mSpriteIDs.at(i), mSpriteColors.at(i)); @@ -185,28 +149,12 @@ void Player::setGM(bool gm) { mIsGM = gm; - if (gm && mName) - mName->setColor(&guiPalette->getColor(Palette::GM)); -} - -void Player::setHairStyle(int style, int color) -{ - style = style < 0 ? mHairStyle : style % mNumberOfHairstyles; - color = color < 0 ? mHairColor : color % ColorDB::size(); - if (style == mHairStyle && color == mHairColor) return; - - Being::setHairStyle(style, color); - - setSprite(HAIR_SPRITE, style * -1, ColorDB::get(color)); - - setAction(mAction); + updateColors(); } -void Player::setSprite(int slot, int id, const std::string &color) +void Player::setSprite(unsigned int slot, int id, const std::string &color) { - // TODO: Find a better way - if (getType() == NPC) - return; + assert(slot >= BASE_SPRITE && slot < VECTOREND_SPRITE); // id = 0 means unequip if (id == 0) @@ -237,6 +185,7 @@ void Player::setSprite(int slot, int id, const std::string &color) equipmentSprite->setDirection(getSpriteDirection()); delete mSprites[slot]; + mSprites[slot] = equipmentSprite; if (slot == WEAPON_SPRITE) @@ -245,13 +194,18 @@ void Player::setSprite(int slot, int id, const std::string &color) setAction(mAction); } - Being::setSprite(slot, id, color); + mSpriteIDs[slot] = id; + mSpriteColors[slot] = color; +} + +void Player::setSpriteID(unsigned int slot, int id) +{ + setSprite(slot, id, mSpriteColors[slot]); } -void Player::updateCoords() +void Player::setSpriteColor(unsigned int slot, const std::string &color) { - if (mName) - mName->adviseXY(getPixelX(), getPixelY()); + setSprite(slot, mSpriteIDs[slot], color); } #ifdef TMWSERV_SUPPORT @@ -305,26 +259,32 @@ void Player::setInParty(bool inParty) { mInParty = inParty; - if (this != player_node && mName) + updateColors(); +} + +void Player::optionChanged(const std::string &value) +{ + if (value == "visiblenames") { - Palette::ColorType colorType = mInParty ? Palette::PARTY : Palette::PC; - mName->setColor(&guiPalette->getColor(colorType)); + setShowName(config.getValue("visiblenames", 1)); } } -void Player::optionChanged(const std::string &value) +void Player::updateColors() { - if (value == "visiblenames" && getType() == Being::PLAYER && player_node != this) + mTextColor = &guiPalette->getColor(Palette::PLAYER); + + if (mIsGM) { - mShowName = config.getValue("visiblenames", 1); - if (!mShowName && mName) - { - delete mName; - mName = NULL; - } - else if (mShowName && !mName && !(getName().empty())) - { - setName(getName()); - } + mTextColor = &guiPalette->getColor(Palette::GM); + mNameColor = &guiPalette->getColor(Palette::GM_NAME); + } + else if (mInParty) + { + mNameColor = &guiPalette->getColor(Palette::PARTY); + } + else + { + mNameColor = &guiPalette->getColor(Palette::PC); } } diff --git a/src/player.h b/src/player.h index 9a5c6c94..3c4cc258 100644 --- a/src/player.h +++ b/src/player.h @@ -24,13 +24,19 @@ #include "being.h" -class FlashText; class Graphics; class Map; #ifdef TMWSERV_SUPPORT class Guild; #endif +enum Gender +{ + GENDER_MALE = 0, + GENDER_FEMALE = 1, + GENDER_UNSPECIFIED = 2 +}; + /** * A player being. Players have their name drawn beneath them. This class also * implements player-specific loading of base sprite, hair sprite and equipment @@ -39,26 +45,49 @@ class Guild; class Player : public Being { public: + enum Sprite + { + BASE_SPRITE = 0, + SHOE_SPRITE, + BOTTOMCLOTHES_SPRITE, + TOPCLOTHES_SPRITE, +#ifdef EATHENA_SUPPORT + MISC1_SPRITE, + MISC2_SPRITE, +#endif + HAIR_SPRITE, + HAT_SPRITE, +#ifdef EATHENA_SUPPORT + CAPE_SPRITE, + GLOVES_SPRITE, +#endif + WEAPON_SPRITE, +#ifdef EATHENA_SUPPORT + SHIELD_SPRITE, +#endif + VECTOREND_SPRITE + }; + /** * Constructor. */ - Player(int id, int job, Map *map); + Player(int id, int job, Map *map, bool isNPC = false); ~Player(); - /** - * Set up mName to be the character's name - */ - virtual void setName(const std::string &name); - #ifdef EATHENA_SUPPORT virtual void logic(); #endif - virtual Type getType() const; + virtual Type getType() const { return PLAYER; } + /** + * Sets the gender of this being. + */ virtual void setGender(Gender gender); + Gender getGender() const { return mGender; } + /** * Whether or not this player is a GM. */ @@ -70,26 +99,15 @@ class Player : public Being virtual void setGM(bool gm); /** - * Sets the hair style and color for this player. - * - * Only for convenience in 0.0 client. When porting - * this to the trunk remove this function and - * call setSprite directly instead. The server should - * provide the hair ID and coloring in the same way - * it does for other equipment pieces. - * - */ - void setHairStyle(int style, int color); - - /** * Sets visible equipments for this player. */ - virtual void setSprite(int slot, int id, const std::string &color = ""); + virtual void setSprite(unsigned int slot, int id, + const std::string &color = ""); - /** - * Flash the player's name - */ - void flash(int time); + virtual void setSpriteID(unsigned int slot, int id); + + virtual void setSpriteColor(unsigned int slot, + const std::string &color = ""); #ifdef TMWSERV_SUPPORT /** @@ -137,7 +155,7 @@ class Player : public Being /** * Called when a option (set with config.addListener()) is changed */ - void optionChanged(const std::string &value); + virtual void optionChanged(const std::string &value); protected: /** @@ -146,16 +164,17 @@ class Player : public Being virtual Map::BlockType getBlockType() const { return Map::BLOCKTYPE_CHARACTER; } - virtual void updateCoords(); + virtual void updateColors(); + + Gender mGender; + std::vector<int> mSpriteIDs; + std::vector<std::string> mSpriteColors; #ifdef TMWSERV_SUPPORT // Character guild information std::map<int, Guild*> mGuilds; #endif - bool mShowName; - FlashText *mName; - bool mIsGM; private: diff --git a/src/playerrelations.cpp b/src/playerrelations.cpp index 7bc1b14d..2825d114 100644 --- a/src/playerrelations.cpp +++ b/src/playerrelations.cpp @@ -329,7 +329,7 @@ public: virtual void ignore(Player *player, unsigned int flags) { - player->flash(200); + player->flashName(200); } }; diff --git a/src/resources/image.cpp b/src/resources/image.cpp index 9af3059a..d2a1c82e 100644 --- a/src/resources/image.cpp +++ b/src/resources/image.cpp @@ -34,31 +34,56 @@ int Image::mTextureType = 0; int Image::mTextureSize = 0; #endif -Image::Image(SDL_Surface *image): +Image::Image(SDL_Surface *image, bool hasAlphaChannel, Uint8 *alphaChannel): + mAlpha(1.0f), + mHasAlphaChannel(hasAlphaChannel), + mSDLSurface(image), + mAlphaChannel(alphaChannel) +{ #ifdef USE_OPENGL - mGLImage(0), + mGLImage = 0; #endif - mImage(image), - mAlpha(1.0f) -{ + mBounds.x = 0; mBounds.y = 0; - mBounds.w = mImage->w; - mBounds.h = mImage->h; + + mLoaded = false; + + if (mSDLSurface) + { + mBounds.w = mSDLSurface->w; + mBounds.h = mSDLSurface->h; + + mLoaded = true; + } + else + logger->log( + "Image::Image(SDL_Surface*): Couldn't load invalid Surface!"); } #ifdef USE_OPENGL Image::Image(GLuint glimage, int width, int height, int texWidth, int texHeight): + mAlpha(1.0f), + mHasAlphaChannel(true), + mSDLSurface(0), + mAlphaChannel(0), mGLImage(glimage), mTexWidth(texWidth), - mTexHeight(texHeight), - mImage(0), - mAlpha(1.0) + mTexHeight(texHeight) { mBounds.x = 0; mBounds.y = 0; mBounds.w = width; mBounds.h = height; + + if (mGLImage) + mLoaded = true; + else + { + logger->log( + "Image::Image(GLuint*, ...): Couldn't load invalid Surface!"); + mLoaded = false; + } } #endif @@ -132,153 +157,23 @@ Image *Image::load(SDL_Surface *tmpImage) { #ifdef USE_OPENGL if (mUseOpenGL) - { - // Flush current error flag. - glGetError(); - - int width = tmpImage->w; - int height = tmpImage->h; - int realWidth = powerOfTwo(width); - int realHeight = powerOfTwo(height); - - if (realWidth < width || realHeight < height) - { - logger->log("Warning: image too large, cropping to %dx%d texture!", - tmpImage->w, tmpImage->h); - } - - // Make sure the alpha channel is not used, but copied to destination - SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE); - - // Determine 32-bit masks based on byte order - Uint32 rmask, gmask, bmask, amask; -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - rmask = 0xff000000; - gmask = 0x00ff0000; - bmask = 0x0000ff00; - amask = 0x000000ff; -#else - rmask = 0x000000ff; - gmask = 0x0000ff00; - bmask = 0x00ff0000; - amask = 0xff000000; -#endif - - SDL_Surface *oldImage = tmpImage; - tmpImage = SDL_CreateRGBSurface(SDL_SWSURFACE, realWidth, realHeight, - 32, rmask, gmask, bmask, amask); - - if (!tmpImage) - { - logger->log("Error, image convert failed: out of memory"); - return NULL; - } - - SDL_BlitSurface(oldImage, NULL, tmpImage, NULL); - - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(mTextureType, texture); - - if (SDL_MUSTLOCK(tmpImage)) - SDL_LockSurface(tmpImage); - - glTexImage2D( - mTextureType, 0, 4, - tmpImage->w, tmpImage->h, - 0, GL_RGBA, GL_UNSIGNED_BYTE, - tmpImage->pixels); - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - if (SDL_MUSTLOCK(tmpImage)) { - SDL_UnlockSurface(tmpImage); - } - - SDL_FreeSurface(tmpImage); - - GLenum error = glGetError(); - if (error) - { - std::string errmsg = "Unknown error"; - switch (error) - { - case GL_INVALID_ENUM: - errmsg = "GL_INVALID_ENUM"; - break; - case GL_INVALID_VALUE: - errmsg = "GL_INVALID_VALUE"; - break; - case GL_INVALID_OPERATION: - errmsg = "GL_INVALID_OPERATION"; - break; - case GL_STACK_OVERFLOW: - errmsg = "GL_STACK_OVERFLOW"; - break; - case GL_STACK_UNDERFLOW: - errmsg = "GL_STACK_UNDERFLOW"; - break; - case GL_OUT_OF_MEMORY: - errmsg = "GL_OUT_OF_MEMORY"; - break; - } - logger->log("Error: Image GL import failed: %s", errmsg.c_str()); - return NULL; - } - - return new Image(texture, width, height, realWidth, realHeight); - } + return _GLload(tmpImage); #endif - - bool hasAlpha = false; - - if (tmpImage->format->BitsPerPixel == 32) - { - // Figure out whether the image uses its alpha layer - for (int i = 0; i < tmpImage->w * tmpImage->h; ++i) - { - Uint8 r, g, b, a; - SDL_GetRGBA( - ((Uint32*) tmpImage->pixels)[i], - tmpImage->format, - &r, &g, &b, &a); - - if (a != 255) - { - hasAlpha = true; - break; - } - } - } - - SDL_Surface *image; - - // Convert the surface to the current display format - if (hasAlpha) - image = SDL_DisplayFormatAlpha(tmpImage); - else - image = SDL_DisplayFormat(tmpImage); - - if (!image) - { - logger->log("Error: Image convert failed."); - return NULL; - } - - return new Image(image); + return _SDLload(tmpImage); } void Image::unload() { mLoaded = false; - if (mImage) + if (mSDLSurface) { // Free the image surface. - SDL_FreeSurface(mImage); - mImage = NULL; + SDL_FreeSurface(mSDLSurface); + mSDLSurface = NULL; + + delete[] mAlphaChannel; + mAlphaChannel = NULL; } #ifdef USE_OPENGL @@ -299,34 +194,79 @@ bool Image::isAnOpenGLOne() const #endif } -void Image::setAlpha(float a) +bool Image::hasAlphaChannel() { - if (mAlpha == a) + if (mLoaded) + return mHasAlphaChannel; + +#ifdef USE_OPENGL + if (mUseOpenGL) + return true; +#endif + + return false; +} + +void Image::setAlpha(float alpha) +{ + if (mAlpha == alpha) return; - mAlpha = a; + if (alpha < 0.0f || alpha > 1.0f) + return; - if (mImage) + mAlpha = alpha; + + if (mSDLSurface) { - // Set the alpha value this image is drawn at - SDL_SetAlpha(mImage, SDL_SRCALPHA, (int) (255 * mAlpha)); + if (!hasAlphaChannel()) + { + // Set the alpha value this image is drawn at + SDL_SetAlpha(mSDLSurface, SDL_SRCALPHA, (int) (255 * mAlpha)); + } + else + { + if (SDL_MUSTLOCK(mSDLSurface)) + SDL_LockSurface(mSDLSurface); + + for (int i = 0; i < mSDLSurface->w * mSDLSurface->h; ++i) + { + Uint8 r, g, b, a; + SDL_GetRGBA( + ((Uint32*) mSDLSurface->pixels)[i], + mSDLSurface->format, + &r, &g, &b, &a); + + a = (Uint8) (mAlphaChannel[i] * mAlpha); + + // Here is the pixel we want to set + ((Uint32 *)(mSDLSurface->pixels))[i] = + SDL_MapRGBA(mSDLSurface->format, r, g, b, a); + } + + if (SDL_MUSTLOCK(mSDLSurface)) + SDL_UnlockSurface(mSDLSurface); + } } } -Image* Image::merge(Image *image, int x, int y) +Image* Image::SDLmerge(Image *image, int x, int y) { - SDL_Surface* surface = new SDL_Surface(*(image->mImage)); + if (!mSDLSurface) + return NULL; + + SDL_Surface* surface = new SDL_Surface(*(image->mSDLSurface)); Uint32 surface_pix, cur_pix; Uint8 r, g, b, a, p_r, p_g, p_b, p_a; double f_a, f_ca, f_pa; - SDL_PixelFormat *current_fmt = mImage->format; + SDL_PixelFormat *current_fmt = mSDLSurface->format; SDL_PixelFormat *surface_fmt = surface->format; int current_offset, surface_offset; int offset_x, offset_y; SDL_LockSurface(surface); - SDL_LockSurface(mImage); + SDL_LockSurface(mSDLSurface); // for each pixel lines of a source image for (offset_x = (x > 0 ? 0 : -x); offset_x < image->getWidth() && x + offset_x < getWidth(); offset_x++) @@ -340,7 +280,7 @@ Image* Image::merge(Image *image, int x, int y) // Retrieving a pixel to merge surface_pix = ((Uint32*) surface->pixels)[surface_offset]; - cur_pix = ((Uint32*) mImage->pixels)[current_offset]; + cur_pix = ((Uint32*) mSDLSurface->pixels)[current_offset]; // Retreiving each channel of the pixel using pixel format r = (Uint8)(((surface_pix & surface_fmt->Rmask) >> @@ -381,18 +321,13 @@ Image* Image::merge(Image *image, int x, int y) } } SDL_UnlockSurface(surface); - SDL_UnlockSurface(mImage); + SDL_UnlockSurface(mSDLSurface); Image *newImage = new Image(surface); return newImage; } -float Image::getAlpha() const -{ - return mAlpha; -} - Image* Image::SDLgetScaledImage(int width, int height) { // No scaling on incorrect new values. @@ -406,9 +341,9 @@ Image* Image::SDLgetScaledImage(int width, int height) Image* scaledImage = NULL; SDL_Surface* scaledSurface = NULL; - if (mImage) + if (mSDLSurface) { - scaledSurface = _SDLzoomSurface(mImage, + scaledSurface = _SDLzoomSurface(mSDLSurface, (double) width / getWidth(), (double) height / getHeight(), 1); @@ -421,7 +356,160 @@ Image* Image::SDLgetScaledImage(int width, int height) return scaledImage; } +Image *Image::_SDLload(SDL_Surface *tmpImage) +{ + if (!tmpImage) + return NULL; + + bool hasAlpha = false; + + // The alpha channel to be filled with alpha values + Uint8 *alphaChannel = new Uint8[tmpImage->w * tmpImage->h]; + + if (tmpImage->format->BitsPerPixel == 32) + { + // Figure out whether the image uses its alpha layer + for (int i = 0; i < tmpImage->w * tmpImage->h; ++i) + { + Uint8 r, g, b, a; + SDL_GetRGBA( + ((Uint32*) tmpImage->pixels)[i], + tmpImage->format, + &r, &g, &b, &a); + + if (a != 255) + hasAlpha = true; + + alphaChannel[i] = a; + } + } + + SDL_Surface *image; + + // Convert the surface to the current display format + if (hasAlpha) + image = SDL_DisplayFormatAlpha(tmpImage); + else + { + image = SDL_DisplayFormat(tmpImage); + + // We also delete the alpha channel since + // it's not used. + delete[] alphaChannel; + alphaChannel = NULL; + } + + if (!image) + { + logger->log("Error: Image convert failed."); + delete[] alphaChannel; + return NULL; + } + + return new Image(image, hasAlpha, alphaChannel); +} + #ifdef USE_OPENGL +Image *Image::_GLload(SDL_Surface *tmpImage) +{ + // Flush current error flag. + glGetError(); + + int width = tmpImage->w; + int height = tmpImage->h; + int realWidth = powerOfTwo(width); + int realHeight = powerOfTwo(height); + + if (realWidth < width || realHeight < height) + { + logger->log("Warning: image too large, cropping to %dx%d texture!", + tmpImage->w, tmpImage->h); + } + + // Make sure the alpha channel is not used, but copied to destination + SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE); + + // Determine 32-bit masks based on byte order + Uint32 rmask, gmask, bmask, amask; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0xff000000; + gmask = 0x00ff0000; + bmask = 0x0000ff00; + amask = 0x000000ff; +#else + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; +#endif + + SDL_Surface *oldImage = tmpImage; + tmpImage = SDL_CreateRGBSurface(SDL_SWSURFACE, realWidth, realHeight, + 32, rmask, gmask, bmask, amask); + + if (!tmpImage) + { + logger->log("Error, image convert failed: out of memory"); + return NULL; + } + + SDL_BlitSurface(oldImage, NULL, tmpImage, NULL); + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(mTextureType, texture); + + if (SDL_MUSTLOCK(tmpImage)) + SDL_LockSurface(tmpImage); + + glTexImage2D( + mTextureType, 0, 4, + tmpImage->w, tmpImage->h, + 0, GL_RGBA, GL_UNSIGNED_BYTE, + tmpImage->pixels); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + if (SDL_MUSTLOCK(tmpImage)) { + SDL_UnlockSurface(tmpImage); + } + + SDL_FreeSurface(tmpImage); + + GLenum error = glGetError(); + if (error) + { + std::string errmsg = "Unknown error"; + switch (error) + { + case GL_INVALID_ENUM: + errmsg = "GL_INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + errmsg = "GL_INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + errmsg = "GL_INVALID_OPERATION"; + break; + case GL_STACK_OVERFLOW: + errmsg = "GL_STACK_OVERFLOW"; + break; + case GL_STACK_UNDERFLOW: + errmsg = "GL_STACK_UNDERFLOW"; + break; + case GL_OUT_OF_MEMORY: + errmsg = "GL_OUT_OF_MEMORY"; + break; + } + logger->log("Error: Image GL import failed: %s", errmsg.c_str()); + return NULL; + } + + return new Image(texture, width, height, realWidth, realHeight); +} + void Image::setLoadAsOpenGL(bool useOpenGL) { Image::mUseOpenGL = useOpenGL; @@ -455,7 +543,7 @@ Image *Image::getSubImage(int x, int y, int width, int height) mTexWidth, mTexHeight); #endif - return new SubImage(this, mImage, x, y, width, height); + return new SubImage(this, mSDLSurface, x, y, width, height); } //============================================================================ @@ -469,6 +557,9 @@ SubImage::SubImage(Image *parent, SDL_Surface *image, { mParent->incRef(); + mHasAlphaChannel = mParent->hasAlphaChannel(); + mAlphaChannel = mParent->SDLgetAlphaChannel(); + // Set up the rectangle. mBounds.x = x; mBounds.y = y; @@ -496,7 +587,9 @@ SubImage::SubImage(Image *parent, GLuint image, SubImage::~SubImage() { // Avoid destruction of the image - mImage = 0; + mSDLSurface = 0; + // Avoid possible destruction of its alpha channel + mAlphaChannel = 0; #ifdef USE_OPENGL mGLImage = 0; #endif diff --git a/src/resources/image.h b/src/resources/image.h index f497f608..9c0f9da7 100644 --- a/src/resources/image.h +++ b/src/resources/image.h @@ -88,19 +88,15 @@ class Image : public Resource static Image *load(SDL_Surface *); /** - * Gets an scaled instance of an image. - * - * @param width The desired width of the scaled image. - * @param height The desired height of the scaled image. - * - * @return A new Image* object. + * Frees the resources created by SDL. */ - Image* SDLgetScaledImage(int width, int height); + virtual void unload(); /** - * Frees the resources created by SDL. + * Tells is the image is loaded */ - virtual void unload(); + bool isLoaded() + { return mLoaded; } /** * Returns the width of the image. @@ -115,12 +111,29 @@ class Image : public Resource { return mBounds.h; } /** - * Tells if the image was loade using OpenGL or SDL + * Tells if the image was loaded using OpenGL or SDL * @return true if OpenGL, false if SDL. */ bool isAnOpenGLOne() const; /** + * Tells if the image has got an alpha channel + * @return true if it's true, false otherwise. + */ + bool hasAlphaChannel(); + + /** + * Sets the alpha value of this image. + */ + virtual void setAlpha(float alpha); + + /** + * Returns the alpha value of this image. + */ + float getAlpha() const + { return mAlpha; } + + /** * Creates a new image with the desired clipping rectangle. * * @return <code>NULL</code> if creation failed and a valid @@ -128,17 +141,38 @@ class Image : public Resource */ virtual Image *getSubImage(int x, int y, int width, int height); + + // SDL only public functions + /** - * Sets the alpha value of this image. + * Gets an scaled instance of an image. + * + * @param width The desired width of the scaled image. + * @param height The desired height of the scaled image. + * + * @return A new Image* object. */ - virtual void setAlpha(float alpha); + Image* SDLgetScaledImage(int width, int height); /** - * Returns the alpha value of this image. + * Merges two image SDL_Surfaces together. This is for SDL use only, as + * reducing the number of surfaces that SDL has to render can cut down + * on the number of blit operations necessary, which in turn can help + * improve overall framerates. Don't use unless you are using it to + * reduce the number of overall layers that need to be drawn through SDL. */ - float getAlpha() const; + Image *SDLmerge(Image *image, int x, int y); + + /** + * Get the alpha Channel of a SDL surface. + */ + Uint8 *SDLgetAlphaChannel() const + { return mAlphaChannel; } #ifdef USE_OPENGL + + // OpenGL only public functions + /** * Sets the target image format. Use <code>false</code> for SDL and * <code>true</code> for OpenGL. @@ -150,20 +184,40 @@ class Image : public Resource static int getTextureType() { return mTextureType; } #endif - /** - * Merges two image SDL_Surfaces together. This is for SDL use only, as - * reducing the number of surfaces that SDL has to render can cut down - * on the number of blit operations necessary, which in turn can help - * improve overall framerates. Don't use unless you are using it to - * reduce the number of overall layers that need to be drawn through SDL. - */ - Image *merge(Image *image, int x, int y); - protected: + + // ----------------------- + // Generic protected members + // ----------------------- + + SDL_Rect mBounds; + bool mLoaded; + float mAlpha; + bool mHasAlphaChannel; + + // ----------------------- + // SDL protected members + // ----------------------- + + /** SDL Constructor */ + Image(SDL_Surface *image, bool hasAlphaChannel = false, + Uint8 *alphaChannel = NULL); + + /** SDL_Surface to SDL_Surface Image loader */ + static Image *_SDLload(SDL_Surface *tmpImage); + + SDL_Surface *mSDLSurface; + + /** Alpha Channel pointer used for 32bit based SDL surfaces */ + Uint8 *mAlphaChannel; + + // ----------------------- + // OpenGL protected members + // ----------------------- +#ifdef USE_OPENGL /** - * Constructor. + * OpenGL Constructor. */ -#ifdef USE_OPENGL Image(GLuint glimage, int width, int height, int texWidth, int texHeight); @@ -171,13 +225,9 @@ class Image : public Resource * Returns the first power of two equal or bigger than the input. */ static int powerOfTwo(int input); -#endif - Image(SDL_Surface *image); - SDL_Rect mBounds; - bool mLoaded; + static Image *_GLload(SDL_Surface *tmpImage); -#ifdef USE_OPENGL GLuint mGLImage; int mTexWidth, mTexHeight; @@ -185,8 +235,6 @@ class Image : public Resource static int mTextureType; static int mTextureSize; #endif - SDL_Surface *mImage; - float mAlpha; }; /** diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h index 0c87b585..6c033490 100644 --- a/src/resources/iteminfo.h +++ b/src/resources/iteminfo.h @@ -24,7 +24,7 @@ #include "resources/spritedef.h" -#include "being.h" +#include "player.h" #include <map> #include <string> diff --git a/src/resources/wallpaper.cpp b/src/resources/wallpaper.cpp index bc9728d0..f0e834b1 100644 --- a/src/resources/wallpaper.cpp +++ b/src/resources/wallpaper.cpp @@ -32,18 +32,19 @@ #include <physfs.h> #define WALLPAPER_FOLDER "graphics/images/" -#define WALLPAPER_BASE "login_wallpaper" +#define WALLPAPER_BASE "login_wallpaper.png" -struct WallpaperSize +struct WallpaperData { + std::string filename; int width; int height; }; -static std::vector<WallpaperSize> wallpaperSizes; +static std::vector<WallpaperData> wallpaperData; static bool haveBackup; // Is the backup (no size given) version available? -bool wallpaperCompare(WallpaperSize a, WallpaperSize b) +bool wallpaperCompare(WallpaperData a, WallpaperData b) { int aa = a.width * a.height; int ab = b.width * b.height; @@ -53,60 +54,82 @@ bool wallpaperCompare(WallpaperSize a, WallpaperSize b) void Wallpaper::loadWallpapers() { - wallpaperSizes.clear(); - - size_t baseLen = strlen(WALLPAPER_BASE); - haveBackup = false; + wallpaperData.clear(); char **imgs = PHYSFS_enumerateFiles(WALLPAPER_FOLDER); for (char **i = imgs; *i != NULL; i++) { - if (strncmp(*i, WALLPAPER_BASE, baseLen) == 0) + int width; + int height; + + // If the backup file is found, we tell it. + if (strncmp (*i, WALLPAPER_BASE, strlen(*i)) == 0) + haveBackup = true; + + // If the image format is terminated by: "_<width>x<height>.png" + // It is taken as a potential wallpaper. + + // First, get the base filename of the image: + std::string filename = *i; + int separator = filename.rfind("_"); + filename = filename.substr(0, separator); + + // Then, append the width and height search mask. + filename.append("_%dx%d.png"); + + if (sscanf(*i, filename.c_str(), &width, &height) == 2) { - int width; - int height; - - if (strlen(*i) == baseLen + 4) - { - if (haveBackup) - logger->log("Duplicate default wallpaper!"); - else - haveBackup = true; - } - else if (sscanf(*i, WALLPAPER_BASE "_%dx%d.png", - &width, &height) == 2) - { - WallpaperSize wp; - wp.width = width; - wp.height = height; - wallpaperSizes.push_back(wp); - } + WallpaperData wp; + wp.filename = WALLPAPER_FOLDER; + wp.filename.append(*i); + wp.width = width; + wp.height = height; + wallpaperData.push_back(wp); } } PHYSFS_freeList(imgs); - std::sort(wallpaperSizes.begin(), wallpaperSizes.end(), wallpaperCompare); + std::sort(wallpaperData.begin(), wallpaperData.end(), wallpaperCompare); } std::string Wallpaper::getWallpaper(int width, int height) { - std::vector<WallpaperSize>::iterator iter; - WallpaperSize wp; + std::vector<WallpaperData>::iterator iter; + WallpaperData wp; - for (iter = wallpaperSizes.begin(); iter != wallpaperSizes.end(); iter++) + // Wallpaper filename container + std::vector<std::string> wallPaperVector; + + for (iter = wallpaperData.begin(); iter != wallpaperData.end(); iter++) { wp = *iter; if (wp.width <= width && wp.height <= height) + wallPaperVector.push_back(wp.filename); + } + + + if (!wallPaperVector.empty()) + { + // If we've got more than one occurence of a valid wallpaper... + if (wallPaperVector.size() > 0) { - return std::string(strprintf(WALLPAPER_FOLDER WALLPAPER_BASE - "_%dx%d.png", wp.width, wp.height)); + // Return randomly a wallpaper between vector[0] and + // vector[vector.size() - 1] + srand((unsigned)time(0)); + return wallPaperVector + [int(wallPaperVector.size() * rand() / (RAND_MAX + 1.0))]; } + else // If there at least one, we return it + return wallPaperVector[0]; } + // Return the backup file if everything else failed... if (haveBackup) - return std::string(WALLPAPER_FOLDER WALLPAPER_BASE ".png"); + return std::string(WALLPAPER_FOLDER WALLPAPER_BASE); + // Return an empty string if everything else failed return std::string(); + } diff --git a/src/utils/gettext.h b/src/utils/gettext.h index 55e72555..4f85952c 100644 --- a/src/utils/gettext.h +++ b/src/utils/gettext.h @@ -26,10 +26,10 @@ #include "config.h" #endif -#if ENABLE_NLS - #include <libintl.h> +#if ENABLE_NLS + #define _(s) ((char const *)gettext(s)) #define N_(s) ((char const *)s) |