diff options
author | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2009-03-25 22:50:59 +0100 |
---|---|---|
committer | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2009-03-25 22:50:59 +0100 |
commit | cc79f0fe21e1a2ef73cbe987d54e848b9a47142d (patch) | |
tree | edd316eb6094f0c02d6d014385865dcd88a2bc56 /src | |
parent | b0df784f1be44a657ca8092069488602270629b7 (diff) | |
parent | 99e8a3fd77b63a029fe02dcf771b6af1aad252ed (diff) | |
download | mana-cc79f0fe21e1a2ef73cbe987d54e848b9a47142d.tar.gz mana-cc79f0fe21e1a2ef73cbe987d54e848b9a47142d.tar.bz2 mana-cc79f0fe21e1a2ef73cbe987d54e848b9a47142d.tar.xz mana-cc79f0fe21e1a2ef73cbe987d54e848b9a47142d.zip |
Merge branch 'eathena/master'
Conflicts:
A lot of files.
Diffstat (limited to 'src')
185 files changed, 5836 insertions, 2582 deletions
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 { |