diff options
author | Philipp Sehmisch <tmw@crushnet.org> | 2008-07-01 14:51:23 +0000 |
---|---|---|
committer | Philipp Sehmisch <tmw@crushnet.org> | 2008-07-01 14:51:23 +0000 |
commit | d3ccf6a0b97ce4189eef4fde606cf5b5f93fac14 (patch) | |
tree | f4c4e5b1cc87b34d0a3946c3409882ba222e2ad7 | |
parent | 59c17d5f465ddcf1956e8cdf1aae1dbda0a1431f (diff) | |
download | mana-d3ccf6a0b97ce4189eef4fde606cf5b5f93fac14.tar.gz mana-d3ccf6a0b97ce4189eef4fde606cf5b5f93fac14.tar.bz2 mana-d3ccf6a0b97ce4189eef4fde606cf5b5f93fac14.tar.xz mana-d3ccf6a0b97ce4189eef4fde606cf5b5f93fac14.zip |
Ported some GUI improvements from Legend of Mazeroth (GUI skinning via XML files, item descriptions on mouse-over, map names in minimap window, speech bubbles)
-rw-r--r-- | ChangeLog | 15 | ||||
-rw-r--r-- | data/graphics/gui/default.png | bin | 0 -> 1395 bytes | |||
-rw-r--r-- | data/graphics/gui/gui.xml | 18 | ||||
-rw-r--r-- | data/graphics/gui/speech_bubble.png | bin | 0 -> 2031 bytes | |||
-rw-r--r-- | data/graphics/gui/speechbubble.xml | 18 | ||||
-rw-r--r-- | src/being.cpp | 69 | ||||
-rw-r--r-- | src/being.h | 15 | ||||
-rw-r--r-- | src/engine.cpp | 11 | ||||
-rw-r--r-- | src/gui/inventorywindow.cpp | 60 | ||||
-rw-r--r-- | src/gui/inventorywindow.h | 9 | ||||
-rw-r--r-- | src/gui/itemcontainer.cpp | 41 | ||||
-rw-r--r-- | src/gui/itemcontainer.h | 8 | ||||
-rw-r--r-- | src/gui/itempopup.cpp | 116 | ||||
-rw-r--r-- | src/gui/itempopup.h | 53 | ||||
-rw-r--r-- | src/gui/login.cpp | 21 | ||||
-rw-r--r-- | src/gui/minimap.cpp | 19 | ||||
-rw-r--r-- | src/gui/speechbubble.cpp | 73 | ||||
-rw-r--r-- | src/gui/speechbubble.h | 47 | ||||
-rw-r--r-- | src/gui/window.cpp | 257 | ||||
-rw-r--r-- | src/gui/window.h | 10 |
20 files changed, 721 insertions, 139 deletions
@@ -1,3 +1,18 @@ +2008-07-01 Philipp Sehmisch <tmw@crushnet.org> + + * data/gui/window.cpp, data/gui/window.hpp, data/graphics/gui/default.png, + data/graphics/gui/gui.xml, data/graphics/gui/speech_bubble.png, + data/graphics/gui/speechbubble.xml: Added skinning support to GUI (ported from + Legend of Mazzeroth) + * src/being.cpp, src/gui/speechbubble.cpp, src/gui/speechbubble.hpp: Speech is + now rendered in a speech bubble (ported from Legend of Mazzeroth). + * src/engine.cpp: "mapname" property of map files is now used as headline of the + minimap window (ported from Legend of Mazzeroth). + * src/gui/inventorywindow.cpp, src/gui/inventorywindow.hpp, + src/gui/itemcontainer.cpp, src/gui/itemcontainer.h: Item descriptions are now + shown in a separate GUI window when the mouse cursor hovers over them + (ported from Legend of Mazzeroth). + 2008-06-27 Roderic Morris <roderic@ccs.neu.edu> * src/gui/skill.cpp, src/gui/skill.h, src/localplayer.cpp: diff --git a/data/graphics/gui/default.png b/data/graphics/gui/default.png Binary files differnew file mode 100644 index 00000000..4c312487 --- /dev/null +++ b/data/graphics/gui/default.png diff --git a/data/graphics/gui/gui.xml b/data/graphics/gui/gui.xml new file mode 100644 index 00000000..fe62528e --- /dev/null +++ b/data/graphics/gui/gui.xml @@ -0,0 +1,18 @@ +<skinset name="Default" image="default.png"> + <widget type="Window"> + <!-- Top Row --> + <part type="top-left-corner" xpos="0" ypos="0" width="4" height="4" /> + <part type="top-edge" xpos="4" ypos="0" width="3" height="4" /> + <part type="top-right-corner" xpos="7" ypos="0" width="4" height="4" /> + + <!-- Middle Row --> + <part type="left-edge" xpos="0" ypos="4" width="4" height="10" /> + <part type="bg-quad" xpos="11" ypos="0" width="32" height="32" /> + <part type="right-edge" xpos="7" ypos="4" width="4" height="10" /> + + <!-- Bottom Row --> + <part type="bottom-left-corner" xpos="0" ypos="15" width="4" height="4" /> + <part type="bottom-edge" xpos="4" ypos="15" width="3" height="4" /> + <part type="bottom-right-corner" xpos="7" ypos="15" width="4" height="4" /> + </widget> +</skinset>
\ No newline at end of file diff --git a/data/graphics/gui/speech_bubble.png b/data/graphics/gui/speech_bubble.png Binary files differnew file mode 100644 index 00000000..3e678099 --- /dev/null +++ b/data/graphics/gui/speech_bubble.png diff --git a/data/graphics/gui/speechbubble.xml b/data/graphics/gui/speechbubble.xml new file mode 100644 index 00000000..1b11ea85 --- /dev/null +++ b/data/graphics/gui/speechbubble.xml @@ -0,0 +1,18 @@ +<skinset name="SpeechBubble" image="speech_bubble.png"> + <widget type="Window"> + <!-- Top Row --> + <part type="top-left-corner" xpos="0" ypos="0" width="14" height="14" /> + <part type="top-edge" xpos="15" ypos="0" width="1" height="14" /> + <part type="top-right-corner" xpos="17" ypos="0" width="17" height="14" /> + + <!-- Middle Row --> + <part type="left-edge" xpos="0" ypos="15" width="14" height="1" /> + <part type="bg-quad" xpos="34" ypos="0" width="32" height="32" /> + <part type="right-edge" xpos="17" ypos="15" width="17" height="1" /> + + <!-- Bottom Row --> + <part type="bottom-left-corner" xpos="0" ypos="17" width="14" height="17" /> + <part type="bottom-edge" xpos="15" ypos="17" width="1" height="17" /> + <part type="bottom-right-corner" xpos="17" ypos="17" width="17" height="17" /> + </widget> +</skinset>
\ No newline at end of file diff --git a/src/being.cpp b/src/being.cpp index b984708e..5fa18337 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -39,6 +39,7 @@ #include "resources/iteminfo.h" #include "gui/gui.h" +#include "gui/speechbubble.h" #include "utils/dtor.h" #include "utils/tostring.h" @@ -67,12 +68,15 @@ Being::Being(int id, int job, Map *map): { setMap(map); + mSpeechBubble = new SpeechBubble(); + if (instances == 0) { // Load the emotion set ResourceManager *rm = ResourceManager::getInstance(); emotionSet = rm->getImageSet("graphics/sprites/emotions.png", 30, 32); - if (!emotionSet) logger->error("Unable to load emotions!"); + if (!emotionSet) + logger->error("Unable to load emotions!"); } instances++; @@ -253,20 +257,17 @@ void Being::adjustCourse(Uint16 srcX, Uint16 srcY) } } -void -Being::setDestination(Uint16 destX, Uint16 destY) +void Being::setDestination(Uint16 destX, Uint16 destY) { adjustCourse(mX, mY, destX, destY); } -void -Being::clearPath() +void Being::clearPath() { mPath.clear(); } -void -Being::setPath(const Path &path, int mod) +void Being::setPath(const Path &path, int mod) { mPath = path; mSpeedModifier = mod >= 512 ? (mod <= 2048 ? mod : 2048) : 512; // TODO: tune bounds @@ -296,23 +297,20 @@ Being::setPath(const Path &path, int mod) } } -void -Being::setSprite(int slot, int id, const std::string &color) +void Being::setSprite(int slot, int id, const std::string &color) { assert(slot >= BASE_SPRITE && slot < VECTOREND_SPRITE); mSpriteIDs[slot] = id; mSpriteColors[slot] = color; } -void -Being::setSpeech(const std::string &text, Uint32 time) +void Being::setSpeech(const std::string &text, Uint32 time) { mSpeech = text; mSpeechTime = 500; } -void -Being::takeDamage(int amount) +void Being::takeDamage(int amount) { gcn::Font *font; std::string damage = amount ? toString(amount) : "miss"; @@ -343,14 +341,12 @@ Being::takeDamage(int amount) mPx + 16, mPy + 16); } -void -Being::handleAttack() +void Being::handleAttack() { setAction(Being::ATTACK); } -void -Being::setMap(Map *map) +void Being::setMap(Map *map) { // Remove sprite from potential previous map @@ -373,8 +369,7 @@ Being::setMap(Map *map) mChildParticleEffects.clear(); } -void -Being::controlParticle(Particle *particle) +void Being::controlParticle(Particle *particle) { if (particle) { @@ -384,8 +379,7 @@ Being::controlParticle(Particle *particle) } } -void -Being::setAction(Action action, int attackType) +void Being::setAction(Action action, int attackType) { SpriteAction currentAction = ACTION_INVALID; switch (action) @@ -439,8 +433,7 @@ Being::setAction(Action action, int attackType) } -void -Being::setDirection(Uint8 direction) +void Being::setDirection(Uint8 direction) { if (mDirection == direction) return; @@ -477,8 +470,7 @@ Being::setDirection(Uint8 direction) } } -void -Being::nextStep() +void Being::nextStep() { if (mPath.empty()) { @@ -513,8 +505,7 @@ Being::nextStep() mSpeedModifier / (32 * 1024); } -void -Being::logic() +void Being::logic() { // Determine whether the being should take another step if (mAction == WALK && get_elapsed_time(mWalkTime) >= mStepTime) @@ -563,8 +554,7 @@ Being::logic() } } -void -Being::draw(Graphics *graphics, int offsetX, int offsetY) const +void Being::draw(Graphics *graphics, int offsetX, int offsetY) const { int px = mPx + offsetX; int py = mPy + offsetY; @@ -578,8 +568,7 @@ Being::draw(Graphics *graphics, int offsetX, int offsetY) const } } -void -Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) +void Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) { if (!mEmotion) return; @@ -588,12 +577,11 @@ Being::drawEmotion(Graphics *graphics, int offsetX, int offsetY) const int py = mPy + offsetY - 60; const int emotionIndex = mEmotion - 1; - if (emotionIndex >= 0 && emotionIndex < (int) emotionSet->size()) + if ( emotionIndex >= 0 && emotionIndex < (int) emotionSet->size() ) graphics->drawImage(emotionSet->get(emotionIndex), px, py); } -void -Being::drawSpeech(Graphics *graphics, int offsetX, int offsetY) +void Being::drawSpeech(Graphics *graphics, int offsetX, int offsetY) { int px = mPx + offsetX; int py = mPy + offsetY; @@ -601,14 +589,17 @@ Being::drawSpeech(Graphics *graphics, int offsetX, int offsetY) // Draw speech above this being if (mSpeechTime > 0) { - graphics->setFont(speechFont); - graphics->setColor(gcn::Color(255, 255, 255)); - graphics->drawText(mSpeech, px + 18, py - 60, gcn::Graphics::CENTER); + mSpeechBubble->setPosition(px - 50, py - 80 - (mSpeechBubble->getNumRows()*14) ); + mSpeechBubble->setText( mSpeech ); + mSpeechBubble->setVisible(true); + } + else if (mSpeechTime == 0) + { + mSpeechBubble->setVisible(false); } } -Being::Type -Being::getType() const +Being::Type Being::getType() const { return UNKNOWN; } diff --git a/src/being.h b/src/being.h index f4cdc743..9d04f383 100644 --- a/src/being.h +++ b/src/being.h @@ -44,6 +44,7 @@ class Map; class Graphics; class ImageSet; class Particle; +class SpeechBubble; /** * A position along a being's path. @@ -115,10 +116,10 @@ class Being : public Sprite Uint16 mX, mY; /**< Pixel coordinates of tile center */ Uint8 mEmotion; /**< Currently showing emotion */ Uint8 mEmotionTime; /**< Time until emotion disappears */ - Uint16 mAttackSpeed; /**< Attack speed */ + Uint16 mAttackSpeed; /**< Attack speed */ Uint16 mWalkTime; - Action mAction; /**< Action the being is performing */ - Uint16 mJob; /**< Job (player job, npc, monster, ) */ + Action mAction; /**< Action the being is performing */ + Uint16 mJob; /**< Job (player job, npc, monster, creature ) */ /** * Constructor. @@ -209,7 +210,7 @@ class Being : public Sprite * Draws the speech text above the being. */ void - drawSpeech(Graphics *graphics, int offsetX, int offsetY); + drawSpeech(Graphics* graphics, int offsetX, int offsetY); /** * Draws the emotion picture above the being. @@ -389,8 +390,10 @@ class Being : public Sprite std::list<Particle *> mChildParticleEffects; private: - int - getOffset(int step) const; + int getOffset(int step) const; + + // Speech Bubble components + SpeechBubble *mSpeechBubble; Sint16 mStepX, mStepY; Uint16 mStepTime; diff --git a/src/engine.cpp b/src/engine.cpp index b38ca0a8..e4b25c02 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -91,6 +91,17 @@ void Engine::changeMap(const std::string &mapPath) if (newMap->hasProperty("minimap")) { mapImage = resman->getImage(newMap->getProperty("minimap")); + + // Set the title for the Minimap + if (newMap->hasProperty("mapname")) + { + minimap->setCaption(newMap->getProperty("mapname")); + } + else + { + minimap->setCaption("Unknown"); + logger->log("WARNING: Map file '%s' defines a minimap image but does not define a 'mapname' property", map_path.c_str()); + } } minimap->setMapImage(mapImage); beingManager->setMap(newMap); diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index bd224cf0..2127442a 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -29,11 +29,13 @@ #include <guichan/widgets/label.hpp> #include <guichan/widgets/checkbox.hpp> +#include <guichan/widgets/textbox.hpp> #include "button.h" #include "gui.h" #include "item_amount.h" #include "itemcontainer.h" +#include "itempopup.h" #include "scrollarea.h" #include "sdlinput.h" #include "viewport.h" @@ -53,12 +55,14 @@ InventoryWindow::InventoryWindow(): Window(_("Inventory")), mSplit(false) { - setResizable(true); + setResizable(false); setCloseButton(true); - setMinWidth(240); - setMinHeight(172); + // LEEOR/TODO: Since this window is not resizable, do we really need to set these + // values or can we drop them? + setMinWidth(375); + setMinHeight(283); // If you adjust these defaults, don't forget to adjust the trade window's. - setDefaultSize(115, 25, 368, 326); + setDefaultSize(115, 25, 375, 283); addKeyListener(this); mUseButton = new Button(_("Use"), "use", this); @@ -70,28 +74,18 @@ InventoryWindow::InventoryWindow(): mInvenScroll = new ScrollArea(mItems); - mItemNameLabel = new gcn::Label(strprintf(_("Name: %s"), "")); - mItemDescriptionLabel = new gcn::Label( - strprintf(_("Description: %s"), "")); - mItemEffectLabel = new gcn::Label(strprintf(_("Effect: %s"), "")); - mWeightLabel = new gcn::Label( - strprintf(_("Total Weight: %d - Maximum Weight: %d"), 0, 0)); - - place(0, 0, mWeightLabel, 4); - place(0, 1, mInvenScroll, 4).setPadding(3); - place(0, 2, mItemNameLabel, 4); - place(0, 3, mItemDescriptionLabel, 4); - place(0, 4, mItemEffectLabel, 4); - place(0, 5, mUseButton); - place(1, 5, mDropButton); - place(2, 5, mSplitButton); + place(0, 0, mInvenScroll, 100).setPadding(3); + place(0, 1, mUseButton); + place(1, 1, mDropButton); + place(2, 1, mSplitButton); Layout &layout = getLayout(); layout.setColWidth(0, 48); layout.setColWidth(1, 48); layout.setColWidth(2, 48); - layout.setRowHeight(1, Layout::AUTO_SET); + layout.setRowHeight(0, Layout::AUTO_SET); loadWindowState("Inventory"); + } void InventoryWindow::logic() @@ -103,9 +97,7 @@ void InventoryWindow::logic() updateButtons(); // Update weight information - mWeightLabel->setCaption( - strprintf(_("Total Weight: %d - Maximum Weight: %d"), - player_node->getTotalWeight(), player_node->getMaxWeight())); + // mWeightLabel->setCaption(strprintf(_("Total Weight: %d - Maximum Weight: %d"), player_node->getTotalWeight(), player_node->getMaxWeight())); } void InventoryWindow::action(const gcn::ActionEvent &event) @@ -147,21 +139,13 @@ void InventoryWindow::action(const gcn::ActionEvent &event) void InventoryWindow::valueChanged(const gcn::SelectionEvent &event) { Item *item = mItems->getItem(); - ItemInfo const *info = item ? &item->getInfo() : NULL; - - mItemNameLabel->setCaption(strprintf(_("Name: %s"), - info ? info->getName().c_str() : "")); - mItemEffectLabel->setCaption(strprintf(_("Effect: %s"), - info ? info->getEffect().c_str() : "")); - mItemDescriptionLabel->setCaption(strprintf(_("Description: %s"), - info ? info->getDescription().c_str() : "")); if (mSplit) { - if (item && !item->isEquipment() && item->getQuantity() > 1) { + if (item && !item->isEquipment() && item->getQuantity() > 1) + { mSplit = false; - new ItemAmountWindow(AMOUNT_ITEM_SPLIT, this, item, - (item->getQuantity() - 1)); + new ItemAmountWindow(AMOUNT_ITEM_SPLIT, this, item, (item->getQuantity() - 1)); } } } @@ -174,9 +158,9 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event) { Item *item = mItems->getItem(); - if (!item) { + if (!item) return; - } + /* Convert relative to the window coordinates to absolute screen * coordinates. */ @@ -230,3 +214,7 @@ void InventoryWindow::keyReleased(gcn::KeyEvent &event) mSplit = false; } } +InventoryWindow::~InventoryWindow() +{ + +} diff --git a/src/gui/inventorywindow.h b/src/gui/inventorywindow.h index 01fb118c..f9ff8ea2 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -84,6 +84,11 @@ class InventoryWindow : public Window, */ void valueChanged(const gcn::SelectionEvent &event); + /** + * Tracks when the mouse exits the window + */ + ~InventoryWindow(); + private: void updateButtons(); /**< Updates button states. */ @@ -93,10 +98,6 @@ class InventoryWindow : public Window, gcn::Button *mUseButton, *mDropButton, *mSplitButton; gcn::ScrollArea *mInvenScroll; /**< Inventory Scroll Area. */ - gcn::Label *mItemNameLabel; - gcn::Label *mItemDescriptionLabel; - gcn::Label *mItemEffectLabel; - gcn::Label *mWeightLabel; bool mSplit; }; diff --git a/src/gui/itemcontainer.cpp b/src/gui/itemcontainer.cpp index eddb6011..4c528a16 100644 --- a/src/gui/itemcontainer.cpp +++ b/src/gui/itemcontainer.cpp @@ -65,6 +65,7 @@ ItemContainer::ItemContainer(Inventory *inventory, mSelectionStatus(SEL_NONE), mSwapItems(false) { + mItemPopup = new ItemPopup(); setFocusable(true); ResourceManager *resman = ResourceManager::getInstance(); @@ -281,14 +282,46 @@ void ItemContainer::mouseReleased(gcn::MouseEvent &event) mSelectionStatus = SEL_NONE; } -int -ItemContainer::getSlotIndex(const int posX, const int posY) const + +// Show ItemTooltip +void ItemContainer::mouseMoved(gcn::MouseEvent &event) +{ + Item *item = mInventory->getItem( getSlotIndex(event.getX(), event.getY() ) ); + + if( item ) + { + mItemPopup->setPosition(getParent()->getParent()->getX() + getParent()->getParent()->getWidth(), getParent()->getParent()->getY()); + + mItemPopup->setItem(item); + + mItemPopup->setVisible(true); + } + else + { + mItemPopup->setVisible(false); + } +} + + +// Show ItemTooltip +void ItemContainer::mouseEntered(gcn::MouseEvent &event) +{ + +} + + +// Hide ItemTooltip +void ItemContainer::mouseExited(gcn::MouseEvent &event) +{ + mItemPopup->setVisible(false); +} + +int ItemContainer::getSlotIndex(const int posX, const int posY) const { if (getDimension().isPointInRect(posX, posY)) { // Takes into account, boxes are overlapping each other. - return (posY / (BOX_HEIGHT - 1)) * mGridColumns + - (posX / (BOX_WIDTH - 1)); + return (posY / (BOX_HEIGHT - 1)) * mGridColumns + (posX / (BOX_WIDTH - 1)); } return Inventory::NO_SLOT_INDEX; } diff --git a/src/gui/itemcontainer.h b/src/gui/itemcontainer.h index fad59171..9ae5c9c2 100644 --- a/src/gui/itemcontainer.h +++ b/src/gui/itemcontainer.h @@ -29,6 +29,8 @@ #include <guichan/widget.hpp> +#include "itempopup.h" + #include <list> class Image; @@ -133,6 +135,10 @@ class ItemContainer : public gcn::Widget, */ void keyAction(); + void mouseEntered(gcn::MouseEvent &event); + void mouseExited(gcn::MouseEvent &event); + void mouseMoved(gcn::MouseEvent &event); + /** * Moves the highlight in the direction specified. * @@ -167,6 +173,8 @@ class ItemContainer : public gcn::Widget, bool mSwapItems; int mDragPosX, mDragPosY; + ItemPopup *mItemPopup; + typedef std::list<gcn::SelectionListener*> SelectionListenerList; typedef SelectionListenerList::iterator SelectionListenerIterator; diff --git a/src/gui/itempopup.cpp b/src/gui/itempopup.cpp new file mode 100644 index 00000000..cf719f9f --- /dev/null +++ b/src/gui/itempopup.cpp @@ -0,0 +1,116 @@ +/* + * The Legend of Mazzeroth + * Copyright (C) 2008, The Legend of Mazzeroth Development Team + * + * This file is part of The Legend of Mazzeroth based on original code + * from The Mana World. + * + * The Legend of Mazzeroth 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. + * + * The Legend of Mazzeroth 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 The Legend of Mazzeroth; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "itempopup.h" +#include <guichan/widgets/label.hpp> +#include "widgets/layout.h" + +#include "gui.h" + +#include "../resources/image.h" +#include "../resources/resourcemanager.h" +#include "../resources/iteminfo.h" +#include "../utils/gettext.h" +#include "../utils/strprintf.h" + + +ItemPopup::ItemPopup() +{ + + setResizable(false); + setTitleBarHeight(0); + loadSkin("graphics/gui/gui.xml"); + + // Item Name + mItemName = new gcn::Label("Label"); + mItemName->setFont(gui->getFont()); + mItemName->setPosition(2, 2); + mItemName->setWidth(getWidth() - 4); + + // Item Description + mItemDesc = new TextBox(); + mItemDesc->setEditable(false); + mItemDescScroll = new ScrollArea(mItemDesc); + + mItemDescScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mItemDescScroll->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mItemDescScroll->setDimension(gcn::Rectangle(0, 0, 196, 14)); + mItemDescScroll->setOpaque(false); + mItemDescScroll->setPosition(2, 15); + + // Item Effect + mItemEffect = new TextBox(); + mItemEffect->setEditable(false); + mItemEffectScroll = new ScrollArea(mItemEffect); + + mItemEffectScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mItemEffectScroll->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mItemEffectScroll->setDimension(gcn::Rectangle(0, 0, 196, 14)); + mItemEffectScroll->setOpaque(false); + mItemEffectScroll->setPosition(2, 35); + + add(mItemName); + add(mItemDescScroll); + add(mItemEffectScroll); + + setLocationRelativeTo(getParent()); + + // LEEOR / TODO: This causes an exception error. + //moveToBottom(getParent()); + + mItemDesc->setTextWrapped( "" ); + mItemEffect->setTextWrapped( "" ); +} + +void ItemPopup::setItem(Item *item) +{ + + ItemInfo const *info = item ? &item->getInfo() : NULL; + + mItemName->setCaption(info->getName()); + mItemDesc->setTextWrapped( info->getDescription() ); + mItemEffect->setTextWrapped( info->getEffect() ); + + int numRowsDesc = mItemDesc->getNumberOfRows(); + int numRowsEffect = mItemEffect->getNumberOfRows(); + + if(info->getEffect() == "") + { + setContentSize(200, (numRowsDesc * 14) + 30); + } else { + setContentSize(200, (numRowsDesc * 14) + (numRowsEffect*14) + 30); + } + + mItemDescScroll->setDimension(gcn::Rectangle(2, 0, 196, numRowsDesc * 14)); + + mItemEffectScroll->setDimension(gcn::Rectangle(2, 0, 196, numRowsEffect * 14)); + + mItemDescScroll->setPosition(2, 20); + mItemEffectScroll->setPosition(2, (numRowsDesc * 15) + 25); +} + +unsigned int ItemPopup::getNumRows() +{ + return mItemDesc->getNumberOfRows(), mItemEffect->getNumberOfRows(); +} diff --git a/src/gui/itempopup.h b/src/gui/itempopup.h new file mode 100644 index 00000000..499b2e0a --- /dev/null +++ b/src/gui/itempopup.h @@ -0,0 +1,53 @@ +/* +* + * The Legend of Mazzeroth + * Copyright (C) 2008, The Legend of Mazzeroth Development Team + * + * This file is part of The Legend of Mazzeroth based on original code + * from The Mana World. + * + * The Legend of Mazzeroth 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. + * + * The Legend of Mazzeroth 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 The Legend of Mazzeroth; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _LOM_ITEMPOPUP_H__ +#define _LOM_ITEMPOPUP_H__ + +#include "textbox.h" +#include "scrollarea.h" +#include "window.h" + +#include "../item.h" + +class ItemPopup : public Window + { + public: + + ItemPopup(); + + void setItem(Item *item); + unsigned int getNumRows(); + + private: + gcn::Label *mItemName; + TextBox *mItemDesc; + TextBox *mItemEffect; + ScrollArea *mItemDescScroll; + ScrollArea *mItemEffectScroll; + + }; + +#endif diff --git a/src/gui/login.cpp b/src/gui/login.cpp index f1b32d48..72d7ee51 100644 --- a/src/gui/login.cpp +++ b/src/gui/login.cpp @@ -40,14 +40,13 @@ #include "../utils/gettext.h" -LoginDialog::LoginDialog(LoginData *loginData): - Window(_("Login")), mLoginData(loginData) +LoginDialog::LoginDialog(LoginData *loginData) : Window(_("Login")), mLoginData(loginData) { gcn::Label *userLabel = new gcn::Label(_("Name:")); gcn::Label *passLabel = new gcn::Label(_("Password:")); mUserField = new TextField(mLoginData->username); mPassField = new PasswordField(mLoginData->password); - mKeepCheck = new CheckBox(_("Keep"), mLoginData->remember); + mKeepCheck = new CheckBox(_("Remember Username"), mLoginData->remember); mOkButton = new Button(_("Ok"), "ok", this); mCancelButton = new Button(_("Cancel"), "cancel", this); mRegisterButton = new Button(_("Register"), "register", this); @@ -74,9 +73,12 @@ LoginDialog::LoginDialog(LoginData *loginData): setLocationRelativeTo(getParent()); setVisible(true); - if (mUserField->getText().empty()) { + if (mUserField->getText().empty()) + { mUserField->requestFocus(); - } else { + } + else + { mPassField->requestFocus(); } @@ -87,8 +89,7 @@ LoginDialog::~LoginDialog() { } -void -LoginDialog::action(const gcn::ActionEvent &event) +void LoginDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "ok" && canSubmit()) { @@ -116,14 +117,12 @@ LoginDialog::action(const gcn::ActionEvent &event) } } -void -LoginDialog::keyPressed(gcn::KeyEvent &keyEvent) +void LoginDialog::keyPressed(gcn::KeyEvent &keyEvent) { mOkButton->setEnabled(canSubmit()); } -bool -LoginDialog::canSubmit() +bool LoginDialog::canSubmit() { return !mUserField->getText().empty() && !mPassField->getText().empty() && diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index d205338f..4e5664d6 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -36,8 +36,11 @@ Minimap::Minimap(): Window(_("MiniMap")), mMapImage(NULL) { - setDefaultSize(5, 25, 100, 100); + setDefaultSize(0, 0, 100, 100); loadWindowState("MiniMap"); + // LEEOR: The Window class needs to modified to accept + // setAlignment calls. + setAlignment(gcn::Graphics::CENTER); } Minimap::~Minimap() @@ -60,7 +63,14 @@ void Minimap::setMapImage(Image *img) if (mMapImage) { mMapImage->setAlpha(0.7); + setSize( mMapImage->getWidth() + 6, mMapImage->getHeight() + 23 ); + setVisible(true); } + else + { + setVisible(false); + } + } void Minimap::draw(gcn::Graphics *graphics) @@ -69,8 +79,7 @@ void Minimap::draw(gcn::Graphics *graphics) if (mMapImage != NULL) { - static_cast<Graphics*>(graphics)-> - drawImage(mMapImage, getPadding(), getTitleBarHeight()); + static_cast<Graphics*>(graphics)->drawImage(mMapImage, getPadding(), getTitleBarHeight()); } Beings &beings = beingManager->getAll(); @@ -92,6 +101,10 @@ void Minimap::draw(gcn::Graphics *graphics) graphics->setColor(gcn::Color(61, 52, 209)); break; + case Being::NPC: + graphics->setColor(gcn::Color(255, 255, 0)); + break; + case Being::MONSTER: graphics->setColor(gcn::Color(209, 52, 61)); break; diff --git a/src/gui/speechbubble.cpp b/src/gui/speechbubble.cpp new file mode 100644 index 00000000..815238b9 --- /dev/null +++ b/src/gui/speechbubble.cpp @@ -0,0 +1,73 @@ +/* + * The Legend of Mazzeroth + * Copyright (C) 2008, The Legend of Mazzeroth Development Team + * + * This file is part of The Legend of Mazzeroth based on original code + * from The Mana World. + * + * The Legend of Mazzeroth 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. + * + * The Legend of Mazzeroth 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 The Legend of Mazzeroth; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "speechbubble.h" + +#include "../resources/image.h" +#include "../resources/resourcemanager.h" + +SpeechBubble::SpeechBubble() +{ + mSpeechBox = new TextBox(); + mSpeechBox->setEditable(false); + mSpeechBox->setOpaque(false); + + mSpeechArea = new ScrollArea(mSpeechBox); + + // Height == Top Graphic (14px) + 1 Row of Text (15px) + Bottom Graphic (17px) + setContentSize(135, 46); + setTitleBarHeight(0); + loadSkin("graphics/gui/speechbubble.xml"); + + mSpeechArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mSpeechArea->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mSpeechArea->setDimension(gcn::Rectangle(4, 15, 130, 28)); + mSpeechArea->setOpaque(false); + + add(mSpeechArea); + + setLocationRelativeTo(getParent()); + + // LEEOR / TODO: This causes an exception error. + //moveToBottom(getParent()); + + mSpeechBox->setTextWrapped( "" ); +} + +void SpeechBubble::setText(const std::string mText) +{ + mSpeechBox->setTextWrapped( mText ); + + int numRows = mSpeechBox->getNumberOfRows(); + + // 31 == speechbubble Top + Bottom graphic pixel heights + // 15 == height of each line of text (based on font heights) + setContentSize(135, 31 + (numRows * 15) ); + mSpeechArea->setDimension(gcn::Rectangle(4, 15, 130, (31 + (numRows * 14)) - 18 )); +} + +unsigned int SpeechBubble::getNumRows() +{ + return mSpeechBox->getNumberOfRows(); +} diff --git a/src/gui/speechbubble.h b/src/gui/speechbubble.h new file mode 100644 index 00000000..c4ca9109 --- /dev/null +++ b/src/gui/speechbubble.h @@ -0,0 +1,47 @@ +/* + * The Legend of Mazzeroth + * Copyright (C) 2008, The Legend of Mazzeroth Development Team + * + * This file is part of The Legend of Mazzeroth based on original code + * from The Mana World. + * + * The Legend of Mazzeroth 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. + * + * The Legend of Mazzeroth 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 The Legend of Mazzeroth; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _LOM_SPEECHBUBBLE_H__ +#define _LOM_SPEECHBUBBLE_H__ + +#include "textbox.h" +#include "scrollarea.h" +#include "window.h" + +class SpeechBubble : public Window +{ + public: + + SpeechBubble(); + + void setText(const std::string mText); + void setLocation(int x, int y); + unsigned int getNumRows(); + + private: + TextBox *mSpeechBox; + ScrollArea *mSpeechArea; +}; + +#endif diff --git a/src/gui/window.cpp b/src/gui/window.cpp index 75288eb5..1e5dff50 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -28,6 +28,8 @@ #include <guichan/exception.hpp> #include <guichan/widgets/icon.hpp> +#include <libxml/tree.h> + #include "window.h" #include "gui.h" @@ -45,21 +47,31 @@ #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 mLoaded = false; +bool Window::mAlphaChanged = false; class WindowConfigListener : public ConfigListener { + /* void optionChanged(const std::string &) { for_each(Window::border.grid, Window::border.grid + 9, std::bind2nd(std::mem_fun(&Image::setAlpha), config.getValue("guialpha", 0.8))); } + */ + + void optionChanged(const std::string &) + { + Window::mAlphaChanged = true; + } }; Window::Window(const std::string& caption, bool modal, Window *parent): @@ -77,32 +89,20 @@ Window::Window(const std::string& caption, bool modal, Window *parent): { logger->log("Window::Window(\"%s\")", caption.c_str()); - if (!windowContainer) { - throw GCN_EXCEPTION("Window::Window. no windowContainer set"); + if (!windowContainer) + { + throw GCN_EXCEPTION("Window::Window(): no windowContainer set"); } - if (instances == 0) - { - // Load static resources - ResourceManager *resman = ResourceManager::getInstance(); - Image *dBorders = resman->getImage("graphics/gui/vscroll_grey.png"); - border.grid[0] = dBorders->getSubImage(0, 0, 4, 4); - border.grid[1] = dBorders->getSubImage(4, 0, 3, 4); - border.grid[2] = dBorders->getSubImage(7, 0, 4, 4); - border.grid[3] = dBorders->getSubImage(0, 4, 4, 10); - border.grid[4] = resman->getImage("graphics/gui/bg_quad_dis.png"); - border.grid[5] = dBorders->getSubImage(7, 4, 4, 10); - border.grid[6] = dBorders->getSubImage(0, 15, 4, 4); - border.grid[7] = dBorders->getSubImage(4, 15, 3, 4); - border.grid[8] = dBorders->getSubImage(7, 15, 4, 4); - dBorders->decRef(); - closeImage = resman->getImage("graphics/gui/close_button.png"); - - windowConfigListener = new WindowConfigListener(); + loadSkin("graphics/gui/gui.xml"); + + //if (instances == 0) + //{ + //WindowConfigListener = new WindowConfigListener(); // Send GUI alpha changed for initialization - windowConfigListener->optionChanged("guialpha"); - config.addListener("guialpha", windowConfigListener); - } + //windowConfigListener->optionChanged("guialpha"); + //config.addListener("guialpha", windowConfigListener); + //} instances++; @@ -110,6 +110,8 @@ Window::Window(const std::string& caption, bool modal, Window *parent): setPadding(3); setTitleBarHeight(20); + setGuiAlpha(); + // Add this window to the window container windowContainer->add(this); @@ -127,7 +129,7 @@ Window::Window(const std::string& caption, bool modal, Window *parent): Window::~Window() { - logger->log("Window::~Window(\"%s\")", getCaption().c_str()); + logger->log("UNLOAD: Window::~Window(\"%s\")", getCaption().c_str()); std::string const &name = mConfigName; if (!name.empty()) @@ -161,17 +163,15 @@ Window::~Window() windowConfigListener = NULL; // Clean up static resources - delete border.grid[0]; - delete border.grid[1]; - delete border.grid[2]; - delete border.grid[3]; - border.grid[4]->decRef(); - delete border.grid[5]; - delete border.grid[6]; - delete border.grid[7]; - delete border.grid[8]; closeImage->decRef(); } + + // Clean up Border images. + for( int i = 0; i < 9; i++ ) + { + border[i] = NULL; + } + delete border; } void Window::setWindowContainer(WindowContainer *wc) @@ -181,9 +181,15 @@ void Window::setWindowContainer(WindowContainer *wc) void Window::draw(gcn::Graphics *graphics) { + if(mAlphaChanged) + setGuiAlpha(); + + Graphics *g = static_cast<Graphics*>(graphics); - g->drawImageRect(0, 0, getWidth(), getHeight(), border); + //g->drawImageRect(0, 0, getWidth(), getHeight(), border); + + g->drawImageRect(0, 0, getWidth(), getHeight(), border[0], border[2], border[6], border[8], border[1], border[5], border[7], border[3], border[4]); // Draw title if (getTitleBarHeight()) @@ -557,3 +563,184 @@ void Window::reflowLayout(int w, int h) mLayout = NULL; setContentSize(w, h); } + +void Window::setGuiAlpha() +{ + //logger->log("Window::setGuiAlpha: Alpha Value %f", config.getValue("guialpha", 0.8)); + for(int i = 0; i < 9; i++) + { + //logger->log("Window::setGuiAlpha: Border Image (%i)", i); + border[i]->setAlpha(config.getValue("guialpha", 0.8)); + } + + mAlphaChanged = false; +} + +void Window::loadSkin(const std::string filename) +{ + const std::string windowId = Window::getId(); + + ResourceManager *resman = ResourceManager::getInstance(); + + logger->log("Loading Window Skin '%s'.", filename.c_str()); + logger->log("Loading Window ID '%d'.", windowId.c_str()); + + + if(filename == "") + logger->error("Window::loadSkin(): Invalid File Name."); + + // TODO: + // If there is an error loading the specified file, we should try to revert + // to a 'default' skin file. Only if the 'default' skin file can't be loaded + // should we have a terminating error. + XML::Document doc(filename); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "skinset")) + { + logger->error("Widget Skinning error"); + } + + std::string skinSetImage; + skinSetImage = XML::getProperty(rootNode, "image", ""); + Image *dBorders = NULL; + if(skinSetImage != "") + { + logger->log("Window::loadSkin(): <skinset> defines '%s' as a skin image.", skinSetImage.c_str()); + dBorders = resman->getImage("graphics/gui/" + skinSetImage);//"graphics/gui/speech_bubble.png"); + } + else + { + logger->error("Window::loadSkin(): Skinset does not define an image!"); + } + + //iterate <widget>'s + for_each_xml_child_node(widgetNode, rootNode) + { + if (!xmlStrEqual(widgetNode->name, BAD_CAST "widget")) + continue; + + std::string widgetType; + widgetType = XML::getProperty(widgetNode, "type", "unknown"); + if (widgetType == "Window") + { + // Itarate 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[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[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[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[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[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[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[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[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[8] = dBorders->getSubImage(xPos, yPos, width, height); + } + + // Part is of an uknown type. + else + { + logger->log("Window::loadSkin(): Unknown Part Type '%s'", partType.c_str()); + } + } + } + // Widget is of an uknown type. + else + { + logger->log("Window::loadSkin(): Unknown Widget Type '%s'", widgetType.c_str()); + } + } + dBorders->decRef(); + + logger->log("Finished loading Window Skin."); + + // Hard-coded for now until we update the above code to look for window buttons. + closeImage = resman->getImage("graphics/gui/close_button.png"); +} diff --git a/src/gui/window.h b/src/gui/window.h index 9f5969f0..5c81ba6d 100644 --- a/src/gui/window.h +++ b/src/gui/window.h @@ -251,6 +251,11 @@ class Window : public gcn::Window, gcn::WidgetListener */ ContainerPlacer getPlacer(int x, int y); + /** + * Loads a window skin + */ + void Window::loadSkin(const std::string filename); + private: /** * Determines if the mouse is in a resize area and returns appropriate @@ -285,7 +290,10 @@ class Window : public gcn::Window, gcn::WidgetListener static int mouseResize; /**< Active resize handles */ static int instances; /**< Number of Window instances */ - static ImageRect border; /**< The window border and background */ + + void setGuiAlpha(); + static bool mAlphaChanged; + Image *border[9]; static Image *closeImage; /**< Close Button Image */ /** |