diff options
Diffstat (limited to 'src')
78 files changed, 2855 insertions, 1105 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fa19cfb5..96338338 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -97,6 +97,12 @@ SET(SRCS gui/item_amount.h gui/itemcontainer.cpp gui/itemcontainer.h + gui/itemshortcutcontainer.cpp + gui/itemshortcutcontainer.h + gui/itemshortcutwindow.cpp + gui/itemshortcutwindow.h + gui/item_amount.cpp + gui/item_amount.h gui/linkhandler.h gui/listbox.cpp gui/listbox.h @@ -143,6 +149,8 @@ SET(SRCS gui/setup.h gui/setup_joystick.cpp gui/setup_joystick.h + gui/setup_keyboard.cpp + gui/setup_keyboard.h gui/setuptab.h gui/setup_video.cpp gui/setup_video.h @@ -170,12 +178,13 @@ SET(SRCS gui/updatewindow.h gui/vbox.cpp gui/vbox.h - gui/windowcontainer.cpp - gui/windowcontainer.h gui/viewport.cpp gui/viewport.h gui/window.cpp gui/window.h + gui/windowcontainer.cpp + gui/windowcontainer.h + gui/windowlistener.h gui/widgets/dropdown.cpp gui/widgets/dropdown.h net/beinghandler.cpp @@ -239,9 +248,6 @@ SET(SRCS resources/buddylist.h resources/animation.cpp resources/animation.h - resources/equipmentdb.cpp - resources/equipmentdb.h - resources/equipmentinfo.h resources/image.cpp resources/image.h resources/imagewriter.cpp @@ -281,6 +287,7 @@ SET(SRCS utils/strprintf.cpp utils/strprintf.h utils/tostring.h + utils/trim.h utils/xml.cpp utils/xml.h animatedsprite.cpp @@ -317,8 +324,12 @@ SET(SRCS inventory.h item.cpp item.h + itemshortcut.cpp + itemshortcut.h joystick.cpp joystick.h + keyboardconfig.cpp + keyboardconfig.h localplayer.cpp localplayer.h lockedarray.h @@ -351,6 +362,7 @@ SET(SRCS textparticle.cpp textparticle.h tileset.h + vector.h ) ADD_EXECUTABLE(tmw ${SRCS}) diff --git a/src/Makefile.am b/src/Makefile.am index 7006f66e..2f29e299 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,6 +5,8 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ gui/widgets/dropdown.h \ gui/widgets/resizegrip.cpp \ gui/widgets/resizegrip.h \ + gui/box.h \ + gui/box.cpp \ gui/browserbox.cpp \ gui/browserbox.h \ gui/buddywindow.cpp \ @@ -39,12 +41,20 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ gui/gccontainer.h \ gui/gui.cpp \ gui/gui.h \ + gui/hbox.h \ + gui/hbox.cpp \ gui/help.cpp \ gui/help.h \ + gui/inttextbox.h \ + gui/inttextbox.cpp \ gui/inventorywindow.cpp \ gui/inventorywindow.h \ gui/itemcontainer.cpp \ gui/itemcontainer.h \ + gui/itemshortcutcontainer.cpp \ + gui/itemshortcutcontainer.h \ + gui/itemshortcutwindow.cpp \ + gui/itemshortcutwindow.h \ gui/item_amount.cpp \ gui/item_amount.h \ gui/linkhandler.h \ @@ -95,6 +105,8 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ gui/setup.h \ gui/setup_joystick.cpp \ gui/setup_joystick.h \ + gui/setup_keyboard.cpp \ + gui/setup_keyboard.h \ gui/setuptab.h \ gui/setup_video.cpp \ gui/setup_video.h \ @@ -118,22 +130,17 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ gui/trade.h \ gui/unregisterdialog.cpp \ gui/unregisterdialog.h \ + gui/updatewindow.h \ + gui/updatewindow.cpp \ + gui/vbox.h \ + gui/vbox.cpp \ gui/viewport.cpp \ gui/viewport.h \ gui/window.cpp \ gui/window.h \ gui/windowcontainer.cpp \ gui/windowcontainer.h \ - gui/inttextbox.h \ - gui/inttextbox.cpp \ - gui/box.h \ - gui/box.cpp \ - gui/vbox.h \ - gui/vbox.cpp \ - gui/hbox.h \ - gui/hbox.cpp \ - gui/updatewindow.h \ - gui/updatewindow.cpp \ + gui/windowlistener.h \ net/beinghandler.h \ net/beinghandler.cpp \ net/buysellhandler.h \ @@ -193,9 +200,6 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ resources/ambientoverlay.h \ resources/animation.cpp \ resources/animation.h \ - resources/equipmentdb.cpp \ - resources/equipmentdb.h \ - resources/equipmentinfo.h \ resources/image.cpp \ resources/image.h \ resources/imageloader.cpp \ @@ -235,6 +239,7 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ utils/strprintf.h \ utils/strprintf.cpp \ utils/tostring.h \ + utils/trim.h \ utils/xml.cpp \ utils/xml.h \ animatedsprite.cpp \ @@ -271,8 +276,12 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ inventory.h \ item.cpp \ item.h \ + itemshortcut.cpp \ + itemshortcut.h \ joystick.cpp \ joystick.h \ + keyboardconfig.cpp \ + keyboardconfig.h \ localplayer.cpp \ localplayer.h \ lockedarray.h \ @@ -304,7 +313,8 @@ tmw_SOURCES = gui/widgets/dropdown.cpp \ sprite.h \ textparticle.cpp \ textparticle.h \ - tileset.h + tileset.h \ + vector.h # set the include path found by configure INCLUDES = \ diff --git a/src/animationparticle.cpp b/src/animationparticle.cpp index 30c33da7..c79a5bc4 100644 --- a/src/animationparticle.cpp +++ b/src/animationparticle.cpp @@ -26,7 +26,7 @@ #include "simpleanimation.h" AnimationParticle::AnimationParticle(Map *map, Animation *animation): - ImageParticle(map, 0), + ImageParticle(map, NULL), mAnimation(new SimpleAnimation(animation)) { } @@ -40,6 +40,7 @@ AnimationParticle::AnimationParticle(Map *map, xmlNodePtr animationNode): AnimationParticle::~AnimationParticle() { delete mAnimation; + mImage = NULL; } bool AnimationParticle::update() diff --git a/src/being.cpp b/src/being.cpp index bdb27384..81bed667 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -36,12 +36,10 @@ #include "resources/resourcemanager.h" #include "resources/imageset.h" +#include "resources/iteminfo.h" #include "gui/gui.h" -#include "resources/resourcemanager.h" -#include "resources/imageset.h" - #include "utils/dtor.h" #include "utils/tostring.h" @@ -58,11 +56,11 @@ Being::Being(Uint16 id, Uint16 job, Map *map): mEquipment(new Equipment()), mId(id), mSex(2), - mWeapon(0), mWalkSpeed(150), mSpeedModifier(1024), mSpriteDirection(DIRECTION_DOWN), mDirection(DOWN), mMap(NULL), + mEquippedWeapon(NULL), mHairStyle(0), mHairColor(0), mSpeechTime(0), mPx(0), mPy(0), @@ -302,7 +300,7 @@ Being::setSpeech(const std::string &text, Uint32 time) void Being::takeDamage(int amount) { - gcn::Font* font; + gcn::Font *font; std::string damage = amount ? toString(amount) : "miss"; // Selecting the right color @@ -312,8 +310,9 @@ Being::takeDamage(int amount) } else { - // hit particle effect - controlParticle(particleEngine->addEffect("graphics/particles/hit.particle.xml", 0, 0)); + // Hit particle effect + controlParticle(particleEngine->addEffect( + "graphics/particles/hit.particle.xml", 0, 0)); if (getType() == MONSTER) { @@ -325,7 +324,7 @@ Being::takeDamage(int amount) } } - // show damage number + // Show damage number particleEngine->addTextSplashEffect(damage, 255, 255, 255, font, mPx + 16, mPy + 16); } @@ -340,7 +339,7 @@ void Being::setMap(Map *map) { // Remove sprite from potential previous map - if (mMap != NULL) + if (mMap) { mMap->removeSprite(mSpriteIterator); } @@ -348,12 +347,12 @@ Being::setMap(Map *map) mMap = map; // Add sprite to potential new map - if (mMap != NULL) + if (mMap) { mSpriteIterator = mMap->addSprite(this); } - //clear particle effect list because child particles became invalid + // Clear particle effect list because child particles became invalid mChildParticleEffects.clear(); } @@ -362,7 +361,8 @@ Being::controlParticle(Particle *particle) { if (particle) { - particle->disableAutoDelete(); //the effect may not die without the beings permission or we segvault + // The effect may not die without the beings permission or we segfault + particle->disableAutoDelete(); mChildParticleEffects.push_back(particle); } } @@ -380,20 +380,12 @@ Being::setAction(Action action) currentAction = ACTION_SIT; break; case ATTACK: - switch (getWeapon()) + if (mEquippedWeapon) { - case 3: - currentAction = ACTION_ATTACK; - break; - case 2: - currentAction = ACTION_ATTACK_BOW; - break; - case 1: - currentAction = ACTION_ATTACK_STAB; - break; - case 0: - currentAction = ACTION_ATTACK; - break; + currentAction = mEquippedWeapon->getAttackType(); + } + else { + currentAction = ACTION_ATTACK; } for (int i = 0; i < VECTOREND_SPRITE; i++) { @@ -533,11 +525,9 @@ Being::logic() } } - //Update particle effects - for ( std::list<Particle *>::iterator i = mChildParticleEffects.begin(); - i != mChildParticleEffects.end(); - - ) + // Update particle effects + for (std::list<Particle *>::iterator i = mChildParticleEffects.begin(); + i != mChildParticleEffects.end();) { (*i)->setPosition((float)mPx + 16.0f, (float)mPy + 32.0f); if (!(*i)->isAlive()) @@ -599,46 +589,6 @@ Being::getType() const return UNKNOWN; } -void -Being::setWeaponById(Uint16 weapon) -{ - //TODO: Use an external file to map weapon IDs to weapon types - switch (weapon) - { - case 529: // iron arrows - case 1199: // arrows - break; - - case 623: //scythe - setWeapon(3); - break; - - case 1200: // bow - case 530: // short bow - case 545: // forest bow - setWeapon(2); - break; - - case 521: // sharp knife - /* UNCOMMENT TO TEST SHARP KNIFE AS SCYTHE - * setWeapon(3) - * break; - */ - case 522: // dagger - case 536: // short sword - case 1201: // knife - setWeapon(1); - break; - - case 0: // unequip - setWeapon(0); - break; - - default: - logger->log("Not a weapon: %d", weapon); - } -} - int Being::getOffset(int step) const { // Check whether we're walking in the requested direction diff --git a/src/being.h b/src/being.h index afb3cb8b..fadf9656 100644 --- a/src/being.h +++ b/src/being.h @@ -38,6 +38,7 @@ class AnimatedSprite; class Equipment; +class ItemInfo; class Item; class Map; class Graphics; @@ -161,8 +162,7 @@ class Being : public Sprite void setSpeech(const std::string &text, Uint32 time); /** - * Puts a damage bubble above this being for the specified amount of - * time. + * Puts a damage bubble above this being. * * @param amount The amount of damage. */ @@ -267,27 +267,6 @@ class Being : public Sprite virtual Type getType() const; /** - * Gets the weapon picture id. - */ - Uint16 getWeapon() const { return mWeapon; } - - /** - * Sets the weapon picture id. - * - * @param weapon the picture id - */ - virtual void - setWeapon(Uint16 weapon) { mWeapon = weapon; } - - /** - * Sets the weapon picture id with the weapon id. - * - * @param weapon the weapon id - */ - void - setWeaponById(Uint16 weapon); - - /** * Gets the walk speed. */ Uint16 @@ -380,30 +359,26 @@ class Being : public Sprite getHeight() const; /** - * Returns the required size of a target cursor for this being + * Returns the required size of a target cursor for this being. */ - virtual Being::TargetCursorSize - getTargetCursorSize() const + virtual Being::TargetCursorSize getTargetCursorSize() const { return TC_MEDIUM; } std::auto_ptr<Equipment> mEquipment; /** - * Take control of a particle + * Take control of a particle. */ - void - controlParticle(Particle *particle); + void controlParticle(Particle *particle); protected: /** * Sets the new path for this being. */ - void - setPath(const Path &path, int mod = 1024); + void setPath(const Path &path, int mod = 1024); Uint16 mId; /**< Unique being id */ Uint8 mSex; /**< Character's gender */ - Uint16 mWeapon; /**< Weapon picture id */ Uint16 mWalkSpeed; /**< Walking speed */ Uint16 mSpeedModifier; /**< Modifier to keep course on sync (1024 = normal speed) */ Uint8 mSpriteDirection; /**< Facing direction */ @@ -411,6 +386,9 @@ class Being : public Sprite Map *mMap; /**< Map on which this being resides */ SpriteIterator mSpriteIterator; + /** Engine-related infos about weapon. */ + const ItemInfo* mEquippedWeapon; + Path mPath; std::string mSpeech; Uint16 mHairStyle, mHairColor; diff --git a/src/game.cpp b/src/game.cpp index 495f9a8d..a40bfa28 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -37,7 +37,9 @@ #include "engine.h" #include "flooritemmanager.h" #include "graphics.h" +#include "itemshortcut.h" #include "joystick.h" +#include "keyboardconfig.h" #include "localplayer.h" #include "log.h" #include "npc.h" @@ -53,6 +55,7 @@ #include "gui/gui.h" #include "gui/help.h" #include "gui/inventorywindow.h" +#include "gui/itemshortcutwindow.h" #include "gui/menuwindow.h" #include "gui/minimap.h" #include "gui/ministatus.h" @@ -116,6 +119,7 @@ TradeWindow *tradeWindow; //BuddyWindow *buddyWindow; HelpWindow *helpWindow; DebugWindow *debugWindow; +ItemShortcutWindow *itemShortcutWindow; BeingManager *beingManager = NULL; FloorItemManager *floorItemManager = NULL; @@ -181,6 +185,7 @@ void createGuiWindows() //buddyWindow = new BuddyWindow(); helpWindow = new HelpWindow(); debugWindow = new DebugWindow(); + itemShortcutWindow = new ItemShortcutWindow(); // Initialize window positions //chargeDialog->setPosition( @@ -193,6 +198,7 @@ void createGuiWindows() chatWindow->setVisible(true); miniStatusWindow->setVisible(true); menuWindow->setVisible(true); + itemShortcutWindow->setVisible(true); } /** @@ -220,6 +226,7 @@ void destroyGuiWindows() //delete buddyWindow; delete helpWindow; delete debugWindow; + delete itemShortcutWindow; } Game::Game(): @@ -420,6 +427,14 @@ void Game::handleInput() { gcn::Window *requestedWindow = NULL; + if (setupWindow->isVisible() && + keyboard.getNewKeyIndex() > keyboard.KEY_NO_VALUE) + { + keyboard.setNewKey((int) event.key.keysym.sym); + keyboard.callbackNewKey(); + keyboard.setNewKeyIndex(keyboard.KEY_NO_VALUE); + return; + } switch (event.key.keysym.sym) { case SDLK_F1: @@ -473,65 +488,7 @@ void Game::handleInput() used = true; } break; - - // Hide certain windows - case SDLK_h: - if (!chatWindow->isFocused()) - { - statusWindow->setVisible(false); - inventoryWindow->setVisible(false); - skillDialog->setVisible(false); - setupWindow->setVisible(false); - equipmentWindow->setVisible(false); - helpWindow->setVisible(false); - debugWindow->setVisible(false); - } - break; - - // Picking up items on the floor - case SDLK_g: - case SDLK_z: - if (!chatWindow->isFocused()) - { - Uint16 x = player_node->mX / 32, y = player_node->mY / 32; - FloorItem *item = floorItemManager->findByCoordinates(x, y); - - // If none below the player, try the tile in front of - // the player - if (!item) - { - // Temporary until tile-based picking is removed. - switch (player_node->getSpriteDirection()) - { - case DIRECTION_UP : --y; break; - case DIRECTION_DOWN : ++y; break; - case DIRECTION_LEFT : --x; break; - case DIRECTION_RIGHT: ++x; break; - default: break; - } - - item = floorItemManager->findByCoordinates(x, y); - } - - if (item) - player_node->pickUp(item); - - used = true; - } - break; - - // attacking - // TODO: Reimplement attacking with joystick buttons - // (old code allowed permanent attacking and not 1 attack - // with each button push) - // I would like to do this but i don't own a joystick. - case SDLK_LCTRL: - case SDLK_RCTRL: - player_node->attack(); - used = true; - break; - - // Quitting confirmation dialog + // Quitting confirmation dialog case SDLK_ESCAPE: if (!quitDialog) { @@ -547,6 +504,74 @@ void Game::handleInput() break; } + if (keyboard.isEnabled() && !chatWindow->isFocused()) + { + const int tKey = keyboard.getKeyIndex(event.key.keysym.sym); + // Checks if any item shortcut is pressed. + for (int i = KeyboardConfig::KEY_SHORTCUT_0; + i <= KeyboardConfig::KEY_SHORTCUT_9; + i++) + { + if (tKey == i) { + itemShortcut->useItem( + i - KeyboardConfig::KEY_SHORTCUT_0); + break; + } + } + switch (tKey) { + case KeyboardConfig::KEY_PICKUP: + { + Uint16 x = player_node->mX / 32; + Uint16 y = player_node->mY / 32; + FloorItem *item = + floorItemManager->findByCoordinates(x, y); + + // If none below the player, try the tile in front + // of the player + if (!item) + { + // Temporary until tile-based picking is + // removed. + switch (player_node->getSpriteDirection()) + { + case DIRECTION_UP : --y; break; + case DIRECTION_DOWN : ++y; break; + case DIRECTION_LEFT : --x; break; + case DIRECTION_RIGHT: ++x; break; + default: break; + } + + item = + floorItemManager->findByCoordinates(x, y); + } + + if (item) + player_node->pickUp(item); + + used = true; + } + break; + case KeyboardConfig::KEY_SIT: + // Player sit action + player_node->toggleSit(); + used = true; + break; + case KeyboardConfig::KEY_HIDE_WINDOWS: + // Hide certain windows + if (!chatWindow->isFocused()) + { + statusWindow->setVisible(false); + inventoryWindow->setVisible(false); + skillDialog->setVisible(false); + setupWindow->setVisible(false); + equipmentWindow->setVisible(false); + helpWindow->setVisible(false); + debugWindow->setVisible(false); + } + break; + } + } + if (requestedWindow) { requestedWindow->setVisible(!requestedWindow->isVisible()); @@ -563,19 +588,14 @@ void Game::handleInput() { switch (event.key.keysym.sym) { - case SDLK_s: - // Player sit action - player_node->toggleSit(); - used = true; - break; - case SDLK_p: // Screenshot (picture, hence the p) { SDL_Surface *screenshot = graphics->getScreenshot(); if (!saveScreenshot(screenshot)) { - logger->log("Error: could not save Screenshot."); + logger->log( + "Error: could not save Screenshot."); } SDL_FreeSurface(screenshot); } @@ -606,6 +626,8 @@ void Game::handleInput() case SDLK_8: emotion = 8; break; case SDLK_9: emotion = 9; break; case SDLK_0: emotion = 10; break; + case SDLK_MINUS: emotion = 11; break; + case SDLK_EQUALS: emotion = 12; break; default: emotion = 0; break; } @@ -638,41 +660,41 @@ void Game::handleInput() } } // End while - + // If the user is configuring the keys then don't respond. + if (!keyboard.isEnabled()) + { + return; + } // Moving player around if (player_node->mAction != Being::DEAD && current_npc == 0 && !chatWindow->isFocused()) { // Get the state of the keyboard keys - Uint8* keys; - keys = SDL_GetKeyState(NULL); + keyboard.refreshActiveKeys(); Uint16 x = player_node->mX / 32, y = player_node->mY / 32; unsigned char direction = 0; // Translate pressed keys to movement and direction - if (keys[SDLK_UP] || keys[SDLK_KP8] || - keys[SDLK_KP7] || keys[SDLK_KP9] || + if ( keyboard.isKeyActive(keyboard.KEY_MOVE_UP) || joystick && joystick->isUp()) { direction |= Being::UP; } - else if (keys[SDLK_DOWN] || keys[SDLK_KP2] || - keys[SDLK_KP1] || keys[SDLK_KP3] || + else if ( keyboard.isKeyActive(keyboard.KEY_MOVE_DOWN) || joystick && joystick->isDown()) { direction |= Being::DOWN; } - if (keys[SDLK_LEFT] || keys[SDLK_KP4] || - keys[SDLK_KP1] || keys[SDLK_KP7] || + + if ( keyboard.isKeyActive(keyboard.KEY_MOVE_LEFT) || joystick && joystick->isLeft()) { direction |= Being::LEFT; } - else if (keys[SDLK_RIGHT] || keys[SDLK_KP6] || - keys[SDLK_KP3] || keys[SDLK_KP9] || + else if ( keyboard.isKeyActive(keyboard.KEY_MOVE_RIGHT) || joystick && joystick->isRight()) { direction |= Being::RIGHT; @@ -681,7 +703,8 @@ void Game::handleInput() player_node->setWalkingDir(direction); // Target the nearest monster if 'a' pressed - if (keys[SDLK_a]) + if ( keyboard.isKeyActive(keyboard.KEY_TARGET_CLOSEST) ) + //if (keys[SDLK_a]) { Being *target = beingManager->findNearestLivingBeing(x, y, 20, Being::MONSTER); diff --git a/src/gui/button.cpp b/src/gui/button.cpp index 4e236c33..0379ebc0 100644 --- a/src/gui/button.cpp +++ b/src/gui/button.cpp @@ -60,11 +60,26 @@ static ButtonData const data[BUTTON_COUNT] = { ImageRect Button::button[BUTTON_COUNT]; +Button::Button(): + mIsLogged(false) +{ + init(); +} + Button::Button(const std::string& caption, const std::string &actionEventId, gcn::ActionListener *listener): gcn::Button(caption), mIsLogged(false) { + init(); + setActionEventId(actionEventId); + if (listener) { + addActionListener(listener); + } +} + +void Button::init() +{ setBorderSize(0); if (mInstances == 0) @@ -91,12 +106,7 @@ Button::Button(const std::string& caption, const std::string &actionEventId, btn[mode]->decRef(); } } - mInstances++; - setActionEventId(actionEventId); - if (listener) { - addActionListener(listener); - } } Button::~Button() diff --git a/src/gui/button.h b/src/gui/button.h index eb73e311..d12173b2 100644 --- a/src/gui/button.h +++ b/src/gui/button.h @@ -38,7 +38,13 @@ class ImageRect; class Button : public gcn::Button { public: /** - * Constructor, sets the caption of the button to the given string. + * Default constructor. + */ + Button(); + + /** + * Constructor, sets the caption of the button to the given string and + * adds the given action listener. */ Button(const std::string& caption, const std::string &actionEventId, gcn::ActionListener *listener); @@ -60,6 +66,8 @@ class Button : public gcn::Button { { mIsLogged = enable; } private: + void init(); + static ImageRect button[4]; /**< Button state graphics */ static int mInstances; /**< Number of button instances */ bool mIsLogged; /**< Makes the button appear pressed all the time */ diff --git a/src/gui/buy.cpp b/src/gui/buy.cpp index 6cfe5e18..bbf2102e 100644 --- a/src/gui/buy.cpp +++ b/src/gui/buy.cpp @@ -28,6 +28,7 @@ #include "button.h" #include "scrollarea.h" #include "shop.h" +#include "shoplistbox.h" #include "slider.h" #include "../npc.h" @@ -35,11 +36,15 @@ #include "../resources/itemdb.h" #include "../utils/tostring.h" - BuyDialog::BuyDialog(): Window("Buy"), mMoney(0), mAmountItems(0), mMaxItems(0) { + setResizable(true); + setMinWidth(260); + setMinHeight(230); + setDefaultSize(0, 0, 260, 230); + mShopItems = new ShopItems; mShopItemList = new ShopListBox(mShopItems, mShopItems); @@ -54,32 +59,15 @@ BuyDialog::BuyDialog(): mItemDescLabel = new gcn::Label("Description:"); mItemEffectLabel = new gcn::Label("Effect:"); - setContentSize(260, 210); - mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); - mScrollArea->setDimension(gcn::Rectangle(5, 5, 250, 110)); - mShopItemList->setDimension(gcn::Rectangle(5, 5, 238, 110)); - - mSlider->setDimension(gcn::Rectangle(5, 120, 200, 10)); - mSlider->setEnabled(false); - - mQuantityLabel->setPosition(215, 120); - mMoneyLabel->setPosition(5, 130); - - mIncreaseButton->setPosition(40, 186); mIncreaseButton->setSize(20, 20); - mIncreaseButton->setEnabled(false); - - mDecreaseButton->setPosition(10, 186); mDecreaseButton->setSize(20, 20); - mDecreaseButton->setEnabled(false); + mQuantityLabel->setWidth(60); - mBuyButton->setPosition(180, 186); + mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mIncreaseButton->setEnabled(false); + mDecreaseButton->setEnabled(false); mBuyButton->setEnabled(false); - - mQuitButton->setPosition(212, 186); - - mItemEffectLabel->setDimension(gcn::Rectangle(5, 150, 240, 14)); - mItemDescLabel->setDimension(gcn::Rectangle(5, 169, 240, 14)); + mSlider->setEnabled(false); mShopItemList->setActionEventId("item"); mSlider->setActionEventId("slider"); @@ -98,6 +86,8 @@ BuyDialog::BuyDialog(): add(mItemDescLabel); add(mItemEffectLabel); + addWindowListener(this); + loadWindowState("Buy"); setLocationRelativeTo(getParent()); } @@ -118,13 +108,12 @@ void BuyDialog::reset() { mShopItems->clear(); mShopItemList->adjustSize(); - mMoney = 0; - mSlider->setValue(0.0); - // Reset Previous Selected Items to prevent failing asserts + // Reset previous selected items to prevent failing asserts mShopItemList->setSelected(-1); + mSlider->setValue(0); - updateButtonsAndLabels(); + setMoney(0); } void BuyDialog::addItem(int id, int amount, int price) @@ -141,6 +130,7 @@ void BuyDialog::action(const gcn::ActionEvent &event) { setVisible(false); current_npc = 0; + return; } // The following actions require a valid selection @@ -152,21 +142,19 @@ void BuyDialog::action(const gcn::ActionEvent &event) if (event.getId() == "slider") { - mAmountItems = (int)(mSlider->getValue() * mMaxItems); + mAmountItems = (int) mSlider->getValue(); updateButtonsAndLabels(); } else if (event.getId() == "+" && mAmountItems < mMaxItems) { mAmountItems++; - - mSlider->setValue((double) mAmountItems / (double) mMaxItems); + mSlider->setValue(mAmountItems); updateButtonsAndLabels(); } - else if (event.getId() == "-" && mAmountItems > 0) + else if (event.getId() == "-" && mAmountItems > 1) { mAmountItems--; - - mSlider->setValue((double) mAmountItems / (double) mMaxItems); + mSlider->setValue(mAmountItems); updateButtonsAndLabels(); } // TODO: Actually we'd have a bug elsewhere if this check for the number @@ -178,25 +166,69 @@ void BuyDialog::action(const gcn::ActionEvent &event) Net::GameServer::Player::tradeWithNPC (mShopItems->at(selectedItem).id, mAmountItems); - // Update money and adjust the max number of items that can be bought - mMoney -= mAmountItems * mShopItems->at(selectedItem).price; - mMaxItems -= mAmountItems; - // Reset selection - mAmountItems = 0; - mSlider->setValue(0.0); + mAmountItems = 1; + mSlider->setValue(1); + mSlider->gcn::Slider::setScale(1, mMaxItems); - updateButtonsAndLabels(); + // Update money and adjust the max number of items that can be bought + mMaxItems -= mAmountItems; + setMoney(mMoney - mAmountItems * mShopItems->at(selectedItem).price); } } void BuyDialog::selectionChanged(const SelectionEvent &event) { + // Reset amount of items and update labels - mAmountItems = 0; - mSlider->setValue(0.0); + mAmountItems = 1; + mSlider->setValue(1); updateButtonsAndLabels(); + mSlider->gcn::Slider::setScale(1, mMaxItems); +} + +void BuyDialog::windowResized(const WindowEvent &event) +{ + gcn::Rectangle area = getChildrenArea(); + int width = area.width; + int height = area.height; + + mDecreaseButton->setPosition(8, height - 8 - mDecreaseButton->getHeight()); + mIncreaseButton->setPosition( + mDecreaseButton->getX() + mDecreaseButton->getWidth() + 5, + mDecreaseButton->getY()); + + mQuitButton->setPosition( + width - 8 - mQuitButton->getWidth(), + height - 8 - mQuitButton->getHeight()); + mBuyButton->setPosition( + mQuitButton->getX() - 5 - mBuyButton->getWidth(), + mQuitButton->getY()); + + mItemDescLabel->setDimension(gcn::Rectangle(8, + mBuyButton->getY() - 5 - mItemDescLabel->getHeight(), + width - 16, + mItemDescLabel->getHeight())); + mItemEffectLabel->setDimension(gcn::Rectangle(8, + mItemDescLabel->getY() - 5 - mItemEffectLabel->getHeight(), + width - 16, + mItemEffectLabel->getHeight())); + mMoneyLabel->setDimension(gcn::Rectangle(8, + mItemEffectLabel->getY() - 5 - mMoneyLabel->getHeight(), + width - 16, + mMoneyLabel->getHeight())); + + mQuantityLabel->setPosition( + width - mQuantityLabel->getWidth() - 8, + mMoneyLabel->getY() - 5 - mQuantityLabel->getHeight()); + mSlider->setDimension(gcn::Rectangle(8, + mQuantityLabel->getY(), + mQuantityLabel->getX() - 8 - 8, + 10)); + + mScrollArea->setDimension(gcn::Rectangle(8, 8, width - 16, + mSlider->getY() - 5 - 8)); } void @@ -232,14 +264,13 @@ BuyDialog::updateButtonsAndLabels() // Enable or disable buttons and slider mIncreaseButton->setEnabled(mAmountItems < mMaxItems); - mDecreaseButton->setEnabled(mAmountItems > 0); + mDecreaseButton->setEnabled(mAmountItems > 1); mBuyButton->setEnabled(mAmountItems > 0); - mSlider->setEnabled(mMaxItems > 0); + mSlider->setEnabled(mMaxItems > 1); // Update quantity and money labels - mQuantityLabel->setCaption(toString(mAmountItems)); - mQuantityLabel->adjustSize(); + mQuantityLabel->setCaption( + toString(mAmountItems) + " / " + toString(mMaxItems)); mMoneyLabel->setCaption("Price: " + toString(price) + " GP / " + toString(mMoney - price) + " GP" ); - mMoneyLabel->adjustSize(); } diff --git a/src/gui/buy.h b/src/gui/buy.h index 875deef9..63d25583 100644 --- a/src/gui/buy.h +++ b/src/gui/buy.h @@ -28,11 +28,11 @@ #include "window.h" #include "selectionlistener.h" -#include "shoplistbox.h" #include "../guichanfwd.h" class ShopItems; +class ShopListBox; class ListBox; /** @@ -40,7 +40,8 @@ class ListBox; * * \ingroup Interface */ -class BuyDialog : public Window, public gcn::ActionListener, SelectionListener +class BuyDialog : public Window, public gcn::ActionListener, SelectionListener, + WindowListener { public: /** @@ -98,6 +99,11 @@ class BuyDialog : public Window, public gcn::ActionListener, SelectionListener void updateButtonsAndLabels(); + /** + * Called whenever the window is resized. + */ + void windowResized(const WindowEvent &event); + private: gcn::Button *mBuyButton; gcn::Button *mQuitButton; diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp index d752cdb3..e8381bef 100644 --- a/src/gui/char_select.cpp +++ b/src/gui/char_select.cpp @@ -48,6 +48,7 @@ #include "../utils/gettext.h" #include "../utils/strprintf.h" #include "../utils/tostring.h" +#include "../utils/trim.h" // Defined in main.cpp, used here for setting the char create dialog extern CharServerHandler charServerHandler; @@ -255,11 +256,6 @@ bool CharSelectDialog::selectByName(const std::string &name) return false; } -std::string CharSelectDialog::getName() -{ - return mNameLabel->getCaption(); -} - CharCreateDialog::CharCreateDialog(Window *parent, int slot): Window(_("Create Character"), true, parent), mSlot(slot) { @@ -410,10 +406,12 @@ CharCreateDialog::action(const gcn::ActionEvent &event) } } -const std::string& +std::string CharCreateDialog::getName() { - return mNameField->getText(); + std::string name = mNameField->getText(); + trim(name); + return name; } void CharCreateDialog::UpdateSliders() diff --git a/src/gui/char_select.h b/src/gui/char_select.h index 9d9184ea..5d0b42fa 100644 --- a/src/gui/char_select.h +++ b/src/gui/char_select.h @@ -60,11 +60,6 @@ class CharSelectDialog : public Window, public gcn::ActionListener bool selectByName(const std::string &name); - /** - * Returns name of selected player - */ - std::string getName(); - private: LockedArray<LocalPlayer*> *mCharInfo; @@ -114,22 +109,28 @@ class CharCreateDialog : public Window, public gcn::ActionListener */ ~CharCreateDialog(); - void - action(const gcn::ActionEvent &event); - - const std::string& - getName(); + void action(const gcn::ActionEvent &event); /** * Unlocks the dialog, enabling the create character button again. */ - void - unlock(); + void unlock(); private: int getDistributedPoints(); + void UpdateSliders(); + /** + * Returns the name of the character to create. + */ + std::string getName(); + + /** + * Communicate character creation to the server. + */ + void attemptCharCreate(); + gcn::TextField *mNameField; gcn::Label *mNameLabel; gcn::Button *mNextHairColorButton; @@ -154,12 +155,6 @@ class CharCreateDialog : public Window, public gcn::ActionListener static const int mMaxPoints = 70; int mUsedPoints; - - - /** - * Communicate character creation to the server. - */ - void attemptCharCreate(); }; #endif diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp index ba7c7f02..4ed8bb97 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -44,6 +44,7 @@ #include "../net/gameserver/player.h" #include "../utils/dtor.h" +#include "../utils/trim.h" ChatWindow::ChatWindow(): Window(), @@ -125,7 +126,7 @@ void ChatWindow::chatLog(std::string line, int own, std::string channelName) { // Delete overhead from the end of the list - while ((int)mChatlog.size() > mItemsKeep) { + while ((int) mChatlog.size() > mItemsKeep) { mChatlog.pop_back(); } @@ -142,12 +143,15 @@ ChatWindow::chatLog(std::string line, int own, std::string channelName) own = BY_SERVER; } - int pos = line.find(" : "); - if (pos > 0) { + std::string::size_type pos = line.find(" : "); + if (pos != std::string::npos) { tmp.nick = line.substr(0, pos); line.erase(0, pos + 3); } + // Trim whitespace + trim(line); + std::string lineColor = "##0"; // Equiv. to BrowserBox::BLACK switch (own) { case BY_GM: diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp index 11ad37c2..884cdf7e 100644 --- a/src/gui/debugwindow.cpp +++ b/src/gui/debugwindow.cpp @@ -42,6 +42,7 @@ DebugWindow::DebugWindow(): Window("Debug") { setResizable(true); + setCloseButton(true); setDefaultSize(0, 0, 400, 100); loadWindowState("Debug"); @@ -60,15 +61,11 @@ DebugWindow::DebugWindow(): mParticleCountLabel = new gcn::Label("[Particle count: 0]"); mParticleCountLabel->setPosition(100, 60); - Button *closeButton = new Button("Close", "close", this); - closeButton->setPosition(5, 60); - add(mFPSLabel); add(mMusicFileLabel); add(mMapFileLabel); add(mTileMouseLabel); add(mParticleCountLabel); - add(closeButton); } void @@ -106,12 +103,3 @@ DebugWindow::logic() +"]"); mParticleCountLabel->adjustSize(); } - -void -DebugWindow::action(const gcn::ActionEvent &event) -{ - if (event.getId() == "close") - { - setVisible(false); - } -} diff --git a/src/gui/debugwindow.h b/src/gui/debugwindow.h index d082b2ca..9b6f2017 100644 --- a/src/gui/debugwindow.h +++ b/src/gui/debugwindow.h @@ -33,11 +33,11 @@ #include "../guichanfwd.h" /** - * The chat window. + * The debug window. * * \ingroup Interface */ -class DebugWindow : public Window, public gcn::ActionListener +class DebugWindow : public Window { public: /** @@ -50,11 +50,6 @@ class DebugWindow : public Window, public gcn::ActionListener */ void logic(); - /** - * Performs action. - */ - void action(const gcn::ActionEvent &event); - private: gcn::Label *mMusicFileLabel, *mMapFileLabel; gcn::Label *mTileMouseLabel, *mFPSLabel; diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index 5e835985..c86a27fc 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -59,6 +59,7 @@ EquipmentWindow::EquipmentWindow(Equipment *equipment): mBackground(NULL), mSelected(-1) { + setCloseButton(true); setDefaultSize(5, 195, 216, 260); loadWindowState("Equipment"); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index dc51054c..97dd4d44 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -42,7 +42,7 @@ #include "../graphics.h" #include "../log.h" -#include "../resources/image.h" +#include "../resources/imageset.h" #include "../resources/resourcemanager.h" #include "../resources/imageloader.h" @@ -77,8 +77,9 @@ class GuiConfigListener : public ConfigListener }; Gui::Gui(Graphics *graphics): - mMouseCursor(NULL), - mCustomCursor(false) + mCustomCursor(false), + mMouseCursors(NULL), + mCursorType(CURSOR_POINTER) { logger->log("Initializing GUI..."); // Set graphics @@ -106,7 +107,7 @@ Gui::Gui(Graphics *graphics): // Set global font try { - mGuiFont = new TrueTypeFont("data/fonts/dejavusans.ttf", 12); + mGuiFont = new TrueTypeFont("data/fonts/dejavusans.ttf", 11); } catch (gcn::Exception e) { @@ -139,7 +140,7 @@ Gui::Gui(Graphics *graphics): hitBlueFont = new gcn::ImageFont("graphics/gui/hits_blue.png", "0123456789"); hitYellowFont = new gcn::ImageFont("graphics/gui/hits_yellow.png", - "mis"); + "0123456789misxp "); } catch (gcn::Exception e) { @@ -169,8 +170,8 @@ Gui::~Gui() delete hitBlueFont; delete hitYellowFont; - if (mMouseCursor) { - mMouseCursor->decRef(); + if (mMouseCursors) { + mMouseCursors->decRef(); } delete mGuiFont; @@ -182,12 +183,6 @@ Gui::~Gui() } void -Gui::logic() -{ - gcn::Gui::logic(); -} - -void Gui::draw() { mGraphics->pushClipArea(mTop->getDimension()); @@ -196,11 +191,13 @@ Gui::draw() int mouseX, mouseY; Uint8 button = SDL_GetMouseState(&mouseX, &mouseY); - if ((SDL_GetAppState() & SDL_APPMOUSEFOCUS || button & SDL_BUTTON(1)) - && mCustomCursor) + if ((SDL_GetAppState() & SDL_APPMOUSEFOCUS || button & SDL_BUTTON(1)) && + mCustomCursor) { - static_cast<Graphics*>(mGraphics)-> - drawImage(mMouseCursor, mouseX - 5, mouseY - 2); + static_cast<Graphics*>(mGraphics)->drawImage( + mMouseCursors->get(mCursorType), + mouseX - 15, + mouseY - 17); } mGraphics->popClipArea(); @@ -220,9 +217,11 @@ Gui::setUseCustomCursor(bool customCursor) // Load the mouse cursor ResourceManager *resman = ResourceManager::getInstance(); - mMouseCursor = resman->getImage("graphics/gui/mouse.png"); - if (!mMouseCursor) { - logger->error("Unable to load mouse cursor."); + mMouseCursors = + resman->getImageSet("graphics/gui/mouse.png", 40, 40); + + if (!mMouseCursors) { + logger->error("Unable to load mouse cursors."); } } else @@ -231,9 +230,9 @@ Gui::setUseCustomCursor(bool customCursor) SDL_ShowCursor(SDL_ENABLE); // Unload the mouse cursor - if (mMouseCursor) { - mMouseCursor->decRef(); - mMouseCursor = NULL; + if (mMouseCursors) { + mMouseCursors->decRef(); + mMouseCursors = NULL; } } } diff --git a/src/gui/gui.h b/src/gui/gui.h index 5f2cc810..1e4b9348 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -30,7 +30,7 @@ class GuiConfigListener; class Graphics; -class Image; +class ImageSet; class Viewport; /** @@ -60,35 +60,48 @@ class Gui : public gcn::Gui ~Gui(); /** - * Works around Guichan bug - */ - void - logic(); - - /** * Draws the whole Gui by calling draw functions down in the * Gui hierarchy. It also draws the mouse pointer. */ - void - draw(); + void draw(); /** - * Return game font + * Return game font. */ - gcn::Font* - getFont() { return mGuiFont; } + gcn::Font* getFont() const + { return mGuiFont; } /** * Sets whether a custom cursor should be rendered. */ - void - setUseCustomCursor(bool customCursor); + void setUseCustomCursor(bool customCursor); + + /** + * Sets which cursor should be used. + */ + void setCursorType(int index) + { mCursorType = index; } + + /** + * Cursors are in graphic order from left to right. + * CURSOR_POINTER should be left untouched. + * CURSOR_TOTAL should always be last. + */ + enum { + CURSOR_POINTER = 0, + CURSOR_RESIZE_ACROSS, + CURSOR_RESIZE_DOWN, + CURSOR_RESIZE_DOWN_LEFT, + CURSOR_RESIZE_DOWN_RIGHT, + CURSOR_TOTAL + }; private: GuiConfigListener *mConfigListener; gcn::Font *mGuiFont; /**< The global GUI font */ - Image *mMouseCursor; /**< Mouse cursor image */ bool mCustomCursor; /**< Show custom cursor */ + ImageSet *mMouseCursors; /**< Mouse cursor images */ + int mCursorType; }; extern Gui *gui; /**< The GUI system */ diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index 085ab188..1e62b130 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -51,6 +51,7 @@ InventoryWindow::InventoryWindow(): mSplit(false) { setResizable(true); + setCloseButton(true); setMinWidth(240); setMinHeight(172); // If you adjust these defaults, don't forget to adjust the trade window's. @@ -69,7 +70,6 @@ InventoryWindow::InventoryWindow(): mItems->addSelectionListener(this); mInvenScroll = new ScrollArea(mItems); - mInvenScroll->setPosition(8, 8); mItemNameLabel = new gcn::Label(strprintf(_("Name: %s"), "")); mItemDescriptionLabel = new gcn::Label( @@ -100,8 +100,8 @@ InventoryWindow::InventoryWindow(): mSplitButton->setWidth(48); } + addWindowListener(this); loadWindowState("Inventory"); - updateContentSize(); } InventoryWindow::~InventoryWindow() @@ -130,7 +130,6 @@ void InventoryWindow::logic() mWeightLabel->setCaption( strprintf(_("Total Weight: %d - Maximum Weight: %d"), player_node->getTotalWeight(), player_node->getMaxWeight())); - mWeightLabel->adjustSize(); } void InventoryWindow::action(const gcn::ActionEvent &event) @@ -215,7 +214,7 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event) } } -void InventoryWindow::updateContentSize() +void InventoryWindow::windowResized(const WindowEvent &event) { const gcn::Rectangle area = getChildrenArea(); @@ -260,8 +259,7 @@ Item* InventoryWindow::getItem() return mItems->getItem(); } -void -InventoryWindow::keyPressed(gcn::KeyEvent &event) +void InventoryWindow::keyPressed(gcn::KeyEvent &event) { switch (event.getKey().getValue()) { @@ -271,8 +269,7 @@ InventoryWindow::keyPressed(gcn::KeyEvent &event) } } -void -InventoryWindow::keyReleased(gcn::KeyEvent &event) +void InventoryWindow::keyReleased(gcn::KeyEvent &event) { switch (event.getKey().getValue()) { diff --git a/src/gui/inventorywindow.h b/src/gui/inventorywindow.h index 37ef0406..a9fdadf2 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -29,8 +29,9 @@ #include <guichan/widgets/checkbox.hpp> -#include "window.h" #include "selectionlistener.h" +#include "window.h" +#include "windowlistener.h" #include "../guichanfwd.h" @@ -45,7 +46,8 @@ class ItemContainer; class InventoryWindow : public Window, public gcn::ActionListener, public gcn::KeyListener, - public SelectionListener + public SelectionListener, + public WindowListener { public: /** @@ -92,7 +94,10 @@ class InventoryWindow : public Window, */ void selectionChanged(const SelectionEvent &event); - void updateContentSize(); /**< Updates widgets size/position. */ + /** + * Called whenever the window is resized. + */ + void windowResized(const WindowEvent &event); private: void updateButtons(); /**< Updates button states. */ diff --git a/src/gui/itemcontainer.cpp b/src/gui/itemcontainer.cpp index b032bd49..bddf7cee 100644 --- a/src/gui/itemcontainer.cpp +++ b/src/gui/itemcontainer.cpp @@ -30,6 +30,7 @@ #include "../graphics.h" #include "../inventory.h" #include "../item.h" +#include "../itemshortcut.h" #include "../localplayer.h" #include "../resources/image.h" @@ -38,6 +39,9 @@ #include "../utils/tostring.h" +// TODO: Add support for adding items to the item shortcut window (global +// itemShortcut). + static const int BOX_WIDTH = 36; static const int BOX_HEIGHT = 44; diff --git a/src/gui/itemshortcutcontainer.cpp b/src/gui/itemshortcutcontainer.cpp new file mode 100644 index 00000000..1943ef93 --- /dev/null +++ b/src/gui/itemshortcutcontainer.cpp @@ -0,0 +1,224 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "itemshortcutcontainer.h" + +#include "../graphics.h" +#include "../item.h" +#include "../itemshortcut.h" +#include "../keyboardconfig.h" + +#include "../resources/image.h" +#include "../resources/resourcemanager.h" + +#include "../utils/tostring.h" + +ItemShortcutContainer::ItemShortcutContainer(): + mGridWidth(1), + mGridHeight(1), + mItemClicked(false), + mItemMoved(NULL) +{ + addMouseListener(this); + + ResourceManager *resman = ResourceManager::getInstance(); + + mBackgroundImg = resman->getImage("graphics/gui/item_shortcut_bgr.png"); + mMaxItems = itemShortcut->getItemCount(); + + mBoxHeight = 42; + mBoxWidth = 36; +} + +ItemShortcutContainer::~ItemShortcutContainer() +{ + mBackgroundImg->decRef(); +} + +void +ItemShortcutContainer::logic() +{ + gcn::Widget::logic(); + + int i = itemShortcut->getItemCount(); + + if (i != mMaxItems) + { + mMaxItems = i; + setWidth(getWidth()); + } +} + +void +ItemShortcutContainer::draw(gcn::Graphics *graphics) +{ + Graphics *g = static_cast<Graphics*>(graphics); + + for (int i = 0; i < mMaxItems; i++) + { + const int itemX = (i % mGridWidth) * mBoxWidth; + const int itemY = (i / mGridWidth) * mBoxHeight; + + g->drawImage(mBackgroundImg, itemX, itemY); + + // Draw item keyboard shortcut. + const char *key = SDL_GetKeyName( + (SDLKey) keyboard.getKeyValue(keyboard.KEY_SHORTCUT_0+i)); + g->drawText(key, itemX + 2, itemY + 2, gcn::Graphics::LEFT); + + Item *item = itemShortcut->getItem(i); + if (item) { + // Draw item icon. + Image* image = item->getInfo().getImage(); + if (image) { + g->drawImage(image, itemX, itemY); + g->drawText( + toString(item->getQuantity()), + itemX + mBoxWidth / 2, + itemY + mBoxHeight - 14, + gcn::Graphics::CENTER); + } + } + } + if (mItemMoved) + { + // Draw the item image being dragged by the cursor. + Image* image = mItemMoved->getInfo().getImage(); + if (image) + { + const int tPosX = mCursorPosX - (image->getWidth() / 2); + 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); + } + } +} + +void +ItemShortcutContainer::setWidth(int width) +{ + gcn::Widget::setWidth(width); + + mGridWidth = getWidth() / mBoxWidth; + if (mGridWidth < 1) { + mGridWidth = 1; + } + + setHeight((mMaxItems / mGridWidth + + (mMaxItems % mGridWidth > 0 ? 1 : 0)) * mBoxHeight); + + mGridHeight = getHeight() / mBoxHeight; + if (mGridHeight < 1) { + mGridHeight = 1; + } +} + +void +ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event) +{ + if (event.getButton() == gcn::MouseEvent::LEFT) { + if (!mItemMoved && mItemClicked) { + const int index = getIndexFromGrid(event.getX(), event.getY()); + if (index == -1) { + return; + } + Item *item = itemShortcut->getItem(index); + if (item) + { + mItemMoved = item; + itemShortcut->removeItem(index); + } + } + if (mItemMoved) { + mCursorPosX = event.getX(); + mCursorPosY = event.getY(); + } + } +} + +void +ItemShortcutContainer::mousePressed(gcn::MouseEvent &event) +{ + const int index = getIndexFromGrid(event.getX(), event.getY()); + if (index == -1) { + return; + } + + // Stores the selected item if theirs one. + if (itemShortcut->isItemSelected()) { + itemShortcut->setItem(index); + itemShortcut->setItemSelected(NULL); + } + else if (itemShortcut->getItem(index)) { + mItemClicked = true; + } +} + +void +ItemShortcutContainer::mouseReleased(gcn::MouseEvent &event) +{ + if (event.getButton() == gcn::MouseEvent::LEFT) + { + if (itemShortcut->isItemSelected()) + { + itemShortcut->setItemSelected(NULL); + } + const int index = getIndexFromGrid(event.getX(), event.getY()); + if (index == -1) { + mItemMoved = NULL; + return; + } + if (mItemMoved) { + itemShortcut->setItems(index, mItemMoved); + mItemMoved = NULL; + } + else if (itemShortcut->getItem(index) && mItemClicked) + { + itemShortcut->useItem(index); + } + if (mItemClicked) { + mItemClicked = false; + } + } +} + +int +ItemShortcutContainer::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; + } + return index; +} diff --git a/src/gui/itemshortcutcontainer.h b/src/gui/itemshortcutcontainer.h new file mode 100644 index 00000000..4b154cbb --- /dev/null +++ b/src/gui/itemshortcutcontainer.h @@ -0,0 +1,115 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _TMW_ITEMSHORTCUTCONTAINER_H__ +#define _TMW_ITEMSHORTCUTCONTAINER_H__ + +#include <guichan/mouselistener.hpp> +#include <guichan/widget.hpp> + +#include <list> + +class Image; +class Item; + +/** + * An item shortcut container. Used to quickly use items. + * + * \ingroup GUI + */ +class ItemShortcutContainer : public gcn::Widget, public gcn::MouseListener +{ + public: + /** + * Constructor. Initializes the graphic. + */ + ItemShortcutContainer(); + + /** + * Destructor. + */ + virtual ~ItemShortcutContainer(); + + /** + * Handles the logic of the ItemContainer + */ + void logic(); + + /** + * Draws the items. + */ + void draw(gcn::Graphics *graphics); + + /** + * Sets the width of the container. This is used to determine the new + * height of the container. + */ + void setWidth(int width); + + /** + * Handles mouse when dragged. + */ + void mouseDragged(gcn::MouseEvent &event); + + /** + * Handles mouse when pressed. + */ + void mousePressed(gcn::MouseEvent &event); + + /** + * Handles mouse release. + */ + void mouseReleased(gcn::MouseEvent &event); + + + int getMaxItems() + { return mMaxItems; } + + int getBoxWidth() + { return mBoxWidth; } + + int getBoxHeight() + { return mBoxHeight; } + + private: + /** + * Gets the index from the grid provided the point is in an item box. + * + * @param pointX X coordinate of the point. + * @param pointY Y coordinate of the point. + * @return index on success, -1 on failure. + */ + int getIndexFromGrid(int pointX, int pointY) const; + + Image *mBackgroundImg; + + int mMaxItems; + int mBoxWidth; + int mBoxHeight; + int mCursorPosX, mCursorPosY; + int mGridWidth, mGridHeight; + bool mItemClicked; + Item *mItemMoved; + +}; + +#endif diff --git a/src/gui/itemshortcutwindow.cpp b/src/gui/itemshortcutwindow.cpp new file mode 100644 index 00000000..dd97a7db --- /dev/null +++ b/src/gui/itemshortcutwindow.cpp @@ -0,0 +1,74 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "itemshortcutwindow.h" + +#include "itemshortcutcontainer.h" +#include "scrollarea.h" + +static const int SCROLL_PADDING = 0; + +ItemShortcutWindow::ItemShortcutWindow() +{ + // no title presented, title bar is padding so window can be moved. + gcn::Window::setTitleBarHeight(gcn::Window::getPadding()); + setShowTitle(false); + setResizable(true); + setDefaultSize(758, 174, 42, 426); + + mItems = new ItemShortcutContainer(); + + 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); + + mInvenScroll = new ScrollArea(mItems); + mInvenScroll->setPosition(SCROLL_PADDING, SCROLL_PADDING); + mInvenScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + + add(mInvenScroll); + + addWindowListener(this); + loadWindowState("ItemShortcut"); +} + +ItemShortcutWindow::~ItemShortcutWindow() +{ + delete mItems; + delete mInvenScroll; +} + +void ItemShortcutWindow::logic() +{ + Window::logic(); +} + +void ItemShortcutWindow::windowResized(const WindowEvent &event) +{ + const gcn::Rectangle area = getChildrenArea(); + + mInvenScroll->setSize( + area.width - SCROLL_PADDING, + area.height - SCROLL_PADDING); +} diff --git a/src/gui/itemshortcutwindow.h b/src/gui/itemshortcutwindow.h new file mode 100644 index 00000000..83bc348d --- /dev/null +++ b/src/gui/itemshortcutwindow.h @@ -0,0 +1,69 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _TMW_ITEMSHORTCUTWINDOW_H +#define _TMW_ITEMSHORTCUTWINDOW_H + +#include "window.h" +#include "windowlistener.h" + +#include "../guichanfwd.h" + +class ItemShortcutContainer; + +/** + * Inventory dialog. + * + * \ingroup Interface + */ +class ItemShortcutWindow : public Window, WindowListener +{ + public: + /** + * Constructor. + */ + ItemShortcutWindow(); + + /** + * Destructor. + */ + ~ItemShortcutWindow(); + + /** + * Logic (updates buttons and weight information). + */ + void logic(); + + /** + * Called whenever the window is resized. + */ + void windowResized(const WindowEvent &event); + + private: + ItemShortcutContainer *mItems; + + gcn::ScrollArea *mInvenScroll; +}; + +extern ItemShortcutWindow *itemShortcutWindow; + +#endif diff --git a/src/gui/menuwindow.cpp b/src/gui/menuwindow.cpp index bfa2f1f2..8d4d184d 100644 --- a/src/gui/menuwindow.cpp +++ b/src/gui/menuwindow.cpp @@ -37,6 +37,7 @@ extern Window *inventoryWindow; extern Window *equipmentWindow; extern Window *skillDialog; extern Window *statusWindow; +extern Window *itemShortcutWindow; namespace { struct MenuWindowListener : public gcn::ActionListener @@ -62,6 +63,7 @@ MenuWindow::MenuWindow(): N_("Equipment"), N_("Inventory"), N_("Skills"), + N_("Shortcut"), N_("Setup"), 0 }; @@ -109,14 +111,20 @@ void MenuWindowListener::action(const gcn::ActionEvent &event) { window = skillDialog; } + else if (event.getId() == "Shortcut") + { + window = itemShortcutWindow; + } else if (event.getId() == "Setup") { window = setupWindow; } - if (window) { + if (window) + { window->setVisible(!window->isVisible()); - if (window->isVisible()) { + if (window->isVisible()) + { window->requestMoveToTop(); } } diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 48bbd3d0..aeb6637d 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -200,12 +200,6 @@ void PopupMenu::handleLink(const std::string& link) setVisible(false); - /* - * This is need cause of a bug in guichan that leave - * the focus on the popup menu even if is not visible. - */ - _getFocusHandler()->focusNone(); - mBeing = NULL; mFloorItem = NULL; mItem = NULL; diff --git a/src/gui/selectionlistener.h b/src/gui/selectionlistener.h index b39672b5..917a4871 100644 --- a/src/gui/selectionlistener.h +++ b/src/gui/selectionlistener.h @@ -25,33 +25,23 @@ #define _TMW_SELECTIONLISTENER_H__ #include <guichan/widget.hpp> +#include <guichan/event.hpp> /** * An event that characterizes a change in the current selection. * * \ingroup GUI */ -class SelectionEvent +class SelectionEvent : public gcn::Event { public: /** * Constructor. */ SelectionEvent(gcn::Widget *source): - mSource(source) + gcn::Event(source) { } - - /** - * The widget from which the event originated. - */ - gcn::Widget* getSource() const - { - return mSource; - } - - private: - gcn::Widget *mSource; }; /** diff --git a/src/gui/sell.cpp b/src/gui/sell.cpp index b601d70c..5f1011c1 100644 --- a/src/gui/sell.cpp +++ b/src/gui/sell.cpp @@ -44,58 +44,44 @@ SellDialog::SellDialog(): Window("Sell"), mMaxItems(0), mAmountItems(0) { + setResizable(true); + setMinWidth(260); + setMinHeight(230); + setDefaultSize(0, 0, 260, 230); + mShopItems = new ShopItems(); mShopItemList = new ShopListBox(mShopItems, mShopItems); - ScrollArea *scrollArea = new ScrollArea(mShopItemList); + mScrollArea = new ScrollArea(mShopItemList); mSlider = new Slider(1.0); mQuantityLabel = new gcn::Label("0"); mMoneyLabel = new gcn::Label("Money: 0 GP / Total: 0 GP"); - mItemDescLabel = new gcn::Label("Description:"); - mItemEffectLabel = new gcn::Label("Effect:"); mIncreaseButton = new Button("+", "+", this); mDecreaseButton = new Button("-", "-", this); mSellButton = new Button("Sell", "sell", this); - Button *quitButton = new Button("Quit", "quit", this); - mSellButton->setEnabled(false); - - setContentSize(260, 210); - scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); - scrollArea->setDimension(gcn::Rectangle(5, 5, 250, 110)); - mShopItemList->setDimension(gcn::Rectangle(5, 5, 238, 110)); - - mSlider->setDimension(gcn::Rectangle(5, 120, 200, 10)); - mSlider->setEnabled(false); - - mQuantityLabel->setPosition(215, 120); + mQuitButton = new Button("Quit", "quit", this); + mItemDescLabel = new gcn::Label("Description:"); + mItemEffectLabel = new gcn::Label("Effect:"); - mIncreaseButton->setPosition(40, 186); mIncreaseButton->setSize(20, 20); - mIncreaseButton->setEnabled(false); - - mDecreaseButton->setPosition(10, 186); mDecreaseButton->setSize(20, 20); - mDecreaseButton->setEnabled(false); + mQuantityLabel->setWidth(60); - mMoneyLabel->setPosition(5, 130); - mItemEffectLabel->setDimension(gcn::Rectangle(5, 150, 240, 14)); - mItemDescLabel->setDimension(gcn::Rectangle(5, 169, 240, 14)); - - mSellButton->setPosition(175, 186); + mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mIncreaseButton->setEnabled(false); + mDecreaseButton->setEnabled(false); mSellButton->setEnabled(false); + mSlider->setEnabled(false); - quitButton->setPosition(208, 186); - + mShopItemList->setPriceCheck(false); mShopItemList->setActionEventId("item"); mSlider->setActionEventId("slider"); - mShopItemList->setPriceCheck(false); - mShopItemList->addActionListener(this); mShopItemList->addSelectionListener(this); mSlider->addActionListener(this); - add(scrollArea); + add(mScrollArea); add(mSlider); add(mQuantityLabel); add(mMoneyLabel); @@ -104,8 +90,10 @@ SellDialog::SellDialog(): add(mIncreaseButton); add(mDecreaseButton); add(mSellButton); - add(quitButton); + add(mQuitButton); + addWindowListener(this); + loadWindowState("Sell"); setLocationRelativeTo(getParent()); } @@ -117,7 +105,7 @@ SellDialog::~SellDialog() void SellDialog::reset() { mShopItems->clear(); - mSlider->setValue(0.0); + mSlider->setValue(0); // Reset previous selected item to prevent failing asserts mShopItemList->setSelected(-1); @@ -135,16 +123,11 @@ void SellDialog::action(const gcn::ActionEvent &event) { int selectedItem = mShopItemList->getSelected(); - if (event.getId() == "item") - { - mAmountItems = 0; - mSlider->setValue(0); - updateButtonsAndLabels(); - } - else if (event.getId() == "quit") + if (event.getId() == "quit") { setVisible(false); current_npc = 0; + return; } // The following actions require a valid item selection @@ -156,21 +139,19 @@ void SellDialog::action(const gcn::ActionEvent &event) if (event.getId() == "slider") { - mAmountItems = (int) (mSlider->getValue() * mMaxItems); + mAmountItems = (int) mSlider->getValue(); updateButtonsAndLabels(); } else if (event.getId() == "+" && mAmountItems < mMaxItems) { mAmountItems++; - - mSlider->setValue((double) mAmountItems /(double) mMaxItems); + mSlider->setValue(mAmountItems); updateButtonsAndLabels(); } - else if (event.getId() == "-" && mAmountItems > 0) + else if (event.getId() == "-" && mAmountItems > 1) { mAmountItems--; - - mSlider->setValue((double) mAmountItems / (double) mMaxItems); + mSlider->setValue(mAmountItems); updateButtonsAndLabels(); } else if (event.getId() == "sell" && mAmountItems > 0 @@ -182,8 +163,7 @@ void SellDialog::action(const gcn::ActionEvent &event) mMaxItems -= mAmountItems; mShopItems->getShop()->at(selectedItem).quantity = mMaxItems; mPlayerMoney += (mAmountItems * mShopItems->at(selectedItem).price); - mAmountItems = 0; - mSlider->setValue(0); + mAmountItems = 1; if (!mMaxItems) { @@ -194,6 +174,7 @@ void SellDialog::action(const gcn::ActionEvent &event) } else { + mSlider->gcn::Slider::setScale(1, mMaxItems); // Update only when there are items left, the entry doesn't exist // otherwise and can't be updated updateButtonsAndLabels(); @@ -204,10 +185,54 @@ void SellDialog::action(const gcn::ActionEvent &event) void SellDialog::selectionChanged(const SelectionEvent &event) { // Reset amount of items and update labels - mAmountItems = 0; + mAmountItems = 1; mSlider->setValue(0); updateButtonsAndLabels(); + mSlider->gcn::Slider::setScale(1, mMaxItems); +} + +void SellDialog::windowResized(const WindowEvent &event) +{ + gcn::Rectangle area = getChildrenArea(); + int width = area.width; + int height = area.height; + + mDecreaseButton->setPosition(8, height - 8 - mDecreaseButton->getHeight()); + mIncreaseButton->setPosition( + mDecreaseButton->getX() + mDecreaseButton->getWidth() + 5, + mDecreaseButton->getY()); + + mQuitButton->setPosition( + width - 8 - mQuitButton->getWidth(), + height - 8 - mQuitButton->getHeight()); + mSellButton->setPosition( + mQuitButton->getX() - 5 - mSellButton->getWidth(), + mQuitButton->getY()); + + mItemDescLabel->setDimension(gcn::Rectangle(8, + mSellButton->getY() - 5 - mItemDescLabel->getHeight(), + width - 16, + mItemDescLabel->getHeight())); + mItemEffectLabel->setDimension(gcn::Rectangle(8, + mItemDescLabel->getY() - 5 - mItemEffectLabel->getHeight(), + width - 16, + mItemEffectLabel->getHeight())); + mMoneyLabel->setDimension(gcn::Rectangle(8, + mItemEffectLabel->getY() - 5 - mMoneyLabel->getHeight(), + width - 16, + mMoneyLabel->getHeight())); + + mQuantityLabel->setPosition( + width - mQuantityLabel->getWidth() - 8, + mMoneyLabel->getY() - 5 - mQuantityLabel->getHeight()); + mSlider->setDimension(gcn::Rectangle(8, + mQuantityLabel->getY(), + mQuantityLabel->getX() - 8 - 8, + 10)); + + mScrollArea->setDimension(gcn::Rectangle(8, 8, width - 16, + mSlider->getY() - 5 - 8)); } void SellDialog::setMoney(int amount) @@ -246,15 +271,13 @@ SellDialog::updateButtonsAndLabels() // Update Buttons and slider mSellButton->setEnabled(mAmountItems > 0); - mDecreaseButton->setEnabled(mAmountItems > 0); + mDecreaseButton->setEnabled(mAmountItems > 1); mIncreaseButton->setEnabled(mAmountItems < mMaxItems); - mSlider->setEnabled(selectedItem > -1); + mSlider->setEnabled(mMaxItems > 1); // Update the quantity and money labels mQuantityLabel->setCaption( toString(mAmountItems) + " / " + toString(mMaxItems)); - mQuantityLabel->adjustSize(); mMoneyLabel->setCaption("Money: " + toString(income) + " GP / Total: " + toString(mPlayerMoney + income) + " GP"); - mMoneyLabel->adjustSize(); } diff --git a/src/gui/sell.h b/src/gui/sell.h index fc42fd1c..d1e2ddd2 100644 --- a/src/gui/sell.h +++ b/src/gui/sell.h @@ -28,6 +28,7 @@ #include "window.h" #include "selectionlistener.h" +#include "windowlistener.h" #include "../guichanfwd.h" @@ -40,7 +41,8 @@ class ShopListBox; * * \ingroup Interface */ -class SellDialog : public Window, gcn::ActionListener, SelectionListener +class SellDialog : public Window, gcn::ActionListener, SelectionListener, + WindowListener { public: /** @@ -78,21 +80,27 @@ class SellDialog : public Window, gcn::ActionListener, SelectionListener void selectionChanged(const SelectionEvent &event); /** + * Called whenever the window is resized. + */ + void windowResized(const WindowEvent &event); + + /** * Gives Player's Money amount */ void setMoney(int amount); + private: /** * Updates the state of buttons and labels. */ - void - updateButtonsAndLabels(); + void updateButtonsAndLabels(); - private: gcn::Button *mSellButton; + gcn::Button *mQuitButton; gcn::Button *mIncreaseButton; gcn::Button *mDecreaseButton; ShopListBox *mShopItemList; + gcn::ScrollArea *mScrollArea; gcn::Label *mMoneyLabel; gcn::Label *mItemDescLabel; gcn::Label *mItemEffectLabel; diff --git a/src/gui/setup.cpp b/src/gui/setup.cpp index 3ea19059..6a13232a 100644 --- a/src/gui/setup.cpp +++ b/src/gui/setup.cpp @@ -27,6 +27,7 @@ #include "setup_audio.h" #include "setup_joystick.h" #include "setup_video.h" +#include "setup_keyboard.h" #include "tabbedcontainer.h" #include "../utils/dtor.h" @@ -43,7 +44,8 @@ extern Window *skillDialog; Setup::Setup(): Window(_("Setup")) { - int width = 230; + setCloseButton(true); + int width = 250; int height = 245; setContentSize(width, height); @@ -59,7 +61,7 @@ Setup::Setup(): } TabbedContainer *panel = new TabbedContainer(); - panel->setDimension(gcn::Rectangle(5, 5, 220, 205)); + panel->setDimension(gcn::Rectangle(5, 5, 250, 205)); panel->setOpaque(false); SetupTab *tab; @@ -76,6 +78,10 @@ Setup::Setup(): panel->addTab(tab, _("Joystick")); mTabs.push_back(tab); + tab = new Setup_Keyboard(); + panel->addTab(tab, "Keyboard"); + mTabs.push_back(tab); + add(panel); setLocationRelativeTo(getParent()); diff --git a/src/gui/setup_keyboard.cpp b/src/gui/setup_keyboard.cpp new file mode 100644 index 00000000..e88080b5 --- /dev/null +++ b/src/gui/setup_keyboard.cpp @@ -0,0 +1,187 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "setup_keyboard.h" + +#include <guichan/widgets/label.hpp> +#include <guichan/listmodel.hpp> + +#include "button.h" +#include "listbox.h" +#include "ok_dialog.h" +#include "scrollarea.h" + +#include "../configuration.h" +#include "../keyboardconfig.h" + +#include "../utils/tostring.h" + +#include <SDL_keyboard.h> + +/** + * The list model for key function list. + * + * \ingroup Interface + */ +class KeyListModel : public gcn::ListModel +{ + public: + /** + * Returns the number of elements in container. + */ + int getNumberOfElements() { return keyboard.KEY_TOTAL; } + + /** + * Returns element from container. + */ + std::string getElementAt(int i) { return mKeyFunctions[i]; } + + /** + * Sets element from container. + */ + void setElementAt(int i, std::string caption) + { + mKeyFunctions[i] = caption; + } + + private: + std::string mKeyFunctions[KeyboardConfig::KEY_TOTAL]; +}; + +Setup_Keyboard::Setup_Keyboard(): + mKeyListModel(new KeyListModel()), + mKeyList(new ListBox(mKeyListModel)), + mKeySetting(false) +{ + keyboard.setSetupKeyboard(this); + setOpaque(false); + + refreshKeys(); + + mKeyList->setDimension(gcn::Rectangle(0, 0, 185, 140)); + mKeyList->addActionListener(this); + mKeyList->setSelected(-1); + + ScrollArea *scrollArea = new ScrollArea(mKeyList); + scrollArea->setDimension(gcn::Rectangle(10, 10, 200, 140)); + add(scrollArea); + + mAssignKeyButton = new Button("Assign", "assign", this); + mAssignKeyButton->setPosition(165, 155); + mAssignKeyButton->addActionListener(this); + mAssignKeyButton->setEnabled(false); + add(mAssignKeyButton); + + mMakeDefaultButton = new Button("Default", "makeDefault", this); + mMakeDefaultButton->setPosition(10, 155); + mMakeDefaultButton->addActionListener(this); + add(mMakeDefaultButton); +} + +Setup_Keyboard::~Setup_Keyboard() +{ + delete mKeyList; + delete mKeyListModel; + + delete mAssignKeyButton; + delete mMakeDefaultButton; +} + +void Setup_Keyboard::apply() +{ + keyUnresolved(); + + if (keyboard.hasConflicts()) + { + new OkDialog("Key Conflict(s) Detected.", + "Resolve them, or gameplay may result in strange behaviour."); + } + keyboard.setEnabled(true); + keyboard.store(); +} + +void Setup_Keyboard::cancel() +{ + keyUnresolved(); + + keyboard.retrieve(); + keyboard.setEnabled(true); + + refreshKeys(); +} + +void Setup_Keyboard::action(const gcn::ActionEvent &event) +{ + if (event.getSource() == mKeyList) + { + if (!mKeySetting) { + mAssignKeyButton->setEnabled(true); + } + } + else if (event.getId() == "assign") + { + mKeySetting = true; + mAssignKeyButton->setEnabled(false); + keyboard.setEnabled(false); + int i(mKeyList->getSelected()); + keyboard.setNewKeyIndex(i); + mKeyListModel->setElementAt(i, keyboard.getKeyCaption(i) + ": ?"); + } + else if (event.getId() == "makeDefault") + { + keyboard.makeDefault(); + refreshKeys(); + } +} + +void Setup_Keyboard::refreshAssignedKey(int index) +{ + std::string caption; + char *temp = SDL_GetKeyName( + (SDLKey) keyboard.getKeyValue(index)); + caption = keyboard.getKeyCaption(index) + ": " + toString(temp); + mKeyListModel->setElementAt(index, caption); +} + +void Setup_Keyboard::newKeyCallback(int index) +{ + mKeySetting = false; + refreshAssignedKey(index); + mAssignKeyButton->setEnabled(true); +} + +void Setup_Keyboard::refreshKeys() +{ + for(int i = 0; i < keyboard.KEY_TOTAL; i++) + { + refreshAssignedKey(i); + } +} + +void Setup_Keyboard::keyUnresolved() +{ + if (mKeySetting) { + newKeyCallback(keyboard.getNewKeyIndex()); + keyboard.setNewKeyIndex(keyboard.KEY_NO_VALUE); + } +} diff --git a/src/gui/setup_keyboard.h b/src/gui/setup_keyboard.h new file mode 100644 index 00000000..b72e8746 --- /dev/null +++ b/src/gui/setup_keyboard.h @@ -0,0 +1,84 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_GUI_SETUP_KEYBOARD_H +#define _TMW_GUI_SETUP_KEYBOARD_H + +#include "setuptab.h" +#include "button.h" +#include "../guichanfwd.h" + +#include <guichan/actionlistener.hpp> + + +#include <string> + +class Setup_Keyboard : public SetupTab, public gcn::ActionListener +{ + public: + /** + * Constructor + */ + Setup_Keyboard(); + + /** + * Destructor + */ + ~Setup_Keyboard(); + + void apply(); + void cancel(); + + void action(const gcn::ActionEvent &event); + + /** + * Get an update on the assigned key. + */ + void refreshAssignedKey(int index); + + /** + * The callback function when a new key has been pressed. + */ + void newKeyCallback(int index); + + /** + * Shorthand method to update all the keys. + */ + void refreshKeys(); + + /** + * If a key function is unresolved, then this reverts it. + */ + void keyUnresolved(); + + private: + class KeyListModel *mKeyListModel; + gcn::ListBox *mKeyList; + + gcn::Button *mAssignKeyButton; + gcn::Button *mMakeDefaultButton; + + bool mKeySetting; /**< flag to check if key being set. */ +}; + +#endif diff --git a/src/gui/shoplistbox.cpp b/src/gui/shoplistbox.cpp index 0e2ea6d3..ae787ab7 100644 --- a/src/gui/shoplistbox.cpp +++ b/src/gui/shoplistbox.cpp @@ -129,24 +129,8 @@ void ShopListBox::mousePressed(gcn::MouseEvent &event) { if (event.getButton() == gcn::MouseEvent::LEFT) { - bool enoughMoney = false; - int y = event.getY(); - - if (mShopItems && mPriceCheck) - { - if (mPlayerMoney >= mShopItems->at(y / mRowHeight).price) - enoughMoney = true; - } - else // Old Behaviour - { - enoughMoney = true; - } - - if (enoughMoney) - { - setSelected(y / mRowHeight); - generateAction(); - } + setSelected(event.getY() / mRowHeight); + generateAction(); } } diff --git a/src/gui/skill.cpp b/src/gui/skill.cpp index 884b3744..d5cfe76a 100644 --- a/src/gui/skill.cpp +++ b/src/gui/skill.cpp @@ -63,22 +63,18 @@ const char *skill_db[] = { SkillDialog::SkillDialog(): Window("Skills") { + setCloseButton(true); setDefaultSize(windowContainer->getWidth() - 255, 25, 240, 240); mSkillListBox = new ListBox(this); ScrollArea *skillScrollArea = new ScrollArea(mSkillListBox); - mCloseButton = new Button("Close", "close", this); mSkillListBox->setActionEventId("skill"); skillScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); skillScrollArea->setDimension(gcn::Rectangle(5, 5, 230, 180)); - mCloseButton->setPosition( - skillScrollArea->getX() + skillScrollArea->getWidth() - mCloseButton->getWidth(), - 210); add(skillScrollArea); - add(mCloseButton); mSkillListBox->addActionListener(this); diff --git a/src/gui/skill.h b/src/gui/skill.h index b8794e35..f1a14d50 100644 --- a/src/gui/skill.h +++ b/src/gui/skill.h @@ -71,7 +71,6 @@ class SkillDialog : public Window, public gcn::ActionListener, private: gcn::ListBox *mSkillListBox; - gcn::Button *mCloseButton; std::vector<SKILL*> mSkillList; }; diff --git a/src/gui/status.cpp b/src/gui/status.cpp index 7da3b905..323a6b16 100644 --- a/src/gui/status.cpp +++ b/src/gui/status.cpp @@ -39,6 +39,7 @@ StatusWindow::StatusWindow(LocalPlayer *player): mPlayer(player) { setResizable(true); + setCloseButton(true); setDefaultSize((windowContainer->getWidth() - 365) / 2, (windowContainer->getHeight() - 255) / 2, 365, 280); loadWindowState("Status"); @@ -219,7 +220,7 @@ void StatusWindow::update() mHpBar->setColor(0, 171, 34); // Green } - mHpBar->setProgress((float)hp / maxHp); + mHpBar->setProgress((float) hp / maxHp); // Stats Part // ---------- @@ -235,7 +236,8 @@ void StatusWindow::update() int statusPoints = mPlayer->getAttributeIncreasePoints(); // Update labels - for (int i = 0; i < 7; i++) { + for (int i = 0; i < 7; i++) + { mStatsLabel[i]->setCaption(attrNames[i]); mStatsDisplayLabel[i]->setCaption( strprintf("%d / %d", diff --git a/src/gui/status.h b/src/gui/status.h index 40d25a2a..62cd8805 100644 --- a/src/gui/status.h +++ b/src/gui/status.h @@ -41,7 +41,8 @@ class ProgressBar; * * \ingroup Interface */ -class StatusWindow : public Window, public gcn::ActionListener { +class StatusWindow : public Window, public gcn::ActionListener +{ public: /** * Constructor. diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index ed75e5b3..5d81bb9c 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -46,6 +46,8 @@ #include "../resources/resourcemanager.h" +extern std::string homeDir; + /** * Calculates the Alder-32 checksum for the given file. */ @@ -119,7 +121,7 @@ UpdaterWindow::UpdaterWindow(): mUpdateHost = config.getValue("updatehost", "http://updates.themanaworld.org"); - mBasePath = config.getValue("homeDir", "."); + mBasePath = homeDir; // Try to download the updates list download(); diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index 2af1d960..4c6b04a2 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -55,8 +55,7 @@ Viewport::Viewport(): mCameraX(0), mCameraY(0), mShowDebugPath(false), - mPlayerFollowMouse(false), - mPopupActive(false) + mPlayerFollowMouse(false) { setOpaque(false); addMouseListener(this); @@ -372,21 +371,20 @@ Viewport::mousePressed(gcn::MouseEvent &event) if ((being = beingManager->findBeing(tilex, tiley)) && being->getType() != Being::LOCALPLAYER) { - showPopup(event.getX(), event.getY(), being); + mPopupMenu->showPopup(event.getX(), event.getY(), being); return; } else if((floorItem = floorItemManager->findByCoordinates(tilex, tiley))) { - showPopup(event.getX(), event.getY(), floorItem); + mPopupMenu->showPopup(event.getX(), event.getY(), floorItem); return; } } // If a popup is active, just remove it - if (mPopupActive) + if (mPopupMenu->isVisible()) { mPopupMenu->setVisible(false); - mPopupActive = false; return; } @@ -450,21 +448,6 @@ void Viewport::showPopup(int x, int y, Item *item) { mPopupMenu->showPopup(x, y, item); - mPopupActive = true; -} - -void -Viewport::showPopup(int x, int y, FloorItem *floorItem) -{ - mPopupMenu->showPopup(x, y, floorItem); - mPopupActive = true; -} - -void -Viewport::showPopup(int x, int y, Being *being) -{ - mPopupMenu->showPopup(x, y, being); - mPopupActive = true; } void diff --git a/src/gui/viewport.h b/src/gui/viewport.h index 22d0f249..eeb31bae 100644 --- a/src/gui/viewport.h +++ b/src/gui/viewport.h @@ -130,18 +130,6 @@ class Viewport : public WindowContainer, public gcn::MouseListener, private: /** - * Shows a popup for a floor item. - * TODO Find some way to get rid of FloorItem here - */ - void showPopup(int x, int y, FloorItem *floorItem); - - /** - * Shows a popup for a being. - * TODO Find some way to get rid of Being here - */ - void showPopup(int x, int y, Being *being); - - /** * Helper function for loading target cursors */ void @@ -187,7 +175,6 @@ class Viewport : public WindowContainer, public gcn::MouseListener, int mWalkTime; PopupMenu *mPopupMenu; /**< Popup menu. */ - bool mPopupActive; }; #endif diff --git a/src/gui/window.cpp b/src/gui/window.cpp index 84f4466c..17447009 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -26,6 +26,7 @@ #include <guichan/exception.hpp> #include <guichan/widgets/icon.hpp> +#include "gui.h" #include "gccontainer.h" #include "windowcontainer.h" @@ -42,7 +43,9 @@ ConfigListener *Window::windowConfigListener = 0; WindowContainer *Window::windowContainer = 0; int Window::instances = 0; +int Window::mouseResize = 0; ImageRect Window::border; +Image *Window::closeImage = NULL; class WindowConfigListener : public ConfigListener { @@ -60,7 +63,7 @@ Window::Window(const std::string& caption, bool modal, Window *parent): mParent(parent), mModal(modal), mResizable(false), - mMouseResize(0), + mCloseButton(false), mSticky(false), mMinWinWidth(100), mMinWinHeight(40), @@ -88,6 +91,8 @@ Window::Window(const std::string& caption, bool modal, Window *parent): border.grid[7] = dBorders->getSubImage(4, 15, 3, 4); border.grid[8] = dBorders->getSubImage(7, 15, 4, 4); dBorders->decRef(); + closeImage = resman->getImage("graphics/gui/close_button.png"); + windowConfigListener = new WindowConfigListener(); // Send GUI alpha changed for initialization windowConfigListener->optionChanged("guialpha"); @@ -110,6 +115,7 @@ Window::Window(const std::string& caption, bool modal, Window *parent): if (mModal) { + gui->setCursorType(Gui::CURSOR_POINTER); requestModalFocus(); } @@ -153,6 +159,7 @@ Window::~Window() delete border.grid[6]; delete border.grid[7]; delete border.grid[8]; + closeImage->decRef(); } delete mChrome; @@ -173,11 +180,19 @@ void Window::draw(gcn::Graphics *graphics) // Draw title if (getTitleBarHeight()) { - graphics->setColor(gcn::Color(0, 0, 0)); - graphics->setFont(getFont()); - graphics->drawText(getCaption(), 7, 5, gcn::Graphics::LEFT); + g->setColor(gcn::Color(0, 0, 0)); + g->setFont(getFont()); + g->drawText(getCaption(), 7, 5, gcn::Graphics::LEFT); } + // Draw Close Button + if (mCloseButton) + { + g->drawImage(closeImage, + getWidth() - closeImage->getWidth() - getPadding(), + getPadding() + ); + } drawChildren(graphics); } @@ -207,6 +222,8 @@ void Window::setWidth(int width) { mGrip->setX(getWidth() - mGrip->getWidth() - getChildrenArea().x); } + + fireWindowEvent(WindowEvent(this, WindowEvent::WINDOW_RESIZED)); } void Window::setHeight(int height) @@ -217,6 +234,8 @@ void Window::setHeight(int height) { mGrip->setY(getHeight() - mGrip->getHeight() - getChildrenArea().y); } + + fireWindowEvent(WindowEvent(this, WindowEvent::WINDOW_RESIZED)); } void Window::setDimension(const gcn::Rectangle &dimension) @@ -228,6 +247,27 @@ void Window::setDimension(const gcn::Rectangle &dimension) mGrip->setX(getWidth() - mGrip->getWidth() - getChildrenArea().x); mGrip->setY(getHeight() - mGrip->getHeight() - getChildrenArea().y); } + + fireWindowEvent(WindowEvent(this, WindowEvent::WINDOW_RESIZED)); + fireWindowEvent(WindowEvent(this, WindowEvent::WINDOW_MOVED)); +} + +void Window::setPosition(int x, int y) +{ + gcn::Window::setPosition(x, y); + fireWindowEvent(WindowEvent(this, WindowEvent::WINDOW_MOVED)); +} + +void Window::setX(int x) +{ + gcn::Window::setX(x); + fireWindowEvent(WindowEvent(this, WindowEvent::WINDOW_MOVED)); +} + +void Window::setY(int y) +{ + gcn::Window::setY(y); + fireWindowEvent(WindowEvent(this, WindowEvent::WINDOW_MOVED)); } void Window::setLocationRelativeTo(gcn::Widget *widget) @@ -280,6 +320,11 @@ void Window::setResizable(bool r) } } +void Window::setCloseButton(bool flag) +{ + mCloseButton = flag; +} + bool Window::isResizable() { return mResizable; @@ -327,27 +372,73 @@ void Window::mousePressed(gcn::MouseEvent &event) // Let Guichan move window to top and figure out title bar drag gcn::Window::mousePressed(event); - const int x = event.getX(); - const int y = event.getY(); - mMouseResize = 0; + if (event.getButton() == gcn::MouseEvent::LEFT) + { + const int x = event.getX(); + const int y = event.getY(); + + // Handle close button + if (mCloseButton) + { + gcn::Rectangle closeButtonRect( + getWidth() - closeImage->getWidth() - getPadding(), + getPadding(), + closeImage->getWidth(), + closeImage->getHeight()); + + if (closeButtonRect.isPointInRect(x, y)) + { + setVisible(false); + } + } + + // Handle window resizing + mouseResize = getResizeHandles(event); + } +} + +void Window::mouseReleased(gcn::MouseEvent &event) +{ + if (mResizable && mouseResize) + { + mouseResize = 0; + gui->setCursorType(Gui::CURSOR_POINTER); + } + + // This should be the responsibility of Guichan (and is from 0.8.0 on) + mIsMoving = false; +} - // Activate resizing handles as appropriate - if (event.getSource() == this && isResizable() && - event.getButton() == gcn::MouseEvent::LEFT && - !getChildrenArea().isPointInRect(x, y)) +void Window::mouseExited(gcn::MouseEvent &event) +{ + if (mResizable && !mouseResize) { - mMouseResize |= (x > getWidth() - resizeBorderWidth) ? RIGHT : - (x < resizeBorderWidth) ? LEFT : 0; - mMouseResize |= (y > getHeight() - resizeBorderWidth) ? BOTTOM : - (y < resizeBorderWidth) ? TOP : 0; + gui->setCursorType(Gui::CURSOR_POINTER); } - else if (event.getSource() == mGrip && - event.getButton() == gcn::MouseEvent::LEFT) +} + +void Window::mouseMoved(gcn::MouseEvent &event) +{ + int resizeHandles = getResizeHandles(event); + + // Changes the custom mouse cursor based on it's current position. + switch (resizeHandles) { - mDragOffsetX = x; - mDragOffsetY = y; - mMouseResize |= BOTTOM | RIGHT; - mIsMoving = false; + case BOTTOM | RIGHT: + gui->setCursorType(Gui::CURSOR_RESIZE_DOWN_RIGHT); + break; + case BOTTOM | LEFT: + gui->setCursorType(Gui::CURSOR_RESIZE_DOWN_LEFT); + break; + case BOTTOM: + gui->setCursorType(Gui::CURSOR_RESIZE_DOWN); + break; + case RIGHT: + case LEFT: + gui->setCursorType(Gui::CURSOR_RESIZE_ACROSS); + break; + default: + gui->setCursorType(Gui::CURSOR_POINTER); } } @@ -366,31 +457,31 @@ void Window::mouseDragged(gcn::MouseEvent &event) setPosition(newX, newY); } - if (mMouseResize && !mIsMoving) + if (mouseResize && !mIsMoving) { const int dx = event.getX() - mDragOffsetX; const int dy = event.getY() - mDragOffsetY; gcn::Rectangle newDim = getDimension(); - if (mMouseResize & (TOP | BOTTOM)) + if (mouseResize & (TOP | BOTTOM)) { - int newHeight = newDim.height + ((mMouseResize & TOP) ? -dy : dy); + int newHeight = newDim.height + ((mouseResize & TOP) ? -dy : dy); newDim.height = std::min(mMaxWinHeight, std::max(mMinWinHeight, newHeight)); - if (mMouseResize & TOP) + if (mouseResize & TOP) { newDim.y -= newDim.height - getHeight(); } } - if (mMouseResize & (LEFT | RIGHT)) + if (mouseResize & (LEFT | RIGHT)) { - int newWidth = newDim.width + ((mMouseResize & LEFT) ? -dx : dx); + int newWidth = newDim.width + ((mouseResize & LEFT) ? -dx : dx); newDim.width = std::min(mMaxWinWidth, std::max(mMinWinWidth, newWidth)); - if (mMouseResize & LEFT) + if (mouseResize & LEFT) { newDim.x -= newDim.width - getWidth(); } @@ -417,11 +508,11 @@ void Window::mouseDragged(gcn::MouseEvent &event) } // Update mouse offset when dragging bottom or right border - if (mMouseResize & BOTTOM) + if (mouseResize & BOTTOM) { mDragOffsetY += newDim.height - getHeight(); } - if (mMouseResize & RIGHT) + if (mouseResize & RIGHT) { mDragOffsetX += newDim.width - getWidth(); } @@ -469,3 +560,50 @@ void Window::resetToDefaultSize() setContentSize(mDefaultWidth, mDefaultHeight); updateContentSize(); } + +int Window::getResizeHandles(gcn::MouseEvent &event) +{ + int resizeHandles = 0; + const int y = event.getY(); + + if (mResizable && y > (int) mTitleBarHeight) + { + const int x = event.getX(); + + if (!getChildrenArea().isPointInRect(x, y) && + event.getSource() == this) + { + resizeHandles |= (x > getWidth() - resizeBorderWidth) ? RIGHT : + (x < resizeBorderWidth) ? LEFT : 0; + resizeHandles |= (y > getHeight() - resizeBorderWidth) ? BOTTOM : + (y < resizeBorderWidth) ? TOP : 0; + } + + if (event.getSource() == mGrip) + { + mDragOffsetX = x; + mDragOffsetY = y; + resizeHandles |= BOTTOM | RIGHT; + } + } + + return resizeHandles; +} + +void Window::fireWindowEvent(const WindowEvent &event) +{ + WindowListeners::iterator i_end = mListeners.end(); + WindowListeners::iterator i = mListeners.begin(); + + switch (event.getType()) + { + case WindowEvent::WINDOW_MOVED: + for (; i != i_end; ++i) + { (*i)->windowMoved(event); } + break; + case WindowEvent::WINDOW_RESIZED: + for (; i != i_end; ++i) + { (*i)->windowResized(event); } + break; + } +} diff --git a/src/gui/window.h b/src/gui/window.h index 1ba23fb2..ab266422 100644 --- a/src/gui/window.h +++ b/src/gui/window.h @@ -28,11 +28,14 @@ #include "../guichanfwd.h" +#include "windowlistener.h" + class ConfigListener; class GCContainer; class ImageRect; class ResizeGrip; class WindowContainer; +class Image; /** * A window. This window can be dragged around and has a title bar. Windows are @@ -120,16 +123,36 @@ class Window : public gcn::Window void setDimension(const gcn::Rectangle &dimension); /** + * Sets the position of this window. + */ + void setPosition(int x, int y); + + /** + * Sets the window x coordinate. + */ + void setX(int x); + + /** + * Sets the window y coordinate. + */ + void setY(int y); + + /** * Sets the location relative to the given widget. */ void setLocationRelativeTo(gcn::Widget *widget); /** - * Sets whether of not the window can be resized. + * Sets whether or not the window can be resized. */ void setResizable(bool resize); /** + * Sets whether or not the window has a close button. + */ + void setCloseButton(bool flag); + + /** * Returns whether the window can be resized. */ bool isResizable(); @@ -155,9 +178,14 @@ class Window : public gcn::Window void setMaxHeight(unsigned int height); /** - * Sets whether the window is sticky. - * A sticky window will not have its visibility set to false - * on a general setVisible(false) call. + * Sets flag to show a title or not. + */ + void setShowTitle(bool flag) + { mShowTitle = flag; } + + /** + * Sets whether the window is sticky. A sticky window will not have + * its visibility set to false on a general setVisible(false) call. */ void setSticky(bool sticky); @@ -167,10 +195,9 @@ class Window : public gcn::Window bool isSticky(); /** - * Overloads window setVisible by guichan to allow sticky window - * Handling + * Overloads window setVisible by Guichan to allow sticky window + * handling. */ - void setVisible(bool visible); /** @@ -199,6 +226,24 @@ class Window : public gcn::Window void mouseDragged(gcn::MouseEvent &event); /** + * Implements custom cursor image changing context, based on mouse + * relative position. + */ + void mouseMoved(gcn::MouseEvent &event); + + /** + * When the mouse button has been let go, this ensures that the mouse + * custom cursor is restored back to it's standard image. + */ + void mouseReleased(gcn::MouseEvent &event); + + /** + * When the mouse leaves the window this ensures that the custom cursor + * is restored back to it's standard image. + */ + void mouseExited(gcn::MouseEvent &event); + + /** * Read the x, y, and width and height for resizables in the config * based on the given string. * That function let the values set with set{X, Y, Height, width}() @@ -216,11 +261,25 @@ class Window : public gcn::Window int defaultWidth, int defaultHeight); /** - * Reset the win pos and size to default. - * Don't forget to set defaults first. + * Reset the win pos and size to default. Don't forget to set defaults + * first. */ void resetToDefaultSize(); + /** + * Adds a listener to the list that's notified when the window is + * moved or resized. + */ + void addWindowListener(WindowListener *listener) + { mListeners.push_back(listener); } + + /** + * Removes a listener from the list that's notified when the window is + * moved or resized. + */ + void removeWindowListener(WindowListener *listener) + { mListeners.remove(listener); } + enum ResizeHandles { TOP = 0x01, @@ -233,13 +292,23 @@ class Window : public gcn::Window static WindowContainer *windowContainer; private: + /** + * Determines if the mouse is in a resize area and returns appropriate + * resize handles. Also initializes drag offset in case the resize + * grip is used. + * + * @see ResizeHandles + */ + int getResizeHandles(gcn::MouseEvent &event); + GCContainer *mChrome; /**< Contained container */ ResizeGrip *mGrip; /**< Resize grip */ Window *mParent; /**< The parent window */ std::string mConfigName; /**< Name used for saving window-related data */ + bool mShowTitle; /**< Window has a title bar */ bool mModal; /**< Window is modal */ bool mResizable; /**< Window can be resized */ - int mMouseResize; /**< Window is being resized */ + bool mCloseButton; /**< Window has a close button */ bool mSticky; /**< Window resists minimization */ int mMinWinWidth; /**< Minimum window width */ int mMinWinHeight; /**< Minimum window height */ @@ -255,8 +324,10 @@ class Window : public gcn::Window */ static ConfigListener *windowConfigListener; + static int mouseResize; /**< Active resize handles */ static int instances; /**< Number of Window instances */ static ImageRect border; /**< The window border and background */ + static Image *closeImage; /**< Close Button Image */ /** * The width of the resize border. Is independent of the actual window @@ -264,6 +335,14 @@ class Window : public gcn::Window * where two borders are moved at the same time. */ static const int resizeBorderWidth = 10; + + private: + /** + * Sends out a window event to the list of selection listeners. + */ + void fireWindowEvent(const WindowEvent &event); + + WindowListeners mListeners; }; #endif diff --git a/src/gui/windowlistener.h b/src/gui/windowlistener.h new file mode 100644 index 00000000..08aab71d --- /dev/null +++ b/src/gui/windowlistener.h @@ -0,0 +1,86 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_WINDOW_LISTENER_H_ +#define _TMW_WINDOW_LISTENER_H_ + +#include <guichan/widgets/window.hpp> +#include <guichan/event.hpp> + +/** + * An event that characterizes a window move or resize. + * + * \ingroup GUI + */ +class WindowEvent : public gcn::Event +{ + public: + /** + * Constructor. + */ + WindowEvent(gcn::Window *source, int type): + gcn::Event(source) + { + mType = type; + } + + /** + * Returns the event type. + */ + int getType() const + { return mType; } + + enum WindowEventType + { + WINDOW_MOVED, + WINDOW_RESIZED + }; +}; + +/** + * The listener that's notified when a window is moved or resized. + * + * \ingroup GUI + */ +class WindowListener +{ + public: + /** + * Virtual destructor. + */ + virtual ~WindowListener() {} + + /** + * Called whenever the window is moved. + */ + virtual void windowMoved(const WindowEvent &event) {} + + /** + * Called whenever the window is resized. + */ + virtual void windowResized(const WindowEvent &event) {} +}; + +typedef std::list<WindowListener*> WindowListeners; + +#endif diff --git a/src/imageparticle.cpp b/src/imageparticle.cpp index 35cc21ad..17581a2a 100644 --- a/src/imageparticle.cpp +++ b/src/imageparticle.cpp @@ -31,12 +31,12 @@ ImageParticle::ImageParticle(Map *map, Image *image): Particle(map), mImage(image) { - mImage->incRef(); + if (mImage) mImage->incRef(); } ImageParticle::~ImageParticle() { - mImage->decRef(); + if (mImage) mImage->decRef(); } void ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const @@ -44,8 +44,8 @@ void ImageParticle::draw(Graphics *graphics, int offsetX, int offsetY) const if (!mAlive) return; - int screenX = (int) mPosX + offsetX - mImage->getWidth() / 2; - int screenY = (int) mPosY - (int) mPosZ + offsetY - mImage->getHeight()/2; + int screenX = (int) mPos.x + offsetX - mImage->getWidth() / 2; + int screenY = (int) mPos.y - (int)mPos.z + offsetY - mImage->getHeight()/2; // Check if on screen if (screenX + mImage->getWidth() < 0 || diff --git a/src/itemshortcut.cpp b/src/itemshortcut.cpp new file mode 100644 index 00000000..8a514c7e --- /dev/null +++ b/src/itemshortcut.cpp @@ -0,0 +1,87 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "itemshortcut.h" + +#include "localplayer.h" +#include "configuration.h" + +#include "utils/tostring.h" + +ItemShortcut::ItemShortcut *itemShortcut; + +ItemShortcut::ItemShortcut(): + mItemSelected(NULL) +{ + for (int i = 0; i < SHORTCUT_ITEMS; i++) + { + mItems[i] = NULL; + } +} + +ItemShortcut::~ItemShortcut() +{ + save(); +} + +void ItemShortcut::load() +{ + for (int i = 0; i < SHORTCUT_ITEMS; i++) + { + int itemId = (int) config.getValue("itemShortcut" + toString(i), -1); + + if (itemId != -1) + { + ItemPtr item = player_node->searchForItem(itemId); + if (item) + { + mItems[i] = item; + } + } + } +} + +void ItemShortcut::save() +{ + for (int i = 0; i < SHORTCUT_ITEMS; i++) + { + if (mItems[i]) + { + config.setValue("shortcut" + toString(i), mItems[i]->getId()); + } + else + { + config.setValue("shortcut" + toString(i), -1); + } + } +} + +void ItemShortcut::useItem(int index) +{ + if (mItems[index]) + { + if (mItems[index]->getQuantity()) { + // TODO: Fix this (index vs. pointer mismatch) + //player_node->useItem(mItems[index]); + } + } +} diff --git a/src/itemshortcut.h b/src/itemshortcut.h new file mode 100644 index 00000000..d211c7f3 --- /dev/null +++ b/src/itemshortcut.h @@ -0,0 +1,130 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _TMW_ITEMSHORTCUT_H__ +#define _TMW_ITEMSHORTCUT_H__ + +#include "item.h" + +#define SHORTCUT_ITEMS 10 + +/** + * The item pointer + */ +typedef Item* ItemPtr; + +class ItemShortcut +{ + public: + /** + * Constructor. + */ + ItemShortcut(); + + /** + * Destructor. + */ + ~ItemShortcut(); + + /** + * Load the configuration information. + */ + void load(); + + /** + * Returns the shortcut item specified by the index. + * + * @param index Index of the shortcut item. + */ + ItemPtr getItem(int index) const + { return mItems[index]; } + + /** + * Returns the amount of shortcut items. + */ + int getItemCount() const + { return SHORTCUT_ITEMS; } + + /** + * Returns the item that is currently selected. + */ + ItemPtr getItemSelected() const + { return mItemSelected; } + + /** + * Adds the selected item to the items specified by the index. + * + * @param index Index of the items. + */ + void setItem(int index) + { mItems[index] = mItemSelected; } + + /** + * Adds an item to the items store specified by the index. + * + * @param index Index of the items. + * @param item Item to store. + */ + void setItems(int index, Item *item) + { mItems[index] = item; } + + /** + * Set the item that is selected. + * + * @param item The item that is to be assigned. + */ + void setItemSelected(ItemPtr item) + { mItemSelected = item; } + + /** + * A flag to check if the item is selected. + */ + bool isItemSelected() + { return (mItemSelected) ? true : false; } + + /** + * Remove a item from the shortcut. + */ + void removeItem(int index) + { mItems[index] = NULL; } + + /** + * Try to use the item specified by the index. + * + * @param index Index of the item shortcut. + */ + void useItem(int index); + + private: + /** + * Save the configuration information. + */ + void save(); + + ItemPtr mItems[SHORTCUT_ITEMS]; /**< the items stored */ + ItemPtr mItemSelected; /**< the item held by cursor */ + +}; + +extern ItemShortcut *itemShortcut; + +#endif diff --git a/src/keyboardconfig.cpp b/src/keyboardconfig.cpp new file mode 100644 index 00000000..271961c8 --- /dev/null +++ b/src/keyboardconfig.cpp @@ -0,0 +1,144 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "keyboardconfig.h" +#include "configuration.h" +#include "log.h" + +#include <guichan/sdl/sdlinput.hpp> + +#include "gui/setup_keyboard.h" + +struct KeyData +{ + const char *configField; + int defaultValue; + const char *caption; +}; + +// keyData must be in same order as enum keyAction. +static KeyData const keyData[KeyboardConfig::KEY_TOTAL] = { + {"keyMoveUp", SDLK_UP, "Move Up"}, + {"keyMoveDown", SDLK_DOWN, "Move Down"}, + {"keyMoveLeft", SDLK_LEFT, "Move Left"}, + {"keyMoveRight", SDLK_RIGHT, "Move Right"}, + {"keyAttack", SDLK_LCTRL, "Attack"}, + {"keyTarget", SDLK_LSHIFT, "Target"}, + {"keyTargetClosest", SDLK_a, "Target Closest"}, + {"keyPickup", SDLK_z, "Pickup"}, + {"keyHideWindows", SDLK_h, "Hide Windows"}, + {"keyBeingSit", SDLK_s, "Sit"}, + {"keyShortcut0", SDLK_0, "Item Shortcut 0"}, + {"keyShortcut1", SDLK_1, "Item Shortcut 1"}, + {"keyShortcut2", SDLK_2, "Item Shortcut 2"}, + {"keyShortcut3", SDLK_3, "Item Shortcut 3"}, + {"keyShortcut4", SDLK_4, "Item Shortcut 4"}, + {"keyShortcut5", SDLK_5, "Item Shortcut 5"}, + {"keyShortcut6", SDLK_6, "Item Shortcut 6"}, + {"keyShortcut7", SDLK_7, "Item Shortcut 7"}, + {"keyShortcut8", SDLK_8, "Item Shortcut 8"}, + {"keyShortcut9", SDLK_9, "Item Shortcut 9"} +}; + +void KeyboardConfig::init() +{ + for (int i = 0; i < KEY_TOTAL; i++) + { + mKey[i].configField = keyData[i].configField; + mKey[i].defaultValue = keyData[i].defaultValue; + mKey[i].caption = keyData[i].caption; + mKey[i].value = KEY_NO_VALUE; + } + mNewKeyIndex = KEY_NO_VALUE; + mEnabled = true; + + retrieve(); +} + +void KeyboardConfig::retrieve() +{ + for (int i = 0; i < KEY_TOTAL; i++) + { + mKey[i].value = (int) config.getValue( + mKey[i].configField, mKey[i].defaultValue); + } +} + +void KeyboardConfig::store() +{ + for (int i = 0; i < KEY_TOTAL; i++) + { + config.setValue(mKey[i].configField, mKey[i].value); + } +} + +void KeyboardConfig::makeDefault() +{ + for (int i = 0; i < KEY_TOTAL; i++) + { + mKey[i].value = mKey[i].defaultValue; + } +} + +bool KeyboardConfig::hasConflicts() +{ + int i, j; + for (i = 0; i < KEY_TOTAL; i++) + { + for (j = 0; j < KEY_TOTAL; j++) + { + if (i != j && mKey[i].value == mKey[j].value) + { + return true; + } + } + } + return false; +} + +void KeyboardConfig::callbackNewKey() +{ + mSetupKey->newKeyCallback(mNewKeyIndex); +} + +int KeyboardConfig::getKeyIndex(int keyValue) const +{ + for (int i = 0; i < KEY_TOTAL; i++) + { + if(keyValue == mKey[i].value) + { + return i; + } + } + return KEY_NO_VALUE; +} + +bool KeyboardConfig::isKeyActive(int index) +{ + return mActiveKeys[ mKey[index].value]; +} + +void KeyboardConfig::refreshActiveKeys() +{ + mActiveKeys = SDL_GetKeyState(NULL); +} diff --git a/src/keyboardconfig.h b/src/keyboardconfig.h new file mode 100644 index 00000000..46394fa5 --- /dev/null +++ b/src/keyboardconfig.h @@ -0,0 +1,185 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_KEYBOARDCONFIG_H +#define _TMW_KEYBOARDCONFIG_H + +#include <string> + +#include "gui/setup_keyboard.h" + +#include <guichan/sdl/sdlinput.hpp> + +/** + * Each key represents a key function. Such as 'Move up', 'Attack' etc. + */ +struct KeyFunction +{ + const char* configField; /** Field index that is in the config file. */ + int defaultValue; /** The default key value used. */ + std::string caption; /** The caption value for the key function. */ + int value; /** The actual value that is used. */ +}; + +class KeyboardConfig +{ + public: + /** + * Initializes the keyboard config explicitly. + */ + void init(); + + /** + * Retrieve the key values from config file. + */ + void retrieve(); + + /** + * Store the key values to config file. + */ + void store(); + + /** + * Make the keys their default values. + */ + void makeDefault(); + + /** + * Determines if any key assignments are the same as each other. + */ + bool hasConflicts(); + + /** + * Calls a function back so the key re-assignment(s) can be seen. + */ + void callbackNewKey(); + + /** + * Obtain the value stored in memory. + */ + int getKeyValue(int index) const + { return mKey[index].value; } + + /** + * Get the index of the new key to be assigned. + */ + int getNewKeyIndex() const + { return mNewKeyIndex; } + + /** + * Get the enable flag, which will stop the user from doing actions. + */ + bool isEnabled() const + { return mEnabled; } + + /** + * Get the key caption, providing more meaning to the user. + */ + const std::string &getKeyCaption(int index) const + { return mKey[index].caption; } + + /** + * Get the key function index by providing the keys value. + */ + int getKeyIndex(int keyValue) const; + + /** + * Set the enable flag, which will stop the user from doing actions. + */ + void setEnabled(bool flag) + { mEnabled = flag; } + + /** + * Set the index of the new key to be assigned. + */ + void setNewKeyIndex(int value) + { mNewKeyIndex = value; } + + /** + * Set the value of the new key. + */ + void setNewKey(int value) + { mKey[mNewKeyIndex].value = value; } + + /** + * Set a reference to the key setup window. + */ + void setSetupKeyboard(Setup_Keyboard *setupKey) + { mSetupKey = setupKey; } + + /** + * Checks if the key is active, by providing the key function index. + */ + bool isKeyActive(int index); + + /** + * Takes a snapshot of all the active keys. + */ + void refreshActiveKeys(); + + /** + * All the key functions. + * KEY_NO_VALUE is used in initialization, and should be unchanged. + * KEY_TOTAL should always be last (used as a conditional in loops). + * The key assignment view gets arranged according to the order of + * these values. + */ + enum KeyAction { + KEY_NO_VALUE = -1, + KEY_MOVE_UP, + KEY_MOVE_DOWN, + KEY_MOVE_LEFT, + KEY_MOVE_RIGHT, + KEY_ATTACK, + KEY_TARGET, + KEY_TARGET_CLOSEST, + KEY_PICKUP, + KEY_HIDE_WINDOWS, + KEY_SIT, + KEY_SHORTCUT_0, + KEY_SHORTCUT_1, + KEY_SHORTCUT_2, + KEY_SHORTCUT_3, + KEY_SHORTCUT_4, + KEY_SHORTCUT_5, + KEY_SHORTCUT_6, + KEY_SHORTCUT_7, + KEY_SHORTCUT_8, + KEY_SHORTCUT_9, + KEY_TOTAL + }; + + private: + int mNewKeyIndex; /**< Index of new key to be assigned */ + bool mEnabled; /**< Flag to respond to key input */ + + Setup_Keyboard *mSetupKey; /**< Reference to setup window */ + + KeyFunction mKey[KEY_TOTAL]; /**< Pointer to all the key data */ + + Uint8 *mActiveKeys; /**< Stores a list of all the keys */ +}; + +extern KeyboardConfig keyboard; + +#endif diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 95e7a478..f4ad7587 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -29,11 +29,19 @@ #include "inventory.h" #include "item.h" #include "main.h" +#include "particle.h" #include "sound.h" #include "log.h" #include "net/gameserver/player.h" +#include "gui/gui.h" + +#include "net/messageout.h" +#include "net/protocol.h" + +#include "utils/tostring.h" + LocalPlayer *player_node = NULL; LocalPlayer::LocalPlayer(): @@ -46,6 +54,7 @@ LocalPlayer::LocalPlayer(): mLevel(1), mMoney(0), mTotalWeight(1), mMaxWeight(1), mHP(1), mMaxHP(1), + mXp(0), mTarget(NULL), mPickUpTarget(NULL), mTrading(false), mLastAction(-1), mWalkingDir(0), @@ -112,6 +121,17 @@ LocalPlayer::moveInvItem(Item *item, int newIndex) item->getInvIndex(), newIndex, item->getQuantity()); } +Item* LocalPlayer::searchForItem(int itemId) +{ + for (int i = 0; i < INVENTORY_SIZE; i++) + { + if (itemId == mInventory->getItem(i)->getId()) { + return mInventory->getItem(i); + } + } + return NULL; +} + void LocalPlayer::equipItem(Item *item) { Net::GameServer::Player::equip(item->getInvIndex()); @@ -302,10 +322,14 @@ void LocalPlayer::attack() setAction(ATTACK); - if (getWeapon() == 2) - sound.playSfx("sfx/bow_shoot_1.ogg"); - else + if (mEquippedWeapon) + { + std::string soundFile = mEquippedWeapon->getSound(EQUIP_EVENT_STRIKE); + if (soundFile != "") sound.playSfx(soundFile); + } + else { sound.playSfx("sfx/fist-swish.ogg"); + } Net::GameServer::Player::attack(getSpriteDirection()); } @@ -329,3 +353,16 @@ void LocalPlayer::raiseAttribute(size_t attr) mAttributeBase.at(attr)++; // TODO: Inform the server about our desire to raise the attribute } + +void LocalPlayer::setXp(int xp) +{ + if (mMap && xp > mXp) + { + const std::string text = toString(xp - mXp) + " xp"; + + // Show XP number + particleEngine->addTextRiseFadeOutEffect(text, hitYellowFont, + mPx + 16, mPy - 16); + } + mXp = xp; +} diff --git a/src/localplayer.h b/src/localplayer.h index 4f38fdad..ff7bf0b7 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -152,6 +152,14 @@ class LocalPlayer : public Player void moveInvItem(Item *item, int newIndex); /** + * Searches for the specified item by it's identification. + * + * @param itemId The id of the item to be searched. + * @return Item found on success, NULL on failure. + */ + Item* searchForItem(int itemId); + + /** * Equips an item. */ void equipItem(Item *item); @@ -237,6 +245,19 @@ class LocalPlayer : public Player int getHP() const { return mHP; } + /** + * Sets the amount of XP. Shows XP gaining effect if the player is on + * a map. + */ + void setXp(int xp); + + /** + * Returns the amount of experience points. + */ + int getXp() const { return mXp; } + + Uint32 mCharId; + int getMaxHP() const { return mMaxHP; } @@ -298,6 +319,7 @@ class LocalPlayer : public Player int mMaxWeight; int mHP; int mMaxHP; + int mXp; /**< Experience points. */ Being *mTarget; FloorItem *mPickUpTarget; diff --git a/src/main.cpp b/src/main.cpp index 1f4eafde..30fb7c1c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -48,8 +48,10 @@ #endif #include "configuration.h" +#include "keyboardconfig.h" #include "game.h" #include "graphics.h" +#include "itemshortcut.h" #include "lockedarray.h" #include "localplayer.h" #include "log.h" @@ -84,7 +86,6 @@ #include "net/gameserver/gameserver.h" -#include "resources/equipmentdb.h" #include "resources/image.h" #include "resources/itemdb.h" #include "resources/monsterdb.h" @@ -108,6 +109,7 @@ Music *bgm; Configuration config; /**< XML file configuration reader */ Logger *logger; /**< Log object */ +KeyboardConfig keyboard; Net::Connection *accountServerConnection = 0; Net::Connection *gameServerConnection = 0; @@ -189,7 +191,6 @@ void initConfiguration(const Options &options) config.setValue("fpslimit", 0); config.setValue("updatehost", "http://updates.themanaworld.org"); config.setValue("customcursor", 1); - config.setValue("homeDir", homeDir); // Checking if the configuration file exists... otherwise create it with // default options. @@ -319,6 +320,9 @@ void initEngine() // Initialize for drawing graphics->_beginDraw(); + // Initialize the item shortcuts. + itemShortcut = new ItemShortcut(); + gui = new Gui(graphics); state = STATE_CHOOSE_SERVER; /**< Initial game state */ @@ -337,12 +341,19 @@ void initEngine() errorMessage = err; logger->log("Warning: %s", err); } + + // Initialize keyboard + keyboard.init(); } /** Clear the engine */ void exit_engine() { + // Before config.write() since it writes the shortcuts to the config + delete itemShortcut; + config.write(); + delete gui; delete graphics; @@ -353,7 +364,6 @@ void exit_engine() sound.close(); // Unload XML databases - EquipmentDB::unload(); ItemDB::unload(); MonsterDB::unload(); @@ -457,7 +467,6 @@ void loadUpdates() const std::string updatesFile = "updates/resources2.txt"; ResourceManager *resman = ResourceManager::getInstance(); std::vector<std::string> lines = resman->loadTextFile(updatesFile); - std::string homeDir = config.getValue("homeDir", ""); for (unsigned int i = 0; i < lines.size(); ++i) { @@ -895,7 +904,6 @@ int main(int argc, char *argv[]) false); // Load XML databases - EquipmentDB::load(); ItemDB::load(); MonsterDB::load(); state = STATE_LOGIN; @@ -1039,7 +1047,6 @@ int main(int argc, char *argv[]) } } } - } catch (...) { diff --git a/src/monster.cpp b/src/monster.cpp index 768bf16a..2522a3e1 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -57,7 +57,7 @@ Monster::setAction(Action action) break; case DEAD: currentAction = ACTION_DEAD; - sound.playSfx(getInfo().getSound(EVENT_DIE)); + sound.playSfx(getInfo().getSound(MONSTER_EVENT_DIE)); break; case ATTACK: currentAction = ACTION_ATTACK; @@ -87,10 +87,10 @@ Monster::handleAttack() const MonsterInfo &mi = getInfo(); - // TODO: It's not possible to determine hit or miss here, so this stuff probably needs - // to be moved somewhere else. We may lose synchronization between attack animation and - // the sound, unless we adapt the protocol... - sound.playSfx(mi.getSound(EVENT_HIT)); + // TODO: It's not possible to determine hit or miss here, so this stuff + // probably needs to be moved somewhere else. We may lose synchronization + // between attack animation and the sound, unless we adapt the protocol... + sound.playSfx(mi.getSound(MONSTER_EVENT_HIT)); } Being::TargetCursorSize diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp index 87972212..b88d443e 100644 --- a/src/net/beinghandler.cpp +++ b/src/net/beinghandler.cpp @@ -114,7 +114,7 @@ void BeingHandler::handleMessage(MessageIn &msg) dstBeing = beingManager->findBeing(id); - if (dstBeing == NULL) + if (!dstBeing) { // Being with id >= 110000000 and job 0 are better // known as ghosts, so don't create those. @@ -138,30 +138,32 @@ void BeingHandler::handleMessage(MessageIn &msg) dstBeing->setWalkSpeed(speed); dstBeing->mJob = job; - dstBeing->setHairStyle(msg.readShort()); - dstBeing->setWeapon(msg.readShort()); + dstBeing->setHairStyle(msg->readShort()); + dstBeing->setVisibleEquipment( + Being::WEAPON_SPRITE, msg->readShort()); dstBeing->setVisibleEquipment( - Being::BOTTOMCLOTHES_SPRITE, msg.readShort()); + Being::BOTTOMCLOTHES_SPRITE, msg->readShort()); if (msg.getId() == SMSG_BEING_MOVE) { msg.readLong(); // server tick } - msg.readShort(); // shield - dstBeing->setVisibleEquipment(Being::HAIT_SPRITE, msg.readShort()); - dstBeing->setVisibleEquipment( - Being::TOPCLOTHES_SPRITE, msg.readShort()); - dstBeing->setHairColor(msg.readShort()); - msg.readShort(); // unknown - msg.readShort(); // head dir - msg.readShort(); // guild - msg.readShort(); // unknown - msg.readShort(); // unknown - msg.readShort(); // manner - msg.readShort(); // karma - msg.readByte(); // unknown - dstBeing->setSex(1 - msg.readByte()); // sex + msg->readShort(); // shield + headTop = msg->readShort(); + headMid = msg->readShort(); + dstBeing->setVisibleEquipment(Being::HAT_SPRITE, headTop); + dstBeing->setVisibleEquipment(Being::TOPCLOTHES_SPRITE, headMid); + dstBeing->setHairColor(msg->readShort()); + msg->readShort(); // unknown + msg->readShort(); // head dir + msg->readShort(); // guild + msg->readShort(); // unknown + msg->readShort(); // unknown + msg->readShort(); // manner + msg->readShort(); // karma + msg->readByte(); // unknown + dstBeing->setSex(1 - msg->readByte()); // sex if (msg.getId() == SMSG_BEING_MOVE) { @@ -256,10 +258,12 @@ void BeingHandler::handleMessage(MessageIn &msg) } Particle *levelupFX; if (msg->readLong() == 0) { // type - levelupFX = particleEngine->addEffect("graphics/particles/levelup.particle.xml", 0, 0); + levelupFX = particleEngine->addEffect( + "graphics/particles/levelup.particle.xml", 0, 0); } else { - levelupFX = particleEngine->addEffect("graphics/particles/skillup.particle.xml", 0, 0); + levelupFX = particleEngine->addEffect( + "graphics/particles/skillup.particle.xml", 0, 0); } beingManager->findBeing(id)->controlParticle(levelupFX); break; @@ -289,28 +293,26 @@ void BeingHandler::handleMessage(MessageIn &msg) dstBeing->setHairStyle(id); break; case 2: - dstBeing->setWeapon(id); + dstBeing->setVisibleEquipment(Being::WEAPON_SPRITE, id); break; case 3: // Change lower headgear for eAthena, pants for us dstBeing->setVisibleEquipment( - Being::BOTTOMCLOTHES_SPRITE, - id); + Being::BOTTOMCLOTHES_SPRITE, id); break; case 4: // Change upper headgear for eAthena, hat for us dstBeing->setVisibleEquipment( - Being::HAT_SPRITE, - id); + Being::HAT_SPRITE, id); break; case 5: // Change middle headgear for eathena, armor for us dstBeing->setVisibleEquipment( - Being::TOPCLOTHES_SPRITE, - id); + Being::TOPCLOTHES_SPRITE, id); break; case 6: dstBeing->setHairColor(id); break; default: - logger->log("c3: %i\n", id); // unsupported + logger->log("SMSG_BEING_CHANGE_LOOKS: unsupported type: " + "%d, id: %d", type, id); break; } } @@ -336,17 +338,18 @@ void BeingHandler::handleMessage(MessageIn &msg) dstBeing = beingManager->findBeing(id); - if (dstBeing == NULL) + if (!dstBeing) { dstBeing = beingManager->createBeing(id, job); } dstBeing->setWalkSpeed(speed); dstBeing->mJob = job; - dstBeing->setHairStyle(msg.readShort()); - dstBeing->setWeaponById(msg.readShort()); // item id 1 - msg.readShort(); // item id 2 - headBottom = msg.readShort(); + dstBeing->setHairStyle(msg->readShort()); + dstBeing->setVisibleEquipment( + Being::WEAPON_SPRITE, msg->readShort()); + msg->readShort(); // item id 2 + headBottom = msg->readShort(); if (msg.getId() == SMSG_PLAYER_MOVE) { @@ -407,9 +410,9 @@ void BeingHandler::handleMessage(MessageIn &msg) case 0x0119: // Change in players look - logger->log("0x0119 %li %i %i %x %i\n", msg.readLong(), - msg.readShort(), msg.readShort(), msg.readShort(), - msg.readByte()); + logger->log("0x0119 %i %i %i %x %i", msg->readLong(), + msg->readShort(), msg->readShort(), msg->readShort(), + msg->readByte()); break; */ } @@ -428,11 +431,9 @@ static void handleLooks(Being *being, MessageIn &msg) if (mask & (1 << 7)) { // The equipment has to be cleared first. - being->setWeaponById(0); for (int i = 0; i < nb_slots; ++i) { - if (slots[i] != Being::WEAPON_SPRITE) - being->setVisibleEquipment(slots[i], 0); + being->setVisibleEquipment(slots[i], 0); } } @@ -441,10 +442,7 @@ static void handleLooks(Being *being, MessageIn &msg) { if (!(mask & (1 << i))) continue; int id = msg.readShort(); - if (slots[i] != Being::WEAPON_SPRITE) - being->setVisibleEquipment(slots[i], id); - else - being->setWeaponById(id); + being->setVisibleEquipment(slots[i], id); } } diff --git a/src/net/chathandler.cpp b/src/net/chathandler.cpp index 3efe3174..afdc2ab5 100644 --- a/src/net/chathandler.cpp +++ b/src/net/chathandler.cpp @@ -37,6 +37,7 @@ #include "../gui/chat.h" #include "../utils/tostring.h" +#include "../utils/trim.h" extern Being *player_node; @@ -167,6 +168,7 @@ void ChatHandler::handleMessage(MessageIn &msg) chatMsg = msg.readString(chatMsgLength); chatWindow->chatLog(chatMsg, BY_OTHER); chatMsg.erase(0, chatMsg.find(" : ", 0) + 3); + trim(chatMsg); being->setSpeech(chatMsg, SPEECH_TIME); break; diff --git a/src/net/inventoryhandler.cpp b/src/net/inventoryhandler.cpp index 354dd685..de74e8f5 100644 --- a/src/net/inventoryhandler.cpp +++ b/src/net/inventoryhandler.cpp @@ -31,6 +31,7 @@ #include "../equipment.h" #include "../inventory.h" #include "../item.h" +#include "../itemshortcut.h" #include "../localplayer.h" #include "../gui/chat.h" @@ -77,6 +78,7 @@ void InventoryHandler::handleMessage(MessageIn &msg) it->setQuantity(amount); } }; + itemShortcut->load(); break; } } diff --git a/src/net/playerhandler.cpp b/src/net/playerhandler.cpp index b94b5128..afaeca69 100644 --- a/src/net/playerhandler.cpp +++ b/src/net/playerhandler.cpp @@ -133,7 +133,8 @@ void PlayerHandler::handleMessage(MessageIn &msg) } else { - logger->log("Warning: server wants to update unknown attribute %d to %d", stat, value); + logger->log("Warning: server wants to update unknown " + "attribute %d to %d", stat, value); } } } break; diff --git a/src/particle.cpp b/src/particle.cpp index 805da102..148bbf6f 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -52,14 +52,12 @@ const float Particle::PARTICLE_SKY = 800.0f; Particle::Particle(Map *map): mAlive(true), - mPosX(0.0f), mPosY(0.0f), mPosZ(0.0f), mLifetimeLeft(-1), mLifetimePast(0), mFadeOut(0), mFadeIn(0), mAutoDelete(true), mMap(map), - mVectorX(0.0f), mVectorY(0.0f), mVectorZ(0.0f), mGravity(0.0f), mRandomnes(0), mBounce(0.0f), @@ -109,7 +107,7 @@ Particle::update() p++ ) { - (*p)->moveBy(mPosX, mPosY, mPosZ); + (*p)->moveBy(mPos.x, mPos.y, mPos.z); mChildParticles.push_back (*p); } } @@ -117,31 +115,28 @@ Particle::update() if (mMomentum != 1.0f) { - mVectorX *= mMomentum; - mVectorY *= mMomentum; - mVectorZ *= mMomentum; + mVelocity *= mMomentum; } if (mTarget && mAcceleration != 0.0f) { - float distX = (mPosX - mTarget->getPosX()) * SIN45; - float distY = mPosY - mTarget->getPosY(); - float distZ = mPosZ - mTarget->getPosZ(); + Vector dist = mPos - mTarget->getPosition(); + dist.x *= SIN45; float invHypotenuse; - switch(Particle::fastPhysics) + switch (Particle::fastPhysics) { case 1: invHypotenuse = fastInvSqrt( - distX * distX + distY * distY + distZ * distZ); + dist.x * dist.x + dist.y * dist.y + dist.z * dist.z); break; case 2: invHypotenuse = 2.0f / - fabs(distX) + fabs(distY) + fabs(distZ); + fabs(dist.x) + fabs(dist.y) + fabs(dist.z); break; default: invHypotenuse = 1.0f / sqrt( - distX * distX + distY * distY + distZ * distZ); + dist.x * dist.x + dist.y * dist.y + dist.z * dist.z); break; } @@ -152,25 +147,23 @@ Particle::update() mAlive = false; } float accFactor = invHypotenuse * mAcceleration; - mVectorX -= distX * accFactor; - mVectorY -= distY * accFactor; - mVectorZ -= distZ * accFactor; + mVelocity -= dist * accFactor; } } if (mRandomnes > 0) { - mVectorX += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f; - mVectorY += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f; - mVectorZ += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f; + mVelocity.x += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f; + mVelocity.y += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f; + mVelocity.z += (rand()%mRandomnes - rand()%mRandomnes) / 1000.0f; } - mVectorZ -= mGravity; + mVelocity.z -= mGravity; // Update position - mPosX += mVectorX; - mPosY += mVectorY * SIN45; - mPosZ += mVectorZ * SIN45; + mPos.x += mVelocity.x; + mPos.y += mVelocity.y * SIN45; + mPos.z += mVelocity.z * SIN45; // Update other stuff if (mLifetimeLeft > 0) @@ -179,14 +172,13 @@ Particle::update() } mLifetimePast++; - if (mPosZ > PARTICLE_SKY || mPosZ < 0.0f) + if (mPos.z > PARTICLE_SKY || mPos.z < 0.0f) { if (mBounce > 0.0f) { - mPosZ *= -mBounce; - mVectorX *= mBounce; - mVectorY *= mBounce; - mVectorZ *= -mBounce; + mPos.z *= -mBounce; + mVelocity *= mBounce; + mVelocity.z = -mVelocity.z; } else { mAlive = false; @@ -195,10 +187,8 @@ Particle::update() } // Update child particles - for ( ParticleIterator p = mChildParticles.begin(); - p != mChildParticles.end(); - - ) + for (ParticleIterator p = mChildParticles.begin(); + p != mChildParticles.end();) { if ((*p)->update()) { @@ -217,14 +207,9 @@ Particle::update() return true; } - -void Particle::draw(Graphics *graphics, int offsetX, int offsetY) const -{ -} - - Particle* -Particle::addEffect(std::string particleEffectFile, int pixelX, int pixelY) +Particle::addEffect(const std::string &particleEffectFile, + int pixelX, int pixelY) { Particle *newParticle = NULL; @@ -289,9 +274,9 @@ Particle::addEffect(std::string particleEffectFile, int pixelX, int pixelY) int offsetY = XML::getProperty(effectChildNode, "position-y", 0); int offsetZ = XML::getProperty(effectChildNode, "position-z", 0); - int particleX = (int)mPosX + pixelX + offsetX; - int particleY = (int)mPosY + pixelY + offsetY; - int particleZ = (int)mPosZ + offsetZ; + int particleX = (int) mPos.x + pixelX + offsetX; + int particleY = (int) mPos.y + pixelY + offsetY; + int particleZ = (int) mPos.z + offsetZ; int lifetime = XML::getProperty(effectChildNode, "lifetime", -1); @@ -317,17 +302,16 @@ Particle::addEffect(std::string particleEffectFile, int pixelX, int pixelY) Particle* -Particle::addTextSplashEffect(std::string text, +Particle::addTextSplashEffect(const std::string &text, int colorR, int colorG, int colorB, gcn::Font *font, int x, int y) { Particle *newParticle = new TextParticle(mMap, text, colorR, colorG, colorB, font); newParticle->setPosition(x, y, 0); - newParticle->setVector ( ((rand()%100) - 50) / 200.0f, // X vector - ((rand()%100) - 50) / 200.0f, // Y vector - ((rand()%100) / 200.0f) + 4.0f // Z vector - ); + newParticle->setVelocity(((rand() % 100) - 50) / 200.0f, // X + ((rand() % 100) - 50) / 200.0f, // Y + ((rand() % 100) / 200.0f) + 4.0f); // Z newParticle->setGravity(0.1f); newParticle->setBounce(0.5f); newParticle->setLifetime(200); @@ -338,14 +322,28 @@ Particle::addTextSplashEffect(std::string text, return newParticle; } +Particle* +Particle::addTextRiseFadeOutEffect(const std::string &text, gcn::Font *font, + int x, int y) +{ + Particle *newParticle = new TextParticle(mMap, text, 255, 255, 255, font); + newParticle->setPosition(x, y, 0); + newParticle->setVelocity(0.0f, 0.0f, 0.5f); + newParticle->setGravity(0.0015f); + newParticle->setLifetime(300); + newParticle->setFadeOut(50); + newParticle->setFadeIn(200); + + mChildParticles.push_back(newParticle); + + return newParticle; +} void Particle::setMap(Map *map) { mMap = map; if (mMap) setSpriteIterator(mMap->addSprite(this)); - - // TODO: Create map emitters based on emitter data in map data } diff --git a/src/particle.h b/src/particle.h index 9e9223c7..dd7c5ee2 100644 --- a/src/particle.h +++ b/src/particle.h @@ -31,7 +31,7 @@ #include "guichanfwd.h" #include "sprite.h" - +#include "vector.h" class Map; class Particle; @@ -67,43 +67,37 @@ class Particle : public Sprite ~Particle(); /** - * Deletes all child particles and emitters + * Deletes all child particles and emitters. */ void clear(); /** * Gives a particle the properties of an engine root particle and loads - * the particle-related config settings + * the particle-related config settings. */ void setupEngine(); /** * Updates particle position, returns false when the particle should - * be deleted + * be deleted. */ virtual bool update(); /** - * Draws the particle image + * Draws the particle image. */ virtual void - draw(Graphics *graphics, int offsetX, int offsetY) const; + draw(Graphics *graphics, int offsetX, int offsetY) const {} /** - * Necessary for sorting with the other sprites + * Necessary for sorting with the other sprites. */ virtual int getPixelY() const - { - return (int)(mPosY + mPosZ) - 64; - }; - - /* - Basic Particle properties: - */ + { return (int) (mPos.y + mPos.z) - 64; } /** * Sets the map the particle is on. @@ -115,16 +109,25 @@ class Particle : public Sprite * particleEffectFile. */ Particle* - addEffect(std::string particleEffectFile, int pixelX, int pixelY); + addEffect(const std::string &particleEffectFile, + int pixelX, int pixelY); /** * Creates a standalone text particle. */ Particle* - addTextSplashEffect(std::string text, int colorR, int colorG, int colorB, + addTextSplashEffect(const std::string &text, + int colorR, int colorG, int colorB, gcn::Font *font, int x, int y); /** + * Creates a standalone text particle. + */ + Particle* + addTextRiseFadeOutEffect(const std::string &text, gcn::Font *font, + int x, int y); + + /** * Adds an emitter to the particle. */ void @@ -132,34 +135,31 @@ class Particle : public Sprite { mChildEmitters.push_back(emitter); } /** - * Sets the position in 3 dimensional space in pixels relative to map + * Sets the position in 3 dimensional space in pixels relative to map. */ void setPosition(float x, float y, float z) - { mPosX = x; mPosY = y; mPosZ = z; } + { mPos.x = x; mPos.y = y; mPos.z = z; } /** - * Sets the position in 2 dimensional space in pixels relative to map + * Sets the position in 2 dimensional space in pixels relative to map. */ void setPosition(float x, float y) - { mPosX = x; mPosY = y; } - - float getPosX() const - { return mPosX; } + { mPos.x = x; mPos.y = y; } - float getPosY() const - { return mPosY; } - - float getPosZ() const - { return mPosZ; } + /** + * Returns the particle position. + */ + const Vector& getPosition() const + { return mPos; } /** * Changes the particle position relative */ void moveBy(float x, float y, float z) - { mPosX += x; mPosY += y; mPosZ += z; } + { mPos.x += x; mPos.y += y; mPos.z += z; } /** * Sets the time in game ticks until the particle is destroyed. @@ -170,48 +170,48 @@ class Particle : public Sprite /** * Sets the age of the pixel in game ticks where the particle has - * faded in completely + * faded in completely. */ void - setFadeOut (int fadeOut) + setFadeOut(int fadeOut) { mFadeOut = fadeOut; } /** * Sets the remaining particle lifetime where the particle starts to - * fade out + * fade out. */ void - setFadeIn (int fadeIn) + setFadeIn(int fadeIn) { mFadeIn = fadeIn; } /** * Sets the sprite iterator of the particle on the current map to make - * it easier to remove the particle from the map when it is destroyed + * it easier to remove the particle from the map when it is destroyed. */ void setSpriteIterator(std::list<Sprite*>::iterator spriteIterator) { mSpriteIterator = spriteIterator; } /** - * Gets the sprite iterator of the particle on the current map + * Gets the sprite iterator of the particle on the current map. */ std::list<Sprite*>::iterator getSpriteIterator() const { return mSpriteIterator; } /** - * Sets the current velocity in 3 dimensional space + * Sets the current velocity in 3 dimensional space. */ void - setVector(float x, float y, float z) - { mVectorX = x; mVectorY = y; mVectorZ = z; } + setVelocity(float x, float y, float z) + { mVelocity.x = x; mVelocity.y = y; mVelocity.z = z; } /** - * Sets the downward acceleration + * Sets the downward acceleration. */ void - setGravity(float g) - { mGravity = g; } + setGravity(float gravity) + { mGravity = gravity; } /** * Sets the ammount of random vector changes @@ -237,8 +237,8 @@ class Particle : public Sprite /** * Sets the distance in pixel the particle can come near the target - * particle before it is destroyed. Does only make sense after a - * target particle has been set using setDestination. + * particle before it is destroyed. Does only make sense after a target + * particle has been set using setDestination. */ void setDieDistance(float dist) { mInvDieDistance = 1.0f / dist; } @@ -247,7 +247,7 @@ class Particle : public Sprite { return mAlive; } /** - * Manually marks the particle for deletion + * Manually marks the particle for deletion. */ void kill() { mAlive = false; mAutoDelete = true; } @@ -261,7 +261,7 @@ class Particle : public Sprite protected: bool mAlive; /**< Is the particle supposed to be drawn and updated?*/ - float mPosX, mPosY, mPosZ; /**< Position in 3 dimensonal space - pixel based relative to map */ + Vector mPos; /**< Position in pixels relative to map. */ int mLifetimeLeft; /**< Lifetime left in game ticks*/ int mLifetimePast; /**< Age of the particle in game ticks*/ int mFadeOut; /**< Lifetime in game ticks left where fading out begins*/ @@ -269,17 +269,19 @@ class Particle : public Sprite private: // generic properties - bool mAutoDelete; /**< May the particle request its deletion by the parent particle?*/ - Map *mMap; /**< Map the particle is on*/ + bool mAutoDelete; /**< May the particle request its deletion by the parent particle? */ + Map *mMap; /**< Map the particle is on. */ std::list<Sprite*>::iterator mSpriteIterator; /**< iterator of the particle on the current map */ - Emitters mChildEmitters; /**< List of child emitters*/ - Particles mChildParticles; /**< List of particles controlled by this particle*/ - //dynamic particle - float mVectorX, mVectorY, mVectorZ; /**< Speed in 3 dimensional space in pixels per game-tick */ - float mGravity; /**< Downward acceleration in pixels per game-tick²*/ - int mRandomnes; /**< Ammount of random vector change*/ - float mBounce; /**< How much the particle bounces off when hitting the ground*/ - //follow-point particles + Emitters mChildEmitters; /**< List of child emitters. */ + Particles mChildParticles; /**< List of particles controlled by this particle */ + + // dynamic particle + Vector mVelocity; /**< Speed in pixels per game-tick. */ + float mGravity; /**< Downward acceleration in pixels per game-tick. */ + int mRandomnes; /**< Ammount of random vector change */ + float mBounce; /**< How much the particle bounces off when hitting the ground */ + + // follow-point particles Particle *mTarget; /**< The particle that attracts this particle*/ float mAcceleration; /**< Acceleration towards the target particle in pixels per game-tick²*/ float mInvDieDistance; /**< Distance in pixels from the target particle that causes the destruction of the particle*/ diff --git a/src/particleemitter.cpp b/src/particleemitter.cpp index 60a98cc5..035882b6 100644 --- a/src/particleemitter.cpp +++ b/src/particleemitter.cpp @@ -248,10 +248,9 @@ ParticleEmitter::readMinMax(xmlNodePtr propertyNode, T def) { MinMax<T> retval; - def = (T)XML::getFloatProperty(propertyNode, "value", (double)def); - retval.set ( (T)XML::getFloatProperty(propertyNode, "min", (double)def), - (T)XML::getFloatProperty(propertyNode, "max", (double)def) - ); + def = (T) XML::getFloatProperty(propertyNode, "value", (double) def); + retval.set((T) XML::getFloatProperty(propertyNode, "min", (double) def), + (T) XML::getFloatProperty(propertyNode, "max", (double) def)); return retval; } @@ -284,19 +283,17 @@ ParticleEmitter::createParticles() newParticle->setPosition( - mParticlePosX.value(), - mParticlePosY.value(), - mParticlePosZ.value() - ); + mParticlePosX.value(), + mParticlePosY.value(), + mParticlePosZ.value()); float angleH = mParticleAngleHorizontal.value(); float angleV = mParticleAngleVertical.value(); float power = mParticlePower.value(); - newParticle->setVector( - cos(angleH) * cos(angleV) * power, - sin(angleH) * cos(angleV) * power, - sin(angleV) * power - ); + newParticle->setVelocity( + cos(angleH) * cos(angleV) * power, + sin(angleH) * cos(angleV) * power, + sin(angleV) * power); newParticle->setRandomnes(mParticleRandomnes.value()); newParticle->setGravity(mParticleGravity.value()); @@ -312,10 +309,9 @@ ParticleEmitter::createParticles() newParticle->setFadeOut(mParticleFadeOut.value()); newParticle->setFadeIn(mParticleFadeIn.value()); - for ( std::list<ParticleEmitter>::iterator i = mParticleChildEmitters.begin(); - i != mParticleChildEmitters.end(); - i++ - ) + for (std::list<ParticleEmitter>::iterator i = mParticleChildEmitters.begin(); + i != mParticleChildEmitters.end(); + i++) { newParticle->addEmitter(new ParticleEmitter(*i)); } diff --git a/src/player.cpp b/src/player.cpp index d3c27aa0..b63dcd5b 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -28,7 +28,8 @@ #include "graphics.h" #include "log.h" -#include "resources/equipmentdb.h" +#include "resources/itemdb.h" +#include "resources/iteminfo.h" #include "utils/tostring.h" @@ -37,7 +38,6 @@ Player::Player(Uint16 id, Uint16 job, Map *map): Being(id, job, map) { - setWeapon(0); } Being::Type @@ -93,8 +93,8 @@ Player::setSex(Uint8 sex) if (i != HAIR_SPRITE && mEquipmentSpriteIDs.at(i) != 0) { AnimatedSprite *newEqSprite = new AnimatedSprite( - "graphics/sprites/" + EquipmentDB::get( - mEquipmentSpriteIDs.at(i))->getSprite(sex)); + "graphics/sprites/" + ItemDB::get( + mEquipmentSpriteIDs.at(i)).getSprite(sex)); delete mSprites[i]; mSprites[i] = newEqSprite; } @@ -103,39 +103,6 @@ Player::setSex(Uint8 sex) } void -Player::setWeapon(Uint16 weapon) -{ - if (weapon != mWeapon) - { - AnimatedSprite *newWeaponSprite = NULL; - - switch (weapon) - { - case 0: - newWeaponSprite = - new AnimatedSprite("graphics/sprites/weapon-fist.xml"); - break; - case 1: - newWeaponSprite = - new AnimatedSprite("graphics/sprites/weapon-dagger.xml"); - break; - case 2: - newWeaponSprite = - new AnimatedSprite("graphics/sprites/weapon-bow.xml"); - break; - case 3: - newWeaponSprite = - new AnimatedSprite("graphics/sprites/weapon-scythe.xml"); - break; - } - - delete mSprites[WEAPON_SPRITE]; - mSprites[WEAPON_SPRITE] = newWeaponSprite; - } - Being::setWeapon(weapon); -} - -void Player::setHairColor(Uint16 color) { if (color != mHairColor) @@ -189,11 +156,11 @@ Player::setVisibleEquipment(Uint8 slot, int id) if (mSex == 0) { equipmentSprite = new AnimatedSprite( - "graphics/sprites/" + EquipmentDB::get(id)->getSprite(0)); + "graphics/sprites/" + ItemDB::get(id).getSprite(0)); } else { equipmentSprite = new AnimatedSprite( - "graphics/sprites/" + EquipmentDB::get(id)->getSprite(1)); + "graphics/sprites/" + ItemDB::get(id).getSprite(1)); } equipmentSprite->setDirection(getSpriteDirection()); @@ -201,6 +168,11 @@ Player::setVisibleEquipment(Uint8 slot, int id) delete mSprites[slot]; mSprites[slot] = equipmentSprite; + if (slot == WEAPON_SPRITE) + { + mEquippedWeapon = &ItemDB::get(id); + } + setAction(mAction); } diff --git a/src/player.h b/src/player.h index 8aa84992..2c06bfba 100644 --- a/src/player.h +++ b/src/player.h @@ -59,9 +59,6 @@ class Player : public Being virtual void setVisibleEquipment(Uint8 slot, int id); - - virtual void - setWeapon(Uint16 weapon); }; #endif diff --git a/src/resources/equipmentdb.cpp b/src/resources/equipmentdb.cpp deleted file mode 100644 index 38ac6415..00000000 --- a/src/resources/equipmentdb.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * The Mana World - * Copyright 2006 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * $Id$ - */ - -#include "equipmentdb.h" - -#include "resourcemanager.h" - -#include "../log.h" - -#include "../utils/dtor.h" -#include "../utils/xml.h" - -namespace -{ - EquipmentDB::EquipmentInfos mEquipmentInfos; - EquipmentInfo mUnknown; - bool mLoaded = false; -} - -void -EquipmentDB::load() -{ - if (mLoaded) - return; - - logger->log("Initializing equipment database..."); - mUnknown.setSprite("error.xml", 0); - mUnknown.setSprite("error.xml", 1); - - ResourceManager *resman = ResourceManager::getInstance(); - int size; - char *data = (char*)resman->loadFile("equipment.xml", size); - - if (!data) - { - logger->error("Equipment Database: Could not find equipment.xml!"); - } - - xmlDocPtr doc = xmlParseMemory(data, size); - free(data); - - if (!doc) - { - logger->error("Equipment Database: Error while parsing equipment database (equipment.xml)!"); - } - - xmlNodePtr rootNode = xmlDocGetRootElement(doc); - if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "equipments")) - { - logger->error("Equipment Database: equipment.xml is not a valid database file!"); - } - - //iterate <equipment>s - for_each_xml_child_node(equipmentNode, rootNode) - { - if (!xmlStrEqual(equipmentNode->name, BAD_CAST "equipment")) - { - continue; - } - - EquipmentInfo *currentInfo = new EquipmentInfo(); - - currentInfo->setSlot (XML::getProperty(equipmentNode, "slot", 0)); - - //iterate <sprite>s - for_each_xml_child_node(spriteNode, equipmentNode) - { - if (!xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) - { - continue; - } - - std::string gender = XML::getProperty(spriteNode, "gender", "unisex"); - std::string filename = (const char*) spriteNode->xmlChildrenNode->content; - - if (gender == "male" || gender == "unisex") - { - currentInfo->setSprite(filename, 0); - } - - if (gender == "female" || gender == "unisex") - { - currentInfo->setSprite(filename, 1); - } - } - - setEquipment( XML::getProperty(equipmentNode, "id", 0), - currentInfo); - } - - mLoaded = true; -} - -void -EquipmentDB::unload() -{ - // kill EquipmentInfos - for_each ( mEquipmentInfos.begin(), mEquipmentInfos.end(), - make_dtor(mEquipmentInfos)); - mEquipmentInfos.clear(); - - mLoaded = false; -} - -EquipmentInfo* -EquipmentDB::get(int id) -{ - if (!mLoaded) { - logger->error("Error: Equipment database used before initialization!"); - } - - EquipmentInfoIterator i = mEquipmentInfos.find(id); - - if (i == mEquipmentInfos.end() ) - { - logger->log("EquipmentDB: Error, unknown equipment ID# %d", id); - return &mUnknown; - } - else - { - return i->second; - } -} - -void -EquipmentDB::setEquipment(int id, EquipmentInfo* equipmentInfo) -{ - if (mEquipmentInfos.find(id) != mEquipmentInfos.end()) { - logger->log("Warning: Equipment Piece with ID %d defined multiple times", - id); - delete equipmentInfo; - } - else { - mEquipmentInfos[id] = equipmentInfo; - }; -} diff --git a/src/resources/equipmentdb.h b/src/resources/equipmentdb.h deleted file mode 100644 index 1c1db7d1..00000000 --- a/src/resources/equipmentdb.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * The Mana World - * Copyright 2006 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * $Id$ - */ - -#ifndef _TMW_EQUIPMENT_DB_H -#define _TMW_EQUIPMENT_DB_H - -#include <map> - -#include "equipmentinfo.h" - -/** - * Equipment information database. - */ -namespace EquipmentDB -{ - /** - * Loads the equipment info from Items.xml - */ - void load(); - - /** - * Frees equipment data - */ - void unload(); - - void setEquipment(int id, EquipmentInfo* equipmentInfo); - - EquipmentInfo* get(int id); - - // Equipment database types - typedef std::map<int, EquipmentInfo*> EquipmentInfos; - typedef EquipmentInfos::iterator EquipmentInfoIterator; -} - -#endif diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp index 7b16339c..18952ae9 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -18,9 +18,11 @@ * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * $Id: + * $Id$ */ +#include <cassert> + #include "itemdb.h" #include <libxml/tree.h> @@ -36,10 +38,13 @@ namespace { ItemDB::ItemInfos mItemInfos; - ItemInfo mUnknown; + ItemInfo *mUnknown; bool mLoaded = false; } +// Forward declarations +static void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node); +static void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node); void ItemDB::load() { @@ -47,11 +52,16 @@ void ItemDB::load() return; logger->log("Initializing item database..."); - mUnknown.setName("Unknown item"); + + mUnknown = new ItemInfo(); + mUnknown->setName("Unknown item"); + mUnknown->setImage(""); + mUnknown->setSprite("error.xml", 0); + mUnknown->setSprite("error.xml", 1); ResourceManager *resman = ResourceManager::getInstance(); int size; - char *data = (char*)resman->loadFile("items.xml", size); + char *data = (char*) resman->loadFile("items.xml", size); if (!data) { logger->error("ItemDB: Could not find items.xml!"); @@ -73,48 +83,66 @@ void ItemDB::load() for_each_xml_child_node(node, rootNode) { - if (!xmlStrEqual(node->name, BAD_CAST "item")) { + if (!xmlStrEqual(node->name, BAD_CAST "item")) continue; - } int id = XML::getProperty(node, "id", 0); - int art = XML::getProperty(node, "art", 0); + + if (id == 0) + { + logger->log("ItemDB: Invalid or missing item ID in items.xml!"); + continue; + } + else if (mItemInfos.find(id) != mItemInfos.end()) + { + logger->log("ItemDB: Redefinition of item ID %d", id); + } + int type = XML::getProperty(node, "type", 0); int weight = XML::getProperty(node, "weight", 0); + int view = XML::getProperty(node, "view", 0); int slot = XML::getProperty(node, "slot", 0); std::string name = XML::getProperty(node, "name", ""); std::string image = XML::getProperty(node, "image", ""); std::string description = XML::getProperty(node, "description", ""); std::string effect = XML::getProperty(node, "effect", ""); + std::string attackType = XML::getProperty(node, "attacktype", ""); - if (id && name != "") + if (id) { ItemInfo *itemInfo = new ItemInfo(); itemInfo->setImage(image); - itemInfo->setArt(art); - itemInfo->setName(name); + itemInfo->setName((name == "") ? "Unnamed" : name); itemInfo->setDescription(description); itemInfo->setEffect(effect); itemInfo->setType(type); + itemInfo->setView(view); itemInfo->setWeight(weight); itemInfo->setSlot(slot); - mItemInfos[id] = itemInfo; - } + itemInfo->setAttackType(attackType); + + for_each_xml_child_node(itemChild, node) + { + if (xmlStrEqual(itemChild->name, BAD_CAST "sprite")) + { + loadSpriteRef(itemInfo, itemChild); + } + else if (xmlStrEqual(itemChild->name, BAD_CAST "sound")) + { + loadSoundRef(itemInfo, itemChild); + } + } - if (id == 0) - { - logger->log("ItemDB: An item has no ID in items.xml!"); + mItemInfos[id] = itemInfo; } #define CHECK_PARAM(param, error_value) \ if (param == error_value) \ - logger->log("ItemDB: Missing" #param " parameter for item %i! %s", \ - id, name.c_str()) + logger->log("ItemDB: Missing " #param " attribute for item %i!",id) CHECK_PARAM(name, ""); CHECK_PARAM(image, ""); - // CHECK_PARAM(art, 0); // CHECK_PARAM(description, ""); // CHECK_PARAM(effect, ""); // CHECK_PARAM(type, 0); @@ -131,19 +159,65 @@ void ItemDB::load() void ItemDB::unload() { - for (ItemInfoIterator i = mItemInfos.begin(); i != mItemInfos.end(); i++) - { - delete i->second; - } - mItemInfos.clear(); + logger->log("Unloading item database..."); + + delete mUnknown; + mUnknown = NULL; + for_each(mItemInfos.begin(), mItemInfos.end(), make_dtor(mItemInfos)); + mItemInfos.clear(); mLoaded = false; } -const ItemInfo& -ItemDB::get(int id) +const ItemInfo& ItemDB::get(int id) { + assert(mLoaded); + ItemInfoIterator i = mItemInfos.find(id); - return (i != mItemInfos.end()) ? *(i->second) : mUnknown; + if (i == mItemInfos.end()) + { + logger->log("ItemDB: Error, unknown item ID# %d", id); + return *mUnknown; + } + else + { + return *(i->second); + } +} + +void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node) +{ + std::string gender = XML::getProperty(node, "gender", "unisex"); + std::string filename = (const char*) node->xmlChildrenNode->content; + + if (gender == "male" || gender == "unisex") + { + itemInfo->setSprite(filename, 0); + } + + if (gender == "female" || gender == "unisex") + { + itemInfo->setSprite(filename, 1); + } +} + +void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node) +{ + std::string event = XML::getProperty(node, "event", ""); + std::string filename = (const char*) node->xmlChildrenNode->content; + + if (event == "hit") + { + itemInfo->addSound(EQUIP_EVENT_HIT, filename); + } + else if (event == "strike") + { + itemInfo->addSound(EQUIP_EVENT_STRIKE, filename); + } + else + { + logger->log("ItemDB: Ignoring unknown sound event '%s'", + event.c_str()); + } } diff --git a/src/resources/iteminfo.cpp b/src/resources/iteminfo.cpp index 3a41c657..b5b25ac0 100644 --- a/src/resources/iteminfo.cpp +++ b/src/resources/iteminfo.cpp @@ -25,11 +25,11 @@ #include "resourcemanager.h" #include "image.h" - +#include "itemdb.h" ItemInfo::~ItemInfo() { - if (mImage != NULL) + if (mImage) { mImage->decRef(); } @@ -38,19 +38,87 @@ ItemInfo::~ItemInfo() void ItemInfo::setImage(const std::string &image) { + if (mImage) + { + mImage->decRef(); + } + + ResourceManager *resman = ResourceManager::getInstance(); mImageName = "graphics/items/" + image; + mImage = ResourceManager::getInstance()->getImage(mImageName); - if (mImageName != "") + if (!mImage) { - if (mImage != NULL) - { - mImage->decRef(); - } + mImage = resman->getImage("graphics/gui/unknown-item.png"); + } +} - mImage = ResourceManager::getInstance()->getImage(mImageName); +const std::string& +ItemInfo::getSprite(int gender) const +{ + if (mView) + { + // Forward the request to the item defining how to view this item + return ItemDB::get(mView).getSprite(gender); } else { - mImage = NULL; + static const std::string empty = ""; + std::map<int, std::string>::const_iterator i = + mAnimationFiles.find(gender); + + return (i != mAnimationFiles.end()) ? i->second : empty; + } +} + +void +ItemInfo::setAttackType(const std::string &attackType) +{ + if (attackType == "swing") + { + mAttackType = ACTION_ATTACK_SWING; + } + else if (attackType == "stab") + { + mAttackType = ACTION_ATTACK_STAB; + } + else if (attackType == "bow") + { + mAttackType = ACTION_ATTACK_BOW; } + else if (attackType == "throw") + { + mAttackType = ACTION_ATTACK_THROW; + } + else if (attackType == "none") + { + mAttackType = ACTION_DEFAULT; + } + else + { + mAttackType = ACTION_ATTACK; + } +} + +void +ItemInfo::addSound(EquipmentSoundEvent event, const std::string &filename) +{ + if (mSounds.find(event) == mSounds.end()) + { + mSounds[event] = new std::vector<std::string>; + } + + mSounds[event]->push_back("sfx/" + filename); +} + + +const std::string& +ItemInfo::getSound(EquipmentSoundEvent event) const +{ + static const std::string empty = ""; + std::map<EquipmentSoundEvent, std::vector<std::string>*>::const_iterator i; + i = mSounds.find(event); + + return (i == mSounds.end()) ? empty : + i->second->at(rand() % i->second->size()); } diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h index e4f851bb..4fd1638e 100644 --- a/src/resources/iteminfo.h +++ b/src/resources/iteminfo.h @@ -24,12 +24,23 @@ #ifndef _TMW_ITEMINFO_H_ #define _TMW_ITEMINFO_H_ +#include <map> #include <string> +#include <vector> + +#include "spritedef.h" class Image; +enum EquipmentSoundEvent +{ + EQUIP_EVENT_STRIKE, + EQUIP_EVENT_HIT +}; + /** - * Defines a class for storing item infos. + * Defines a class for storing item infos. This includes information used when + * the item is equipped. */ class ItemInfo { @@ -40,10 +51,11 @@ class ItemInfo ItemInfo(): mImageName(""), mImage(NULL), - mArt(0), mType(0), mWeight(0), - mSlot(0) + mView(0), + mSlot(0), + mAttackType(ACTION_DEFAULT) { } @@ -52,73 +64,89 @@ class ItemInfo */ ~ItemInfo(); - void - setArt(short art) { mArt = art; } + void setName(const std::string &name) + { mName = name; } - short - getArt() const { return mArt; } + const std::string& getName() const + { return mName; } - void - setName(const std::string &name) { mName = name; } + void setImage(const std::string &image); - const std::string& - getName() const { return mName; } + Image* getImage() const + { return mImage; } - void - setImage(const std::string &image); + void setDescription(const std::string &description) + { mDescription = description; } - Image* - getImage() const { return mImage; } + const std::string& getDescription() const + { return mDescription; } - void - setDescription(const std::string &description) - { - mDescription = description; - } + void setEffect(const std::string &effect) + { mEffect = effect; } const std::string& - getDescription() const { return mDescription; } + getEffect() const { return mEffect; } - void - setEffect(const std::string &effect) { mEffect = effect; } + void setType(short type) + { mType = type; } - const std::string& - getEffect() const { return mEffect; } + short getType() const + { return mType; } + + void setWeight(short weight) + { mWeight = weight; } + + short getWeight() const + { return mWeight; } + + void setView(int view) + { mView = view; } + + void setSlot(char slot) + { mSlot = slot; } - void - setType(short type) { mType = type; } + char getSlot() const + { return mSlot; } - short - getType() const { return mType; } + void setSprite(const std::string &animationFile, int gender) + { mAnimationFiles[gender] = animationFile; } - void - setWeight(short weight) { mWeight = weight; } + const std::string& getSprite(int gender) const; - short - getWeight() const { return mWeight; } + void setAttackType(const std::string &attackType); - void - setSlot(char slot) { mSlot = slot; } + const SpriteAction getAttackType() const + { return mAttackType; } - char - getSlot() const { return mSlot; } + void addSound(EquipmentSoundEvent event, const std::string &filename); + + const std::string& getSound(EquipmentSoundEvent event) const; protected: - std::string mImageName; + std::string mImageName; /**< The filename of the icon image. */ /* TODO (BL): I do not think the item info should keep a reference to * the item icon. It would probably be better if this was kept in the * Item class, so that the images can be lazily instantiated and also * unloaded when no longer used. */ - Image *mImage; - short mArt; + Image *mImage; /**< The loaded icon image. */ std::string mName; - std::string mDescription; - std::string mEffect; - short mType; - short mWeight; - char mSlot; + std::string mDescription; /**< Short description. */ + std::string mEffect; /**< Description of effects. */ + short mType; /**< Item type (never used). */ + short mWeight; /**< Weight in grams. */ + int mView; /**< Item ID of how this item looks. */ + + // Equipment related members + char mSlot; /**< Equipment slot. */ + SpriteAction mAttackType; /**< Attack type, in case of weapon. */ + + /** Maps gender to sprite filenames. */ + std::map<int, std::string> mAnimationFiles; + + /** Stores the names of sounds to be played at certain event. */ + std::map<EquipmentSoundEvent, std::vector<std::string>* > mSounds; }; #endif diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp index 260d5aa9..940ded36 100644 --- a/src/resources/mapreader.cpp +++ b/src/resources/mapreader.cpp @@ -156,7 +156,7 @@ MapReader::readMap(const std::string &filename) if (buffer == NULL) { - logger->log("Map file not found (%s)\n", filename.c_str()); + logger->log("Map file not found (%s)", filename.c_str()); return NULL; } diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp index 339ed6ba..f4864eea 100644 --- a/src/resources/monsterdb.cpp +++ b/src/resources/monsterdb.cpp @@ -120,19 +120,19 @@ MonsterDB::load() if (event == "hit") { - currentInfo->addSound(EVENT_HIT, filename); + currentInfo->addSound(MONSTER_EVENT_HIT, filename); } else if (event == "miss") { - currentInfo->addSound(EVENT_MISS, filename); + currentInfo->addSound(MONSTER_EVENT_MISS, filename); } else if (event == "hurt") { - currentInfo->addSound(EVENT_HURT, filename); + currentInfo->addSound(MONSTER_EVENT_HURT, filename); } else if (event == "die") { - currentInfo->addSound(EVENT_DIE, filename); + currentInfo->addSound(MONSTER_EVENT_DIE, filename); } else { diff --git a/src/resources/monsterinfo.cpp b/src/resources/monsterinfo.cpp index 2a59419e..2e896237 100644 --- a/src/resources/monsterinfo.cpp +++ b/src/resources/monsterinfo.cpp @@ -33,15 +33,15 @@ MonsterInfo::MonsterInfo(): MonsterInfo::~MonsterInfo() { - //kill vectors in mSoundEffects - for_each ( mSounds.begin(), mSounds.end(), - make_dtor(mSounds)); + // kill vectors in mSoundEffects + for_each (mSounds.begin(), mSounds.end(), + make_dtor(mSounds)); mSounds.clear(); } void -MonsterInfo::addSound (SoundEvent event, std::string filename) +MonsterInfo::addSound(MonsterSoundEvent event, std::string filename) { if (mSounds.find(event) == mSounds.end()) { @@ -53,9 +53,9 @@ MonsterInfo::addSound (SoundEvent event, std::string filename) std::string -MonsterInfo::getSound (SoundEvent event) const +MonsterInfo::getSound(MonsterSoundEvent event) const { - std::map<SoundEvent, std::vector<std::string>* >::const_iterator i; + std::map<MonsterSoundEvent, std::vector<std::string>* >::const_iterator i; i = mSounds.find(event); diff --git a/src/resources/monsterinfo.h b/src/resources/monsterinfo.h index aa7db9f0..c9fbd4c9 100644 --- a/src/resources/monsterinfo.h +++ b/src/resources/monsterinfo.h @@ -31,12 +31,12 @@ #include "../being.h" -enum SoundEvent +enum MonsterSoundEvent { - EVENT_HIT, - EVENT_MISS, - EVENT_HURT, - EVENT_DIE + MONSTER_EVENT_HIT, + MONSTER_EVENT_MISS, + MONSTER_EVENT_HURT, + MONSTER_EVENT_DIE }; /** @@ -69,7 +69,7 @@ class MonsterInfo { mTargetCursorSize = targetCursorSize; } void - addSound(SoundEvent event, std::string filename); + addSound(MonsterSoundEvent event, std::string filename); const std::string& getName () const { return mName; }; @@ -81,13 +81,13 @@ class MonsterInfo getTargetCursorSize() const { return mTargetCursorSize; } std::string - getSound (SoundEvent event) const; + getSound(MonsterSoundEvent event) const; private: std::string mName; std::string mSprite; Being::TargetCursorSize mTargetCursorSize; - std::map<SoundEvent, std::vector<std::string>* > mSounds; + std::map<MonsterSoundEvent, std::vector<std::string>* > mSounds; }; #endif diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index 6d335b02..55d7f459 100644 --- a/src/resources/spritedef.h +++ b/src/resources/spritedef.h @@ -24,11 +24,11 @@ #ifndef _TMW_SPRITEDEF_H #define _TMW_SPRITEDEF_H -#include "resource.h" - #include <map> #include <string> +#include "resource.h" + #include <libxml/tree.h> class Action; diff --git a/src/textparticle.cpp b/src/textparticle.cpp index dd01d2fe..4bc859cd 100644 --- a/src/textparticle.cpp +++ b/src/textparticle.cpp @@ -26,7 +26,8 @@ #include "graphics.h" TextParticle::TextParticle(Map *map, const std::string &text, - int colorR, int colorG, int colorB, gcn::Font *font): + int colorR, int colorG, int colorB, + gcn::Font *font): Particle(map), mText(text), mTextFont(font), @@ -41,8 +42,8 @@ void TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const if (!mAlive) return; - int screenX = (int)mPosX + offsetX; - int screenY = (int)mPosY - int(mPosZ) + offsetY; + int screenX = (int) mPos.x + offsetX; + int screenY = (int) mPos.y - (int) mPos.z + offsetY; int alpha = 255; @@ -50,7 +51,7 @@ void TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const { alpha *= mLifetimeLeft; alpha /= mFadeOut; - }; + } if (mLifetimePast < mFadeIn) { @@ -59,6 +60,6 @@ void TextParticle::draw(Graphics *graphics, int offsetX, int offsetY) const } graphics->setFont(mTextFont); - graphics->setColor(gcn::Color (mColorR, mColorG, mColorB, alpha)); + graphics->setColor(gcn::Color(mColorR, mColorG, mColorB, alpha)); graphics->drawText(mText, screenX, screenY, gcn::Graphics::CENTER); } diff --git a/src/textparticle.h b/src/textparticle.h index b365c885..34badb57 100644 --- a/src/textparticle.h +++ b/src/textparticle.h @@ -33,21 +33,26 @@ class TextParticle : public Particle { public: + /** + * Constructor. + */ TextParticle(Map *map, const std::string &text, - int colorR, int colorG, int colorB, gcn::Font *font); + int colorR, int colorG, int colorB, + gcn::Font *font); /** - * Draws the particle image + * Draws the particle image. */ virtual void draw(Graphics *graphics, int offsetX, int offsetY) const; // hack to improve text visibility - virtual int getPixelY() const { return (int)(mPosY + mPosZ); } + virtual int getPixelY() const + { return (int) (mPos.y + mPos.z); } 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 */ + 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. */ }; #endif diff --git a/src/utils/fastsqrt.h b/src/utils/fastsqrt.h index 78768149..b7b036e9 100644 --- a/src/utils/fastsqrt.h +++ b/src/utils/fastsqrt.h @@ -5,6 +5,8 @@ * http://www.math.purdue.edu/~clomont/Math/Papers/2003/InvSqrt.pdf * * Unfortunately the original creator of this function seems to be unknown. + * + * $Id$ */ float fastInvSqrt(float x) diff --git a/src/utils/minmax.h b/src/utils/minmax.h index 27eb2565..ea6ad9e0 100644 --- a/src/utils/minmax.h +++ b/src/utils/minmax.h @@ -18,6 +18,7 @@ * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * $Id$ */ /** diff --git a/src/resources/equipmentinfo.h b/src/utils/trim.h index 75ed1b8a..1b5311e6 100644 --- a/src/resources/equipmentinfo.h +++ b/src/utils/trim.h @@ -1,6 +1,6 @@ /* * The Mana World - * Copyright 2006 The Mana World Development Team + * Copyright 2007 The Mana World Development Team * * This file is part of The Mana World. * @@ -18,35 +18,36 @@ * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * $Id: + * $Id$ */ -#ifndef _TMW_EQUIPMENTINFO_H_ -#define _TMW_EQUIPMENTINFO_H_ +#ifndef _TMW_UTILS_TRIM_H_ +#define _TMW_UTILS_TRIM_H_ #include <string> -#include <map> -class EquipmentInfo +/** + * Trims spaces off the end and the beginning of the given string. + * + * @param str the string to trim spaces off + */ +static void trim(std::string &str) { - public: - EquipmentInfo(): - mSlot (0) + std::string::size_type pos = str.find_last_not_of(' '); + if (pos != std::string::npos) + { + str.erase(pos + 1); + pos = str.find_first_not_of(' '); + if (pos != std::string::npos) { - }; - - void - setSlot (int slot) { mSlot = slot; }; - - const std::string& - getSprite(int gender) {return animationFiles[gender]; }; - - void - setSprite(std::string animationFile, int gender) {animationFiles[gender] = animationFile; }; - - private: - int mSlot; //not used at the moment but maybe useful on our own server - std::map<int, std::string> animationFiles; -}; + str.erase(0, pos); + } + } + else + { + // There is nothing else but whitespace in the string + str.clear(); + } +} #endif diff --git a/src/vector.h b/src/vector.h new file mode 100644 index 00000000..7a5da241 --- /dev/null +++ b/src/vector.h @@ -0,0 +1,134 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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 Mana World 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 Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_VECTOR_H_ +#define _TMW_VECTOR_H_ + +/** + * Vector class. Represents either a 3D point in space, a velocity or a force. + * Provides several convenient operator overloads. + */ +class Vector +{ + public: + /** + * Constructor. + */ + Vector(): + x(0.0f), + y(0.0f), + z(0.0f) + {} + + /** + * Constructor. + */ + Vector(float x, float y, float z): + x(x), + y(y), + z(z) + {} + + /** + * Copy constructor. + */ + Vector(const Vector &v): + x(v.x), + y(v.y), + z(v.z) + {} + + /** + * Scale vector operator. + */ + Vector operator*(float c) const + { + return Vector(x * c, + y * c, + z * c); + } + + /** + * In-place scale vector operator. + */ + void operator*=(float c) + { + x *= c; + y *= c; + z *= c; + } + + /** + * Scale vector operator. + */ + Vector operator/(float c) const + { + return Vector(x / c, + y / c, + z / c); + } + + /** + * Add vector operator. + */ + Vector operator+(const Vector &v) const + { + return Vector(x + v.x, + y + v.y, + z + v.z); + } + + /** + * In-place add vector operator. + */ + void operator+=(const Vector &v) + { + x += v.x; + y += v.y; + z += v.z; + } + + /** + * Substract vector operator. + */ + Vector operator-(const Vector &v) const + { + return Vector(x - v.x, + y - v.y, + z - v.z); + } + + /** + * In-place substract vector operator. + */ + void operator-=(const Vector &v) + { + x -= v.x; + y -= v.y; + z -= v.z; + } + + float x, y, z; +}; + +#endif |