diff options
199 files changed, 6093 insertions, 2635 deletions
@@ -1,2 +1,3 @@ Bjørn Lindeijer <bjorn@lindeijer.nl> +Majin Sniper <sniper@livecd.janhome.net> Victor Fury <frictor@unknown> @@ -35,6 +35,11 @@ If you've cloned the Git repository, you will also need these tools to compile: * GNU gettext http://www.gnu.org/software/gettext/ * CVS http://www.nongnu.org/cvs/ (needed to run autopoint) +On some machines, it's been reported that this package is also needed. However, +you might not need it: + +* GNU libtool http://www.gnu.org/software/libtool/ + Installing these dependencies is distributions-specific, and we'll leave it to you to figure this out. diff --git a/Makefile.am b/Makefile.am index 2d7250cc..72c7461c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ desktopdir = $(datadir)/applications desktop_DATA = tmw.desktop # Extra files to include -EXTRA_DIST = $(desktop_DATA) +EXTRA_DIST = $(desktop_DATA) tmw.cbp # Autopoint m4 stuff ACLOCAL_AMFLAGS = -I m4 diff --git a/data/graphics/gui/CMakeLists.txt b/data/graphics/gui/CMakeLists.txt index 347e9f5e..cca412f2 100644 --- a/data/graphics/gui/CMakeLists.txt +++ b/data/graphics/gui/CMakeLists.txt @@ -13,9 +13,6 @@ SET (FILES default.png equip_bg.png gui.xml - hits_blue.png - hits_red.png - hits_yellow.png hscroll_left_default.png hscroll_left_highlight.png hscroll_left_pressed.png diff --git a/data/graphics/gui/Makefile.am b/data/graphics/gui/Makefile.am index d697ffd7..aaaac211 100644 --- a/data/graphics/gui/Makefile.am +++ b/data/graphics/gui/Makefile.am @@ -15,9 +15,6 @@ gui_DATA = \ deepbox.png \ default.png \ gui.xml \ - hits_blue.png \ - hits_red.png \ - hits_yellow.png \ hscroll_left_default.png \ hscroll_left_highlight.png \ hscroll_left_pressed.png \ diff --git a/data/graphics/gui/hits_blue.png b/data/graphics/gui/hits_blue.png Binary files differdeleted file mode 100644 index cfb04ab8..00000000 --- a/data/graphics/gui/hits_blue.png +++ /dev/null diff --git a/data/graphics/gui/hits_red.png b/data/graphics/gui/hits_red.png Binary files differdeleted file mode 100644 index 150f1c1e..00000000 --- a/data/graphics/gui/hits_red.png +++ /dev/null diff --git a/data/graphics/gui/hits_yellow.png b/data/graphics/gui/hits_yellow.png Binary files differdeleted file mode 100644 index 6975dfd5..00000000 --- a/data/graphics/gui/hits_yellow.png +++ /dev/null diff --git a/docs/HACKING.txt b/docs/HACKING.txt index f2ae8b2b..8c9c4dcb 100644 --- a/docs/HACKING.txt +++ b/docs/HACKING.txt @@ -115,20 +115,3 @@ necessary. * Whenever you add a new source file somewhere in ./src do not forget to add them in ./src/Makefile.am as well! -* ChangeLog file format: - - YYYY-MM-DD[space][space]Firstname[space]Lastname[space][space]<email@address> - [newline] - [tab]*[space]filename:[space]Comment - [newline] - - The last character on each line is at max at column 79. - - Example: - -1234-12-24 Some Body <mymail@mailserver.tld> - - * some/file: My comment. - * some/file, anotherfile: This is a pretty long comment and needs a - line break, to avoid characters at colums > 79. - diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 94688e66..babf1629 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -67,10 +67,10 @@ SET(SRCS gui/widgets/tab.h gui/widgets/tabbedarea.cpp gui/widgets/tabbedarea.h + gui/widgets/textpreview.cpp + gui/widgets/textpreview.h gui/browserbox.cpp gui/browserbox.h - gui/buddywindow.cpp - gui/buddywindow.h gui/button.cpp gui/button.h gui/buy.cpp @@ -91,8 +91,6 @@ SET(SRCS gui/chatinput.h gui/checkbox.cpp gui/checkbox.h - gui/color.cpp - gui/color.h gui/confirm_dialog.cpp gui/confirm_dialog.h gui/connection.cpp @@ -132,9 +130,11 @@ SET(SRCS gui/itempopup.cpp gui/itempopup.h gui/itemshortcutcontainer.cpp - gui/itemshortcutcontainer.h\ + gui/itemshortcutcontainer.h gui/item_amount.cpp gui/item_amount.h + gui/label.cpp + gui/label.h gui/linkhandler.h gui/listbox.cpp gui/listbox.h @@ -160,12 +160,16 @@ SET(SRCS gui/npc_text.h gui/ok_dialog.cpp gui/ok_dialog.h + gui/palette.cpp + gui/palette.h gui/partywindow.h gui/partywindow.cpp gui/passwordfield.cpp gui/passwordfield.h gui/playerbox.cpp gui/playerbox.h + gui/popup.cpp + gui/popup.h gui/popupmenu.cpp gui/popupmenu.h gui/progressbar.cpp @@ -211,12 +215,16 @@ SET(SRCS gui/shortcutcontainer.h gui/skill.cpp gui/skill.h + gui/skin.cpp + gui/skin.h gui/slider.cpp gui/slider.h gui/speechbubble.cpp gui/speechbubble.h gui/status.cpp gui/status.h + gui/storagewindow.cpp + gui/storagewindow.h gui/table.cpp gui/table.h gui/table_model.cpp @@ -227,6 +235,7 @@ SET(SRCS gui/textdialog.h gui/textfield.cpp gui/textfield.h + gui/textrenderer.h gui/trade.cpp gui/trade.h gui/truetypefont.cpp @@ -461,6 +470,8 @@ SET(SRCS textparticle.cpp textparticle.h tileset.h + units.cpp + units.h vector.cpp vector.h effectmanager.cpp diff --git a/src/Makefile.am b/src/Makefile.am index dc03e66a..cafe46a7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,6 +19,8 @@ tmw_SOURCES = gui/widgets/avatar.cpp \ gui/widgets/tab.h \ gui/widgets/tabbedarea.cpp \ gui/widgets/tabbedarea.h \ + gui/widgets/textpreview.cpp \ + gui/widgets/textpreview.h \ gui/browserbox.cpp \ gui/browserbox.h \ gui/button.cpp \ @@ -35,8 +37,6 @@ tmw_SOURCES = gui/widgets/avatar.cpp \ gui/chatinput.h \ gui/checkbox.cpp \ gui/checkbox.h \ - gui/color.cpp \ - gui/color.h \ gui/confirm_dialog.cpp \ gui/confirm_dialog.h \ gui/debugwindow.cpp \ @@ -73,6 +73,8 @@ tmw_SOURCES = gui/widgets/avatar.cpp \ gui/itemshortcutcontainer.h \ gui/item_amount.cpp \ gui/item_amount.h \ + gui/label.cpp \ + gui/label.h \ gui/linkhandler.h \ gui/listbox.cpp \ gui/listbox.h \ @@ -94,10 +96,14 @@ tmw_SOURCES = gui/widgets/avatar.cpp \ gui/npc_text.h \ gui/ok_dialog.cpp \ gui/ok_dialog.h \ + gui/palette.cpp \ + gui/palette.h \ gui/passwordfield.cpp \ gui/passwordfield.h \ gui/playerbox.cpp \ gui/playerbox.h \ + gui/popup.cpp \ + gui/popup.h \ gui/popupmenu.cpp \ gui/popupmenu.h \ gui/progressbar.cpp \ @@ -137,6 +143,8 @@ tmw_SOURCES = gui/widgets/avatar.cpp \ gui/shortcutwindow.h \ gui/shortcutcontainer.cpp \ gui/shortcutcontainer.h \ + gui/skin.cpp \ + gui/skin.h \ gui/slider.cpp \ gui/slider.h \ gui/speechbubble.cpp \ @@ -151,6 +159,7 @@ tmw_SOURCES = gui/widgets/avatar.cpp \ gui/textdialog.h \ gui/textfield.cpp \ gui/textfield.h \ + gui/textrenderer.h \ gui/trade.cpp \ gui/trade.h \ gui/truetypefont.cpp \ @@ -424,6 +433,8 @@ tmw_SOURCES += \ gui/skill.h \ gui/status.cpp \ gui/status.h \ + gui/storagewindow.cpp \ + gui/storagewindow.h \ net/ea/beinghandler.cpp \ net/ea/beinghandler.h \ net/ea/buysellhandler.cpp \ diff --git a/src/being.cpp b/src/being.cpp index 7f5a7d33..c0623255 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -45,6 +45,7 @@ #include "resources/resourcemanager.h" #include "gui/gui.h" +#include "gui/palette.h" #include "gui/speechbubble.h" #include "utils/dtor.h" @@ -63,9 +64,9 @@ const bool debug_movement = true; #define BEING_EFFECTS_FILE "effects.xml" #define HAIR_FILE "hair.xml" -int Being::instances = 0; +int Being::mNumberOfHairColors = 1; int Being::mNumberOfHairstyles = 1; -std::vector<AnimatedSprite*> Being::emotionSet; +std::vector<std::string> Being::hairColors; static const int X_SPEECH_OFFSET = 18; static const int Y_SPEECH_OFFSET = 60; @@ -79,6 +80,7 @@ Being::Being(int id, int job, Map *map): mWalkTime(0), #endif mEmotion(0), mEmotionTime(0), + mSpeechTime(0), mAttackSpeed(350), mAction(STAND), mJob(job), @@ -99,7 +101,6 @@ Being::Being(int id, int job, Map *map): #endif mHairColor(0), mGender(GENDER_UNSPECIFIED), - mSpeechTime(0), mPx(0), mPy(0), mStunMode(0), mSprites(VECTOREND_SPRITE, NULL), @@ -119,31 +120,8 @@ Being::Being(int id, int job, Map *map): mSpeechBubble = new SpeechBubble; - if (instances == 0) - { - // Setup emote sprites - for (int i = 0; i <= EmoteDB::getLast(); i++) - { - EmoteInfo info = EmoteDB::get(i); - - std::string file = "graphics/sprites/" + info.sprites.front()->sprite; - int variant = info.sprites.front()->variant; - emotionSet.push_back(AnimatedSprite::load(file, variant)); - } - - // Hairstyles are encoded as negative numbers. Count how far negative - // we can go. - int hairstyles = 1; - while (ItemDB::get(-hairstyles).getSprite(GENDER_MALE) != "error.xml") - { - hairstyles++; - } - mNumberOfHairstyles = hairstyles; - } - - instances++; mSpeech = ""; - mNameColor = 0x202020; + mNameColor = &guiPalette->getColor(Palette::CHAT); mText = 0; } @@ -158,13 +136,6 @@ Being::~Being() setMap(NULL); - instances--; - - if (instances == 0) - { - delete_all(emotionSet); - } - delete mSpeechBubble; delete mText; } @@ -383,7 +354,7 @@ void Being::setSprite(int slot, int id, const std::string &color) mSpriteColors[slot] = color; } -void Being::setSpeech(const std::string &text, Uint32 time) +void Being::setSpeech(const std::string &text, int time) { mSpeech = text; @@ -425,43 +396,69 @@ void Being::setSpeech(const std::string &text, Uint32 time) mSpeechTime = time <= SPEECH_MAX_TIME ? time : SPEECH_MAX_TIME; } -void Being::takeDamage(int amount) +void Being::takeDamage(Being *attacker, int amount, AttackType type) { gcn::Font *font; - std::string damage = amount ? toString(amount) : "miss"; + std::string damage = amount ? toString(amount) : type == FLEE ? + "dodge" : "miss"; + const gcn::Color* color; + + font = gui->getInfoParticleFont(); // Selecting the right color - if (damage == "miss") - font = hitYellowFont; - else + if (type == CRITICAL || type == FLEE) { - if (getType() == MONSTER) - font = hitBlueFont; + color = &guiPalette->getColor(Palette::HIT_CRITICAL); + } + else if (!amount) + { + if (attacker == player_node) + { + // This is intended to be the wrong direction to visually + // differentiate between hits and misses + color = &guiPalette->getColor(Palette::HIT_MONSTER_PLAYER); + } else - font = hitRedFont; + { + color = &guiPalette->getColor(Palette::MISS); + } + } + else if (getType() == MONSTER) + { + color = &guiPalette->getColor(Palette::HIT_PLAYER_MONSTER); + } + else + { + color = &guiPalette->getColor(Palette::HIT_MONSTER_PLAYER); } // Show damage number - particleEngine->addTextSplashEffect(damage, 255, 255, 255, font, + particleEngine->addTextSplashEffect(damage, #ifdef TMWSERV_SUPPORT (int) mPos.x + 16, - (int) mPos.y + 16); + (int) mPos.y + 16, #else - mPx + 16, mPy + 16); + mPx + 16, mPy + 16, #endif - effectManager->trigger(26, this); -} - -void Being::showCrit() -{ - effectManager->trigger(28, this); + color, font, true); + if (amount > 0) + { + if (type != CRITICAL) + { + effectManager->trigger(26, this); + } + else + { + effectManager->trigger(28, this); + } + } } #ifdef TMWSERV_SUPPORT void Being::handleAttack() #else -void Being::handleAttack(Being *victim, int damage) +void Being::handleAttack(Being *victim, int damage, AttackType type) #endif { setAction(Being::ATTACK); @@ -507,13 +504,10 @@ void Being::setAction(Action action, int attackType) break; case ATTACK: if (mEquippedWeapon) - { currentAction = mEquippedWeapon->getAttackType(); - } else - { currentAction = ACTION_ATTACK; - } + for (int i = 0; i < VECTOREND_SPRITE; i++) { if (mSprites[i]) @@ -584,21 +578,13 @@ SpriteDirection Being::getSpriteDirection() const SpriteDirection dir; if (mDirection & UP) - { dir = DIRECTION_UP; - } else if (mDirection & DOWN) - { dir = DIRECTION_DOWN; - } else if (mDirection & RIGHT) - { dir = DIRECTION_RIGHT; - } else - { - dir = DIRECTION_LEFT; - } + dir = DIRECTION_LEFT; return dir; } @@ -645,7 +631,7 @@ void Being::logic() if (mSpeechTime > 0) mSpeechTime--; - // Remove text if speech boxes aren't being used + // Remove text and speechbubbles if speech boxes aren't being used if (mSpeechTime == 0 && mText) { delete mText; @@ -749,9 +735,7 @@ void Being::draw(Graphics *graphics, int offsetX, int offsetY) const #endif if (mUsedTargetCursor) - { mUsedTargetCursor->draw(graphics, px, py); - } for (int i = 0; i < VECTOREND_SPRITE; i++) { @@ -774,39 +758,44 @@ void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) return; #ifdef TMWSERV_SUPPORT - const int px = (int) mPos.x + offsetX + 3; - const int py = (int) mPos.y + offsetY - 60; + const int px = (int) mPos.x + offsetX; + const int py = (int) mPos.y + offsetY - 64; #else - const int px = mPx + offsetX + 3; - const int py = mPy + offsetY - 60; + const int px = mPx - offsetX; + const int py = mPy - offsetY - 64; #endif const int emotionIndex = mEmotion - 1; if (emotionIndex >= 0 && emotionIndex <= EmoteDB::getLast()) - emotionSet[emotionIndex]->draw(graphics, px, py); + EmoteDB::getAnimation(emotionIndex)->draw(graphics, px, py); } void Being::drawSpeech(int offsetX, int offsetY) { #ifdef TMWSERV_SUPPORT - int px = (int) mPos.x + offsetX; - int py = (int) mPos.y + offsetY; + int px = (int) mPos.x - offsetX; + int py = (int) mPos.y - offsetY; #else - const int px = mPx + offsetX; - const int py = mPy + offsetY; + const int px = mPx - offsetX; + const int py = mPy - offsetY; #endif const int speech = (int) config.getValue("speech", NAME_IN_BUBBLE); // Draw speech above this being - if (mSpeechTime > 0 && (speech == NAME_IN_BUBBLE || - speech == NO_NAME_IN_BUBBLE)) + if (mSpeechTime == 0) + { + if (mSpeechBubble->isVisible()) + mSpeechBubble->setVisible(false); + } + else if (mSpeechTime > 0 && (speech == NAME_IN_BUBBLE || + speech == NO_NAME_IN_BUBBLE)) { const bool showName = (speech == NAME_IN_BUBBLE); if (mText) { delete mText; - mText = 0; + mText = NULL; } mSpeechBubble->setCaption(showName ? mName : "", mNameColor); @@ -822,24 +811,24 @@ void Being::drawSpeech(int offsetX, int offsetY) else if (mSpeechTime > 0 && speech == TEXT_OVERHEAD) { mSpeechBubble->setVisible(false); + // don't introduce a memory leak if (mText) delete mText; mText = new Text(mSpeech, mPx + X_SPEECH_OFFSET, mPy - Y_SPEECH_OFFSET, - gcn::Graphics::CENTER, gcn::Color(255, 255, 255)); + gcn::Graphics::CENTER, + &guiPalette->getColor(Palette::PARTICLE)); } else if (speech == NO_SPEECH) { mSpeechBubble->setVisible(false); + if (mText) delete mText; + mText = NULL; } - else if (mSpeechTime == 0) - { - mSpeechBubble->setVisible(false); - } } Being::Type Being::getType() const @@ -908,24 +897,18 @@ int Being::getOffset(char pos, char neg) const { // Check whether we're walking in the requested direction if (mAction != WALK || !(mDirection & (pos | neg))) - { return 0; - } int offset = (get_elapsed_time(mWalkTime) * 32) / mWalkSpeed; // We calculate the offset _from_ the _target_ location offset -= 32; if (offset > 0) - { offset = 0; - } // Going into negative direction? Invert the offset. if (mDirection & pos) - { offset = -offset; - } return offset; } @@ -1057,58 +1040,42 @@ void Being::internalTriggerEffect(int effectId, bool sfx, bool gfx) } } - - - -static int hairStylesNr; -static int hairColorsNr; -static std::vector<std::string> hairColors; - -static void initializeHair(); - -int Being::getHairStylesNr() +int Being::getHairStyleCount() { - initializeHair(); - return hairStylesNr; + return mNumberOfHairstyles; } -int Being::getHairColorsNr() +int Being::getHairColorCount() { - initializeHair(); - return hairColorsNr; + return mNumberOfHairColors; } std::string Being::getHairColor(int index) { - initializeHair(); - if (index < 0 || index >= hairColorsNr) + if (index < 0 || index >= mNumberOfHairColors) return "#000000"; return hairColors[index]; } -static bool hairInitialized = false; - -static void initializeHair() +void Being::load() { - if (hairInitialized) - return; - - // Hairstyles are encoded as negative numbers. Count how far negative we - // can go. - int hairstylesCtr = -1; - while (ItemDB::get(hairstylesCtr).getSprite(GENDER_MALE) != "error.xml") - --hairstylesCtr; + // Hairstyles are encoded as negative numbers. Count how far negative + // we can go. + int hairstyles = 1; - hairStylesNr = -hairstylesCtr; // done. - if (hairStylesNr == 0) - hairStylesNr = 1; // No hair style -> no hair - - hairColorsNr = 0; + while (ItemDB::get(-hairstyles).getSprite(GENDER_MALE) != "error.xml") + { + hairstyles++; + } + mNumberOfHairstyles = hairstyles; XML::Document doc(HAIR_FILE); xmlNodePtr root = doc.rootNode(); + // Add an initial hair color + hairColors.resize(1, "#000000"); + if (!root || !xmlStrEqual(root->name, BAD_CAST "colors")) { logger->log("Error loading being hair configuration file"); @@ -1121,20 +1088,13 @@ static void initializeHair() std::string value = XML::getProperty(node, "value", ""); if (index >= 0 && !value.empty()) { - if (index >= hairColorsNr) { - hairColorsNr = index + 1; - hairColors.resize(hairColorsNr, "#000000"); + if (index >= mNumberOfHairColors) { + mNumberOfHairColors = index + 1; + hairColors.resize(mNumberOfHairColors, "#000000"); } hairColors[index] = value; } } } - } // done initializing - - if (hairColorsNr == 0) { // No colors -> black only - hairColorsNr = 1; - hairColors.resize(hairColorsNr, "#000000"); } - - hairInitialized = 1; } diff --git a/src/being.h b/src/being.h index a704b3df..e9805d9b 100644 --- a/src/being.h +++ b/src/being.h @@ -134,22 +134,32 @@ class Being : public Sprite NUM_SPEECH }; + enum AttackType + { + HIT = 0x00, + CRITICAL = 0x0a, + MULTI = 0x08, + REFLECT = 0x04, + FLEE = 0x0b + }; + /** * Directions, to be used as bitmask values */ enum { DOWN = 1, LEFT = 2, UP = 4, RIGHT = 8 }; #ifdef EATHENA_SUPPORT - Uint16 mX, mY; /**< Tile coordinates */ - Uint16 mFrame; - Uint16 mWalkTime; + Uint16 mX, mY; /**< Tile coordinates */ + int mFrame; + int mWalkTime; #endif - Uint8 mEmotion; /**< Currently showing emotion */ - Uint8 mEmotionTime; /**< Time until emotion disappears */ + int mEmotion; /**< Currently showing emotion */ + int mEmotionTime; /**< Time until emotion disappears */ + int mSpeechTime; - Uint16 mAttackSpeed; /**< Attack speed */ - Action mAction; /**< Action the being is performing */ - Uint16 mJob; /**< Job (player job, npc, monster, creature ) */ + int mAttackSpeed; /**< Attack speed */ + Action mAction; /**< Action the being is performing */ + Uint16 mJob; /**< Job (player job, npc, monster, creature ) */ /** * Constructor. @@ -197,30 +207,28 @@ class Being : public Sprite * @param text The text that should appear. * @param time The amount of time the text should stay in milliseconds. */ - void setSpeech(const std::string &text, Uint32 time = 500); + void setSpeech(const std::string &text, int time = 500); /** * Puts a damage bubble above this being. * - * @param amount The amount of damage. + * @param attacker the attacking being + * @param damage the amount of damage recieved (0 means miss) + * @param type the attack type */ - virtual void takeDamage(int amount); - - /** - * Puts a crit notification bubble above this being. - */ - virtual void showCrit(); + virtual void takeDamage(Being *attacker, int damage, AttackType type); /** * Handles an attack of another being by this being. * - * @param victim The attacked being. - * @param damage The amount of damage dealt (0 means miss). + * @param victim the victim being + * @param damage the amount of damage dealt (0 means miss) + * @param type the attack type */ #ifdef TMWSERV_SUPPORT virtual void handleAttack(); #else - virtual void handleAttack(Being *victim, int damage); + virtual void handleAttack(Being *victim, int damage, AttackType type); #endif /** @@ -240,14 +248,12 @@ class Being : public Sprite /** * Gets the hair color for this being. */ - int getHairColor() const - { return mHairColor; } + int getHairColor() const { return mHairColor; } /** * Gets the hair style for this being. */ - int getHairStyle() const - { return mHairStyle; } + int getHairStyle() const { return mHairStyle; } /** * Get the number of hairstyles implemented @@ -324,12 +330,12 @@ class Being : public Sprite /** * Gets the being id. */ - Uint32 getId() const { return mId; } + int getId() const { return mId; } /** * Sets the sprite id. */ - void setId(Uint32 id) { mId = id; } + void setId(int id) { mId = id; } /** * Sets the map the being is on @@ -479,8 +485,6 @@ class Being : public Sprite */ void untarget() { mUsedTargetCursor = NULL; } - AnimatedSprite* getEmote(int index) { return emotionSet[index]; } - void setEmote(Uint8 emotion, Uint8 emote_time) { mEmotion = emotion; @@ -523,15 +527,17 @@ class Being : public Sprite // Target cursor being used by the being Image *mTargetCursor; - static int getHairColorsNr(); + static int getHairColorCount(); - static int getHairStylesNr(); + static int getHairStyleCount(); static std::string getHairColor(int index); virtual AnimatedSprite* getSprite(int index) const { return mSprites[index]; } + static void load(); + protected: /** * Sets the new path for this being. @@ -578,7 +584,7 @@ class Being : public Sprite */ virtual void handleStatusEffect(StatusEffect *effect, int effectId); - Uint32 mId; /**< Unique sprite id */ + int mId; /**< Unique sprite id */ Uint8 mDirection; /**< Facing direction */ #ifdef TMWSERV_SUPPORT Uint8 mSpriteDirection; /**< Facing direction */ @@ -592,6 +598,8 @@ class Being : public Sprite /** Engine-related infos about weapon. */ const ItemInfo* mEquippedWeapon; + static std::vector<std::string> hairColors; + static int mNumberOfHairColors; /** Number of hair colors in use */ static int mNumberOfHairstyles; /** Number of hair styles in use */ Path mPath; @@ -600,12 +608,11 @@ class Being : public Sprite int mHairStyle; int mHairColor; Gender mGender; - Uint32 mSpeechTime; - Sint32 mPx, mPy; /**< Pixel coordinates */ + int mPx, mPy; /**< Pixel coordinates */ Uint16 mStunMode; /**< Stun mode; zero if not stunned */ std::set<int> mStatusEffects; /**< set of active status effects */ - gcn::Color mNameColor; + const gcn::Color* mNameColor; std::vector<AnimatedSprite*> mSprites; std::vector<int> mSpriteIDs; @@ -641,9 +648,6 @@ class Being : public Sprite // Target cursor being used SimpleAnimation* mUsedTargetCursor; - - static int instances; /**< Number of Being instances */ - static std::vector<AnimatedSprite*> emotionSet; /**< Emoticons used by beings */ }; #endif diff --git a/src/beingmanager.cpp b/src/beingmanager.cpp index d2cac6ad..643d887f 100644 --- a/src/beingmanager.cpp +++ b/src/beingmanager.cpp @@ -66,6 +66,11 @@ BeingManager::BeingManager(Network *network): } #endif +BeingManager::~BeingManager() +{ + clear(); +} + void BeingManager::setMap(Map *map) { mMap = map; @@ -82,7 +87,7 @@ void BeingManager::setPlayer(LocalPlayer *player) #ifdef TMWSERV_SUPPORT Being* BeingManager::createBeing(int id, int type, int subtype) #else -Being* BeingManager::createBeing(Uint32 id, Uint16 job) +Being *BeingManager::createBeing(int id, Uint16 job) #endif { Being *being; @@ -103,17 +108,17 @@ Being* BeingManager::createBeing(Uint32 id, Uint16 job) assert(false); } #else - if (job < 10) + if (job <= 25 || (job >= 4001 && job <= 4049)) being = new Player(id, job, mMap); - else if (job >= 50 && job < 1002) + else if (job >= 46 && job <= 1000) being = new NPC(id, job, mMap, mNetwork); - else if (job >= 1002 && job < 1500) + else if (job > 1000 && job <= 2000) being = new Monster(id, job, mMap); else being = new Being(id, job, mMap); // Player or NPC - if (job < 1002) + if (job <= 1000 || (job >= 4001 && job <= 4049)) { MessageOut outMsg(mNetwork); outMsg.writeInt16(0x0094); @@ -135,7 +140,7 @@ void BeingManager::destroyBeing(Being *being) delete being; } -Being* BeingManager::findBeing(Uint32 id) +Being *BeingManager::findBeing(int id) { for (BeingIterator i = mBeings.begin(); i != mBeings.end(); i++) { @@ -147,7 +152,7 @@ Being* BeingManager::findBeing(Uint32 id) return NULL; } -Being* BeingManager::findBeing(Uint16 x, Uint16 y, Being::Type type) +Being *BeingManager::findBeing(int x, int y, Being::Type type) { beingFinder.x = x; beingFinder.y = y; @@ -158,7 +163,7 @@ Being* BeingManager::findBeing(Uint16 x, Uint16 y, Being::Type type) return (i == mBeings.end()) ? NULL : *i; } -Being* BeingManager::findBeingByPixel(Uint16 x, Uint16 y) +Being *BeingManager::findBeingByPixel(int x, int y) { BeingIterator itr = mBeings.begin(); BeingIterator itr_end = mBeings.end(); @@ -184,20 +189,19 @@ Being* BeingManager::findBeingByPixel(Uint16 x, Uint16 y) return NULL; } -Being* BeingManager::findBeingByName(std::string name, Being::Type type) +Being *BeingManager::findBeingByName(std::string name, Being::Type type) { for (BeingIterator i = mBeings.begin(); i != mBeings.end(); i++) { Being *being = (*i); - if (being->getName() == name - && (type == Being::UNKNOWN - || type == being->getType())) + if (being->getName() == name && + (type == Being::UNKNOWN || type == being->getType())) return being; } return NULL; } -Beings& BeingManager::getAll() +Beings &BeingManager::getAll() { return mBeings; } @@ -228,20 +232,16 @@ void BeingManager::logic() void BeingManager::clear() { if (player_node) - { mBeings.remove(player_node); - } delete_all(mBeings); mBeings.clear(); if (player_node) - { mBeings.push_back(player_node); - } } -Being* BeingManager::findNearestLivingBeing(Uint16 x, Uint16 y, int maxdist, +Being *BeingManager::findNearestLivingBeing(int x, int y, int maxdist, Being::Type type) { Being *closestBeing = NULL; @@ -274,8 +274,7 @@ Being* BeingManager::findNearestLivingBeing(Uint16 x, Uint16 y, int maxdist, if ((being->getType() == type || type == Being::UNKNOWN) && (d < dist || closestBeing == NULL) // it is closer - && being->mAction != Being::DEAD // no dead beings - ) + && being->mAction != Being::DEAD) // no dead beings { dist = d; closestBeing = being; @@ -285,7 +284,7 @@ Being* BeingManager::findNearestLivingBeing(Uint16 x, Uint16 y, int maxdist, return (maxdist >= dist) ? closestBeing : NULL; } -Being* BeingManager::findNearestLivingBeing(Being *aroundBeing, int maxdist, +Being *BeingManager::findNearestLivingBeing(Being *aroundBeing, int maxdist, Being::Type type) { Being *closestBeing = NULL; @@ -313,8 +312,7 @@ Being* BeingManager::findNearestLivingBeing(Being *aroundBeing, int maxdist, if ((being->getType() == type || type == Being::UNKNOWN) && (d < dist || closestBeing == NULL) // it is closer && being->mAction != Being::DEAD // no dead beings - && being != aroundBeing - ) + && being != aroundBeing) { dist = d; closestBeing = being; @@ -323,3 +321,13 @@ Being* BeingManager::findNearestLivingBeing(Being *aroundBeing, int maxdist, return (maxdist >= dist) ? closestBeing : NULL; } + +bool BeingManager::hasBeing(Being *being) +{ + for (BeingIterator i = mBeings.begin(); i != mBeings.end(); i++) + { + if (being == *i) return true; + } + + return false; +} diff --git a/src/beingmanager.h b/src/beingmanager.h index 726a73ec..05821bcf 100644 --- a/src/beingmanager.h +++ b/src/beingmanager.h @@ -40,6 +40,8 @@ class BeingManager BeingManager(Network *network); #endif + ~BeingManager(); + /** * Sets the map on which beings are created */ @@ -56,7 +58,7 @@ class BeingManager #ifdef TMWSERV_SUPPORT Being *createBeing(int id, int type, int subtype); #else - Being *createBeing(Uint32 id, Uint16 job); + Being *createBeing(int id, Uint16 job); #endif /** @@ -67,13 +69,13 @@ class BeingManager /** * Returns a specific id Being. */ - Being* findBeing(Uint32 id); + Being *findBeing(int id); /** * Returns a being at specific coordinates. */ - Being* findBeing(Uint16 x, Uint16 y, Being::Type type = Being::UNKNOWN); - Being* findBeingByPixel(Uint16 x, Uint16 y); + Being *findBeing(int x, int y, Being::Type type = Being::UNKNOWN); + Being *findBeingByPixel(int x, int y); /** * Returns a being nearest to specific coordinates. @@ -84,21 +86,21 @@ class BeingManager * no being is returned. * @param type The type of being to look for. */ - Being* findNearestLivingBeing(Uint16 x, Uint16 y, int maxdist, + Being *findNearestLivingBeing(int x, int y, int maxdist, Being::Type type = Being::UNKNOWN); - /** - * Finds a being by name and (optionally) by type. - */ - Being* findBeingByName(std::string name, Being::Type type = Being::UNKNOWN); - - /** - * Returns a being nearest to another being. - * - * \param maxdist maximal distance. If minimal distance is larger, - * no being is returned - */ - Being* findNearestLivingBeing(Being *aroundBeing, int maxdist, + /** + * Finds a being by name and (optionally) by type. + */ + Being *findBeingByName(std::string name, Being::Type type = Being::UNKNOWN); + + /** + * Returns a being nearest to another being. + * + * \param maxdist maximal distance. If minimal distance is larger, + * no being is returned + */ + Being *findNearestLivingBeing(Being *aroundBeing, int maxdist, Being::Type type = Being::UNKNOWN); /** @@ -107,6 +109,14 @@ class BeingManager Beings& getAll(); /** + * Returns true if the given being is in the manager's list, false + * otherwise. + * + * \param being the being to search for + */ + bool hasBeing(Being *being); + + /** * Logic. */ void logic(); diff --git a/src/engine.cpp b/src/engine.cpp index 872ecd57..3b982694 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -44,8 +44,6 @@ #include "utils/stringutils.h" -char itemCurrenyQ[10] = "0"; - #ifdef TMWSERV_SUPPORT Engine::Engine(): mCurrentMap(NULL) diff --git a/src/floor_item.cpp b/src/floor_item.cpp index fbe606b4..000835ad 100644 --- a/src/floor_item.cpp +++ b/src/floor_item.cpp @@ -26,10 +26,10 @@ #include "resources/image.h" -FloorItem::FloorItem(unsigned int id, - unsigned int itemId, - unsigned short x, - unsigned short y, +FloorItem::FloorItem(int id, + int itemId, + int x, + int y, Map *map): mId(id), mX(x), @@ -51,7 +51,7 @@ FloorItem::~FloorItem() delete mItem; } -unsigned int FloorItem::getItemId() const +int FloorItem::getItemId() const { return mItem->getId(); } diff --git a/src/floor_item.h b/src/floor_item.h index 444c756a..7ca0f5a3 100644 --- a/src/floor_item.h +++ b/src/floor_item.h @@ -42,10 +42,10 @@ class FloorItem : public Sprite /** * Constructor. */ - FloorItem(unsigned int id, - unsigned int itemId, - unsigned short x, - unsigned short y, + FloorItem(int id, + int itemId, + int x, + int y, Map *map); /** @@ -56,22 +56,22 @@ class FloorItem : public Sprite /** * Returns instance id of this item. */ - unsigned int getId() const { return mId; } + int getId() const { return mId; } /** * Returns the item id. */ - unsigned int getItemId() const; + int getItemId() const; /** * Returns the x coordinate. */ - unsigned short getX() const { return mX; } + int getX() const { return mX; } /** * Returns the y coordinate. */ - unsigned short getY() const { return mY; } + int getY() const { return mY; } /** * Returns the pixel y coordinate. @@ -88,8 +88,8 @@ class FloorItem : public Sprite void draw(Graphics *graphics, int offsetX, int offsetY) const; private: - unsigned int mId; - unsigned short mX, mY; + int mId; + int mX, mY; Item *mItem; Sprites::iterator mSpriteIterator; Map *mMap; diff --git a/src/flooritemmanager.cpp b/src/flooritemmanager.cpp index 65556abb..65fb2146 100644 --- a/src/flooritemmanager.cpp +++ b/src/flooritemmanager.cpp @@ -29,8 +29,8 @@ FloorItemManager::~FloorItemManager() clear(); } -FloorItem* FloorItemManager::create(unsigned int id, unsigned int itemId, - unsigned short x, unsigned short y, Map *map) +FloorItem* FloorItemManager::create(int id, int itemId, + int x, int y, Map *map) { FloorItem *floorItem = new FloorItem(id, itemId, x, y, map); mFloorItems.push_back(floorItem); @@ -49,7 +49,7 @@ void FloorItemManager::clear() mFloorItems.clear(); } -FloorItem* FloorItemManager::findById(unsigned int id) +FloorItem *FloorItemManager::findById(int id) { FloorItemIterator i; for (i = mFloorItems.begin(); i != mFloorItems.end(); i++) @@ -63,8 +63,7 @@ FloorItem* FloorItemManager::findById(unsigned int id) return NULL; } -FloorItem* FloorItemManager::findByCoordinates(unsigned short x, - unsigned short y) +FloorItem *FloorItemManager::findByCoordinates(int x, int y) { FloorItemIterator i; for (i = mFloorItems.begin(); i != mFloorItems.end(); i++) diff --git a/src/flooritemmanager.h b/src/flooritemmanager.h index 3f96b587..704b39fd 100644 --- a/src/flooritemmanager.h +++ b/src/flooritemmanager.h @@ -32,15 +32,14 @@ class FloorItemManager public: ~FloorItemManager(); - FloorItem* create(unsigned int id, unsigned int itemId, - unsigned short x, unsigned short y, Map *map); + FloorItem* create(int id, int itemId, int x, int y, Map *map); void destroy(FloorItem *item); void clear(); - FloorItem* findById(unsigned int id); - FloorItem* findByCoordinates(unsigned short x, unsigned short y); + FloorItem* findById(int id); + FloorItem* findByCoordinates(int x, int y); private: typedef std::list<FloorItem*> FloorItems; diff --git a/src/game.cpp b/src/game.cpp index 79eaf9c5..b1542891 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -83,6 +83,8 @@ #include "gui/npcpostdialog.h" #include "gui/partywindow.h" #include "gui/quitdialog.h" +#else +#include "gui/storagewindow.h" #endif #ifdef TMWSERV_SUPPORT @@ -144,7 +146,9 @@ StatusWindow *statusWindow; MiniStatusWindow *miniStatusWindow; BuyDialog *buyDialog; SellDialog *sellDialog; +#ifdef EATHENA_SUPPORT BuySellDialog *buySellDialog; +#endif InventoryWindow *inventoryWindow; EmoteWindow *emoteWindow; NpcIntegerDialog *npcIntegerDialog; @@ -158,8 +162,9 @@ GuildWindow *guildWindow; MagicDialog *magicDialog; NpcPostDialog *npcPostDialog; PartyWindow *partyWindow; +#else +StorageWindow *storageWindow; #endif -Setup* setupWindow; Minimap *minimap; EquipmentWindow *equipmentWindow; TradeWindow *tradeWindow; @@ -189,9 +194,9 @@ namespace { { void action(const gcn::ActionEvent &event) { - if (event.getId() == "yes" || event.getId() == "ok") { + if (event.getId() == "yes" || event.getId() == "ok") done = true; - } + #ifdef EATHENA_SUPPORT exitConfirm = NULL; #endif @@ -224,13 +229,9 @@ Uint32 nextSecond(Uint32 interval, void *param) int get_elapsed_time(int start_time) { if (start_time <= tick_time) - { return (tick_time - start_time) * 10; - } else - { return (tick_time + (MAX_TIME - start_time)) * 10; - } } /** @@ -248,6 +249,10 @@ void createGuiWindows(Network *network) buyDialog = new BuyDialog; sellDialog = new SellDialog; tradeWindow = new TradeWindow; + npcTextDialog = new NpcTextDialog; + npcIntegerDialog = new NpcIntegerDialog; + npcListDialog = new NpcListDialog; + npcStringDialog = new NpcStringDialog; npcPostDialog = new NpcPostDialog(); magicDialog = new MagicDialog(); equipmentWindow = new EquipmentWindow(player_node->mEquipment.get()); @@ -258,45 +263,57 @@ void createGuiWindows(Network *network) chatWindow = new ChatWindow(network); buyDialog = new BuyDialog(network); sellDialog = new SellDialog(network); + buySellDialog = new BuySellDialog(network); tradeWindow = new TradeWindow(network); equipmentWindow = new EquipmentWindow; + npcTextDialog = new NpcTextDialog(network); + npcIntegerDialog = new NpcIntegerDialog(network); + npcListDialog = new NpcListDialog(network); + npcStringDialog = new NpcStringDialog(network); + storageWindow = new StorageWindow(network); #endif menuWindow = new MenuWindow; statusWindow = new StatusWindow(player_node); miniStatusWindow = new MiniStatusWindow; - buySellDialog = new BuySellDialog; inventoryWindow = new InventoryWindow; emoteWindow = new EmoteWindow; - npcTextDialog = new NpcTextDialog; - npcIntegerDialog = new NpcIntegerDialog; - npcListDialog = new NpcListDialog; - npcStringDialog = new NpcStringDialog; skillDialog = new SkillDialog; - setupWindow = new Setup; minimap = new Minimap; helpWindow = new HelpWindow; debugWindow = new DebugWindow; - itemShortcutWindow = new ShortcutWindow("ItemShortcut",new ItemShortcutContainer); - emoteShortcutWindow = new ShortcutWindow("emoteShortcut",new EmoteShortcutContainer); + itemShortcutWindow = new ShortcutWindow("ItemShortcut", + new ItemShortcutContainer); + emoteShortcutWindow = new ShortcutWindow("emoteShortcut", + new EmoteShortcutContainer); // Set initial window visibility chatWindow->setVisible((bool) config.getValue( chatWindow->getWindowName() + "Visible", true)); miniStatusWindow->setVisible((bool) config.getValue( - miniStatusWindow->getWindowName() + "Visible", true)); + miniStatusWindow->getPopupName() + "Visible", true)); buyDialog->setVisible(false); sellDialog->setVisible(false); minimap->setVisible((bool) config.getValue( minimap->getWindowName() + "Visible", true)); tradeWindow->setVisible(false); menuWindow->setVisible((bool) config.getValue( - menuWindow->getWindowName() + "Visible", true)); + menuWindow->getPopupName() + "Visible", true)); itemShortcutWindow->setVisible((bool) config.getValue( itemShortcutWindow->getWindowName() + "Visible", true)); emoteShortcutWindow->setVisible((bool) config.getValue( emoteShortcutWindow->getWindowName() + "Visible", true)); minimap->setVisible((bool) config.getValue( minimap->getWindowName() + "Visible", true)); +#ifdef EATHENA_SUPPORT + buySellDialog->setVisible(false); +#endif + npcTextDialog->setVisible(false); + npcIntegerDialog->setVisible(false); + npcListDialog->setVisible(false); + npcStringDialog->setVisible(false); +#ifdef EATHENA_SUPPORT + storageWindow->setVisible(false); +#endif if (config.getValue("logToChat", 0)) { @@ -316,7 +333,9 @@ void destroyGuiWindows() delete menuWindow; delete buyDialog; delete sellDialog; +#ifdef EATHENA_SUPPORT delete buySellDialog; +#endif delete inventoryWindow; delete emoteWindow; delete npcIntegerDialog; @@ -331,7 +350,6 @@ void destroyGuiWindows() delete partyWindow; #endif delete skillDialog; - delete setupWindow; delete minimap; delete equipmentWindow; delete tradeWindow; @@ -339,6 +357,9 @@ void destroyGuiWindows() delete debugWindow; delete itemShortcutWindow; delete emoteShortcutWindow; +#ifdef EATHENA_SUPPORT + delete storageWindow; +#endif } #ifdef TMWSERV_SUPPORT @@ -361,6 +382,7 @@ Game::Game(Network *network): mNpcHandler(new NPCHandler), mPlayerHandler(new PlayerHandler), mTradeHandler(new TradeHandler), + mLastTarget(Being::UNKNOWN), mLogicCounterId(0), mSecondsCounterId(0) { done = false; @@ -447,6 +469,8 @@ Game::Game(Network *network): engine->changeMap(map_path); #endif + + setupWindow->setInGame(true); } Game::~Game() @@ -457,10 +481,10 @@ Game::~Game() delete playerParty; #endif - delete player_node; destroyGuiWindows(); delete beingManager; + delete player_node; delete floorItemManager; delete channelManager; delete commandHandler; @@ -530,8 +554,11 @@ void Game::optionChanged(const std::string &name) { int fpsLimit = (int) config.getValue("fpslimit", 0); - // Calculate new minimum frame time - mMinFrameTime = fpsLimit ? 1000 / fpsLimit : 0; + // Calculate new minimum frame time. If one isn't set, use 60 FPS. + // (1000 / 60 is 16.66) Since the client can go well above the refresh + // rates for monitors now in OpenGL mode, this cutoff is done to help + // conserve on CPU time. + mMinFrameTime = fpsLimit ? 1000 / fpsLimit : 16; // Reset draw time to current time mDrawTime = tick_time * 10; @@ -598,8 +625,9 @@ void Game::logic() if (!disconnectedDialog) { disconnectedDialog = new OkDialog(_("Network Error"), - _("The connection to the server was lost, " - "the program will now quit")); + _("The connection to the " + "server was lost, the " + "program will now quit")); disconnectedDialog->addActionListener(&exitListener); disconnectedDialog->requestMoveToTop(); } @@ -625,7 +653,7 @@ void Game::handleInput() gcn::Window *requestedWindow = NULL; if (setupWindow->isVisible() && - keyboard.getNewKeyIndex() > keyboard.KEY_NO_VALUE) + keyboard.getNewKeyIndex() > keyboard.KEY_NO_VALUE) { keyboard.setNewKey((int) event.key.keysym.sym); keyboard.callbackNewKey(); @@ -663,47 +691,41 @@ void Game::handleInput() } } - if (keyboard.isKeyActive(keyboard.KEY_TOGGLE_CHAT) || - keyboard.isKeyActive(keyboard.KEY_OK)) - { - // Input chat window - if (!(chatWindow->isInputFocused() || - deathNotice || - weightNotice)) + if (!(chatWindow->isInputFocused() || deathNotice || weightNotice)) + if (keyboard.isKeyActive(keyboard.KEY_OK)) { #ifdef TMWSERV_SUPPORT // Don not focus chat input when quit dialog is active if (quitDialog != NULL && quitDialog->isVisible()) continue; #else - // Quit by pressing Enter if the exit confirm is there if (exitConfirm && keyboard.isKeyActive(keyboard.KEY_TOGGLE_CHAT)) done = true; #endif // Close the Browser if opened else if (helpWindow->isVisible() && - keyboard.isKeyActive(keyboard.KEY_OK)) + keyboard.isKeyActive(keyboard.KEY_OK)) helpWindow->setVisible(false); // Close the config window, cancelling changes if opened else if (setupWindow->isVisible() && - keyboard.isKeyActive(keyboard.KEY_OK)) + keyboard.isKeyActive(keyboard.KEY_OK)) setupWindow->action(gcn::ActionEvent(NULL, "cancel")); // Submits the text and proceeds to the next dialog else if (npcStringDialog->isVisible() && - keyboard.isKeyActive(keyboard.KEY_OK)) + keyboard.isKeyActive(keyboard.KEY_OK)) npcStringDialog->action(gcn::ActionEvent(NULL, "ok")); // Proceed to the next dialog option, or close the window else if (npcTextDialog->isVisible() && - keyboard.isKeyActive(keyboard.KEY_OK)) + keyboard.isKeyActive(keyboard.KEY_OK)) npcTextDialog->action(gcn::ActionEvent(NULL, "ok")); // Choose the currently highlighted dialogue option else if (npcListDialog->isVisible() && - keyboard.isKeyActive(keyboard.KEY_OK)) + keyboard.isKeyActive(keyboard.KEY_OK)) npcListDialog->action(gcn::ActionEvent(NULL, "ok")); // Submits the text and proceeds to the next dialog else if (npcIntegerDialog->isVisible() && - keyboard.isKeyActive(keyboard.KEY_OK)) + keyboard.isKeyActive(keyboard.KEY_OK)) npcIntegerDialog->action(gcn::ActionEvent(NULL, "ok")); /* else if (guildWindow->isVisible()) @@ -711,22 +733,12 @@ void Game::handleInput() // TODO: Check if a dialog is open and close it if so } */ - else if (!(keyboard.getKeyValue( - KeyboardConfig::KEY_TOGGLE_CHAT) == - keyboard.getKeyValue( - KeyboardConfig::KEY_OK) && - (helpWindow->isVisible() || - setupWindow->isVisible() || - npcStringDialog->isVisible() || - npcTextDialog->isVisible() || - npcListDialog->isVisible() || - npcIntegerDialog->isVisible()))) - { - chatWindow->requestChatFocus(); + } + if (keyboard.isKeyActive(keyboard.KEY_TOGGLE_CHAT)) + { + if (chatWindow->requestChatFocus()) used = true; - } } - } const int tKey = keyboard.getKeyIndex(event.key.keysym.sym); switch (tKey) @@ -1054,8 +1066,7 @@ void Game::handleInput() if (keyboard.isKeyActive(keyboard.KEY_ATTACK) || (joystick && joystick->buttonPressed(0))) { - Being *target = beingManager->findNearestLivingBeing(x, y, 20, - Being::MONSTER); + Being *target = NULL; bool newTarget = !keyboard.isKeyActive(keyboard.KEY_TARGET); // A set target has highest priority @@ -1072,59 +1083,50 @@ void Game::handleInput() default: break; } - // Attack priorioty is: Monster, Player, auto target - target = beingManager->findBeing(targetX, targetY, Being::MONSTER); - if (!target) - target = beingManager->findBeing(targetX, targetY, Being::PLAYER); + // Only auto target Monsters + target = beingManager->findNearestLivingBeing(targetX, targetY, + 20, Being::MONSTER); } player_node->attack(target, newTarget); } #endif - // Target the nearest player if 'q' is pressed - if ( keyboard.isKeyActive(keyboard.KEY_TARGET_PLAYER) && - !keyboard.isKeyActive(keyboard.KEY_TARGET) ) - { - Being *target = beingManager->findNearestLivingBeing(player_node, 20, Being::PLAYER); - - player_node->setTarget(target); - } - - // Target the nearest monster if 'a' pressed - if ((keyboard.isKeyActive(keyboard.KEY_TARGET_CLOSEST) || + // Target the nearest player/monster/npc + if ((keyboard.isKeyActive(keyboard.KEY_TARGET_PLAYER) || + keyboard.isKeyActive(keyboard.KEY_TARGET_CLOSEST) || + keyboard.isKeyActive(keyboard.KEY_TARGET_NPC) || (joystick && joystick->buttonPressed(3))) && !keyboard.isKeyActive(keyboard.KEY_TARGET)) { - Being *target = beingManager->findNearestLivingBeing( - x, y, 20, Being::MONSTER); - - player_node->setTarget(target); - } - - // Target the nearest npc if 'n' pressed - if ( keyboard.isKeyActive(keyboard.KEY_TARGET_NPC) && - !keyboard.isKeyActive(keyboard.KEY_TARGET) ) - { - Being *target = beingManager->findNearestLivingBeing( - x, y, 20, Being::NPC); - - player_node->setTarget(target); - } + Being::Type currentTarget = Being::UNKNOWN; + if (keyboard.isKeyActive(keyboard.KEY_TARGET_CLOSEST) || + (joystick && joystick->buttonPressed(3))) + currentTarget = Being::MONSTER; + else if (keyboard.isKeyActive(keyboard.KEY_TARGET_PLAYER)) + currentTarget = Being::PLAYER; + else if (keyboard.isKeyActive(keyboard.KEY_TARGET_NPC)) + currentTarget = Being::NPC; + + Being *target = beingManager->findNearestLivingBeing(player_node, + 20, currentTarget); + + if (target && (target != player_node->getTarget() || + currentTarget != mLastTarget)) + { + player_node->setTarget(target); + mLastTarget = currentTarget; + } + } else mLastTarget = Being::UNKNOWN; // Reset last target // Talk to the nearest NPC if 't' pressed if ( keyboard.isKeyActive(keyboard.KEY_TALK) ) { - if (!npcTextDialog->isVisible() && !npcListDialog->isVisible()) + if (!npcTextDialog->isVisible() && !npcListDialog->isVisible() && + !npcStringDialog->isVisible() && !npcIntegerDialog->isVisible()) { Being *target = player_node->getTarget(); - if (!target) - { - target = beingManager->findNearestLivingBeing( - x, y, 20, Being::NPC); - } - if (target) { if (target->getType() == Being::NPC) @@ -83,6 +83,8 @@ class Game : public ConfigListener MessageHandlerPtr mPostHandler; MessageHandlerPtr mTradeHandler; + int mLastTarget; + SDL_TimerID mLogicCounterId; SDL_TimerID mSecondsCounterId; }; diff --git a/src/graphics.cpp b/src/graphics.cpp index 4af7b723..b9bd9fa6 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -49,30 +49,25 @@ bool Graphics::setVideoMode(int w, int h, int bpp, bool fs, bool hwaccel) mFullscreen = fs; mHWAccel = hwaccel; - if (fs) { + if (fs) displayFlags |= SDL_FULLSCREEN; - } - if (hwaccel) { + if (hwaccel) displayFlags |= SDL_HWSURFACE | SDL_DOUBLEBUF; - } else { + else displayFlags |= SDL_SWSURFACE; - } mScreen = SDL_SetVideoMode(w, h, bpp, displayFlags); - if (!mScreen) { + if (!mScreen) return false; - } char videoDriverName[64]; - if (SDL_VideoDriverName(videoDriverName, 64)) { + if (SDL_VideoDriverName(videoDriverName, 64)) logger->log("Using video driver: %s", videoDriverName); - } - else { + else logger->log("Using video driver: unknown"); - } const SDL_VideoInfo *vi = SDL_GetVideoInfo(); @@ -103,9 +98,8 @@ bool Graphics::setVideoMode(int w, int h, int bpp, bool fs, bool hwaccel) bool Graphics::setFullscreen(bool fs) { - if (mFullscreen == fs) { + if (mFullscreen == fs) return true; - } return setVideoMode(mScreen->w, mScreen->h, mScreen->format->BitsPerPixel, fs, mHWAccel); @@ -127,7 +121,7 @@ bool Graphics::drawImage(Image *image, int x, int y) } bool Graphics::drawImage(Image *image, int srcX, int srcY, int dstX, int dstY, - int width, int height, bool) + int width, int height, bool) { // Check that preconditions for blitting are met. if (!mScreen || !image || !image->mImage) return false; @@ -149,7 +143,7 @@ bool Graphics::drawImage(Image *image, int srcX, int srcY, int dstX, int dstY, } void Graphics::drawImage(gcn::Image const *image, int srcX, int srcY, - int dstX, int dstY, int width, int height) + int dstX, int dstY, int width, int height) { ProxyImage const *srcImage = dynamic_cast< ProxyImage const * >(image); @@ -159,22 +153,34 @@ void Graphics::drawImage(gcn::Image const *image, int srcX, int srcY, void Graphics::drawImagePattern(Image *image, int x, int y, int w, int h) { - int iw = image->getWidth(); - int ih = image->getHeight(); - if (iw == 0 || ih == 0) return; + // Check that preconditions for blitting are met. + if (!mScreen || !image || !image->mImage) return; + + const int iw = image->getWidth(); + const int ih = image->getHeight(); + + if (iw == 0 || ih == 0) return; - int px = 0; // X position on pattern plane - int py = 0; // Y position on pattern plane + for (int py = 0; py < h; py += ih) // Y position on pattern plane + { + int dh = (py + ih >= h) ? h - py : ih; + int srcY = image->mBounds.y; + int dstY = y + py + mClipStack.top().yOffset; - while (py < h) { - while (px < w) { + for (int px = 0; px < w; px += iw) // X position on pattern plane + { int dw = (px + iw >= w) ? w - px : iw; - int dh = (py + ih >= h) ? h - py : ih; - drawImage(image, 0, 0, x + px, y + py, dw, dh); - px += iw; + int srcX = image->mBounds.x; + int dstX = x + px + mClipStack.top().xOffset; + + SDL_Rect dstRect; + SDL_Rect srcRect; + dstRect.x = dstX; dstRect.y = dstY; + srcRect.x = srcX; srcRect.y = srcY; + srcRect.w = dw; srcRect.h = dh; + + SDL_BlitSurface(image->mImage, &srcRect, mScreen, &dstRect); } - py += ih; - px = 0; } } diff --git a/src/graphics.h b/src/graphics.h index 172032dc..c4004ffc 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -48,6 +48,19 @@ struct SDL_Surface; */ struct ImageRect { + enum ImagePosition + { + UPPER_LEFT = 0, + UPPER_CENTER = 1, + UPPER_RIGHT = 2, + LEFT = 3, + CENTER = 4, + RIGHT = 5, + LOWER_LEFT = 6, + LOWER_CENTER = 7, + LOWER_RIGHT = 8, + }; + Image *grid[9]; }; diff --git a/src/gui/browserbox.cpp b/src/gui/browserbox.cpp index 7c0ae1a7..2f667237 100644 --- a/src/gui/browserbox.cpp +++ b/src/gui/browserbox.cpp @@ -24,8 +24,8 @@ #include <guichan/graphics.hpp> #include "browserbox.h" -#include "color.h" #include "linkhandler.h" +#include "palette.h" #include "truetypefont.h" BrowserBox::BrowserBox(unsigned int mode, bool opaque): @@ -225,6 +225,7 @@ struct MouseOverLink void BrowserBox::mousePressed(gcn::MouseEvent &event) { + if (!mLinkHandler) return; LinkIterator i = find_if(mLinks.begin(), mLinks.end(), MouseOverLink(event.getX(), event.getY())); @@ -243,18 +244,20 @@ void BrowserBox::mouseMoved(gcn::MouseEvent &event) void BrowserBox::draw(gcn::Graphics *graphics) { + if (!isVisible()) + return; + if (mOpaque) { - graphics->setColor(gcn::Color(BGCOLOR)); + graphics->setColor(guiPalette->getColor(Palette::BACKGROUND)); graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); } if (mSelectedLink >= 0) { - bool valid; if ((mHighMode & BACKGROUND)) { - graphics->setColor(gcn::Color(textColor->getColor('H', valid))); + graphics->setColor(guiPalette->getColor(Palette::HIGHLIGHT)); graphics->fillRectangle(gcn::Rectangle( mLinks[mSelectedLink].x1, mLinks[mSelectedLink].y1, @@ -265,7 +268,7 @@ void BrowserBox::draw(gcn::Graphics *graphics) if ((mHighMode & UNDERLINE)) { - graphics->setColor(gcn::Color(textColor->getColor('<', valid))); + graphics->setColor(guiPalette->getColor(Palette::HYPERLINK)); graphics->drawLine( mLinks[mSelectedLink].x1, mLinks[mSelectedLink].y2, @@ -279,11 +282,11 @@ void BrowserBox::draw(gcn::Graphics *graphics) int link = 0; TrueTypeFont *font = static_cast<TrueTypeFont*>(getFont()); - graphics->setColor(BLACK); + graphics->setColor(guiPalette->getColor(Palette::TEXT)); for (TextRowIterator i = mTextRows.begin(); i != mTextRows.end(); i++) { - int selColor = BLACK; - int prevColor = selColor; + const gcn::Color *selColor = &guiPalette->getColor(Palette::TEXT); + const gcn::Color *prevColor = selColor; std::string row = *(i); bool wrapped = false; x = 0; @@ -331,7 +334,7 @@ void BrowserBox::draw(gcn::Graphics *graphics) else { bool valid; - int rgb = textColor->getColor(c, valid); + const gcn::Color *col = &guiPalette->getColor(c, valid); if (c == '<') { const int size = mLinks[link].x2 - mLinks[link].x1; @@ -344,7 +347,7 @@ void BrowserBox::draw(gcn::Graphics *graphics) } if (valid) { - selColor = rgb; + selColor = col; } } start += 3; @@ -354,7 +357,7 @@ void BrowserBox::draw(gcn::Graphics *graphics) break; } } - graphics->setColor(gcn::Color(selColor)); + graphics->setColor(*selColor); } std::string::size_type len = diff --git a/src/gui/button.cpp b/src/gui/button.cpp index 1d3a04e4..f9e5e9dc 100644 --- a/src/gui/button.cpp +++ b/src/gui/button.cpp @@ -23,6 +23,7 @@ #include <guichan/font.hpp> #include "button.h" +#include "palette.h" #include "../configuration.h" #include "../graphics.h" @@ -72,9 +73,9 @@ Button::Button(const std::string& caption, const std::string &actionEventId, { init(); setActionEventId(actionEventId); - if (listener) { + + if (listener) addActionListener(listener); - } } void Button::init() @@ -93,8 +94,10 @@ void Button::init() { btn[mode] = resman->getImage(data[mode].file); a = 0; - for (y = 0; y < 3; y++) { - for (x = 0; x < 3; x++) { + for (y = 0; y < 3; y++) + { + for (x = 0; x < 3; x++) + { button[mode].grid[a] = btn[mode]->getSubImage( data[x].gridX, data[y].gridY, data[x + 1].gridX - data[x].gridX + 1, @@ -150,7 +153,7 @@ void Button::draw(gcn::Graphics *graphics) static_cast<Graphics*>(graphics)-> drawImageRect(0, 0, getWidth(), getHeight(), button[mode]); - graphics->setColor(getForegroundColor()); + graphics->setColor(guiPalette->getColor(Palette::TEXT)); int textX; int textY = getHeight() / 2 - getFont()->getHeight() / 2; diff --git a/src/gui/buy.cpp b/src/gui/buy.cpp index 53a586c3..0b572a23 100644 --- a/src/gui/buy.cpp +++ b/src/gui/buy.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/widgets/label.hpp> - #include "button.h" #include "buy.h" +#include "label.h" #include "scrollarea.h" #include "shop.h" #include "shoplistbox.h" @@ -56,29 +55,34 @@ BuyDialog::BuyDialog(Network *network): { setWindowName("Buy"); setResizable(true); + setCloseButton(true); setMinWidth(260); setMinHeight(230); - setDefaultSize(0, 0, 260, 230); + setDefaultSize(260, 230, ImageRect::CENTER); mShopItems = new ShopItems; mShopItemList = new ShopListBox(mShopItems, mShopItems); mScrollArea = new ScrollArea(mShopItemList); + mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mSlider = new Slider(1.0); - mQuantityLabel = new gcn::Label("0"); + mQuantityLabel = new Label(strprintf("%d / %d", mAmountItems, mMaxItems)); + mQuantityLabel->setAlignment(gcn::Graphics::CENTER); mMoneyLabel = new gcn::Label(strprintf(_("Price: %s / Total: %s"), "", "")); + mIncreaseButton = new Button("+", "+", this); mDecreaseButton = new Button("-", "-", this); mBuyButton = new Button(_("Buy"), "buy", this); mQuitButton = new Button(_("Quit"), "quit", this); - mItemDescLabel = new gcn::Label(strprintf(_("Description: %s"), "")); - mItemEffectLabel = new gcn::Label(strprintf(_("Effect: %s"), "")); + mAddMaxButton = new Button(_("Max"), "max", this); + mItemDescLabel = new Label(strprintf(_("Description: %s"), "")); + mItemEffectLabel = new Label(strprintf(_("Effect: %s"), "")); - mIncreaseButton->setSize(20, 20); - mDecreaseButton->setSize(20, 20); + mDecreaseButton->adjustSize(); + mDecreaseButton->setWidth(mIncreaseButton->getWidth()); - mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); mIncreaseButton->setEnabled(false); mDecreaseButton->setEnabled(false); mBuyButton->setEnabled(false); @@ -88,21 +92,26 @@ BuyDialog::BuyDialog(Network *network): mSlider->addActionListener(this); mShopItemList->addSelectionListener(this); - place(0, 0, mScrollArea, 5).setPadding(3); - place(0, 1, mQuantityLabel, 2); - place(2, 1, mSlider, 3); - place(0, 2, mMoneyLabel, 5); - place(0, 3, mItemEffectLabel, 5); - place(0, 4, mItemDescLabel, 5); + ContainerPlacer place; + place = getPlacer(0, 0); + + place(0, 0, mScrollArea, 8, 5).setPadding(3); place(0, 5, mDecreaseButton); - place(1, 5, mIncreaseButton); - place(3, 5, mBuyButton); - place(4, 5, mQuitButton); + place(1, 5, mSlider, 3); + place(4, 5, mIncreaseButton); + place(5, 5, mQuantityLabel, 2); + place(7, 5, mAddMaxButton); + place(0, 6, mMoneyLabel, 8); + place(0, 7, mItemEffectLabel, 8); + place(0, 8, mItemDescLabel, 8); + place(6, 9, mBuyButton); + place(7, 9, mQuitButton); + Layout &layout = getLayout(); layout.setRowHeight(0, Layout::AUTO_SET); + center(); loadWindowState(); - setLocationRelativeTo(getParent()); } BuyDialog::~BuyDialog() @@ -138,15 +147,14 @@ void BuyDialog::addItem(int id, int amount, int price) void BuyDialog::action(const gcn::ActionEvent &event) { - int selectedItem = mShopItemList->getSelected(); - if (event.getId() == "quit") { - setVisible(false); - if (current_npc) current_npc->handleDeath(); + close(); return; } + int selectedItem = mShopItemList->getSelected(); + // The following actions require a valid selection if (selectedItem < 0 || selectedItem >= (int) mShopItems->getNumberOfElements()) @@ -171,6 +179,12 @@ void BuyDialog::action(const gcn::ActionEvent &event) mSlider->setValue(mAmountItems); updateButtonsAndLabels(); } + else if (event.getId() == "max") + { + mAmountItems = mMaxItems; + mSlider->setValue(mAmountItems); + updateButtonsAndLabels(); + } // TODO: Actually we'd have a bug elsewhere if this check for the number // of items to be bought ever fails, Bertram removed the assertions, is // there a better way to ensure this fails in an _obvious_ way in C++? @@ -257,3 +271,24 @@ void BuyDialog::updateButtonsAndLabels() Units::formatCurrency(price).c_str(), Units::formatCurrency(mMoney - price).c_str())); } + +void BuyDialog::logic() +{ + Window::logic(); + + if (!current_npc) setVisible(false); +} + +void BuyDialog::setVisible(bool visible) +{ + Window::setVisible(visible); + + if (visible) + requestFocus(); +} + +void BuyDialog::close() +{ + setVisible(false); + current_npc = 0; +} diff --git a/src/gui/buy.h b/src/gui/buy.h index 5510ccc6..200394b9 100644 --- a/src/gui/buy.h +++ b/src/gui/buy.h @@ -101,12 +101,27 @@ class BuyDialog : public Window, public gcn::ActionListener, */ void updateButtonsAndLabels(); + /** + * Check for current NPC + */ + void logic(); + + /** + * Sets the visibility of this window. + */ + void setVisible(bool visible); + + /** + * Closes the Buy Window, as well as resetting the current npc. + */ + void close(); private: #ifdef EATHENA_SUPPORT Network *mNetwork; #endif gcn::Button *mBuyButton; gcn::Button *mQuitButton; + gcn::Button *mAddMaxButton; gcn::Button *mIncreaseButton; gcn::Button *mDecreaseButton; ShopListBox *mShopItemList; @@ -124,4 +139,6 @@ class BuyDialog : public Window, public gcn::ActionListener, int mMaxItems; }; +extern BuyDialog *buyDialog; + #endif diff --git a/src/gui/buysell.cpp b/src/gui/buysell.cpp index 2d39eac7..c56f6435 100644 --- a/src/gui/buysell.cpp +++ b/src/gui/buysell.cpp @@ -24,11 +24,17 @@ #include "../npc.h" +#include "../net/messageout.h" +#ifdef EATHENA_SUPPORT +#include "../net/ea/protocol.h" +#endif + #include "../utils/gettext.h" -BuySellDialog::BuySellDialog(): - Window(_("Shop")) +BuySellDialog::BuySellDialog(Network *network): + Window(_("Shop")), mNetwork(network) { + setWindowName("BuySell"); Button *buyButton = 0; static const char *buttonNames[] = { N_("Buy"), N_("Sell"), N_("Cancel"), 0 @@ -46,19 +52,53 @@ BuySellDialog::BuySellDialog(): buyButton->requestFocus(); setContentSize(x, 2 * y + buyButton->getHeight()); - setLocationRelativeTo(getParent()); - requestFocus(); + center(); + setDefaultSize(); + loadWindowState(); +} + +void BuySellDialog::logic() +{ + Window::logic(); + + if (isVisible() && !current_npc) + setVisible(false); +} + +void BuySellDialog::setVisible(bool visible) +{ + Window::setVisible(visible); + + if (visible) + requestFocus(); } void BuySellDialog::action(const gcn::ActionEvent &event) { - if (event.getId() == "Buy") { - current_npc->buy(); - } else if (event.getId() == "Sell") { - current_npc->sell(); - } else if (event.getId() == "Cancel") { - if (current_npc) current_npc->handleDeath(); - } setVisible(false); + int action = 0; + + NPC::isTalking = false; + + if (event.getId() == "Buy") + { + action = 0; + } + else if (event.getId() == "Sell") + { + action = 1; + } + else if (event.getId() == "Cancel") + { + current_npc = 0; + return; + } + +#ifdef EATHENA_SUPPORT + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_BUY_SELL_REQUEST); + outMsg.writeInt32(current_npc); + outMsg.writeInt8(action); +#endif } diff --git a/src/gui/buysell.h b/src/gui/buysell.h index e3cdc52a..4b137554 100644 --- a/src/gui/buysell.h +++ b/src/gui/buysell.h @@ -26,6 +26,8 @@ #include "window.h" +class Network; + /** * A dialog to choose between buying or selling at a shop. * @@ -40,12 +42,24 @@ class BuySellDialog : public Window, public gcn::ActionListener * * @see Window::Window */ - BuySellDialog(); + BuySellDialog(Network *network); + + /** + * Check for current NPC + */ + void logic(); + + void setVisible(bool visible); /** * Called when receiving actions from the widgets. */ void action(const gcn::ActionEvent &event); + + private: + Network *mNetwork; }; +extern BuySellDialog *buySellDialog; + #endif diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp index e4f560ce..7c0e2ab2 100644 --- a/src/gui/char_select.cpp +++ b/src/gui/char_select.cpp @@ -23,11 +23,10 @@ #include <guichan/font.hpp> -#include <guichan/widgets/label.hpp> - #include "button.h" #include "char_select.h" #include "confirm_dialog.h" +#include "label.h" #include "ok_dialog.h" #include "playerbox.h" #include "textfield.h" @@ -63,6 +62,8 @@ #include "../utils/strprintf.h" #include "../utils/stringutils.h" +#define MAX_SLOT 2 + // Defined in main.cpp, used here for setting the char create dialog extern CharServerHandler charServerHandler; @@ -114,8 +115,8 @@ CharSelectDialog::CharSelectDialog(Network *network, mCancelButton = new Button(_("Cancel"), "cancel", this); mPreviousButton = new Button(_("Previous"), "previous", this); mNextButton = new Button(_("Next"), "next", this); - mNameLabel = new gcn::Label(strprintf(_("Name: %s"), "")); - mLevelLabel = new gcn::Label(strprintf(_("Level: %d"), 0)); + mNameLabel = new Label(strprintf(_("Name: %s"), "")); + mLevelLabel = new Label(strprintf(_("Level: %d"), 0)); #ifdef TMWSERV_SUPPORT mNewCharButton = new Button(_("New"), "new", this); mDelCharButton = new Button(_("Delete"), "delete", this); @@ -123,10 +124,10 @@ CharSelectDialog::CharSelectDialog(Network *network, mChangePasswordButton = new Button(_("Change Password"), "change_password", this); mChangeEmailButton = new Button(_("Change Email Address"), "change_email", this); - mAccountNameLabel = new gcn::Label(strprintf(_("Account: %s"), mLoginData->username.c_str())); - mNameLabel = new gcn::Label(strprintf(_("Name: %s"), "")); - mLevelLabel = new gcn::Label(strprintf(_("Level: %d"), 0)); - mMoneyLabel = new gcn::Label(strprintf(_("Money: %d"), 0)); + mAccountNameLabel = new Label(strprintf(_("Account: %s"), mLoginData->username.c_str())); + mNameLabel = new Label(strprintf(_("Name: %s"), "")); + mLevelLabel = new Label(strprintf(_("Level: %d"), 0)); + mMoneyLabel = new Label(strprintf(_("Money: %d"), 0)); // Control that shows the Player mPlayerBox = new PlayerBox; @@ -161,8 +162,8 @@ CharSelectDialog::CharSelectDialog(Network *network, mPlayerBox = new PlayerBox; mPlayerBox->setWidth(74); - mJobLevelLabel = new gcn::Label(strprintf(_("Job Level: %d"), 0)); - mMoneyLabel = new gcn::Label(strprintf(_("Money: %s"), mMoney.c_str())); + mJobLevelLabel = new Label(strprintf(_("Job Level: %d"), 0)); + mMoneyLabel = new Label(strprintf(_("Money: %s"), mMoney.c_str())); const std::string tempString = getFont()->getWidth(_("New")) < getFont()->getWidth(_("Delete")) ? @@ -189,7 +190,7 @@ CharSelectDialog::CharSelectDialog(Network *network, reflowLayout(250, 0); #endif - setLocationRelativeTo(getParent()); + center(); setVisible(true); mSelectButton->requestFocus(); updatePlayerInfo(); @@ -252,11 +253,11 @@ void CharSelectDialog::action(const gcn::ActionEvent &event) else if (event.getId() == "newdel") { // Check for a character - if (mCharInfo->getEntry() && n_character <= MAX_SLOT + 1) + if (mCharInfo->getEntry()) { new CharDeleteConfirm(this); } - else + else if (n_character <= MAX_SLOT) { // Start new character dialog CharCreateDialog *charCreateDialog = @@ -421,13 +422,13 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, rand() % numberOfHairColors); mNameField = new TextField(""); - mNameLabel = new gcn::Label(_("Name:")); + mNameLabel = new Label(_("Name:")); mNextHairColorButton = new Button(">", "nextcolor", this); mPrevHairColorButton = new Button("<", "prevcolor", this); - mHairColorLabel = new gcn::Label(_("Hair Color:")); + mHairColorLabel = new Label(_("Hair Color:")); mNextHairStyleButton = new Button(">", "nextstyle", this); mPrevHairStyleButton = new Button("<", "prevstyle", this); - mHairStyleLabel = new gcn::Label(_("Hair Style:")); + mHairStyleLabel = new Label(_("Hair Style:")); mCreateButton = new Button(_("Create"), "create", this); mCancelButton = new Button(_("Cancel"), "cancel", this); #ifdef TMWSERV_SUPPORT @@ -545,7 +546,7 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, reflowLayout(225, 0); #endif - setLocationRelativeTo(getParent()); + center(); setVisible(true); mNameField->requestFocus(); } diff --git a/src/gui/char_server.cpp b/src/gui/char_server.cpp index fa03bdc2..5cfcef4d 100644 --- a/src/gui/char_server.cpp +++ b/src/gui/char_server.cpp @@ -85,7 +85,7 @@ ServerSelectDialog::ServerSelectDialog(LoginData *loginData, int nextState): // Select first server mServerList->setSelected(1); - setLocationRelativeTo(getParent()); + center(); setVisible(true); mOkButton->requestFocus(); } diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp index a072704b..597eddfd 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -71,7 +71,7 @@ ChatWindow::ChatWindow(Network * network): setWindowName("Chat"); setResizable(true); - setDefaultSize(0, windowContainer->getHeight() - 123, 600, 123); + setDefaultSize(600, 123, ImageRect::LOWER_LEFT); setMinWidth(150); setMinHeight(90); @@ -121,7 +121,15 @@ ChatWindow::~ChatWindow() config.setValue("PartyPrefix", partyPrefix); config.setValue("ReturnToggles", mReturnToggles ? "1" : "0"); delete mRecorder; + delete mParty; #endif + delete mItemLinkHandler; +} + +void ChatWindow::resetToDefaultSize() +{ + mRecorder->resetToDefaultSize(); + Window::resetToDefaultSize(); } void ChatWindow::widgetResized(const gcn::Event &event) @@ -303,6 +311,42 @@ void ChatWindow::chatLog(std::string line, int own, std::string channelName, << (int) ((t / 60) % 60) << "] "; + // Check for item link + std::string::size_type start = tmp.text.find('['); + while (start != std::string::npos && tmp.text[start+1] != '@') + { + std::string::size_type end = tmp.text.find(']', start); + if (start+1 != end && end != std::string::npos) + { + // Catch multiple embeds and ignore them + // so it doesn't crash the client. + while ((tmp.text.find('[', start + 1) != std::string::npos) && + (tmp.text.find('[', start + 1) < end)) + { + start = tmp.text.find('[', start + 1); + } + + std::string temp = tmp.text.substr(start+1, end - start - 1); + + trim(temp); + + for (unsigned int i = 0; i < temp.size(); i++) + { + temp[i] = (char) tolower(temp[i]); + } + + const ItemInfo itemInfo = ItemDB::get(temp); + if (itemInfo.getName() != _("Unknown item")) + { + tmp.text.insert(end, "@@"); + tmp.text.insert(start+1, "|"); + tmp.text.insert(start+1, toString(itemInfo.getId())); + tmp.text.insert(start+1, "@@"); + } + } + start = tmp.text.find('[', start + 1); + } + line = lineColor + timeStr.str() + tmp.nick + tmp.text; // We look if the Vertical Scroll Bar is set at the max before @@ -370,7 +414,7 @@ void ChatWindow::action(const gcn::ActionEvent &event) } } -void ChatWindow::requestChatFocus() +bool ChatWindow::requestChatFocus() { // Make sure chatWindow is visible if (!isVisible()) @@ -385,9 +429,14 @@ void ChatWindow::requestChatFocus() mTmpVisible = true; } + // Don't do anything else if the input is already visible and has focus + if (mChatInput->isVisible() && mChatInput->isFocused()) + return false; + // Give focus to the chat input mChatInput->setVisible(true); mChatInput->requestFocus(); + return true; } bool ChatWindow::isInputFocused() @@ -488,38 +537,6 @@ void ChatWindow::chatSend(std::string &msg) } #endif - // Check for item link - std::string::size_type start = msg.find('['); - while (start != std::string::npos && msg[start+1] != '@') - { - std::string::size_type end = msg.find(']', start); - if (start+1 != end && end != std::string::npos) - { - // Catch multiple embeds and ignore them - // so it doesn't crash the client. - while ((msg.find('[', start + 1) != std::string::npos) && - (msg.find('[', start + 1) < end)) - { - start = msg.find('[', start + 1); - } - - std::string temp = msg.substr(start + 1, end - start - 1); - - toLower(trim(temp)); - - const ItemInfo itemInfo = ItemDB::get(temp); - if (itemInfo.getName() != _("Unknown item")) - { - msg.insert(end, "@@"); - msg.insert(start+1, "|"); - msg.insert(start+1, toString(itemInfo.getId())); - msg.insert(start+1, "@@"); - } - } - start = msg.find('[', start + 1); - } - - // Prepare ordinary message if (msg[0] != '/') { diff --git a/src/gui/chat.h b/src/gui/chat.h index 972ecf9a..3c553c67 100644 --- a/src/gui/chat.h +++ b/src/gui/chat.h @@ -100,6 +100,12 @@ class ChatWindow : public Window, void logic(); /** + * Reset the chat window and recorder window attached to it to their + * default positions. + */ + void resetToDefaultSize(); + + /** * Adds a line of text to our message list. Parameters: * * @param line Text message. @@ -129,8 +135,11 @@ class ChatWindow : public Window, /** * Request focus for typing chat message. + * + * \returns true if the input was shown + * false otherwise */ - void requestChatFocus(); + bool requestChatFocus(); /** * Checks whether ChatWindow is Focused or not. @@ -248,14 +257,15 @@ class ChatWindow : public Window, typedef std::list<std::string> History; typedef History::iterator HistoryIterator; - History mHistory; /**< Command history. */ - HistoryIterator mCurHist; /**< History iterator. */ - Recorder *mRecorder; /**< Recording class */ - bool mReturnToggles; /**< Marks whether <Return> toggles the chat log - or not */ + History mHistory; /**< Command history */ + HistoryIterator mCurHist; /**< History iterator */ + Recorder *mRecorder; /**< Recording class */ + bool mReturnToggles; /**< Marks whether <Return> toggles the chat + log or not */ #ifdef EATHENA_SUPPORT - char mPartyPrefix; /**< Messages beginning with the prefix are sent to - the party */ + char mPartyPrefix; /**< Messages beginning with the prefix are + sent to the party */ + Party *mParty; #endif }; diff --git a/src/gui/checkbox.cpp b/src/gui/checkbox.cpp index 7fa4fa81..5695a23f 100644 --- a/src/gui/checkbox.cpp +++ b/src/gui/checkbox.cpp @@ -20,6 +20,7 @@ */ #include "checkbox.h" +#include "palette.h" #include "../configuration.h" #include "../graphics.h" @@ -68,6 +69,18 @@ CheckBox::~CheckBox() } } +void CheckBox::draw(gcn::Graphics* graphics) +{ + drawBox(graphics); + + graphics->setFont(getFont()); + graphics->setColor(guiPalette->getColor(Palette::TEXT)); + + const int h = getHeight() + getHeight() / 2; + + graphics->drawText(getCaption(), h - 2, 0); +} + void CheckBox::drawBox(gcn::Graphics* graphics) { Image *box; diff --git a/src/gui/checkbox.h b/src/gui/checkbox.h index 20adb43c..dd59493c 100644 --- a/src/gui/checkbox.h +++ b/src/gui/checkbox.h @@ -45,6 +45,11 @@ class CheckBox : public gcn::CheckBox ~CheckBox(); /** + * Draws the caption, then calls drawBox to draw the check box. + */ + void draw(gcn::Graphics* graphics); + + /** * Draws the check box, not the caption. */ void drawBox(gcn::Graphics* graphics); diff --git a/src/gui/color.cpp b/src/gui/color.cpp deleted file mode 100644 index f9b89857..00000000 --- a/src/gui/color.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Configurable text colors - * Copyright (C) 2008 Douglas Boffey <dougaboffey@netscape.net> - * - * 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 "color.h" - -#include "../configuration.h" - -#include "../utils/gettext.h" -#include "../utils/stringutils.h" - -Color::Color() -{ - addColor('C', 0x000000, _("Chat")); - addColor('G', 0xff0000, _("GM")); - addColor('H', 0xebc873, _("Highlight")); - addColor('Y', 0x1fa052, _("Player")); - addColor('W', 0x0000ff, _("Whisper")); - addColor('I', 0xa08527, _("Is")); - addColor('P', 0xff00d8, _("Party")); - addColor('S', 0x8415e2, _("Server")); - addColor('L', 0x919191, _("Logger")); - addColor('<', 0xe50d0d, _("Hyperlink")); - commit(); -} - -Color::~Color() -{ - for (ColVector::iterator col = mColVector.begin(), - colEnd = mColVector.end(); - col != colEnd; - ++col) - { - config.setValue("Color" + col->text, toString(col->rgb)); - } -} - -void Color::setColor(char c, int rgb) -{ - for (ColVector::iterator col = mColVector.begin(), - colEnd = mColVector.end(); - col != colEnd; - ++col) - { - if (col->ch == c) - { - col->rgb = rgb; - return; - } - } -} - -int Color::getColor(char c, bool &valid) const -{ - for (ColVector::const_iterator col = mColVector.begin(), - colEnd = mColVector.end(); - col != colEnd; - ++col) - { - if (col->ch == c) - { - valid = true; - return col->rgb; - } - } - valid = false; - return 0x000000; -} - -std::string Color::getElementAt(int i) -{ - if (i < 0 || i >= getNumberOfElements()) - { - return ""; - } - return mColVector[i].text; -} - -char Color::getColorCharAt(int i) -{ - if (i < 0 || i >= getNumberOfElements()) - { - return 'C'; - } - return mColVector[i].ch; -} - -void Color::addColor(char c, int rgb, const std::string &text) -{ - int trueRgb = (int) config.getValue("Color" + text, rgb); - mColVector.push_back(ColorElem(c, trueRgb, text)); -} - -int Color::getColorAt(int i) -{ - if (i < 0 || i >= getNumberOfElements()) - { - return 0; - } - return mColVector[i].rgb; -} - -void Color::setColorAt(int i, int rgb) -{ - if (i >= 0 && i < getNumberOfElements()) - { - mColVector[i].rgb = rgb; - } -} - -void Color::commit() -{ - for (ColVector::iterator i = mColVector.begin(), iEnd = mColVector.end(); - i != iEnd; - ++i) - { - i->committedRgb = i->rgb; - } -} - -void Color::rollback() -{ - for (ColVector::iterator i = mColVector.begin(), iEnd = mColVector.end(); - i != iEnd; - ++i) - { - i->rgb = i->committedRgb; - } -} diff --git a/src/gui/color.h b/src/gui/color.h deleted file mode 100644 index 2816cedc..00000000 --- a/src/gui/color.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Configurable text colors - * Copyright (C) 2008 Douglas Boffey <dougaboffey@netscape.net> - * - * 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 COLOR_H -#define COLOR_H - -#include <string> -#include <vector> - -#include <guichan/listmodel.hpp> - -class Color : public gcn::ListModel -{ - public: - /** - * Constructor - */ - Color(); - - /** - * Destructor - */ - ~Color(); - - /** - * Define the color replacement for a character - * - * @param c charater to be replaced - * @param rgb color to replace character - */ - void setColor(char c, int rgb); - - /** - * Define the color replacement for a character - * - * @param c character to be replaced - * @param r red component - * @param g green component - * @param b blue component - */ - void setColor(char c, int r, int g, int b) - { - setColor(c, (r << 16) | (g << 8) | b); - } - - /** - * Return the color associated with a character, if exists - * - * @param c character requested - * @param valid indicate whether character is known - */ - int getColor(char c, bool &valid) const; - - /** - * Return the number of colors known - */ - int getNumberOfElements() { return mColVector.size(); } - - /** - * Return the name of the ith color - * - * @param i index of color interested in - */ - std::string getElementAt(int i); - - /** - * Get the color for the element at index i in the current color - * model - */ - int getColorAt(int i); - - /** - * Get the character used by the color for the element at index i in - * the current color model - */ - char getColorCharAt(int i); - - /** - * Set the color for the element at index i - */ - void setColorAt(int i, int rgb); - - /** - * Commit the colors - */ - void commit(); - - /** - * Rollback the colors - */ - void rollback(); - - private: - struct ColorElem - { - ColorElem(char c, int rgb, const std::string &text) : - ch(c), rgb(rgb), text(text) {} - char ch; - int rgb; - int committedRgb; - std::string text; - }; - typedef std::vector<ColorElem> ColVector; - ColVector mColVector; - - /** - * Initialise color - * - * @param c character that needs initialising - * @param rgb default color if not found in config - * @param text identifier of color - */ - void addColor(char c, int rgb, const std::string &text); -}; - -extern Color *textColor; - -#endif diff --git a/src/gui/confirm_dialog.cpp b/src/gui/confirm_dialog.cpp index 5ad2e26c..a40593e3 100644 --- a/src/gui/confirm_dialog.cpp +++ b/src/gui/confirm_dialog.cpp @@ -23,6 +23,7 @@ #include "button.h" #include "confirm_dialog.h" +#include "gui.h" #include "scrollarea.h" #include "textbox.h" @@ -49,14 +50,16 @@ ConfirmDialog::ConfirmDialog(const std::string &title, const std::string &msg, int numRows = mTextBox->getNumberOfRows(); int width = getFont()->getWidth(title); int inWidth = yesButton->getWidth() + noButton->getWidth() + 5; + const int fontHeight = getFont()->getHeight(); if (numRows > 1) { - // 15 == height of each line of text (based on font heights) + // fontHeight == height of each line of text (based on font heights) // 14 == row top + bottom graphic pixel heights - setContentSize(mTextBox->getMinWidth() + 15, 15 + (numRows * 15) + noButton->getHeight()); + setContentSize(mTextBox->getMinWidth() + fontHeight, ((numRows + 1) * + fontHeight) + noButton->getHeight()); mTextArea->setDimension(gcn::Rectangle(4, 5, mTextBox->getMinWidth() + 5, - 3 + (numRows * 14))); + 3 + (numRows * fontHeight))); } else { @@ -64,16 +67,17 @@ ConfirmDialog::ConfirmDialog(const std::string &title, const std::string &msg, width = getFont()->getWidth(msg); if (width < inWidth) width = inWidth; - setContentSize(width + 15, 30 + noButton->getHeight()); + setContentSize(width + fontHeight, (2 * fontHeight) + + noButton->getHeight()); mTextArea->setDimension(gcn::Rectangle(4, 5, width + 5, 17)); } yesButton->setPosition( (mTextBox->getMinWidth() - inWidth) / 2, - (numRows * 14) + noButton->getHeight() - 8); + ((numRows - 1) * fontHeight) + noButton->getHeight() + 2); noButton->setPosition( yesButton->getX() + yesButton->getWidth() + 5, - (numRows * 14) + noButton->getHeight() - 8); + ((numRows - 1) * fontHeight) + noButton->getHeight() + 2); add(mTextArea); add(yesButton); @@ -81,7 +85,7 @@ ConfirmDialog::ConfirmDialog(const std::string &title, const std::string &msg, if (getParent()) { - setLocationRelativeTo(getParent()); + center(); getParent()->moveToTop(this); } setVisible(true); @@ -104,7 +108,5 @@ void ConfirmDialog::action(const gcn::ActionEvent &event) // Can we receive anything else anyway? if (event.getId() == "yes" || event.getId() == "no") - { scheduleDelete(); - } } diff --git a/src/gui/connection.cpp b/src/gui/connection.cpp index fbf127de..5fb21ff2 100644 --- a/src/gui/connection.cpp +++ b/src/gui/connection.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/widgets/label.hpp> - #include "button.h" #include "connection.h" +#include "label.h" #include "progressbar.h" #include "../main.h" @@ -37,7 +36,7 @@ ConnectionDialog::ConnectionDialog(int previousState): Button *cancelButton = new Button(_("Cancel"), "cancelButton", this); mProgressBar = new ProgressBar(0.0, 200 - 10, 20, 128, 128, 128); - gcn::Label *label = new gcn::Label(_("Connecting...")); + gcn::Label *label = new Label(_("Connecting...")); cancelButton->setPosition(5, 100 - 5 - cancelButton->getHeight()); mProgressBar->setPosition(5, cancelButton->getY() - 25); @@ -47,7 +46,7 @@ ConnectionDialog::ConnectionDialog(int previousState): add(cancelButton); add(mProgressBar); - setLocationRelativeTo(getParent()); + center(); setVisible(true); } diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp index 2ed891db..a98c9af4 100644 --- a/src/gui/debugwindow.cpp +++ b/src/gui/debugwindow.cpp @@ -19,11 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <SDL_mouse.h> - -#include <guichan/widgets/label.hpp> - #include "debugwindow.h" +#include "label.h" #include "viewport.h" #include "widgets/layout.h" @@ -42,40 +39,38 @@ DebugWindow::DebugWindow(): setResizable(true); setCloseButton(true); - setDefaultSize(0, 0, 400, 60); - loadWindowState(); + setDefaultSize(400, 100, ImageRect::CENTER); - mFPSLabel = new gcn::Label("0 FPS"); - mMusicFileLabel = new gcn::Label("Music: "); - mMapLabel = new gcn::Label("Map: "); - mMiniMapLabel = new gcn::Label("Mini-Map: "); - mTileMouseLabel = new gcn::Label("Mouse: 0, 0"); - mParticleCountLabel = new gcn::Label("Particle count: 0"); + mFPSLabel = new Label("0 FPS"); + mMusicFileLabel = new Label("Music: "); + mMapLabel = new Label("Map: "); + mMiniMapLabel = new Label("Mini-Map: "); + mTileMouseLabel = new Label("Mouse: 0, 0"); + mParticleCountLabel = new Label("Particle count: 0"); - place(0, 0, mFPSLabel); + place(0, 0, mFPSLabel, 3); place(3, 0, mTileMouseLabel); - place(0, 1, mMusicFileLabel, 2); + place(0, 1, mMusicFileLabel, 3); place(3, 1, mParticleCountLabel); - place(0, 2, mMapLabel, 2); - place(0, 3, mMiniMapLabel, 2); + place(0, 2, mMapLabel, 4); + place(0, 3, mMiniMapLabel, 4); - reflowLayout(375, 0); + loadWindowState(); } void DebugWindow::logic() { + if (!isVisible()) + return; + // Get the current mouse position - int mouseX, mouseY; - SDL_GetMouseState(&mouseX, &mouseY); - int mouseTileX = mouseX / 32 + viewport->getCameraX(); - int mouseTileY = mouseY / 32 + viewport->getCameraY(); + int mouseTileX = (viewport->getMouseX() + viewport->getCameraX()) / 32; + int mouseTileY = (viewport->getMouseY() + viewport->getCameraY()) / 32; mFPSLabel->setCaption(toString(fps) + " FPS"); - mFPSLabel->adjustSize(); - mTileMouseLabel->setCaption("Mouse: " + - toString(mouseTileX) + ", " + toString(mouseTileY)); - mTileMouseLabel->adjustSize(); + mTileMouseLabel->setCaption("Tile: (" + toString(mouseTileX) + ", " + + toString(mouseTileY) + ")"); Map *currentMap = engine->getCurrentMap(); if (currentMap) @@ -83,20 +78,16 @@ void DebugWindow::logic() const std::string music = "Music: " + currentMap->getProperty("music"); mMusicFileLabel->setCaption(music); - mMusicFileLabel->adjustSize(); const std::string minimap = "MiniMap: " + currentMap->getProperty("minimap"); mMiniMapLabel->setCaption(minimap); - mMiniMapLabel->adjustSize(); const std::string map = "Map: " + currentMap->getProperty("_filename"); mMapLabel->setCaption(map); - mMapLabel->adjustSize(); } mParticleCountLabel->setCaption("Particle count: " + toString(Particle::particleCount)); - mParticleCountLabel->adjustSize(); } diff --git a/src/gui/debugwindow.h b/src/gui/debugwindow.h index e089de27..8097132c 100644 --- a/src/gui/debugwindow.h +++ b/src/gui/debugwindow.h @@ -50,4 +50,6 @@ class DebugWindow : public Window gcn::Label *mParticleCountLabel; }; +extern DebugWindow *debugWindow; + #endif diff --git a/src/gui/emotecontainer.cpp b/src/gui/emotecontainer.cpp index e22b031b..ececd9aa 100644 --- a/src/gui/emotecontainer.cpp +++ b/src/gui/emotecontainer.cpp @@ -53,7 +53,7 @@ EmoteContainer::EmoteContainer(): // Setup emote sprites for (int i = 0; i <= EmoteDB::getLast(); i++) { - mEmoteImg.push_back(player_node->getEmote(i)); + mEmoteImg.push_back(EmoteDB::getAnimation(i)); } mSelImg = resman->getImage("graphics/gui/selection.png"); @@ -78,13 +78,14 @@ EmoteContainer::~EmoteContainer() void EmoteContainer::draw(gcn::Graphics *graphics) { + if (!isVisible()) + return; + int columns = getWidth() / gridWidth; // Have at least 1 column if (columns < 1) - { columns = 1; - } for (int i = 0; i < mMaxEmote ; i++) { diff --git a/src/gui/emotecontainer.h b/src/gui/emotecontainer.h index fefce793..88df29fc 100644 --- a/src/gui/emotecontainer.h +++ b/src/gui/emotecontainer.h @@ -121,7 +121,7 @@ class EmoteContainer : public gcn::Widget, */ void distributeValueChangedEvent(void); - std::vector<AnimatedSprite*> mEmoteImg; + std::vector<const AnimatedSprite*> mEmoteImg; Image *mSelImg; int mSelectedEmoteIndex; diff --git a/src/gui/emoteshortcutcontainer.cpp b/src/gui/emoteshortcutcontainer.cpp index a0739723..661f42a7 100644 --- a/src/gui/emoteshortcutcontainer.cpp +++ b/src/gui/emoteshortcutcontainer.cpp @@ -20,6 +20,7 @@ */ #include "emoteshortcutcontainer.h" +#include "palette.h" #include "../animatedsprite.h" #include "../configuration.h" @@ -59,7 +60,7 @@ EmoteShortcutContainer::EmoteShortcutContainer(): // Setup emote sprites for (int i = 0; i <= EmoteDB::getLast(); i++) { - mEmoteImg.push_back(player_node->getEmote(i)); + mEmoteImg.push_back(EmoteDB::getAnimation(i)); } mMaxItems = EmoteDB::getLast() < MAX_ITEMS ? EmoteDB::getLast() : MAX_ITEMS; @@ -75,6 +76,15 @@ EmoteShortcutContainer::~EmoteShortcutContainer() void EmoteShortcutContainer::draw(gcn::Graphics *graphics) { + if (!isVisible()) + return; + + if (config.getValue("guialpha", 0.8) != mAlpha) + { + mAlpha = config.getValue("guialpha", 0.8); + mBackgroundImg->setAlpha(mAlpha); + } + Graphics *g = static_cast<Graphics*>(graphics); graphics->setFont(getFont()); @@ -89,12 +99,13 @@ void EmoteShortcutContainer::draw(gcn::Graphics *graphics) // Draw emote keyboard shortcut. const char *key = SDL_GetKeyName( (SDLKey) keyboard.getKeyValue(keyboard.KEY_EMOTE_1 + i)); - graphics->setColor(0x000000); + graphics->setColor(guiPalette->getColor(Palette::TEXT)); g->drawText(key, emoteX + 2, emoteY + 2, gcn::Graphics::LEFT); if (emoteShortcut->getEmote(i)) { - mEmoteImg[emoteShortcut->getEmote(i) - 1]->draw(g, emoteX + 2, emoteY + 10); + mEmoteImg[emoteShortcut->getEmote(i) - 1]->draw(g, emoteX + 2, + emoteY + 10); } } @@ -102,7 +113,7 @@ void EmoteShortcutContainer::draw(gcn::Graphics *graphics) if (mEmoteMoved) { // Draw the emote image being dragged by the cursor. - AnimatedSprite* sprite = mEmoteImg[mEmoteMoved - 1]; + const AnimatedSprite* sprite = mEmoteImg[mEmoteMoved - 1]; if (sprite) { const int tPosX = mCursorPosX - (sprite->getWidth() / 2); @@ -111,12 +122,6 @@ void EmoteShortcutContainer::draw(gcn::Graphics *graphics) sprite->draw(g, tPosX, tPosY); } } - - if (config.getValue("guialpha", 0.8) != mAlpha) - { - mAlpha = config.getValue("guialpha", 0.8); - mBackgroundImg->setAlpha(mAlpha); - } } void EmoteShortcutContainer::mouseDragged(gcn::MouseEvent &event) @@ -129,9 +134,7 @@ void EmoteShortcutContainer::mouseDragged(gcn::MouseEvent &event) const int emoteId = emoteShortcut->getEmote(index); if (index == -1) - { return; - } if (emoteId) { @@ -152,19 +155,17 @@ void EmoteShortcutContainer::mousePressed(gcn::MouseEvent &event) const int index = getIndexFromGrid(event.getX(), event.getY()); if (index == -1) - { - return; - } + return; // Stores the selected emote if there is one. if (emoteShortcut->isEmoteSelected()) { - emoteShortcut->setEmote(index); - emoteShortcut->setEmoteSelected(0); + emoteShortcut->setEmote(index); + emoteShortcut->setEmoteSelected(0); } else if (emoteShortcut->getEmote(index)) { - mEmoteClicked = true; + mEmoteClicked = true; } } @@ -175,9 +176,7 @@ void EmoteShortcutContainer::mouseReleased(gcn::MouseEvent &event) const int index = getIndexFromGrid(event.getX(), event.getY()); if (emoteShortcut->isEmoteSelected()) - { emoteShortcut->setEmoteSelected(0); - } if (index == -1) { @@ -196,9 +195,7 @@ void EmoteShortcutContainer::mouseReleased(gcn::MouseEvent &event) } if (mEmoteClicked) - { mEmoteClicked = false; - } } } diff --git a/src/gui/emoteshortcutcontainer.h b/src/gui/emoteshortcutcontainer.h index d32a9f79..2997cb09 100644 --- a/src/gui/emoteshortcutcontainer.h +++ b/src/gui/emoteshortcutcontainer.h @@ -68,7 +68,7 @@ class EmoteShortcutContainer : public ShortcutContainer void mouseReleased(gcn::MouseEvent &event); private: - std::vector<AnimatedSprite*> mEmoteImg; + std::vector<const AnimatedSprite*> mEmoteImg; bool mEmoteClicked; int mEmoteMoved; diff --git a/src/gui/emotewindow.cpp b/src/gui/emotewindow.cpp index 48635720..d4b3cf2e 100644 --- a/src/gui/emotewindow.cpp +++ b/src/gui/emotewindow.cpp @@ -40,7 +40,7 @@ EmoteWindow::EmoteWindow(): setCloseButton(true); setMinWidth(80); setMinHeight(130); - setDefaultSize(115, 25, 322, 200); + setDefaultSize(322, 200, ImageRect::CENTER); mUseButton = new Button(_("Use"), "use", this); diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index a44ae3ec..7ac9051f 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -27,6 +27,7 @@ #include "button.h" #include "equipmentwindow.h" #include "itempopup.h" +#include "palette.h" #include "playerbox.h" #include "viewport.h" @@ -71,6 +72,7 @@ EquipmentWindow::EquipmentWindow(): mSelected(-1) { mItemPopup = new ItemPopup; + mItemPopup->setOpaque(false); // Control that shows the Player mPlayerBox = new PlayerBox; @@ -79,7 +81,7 @@ EquipmentWindow::EquipmentWindow(): setWindowName("Equipment"); setCloseButton(true); - setDefaultSize(5, 195, 180, 300); + setDefaultSize(180, 300, ImageRect::CENTER); loadWindowState(); mUnequip = new Button(_("Unequip"), "unequip", this); @@ -119,6 +121,9 @@ EquipmentWindow::~EquipmentWindow() void EquipmentWindow::draw(gcn::Graphics *graphics) { + if (!isVisible()) + return; + // Draw window graphics Window::draw(graphics); @@ -132,6 +137,22 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) for (int i = EQUIP_LEGS_SLOT; i < EQUIP_VECTOREND; i++) #endif { + if (i == mSelected) + { + const gcn::Color color = guiPalette->getColor(Palette::HIGHLIGHT); + + // Set color to the highligh color + g->setColor(gcn::Color(color.r, color.g, color.b, getGuiAlpha())); + g->fillRectangle(gcn::Rectangle(mEquipBox[i].posX, mEquipBox[i].posY, + BOX_WIDTH, BOX_HEIGHT)); + } + + // Set color black. + g->setColor(gcn::Color(0, 0, 0)); + // Draw box border. + g->drawRectangle(gcn::Rectangle(mEquipBox[i].posX, mEquipBox[i].posY, + BOX_WIDTH, BOX_HEIGHT)); + #ifdef TMWSERV_SUPPORT Item *item = mEquipment->getEquipment(i); #else @@ -147,7 +168,7 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) #ifdef EATHENA_SUPPORT if (i == EQUIP_AMMO_SLOT) { - g->setColor(gcn::Color(0, 0, 0)); + g->setColor(guiPalette->getColor(Palette::TEXT)); graphics->drawText(toString(item->getQuantity()), mEquipBox[i].posX + (BOX_WIDTH / 2), mEquipBox[i].posY - getFont()->getHeight(), @@ -155,21 +176,6 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) } #endif } - - if (i == mSelected) - { - // Set color red. - g->setColor(gcn::Color(255, 0, 0)); - } - else - { - // Set color black. - g->setColor(gcn::Color(0, 0, 0)); - } - - // Draw box border. - g->drawRectangle(gcn::Rectangle(mEquipBox[i].posX, mEquipBox[i].posY, - BOX_WIDTH, BOX_HEIGHT)); } } @@ -280,8 +286,9 @@ void EquipmentWindow::mouseMoved(gcn::MouseEvent &event) int mouseX, mouseY; SDL_GetMouseState(&mouseX, &mouseY); - mItemPopup->setItem(item->getInfo()); - mItemPopup->setOpaque(false); + if (item->getInfo().getName() != mItemPopup->getItemName()) + mItemPopup->setItem(item->getInfo()); + mItemPopup->updateColors(); mItemPopup->view(x + getX(), y + getY()); } else diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 1ef0219a..87ce74fa 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -25,7 +25,9 @@ #include "focushandler.h" #include "gui.h" +#include "palette.h" #include "sdlinput.h" +#include "skin.h" #include "truetypefont.h" #include "viewport.h" #include "window.h" @@ -46,11 +48,6 @@ Gui *gui = 0; Viewport *viewport = 0; /**< Viewport on the map. */ SDLInput *guiInput = 0; -// Fonts used in showing hits -gcn::Font *hitRedFont = 0; -gcn::Font *hitBlueFont = 0; -gcn::Font *hitYellowFont = 0; - // Bolded font gcn::Font *boldFont = 0; @@ -113,6 +110,7 @@ Gui::Gui(Graphics *graphics): { const int fontSize = (int)config.getValue("fontSize", 11); mGuiFont = new TrueTypeFont(path, fontSize); + mInfoParticleFont = new TrueTypeFont(path, fontSize, 1); } catch (gcn::Exception e) { @@ -136,22 +134,6 @@ Gui::Gui(Graphics *graphics): gcn::Widget::setGlobalFont(mGuiFont); - // Load hits' colorful fonts - try - { - hitRedFont = new gcn::ImageFont("graphics/gui/hits_red.png", - "0123456789crit! "); - hitBlueFont = new gcn::ImageFont("graphics/gui/hits_blue.png", - "0123456789crit! "); - hitYellowFont = new gcn::ImageFont("graphics/gui/hits_yellow.png", - "0123456789misxp "); - } - catch (gcn::Exception e) - { - logger->error(std::string("Unable to load colored hits' fonts: ") - + e.getMessage()); - } - // Initialize mouse cursor and listen for changes to the option setUseCustomCursor(config.getValue("customcursor", 1) == 1); mConfigListener = new GuiConfigListener(this); @@ -169,16 +151,12 @@ Gui::~Gui() config.removeListener("customcursor", mConfigListener); delete mConfigListener; - // Fonts used in showing hits - delete hitRedFont; - delete hitBlueFont; - delete hitYellowFont; - if (mMouseCursors) mMouseCursors->decRef(); delete mGuiFont; delete boldFont; + delete mInfoParticleFont; delete viewport; delete getTop(); @@ -196,6 +174,8 @@ void Gui::logic() else mMouseCursorAlpha = std::max(0.0f, mMouseCursorAlpha - 0.005f); + guiPalette->advanceGradient(); + gcn::Gui::logic(); } @@ -262,3 +242,8 @@ void Gui::handleMouseMoved(const gcn::MouseInput &mouseInput) gcn::Gui::handleMouseMoved(mouseInput); mMouseInactivityTimer = 0; } + +const int Gui::getFontHeight() const +{ + return mGuiFont->getHeight(); +} diff --git a/src/gui/gui.h b/src/gui/gui.h index 5c0c24f7..2ce153db 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -77,6 +77,18 @@ class Gui : public gcn::Gui { return mGuiFont; } /** + * Return game font height. + */ + const int getFontHeight() const; + + /** + * Return the Font used for "Info Particles", i.e. ones showing, what + * you picked up, etc. + */ + gcn::Font* getInfoParticleFont() const + { return mInfoParticleFont; } + + /** * Sets whether a custom cursor should be rendered. */ void setUseCustomCursor(bool customCursor); @@ -107,6 +119,7 @@ class Gui : public gcn::Gui private: GuiConfigListener *mConfigListener; gcn::Font *mGuiFont; /**< The global GUI font */ + gcn::Font *mInfoParticleFont; /**< Font for Info Particles*/ bool mCustomCursor; /**< Show custom cursor */ ImageSet *mMouseCursors; /**< Mouse cursor images */ float mMouseCursorAlpha; @@ -118,13 +131,6 @@ extern Gui *gui; /**< The GUI system */ extern SDLInput *guiInput; /**< GUI input */ /** - * Fonts used in showing hits - */ -extern gcn::Font *hitRedFont; -extern gcn::Font *hitBlueFont; -extern gcn::Font *hitYellowFont; - -/** * Bolded text font */ extern gcn::Font *boldFont; diff --git a/src/gui/help.cpp b/src/gui/help.cpp index 30c6a9c4..03dfd08d 100644 --- a/src/gui/help.cpp +++ b/src/gui/help.cpp @@ -39,16 +39,17 @@ HelpWindow::HelpWindow(): setWindowName("Help"); setResizable(true); + setDefaultSize(500, 400, ImageRect::CENTER); + mBrowserBox = new BrowserBox; mBrowserBox->setOpaque(false); mScrollArea = new ScrollArea(mBrowserBox); Button *okButton = new Button(_("Close"), "close", this); - mScrollArea->setDimension(gcn::Rectangle( - 5, 5, 445, 335 - okButton->getHeight())); - okButton->setPosition( - 450 - okButton->getWidth(), - 345 - okButton->getHeight()); + mScrollArea->setDimension(gcn::Rectangle(5, 5, 445, + 335 - okButton->getHeight())); + okButton->setPosition(450 - okButton->getWidth(), + 345 - okButton->getHeight()); mBrowserBox->setLinkHandler(this); @@ -58,7 +59,7 @@ HelpWindow::HelpWindow(): Layout &layout = getLayout(); layout.setRowHeight(0, Layout::AUTO_SET); - setLocationRelativeTo(getParent()); + loadWindowState(); } void HelpWindow::action(const gcn::ActionEvent &event) diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index d18490a4..0b554469 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -24,12 +24,11 @@ #include <guichan/font.hpp> #include <guichan/mouseinput.hpp> -#include <guichan/widgets/label.hpp> - #include "button.h" #include "inventorywindow.h" #include "item_amount.h" #include "itemcontainer.h" +#include "label.h" #include "progressbar.h" #include "scrollarea.h" #include "sdlinput.h" @@ -40,7 +39,6 @@ #include "../inventory.h" #include "../item.h" #include "../localplayer.h" -#include "../log.h" #include "../units.h" #include "../resources/iteminfo.h" @@ -63,7 +61,7 @@ InventoryWindow::InventoryWindow(int invSize): setMinWidth(375); setMinHeight(283); // If you adjust these defaults, don't forget to adjust the trade window's. - setDefaultSize(115, 30, 375, 283); + setDefaultSize(375, 300, ImageRect::CENTER); addKeyListener(this); std::string longestUseString = getFont()->getWidth(_("Equip")) > @@ -96,8 +94,8 @@ InventoryWindow::InventoryWindow(int invSize): mMaxWeight = -1; mUsedSlots = toString(player_node->getInventory()->getNumberOfSlotsUsed()); - mSlotsLabel = new gcn::Label(_("Slots: ")); - mWeightLabel = new gcn::Label(_("Weight: ")); + mSlotsLabel = new Label(_("Slots: ")); + mWeightLabel = new Label(_("Weight: ")); mSlotsBar = new ProgressBar(1.0f, 100, 20, 225, 200, 25); mWeightBar = new ProgressBar(1.0f, 100, 20, 0, 0, 255); @@ -129,6 +127,9 @@ InventoryWindow::~InventoryWindow() void InventoryWindow::logic() { + if (!isVisible()) + return; + Window::logic(); // It would be nicer if this update could be event based, needs some diff --git a/src/gui/inventorywindow.h b/src/gui/inventorywindow.h index 6a51c66d..95a47bdb 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -22,13 +22,13 @@ #ifndef INVENTORYWINDOW_H #define INVENTORYWINDOW_H -#include <guichan/actionlistener.hpp> -#include <guichan/selectionlistener.hpp> -#include <guichan/keylistener.hpp> - #include "window.h" -#include "../localplayer.h" +#include "../inventory.h" + +#include <guichan/actionlistener.hpp> +#include <guichan/keylistener.hpp> +#include <guichan/selectionlistener.hpp> class Item; class ItemContainer; diff --git a/src/gui/item_amount.cpp b/src/gui/item_amount.cpp index 3bd388f4..0f6aa593 100644 --- a/src/gui/item_amount.cpp +++ b/src/gui/item_amount.cpp @@ -20,9 +20,12 @@ */ #include "button.h" -#include "inttextfield.h" #include "item_amount.h" +#include "label.h" #include "slider.h" +#ifdef EATHENA_SUPPORT +#include "storagewindow.h" +#endif #include "trade.h" #include "widgets/layout.h" @@ -31,46 +34,54 @@ #include "../localplayer.h" #include "../utils/gettext.h" +#include "../utils/strprintf.h" ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item, int maxRange): Window("", true, parent), - mItem(item) + mItem(item), + mMax(maxRange), + mUsage(usage) { - if (!maxRange) - { - maxRange = mItem->getQuantity(); - } + if (!mMax) + mMax = mItem->getQuantity(); + + setCloseButton(true); // Integer field - mItemAmountTextField = new IntTextField(1); - mItemAmountTextField->setRange(1, maxRange); - mItemAmountTextField->setWidth(30); - mItemAmountTextField->setActionEventId("Dummy"); - mItemAmountTextField->addActionListener(this); + + mItemAmountLabel = new Label(strprintf("%d / %d", 1, mMax)); + mItemAmountLabel->setAlignment(gcn::Graphics::CENTER); // Slider - mItemAmountSlide = new Slider(1.0, maxRange); + mItemAmountSlide = new Slider(1.0, mMax); mItemAmountSlide->setHeight(10); mItemAmountSlide->setActionEventId("Slide"); mItemAmountSlide->addActionListener(this); // Buttons Button *minusButton = new Button("-", "Minus", this); - minusButton->setSize(20, 20); Button *plusButton = new Button("+", "Plus", this); - plusButton->setSize(20, 20); - Button *okButton = new Button(_("Ok"), "Drop", this); + Button *okButton = new Button(_("Ok"), "Ok", this); Button *cancelButton = new Button(_("Cancel"), "Cancel", this); + Button *addAllButton = new Button(_("All"), "All", this); + + minusButton->adjustSize(); + minusButton->setWidth(plusButton->getWidth()); // Set positions + ContainerPlacer place; + place = getPlacer(0, 0); + place(0, 0, minusButton); - place(1, 0, mItemAmountTextField).setPadding(2); - place(2, 0, plusButton); - place(0, 1, mItemAmountSlide, 6); - place(4, 2, okButton); - place(5, 2, cancelButton); - reflowLayout(250, 0); + place(1, 0, mItemAmountSlide, 3); + place(4, 0, plusButton); + place(5, 0, mItemAmountLabel, 2); + place(7, 0, addAllButton); + place = getPlacer(0, 1); + place(4, 0, cancelButton); + place(5, 0, okButton); + reflowLayout(225, 0); resetAmount(); @@ -78,15 +89,18 @@ ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item, { case AMOUNT_TRADE_ADD: setCaption(_("Select amount of items to trade.")); - okButton->setActionEventId("AddTrade"); break; case AMOUNT_ITEM_DROP: setCaption(_("Select amount of items to drop.")); - okButton->setActionEventId("Drop"); + break; + case AMOUNT_STORE_ADD: + setCaption(_("Select amount of items to store.")); + break; + case AMOUNT_STORE_REMOVE: + setCaption(_("Select amount of items to retrieve.")); break; case AMOUNT_ITEM_SPLIT: setCaption(_("Select amount of items to split.")); - okButton->setActionEventId("Split"); break; default: break; @@ -98,22 +112,22 @@ ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item, void ItemAmountWindow::resetAmount() { - mItemAmountTextField->setValue(1); + mItemAmountLabel->setCaption(strprintf("%d / %d", 1, mMax)); } void ItemAmountWindow::action(const gcn::ActionEvent &event) { - int amount = mItemAmountTextField->getValue(); + int amount = mItemAmountSlide->getValue(); if (event.getId() == "Cancel") { - scheduleDelete(); + close(); } - else if (event.getId() == "Plus") + else if (event.getId() == "Plus" && amount < mMax) { amount++; } - else if (event.getId() == "Minus") + else if (event.getId() == "Minus" && amount > 1) { amount--; } @@ -121,23 +135,45 @@ void ItemAmountWindow::action(const gcn::ActionEvent &event) { amount = static_cast<int>(mItemAmountSlide->getValue()); } - else if (event.getId() == "Drop") - { - player_node->dropItem(mItem, mItemAmountTextField->getValue()); - scheduleDelete(); - } - else if (event.getId() == "AddTrade") + else if (event.getId() == "Ok" || event.getId() == "All") { - tradeWindow->tradeItem(mItem, mItemAmountTextField->getValue()); - scheduleDelete(); - } + if (event.getId() == "All") + amount = mMax; + + switch (mUsage) + { + case AMOUNT_TRADE_ADD: + tradeWindow->tradeItem(mItem, amount); + break; + case AMOUNT_ITEM_DROP: + player_node->dropItem(mItem, amount); + break; #ifdef TMWSERV_SUPPORT - else if (event.getId() == "Split") - { - player_node->splitItem(mItem, mItemAmountTextField->getValue()); + case AMOUNT_ITEM_SPLIT: + player_node->splitItem(mItem, amount); + break; +#else + case AMOUNT_STORE_ADD: + storageWindow->addStore(mItem, amount); + break; + case AMOUNT_STORE_REMOVE: + storageWindow->removeStore(mItem, amount); + break; +#endif + default: + return; + break; + } + scheduleDelete(); + return; } -#endif - mItemAmountTextField->setValue(amount); + + mItemAmountLabel->setCaption(strprintf("%d / %d", amount, mMax)); mItemAmountSlide->setValue(amount); } + +void ItemAmountWindow::close() +{ + scheduleDelete(); +} diff --git a/src/gui/item_amount.h b/src/gui/item_amount.h index 4fdb8dc6..344f8c28 100644 --- a/src/gui/item_amount.h +++ b/src/gui/item_amount.h @@ -31,7 +31,9 @@ class Item; #define AMOUNT_TRADE_ADD 1 #define AMOUNT_ITEM_DROP 2 -#define AMOUNT_ITEM_SPLIT 3 +#define AMOUNT_STORE_ADD 3 +#define AMOUNT_STORE_REMOVE 4 +#define AMOUNT_ITEM_SPLIT 5 /** * Window used for selecting the amount of items to drop, trade or split. @@ -56,10 +58,17 @@ class ItemAmountWindow : public Window, public gcn::ActionListener */ void resetAmount(); + /** + * Schedules the Item Amount window for deletion. + */ + void close(); + private: - IntTextField *mItemAmountTextField; /**< Item amount caption. */ + gcn::Label *mItemAmountLabel; /**< Item amount caption. */ Item *mItem; + int mMax, mUsage; + /** * Item Amount buttons. */ diff --git a/src/gui/itemcontainer.cpp b/src/gui/itemcontainer.cpp index bdae9ada..38a41e0e 100644 --- a/src/gui/itemcontainer.cpp +++ b/src/gui/itemcontainer.cpp @@ -19,15 +19,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "itemcontainer.h" -#include "chat.h" - -#include "itempopup.h" #include <guichan/mouseinput.hpp> #include <guichan/selectionlistener.hpp> +#include "chat.h" +#include "itemcontainer.h" +#include "itempopup.h" +#include "palette.h" #include "sdlinput.h" +#include "viewport.h" #include "../graphics.h" #include "../inventory.h" @@ -306,12 +307,10 @@ void ItemContainer::mouseMoved(gcn::MouseEvent &event) if (item) { - int mouseX, mouseY; - SDL_GetMouseState(&mouseX, &mouseY); - - mItemPopup->setItem(item->getInfo()); - mItemPopup->setOpaque(false); - mItemPopup->view(mouseX, mouseY); + if (item->getInfo().getName() != mItemPopup->getItemName()) + mItemPopup->setItem(item->getInfo()); + mItemPopup->updateColors(); + mItemPopup->view(viewport->getMouseX(), viewport->getMouseY()); } else { diff --git a/src/gui/itemlinkhandler.cpp b/src/gui/itemlinkhandler.cpp index 4060b303..29fa310d 100644 --- a/src/gui/itemlinkhandler.cpp +++ b/src/gui/itemlinkhandler.cpp @@ -22,10 +22,9 @@ #include <sstream> #include <string> -#include <SDL_mouse.h> - #include "itemlinkhandler.h" #include "itempopup.h" +#include "viewport.h" #include "../resources/iteminfo.h" #include "../resources/itemdb.h" @@ -33,6 +32,7 @@ ItemLinkHandler::ItemLinkHandler() { mItemPopup = new ItemPopup; + mItemPopup->setOpaque(false); } ItemLinkHandler::~ItemLinkHandler() @@ -49,15 +49,18 @@ void ItemLinkHandler::handleLink(const std::string &link) if (id > 0) { const ItemInfo &iteminfo = ItemDB::get(id); - int mouseX, mouseY; - - SDL_GetMouseState(&mouseX, &mouseY); - mItemPopup->setItem(iteminfo); + if (iteminfo.getName() != mItemPopup->getItemName()) + mItemPopup->setItem(iteminfo); if (mItemPopup->isVisible()) + { mItemPopup->setVisible(false); + } else - mItemPopup->view(mouseX, mouseY); + { + mItemPopup->updateColors(); + mItemPopup->view(viewport->getMouseX(), viewport->getMouseY()); + } } } diff --git a/src/gui/itempopup.cpp b/src/gui/itempopup.cpp index 25e6e78e..1b0a2bb2 100644 --- a/src/gui/itempopup.cpp +++ b/src/gui/itempopup.cpp @@ -26,11 +26,11 @@ #include "gui.h" #include "itempopup.h" +#include "palette.h" #include "scrollarea.h" #include "textbox.h" -#include "windowcontainer.h" -#include "widgets/layout.h" +#include "../graphics.h" #include "../units.h" @@ -40,14 +40,12 @@ #include "../utils/stringutils.h" ItemPopup::ItemPopup(): - Window() + Popup() { - setResizable(false); - setShowTitle(false); - setTitleBarHeight(0); + mItemType = ""; // Item Name - mItemName = new gcn::Label("Label"); + mItemName = new gcn::Label(""); mItemName->setFont(boldFont); mItemName->setPosition(2, 2); @@ -88,8 +86,6 @@ ItemPopup::ItemPopup(): add(mItemDescScroll); add(mItemEffectScroll); add(mItemWeightScroll); - - setLocationRelativeTo(getParent()); } ItemPopup::~ItemPopup() @@ -105,15 +101,18 @@ ItemPopup::~ItemPopup() void ItemPopup::setItem(const ItemInfo &item) { + if (item.getName() == mItemName->getCaption()) + return; + mItemName->setCaption(item.getName()); -#ifdef EATHENA_SUPPORT - mItemName->setForegroundColor(getColor(item.getType())); -#endif mItemName->setWidth(boldFont->getWidth(item.getName())); mItemDesc->setTextWrapped(item.getDescription(), 196); mItemEffect->setTextWrapped(item.getEffect(), 196); mItemWeight->setTextWrapped(_("Weight: ") + Units::formatWeight(item.getWeight()), 196); +#ifdef EATHENA_SUPPORT + mItemType = item.getType(); +#endif int minWidth = mItemName->getWidth(); @@ -166,40 +165,53 @@ void ItemPopup::setItem(const ItemInfo &item) (2 * getFont()->getHeight())); } +void ItemPopup::updateColors() +{ +#ifdef EATHENA_SUPPORT + mItemName->setForegroundColor(getColor(mItemType)); +#endif + graphics->setColor(guiPalette->getColor(Palette::TEXT)); +} + gcn::Color ItemPopup::getColor(const std::string& type) { gcn::Color color; if (type.compare("generic") == 0) - color = 0x21a5b1; + color = guiPalette->getColor(Palette::GENERIC); else if (type.compare("equip-head") == 0) - color = 0x527fa4; + color = guiPalette->getColor(Palette::HEAD); else if (type.compare("usable") == 0) - color = 0x268d24; + color = guiPalette->getColor(Palette::USABLE); else if (type.compare("equip-torso") == 0) - color = 0xd12aa4; + color = guiPalette->getColor(Palette::TORSO); else if (type.compare("equip-1hand") == 0) - color = 0xf42a2a; + color = guiPalette->getColor(Palette::ONEHAND); else if (type.compare("equip-legs") == 0) - color = 0x699900; + color = guiPalette->getColor(Palette::LEGS); else if (type.compare("equip-feet") == 0) - color = 0xaa1d48; + color = guiPalette->getColor(Palette::FEET); else if (type.compare("equip-2hand") == 0) - color = 0xf46d0e; + color = guiPalette->getColor(Palette::TWOHAND); else if (type.compare("equip-shield") == 0) - color = 0x9c2424; + color = guiPalette->getColor(Palette::SHIELD); else if (type.compare("equip-ring") == 0) - color = 0x0000ff; + color = guiPalette->getColor(Palette::RING); else if (type.compare("equip-arms") == 0) - color = 0x9c24e8; + color = guiPalette->getColor(Palette::ARMS); else if (type.compare("equip-ammo") == 0) - color = 0x8b6311; + color = guiPalette->getColor(Palette::AMMO); else - color = 0x000000; + color = guiPalette->getColor(Palette::UNKNOWN_ITEM); return color; } +std::string ItemPopup::getItemName() +{ + return mItemName->getCaption(); +} + unsigned int ItemPopup::getNumRows() { return mItemDesc->getNumberOfRows() + mItemEffect->getNumberOfRows() + @@ -208,8 +220,8 @@ unsigned int ItemPopup::getNumRows() void ItemPopup::view(int x, int y) { - if (windowContainer->getWidth() < (x + getWidth() + 5)) - x = windowContainer->getWidth() - getWidth(); + if (graphics->getWidth() < (x + getWidth() + 5)) + x = graphics->getWidth() - getWidth(); if ((y - getHeight() - 10) < 0) y = 0; else diff --git a/src/gui/itempopup.h b/src/gui/itempopup.h index c820e3a0..03e79886 100644 --- a/src/gui/itempopup.h +++ b/src/gui/itempopup.h @@ -23,20 +23,48 @@ #ifndef ITEMPOPUP_H #define ITEMPOPUP_H -#include "window.h" +#include "popup.h" class ItemInfo; class ScrollArea; class TextBox; -class ItemPopup : public Window +class ItemPopup : public Popup { public: + /** + * Constructor. Initializes the item popup. + */ ItemPopup(); + + /** + * Destructor. Cleans up the item popup on deletion. + */ ~ItemPopup(); + /** + * Sets the info to be displayed given a particular item. + */ void setItem(const ItemInfo &item); + + /** + * Gets the number of rows that the item popup currently has. + */ unsigned int getNumRows(); + + /** + * Gets the name of the currently stored item in this popup. + */ + std::string getItemName(); + + /** + * Updates the colors used within the item popup. + */ + void updateColors(); + + /** + * Sets the location to display the item popup. + */ void view(int x, int y); private: @@ -44,6 +72,7 @@ class ItemPopup : public Window TextBox *mItemDesc; TextBox *mItemEffect; TextBox *mItemWeight; + std::string mItemType; ScrollArea *mItemDescScroll; ScrollArea *mItemEffectScroll; ScrollArea *mItemWeightScroll; diff --git a/src/gui/itemshortcutcontainer.cpp b/src/gui/itemshortcutcontainer.cpp index 8864cbd9..45a5ffa0 100644 --- a/src/gui/itemshortcutcontainer.cpp +++ b/src/gui/itemshortcutcontainer.cpp @@ -18,10 +18,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <SDL_mouse.h> +#include "inventorywindow.h" #include "itemshortcutcontainer.h" #include "itempopup.h" +#include "palette.h" #include "viewport.h" #include "../configuration.h" @@ -46,6 +47,7 @@ ItemShortcutContainer::ItemShortcutContainer(): addWidgetListener(this); mItemPopup = new ItemPopup; + mItemPopup->setOpaque(false); ResourceManager *resman = ResourceManager::getInstance(); @@ -64,24 +66,19 @@ ItemShortcutContainer::~ItemShortcutContainer() delete mItemPopup; } -void ItemShortcutContainer::logic() +void ItemShortcutContainer::draw(gcn::Graphics *graphics) { - gcn::Widget::logic(); - - int i = itemShortcut->getItemCount(); + if (!isVisible()) + return; - if (i != mMaxItems) + if (config.getValue("guialpha", 0.8) != mAlpha) { - mMaxItems = i; - setWidth(getWidth()); + mAlpha = config.getValue("guialpha", 0.8); + mBackgroundImg->setAlpha(mAlpha); } -} -void ItemShortcutContainer::draw(gcn::Graphics *graphics) -{ Graphics *g = static_cast<Graphics*>(graphics); - graphics->setColor(gcn::Color(0, 0, 0)); graphics->setFont(getFont()); for (int i = 0; i < mMaxItems; i++) @@ -94,7 +91,7 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) // Draw item keyboard shortcut. const char *key = SDL_GetKeyName( (SDLKey) keyboard.getKeyValue(keyboard.KEY_SHORTCUT_1 + i)); - graphics->setColor(0x000000); + graphics->setColor(guiPalette->getColor(Palette::TEXT)); g->drawText(key, itemX + 2, itemY + 2, gcn::Graphics::LEFT); if (itemShortcut->getItem(i) < 0) @@ -102,6 +99,7 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) Item *item = player_node->getInventory()->findItem(itemShortcut->getItem(i)); + if (item) { // Draw item icon. @@ -115,14 +113,12 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) #endif toString(item->getQuantity()); g->drawImage(image, itemX, itemY); - g->drawText( - label, - itemX + mBoxWidth / 2, - itemY + mBoxHeight - 14, - gcn::Graphics::CENTER); + g->drawText(label, itemX + mBoxWidth / 2, + itemY + mBoxHeight - 14, gcn::Graphics::CENTER); } } } + if (mItemMoved) { // Draw the item image being dragged by the cursor. @@ -133,18 +129,11 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) const int tPosY = mCursorPosY - (image->getHeight() / 2); g->drawImage(image, tPosX, tPosY); - g->drawText( - toString(mItemMoved->getQuantity()), - tPosX + mBoxWidth / 2, - tPosY + mBoxHeight - 14, - gcn::Graphics::CENTER); + g->drawText(toString(mItemMoved->getQuantity()), + tPosX + mBoxWidth / 2, tPosY + mBoxHeight - 14, + gcn::Graphics::CENTER); } } - - if (config.getValue("guialpha", 0.8) != mAlpha) - { - mBackgroundImg->setAlpha(config.getValue("guialpha", 0.8)); - } } void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) @@ -156,10 +145,7 @@ void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) const int index = getIndexFromGrid(event.getX(), event.getY()); const int itemId = itemShortcut->getItem(index); - if (index == -1) - return; - - if (itemId < 0) + if (index == -1 || itemId < 0) return; Item *item = player_node->getInventory()->findItem(itemId); @@ -170,7 +156,8 @@ void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) itemShortcut->removeItem(index); } } - if (mItemMoved) { + if (mItemMoved) + { mCursorPosX = event.getX(); mCursorPosY = event.getY(); } @@ -180,14 +167,14 @@ void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) void ItemShortcutContainer::mousePressed(gcn::MouseEvent &event) { const int index = getIndexFromGrid(event.getX(), event.getY()); + if (index == -1) return; if (event.getButton() == gcn::MouseEvent::LEFT) { - // Stores the selected item if theirs one. - if (itemShortcut->isItemSelected()) + if (itemShortcut->isItemSelected() && inventoryWindow->isVisible()) { itemShortcut->setItem(index); itemShortcut->setItemSelected(-1); @@ -203,12 +190,9 @@ void ItemShortcutContainer::mousePressed(gcn::MouseEvent &event) if (!item) return; - /* Convert relative to the window coordinates to absolute screen - * coordinates. - */ - int mx, my; - SDL_GetMouseState(&mx, &my); - viewport->showPopup(mx, my, item); + // Convert relative to the window coordinates to absolute screen + // coordinates. + viewport->showPopup(viewport->getMouseX(), viewport->getMouseY(), item); } } @@ -234,6 +218,7 @@ void ItemShortcutContainer::mouseReleased(gcn::MouseEvent &event) { itemShortcut->useItem(index); } + if (mItemClicked) mItemClicked = false; } @@ -245,22 +230,17 @@ void ItemShortcutContainer::mouseMoved(gcn::MouseEvent &event) const int index = getIndexFromGrid(event.getX(), event.getY()); const int itemId = itemShortcut->getItem(index); - if (index == -1) - return; - - if (itemId < 0) + if (index == -1 || itemId < 0) return; Item *item = player_node->getInventory()->findItem(itemId); - if (item) + if (item && inventoryWindow->isVisible()) { - int mouseX, mouseY; - SDL_GetMouseState(&mouseX, &mouseY); - - mItemPopup->setItem(item->getInfo()); - mItemPopup->setOpaque(false); - mItemPopup->view(mouseX, mouseY); + if (item->getInfo().getName() != mItemPopup->getItemName()) + mItemPopup->setItem(item->getInfo()); + mItemPopup->updateColors(); + mItemPopup->view(viewport->getMouseX(), viewport->getMouseY()); } else { diff --git a/src/gui/itemshortcutcontainer.h b/src/gui/itemshortcutcontainer.h index 22d94ec2..9d188bf0 100644 --- a/src/gui/itemshortcutcontainer.h +++ b/src/gui/itemshortcutcontainer.h @@ -49,11 +49,6 @@ class ItemShortcutContainer : public ShortcutContainer virtual ~ItemShortcutContainer(); /** - * Handles the logic of the ItemContainer - */ - void logic(); - - /** * Draws the items. */ void draw(gcn::Graphics *graphics); diff --git a/src/gui/label.cpp b/src/gui/label.cpp new file mode 100644 index 00000000..e8d72ace --- /dev/null +++ b/src/gui/label.cpp @@ -0,0 +1,40 @@ +/* + * Aethyra + * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson + * Copyright (c) 2009 Aethyra Development Team + * + * This file is part of Aethyra based on original code + * from GUIChan. + * + * 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 "label.h" +#include "palette.h" + +Label::Label() : + gcn::Label() +{ +} + +Label::Label(const std::string& caption) : + gcn::Label(caption) +{ +} + +void Label::draw(gcn::Graphics* graphics) +{ + setForegroundColor(guiPalette->getColor(Palette::TEXT)); + gcn::Label::draw(static_cast<gcn::Graphics*>(graphics)); +} diff --git a/src/gui/label.h b/src/gui/label.h new file mode 100644 index 00000000..961286e0 --- /dev/null +++ b/src/gui/label.h @@ -0,0 +1,55 @@ +/* + * Aethyra + * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson + * Copyright (c) 2009 Aethyra Development Team + * + * This file is part of Aethyra based on original code + * from GUIChan. + * + * 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 LABEL_H +#define LABEL_H + +#include <guichan/widgets/label.hpp> + +/** + * Label widget. Same as the Guichan label but modified to use the palette + * system. + * + * \ingroup GUI + */ +class Label : public gcn::Label +{ + public: + /** + * Constructor. + */ + Label(); + + /** + * Constructor. This version of the constructor sets the label with an + * inintialization string. + */ + Label(const std::string& caption); + + /** + * Draws the label. + */ + void draw(gcn::Graphics* graphics); +}; + +#endif diff --git a/src/gui/listbox.cpp b/src/gui/listbox.cpp index 74d0b9ad..7ba84ee7 100644 --- a/src/gui/listbox.cpp +++ b/src/gui/listbox.cpp @@ -21,10 +21,11 @@ #include <guichan/font.hpp> #include <guichan/graphics.hpp> +#include <guichan/key.hpp> #include <guichan/listmodel.hpp> -#include "color.h" #include "listbox.h" +#include "palette.h" #include "../configuration.h" @@ -37,30 +38,25 @@ ListBox::ListBox(gcn::ListModel *listModel): void ListBox::draw(gcn::Graphics *graphics) { - if (!mListModel) + if (!mListModel || !isVisible()) return; if (config.getValue("guialpha", 0.8) != mAlpha) mAlpha = config.getValue("guialpha", 0.8); - bool valid; - const int red = (textColor->getColor('H', valid) >> 16) & 0xFF; - const int green = (textColor->getColor('H', valid) >> 8) & 0xFF; - const int blue = textColor->getColor('H', valid) & 0xFF; - const int alpha = (int)(mAlpha * 255.0f); - - graphics->setColor(gcn::Color(red, green, blue, alpha)); + graphics->setColor(guiPalette->getColor(Palette::HIGHLIGHT, + (int)(mAlpha * 255.0f))); graphics->setFont(getFont()); const int fontHeight = getFont()->getHeight(); - // Draw rectangle below the selected list element + // Draw filled rectangle around the selected list element if (mSelected >= 0) graphics->fillRectangle(gcn::Rectangle(0, fontHeight * mSelected, getWidth(), fontHeight)); // Draw the list elements - graphics->setColor(gcn::Color(0, 0, 0, 255)); + graphics->setColor(guiPalette->getColor(Palette::TEXT)); for (int i = 0, y = 0; i < mListModel->getNumberOfElements(); ++i, y += fontHeight) { @@ -68,6 +64,91 @@ void ListBox::draw(gcn::Graphics *graphics) } } +void ListBox::setSelected(int selected) +{ + if (!mListModel) + { + mSelected = -1; + } + else + { + if (selected < 0 && !mWrappingEnabled) + { + mSelected = -1; + } + else if (selected >= mListModel->getNumberOfElements() && + mWrappingEnabled) + { + mSelected = 0; + } + else if ((selected >= mListModel->getNumberOfElements() && + !mWrappingEnabled) || (selected < 0 && mWrappingEnabled)) + { + mSelected = mListModel->getNumberOfElements() - 1; + } + else + { + mSelected = selected; + } + } + gcn::ListBox::setSelected(mSelected); +} + +// -- KeyListener notifications +void ListBox::keyPressed(gcn::KeyEvent& keyEvent) +{ + gcn::Key key = keyEvent.getKey(); + + if (key.getValue() == gcn::Key::ENTER || key.getValue() == gcn::Key::SPACE) + { + distributeActionEvent(); + keyEvent.consume(); + } + else if (key.getValue() == gcn::Key::UP) + { + setSelected(mSelected - 1); + keyEvent.consume(); + } + else if (key.getValue() == gcn::Key::DOWN) + { + setSelected(mSelected + 1); + keyEvent.consume(); + } + else if (key.getValue() == gcn::Key::HOME) + { + setSelected(0); + keyEvent.consume(); + } + else if (key.getValue() == gcn::Key::END) + { + setSelected(getListModel()->getNumberOfElements() - 1); + keyEvent.consume(); + } +} + +void ListBox::mouseWheelMovedUp(gcn::MouseEvent& mouseEvent) +{ + if (isFocused()) + { + if (getSelected() > 0 || (getSelected() == 0 && mWrappingEnabled)) + { + setSelected(getSelected() - 1); + } + + mouseEvent.consume(); + } +} + +void ListBox::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent) +{ + if (isFocused()) + { + setSelected(getSelected() + 1); + + mouseEvent.consume(); + } +} + void ListBox::mouseDragged(gcn::MouseEvent &event) { // Pretend mouse is pressed continuously while dragged. Causes list diff --git a/src/gui/listbox.h b/src/gui/listbox.h index 12fcb955..cfb58f15 100644 --- a/src/gui/listbox.h +++ b/src/gui/listbox.h @@ -46,8 +46,27 @@ class ListBox : public gcn::ListBox */ void draw(gcn::Graphics *graphics); + // Inherited from KeyListener + + void keyPressed(gcn::KeyEvent& keyEvent); + + // Inherited from MouseListener + + void mouseWheelMovedUp(gcn::MouseEvent& mouseEvent); + + void mouseWheelMovedDown(gcn::MouseEvent& mouseEvent); + void mouseDragged(gcn::MouseEvent &event); + /** + * Sets the selected item. The selected item is represented by + * an index from the list model. + * + * @param selected the selected item as an index from the list model. + * @see getSelected + */ + void setSelected(int selected); + private: static float mAlpha; }; diff --git a/src/gui/login.cpp b/src/gui/login.cpp index e68a8da1..281a25a2 100644 --- a/src/gui/login.cpp +++ b/src/gui/login.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/widgets/label.hpp> - #include "button.h" #include "checkbox.h" +#include "label.h" #include "listbox.h" #include "login.h" #include "ok_dialog.h" @@ -48,12 +47,12 @@ static const int FIELD_WIDTH = LOGIN_DIALOG_WIDTH - 70; LoginDialog::LoginDialog(LoginData *loginData): Window(_("Login")), mLoginData(loginData) { - gcn::Label *userLabel = new gcn::Label(_("Name:")); - gcn::Label *passLabel = new gcn::Label(_("Password:")); + gcn::Label *userLabel = new Label(_("Name:")); + gcn::Label *passLabel = new Label(_("Password:")); #ifdef EATHENA_SUPPORT - gcn::Label *serverLabel = new gcn::Label(_("Server:")); - gcn::Label *portLabel = new gcn::Label(_("Port:")); - gcn::Label *dropdownLabel = new gcn::Label(_("Recent:")); + gcn::Label *serverLabel = new Label(_("Server:")); + gcn::Label *portLabel = new Label(_("Port:")); + gcn::Label *dropdownLabel = new Label(_("Recent:")); std::vector<std::string> dfltServer; dfltServer.push_back("server.themanaworld.org"); std::vector<std::string> dfltPort; @@ -124,14 +123,13 @@ LoginDialog::LoginDialog(LoginData *loginData): place(3, 6, mOkButton); reflowLayout(250, 0); - setLocationRelativeTo(getParent()); + center(); setVisible(true); - if (mUserField->getText().empty()) { + if (mUserField->getText().empty()) mUserField->requestFocus(); - } else { + else mPassField->requestFocus(); - } mOkButton->setEnabled(canSubmit()); } @@ -179,15 +177,13 @@ void LoginDialog::action(const gcn::ActionEvent &event) #ifdef EATHENA_SUPPORT // Transfer these fields on to the register dialog mLoginData->hostname = mServerField->getText(); + if (isUShort(mPortField->getText())) - { mLoginData->port = getUShort(mPortField->getText()); - } else - { mLoginData->port = 6901; - } #endif + mLoginData->username = mUserField->getText(); mLoginData->password = mPassField->getText(); @@ -223,14 +219,12 @@ bool LoginDialog::isUShort(const std::string &str) strPtr != strEnd; ++strPtr) { if (*strPtr < '0' || *strPtr > '9') - { return false; - } + l = l * 10 + (*strPtr - '0'); // *strPtr - '0' will never be negative + if (l > 65535) - { return false; - } } return true; } @@ -306,9 +300,7 @@ void LoginDialog::DropDownList::save(const std::string &server, ++sPtr, ++pPtr) { if (*sPtr != server || *pPtr != port) - { saveEntry(*sPtr, *pPtr, position); - } } } @@ -320,27 +312,24 @@ int LoginDialog::DropDownList::getNumberOfElements() std::string LoginDialog::DropDownList::getElementAt(int i) { if (i < 0 || i >= getNumberOfElements()) - { - return ""; - } + return ""; + return getServerAt(i) + ":" + getPortAt(i); } std::string LoginDialog::DropDownList::getServerAt(int i) { if (i < 0 || i >= getNumberOfElements()) - { return ""; - } + return mServers.at(i); } std::string LoginDialog::DropDownList::getPortAt(int i) { if (i < 0 || i >= getNumberOfElements()) - { return ""; - } + return mPorts.at(i); } #endif diff --git a/src/gui/menuwindow.cpp b/src/gui/menuwindow.cpp index 25ece461..e6ae2d3b 100644 --- a/src/gui/menuwindow.cpp +++ b/src/gui/menuwindow.cpp @@ -25,7 +25,9 @@ #include "button.h" #include "menuwindow.h" -#include "windowcontainer.h" +#include "window.h" + +#include "../graphics.h" #include "../utils/gettext.h" @@ -54,13 +56,8 @@ namespace { } MenuWindow::MenuWindow(): - Window() + Popup("Menu") { - setResizable(false); - setWindowName("Menu"); - setMovable(false); - setTitleBarHeight(0); - // Buttons static const char *buttonNames[] = { @@ -90,7 +87,7 @@ MenuWindow::MenuWindow(): h = btn->getHeight(); } - setPosition(windowContainer->getWidth() - x - 3, 3); + setPosition(graphics->getWidth() - x - 3, 3); setContentSize(x - 3, h); } @@ -99,7 +96,6 @@ void MenuWindow::draw(gcn::Graphics *graphics) drawChildren(graphics); } - void MenuWindowListener::action(const gcn::ActionEvent &event) { Window *window = NULL; diff --git a/src/gui/menuwindow.h b/src/gui/menuwindow.h index 9bb54e29..c3d5673e 100644 --- a/src/gui/menuwindow.h +++ b/src/gui/menuwindow.h @@ -22,14 +22,14 @@ #ifndef MENU_H #define MENU_H -#include "window.h" +#include "popup.h" /** * The Button Menu. * * \ingroup Interface */ -class MenuWindow : public Window +class MenuWindow : public Popup { public: /** diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index 4347c9cc..93a55688 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -79,19 +79,6 @@ void Minimap::setMapImage(Image *img) mMapImage->getWidth() + offsetX : titleWidth); setMaxHeight(mMapImage->getHeight() + offsetY); - // Make sure the window is within the minimum and maximum boundaries - // TODO: Shouldn't this be happening automatically within the Window - // class? - if (getMinWidth() > getWidth()) - setWidth(getMinWidth()); - else if (getMaxWidth() < getWidth()) - setWidth(getMaxWidth()); - if (getMinHeight() > getHeight()) - setHeight(getMinHeight()); - else if (getMaxHeight() < getHeight()) - setHeight(getMaxHeight()); - - setContentSize(getWidth() - offsetX, getHeight() - offsetY); setDefaultSize(getX(), getY(), getWidth(), getHeight()); resetToDefaultSize(); @@ -112,6 +99,9 @@ void Minimap::draw(gcn::Graphics *graphics) { setVisible(mShow); + if (!isVisible()) + return; + Window::draw(graphics); if (!mShow) diff --git a/src/gui/ministatus.cpp b/src/gui/ministatus.cpp index 5bc25bdb..95577e69 100644 --- a/src/gui/ministatus.cpp +++ b/src/gui/ministatus.cpp @@ -22,6 +22,7 @@ #include "gui.h" #include "ministatus.h" #include "progressbar.h" +#include "status.h" #include "../animatedsprite.h" #include "../configuration.h" @@ -30,13 +31,9 @@ #include "../utils/stringutils.h" -MiniStatusWindow::MiniStatusWindow() +MiniStatusWindow::MiniStatusWindow(): + Popup("MiniStatus") { - setWindowName("MiniStatus"); - setResizable(false); - setMovable(false); - setTitleBarHeight(0); - mHpBar = new ProgressBar(1.0f, 100, 20, 0, 171, 34); #ifdef EATHENA_SUPPORT mMpBar = new ProgressBar(1.0f, 100, 20, 26, 102, 230); @@ -62,8 +59,6 @@ MiniStatusWindow::MiniStatusWindow() setContentSize(mHpBar->getX() + mHpBar->getWidth(), mHpBar->getY() + mHpBar->getHeight()); #endif - setDefaultSize(0, 0, getWidth(), getHeight()); - loadWindowState(); } void MiniStatusWindow::setIcon(int index, AnimatedSprite *sprite) @@ -86,43 +81,10 @@ extern volatile int tick_time; void MiniStatusWindow::update() { - // HP Bar coloration - int maxHp = player_node->getMaxHp(); - int hp = player_node->getHp(); - if (hp < int(maxHp / 3)) - { - mHpBar->setColor(223, 32, 32); // Red - } - else if (hp < int((maxHp / 3) * 2)) - { - mHpBar->setColor(230, 171, 34); // Orange - } - else - { - mHpBar->setColor(0, 171, 34); // Green - } - + StatusWindow::updateHPBar(mHpBar); #ifdef EATHENA_SUPPORT - float xp = (float) player_node->getXp() / player_node->mXpForNextLevel; - - if (xp != xp) xp = 0.0f; // check for NaN - if (xp < 0.0f) xp = 0.0f; // make sure the experience isn't negative (uninitialized pointer most likely) - if (xp > 1.0f) xp = 1.0f; -#endif - - mHpBar->setProgress((float) hp / maxHp); -#ifdef EATHENA_SUPPORT - mMpBar->setProgress((float) player_node->mMp / player_node->mMaxMp); - mXpBar->setProgress(xp); -#endif - - // Update labels - mHpBar->setText(toString(player_node->getHp())); -#ifdef EATHENA_SUPPORT - mMpBar->setText(toString(player_node->mMp)); - - std::stringstream updatedText; - updatedText << (float) ((int) (xp * 10000.0f)) / 100.0f << "%"; + StatusWindow::updateMPBar(mMpBar); + StatusWindow::updateXPBar(mXpBar); // Displays the number of monsters to next lvl // (disabled for now but interesting idea) @@ -136,8 +98,6 @@ void MiniStatusWindow::update() << config.getValue("xpBarMonsterCounterName", "Monsters") <<" left..."; } */ - - mXpBar->setText(updatedText.str()); #endif for (unsigned int i = 0; i < mIcons.size(); i++) diff --git a/src/gui/ministatus.h b/src/gui/ministatus.h index b69f9a14..f6d80ca7 100644 --- a/src/gui/ministatus.h +++ b/src/gui/ministatus.h @@ -22,7 +22,7 @@ #ifndef MINISTATUS_H #define MINISTATUS_H -#include "window.h" +#include "popup.h" #include <vector> @@ -34,7 +34,7 @@ class ProgressBar; * * \ingroup Interface */ -class MiniStatusWindow : public Window +class MiniStatusWindow : public Popup { public: /** diff --git a/src/gui/npc_text.cpp b/src/gui/npc_text.cpp index 48f2adb7..5158e966 100644 --- a/src/gui/npc_text.cpp +++ b/src/gui/npc_text.cpp @@ -28,18 +28,33 @@ #include "../npc.h" +#include "../net/messageout.h" +#ifdef TMWSERV_SUPPORT +#include "../net/tmwserv/gameserver/player.h" +#else +#include "../net/ea/protocol.h" +#endif + #include "../utils/gettext.h" -NpcTextDialog::NpcTextDialog(): - Window(_("NPC")), - mState(NPC_TEXT_STATE_WAITING) +#ifdef TMWSERV_SUPPORT +NpcTextDialog::NpcTextDialog() +#else +NpcTextDialog::NpcTextDialog(Network *network) +#endif + : Window(_("NPC")) +#ifdef EATHENA_SUPPORT + , mNetwork(network) +#endif + , mState(NPC_TEXT_STATE_WAITING) { + setWindowName("NPCText"); setResizable(true); setMinWidth(200); setMinHeight(150); - setDefaultSize(0, 0, 260, 200); + setDefaultSize(260, 200, ImageRect::CENTER); mTextBox = new TextBox; mTextBox->setEditable(false); @@ -57,8 +72,14 @@ NpcTextDialog::NpcTextDialog(): Layout &layout = getLayout(); layout.setRowHeight(0, Layout::AUTO_SET); + center(); loadWindowState(); - setLocationRelativeTo(getParent()); +} + +void NpcTextDialog::clearText() +{ + NPC::isTalking = false; + setText(""); } void NpcTextDialog::setText(const std::string &text) @@ -92,13 +113,15 @@ void NpcTextDialog::action(const gcn::ActionEvent &event) if (event.getId() == "ok") { if (mState == NPC_TEXT_STATE_NEXT && current_npc) { - current_npc->nextDialog(); + nextDialog(); addText("\n> Next\n"); } else if (mState == NPC_TEXT_STATE_CLOSE || (mState == NPC_TEXT_STATE_NEXT && !current_npc)) { setText(""); + if (current_npc) nextDialog(); setVisible(false); - if (current_npc) current_npc->handleDeath(); + current_npc = 0; + NPC::isTalking = false; } else return; } else return; @@ -108,6 +131,26 @@ void NpcTextDialog::action(const gcn::ActionEvent &event) mState = NPC_TEXT_STATE_WAITING; } +void NpcTextDialog::nextDialog(int npcID) +{ +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::talkToNPC(npcID, false); +#else + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_NEXT_REQUEST); + outMsg.writeInt32(npcID); +#endif +} + +void NpcTextDialog::closeDialog(int npcID) +{ +#ifdef EATHENA_SUPPORT + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_CLOSE); + outMsg.writeInt32(npcID); +#endif +} + void NpcTextDialog::widgetResized(const gcn::Event &event) { Window::widgetResized(event); @@ -115,3 +158,8 @@ void NpcTextDialog::widgetResized(const gcn::Event &event) setText(mText); } +void NpcTextDialog::requestFocus() +{ + loadWindowState(); + setVisible(true); +} diff --git a/src/gui/npc_text.h b/src/gui/npc_text.h index a1373830..4c0c31e3 100644 --- a/src/gui/npc_text.h +++ b/src/gui/npc_text.h @@ -28,6 +28,11 @@ #include "window.h" +#include "../npc.h" + +#ifdef EATHENA_SUPPORT +class Network; +#endif class TextBox; /** @@ -43,7 +48,11 @@ class NpcTextDialog : public Window, public gcn::ActionListener * * @see Window::Window */ +#ifdef TMWSERV_SUPPORT NpcTextDialog(); +#else + NpcTextDialog(Network *network); +#endif /** * Called when receiving actions from the widgets. @@ -75,6 +84,23 @@ class NpcTextDialog : public Window, public gcn::ActionListener void showCloseButton(); /** + * Notifies the server that the client has performed a next action. + */ + void nextDialog(int npcID = current_npc); + + /** + * Notifies the server that the client has performed a close action. + */ + void closeDialog(int npcID = current_npc); + + /** + * Initializes window width to the last known setting. Since the dialog + * doesn't need any extra focus outside of what it's given in the Game + * class, this is all it does for now. + */ + void requestFocus(); + + /** * Called when resizing the window. * * @param event The calling event @@ -82,6 +108,9 @@ class NpcTextDialog : public Window, public gcn::ActionListener void widgetResized(const gcn::Event &event); private: +#ifdef EATHENA_SUPPORT + Network *mNetwork; +#endif gcn::ScrollArea *mScrollArea; TextBox *mTextBox; gcn::Button *mButton; @@ -93,7 +122,9 @@ class NpcTextDialog : public Window, public gcn::ActionListener NPC_TEXT_STATE_NEXT, NPC_TEXT_STATE_CLOSE }; - int mState; + NPCTextState mState; }; +extern NpcTextDialog *npcTextDialog; + #endif // NPC_TEXT_H diff --git a/src/gui/npcintegerdialog.cpp b/src/gui/npcintegerdialog.cpp index 463f46ae..a7ae2748 100644 --- a/src/gui/npcintegerdialog.cpp +++ b/src/gui/npcintegerdialog.cpp @@ -28,24 +28,37 @@ #include "../npc.h" +#include "../net/messageout.h" +#ifdef EATHENA_SUPPORT +#include "../net/ea/protocol.h" +#endif + #include "../utils/gettext.h" #include "../utils/strprintf.h" -extern NpcTextDialog *npcTextDialog; - -NpcIntegerDialog::NpcIntegerDialog(): - Window(_("NPC Number Request")) +#ifdef TMWSERV_SUPPORT +NpcIntegerDialog::NpcIntegerDialog() +#else +NpcIntegerDialog::NpcIntegerDialog(Network *network) +#endif + : Window(_("NPC Number Request")) +#ifdef EATHENA_SUPPORT + , mNetwork(network) +#endif { + setWindowName("NPCInteger"); mValueField = new IntTextField; + setDefaultSize(175, 75, ImageRect::CENTER); + mDecButton = new Button("-", "decvalue", this); mIncButton = new Button("+", "incvalue", this); gcn::Button *okButton = new Button(_("OK"), "ok", this); gcn::Button *cancelButton = new Button(_("Cancel"), "cancel", this); gcn::Button *resetButton = new Button(_("Reset"), "reset", this); - mDecButton->setSize(20, 20); - mIncButton->setSize(20, 20); + mDecButton->adjustSize(); + mDecButton->setWidth(mIncButton->getWidth()); ContainerPlacer place; place = getPlacer(0, 0); @@ -60,7 +73,9 @@ NpcIntegerDialog::NpcIntegerDialog(): place(3, 0, okButton); reflowLayout(175, 0); - setLocationRelativeTo(getParent()); + center(); + setDefaultSize(); + loadWindowState(); } void NpcIntegerDialog::setRange(int min, int max) @@ -73,18 +88,23 @@ int NpcIntegerDialog::getValue() return mValueField->getValue(); } +void NpcIntegerDialog::reset() +{ + mValueField->reset(); +} + void NpcIntegerDialog::action(const gcn::ActionEvent &event) { - int finish = 0; + bool finish = false; if (event.getId() == "ok") { - finish = 1; + finish = true; npcTextDialog->addText(strprintf("\n> %d\n", mValueField->getValue())); } else if (event.getId() == "cancel") { - finish = 1; + finish = true; mValueField->reset(); npcTextDialog->addText(_("\n> Cancel\n")); } @@ -104,7 +124,15 @@ void NpcIntegerDialog::action(const gcn::ActionEvent &event) if (finish) { setVisible(false); - current_npc->integerInput(mValueField->getValue()); + NPC::isTalking = false; + +#ifdef EATHENA_SUPPORT + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_INT_RESPONSE); + outMsg.writeInt32(current_npc); + outMsg.writeInt32(mValueField->getValue()); +#endif + mValueField->reset(); } } @@ -123,3 +151,13 @@ void NpcIntegerDialog::requestFocus() { mValueField->requestFocus(); } + +void NpcIntegerDialog::setVisible(bool visible) +{ + if (visible) { + npcTextDialog->setVisible(true); + requestFocus(); + } + + Window::setVisible(visible); +} diff --git a/src/gui/npcintegerdialog.h b/src/gui/npcintegerdialog.h index 941bb55a..df74c904 100644 --- a/src/gui/npcintegerdialog.h +++ b/src/gui/npcintegerdialog.h @@ -26,6 +26,9 @@ #include "window.h" +#ifdef EATHENA_SUPPORT +class Network; +#endif class IntTextField; /** @@ -41,7 +44,11 @@ class NpcIntegerDialog : public Window, public gcn::ActionListener * * @see Window::Window */ +#ifdef TMWSERV_SUPPORT NpcIntegerDialog(); +#else + NpcIntegerDialog(Network *network); +#endif /** * Called when receiving actions from the widgets. @@ -54,6 +61,11 @@ class NpcIntegerDialog : public Window, public gcn::ActionListener int getValue(); /** + * Resets the integer input field. + */ + void reset(); + + /** * Prepares the NPC dialog. * * @param min The minimum value to allow @@ -78,10 +90,17 @@ class NpcIntegerDialog : public Window, public gcn::ActionListener */ void requestFocus(); + void setVisible(bool visible); + private: +#ifdef EATHENA_SUPPORT + Network *mNetwork; +#endif gcn::Button *mDecButton; gcn::Button *mIncButton; IntTextField *mValueField; }; +extern NpcIntegerDialog *npcIntegerDialog; + #endif // GUI_NPCINTEGERDIALOG_H diff --git a/src/gui/npclistdialog.cpp b/src/gui/npclistdialog.cpp index 82e05fd5..968e2514 100644 --- a/src/gui/npclistdialog.cpp +++ b/src/gui/npclistdialog.cpp @@ -31,24 +31,39 @@ #include "../npc.h" +#include "../net/messageout.h" +#ifdef TMWSERV_SUPPORT +#include "../net/tmwserv/gameserver/player.h" +#else +#include "../net/ea/protocol.h" +#endif + #include "../utils/gettext.h" #include "../utils/strprintf.h" -extern NpcTextDialog *npcTextDialog; - -NpcListDialog::NpcListDialog(): - Window(_("NPC")) +#ifdef TMWSERV_SUPPORT +NpcListDialog::NpcListDialog() +#else +NpcListDialog::NpcListDialog(Network *network) +#endif + : Window("NPC") +#ifdef EATHENA_SUPPORT + , mNetwork(network) +#endif { + setWindowName("NPCList"); setResizable(true); setMinWidth(200); setMinHeight(150); - setDefaultSize(0, 0, 260, 200); + setDefaultSize(260, 200, ImageRect::CENTER); mItemList = new ListBox(this); mItemList->setWrappingEnabled(true); + gcn::ScrollArea *scrollArea = new ScrollArea(mItemList); + gcn::Button *okButton = new Button(_("OK"), "ok", this); gcn::Button *cancelButton = new Button(_("Cancel"), "cancel", this); @@ -56,14 +71,14 @@ NpcListDialog::NpcListDialog(): scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); place(0, 0, scrollArea, 5).setPadding(3); - place(3, 1, okButton); - place(4, 1, cancelButton); + place(3, 1, cancelButton); + place(4, 1, okButton); Layout &layout = getLayout(); layout.setRowHeight(0, Layout::AUTO_SET); + center(); loadWindowState(); - setLocationRelativeTo(getParent()); } int NpcListDialog::getNumberOfElements() @@ -92,6 +107,8 @@ void NpcListDialog::parseItems(const std::string &itemString) void NpcListDialog::reset() { + NPC::isTalking = false; + mItemList->setSelected(-1); mItems.clear(); } @@ -102,6 +119,7 @@ void NpcListDialog::action(const gcn::ActionEvent &event) { // Send the selected index back to the server int selectedIndex = mItemList->getSelected(); + if (selectedIndex > -1) { choice = selectedIndex + 1; @@ -113,12 +131,40 @@ void NpcListDialog::action(const gcn::ActionEvent &event) { choice = 0xff; // 0xff means cancel npcTextDialog->addText(_("\n> Cancel\n")); + npcTextDialog->showCloseButton(); } if (choice) { setVisible(false); + saveWindowState(); reset(); - current_npc->dialogChoice(choice); + +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::selectFromNPC(current_npc, choice); +#else + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_LIST_CHOICE); + outMsg.writeInt32(current_npc); + outMsg.writeInt8(choice); +#endif } } + +void NpcListDialog::setVisible(bool visible) +{ + if (visible) { + npcTextDialog->setVisible(true); + requestFocus(); + } + + Window::setVisible(visible); +} + +void NpcListDialog::requestFocus() +{ + mItemList->requestFocus(); + mItemList->setSelected(0); + loadWindowState(); + setVisible(true); +} diff --git a/src/gui/npclistdialog.h b/src/gui/npclistdialog.h index 7e37c7e6..6c1e02e3 100644 --- a/src/gui/npclistdialog.h +++ b/src/gui/npclistdialog.h @@ -29,6 +29,10 @@ #include <vector> +#ifdef EATHENA_SUPPORT +class Network; +#endif + /** * The npc list dialog. * @@ -43,7 +47,11 @@ class NpcListDialog : public Window, public gcn::ActionListener, * * @see Window::Window */ +#ifdef TMWSERV_SUPPORT NpcListDialog(); +#else + NpcListDialog(Network *network); +#endif /** * Called when receiving actions from the widgets. @@ -77,10 +85,23 @@ class NpcListDialog : public Window, public gcn::ActionListener, */ void reset(); + void setVisible(bool visible); + + /** + * Requests the listbox to take focus for input and sets window width + * to the last known setting. + */ + void requestFocus(); + private: +#ifdef EATHENA_SUPPORT + Network *mNetwork; +#endif gcn::ListBox *mItemList; std::vector<std::string> mItems; }; +extern NpcListDialog *npcListDialog; + #endif // GUI_NPCLISTDIALOG_H diff --git a/src/gui/npcstringdialog.cpp b/src/gui/npcstringdialog.cpp index d9bf5682..c84de015 100644 --- a/src/gui/npcstringdialog.cpp +++ b/src/gui/npcstringdialog.cpp @@ -28,16 +28,29 @@ #include "../npc.h" +#include "../net/messageout.h" +#ifdef EATHENA_SUPPORT +#include "../net/ea/protocol.h" +#endif + #include "../utils/gettext.h" #include "../utils/strprintf.h" -extern NpcTextDialog *npcTextDialog; - -NpcStringDialog::NpcStringDialog(): - Window(_("NPC Text Request")) +#ifdef TMWSERV_SUPPORT +NpcStringDialog::NpcStringDialog() +#else +NpcStringDialog::NpcStringDialog(Network *network) +#endif + : Window(_("NPC Text Request")) +#ifdef EATHENA_SUPPORT + , mNetwork(network) +#endif { + setWindowName("NPCString"); mValueField = new TextField(""); + setDefaultSize(175, 75, ImageRect::CENTER); + gcn::Button *okButton = new Button(_("OK"), "ok", this); gcn::Button *cancelButton = new Button(_("Cancel"), "cancel", this); @@ -46,7 +59,9 @@ NpcStringDialog::NpcStringDialog(): place(2, 1, okButton); reflowLayout(175, 0); - setLocationRelativeTo(getParent()); + center(); + setDefaultSize(); + loadWindowState(); } std::string NpcStringDialog::getValue() @@ -74,8 +89,19 @@ void NpcStringDialog::action(const gcn::ActionEvent &event) } setVisible(false); - current_npc->stringInput(mValueField->getText()); + NPC::isTalking = false; + + std::string text = mValueField->getText(); mValueField->setText(""); + +#ifdef EATHENA_SUPPORT + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_STR_RESPONSE); + outMsg.writeInt16(text.length() + 9); + outMsg.writeInt32(current_npc); + outMsg.writeString(text, text.length()); + outMsg.writeInt8(0); +#endif } bool NpcStringDialog::isInputFocused() @@ -87,3 +113,13 @@ void NpcStringDialog::requestFocus() { mValueField->requestFocus(); } + +void NpcStringDialog::setVisible(bool visible) +{ + if (visible) { + npcTextDialog->setVisible(true); + requestFocus(); + } + + Window::setVisible(visible); +} diff --git a/src/gui/npcstringdialog.h b/src/gui/npcstringdialog.h index 0faaf203..94cd59b2 100644 --- a/src/gui/npcstringdialog.h +++ b/src/gui/npcstringdialog.h @@ -26,6 +26,10 @@ #include <guichan/actionlistener.hpp> +#ifdef EATHENA_SUPPORT +class Network; +#endif + /** * The npc integer input dialog. * @@ -39,7 +43,11 @@ class NpcStringDialog : public Window, public gcn::ActionListener * * @see Window::Window */ +#ifdef TMWSERV_SUPPORT NpcStringDialog(); +#else + NpcStringDialog(Network *network); +#endif /** * Called when receiving actions from the widgets. @@ -68,9 +76,16 @@ class NpcStringDialog : public Window, public gcn::ActionListener */ void requestFocus(); + void setVisible(bool visible); + private: +#ifdef EATHENA_SUPPORT + Network *mNetwork; +#endif gcn::TextField *mValueField; std::string mDefault; }; +extern NpcStringDialog *npcStringDialog; + #endif // GUI_NPCSTRINGDIALOG_H diff --git a/src/gui/ok_dialog.cpp b/src/gui/ok_dialog.cpp index 4df3fa07..f1a97b49 100644 --- a/src/gui/ok_dialog.cpp +++ b/src/gui/ok_dialog.cpp @@ -22,6 +22,7 @@ #include <guichan/font.hpp> #include "button.h" +#include "gui.h" #include "ok_dialog.h" #include "scrollarea.h" #include "textbox.h" @@ -46,14 +47,15 @@ OkDialog::OkDialog(const std::string &title, const std::string &msg, mTextBox->setTextWrapped(msg, 260); int numRows = mTextBox->getNumberOfRows(); + const int fontHeight = getFont()->getHeight(); if (numRows > 1) { - // 15 == height of each line of text (based on font heights) // 14 == row top + bottom graphic pixel heights - setContentSize(mTextBox->getMinWidth() + 15, 15 + (numRows * 15) + okButton->getHeight()); - mTextArea->setDimension(gcn::Rectangle(4, 5, mTextBox->getMinWidth() + 5, - 3 + (numRows * 14))); + setContentSize(mTextBox->getMinWidth() + fontHeight, ((numRows + 1) * + fontHeight) + okButton->getHeight()); + mTextArea->setDimension(gcn::Rectangle(4, 5, + mTextBox->getMinWidth() + 5, 3 + (numRows * fontHeight))); } else { @@ -62,17 +64,17 @@ OkDialog::OkDialog(const std::string &title, const std::string &msg, width = getFont()->getWidth(msg); if (width < okButton->getWidth()) width = okButton->getWidth(); - setContentSize(width + 15, 30 + okButton->getHeight()); + setContentSize(width + fontHeight, 30 + okButton->getHeight()); mTextArea->setDimension(gcn::Rectangle(4, 5, width + 5, 17)); } okButton->setPosition((mTextBox->getMinWidth() - okButton->getWidth()) / 2, - (numRows * 14) + okButton->getHeight() - 8); + ((numRows - 1) * fontHeight) + okButton->getHeight() + 2); add(mTextArea); add(okButton); - setLocationRelativeTo(getParent()); + center(); setVisible(true); okButton->requestFocus(); } @@ -92,7 +94,6 @@ void OkDialog::action(const gcn::ActionEvent &event) } // Can we receive anything else anyway? - if (event.getId() == "ok") { + if (event.getId() == "ok") scheduleDelete(); - } } diff --git a/src/gui/palette.cpp b/src/gui/palette.cpp new file mode 100644 index 00000000..b1e165aa --- /dev/null +++ b/src/gui/palette.cpp @@ -0,0 +1,347 @@ +/* + * Configurable text colors + * Copyright (C) 2008 Douglas Boffey <dougaboffey@netscape.net> + * 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 <math.h> + +#include "palette.h" +#include "gui.h" + +#include "../configuration.h" +#include "../game.h" + +#include "../utils/gettext.h" +#include "../utils/stringutils.h" + +const gcn::Color Palette::BLACK = gcn::Color(0, 0, 0); + +const gcn::Color Palette::RAINBOW_COLORS[7] = { + gcn::Color(255, 0, 0), + gcn::Color(255, 153, 0), + gcn::Color(255, 255, 0), + gcn::Color(0, 153, 0), + gcn::Color(0, 204, 204), + gcn::Color(51, 0, 153), + gcn::Color(153, 0, 153) +}; +/** Number of Elemets of RAINBOW_COLORS */ +const int Palette::RAINBOW_COLOR_COUNT = 7; + +std::string Palette::getConfigName(const std::string &typeName) +{ + std::string res = "Color" + typeName; + + int pos = 5; + for (size_t i = 0; i < typeName.length(); i++) + { + if (i == 0 || typeName[i] == '_') + { + if (i > 0) + i++; + + res[pos] = typeName[i]; + } + else + { + res[pos] = tolower(typeName[i]); + } + pos++; + } + res.erase(pos, res.length() - pos); + + return res; +} + +DEFENUMNAMES(ColorType, COLOR_TYPE) + +const int Palette::GRADIENT_DELAY = 40; + +Palette::Palette() : + mRainbowTime(tick_time), + mColVector(ColVector(TYPE_COUNT)) +{ + std::string indent = " "; + addColor(TEXT, 0x000000, STATIC, _("Text")); + addColor(SHADOW, 0x000000, STATIC, indent + _("Text Shadow")); + addColor(OUTLINE, 0x000000, STATIC, indent + _("Text Outline")); + addColor(PROGRESS_BAR, 0xffffff, STATIC, indent + _("Progress Bar Labels")); + + addColor(BACKGROUND, 0xffffff, STATIC, _("Background")); + + addColor(HIGHLIGHT, 0xebc873, STATIC, _("Highlight"), 'H'); + addColor(TAB_HIGHLIGHT, 0xff0000, PULSE, indent + _("Tab Highlight")); + addColor(SHOP_WARNING, 0x910000, STATIC, indent + + _("Item too expensive")); + + addColor(CHAT, 0x000000, STATIC, _("Chat"), 'C'); + addColor(GM, 0xff0000, STATIC, indent + _("GM"), 'G'); + addColor(PLAYER, 0x1fa052, STATIC, indent + _("Player"), 'Y'); + addColor(WHISPER, 0x0000ff, STATIC, indent + _("Whisper"), 'W'); + addColor(IS, 0xa08527, STATIC, indent + _("Is"), 'I'); + addColor(PARTY, 0xff00d8, STATIC, indent + _("Party"), 'P'); + addColor(SERVER, 0x8415e2, STATIC, indent + _("Server"), 'S'); + addColor(LOGGER, 0x919191, STATIC, indent + _("Logger"), 'L'); + addColor(HYPERLINK, 0xe50d0d, STATIC, indent + _("Hyperlink"), '<'); + + addColor(BEING, 0xffffff, STATIC, _("Being")); + addColor(PC, 0xffffff, STATIC, indent + _("Other Player's Names")); + addColor(SELF, 0xff8040, STATIC, indent + _("Own Name")); + addColor(GM_NAME, 0x00ff00, STATIC, indent + _("GM Names")); + addColor(NPC, 0xc8c8ff, STATIC, indent + _("NPCs")); + addColor(MONSTER, 0xff4040, STATIC, indent + _("Monsters")); + + addColor(UNKNOWN_ITEM, 0x000000, STATIC, _("Unknown Item Type")); + addColor(GENERIC, 0x21a5b1, STATIC, indent + _("Generic")); + addColor(HEAD, 0x527fa4, STATIC, indent + _("Hat")); + addColor(USABLE, 0x268d24, STATIC, indent + _("Usable")); + addColor(TORSO, 0xd12aa4, STATIC, indent + _("Shirt")); + addColor(ONEHAND, 0xf42a2a, STATIC, indent + _("1 Handed Weapons")); + addColor(LEGS, 0x699900, STATIC, indent + _("Pants")); + addColor(FEET, 0xaa1d48, STATIC, indent + _("Shoes")); + addColor(TWOHAND, 0xf46d0e, STATIC, indent + _("2 Handed Weapons")); + addColor(SHIELD, 0x9c2424, STATIC, indent + _("Shield")); + addColor(RING, 0x0000ff, STATIC, indent + _("Ring")); + addColor(ARMS, 0x9c24e8, STATIC, indent + _("Arms")); + addColor(AMMO, 0x8b6311, STATIC, indent + _("Ammo")); + + addColor(PARTICLE, 0xffffff, STATIC, _("Particle Effects")); + addColor(PICKUP_INFO, 0x28dc28, STATIC, indent + _("Pickup Notification")); + addColor(EXP_INFO, 0xffff00, STATIC, indent + _("Exp Notification")); + addColor(HIT_PLAYER_MONSTER, 0x0064ff, STATIC, + indent + _("Player hits Monster")); + addColor(HIT_MONSTER_PLAYER, 0xff3232, STATIC, + indent + _("Monster hits Player")); + addColor(HIT_CRITICAL, 0xff0000, RAINBOW, indent + _("Critical Hit")); + addColor(MISS, 0xffff00, STATIC, indent + _("Misses")); + commit(true); +} + +Palette::~Palette() +{ + const std::string *configName; + for (ColVector::iterator col = mColVector.begin(), + colEnd = mColVector.end(); col != colEnd; ++col) + { + configName = &ColorTypeNames[col->type]; + config.setValue(*configName + "Gradient", col->committedGrad); + if (col->grad == STATIC || col->grad == PULSE) + { + config.setValue(*configName, toString(col->getRGB())); + } + } +} + +const gcn::Color& Palette::getColor(char c, bool &valid) + { + for (ColVector::const_iterator col = mColVector.begin(), + colEnd = mColVector.end(); col != colEnd; ++col) + { + if (col->ch == c) + { + valid = true; + return col->color; + } + } + valid = false; + return BLACK; +} + +void Palette::setColor(ColorType type, int r, int g, int b) +{ + mColVector[type].color.r = r; + mColVector[type].color.g = g; + mColVector[type].color.b = b; +} + +void Palette::setGradient(ColorType type, GradientType grad) +{ + ColorElem *elem = &mColVector[type]; + if (elem->grad != STATIC && grad == STATIC) + { + for (size_t i = 0; i < mGradVector.size(); i++) + { + if (mGradVector[i] == elem) + { + mGradVector.erase(mGradVector.begin() + i); + break; + } + } + } + else if (elem->grad == STATIC && grad != STATIC) + { + mGradVector.push_back(elem); + } + + if (elem->grad != grad) + { + elem->grad = grad; + } +} + +std::string Palette::getElementAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return ""; + } + return mColVector[i].text; +} + +Palette::ColorType Palette::getColorTypeAt(int i) +{ + if (i < 0 || i >= getNumberOfElements()) + { + return CHAT; + } + return mColVector[i].type; +} + +void Palette::commit(bool commitNonStatic) +{ + for (ColVector::iterator i = mColVector.begin(), iEnd = mColVector.end(); + i != iEnd; ++i) + { + i->committedGrad = i->grad; + if (commitNonStatic || i->grad == STATIC) + { + i->committedColor = i->color; + } + else if (i->grad == PULSE) + { + i->committedColor = i->testColor; + } + } +} + +void Palette::rollback() +{ + for (ColVector::iterator i = mColVector.begin(), iEnd = mColVector.end(); + i != iEnd; + ++i) + { + if (i->grad != i->committedGrad) + { + setGradient(i->type, i->committedGrad); + } + setColor(i->type, i->committedColor.r, i->committedColor.g, + i->committedColor.b); + if (i->grad == PULSE) + { + i->testColor.r = i->committedColor.r; + i->testColor.g = i->committedColor.g; + i->testColor.b = i->committedColor.b; + } + } +} + +void Palette::addColor(Palette::ColorType type, int rgb, + Palette::GradientType grad, + const std::string &text, char c) +{ + const std::string *configName = &ColorTypeNames[type]; + gcn::Color trueCol = (int)config.getValue(*configName, rgb); + grad = (GradientType)config.getValue(*configName + "Gradient", grad); + mColVector[type].set(type, trueCol, grad, text, c); + if (grad != STATIC) + { + mGradVector.push_back(&mColVector[type]); + } +} + +void Palette::advanceGradient () +{ + if (get_elapsed_time(mRainbowTime) > 5) + { + int pos, colIndex, colVal; + // For slower systems, advance can be greater than one (advance > 1 + // skips advance-1 steps). Should make gradient look the same + // independent of the framerate. + int advance = get_elapsed_time(mRainbowTime) / 5; + double startColVal, destColVal; + + for (size_t i = 0; i < mGradVector.size(); i++) + { + mGradVector[i]->gradientIndex = + (mGradVector[i]->gradientIndex + advance) % + (GRADIENT_DELAY * ((mGradVector[i]->grad == SPECTRUM) ? + (mGradVector[i]->grad == PULSE) ? 255 : 6 : + RAINBOW_COLOR_COUNT)); + + pos = mGradVector[i]->gradientIndex % GRADIENT_DELAY; + colIndex = mGradVector[i]->gradientIndex / GRADIENT_DELAY; + + if (mGradVector[i]->grad == PULSE) + { + colVal = (int) (255.0 * (sin(M_PI * + (mGradVector[i]->gradientIndex) / 255) + 1) / 2); + + const gcn::Color* col = &mGradVector[i]->testColor; + + mGradVector[i]->color.r = ((colVal * col->r) / 255) % (col->r + 1); + mGradVector[i]->color.g = ((colVal * col->g) / 255) % (col->g + 1); + mGradVector[i]->color.b = ((colVal * col->b) / 255) % (col->b + 1); + } + if (mGradVector[i]->grad == SPECTRUM) + { + if (colIndex % 2) + { // falling curve + colVal = (int)(255.0 * (cos(M_PI * pos / GRADIENT_DELAY) + + 1) / 2); + } + else + { // ascending curve + colVal = (int)(255.0 * (cos(M_PI * (GRADIENT_DELAY-pos) / + GRADIENT_DELAY) + 1) / 2); + } + + mGradVector[i]->color.r = + (colIndex == 0 || colIndex == 5) ? 255 : + (colIndex == 1 || colIndex == 4) ? colVal : 0; + mGradVector[i]->color.g = + (colIndex == 1 || colIndex == 2) ? 255 : + (colIndex == 0 || colIndex == 3) ? colVal : 0; + mGradVector[i]->color.b = + (colIndex == 3 || colIndex == 4) ? 255 : + (colIndex == 2 || colIndex == 5) ? colVal : 0; + } + else if (mGradVector[i]->grad == RAINBOW) + { + const gcn::Color* startCol = &RAINBOW_COLORS[colIndex]; + const gcn::Color* destCol = + &RAINBOW_COLORS[(colIndex + 1) % RAINBOW_COLOR_COUNT]; + + startColVal = (cos(M_PI * pos / GRADIENT_DELAY) + 1) / 2; + destColVal = 1 - startColVal; + + mGradVector[i]->color.r =(int)(startColVal * startCol->r + + destColVal * destCol->r); + + mGradVector[i]->color.g =(int)(startColVal * startCol->g + + destColVal * destCol->g); + + mGradVector[i]->color.b =(int)(startColVal * startCol->b + + destColVal * destCol->b); + } + } + + mRainbowTime = tick_time; + } +} diff --git a/src/gui/palette.h b/src/gui/palette.h new file mode 100644 index 00000000..1a466ed4 --- /dev/null +++ b/src/gui/palette.h @@ -0,0 +1,350 @@ +/* + * Configurable text colors + * Copyright (C) 2008 Douglas Boffey <dougaboffey@netscape.net> + * 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 PALETTE_H +#define PALETTE_H + +#include <cstdlib> +#include <string> +#include <vector> + +#include <guichan/listmodel.hpp> +#include <guichan/color.hpp> + +// Generate strings from an enum ... some preprocessor fun. +#define EDEF(a) a, +#define ECONFIGSTR(a) Palette::getConfigName(#a), + +#define TEXTENUM(name,def)\ + enum name { def(EDEF) };\ + static const std::string name ## Names[]; +#define DEFENUMNAMES(name,def)\ + const std::string Palette::name ## Names[] = { def(ECONFIGSTR) "" }; + +/** + * Class controlling the game's color palette. + */ +class Palette : public gcn::ListModel +{ + public: + /** List of all colors that are configurable. */ + #define COLOR_TYPE(ENTRY)\ + ENTRY(TEXT)\ + ENTRY(SHADOW)\ + ENTRY(OUTLINE)\ + ENTRY(PROGRESS_BAR)\ + ENTRY(BACKGROUND)\ + ENTRY(HIGHLIGHT)\ + ENTRY(TAB_HIGHLIGHT)\ + ENTRY(SHOP_WARNING)\ + ENTRY(CHAT)\ + ENTRY(GM)\ + ENTRY(PLAYER)\ + ENTRY(WHISPER)\ + ENTRY(IS)\ + ENTRY(PARTY)\ + ENTRY(SERVER)\ + ENTRY(LOGGER)\ + ENTRY(HYPERLINK)\ + ENTRY(BEING)\ + ENTRY(PC)\ + ENTRY(SELF)\ + ENTRY(GM_NAME)\ + ENTRY(NPC)\ + ENTRY(MONSTER)\ + ENTRY(UNKNOWN_ITEM)\ + ENTRY(GENERIC)\ + ENTRY(HEAD)\ + ENTRY(USABLE)\ + ENTRY(TORSO)\ + ENTRY(ONEHAND)\ + ENTRY(LEGS)\ + ENTRY(FEET)\ + ENTRY(TWOHAND)\ + ENTRY(SHIELD)\ + ENTRY(RING)\ + ENTRY(ARMS)\ + ENTRY(AMMO)\ + ENTRY(PARTICLE)\ + ENTRY(EXP_INFO)\ + ENTRY(PICKUP_INFO)\ + ENTRY(HIT_PLAYER_MONSTER)\ + ENTRY(HIT_MONSTER_PLAYER)\ + ENTRY(HIT_CRITICAL)\ + ENTRY(MISS)\ + ENTRY(TYPE_COUNT)\ + + TEXTENUM(ColorType, COLOR_TYPE) + + /** Colors can be static or can alter over time. */ + enum GradientType { + STATIC, + PULSE, + SPECTRUM, + RAINBOW + }; + + /** + * Constructor + */ + Palette(); + + /** + * Destructor + */ + ~Palette(); + + /** + * Returns the color associated with a character, if it exists. Returns + * Palette::BLACK if the character is not found. + * + * @param c character requested + * @param valid indicate whether character is known + * + * @return the requested color or Palette::BLACK + */ + const gcn::Color& getColor(char c, bool &valid); + + /** + * Gets the color associated with the type. Sets the alpha channel + * before returning. + * + * @param type the color type requested + * @param alpha alpha channel to use + * + * @return the requested color + */ + inline const gcn::Color& getColor(ColorType type, int alpha = 255) + { + gcn::Color* col = &mColVector[type].color; + col->a = alpha; + return *col; + } + + /** + * Gets the committed color associated with the specified type. + * + * @param type the color type requested + * + * @return the requested committed color + */ + inline const gcn::Color& getCommittedColor(ColorType type) + { + return mColVector[type].committedColor; + } + + /** + * Gets the test color associated with the specified type. + * + * @param type the color type requested + * + * @return the requested test color + */ + inline const gcn::Color& getTestColor(ColorType type) + { + return mColVector[type].testColor; + } + + /** + * Sets the test color associated with the specified type. + * + * @param type the color type requested + * @param color the color that should be tested + */ + inline void setTestColor(ColorType type, gcn::Color color) + { + mColVector[type].testColor = color; + } + + /** + * Gets the GradientType associated with the specified type. + * + * @param type the color type of the color + * + * @return the gradient type of the color with the given index + */ + inline GradientType getGradientType(ColorType type) + { + return mColVector[type].grad; + } + + /** + * Get the character used by the specified color. + * + * @param type the color type of the color + * + * @return the color char of the color with the given index + */ + inline char getColorChar(ColorType type) + { + return mColVector[type].ch; + } + + /** + * Sets the color for the specified type. + * + * @param type color to be set + * @param r red component + * @param g green component + * @param b blue component + */ + void setColor(ColorType type, int r, int g, int b); + + /** + * Sets the gradient type for the specified color. + * + * @param grad gradient type to set + */ + void setGradient(ColorType type, GradientType grad); + + /** + * Returns the number of colors known. + * + * @return the number of colors known + */ + inline int getNumberOfElements() { return mColVector.size(); } + + /** + * Returns the name of the ith color. + * + * @param i index of color interested in + * + * @return the name of the color + */ + std::string getElementAt(int i); + + /** + * Gets the ColorType used by the color for the element at index i in + * the current color model. + * + * @param i the index of the color + * + * @return the color type of the color with the given index + */ + ColorType getColorTypeAt(int i); + + /** + * Commit the colors + */ + inline void commit() + { + commit(false); + } + + /** + * Rollback the colors + */ + void rollback(); + + /** + * Updates all colors, that are non-static. + */ + void advanceGradient(); + + private: + /** Black Color Constant */ + static const gcn::Color BLACK; + + /** Colors used for the rainbow gradient */ + static const gcn::Color RAINBOW_COLORS[]; + static const int RAINBOW_COLOR_COUNT; + /** Parameter to control the speed of the gradient */ + static const int GRADIENT_DELAY; + /** Time tick, that gradient-type colors were updated the last time. */ + int mRainbowTime; + + /** + * Define a color replacement. + * + * @param i the index of the color to replace + * @param r red component + * @param g green component + * @param b blue component + */ + void setColorAt(int i, int r, int g, int b); + + /** + * Commit the colors. Commit the non-static color values, if + * commitNonStatic is true. Only needed in the constructor. + */ + void commit(bool commitNonStatic); + + struct ColorElem + { + ColorType type; + gcn::Color color; + gcn::Color testColor; + gcn::Color committedColor; + std::string text; + char ch; + GradientType grad; + GradientType committedGrad; + int gradientIndex; + + void set(ColorType type, gcn::Color& color, GradientType grad, + const std::string &text, char c) + { + ColorElem::type = type; + ColorElem::color = color; + ColorElem::text = text; + ColorElem::ch = c; + ColorElem::grad = grad; + ColorElem::gradientIndex = rand(); + } + + inline int getRGB() + { + return (committedColor.r << 16) | (committedColor.g << 8) | + committedColor.b; + } + }; + typedef std::vector<ColorElem> ColVector; + /** Vector containing the colors. */ + ColVector mColVector; + std::vector<ColorElem*> mGradVector; + + /** + * Initialise color + * + * @param c character that needs initialising + * @param rgb default color if not found in config + * @param text identifier of color + */ + void addColor(ColorType type, int rgb, GradientType grad, + const std::string &text, char c = 0); + + /** + * Prefixes the given string with "Color", lowercases all letters but + * the first and all following a '_'. All '_'s will be removed. + * + * E.g.: HIT_PLAYER_MONSTER -> HitPlayerMonster + * + * @param typeName string to transform + * + * @return the transformed string + */ + static std::string getConfigName(const std::string& typeName); +}; + +extern Palette *guiPalette; + +#endif diff --git a/src/gui/playerbox.cpp b/src/gui/playerbox.cpp index 2bfa798c..b7e553dc 100644 --- a/src/gui/playerbox.cpp +++ b/src/gui/playerbox.cpp @@ -78,6 +78,9 @@ PlayerBox::~PlayerBox() void PlayerBox::draw(gcn::Graphics *graphics) { + if (!isVisible()) + return; + if (mPlayer) { // Draw character diff --git a/src/gui/popup.cpp b/src/gui/popup.cpp new file mode 100644 index 00000000..17d299a5 --- /dev/null +++ b/src/gui/popup.cpp @@ -0,0 +1,209 @@ +/* + * Aethyra + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of Aethyra based on original code + * from 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 <algorithm> +#include <cassert> +#include <climits> + +#include <guichan/exception.hpp> + +#include "gui.h" +#include "skin.h" +#include "popup.h" +#include "window.h" +#include "windowcontainer.h" + +#include "../configlistener.h" +#include "../configuration.h" +#include "../log.h" + +#include "../resources/image.h" + +ConfigListener *Popup::popupConfigListener = 0; +int Popup::instances = 0; +bool Popup::mAlphaChanged = false; + +class PopupConfigListener : public ConfigListener +{ + void optionChanged(const std::string &) + { + Popup::mAlphaChanged = true; + } +}; + +Popup::Popup(const std::string& name, Window *parent, + const std::string& skin): + mParent(parent), + mPopupName(name), + mMinWidth(100), + mMinHeight(40), + mMaxWidth(INT_MAX), + mMaxHeight(INT_MAX) +{ + logger->log("Popup::Popup(\"%s\")", name.c_str()); + + if (!windowContainer) + throw GCN_EXCEPTION("Popup::Popup(): no windowContainer set"); + + if (instances == 0) + { + popupConfigListener = new PopupConfigListener(); + // Send GUI alpha changed for initialization + popupConfigListener->optionChanged("guialpha"); + config.addListener("guialpha", popupConfigListener); + } + + setPadding(3); + + instances++; + + // Loads the skin + mSkin = skinLoader->load(skin); + + setGuiAlpha(); + + // Add this window to the window container + windowContainer->add(this); + + // Popups are invisible by default + setVisible(false); +} + +Popup::~Popup() +{ + logger->log("Popup::~Popup(\"%s\")", mPopupName.c_str()); + + while (!mWidgets.empty()) + { + gcn::Widget *w = mWidgets.front(); + remove(w); + delete(w); + } + + instances--; + + mSkin->instances--; + + if (instances == 0) + { + config.removeListener("guialpha", popupConfigListener); + delete popupConfigListener; + popupConfigListener = NULL; + } +} + +void Popup::setWindowContainer(WindowContainer *wc) +{ + windowContainer = wc; +} + +void Popup::draw(gcn::Graphics *graphics) +{ + if (!isVisible()) + return; + + Graphics *g = static_cast<Graphics*>(graphics); + + g->drawImageRect(0, 0, getWidth(), getHeight(), mSkin->getBorder()); + + // Update Popup alpha values + if (mAlphaChanged) + { + for_each(mSkin->getBorder().grid, mSkin->getBorder().grid + 9, + std::bind2nd(std::mem_fun(&Image::setAlpha), + config.getValue("guialpha", 0.8))); + } + drawChildren(graphics); +} + +gcn::Rectangle Popup::getChildrenArea() +{ + return gcn::Rectangle(getPadding(), 0, getWidth() - getPadding() * 2, + getHeight() - getPadding() * 2); +} + +void Popup::setContentSize(int width, int height) +{ + width += 2 * getPadding(); + height += 2 * getPadding(); + + if (getMinWidth() > width) + width = getMinWidth(); + else if (getMaxWidth() < width) + width = getMaxWidth(); + if (getMinHeight() > height) + height = getMinHeight(); + else if (getMaxHeight() < height) + height = getMaxHeight(); + + setSize(width, height); +} + +void Popup::setLocationRelativeTo(gcn::Widget *widget) +{ + int wx, wy; + int x, y; + + widget->getAbsolutePosition(wx, wy); + getAbsolutePosition(x, y); + + setPosition(getX() + (wx + (widget->getWidth() - getWidth()) / 2 - x), + getY() + (wy + (widget->getHeight() - getHeight()) / 2 - y)); +} + +void Popup::setMinWidth(unsigned int width) +{ + mMinWidth = width > mSkin->getMinWidth() ? width : mSkin->getMinWidth(); +} + +void Popup::setMinHeight(unsigned int height) +{ + mMinHeight = height > mSkin->getMinHeight() ? height : mSkin->getMinHeight(); +} + +void Popup::setMaxWidth(unsigned int width) +{ + mMaxWidth = width; +} + +void Popup::setMaxHeight(unsigned int height) +{ + mMaxHeight = height; +} + +void Popup::scheduleDelete() +{ + windowContainer->scheduleDelete(this); +} + +void Popup::setGuiAlpha() +{ + //logger->log("Popup::setGuiAlpha: Alpha Value %f", config.getValue("guialpha", 0.8)); + for (int i = 0; i < 9; i++) + { + //logger->log("Popup::setGuiAlpha: Border Image (%i)", i); + mSkin->getBorder().grid[i]->setAlpha(config.getValue("guialpha", 0.8)); + } + + mAlphaChanged = false; +} + diff --git a/src/gui/popup.h b/src/gui/popup.h new file mode 100644 index 00000000..bfe8d7e9 --- /dev/null +++ b/src/gui/popup.h @@ -0,0 +1,194 @@ +/* + * Aethyra + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of Aethyra based on original code + * from 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 POPUP_H +#define POPUP_H + +#include <guichan/widgets/container.hpp> + +#include "../graphics.h" +#include "../guichanfwd.h" + +class ConfigListener; +class Skin; +class SkinLoader; +class Window; +class WindowContainer; + +/** + * A rather reduced down version of the Window class that is particularly suited + * for + * + * \ingroup GUI + */ +class Popup : public gcn::Container +{ + public: + friend class PopupConfigListener; + + /** + * Constructor. Initializes the title to the given text and hooks + * itself into the popup container. + * + * @param name A human readable name for the popup. Only useful for + * debugging purposes. + * @param parent The parent Window. This is the Window standing above + * this one in the Window hiearchy. When reordering, + * a Popup will never go below its parent Window. + * @param skin The location where the Popup's skin XML can be found. + */ + Popup(const std::string& name = "", Window *parent = NULL, + const std::string &skin = "graphics/gui/gui.xml"); + + /** + * Destructor. Deletes all the added widgets. + */ + ~Popup(); + + /** + * Sets the window container to be used by new popups. + */ + static void setWindowContainer(WindowContainer *windowContainer); + + /** + * Draws the popup. + */ + void draw(gcn::Graphics *graphics); + + /** + * Sets the size of this popup. + */ + void setContentSize(int width, int height); + + /** + * Sets the location relative to the given widget. + */ + void setLocationRelativeTo(gcn::Widget *widget); + + /** + * Sets the minimum width of the popup. + */ + void setMinWidth(unsigned int width); + + /** + * Sets the minimum height of the popup. + */ + void setMinHeight(unsigned int height); + + /** + * Sets the maximum width of the popup. + */ + void setMaxWidth(unsigned int width); + + /** + * Sets the minimum height of the popup. + */ + void setMaxHeight(unsigned int height); + + /** + * Gets the minimum width of the popup. + */ + int getMinWidth() { return mMinWidth; } + + /** + * Gets the minimum height of the popup. + */ + int getMinHeight() { return mMinHeight; } + + /** + * Gets the maximum width of the popup. + */ + int getMaxWidth() { return mMaxWidth; } + + /** + * Gets the minimum height of the popup. + */ + int getMaxHeight() { return mMaxHeight; } + + /** + * Gets the padding of the popup. The padding is the distance between + * the popup border and the content. + * + * @return The padding of the popup. + * @see setPadding + */ + unsigned int getPadding() const { return mPadding; } + + /** + * Sets the padding of the popup. The padding is the distance between the + * popup border and the content. + * + * @param padding The padding of the popup. + * @see getPadding + */ + void setPadding(unsigned int padding) { mPadding = padding; } + + /** + * Returns the parent Window. + * + * @return The parent Window or <code>NULL</code> if there is none. + */ + Window* getParentWindow() { return mParent; } + + /** + * Sets the name of the popup. This is only useful for debug purposes. + */ + void setPopupName(const std::string &name) { mPopupName = name; } + + /** + * Returns the name of the popup. This is only useful for debug purposes. + */ + const std::string& getPopupName() { return mPopupName; } + + /** + * Schedule this popup for deletion. It will be deleted at the start + * of the next logic update. + */ + void scheduleDelete(); + + // Inherited from BasicContainer + + virtual gcn::Rectangle getChildrenArea(); + + private: + void setGuiAlpha(); + + Window *mParent; /**< The parent Window (if there is one) */ + std::string mPopupName; /**< Name of the Popup */ + static bool mAlphaChanged; /**< Whether the alpha percent was changed */ + int mMinWidth; /**< Minimum Popup width */ + int mMinHeight; /**< Minimum Popup height */ + int mMaxWidth; /**< Maximum Popup width */ + int mMaxHeight; /**< Maximum Popup height */ + unsigned int mPadding; /**< Holds the padding of the window. */ + + /** + * The config listener that listens to changes relevant to all Popups. + */ + static ConfigListener *popupConfigListener; + + static int instances; /**< Number of Popup instances */ + + Skin* mSkin; /**< Skin in use by this Popup */ +}; + +#endif diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index ced44f42..9ff9b23f 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -26,11 +26,11 @@ #include "inventorywindow.h" #include "item_amount.h" #include "popupmenu.h" -#include "windowcontainer.h" #include "../being.h" #include "../beingmanager.h" #include "../floor_item.h" +#include "../graphics.h" #include "../item.h" #include "../localplayer.h" #include "../npc.h" @@ -75,7 +75,7 @@ void PopupMenu::showPopup(int x, int y, Being *being) { case Being::PLAYER: { - // Players can be traded with. Later also attack, follow and + // Players can be traded with. Later also follow and // add as buddy will be options in this menu. const std::string &name = being->getName(); mBrowserBox->addRow(strprintf(_("@@trade|Trade With %s@@"), name.c_str())); @@ -345,10 +345,10 @@ void PopupMenu::showPopup(int x, int y, Item *item) void PopupMenu::showPopup(int x, int y) { setContentSize(mBrowserBox->getWidth() + 8, mBrowserBox->getHeight() + 8); - if (windowContainer->getWidth() < (x + getWidth() + 5)) - x = windowContainer->getWidth() - getWidth(); - if (windowContainer->getHeight() < (y + getHeight() + 5)) - y = windowContainer->getHeight() - getHeight(); + if (graphics->getWidth() < (x + getWidth() + 5)) + x = graphics->getWidth() - getWidth(); + if (graphics->getHeight() < (y + getHeight() + 5)) + y = graphics->getHeight() - getHeight(); setPosition(x, y); setVisible(true); requestMoveToTop(); diff --git a/src/gui/popupmenu.h b/src/gui/popupmenu.h index 2694abd8..0a6877d9 100644 --- a/src/gui/popupmenu.h +++ b/src/gui/popupmenu.h @@ -22,8 +22,6 @@ #ifndef POPUP_MENU_H #define POPUP_MENU_H -#include <SDL.h> // for Uint32 - #include "linkhandler.h" #include "window.h" @@ -68,7 +66,7 @@ class PopupMenu : public Window, public LinkHandler private: BrowserBox* mBrowserBox; - Uint32 mBeingId; + int mBeingId; FloorItem* mFloorItem; Item *mItem; diff --git a/src/gui/progressbar.cpp b/src/gui/progressbar.cpp index 85f21604..02ddab16 100644 --- a/src/gui/progressbar.cpp +++ b/src/gui/progressbar.cpp @@ -22,7 +22,9 @@ #include <guichan/font.hpp> #include "gui.h" +#include "palette.h" #include "progressbar.h" +#include "textrenderer.h" #include "../configuration.h" #include "../graphics.h" @@ -139,32 +141,22 @@ void ProgressBar::draw(gcn::Graphics *graphics) // The bar if (mProgress > 0) { - graphics->setColor(gcn::Color(mRed, mGreen, mBlue, alpha)); graphics->fillRectangle(gcn::Rectangle(4, 4, - (int) (mProgress * (getWidth() - 8)), - getHeight() - 8)); + (int) (mProgress * (getWidth() - 8)), + getHeight() - 8)); } // The label if (!mText.empty()) { - gcn::Font *f = boldFont; const int textX = getWidth() / 2; - const int textY = (getHeight() - f->getHeight()) / 2; - - graphics->setFont(f); - - graphics->setColor(gcn::Color(0, 0, 0, alpha)); - graphics->drawText(mText, textX + 1, textY, gcn::Graphics::CENTER); - graphics->drawText(mText, textX, textY - 1, gcn::Graphics::CENTER); - graphics->drawText(mText, textX, textY + 1, gcn::Graphics::CENTER); - graphics->drawText(mText, textX - 1, textY, gcn::Graphics::CENTER); - - graphics->setColor(gcn::Color(255, 255, 255, alpha)); - graphics->drawText(mText, textX, textY, gcn::Graphics::CENTER); + const int textY = (getHeight() - boldFont->getHeight()) / 2; - graphics->setColor(gcn::Color(0, 0, 0)); + TextRenderer::renderText(graphics, mText, textX, textY, + gcn::Graphics::CENTER, + guiPalette->getColor(Palette::PROGRESS_BAR, + alpha), boldFont, true, false); } } diff --git a/src/gui/progressbar.h b/src/gui/progressbar.h index 49bc3edd..e75b1d44 100644 --- a/src/gui/progressbar.h +++ b/src/gui/progressbar.h @@ -124,10 +124,13 @@ class ProgressBar : public gcn::Widget bool mSmoothColorChange; std::string mText; + bool mUpdated; static ImageRect mBorder; static int mInstances; static float mAlpha; + + static const gcn::Color TEXT_COLOR; }; #endif diff --git a/src/gui/radiobutton.cpp b/src/gui/radiobutton.cpp index c8ae2fad..52ceb837 100644 --- a/src/gui/radiobutton.cpp +++ b/src/gui/radiobutton.cpp @@ -69,6 +69,9 @@ RadioButton::~RadioButton() void RadioButton::drawBox(gcn::Graphics* graphics) { + if (!isVisible()) + return; + if (config.getValue("guialpha", 0.8) != mAlpha) { mAlpha = config.getValue("guialpha", 0.8); diff --git a/src/gui/recorder.cpp b/src/gui/recorder.cpp index a94af4cc..0536188c 100644 --- a/src/gui/recorder.cpp +++ b/src/gui/recorder.cpp @@ -40,9 +40,11 @@ Recorder::Recorder(ChatWindow *chat, const std::string &title, mChat = chat; Button *button = new Button(buttonTxt, "activate", this); - setDefaultSize(0, windowContainer->getHeight() - 123 - button->getHeight() - - offsetY, button->getWidth() + offsetX, button->getHeight() + - offsetY); + + // 123 is the default chat window height. If you change this in Chat, please + // change it here as well + setDefaultSize(button->getWidth() + offsetX, button->getHeight() + + offsetY, ImageRect::LOWER_LEFT, 0, 123); place(0, 0, button); diff --git a/src/gui/register.cpp b/src/gui/register.cpp index 216ac211..5fb8b579 100644 --- a/src/gui/register.cpp +++ b/src/gui/register.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/widgets/label.hpp> - #include "../configuration.h" #include "../log.h" #include "../logindata.h" @@ -28,6 +26,7 @@ #include "button.h" #include "checkbox.h" +#include "label.h" #include "login.h" #include "ok_dialog.h" #include "passwordfield.h" @@ -49,23 +48,20 @@ void WrongDataNoticeListener::setTarget(gcn::TextField *textField) void WrongDataNoticeListener::action(const gcn::ActionEvent &event) { if (event.getId() == "ok") - { mTarget->requestFocus(); - } } - RegisterDialog::RegisterDialog(LoginData *loginData): Window(_("Register")), mWrongDataNoticeListener(new WrongDataNoticeListener), mLoginData(loginData) { - gcn::Label *userLabel = new gcn::Label(_("Name:")); - gcn::Label *passwordLabel = new gcn::Label(_("Password:")); - gcn::Label *confirmLabel = new gcn::Label(_("Confirm:")); + gcn::Label *userLabel = new Label(_("Name:")); + gcn::Label *passwordLabel = new Label(_("Password:")); + gcn::Label *confirmLabel = new Label(_("Confirm:")); #ifdef EATHENA_SUPPORT - gcn::Label *serverLabel = new gcn::Label(_("Server:")); - gcn::Label *portLabel = new gcn::Label(_("Port:")); + gcn::Label *serverLabel = new Label(_("Server:")); + gcn::Label *portLabel = new Label(_("Port:")); #endif mUserField = new TextField(loginData->username); mPasswordField = new PasswordField(loginData->password); @@ -131,7 +127,7 @@ RegisterDialog::RegisterDialog(LoginData *loginData): mPortField->addActionListener(this); #endif - setLocationRelativeTo(getParent()); + center(); setVisible(true); mUserField->requestFocus(); mUserField->setCaretPosition(mUserField->getText().length()); diff --git a/src/gui/register.h b/src/gui/register.h index fde82a40..c37305e4 100644 --- a/src/gui/register.h +++ b/src/gui/register.h @@ -37,7 +37,8 @@ class OkDialog; * to the field which contained wrong data when the Ok button was pressed on * the error notice. */ -class WrongDataNoticeListener : public gcn::ActionListener { +class WrongDataNoticeListener : public gcn::ActionListener +{ public: void setTarget(gcn::TextField *textField); void action(const gcn::ActionEvent &event); diff --git a/src/gui/scrollarea.cpp b/src/gui/scrollarea.cpp index 70504a03..44ef929b 100644 --- a/src/gui/scrollarea.cpp +++ b/src/gui/scrollarea.cpp @@ -145,6 +145,9 @@ void ScrollArea::init() void ScrollArea::logic() { + if (!isVisible()) + return; + gcn::ScrollArea::logic(); gcn::Widget *content = getContent(); @@ -167,6 +170,9 @@ void ScrollArea::logic() void ScrollArea::draw(gcn::Graphics *graphics) { + if (!isVisible()) + return; + if (mVBarVisible) { drawUpButton(graphics); diff --git a/src/gui/sell.cpp b/src/gui/sell.cpp index cab88560..22f56195 100644 --- a/src/gui/sell.cpp +++ b/src/gui/sell.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/widgets/label.hpp> - #include "button.h" +#include "label.h" #include "scrollarea.h" #include "sell.h" #include "shop.h" @@ -55,29 +54,36 @@ SellDialog::SellDialog(Network *network): { setWindowName("Sell"); setResizable(true); + setCloseButton(true); setMinWidth(260); setMinHeight(230); - setDefaultSize(0, 0, 260, 230); + setDefaultSize(260, 230, ImageRect::CENTER); - mShopItems = new ShopItems; + // Create a ShopItems instance, that is aware of duplicate entries. + mShopItems = new ShopItems(true); mShopItemList = new ShopListBox(mShopItems, mShopItems); mScrollArea = new ScrollArea(mShopItemList); + mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mSlider = new Slider(1.0); - mQuantityLabel = new gcn::Label("0"); - mMoneyLabel = new gcn::Label(strprintf(_("Price: %s / Total: %s"), - "", "")); + + mQuantityLabel = new Label(strprintf("%d / %d", mAmountItems, mMaxItems)); + mQuantityLabel->setAlignment(gcn::Graphics::CENTER); + mMoneyLabel = new Label(strprintf(_("Price: %s / Total: %s"), + "", "")); + mIncreaseButton = new Button("+", "+", this); mDecreaseButton = new Button("-", "-", this); mSellButton = new Button(_("Sell"), "sell", this); mQuitButton = new Button(_("Quit"), "quit", this); - mItemDescLabel = new gcn::Label(strprintf(_("Description: %s"), "")); - mItemEffectLabel = new gcn::Label(strprintf(_("Effect: %s"), "")); + mAddMaxButton = new Button(_("Max"), "max", this); + mItemDescLabel = new Label(strprintf(_("Description: %s"), "")); + mItemEffectLabel = new Label(strprintf(_("Effect: %s"), "")); - mIncreaseButton->setSize(20, 20); - mDecreaseButton->setSize(20, 20); + mDecreaseButton->adjustSize(); + mDecreaseButton->setWidth(mIncreaseButton->getWidth()); - mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); mIncreaseButton->setEnabled(false); mDecreaseButton->setEnabled(false); mSellButton->setEnabled(false); @@ -88,21 +94,26 @@ SellDialog::SellDialog(Network *network): mSlider->setActionEventId("slider"); mSlider->addActionListener(this); - place(0, 0, mScrollArea, 5).setPadding(3); - place(0, 1, mQuantityLabel, 2); - place(2, 1, mSlider, 3); - place(0, 2, mMoneyLabel, 5); - place(0, 3, mItemEffectLabel, 5); - place(0, 4, mItemDescLabel, 5); + ContainerPlacer place; + place = getPlacer(0, 0); + + place(0, 0, mScrollArea, 8, 5).setPadding(3); place(0, 5, mDecreaseButton); - place(1, 5, mIncreaseButton); - place(3, 5, mSellButton); - place(4, 5, mQuitButton); + place(1, 5, mSlider, 3); + place(4, 5, mIncreaseButton); + place(5, 5, mQuantityLabel, 2); + place(7, 5, mAddMaxButton); + place(0, 6, mMoneyLabel, 8); + place(0, 7, mItemEffectLabel, 8); + place(0, 8, mItemDescLabel, 8); + place(6, 9, mSellButton); + place(7, 9, mQuitButton); + Layout &layout = getLayout(); layout.setRowHeight(0, Layout::AUTO_SET); + center(); loadWindowState(); - setLocationRelativeTo(getParent()); } SellDialog::~SellDialog() @@ -136,9 +147,8 @@ void SellDialog::addItem(const Item *item, int price) if (!item) return; - mShopItems->addItem( - item->getInvIndex(), item->getId(), - item->getQuantity(), price); + mShopItems->addItem(item->getInvIndex(), item->getId(), + item->getQuantity(), price); mShopItemList->adjustSize(); } @@ -147,15 +157,14 @@ void SellDialog::addItem(const Item *item, int price) void SellDialog::action(const gcn::ActionEvent &event) { - int selectedItem = mShopItemList->getSelected(); - if (event.getId() == "quit") { - setVisible(false); - if (current_npc) current_npc->handleDeath(); + close(); return; } + int selectedItem = mShopItemList->getSelected(); + // The following actions require a valid item selection if (selectedItem == -1 || selectedItem >= (int) mShopItems->getNumberOfElements()) @@ -180,6 +189,12 @@ void SellDialog::action(const gcn::ActionEvent &event) mSlider->setValue(mAmountItems); updateButtonsAndLabels(); } + else if (event.getId() == "max") + { + mAmountItems = mMaxItems; + mSlider->setValue(mAmountItems); + updateButtonsAndLabels(); + } else if (event.getId() == "sell" && mAmountItems > 0 && mAmountItems <= mMaxItems) { @@ -189,24 +204,35 @@ void SellDialog::action(const gcn::ActionEvent &event) #else // Attempt sell MessageOut outMsg(mNetwork); - outMsg.writeInt16(CMSG_NPC_SELL_REQUEST); - outMsg.writeInt16(8); - outMsg.writeInt16(mShopItems->at(selectedItem)->getInvIndex()); - outMsg.writeInt16(mAmountItems); -#endif + ShopItem* item = mShopItems->at(selectedItem); + int sellCount; + mPlayerMoney += + mAmountItems * mShopItems->at(selectedItem)->getPrice(); mMaxItems -= mAmountItems; - mShopItems->getShop()->at(selectedItem)->setQuantity(mMaxItems); + while (mAmountItems > 0) { + outMsg.writeInt16(CMSG_NPC_SELL_REQUEST); + outMsg.writeInt16(8); + outMsg.writeInt16(item->getCurrentInvIndex()); + // This order is important, item->getCurrentInvIndex() would return + // the inventory index of the next Duplicate otherwise. + sellCount = item->sellCurrentDuplicate(mAmountItems); + mAmountItems -= sellCount; + outMsg.writeInt16(sellCount); + } +#endif + mPlayerMoney += mAmountItems * mShopItems->at(selectedItem)->getPrice(); mAmountItems = 1; + mSlider->setValue(0); if (!mMaxItems) { // All were sold mShopItemList->setSelected(-1); - mShopItems->getShop()->erase( - mShopItems->getShop()->begin() + selectedItem); + delete mShopItems->at(selectedItem); + mShopItems->erase(selectedItem); gcn::Rectangle scroll; scroll.y = mShopItemList->getRowHeight() * (selectedItem + 1); @@ -278,5 +304,26 @@ void SellDialog::updateButtonsAndLabels() mQuantityLabel->setCaption(strprintf("%d / %d", mAmountItems, mMaxItems)); mMoneyLabel->setCaption(strprintf(_("Price: %s / Total: %s"), Units::formatCurrency(income).c_str(), - Units::formatCurrency(mPlayerMoney - income).c_str())); + Units::formatCurrency(mPlayerMoney + income).c_str())); +} + +void SellDialog::logic() +{ + Window::logic(); + + if (!current_npc) setVisible(false); +} + +void SellDialog::setVisible(bool visible) +{ + Window::setVisible(visible); + + if (visible) + requestFocus(); +} + +void SellDialog::close() +{ + setVisible(false); + current_npc = 0; } diff --git a/src/gui/sell.h b/src/gui/sell.h index f64a6fd5..b6388a1f 100644 --- a/src/gui/sell.h +++ b/src/gui/sell.h @@ -91,6 +91,20 @@ class SellDialog : public Window, gcn::ActionListener, gcn::SelectionListener */ void setMoney(int amount); + /** + * Check for current NPC + */ + void logic(); + + /** + * Sets the visibility of this window. + */ + void setVisible(bool visible); + + /** + * Closes the Buy Window, as well as resetting the current npc. + */ + void close(); private: /** * Updates the state of buttons and labels. @@ -102,6 +116,7 @@ class SellDialog : public Window, gcn::ActionListener, gcn::SelectionListener #endif gcn::Button *mSellButton; gcn::Button *mQuitButton; + gcn::Button *mAddMaxButton; gcn::Button *mIncreaseButton; gcn::Button *mDecreaseButton; ShopListBox *mShopItemList; @@ -119,4 +134,6 @@ class SellDialog : public Window, gcn::ActionListener, gcn::SelectionListener int mAmountItems; }; +extern SellDialog *sellDialog; + #endif diff --git a/src/gui/setup.cpp b/src/gui/setup.cpp index c8b7f900..72dfbce5 100644 --- a/src/gui/setup.cpp +++ b/src/gui/setup.cpp @@ -34,19 +34,27 @@ #include "../utils/gettext.h" extern Window *chatWindow; -extern Window *equipmentWindow; -extern Window *helpWindow; +extern Window *statusWindow; +extern Window *buyDialog; +extern Window *sellDialog; +extern Window *buySellDialog; extern Window *inventoryWindow; -extern Window *minimap; +extern Window *emoteWindow; +extern Window *npcTextDialog; +extern Window *npcStringDialog; extern Window *skillDialog; -extern Window *statusWindow; +extern Window *minimap; +extern Window *equipmentWindow; +extern Window *tradeWindow; +extern Window *helpWindow; +extern Window *debugWindow; extern Window *itemShortcutWindow; extern Window *emoteShortcutWindow; -extern Window *emoteWindow; -extern Window *tradeWindow; #ifdef TMWSERV_SUPPORT extern Window *magicDialog; extern Window *guildWindow; +#else +extern Window *storageWindow; #endif Setup::Setup(): @@ -68,9 +76,9 @@ Setup::Setup(): btn->setPosition(x, height - btn->getHeight() - 5); add(btn); - // Disable this button when the windows aren't created yet + // Store this button, as it needs to be enabled/disabled if (!strcmp(*curBtn, "Reset Windows")) - btn->setEnabled(statusWindow != NULL); + mResetWindows = btn; } TabbedArea *panel = new TabbedArea; @@ -104,7 +112,9 @@ Setup::Setup(): add(panel); - setLocationRelativeTo(getParent()); + center(); + + setInGame(false); } Setup::~Setup() @@ -131,20 +141,37 @@ void Setup::action(const gcn::ActionEvent &event) if (!statusWindow) return; - statusWindow->resetToDefaultSize(); - minimap->resetToDefaultSize(); chatWindow->resetToDefaultSize(); + statusWindow->resetToDefaultSize(); + buyDialog->resetToDefaultSize(); + sellDialog->resetToDefaultSize(); +#ifdef EATHENA_SUPPORT + buySellDialog->resetToDefaultSize(); +#endif inventoryWindow->resetToDefaultSize(); + emoteWindow->resetToDefaultSize(); + npcTextDialog->resetToDefaultSize(); + npcStringDialog->resetToDefaultSize(); + skillDialog->resetToDefaultSize(); + minimap->resetToDefaultSize(); equipmentWindow->resetToDefaultSize(); + tradeWindow->resetToDefaultSize(); helpWindow->resetToDefaultSize(); - skillDialog->resetToDefaultSize(); + debugWindow->resetToDefaultSize(); itemShortcutWindow->resetToDefaultSize(); emoteShortcutWindow->resetToDefaultSize(); - emoteWindow->resetToDefaultSize(); - tradeWindow->resetToDefaultSize(); #ifdef TMWSERV_SUPPORT magicDialog->resetToDefaultSize(); guildWindow->resetToDefaultSize(); +#else + storageWindow->resetToDefaultSize(); #endif } } + +void Setup::setInGame(bool inGame) +{ + mResetWindows->setEnabled(inGame); +} + +Setup* setupWindow; diff --git a/src/gui/setup.h b/src/gui/setup.h index e4eb0902..4c387d34 100644 --- a/src/gui/setup.h +++ b/src/gui/setup.h @@ -55,8 +55,16 @@ class Setup : public Window, public gcn::ActionListener */ void action(const gcn::ActionEvent &event); + /** + * Enables the reset button when in game. + */ + void setInGame(bool inGame); + private: std::list<SetupTab*> mTabs; + gcn::Button *mResetWindows; }; +extern Setup* setupWindow; + #endif diff --git a/src/gui/setup_audio.cpp b/src/gui/setup_audio.cpp index 43cc28e6..28a80b3d 100644 --- a/src/gui/setup_audio.cpp +++ b/src/gui/setup_audio.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/widgets/label.hpp> - #include "checkbox.h" +#include "label.h" #include "ok_dialog.h" #include "setup_audio.h" #include "slider.h" @@ -45,8 +44,8 @@ Setup_Audio::Setup_Audio(): setOpaque(false); setDimension(gcn::Rectangle(0, 0, 250, 200)); - gcn::Label *sfxLabel = new gcn::Label(_("Sfx volume")); - gcn::Label *musicLabel = new gcn::Label(_("Music volume")); + gcn::Label *sfxLabel = new Label(_("Sfx volume")); + gcn::Label *musicLabel = new Label(_("Music volume")); mSfxSlider->setActionEventId("sfx"); mMusicSlider->setActionEventId("music"); @@ -80,7 +79,8 @@ void Setup_Audio::apply() if (mSoundCheckBox->isSelected()) { config.setValue("sound", 1); - try { + try + { sound.init(); } catch (const char *err) diff --git a/src/gui/setup_colors.cpp b/src/gui/setup_colors.cpp index 49c99996..033ba372 100644 --- a/src/gui/setup_colors.cpp +++ b/src/gui/setup_colors.cpp @@ -23,13 +23,14 @@ #include <cmath> #include <guichan/listmodel.hpp> -#include <guichan/widgets/label.hpp> #include <guichan/widgets/slider.hpp> #include "browserbox.h" -#include "color.h" +#include "gui.h" #include "itemlinkhandler.h" +#include "label.h" #include "listbox.h" +#include "palette.h" #include "scrollarea.h" #include "setup_colors.h" #include "slider.h" @@ -42,71 +43,91 @@ #include "../utils/gettext.h" #include "../utils/stringutils.h" +const std::string Setup_Colors::rawmsg = _("This is what the color looks like"); + Setup_Colors::Setup_Colors() : mSelected(-1) { setOpaque(false); - mColorBox = new ListBox(textColor); + mColorBox = new ListBox(guiPalette); mColorBox->setActionEventId("color_box"); mColorBox->addActionListener(this); mScroll = new ScrollArea(mColorBox); mScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mTextPreview = new TextPreview(&rawmsg); + mPreview = new BrowserBox(BrowserBox::AUTO_WRAP); mPreview->setOpaque(false); - // Replace this later with a more appropriate link handler. For now, this'll - // do, as it'll do nothing when clicked on. - mPreview->setLinkHandler(new ItemLinkHandler); + // don't do anything with links + mPreview->setLinkHandler(NULL); mPreviewBox = new ScrollArea(mPreview); mPreviewBox->setHeight(20); mPreviewBox->setScrollPolicy(gcn::ScrollArea::SHOW_NEVER, gcn::ScrollArea::SHOW_NEVER); - mRedLabel = new gcn::Label(_("Red: ")); + mGradTypeLabel = new Label(_("Type: ")); + + mGradTypeSlider = new Slider(0, 3); + mGradTypeSlider->setWidth(160); + mGradTypeSlider->setActionEventId("slider_grad"); + mGradTypeSlider->setValue(0); + mGradTypeSlider->addActionListener(this); + mGradTypeSlider->setEnabled(false); + + mGradTypeText = new Label(); + + mRedLabel = new Label(_("Red: ")); mRedText = new TextField; mRedText->setWidth(40); mRedText->setRange(0, 255); mRedText->setNumeric(true); mRedText->addListener(this); + mRedText->setEnabled(false); mRedSlider = new Slider(0, 255); mRedSlider->setWidth(160); mRedSlider->setValue(mRedText->getValue()); mRedSlider->setActionEventId("slider_red"); mRedSlider->addActionListener(this); + mRedSlider->setEnabled(false); - mGreenLabel = new gcn::Label(_("Green: ")); + mGreenLabel = new Label(_("Green: ")); mGreenText = new TextField; mGreenText->setWidth(40); mGreenText->setRange(0, 255); mGreenText->setNumeric(true); mGreenText->addListener(this); + mGreenText->setEnabled(false); mGreenSlider = new Slider(0, 255); mGreenSlider->setWidth(160); mGreenSlider->setValue(mGreenText->getValue()); mGreenSlider->setActionEventId("slider_green"); mGreenSlider->addActionListener(this); + mGreenSlider->setEnabled(false); - mBlueLabel = new gcn::Label(_("Blue: ")); + mBlueLabel = new Label(_("Blue: ")); mBlueText = new TextField; mBlueText->setWidth(40); mBlueText->setRange(0, 255); mBlueText->setNumeric(true); mBlueText->addListener(this); + mBlueText->setEnabled(false); mBlueSlider = new Slider(0, 255); mBlueSlider->setWidth(160); mBlueSlider->setValue(mBlueText->getValue()); mBlueSlider->setActionEventId("slider_blue"); mBlueSlider->addActionListener(this); + mBlueSlider->setEnabled(false); setOpaque(false); @@ -114,8 +135,11 @@ Setup_Colors::Setup_Colors() : LayoutHelper h(this); ContainerPlacer place = h.getPlacer(0, 0); - place(0, 0, mScroll, 4, 7).setPadding(2); - place(0, 7, mPreviewBox, 4).setPadding(2); + place(0, 0, mScroll, 4, 6).setPadding(2); + place(0, 6, mPreviewBox, 4).setPadding(2); + place(0, 7, mGradTypeLabel, 2); + place(2, 7, mGradTypeSlider); + place(3, 7, mGradTypeText); place(0, 8, mRedLabel, 2); place(2, 8, mRedSlider); place(3, 8, mRedText).setPadding(1); @@ -131,19 +155,10 @@ Setup_Colors::Setup_Colors() : Setup_Colors::~Setup_Colors() { - delete mRedLabel; - delete mRedSlider; - delete mRedText; - - delete mGreenLabel; - delete mGreenSlider; - delete mGreenText; - - delete mBlueLabel; - delete mBlueSlider; - delete mBlueText; - - delete mScroll; + if (mPreviewBox->getContent() == mPreview) + delete mTextPreview; + else + delete mPreview; } void Setup_Colors::action(const gcn::ActionEvent &event) @@ -151,22 +166,140 @@ void Setup_Colors::action(const gcn::ActionEvent &event) if (event.getId() == "color_box") { mSelected = mColorBox->getSelected(); - int col = textColor->getColorAt(mSelected); - char ch = textColor->getColorCharAt(mSelected); - std::string msg; + Palette::ColorType type = guiPalette->getColorTypeAt(mSelected); + const gcn::Color *col = &guiPalette->getColor(type); + Palette::GradientType grad = guiPalette->getGradientType(type); - if (ch == '<') - msg = toString("@@|") + - _("This is what the color looks like") + "@@"; - else - msg = "##" + toString(ch) + - _("This is what the color looks like"); + std::string msg; + char ch = guiPalette->getColorChar(type); mPreview->clearRows(); - mPreview->addRow(msg); - setEntry(mRedSlider, mRedText, col >> 16); - setEntry(mGreenSlider, mGreenText, (col >> 8) & 0xff); - setEntry(mBlueSlider, mBlueText, col & 0xff); + mPreviewBox->setContent(mTextPreview); + mTextPreview->setFont(gui->getFont()); + mTextPreview->setTextColor(&guiPalette->getColor(Palette::TEXT)); + mTextPreview->setTextBGColor(NULL); + mTextPreview->setOpaque(false); + mTextPreview->setShadow(true); + mTextPreview->setOutline(true); + mTextPreview->useTextAlpha(false); + + switch (type) + { + case Palette::TEXT: + case Palette::SHADOW: + case Palette::OUTLINE: + mTextPreview->setFont(gui->getFont()); + mTextPreview->setShadow(type == Palette::SHADOW); + mTextPreview->setOutline(type == Palette::OUTLINE); + break; + case Palette::PROGRESS_BAR: + mTextPreview->useTextAlpha(true); + mTextPreview->setFont(boldFont); + mTextPreview->setTextColor(col); + mTextPreview->setOutline(true); + mTextPreview->setShadow(false); + break; + case Palette::TAB_HIGHLIGHT: + mTextPreview->setFont(gui->getFont()); + mTextPreview->setTextColor(col); + mTextPreview->setOutline(false); + mTextPreview->setShadow(false); + break; + case Palette::BACKGROUND: + case Palette::SHOP_WARNING: + mTextPreview->setBGColor(col); + mTextPreview->setOpaque(true); + mTextPreview->setOutline(false); + mTextPreview->setShadow(false); + break; + case Palette::HIGHLIGHT: + mTextPreview->setTextBGColor(col); + mTextPreview->setOutline(false); + mTextPreview->setShadow(false); + mPreview->addRow(rawmsg); + break; + case Palette::CHAT: + case Palette::GM: + case Palette::PLAYER: + case Palette::WHISPER: + case Palette::IS: + case Palette::PARTY: + case Palette::SERVER: + case Palette::LOGGER: + case Palette::HYPERLINK: + mPreviewBox->setContent(mPreview); + mPreview->clearRows(); + + if (ch == '<') + msg = toString("@@|") + rawmsg + "@@"; + else + msg = "##" + toString(ch) + rawmsg; + + mPreview->addRow(msg); + break; + case Palette::UNKNOWN_ITEM: + case Palette::GENERIC: + case Palette::HEAD: + case Palette::USABLE: + case Palette::TORSO: + case Palette::ONEHAND: + case Palette::LEGS: + case Palette::FEET: + case Palette::TWOHAND: + case Palette::SHIELD: + case Palette::RING: + case Palette::ARMS: + case Palette::AMMO: + mTextPreview->setFont(boldFont); + mTextPreview->setTextColor(col); + mTextPreview->setOutline(false); + mTextPreview->setShadow(false); + break; + case Palette::PARTICLE: + case Palette::EXP_INFO: + case Palette::PICKUP_INFO: + case Palette::HIT_PLAYER_MONSTER: + case Palette::HIT_MONSTER_PLAYER: + case Palette::HIT_CRITICAL: + case Palette::MISS: + mTextPreview->setShadow(false); + case Palette::BEING: + case Palette::PC: + case Palette::SELF: + case Palette::GM_NAME: + case Palette::NPC: + case Palette::MONSTER: + mTextPreview->setFont(boldFont); + mTextPreview->setTextColor(col); + case Palette::TYPE_COUNT: + break; + } + + if (grad != Palette::STATIC && grad != Palette::PULSE) + { // If nonstatic color, don't display the current, but the committed + // color at the sliders + col = &guiPalette->getCommittedColor(type); + } + else if (grad == Palette::PULSE) + { + col = &guiPalette->getTestColor(type); + } + + setEntry(mRedSlider, mRedText, col->r); + setEntry(mGreenSlider, mGreenText, col->g); + setEntry(mBlueSlider, mBlueText, col->b); + + mGradTypeSlider->setValue(grad); + updateGradType(); + mGradTypeSlider->setEnabled(true); + + return; + } + + if (event.getId() == "slider_grad") + { + updateGradType(); + updateColor(); return; } @@ -202,16 +335,18 @@ void Setup_Colors::setEntry(gcn::Slider *s, TextField *t, int value) void Setup_Colors::apply() { - textColor->commit(); + guiPalette->commit(); } void Setup_Colors::cancel() { - textColor->rollback(); - int col = textColor->getColorAt(mSelected); - setEntry(mRedSlider, mRedText, col >> 16); - setEntry(mGreenSlider, mGreenText, (col >> 8) & 0xff); - setEntry(mBlueSlider, mBlueText, col & 0xff); + guiPalette->rollback(); + Palette::ColorType type = guiPalette->getColorTypeAt(mSelected); + const gcn::Color *col = &guiPalette->getColor(type); + mGradTypeSlider->setValue(guiPalette->getGradientType(type)); + setEntry(mRedSlider, mRedText, col->r); + setEntry(mGreenSlider, mGreenText, col->g); + setEntry(mBlueSlider, mBlueText, col->b); } void Setup_Colors::listen(const TextField *tf) @@ -236,14 +371,51 @@ void Setup_Colors::listen(const TextField *tf) } } +void Setup_Colors::updateGradType() +{ + if (mSelected == -1) + return; + + mSelected = mColorBox->getSelected(); + Palette::ColorType type = guiPalette->getColorTypeAt(mSelected); + Palette::GradientType grad = guiPalette->getGradientType(type); + + mGradTypeText->setCaption( + (grad == Palette::STATIC) ? _("Static") : + (grad == Palette::PULSE) ? _("Pulse") : + (grad == Palette::RAINBOW) ? _("Rainbow") : _("Spectrum")); + + bool enable = (grad == Palette::STATIC || grad == Palette::PULSE); + mRedText->setEnabled(enable); + mRedSlider->setEnabled(enable); + mGreenText->setEnabled(enable); + mGreenSlider->setEnabled(enable); + mBlueText->setEnabled(enable); + mBlueSlider->setEnabled(enable); +} + void Setup_Colors::updateColor() { if (mSelected == -1) - { return; + + Palette::ColorType type = guiPalette->getColorTypeAt(mSelected); + Palette::GradientType grad = + static_cast<Palette::GradientType>(mGradTypeSlider->getValue()); + guiPalette->setGradient(type, grad); + + if (grad == Palette::STATIC) + { + guiPalette->setColor(type, + static_cast<int>(mRedSlider->getValue()), + static_cast<int>(mGreenSlider->getValue()), + static_cast<int>(mBlueSlider->getValue())); + } + else if (grad == Palette::PULSE) + { + guiPalette->setTestColor(type, gcn::Color( + static_cast<int>(mRedSlider->getValue()), + static_cast<int>(mGreenSlider->getValue()), + static_cast<int>(mBlueSlider->getValue()))); } - int rgb = static_cast<int>(mRedSlider->getValue()) << 16 | - static_cast<int>(mGreenSlider->getValue()) << 8 | - static_cast<int>(mBlueSlider->getValue()); - textColor->setColorAt(mSelected, rgb); } diff --git a/src/gui/setup_colors.h b/src/gui/setup_colors.h index 2831297f..52d3f727 100644 --- a/src/gui/setup_colors.h +++ b/src/gui/setup_colors.h @@ -34,6 +34,8 @@ #include "../guichanfwd.h" +#include "widgets/textpreview.h" + class BrowserBox; class Setup_Colors : public SetupTab, public gcn::ActionListener, @@ -48,12 +50,19 @@ class Setup_Colors : public SetupTab, public gcn::ActionListener, void listen(const TextField *tf); private: + static const std::string rawmsg; + gcn::ListBox *mColorBox; gcn::ScrollArea *mScroll; BrowserBox *mPreview; + TextPreview *mTextPreview; gcn::ScrollArea *mPreviewBox; int mSelected; + gcn::Label *mGradTypeLabel; + gcn::Slider *mGradTypeSlider; + gcn::Label *mGradTypeText; + gcn::Label *mRedLabel; gcn::Slider *mRedSlider; TextField *mRedText; @@ -71,5 +80,6 @@ class Setup_Colors : public SetupTab, public gcn::ActionListener, void setEntry(gcn::Slider *s, TextField *t, int value); void updateColor(); + void updateGradType(); }; #endif diff --git a/src/gui/setup_joystick.cpp b/src/gui/setup_joystick.cpp index c0c04949..59a882c7 100644 --- a/src/gui/setup_joystick.cpp +++ b/src/gui/setup_joystick.cpp @@ -19,10 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/widgets/label.hpp> - #include "button.h" #include "checkbox.h" +#include "label.h" #include "setup_joystick.h" #include "widgets/layouthelper.h" @@ -35,7 +34,7 @@ extern Joystick *joystick; Setup_Joystick::Setup_Joystick(): - mCalibrateLabel(new gcn::Label(_("Press the button to start calibration"))), + mCalibrateLabel(new Label(_("Press the button to start calibration"))), mCalibrateButton(new Button(_("Calibrate"), "calibrate", this)), mJoystickEnabled(new CheckBox(_("Enable joystick"))) { diff --git a/src/gui/setup_players.cpp b/src/gui/setup_players.cpp index 96792436..1451e71e 100644 --- a/src/gui/setup_players.cpp +++ b/src/gui/setup_players.cpp @@ -22,10 +22,9 @@ #include <string> #include <vector> -#include <guichan/widgets/label.hpp> - #include "button.h" #include "checkbox.h" +#include "label.h" #include "listbox.h" #include "ok_dialog.h" #include "scrollarea.h" @@ -137,7 +136,7 @@ public: for (unsigned int r = 0; r < player_names->size(); ++r) { std::string name = (*player_names)[r]; - gcn::Widget *widget = new gcn::Label(name); + gcn::Widget *widget = new Label(name); mWidgets.push_back(widget); gcn::ListModel *playerRelation = new PlayerRelationListModel; @@ -247,7 +246,7 @@ Setup_Players::Setup_Players(): for (int i = 0; i < COLUMNS_NR; i++) { mPlayerTableTitleModel->set(0, i, - new gcn::Label(gettext(table_titles[i]))); + new Label(gettext(table_titles[i]))); } mPlayerTitleTable->setLinewiseSelection(true); @@ -257,7 +256,7 @@ Setup_Players::Setup_Players(): mPlayerTable->setLinewiseSelection(true); mPlayerTable->addActionListener(this); - gcn::Label *ignore_action_label = new gcn::Label(_("When ignoring:")); + gcn::Label *ignore_action_label = new Label(_("When ignoring:")); mIgnoreActionChoicesBox->setActionEventId(ACTION_STRATEGY); mIgnoreActionChoicesBox->addActionListener(this); @@ -329,7 +328,7 @@ void Setup_Players::apply() ~(PlayerRelation::TRADE | PlayerRelation::WHISPER); player_relations.setDefault(old_default_relations - | (mDefaultTrading->isSelected() ? + | (mDefaultTrading->isSelected() ? PlayerRelation::TRADE : 0) | (mDefaultWhisper->isSelected() ? PlayerRelation::WHISPER : 0)); @@ -341,7 +340,7 @@ void Setup_Players::cancel() void Setup_Players::action(const gcn::ActionEvent &event) { - if (event.getId() == ACTION_TABLE) + if (event.getId() == ACTION_TABLE) { // temporarily eliminate ourselves: we are fully aware of this change, // so there is no need for asynchronous updates. (In fact, thouse diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index 526c67ce..07f073db 100644 --- a/src/gui/setup_video.cpp +++ b/src/gui/setup_video.cpp @@ -26,9 +26,8 @@ #include <guichan/key.hpp> #include <guichan/listmodel.hpp> -#include <guichan/widgets/label.hpp> - #include "checkbox.h" +#include "label.h" #include "listbox.h" #include "ok_dialog.h" #include "scrollarea.h" @@ -88,13 +87,15 @@ ModeListModel::ModeListModel() SDL_Rect **modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE); /* Check which modes are available */ - if (modes == (SDL_Rect **)0) { + if (modes == (SDL_Rect **)0) logger->log("No modes available"); - } else if (modes == (SDL_Rect **)-1) { + else if (modes == (SDL_Rect **)-1) logger->log("All resolutions available"); - } else { + else + { //logger->log("Available Modes"); - for (int i = 0; modes[i]; ++i) { + for (int i = 0; modes[i]; ++i) + { const std::string modeString = toString((int)modes[i]->w) + "x" + toString((int)modes[i]->h); //logger->log(modeString.c_str()); @@ -110,6 +111,8 @@ Setup_Video::Setup_Video(): mVisibleNamesEnabled(config.getValue("visiblenames", 1)), mParticleEffectsEnabled(config.getValue("particleeffects", true)), mNameEnabled(config.getValue("showownname", false)), + mPickupChatEnabled(config.getValue("showpickupchat", true)), + mPickupParticleEnabled(config.getValue("showpickupparticle", false)), mOpacity(config.getValue("guialpha", 0.8)), mFps((int) config.getValue("fpslimit", 0)), mSpeechMode((int) config.getValue("speech", 3)), @@ -121,11 +124,15 @@ Setup_Video::Setup_Video(): mVisibleNamesCheckBox(new CheckBox(_("Visible names"), mVisibleNamesEnabled)), mParticleEffectsCheckBox(new CheckBox(_("Particle effects"), mParticleEffectsEnabled)), mNameCheckBox(new CheckBox(_("Show name"), mNameEnabled)), + mPickupNotifyLabel(new Label(_("Show pickup notification"))), + mPickupChatCheckBox(new CheckBox(_("in chat"), mPickupChatEnabled)), + mPickupParticleCheckBox(new CheckBox(_("as particle"), + mPickupParticleEnabled)), mSpeechSlider(new Slider(0, 3)), - mSpeechLabel(new gcn::Label("")), + mSpeechLabel(new Label("")), mAlphaSlider(new Slider(0.2, 1.0)), mFpsCheckBox(new CheckBox(_("FPS Limit:"))), - mFpsSlider(new Slider(10, 200)), + mFpsSlider(new Slider(10, 120)), mFpsField(new TextField), mOriginalScrollLaziness((int) config.getValue("ScrollLaziness", 16)), mScrollLazinessSlider(new Slider(1, 64)), @@ -135,22 +142,22 @@ Setup_Video::Setup_Video(): mScrollRadiusField(new TextField), mOverlayDetail((int) config.getValue("OverlayDetail", 2)), mOverlayDetailSlider(new Slider(0, 2)), - mOverlayDetailField(new gcn::Label("")), + mOverlayDetailField(new Label("")), mParticleDetail(3 - (int) config.getValue("particleEmitterSkip", 1)), mParticleDetailSlider(new Slider(0, 3)), - mParticleDetailField(new gcn::Label("")) + mParticleDetailField(new Label("")) { setOpaque(false); ScrollArea *scrollArea = new ScrollArea(mModeList); scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); - speechLabel = new gcn::Label(_("Overhead text")); - alphaLabel = new gcn::Label(_("Gui opacity")); - scrollRadiusLabel = new gcn::Label(_("Scroll radius")); - scrollLazinessLabel = new gcn::Label(_("Scroll laziness")); - overlayDetailLabel = new gcn::Label(_("Ambient FX")); - particleDetailLabel = new gcn::Label(_("Particle Detail")); + speechLabel = new Label(_("Overhead text")); + alphaLabel = new Label(_("Gui opacity")); + scrollRadiusLabel = new Label(_("Scroll radius")); + scrollLazinessLabel = new Label(_("Scroll laziness")); + overlayDetailLabel = new Label(_("Ambient FX")); + particleDetailLabel = new Label(_("Particle Detail")); mModeList->setEnabled(true); #ifndef USE_OPENGL @@ -172,6 +179,8 @@ Setup_Video::Setup_Video(): mCustomCursorCheckBox->setActionEventId("customcursor"); mVisibleNamesCheckBox->setActionEventId("visiblenames"); mParticleEffectsCheckBox->setActionEventId("particleeffects"); + mPickupChatCheckBox->setActionEventId("pickupchat"); + mPickupParticleCheckBox->setActionEventId("pickupparticle"); mNameCheckBox->setActionEventId("showownname"); mAlphaSlider->setActionEventId("guialpha"); mFpsCheckBox->setActionEventId("fpslimitcheckbox"); @@ -190,6 +199,8 @@ Setup_Video::Setup_Video(): mCustomCursorCheckBox->addActionListener(this); mVisibleNamesCheckBox->addActionListener(this); mParticleEffectsCheckBox->addActionListener(this); + mPickupChatCheckBox->addActionListener(this); + mPickupParticleCheckBox->addActionListener(this); mNameCheckBox->addActionListener(this); mAlphaSlider->addActionListener(this); mFpsCheckBox->addActionListener(this); @@ -264,12 +275,15 @@ Setup_Video::Setup_Video(): ContainerPlacer place = h.getPlacer(0, 0); place(0, 0, scrollArea, 1, 6).setPadding(2); - place(1, 0, mFsCheckBox, 3); - place(1, 1, mOpenGLCheckBox, 3); - place(1, 2, mCustomCursorCheckBox, 3); - place(1, 3, mVisibleNamesCheckBox, 3); - place(1, 4, mNameCheckBox, 3); - place(1, 5, mParticleEffectsCheckBox, 3); + place(1, 0, mFsCheckBox, 2); + place(3, 0, mOpenGLCheckBox, 1); + place(1, 1, mCustomCursorCheckBox, 3); + place(1, 2, mVisibleNamesCheckBox, 3); + place(3, 2, mNameCheckBox, 1); + place(1, 3, mParticleEffectsCheckBox, 3); + place(1, 4, mPickupNotifyLabel, 3); + place(1, 5, mPickupChatCheckBox, 1); + place(2, 5, mPickupParticleCheckBox, 2); place(0, 6, mAlphaSlider); place(0, 7, mFpsSlider); @@ -279,7 +293,7 @@ Setup_Video::Setup_Video(): place(0, 11, mOverlayDetailSlider); place(0, 12, mParticleDetailSlider); - place(1, 6, alphaLabel, 2); + place(1, 6, alphaLabel, 3); place(1, 7, mFpsCheckBox).setPadding(3); place(1, 8, scrollRadiusLabel); place(1, 9, scrollLazinessLabel); @@ -290,9 +304,9 @@ Setup_Video::Setup_Video(): place(2, 7, mFpsField).setPadding(1); place(2, 8, mScrollRadiusField).setPadding(1); place(2, 9, mScrollLazinessField).setPadding(1); - place(2, 10, mSpeechLabel, 2).setPadding(2); - place(2, 11, mOverlayDetailField, 2).setPadding(2); - place(2, 12, mParticleDetailField, 2).setPadding(2); + place(2, 10, mSpeechLabel, 3).setPadding(2); + place(2, 11, mOverlayDetailField, 3).setPadding(2); + place(2, 12, mParticleDetailField, 3).setPadding(2); setDimension(gcn::Rectangle(0, 0, 325, 280)); } @@ -329,9 +343,11 @@ void Setup_Video::apply() } } #if defined(WIN32) || defined(__APPLE__) - } else { + } + else + { new OkDialog(_("Switching to full screen"), - _("Restart needed for changes to take effect.")); + _("Restart needed for changes to take effect.")); } #endif config.setValue("screen", fullscreen ? true : false); @@ -344,7 +360,7 @@ void Setup_Video::apply() // OpenGL can currently only be changed by restarting, notify user. new OkDialog(_("Changing OpenGL"), - _("Applying change to OpenGL requires restart.")); + _("Applying change to OpenGL requires restart.")); } // FPS change @@ -360,6 +376,8 @@ void Setup_Video::apply() mOpacity = config.getValue("guialpha", 0.8); mOverlayDetail = (int) config.getValue("OverlayDetail", 2); mOpenGLEnabled = config.getValue("opengl", false); + mPickupChatEnabled = config.getValue("showpickupchat", true); + mPickupParticleEnabled = config.getValue("showpickupparticle", false); } int Setup_Video::updateSlider(gcn::Slider *slider, gcn::TextField *field, @@ -406,8 +424,13 @@ void Setup_Video::cancel() config.setValue("particleeffects", mParticleEffectsEnabled ? true : false); config.setValue("speech", mSpeechMode); config.setValue("showownname", mNameEnabled ? true : false); + if (player_node) + player_node->mUpdateName = true; config.setValue("guialpha", mOpacity); config.setValue("opengl", mOpenGLEnabled ? true : false); + config.setValue("showpickupchat", mPickupChatEnabled ? true : false); + config.setValue("showpickupparticle", mPickupParticleEnabled ? + true : false); } void Setup_Video::action(const gcn::ActionEvent &event) @@ -443,8 +466,19 @@ void Setup_Video::action(const gcn::ActionEvent &event) { config.setValue("particleeffects", mParticleEffectsCheckBox->isSelected() ? true : false); - new OkDialog(_("Particle effect settings changed"), - _("Restart your client or change maps for the change to take effect.")); + new OkDialog(_("Particle effect settings changed."), + _("Restart your client or change maps " + "for the change to take effect.")); + } + else if (event.getId() == "pickupchat") + { + config.setValue("showpickupchat", mPickupChatCheckBox->isSelected() + ? true : false); + } + else if (event.getId() == "pickupparticle") + { + config.setValue("showpickupparticle", + mPickupParticleCheckBox->isSelected() ? true : false); } else if (event.getId() == "speech") { @@ -558,9 +592,9 @@ void Setup_Video::keyPressed(gcn::KeyEvent &event) { mFps = 10; } - else if (mFps > 200) + else if (mFps > 120) { - mFps = 200; + mFps = 120; } mFpsField->setText(toString(mFps)); mFpsSlider->setValue(mFps); diff --git a/src/gui/setup_video.h b/src/gui/setup_video.h index 44ecdfe5..d2680fa8 100644 --- a/src/gui/setup_video.h +++ b/src/gui/setup_video.h @@ -53,6 +53,8 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, bool mVisibleNamesEnabled; bool mParticleEffectsEnabled; bool mNameEnabled; + bool mPickupChatEnabled; + bool mPickupParticleEnabled; double mOpacity; int mFps; int mSpeechMode; @@ -74,6 +76,10 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, gcn::CheckBox *mParticleEffectsCheckBox; gcn::CheckBox *mNameCheckBox; + gcn::Label *mPickupNotifyLabel; + gcn::CheckBox *mPickupChatCheckBox; + gcn::CheckBox *mPickupParticleCheckBox; + gcn::Slider *mSpeechSlider; gcn::Label *mSpeechLabel; gcn::Slider *mAlphaSlider; diff --git a/src/gui/shop.cpp b/src/gui/shop.cpp index 7b28cef4..3cdc304c 100644 --- a/src/gui/shop.cpp +++ b/src/gui/shop.cpp @@ -23,6 +23,11 @@ #include "../utils/dtor.h" +ShopItems::ShopItems(bool mergeDuplicates) : + mMergeDuplicates(mergeDuplicates) +{ +} + ShopItems::~ShopItems() { clear(); @@ -40,15 +45,27 @@ std::string ShopItems::getElementAt(int i) void ShopItems::addItem(int id, int amount, int price) { - mShopItems.push_back(new ShopItem(id, amount, price)); + mShopItems.push_back(new ShopItem(-1, id, amount, price)); } #ifdef EATHENA_SUPPORT -void ShopItems::addItem(int inventoryIndex, int id, int amount, int price) +void ShopItems::addItem(int inventoryIndex, int id, int quantity, int price) { - ShopItem *item = new ShopItem(id, amount, price); - item->setInvIndex(inventoryIndex); - mShopItems.push_back(item); + ShopItem* item = 0; + if (mMergeDuplicates) + { + item = findItem(id); + } + + if (item) + { + item->addDuplicate (inventoryIndex, quantity); + } + else + { + item = new ShopItem(inventoryIndex, id, quantity, price); + mShopItems.push_back(item); + } } #endif @@ -57,13 +74,30 @@ ShopItem* ShopItems::at(int i) const return mShopItems.at(i); } +void ShopItems::erase(int i) +{ + mShopItems.erase(mShopItems.begin() + i); +} + void ShopItems::clear() { delete_all(mShopItems); mShopItems.clear(); } -std::vector<ShopItem*>* ShopItems::getShop() +ShopItem* ShopItems::findItem(int id) { - return &mShopItems; + ShopItem *item; + + std::vector<ShopItem*>::iterator it; + for(it = mShopItems.begin(); it != mShopItems.end(); it++) + { + item = *(it); + if (item->getId() == id) + { + return item; + } + } + + return 0; } diff --git a/src/gui/shop.h b/src/gui/shop.h index aa72bf2a..6c3031af 100644 --- a/src/gui/shop.h +++ b/src/gui/shop.h @@ -31,10 +31,27 @@ class ShopItem; +/** + * This class handles the list of items available in a shop. + * + * The addItem routine can automatically check, if an item already exists and + * only adds duplicates to the old item, if one is found. The original + * distribution of the duplicates can be retrieved from the item. + * + * This functionality can be enabled in the constructor. + */ class ShopItems : public gcn::ListModel { public: /** + * Constructor. Creates a new ShopItems instance. + * + * @param mergeDuplicates lets the Shop look for duplicate entries and + * merges them to one item. + */ + ShopItems(bool mergeDuplicates = false); + + /** * Destructor. */ ~ShopItems(); @@ -46,18 +63,28 @@ class ShopItems : public gcn::ListModel #ifdef EATHENA_SUPPORT /** - * Adds an item to the list (used by eAthena sell dialog). + * Adds an item to the list (used by sell dialog). Looks for + * duplicate entries, if mergeDuplicates was turned on. + * + * @param inventoryIndex the inventory index of the item + * @param id the id of the item + * @param quantity number of available copies of the item + * @param price price of the item */ void addItem(int inventoryIndex, int id, int amount, int price); #endif /** * Returns the number of items in the shop. + * + * @return the number of items in the shop */ int getNumberOfElements(); /** * Returns the name of item number i in the shop. + * + * @param i the index to retrieve */ std::string getElementAt(int i); @@ -67,17 +94,31 @@ class ShopItems : public gcn::ListModel ShopItem* at(int i) const; /** + * Removes an element from the shop. + * + * @param i index to remove + */ + void erase(int i); + + /** * Clear the vector. */ void clear(); + private: /** - * Direct access to the vector. + * Searches the current items in the shop for the specified + * id and returns the item if found, or 0 else. + * + * @return the item found or 0 */ - std::vector<ShopItem*>* getShop(); + ShopItem* findItem(int id); - private: + /** the shop storage */ std::vector<ShopItem*> mShopItems; + + /** Look for duplicate entries on addition */ + bool mMergeDuplicates; }; #endif diff --git a/src/gui/shoplistbox.cpp b/src/gui/shoplistbox.cpp index 8aed3c77..aa42c294 100644 --- a/src/gui/shoplistbox.cpp +++ b/src/gui/shoplistbox.cpp @@ -22,7 +22,7 @@ #include <guichan/font.hpp> #include <guichan/listmodel.hpp> -#include "color.h" +#include "palette.h" #include "shop.h" #include "shoplistbox.h" @@ -57,17 +57,15 @@ void ShopListBox::setPlayersMoney(int money) void ShopListBox::draw(gcn::Graphics *gcnGraphics) { - if (!mListModel) + if (!mListModel || !isVisible()) return; if (config.getValue("guialpha", 0.8) != mAlpha) mAlpha = config.getValue("guialpha", 0.8); - bool valid; - const int red = (textColor->getColor('H', valid) >> 16) & 0xFF; - const int green = (textColor->getColor('H', valid) >> 8) & 0xFF; - const int blue = textColor->getColor('H', valid) & 0xFF; - const int alpha = (int)(mAlpha * 255.0f); + int alpha = (int)(mAlpha * 255.0f); + const gcn::Color* highlightColor = + &guiPalette->getColor(Palette::HIGHLIGHT, alpha); Graphics *graphics = static_cast<Graphics*>(gcnGraphics); @@ -78,19 +76,27 @@ void ShopListBox::draw(gcn::Graphics *gcnGraphics) i < mListModel->getNumberOfElements(); ++i, y += mRowHeight) { - gcn::Color backgroundColor = gcn::Color(255, 255, 255, alpha); + gcn::Color temp; + const gcn::Color* backgroundColor = + &guiPalette->getColor(Palette::BACKGROUND, alpha); - if (i == mSelected) - { - backgroundColor = gcn::Color(red, green, blue, alpha); - } - else if (mShopItems && + if (mShopItems && mPlayerMoney < mShopItems->at(i)->getPrice() && mPriceCheck) - { - backgroundColor = gcn::Color(145, 145, 145, alpha); - } + if (i != mSelected) + backgroundColor = &guiPalette->getColor(Palette::SHOP_WARNING, + alpha); + else + { + temp = guiPalette->getColor(Palette::SHOP_WARNING, alpha); + temp.r = (temp.r + highlightColor->r) / 2; + temp.g = (temp.g + highlightColor->g) / 2; + temp.b = (temp.g + highlightColor->b) / 2; + backgroundColor = &temp; + } + else if (i == mSelected) + backgroundColor = highlightColor; - graphics->setColor(backgroundColor); + graphics->setColor(*backgroundColor); graphics->fillRectangle(gcn::Rectangle(0, y, getWidth(), mRowHeight)); if (mShopItems) @@ -101,21 +107,12 @@ void ShopListBox::draw(gcn::Graphics *gcnGraphics) graphics->drawImage(icon, 1, y); } } - graphics->setColor(gcn::Color(0, 0, 0)); + graphics->setColor(guiPalette->getColor(Palette::TEXT)); graphics->drawText(mListModel->getElementAt(i), ITEM_ICON_SIZE + 5, y + (ITEM_ICON_SIZE - getFont()->getHeight()) / 2); } } -void ShopListBox::mousePressed(gcn::MouseEvent &event) -{ - if (event.getButton() == gcn::MouseEvent::LEFT) - { - setSelected(event.getY() / mRowHeight); - distributeActionEvent(); - } -} - void ShopListBox::adjustSize() { if (mListModel) diff --git a/src/gui/shoplistbox.h b/src/gui/shoplistbox.h index cde4786e..5d3378b1 100644 --- a/src/gui/shoplistbox.h +++ b/src/gui/shoplistbox.h @@ -56,8 +56,6 @@ class ShopListBox : public ListBox */ unsigned int getRowHeight() const { return mRowHeight; } - void mousePressed(gcn::MouseEvent &event); - /** * gives information about the current player's money */ diff --git a/src/gui/shortcutcontainer.cpp b/src/gui/shortcutcontainer.cpp index 74609fa1..901095e5 100644 --- a/src/gui/shortcutcontainer.cpp +++ b/src/gui/shortcutcontainer.cpp @@ -38,34 +38,27 @@ ShortcutContainer::ShortcutContainer(): void ShortcutContainer::widgetResized(const gcn::Event &event) { mGridWidth = getWidth() / mBoxWidth; + if (mGridWidth < 1) - { mGridWidth = 1; - } - setHeight((mMaxItems / mGridWidth + - (mMaxItems % mGridWidth > 0 ? 1 : 0)) * mBoxHeight); + setHeight((mMaxItems / mGridWidth) * mBoxHeight); mGridHeight = getHeight() / mBoxHeight; + if (mGridHeight < 1) - { mGridHeight = 1; - } } int ShortcutContainer::getIndexFromGrid(int pointX, int pointY) const { - const gcn::Rectangle tRect = gcn::Rectangle( - 0, 0, mGridWidth * mBoxWidth, mGridHeight * mBoxHeight); - if (!tRect.isPointInRect(pointX, pointY)) - { - return -1; - } - const int index = ((pointY / mBoxHeight) * mGridWidth) + - pointX / mBoxWidth; - if (index >= mMaxItems) - { - return -1; - } + const gcn::Rectangle tRect = gcn::Rectangle(0, 0, mGridWidth * mBoxWidth, + mGridHeight * mBoxHeight); + + int index = ((pointY / mBoxHeight) * mGridWidth) + pointX / mBoxWidth; + + if (!tRect.isPointInRect(pointX, pointY) || index >= mMaxItems) + index = -1; + return index; } diff --git a/src/gui/shortcutwindow.cpp b/src/gui/shortcutwindow.cpp index c704fc44..dcc7f72e 100644 --- a/src/gui/shortcutwindow.cpp +++ b/src/gui/shortcutwindow.cpp @@ -23,6 +23,8 @@ #include "shortcutcontainer.h" #include "shortcutwindow.h" +#include "widgets/layout.h" + #include "../configuration.h" static const int SCROLL_PADDING = 0; @@ -39,29 +41,28 @@ ShortcutWindow::ShortcutWindow(const char *title, ShortcutContainer *content) mItems = content; - mInstances++; - const int border = SCROLL_PADDING * 2 + getPadding() * 2; setMinWidth(mItems->getBoxWidth() + border); setMinHeight(mItems->getBoxHeight() + border); setMaxWidth(mItems->getBoxWidth() * mItems->getMaxItems() + border); setMaxHeight(mItems->getBoxHeight() * mItems->getMaxItems() + border); - const int width = (int) config.getValue("screenwidth", 800); - const int height = (int) config.getValue("screenheight", 600); + setDefaultSize(mItems->getBoxWidth() + border, (mItems->getBoxHeight() * + mItems->getMaxItems()) + border, ImageRect::LOWER_RIGHT, + mInstances * mItems->getBoxWidth(), 0); - setDefaultSize(width - (mInstances * mItems->getBoxWidth()) - - (mInstances * border), height - (mItems->getBoxHeight() * - mItems->getMaxItems()) - border, mItems->getBoxWidth() + - border, (mItems->getBoxHeight() * mItems->getMaxItems()) + - border); + mInstances++; mScrollArea = new ScrollArea(mItems); mScrollArea->setPosition(SCROLL_PADDING, SCROLL_PADDING); mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); mScrollArea->setOpaque(false); - add(mScrollArea); + place(0, 0, mScrollArea, 5, 5).setPadding(0); + + Layout &layout = getLayout(); + layout.setRowHeight(0, Layout::AUTO_SET); + layout.setMargin(0); loadWindowState(); } @@ -71,13 +72,3 @@ ShortcutWindow::~ShortcutWindow() delete mItems; } -void ShortcutWindow::widgetResized(const gcn::Event &event) -{ - Window::widgetResized(event); - - const gcn::Rectangle &area = getChildrenArea(); - - mScrollArea->setSize( - area.width - SCROLL_PADDING, - area.height - SCROLL_PADDING); -} diff --git a/src/gui/shortcutwindow.h b/src/gui/shortcutwindow.h index 64592328..eae881ba 100644 --- a/src/gui/shortcutwindow.h +++ b/src/gui/shortcutwindow.h @@ -45,11 +45,6 @@ class ShortcutWindow : public Window */ ~ShortcutWindow(); - /** - * Called whenever the widget changes size. - */ - void widgetResized(const gcn::Event &event); - private: ShortcutWindow(); ShortcutContainer *mItems; diff --git a/src/gui/skill.cpp b/src/gui/skill.cpp index 61bb9ce9..39ccbb06 100644 --- a/src/gui/skill.cpp +++ b/src/gui/skill.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/widgets/label.hpp> - #include "button.h" +#include "label.h" #include "listbox.h" #include "scrollarea.h" #include "skill.h" @@ -102,13 +101,13 @@ public: info = &fakeSkillInfo; sprintf(tmp, "%c%s", info->modifiable? ' ' : '*', info->name.c_str()); - gcn::Label *name_label = new gcn::Label(tmp); + gcn::Label *name_label = new Label(tmp); sprintf(tmp, "Lv:%i", skill->lv); - gcn::Label *lv_label = new gcn::Label(tmp); + gcn::Label *lv_label = new Label(tmp); sprintf(tmp, "Sp:%i", skill->sp); - gcn::Label *sp_label = new gcn::Label(tmp); + gcn::Label *sp_label = new Label(tmp); set(i, 0, name_label); set(i, 1, lv_label); @@ -136,13 +135,13 @@ SkillDialog::SkillDialog(): setWindowName("Skills"); setCloseButton(true); - setDefaultSize(windowContainer->getWidth() - 260, 25, 255, 260); + setDefaultSize(255, 260, ImageRect::CENTER); setMinHeight(50 + mTableModel->getHeight()); setMinWidth(200); ScrollArea *skillScrollArea = new ScrollArea(mTable); - mPointsLabel = new gcn::Label(strprintf(_("Skill points: %d"), 0)); + mPointsLabel = new Label(strprintf(_("Skill points: %d"), 0)); mIncButton = new Button(_("Up"), _("inc"), this); mUseButton = new Button(_("Use"), _("use"), this); mUseButton->setEnabled(false); @@ -157,13 +156,13 @@ SkillDialog::SkillDialog(): Layout &layout = getLayout(); layout.setRowHeight(0, Layout::AUTO_SET); - setLocationRelativeTo(getParent()); + center(); loadWindowState(); } SkillDialog::~SkillDialog() { - delete mTable; + delete_all(mSkillList); } void SkillDialog::action(const gcn::ActionEvent &event) diff --git a/src/gui/skin.cpp b/src/gui/skin.cpp new file mode 100644 index 00000000..d44c54a8 --- /dev/null +++ b/src/gui/skin.cpp @@ -0,0 +1,191 @@ +/* + * Aethyra + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * 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 "skin.h" + +#include "../log.h" + +#include "../resources/image.h" +#include "../resources/resourcemanager.h" + +#include "../utils/dtor.h" +#include "../utils/xml.h" + +SkinLoader* skinLoader = NULL; + +Skin::Skin(ImageRect skin, Image* close, std::string name): + instances(0), + mName(name), + border(skin), + closeImage(close) +{ +} + +Skin::~Skin() +{ + // Clean up static resources + for (int i = 0; i < 9; i++) + { + delete border.grid[i]; + border.grid[i] = NULL; + } + + closeImage->decRef(); +} + +unsigned int Skin::getMinWidth() +{ + return (border.grid[0]->getWidth() + border.grid[1]->getWidth()) + + border.grid[2]->getWidth(); +} + +unsigned int Skin::getMinHeight() +{ + return (border.grid[0]->getHeight() + border.grid[3]->getHeight()) + + border.grid[6]->getHeight(); +} + +Skin* SkinLoader::load(const std::string &filename) +{ + SkinIterator skinIterator = mSkins.find(filename); + + if (mSkins.end() != skinIterator) + { + skinIterator->second->instances++; + return skinIterator->second; + } + + ResourceManager *resman = ResourceManager::getInstance(); + + logger->log("Loading Skin '%s'.", filename.c_str()); + + if (filename.empty()) + logger->error("SkinLoader::load(): Invalid File Name."); + + // TODO: + // If there is an error loading the specified file, we should try to revert + // to a 'default' skin file. Only if the 'default' skin file can't be loaded + // should we have a terminating error. + XML::Document doc(filename); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "skinset")) + logger->error("Widget Skinning error"); + + std::string skinSetImage; + skinSetImage = XML::getProperty(rootNode, "image", ""); + Image *dBorders = NULL; + ImageRect border; + + if (!skinSetImage.empty()) + { + logger->log("SkinLoader::load(): <skinset> defines " + "'%s' as a skin image.", skinSetImage.c_str()); + dBorders = resman->getImage("graphics/gui/" + skinSetImage); + } + else + { + logger->error("SkinLoader::load(): Skinset does not define an image!"); + } + + //iterate <widget>'s + for_each_xml_child_node(widgetNode, rootNode) + { + if (!xmlStrEqual(widgetNode->name, BAD_CAST "widget")) + continue; + + std::string widgetType; + widgetType = XML::getProperty(widgetNode, "type", "unknown"); + if (widgetType == "Window") + { + // Iterate through <part>'s + // LEEOR / TODO: + // We need to make provisions to load in a CloseButton image. For + // now it can just be hard-coded. + for_each_xml_child_node(partNode, widgetNode) + { + if (!xmlStrEqual(partNode->name, BAD_CAST "part")) + continue; + + std::string partType; + partType = XML::getProperty(partNode, "type", "unknown"); + // TOP ROW + const int xPos = XML::getProperty(partNode, "xpos", 0); + const int yPos = XML::getProperty(partNode, "ypos", 0); + const int width = XML::getProperty(partNode, "width", 1); + const int height = XML::getProperty(partNode, "height", 1); + + if (partType == "top-left-corner") + border.grid[0] = dBorders->getSubImage(xPos, yPos, width, height); + else if (partType == "top-edge") + border.grid[1] = dBorders->getSubImage(xPos, yPos, width, height); + else if (partType == "top-right-corner") + border.grid[2] = dBorders->getSubImage(xPos, yPos, width, height); + + // MIDDLE ROW + else if (partType == "left-edge") + border.grid[3] = dBorders->getSubImage(xPos, yPos, width, height); + else if (partType == "bg-quad") + border.grid[4] = dBorders->getSubImage(xPos, yPos, width, height); + else if (partType == "right-edge") + border.grid[5] = dBorders->getSubImage(xPos, yPos, width, height); + + // BOTTOM ROW + else if (partType == "bottom-left-corner") + border.grid[6] = dBorders->getSubImage(xPos, yPos, width, height); + else if (partType == "bottom-edge") + border.grid[7] = dBorders->getSubImage(xPos, yPos, width, height); + else if (partType == "bottom-right-corner") + border.grid[8] = dBorders->getSubImage(xPos, yPos, width, height); + + // Part is of an uknown type. + else + logger->log("SkinLoader::load(): Unknown Part Type '%s'", partType.c_str()); + } + } + // Widget is of an uknown type. + else + { + logger->log("SkinLoader::load(): Unknown Widget Type '%s'", widgetType.c_str()); + } + } + dBorders->decRef(); + + logger->log("Finished loading Skin."); + + // Hard-coded for now until we update the above code to look for window buttons. + Image* closeImage = resman->getImage("graphics/gui/close_button.png"); + + Skin* skin = new Skin(border, closeImage); + + mSkins[filename] = skin; + return skin; +} + +SkinLoader::SkinLoader() +{ +} + +SkinLoader::~SkinLoader() +{ + delete_all(mSkins); +} + diff --git a/src/gui/skin.h b/src/gui/skin.h new file mode 100644 index 00000000..b8a1242e --- /dev/null +++ b/src/gui/skin.h @@ -0,0 +1,101 @@ +/* + * Aethyra + * Copyright (C) 2009 Aethyra Development Team + * + * This file is part of Aethyra. + * + * 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 SKIN_H +#define SKIN_H + +#include <map> +#include <string> + +#include "../graphics.h" + +class Image; + +class Skin +{ + public: + Skin(ImageRect skin, Image* close, std::string name = ""); + ~Skin(); + + /** + * Returns the skin's name. Useful for giving a human friendly skin + * name if a dialog for skin selection for a specific window type is + * done. + */ + std::string getName() { return mName; } + + /** + * Returns the background skin. + */ + ImageRect getBorder() { return border; } + + /** + * Returns the image used by a close button for this skin. + */ + Image* getCloseImage() { return closeImage; } + + /** + * Returns the number of instances which use this skin. + */ + int getNumberOfInstances() { return instances; } + + /** + * Returns the minimum width which can be used with this skin. + */ + unsigned int getMinWidth(); + + /** + * Returns the minimum height which can be used with this skin. + */ + unsigned int getMinHeight(); + + int instances; + + private: + std::string mName; /**< Name of the skin to use */ + ImageRect border; /**< The window border and background */ + Image *closeImage; /**< Close Button Image */ +}; + +// Map containing all window skins +typedef std::map<std::string, Skin*> Skins; + +// Iterator for window skins +typedef Skins::iterator SkinIterator; + +class SkinLoader +{ + public: + SkinLoader(); + ~SkinLoader(); + + /** + * Loads a skin + */ + Skin* load(const std::string &filename); + + private: + Skins mSkins; +}; + +extern SkinLoader* skinLoader; + +#endif diff --git a/src/gui/slider.cpp b/src/gui/slider.cpp index 9bfa840f..cc381c32 100644 --- a/src/gui/slider.cpp +++ b/src/gui/slider.cpp @@ -95,6 +95,16 @@ void Slider::init() vGrip = slider->getSubImage(x, y, w, h); slider->decRef(); + + hStart->setAlpha(mAlpha); + hMid->setAlpha(mAlpha); + hEnd->setAlpha(mAlpha); + hGrip->setAlpha(mAlpha); + + vStart->setAlpha(mAlpha); + vMid->setAlpha(mAlpha); + vEnd->setAlpha(mAlpha); + vGrip->setAlpha(mAlpha); } mInstances++; diff --git a/src/gui/speechbubble.cpp b/src/gui/speechbubble.cpp index 165d216f..73ada2e1 100644 --- a/src/gui/speechbubble.cpp +++ b/src/gui/speechbubble.cpp @@ -29,14 +29,17 @@ #include "speechbubble.h" #include "textbox.h" +#include "../graphics.h" + #include "../utils/gettext.h" SpeechBubble::SpeechBubble(): - Window(_("Speech"), false, NULL, "graphics/gui/speechbubble.xml") + Popup("Speech", NULL, "graphics/gui/speechbubble.xml"), + mText("") { setContentSize(140, 46); - setShowTitle(false); - setTitleBarHeight(0); + setMinWidth(29); + setMinHeight(29); mCaption = new gcn::Label(""); mCaption->setFont(boldFont); @@ -45,6 +48,7 @@ SpeechBubble::SpeechBubble(): mSpeechBox = new TextBox; mSpeechBox->setEditable(false); mSpeechBox->setOpaque(false); + mSpeechBox->setTextColor(&guiPalette->getColor(Palette::CHAT)); mSpeechArea = new ScrollArea(mSpeechBox); @@ -56,26 +60,29 @@ SpeechBubble::SpeechBubble(): add(mCaption); add(mSpeechArea); - - setLocationRelativeTo(getParent()); } -void SpeechBubble::setCaption(const std::string &name, const gcn::Color &color) +void SpeechBubble::setCaption(const std::string &name, const gcn::Color *color) { mCaption->setCaption(name); mCaption->adjustSize(); - mCaption->setForegroundColor(color); + mCaption->setForegroundColor(*color); } -void SpeechBubble::setText(std::string mText, bool showName) +void SpeechBubble::setText(std::string text, bool showName) { + if ((text == mText) && (mCaption->getWidth() <= mSpeechBox->getMinWidth())) + return; + + graphics->setColor(guiPalette->getColor(Palette::TEXT)); + int width = mCaption->getWidth(); - mSpeechBox->setTextWrapped(mText, 130 > width ? 130 : width); + mSpeechBox->setTextWrapped(text, 130 > width ? 130 : width); const int fontHeight = getFont()->getHeight(); const int numRows = showName ? mSpeechBox->getNumberOfRows() + 1 : mSpeechBox->getNumberOfRows(); - int yPos = showName ? fontHeight + 3 : 3; + int yPos = showName ? fontHeight + getPadding() : getPadding(); int height = (numRows * fontHeight); if (width < mSpeechBox->getMinWidth()) @@ -83,11 +90,11 @@ void SpeechBubble::setText(std::string mText, bool showName) if (numRows == 1) { - yPos = (fontHeight / 4) + 3; + yPos = (fontHeight / 4) + getPadding(); height = ((3 * fontHeight) / 2) + 1; } - setContentSize(width + fontHeight, height + 6); + setContentSize(width + fontHeight, height + getPadding()); mSpeechArea->setDimension(gcn::Rectangle(4, yPos, width + 5, height)); } diff --git a/src/gui/speechbubble.h b/src/gui/speechbubble.h index 5d582b1d..3eead884 100644 --- a/src/gui/speechbubble.h +++ b/src/gui/speechbubble.h @@ -23,23 +23,44 @@ #ifndef SPEECHBUBBLE_H #define SPEECHBUBBLE_H -#include "window.h" +#include "palette.h" +#include "popup.h" class ScrollArea; class TextBox; -class SpeechBubble : public Window +class SpeechBubble : public Popup { public: + /** + * Constructor. Initializes the speech bubble. + */ SpeechBubble(); + /** + * Sets the name displayed for the speech bubble, and in what color. + */ void setCaption(const std::string &name, - const gcn::Color &color = 0x000000); - void setText(std::string mText, bool showName = true); + const gcn::Color *color = + &guiPalette->getColor(Palette::TEXT)); + + /** + * Sets the text to be displayed. + */ + void setText(std::string text, bool showName = true); + + /** + * Sets the location in which the speech bubble will be displayed. + */ void setLocation(int x, int y); + + /** + * Gets the number of rows the speech bubble has. + */ unsigned int getNumRows(); private: + std::string mText; gcn::Label *mCaption; TextBox *mSpeechBox; ScrollArea *mSpeechArea; diff --git a/src/gui/status.cpp b/src/gui/status.cpp index 3c48d045..2f95f1c8 100644 --- a/src/gui/status.cpp +++ b/src/gui/status.cpp @@ -19,9 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/widgets/label.hpp> - #include "button.h" +#include "label.h" #include "progressbar.h" #include "status.h" #include "windowcontainer.h" @@ -42,28 +41,27 @@ StatusWindow::StatusWindow(LocalPlayer *player): { setWindowName("Status"); setCloseButton(true); - setDefaultSize((windowContainer->getWidth() - 365) / 2, - (windowContainer->getHeight() - 255) / 2, 400, 345); + setDefaultSize(400, 345, ImageRect::CENTER); // ---------------------- // Status Part // ---------------------- - mLvlLabel = new gcn::Label(strprintf(_("Level: %d"), 0)); - mJobLvlLabel = new gcn::Label(strprintf(_("Job: %d"), 0)); - mGpLabel = new gcn::Label(strprintf(_("Money: %s"), + mLvlLabel = new Label(strprintf(_("Level: %d"), 0)); + mJobLvlLabel = new Label(strprintf(_("Job: %d"), 0)); + mGpLabel = new Label(strprintf(_("Money: %s"), Units::formatCurrency(mCurrency).c_str())); - mHpLabel = new gcn::Label(_("HP:")); + mHpLabel = new Label(_("HP:")); mHpBar = new ProgressBar(1.0f, 80, 15, 0, 171, 34); - mXpLabel = new gcn::Label(_("Exp:")); + mXpLabel = new Label(_("Exp:")); mXpBar = new ProgressBar(1.0f, 80, 15, 143, 192, 211); - mMpLabel = new gcn::Label(_("MP:")); + mMpLabel = new Label(_("MP:")); mMpBar = new ProgressBar(1.0f, 80, 15, 26, 102, 230); - mJobLabel = new gcn::Label(_("Job:")); + mJobLabel = new Label(_("Job:")); mJobBar = new ProgressBar(1.0f, 80, 15, 220, 135, 203); // ---------------------- @@ -71,41 +69,41 @@ StatusWindow::StatusWindow(LocalPlayer *player): // ---------------------- // Static Labels - gcn::Label *mStatsTitleLabel = new gcn::Label(_("Stats")); - gcn::Label *mStatsTotalLabel = new gcn::Label(_("Total")); - gcn::Label *mStatsCostLabel = new gcn::Label(_("Cost")); + gcn::Label *mStatsTitleLabel = new Label(_("Stats")); + gcn::Label *mStatsTotalLabel = new Label(_("Total")); + gcn::Label *mStatsCostLabel = new Label(_("Cost")); mStatsTotalLabel->setAlignment(gcn::Graphics::CENTER); // Derived Stats - mStatsAttackLabel = new gcn::Label(_("Attack:")); - mStatsDefenseLabel= new gcn::Label(_("Defense:")); - mStatsMagicAttackLabel = new gcn::Label(_("M.Attack:")); - mStatsMagicDefenseLabel = new gcn::Label(_("M.Defense:")); + mStatsAttackLabel = new Label(_("Attack:")); + mStatsDefenseLabel= new Label(_("Defense:")); + mStatsMagicAttackLabel = new Label(_("M.Attack:")); + mStatsMagicDefenseLabel = new Label(_("M.Defense:")); // Gettext flag for next line: xgettext:no-c-format - mStatsAccuracyLabel = new gcn::Label(_("% Accuracy:")); + mStatsAccuracyLabel = new Label(_("% Accuracy:")); // Gettext flag for next line: xgettext:no-c-format - mStatsEvadeLabel = new gcn::Label(_("% Evade:")); + mStatsEvadeLabel = new Label(_("% Evade:")); // Gettext flag for next line: xgettext:no-c-format - mStatsReflexLabel = new gcn::Label(_("% Reflex:")); + mStatsReflexLabel = new Label(_("% Reflex:")); - mStatsAttackPoints = new gcn::Label; - mStatsDefensePoints = new gcn::Label; - mStatsMagicAttackPoints = new gcn::Label; - mStatsMagicDefensePoints = new gcn::Label; - mStatsAccuracyPoints = new gcn::Label; - mStatsEvadePoints = new gcn::Label; - mStatsReflexPoints = new gcn::Label; + mStatsAttackPoints = new Label; + mStatsDefensePoints = new Label; + mStatsMagicAttackPoints = new Label; + mStatsMagicDefensePoints = new Label; + mStatsAccuracyPoints = new Label; + mStatsEvadePoints = new Label; + mStatsReflexPoints = new Label; // New labels for (int i = 0; i < 6; i++) { - mStatsLabel[i] = new gcn::Label("0"); + mStatsLabel[i] = new Label("0"); mStatsLabel[i]->setAlignment(gcn::Graphics::CENTER); - mStatsDisplayLabel[i] = new gcn::Label; - mPointsLabel[i] = new gcn::Label("0"); + mStatsDisplayLabel[i] = new Label; + mPointsLabel[i] = new Label("0"); mPointsLabel[i]->setAlignment(gcn::Graphics::CENTER); } - mRemainingStatsPointsLabel = new gcn::Label; + mRemainingStatsPointsLabel = new Label; // Set button events Id mStatsButton[0] = new Button("+", "STR", this); @@ -181,39 +179,13 @@ void StatusWindow::update() mGpLabel->adjustSize(); } - mHpBar->setText(toString(mPlayer->getHp()) + - "/" + toString(mPlayer->getMaxHp())); - - mMpBar->setText(toString(mPlayer->mMp) + - "/" + toString(mPlayer->mMaxMp)); - - mXpBar->setText(toString(mPlayer->getXp()) + - "/" + toString(mPlayer->mXpForNextLevel)); - - mJobBar->setText(toString(mPlayer->mJobXp) + - "/" + toString(mPlayer->mJobXpForNextLevel)); + updateHPBar(mHpBar, true); - // HP Bar coloration - if (mPlayer->getHp() < int(mPlayer->getMaxHp() / 3)) - { - mHpBar->setColor(223, 32, 32); // Red - } - else if (mPlayer->getHp() < int((mPlayer->getMaxHp() / 3) * 2)) - { - mHpBar->setColor(230, 171, 34); // Orange - } - else - { - mHpBar->setColor(0, 171, 34); // Green - } + updateMPBar(mMpBar, true); - mHpBar->setProgress((float) mPlayer->getHp() / (float) mPlayer->getMaxHp()); - mMpBar->setProgress((float) mPlayer->mMp / (float) mPlayer->mMaxMp); + updateXPBar(mXpBar, false); - mXpBar->setProgress( - (float) mPlayer->getXp() / (float) mPlayer->mXpForNextLevel); - mJobBar->setProgress( - (float) mPlayer->mJobXp / (float) mPlayer->mJobXpForNextLevel); + updateJobBar(mJobBar, false); // Stats Part // ---------- @@ -281,6 +253,9 @@ void StatusWindow::update() void StatusWindow::draw(gcn::Graphics *g) { + if (!isVisible()) + return; + update(); Window::draw(g); @@ -292,29 +267,97 @@ void StatusWindow::action(const gcn::ActionEvent &event) if (event.getId().length() == 3) { if (event.getId() == "STR") - { player_node->raiseAttribute(LocalPlayer::STR); - } if (event.getId() == "AGI") - { player_node->raiseAttribute(LocalPlayer::AGI); - } if (event.getId() == "VIT") - { player_node->raiseAttribute(LocalPlayer::VIT); - } if (event.getId() == "INT") - { player_node->raiseAttribute(LocalPlayer::INT); - } if (event.getId() == "DEX") - { player_node->raiseAttribute(LocalPlayer::DEX); - } if (event.getId() == "LUK") - { player_node->raiseAttribute(LocalPlayer::LUK); + } +} + +void StatusWindow::updateHPBar(ProgressBar *bar, bool showMax) +{ + if (showMax) + bar->setText(toString(player_node->getHp()) + + "/" + toString(player_node->getMaxHp())); + else + bar->setText(toString(player_node->getHp())); + + // HP Bar coloration + if (player_node->getHp() < player_node->getMaxHp() / 3) + { + bar->setColor(223, 32, 32); // Red + } + else if (player_node->getHp() < (player_node->getMaxHp() / 3) * 2) + { + bar->setColor(230, 171, 34); // Orange + } + else + { + bar->setColor(0, 171, 34); // Green + } + + bar->setProgress((float) player_node->getHp() / (float) player_node->getMaxHp()); +} + +void StatusWindow::updateMPBar(ProgressBar *bar, bool showMax) +{ + if (showMax) + bar->setText(toString(player_node->mMp) + + "/" + toString(player_node->mMaxMp)); + else + bar->setText(toString(player_node->mMp)); + + + bar->setProgress((float) player_node->mMp / (float) player_node->mMaxMp); +} + +void StatusWindow::updateXPBar(ProgressBar *bar, bool percent) +{ + if (player_node->mXpForNextLevel == 0) { + bar->setText(_("Max level")); + bar->setProgress(1.0); + } else { + if (percent) + { + float xp = (float) player_node->getXp() / + player_node->mXpForNextLevel; + bar->setText(toString((float) ((int) (xp * 10000.0f)) / 100.0f) + + "%"); } + else + bar->setText(toString(player_node->getXp()) + + "/" + toString(player_node->mXpForNextLevel)); + + bar->setProgress((float) player_node->getXp() / + (float) player_node->mXpForNextLevel); } } +void StatusWindow::updateJobBar(ProgressBar *bar, bool percent) +{ + if (player_node->mJobXpForNextLevel == 0) { + bar->setText(_("Max level")); + bar->setProgress(1.0); + } else { + if (percent) + { + float xp = (float) player_node->mJobXp / + player_node->mJobXpForNextLevel; + bar->setText(toString((float) ((int) (xp * 10000.0f)) / 100.0f) + + "%"); + } + else + bar->setText(toString(player_node->mJobXp) + + "/" + toString(player_node->mJobXpForNextLevel)); + + bar->setProgress((float) player_node->mJobXp / + (float) player_node->mJobXpForNextLevel); + } +} diff --git a/src/gui/status.h b/src/gui/status.h index 9e4de3fd..1425fe12 100644 --- a/src/gui/status.h +++ b/src/gui/status.h @@ -57,6 +57,11 @@ class StatusWindow : public Window, public gcn::ActionListener */ void update(); + static void updateHPBar(ProgressBar *bar, bool showMax = false); + static void updateMPBar(ProgressBar *bar, bool showMax = false); + static void updateXPBar(ProgressBar *bar, bool percent = true); + static void updateJobBar(ProgressBar *bar, bool percent = true); + private: LocalPlayer *mPlayer; diff --git a/src/gui/statuswindow.cpp b/src/gui/statuswindow.cpp index bcac0a90..8d4363d9 100644 --- a/src/gui/statuswindow.cpp +++ b/src/gui/statuswindow.cpp @@ -208,28 +208,7 @@ void StatusWindow::update() mMoneyLabel->setCaption("Money: " + toString(mPlayer->getMoney()) + " GP"); mMoneyLabel->adjustSize(); - int hp = mPlayer->getHp(); - int maxHp = mPlayer->getMaxHp(); - - mHpValueLabel->setCaption(toString(hp) + - " / " + toString(maxHp)); - mHpValueLabel->adjustSize(); - - // HP Bar coloration - if (hp < int(maxHp / 3)) - { - mHpBar->setColor(223, 32, 32); // Red - } - else if (hp < int((maxHp / 3) * 2)) - { - mHpBar->setColor(230, 171, 34); // Orange - } - else - { - mHpBar->setColor(0, 171, 34); // Green - } - - mHpBar->setProgress((float) hp / maxHp); + updateHPBar(mHpBar, true); // Stats Part // ---------- @@ -368,3 +347,30 @@ void StatusWindow::action(const gcn::ActionEvent &event) mPlayer->lowerAttribute(LocalPlayer::WIL); } } + +// WARNING: Duplicated method! + +void StatusWindow::updateHPBar(ProgressBar *bar, bool showMax) +{ + if (showMax) + bar->setText(toString(player_node->getHp()) + + "/" + toString(player_node->getMaxHp())); + else + bar->setText(toString(player_node->getHp())); + + // HP Bar coloration + if (player_node->getHp() < player_node->getMaxHp() / 3) + { + bar->setColor(223, 32, 32); // Red + } + else if (player_node->getHp() < (player_node->getMaxHp() / 3) * 2) + { + bar->setColor(230, 171, 34); // Orange + } + else + { + bar->setColor(0, 171, 34); // Green + } + + bar->setProgress((float) player_node->getHp() / (float) player_node->getMaxHp()); +} diff --git a/src/gui/statuswindow.h b/src/gui/statuswindow.h index 262b89f6..05bac4e5 100644 --- a/src/gui/statuswindow.h +++ b/src/gui/statuswindow.h @@ -62,6 +62,8 @@ class StatusWindow : public Window, public gcn::ActionListener */ void update(); + static void updateHPBar(ProgressBar *bar, bool showMax = false); + private: LocalPlayer *mPlayer; diff --git a/src/gui/storagewindow.cpp b/src/gui/storagewindow.cpp new file mode 100644 index 00000000..81212ae8 --- /dev/null +++ b/src/gui/storagewindow.cpp @@ -0,0 +1,210 @@ +/* + * 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 <string> + +#include <guichan/font.hpp> +#include <guichan/mouseinput.hpp> + +#include "button.h" +#include "inventorywindow.h" +#include "item_amount.h" +#include "itemcontainer.h" +#include "label.h" +#include "progressbar.h" +#include "scrollarea.h" +#include "storagewindow.h" +#include "viewport.h" + +#include "widgets/layout.h" + +#include "../inventory.h" +#include "../item.h" +#include "../localplayer.h" +#include "../units.h" + +#include "../net/messageout.h" +#ifdef EATHENA_SUPPORT +#include "../net/ea/protocol.h" +#endif + +#include "../resources/iteminfo.h" + +#include "../utils/gettext.h" +#include "../utils/strprintf.h" +#include "../utils/stringutils.h" + +StorageWindow::StorageWindow(Network *network, int invSize): + Window(_("Storage")), + mNetwork(network), + mMaxSlots(invSize), + mItemDesc(false) +{ + setWindowName("Storage"); + setResizable(true); + setCloseButton(true); + + // If you adjust these defaults, don't forget to adjust the trade window's. + setDefaultSize(375, 300, ImageRect::CENTER); + + mStoreButton = new Button(_("Store"), "store", this); + mRetrieveButton = new Button(_("Retrieve"), "retrieve", this); + + mItems = new ItemContainer(player_node->getStorage(), 10, 5, 1); + mItems->addSelectionListener(this); + + mInvenScroll = new ScrollArea(mItems); + mInvenScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + + mUsedSlots = toString(player_node->getStorage()->getNumberOfSlotsUsed()); + + mSlotsLabel = new Label(_("Slots: ")); + + mSlotsBar = new ProgressBar(1.0f, 100, 20, 225, 200, 25); + + setMinHeight(130); + setMinWidth(mSlotsLabel->getWidth()); + + place(0, 0, mSlotsLabel).setPadding(3); + place(1, 0, mSlotsBar, 3); + place(0, 1, mInvenScroll, 4, 4); + place(2, 5, mStoreButton); + place(3, 5, mRetrieveButton); + + Layout &layout = getLayout(); + layout.setRowHeight(0, mStoreButton->getHeight()); + + loadWindowState(); +} + +StorageWindow::~StorageWindow() +{ + delete mItems; +} + +void StorageWindow::logic() +{ + if (!isVisible()) + return; + + Window::logic(); + + if (mUsedSlots != toString(player_node->getStorage()->getNumberOfSlotsUsed())) + { + mUsedSlots = toString(player_node->getStorage()->getNumberOfSlotsUsed()); + + mSlotsBar->setProgress((float) + player_node->getStorage()->getNumberOfSlotsUsed() / mMaxSlots); + + mSlotsBar->setText(strprintf("%s/%d", mUsedSlots.c_str(), mMaxSlots)); + } +} + +void StorageWindow::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "store") + { + if (!inventoryWindow->isVisible()) return; + + Item *item = inventoryWindow->getSelectedItem(); + + if (!item) + return; + + if (item->getQuantity() == 1) + { + addStore(item, 1); + } + else + { + // Choose amount of items to trade + new ItemAmountWindow(AMOUNT_STORE_ADD, this, item); + } + } + else if (event.getId() == "retrieve") + { + Item *item = mItems->getSelectedItem(); + + if (!item) + return; + + if (item->getQuantity() == 1) + { + removeStore(item, 1); + } + else + { + // Choose amount of items to trade + new ItemAmountWindow(AMOUNT_STORE_REMOVE, this, item); + } + } +} + +void StorageWindow::mouseClicked(gcn::MouseEvent &event) +{ + Window::mouseClicked(event); + + if (event.getButton() == gcn::MouseEvent::RIGHT) + { + Item *item = mItems->getSelectedItem(); + + if (!item) { + mRetrieveButton->setEnabled(false); + return; + } + + mRetrieveButton->setEnabled(true); + + /* Convert relative to the window coordinates to absolute screen + * coordinates. + */ + const int mx = event.getX() + getX(); + const int my = event.getY() + getY(); + viewport->showPopup(mx, my, item); + } +} + +Item* StorageWindow::getSelectedItem() const +{ + return mItems->getSelectedItem(); +} + +void StorageWindow::addStore(Item* item, int ammount) +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_MOVE_TO_STORAGE); + outMsg.writeInt16(item->getInvIndex()); + outMsg.writeInt32(ammount); +} + +void StorageWindow::removeStore(Item* item, int ammount) +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CSMG_MOVE_FROM_STORAGE); + outMsg.writeInt16(item->getInvIndex()); + outMsg.writeInt32(ammount); +} + +void StorageWindow::close() +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_CLOSE_STORAGE); +} diff --git a/src/gui/storagewindow.h b/src/gui/storagewindow.h new file mode 100644 index 00000000..c78d33a7 --- /dev/null +++ b/src/gui/storagewindow.h @@ -0,0 +1,110 @@ +/* + * 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 STORAGEWINDOW_H +#define STORAGEWINDOW_H + +#include "window.h" + +#include "../inventory.h" + +#include <guichan/actionlistener.hpp> +#include <guichan/selectionlistener.hpp> + +class Item; +class ItemContainer; +class Network; +class ProgressBar; +class TextBox; + +/** + * Storage dialog. + * + * \ingroup Interface + */ +class StorageWindow : public Window, gcn::ActionListener, + gcn::SelectionListener +{ + public: + /** + * Constructor. + */ + StorageWindow(Network *network, int invSize = (STORAGE_SIZE - 1)); + + /** + * Destructor. + */ + ~StorageWindow(); + + /** + * Logic (updates buttons and weight information). + */ + void logic(); + + /** + * Called when receiving actions from the widgets. + */ + void action(const gcn::ActionEvent &event); + + /** + * Returns the selected item. + */ + Item* getSelectedItem() const; + + void mouseClicked(gcn::MouseEvent &event); + + /** + * Add the specified ammount of the specified item to storage + */ + void addStore(Item* item, int ammount); + + /** + * Remove the specified ammount of the specified item from storage + */ + void removeStore(Item* item, int ammount); + + /** + * Closes the Storage Window, as well as telling the server that the + * window has been closed. + */ + void close(); + + private: + Network *mNetwork; + ItemContainer *mItems; + + std::string mSlots; + std::string mUsedSlots; + gcn::Button *mStoreButton, *mRetrieveButton; + gcn::ScrollArea *mInvenScroll; + + gcn::Label *mSlotsLabel; + + ProgressBar *mSlotsBar; + + int mMaxSlots; + + bool mItemDesc; +}; + +extern StorageWindow *storageWindow; + +#endif // STORAGEWINDOW_H diff --git a/src/gui/table.cpp b/src/gui/table.cpp index 1fd088ce..ec5b0480 100644 --- a/src/gui/table.cpp +++ b/src/gui/table.cpp @@ -23,7 +23,7 @@ #include <guichan/graphics.hpp> #include <guichan/key.hpp> -#include "color.h" +#include "palette.h" #include "table.h" #include "../configuration.h" @@ -98,6 +98,7 @@ GuiTable::GuiTable(TableModel *initial_model, gcn::Color background, GuiTable::~GuiTable() { + uninstallActionListeners(); delete mModel; } @@ -264,7 +265,7 @@ void GuiTable::installActionListeners() // -- widget ops void GuiTable::draw(gcn::Graphics* graphics) { - if (!mModel) + if (!mModel || !isVisible()) return; if (config.getValue("guialpha", 0.8) != mAlpha) @@ -272,11 +273,8 @@ void GuiTable::draw(gcn::Graphics* graphics) if (mOpaque) { - const int red = getBackgroundColor().r; - const int green = getBackgroundColor().g; - const int blue = getBackgroundColor().b; - const int alpha = (int)(mAlpha * 255.0f); - graphics->setColor(gcn::Color(red, green, blue, alpha)); + graphics->setColor(guiPalette->getColor(Palette::BACKGROUND, + (int)(mAlpha * 255.0f))); graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); } @@ -321,18 +319,19 @@ void GuiTable::draw(gcn::Graphics* graphics) widget->setDimension(bounds); - if (!mLinewiseMode && c == mSelectedColumn && r == mSelectedRow) + graphics->setColor(guiPalette->getColor(Palette::HIGHLIGHT, + (int)(mAlpha * 255.0f))); + + if (mLinewiseMode && r == mSelectedRow && c == 0) + { + graphics->fillRectangle(gcn::Rectangle(0, y_offset, + getWidth(), height)); + } + else if (!mLinewiseMode && + c == mSelectedColumn && r == mSelectedRow) { - bool valid; - const int red = - (textColor->getColor('H', valid) >> 16) & 0xFF; - const int green = - (textColor->getColor('H', valid) >> 8) & 0xFF; - const int blue = textColor->getColor('H', valid) & 0xFF; - const int alpha = (int)(mAlpha * 127.0f); - - graphics->setColor(gcn::Color(red, green, blue, alpha)); - graphics->fillRectangle(bounds); + graphics->fillRectangle(gcn::Rectangle(x_offset, y_offset, + width, height)); } graphics->pushClipArea(bounds); @@ -343,21 +342,6 @@ void GuiTable::draw(gcn::Graphics* graphics) x_offset += width; } - if (mLinewiseMode && r == mSelectedRow) - { - bool valid; - const int red = - (textColor->getColor('H', valid) >> 16) & 0xFF; - const int green = - (textColor->getColor('H', valid) >> 8) & 0xFF; - const int blue = textColor->getColor('H', valid) & 0xFF; - const int alpha = (int)(mAlpha * 127.0f); - - graphics->setColor(gcn::Color(red, green, blue, alpha)); - graphics->fillRectangle(gcn::Rectangle(0, y_offset, - x_offset, height)); - } - y_offset += height; } @@ -401,25 +385,21 @@ void GuiTable::keyPressed(gcn::KeyEvent& keyEvent) else if (key.getValue() == gcn::Key::UP) { setSelectedRow(mSelectedRow - 1); - keyEvent.consume(); } else if (key.getValue() == gcn::Key::DOWN) { setSelectedRow(mSelectedRow + 1); - keyEvent.consume(); } else if (key.getValue() == gcn::Key::LEFT) { setSelectedColumn(mSelectedColumn - 1); - keyEvent.consume(); } else if (key.getValue() == gcn::Key::RIGHT) { setSelectedColumn(mSelectedColumn + 1); - keyEvent.consume(); } else if (key.getValue() == gcn::Key::HOME) @@ -459,7 +439,7 @@ void GuiTable::mouseWheelMovedUp(gcn::MouseEvent& mouseEvent) { if (isFocused()) { - if (getSelectedRow() >= 0 ) + if (getSelectedRow() > 0 || (getSelectedRow() == 0 && mWrappingEnabled)) { setSelectedRow(getSelectedRow() - 1); } diff --git a/src/gui/textbox.cpp b/src/gui/textbox.cpp index 2a86d549..10f727e3 100644 --- a/src/gui/textbox.cpp +++ b/src/gui/textbox.cpp @@ -23,10 +23,11 @@ #include <guichan/font.hpp> +#include "palette.h" #include "textbox.h" -TextBox::TextBox(): - gcn::TextBox() +TextBox::TextBox() : + gcn::TextBox(), mTextColor(&guiPalette->getColor(Palette::TEXT)) { setOpaque(false); setFrameSize(0); @@ -37,52 +38,46 @@ void TextBox::setTextWrapped(const std::string &text, int minDimension) { // Make sure parent scroll area sets width of this widget if (getParent()) - { getParent()->logic(); - } // Take the supplied minimum dimension as a starting point and try to beat it mMinWidth = minDimension; std::stringstream wrappedStream; - std::string::size_type newlinePos, lastNewlinePos = 0; + std::string::size_type spacePos, newlinePos, lastNewlinePos = 0; int minWidth = 0; int xpos; + spacePos = text.rfind(" ", text.size()); + + if (spacePos != std::string::npos) + { + const std::string word = text.substr(spacePos + 1); + const int length = getFont()->getWidth(word); + + if (length > mMinWidth) + mMinWidth = length; + } + do { // Determine next piece of string to wrap newlinePos = text.find("\n", lastNewlinePos); if (newlinePos == std::string::npos) - { newlinePos = text.size(); - } std::string line = text.substr(lastNewlinePos, newlinePos - lastNewlinePos); - std::string::size_type spacePos, lastSpacePos = 0; + std::string::size_type lastSpacePos = 0; xpos = 0; - spacePos = text.rfind(" ", text.size()); - - if (spacePos != std::string::npos) - { - const std::string word = text.substr(spacePos + 1); - const int length = getFont()->getWidth(word); - - if (length > mMinWidth) - mMinWidth = length; - } - do { spacePos = line.find(" ", lastSpacePos); if (spacePos == std::string::npos) - { spacePos = line.size(); - } std::string word = line.substr(lastSpacePos, spacePos - lastSpacePos); diff --git a/src/gui/textbox.h b/src/gui/textbox.h index 10a81fc0..5884e11c 100644 --- a/src/gui/textbox.h +++ b/src/gui/textbox.h @@ -39,6 +39,11 @@ class TextBox : public gcn::TextBox */ TextBox(); + inline void setTextColor(const gcn::Color* color) + { + mTextColor = color; + } + /** * Sets the text after wrapping it to the current width of the widget. */ @@ -49,8 +54,18 @@ class TextBox : public gcn::TextBox */ int getMinWidth() { return mMinWidth; } + /** + * Draws the text. + */ + inline void draw(gcn::Graphics *graphics) + { + setForegroundColor(*mTextColor); + gcn::TextBox::draw(graphics); + } + private: int mMinWidth; + const gcn::Color* mTextColor; }; #endif diff --git a/src/gui/textfield.cpp b/src/gui/textfield.cpp index 99a95a2e..5c6e4f49 100644 --- a/src/gui/textfield.cpp +++ b/src/gui/textfield.cpp @@ -21,6 +21,7 @@ #include <guichan/font.hpp> +#include "palette.h" #include "sdlinput.h" #include "textfield.h" @@ -54,8 +55,10 @@ TextField::TextField(const std::string& text): int gridy[4] = {0, 3, 28, 31}; int a = 0, x, y; - for (y = 0; y < 3; y++) { - for (x = 0; x < 3; x++) { + for (y = 0; y < 3; y++) + { + for (x = 0; x < 3; x++) + { skin.grid[a] = textbox->getSubImage( gridx[x], gridy[y], gridx[x + 1] - gridx[x] + 1, @@ -76,20 +79,22 @@ TextField::~TextField() instances--; if (instances == 0) - { for_each(skin.grid, skin.grid + 9, dtor<Image*>()); - } } void TextField::draw(gcn::Graphics *graphics) { - if (isFocused()) { + if (!isVisible()) + return; + + if (isFocused()) + { drawCaret(graphics, - getFont()->getWidth(mText.substr(0, mCaretPosition)) - - mXScroll); + getFont()->getWidth(mText.substr(0, mCaretPosition)) - + mXScroll); } - graphics->setColor(getForegroundColor()); + graphics->setColor(guiPalette->getColor(Palette::TEXT)); graphics->setFont(getFont()); graphics->drawText(mText, 1 - mXScroll, 1); @@ -97,9 +102,7 @@ void TextField::draw(gcn::Graphics *graphics) { mAlpha = config.getValue("guialpha", 0.8); for (int a = 0; a < 9; a++) - { skin.grid[a]->setAlpha(mAlpha); - } } } @@ -117,9 +120,8 @@ void TextField::setNumeric(bool numeric) { mNumeric = numeric; if (!numeric) - { return; - } + const char *text = mText.c_str(); for (const char *textPtr = text; *textPtr; ++textPtr) { @@ -134,18 +136,15 @@ void TextField::setNumeric(bool numeric) int TextField::getValue() const { if (!mNumeric) - { return 0; - } + int value = atoi(mText.c_str()); if (value < mMinimum) - { return mMinimum; - } + if (value > mMaximum) - { return mMaximum; - } + return value; } diff --git a/src/gui/textrenderer.h b/src/gui/textrenderer.h new file mode 100644 index 00000000..b69e72a7 --- /dev/null +++ b/src/gui/textrenderer.h @@ -0,0 +1,81 @@ +/* + * Text Renderer + * 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 TEXT_RENDERER_H +#define TEXT_RENDERER_H + +#include "graphics.h" +#include "palette.h" + +/** + * Class for text rendering. Used by the TextParticle, the Text and FlashText + * objects and the Preview in the color dialog. + */ +class TextRenderer +{ + public: + /** + * Renders a specified text. + */ + static inline void renderText(gcn::Graphics *graphics, const std::string& + text, int x, int y, gcn::Graphics::Alignment align, + const gcn::Color color, gcn::Font *font, bool outline = false, + bool shadow = false, int alpha = 255) + { + graphics->setFont(font); + + // Text shadow + if (shadow) + { + graphics->setColor(guiPalette->getColor(Palette::SHADOW, + alpha / 2)); + if (outline) + { + graphics->drawText(text, x + 2, y + 2, align); + } + else + { + graphics->drawText(text, x + 1, y + 1, align); + } + } + + if (outline) { +/* graphics->setColor(guiPalette->getColor(Palette::OUTLINE, + alpha/4)); + // TODO: Reanable when we can draw it nicely in software mode + graphics->drawText(text, x + 2, y + 2, align); + graphics->drawText(text, x + 1, y + 2, align); + graphics->drawText(text, x + 2, y + 1, align);*/ + + // Text outline + graphics->setColor(guiPalette->getColor(Palette::OUTLINE, alpha)); + graphics->drawText(text, x + 1, y, align); + graphics->drawText(text, x - 1, y, align); + graphics->drawText(text, x, y + 1, align); + graphics->drawText(text, x, y - 1, align); + } + + graphics->setColor(color); + graphics->drawText(text, x, y, align); + } +}; + +#endif diff --git a/src/gui/trade.cpp b/src/gui/trade.cpp index 9910f4ce..5be71a6f 100644 --- a/src/gui/trade.cpp +++ b/src/gui/trade.cpp @@ -22,13 +22,13 @@ #include <sstream> #include <guichan/font.hpp> -#include <guichan/widgets/label.hpp> #include "button.h" #include "chat.h" #include "inventorywindow.h" #include "item_amount.h" #include "itemcontainer.h" +#include "label.h" #include "scrollarea.h" #include "textfield.h" #include "trade.h" @@ -59,20 +59,29 @@ TradeWindow::TradeWindow(Network *network): Window(_("Trade: You")), #ifdef EATHENA_SUPPORT mNetwork(network), -#endif + mMyInventory(new Inventory(INVENTORY_SIZE, 2)), + mPartnerInventory(new Inventory(INVENTORY_SIZE, 2)) +#else mMyInventory(new Inventory(INVENTORY_SIZE)), - mPartnerInventory(new Inventory(INVENTORY_SIZE)) -#ifdef TMWSERV_SUPPORT - , mStatus(PREPARING) + mPartnerInventory(new Inventory(INVENTORY_SIZE)), + mStatus(PREPARING) #endif { setWindowName("Trade"); setResizable(true); - setDefaultSize(115, 197, 332, 209); + setCloseButton(true); + setDefaultSize(342, 209, ImageRect::CENTER); + + setMinWidth(342); + setMinHeight(209); + + std::string longestName = getFont()->getWidth(_("OK")) > + getFont()->getWidth(_("Trade")) ? + _("OK") : _("Trade"); Button *mAddButton = new Button(_("Add"), "add", this); #ifdef EATHENA_SUPPORT - mOkButton = new Button(_("Ok"), "ok", this); + mOkButton = new Button(longestName, "ok", this); #endif Button *mCancelButton = new Button(_("Cancel"), "cancel", this); mTradeButton = new Button(_("Propose trade"), "trade", this); @@ -86,6 +95,7 @@ TradeWindow::TradeWindow(Network *network): mMyItemContainer = new ItemContainer(mMyInventory.get(), 4, 3, 2); #endif mMyItemContainer->addSelectionListener(this); + ScrollArea *mMyScroll = new ScrollArea(mMyItemContainer); #ifdef TMWSERV_SUPPORT @@ -94,10 +104,12 @@ TradeWindow::TradeWindow(Network *network): mPartnerItemContainer = new ItemContainer(mPartnerInventory.get(), 4, 3, 2); #endif mPartnerItemContainer->addSelectionListener(this); + ScrollArea *mPartnerScroll = new ScrollArea(mPartnerItemContainer); - mMoneyLabel = new gcn::Label(strprintf(_("You get %d GP."), 0)); - gcn::Label *mMoneyLabel2 = new gcn::Label(_("You give:")); + mMoneyLabel = new Label(strprintf(_("You get %s."), "")); + gcn::Label *mMoneyLabel2 = new Label(_("You give:")); + mMoneyField = new TextField; mMoneyField->setWidth(40); Button *mMoneyChange = new Button(_("Change"), "money", this); @@ -114,9 +126,10 @@ TradeWindow::TradeWindow(Network *network): place(0, 0, mAddButton); #ifdef EATHENA_SUPPORT place(1, 0, mOkButton); -#endif +#else place(2, 0, mTradeButton); place(3, 0, mCancelButton); +#endif Layout &layout = getLayout(); layout.extend(0, 2, 2, 1); layout.setRowHeight(1, Layout::AUTO_SET); @@ -124,6 +137,10 @@ TradeWindow::TradeWindow(Network *network): layout.setColWidth(0, Layout::AUTO_SET); layout.setColWidth(1, Layout::AUTO_SET); +#ifdef EATHENA_SUPPORT + mOkButton->setCaption(_("OK")); +#endif + loadWindowState(); } @@ -184,7 +201,8 @@ void TradeWindow::reset() mMyInventory->clear(); mPartnerInventory->clear(); #ifdef EATHENA_SUPPORT - mTradeButton->setEnabled(false); + mOkButton->setCaption(_("OK")); + mOkButton->setActionEventId("ok"); mOkButton->setEnabled(true); mOkOther = false; mOkMe = false; @@ -206,11 +224,6 @@ void TradeWindow::receivedOk() #else -void TradeWindow::setTradeButton(bool enabled) -{ - mTradeButton->setEnabled(enabled); -} - void TradeWindow::receivedOk(bool own) { if (own) @@ -218,13 +231,8 @@ void TradeWindow::receivedOk(bool own) mOkMe = true; if (mOkOther) { - mTradeButton->setEnabled(true); - mOkButton->setEnabled(false); - } - else - { - mTradeButton->setEnabled(false); - mOkButton->setEnabled(false); + mOkButton->setCaption(_("Trade")); + mOkButton->setActionEventId("trade"); } } else @@ -232,13 +240,8 @@ void TradeWindow::receivedOk(bool own) mOkOther = true; if (mOkMe) { - mTradeButton->setEnabled(true); - mOkButton->setEnabled(false); - } - else - { - mTradeButton->setEnabled(false); - mOkButton->setEnabled(true); + mOkButton->setCaption(_("Trade")); + mOkButton->setActionEventId("trade"); } } } @@ -252,6 +255,10 @@ void TradeWindow::tradeItem(Item *item, int quantity) addItem(item->getId(), true, quantity); item->increaseQuantity(-quantity); #else + // TODO: Our newer version of eAthena doesn't register this following + // function. Detect the actual server version, and re-enable this + // for that version only. + //addItem(item->getId(), true, quantity, item->isEquipment()); MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_ITEM_ADD_REQUEST); outMsg.writeInt16(item->getInvIndex()); @@ -291,15 +298,19 @@ void TradeWindow::action(const gcn::ActionEvent &event) if (event.getId() == "add") { + if (!inventoryWindow->isVisible()) return; + if (!item) return; if (mMyInventory->getFreeSlot() < 1) return; - if (mMyInventory->contains(item)) { - chatWindow->chatLog("Failed adding item. You can not " - "overlap one kind of item on the window.", BY_SERVER); + if (mMyInventory->contains(item)) + { + chatWindow->chatLog(_("Failed adding item. You can not " + "overlap one kind of item on the window."), + BY_SERVER); return; } @@ -372,3 +383,13 @@ void TradeWindow::action(const gcn::ActionEvent &event) } #endif } + +void TradeWindow::close() +{ +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::acceptTrade(false); +#else + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_TRADE_CANCEL_REQUEST); +#endif +} diff --git a/src/gui/trade.h b/src/gui/trade.h index bde0481c..4c215ba6 100644 --- a/src/gui/trade.h +++ b/src/gui/trade.h @@ -91,11 +91,6 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener * Increase quantity of an item. */ void increaseQuantity(int index, bool own, int quantity); - - /** - * Set trade Button disabled - */ - void setTradeButton(bool enabled); #endif /** @@ -123,6 +118,12 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener */ void action(const gcn::ActionEvent &event); + /** + * Closes the Trade Window, as well as telling the server that the + * window has been closed. + */ + void close(); + private: #ifdef TMWSERV_SUPPORT enum Status @@ -152,6 +153,7 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener gcn::Label *mMoneyLabel; gcn::Button *mTradeButton; #ifdef EATHENA_SUPPORT + gcn::Button *mAddButton; gcn::Button *mOkButton; #endif gcn::TextField *mMoneyField; diff --git a/src/gui/truetypefont.cpp b/src/gui/truetypefont.cpp index 7c72e2f5..b4b839e9 100644 --- a/src/gui/truetypefont.cpp +++ b/src/gui/truetypefont.cpp @@ -76,7 +76,7 @@ typedef std::list<TextChunk>::iterator CacheIterator; static int fontCounter; -TrueTypeFont::TrueTypeFont(const std::string &filename, int size) +TrueTypeFont::TrueTypeFont(const std::string &filename, int size, int style) { if (fontCounter == 0 && TTF_Init() == -1) { @@ -86,6 +86,7 @@ TrueTypeFont::TrueTypeFont(const std::string &filename, int size) ++fontCounter; mFont = TTF_OpenFont(filename.c_str(), size); + TTF_SetFontStyle (mFont, style); if (!mFont) { diff --git a/src/gui/truetypefont.h b/src/gui/truetypefont.h index 71b45fd1..1cf6c2c8 100644 --- a/src/gui/truetypefont.h +++ b/src/gui/truetypefont.h @@ -26,8 +26,8 @@ #include <string> #include <guichan/font.hpp> -#ifndef __APPLE__ -#include <SDL/SDL_ttf.h> +#ifdef __APPLE__ +#include <SDL_ttf/SDL_ttf.h> #else #include <SDL_ttf.h> #endif @@ -48,7 +48,7 @@ class TrueTypeFont : public gcn::Font * @param filename Font filename. * @param size Font size. */ - TrueTypeFont(const std::string &filename, int size); + TrueTypeFont(const std::string &filename, int size, int style = 0); /** * Destructor. diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index ca41dbda..8c903c28 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -24,13 +24,11 @@ #include <SDL_thread.h> #include <zlib.h> -#include <guichan/widgets/label.hpp> - -// Curl should be included after Guichan to avoid Windows redefinitions #include <curl/curl.h> #include "browserbox.h" #include "button.h" +#include "label.h" #include "progressbar.h" #include "scrollarea.h" #include "updatewindow.h" @@ -109,7 +107,7 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, mBrowserBox = new BrowserBox; mScrollArea = new ScrollArea(mBrowserBox); - mLabel = new gcn::Label(_("Connecting...")); + mLabel = new Label(_("Connecting...")); mProgressBar = new ProgressBar(0.0, 310, 20, 168, 116, 31); mCancelButton = new Button(_("Cancel"), "cancel", this); mPlayButton = new Button(_("Play"), "play", this); @@ -131,7 +129,7 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, Layout &layout = getLayout(); layout.setRowHeight(0, Layout::AUTO_SET); - setLocationRelativeTo(getParent()); + center(); setVisible(true); mCancelButton->requestFocus(); diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index cbd1f3f7..c840e456 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -44,6 +44,8 @@ extern volatile int tick_time; Viewport::Viewport(): mMap(0), + mMouseX(0), + mMouseY(0), mPixelViewX(0.0f), mPixelViewY(0.0f), mTileViewX(0), @@ -132,19 +134,23 @@ void Viewport::draw(gcn::Graphics *gcnGraphics) { if (player_x > mPixelViewX + mScrollRadius) { - mPixelViewX += (player_x - mPixelViewX - mScrollRadius) / mScrollLaziness; + mPixelViewX += (player_x - mPixelViewX - mScrollRadius) / + mScrollLaziness; } if (player_x < mPixelViewX - mScrollRadius) { - mPixelViewX += (player_x - mPixelViewX + mScrollRadius) / mScrollLaziness; + mPixelViewX += (player_x - mPixelViewX + mScrollRadius) / + mScrollLaziness; } if (player_y > mPixelViewY + mScrollRadius) { - mPixelViewY += (player_y - mPixelViewY - mScrollRadius) / mScrollLaziness; + mPixelViewY += (player_y - mPixelViewY - mScrollRadius) / + mScrollLaziness; } if (player_y < mPixelViewY - mScrollRadius) { - mPixelViewY += (player_y - mPixelViewY + mScrollRadius) / mScrollLaziness; + mPixelViewY += (player_y - mPixelViewY + mScrollRadius) / + mScrollLaziness; } lastTick++; } @@ -167,18 +173,14 @@ void Viewport::draw(gcn::Graphics *gcnGraphics) mMap->getHeight() * mMap->getTileHeight() - graphics->getHeight(); if (mMap) { - if (mPixelViewX < 0) { + if (mPixelViewX < 0) mPixelViewX = 0; - } - if (mPixelViewY < 0) { + if (mPixelViewY < 0) mPixelViewY = 0; - } - if (mPixelViewX > viewXmax) { + if (mPixelViewX > viewXmax) mPixelViewX = viewXmax; - } - if (mPixelViewY > viewYmax) { + if (mPixelViewY > viewYmax) mPixelViewY = viewYmax; - } } mTileViewX = (int) (mPixelViewX + 16) / 32; @@ -205,7 +207,6 @@ void Viewport::draw(gcn::Graphics *gcnGraphics) player_node->setName(player_node->getName()); } - // Draw text if (textManager) { @@ -216,8 +217,8 @@ void Viewport::draw(gcn::Graphics *gcnGraphics) Beings &beings = beingManager->getAll(); for (BeingIterator i = beings.begin(); i != beings.end(); i++) { - (*i)->drawSpeech(-(int) mPixelViewX, -(int) mPixelViewY); - (*i)->drawEmotion(graphics, -(int) mPixelViewX, -(int) mPixelViewY); + (*i)->drawSpeech((int) mPixelViewX, (int) mPixelViewY); + (*i)->drawEmotion(graphics, (int) mPixelViewX, (int) mPixelViewY); } if (miniStatusWindow) @@ -234,21 +235,20 @@ void Viewport::logic() if (!mMap || !player_node) return; - int mouseX, mouseY; - Uint8 button = SDL_GetMouseState(&mouseX, &mouseY); + Uint8 button = SDL_GetMouseState(&mMouseX, &mMouseY); if (mPlayerFollowMouse && button & SDL_BUTTON(1) && #ifdef TMWSERV_SUPPORT get_elapsed_time(mLocalWalkTime) >= walkingMouseDelay) { mLocalWalkTime = tick_time; - player_node->setDestination(mouseX + (int) mPixelViewX, - mouseY + (int) mPixelViewY); + player_node->setDestination(mMouseX + (int) mPixelViewX, + mMouseY + (int) mPixelViewY); #else mWalkTime != player_node->mWalkTime) { - player_node->setDestination(mouseX / 32 + mTileViewX, - mouseY / 32 + mTileViewY); + player_node->setDestination(mMouseX / 32 + mTileViewX, + mMouseY / 32 + mTileViewY); mWalkTime = player_node->mWalkTime; #endif } @@ -257,11 +257,10 @@ void Viewport::logic() void Viewport::drawDebugPath(Graphics *graphics) { // Get the current mouse position - int mouseX, mouseY; - SDL_GetMouseState(&mouseX, &mouseY); + SDL_GetMouseState(&mMouseX, &mMouseY); - const int mouseTileX = (mouseX + (int) mPixelViewX) / 32; - const int mouseTileY = (mouseY + (int) mPixelViewY) / 32; + const int mouseTileX = (mMouseX + (int) mPixelViewX) / 32; + const int mouseTileY = (mMouseY + (int) mPixelViewY) / 32; const Vector &playerPos = player_node->getPosition(); Path debugPath = mMap->findPath( @@ -353,10 +352,12 @@ void Viewport::mousePressed(gcn::MouseEvent &event) if (being->mAction == Being::DEAD) break; - if (player_node->withinAttackRange(being) || keyboard.isKeyActive(keyboard.KEY_ATTACK)) + if (player_node->withinAttackRange(being) || + keyboard.isKeyActive(keyboard.KEY_ATTACK)) { player_node->setGotoTarget(being); - player_node->attack(being, !keyboard.isKeyActive(keyboard.KEY_TARGET)); + player_node->attack(being, + !keyboard.isKeyActive(keyboard.KEY_TARGET)); } else { @@ -403,9 +404,7 @@ void Viewport::mousePressed(gcn::MouseEvent &event) 20, Being::MONSTER); if (target) - { - player_node->setTarget(target); - } + player_node->setTarget(target); } } diff --git a/src/gui/viewport.h b/src/gui/viewport.h index a097a4ac..c051e5a2 100644 --- a/src/gui/viewport.h +++ b/src/gui/viewport.h @@ -124,6 +124,16 @@ class Viewport : public WindowContainer, public gcn::MouseListener, int getCameraY() const { return (int) mPixelViewY; } /** + * Returns mouse x in pixels. + */ + int getMouseX() const { return mMouseX; } + + /** + * Returns mouse y in pixels. + */ + int getMouseY() const { return mMouseY; } + + /** * Changes viewpoint by relative pixel coordinates. */ void scrollBy(float x, float y) { mPixelViewX += x; mPixelViewY += y; } @@ -140,18 +150,20 @@ class Viewport : public WindowContainer, public gcn::MouseListener, */ void drawPath(Graphics *graphics, const Path &path); - Map *mMap; /**< The current map. */ + Map *mMap; /**< The current map. */ int mScrollRadius; int mScrollLaziness; int mScrollCenterOffsetX; int mScrollCenterOffsetY; - float mPixelViewX; /**< Current viewpoint in pixels. */ - float mPixelViewY; /**< Current viewpoint in pixels. */ + int mMouseX; /**< Current mouse position in pixels. */ + int mMouseY; /**< Current mouse position in pixels. */ + float mPixelViewX; /**< Current viewpoint in pixels. */ + float mPixelViewY; /**< Current viewpoint in pixels. */ int mTileViewX; /**< Current viewpoint in tiles. */ int mTileViewY; /**< Current viewpoint in tiles. */ - bool mShowDebugPath; /**< Show a path from player to pointer. */ - bool mVisibleNames; /**< Show target names. */ + bool mShowDebugPath; /**< Show a path from player to pointer. */ + bool mVisibleNames; /**< Show target names. */ bool mPlayerFollowMouse; #ifdef TMWSERV_SUPPORT @@ -160,11 +172,10 @@ class Viewport : public WindowContainer, public gcn::MouseListener, int mWalkTime; #endif - PopupMenu *mPopupMenu; /**< Popup menu. */ - Being *mSelectedBeing; /**< Current selected being. */ - + PopupMenu *mPopupMenu; /**< Popup menu. */ + Being *mSelectedBeing; /**< Current selected being. */ }; -extern Viewport *viewport; /**< The viewport */ +extern Viewport *viewport; /**< The viewport */ #endif diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp index 92837603..b736591c 100644 --- a/src/gui/widgets/dropdown.cpp +++ b/src/gui/widgets/dropdown.cpp @@ -23,8 +23,8 @@ #include "dropdown.h" -#include "../color.h" #include "../listbox.h" +#include "../palette.h" #include "../scrollarea.h" #include "../../configuration.h" @@ -74,12 +74,15 @@ DropDown::DropDown(gcn::ListModel *listModel, gcn::ScrollArea *scrollArea, int gridy[4] = {0, 3, 28, 31}; int a = 0, x, y; - for (y = 0; y < 3; y++) { - for (x = 0; x < 3; x++) { - skin.grid[a] = boxBorder->getSubImage( - gridx[x], gridy[y], - gridx[x + 1] - gridx[x] + 1, - gridy[y + 1] - gridy[y] + 1); + for (y = 0; y < 3; y++) + { + for (x = 0; x < 3; x++) + { + skin.grid[a] = boxBorder->getSubImage(gridx[x], gridy[y], + gridx[x + 1] - + gridx[x] + 1, + gridy[y + 1] - + gridy[y] + 1); skin.grid[a]->setAlpha(mAlpha); a++; } @@ -104,20 +107,22 @@ DropDown::~DropDown() for_each(skin.grid, skin.grid + 9, dtor<Image*>()); } + + gcn::ListModel *listModel = getListModel(); + if (listModel) delete listModel; } void DropDown::draw(gcn::Graphics* graphics) { + if (!isVisible()) + return; + int h; if (mDroppedDown) - { h = mFoldedUpHeight; - } else - { h = getHeight(); - } if (config.getValue("guialpha", 0.8) != mAlpha) { @@ -134,27 +139,20 @@ void DropDown::draw(gcn::Graphics* graphics) } } - bool valid; const int alpha = (int)(mAlpha * 255.0f); gcn::Color faceColor = getBaseColor(); faceColor.a = alpha; - gcn::Color highlightColor = textColor->getColor('H', valid); - highlightColor.a = alpha; + const gcn::Color* highlightColor = &guiPalette->getColor(Palette::HIGHLIGHT, + alpha); gcn::Color shadowColor = faceColor - 0x303030; shadowColor.a = alpha; if (mOpaque) { - int red = getBackgroundColor().r; - int green = getBackgroundColor().g; - int blue = getBackgroundColor().b; - graphics->setColor(gcn::Color(red, green, blue, alpha)); + graphics->setColor(guiPalette->getColor(Palette::BACKGROUND, alpha)); graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), h)); - red = getForegroundColor().r; - green = getForegroundColor().g; - blue = getForegroundColor().b; - graphics->setColor(gcn::Color(red, green, blue, alpha)); + graphics->setColor(guiPalette->getColor(Palette::TEXT, alpha)); } graphics->setFont(getFont()); @@ -166,7 +164,7 @@ void DropDown::draw(gcn::Graphics* graphics) if (isFocused()) { - graphics->setColor(highlightColor); + graphics->setColor(*highlightColor); graphics->drawRectangle(gcn::Rectangle(0, 0, getWidth() - h, h)); } @@ -178,7 +176,7 @@ void DropDown::draw(gcn::Graphics* graphics) // Draw two lines separating the ListBox with selected // element view. - graphics->setColor(highlightColor); + graphics->setColor(*highlightColor); graphics->drawLine(0, h, getWidth(), h); graphics->setColor(shadowColor); graphics->drawLine(0, h + 1, getWidth(), h + 1); diff --git a/src/gui/widgets/tab.cpp b/src/gui/widgets/tab.cpp index 21402c89..7a2d9ee8 100644 --- a/src/gui/widgets/tab.cpp +++ b/src/gui/widgets/tab.cpp @@ -24,6 +24,8 @@ #include "tab.h" #include "tabbedarea.h" +#include "../palette.h" + #include "../../configuration.h" #include "../../graphics.h" @@ -35,7 +37,7 @@ int Tab::mInstances = 0; float Tab::mAlpha = config.getValue("guialpha", 0.8); -enum{ +enum { TAB_STANDARD, // 0 TAB_HIGHLIGHTED, // 1 TAB_SELECTED, // 2 @@ -94,8 +96,10 @@ void Tab::init() { tab[mode] = resman->getImage(data[mode].file); a = 0; - for (y = 0; y < 3; y++) { - for (x = 0; x < 3; x++) { + for (y = 0; y < 3; y++) + { + for (x = 0; x < 3; x++) + { tabImg[mode].grid[a] = tab[mode]->getSubImage( data[x].gridX, data[y].gridY, data[x + 1].gridX - data[x].gridX + 1, @@ -121,13 +125,17 @@ void Tab::draw(gcn::Graphics *graphics) { mode = TAB_SELECTED; // if tab is selected, it doesnt need to highlight activity - mLabel->setForegroundColor(gcn::Color(0, 0, 0)); + mLabel->setForegroundColor(guiPalette->getColor(Palette::TEXT)); mHighlighted = false; } else if (mHighlighted) { mode = TAB_HIGHLIGHTED; - mLabel->setForegroundColor(gcn::Color(255, 0, 0)); + mLabel->setForegroundColor(guiPalette->getColor(Palette::TAB_HIGHLIGHT)); + } + else + { + mLabel->setForegroundColor(guiPalette->getColor(Palette::TEXT)); } } diff --git a/src/gui/widgets/tabbedarea.cpp b/src/gui/widgets/tabbedarea.cpp index ce11fe69..a8f2b6f0 100644 --- a/src/gui/widgets/tabbedarea.cpp +++ b/src/gui/widgets/tabbedarea.cpp @@ -40,9 +40,8 @@ Tab* TabbedArea::getTab(const std::string &name) while (itr != itr_end) { if ((*itr).first->getCaption() == name) - { return static_cast<Tab*>((*itr).first); - } + ++itr; } return NULL; @@ -51,9 +50,7 @@ Tab* TabbedArea::getTab(const std::string &name) void TabbedArea::draw(gcn::Graphics *graphics) { if (mTabs.empty()) - { return; - } drawChildren(graphics); } @@ -64,9 +61,8 @@ gcn::Widget* TabbedArea::getWidget(const std::string &name) while (itr != itr_end) { if ((*itr).first->getCaption() == name) - { return (*itr).second; - } + ++itr; } @@ -91,9 +87,7 @@ void TabbedArea::addTab(Tab *tab, gcn::Widget *widget) mTabs.push_back(std::pair<Tab*, gcn::Widget*>(tab, widget)); if (!mSelectedTab) - { setSelectedTab(tab); - } adjustTabPositions(); adjustSize(); @@ -107,15 +101,10 @@ void TabbedArea::removeTab(Tab *tab) { int index = getSelectedTabIndex(); - if (index == (int)mTabs.size() - 1 - && mTabs.size() == 1) - { + if (index == (int)mTabs.size() - 1 && mTabs.size() == 1) tabIndexToBeSelected = -1; - } else - { tabIndexToBeSelected = index - 1; - } } TabContainer::iterator iter; diff --git a/src/gui/widgets/textpreview.cpp b/src/gui/widgets/textpreview.cpp new file mode 100644 index 00000000..01790a67 --- /dev/null +++ b/src/gui/widgets/textpreview.cpp @@ -0,0 +1,81 @@ +/* + * The Mana World + * Copyright (C) 2006 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 <typeinfo> + +#include "textpreview.h" + +#include "../gui.h" +#include "../palette.h" +#include "../textrenderer.h" +#include "../truetypefont.h" + +#include "../../configuration.h" + +float TextPreview::mAlpha = config.getValue("guialpha", 0.8); + +TextPreview::TextPreview(const std::string* text) +{ + mText = text; + mTextAlpha = false; + mFont = gui->getFont(); + mTextColor = &guiPalette->getColor(Palette::TEXT); + mTextBGColor = NULL; + mBGColor = &guiPalette->getColor(Palette::BACKGROUND); + mOpaque = false; +} + +void TextPreview::draw(gcn::Graphics* graphics) +{ + if (config.getValue("guialpha", 0.8) != mAlpha) + mAlpha = config.getValue("guialpha", 0.8); + + int alpha = (int) (mAlpha * 255.0f); + + if (!mTextAlpha) + alpha = 255; + + if (mOpaque) + { + graphics->setColor(gcn::Color((int) mBGColor->r, + (int) mBGColor->g, + (int) mBGColor->b, + (int)(mAlpha * 255.0f))); + graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight())); + } + + if (mTextBGColor && typeid(*mFont) == typeid(TrueTypeFont)) + { + TrueTypeFont *font = static_cast<TrueTypeFont*>(mFont); + int x = font->getWidth(*mText) + 1 + 2 * ((mOutline || mShadow) ? 1 :0); + int y = font->getHeight() + 1 + 2 * ((mOutline || mShadow) ? 1 : 0); + graphics->setColor(gcn::Color((int) mTextBGColor->r, + (int) mTextBGColor->g, + (int) mTextBGColor->b, + (int)(mAlpha * 255.0f))); + graphics->fillRectangle(gcn::Rectangle(1, 1, x, y)); + } + + TextRenderer::renderText(graphics, *mText, 2, 2, gcn::Graphics::LEFT, + gcn::Color(mTextColor->r, mTextColor->g, + mTextColor->b, alpha), + mFont, mOutline, mShadow, alpha); +} diff --git a/src/gui/widgets/textpreview.h b/src/gui/widgets/textpreview.h new file mode 100644 index 00000000..e7b7db80 --- /dev/null +++ b/src/gui/widgets/textpreview.h @@ -0,0 +1,142 @@ +/* + * The Mana World + * Copyright (C) 2006 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 TEXTPREVIEW_H +#define TEXTPREVIEW_H + +#include <guichan/color.hpp> +#include <guichan/font.hpp> +#include <guichan/widget.hpp> + +/** + * Preview widget for particle colors, etc. + */ +class TextPreview : public gcn::Widget +{ + public: + TextPreview(const std::string* text); + + /** + * Sets the color the text is printed in. + * + * @param color the color to set + */ + inline void setTextColor(const gcn::Color* color) + { + mTextColor = color; + } + + /** + * Sets the text to use the set alpha value. + * + * @param alpha whether to use alpha values for the text or not + */ + inline void useTextAlpha(bool alpha) + { + mTextAlpha = alpha; + } + + /** + * Sets the color the text background is drawn in. This is only the + * rectangle directly behind the text, not to full widget. + * + * @param color the color to set + */ + inline void setTextBGColor(const gcn::Color* color) + { + mTextBGColor = color; + } + + /** + * Sets the background color of the widget. + * + * @param color the color to set + */ + inline void setBGColor(const gcn::Color* color) + { + mBGColor = color; + } + + /** + * Sets the font to render the text in. + * + * @param font the font to use. + */ + inline void setFont(gcn::Font *font) + { + mFont = font; + } + + /** + * Sets whether to use a shadow while rendering. + * + * @param shadow true, if a shadow is wanted, false else + */ + inline void setShadow(bool shadow) + { + mShadow = shadow; + } + + /** + * Sets whether to use an outline while rendering. + * + * @param outline true, if an outline is wanted, false else + */ + inline void setOutline(bool outline) + { + mOutline = outline; + } + + /** + * Widget's draw method. Does the actual job. + * + * @param graphics graphics to draw into + */ + void draw(gcn::Graphics *graphics); + + /** + * Set opacity for this widget (whether or not to show the background + * color) + * + * @param opaque Whether the widget should be opaque or not + */ + void setOpaque(bool opaque) { mOpaque = opaque; } + + /** + * Gets opacity for this widget (whether or not the background color + * is shown below the widget) + */ + bool isOpaque() { return mOpaque; } + + private: + gcn::Font *mFont; + const std::string* mText; + const gcn::Color* mTextColor; + const gcn::Color* mBGColor; + const gcn::Color* mTextBGColor; + static float mAlpha; + bool mTextAlpha; + bool mOpaque; + bool mShadow; + bool mOutline; +}; + +#endif diff --git a/src/gui/window.cpp b/src/gui/window.cpp index 58439316..4689c86a 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -26,6 +26,8 @@ #include <guichan/exception.hpp> #include "gui.h" +#include "palette.h" +#include "skin.h" #include "window.h" #include "windowcontainer.h" @@ -37,16 +39,10 @@ #include "../log.h" #include "../resources/image.h" -#include "../resources/resourcemanager.h" - -#include "../utils/xml.h" ConfigListener *Window::windowConfigListener = 0; -WindowContainer *Window::windowContainer = 0; int Window::instances = 0; int Window::mouseResize = 0; -//ImageRect Window::border; -Image *Window::closeImage = NULL; bool Window::mAlphaChanged = false; class WindowConfigListener : public ConfigListener @@ -70,23 +66,16 @@ Window::Window(const std::string& caption, bool modal, Window *parent, const std mMinWinWidth(100), mMinWinHeight(40), mMaxWinWidth(INT_MAX), - mMaxWinHeight(INT_MAX), - mSkin(skin) + mMaxWinHeight(INT_MAX) { logger->log("Window::Window(\"%s\")", caption.c_str()); if (!windowContainer) - { throw GCN_EXCEPTION("Window::Window(): no windowContainer set"); - } - - // Loads the skin - loadSkin(mSkin); - - setGuiAlpha(); if (instances == 0) { + skinLoader = new SkinLoader(); windowConfigListener = new WindowConfigListener; // Send GUI alpha changed for initialization windowConfigListener->optionChanged("guialpha"); @@ -99,6 +88,11 @@ Window::Window(const std::string& caption, bool modal, Window *parent, const std setPadding(3); setTitleBarHeight(20); + // Loads the skin + mSkin = skinLoader->load(skin); + + setGuiAlpha(); + // Add this window to the window container windowContainer->add(this); @@ -117,21 +111,8 @@ Window::Window(const std::string& caption, bool modal, Window *parent, const std Window::~Window() { logger->log("Window::~Window(\"%s\")", getCaption().c_str()); - const std::string &name = mWindowName; - // Saving X, Y and Width and Height for resizables in the config - if (!name.empty()) - { - config.setValue(name + "WinX", getX()); - config.setValue(name + "WinY", getY()); - config.setValue(name + "Visible", isVisible()); - - if (mGrip) - { - config.setValue(name + "WinWidth", getWidth()); - config.setValue(name + "WinHeight", getHeight()); - } - } + saveWindowState(); delete mLayout; @@ -142,22 +123,18 @@ Window::~Window() delete(w); } + removeWidgetListener(this); + instances--; - // Clean up static resources - for (int i = 0; i < 9; i++) - { - delete border.grid[i]; - border.grid[i] = NULL; - } + mSkin->instances--; if (instances == 0) { + delete skinLoader; config.removeListener("guialpha", windowConfigListener); delete windowConfigListener; windowConfigListener = NULL; - - closeImage->decRef(); } } @@ -168,14 +145,17 @@ void Window::setWindowContainer(WindowContainer *wc) void Window::draw(gcn::Graphics *graphics) { + if (!isVisible()) + return; + Graphics *g = static_cast<Graphics*>(graphics); - g->drawImageRect(0, 0, getWidth(), getHeight(), border); + g->drawImageRect(0, 0, getWidth(), getHeight(), mSkin->getBorder()); // Draw title if (mShowTitle) { - g->setColor(gcn::Color(0, 0, 0)); + g->setColor(guiPalette->getColor(Palette::TEXT)); g->setFont(getFont()); g->drawText(getCaption(), 7, 5, gcn::Graphics::LEFT); } @@ -183,8 +163,8 @@ void Window::draw(gcn::Graphics *graphics) // Draw Close Button if (mCloseButton) { - g->drawImage(closeImage, - getWidth() - closeImage->getWidth() - getPadding(), + g->drawImage(mSkin->getCloseImage(), + getWidth() - mSkin->getCloseImage()->getWidth() - getPadding(), getPadding() ); } @@ -192,18 +172,29 @@ void Window::draw(gcn::Graphics *graphics) // Update window alpha values if (mAlphaChanged) { - for_each(border.grid, border.grid + 9, + for_each(mSkin->getBorder().grid, mSkin->getBorder().grid + 9, std::bind2nd(std::mem_fun(&Image::setAlpha), config.getValue("guialpha", 0.8))); - closeImage->setAlpha(config.getValue("guialpha", 0.8)); + mSkin->getCloseImage()->setAlpha(config.getValue("guialpha", 0.8)); } drawChildren(graphics); } void Window::setContentSize(int width, int height) { - setSize(width + 2 * getPadding(), - height + getPadding() + getTitleBarHeight()); + width = width + 2 * getPadding(); + height = height + getPadding() + getTitleBarHeight(); + + if (getMinWidth() > width) + width = getMinWidth(); + else if (getMaxWidth() < width) + width = getMaxWidth(); + if (getMinHeight() > height) + height = getMinHeight(); + else if (getMaxHeight() < height) + height = getMaxHeight(); + + setSize(width, height); } void Window::setLocationRelativeTo(gcn::Widget *widget) @@ -218,14 +209,61 @@ void Window::setLocationRelativeTo(gcn::Widget *widget) getY() + (wy + (widget->getHeight() - getHeight()) / 2 - y)); } +void Window::setLocationRelativeTo(ImageRect::ImagePosition position, + int offsetX, int offsetY) +{ + if (position == ImageRect::UPPER_LEFT) + { + } + else if (position == ImageRect::UPPER_CENTER) + { + offsetX += (graphics->getWidth() - getWidth()) / 2; + } + else if (position == ImageRect::UPPER_RIGHT) + { + offsetX += graphics->getWidth() - getWidth(); + } + else if (position == ImageRect::LEFT) + { + offsetY += (graphics->getHeight() - getHeight()) / 2; + } + else if (position == ImageRect::CENTER) + { + offsetX += (graphics->getWidth() - getWidth()) / 2; + offsetY += (graphics->getHeight() - getHeight()) / 2; + } + else if (position == ImageRect::RIGHT) + { + offsetX += graphics->getWidth() - getWidth(); + offsetY += (graphics->getHeight() - getHeight()) / 2; + } + else if (position == ImageRect::LOWER_LEFT) + { + offsetY += graphics->getHeight() - getHeight(); + } + else if (position == ImageRect::LOWER_CENTER) + { + offsetX += (graphics->getWidth() - getWidth()) / 2; + offsetY += graphics->getHeight() - getHeight(); + } + else if (position == ImageRect::LOWER_RIGHT) + { + offsetX += graphics->getWidth() - getWidth(); + offsetY += graphics->getHeight() - getHeight(); + } + + setPosition(offsetX, offsetY); +} + void Window::setMinWidth(unsigned int width) { - mMinWinWidth = width; + mMinWinWidth = width > mSkin->getMinWidth() ? width : mSkin->getMinWidth(); } void Window::setMinHeight(unsigned int height) { - mMinWinHeight = height; + mMinWinHeight = height > mSkin->getMinHeight() ? + height : mSkin->getMinHeight(); } void Window::setMaxWidth(unsigned int width) @@ -317,14 +355,14 @@ void Window::mousePressed(gcn::MouseEvent &event) if (mCloseButton) { gcn::Rectangle closeButtonRect( - getWidth() - closeImage->getWidth() - getPadding(), + getWidth() - mSkin->getCloseImage()->getWidth() - getPadding(), getPadding(), - closeImage->getWidth(), - closeImage->getHeight()); + mSkin->getCloseImage()->getWidth(), + mSkin->getCloseImage()->getHeight()); if (closeButtonRect.isPointInRect(x, y)) { - setVisible(false); + close(); } } @@ -333,6 +371,11 @@ void Window::mousePressed(gcn::MouseEvent &event) } } +void Window::close() +{ + setVisible(false); +} + void Window::mouseReleased(gcn::MouseEvent &event) { if (mGrip && mouseResize) @@ -388,8 +431,8 @@ void Window::mouseDragged(gcn::MouseEvent &event) { int newX = std::max(0, getX()); int newY = std::max(0, getY()); - newX = std::min(windowContainer->getWidth() - getWidth(), newX); - newY = std::min(windowContainer->getHeight() - getHeight(), newY); + newX = std::min(graphics->getWidth() - getWidth(), newX); + newY = std::min(graphics->getHeight() - getHeight(), newY); setPosition(newX, newY); } @@ -434,13 +477,13 @@ void Window::mouseDragged(gcn::MouseEvent &event) newDim.height += newDim.y; newDim.y = 0; } - if (newDim.x + newDim.width > windowContainer->getWidth()) + if (newDim.x + newDim.width > graphics->getWidth()) { - newDim.width = windowContainer->getWidth() - newDim.x; + newDim.width = graphics->getWidth() - newDim.x; } - if (newDim.y + newDim.height > windowContainer->getHeight()) + if (newDim.y + newDim.height > graphics->getHeight()) { - newDim.height = windowContainer->getHeight() - newDim.y; + newDim.height = graphics->getHeight() - newDim.y; } // Update mouse offset when dragging bottom or right border @@ -469,8 +512,19 @@ void Window::loadWindowState() if (mGrip) { - setSize((int) config.getValue(name + "WinWidth", mDefaultWidth), - (int) config.getValue(name + "WinHeight", mDefaultHeight)); + int width = (int) config.getValue(name + "WinWidth", mDefaultWidth); + int height = (int) config.getValue(name + "WinHeight", mDefaultHeight); + + if (getMinWidth() > width) + width = getMinWidth(); + else if (getMaxWidth() < width) + width = getMaxWidth(); + if (getMinHeight() > height) + height = getMinHeight(); + else if (getMaxHeight() < height) + height = getMaxHeight(); + + setSize(width, height); } else { @@ -478,19 +532,115 @@ void Window::loadWindowState() } } +void Window::saveWindowState() +{ + // Saving X, Y and Width and Height for resizables in the config + if (!mWindowName.empty() && mWindowName != "window") + { + config.setValue(mWindowName + "WinX", getX()); + config.setValue(mWindowName + "WinY", getY()); + config.setValue(mWindowName + "Visible", isVisible()); + + if (mGrip) + { + if (getMinWidth() > getWidth()) + setWidth(getMinWidth()); + else if (getMaxWidth() < getWidth()) + setWidth(getMaxWidth()); + if (getMinHeight() > getHeight()) + setHeight(getMinHeight()); + else if (getMaxHeight() < getHeight()) + setHeight(getMaxHeight()); + + config.setValue(mWindowName + "WinWidth", getWidth()); + config.setValue(mWindowName + "WinHeight", getHeight()); + } + } +} + void Window::setDefaultSize(int defaultX, int defaultY, int defaultWidth, int defaultHeight) { + if (getMinWidth() > defaultWidth) + defaultWidth = getMinWidth(); + else if (getMaxWidth() < defaultWidth) + defaultWidth = getMaxWidth(); + if (getMinHeight() > defaultHeight) + defaultHeight = getMinHeight(); + else if (getMaxHeight() < defaultHeight) + defaultHeight = getMaxHeight(); + mDefaultX = defaultX; mDefaultY = defaultY; mDefaultWidth = defaultWidth; mDefaultHeight = defaultHeight; } +void Window::setDefaultSize() +{ + mDefaultX = getX(); + mDefaultY = getY(); + mDefaultWidth = getWidth(); + mDefaultHeight = getHeight(); +} + +void Window::setDefaultSize(int defaultWidth, int defaultHeight, + ImageRect::ImagePosition position, + int offsetX, int offsetY) +{ + int x = 0, y = 0; + + if (position == ImageRect::UPPER_LEFT) + { + } + else if (position == ImageRect::UPPER_CENTER) + { + x = (graphics->getWidth() - defaultWidth) / 2; + } + else if (position == ImageRect::UPPER_RIGHT) + { + x = graphics->getWidth() - defaultWidth; + } + else if (position == ImageRect::LEFT) + { + y = (graphics->getHeight() - defaultHeight) / 2; + } + else if (position == ImageRect::CENTER) + { + x = (graphics->getWidth() - defaultWidth) / 2; + y = (graphics->getHeight() - defaultHeight) / 2; + } + else if (position == ImageRect::RIGHT) + { + x = graphics->getWidth() - defaultWidth; + y = (graphics->getHeight() - defaultHeight) / 2; + } + else if (position == ImageRect::LOWER_LEFT) + { + y = graphics->getHeight() - defaultHeight; + } + else if (position == ImageRect::LOWER_CENTER) + { + x = (graphics->getWidth() - defaultWidth) / 2; + y = graphics->getHeight() - defaultHeight; + } + else if (position == ImageRect::LOWER_RIGHT) + { + x = graphics->getWidth() - defaultWidth; + y = graphics->getHeight() - defaultHeight; + } + + mDefaultX = x - offsetX; + mDefaultY = y - offsetY; + mDefaultWidth = defaultWidth; + mDefaultHeight = defaultHeight; +} + void Window::resetToDefaultSize() { setPosition(mDefaultX, mDefaultY); setSize(mDefaultWidth, mDefaultHeight); + saveWindowState(); } int Window::getResizeHandles(gcn::MouseEvent &event) @@ -528,179 +678,16 @@ void Window::setGuiAlpha() for (int i = 0; i < 9; i++) { //logger->log("Window::setGuiAlpha: Border Image (%i)", i); - border.grid[i]->setAlpha(config.getValue("guialpha", 0.8)); + mSkin->getBorder().grid[i]->setAlpha(config.getValue("guialpha", 0.8)); } mAlphaChanged = false; } -void Window::loadSkin(const std::string &fileName) +int Window::getGuiAlpha() { - const std::string windowId = Window::getId(); - - ResourceManager *resman = ResourceManager::getInstance(); - - logger->log("Loading Window Skin '%s'.", fileName.c_str()); - logger->log("Loading Window ID '%s'.", windowId.c_str()); - - - if (fileName.empty()) - logger->error("Window::loadSkin(): Invalid File Name."); - - // TODO: - // If there is an error loading the specified file, we should try to revert - // to a 'default' skin file. Only if the 'default' skin file can't be loaded - // should we have a terminating error. - XML::Document doc(fileName); - xmlNodePtr rootNode = doc.rootNode(); - - if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "skinset")) - { - logger->error("Widget Skinning error"); - } - - std::string skinSetImage; - skinSetImage = XML::getProperty(rootNode, "image", ""); - Image *dBorders = NULL; - if (!skinSetImage.empty()) - { - logger->log("Window::loadSkin(): <skinset> defines '%s' as a skin image.", skinSetImage.c_str()); - dBorders = resman->getImage("graphics/gui/" + skinSetImage);//"graphics/gui/speech_bubble.png"); - } - else - { - logger->error("Window::loadSkin(): Skinset does not define an image!"); - } - - //iterate <widget>'s - for_each_xml_child_node(widgetNode, rootNode) - { - if (!xmlStrEqual(widgetNode->name, BAD_CAST "widget")) - continue; - - std::string widgetType; - widgetType = XML::getProperty(widgetNode, "type", "unknown"); - if (widgetType == "Window") - { - // Iterate through <part>'s - // LEEOR / TODO: - // We need to make provisions to load in a CloseButton image. For now it - // can just be hard-coded. - for_each_xml_child_node(partNode, widgetNode) - { - if (!xmlStrEqual(partNode->name, BAD_CAST "part")) - { - continue; - } - - std::string partType; - partType = XML::getProperty(partNode, "type", "unknown"); - // TOP ROW - if (partType == "top-left-corner") - { - const int xPos = XML::getProperty(partNode, "xpos", 0); - const int yPos = XML::getProperty(partNode, "ypos", 0); - const int width = XML::getProperty(partNode, "width", 1); - const int height = XML::getProperty(partNode, "height", 1); - - border.grid[0] = dBorders->getSubImage(xPos, yPos, width, height); - } - else if (partType == "top-edge") - { - const int xPos = XML::getProperty(partNode, "xpos", 0); - const int yPos = XML::getProperty(partNode, "ypos", 0); - const int width = XML::getProperty(partNode, "width", 1); - const int height = XML::getProperty(partNode, "height", 1); - - border.grid[1] = dBorders->getSubImage(xPos, yPos, width, height); - } - else if (partType == "top-right-corner") - { - const int xPos = XML::getProperty(partNode, "xpos", 0); - const int yPos = XML::getProperty(partNode, "ypos", 0); - const int width = XML::getProperty(partNode, "width", 1); - const int height = XML::getProperty(partNode, "height", 1); - - border.grid[2] = dBorders->getSubImage(xPos, yPos, width, height); - } - - // MIDDLE ROW - else if (partType == "left-edge") - { - const int xPos = XML::getProperty(partNode, "xpos", 0); - const int yPos = XML::getProperty(partNode, "ypos", 0); - const int width = XML::getProperty(partNode, "width", 1); - const int height = XML::getProperty(partNode, "height", 1); - - border.grid[3] = dBorders->getSubImage(xPos, yPos, width, height); - } - else if (partType == "bg-quad") - { - const int xPos = XML::getProperty(partNode, "xpos", 0); - const int yPos = XML::getProperty(partNode, "ypos", 0); - const int width = XML::getProperty(partNode, "width", 1); - const int height = XML::getProperty(partNode, "height", 1); - - border.grid[4] = dBorders->getSubImage(xPos, yPos, width, height); - } - else if (partType == "right-edge") - { - const int xPos = XML::getProperty(partNode, "xpos", 0); - const int yPos = XML::getProperty(partNode, "ypos", 0); - const int width = XML::getProperty(partNode, "width", 1); - const int height = XML::getProperty(partNode, "height", 1); - - border.grid[5] = dBorders->getSubImage(xPos, yPos, width, height); - } - - // BOTTOM ROW - else if (partType == "bottom-left-corner") - { - const int xPos = XML::getProperty(partNode, "xpos", 0); - const int yPos = XML::getProperty(partNode, "ypos", 0); - const int width = XML::getProperty(partNode, "width", 1); - const int height = XML::getProperty(partNode, "height", 1); - - border.grid[6] = dBorders->getSubImage(xPos, yPos, width, height); - } - else if (partType == "bottom-edge") - { - const int xPos = XML::getProperty(partNode, "xpos", 0); - const int yPos = XML::getProperty(partNode, "ypos", 0); - const int width = XML::getProperty(partNode, "width", 1); - const int height = XML::getProperty(partNode, "height", 1); - - border.grid[7] = dBorders->getSubImage(xPos, yPos, width, height); - } - else if (partType == "bottom-right-corner") - { - const int xPos = XML::getProperty(partNode, "xpos", 0); - const int yPos = XML::getProperty(partNode, "ypos", 0); - const int width = XML::getProperty(partNode, "width", 1); - const int height = XML::getProperty(partNode, "height", 1); - - border.grid[8] = dBorders->getSubImage(xPos, yPos, width, height); - } - - // Part is of an uknown type. - else - { - logger->log("Window::loadSkin(): Unknown Part Type '%s'", partType.c_str()); - } - } - } - // Widget is of an uknown type. - else - { - logger->log("Window::loadSkin(): Unknown Widget Type '%s'", widgetType.c_str()); - } - } - dBorders->decRef(); - - logger->log("Finished loading Window Skin."); - - // Hard-coded for now until we update the above code to look for window buttons. - closeImage = resman->getImage("graphics/gui/close_button.png"); + float alpha = config.getValue("guialpha", 0.8); + return (int) (alpha * 255.0f); } Layout &Window::getLayout() @@ -728,3 +715,8 @@ void Window::reflowLayout(int w, int h) mLayout = NULL; setContentSize(w, h); } + +void Window::center() +{ + setLocationRelativeTo(getParent()); +} diff --git a/src/gui/window.h b/src/gui/window.h index 3806342a..7f15e262 100644 --- a/src/gui/window.h +++ b/src/gui/window.h @@ -31,11 +31,11 @@ class ConfigListener; class ContainerPlacer; -class Image; -class ImageRect; class Layout; class LayoutCell; class ResizeGrip; +class Skin; +class SkinLoader; class WindowContainer; /** @@ -61,7 +61,7 @@ class Window : public gcn::Window, gcn::WidgetListener * @param skin The location where the window's skin XML can be found. */ Window(const std::string &caption = "Window", bool modal = false, - Window *parent = NULL, const std::string &skin = "graphics/gui/gui.xml"); + Window *parent = NULL, const std::string &skin = "graphics/gui/gui.xml"); /** * Destructor. Deletes all the added widgets. @@ -89,6 +89,12 @@ class Window : public gcn::Window, gcn::WidgetListener void setLocationRelativeTo(gcn::Widget *widget); /** + * Sets the location relative to the given enumerated position. + */ + void setLocationRelativeTo(ImageRect::ImagePosition position, + int offsetX = 0, int offsetY = 0); + + /** * Sets whether or not the window can be resized. */ void setResizable(bool resize); @@ -151,8 +157,7 @@ class Window : public gcn::Window, gcn::WidgetListener /** * Sets flag to show a title or not. */ - void setShowTitle(bool flag) - { mShowTitle = flag; } + void setShowTitle(bool flag) { mShowTitle = flag; } /** * Sets whether the window is sticky. A sticky window will not have @@ -233,6 +238,12 @@ class Window : public gcn::Window, gcn::WidgetListener void loadWindowState(); /** + * Saves the window state so that when the window is reloaded, it'll + * maintain its previous state and location. + */ + void saveWindowState(); + + /** * Set the default win pos and size. * (which can be different of the actual ones.) */ @@ -240,10 +251,25 @@ class Window : public gcn::Window, gcn::WidgetListener int defaultWidth, int defaultHeight); /** + * Set the default win pos and size tot he current ones. + */ + void setDefaultSize(); + + /** + * Set the default win pos and size. + * (which can be different of the actual ones.) + * This version of setDefaultSize sets the window's position based + * on a relative enumerated position, rather than a coordinate position. + */ + void setDefaultSize(int defaultWidth, int defaultHeight, + ImageRect::ImagePosition position, + int offsetx = 0, int offsetY = 0); + + /** * Reset the win pos and size to default. Don't forget to set defaults * first. */ - void resetToDefaultSize(); + virtual void resetToDefaultSize(); /** * Gets the layout handler for this window. @@ -261,11 +287,6 @@ class Window : public gcn::Window, gcn::WidgetListener void reflowLayout(int w = 0, int h = 0); /** - * Loads a window skin - */ - void loadSkin(const std::string &fileName); - - /** * Adds a widget to the window and sets it at given cell. */ LayoutCell &place(int x, int y, gcn::Widget *, int w = 1, int h = 1); @@ -275,9 +296,22 @@ class Window : public gcn::Window, gcn::WidgetListener */ ContainerPlacer getPlacer(int x, int y); - protected: - /** The window container windows add themselves to. */ - static WindowContainer *windowContainer; + /** + * Positions the window in the center of it's parent. + */ + void center(); + + /** + * Overrideable functionality for when the window is to close. This + * allows for class implementations to clean up or do certain actions + * on window close they couldn't do otherwise. + */ + virtual void close(); + + /** + * Gets the alpha value used by the window, in a GUIChan usable format. + */ + int getGuiAlpha(); private: enum ResizeHandles @@ -316,7 +350,6 @@ class Window : public gcn::Window, gcn::WidgetListener int mDefaultY; /**< Default window Y position */ int mDefaultWidth; /**< Default window width */ int mDefaultHeight; /**< Default window height */ - std::string mSkin; /**< Name of the skin to use */ /** * The config listener that listens to changes relevant to all windows. @@ -325,8 +358,8 @@ class Window : public gcn::Window, gcn::WidgetListener static int mouseResize; /**< Active resize handles */ static int instances; /**< Number of Window instances */ - ImageRect border; /**< The window border and background */ - static Image *closeImage; /**< Close Button Image */ + + Skin* mSkin; /**< Skin in use by this window */ /** * The width of the resize border. Is independent of the actual window diff --git a/src/gui/windowcontainer.cpp b/src/gui/windowcontainer.cpp index 2846b1c1..eda739b9 100644 --- a/src/gui/windowcontainer.cpp +++ b/src/gui/windowcontainer.cpp @@ -23,6 +23,8 @@ #include "../utils/dtor.h" +WindowContainer *windowContainer = NULL; + void WindowContainer::logic() { delete_all(mDeathList); diff --git a/src/gui/windowcontainer.h b/src/gui/windowcontainer.h index 62704d1b..bc918184 100644 --- a/src/gui/windowcontainer.h +++ b/src/gui/windowcontainer.h @@ -45,6 +45,11 @@ class WindowContainer : public gcn::Container */ void scheduleDelete(gcn::Widget *widget); + /** + * Get the number of widget instances + */ + int getNumberOfInstances() { return mDeathList.size(); } + private: /** * List of widgets that are scheduled to be deleted. @@ -54,4 +59,6 @@ class WindowContainer : public gcn::Container Widgets mDeathList; }; +extern WindowContainer* windowContainer; + #endif diff --git a/src/inventory.cpp b/src/inventory.cpp index cdd7b61c..80bc582b 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -37,8 +37,9 @@ struct SlotUsed : public std::unary_function<Item*, bool> } }; -Inventory::Inventory(int size): - mSize(size) +Inventory::Inventory(int size, int offset): + mSize(size), + mOffset(offset) { mItems = new Item*[mSize]; std::fill_n(mItems, mSize, (Item*) 0); @@ -134,11 +135,7 @@ bool Inventory::contains(Item *item) const int Inventory::getFreeSlot() const { -#ifdef TMWSERV_SUPPORT - Item **i = std::find_if(mItems, mItems + mSize, -#else - Item **i = std::find_if(mItems + 2, mItems + mSize, -#endif + Item **i = std::find_if(mItems + mOffset, mItems + mSize, std::not1(SlotUsed())); return (i == mItems + mSize) ? -1 : (i - mItems); } @@ -156,3 +153,8 @@ int Inventory::getLastUsedSlot() const return -1; } + +int Inventory::getInventorySize() const +{ + return mSize - mOffset; +} diff --git a/src/inventory.h b/src/inventory.h index d2a81edf..008b7ec4 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -24,13 +24,20 @@ class Item; +#ifdef EATHENA_SUPPORT +#define INVENTORY_SIZE 102 +#define STORAGE_SIZE 301 +#else +#define INVENTORY_SIZE 50 +#endif + class Inventory { public: /** * Constructor. */ - Inventory(int size); + Inventory(int size, int offset = 0); /** * Destructor. @@ -100,10 +107,16 @@ class Inventory */ int getLastUsedSlot() const; + /** + * Returns the number of slots available in the inventory. + */ + int getInventorySize() const; + static const int NO_SLOT_INDEX = -1; /**< Slot has no index. */ protected: Item **mItems; /**< The holder of items */ int mSize; /**< The max number of inventory items */ + int mOffset; /**< Offset used by the inventory */ }; #endif diff --git a/src/keyboardconfig.cpp b/src/keyboardconfig.cpp index 73c0fe0a..06ce4ac7 100644 --- a/src/keyboardconfig.cpp +++ b/src/keyboardconfig.cpp @@ -197,6 +197,7 @@ int KeyboardConfig::getKeyEmoteOffset(int keyValue) const bool KeyboardConfig::isKeyActive(int index) { + if (!mActiveKeys) return false; return mActiveKeys[mKey[index].value]; } diff --git a/src/localplayer.cpp b/src/localplayer.cpp index c0da92ac..2233942d 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -38,6 +38,10 @@ #include "gui/gui.h" #include "gui/ministatus.h" +#include "gui/palette.h" +#ifdef EATHENA_SUPPORT +#include "gui/storagewindow.h" +#endif #ifdef TMWSERV_SUPPORT #include "effectmanager.h" @@ -111,12 +115,11 @@ LocalPlayer::LocalPlayer(Uint32 id, Uint16 job, Map *map): mDestX(0), mDestY(0), #ifdef TMWSERV_SUPPORT mLocalWalkTime(-1), -#endif - mInventory(new Inventory(INVENTORY_SIZE)) -#ifdef EATHENA_SUPPORT - , mStorage(new Inventory(STORAGE_SIZE)) + mInventory(new Inventory(INVENTORY_SIZE)), + mExpMessageTime(0) #else - , mExpMessageTime(0) + mInventory(new Inventory(INVENTORY_SIZE, 2)), + mStorage(new Inventory(STORAGE_SIZE, 1)) #endif { // Variable to keep the local player from doing certain actions before a map @@ -148,7 +151,8 @@ LocalPlayer::~LocalPlayer() void LocalPlayer::logic() { #ifdef EATHENA_SUPPORT - switch (mAction) { + switch (mAction) + { case STAND: break; @@ -168,13 +172,36 @@ void LocalPlayer::logic() break; case ATTACK: + int rotation = 0; + std::string particleEffect = ""; int frames = 4; + if (mEquippedWeapon && mEquippedWeapon->getAttackType() == ACTION_ATTACK_BOW) frames = 5; mFrame = (get_elapsed_time(mWalkTime) * frames) / mAttackSpeed; + //attack particle effect + if (mEquippedWeapon) + particleEffect = mEquippedWeapon->getParticleEffect(); + + if (!particleEffect.empty() && mParticleEffects && mFrame == 1) + { + switch (mDirection) + { + case DOWN: rotation = 0; break; + case LEFT: rotation = 90; break; + case UP: rotation = 180; break; + case RIGHT: rotation = 270; break; + default: break; + } + Particle *p; + p = particleEngine->addEffect("graphics/particles/" + + particleEffect, 0, 0, rotation); + controlParticle(p); + } + if (mFrame >= frames) nextStep(); @@ -193,11 +220,14 @@ void LocalPlayer::logic() if (mExpMessageTime == 0) { const Vector &pos = getPosition(); - particleEngine->addTextRiseFadeOutEffect(mExpMessages.front(), - 0, 128, 255, - gui->getFont(), - (int) pos.x + 16, - (int) pos.y - 16); + + particleEngine->addTextRiseFadeOutEffect( + mExpMessages.front(), + (int) pos.x + 16, + (int) pos.y - 16, + &guiPalette->getColor(Palette::EXP_INFO), + gui->getInfoParticleFont(), true); + mExpMessages.pop_front(); mExpMessageTime = 30; } @@ -218,20 +248,30 @@ void LocalPlayer::logic() if (mTarget) { - // Find whether target is in range - const int rangeX = abs(mTarget->mX - mX); - const int rangeY = abs(mTarget->mY - mY); - const int attackRange = getAttackRange(); - const int inRange = rangeX > attackRange || rangeY > attackRange ? 1 : 0; + if (mTarget->getType() == Being::NPC) + { + // NPCs are always in range + mTarget->setTargetAnimation( + mTargetCursor[0][mTarget->getTargetCursorSize()]); + } + else + { + // Find whether target is in range + const int rangeX = abs(mTarget->mX - mX); + const int rangeY = abs(mTarget->mY - mY); + const int attackRange = getAttackRange(); + const int inRange = rangeX > attackRange || rangeY > attackRange + ? 1 : 0; - mTarget->setTargetAnimation( - mTargetCursor[inRange][mTarget->getTargetCursorSize()]); + mTarget->setTargetAnimation( + mTargetCursor[inRange][mTarget->getTargetCursorSize()]); - if (mTarget->mAction == DEAD) - stopAttack(); + if (mTarget->mAction == DEAD) + stopAttack(); - if (mKeepAttacking && mTarget) - attack(mTarget, true); + if (mKeepAttacking && mTarget) + attack(mTarget, true); + } } #endif @@ -241,7 +281,9 @@ void LocalPlayer::logic() void LocalPlayer::setGM() { mIsGM = !mIsGM; - mNameColor = mIsGM ? 0x009000: 0x202020; + mNameColor = mIsGM ? + &guiPalette->getColor(Palette::GM) : + &guiPalette->getColor(Palette::PLAYER); setName(getName()); config.setValue(getName() + "GMassert", mIsGM); } @@ -871,7 +913,7 @@ void LocalPlayer::attack(Being *target, bool keep) { mKeepAttacking = keep; - if (!target) + if (!target || target->getType() == Being::NPC) return; if ((mTarget != target) || !mTarget) @@ -883,10 +925,8 @@ void LocalPlayer::attack(Being *target, bool keep) int dist_x = target->mX - mX; int dist_y = target->mY - mY; - // Must be standing and be within attack range to continue - if ((mAction != STAND) || (mAttackRange < abs(dist_x)) || - (mAttackRange < abs(dist_y))) - return; + // Must be standing to attack + if (mAction != STAND) return; if (abs(dist_y) >= abs(dist_x)) { @@ -1030,16 +1070,26 @@ void LocalPlayer::setXp(int xp) const std::string text = toString(xp - mXp) + " xp"; // Show XP number - particleEngine->addTextRiseFadeOutEffect(text, - 255, 255, 0, - hitYellowFont, - mPx + 16, mPy - 16); + particleEngine->addTextRiseFadeOutEffect(text, mPx + 16, mPy - 16, + &guiPalette->getColor(Palette::EXP_INFO), + gui->getInfoParticleFont(), true); } mXp = xp; } #endif +void LocalPlayer::pickedUp(std::string item) +{ + if (mMap) + { + // Show pickup notification + particleEngine->addTextRiseFadeOutEffect(item, mPx + 16, mPy - 16, + &guiPalette->getColor(Palette::PICKUP_INFO), + gui->getInfoParticleFont (), true); + } +} + int LocalPlayer::getAttackRange() { #ifdef TMWSERV_SUPPORT @@ -1181,3 +1231,11 @@ void LocalPlayer::loadTargetCursor(std::string filename, int width, int height, mTargetCursor[index][size] = currentCursor; } +#ifdef EATHENA_SUPPORT +void LocalPlayer::setInStorage(bool inStorage) +{ + mInStorage = inStorage; + + storageWindow->setVisible(inStorage); +} +#endif diff --git a/src/localplayer.h b/src/localplayer.h index 7ced3fdf..703dbbfa 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -27,16 +27,6 @@ #include "player.h" -#ifdef EATHENA_SUPPORT -// TODO move into some sane place... -#define MAX_SLOT 2 - -#define INVENTORY_SIZE 102 -#define STORAGE_SIZE 301 -#else -#define INVENTORY_SIZE 50 -#endif - class Equipment; class FloorItem; class ImageSet; @@ -300,10 +290,11 @@ class LocalPlayer : public Player * displayed as soon as the player attacks, not when the server says * the player does. * - * @param victim The attacked being. - * @param damage The amount of damage dealt (0 means miss). + * @param victim the victim being + * @param damage the amount of damage dealt (0 means miss) + * @param type the attack type */ - virtual void handleAttack(Being *victim, int damage) {} + virtual void handleAttack(Being *victim, int damage, AttackType type) {} virtual void handleAttack() {} /** @@ -374,12 +365,17 @@ class LocalPlayer : public Player void revive(); + /** + * Shows item pickup effect if the player is on a map. + */ + void pickedUp(std::string item); + #ifdef EATHENA_SUPPORT /** * Accessors for mInStorage */ bool getInStorage() { return mInStorage; } - void setInStorage(bool inStorage) { mInStorage = inStorage; } + void setInStorage(bool inStorage); /** * Sets the amount of XP. Shows XP gaining effect if the player is on @@ -404,8 +400,8 @@ class LocalPlayer : public Player Uint8 mAttr[6]; Uint8 mAttrUp[6]; - Sint16 ATK, MATK, DEF, MDEF, HIT, FLEE; - Sint16 ATK_BONUS, MATK_BONUS, DEF_BONUS, MDEF_BONUS, FLEE_BONUS; + int ATK, MATK, DEF, MDEF, HIT, FLEE; + int ATK_BONUS, MATK_BONUS, DEF_BONUS, MDEF_BONUS, FLEE_BONUS; Uint16 mStatPoint, mSkillPoint; Uint16 mStatsPointsToAttribute; diff --git a/src/main.cpp b/src/main.cpp index 22607e4e..b7afd93e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,7 +27,6 @@ #include <SDL_image.h> #include <guichan/actionlistener.hpp> -#include <guichan/widgets/label.hpp> #include <libxml/parser.h> @@ -50,6 +49,7 @@ #include "player_relations.h" #include "serverinfo.h" #include "sound.h" +#include "statuseffect.h" #include "units.h" #include "gui/button.h" @@ -57,10 +57,11 @@ #include "gui/char_server.h" #endif #include "gui/char_select.h" -#include "gui/color.h" #include "gui/gui.h" +#include "gui/label.h" #include "gui/login.h" #include "gui/ok_dialog.h" +#include "gui/palette.h" #include "gui/progressbar.h" #include "gui/register.h" #include "gui/sdlinput.h" @@ -128,8 +129,6 @@ namespace { - Window *setupWindow = 0; - struct SetupListener : public gcn::ActionListener { /** @@ -177,7 +176,7 @@ LogoutHandler logoutHandler; #endif LockedArray<LocalPlayer*> charInfo(maxSlot + 1); -Color *textColor; +Palette *guiPalette; // This anonymous namespace hides whatever is inside from other modules. namespace { @@ -191,6 +190,8 @@ LoginHandler loginHandler; MapLoginHandler mapLoginHandler; #endif +SDL_Surface *icon; + /** * A structure holding the values of various options that can be passed from * the command line. @@ -454,7 +455,7 @@ void initEngine(const Options &options) SetClassLong(pInfo.window, GCL_HICON, (LONG) icon); } #else - SDL_Surface *icon = IMG_Load(resman->getPath(branding.getValue("appIcon", "data/icons/tmw.png")).c_str()); + icon = IMG_Load(resman->getPath(branding.getValue("appIcon", "data/icons/tmw.png")).c_str()); if (icon) { SDL_SetAlpha(icon, SDL_SRCALPHA, SDL_ALPHA_OPAQUE); @@ -507,16 +508,18 @@ void initEngine(const Options &options) #endif // Initialize sound engine - try { - if (config.getValue("sound", 0) == 1) { + try + { + if (config.getValue("sound", 0) == 1) sound.init(); - } + sound.setSfxVolume((int) config.getValue("sfxVolume", defaultSfxVolume)); sound.setMusicVolume((int) config.getValue("musicVolume", defaultMusicVolume)); } - catch (const char *err) { + catch (const char *err) + { state = STATE_ERROR; errorMessage = err; logger->log("Warning: %s", err); @@ -553,8 +556,11 @@ void exit_engine() ItemDB::unload(); MonsterDB::unload(); NPCDB::unload(); + StatusEffect::unload(); ResourceManager::deleteInstance(); + + SDL_FreeSurface(icon); } void printHelp() @@ -667,6 +673,7 @@ void parseOptions(int argc, char *argv[], Options &options) */ void loadUpdates() { + if (updatesDir.empty()) return; const std::string updatesFile = "/" + updatesDir + "/resources2.txt"; ResourceManager *resman = ResourceManager::getInstance(); std::vector<std::string> lines = resman->loadTextFile(updatesFile); @@ -1054,7 +1061,7 @@ int main(int argc, char *argv[]) initEngine(options); // Needs to be created in main, as the updater uses it - textColor = new Color; + guiPalette = new Palette; Game *game = NULL; Window *currentDialog = NULL; @@ -1067,14 +1074,14 @@ int main(int argc, char *argv[]) gcn::Container *top = static_cast<gcn::Container*>(gui->getTop()); #ifdef PACKAGE_VERSION #ifdef TMWSERV_SUPPORT - gcn::Label *versionLabel = new gcn::Label(strprintf("%s TMWserv", PACKAGE_VERSION)); + gcn::Label *versionLabel = new Label(strprintf("%s TMWserv", PACKAGE_VERSION)); #else - gcn::Label *versionLabel = new gcn::Label(strprintf("%s eAthena", PACKAGE_VERSION)); + gcn::Label *versionLabel = new Label(strprintf("%s eAthena", PACKAGE_VERSION)); #endif top->add(versionLabel, 25, 2); #endif ProgressBar *progressBar = new ProgressBar(0.0f, 100, 20, 168, 116, 31); - gcn::Label *progressLabel = new gcn::Label(); + gcn::Label *progressLabel = new Label(); top->add(progressBar, 5, top->getHeight() - 5 - progressBar->getHeight()); top->add(progressLabel, 15 + progressBar->getWidth(), progressBar->getY() + 4); @@ -1180,13 +1187,9 @@ int main(int argc, char *argv[]) { #ifdef TMWSERV_SUPPORT if (!quitDialog) - { quitDialog = new QuitDialog(NULL, &quitDialog); - } else - { quitDialog->requestMoveToTop(); - } #else state = STATE_EXIT; #endif @@ -1210,11 +1213,10 @@ int main(int argc, char *argv[]) { state = STATE_ERROR; - if (!network->getError().empty()) { + if (!network->getError().empty()) errorMessage = network->getError(); - } else { + else errorMessage = _("Got disconnected from server!"); - } } #endif @@ -1264,10 +1266,9 @@ int main(int argc, char *argv[]) reconnectAccount(token); state = STATE_WAIT; } -#endif -#ifdef TMWSERV_SUPPORT - if (state != oldstate) { + if (state != oldstate) + { // Load updates after exiting the update state if (oldstate == STATE_UPDATE) { @@ -1548,7 +1549,8 @@ int main(int argc, char *argv[]) #else // no TMWSERV_SUPPORT - if (state != oldstate) { + if (state != oldstate) + { switch (oldstate) { case STATE_UPDATE: @@ -1579,12 +1581,14 @@ int main(int argc, char *argv[]) oldstate = state; if (currentDialog && state != STATE_ACCOUNT && - state != STATE_CHAR_CONNECT) { + state != STATE_CHAR_CONNECT) + { delete currentDialog; currentDialog = NULL; } - switch (state) { + switch (state) + { case STATE_LOADDATA: logger->log("State: LOADDATA"); @@ -1600,6 +1604,8 @@ int main(int argc, char *argv[]) MonsterDB::load(); NPCDB::load(); EmoteDB::load(); + StatusEffect::load(); + Being::load(); // Hairstyles // Load units Units::loadUnits(); @@ -1610,10 +1616,13 @@ int main(int argc, char *argv[]) case STATE_LOGIN: logger->log("State: LOGIN"); - if (!loginData.password.empty()) { + if (!loginData.password.empty()) + { loginData.registerLogin = false; state = STATE_ACCOUNT; - } else { + } + else + { currentDialog = new LoginDialog(&loginData); positionDialog(currentDialog, screenWidth, screenHeight); @@ -1639,8 +1648,7 @@ int main(int argc, char *argv[]) } else { - int nextState = (options.skipUpdate) ? - STATE_LOADDATA : STATE_UPDATE; + int nextState = STATE_UPDATE; currentDialog = new ServerSelectDialog(&loginData, nextState); positionDialog(currentDialog, screenWidth, @@ -1683,12 +1691,10 @@ int main(int argc, char *argv[]) delete progressBar; delete progressLabel; delete setup; - delete setupWindow; progressBar = NULL; progressLabel = NULL; currentDialog = NULL; setup = NULL; - setupWindow = NULL; login_wallpaper->decRef(); login_wallpaper = NULL; @@ -1700,18 +1706,21 @@ int main(int argc, char *argv[]) break; case STATE_UPDATE: - // Determine which source to use for the update host - if (!options.updateHost.empty()) - updateHost = options.updateHost; + if (options.skipUpdate) + { + state = STATE_LOADDATA; + } else - updateHost = loginData.updateHost; + { + // Determine which source to use for the update host + if (!options.updateHost.empty()) + updateHost = options.updateHost; + else + updateHost = loginData.updateHost; - setUpdatesDir(); - logger->log("State: UPDATE"); + setUpdatesDir(); + logger->log("State: UPDATE"); - if (options.skipUpdate) { - state = STATE_LOADDATA; - } else { currentDialog = new UpdaterWindow(updateHost, homeDir + "/" + updatesDir); positionDialog(currentDialog, screenWidth, @@ -1763,12 +1772,12 @@ int main(int argc, char *argv[]) /* * This loop can really stress the CPU, for no reason since it's * just constantly redrawing the wallpaper. Added the following - * usleep to limit it to 20 FPS during the login sequence + * usleep to limit it to 40 FPS during the login sequence */ - usleep(50000); + usleep(25000); } - delete textColor; + delete guiPalette; #ifdef PACKAGE_VERSION delete versionLabel; #endif @@ -1807,16 +1816,12 @@ void SetupListener::action(const gcn::ActionEvent &event) Window *window = NULL; if (event.getId() == "Setup") - { window = setupWindow; - } if (window) { window->setVisible(!window->isVisible()); if (window->isVisible()) - { window->requestMoveToTop(); - } } } diff --git a/src/map.cpp b/src/map.cpp index e7646fd2..551c10f3 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -85,7 +85,7 @@ void TileAnimation::update() Image *img = mAnimation->getCurrentImage(); if (img != mLastImage) { - for (std::list<std::pair<MapLayer*, int> >::iterator i = + for (std::list<std::pair<MapLayer*, int> >::iterator i = mAffected.begin(); i != mAffected.end(); i++) { i->first->setTile(i->second, img); @@ -119,10 +119,8 @@ Image* MapLayer::getTile(int x, int y) const return mTiles[x + y * mWidth]; } -void MapLayer::draw(Graphics *graphics, - int startX, int startY, - int endX, int endY, - int scrollX, int scrollY, +void MapLayer::draw(Graphics *graphics, int startX, int startY, + int endX, int endY, int scrollX, int scrollY, const Sprites &sprites) const { startX -= mX; @@ -167,8 +165,10 @@ void MapLayer::draw(Graphics *graphics, } // Draw any remaining sprites - if (mIsFringeLayer) { - while (si != sprites.end()) { + if (mIsFringeLayer) + { + while (si != sprites.end()) + { (*si)->draw(graphics, -scrollX, -scrollY); si++; } diff --git a/src/monster.cpp b/src/monster.cpp index deabd7a8..c8abcc05 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -27,13 +27,15 @@ #include "sound.h" #include "text.h" +#include "gui/palette.h" + #include "resources/monsterdb.h" #include "resources/monsterinfo.h" static const int NAME_X_OFFSET = 16; static const int NAME_Y_OFFSET = 16; -Monster::Monster(Uint32 id, Uint16 job, Map *map): +Monster::Monster(int id, Uint16 job, Map *map): Being(id, job, map), mText(0) { @@ -71,7 +73,7 @@ Monster::Monster(Uint32 id, Uint16 job, Map *map): } } - mNameColor = 0xff2020; + mNameColor = &guiPalette->getColor(Palette::MONSTER); } Monster::~Monster() @@ -178,9 +180,9 @@ void Monster::handleAttack() #else -void Monster::handleAttack(Being *victim, int damage) +void Monster::handleAttack(Being *victim, int damage, AttackType type) { - Being::handleAttack(victim, damage); + Being::handleAttack(victim, damage, type); const MonsterInfo &mi = getInfo(); sound.playSfx(mi.getSound((damage > 0) ? @@ -189,10 +191,10 @@ void Monster::handleAttack(Being *victim, int damage) #endif -void Monster::takeDamage(int amount) +void Monster::takeDamage(Being *attacker, int amount, AttackType type) { if (amount > 0) sound.playSfx(getInfo().getSound(MONSTER_EVENT_HURT)); - Being::takeDamage(amount); + Being::takeDamage(attacker, amount, type); } Being::TargetCursorSize Monster::getTargetCursorSize() const @@ -219,7 +221,8 @@ void Monster::showName(bool show) { mText = new Text(getInfo().getName(), mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET - getHeight(), - gcn::Graphics::CENTER, gcn::Color(255, 64, 64)); + gcn::Graphics::CENTER, + &guiPalette->getColor(Palette::MONSTER)); } else { diff --git a/src/monster.h b/src/monster.h index 5251d37e..34a2f237 100644 --- a/src/monster.h +++ b/src/monster.h @@ -30,7 +30,7 @@ class Text; class Monster : public Being { public: - Monster(Uint32 id, Uint16 job, Map *map); + Monster(int id, Uint16 job, Map *map); ~Monster(); @@ -49,21 +49,24 @@ class Monster : public Being * Handles an attack of another being by this monster. Plays a hit or * miss sound when appropriate. * - * @param victim The attacked being. - * @param damage The amount of damage dealt (0 means miss). + * @param victim the victim being + * @param damage the amount of damage dealt (0 means miss) + * @param type the attack type */ #ifdef TMWSERV_SUPPORT virtual void handleAttack(); #else - virtual void handleAttack(Being *victim, int damage); + virtual void handleAttack(Being *victim, int damage, AttackType type); #endif /** * Puts a damage bubble above this monster and plays the hurt sound * - * @param amount The amount of damage. + * @param attacker the attacking being + * @param damage the amount of damage recieved (0 means miss) + * @param type the attack type */ - virtual void takeDamage(int amount); + virtual void takeDamage(Being *attacker, int amount, AttackType type); /** * Returns the MonsterInfo, with static data about this monster. diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp index 1edc6079..3629b075 100644 --- a/src/net/ea/beinghandler.cpp +++ b/src/net/ea/beinghandler.cpp @@ -35,6 +35,10 @@ #include "../../npc.h" #include "../../player_relations.h" +#include "../../gui/npc_text.h" + +extern NpcTextDialog *npcTextDialog; + const int EMOTION_TIME = 150; /**< Duration of emotion icon */ BeingHandler::BeingHandler(bool enableSync): @@ -65,16 +69,16 @@ BeingHandler::BeingHandler(bool enableSync): void BeingHandler::handleMessage(MessageIn &msg) { - Uint32 id; + int id; Uint16 job, speed; Uint16 headTop, headMid, headBottom; Uint16 shoes, gloves; Uint16 weapon, shield; Uint16 gmstatus; - Sint16 param1; + int param1; int stunMode; Uint32 statusEffects; - Sint8 type; + int type; Uint16 status; Being *srcBeing, *dstBeing; int hairStyle, hairColor, flag; @@ -204,7 +208,12 @@ void BeingHandler::handleMessage(MessageIn &msg) case SMSG_BEING_REMOVE: // A being should be removed or has died - dstBeing = beingManager->findBeing(msg.readInt32()); + id = msg.readInt32(); + + if (id == current_npc) + npcTextDialog->showCloseButton(); + + dstBeing = beingManager->findBeing(id); if (!dstBeing) break; @@ -213,9 +222,6 @@ void BeingHandler::handleMessage(MessageIn &msg) if (dstBeing == player_node->getTarget()) player_node->stopAttack(); - if (dstBeing == current_npc) - current_npc->handleDeath(); - if (msg.readInt8() == 1) dstBeing->setAction(Being::DEAD); else @@ -236,14 +242,17 @@ void BeingHandler::handleMessage(MessageIn &msg) switch (type) { - case 0x0a: // Critical Damage - if (dstBeing) - dstBeing->showCrit(); - case 0x00: // Damage + case Being::HIT: // Damage + case Being::CRITICAL: // Critical Damage + case Being::MULTI: // Critical Damage + case Being::REFLECT: // Reflected Damage + case Being::FLEE: // Lucky Dodge if (dstBeing) - dstBeing->takeDamage(param1); + dstBeing->takeDamage(srcBeing, param1, + (Being::AttackType)type); if (srcBeing) - srcBeing->handleAttack(dstBeing, param1); + srcBeing->handleAttack(dstBeing, param1, + (Being::AttackType)type); break; case 0x02: // Sit @@ -450,11 +459,6 @@ void BeingHandler::handleMessage(MessageIn &msg) { switch (msg.readInt8()) { - case 1: - if (dstBeing->getType() != Being::NPC) - dstBeing->setAction(Being::DEAD); - break; - case 2: dstBeing->setAction(Being::SIT); break; diff --git a/src/net/ea/buysellhandler.cpp b/src/net/ea/buysellhandler.cpp index 480c71b8..8dbc2953 100644 --- a/src/net/ea/buysellhandler.cpp +++ b/src/net/ea/buysellhandler.cpp @@ -32,15 +32,12 @@ #include "../../npc.h" #include "../../gui/buy.h" +#include "../../gui/buysell.h" #include "../../gui/chat.h" #include "../../gui/sell.h" #include "../../utils/gettext.h" -extern BuyDialog *buyDialog; -extern Window *buySellDialog; -extern SellDialog *sellDialog; - BuySellHandler::BuySellHandler() { static const Uint16 _messages[] = { @@ -64,8 +61,8 @@ void BuySellHandler::handleMessage(MessageIn &msg) buyDialog->reset(); sellDialog->setVisible(false); sellDialog->reset(); + current_npc = msg.readInt32(); buySellDialog->setVisible(true); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg.readInt32())); break; case SMSG_NPC_BUY: @@ -77,10 +74,10 @@ void BuySellHandler::handleMessage(MessageIn &msg) for (int k = 0; k < n_items; k++) { - Sint32 value = msg.readInt32(); + int value = msg.readInt32(); msg.readInt32(); // DCvalue msg.readInt8(); // type - Sint16 itemId = msg.readInt16(); + int itemId = msg.readInt16(); buyDialog->addItem(itemId, 0, value); } break; @@ -88,33 +85,38 @@ void BuySellHandler::handleMessage(MessageIn &msg) case SMSG_NPC_SELL: msg.readInt16(); // length n_items = (msg.getLength() - 4) / 10; - if (n_items > 0) { + if (n_items > 0) + { sellDialog->setMoney(player_node->getMoney()); sellDialog->reset(); sellDialog->setVisible(true); for (int k = 0; k < n_items; k++) { - Sint16 index = msg.readInt16(); - Sint32 value = msg.readInt32(); + int index = msg.readInt16(); + int value = msg.readInt32(); msg.readInt32(); // OCvalue Item *item = player_node->getInventory()->getItem(index); - if (item && !(item->isEquipped())) { + + if (item && !(item->isEquipped())) sellDialog->addItem(item, value); - } } } - else { + else + { chatWindow->chatLog(_("Nothing to sell"), BY_SERVER); - if (current_npc) current_npc->handleDeath(); + current_npc = 0; } break; case SMSG_NPC_BUY_RESPONSE: - if (msg.readInt8() == 0) { + if (msg.readInt8() == 0) + { chatWindow->chatLog(_("Thanks for buying"), BY_SERVER); - } else { + } + else + { // Reset player money since buy dialog already assumed purchase // would go fine buyDialog->setMoney(player_node->getMoney()); @@ -123,11 +125,11 @@ void BuySellHandler::handleMessage(MessageIn &msg) break; case SMSG_NPC_SELL_RESPONSE: - if (msg.readInt8() == 0) { + if (msg.readInt8() == 0) chatWindow->chatLog(_("Thanks for selling"), BY_SERVER); - } else { + else chatWindow->chatLog(_("Unable to sell"), BY_SERVER); - } + break; } } diff --git a/src/net/ea/chathandler.cpp b/src/net/ea/chathandler.cpp index 0293f987..4842c86f 100644 --- a/src/net/ea/chathandler.cpp +++ b/src/net/ea/chathandler.cpp @@ -60,7 +60,7 @@ void ChatHandler::handleMessage(MessageIn &msg) Being *being; std::string chatMsg; std::string nick; - Sint16 chatMsgLength; + int chatMsgLength; switch (msg.getId()) { diff --git a/src/net/ea/equipmenthandler.cpp b/src/net/ea/equipmenthandler.cpp index 19063daf..f5377cf2 100644 --- a/src/net/ea/equipmenthandler.cpp +++ b/src/net/ea/equipmenthandler.cpp @@ -48,9 +48,9 @@ EquipmentHandler::EquipmentHandler() void EquipmentHandler::handleMessage(MessageIn &msg) { - Sint32 itemCount; - Sint16 index, equipPoint, itemId; - Sint8 type; + int itemCount; + int index, equipPoint, itemId; + int type; int mask, position; Item *item; Inventory *inventory = player_node->getInventory(); diff --git a/src/net/ea/inventoryhandler.cpp b/src/net/ea/inventoryhandler.cpp index 71eee291..addcb06f 100644 --- a/src/net/ea/inventoryhandler.cpp +++ b/src/net/ea/inventoryhandler.cpp @@ -25,6 +25,7 @@ #include "../messagein.h" #include "protocol.h" +#include "../../configuration.h" #include "../../inventory.h" #include "../../item.h" #include "../../itemshortcut.h" @@ -32,6 +33,7 @@ #include "../../log.h" #include "../../gui/chat.h" +#include "../../gui/storagewindow.h" #include "../../resources/iteminfo.h" @@ -60,9 +62,9 @@ InventoryHandler::InventoryHandler() void InventoryHandler::handleMessage(MessageIn &msg) { - Sint32 number; - Sint16 index, amount, itemId, equipType, arrow; - Sint16 identified, cards[4], itemType; + int number; + int index, amount, itemId, equipType, arrow; + int identified, cards[4], itemType; Inventory *inventory = player_node->getInventory(); Inventory *storage = player_node->getStorage(); @@ -70,7 +72,6 @@ void InventoryHandler::handleMessage(MessageIn &msg) { case SMSG_PLAYER_INVENTORY: case SMSG_PLAYER_STORAGE_ITEMS: - case SMSG_PLAYER_STORAGE_EQUIP: switch (msg.getId()) { case SMSG_PLAYER_INVENTORY: // Clear inventory - this will be a complete refresh @@ -84,11 +85,10 @@ void InventoryHandler::handleMessage(MessageIn &msg) * clear storage here */ storage->clear(); - logger->log("Received SMSG_PLAYER_STORAGE_ITEMS"); break; default: - logger->log("Received SMSG_PLAYER_STORAGE_EQUIP"); - break; + logger->log("HOW DID WE GET HERE?"); + return; } msg.readInt16(); // length number = (msg.getLength() - 4) / 18; @@ -98,17 +98,8 @@ void InventoryHandler::handleMessage(MessageIn &msg) itemId = msg.readInt16(); itemType = msg.readInt8(); identified = msg.readInt8(); - if (msg.getId() == SMSG_PLAYER_STORAGE_EQUIP) { - amount = 1; - msg.readInt16(); // Equip Point? - } else { - amount = msg.readInt16(); - } + amount = msg.readInt16(); arrow = msg.readInt16(); - if (msg.getId() == SMSG_PLAYER_STORAGE_EQUIP) { - msg.readInt8(); // Attribute (broken) - msg.readInt8(); // Refine level - } for (int i = 0; i < 4; i++) cards[i] = msg.readInt16(); @@ -128,6 +119,29 @@ void InventoryHandler::handleMessage(MessageIn &msg) } break; + case SMSG_PLAYER_STORAGE_EQUIP: + msg.readInt16(); // length + number = (msg.getLength() - 4) / 20; + + for (int loop = 0; loop < number; loop++) { + index = msg.readInt16(); + itemId = msg.readInt16(); + itemType = msg.readInt8(); + identified = msg.readInt8(); + amount = 1; + msg.readInt16(); // Equip Point? + msg.readInt16(); // Another Equip Point? + msg.readInt8(); // Attribute (broken) + msg.readInt8(); // Refine level + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + + logger->log("Index:%d, ID:%d, Type:%d, Identified:%d, Qty:%d, Cards:%d, %d, %d, %d", + index, itemId, itemType, identified, amount, cards[0], cards[1], cards[2], cards[3]); + storage->setItem(index, itemId, amount, false); + } + break; + case SMSG_PLAYER_INVENTORY_ADD: index = msg.readInt16(); amount = msg.readInt16(); @@ -141,13 +155,21 @@ void InventoryHandler::handleMessage(MessageIn &msg) itemType = msg.readInt8(); if (msg.readInt8() > 0) { - chatWindow->chatLog(_("Unable to pick up item"), BY_SERVER); + if (config.getValue("showpickupchat", true)) { + chatWindow->chatLog(_("Unable to pick up item"), BY_SERVER); + } } else { const ItemInfo &itemInfo = ItemDB::get(itemId); const std::string amountStr = (amount > 1) ? toString(amount) : "a"; - chatWindow->chatLog(strprintf(_("You picked up %s %s"), - amountStr.c_str(), itemInfo.getName().c_str()), BY_SERVER); + if (config.getValue("showpickupchat", true)) { + chatWindow->chatLog(strprintf(_("You picked up %s [%s]"), + amountStr.c_str(), itemInfo.getName().c_str()), + BY_SERVER); + } + if (config.getValue("showpickupparticle", false)) { + player_node->pickedUp(itemInfo.getName()); + } if (Item *item = inventory->getItem(index)) { item->setId(itemId); @@ -193,35 +215,54 @@ void InventoryHandler::handleMessage(MessageIn &msg) case SMSG_PLAYER_STORAGE_STATUS: /* - * Basic slots used vs total slots info - * We don't really need this information, but this is - * the closest we get to an "Open Storage" packet - * from the server. It always comes after the two - * SMSG_PLAYER_STORAGE_... packets that update - * storage contents. + * This is the closest we get to an "Open Storage" packet from the + * server. It always comes after the two SMSG_PLAYER_STORAGE_... + * packets that update storage contents. */ - logger->log("Received SMSG_PLAYER_STORAGE_STATUS"); player_node->setInStorage(true); + msg.readInt16(); // Storage capacity + msg.readInt16(); // Used count break; case SMSG_PLAYER_STORAGE_ADD: /* * Move an item into storage */ + index = msg.readInt16(); + amount = msg.readInt32(); + itemId = msg.readInt16(); + identified = msg.readInt8(); + msg.readInt8(); // attribute + msg.readInt8(); // refine + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + + if (Item *item = storage->getItem(index)) { + item->setId(itemId); + item->increaseQuantity(amount); + } else { + storage->setItem(index, itemId, amount, false); + } break; case SMSG_PLAYER_STORAGE_REMOVE: /* - * Move an item out of storage - */ + * Move an item out of storage + */ + index = msg.readInt16(); + amount = msg.readInt16(); + if (Item *item = storage->getItem(index)) { + item->increaseQuantity(-amount); + if (item->getQuantity() == 0) + storage->removeItemAt(index); + } break; case SMSG_PLAYER_STORAGE_CLOSE: /* - * Storage access has been closed - */ + * Storage access has been closed + */ player_node->setInStorage(false); - logger->log("Received SMSG_PLAYER_STORAGE_CLOSE"); break; } } diff --git a/src/net/ea/npchandler.cpp b/src/net/ea/npchandler.cpp index 068a3be6..7bd23135 100644 --- a/src/net/ea/npchandler.cpp +++ b/src/net/ea/npchandler.cpp @@ -19,6 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <SDL_types.h> + #include "../messagein.h" #include "npchandler.h" #include "protocol.h" @@ -32,11 +34,6 @@ #include "../../gui/npclistdialog.h" #include "../../gui/npcstringdialog.h" -extern NpcIntegerDialog *npcIntegerDialog; -extern NpcListDialog *npcListDialog; -extern NpcTextDialog *npcTextDialog; -extern NpcStringDialog *npcStringDialog; - NPCHandler::NPCHandler() { static const Uint16 _messages[] = { @@ -59,40 +56,44 @@ void NPCHandler::handleMessage(MessageIn &msg) { case SMSG_NPC_CHOICE: msg.readInt16(); // length - id = msg.readInt32(); + current_npc = msg.readInt32(); player_node->setAction(LocalPlayer::STAND); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); npcListDialog->parseItems(msg.readString(msg.getLength() - 8)); - npcListDialog->setVisible(true); + npcListDialog->requestFocus(); break; case SMSG_NPC_MESSAGE: msg.readInt16(); // length - id = msg.readInt32(); + current_npc = msg.readInt32(); player_node->setAction(LocalPlayer::STAND); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); npcTextDialog->addText(msg.readString(msg.getLength() - 8)); - npcListDialog->setVisible(false); - npcTextDialog->setVisible(true); + npcTextDialog->requestFocus(); break; case SMSG_NPC_CLOSE: id = msg.readInt32(); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); - npcTextDialog->showCloseButton(); + // If we're talking to that NPC, show the close button + if (id == current_npc) + npcTextDialog->showCloseButton(); + // Otherwise, move on as an empty dialog doesn't help + else + npcTextDialog->closeDialog(id); break; case SMSG_NPC_NEXT: - // Next button in NPC dialog, currently unused id = msg.readInt32(); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); - npcTextDialog->showNextButton(); + // If we're talking to that NPC, show the next button + if (id == current_npc) + npcTextDialog->showNextButton(); + // Otherwise, move on as an empty dialog doesn't help + else + npcTextDialog->nextDialog(id); break; case SMSG_NPC_INT_INPUT: // Request for an integer - id = msg.readInt32(); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); + current_npc = msg.readInt32(); + player_node->setAction(LocalPlayer::STAND); npcIntegerDialog->setRange(0, 2147483647); npcIntegerDialog->setDefaultValue(0); npcIntegerDialog->setVisible(true); @@ -101,8 +102,8 @@ void NPCHandler::handleMessage(MessageIn &msg) case SMSG_NPC_STR_INPUT: // Request for a string - id = msg.readInt32(); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); + current_npc = msg.readInt32(); + player_node->setAction(LocalPlayer::STAND); npcStringDialog->setValue(""); npcStringDialog->setVisible(true); npcStringDialog->requestFocus(); diff --git a/src/net/ea/playerhandler.cpp b/src/net/ea/playerhandler.cpp index 9f0acbb3..fcc44cb0 100644 --- a/src/net/ea/playerhandler.cpp +++ b/src/net/ea/playerhandler.cpp @@ -30,13 +30,17 @@ #include "../../units.h" #include "../../gui/buy.h" +#include "../../gui/buysell.h" #include "../../gui/chat.h" #include "../../gui/gui.h" -#include "../../gui/npclistdialog.h" #include "../../gui/npc_text.h" +#include "../../gui/npcintegerdialog.h" +#include "../../gui/npclistdialog.h" +#include "../../gui/npcstringdialog.h" #include "../../gui/ok_dialog.h" #include "../../gui/sell.h" #include "../../gui/skill.h" +#include "../../gui/storagewindow.h" #include "../../gui/viewport.h" #include "../../utils/stringutils.h" @@ -46,12 +50,6 @@ OkDialog *weightNotice = NULL; OkDialog *deathNotice = NULL; -extern NpcListDialog *npcListDialog; -extern NpcTextDialog *npcTextDialog; -extern BuyDialog *buyDialog; -extern SellDialog *sellDialog; -extern Window *buySellDialog; - // Max. distance we are willing to scroll after a teleport; // everything beyond will reset the port hard. static const int MAP_TELEPORT_SCROLL_DISTANCE = 8; @@ -81,12 +79,19 @@ namespace { { player_node->revive(); deathNotice = NULL; + npcIntegerDialog->reset(); + npcIntegerDialog->setVisible(false); + npcListDialog->reset(); npcListDialog->setVisible(false); + npcStringDialog->setValue(""); + npcStringDialog->setVisible(false); + npcTextDialog->clearText(); npcTextDialog->setVisible(false); buyDialog->setVisible(false); sellDialog->setVisible(false); buySellDialog->setVisible(false); - if (current_npc) current_npc->handleDeath(); + + if (storageWindow->isVisible()) storageWindow->close(); } } deathListener; } @@ -140,8 +145,6 @@ void PlayerHandler::handleMessage(MessageIn &msg) // Switch the actual map, deleting the previous one if necessary engine->changeMap(mapPath); - if (current_npc) current_npc->handleDeath(); - float scrollOffsetX = 0.0f; float scrollOffsetY = 0.0f; @@ -160,8 +163,8 @@ void PlayerHandler::handleMessage(MessageIn &msg) player_node->mY = y; logger->log("Adjust scrolling by %d:%d", - (int)scrollOffsetX, - (int)scrollOffsetY); + (int) scrollOffsetX, + (int) scrollOffsetY); viewport->scrollBy(scrollOffsetX, scrollOffsetY); } @@ -169,7 +172,7 @@ void PlayerHandler::handleMessage(MessageIn &msg) case SMSG_PLAYER_STAT_UPDATE_1: { - Sint16 type = msg.readInt16(); + int type = msg.readInt16(); Uint32 value = msg.readInt32(); switch (type) @@ -297,10 +300,10 @@ void PlayerHandler::handleMessage(MessageIn &msg) case SMSG_PLAYER_STAT_UPDATE_3: { - Sint32 type = msg.readInt32(); - Sint32 base = msg.readInt32(); - Sint32 bonus = msg.readInt32(); - Sint32 total = base + bonus; + int type = msg.readInt32(); + int base = msg.readInt32(); + int bonus = msg.readInt32(); + int total = base + bonus; switch (type) { case 0x000d: player_node->mAttr[LocalPlayer::STR] = total; @@ -321,9 +324,9 @@ void PlayerHandler::handleMessage(MessageIn &msg) case SMSG_PLAYER_STAT_UPDATE_4: { - Sint16 type = msg.readInt16(); - Sint8 fail = msg.readInt8(); - Sint8 value = msg.readInt8(); + int type = msg.readInt16(); + int fail = msg.readInt8(); + int value = msg.readInt8(); if (fail != 1) break; @@ -400,7 +403,7 @@ void PlayerHandler::handleMessage(MessageIn &msg) case SMSG_PLAYER_ARROW_MESSAGE: { - Sint16 type = msg.readInt16(); + int type = msg.readInt16(); switch (type) { case 0: diff --git a/src/net/ea/protocol.h b/src/net/ea/protocol.h index 55c0d8b6..b806b13b 100644 --- a/src/net/ea/protocol.h +++ b/src/net/ea/protocol.h @@ -121,30 +121,32 @@ #define CMSG_TRADE_RESPONSE 0x00e6 #define CMSG_ITEM_PICKUP 0x009f #define CMSG_MAP_LOADED 0x007d -#define CMSG_NPC_BUY_REQUEST 0x00c8 -#define CMSG_NPC_BUY_SELL_REQUEST 0x00c5 #define CMSG_CHAT_MESSAGE 0x008c #define CMSG_CHAT_WHISPER 0x0096 #define CMSG_CHAT_ANNOUNCE 0x0099 #define CMSG_CHAT_WHO 0x00c1 -#define CMSG_NPC_LIST_CHOICE 0x00b8 -#define CMSG_NPC_NEXT_REQUEST 0x00b9 -#define CMSG_NPC_SELL_REQUEST 0x00c9 -#define CMSG_NPC_INT_RESPONSE 0x0143 -#define CMSG_NPC_STR_RESPONSE 0x01d5 #define CMSG_SKILL_LEVELUP_REQUEST 0x0112 #define CMSG_STAT_UPDATE_REQUEST 0x00bb #define CMSG_TRADE_ITEM_ADD_REQUEST 0x00e8 #define CMSG_TRADE_CANCEL_REQUEST 0x00ed #define CMSG_TRADE_ADD_COMPLETE 0x00eb #define CMSG_TRADE_OK 0x00ef -#define CMSG_NPC_TALK 0x0090 #define CMSG_TRADE_REQUEST 0x00e4 #define CMSG_PLAYER_INVENTORY_USE 0x00a7 #define CMSG_PLAYER_INVENTORY_DROP 0x00a2 #define CMSG_PLAYER_EQUIP 0x00a9 #define CMSG_PLAYER_UNEQUIP 0x00ab +#define CMSG_NPC_TALK 0x0090 +#define CMSG_NPC_NEXT_REQUEST 0x00b9 +#define CMSG_NPC_CLOSE 0x0146 +#define CMSG_NPC_LIST_CHOICE 0x00b8 +#define CMSG_NPC_INT_RESPONSE 0x0143 +#define CMSG_NPC_STR_RESPONSE 0x01d5 +#define CMSG_NPC_BUY_SELL_REQUEST 0x00c5 +#define CMSG_NPC_BUY_REQUEST 0x00c8 +#define CMSG_NPC_SELL_REQUEST 0x00c9 + #define CMSG_PARTY_CREATE 0x00f9 #define CMSG_PARTY_INVITE 0x00fc #define CMSG_PARTY_INVITED 0x00ff diff --git a/src/net/ea/skillhandler.cpp b/src/net/ea/skillhandler.cpp index 4361b147..12c38aaa 100644 --- a/src/net/ea/skillhandler.cpp +++ b/src/net/ea/skillhandler.cpp @@ -85,14 +85,14 @@ void SkillHandler::handleMessage(MessageIn &msg) for (int k = 0; k < skillCount; k++) { - Sint16 skillId = msg.readInt16(); + int skillId = msg.readInt16(); msg.readInt16(); // target type msg.readInt16(); // unknown - Sint16 level = msg.readInt16(); - Sint16 sp = msg.readInt16(); + int level = msg.readInt16(); + int sp = msg.readInt16(); msg.readInt16(); // range std::string skillName = msg.readString(24); - Sint8 up = msg.readInt8(); + int up = msg.readInt8(); if (level != 0 || up != 0) { diff --git a/src/net/ea/tradehandler.cpp b/src/net/ea/tradehandler.cpp index 6c953a11..78472083 100644 --- a/src/net/ea/tradehandler.cpp +++ b/src/net/ea/tradehandler.cpp @@ -140,8 +140,8 @@ void TradeHandler::handleMessage(MessageIn &msg) case SMSG_TRADE_ITEM_ADD: { - Sint32 amount = msg.readInt32(); - Sint16 type = msg.readInt16(); + int amount = msg.readInt32(); + int type = msg.readInt16(); msg.readInt8(); // identified flag msg.readInt8(); // attribute msg.readInt8(); // refine @@ -166,7 +166,7 @@ void TradeHandler::handleMessage(MessageIn &msg) tradeWindow->receivedOk(true); return; } - Sint16 quantity = msg.readInt16(); + int quantity = msg.readInt16(); switch (msg.readInt8()) { diff --git a/src/net/tmwserv/beinghandler.cpp b/src/net/tmwserv/beinghandler.cpp index ac33b6fe..947d6b80 100644 --- a/src/net/tmwserv/beinghandler.cpp +++ b/src/net/tmwserv/beinghandler.cpp @@ -289,7 +289,7 @@ void BeingHandler::handleBeingsDamageMessage(MessageIn &msg) int damage = msg.readInt16(); if (being) { - being->takeDamage(damage); + being->takeDamage(0, damage, Being::HIT); } } } diff --git a/src/net/tmwserv/buysellhandler.cpp b/src/net/tmwserv/buysellhandler.cpp index 54750cbb..6e4b9d56 100644 --- a/src/net/tmwserv/buysellhandler.cpp +++ b/src/net/tmwserv/buysellhandler.cpp @@ -57,7 +57,7 @@ void BuySellHandler::handleMessage(MessageIn &msg) return; } - current_npc = static_cast< NPC * >(being); + current_npc = being->getId(); switch (msg.getId()) { diff --git a/src/net/tmwserv/npchandler.cpp b/src/net/tmwserv/npchandler.cpp index 53f3c7b2..b3e36749 100644 --- a/src/net/tmwserv/npchandler.cpp +++ b/src/net/tmwserv/npchandler.cpp @@ -55,7 +55,7 @@ void NPCHandler::handleMessage(MessageIn &msg) return; } - current_npc = static_cast< NPC * >(being); + current_npc = being->getId(); switch (msg.getId()) { diff --git a/src/net/tmwserv/playerhandler.cpp b/src/net/tmwserv/playerhandler.cpp index d4850f43..f4cc4b23 100644 --- a/src/net/tmwserv/playerhandler.cpp +++ b/src/net/tmwserv/playerhandler.cpp @@ -84,7 +84,9 @@ namespace { npcTextDialog->setVisible(false); buyDialog->setVisible(false); sellDialog->setVisible(false); +#ifdef EATHENA_SUPPORT buySellDialog->setVisible(false); +#endif current_npc = 0; } } deathListener; diff --git a/src/npc.cpp b/src/npc.cpp index 792d1aa4..df43d548 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -20,11 +20,13 @@ */ #include "animatedsprite.h" +#include "beingmanager.h" #include "npc.h" #include "particle.h" #include "text.h" #include "gui/npc_text.h" +#include "gui/palette.h" #ifdef TMWSERV_SUPPORT #include "net/tmwserv/gameserver/player.h" @@ -35,9 +37,8 @@ #include "resources/npcdb.h" -extern NpcTextDialog *npcTextDialog; - -NPC *current_npc = 0; +bool NPC::isTalking = false; +int current_npc = 0; static const int NAME_X_OFFSET = 15; static const int NAME_Y_OFFSET = 30; @@ -46,7 +47,7 @@ static const int NAME_Y_OFFSET = 30; NPC::NPC(Uint16 id, int job, Map *map): Player(id, job, map) #else -NPC::NPC(Uint32 id, Uint16 job, Map *map, Network *network): +NPC::NPC(int id, Uint16 job, Map *map, Network *network): Player(id, job, map), mNetwork(network) #endif @@ -59,7 +60,8 @@ NPC::NPC(Uint32 id, Uint16 job, Map *map, Network *network): i != info.sprites.end(); i++) { - if (c == VECTOREND_SPRITE) break; + if (c == VECTOREND_SPRITE) + break; std::string file = "graphics/sprites/" + (*i)->sprite; int variant = (*i)->variant; @@ -80,14 +82,12 @@ NPC::NPC(Uint32 id, Uint16 job, Map *map, Network *network): } mName = 0; - mNameColor = 0x21bbbb; + mNameColor = &guiPalette->getColor(Palette::NPC); } NPC::~NPC() { delete mName; - - if (current_npc == this) handleDeath(); } void NPC::setName(const std::string &name) @@ -96,7 +96,8 @@ void NPC::setName(const std::string &name) delete mName; mName = new Text(displayName, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, - gcn::Graphics::CENTER, gcn::Color(200, 200, 255)); + gcn::Graphics::CENTER, + &guiPalette->getColor(Palette::NPC)); Being::setName(displayName + " (NPC)"); } @@ -118,88 +119,25 @@ Being::Type NPC::getType() const void NPC::talk() { -#ifdef TMWSERV_SUPPORT - Net::GameServer::Player::talkToNPC(mId, true); -#else - MessageOut outMsg(mNetwork); - outMsg.writeInt16(CMSG_NPC_TALK); - outMsg.writeInt32(mId); - outMsg.writeInt8(0); -#endif - current_npc = this; -} + if (isTalking) + return; -void NPC::nextDialog() -{ -#ifdef TMWSERV_SUPPORT - Net::GameServer::Player::talkToNPC(mId, false); -#else - MessageOut outMsg(mNetwork); - outMsg.writeInt16(CMSG_NPC_NEXT_REQUEST); - outMsg.writeInt32(mId); -#endif -} + isTalking = true; -void NPC::dialogChoice(char choice) -{ #ifdef TMWSERV_SUPPORT - Net::GameServer::Player::selectFromNPC(mId, choice); + Net::GameServer::Player::talkToNPC(mId, true); #else - MessageOut outMsg(mNetwork); - outMsg.writeInt16(CMSG_NPC_LIST_CHOICE); - outMsg.writeInt32(mId); - outMsg.writeInt8(choice); -#endif -} - -void NPC::integerInput(int value) -{ -#ifdef EATHENA_SUPPORT - MessageOut outMsg(mNetwork); - outMsg.writeInt16(CMSG_NPC_INT_RESPONSE); - outMsg.writeInt32(mId); - outMsg.writeInt32(value); -#endif -} + if (!mNetwork) + return; -void NPC::stringInput(const std::string &value) -{ -#ifdef EATHENA_SUPPORT - MessageOut outMsg(mNetwork); - outMsg.writeInt16(CMSG_NPC_STR_RESPONSE); - outMsg.writeInt16(value.length() + 9); - outMsg.writeInt32(mId); - outMsg.writeString(value, value.length()); - outMsg.writeInt8(0); -#endif -} - -/* - * TODO Unify the buy() and sell() methods, without sacrificing readability of - * the code calling the method. buy(bool buySell) would be bad... - */ -void NPC::buy() -{ - // XXX Convert for new server -#ifdef EATHENA_SUPPORT MessageOut outMsg(mNetwork); - outMsg.writeInt16(CMSG_NPC_BUY_SELL_REQUEST); + outMsg.writeInt16(CMSG_NPC_TALK); + outMsg.writeInt16(CMSG_NPC_TALK); outMsg.writeInt32(mId); outMsg.writeInt8(0); #endif } -void NPC::sell() -{ - // XXX Convert for new server -#ifdef EATHENA_SUPPORT - MessageOut outMsg(mNetwork); - outMsg.writeInt16(CMSG_NPC_BUY_SELL_REQUEST); - outMsg.writeInt32(mId); - outMsg.writeInt8(1); -#endif -} - void NPC::updateCoords() { if (mName) @@ -215,13 +153,3 @@ void NPC::updateCoords() mName->adviseXY(px, py); } } - -void NPC::handleDeath() -{ - printf("NPC::handleDeath\n"); - if (this != current_npc) return; - - if (npcTextDialog->isVisible()) - npcTextDialog->showCloseButton(); - else current_npc = NULL; -} @@ -36,7 +36,7 @@ class NPC : public Player #ifdef TMWSERV_SUPPORT NPC(Uint16 id, int sprite, Map *map); #else - NPC(Uint32 id, Uint16 job, Map *map, Network *network); + NPC(int id, Uint16 job, Map *map, Network *network); #endif ~NPC(); @@ -48,19 +48,6 @@ class NPC : public Player virtual Type getType() const; void talk(); - void nextDialog(); - void dialogChoice(char choice); - void integerInput(int value); - void stringInput(const std::string &value); - - void buy(); - void sell(); - - /** - * Call this to ease clean up of the current NPC, without causing - * interface problems - */ - void handleDeath(); /** * Gets the way an NPC is blocked by other things on the map @@ -68,6 +55,7 @@ class NPC : public Player virtual unsigned char getWalkMask() const { return 0x83; } // blocked like a monster by walls, monsters and characters ( bin 1000 0011) + static bool isTalking; protected: /** * Gets the way a monster blocks pathfinding for other objects @@ -83,6 +71,6 @@ class NPC : public Player Text *mName; }; -extern NPC *current_npc; +extern int current_npc; #endif diff --git a/src/openglgraphics.cpp b/src/openglgraphics.cpp index e7e7b204..7a1d259e 100644 --- a/src/openglgraphics.cpp +++ b/src/openglgraphics.cpp @@ -26,12 +26,12 @@ #include "resources/image.h" +#ifdef USE_OPENGL + #ifdef __APPLE__ #include <OpenGL/OpenGL.h> #endif -#ifdef USE_OPENGL - #ifndef GL_TEXTURE_RECTANGLE_ARB #define GL_TEXTURE_RECTANGLE_ARB 0x84F5 #define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 @@ -62,18 +62,17 @@ bool OpenGLGraphics::setVideoMode(int w, int h, int bpp, bool fs, bool hwaccel) mFullscreen = fs; mHWAccel = hwaccel; - if (fs) { + if (fs) displayFlags |= SDL_FULLSCREEN; - } SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - if (!(mScreen = SDL_SetVideoMode(w, h, bpp, displayFlags))) { + if (!(mScreen = SDL_SetVideoMode(w, h, bpp, displayFlags))) return false; - } #ifdef __APPLE__ - if (mSync) { + if (mSync) + { const GLint VBL = 1; CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &VBL); } @@ -110,6 +109,8 @@ bool OpenGLGraphics::setVideoMode(int w, int h, int bpp, bool fs, bool hwaccel) bool OpenGLGraphics::drawImage(Image *image, int srcX, int srcY, int dstX, int dstY, int width, int height, bool useColor) { + if (!image) return false; + srcX += image->mBounds.x; srcY += image->mBounds.y; @@ -157,13 +158,80 @@ bool OpenGLGraphics::drawImage(Image *image, int srcX, int srcY, glEnd(); if (!useColor) - { glColor4ub(mColor.r, mColor.g, mColor.b, mColor.a); - } return true; } +/* Optimising the functions that Graphics::drawImagePattern would call, + * so that glBegin...glEnd are outside the main loop. */ +void OpenGLGraphics::drawImagePattern(Image *image, int x, int y, int w, int h) +{ + if (!image) return; + + const int srcX = image->mBounds.x; + const int srcY = image->mBounds.y; + + const float texX1 = srcX / (float)image->mTexWidth; + const float texY1 = srcY / (float)image->mTexHeight; + + int iw = image->getWidth(); + int ih = image->getHeight(); + if (iw == 0 || ih == 0) + return; + + glColor4f(1.0f, 1.0f, 1.0f, image->mAlpha); + + glBindTexture(Image::mTextureType, image->mGLImage); + + setTexturingAndBlending(true); + + // Draw a set of textured rectangles + glBegin(GL_QUADS); + + for (int py = 0; py < h; py += ih) + { + int height = (py + ih >= h) ? h - py : ih; + int dstY = y + py; + for (int px = 0; px < w; px += iw) + { + int width = (px + iw >= w) ? w - px : iw; + int dstX = x + px; + + if (Image::mTextureType == GL_TEXTURE_2D) + { + // Find OpenGL normalized texture coordinates. + float texX2 = (srcX + width) / (float) image->mTexWidth; + float texY2 = (srcY + height) / (float) image->mTexHeight; + + glTexCoord2f(texX1, texY1); + glVertex2i(dstX, dstY); + glTexCoord2f(texX2, texY1); + glVertex2i(dstX + width, dstY); + glTexCoord2f(texX2, texY2); + glVertex2i(dstX + width, dstY + height); + glTexCoord2f(texX1, texY2); + glVertex2i(dstX, dstY + height); + } + else + { + glTexCoord2i(srcX, srcY); + glVertex2i(dstX, dstY); + glTexCoord2i(srcX + width, srcY); + glVertex2i(dstX + width, dstY); + glTexCoord2i(srcX + width, srcY + height); + glVertex2i(dstX + width, dstY + height); + glTexCoord2i(srcX, srcY + height); + glVertex2i(dstX, dstY + height); + } + } + } + + glEnd(); + + glColor4ub(mColor.r, mColor.g, mColor.b, mColor.a); +} + void OpenGLGraphics::updateScreen() { glFlush(); @@ -205,9 +273,8 @@ SDL_Surface* OpenGLGraphics::getScreenshot() w, h, 24, 0xff0000, 0x00ff00, 0x0000ff, 0x000000); - if (SDL_MUSTLOCK(screenshot)) { + if (SDL_MUSTLOCK(screenshot)) SDL_LockSurface(screenshot); - } // Grap the pixel buffer and write it to the SDL surface glPixelStorei(GL_PACK_ALIGNMENT, 1); @@ -229,9 +296,8 @@ SDL_Surface* OpenGLGraphics::getScreenshot() free(buf); - if (SDL_MUSTLOCK(screenshot)) { + if (SDL_MUSTLOCK(screenshot)) SDL_UnlockSurface(screenshot); - } return screenshot; } @@ -241,7 +307,8 @@ bool OpenGLGraphics::pushClipArea(gcn::Rectangle area) int transX = 0; int transY = 0; - if (!mClipStack.empty()) { + if (!mClipStack.empty()) + { transX = -mClipStack.top().xOffset; transY = -mClipStack.top().yOffset; } @@ -266,9 +333,7 @@ void OpenGLGraphics::popClipArea() gcn::Graphics::popClipArea(); if (mClipStack.empty()) - { return; - } glPopMatrix(); glScissor(mClipStack.top().x, @@ -324,8 +389,10 @@ void OpenGLGraphics::setTargetPlane(int width, int height) void OpenGLGraphics::setTexturingAndBlending(bool enable) { - if (enable) { - if (!mTexture) { + if (enable) + { + if (!mTexture) + { glEnable(Image::mTextureType); mTexture = true; } @@ -335,16 +402,22 @@ void OpenGLGraphics::setTexturingAndBlending(bool enable) glEnable(GL_BLEND); mAlpha = true; } - } else { - if (mAlpha && !mColorAlpha) { + } + else + { + if (mAlpha && !mColorAlpha) + { glDisable(GL_BLEND); mAlpha = false; - } else if (!mAlpha && mColorAlpha) { + } + else if (!mAlpha && mColorAlpha) + { glEnable(GL_BLEND); mAlpha = true; } - if (mTexture) { + if (mTexture) + { glDisable(Image::mTextureType); mTexture = false; } diff --git a/src/openglgraphics.h b/src/openglgraphics.h index 566d6252..469e1f53 100644 --- a/src/openglgraphics.h +++ b/src/openglgraphics.h @@ -46,6 +46,10 @@ class OpenGLGraphics : public Graphics int width, int height, bool useColor); + void drawImagePattern(Image *image, + int x, int y, + int w, int h); + void updateScreen(); void _beginDraw(); diff --git a/src/particle.cpp b/src/particle.cpp index f021f6e5..21844f01 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -176,7 +176,8 @@ bool Particle::update() mVelocity *= mBounce; mVelocity.z = -mVelocity.z; } - else { + else + { mAlive = false; } } @@ -184,16 +185,12 @@ bool Particle::update() // Update child emitters if ((mLifetimePast-1)%Particle::emitterSkip == 0) { - for ( EmitterIterator e = mChildEmitters.begin(); - e != mChildEmitters.end(); - e++ - ) + for (EmitterIterator e = mChildEmitters.begin(); + e != mChildEmitters.end(); e++) { Particles newParticles = (*e)->createParticles(mLifetimePast); - for ( ParticleIterator p = newParticles.begin(); - p != newParticles.end(); - p++ - ) + for (ParticleIterator p = newParticles.begin(); + p != newParticles.end(); p++) { (*p)->moveBy(mPos); mChildParticles.push_back (*p); @@ -218,7 +215,9 @@ bool Particle::update() if ((*p)->update()) { p++; - } else { + } + else + { delete (*p); p = mChildParticles.erase(p); } @@ -236,8 +235,7 @@ void Particle::moveBy(const Vector &change) { mPos += change; for (ParticleIterator p = mChildParticles.begin(); - p != mChildParticles.end(); - p++) + p != mChildParticles.end(); p++) { if ((*p)->doesFollow()) { @@ -278,20 +276,21 @@ Particle *Particle::addEffect(const std::string &particleEffectFile, xmlNodePtr node; // Animation - if ((node = XML::findFirstChildByName( - effectChildNode, "animation"))) { + if ((node = XML::findFirstChildByName(effectChildNode, "animation"))) + { newParticle = new AnimationParticle(mMap, node); } // Image - else if ((node = XML::findFirstChildByName( - effectChildNode, "image"))) { + else if ((node = XML::findFirstChildByName(effectChildNode, "image"))) + { Image *img= resman->getImage((const char*) node->xmlChildrenNode->content); newParticle = new ImageParticle(mMap, img); } // Other - else { + else + { newParticle = new Particle(mMap); } @@ -314,7 +313,8 @@ Particle *Particle::addEffect(const std::string &particleEffectFile, continue; ParticleEmitter *newEmitter; - newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap, rotation); + newEmitter = new ParticleEmitter(emitterNode, newParticle, mMap, + rotation); newParticle->addEmitter(newEmitter); } @@ -324,12 +324,11 @@ Particle *Particle::addEffect(const std::string &particleEffectFile, return newParticle; } -Particle *Particle::addTextSplashEffect(const std::string &text, - int colorR, int colorG, int colorB, - gcn::Font *font, int x, int y) +Particle *Particle::addTextSplashEffect(const std::string &text, int x, int y, + const gcn::Color *color, + gcn::Font *font, bool outline) { - Particle *newParticle = new TextParticle(mMap, text, colorR, colorG, colorB, - font); + Particle *newParticle = new TextParticle(mMap, text, color, font, outline); newParticle->moveTo(x, y); newParticle->setVelocity(((rand() % 100) - 50) / 200.0f, // X ((rand() % 100) - 50) / 200.0f, // Y @@ -345,11 +344,11 @@ Particle *Particle::addTextSplashEffect(const std::string &text, } Particle *Particle::addTextRiseFadeOutEffect(const std::string &text, - int colorR, int colorG, int colorB, - gcn::Font *font, - int x, int y) + int x, int y, + const gcn::Color *color, + gcn::Font *font, bool outline) { - Particle *newParticle = new TextParticle(mMap, text, colorR, colorG, colorB, font); + Particle *newParticle = new TextParticle(mMap, text, color, font, outline); newParticle->moveTo(x, y); newParticle->setVelocity(0.0f, 0.0f, 0.5f); newParticle->setGravity(0.0015f); diff --git a/src/particle.h b/src/particle.h index 008c343b..f3f5571a 100644 --- a/src/particle.h +++ b/src/particle.h @@ -105,17 +105,16 @@ class Particle : public Sprite /** * Creates a standalone text particle. */ - Particle *addTextSplashEffect(const std::string &text, - int colorR, int colorG, int colorB, - gcn::Font *font, int x, int y); + Particle *addTextSplashEffect(const std::string &text, int x, int y, + const gcn::Color *color, gcn::Font *font, + bool outline = false); /** * Creates a standalone text particle. */ Particle *addTextRiseFadeOutEffect(const std::string &text, - int colorR, int colorG, int colorB, - gcn::Font *font, - int x, int y); + int x, int y, const gcn::Color *color, gcn::Font *font, + bool outline = false); /** * Adds an emitter to the particle. diff --git a/src/player.cpp b/src/player.cpp index 09eec0d1..8754e986 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -24,9 +24,13 @@ #ifdef TMWSERV_SUPPORT #include "guild.h" #endif +#include "localplayer.h" +#include "particle.h" #include "player.h" #include "text.h" +#include "gui/palette.h" + #include "resources/colordb.h" #include "resources/itemdb.h" @@ -52,17 +56,19 @@ void Player::setName(const std::string &name) { if (mIsGM) { - mNameColor = 0x009000; + mNameColor = &guiPalette->getColor(Palette::GM); mName = new FlashText("(GM) " + name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, gcn::Graphics::CENTER, - gcn::Color(0, 255, 0)); + &guiPalette->getColor(Palette::GM_NAME)); } else { - mNameColor = 0x202020; + mNameColor = &guiPalette->getColor(Palette::PLAYER); mName = new FlashText(name, mPx + NAME_X_OFFSET, mPy + NAME_Y_OFFSET, gcn::Graphics::CENTER, - gcn::Color(255, 255, 255)); + (this == player_node) ? + &guiPalette->getColor(Palette::SELF) : + &guiPalette->getColor(Palette::PC)); } Being::setName(name); } @@ -94,7 +100,10 @@ void Player::logic() break; case ATTACK: + int rotation = 0; + std::string particleEffect = ""; int frames = 4; + if (mEquippedWeapon && mEquippedWeapon->getAttackType() == ACTION_ATTACK_BOW) { @@ -103,6 +112,26 @@ void Player::logic() mFrame = (get_elapsed_time(mWalkTime) * frames) / mAttackSpeed; + //attack particle effect + if (mEquippedWeapon) + particleEffect = mEquippedWeapon->getParticleEffect(); + + if (!particleEffect.empty() && mParticleEffects && mFrame == 1) + { + switch (mDirection) + { + case DOWN: rotation = 0; break; + case LEFT: rotation = 90; break; + case UP: rotation = 180; break; + case RIGHT: rotation = 270; break; + default: break; + } + Particle *p; + p = particleEngine->addEffect("graphics/particles/" + + particleEffect, 0, 0, rotation); + controlParticle(p); + } + if (mFrame >= frames) nextStep(); diff --git a/src/player_relations.cpp b/src/player_relations.cpp index c82876e1..ef2ef1bc 100644 --- a/src/player_relations.cpp +++ b/src/player_relations.cpp @@ -28,6 +28,8 @@ #include "player.h" #include "player_relations.h" +#include "utils/dtor.h" + #define PLAYER_IGNORE_STRATEGY_NOP "nop" #define PLAYER_IGNORE_STRATEGY_EMOTE0 "emote0" #define DEFAULT_IGNORE_STRATEGY PLAYER_IGNORE_STRATEGY_EMOTE0 @@ -37,7 +39,6 @@ #define IGNORE_EMOTE_TIME 100 - // (De)serialisation class class PlayerConfSerialiser : public ConfigurationListManager<std::pair<std::string, PlayerRelation *>, std::map<std::string, PlayerRelation *> *> @@ -92,6 +93,11 @@ PlayerRelationsManager::PlayerRelationsManager() : { } +PlayerRelationsManager::~PlayerRelationsManager() +{ + delete_all(mIgnoreStrategies); +} + void PlayerRelationsManager::clear() { std::vector<std::string> *names = getPlayers(); @@ -345,24 +351,23 @@ private: -static std::vector<PlayerIgnoreStrategy *> player_ignore_strategies; - std::vector<PlayerIgnoreStrategy *> * PlayerRelationsManager::getPlayerIgnoreStrategies() { - if (player_ignore_strategies.size() == 0) { + if (mIgnoreStrategies.size() == 0) + { // not initialised yet? - player_ignore_strategies.push_back(new PIS_emote(FIRST_IGNORE_EMOTE, - "floating '...' bubble", - PLAYER_IGNORE_STRATEGY_EMOTE0)); - player_ignore_strategies.push_back(new PIS_emote(FIRST_IGNORE_EMOTE + 1, - "floating bubble", - "emote1")); - player_ignore_strategies.push_back(new PIS_nothing()); - player_ignore_strategies.push_back(new PIS_dotdotdot()); - player_ignore_strategies.push_back(new PIS_blinkname()); + mIgnoreStrategies.push_back(new PIS_emote(FIRST_IGNORE_EMOTE, + "floating '...' bubble", + PLAYER_IGNORE_STRATEGY_EMOTE0)); + mIgnoreStrategies.push_back(new PIS_emote(FIRST_IGNORE_EMOTE + 1, + "floating bubble", + "emote1")); + mIgnoreStrategies.push_back(new PIS_nothing()); + mIgnoreStrategies.push_back(new PIS_dotdotdot()); + mIgnoreStrategies.push_back(new PIS_blinkname()); } - return &player_ignore_strategies; + return &mIgnoreStrategies; } diff --git a/src/player_relations.h b/src/player_relations.h index 1eb4ede6..a6c6a115 100644 --- a/src/player_relations.h +++ b/src/player_relations.h @@ -94,6 +94,7 @@ class PlayerRelationsManager { public: PlayerRelationsManager(); + ~PlayerRelationsManager(); /** * Initialise player relations manager (load config file etc.) @@ -142,7 +143,6 @@ public: */ void removePlayer(const std::string &name); - /** * Retrieves the default permissions. */ @@ -153,8 +153,6 @@ public: */ void setDefault(unsigned int permissions); - - /** * Retrieves all known player ignore strategies. * @@ -232,6 +230,7 @@ private: PlayerIgnoreStrategy *mIgnoreStrategy; std::map<std::string, PlayerRelation *> mRelations; std::list<PlayerRelationsListener *> mListeners; + std::vector<PlayerIgnoreStrategy *> mIgnoreStrategies; }; diff --git a/src/resources/dye.cpp b/src/resources/dye.cpp index 22bd2411..1e4fd2fd 100644 --- a/src/resources/dye.cpp +++ b/src/resources/dye.cpp @@ -25,7 +25,7 @@ #include "../log.h" -Palette::Palette(const std::string &description) +DyePalette::DyePalette(const std::string &description) { int size = description.length(); if (size == 0) return; @@ -62,7 +62,7 @@ Palette::Palette(const std::string &description) logger->log("Error, invalid embedded palette: %s", description.c_str()); } -void Palette::getColor(int intensity, int color[3]) const +void DyePalette::getColor(int intensity, int color[3]) const { if (intensity == 0) { @@ -111,7 +111,7 @@ void Palette::getColor(int intensity, int color[3]) const Dye::Dye(const std::string &description) { for (int i = 0; i < 7; ++i) - mPalettes[i] = 0; + mDyePalettes[i] = 0; if (description.empty()) return; @@ -141,7 +141,7 @@ Dye::Dye(const std::string &description) logger->log("Error, invalid dye: %s", description.c_str()); return; } - mPalettes[i] = new Palette(description.substr(pos + 2, next_pos - pos - 2)); + mDyePalettes[i] = new DyePalette(description.substr(pos + 2, next_pos - pos - 2)); ++next_pos; } while (next_pos < length); @@ -150,7 +150,7 @@ Dye::Dye(const std::string &description) Dye::~Dye() { for (int i = 0; i < 7; ++i) - delete mPalettes[i]; + delete mDyePalettes[i]; } void Dye::update(int color[3]) const @@ -170,8 +170,8 @@ void Dye::update(int color[3]) const int i = (color[0] != 0) | ((color[1] != 0) << 1) | ((color[2] != 0) << 2); - if (mPalettes[i - 1]) - mPalettes[i - 1]->getColor(cmax, color); + if (mDyePalettes[i - 1]) + mDyePalettes[i - 1]->getColor(cmax, color); } void Dye::instantiate(std::string &target, const std::string &palettes) diff --git a/src/resources/dye.h b/src/resources/dye.h index 3cef334a..d0d010bc 100644 --- a/src/resources/dye.h +++ b/src/resources/dye.h @@ -28,7 +28,7 @@ /** * Class for performing a linear interpolation between colors. */ -class Palette +class DyePalette { public: @@ -37,7 +37,7 @@ class Palette * The string is either a file name or a sequence of hexadecimal RGB * values separated by ',' and starting with '#'. */ - Palette(const std::string &pallete); + DyePalette(const std::string &pallete); /** * Gets a pixel color depending on its intensity. @@ -89,7 +89,7 @@ class Dye * * Red, Green, Yellow, Blue, Magenta, White (or rather gray). */ - Palette *mPalettes[7]; + DyePalette *mDyePalettes[7]; }; #endif diff --git a/src/resources/emotedb.cpp b/src/resources/emotedb.cpp index 5e9a146c..5bc8407a 100644 --- a/src/resources/emotedb.cpp +++ b/src/resources/emotedb.cpp @@ -21,6 +21,7 @@ #include "emotedb.h" +#include "../animatedsprite.h" #include "../log.h" #include "../utils/xml.h" @@ -41,9 +42,8 @@ void EmoteDB::load() mLastEmote = 0; EmoteSprite *unknownSprite = new EmoteSprite; - unknownSprite->sprite = "error.xml"; + unknownSprite->sprite = AnimatedSprite::load("error.xml"); unknownSprite->name = "unknown"; - unknownSprite->variant = 0; mUnknown.sprites.push_back(unknownSprite); logger->log("Initializing emote database..."); @@ -77,8 +77,10 @@ void EmoteDB::load() if (xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) { EmoteSprite *currentSprite = new EmoteSprite; - currentSprite->sprite = (const char*) spriteNode->xmlChildrenNode->content; - currentSprite->variant = XML::getProperty(spriteNode, "variant", 0); + std::string file = "graphics/sprites/" + (std::string) + (const char*) spriteNode->xmlChildrenNode->content; + currentSprite->sprite = AnimatedSprite::load(file, + XML::getProperty(spriteNode, "variant", 0)); currentInfo->sprites.push_back(currentSprite); } else if (xmlStrEqual(spriteNode->name, BAD_CAST "particlefx")) @@ -102,6 +104,7 @@ void EmoteDB::unload() { while (!i->second->sprites.empty()) { + delete i->second->sprites.front()->sprite; delete i->second->sprites.front(); i->second->sprites.pop_front(); } @@ -112,6 +115,7 @@ void EmoteDB::unload() while (!mUnknown.sprites.empty()) { + delete mUnknown.sprites.front()->sprite; delete mUnknown.sprites.front(); mUnknown.sprites.pop_front(); } @@ -119,21 +123,27 @@ void EmoteDB::unload() mLoaded = false; } -const EmoteInfo& EmoteDB::get(int id) +const EmoteInfo *EmoteDB::get(int id) { EmoteInfosIterator i = mEmoteInfos.find(id); if (i == mEmoteInfos.end()) { logger->log("EmoteDB: Warning, unknown emote ID %d requested", id); - return mUnknown; + return &mUnknown; } else { - return *(i->second); + return i->second; } } +const AnimatedSprite* EmoteDB::getAnimation(int id) +{ + const EmoteInfo *info = get(id); + return info->sprites.front()->sprite; +} + const int& EmoteDB::getLast() { return mLastEmote; diff --git a/src/resources/emotedb.h b/src/resources/emotedb.h index ad21722a..691881c8 100644 --- a/src/resources/emotedb.h +++ b/src/resources/emotedb.h @@ -26,11 +26,12 @@ #include <map> #include <string> +class AnimatedSprite; + struct EmoteSprite { - std::string sprite; + const AnimatedSprite *sprite; std::string name; - int variant; }; struct EmoteInfo @@ -50,7 +51,9 @@ namespace EmoteDB void unload(); - const EmoteInfo& get(int id); + const EmoteInfo *get(int id); + + const AnimatedSprite *getAnimation(int id); const int& getLast(); diff --git a/src/resources/image.cpp b/src/resources/image.cpp index 7a7e6ac8..b696389f 100644 --- a/src/resources/image.cpp +++ b/src/resources/image.cpp @@ -25,6 +25,7 @@ #include "image.h" #include "../log.h" +#include "../position.h" #ifdef USE_OPENGL bool Image::mUseOpenGL = false; @@ -46,8 +47,7 @@ Image::Image(SDL_Surface *image): } #ifdef USE_OPENGL -Image::Image(GLuint glimage, int width, int height, - int texWidth, int texHeight): +Image::Image(GLuint glimage, int width, int height, int texWidth, int texHeight): mGLImage(glimage), mTexWidth(texWidth), mTexHeight(texHeight), @@ -315,6 +315,81 @@ void Image::setAlpha(float a) } } +Image* Image::merge(Image* image, const Position& pos) +{ + SDL_Surface* surface = new SDL_Surface(*(image->mImage)); + + 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 *surface_fmt = surface->format; + int current_offset, surface_offset; + Position offset(0, 0); + + SDL_LockSurface(surface); + SDL_LockSurface(mImage); + // for each pixel lines of a source image + for (offset.x = (pos.x > 0 ? 0 : -pos.x); offset.x < image->getWidth() && + pos.x + offset.x < getWidth(); offset.x++) + { + for (offset.y = (pos.y > 0 ? 0 : -pos.y); offset.y < image->getHeight() + && pos.y + offset.y < getHeight(); offset.y++) + { + // Computing offset on both images + current_offset = (pos.y + offset.y) * getWidth() + pos.x + offset.x; + surface_offset = offset.y * surface->w + offset.x; + + // Retrieving a pixel to merge + surface_pix = ((Uint32*) surface->pixels)[surface_offset]; + cur_pix = ((Uint32*) mImage->pixels)[current_offset]; + + // Retreiving each channel of the pixel using pixel format + r = (Uint8)(((surface_pix & surface_fmt->Rmask) >> + surface_fmt->Rshift) << surface_fmt->Rloss); + g = (Uint8)(((surface_pix & surface_fmt->Gmask) >> + surface_fmt->Gshift) << surface_fmt->Gloss); + b = (Uint8)(((surface_pix & surface_fmt->Bmask) >> + surface_fmt->Bshift) << surface_fmt->Bloss); + a = (Uint8)(((surface_pix & surface_fmt->Amask) >> + surface_fmt->Ashift) << surface_fmt->Aloss); + + // Retreiving previous alpha value + p_a = (Uint8)(((cur_pix & current_fmt->Amask) >> + current_fmt->Ashift) << current_fmt->Aloss); + + // new pixel with no alpha or nothing on previous pixel + if (a == SDL_ALPHA_OPAQUE || (p_a == 0 && a > 0)) + ((Uint32 *)(surface->pixels))[current_offset] = + SDL_MapRGBA(current_fmt, r, g, b, a); + else if (a > 0) + { // alpha is lower => merge color with previous value + f_a = (double) a / 255.0; + f_ca = 1.0 - f_a; + f_pa = (double) p_a / 255.0; + p_r = (Uint8)(((cur_pix & current_fmt->Rmask) >> + current_fmt->Rshift) << current_fmt->Rloss); + p_g = (Uint8)(((cur_pix & current_fmt->Gmask) >> + current_fmt->Gshift) << current_fmt->Gloss); + p_b = (Uint8)(((cur_pix & current_fmt->Bmask) >> + current_fmt->Bshift) << current_fmt->Bloss); + r = (Uint8)((double) p_r * f_ca * f_pa + (double)r * f_a); + g = (Uint8)((double) p_g * f_ca * f_pa + (double)g * f_a); + b = (Uint8)((double) p_b * f_ca * f_pa + (double)b * f_a); + a = (a > p_a ? a : p_a); + ((Uint32 *)(surface->pixels))[current_offset] = + SDL_MapRGBA(current_fmt, r, g, b, a); + } + } + } + SDL_UnlockSurface(surface); + SDL_UnlockSurface(mImage); + + Image* newImage = new Image(surface); + + return newImage; +} + float Image::getAlpha() { return mAlpha; diff --git a/src/resources/image.h b/src/resources/image.h index 3160add8..260aeeba 100644 --- a/src/resources/image.h +++ b/src/resources/image.h @@ -40,6 +40,7 @@ #include "resource.h" class Dye; +class Position; /** * Defines a class for loading and storing images. @@ -129,6 +130,15 @@ class Image : public Resource static void setLoadAsOpenGL(bool useOpenGL); #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, const Position& pos); + protected: /** * Constructor. diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp index 7304f8a7..f7162db5 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -175,6 +175,10 @@ void ItemDB::load() { if (xmlStrEqual(itemChild->name, BAD_CAST "sprite")) { + std::string attackParticle = XML::getProperty( + itemChild, "particle-effect", ""); + itemInfo->setParticleEffect(attackParticle); + loadSpriteRef(itemInfo, itemChild); } else if (xmlStrEqual(itemChild->name, BAD_CAST "sound")) @@ -204,9 +208,12 @@ void ItemDB::load() if (param == error_value) \ logger->log("ItemDB: Missing " #param " attribute for item %i!",id) - CHECK_PARAM(name, ""); + if (id >= 0) + { + CHECK_PARAM(name, ""); + CHECK_PARAM(description, ""); + } CHECK_PARAM(image, ""); - CHECK_PARAM(description, ""); // CHECK_PARAM(effect, ""); // CHECK_PARAM(type, 0); // CHECK_PARAM(weight, 0); diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h index 10749c9e..51b39876 100644 --- a/src/resources/iteminfo.h +++ b/src/resources/iteminfo.h @@ -138,6 +138,11 @@ class ItemInfo const std::string& getName() const { return mName; } + void setParticleEffect(const std::string &particleEffect) + { mParticle = particleEffect; } + + std::string getParticleEffect() const { return mParticle; } + void setImageName(const std::string &imageName) { mImageName = imageName; } @@ -198,22 +203,23 @@ class ItemInfo const std::string& getSound(EquipmentSoundEvent event) const; protected: - std::string mImageName; /**< The filename of the icon image. */ + std::string mImageName; /**< The filename of the icon image. */ std::string mName; - std::string mDescription; /**< Short description. */ - std::string mEffect; /**< Description of effects. */ + std::string mDescription; /**< Short description. */ + std::string mEffect; /**< Description of effects. */ #ifdef TMWSERV_SUPPORT - char mType; /**< Item type. */ + char mType; /**< Item type. */ #else - std::string mType; /**< Item type. */ + std::string mType; /**< Item type. */ #endif - short mWeight; /**< Weight in grams. */ - int mView; /**< Item ID of how this item looks. */ - int mId; /**< Item ID */ + std::string mParticle; /**< Particle effect used with this item */ + short mWeight; /**< Weight in grams. */ + int mView; /**< Item ID of how this item looks. */ + int mId; /**< Item ID */ // Equipment related members - SpriteAction mAttackType; /**< Attack type, in case of weapon. */ - int mAttackRange; /**< Attack range, will be zero if non weapon. */ + SpriteAction mAttackType; /**< Attack type, in case of weapon. */ + int mAttackRange; /**< Attack range, will be zero if non weapon. */ /** Maps gender to sprite filenames. */ std::map<int, std::string> mAnimationFiles; diff --git a/src/shopitem.cpp b/src/shopitem.cpp index 6547aaf1..ee55799f 100644 --- a/src/shopitem.cpp +++ b/src/shopitem.cpp @@ -23,10 +23,62 @@ #include "units.h" -ShopItem::ShopItem(int id, int quantity, int price): - Item(id, quantity), - mPrice(price) +ShopItem::ShopItem (int inventoryIndex, int id, + int quantity, int price) : + Item (id, 0), mPrice(price) { mDisplayName = getInfo().getName() + " (" + Units::formatCurrency(mPrice).c_str() + ")"; + setInvIndex(inventoryIndex); + addDuplicate(inventoryIndex, quantity); +} + +ShopItem::ShopItem (int id, int price) : Item (id, 0), mPrice(price) +{ + mDisplayName = getInfo().getName() + + " (" + Units::formatCurrency(mPrice).c_str() + ")"; + setInvIndex(-1); + addDuplicate(-1, 0); +} + +ShopItem::~ShopItem() +{ + /** Clear all remaining duplicates on Object destruction. */ + while (!mDuplicates.empty()) + { + delete mDuplicates.top(); + mDuplicates.pop(); + } +} + +void ShopItem::addDuplicate(int inventoryIndex, int quantity) +{ + DuplicateItem* di = new DuplicateItem; + di->inventoryIndex = inventoryIndex; + di->quantity = quantity; + mDuplicates.push(di); + mQuantity += quantity; +} + + +void ShopItem::addDuplicate() +{ + DuplicateItem* di = new DuplicateItem; + di->inventoryIndex = -1; + di->quantity = 0; + mDuplicates.push(di); +} + +int ShopItem::sellCurrentDuplicate(int quantity) +{ + DuplicateItem* dupl = mDuplicates.top(); + int sellCount = quantity <= dupl->quantity ? quantity : dupl->quantity; + dupl->quantity -= sellCount; + mQuantity -= sellCount; + if (dupl->quantity == 0) + { + delete dupl; + mDuplicates.pop(); + } + return sellCount; } diff --git a/src/shopitem.h b/src/shopitem.h index 50af4991..08a5c8d2 100644 --- a/src/shopitem.h +++ b/src/shopitem.h @@ -22,27 +22,100 @@ #ifndef _SHOPITEM_H #define _SHOPITEM_H +#include <stack> + #include "item.h" /** - * Represents an item in a shop inventory. + * Represents an item in a shop inventory. It can store quantity and inventory + * indices of duplicate entries in the shop as well. */ class ShopItem : public Item { public: /** - * Constructor. + * Constructor. Creates a new ShopItem. + * + * @param inventoryIndex the inventory index of the item + * @param id the id of the item + * @param quantity number of available copies of the item + * @param price price of the item + */ + ShopItem(int inventoryIndex, int id, int quantity, int price); + + /** + * Constructor. Creates a new ShopItem. Inventory index will be set to + * -1 and quantity to 0. + * + * @param id the id of the item + * @param price price of the item + */ + ShopItem(int id, int price); + + /** + * Destructor. + */ + ~ShopItem(); + + /** + * Add a duplicate. Id and price will be taken from this item. + * + * @param inventoryIndex the inventory index of the item + * @param quantity number of available copies of the item + */ + void addDuplicate (int inventoryIndex, int quantity); + + /** + * Add a duplicate. Id and price will be taken from this item. + * Needed for compatibility with ShopDuplicateItems (see) class + * documentation). */ - ShopItem(int id, int quantity, int price); + void addDuplicate (); + + /** + * Gets the quantity of the currently topmost duplicate. + * + * @return the quantity of the currently topmost duplicate + */ + int getCurrentQuantity() { + return mDuplicates.empty() ? 0 : mDuplicates.top()->quantity; + } + + /** + * Gets the inventory index of the currently topmost duplicate. + * + * @return the inventory index of the currently topmost duplicate + */ + int getCurrentInvIndex() { + return mDuplicates.empty() ? mInvIndex : + mDuplicates.top()->inventoryIndex; + } + + /** + * Reduces the quantity of the topmost duplicate by the specified + * amount. Also reduces the total quantity of this DuplicateItem. + * Empty duplicates are automatically removed. + * + * If the amount is bigger than the quantity of the current topmost, + * only sell as much as possible. Returns the amount actually sold (do + * not ignore the return value!) + * + * @return the amount, that actually was sold. + */ + int sellCurrentDuplicate(int quantity); /** * Gets the price of the item. + * + * @return the price of the item */ int getPrice() const { return mPrice; } /** - * Gets the display name for in the shop list. + * Gets the display name for the item in the shop list. + * + * @return the display name for the item in the shop list */ const std::string& getDisplayName() const { return mDisplayName; } @@ -50,6 +123,15 @@ class ShopItem : public Item protected: int mPrice; std::string mDisplayName; + + /** + * Struct to keep track of duplicates. + */ + typedef struct { + int inventoryIndex; + int quantity; + } DuplicateItem; + std::stack<DuplicateItem*> mDuplicates; /** <-- Stores duplicates */ }; #endif diff --git a/src/statuseffect.cpp b/src/statuseffect.cpp index c0d689f0..70e8d59f 100644 --- a/src/statuseffect.cpp +++ b/src/statuseffect.cpp @@ -92,7 +92,6 @@ static std::map<int, int> blockEffectIndexMap; int StatusEffect::blockEffectIndexToEffectIndex(int blockIndex) { - load(); if (blockEffectIndexMap.find(blockIndex) == blockEffectIndexMap.end()) return -1; return blockEffectIndexMap[blockIndex]; @@ -100,27 +99,16 @@ int StatusEffect::blockEffectIndexToEffectIndex(int blockIndex) StatusEffect *StatusEffect::getStatusEffect(int index, bool enabling) { - load(); return statusEffects[enabling][index]; } StatusEffect *StatusEffect::getStunEffect(int index, bool enabling) { - load(); return stunEffects[enabling][index]; } -static bool status_effects_loaded = false; - - - void StatusEffect::load() { - if (status_effects_loaded) - return; - - status_effects_loaded = true; - XML::Document doc(STATUS_EFFECTS_FILE); xmlNodePtr rootNode = doc.rootNode(); @@ -168,3 +156,19 @@ void StatusEffect::load() } } } + +void unloadMap(std::map<int, StatusEffect *> map) +{ + std::map<int, StatusEffect *>::iterator it; + + for (it = map.begin(); it != map.end(); it++) + delete (*it).second; +} + +void StatusEffect::unload() +{ + unloadMap(statusEffects[0]); + unloadMap(statusEffects[1]); + unloadMap(stunEffects[0]); + unloadMap(stunEffects[1]); +}
\ No newline at end of file diff --git a/src/statuseffect.h b/src/statuseffect.h index 16fed69a..f1af93d2 100644 --- a/src/statuseffect.h +++ b/src/statuseffect.h @@ -94,9 +94,11 @@ public: */ static int blockEffectIndexToEffectIndex(int blocKIndex); -private: static void load(); + static void unload(); +private: + std::string mMessage; std::string mSFXEffect; std::string mParticleEffect; diff --git a/src/text.cpp b/src/text.cpp index cb4587d3..83bd6c24 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -30,6 +30,8 @@ #include "resources/image.h" #include "gui/gui.h" +#include "gui/palette.h" +#include "gui/textrenderer.h" int Text::mInstances = 0; ImageRect Text::mBubble; @@ -37,7 +39,7 @@ Image *Text::mBubbleArrow; Text::Text(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - gcn::Color color, bool isSpeech) : + const gcn::Color* color, bool isSpeech) : mText(text), mColor(color), mIsSpeech(isSpeech) @@ -129,42 +131,14 @@ void Text::draw(gcn::Graphics *graphics, int xOff, int yOff) */ } - // Text shadow - graphics->setColor(gcn::Color(0, 0, 0)); - graphics->drawText(mText, mX - xOff + 1, mY - yOff + 1, - gcn::Graphics::LEFT); - - if (!mIsSpeech) { - graphics->setColor(gcn::Color(0, 0, 0, 64)); - /* - // TODO: Reanable when we can draw it nicely in software mode - graphics->drawText(mText, mX - xOff + 2, mY - yOff + 2, - gcn::Graphics::LEFT); - graphics->drawText(mText, mX - xOff + 1, mY - yOff + 2, - gcn::Graphics::LEFT); - graphics->drawText(mText, mX - xOff + 2, mY - yOff + 1, - gcn::Graphics::LEFT); - */ - - // Text outline - graphics->setColor(gcn::Color(0, 0, 0)); - graphics->drawText(mText, mX - xOff + 1, mY - yOff, - gcn::Graphics::LEFT); - graphics->drawText(mText, mX - xOff - 1, mY - yOff, - gcn::Graphics::LEFT); - graphics->drawText(mText, mX - xOff, mY - yOff + 1, - gcn::Graphics::LEFT); - graphics->drawText(mText, mX - xOff, mY - yOff - 1, - gcn::Graphics::LEFT); - } - - graphics->setColor(mColor); - graphics->drawText(mText, mX - xOff, mY - yOff, gcn::Graphics::LEFT); + TextRenderer::renderText(graphics, mText, + mX - xOff, mY - yOff, gcn::Graphics::LEFT, + *mColor, boldFont, !mIsSpeech, true); } FlashText::FlashText(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - gcn::Color color) : + const gcn::Color *color) : Text(text, x, y, alignment, color), mTime(0) { @@ -175,9 +149,7 @@ void FlashText::draw(gcn::Graphics *graphics, int xOff, int yOff) if (mTime) { if ((--mTime & 4) == 0) - { return; - } } Text::draw(graphics, xOff, yOff); } @@ -40,7 +40,7 @@ class Text */ Text(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - gcn::Color color, bool isSpeech = false); + const gcn::Color *color, bool isSpeech = false); /** * Destructor. The text is removed from the screen. @@ -65,7 +65,7 @@ class Text int mXOffset; /**< The offset of mX from the desired x. */ static int mInstances; /**< Instances of text. */ std::string mText; /**< The text to display. */ - gcn::Color mColor; /**< The color of the text. */ + const gcn::Color *mColor; /**< The color of the text. */ bool mIsSpeech; /**< Is this text a speech bubble? */ protected: @@ -78,7 +78,7 @@ class FlashText : public Text public: FlashText(const std::string &text, int x, int y, gcn::Graphics::Alignment alignment, - gcn::Color color); + const gcn::Color* color); /** * Remove the text from the screen diff --git a/src/textparticle.cpp b/src/textparticle.cpp index f38c32ce..04b7abe1 100644 --- a/src/textparticle.cpp +++ b/src/textparticle.cpp @@ -21,18 +21,18 @@ #include <guichan/color.hpp> -#include "graphics.h" #include "textparticle.h" +#include "gui/textrenderer.h" + TextParticle::TextParticle(Map *map, const std::string &text, - int colorR, int colorG, int colorB, - gcn::Font *font): + const gcn::Color* color, + gcn::Font *font, bool outline): Particle(map), mText(text), mTextFont(font), - mColorR(colorR), - mColorG(colorG), - mColorB(colorB) + mColor(color), + mOutline(outline) { } @@ -58,7 +58,7 @@ void TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const alpha /= mFadeIn; } - graphics->setFont(mTextFont); - graphics->setColor(gcn::Color(mColorR, mColorG, mColorB, (int)alpha)); - graphics->drawText(mText, screenX, screenY, gcn::Graphics::CENTER); + TextRenderer::renderText(graphics, mText, + screenX, screenY, gcn::Graphics::CENTER, + *mColor, mTextFont, mOutline, false, (int) alpha); } diff --git a/src/textparticle.h b/src/textparticle.h index 76c247bf..8b7d3e01 100644 --- a/src/textparticle.h +++ b/src/textparticle.h @@ -32,8 +32,8 @@ class TextParticle : public Particle * Constructor. */ TextParticle(Map *map, const std::string &text, - int colorR, int colorG, int colorB, - gcn::Font *font); + const gcn::Color* color, + gcn::Font *font, bool outline = false); /** * Draws the particle image. @@ -47,7 +47,8 @@ class TextParticle : public Particle private: std::string mText; /**< Text of the particle. */ gcn::Font *mTextFont; /**< Font used for drawing the text. */ - int mColorR, mColorG, mColorB; /**< Color used for drawing the text. */ + const gcn::Color* mColor; /**< Color used for drawing the text. */ + bool mOutline; /**< Make the text better readable */ }; #endif diff --git a/src/units.cpp b/src/units.cpp index 4f8b95f0..7e8d8e6a 100644 --- a/src/units.cpp +++ b/src/units.cpp @@ -164,8 +164,8 @@ std::string formatUnit(int value, int type) struct UnitLevel ul; double amount = ud.conversion * value; - // Shortcut for 0 - if (value == 0) { + // Shortcut for 0; do the same for values less than 0 (for now) + if (value <= 0) { ul = ud.levels[0]; return strprintf("0%s", ul.symbol.c_str()); } else { @@ -126,8 +126,6 @@ <Unit filename="src\gui\chatinput.h" /> <Unit filename="src\gui\checkbox.cpp" /> <Unit filename="src\gui\checkbox.h" /> - <Unit filename="src\gui\color.cpp" /> - <Unit filename="src\gui\color.h" /> <Unit filename="src\gui\confirm_dialog.cpp" /> <Unit filename="src\gui\confirm_dialog.h" /> <Unit filename="src\gui\connection.cpp" /> @@ -164,6 +162,8 @@ <Unit filename="src\gui\itempopup.h" /> <Unit filename="src\gui\itemshortcutcontainer.cpp" /> <Unit filename="src\gui\itemshortcutcontainer.h" /> + <Unit filename="src\gui\label.cpp" /> + <Unit filename="src\gui\label.h" /> <Unit filename="src\gui\linkhandler.h" /> <Unit filename="src\gui\listbox.cpp" /> <Unit filename="src\gui\listbox.h" /> @@ -185,10 +185,14 @@ <Unit filename="src\gui\npcstringdialog.h" /> <Unit filename="src\gui\ok_dialog.cpp" /> <Unit filename="src\gui\ok_dialog.h" /> + <Unit filename="src\gui\palette.cpp" /> + <Unit filename="src\gui\palette.h" /> <Unit filename="src\gui\passwordfield.cpp" /> <Unit filename="src\gui\passwordfield.h" /> <Unit filename="src\gui\playerbox.cpp" /> <Unit filename="src\gui\playerbox.h" /> + <Unit filename="src\gui\popup.cpp" /> + <Unit filename="src\gui\popup.h" /> <Unit filename="src\gui\popupmenu.cpp" /> <Unit filename="src\gui\popupmenu.h" /> <Unit filename="src\gui\progressbar.cpp" /> @@ -230,12 +234,16 @@ <Unit filename="src\gui\shortcutwindow.h" /> <Unit filename="src\gui\skill.cpp" /> <Unit filename="src\gui\skill.h" /> + <Unit filename="src\gui\skin.cpp" /> + <Unit filename="src\gui\skin.h" /> <Unit filename="src\gui\slider.cpp" /> <Unit filename="src\gui\slider.h" /> <Unit filename="src\gui\speechbubble.cpp" /> <Unit filename="src\gui\speechbubble.h" /> <Unit filename="src\gui\status.cpp" /> <Unit filename="src\gui\status.h" /> + <Unit filename="src\gui\storagewindow.cpp" /> + <Unit filename="src\gui\storagewindow.h" /> <Unit filename="src\gui\table.cpp" /> <Unit filename="src\gui\table.h" /> <Unit filename="src\gui\table_model.cpp" /> @@ -244,6 +252,7 @@ <Unit filename="src\gui\textbox.h" /> <Unit filename="src\gui\textfield.cpp" /> <Unit filename="src\gui\textfield.h" /> + <Unit filename="src\gui\textrenderer.h" /> <Unit filename="src\gui\trade.cpp" /> <Unit filename="src\gui\trade.h" /> <Unit filename="src\gui\truetypefont.cpp" /> @@ -264,6 +273,8 @@ <Unit filename="src\gui\widgets\tab.h" /> <Unit filename="src\gui\widgets\tabbedarea.cpp" /> <Unit filename="src\gui\widgets\tabbedarea.h" /> + <Unit filename="src\gui\widgets\textpreview.cpp" /> + <Unit filename="src\gui\widgets\textpreview.h" /> <Unit filename="src\gui\window.cpp" /> <Unit filename="src\gui\window.h" /> <Unit filename="src\gui\windowcontainer.cpp" /> @@ -414,16 +425,18 @@ <Unit filename="src\tmw.rc"> <Option compilerVar="CPP" /> </Unit> + <Unit filename="src\units.cpp" /> + <Unit filename="src\units.h" /> <Unit filename="src\utils\base64.cpp" /> <Unit filename="src\utils\base64.h" /> <Unit filename="src\utils\dtor.h" /> <Unit filename="src\utils\fastsqrt.h" /> <Unit filename="src\utils\gettext.h" /> <Unit filename="src\utils\mutex.h" /> + <Unit filename="src\utils\stringutils.cpp" /> + <Unit filename="src\utils\stringutils.h" /> <Unit filename="src\utils\strprintf.cpp" /> <Unit filename="src\utils\strprintf.h" /> - <Unit filename="src\utils\tostring.h" /> - <Unit filename="src\utils\trim.h" /> <Unit filename="src\utils\xml.cpp" /> <Unit filename="src\utils\xml.h" /> <Unit filename="src\vector.cpp" /> diff --git a/tools/tmxcopy/main.cpp b/tools/tmxcopy/main.cpp index ac18ce04..4926d69f 100644 --- a/tools/tmxcopy/main.cpp +++ b/tools/tmxcopy/main.cpp @@ -1,6 +1,7 @@ /* * TMXCopy * Copyright (C) 2007 Philipp Sehmisch + * Copyright (C) 2009 Steve Cotton * * 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 @@ -19,34 +20,70 @@ #include <iostream> #include <string> +#include <unistd.h> #include "map.hpp" +void printUsage() +{ + std::cerr<<"Usage: tmxcopy [-c] [-n] srcFile x y width height tgtFile x y [outfile]"<<std::endl + <<" -c create layers, if they don't already exist in the target"<<std::endl + <<" -n copy layers by number, not name"<<std::endl; +} + int main(int argc, char * argv[] ) { - // parsing command line options - if (argc < 9 || argc > 10) + ConfigurationOptions config = {0}; + int opt; + while ((opt = getopt(argc, argv, "cn")) != -1) + { + switch (opt) + { + case 'c': + config.createMissingLayers = true; + break; + case 'n': + config.copyLayersByOrdinal = true; + break; + case '?': + std::cerr<<"Unrecognized option"<<std::endl; + printUsage(); + return -1; + } + } + + if ((argc-optind) < 8) + { + std::cerr<<"Too few args"<<std::endl; + printUsage(); + return -1; + } + if ((argc-optind) > 9) { - std::cerr<<"Usage: srcFile x y width height tgtFile x y [outfile]\n"; + std::cerr<<"Too many args"<<std::endl; + printUsage(); return -1; } - std::string srcFile = argv[1]; - int srcX= atoi(argv[2]); - int srcY= atoi(argv[3]); - int width= atoi(argv[4]); - int height=atoi(argv[5]); - std::string tgtFile = argv[6]; - int destX=atoi(argv[7]); - int destY=atoi(argv[8]); + std::string srcFile = argv[optind+0]; + int srcX= atoi(argv[optind+1]); + int srcY= atoi(argv[optind+2]); + int width= atoi(argv[optind+3]); + int height=atoi(argv[optind+4]); + std::string tgtFile = argv[optind+5]; + int destX=atoi(argv[optind+6]); + int destY=atoi(argv[optind+7]); std::string outFile = tgtFile; - if (argc == 10) outFile = argv[9]; + if (argc == optind+9) + { + outFile = argv[optind+8]; + } // plausibility check of command line options if (height < 1 || width < 1 || srcX < 0 || srcY < 0 || destX < 0 || destY < 0) { std::cerr<<"Illegal coordinates!"<<std::endl; - std::cerr<<"Usage: sourceFile x y height width targetFile x y [outputFile]"<<std::endl; + printUsage(); return -1; } @@ -54,16 +91,17 @@ int main(int argc, char * argv[] ) { Map* srcMap = new Map(srcFile); Map* tgtMap = new Map(tgtFile); - if (tgtMap->overwrite(srcMap, srcX, srcY, width, height, destX, destY)) + if (tgtMap->overwrite(srcMap, srcX, srcY, width, height, destX, destY, config)) { tgtMap->save(outFile); } else { return -1; } + delete srcMap; + delete tgtMap; } catch (int) { return -1; } - } diff --git a/tools/tmxcopy/map.cpp b/tools/tmxcopy/map.cpp index d9fc8ada..75cbecbb 100644 --- a/tools/tmxcopy/map.cpp +++ b/tools/tmxcopy/map.cpp @@ -1,6 +1,7 @@ /* * TMXCopy * Copyright (C) 2007 Philipp Sehmisch + * Copyright (C) 2009 Steve Cotton * * 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 @@ -24,6 +25,7 @@ #include <string.h> #include <zlib.h> +#include <cassert> #include "xmlutils.h" #include "zlibutils.h" @@ -80,9 +82,19 @@ Map::Map(std::string filename): { if (xmlStrEqual(node->name, BAD_CAST "layer")) { - Layer* layer = new Layer; - layer->resize(mWidth * mHeight); //build layer information + std::string name = XML::getProperty(node, "name", ""); + Layer* layer = new Layer(name, mWidth * mHeight); + if ( + (mWidth != XML::getProperty(node, "width" , 0)) || + (mHeight != XML::getProperty(node, "height", 0)) || + (0 != XML::getProperty(node, "x" , 0)) || + (0 != XML::getProperty(node, "y" , 0))) + { + std::cerr<<"Error: layer size does not match map size for layer \""<<name<<"\" in "<<filename<<std::endl; + throw 1; + } + for_each_xml_child_node(dataNode, node) { if (!xmlStrEqual(dataNode->name, BAD_CAST "data")) continue; @@ -155,7 +167,7 @@ Map::Map(std::string filename): { for (int s = mTilesets.size()-1; s >= 0; s--) { - if (mTilesets.at(s)->firstgid < gid) + if (mTilesets.at(s)->firstgid <= gid) { layer->at(c).tileset = s; layer->at(c).index = gid - mTilesets.at(s)->firstgid; @@ -184,7 +196,8 @@ Map::Map(std::string filename): bool Map::overwrite( Map* srcMap, int srcX, int srcY, int srcWidth, int srcHeight, - int destX, int destY) + int destX, int destY, + const ConfigurationOptions& config) { //plausibility check of coordinates bool checkPassed = true; @@ -204,6 +217,32 @@ bool Map::overwrite( Map* srcMap, std::cerr<<"Error: Area exceeds lower map border of target map!"<<std::endl; checkPassed = false; } + if (!config.createMissingLayers) + { + if (config.copyLayersByOrdinal) + { + if (srcMap->getNumberOfLayers() > mLayers.size()) { + std::cerr<<"Error: Source has more layers than target map"<<std::endl + <<"(and the command-line \"create layers\" option was not used)"<<std::endl; + checkPassed = false; + } + } + else + { + for (int i = 0; i < srcMap->getNumberOfLayers(); i++) + { + Layer* srcLayer = srcMap->getLayer(i); + Layer* destLayer = getLayer(srcLayer->getName()); + if (!destLayer) + { + std::cerr<<"Error: target map has no layer named \""<<srcLayer->getName()<<"\""<<std::endl + <<"(and the command-line \"create layers\" option was not used)"<<std::endl; + checkPassed = false; + } + } + } + } + if (!checkPassed) return false; std::map<int, int> translation; @@ -234,7 +273,43 @@ bool Map::overwrite( Map* srcMap, for (int i = 0; i < srcMap->getNumberOfLayers(); i++) { Layer* srcLayer = srcMap->getLayer(i); - Layer* destLayer = mLayers.at(i); + Layer* destLayer = NULL; + if (config.copyLayersByOrdinal) + { + if (i < mLayers.size()) + { + destLayer = mLayers.at(i); + } + } + else + { + destLayer = getLayer(srcLayer->getName()); + } + + if (!destLayer) + { + assert(config.createMissingLayers); /* Tested earlier, in the checkPassed section */ + /* Generate a name for the new layer, which must be + * unique in the target map, and should be unique in + * the source map (to avoid collisions later in the + * copying process). + * Start by trying the name of the source layer. + */ + std::string name = srcLayer->getName(); + if (getLayer(name)) + { + int k=0; + do + { + name = "Layer" + toString(k); + k++; + } while (getLayer(name) || srcMap->getLayer(name)); + } + + destLayer = new Layer(name, mWidth * mHeight); + mLayers.push_back(destLayer); + std::cout<<"Created new layer "<<name<<std::endl; + } for (int y=0; y<srcHeight; y++) { @@ -340,7 +415,7 @@ int Map::save(std::string filename) xmlNodePtr newNode; xmlAddChild(rootNode, xmlNewDocText(mXmlDoc, BAD_CAST " ")); newNode = xmlNewNode(NULL, BAD_CAST "layer"); - xmlNewProp(newNode, BAD_CAST "name", BAD_CAST ("Layer" + toString(i)).c_str()); + xmlNewProp(newNode, BAD_CAST "name", BAD_CAST (mLayers.at(i)->getName()).c_str()); xmlNewProp(newNode, BAD_CAST "width", BAD_CAST toString(mWidth).c_str()); xmlNewProp(newNode, BAD_CAST "height", BAD_CAST toString(mHeight).c_str()); xmlAddChild(newNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n ")); @@ -354,6 +429,9 @@ int Map::save(std::string filename) xmlAddChild(newNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n ")); xmlAddChild(rootNode, newNode); xmlAddChild(rootNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n")); + + free(base64Data); + free(binData); } //save XML tree @@ -370,3 +448,34 @@ int Map::save(std::string filename) return true; } } + +Layer* Map::getLayer(std::string name) +{ + for (std::vector<Layer*>::iterator layer = mLayers.begin(); + layer != mLayers.end(); + layer++) + { + if ((*layer)->getName() == name) + return *layer; + } + return NULL; +} + +Map::~Map() +{ + for (std::vector<Layer*>::iterator layer = mLayers.begin(); + layer != mLayers.end(); + layer++) + { + delete *layer; + } + + for (std::vector<Tileset*>::iterator tileset = mTilesets.begin(); + tileset != mTilesets.end(); + tileset++) + { + delete *tileset; + } + + xmlFreeDoc(mXmlDoc); +} diff --git a/tools/tmxcopy/map.hpp b/tools/tmxcopy/map.hpp index 2ca9061a..af2f5385 100644 --- a/tools/tmxcopy/map.hpp +++ b/tools/tmxcopy/map.hpp @@ -1,6 +1,7 @@ /* * TMXCopy * Copyright (C) 2007 Philipp Sehmisch + * Copyright (C) 2009 Steve Cotton * * 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 @@ -22,6 +23,22 @@ #include <set> #include <libxml/parser.h> +struct ConfigurationOptions +{ + /* When copying map layers, how to match source layer to + * destination layer. + * + * True: Pair the first layer to the first layer, the second + * to the second, etc. + * + * False: Pair up layers with matching names. + */ + bool copyLayersByOrdinal; + + /* Create extra layers in the target as necessary. */ + bool createMissingLayers; +}; + struct Tileset { std::string imagefile; @@ -45,22 +62,50 @@ struct Tile size_t index; // index in said tileset }; -typedef std::vector<Tile> Layer; +typedef std::vector<Tile> LayerTiles; + +/* This represents an empty tile in the layer. + * Note that {0,0} would be the first tile in the first tileset. + */ +const Tile defaultTile = {-1, 0}; + +class Layer +{ + public: + /* name - the name of the layer, as shown in Tiled + * tileCount - total number of tiles (width*height) + */ + Layer(std::string name, LayerTiles::size_type tileCount) + : mTiles(tileCount, defaultTile), + mName (name) + { + } + + std::string getName() { return mName; } + Tile& at(LayerTiles::size_type c) { return mTiles.at(c); } + + private: + LayerTiles mTiles; + std::string mName; +}; class Map { public: Map(std::string filename); + ~Map(); - bool overwrite( Map* srcMap, + bool overwrite(Map* srcMap, int srcX, int srcY, int srcWidth, int srcHeight, - int destX, int destY); + int destX, int destY, + const ConfigurationOptions& config); int save(std::string filename); int getNumberOfLayers() { return mLayers.size(); } Layer* getLayer(size_t num) { return mLayers.at(num); } + Layer* getLayer(std::string name); std::vector<Tileset*>* getTilesets() { return &mTilesets; } diff --git a/tools/tmxcopy/readme.txt b/tools/tmxcopy/readme.txt index e4235b94..e8ec830a 100644 --- a/tools/tmxcopy/readme.txt +++ b/tools/tmxcopy/readme.txt @@ -23,6 +23,22 @@ But when you enter this command the mapB will be overwritten. This could be a pr Now we can check temp.tmx to see if the copying worked correctly. +Which layer gets copied to which: +By default layers are copied to layers of the same name. The -n option will make it copy by layer number instead. + + mapA: Ground, Fringe, Over, Collision, Object + mapB: Ground, Fencing, Fringe, Over, Collision, Object + The default copies Ground->Ground, Fringe->Fringe, Over->Over, Collision->Collision (the object layer is not affected) + -n copies Ground->Ground, Fringe->Fencing, Over->Fringe, Collision->Over (mapB's collision and object layers are not affected) + + mapA: Ground, Fringe, Over, Collision, Object + mapC: Ground, Fringe, Overhead, Collision, Object + The default quits with an error + -n copies Over->Overhead + +The -c option creates layers as needed. Using it to copy mapB to mapA will add a Fencing layer to mapA. + + The program works so far but there are still some minor problems: -Only tested for TMW-compilant maps. I don't guarantee that it works with Tiled maps that are made for other games and thus use different features. It is assumed that the target map and the source maps have the same number of layers, for example. @@ -31,4 +47,4 @@ The program works so far but there are still some minor problems: -Layer data of output file isn't gzip-compressed yet -Created TMX file is a bit malformated (but working properly) -The last 2 problems can be solved easily by opening and saving the map in Tiled.
\ No newline at end of file +The last 2 problems can be solved easily by opening and saving the map in Tiled. |