diff options
author | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2009-03-22 19:45:03 +0100 |
---|---|---|
committer | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2009-03-22 19:45:56 +0100 |
commit | 0c43d04b438d41c277ae80402d4b4888db1a0b64 (patch) | |
tree | 3aaeb75ecd1bcbe85decedab5f1fa426fe0411e3 /src/gui | |
parent | a7f5eaeb7f643658d356533a608f0f18d85b6d32 (diff) | |
parent | 401802c1d7a1b3d659bdc53a45d9a6292fc1121e (diff) | |
download | mana-0c43d04b438d41c277ae80402d4b4888db1a0b64.tar.gz mana-0c43d04b438d41c277ae80402d4b4888db1a0b64.tar.bz2 mana-0c43d04b438d41c277ae80402d4b4888db1a0b64.tar.xz mana-0c43d04b438d41c277ae80402d4b4888db1a0b64.zip |
Merged the tmwserv client with the eAthena client
This merge involved major changes on both sides, and as such took
several weeks. Lots of things are expected to be broken now, however, we
now have a single code base to improve and extend, which can be compiled
to support either eAthena or tmwserv.
In the coming months, the plan is to work towards a client that supports
both eAthena and tmwserv, without needing to be recompiled.
Conflicts:
Everywhere!
Diffstat (limited to 'src/gui')
94 files changed, 6131 insertions, 534 deletions
diff --git a/src/gui/browserbox.cpp b/src/gui/browserbox.cpp index a06b9a6e..7c0ae1a7 100644 --- a/src/gui/browserbox.cpp +++ b/src/gui/browserbox.cpp @@ -415,4 +415,3 @@ void BrowserBox::draw(gcn::Graphics *graphics) setHeight((mTextRows.size() + wrappedLines) * font->getHeight()); } } - diff --git a/src/gui/browserbox.h b/src/gui/browserbox.h index 5dde402e..090c03e1 100644 --- a/src/gui/browserbox.h +++ b/src/gui/browserbox.h @@ -162,4 +162,3 @@ class BrowserBox : public gcn::Widget, public gcn::MouseListener }; #endif - diff --git a/src/gui/buddywindow.cpp b/src/gui/buddywindow.cpp new file mode 100644 index 00000000..25084593 --- /dev/null +++ b/src/gui/buddywindow.cpp @@ -0,0 +1,68 @@ +/* + * 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 + */ + +#include "buddywindow.h" +#include "chat.h" +#include "icon.h" + +#include "widgets/avatar.h" + +#include "../resources/resourcemanager.h" +#include "../utils/gettext.h" + +extern ChatWindow *chatWindow; + +BuddyWindow::BuddyWindow(): + Window(_("Buddy")) +{ + setVisible(false); + setWindowName("Buddy Window"); + setCaption(_("Buddy List")); + setResizable(true); + setCloseButton(true); + setMinWidth(110); + setMinHeight(200); + setDefaultSize(124, 41, 288, 330); + + Image *addImg = ResourceManager::getInstance()->getImage("buddyadd.png"); + Image *delImg = ResourceManager::getInstance()->getImage("buddydel.png"); + + if (addImg && delImg) + { + Icon *addBuddy = new Icon(addImg); + Icon *delBuddy = new Icon(delImg); + + add(addBuddy); + add(delBuddy); + } + + loadWindowState(); +} + +void BuddyWindow::action(const gcn::ActionEvent &event) +{ + +} + +void BuddyWindow::draw(gcn::Graphics *graphics) +{ + Window::draw(graphics); +} diff --git a/src/gui/buddywindow.h b/src/gui/buddywindow.h new file mode 100644 index 00000000..dfe356d5 --- /dev/null +++ b/src/gui/buddywindow.h @@ -0,0 +1,60 @@ +/* + * 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 + */ + +#ifndef _TMW_BUDDYWINDOW_H +#define _TMW_BUDDYWINDOW_H + +#include <guichan/actionlistener.hpp> +#include <guichan/actionevent.hpp> + +#include "window.h" + +class Avatar; + +/** + * Window showing buddy list. + * + * \ingroup Interface + */ +class BuddyWindow : public Window, public gcn::ActionListener +{ + public: + /** + * Constructor. + */ + BuddyWindow(); + + /** + * Performs action. + */ + void action(const gcn::ActionEvent &event); + + /** + * Draws the window + */ + void draw(gcn::Graphics *graphics); + + + private: + std::list<Avatar*> mBuddyList; +}; + +#endif /* _TMW_BUDDYWINDOW_H */ diff --git a/src/gui/buy.cpp b/src/gui/buy.cpp index b31d48e1..15a558a3 100644 --- a/src/gui/buy.cpp +++ b/src/gui/buy.cpp @@ -33,14 +33,25 @@ #include "../npc.h" #include "../units.h" +#ifdef TMWSERV_SUPPORT +#include "../net/gameserver/player.h" +#else #include "../net/messageout.h" -#include "../net/protocol.h" +#include "../net/ea/protocol.h" +#endif #include "../utils/gettext.h" #include "../utils/strprintf.h" +#ifdef TMWSERV_SUPPORT +BuyDialog::BuyDialog(): +#else BuyDialog::BuyDialog(Network *network): - Window(_("Buy")), mNetwork(network), +#endif + Window(_("Buy")), +#ifndef TMWSERV_SUPPORT + mNetwork(network), +#endif mMoney(0), mAmountItems(0), mMaxItems(0) { setWindowName("Buy"); @@ -119,9 +130,9 @@ void BuyDialog::reset() setMoney(0); } -void BuyDialog::addItem(short id, int price) +void BuyDialog::addItem(int id, int amount, int price) { - mShopItems->addItem(id, price); + mShopItems->addItem(id, amount, price); mShopItemList->adjustSize(); } @@ -166,11 +177,16 @@ void BuyDialog::action(const gcn::ActionEvent &event) else if (event.getId() == "buy" && mAmountItems > 0 && mAmountItems <= mMaxItems) { +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::tradeWithNPC + (mShopItems->at(selectedItem)->getId(), mAmountItems); +#else MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_NPC_BUY_REQUEST); outMsg.writeInt16(8); outMsg.writeInt16(mAmountItems); outMsg.writeInt16(mShopItems->at(selectedItem)->getId()); +#endif // Update money and adjust the max number of items that can be bought mMaxItems -= mAmountItems; diff --git a/src/gui/buy.h b/src/gui/buy.h index 0f1cfede..5510ccc6 100644 --- a/src/gui/buy.h +++ b/src/gui/buy.h @@ -25,11 +25,13 @@ #include <guichan/actionlistener.hpp> #include <guichan/selectionlistener.hpp> -#include <SDL_types.h> - #include "window.h" +#include "../guichanfwd.h" + +#ifndef TMWSERV_SUPPORT class Network; +#endif class ShopItems; class ShopListBox; class ListBox; @@ -48,7 +50,11 @@ class BuyDialog : public Window, public gcn::ActionListener, * * @see Window::Window */ +#ifdef TMWSERV_SUPPORT + BuyDialog(); +#else BuyDialog(Network *network); +#endif /** * Destructor @@ -68,7 +74,7 @@ class BuyDialog : public Window, public gcn::ActionListener, /** * Adds an item to the shop inventory. */ - void addItem(short id, int price); + void addItem(int id, int amount, int price); /** * Called when receiving actions from the widgets. @@ -96,7 +102,9 @@ class BuyDialog : public Window, public gcn::ActionListener, void updateButtonsAndLabels(); private: +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif gcn::Button *mBuyButton; gcn::Button *mQuitButton; gcn::Button *mIncreaseButton; @@ -111,9 +119,9 @@ class BuyDialog : public Window, public gcn::ActionListener, ShopItems *mShopItems; - Uint32 mMoney; - Uint32 mAmountItems; - Uint32 mMaxItems; + int mMoney; + int mAmountItems; + int mMaxItems; }; #endif diff --git a/src/gui/changeemaildialog.cpp b/src/gui/changeemaildialog.cpp new file mode 100644 index 00000000..c9bc2570 --- /dev/null +++ b/src/gui/changeemaildialog.cpp @@ -0,0 +1,167 @@ +/* + * The Mana World + * Copyright 2008 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 "changeemaildialog.h" + +#include <string> +#include <sstream> + +#include <guichan/widgets/label.hpp> + +#include "../main.h" +#include "../log.h" +#include "../logindata.h" + +#include "button.h" +#include "register.h" +#include "textfield.h" +#include "ok_dialog.h" + +#include "../utils/gettext.h" +#include "../utils/strprintf.h" + +ChangeEmailDialog::ChangeEmailDialog(Window *parent, LoginData *loginData): + Window(_("Change Email Address"), true, parent), + mWrongDataNoticeListener(new WrongDataNoticeListener()), + mLoginData(loginData) +{ + gcn::Label *accountLabel = new gcn::Label(strprintf(_("Account: %s"), + mLoginData->username.c_str())); + gcn::Label *newEmailLabel = new gcn::Label(_("Type New Email Address twice:")); + mFirstEmailField = new TextField(); + mSecondEmailField = new TextField(); + mChangeEmailButton = new Button(_("Change Email Address"), "change_email", this); + mCancelButton = new Button(_("Cancel"), "cancel", this); + + const int width = 200; + const int height = 130; + setContentSize(width, height); + + accountLabel->setPosition(5, 5); + accountLabel->setWidth(130); + + newEmailLabel->setPosition( + 5, accountLabel->getY() + accountLabel->getHeight() + 7); + newEmailLabel->setWidth(width - 5); + + mFirstEmailField->setPosition( + 5, newEmailLabel->getY() + newEmailLabel->getHeight() + 7); + mFirstEmailField->setWidth(130); + + mSecondEmailField->setPosition( + 5, mFirstEmailField->getY() + mFirstEmailField->getHeight() + 7); + mSecondEmailField->setWidth(130); + + mCancelButton->setPosition( + width - 5 - mCancelButton->getWidth(), + height - 5 - mCancelButton->getHeight()); + mChangeEmailButton->setPosition( + mCancelButton->getX() - 5 - mChangeEmailButton->getWidth(), + mCancelButton->getY()); + + add(accountLabel); + add(newEmailLabel); + add(mFirstEmailField); + add(mSecondEmailField); + add(mChangeEmailButton); + add(mCancelButton); + + setLocationRelativeTo(getParent()); + setVisible(true); + mFirstEmailField->requestFocus(); + + mFirstEmailField->setActionEventId("change_email"); + mSecondEmailField->setActionEventId("change_email"); +} + +ChangeEmailDialog::~ChangeEmailDialog() +{ + delete mWrongDataNoticeListener; +} + +void +ChangeEmailDialog::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "cancel") + { + scheduleDelete(); + } + else if (event.getId() == "change_email") + { + + const std::string username = mLoginData->username.c_str(); + const std::string newFirstEmail = mFirstEmailField->getText(); + const std::string newSecondEmail = mSecondEmailField->getText(); + logger->log("ChangeEmailDialog::Email change, Username is %s", + username.c_str()); + + std::stringstream errorMsg; + int error = 0; + + if (newFirstEmail.length() < LEN_MIN_PASSWORD) + { + // First email address too short + errorMsg << "The new email address needs to be at least " + << LEN_MIN_PASSWORD + << " characters long."; + error = 1; + } + else if (newFirstEmail.length() > LEN_MAX_PASSWORD - 1 ) + { + // First email address too long + errorMsg << "The new email address needs to be less than " + << LEN_MAX_PASSWORD + << " characters long."; + error = 1; + } + else if (newFirstEmail != newSecondEmail) + { + // Second Pass mismatch + errorMsg << "The email address entries mismatch."; + error = 2; + } + + if (error > 0) + { + if (error == 1) + { + mWrongDataNoticeListener->setTarget(this->mFirstEmailField); + } + else if (error == 2) + { + mWrongDataNoticeListener->setTarget(this->mSecondEmailField); + } + + OkDialog *dlg = new OkDialog("Error", errorMsg.str()); + dlg->addActionListener(mWrongDataNoticeListener); + } + else + { + // No errors detected, change account password. + mChangeEmailButton->setEnabled(false); + // Set the new email address + mLoginData->newEmail = newFirstEmail; + state = STATE_CHANGEEMAIL_ATTEMPT; + scheduleDelete(); + } + + } +} diff --git a/src/gui/changeemaildialog.h b/src/gui/changeemaildialog.h new file mode 100644 index 00000000..8ec3705d --- /dev/null +++ b/src/gui/changeemaildialog.h @@ -0,0 +1,71 @@ +/* + * The Mana World + * Copyright 2008 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_GUI_CHANGEEMAIL_H +#define _TMW_GUI_CHANGEEMAIL_H + +#include <iosfwd> +#include <guichan/actionlistener.hpp> + +#include "window.h" +#include "../guichanfwd.h" + +class LoginData; +class OkDialog; +class WrongDataNoticeListener; + +/** + * The Change email dialog. + * + * \ingroup Interface + */ +class ChangeEmailDialog : public Window, public gcn::ActionListener { + public: + /** + * Constructor. + * + * @see Window::Window + */ + ChangeEmailDialog(Window *parent, LoginData *loginData); + + /** + * Destructor. + */ + ~ChangeEmailDialog(); + + /** + * Called when receiving actions from the widgets. + */ + void action(const gcn::ActionEvent &event); + + private: + gcn::TextField *mFirstEmailField; + gcn::TextField *mSecondEmailField; + + gcn::Button *mChangeEmailButton; + gcn::Button *mCancelButton; + + WrongDataNoticeListener *mWrongDataNoticeListener; + + LoginData *mLoginData; +}; + +#endif diff --git a/src/gui/changepassworddialog.cpp b/src/gui/changepassworddialog.cpp new file mode 100644 index 00000000..8d667790 --- /dev/null +++ b/src/gui/changepassworddialog.cpp @@ -0,0 +1,193 @@ +/* + * 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 + */ + +#include "changepassworddialog.h" + +#include <string> +#include <sstream> + +#include <guichan/widgets/label.hpp> + +#include "../main.h" +#include "../log.h" +#include "../logindata.h" + +#include "button.h" +#include "register.h" +#include "passwordfield.h" +#include "textfield.h" +#include "ok_dialog.h" + +#include "../utils/gettext.h" +#include "../utils/strprintf.h" + +ChangePasswordDialog::ChangePasswordDialog(Window *parent, LoginData *loginData): + Window(_("Change Password"), true, parent), + mWrongDataNoticeListener(new WrongDataNoticeListener()), + mLoginData(loginData) +{ + gcn::Label *accountLabel = new gcn::Label(strprintf(_("Account: %s"), + mLoginData->username.c_str())); + gcn::Label *oldPassLabel = new gcn::Label(_("Password:")); + mOldPassField = new PasswordField(); + gcn::Label *newPassLabel = new gcn::Label(_("Type New Password twice:")); + mFirstPassField = new PasswordField(); + mSecondPassField = new PasswordField(); + mChangePassButton = new Button(_("Change Password"), "change_password", this); + mCancelButton = new Button(_("Cancel"), "cancel", this); + + const int width = 200; + const int height = 170; + setContentSize(width, height); + + accountLabel->setPosition(5, 5); + accountLabel->setWidth(130); + oldPassLabel->setPosition( + 5, accountLabel->getY() + accountLabel->getHeight() + 7); + oldPassLabel->setWidth(130); + + mOldPassField->setPosition( + 5, oldPassLabel->getY() + oldPassLabel->getHeight() + 7); + mOldPassField->setWidth(130); + + newPassLabel->setPosition( + 5, mOldPassField->getY() + mOldPassField->getHeight() + 7); + newPassLabel->setWidth(width - 5); + + mFirstPassField->setPosition( + 5, newPassLabel->getY() + newPassLabel->getHeight() + 7); + mFirstPassField->setWidth(130); + + mSecondPassField->setPosition( + 5, mFirstPassField->getY() + mFirstPassField->getHeight() + 7); + mSecondPassField->setWidth(130); + + mCancelButton->setPosition( + width - 5 - mCancelButton->getWidth(), + height - 5 - mCancelButton->getHeight()); + mChangePassButton->setPosition( + mCancelButton->getX() - 5 - mChangePassButton->getWidth(), + mCancelButton->getY()); + + add(accountLabel); + add(oldPassLabel); + add(mOldPassField); + add(newPassLabel); + add(mFirstPassField); + add(mSecondPassField); + add(mChangePassButton); + add(mCancelButton); + + setLocationRelativeTo(getParent()); + setVisible(true); + mOldPassField->requestFocus(); + + mOldPassField->setActionEventId("change_password"); + mFirstPassField->setActionEventId("change_password"); + mSecondPassField->setActionEventId("change_password"); +} + +ChangePasswordDialog::~ChangePasswordDialog() +{ + delete mWrongDataNoticeListener; +} + +void +ChangePasswordDialog::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "cancel") + { + scheduleDelete(); + } + else if (event.getId() == "change_password") + { + + const std::string username = mLoginData->username.c_str(); + const std::string oldPassword = mOldPassField->getText(); + const std::string newFirstPass = mFirstPassField->getText(); + const std::string newSecondPass = mSecondPassField->getText(); + logger->log("ChangePasswordDialog::Password change, Username is %s", + username.c_str()); + + std::stringstream errorMsg; + int error = 0; + + // Check old Password + if (oldPassword.empty()) + { + // No old password + errorMsg << "Enter the old Password first."; + error = 1; + } + else if (newFirstPass.length() < LEN_MIN_PASSWORD) + { + // First password too short + errorMsg << "The new password needs to be at least " + << LEN_MIN_PASSWORD + << " characters long."; + error = 2; + } + else if (newFirstPass.length() > LEN_MAX_PASSWORD - 1 ) + { + // First password too long + errorMsg << "The new password needs to be less than " + << LEN_MAX_PASSWORD + << " characters long."; + error = 2; + } + else if (newFirstPass != newSecondPass) + { + // Second Pass mismatch + errorMsg << "The new password entries mismatch."; + error = 3; + } + + if (error > 0) + { + if (error == 1) + { + mWrongDataNoticeListener->setTarget(this->mOldPassField); + } + else if (error == 2) + { + mWrongDataNoticeListener->setTarget(this->mFirstPassField); + } + else if (error == 3) + { + mWrongDataNoticeListener->setTarget(this->mSecondPassField); + } + + OkDialog *dlg = new OkDialog("Error", errorMsg.str()); + dlg->addActionListener(mWrongDataNoticeListener); + } + else + { + // No errors detected, change account password. + mChangePassButton->setEnabled(false); + // Set the new password + mLoginData->password = oldPassword; + mLoginData->newPassword = newFirstPass; + state = STATE_CHANGEPASSWORD_ATTEMPT; + scheduleDelete(); + } + + } +} diff --git a/src/gui/changepassworddialog.h b/src/gui/changepassworddialog.h new file mode 100644 index 00000000..0cf744da --- /dev/null +++ b/src/gui/changepassworddialog.h @@ -0,0 +1,72 @@ +/* + * 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 + */ + +#ifndef _TMW_CHANGEPASSWORDDIALOG_H +#define _TMW_CHANGEPASSWORDDIALOG_H + +#include <iosfwd> +#include <guichan/actionlistener.hpp> + +#include "window.h" +#include "../guichanfwd.h" + +class LoginData; +class OkDialog; +class WrongDataNoticeListener; + +/** + * The Change password dialog. + * + * \ingroup Interface + */ +class ChangePasswordDialog : public Window, public gcn::ActionListener { + public: + /** + * Constructor + * + * @see Window::Window + */ + ChangePasswordDialog(Window *parent,LoginData *loginData); + + /** + * Destructor + */ + ~ChangePasswordDialog(); + + /** + * Called when receiving actions from the widgets. + */ + void action(const gcn::ActionEvent &event); + + private: + gcn::TextField *mOldPassField; + gcn::TextField *mFirstPassField; + gcn::TextField *mSecondPassField; + + gcn::Button *mChangePassButton; + gcn::Button *mCancelButton; + + WrongDataNoticeListener *mWrongDataNoticeListener; + + LoginData *mLoginData; +}; + +#endif diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp index 34218477..84270d10 100644 --- a/src/gui/char_select.cpp +++ b/src/gui/char_select.cpp @@ -32,6 +32,19 @@ #include "playerbox.h" #include "textfield.h" +#ifdef TMWSERV_SUPPORT +#include "radiobutton.h" +#include "slider.h" + +#include "unregisterdialog.h" +#include "changepassworddialog.h" +#include "changeemaildialog.h" + +#include "../logindata.h" + +#include "../net/accountserver/account.h" +#endif + #include "widgets/layout.h" #include "../game.h" @@ -80,22 +93,72 @@ void CharDeleteConfirm::action(const gcn::ActionEvent &event) ConfirmDialog::action(event); } +#ifdef TMWSERV_SUPPORT +CharSelectDialog::CharSelectDialog(LockedArray<LocalPlayer*> *charInfo, + LoginData *loginData): + Window(_("Account and Character Management")), + mCharInfo(charInfo), mCharSelected(false), mLoginData(loginData) +#else CharSelectDialog::CharSelectDialog(Network *network, LockedArray<LocalPlayer*> *charInfo, Gender gender): Window(_("Select Character")), mNetwork(network), - mCharInfo(charInfo), mGender(gender), mCharSelected(false) + mCharInfo(charInfo), + mCharSelected(false), + mGender(gender) +#endif { + mSelectButton = new Button(_("Ok"), "ok", this); + mCancelButton = new Button(_("Cancel"), "cancel", this); + mPreviousButton = new Button(_("Previous"), "previous", this); + mNextButton = new Button(_("Next"), "next", this); + mNameLabel = new gcn::Label(strprintf(_("Name: %s"), "")); + mLevelLabel = new gcn::Label(strprintf(_("Level: %d"), 0)); +#ifdef TMWSERV_SUPPORT + mNewCharButton = new Button(_("New"), "new", this); + mDelCharButton = new Button(_("Delete"), "delete", this); + mUnRegisterButton = new Button(_("Unregister"), "unregister", this); + mChangePasswordButton = new Button(_("Change Password"), "change_password", this); + mChangeEmailButton = new Button(_("Change Email Address"), "change_email", this); + + mAccountNameLabel = new gcn::Label(strprintf(_("Account: %s"), mLoginData->username.c_str())); + mNameLabel = new gcn::Label(strprintf(_("Name: %s"), "")); + mLevelLabel = new gcn::Label(strprintf(_("Level: %d"), 0)); + mMoneyLabel = new gcn::Label(strprintf(_("Money: %d"), 0)); + + // Control that shows the Player + mPlayerBox = new PlayerBox; + mPlayerBox->setWidth(74); + + ContainerPlacer place; + place = getPlacer(0, 0); + place(0, 0, mAccountNameLabel); + place(0, 1, mUnRegisterButton); + place(0, 2, mChangePasswordButton); + place(1, 2, mChangeEmailButton); + place = getPlacer(0, 1); + place(0, 0, mPlayerBox, 1, 5).setPadding(3); + place(1, 0, mNameLabel, 3); + place(1, 1, mLevelLabel, 3); + place(1, 2, mMoneyLabel, 3); + place(1, 3, mPreviousButton); + place(2, 3, mNextButton); + place(1, 4, mNewCharButton); + place(2, 4, mDelCharButton); + place.getCell().matchColWidth(1, 2); + place = getPlacer(0, 2); + place(0, 0, mSelectButton); + place(1, 0, mCancelButton); + reflowLayout(265, 0); +#else mCharInfo->select(0); LocalPlayer *pi = mCharInfo->getEntry(); if (pi) - mMoney = Units::formatCurrency(pi->mGp); + mMoney = Units::formatCurrency(pi->getMoney()); // Control that shows the Player mPlayerBox = new PlayerBox; mPlayerBox->setWidth(74); - mNameLabel = new gcn::Label(strprintf(_("Name: %s"), "")); - mLevelLabel = new gcn::Label(strprintf(_("Level: %d"), 0)); mJobLevelLabel = new gcn::Label(strprintf(_("Job Level: %d"), 0)); mMoneyLabel = new gcn::Label(strprintf(_("Money: %s"), mMoney.c_str())); @@ -103,11 +166,7 @@ CharSelectDialog::CharSelectDialog(Network *network, getFont()->getWidth(_("Delete")) ? _("Delete") : _("New"); - mPreviousButton = new Button(_("Previous"), "previous", this); - mNextButton = new Button(_("Next"), "next", this); mNewDelCharButton = new Button(tempString, "newdel", this); - mSelectButton = new Button(_("Ok"), "ok", this); - mCancelButton = new Button(_("Cancel"), "cancel", this); ContainerPlacer place; place = getPlacer(0, 0); @@ -126,6 +185,7 @@ CharSelectDialog::CharSelectDialog(Network *network, place(5, 0, mSelectButton); reflowLayout(250, 0); +#endif setLocationRelativeTo(getParent()); setVisible(true); @@ -135,10 +195,21 @@ CharSelectDialog::CharSelectDialog(Network *network, void CharSelectDialog::action(const gcn::ActionEvent &event) { +#ifdef TMWSERV_SUPPORT + // The pointers are set to NULL if there is no character stored + if (event.getId() == "ok" && (mCharInfo->getEntry())) +#else if (event.getId() == "ok" && n_character > 0) +#endif { // Start game +#ifdef TMWSERV_SUPPORT + mNewCharButton->setEnabled(false); + mDelCharButton->setEnabled(false); + mUnRegisterButton->setEnabled(false); +#else mNewDelCharButton->setEnabled(false); +#endif mSelectButton->setEnabled(false); mPreviousButton->setEnabled(false); mNextButton->setEnabled(false); @@ -147,8 +218,35 @@ void CharSelectDialog::action(const gcn::ActionEvent &event) } else if (event.getId() == "cancel") { - state = EXIT_STATE; +#ifdef TMWSERV_SUPPORT + mCharInfo->clear(); + state = STATE_SWITCH_ACCOUNTSERVER_ATTEMPT; +#else + state = STATE_EXIT; +#endif + } +#ifdef TMWSERV_SUPPORT + else if (event.getId() == "new") + { + // TODO: Search the first free slot, and start CharCreateDialog + // maybe add that search to the constructor. + if (!(mCharInfo->getEntry())) + { + // Start new character dialog + CharCreateDialog *charCreateDialog = + new CharCreateDialog(this, mCharInfo->getPos()); + charServerHandler.setCharCreateDialog(charCreateDialog); + } + } + else if (event.getId() == "delete") + { + // Delete character + if (mCharInfo->getEntry()) + { + new CharDeleteConfirm(this); + } } +#else else if (event.getId() == "newdel") { // Check for a character @@ -165,20 +263,35 @@ void CharSelectDialog::action(const gcn::ActionEvent &event) charServerHandler.setCharCreateDialog(charCreateDialog); } } +#endif else if (event.getId() == "previous") { mCharInfo->prev(); LocalPlayer *pi = mCharInfo->getEntry(); if (pi) - mMoney = Units::formatCurrency(pi->mGp); + mMoney = Units::formatCurrency(pi->getMoney()); } else if (event.getId() == "next") { mCharInfo->next(); LocalPlayer *pi = mCharInfo->getEntry(); if (pi) - mMoney = Units::formatCurrency(pi->mGp); + mMoney = Units::formatCurrency(pi->getMoney()); + } +#ifdef TMWSERV_SUPPORT + else if (event.getId() == "unregister") + { + new UnRegisterDialog(this, mLoginData); } + else if (event.getId() == "change_password") + { + new ChangePasswordDialog(this, mLoginData); + } + else if (event.getId() == "change_email") + { + new ChangeEmailDialog(this, mLoginData); + } +#endif } void CharSelectDialog::updatePlayerInfo() @@ -189,13 +302,20 @@ void CharSelectDialog::updatePlayerInfo() { mNameLabel->setCaption(strprintf(_("Name: %s"), pi->getName().c_str())); - mLevelLabel->setCaption(strprintf(_("Level: %d"), pi->mLevel)); + mLevelLabel->setCaption(strprintf(_("Level: %d"), pi->getLevel())); +#ifndef TMWSERV_SUPPORT mJobLevelLabel->setCaption(strprintf(_("Job Level: %d"), pi->mJobLevel)); +#endif mMoneyLabel->setCaption(strprintf(_("Money: %s"), mMoney.c_str())); if (!mCharSelected) { +#ifdef TMWSERV_SUPPORT + mNewCharButton->setEnabled(false); + mDelCharButton->setEnabled(true); +#else mNewDelCharButton->setCaption(_("Delete")); +#endif mSelectButton->setEnabled(true); } } @@ -203,9 +323,16 @@ void CharSelectDialog::updatePlayerInfo() { mNameLabel->setCaption(strprintf(_("Name: %s"), "")); mLevelLabel->setCaption(strprintf(_("Level: %d"), 0)); +#ifndef TMWSERV_SUPPORT mJobLevelLabel->setCaption(strprintf(_("Job Level: %d"), 0)); +#endif mMoneyLabel->setCaption(strprintf(_("Money: %s"), "")); +#ifdef TMWSERV_SUPPORT + mNewCharButton->setEnabled(true); + mDelCharButton->setEnabled(false); +#else mNewDelCharButton->setCaption(_("New")); +#endif mSelectButton->setEnabled(false); } @@ -214,20 +341,28 @@ void CharSelectDialog::updatePlayerInfo() void CharSelectDialog::attemptCharDelete() { +#ifdef TMWSERV_SUPPORT + Net::AccountServer::Account::deleteCharacter(mCharInfo->getPos()); +#else // Request character deletion MessageOut outMsg(mNetwork); outMsg.writeInt16(0x0068); outMsg.writeInt32(mCharInfo->getEntry()->mCharId); outMsg.writeString("a@a.com", 40); +#endif mCharInfo->lock(); } void CharSelectDialog::attemptCharSelect() { +#ifdef TMWSERV_SUPPORT + Net::AccountServer::Account::selectCharacter(mCharInfo->getPos()); +#else // Request character selection MessageOut outMsg(mNetwork); outMsg.writeInt16(0x0066); outMsg.writeInt8(mCharInfo->getPos()); +#endif mCharInfo->lock(); } @@ -259,16 +394,29 @@ bool CharSelectDialog::selectByName(const std::string &name) return false; } +#ifdef TMWSERV_SUPPORT +CharCreateDialog::CharCreateDialog(Window *parent, int slot): +#else CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, Gender gender): - Window(_("Create Character"), true, parent), mNetwork(network), mSlot(slot) +#endif + Window(_("Create Character"), true, parent), +#ifndef TMWSERV_SUPPORT + mNetwork(network), +#endif + mSlot(slot) { mPlayer = new Player(0, 0, NULL); +#ifdef TMWSERV_SUPPORT + mPlayer->setGender(GENDER_MALE); +#else mPlayer->setGender(gender); +#endif int numberOfHairColors = ColorDB::size(); - mPlayer->setHairStyle(rand() % mPlayer->getNumOfHairstyles(), rand() % numberOfHairColors); + mPlayer->setHairStyle(rand() % mPlayer->getNumOfHairstyles(), + rand() % numberOfHairColors); mNameField = new TextField(""); mNameLabel = new gcn::Label(_("Name:")); @@ -280,6 +428,19 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, mHairStyleLabel = new gcn::Label(_("Hair Style:")); mCreateButton = new Button(_("Create"), "create", this); mCancelButton = new Button(_("Cancel"), "cancel", this); +#ifdef TMWSERV_SUPPORT + mMale = new RadioButton(_("Male"), "gender"); + mFemale = new RadioButton(_("Female"), "gender"); + + // Default to a Male character + mMale->setSelected(true); + + mMale->setActionEventId("gender"); + mFemale->setActionEventId("gender"); + + mMale->addActionListener(this); + mFemale->addActionListener(this); +#endif mPlayerBox = new PlayerBox(mPlayer); mPlayerBox->setWidth(74); @@ -287,6 +448,81 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, mNameField->setActionEventId("create"); mNameField->addActionListener(this); +#ifdef TMWSERV_SUPPORT + mAttributeLabel[0] = new gcn::Label(_("Strength:")); + mAttributeLabel[1] = new gcn::Label(_("Agility:")); + mAttributeLabel[2] = new gcn::Label(_("Dexterity:")); + mAttributeLabel[3] = new gcn::Label(_("Vitality:")); + mAttributeLabel[4] = new gcn::Label(_("Intelligence:")); + mAttributeLabel[5] = new gcn::Label(_("Willpower:")); + for (int i = 0; i < 6; i++) + { + mAttributeLabel[i]->setWidth(70); + mAttributeSlider[i] = new Slider(1, 20); + mAttributeValue[i] = new gcn::Label("1"); + }; + + mAttributesLeft = new gcn::Label(strprintf(_("Please distribute %d points"), 99)); + + int w = 200; + int h = 330; + setContentSize(w, h); + mPlayerBox->setDimension(gcn::Rectangle(80, 30, 110, 85)); + mNameLabel->setPosition(5, 5); + mNameField->setDimension( + gcn::Rectangle(45, 5, w - 45 - 7, mNameField->getHeight())); + mPrevHairColorButton->setPosition(90, 35); + mNextHairColorButton->setPosition(165, 35); + mHairColorLabel->setPosition(5, 40); + mPrevHairStyleButton->setPosition(90, 64); + mNextHairStyleButton->setPosition(165, 64); + mHairStyleLabel->setPosition(5, 70); + for (int i=0; i<6; i++) + { + mAttributeSlider[i]->setValue(10); + mAttributeSlider[i]->setDimension(gcn::Rectangle( 75, 140 + i*20, + 100, 10)); + mAttributeSlider[i]->setActionEventId("statslider"); + mAttributeSlider[i]->addActionListener(this); + mAttributeValue[i]->setPosition(180, 140 + i*20); + mAttributeLabel[i]->setPosition(5, 140 + i*20); + }; + mAttributesLeft->setPosition(15, 280); + UpdateSliders(); + mCancelButton->setPosition( + w - 5 - mCancelButton->getWidth(), + h - 5 - mCancelButton->getHeight()); + mCreateButton->setPosition( + mCancelButton->getX() - 5 - mCreateButton->getWidth(), + h - 5 - mCancelButton->getHeight()); + + mMale->setPosition(30, 120); + mFemale->setPosition(100, 120); + + add(mPlayerBox); + add(mNameField); + add(mNameLabel); + add(mNextHairColorButton); + add(mPrevHairColorButton); + add(mHairColorLabel); + add(mNextHairStyleButton); + add(mPrevHairStyleButton); + add(mHairStyleLabel); + for (int i = 0; i < 6; i++) + { + add(mAttributeSlider[i]); + add(mAttributeValue[i]); + add(mAttributeLabel[i]); + }; + add(mAttributesLeft); + add(mCreateButton); + add(mCancelButton); + + add(mMale); + add(mFemale); + +#else + ContainerPlacer place; place = getPlacer(0, 0); @@ -305,6 +541,7 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, place(5, 0, mCreateButton); reflowLayout(225, 0); +#endif setLocationRelativeTo(getParent()); setVisible(true); @@ -328,12 +565,35 @@ void CharCreateDialog::action(const gcn::ActionEvent &event) { // Attempt to create the character mCreateButton->setEnabled(false); +#ifdef TMWSERV_SUPPORT + unsigned int genderSelected; + if (mMale->isSelected()) { + genderSelected = GENDER_MALE; + } else { + genderSelected = GENDER_FEMALE; + } + + Net::AccountServer::Account::createCharacter( + getName(), + mPlayer->getHairStyle(), + mPlayer->getHairColor(), + genderSelected, // gender + (int) mAttributeSlider[0]->getValue(), // STR + (int) mAttributeSlider[1]->getValue(), // AGI + (int) mAttributeSlider[2]->getValue(), // DEX + (int) mAttributeSlider[3]->getValue(), // VIT + (int) mAttributeSlider[4]->getValue(), // INT + (int) mAttributeSlider[5]->getValue() // WILL + ); +#else attemptCharCreate(); +#endif } else { - new OkDialog("Error", - "Your name needs to be at least 4 characters.", this); + new OkDialog(_("Error"), + _("Your name needs to be at least 4 characters."), + this); } } else if (event.getId() == "cancel") @@ -352,6 +612,18 @@ void CharCreateDialog::action(const gcn::ActionEvent &event) mPlayer->setHairStyle(mPlayer->getHairStyle() + mPlayer->getNumOfHairstyles() - 1, mPlayer->getHairColor()); +#ifdef TMWSERV_SUPPORT + else if (event.getId() == "statslider") { + UpdateSliders(); + } + else if (event.getId() == "gender"){ + if (mMale->isSelected()) { + mPlayer->setGender(GENDER_MALE); + } else { + mPlayer->setGender(GENDER_FEMALE); + } + } +#endif } std::string CharCreateDialog::getName() @@ -361,11 +633,60 @@ std::string CharCreateDialog::getName() return name; } +#ifdef TMWSERV_SUPPORT +void CharCreateDialog::UpdateSliders() +{ + for (int i = 0; i < 6; i++) + { + // Update captions + mAttributeValue[i]->setCaption( + toString((int) (mAttributeSlider[i]->getValue()))); + mAttributeValue[i]->adjustSize(); + } + + // Update distributed points + int pointsLeft = 60 - getDistributedPoints(); + if (pointsLeft == 0) + { + mAttributesLeft->setCaption(_("Character stats OK")); + mCreateButton->setEnabled(true); + } + else + { + mCreateButton->setEnabled(false); + if (pointsLeft > 0) + { + mAttributesLeft->setCaption(strprintf(_("Please distribute %d points"), pointsLeft)); + } + else + { + mAttributesLeft->setCaption(strprintf(_("Please remove %d points"), -pointsLeft)); + } + } + + mAttributesLeft->adjustSize(); +} +#endif + void CharCreateDialog::unlock() { mCreateButton->setEnabled(true); } +#ifdef TMWSERV_SUPPORT +int CharCreateDialog::getDistributedPoints() +{ + int points = 0; + + for (int i = 0; i < 6; i++) + { + points += (int) mAttributeSlider[i]->getValue(); + } + return points; +} +#endif + +#ifndef TMWSERV_SUPPORT void CharCreateDialog::attemptCharCreate() { // Send character infos @@ -382,3 +703,4 @@ void CharCreateDialog::attemptCharCreate() outMsg.writeInt16(mPlayer->getHairColor()); outMsg.writeInt16(mPlayer->getHairStyle()); } +#endif diff --git a/src/gui/char_select.h b/src/gui/char_select.h index 28091a18..0ff1d18f 100644 --- a/src/gui/char_select.h +++ b/src/gui/char_select.h @@ -27,10 +27,16 @@ #include "window.h" #include "../being.h" +#include "../guichanfwd.h" #include "../lockedarray.h" -class LocalPlayer; +#ifdef TMWSERV_SUPPORT +#include "../logindata.h" +#else class Network; +#endif + +class LocalPlayer; class Player; class PlayerBox; @@ -46,9 +52,14 @@ class CharSelectDialog : public Window, public gcn::ActionListener /** * Constructor. */ +#ifdef TMWSERV_SUPPORT + CharSelectDialog(LockedArray<LocalPlayer*> *charInfo, + LoginData *loginData); +#else CharSelectDialog(Network *network, LockedArray<LocalPlayer*> *charInfo, Gender gender); +#endif void action(const gcn::ActionEvent &event); @@ -59,25 +70,40 @@ class CharSelectDialog : public Window, public gcn::ActionListener bool selectByName(const std::string &name); private: +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif LockedArray<LocalPlayer*> *mCharInfo; gcn::Button *mSelectButton; gcn::Button *mCancelButton; - gcn::Button *mNewDelCharButton; gcn::Button *mPreviousButton; gcn::Button *mNextButton; gcn::Label *mNameLabel; gcn::Label *mLevelLabel; - gcn::Label *mJobLevelLabel; - gcn::Label *mMoneyLabel; std::string mMoney; + gcn::Label *mMoneyLabel; + std::string mMoney; PlayerBox *mPlayerBox; - Gender mGender; bool mCharSelected; +#ifdef TMWSERV_SUPPORT + gcn::Button *mNewCharButton; + gcn::Button *mDelCharButton; + gcn::Button *mUnRegisterButton; + gcn::Button *mChangePasswordButton; + gcn::Button *mChangeEmailButton; + gcn::Label *mAccountNameLabel; + + LoginData *mLoginData; +#else + gcn::Button *mNewDelCharButton; + gcn::Label *mJobLevelLabel; + Gender mGender; +#endif + /** * Communicate character deletion to the server. */ @@ -92,7 +118,7 @@ class CharSelectDialog : public Window, public gcn::ActionListener /** * Character creation dialog. * - * \ingroup GUI + * \ingroup Interface */ class CharCreateDialog : public Window, public gcn::ActionListener { @@ -100,8 +126,12 @@ class CharCreateDialog : public Window, public gcn::ActionListener /** * Constructor. */ +#ifdef TMWSERV_SUPPORT + CharCreateDialog(Window *parent, int slot); +#else CharCreateDialog(Window *parent, int slot, Network *network, Gender gender); +#endif /** * Destructor. @@ -116,6 +146,12 @@ class CharCreateDialog : public Window, public gcn::ActionListener void unlock(); private: +#ifdef TMWSERV_SUPPORT + int getDistributedPoints(); + + void UpdateSliders(); +#endif + /** * Returns the name of the character to create. */ @@ -126,7 +162,9 @@ class CharCreateDialog : public Window, public gcn::ActionListener */ void attemptCharCreate(); +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif gcn::TextField *mNameField; gcn::Label *mNameLabel; gcn::Button *mNextHairColorButton; @@ -135,6 +173,20 @@ class CharCreateDialog : public Window, public gcn::ActionListener gcn::Button *mNextHairStyleButton; gcn::Button *mPrevHairStyleButton; gcn::Label *mHairStyleLabel; + +#ifdef TMWSERV_SUPPORT + gcn::RadioButton *mMale; + gcn::RadioButton *mFemale; + + gcn::Slider *mAttributeSlider[6]; + gcn::Label *mAttributeLabel[6]; + gcn::Label *mAttributeValue[6]; + gcn::Label *mAttributesLeft; + + static const int mMaxPoints = 60; + int mUsedPoints; +#endif + gcn::Button *mCreateButton; gcn::Button *mCancelButton; diff --git a/src/gui/char_server.cpp b/src/gui/char_server.cpp index 89bfa93a..fa03bdc2 100644 --- a/src/gui/char_server.cpp +++ b/src/gui/char_server.cpp @@ -107,7 +107,7 @@ void ServerSelectDialog::action(const gcn::ActionEvent &event) state = mNextState; } else if (event.getId() == "cancel") - state = LOGIN_STATE; + state = STATE_LOGIN; } int ServerListModel::getNumberOfElements() diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp index ac13355c..37250091 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -31,25 +31,44 @@ #include "windowcontainer.h" #include "widgets/layout.h" +#include "widgets/tab.h" +#include "widgets/tabbedarea.h" #include "../beingmanager.h" +#ifdef TMWSERV_SUPPORT +#include "../commandhandler.h" +#endif +#include "../channelmanager.h" +#include "../channel.h" #include "../configuration.h" #include "../game.h" #include "../localplayer.h" -#include "../party.h" +#ifdef TMWSERV_SUPPORT +#include "../net/chatserver/chatserver.h" +#include "../net/gameserver/player.h" +#else +#include "../party.h" #include "../net/messageout.h" -#include "../net/protocol.h" +#include "../net/ea/protocol.h" +#endif #include "../resources/iteminfo.h" #include "../resources/itemdb.h" +#include "../utils/dtor.h" #include "../utils/gettext.h" #include "../utils/strprintf.h" #include "../utils/stringutils.h" +#ifdef TMWSERV_SUPPORT +ChatWindow::ChatWindow(): + Window("Chat"), +#else ChatWindow::ChatWindow(Network * network): -Window(""), mNetwork(network), mTmpVisible(false) + Window(""), mNetwork(network), +#endif + mTmpVisible(false) { setWindowName("Chat"); @@ -64,18 +83,21 @@ Window(""), mNetwork(network), mTmpVisible(false) mChatInput->setActionEventId("chatinput"); mChatInput->addActionListener(this); - mTextOutput = new BrowserBox(BrowserBox::AUTO_WRAP); - mTextOutput->setOpaque(false); - mTextOutput->setMaxRow((int) config.getValue("ChatLogLength", 0)); - mTextOutput->setLinkHandler(mItemLinkHandler); + BrowserBox *textOutput = new BrowserBox(BrowserBox::AUTO_WRAP); + textOutput->setOpaque(false); + textOutput->setMaxRow((int) config.getValue("ChatLogLength", 0)); + textOutput->setLinkHandler(mItemLinkHandler); - mScrollArea = new ScrollArea(mTextOutput); - mScrollArea->setScrollPolicy(gcn::ScrollArea::SHOW_NEVER, + ScrollArea *scrollArea = new ScrollArea(textOutput); + scrollArea->setScrollPolicy(gcn::ScrollArea::SHOW_NEVER, gcn::ScrollArea::SHOW_ALWAYS); - mScrollArea->setScrollAmount(0, 1); - mScrollArea->setOpaque(false); + scrollArea->setScrollAmount(0, 1); + scrollArea->setOpaque(false); + + mChatTabs = new TabbedArea(); + mChatTabs->addTab("General", scrollArea); - place(0, 0, mScrollArea, 5, 5).setPadding(0); + place(0, 0, mChatTabs, 5, 5).setPadding(0); place(0, 5, mChatInput, 5).setPadding(1); Layout &layout = getLayout(); @@ -88,6 +110,7 @@ Window(""), mNetwork(network), mTmpVisible(false) mChatInput->addKeyListener(this); mCurHist = mHistory.end(); +#ifdef EATHENA_SUPPORT // Read the party prefix std::string partyPrefix = config.getValue("PartyPrefix", "$"); mPartyPrefix = (partyPrefix.empty() ? '$' : partyPrefix.at(0)); @@ -99,19 +122,75 @@ Window(""), mNetwork(network), mTmpVisible(false) // run the @assert command for the player again. Convenience for GMs. if (config.getValue(player_node->getName() + "GMassert", 0)) chatSend(player_node->getName(), "@assert"); +#endif } ChatWindow::~ChatWindow() { +#ifdef EATHENA_SUPPORT char partyPrefix[2] = "."; *partyPrefix = mPartyPrefix; config.setValue("PartyPrefix", partyPrefix); config.setValue("ReturnToggles", mReturnToggles ? "1" : "0"); delete mRecorder; +#endif +} + +void ChatWindow::widgetResized(const gcn::Event &event) +{ + Window::widgetResized(event); + + const gcn::Rectangle area = getChildrenArea(); + + mChatInput->setPosition(mChatInput->getFrameSize(), + area.height - mChatInput->getHeight() - + mChatInput->getFrameSize()); + mChatInput->setWidth(area.width - 2 * mChatInput->getFrameSize()); + + mChatTabs->setWidth(area.width - 2 * mChatTabs->getFrameSize()); + mChatTabs->setHeight(area.height - 2 * mChatTabs->getFrameSize()); + + const std::string &channelName = getFocused(); + ChannelMap::const_iterator chan = mChannels.find(channelName); + if (chan != mChannels.end()) { + ScrollArea *scroll = chan->second.scroll; + scroll->setWidth(area.width - 2 * scroll->getFrameSize()); + scroll->setHeight(area.height - 2 * scroll->getFrameSize() - + mChatInput->getHeight() - 5); + scroll->logic(); + } +} + +void ChatWindow::logic() +{ + Window::logic(); + + const gcn::Rectangle area = getChildrenArea(); + + const std::string &channelName = getFocused(); + ChannelMap::const_iterator chan = mChannels.find(channelName); + if (chan != mChannels.end()) { + ScrollArea *scroll = chan->second.scroll; + scroll->setWidth(area.width - 2 * scroll->getFrameSize()); + scroll->setHeight(area.height - 2 * scroll->getFrameSize() - + mChatInput->getHeight() - 5); + scroll->logic(); + } } -void ChatWindow::chatLog(std::string line, int own, bool ignoreRecord) +void ChatWindow::chatLog(std::string line, int own, std::string channelName, + bool ignoreRecord) { + if(channelName.empty()) + channelName = getFocused(); + + ChannelMap::const_iterator chan = mChannels.find(channelName); + if (chan == mChannels.end()) + return; + + BrowserBox * const output = chan->second.browser; + ScrollArea * const scroll = chan->second.scroll; + // Trim whitespace trim(line); @@ -180,10 +259,17 @@ void ChatWindow::chatLog(std::string line, int own, bool ignoreRecord) tmp.text = line; lineColor = "##S"; break; + case BY_CHANNEL: + tmp.nick = ""; + // TODO: Use a predefined color + lineColor = "##2"; // Equiv. to BrowserBox::GREEN + break; +#ifdef EATHENA_SUPPORT case BY_PARTY: tmp.nick += CAT_NORMAL; lineColor = "##P"; break; +#endif case ACT_WHISPER: tmp.nick = strprintf(_("%s whispers:"), tmp.nick.c_str()); tmp.nick += " "; @@ -206,10 +292,12 @@ void ChatWindow::chatLog(std::string line, int own, bool ignoreRecord) lineColor = "##S"; } +#ifdef EATHENA_SUPPORT if (tmp.nick.empty() && tmp.text.substr(0, 17) == "Visible GM status") { player_node->setGM(); } +#endif // Get the current system time time_t t; @@ -228,25 +316,38 @@ void ChatWindow::chatLog(std::string line, int own, bool ignoreRecord) // We look if the Vertical Scroll Bar is set at the max before // adding a row, otherwise the max will always be a row higher // at comparison. - if (mScrollArea->getVerticalScrollAmount() == - mScrollArea->getVerticalMaxScroll()) + if (scroll->getVerticalScrollAmount() >= scroll->getVerticalMaxScroll()) { - mTextOutput->addRow(line); - mScrollArea->setVerticalScrollAmount(mScrollArea-> - getVerticalMaxScroll()); + output->addRow(line); + scroll->setVerticalScrollAmount(scroll->getVerticalMaxScroll()); } else { - mTextOutput->addRow(line); + output->addRow(line); } + scroll->logic(); mRecorder->record(line.substr(3)); } +#ifdef EATHENA_SUPPORT void ChatWindow::chatLog(CHATSKILL act) { chatLog(const_msg(act), BY_SERVER); } +#endif + +const std::string &ChatWindow::getFocused() const +{ + return mChatTabs->getSelectedTab()->getCaption(); +} + +void ChatWindow::clearTab(const std::string &tab) +{ + ChannelMap::const_iterator chan = mChannels.find(tab); + if (chan != mChannels.end()) + chan->second.browser->clearRows(); +} void ChatWindow::action(const gcn::ActionEvent &event) { @@ -265,7 +366,11 @@ void ChatWindow::action(const gcn::ActionEvent &event) mCurHist = mHistory.end(); // Send the message to the server +#ifdef TMWSERV_SUPPORT + chatSend(message); +#else chatSend(player_node->getName(), message); +#endif // Clear the text from the chat input mChatInput->setText(""); @@ -343,6 +448,8 @@ void ChatWindow::whisper(const std::string &nick, std::string msg) if (tempNick.compare(playerName) == 0 || msg.empty()) return; + // TODO: Implement whispering on tmwserv +#ifdef EATHENA_SUPPORT MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_CHAT_WHISPER); outMsg.writeInt16(msg.length() + 28); @@ -352,8 +459,123 @@ void ChatWindow::whisper(const std::string &nick, std::string msg) chatLog(strprintf(_("Whispering to %s: %s"), recvnick.c_str(), msg.c_str()), BY_PLAYER); +#endif +} + +#ifdef TMWSERV_SUPPORT + +void ChatWindow::chatSend(std::string &msg) +{ + if (msg.empty()) return; + + // check for item link + std::string::size_type start = msg.find('['); + if (start != std::string::npos && msg[start+1] != '@') + { + std::string::size_type end = msg.find(']', start); + if (end != std::string::npos) + { + std::string temp = msg.substr(start+1, end-1); + ItemInfo itemInfo = ItemDB::get(temp); + msg.insert(end, "@@"); + msg.insert(start+1, "|"); + msg.insert(start+1, toString(itemInfo.getId())); + msg.insert(start+1, "@@"); + + } + } + + + // Prepare ordinary message + if (msg[0] != '/') + { + if (getFocused() == "General") + { + Net::GameServer::Player::say(msg); + } + else + { + Channel *channel = channelManager->findByName(getFocused()); + if (channel) + { + Net::ChatServer::chat(channel->getId(), msg); + } + } + } + else + { + commandHandler->handleCommand(std::string(msg, 1)); + } +} + +void ChatWindow::removeChannel(short channelId) +{ + removeChannel(channelManager->findById(channelId)); +} + +void ChatWindow::removeChannel(const std::string &channelName) +{ + removeChannel(channelManager->findByName(channelName)); +} + +void ChatWindow::removeChannel(Channel *channel) +{ + if (channel) + { + Tab *tab = mChatTabs->getTab(channel->getName()); + if (!tab) + return; + clearTab(channel->getName()); + mChatTabs->removeTab(tab); + mChannels.erase(channel->getName()); + channelManager->removeChannel(channel); + + logic(); + } +} + +void ChatWindow::createNewChannelTab(const std::string &channelName) +{ + // Create new channel + BrowserBox *textOutput = new BrowserBox(BrowserBox::AUTO_WRAP); + textOutput->setOpaque(false); + textOutput->disableLinksAndUserColors(); + textOutput->setMaxRow((int) config.getValue("ChatLogLength", 0)); + ScrollArea *scrollArea = new ScrollArea(textOutput); + scrollArea->setPosition(scrollArea->getFrameSize(), scrollArea->getFrameSize()); + scrollArea->setScrollPolicy(gcn::ScrollArea::SHOW_NEVER, gcn::ScrollArea::SHOW_ALWAYS); + scrollArea->setOpaque(false); + scrollArea->setWidth(getChildrenArea().width - 2 * scrollArea->getFrameSize()); + scrollArea->setHeight(getChildrenArea().height - 2 * scrollArea->getFrameSize() - + mChatInput->getHeight() - 5); + scrollArea->logic(); + textOutput->setWidth(scrollArea->getChildrenArea().width); + textOutput->setHeight(scrollArea->getChildrenArea().height); + + // Add channel to the tabbed area + mChatTabs->addTab(channelName, scrollArea); + mChannels.insert( + std::make_pair(channelName, ChatArea(textOutput, scrollArea))); + + // Update UI + logic(); +} + +void ChatWindow::sendToChannel(short channelId, + const std::string &user, + const std::string &msg) +{ + Channel *channel = channelManager->findById(channelId); + if (channel) + { + std::string channelName = channel->getName(); + chatLog(user + ": " + msg, user == player_node->getName() ? BY_PLAYER : BY_OTHER, channelName); + mChatTabs->getTab(channelName)->setHighlighted(true); + } } +#else + void ChatWindow::chatSend(const std::string &nick, std::string msg) { /* Some messages are managed client side, while others @@ -488,7 +710,7 @@ void ChatWindow::chatSend(const std::string &nick, std::string msg) outMsg.writeInt16(0x00c1); } else if (command == "clear") - mTextOutput->clearRows(); + clearTab(getFocused()); else if (command == "whisper" || command == "msg" || command == "w") whisper(nick, msg); else if (command == "record") @@ -608,7 +830,7 @@ void ChatWindow::chatSend(const std::string &nick, std::string msg) mRecorder->record(timeStr.str() + _("Present: ") + response + "."); - chatLog(_("Attendance written to record log."), BY_SERVER, true); + chatLog(_("Attendance written to record log."), BY_SERVER, std::string(), true); } else { @@ -712,16 +934,25 @@ std::string ChatWindow::const_msg(CHATSKILL act) return msg; } +#endif + void ChatWindow::scroll(int amount) { if (!isVisible()) return; - int range = mScrollArea->getHeight() / 8 * amount; + ChannelMap::const_iterator chan = mChannels.find(getFocused()); + if (chan == mChannels.end()) + return; + + BrowserBox *browser = chan->second.browser; + ScrollArea *scroll = chan->second.scroll; + + int range = scroll->getHeight() / 8 * amount; gcn::Rectangle scr; - scr.y = mScrollArea->getVerticalScrollAmount() + range; + scr.y = scroll->getVerticalScrollAmount() + range; scr.height = abs(range); - mTextOutput->showPart(scr); + browser->showPart(scr); } void ChatWindow::keyPressed(gcn::KeyEvent &event) @@ -774,10 +1005,10 @@ void ChatWindow::setVisible(bool isVisible) * For whatever reason, if setVisible is called, the mTmpVisible effect * should be disabled. */ - mTmpVisible = false; } +#ifdef EATHENA_SUPPORT void ChatWindow::party(const std::string & command, const std::string & rest) { if (command == "prefix") @@ -809,6 +1040,7 @@ void ChatWindow::party(const std::string & command, const std::string & rest) else mParty->respond(command, rest); } +#endif void ChatWindow::help(const std::string & msg1, const std::string & msg2) { @@ -861,10 +1093,12 @@ void ChatWindow::help(const std::string & msg1, const std::string & msg2) chatLog(_("This command tell others you are (doing) <msg>."), BY_SERVER); } +#ifdef EATHENA_SUPPORT else if (msg1 == "party") { mParty->help(msg2); } +#endif else if (msg1 == "present") { chatLog(_("Command: /present"), BY_SERVER); @@ -921,3 +1155,9 @@ void ChatWindow::help(const std::string & msg1, const std::string & msg2) chatLog(_("Type /help for a list of commands."), BY_SERVER); } } + +bool ChatWindow::tabExists(const std::string &tabName) +{ + Tab *tab = mChatTabs->getTab(tabName); + return tab != 0; +} diff --git a/src/gui/chat.h b/src/gui/chat.h index 2fadb014..bbca76ad 100644 --- a/src/gui/chat.h +++ b/src/gui/chat.h @@ -24,6 +24,7 @@ #include <list> #include <string> +#include <map> #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> @@ -31,22 +32,30 @@ #include "window.h" class BrowserBox; -class Network; +class Channel; class Recorder; -class Party; class ScrollArea; +class TabbedArea; class ItemLinkHandler; +#ifdef EATHENA_SUPPORT +class Network; +class Party; +#endif -#define BY_GM 0 // those should be self-explanatory =) -#define BY_PLAYER 1 -#define BY_OTHER 2 -#define BY_SERVER 3 -#define BY_PARTY 4 - -#define ACT_WHISPER 5 // getting whispered at -#define ACT_IS 6 // equivalent to "/me" on IRC - -#define BY_LOGGER 7 +enum +{ + BY_GM, +#ifdef EATHENA_SUPPORT + BY_PARTY, +#endif + BY_PLAYER, + BY_OTHER, + BY_SERVER, + BY_CHANNEL, + ACT_WHISPER, // getting whispered at + ACT_IS, // equivalent to "/me" on IRC + BY_LOGGER +}; /** * gets in between usernick and message text depending on @@ -56,6 +65,7 @@ class ItemLinkHandler; #define CAT_IS "" #define CAT_WHISPER " whispers: " +#ifdef EATHENA_SUPPORT /** job dependend identifiers (?) */ #define SKILL_BASIC 0x0001 #define SKILL_WARP 0x001b @@ -88,8 +98,6 @@ class ItemLinkHandler; /** should always be zero if failed */ #define SKILL_FAILED 0x00 -#define DEFAULT_CHAT_WINDOW_SCROLL 7 // 1 means `1/8th of the window size'. - struct CHATSKILL { short skill; @@ -98,20 +106,28 @@ struct CHATSKILL char success; char reason; }; +#endif + +#define DEFAULT_CHAT_WINDOW_SCROLL 7 // 1 means `1/8th of the window size'. /** * The chat window. * * \ingroup Interface */ -class ChatWindow : public Window, public gcn::ActionListener, +class ChatWindow : public Window, + public gcn::ActionListener, public gcn::KeyListener { public: /** * Constructor. */ +#ifdef TMWSERV_SUPPORT + ChatWindow(); +#else ChatWindow(Network *network); +#endif /** * Destructor: used to write back values to the config file @@ -119,17 +135,41 @@ class ChatWindow : public Window, public gcn::ActionListener, ~ChatWindow(); /** + * Called when the widget changes size. Used for adapting the size of + * the tabbed area. + */ + void widgetResized(const gcn::Event &event); + + void logic(); + + /** * Adds a line of text to our message list. Parameters: * * @param line Text message. * @parem own Type of message (usually the owner-type). + * @param channelName which channel to send the message to. */ - void chatLog(std::string line, int own, bool ignoreRecord = false); + void chatLog(std::string line, + int own = BY_SERVER, + std::string channelName = "", + bool ignoreRecord = false); +#ifdef EATHENA_SUPPORT /** * Calls original chat_log() after processing the packet. */ void chatLog(CHATSKILL); +#endif + + /** + * Gets the focused tab's name + */ + const std::string& getFocused() const; + + /** + * Clear the tab with the given name + */ + void clearTab(const std::string &tab); /** * Performs action. @@ -146,6 +186,32 @@ class ChatWindow : public Window, public gcn::ActionListener, */ bool isInputFocused(); +#ifdef TMWSERV_SUPPORT + /** + * Determines whether the message is a command or message, then + * sends the given message to the game server to be said, or to the + * command handler + * + * @param msg The message text which is to be sent. + * + */ + void chatSend(std::string &msg); + + /** Called to remove the channel from the channel manager */ + void removeChannel(short channelId); + + void removeChannel(const std::string &channelName); + + void removeChannel(Channel *channel); + + /** Called to create a new channel tab */ + void createNewChannelTab(const std::string &channelName); + + /** Called to output text to a specific channel */ + void sendToChannel(short channel, + const std::string &user, + const std::string &msg); +#else /** * Determines whether to send a command or an ordinary message, then * contructs packets & sends them. @@ -171,6 +237,7 @@ class ChatWindow : public Window, public gcn::ActionListener, * chatlog.chat_send("Zaeiru", "Hello to all users on the screen!"); */ void chatSend(const std::string &nick, std::string msg); +#endif /** Called when key is pressed */ void keyPressed(gcn::KeyEvent &event); @@ -184,15 +251,19 @@ class ChatWindow : public Window, public gcn::ActionListener, /** Override to reset mTmpVisible */ void setVisible(bool visible); - /** - * Scrolls the chat window - * - * @param amount direction and amount to scroll. Negative numbers scroll - * up, positive numbers scroll down. The absolute amount indicates the - * amount of 1/8ths of chat window real estate that should be scrolled. - */ + /** Check if tab with that name already exists */ + bool tabExists(const std::string &tabName); + + /** + * Scrolls the chat window + * + * @param amount direction and amount to scroll. Negative numbers scroll + * up, positive numbers scroll down. The absolute amount indicates the + * amount of 1/8ths of chat window real estate that should be scrolled. + */ void scroll(int amount); +#ifdef EATHENA_SUPPORT /** * party implements the partying chat commands * @@ -200,6 +271,7 @@ class ChatWindow : public Window, public gcn::ActionListener, * @param msg is the remainder of the message */ void party(const std::string &command, const std::string &msg); +#endif /** * help implements the /help command @@ -210,10 +282,14 @@ class ChatWindow : public Window, public gcn::ActionListener, void help(const std::string &msg1, const std::string &msg2); private: - +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif bool mTmpVisible; + int mItems; + int mItemsKeep; + void whisper(const std::string &nick, std::string msg); /** One item in the chat log */ @@ -224,25 +300,51 @@ class ChatWindow : public Window, public gcn::ActionListener, int own; }; +#ifdef EATHENA_SUPPORT /** Constructs failed messages for actions */ std::string const_msg(CHATSKILL); +#endif + + /** + * A structure combining a BrowserBox with its ScrollArea. + */ + struct ChatArea + { + ChatArea(BrowserBox *b, ScrollArea *s): + browser(b), scroll(s) + {} + + BrowserBox *browser; + ScrollArea *scroll; + }; + + /** Used for showing item popup on clicking links **/ + ItemLinkHandler *mItemLinkHandler; + + /** Tabbed area for holding each channel. */ + TabbedArea *mChatTabs; + + /** Input box for typing chat messages. */ + gcn::TextField *mChatInput; + + typedef std::map<const std::string, ChatArea> ChannelMap; + /** Map each tab to its browser and scroll area. */ + ChannelMap mChannels; - gcn::TextField *mChatInput; /**< Input box for typing chat messages */ - BrowserBox *mTextOutput; /**< Text box for displaying chat history */ - ScrollArea *mScrollArea; /**< Scroll area around text output */ - ItemLinkHandler *mItemLinkHandler; /** Used for showing item popup on - clicking links **/ typedef std::list<std::string> History; typedef History::iterator HistoryIterator; - History mHistory; /**< Command history */ - HistoryIterator mCurHist; /**< History iterator */ + History mHistory; /**< Command history. */ + HistoryIterator mCurHist; /**< History iterator. */ Recorder *mRecorder; /**< Recording class */ - char mPartyPrefix; /**< Messages beginning with the prefix are sent to - the party */ bool mReturnToggles; /**< Marks whether <Return> toggles the chat log or not */ +#ifdef EATHENA_SUPPORT + char mPartyPrefix; /**< Messages beginning with the prefix are sent to + the party */ Party *mParty; +#endif }; + extern ChatWindow *chatWindow; #endif diff --git a/src/gui/chatinput.h b/src/gui/chatinput.h index a4a50502..3bc16928 100644 --- a/src/gui/chatinput.h +++ b/src/gui/chatinput.h @@ -26,6 +26,8 @@ #include "textfield.h" +#include <guichan/focuslistener.hpp> + /** * The chat input hides when it loses focus. It is also invisible by default. */ diff --git a/src/gui/connection.cpp b/src/gui/connection.cpp index a69698e9..fbf127de 100644 --- a/src/gui/connection.cpp +++ b/src/gui/connection.cpp @@ -19,8 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/actionlistener.hpp> - #include <guichan/widgets/label.hpp> #include "button.h" @@ -28,23 +26,16 @@ #include "progressbar.h" #include "../main.h" +#include "../log.h" #include "../utils/gettext.h" -namespace -{ - struct ConnectionActionListener : public gcn::ActionListener - { - void action(const gcn::ActionEvent &event) { state = EXIT_STATE; } - } listener; -} - -ConnectionDialog::ConnectionDialog(): - Window("Info"), mProgress(0) +ConnectionDialog::ConnectionDialog(int previousState): + Window("Info"), mProgress(0), mPreviousState(previousState) { setContentSize(200, 100); - Button *cancelButton = new Button(_("Cancel"), "cancelButton", &listener); + Button *cancelButton = new Button(_("Cancel"), "cancelButton", this); mProgressBar = new ProgressBar(0.0, 200 - 10, 20, 128, 128, 128); gcn::Label *label = new gcn::Label(_("Connecting...")); @@ -60,6 +51,12 @@ ConnectionDialog::ConnectionDialog(): setVisible(true); } +void ConnectionDialog::action(gcn::ActionEvent const &) +{ + logger->log("Cancel pressed"); + state = mPreviousState; +} + void ConnectionDialog::logic() { mProgress += 0.005f; diff --git a/src/gui/connection.h b/src/gui/connection.h index 3caa611f..0e347266 100644 --- a/src/gui/connection.h +++ b/src/gui/connection.h @@ -22,6 +22,8 @@ #ifndef CONNECTION_H #define CONNECTION_H +#include <guichan/actionlistener.hpp> + #include "window.h" class ProgressBar; @@ -31,7 +33,7 @@ class ProgressBar; * * \ingroup Interface */ -class ConnectionDialog : public Window +class ConnectionDialog : public Window, gcn::ActionListener { public: /** @@ -39,13 +41,20 @@ class ConnectionDialog : public Window * * @see Window::Window */ - ConnectionDialog(); + ConnectionDialog(int previousState); + + /** + * Called when the user presses Cancel. Restores the global state to + * the previous one. + */ + void action(gcn::ActionEvent const &); void logic(); private: ProgressBar *mProgressBar; float mProgress; + int mPreviousState; }; #endif diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index 02a0729b..a44ae3ec 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -36,6 +36,7 @@ #include "../item.h" #include "../localplayer.h" +#include "../resources/image.h" #include "../resources/iteminfo.h" #include "../resources/resourcemanager.h" @@ -57,8 +58,16 @@ static const int boxPosition[][2] = { {129, 78} // EQUIP_AMMO_SLOT }; +#ifdef TMWSERV_SUPPORT +EquipmentWindow::EquipmentWindow(Equipment *equipment): +#else EquipmentWindow::EquipmentWindow(): +#endif Window(_("Equipment")), +#ifdef TMWSERV_SUPPORT + mEquipment(equipment), +#endif + mBackground(NULL), mSelected(-1) { mItemPopup = new ItemPopup; @@ -81,19 +90,31 @@ EquipmentWindow::EquipmentWindow(): add(mPlayerBox); add(mUnequip); +#ifdef TMWSERV_SUPPORT + for (int i = 0; i < EQUIPMENT_SIZE; i++) +#else for (int i = EQUIP_LEGS_SLOT; i < EQUIP_VECTOREND; i++) +#endif { mEquipBox[i].posX = boxPosition[i][0] + getPadding(); mEquipBox[i].posY = boxPosition[i][1] + getTitleBarHeight(); } + ResourceManager *resman = ResourceManager::getInstance(); + mBackground = resman->getImage("graphics/gui/equip_bg.png"); + mBackground->setAlpha(0.3); + +#ifdef EATHENA_SUPPORT mEquipment = player_node->mEquipment.get(); mInventory = player_node->getInventory(); +#endif } EquipmentWindow::~EquipmentWindow() { + mBackground->decRef(); delete mItemPopup; + delete mUnequip; } void EquipmentWindow::draw(gcn::Graphics *graphics) @@ -101,22 +122,29 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) // Draw window graphics Window::draw(graphics); - Item* item; - Graphics *g = static_cast<Graphics*>(graphics); Window::drawChildren(graphics); +#ifdef TMWSERV_SUPPORT + for (int i = 0; i < EQUIPMENT_SIZE; i++) +#else for (int i = EQUIP_LEGS_SLOT; i < EQUIP_VECTOREND; i++) +#endif { - item = (i != EQUIP_AMMO_SLOT) ? +#ifdef TMWSERV_SUPPORT + Item *item = mEquipment->getEquipment(i); +#else + Item *item = (i != EQUIP_AMMO_SLOT) ? mInventory->getItem(mEquipment->getEquipment(i)) : mInventory->getItem(mEquipment->getArrows()); +#endif if (item) { // Draw Item. - Image* image = item->getImage(); + Image *image = item->getImage(); g->drawImage(image, mEquipBox[i].posX, mEquipBox[i].posY); +#ifdef EATHENA_SUPPORT if (i == EQUIP_AMMO_SLOT) { g->setColor(gcn::Color(0, 0, 0)); @@ -125,6 +153,7 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) mEquipBox[i].posY - getFont()->getHeight(), gcn::Graphics::CENTER); } +#endif } if (i == mSelected) @@ -148,26 +177,38 @@ void EquipmentWindow::action(const gcn::ActionEvent &event) { if (event.getId() == "unequip" && mSelected > -1) { +#ifdef TMWSERV_SUPPORT + player_node->unequipItem(mSelected); +#else Item* item = (mSelected != EQUIP_AMMO_SLOT) ? mInventory->getItem(mEquipment->getEquipment(mSelected)) : mInventory->getItem(mEquipment->getArrows()); player_node->unequipItem(item); +#endif mSelected = -1; } } Item* EquipmentWindow::getItem(int x, int y) const { +#ifdef TMWSERV_SUPPORT + for (int i = 0; i < EQUIPMENT_SIZE; i++) +#else for (int i = EQUIP_LEGS_SLOT; i < EQUIP_VECTOREND; i++) +#endif { gcn::Rectangle tRect(mEquipBox[i].posX, mEquipBox[i].posY, BOX_WIDTH, BOX_HEIGHT); if (tRect.isPointInRect(x, y)) { +#ifdef TMWSERV_SUPPORT + return mEquipment->getEquipment(i); +#else return (i != EQUIP_AMMO_SLOT) ? mInventory->getItem(mEquipment->getEquipment(i)) : mInventory->getItem(mEquipment->getArrows()); +#endif } } return NULL; @@ -185,11 +226,19 @@ void EquipmentWindow::mousePressed(gcn::MouseEvent& mouseEvent) if (mouseEvent.getButton() == gcn::MouseEvent::LEFT) { // Checks if any of the presses were in the equip boxes. +#ifdef TMWSERV_SUPPORT + for (int i = 0; i < EQUIPMENT_SIZE; i++) +#else for (int i = EQUIP_LEGS_SLOT; i < EQUIP_VECTOREND; i++) +#endif { +#ifdef TMWSERV_SUPPORT + item = mEquipment->getEquipment(i); +#else item = (i != EQUIP_AMMO_SLOT) ? mInventory->getItem(mEquipment->getEquipment(i)) : mInventory->getItem(mEquipment->getArrows()); +#endif gcn::Rectangle tRect(mEquipBox[i].posX, mEquipBox[i].posY, BOX_WIDTH, BOX_HEIGHT); diff --git a/src/gui/equipmentwindow.h b/src/gui/equipmentwindow.h index 24438477..7f7150ff 100644 --- a/src/gui/equipmentwindow.h +++ b/src/gui/equipmentwindow.h @@ -27,6 +27,7 @@ #include "window.h" class Equipment; +class Image; class Inventory; class Item; class ItemPopup; @@ -52,7 +53,11 @@ class EquipmentWindow : public Window, public gcn::ActionListener /** * Constructor. */ +#ifdef TMWSERV_SUPPORT + EquipmentWindow(Equipment *equipment); +#else EquipmentWindow(); +#endif /** * Destructor. @@ -68,6 +73,23 @@ class EquipmentWindow : public Window, public gcn::ActionListener void mousePressed(gcn::MouseEvent& mouseEvent); +#ifdef TMWSERV_SUPPORT + enum{ + // Equipment rules: + EQUIP_TORSO_SLOT = 0, + EQUIP_ARMS_SLOT = 1, + EQUIP_HEAD_SLOT = 2, + EQUIP_LEGS_SLOT = 3, + EQUIP_FEET_SLOT = 4, + EQUIP_RING1_SLOT = 5, + EQUIP_RING2_SLOT = 6, + EQUIP_NECKLACE_SLOT = 7, + EQUIP_FIGHT1_SLOT = 8, + EQUIP_FIGHT2_SLOT = 9, + EQUIP_PROJECTILE_SLOT = 10, + EQUIP_VECTOREND + }; +#else enum { // Equipment rules: EQUIP_LEGS_SLOT = 0, @@ -83,7 +105,7 @@ class EquipmentWindow : public Window, public gcn::ActionListener EQUIP_AMMO_SLOT, EQUIP_VECTOREND }; - +#endif private: void mouseExited(gcn::MouseEvent &event); @@ -92,8 +114,11 @@ class EquipmentWindow : public Window, public gcn::ActionListener Item* getItem(int x, int y) const; Equipment *mEquipment; +#ifdef EATHENA_SUPPORT Inventory *mInventory; +#endif gcn::Button *mUnequip; /**< Button for unequipping. */ + Image *mBackground; /**< Background Image. */ EquipBox mEquipBox[EQUIP_VECTOREND]; /**< Equipment Boxes. */ ItemPopup *mItemPopup; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 9b9e74f4..1ef0219a 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -107,7 +107,8 @@ Gui::Gui(Graphics *graphics): ResourceManager *resman = ResourceManager::getInstance(); // Set global font - std::string path = resman->getPath("fonts/dejavusans.ttf"); + std::string path = resman->getPath( + branding.getValue("font", "fonts/dejavusans.ttf")); try { const int fontSize = (int)config.getValue("fontSize", 11); @@ -120,7 +121,8 @@ Gui::Gui(Graphics *graphics): } // Set bold font - path = resman->getPath("fonts/dejavusans-bold.ttf"); + path = resman->getPath( + branding.getValue("boldFont", "fonts/dejavusans.ttf")); try { const int fontSize = (int)config.getValue("fontSize", 11); diff --git a/src/gui/guildlistbox.cpp b/src/gui/guildlistbox.cpp new file mode 100644 index 00000000..556df3fe --- /dev/null +++ b/src/gui/guildlistbox.cpp @@ -0,0 +1,130 @@ +/* + * The Mana World + * Copyright 2008 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 "guildlistbox.h" + +#include "../graphics.h" + +#include "../resources/image.h" +#include "../resources/resourcemanager.h" + +#include <guichan/font.hpp> + +GuildListBox::GuildListBox(): + ListBox(NULL) +{ + onlineIcon = ResourceManager::getInstance()->getImage("graphics/gui/circle-green.png"); + offlineIcon = ResourceManager::getInstance()->getImage("graphics/gui/circle-gray.png"); +} + +void GuildListBox::draw(gcn::Graphics *gcnGraphics) +{ + if (!mListModel) + return; + + Graphics *graphics = static_cast<Graphics*>(gcnGraphics); + + graphics->setColor(gcn::Color(110, 160, 255)); + graphics->setFont(getFont()); + + int fontHeight = getFont()->getHeight(); + + // Draw rectangle below the selected list element + if (mSelected >= 0) { + graphics->fillRectangle(gcn::Rectangle(0, fontHeight * mSelected, + getWidth(), fontHeight)); + } + + // Draw the list elements + for (int i = 0, y = 0; + i < mListModel->getNumberOfElements(); + ++i, y += fontHeight) + { + // Draw online status + bool online = false; + UserMap::iterator itr = mUsers.find(mListModel->getElementAt(i)); + if (itr != mUsers.end()) + { + online = itr->second; + } + Image *icon = online ? onlineIcon : offlineIcon; + if (icon) + graphics->drawImage(icon, 1, y); + // Draw Name + graphics->setColor(gcn::Color(0, 0, 0)); + graphics->drawText(mListModel->getElementAt(i), 33, y); + } +} +/* +void GuildListBox::setSelected(int selected) +{ + if (!mListModel) + { + mSelected = -1; + } + else + { + // Update mSelected with bounds checking + mSelected = std::min(mListModel->getNumberOfElements() - 1, + std::max(-1, selected)); + + gcn::Widget *parent; + parent = (gcn::Widget*)getParent(); + if (parent) + { + gcn::Rectangle scroll; + scroll.y = (mSelected < 0) ? 0 : getFont()->getHeight() * mSelected; + scroll.height = getFont()->getHeight(); + parent->showWidgetPart(this, scroll); + } + } + + distributeValueChangedEvent(); +} +*/ +void GuildListBox::mousePressed(gcn::MouseEvent &event) +{ + if (event.getButton() == gcn::MouseEvent::LEFT) + { + int y = event.getY(); + setSelected(y / getFont()->getHeight()); + distributeActionEvent(); + } + // TODO: Add guild functions, ie private messaging + if (event.getButton() == gcn::MouseEvent::RIGHT) + { + // Show context menu + } +} + +void GuildListBox::setOnlineStatus(const std::string &user, bool online) +{ + UserMap::iterator itr = mUsers.find(user); + if (itr == mUsers.end()) + { + mUsers.insert(std::pair<std::string, bool>(user, online)); + } + else + { + itr->second = online; + } + adjustSize(); +} diff --git a/src/gui/guildlistbox.h b/src/gui/guildlistbox.h new file mode 100644 index 00000000..cc8e3ce7 --- /dev/null +++ b/src/gui/guildlistbox.h @@ -0,0 +1,65 @@ +/* + * The Mana World + * Copyright 2008 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_GUI_GUILDLISTBOX_H +#define _TMW_GUI_GUILDLISTBOX_H + +#include <map> +#include <string> +#include <vector> + +#include "listbox.h" + +class Image; + +class GuildListBox : public ListBox +{ +public: + /** + * Constructor + */ + GuildListBox(); + + /** + * Draws the list box. + */ + void draw(gcn::Graphics *gcnGraphics); + + void mousePressed(gcn::MouseEvent &event); + + /** + * Sets the index of the selected element. + */ +// void setSelected(int selected); + + /** + * Set whether a member is online or offline + */ + void setOnlineStatus(const std::string &user, bool online); + +private: + Image *onlineIcon; + Image *offlineIcon; + typedef std::map<std::string, bool> UserMap; + UserMap mUsers; +}; + +#endif diff --git a/src/gui/guildwindow.cpp b/src/gui/guildwindow.cpp new file mode 100644 index 00000000..0596c75e --- /dev/null +++ b/src/gui/guildwindow.cpp @@ -0,0 +1,273 @@ +/* + * 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 + * $$ + */ + +#include "guildwindow.h" + +#include "button.h" +#include "chat.h" +#include "confirm_dialog.h" +#include "guildlistbox.h" +#include "scrollarea.h" +#include "textdialog.h" +#include "windowcontainer.h" + +#include "widgets/layout.h" +#include "widgets/tabbedarea.h" + +#include "../guild.h" +#include "../log.h" +#include "../localplayer.h" + +#include "../net/chatserver/guild.h" +#include "../utils/dtor.h" +#include "../utils/gettext.h" + +#include <algorithm> + +#include <guichan/widgets/tab.hpp> + +GuildWindow::GuildWindow(): + Window(_("Guild")), + mFocus(false) +{ + setWindowName("Guild"); + setCaption(_("Guild")); + setResizable(false); + setCloseButton(true); + setMinWidth(200); + setMinHeight(280); + setDefaultSize(124, 41, 288, 330); + + // Set button events Id + mGuildButton[0] = new Button(_("Create Guild"), "CREATE_GUILD", this); + mGuildButton[1] = new Button(_("Invite User"), "INVITE_USER", this); + mGuildButton[2] = new Button(_("Quit Guild"), "QUIT_GUILD", this); + mGuildButton[1]->setEnabled(false); + mGuildButton[2]->setEnabled(false); + + mGuildTabs = new TabbedArea(); + + place(0, 0, mGuildButton[0]); + place(1, 0, mGuildButton[1]); + place(2, 0, mGuildButton[2]); + place(0, 1, mGuildTabs); + Layout &layout = getLayout(); + layout.setColWidth(0, 48); + layout.setColWidth(1, 65); + + loadWindowState(); +} + +GuildWindow::~GuildWindow() +{ + delete mGuildTabs; +} + +void GuildWindow::update() +{ + updateTab(); + + if (mGuildButton[2]->isEnabled() && mGuildTabs->getNumberOfTabs() <= 0) + { + mGuildButton[2]->setEnabled(false); + mGuildButton[1]->setEnabled(false); + } +} + +void GuildWindow::draw(gcn::Graphics *g) +{ + update(); + + Window::draw(g); +} + +void GuildWindow::action(const gcn::ActionEvent &event) +{ + const std::string &eventId = event.getId(); + + // Stats Part + if (eventId == "CREATE_GUILD") + { + // Set focus so that guild name to be created can be typed. + mFocus = true; + guildDialog = new TextDialog("Guild Name", "Choose your guild's name", this); + guildDialog->setOKButtonActionId("CREATE_GUILD_OK"); + guildDialog->addActionListener(this); + } + else if (eventId == "INVITE_USER") + { + // TODO - Give feedback on whether the invite succeeded + mFocus = true; + inviteDialog = new TextDialog("Member Invite", "Who would you like to invite?", this); + inviteDialog->setOKButtonActionId("INVITE_USER_OK"); + inviteDialog->addActionListener(this); + } + else if (eventId == "QUIT_GUILD") + { + short guild = getSelectedGuild(); + if (guild) + { + Net::ChatServer::Guild::quitGuild(guild); + chatWindow->chatLog("Guild " + mGuildTabs->getSelectedTab()->getCaption() + " quit", BY_SERVER); + } + } + else if (eventId == "CREATE_GUILD_OK") + { + std::string name = guildDialog->getText(); + if(name.size() > 16) + { + // TODO : State too many characters in input. + return; + } + // Process guild name to be created, and unfocus. + Net::ChatServer::Guild::createGuild(name); + + // Defocus dialog + mFocus = false; + chatWindow->chatLog("Creating Guild called " + name, BY_SERVER); + guildDialog->scheduleDelete(); + } + else if (eventId == "INVITE_USER_OK") + { + std::string name = inviteDialog->getText(); + short selectedGuild = getSelectedGuild(); + + // Process invited user to be created and unfocus. + Net::ChatServer::Guild::invitePlayer(name, selectedGuild); + + // Defocus dialog + mFocus = false; + chatWindow->chatLog("Invited user " + name, BY_SERVER); + inviteDialog->scheduleDelete(); + } + else if (eventId == "yes") + { + logger->log("Sending invitation acceptance."); + Net::ChatServer::Guild::acceptInvite(invitedGuild); + } +} + +void GuildWindow::newGuildTab(const std::string &guildName) +{ + // Create new tab + GuildListBox *list = new GuildListBox(); + list->setListModel(player_node->getGuild(guildName)); + ScrollArea *sa = new ScrollArea(list); + sa->setDimension(gcn::Rectangle(5, 5, 135, 250)); + + // Add the listbox to the map + mGuildLists.insert(std::pair<std::string, GuildListBox*>(guildName, list)); + + mGuildTabs->addTab(guildName, sa); + mGuildTabs->setDimension(gcn::Rectangle(28,35,140,250)); + + updateTab(); +} + +void GuildWindow::updateTab() +{ + gcn::Tab *tab = mGuildTabs->getSelectedTab(); + if (tab) + { + setTab(tab->getCaption()); + } + mGuildTabs->logic(); +} + +void GuildWindow::setTab(const std::string &guildName) +{ + // Only enable invite button if user has rights + if(player_node->checkInviteRights(guildName)) + { + mGuildButton[1]->setEnabled(true); + } + else + { + mGuildButton[1]->setEnabled(false); + } + + mGuildButton[2]->setEnabled(true); +} + +bool GuildWindow::isWindowFocused() +{ + return mFocus; +} + +short GuildWindow::getSelectedGuild() +{ + if (mGuildTabs->getNumberOfTabs() > 0) + { + + Guild *guild = player_node->getGuild(mGuildTabs->getSelectedTab()->getCaption()); + + if (guild) + { + return guild->getId(); + } + } + + return 0; +} + +void GuildWindow::openAcceptDialog(const std::string &inviterName, + const std::string &guildName) +{ + std::string msg = inviterName + " has invited you to join the guild " + guildName; + chatWindow->chatLog(msg, BY_SERVER); + + acceptDialog = new ConfirmDialog("Accept Guild Invite", msg, this); + acceptDialog->addActionListener(this); + + invitedGuild = guildName; +} + +void GuildWindow::requestMemberList(short guildId) +{ + // Get the list of members for displaying in the guild window. + Net::ChatServer::Guild::getGuildMembers(guildId); +} + +void GuildWindow::removeTab(int guildId) +{ + Guild* guild = player_node->getGuild(guildId); + if (guild) + { + Tab *tab = mGuildTabs->getTab(guild->getName()); + if (tab) + { + mGuildTabs->removeTab(tab); + } + updateTab(); + } + mGuildTabs->logic(); +} + +void GuildWindow::setOnline(const std::string &guildName, const std::string &member, + bool online) +{ + GuildListMap::iterator itr = mGuildLists.find(guildName); + if (itr != mGuildLists.end()) + { + itr->second->setOnlineStatus(member, online); + } +} diff --git a/src/gui/guildwindow.h b/src/gui/guildwindow.h new file mode 100644 index 00000000..4f6b9cbb --- /dev/null +++ b/src/gui/guildwindow.h @@ -0,0 +1,135 @@ +/* + * The Mana World + * Copyright 2008 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_GUI_GUILDWINDOW_H +#define _TMW_GUI_GUILDWINDOW_H + +#include <iosfwd> +#include <map> +#include <vector> + +#include <guichan/actionlistener.hpp> +#include <guichan/widgets/listbox.hpp> + +#include "window.h" + +#include "../guichanfwd.h" + +class LocalPlayer; +class TextDialog; +class ConfirmDialog; +class GuildListBox; +class ScrollArea; +class GCContainer; +class TabbedArea; + +class GuildWindow : public Window, public gcn::ActionListener +{ +public: + /** + * Constructor. + */ + GuildWindow(); + + /** + * Destructor. + */ + ~GuildWindow(); + + /** + * Called when receiving actions from widget. + */ + void action(const gcn::ActionEvent &event); + + /** + * Draw this window. + */ + void draw(gcn::Graphics *graphics); + + /** + * Updates this dialog. + */ + void update(); + + /** + * Create a new tab for a guild list. + */ + void newGuildTab(const std::string &guildName); + + /** + * Display guild's member list to active tab + */ + void setTab(const std::string &guildName); + + /** + * Update the contents of the active tab + */ + void updateTab(); + + /** + * Check if the window is in focus. + */ + bool isWindowFocused(); + + /** + * Create a dialog for accepting an invite + */ + void openAcceptDialog(const std::string &inviterName, const std::string &guildName); + + /** + * Request member list + */ + void requestMemberList(short guildId); + + /** + * Removes the selected tab + */ + void removeTab(int guildId); + + /** + * Set guild member status in userlist + */ + void setOnline(const std::string &guildName, const std::string &member, + bool online); + +protected: + /** + * Get selected guild tab + * @return Returns selected guild + */ + short getSelectedGuild(); + +private: + gcn::Button *mGuildButton[3]; + TextDialog *guildDialog; + TextDialog *inviteDialog; + ConfirmDialog *acceptDialog; + TabbedArea *mGuildTabs; + ScrollArea *mScrollArea; + bool mFocus; + std::string invitedGuild; + typedef std::map<std::string, GuildListBox*> GuildListMap; + GuildListMap mGuildLists; +}; + +extern GuildWindow *guildWindow; + +#endif diff --git a/src/gui/icon.cpp b/src/gui/icon.cpp new file mode 100644 index 00000000..1e352292 --- /dev/null +++ b/src/gui/icon.cpp @@ -0,0 +1,58 @@ +/* + * The Mana World + * Copyright 2008 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 "icon.h" + +#include "../graphics.h" + +#include "../resources/image.h" +#include "../resources/resourcemanager.h" + +Icon::Icon(const std::string &file) + : mImage(0) +{ + mImage = ResourceManager::getInstance()->getImage(file); + setSize(mImage->getWidth(), mImage->getHeight()); + +} + +Icon::Icon(Image *image) + : mImage(image) +{ + setSize(mImage->getWidth(), mImage->getHeight()); +} + +void Icon::setImage(Image *image) +{ + mImage = image; + setSize(mImage->getWidth(), mImage->getHeight()); +} + +void Icon::draw(gcn::Graphics *g) +{ + if(mImage) + { + Graphics *graphics = static_cast<Graphics*>(g); + const int x = (getWidth() - mImage->getWidth()) / 2; + const int y = (getHeight() - mImage->getHeight()) / 2; + graphics->drawImage(mImage, x, y); + } +} diff --git a/src/gui/icon.h b/src/gui/icon.h new file mode 100644 index 00000000..9baf1a99 --- /dev/null +++ b/src/gui/icon.h @@ -0,0 +1,68 @@ +/* + * The Mana World + * Copyright 2008 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_ICON_H +#define _TMW_ICON_H + +#include <guichan/widget.hpp> + +class Image; + + +/** + * An icon. + * + * \ingroup GUI + */ +class Icon : public gcn::Widget { + public: + /** + * Constructor. + */ + Icon(const std::string &filename); + + /** + * Constructor, uses an existing Image. + */ + Icon(Image *image); + + /** + * Gets the current Image. + */ + Image* getImage() { return mImage; } + + /** + * Sets the image to display. + */ + void setImage(Image *image); + + /** + * Draws the Icon. + */ + void draw(gcn::Graphics *g); + + private: + + Image *mImage; +}; + +#endif diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index fea86b81..d18490a4 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -32,31 +32,39 @@ #include "itemcontainer.h" #include "progressbar.h" #include "scrollarea.h" +#include "sdlinput.h" #include "viewport.h" #include "widgets/layout.h" #include "../inventory.h" #include "../item.h" +#include "../localplayer.h" +#include "../log.h" #include "../units.h" #include "../resources/iteminfo.h" #include "../utils/gettext.h" -#include "../utils/strprintf.h" #include "../utils/stringutils.h" +#include "../utils/strprintf.h" InventoryWindow::InventoryWindow(int invSize): Window(_("Inventory")), mMaxSlots(invSize), + mSplit(false), mItemDesc(false) { setWindowName("Inventory"); - setResizable(true); + setResizable(false); setCloseButton(true); - + // LEEOR/TODO: Since this window is not resizable, do we really need to set these + // values or can we drop them? + setMinWidth(375); + setMinHeight(283); // If you adjust these defaults, don't forget to adjust the trade window's. - setDefaultSize(115, 25, 375, 300); + setDefaultSize(115, 30, 375, 283); + addKeyListener(this); std::string longestUseString = getFont()->getWidth(_("Equip")) > getFont()->getWidth(_("Use")) ? @@ -70,8 +78,15 @@ InventoryWindow::InventoryWindow(int invSize): mUseButton = new Button(longestUseString, "use", this); mDropButton = new Button(_("Drop"), "drop", this); - - mItems = new ItemContainer(player_node->getInventory(), 2); +#ifdef TMWSERV_SUPPORT + mSplitButton = new Button(_("Split"), "split", this); +#endif + +#ifdef TMWSERV_SUPPORT + mItems = new ItemContainer(player_node->getInventory(), 10, 5); +#else + mItems = new ItemContainer(player_node->getInventory(), 10, 5, 2); +#endif mItems->addSelectionListener(this); mInvenScroll = new ScrollArea(mItems); @@ -94,15 +109,17 @@ InventoryWindow::InventoryWindow(int invSize): place(1, 0, mWeightBar, 3); place(4, 0, mSlotsLabel).setPadding(3); place(5, 0, mSlotsBar, 2); - place(0, 1, mInvenScroll, 7, 4); - place(5, 5, mDropButton); - place(6, 5, mUseButton); + place(0, 1, mInvenScroll, 100).setPadding(3); + place(0, 2, mUseButton); + place(1, 2, mDropButton); +#ifdef TMWSERV_SUPPORT + place(2, 2, mSplitButton); +#endif Layout &layout = getLayout(); - layout.setRowHeight(0, mDropButton->getHeight()); + layout.setRowHeight(0, Layout::AUTO_SET); loadWindowState(); - setLocationRelativeTo(getParent()); } InventoryWindow::~InventoryWindow() @@ -118,21 +135,21 @@ void InventoryWindow::logic() // redesign of InventoryWindow and ItemContainer probably. updateButtons(); - if (mMaxWeight != player_node->mMaxWeight || - mTotalWeight != player_node->mTotalWeight || + if (mMaxWeight != player_node->getMaxWeight() || + mTotalWeight != player_node->getTotalWeight() || mUsedSlots != toString(player_node->getInventory()->getNumberOfSlotsUsed())) { - mTotalWeight = player_node->mTotalWeight; - mMaxWeight = player_node->mMaxWeight; + mTotalWeight = player_node->getTotalWeight(); + mMaxWeight = player_node->getMaxWeight(); mUsedSlots = toString(player_node->getInventory()->getNumberOfSlotsUsed()); // Weight Bar coloration - if (int(player_node->mTotalWeight) < int(player_node->mMaxWeight / 3)) + if (int(player_node->getTotalWeight()) < int(player_node->getMaxWeight() / 3)) { mWeightBar->setColor(0, 0, 255); // Blue } - else if (int(player_node->mTotalWeight) < - int((player_node->mMaxWeight / 3) * 2)) + else if (int(player_node->getTotalWeight()) < + int((player_node->getMaxWeight() / 3) * 2)) { mWeightBar->setColor(255, 255, 0); // Yellow } @@ -144,8 +161,8 @@ void InventoryWindow::logic() // Adjust progress bars mSlotsBar->setProgress((float) player_node->getInventory()->getNumberOfSlotsUsed() / mMaxSlots); - mWeightBar->setProgress((float) player_node->mTotalWeight / - player_node->mMaxWeight); + mWeightBar->setProgress((float) player_node->getTotalWeight() / + player_node->getMaxWeight()); mSlotsBar->setText(strprintf("%s/%d", mUsedSlots.c_str(), mMaxSlots)); mWeightBar->setText(strprintf("%s/%s", @@ -163,6 +180,14 @@ void InventoryWindow::action(const gcn::ActionEvent &event) if (event.getId() == "use") { +#ifdef TMWSERV_SUPPORT + if (item->isEquipment()) { + player_node->equipItem(item); + } + else { + player_node->useItem(item->getInvIndex()); + } +#else if (item->isEquipment()) { if (item->isEquipped()) @@ -172,19 +197,33 @@ void InventoryWindow::action(const gcn::ActionEvent &event) } else player_node->useItem(item); +#endif } else if (event.getId() == "drop") { - if (item->getQuantity() == 1) - player_node->dropItem(item, 1); - else - { + if (item->getQuantity() > 1) { // Choose amount of items to drop new ItemAmountWindow(AMOUNT_ITEM_DROP, this, item); } + else { + player_node->dropItem(item, 1); + } + mItems->selectNone(); + } + else if (event.getId() == "split") + { + if (item && !item->isEquipment() && item->getQuantity() > 1) { + new ItemAmountWindow(AMOUNT_ITEM_SPLIT, this, item, + (item->getQuantity() - 1)); + } } } +Item* InventoryWindow::getSelectedItem() const +{ + return mItems->getSelectedItem(); +} + void InventoryWindow::mouseClicked(gcn::MouseEvent &event) { Window::mouseClicked(event); @@ -205,15 +244,55 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event) } } +#ifdef TMWSERV_SUPPORT +void InventoryWindow::keyPressed(gcn::KeyEvent &event) +{ + switch (event.getKey().getValue()) + { + case Key::LEFT_SHIFT: + case Key::RIGHT_SHIFT: + mSplit = true; + break; + } +} + +void InventoryWindow::keyReleased(gcn::KeyEvent &event) +{ + switch (event.getKey().getValue()) + { + case Key::LEFT_SHIFT: + case Key::RIGHT_SHIFT: + mSplit = false; + break; + } +} +#endif + +void InventoryWindow::valueChanged(const gcn::SelectionEvent &event) +{ + if (mSplit) + { + Item *item = mItems->getSelectedItem(); + + if (item && !item->isEquipment() && item->getQuantity() > 1) + { + mSplit = false; + new ItemAmountWindow(AMOUNT_ITEM_SPLIT, this, item, (item->getQuantity() - 1)); + } + } +} + void InventoryWindow::updateButtons() { const Item *selectedItem = mItems->getSelectedItem(); if (selectedItem && selectedItem->isEquipment()) { +#ifdef EATHENA_SUPPORT if (selectedItem->isEquipped()) mUseButton->setCaption(_("Unequip")); else +#endif mUseButton->setCaption(_("Equip")); } else @@ -221,9 +300,15 @@ void InventoryWindow::updateButtons() mUseButton->setEnabled(selectedItem != 0); mDropButton->setEnabled(selectedItem != 0); -} -Item* InventoryWindow::getSelectedItem() const -{ - return mItems->getSelectedItem(); +#ifdef TMWSERV_SUPPORT + if (selectedItem && !selectedItem->isEquipment() && + selectedItem->getQuantity() > 1) + { + mSplitButton->setEnabled(true); + } + else { + mSplitButton->setEnabled(false); + } +#endif } diff --git a/src/gui/inventorywindow.h b/src/gui/inventorywindow.h index 45796e74..6a51c66d 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -24,6 +24,7 @@ #include <guichan/actionlistener.hpp> #include <guichan/selectionlistener.hpp> +#include <guichan/keylistener.hpp> #include "window.h" @@ -39,14 +40,20 @@ class TextBox; * * \ingroup Interface */ -class InventoryWindow : public Window, gcn::ActionListener, - gcn::SelectionListener +class InventoryWindow : public Window, + public gcn::ActionListener, + public gcn::KeyListener, + public gcn::SelectionListener { public: /** * Constructor. */ +#ifdef TMWSERV_SUPPORT + InventoryWindow(int invSize = (INVENTORY_SIZE)); +#else InventoryWindow(int invSize = (INVENTORY_SIZE - 2)); +#endif /** * Destructor. @@ -68,8 +75,28 @@ class InventoryWindow : public Window, gcn::ActionListener, */ Item* getSelectedItem() const; + /** + * Handles the mouse clicks. + */ void mouseClicked(gcn::MouseEvent &event); +#ifdef TMWSERV_SUPPORT + /** + * Handles the key presses. + */ + void keyPressed(gcn::KeyEvent &event); + + /** + * Handles the key releases. + */ + void keyReleased(gcn::KeyEvent &event); +#endif + + /** + * Updates labels to currently selected item. + */ + void valueChanged(const gcn::SelectionEvent &event); + private: void updateButtons(); /**< Updates button states. */ @@ -78,10 +105,13 @@ class InventoryWindow : public Window, gcn::ActionListener, std::string mWeight; std::string mSlots; std::string mUsedSlots; - Uint32 mTotalWeight, mMaxWeight; - gcn::Button *mUseButton, *mDropButton; - gcn::ScrollArea *mInvenScroll; - + int mTotalWeight, mMaxWeight; + gcn::Button *mUseButton; + gcn::Button *mDropButton; +#ifdef TMWSERV_SUPPORT + gcn::Button *mSplitButton; +#endif + gcn::ScrollArea *mInvenScroll; /**< Inventory Scroll Area. */ gcn::Label *mWeightLabel; gcn::Label *mSlotsLabel; @@ -90,6 +120,7 @@ class InventoryWindow : public Window, gcn::ActionListener, int mMaxSlots; + bool mSplit; bool mItemDesc; }; diff --git a/src/gui/item_amount.cpp b/src/gui/item_amount.cpp index 92be3d6e..3bd388f4 100644 --- a/src/gui/item_amount.cpp +++ b/src/gui/item_amount.cpp @@ -32,11 +32,15 @@ #include "../utils/gettext.h" -ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item): +ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item, + int maxRange): Window("", true, parent), mItem(item) { - const int maxRange = mItem->getQuantity(); + if (!maxRange) + { + maxRange = mItem->getQuantity(); + } // Integer field mItemAmountTextField = new IntTextField(1); @@ -80,6 +84,10 @@ ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item): setCaption(_("Select amount of items to drop.")); okButton->setActionEventId("Drop"); break; + case AMOUNT_ITEM_SPLIT: + setCaption(_("Select amount of items to split.")); + okButton->setActionEventId("Split"); + break; default: break; } @@ -123,6 +131,13 @@ void ItemAmountWindow::action(const gcn::ActionEvent &event) tradeWindow->tradeItem(mItem, mItemAmountTextField->getValue()); scheduleDelete(); } +#ifdef TMWSERV_SUPPORT + else if (event.getId() == "Split") + { + player_node->splitItem(mItem, mItemAmountTextField->getValue()); + scheduleDelete(); + } +#endif mItemAmountTextField->setValue(amount); mItemAmountSlide->setValue(amount); } diff --git a/src/gui/item_amount.h b/src/gui/item_amount.h index 618d7d51..4fdb8dc6 100644 --- a/src/gui/item_amount.h +++ b/src/gui/item_amount.h @@ -31,9 +31,10 @@ class Item; #define AMOUNT_TRADE_ADD 1 #define AMOUNT_ITEM_DROP 2 +#define AMOUNT_ITEM_SPLIT 3 /** - * Window used for selecting the amount of items to drop or trade. + * Window used for selecting the amount of items to drop, trade or split. * * \ingroup Interface */ @@ -43,7 +44,7 @@ class ItemAmountWindow : public Window, public gcn::ActionListener /** * Constructor. */ - ItemAmountWindow(int usage, Window *parent, Item *item); + ItemAmountWindow(int usage, Window *parent, Item *item, int maxRange = 0); /** * Called when receiving actions from widget. diff --git a/src/gui/itemcontainer.cpp b/src/gui/itemcontainer.cpp index eed31f7a..bdae9ada 100644 --- a/src/gui/itemcontainer.cpp +++ b/src/gui/itemcontainer.cpp @@ -20,13 +20,14 @@ */ #include "itemcontainer.h" +#include "chat.h" #include "itempopup.h" #include <guichan/mouseinput.hpp> #include <guichan/selectionlistener.hpp> -#include <SDL_mouse.h> +#include "sdlinput.h" #include "../graphics.h" #include "../inventory.h" @@ -40,28 +41,48 @@ #include "../utils/stringutils.h" -const int ItemContainer::gridWidth = 36; // item icon width + 4 -const int ItemContainer::gridHeight = 42; // item icon height + 10 +// TODO: Add support for adding items to the item shortcut window (global +// itemShortcut). -static const int NO_ITEM = -1; +static const int BOX_WIDTH = 36; +static const int BOX_HEIGHT = 44; -ItemContainer::ItemContainer(Inventory *inventory, int offset): +enum +{ + SEL_NONE = 0, + SEL_SELECTED, + SEL_SELECTING, + SEL_DESELECTING, + SEL_DRAGGING +}; + +ItemContainer::ItemContainer(Inventory *inventory, + int gridColumns, + int gridRows, + int offset): mInventory(inventory), - mSelectedItemIndex(NO_ITEM), - mLastSelectedItemId(NO_ITEM), - mOffset(offset) + mGridColumns(gridColumns), + mGridRows(gridRows), + mOffset(offset), + mSelectedItem(NULL), + mHighlightedItem(NULL), + mSelectionStatus(SEL_NONE), + mSwapItems(false), + mDescItems(false) { - mItemPopup = new ItemPopup; + mItemPopup = new ItemPopup(); + setFocusable(true); ResourceManager *resman = ResourceManager::getInstance(); mSelImg = resman->getImage("graphics/gui/selection.png"); if (!mSelImg) logger->error("Unable to load selection.png"); - mMaxItems = mInventory->getLastUsedSlot() - 1; // Count from 0, usage from 2 - + addKeyListener(this); addMouseListener(this); - addWidgetListener(this); + + setSize((BOX_WIDTH - 1) * mGridColumns + 1, + (BOX_HEIGHT - 1) * mGridRows + 1); } ItemContainer::~ItemContainer() @@ -70,187 +91,214 @@ ItemContainer::~ItemContainer() delete mItemPopup; } -void ItemContainer::logic() -{ - gcn::Widget::logic(); - - int i = mInventory->getLastUsedSlot() - 1; // Count from 0, usage from 2 - - if (i != mMaxItems) - { - mMaxItems = i; - recalculateHeight(); - } -} - void ItemContainer::draw(gcn::Graphics *graphics) { - int columns = getWidth() / gridWidth; + Graphics *g = static_cast<Graphics*>(graphics); - // Have at least 1 column - if (columns < 1) + for (int i = 0; i < mGridColumns; i++) { - columns = 1; - } + for (int j = 0; j < mGridRows; j++) + { + // Items positions made to overlap on another. + int itemX = i * (BOX_WIDTH - 1); + int itemY = j * (BOX_HEIGHT - 1); - /* - * mOffset is used to compensate for some weirdness that eAthena inherited from - * Ragnarok Online. Inventory slots and cart slots are +2 from their actual index, - * while storage slots are +1. - */ - for (int i = mOffset; i < mInventory->getSize(); i++) - { - Item *item = mInventory->getItem(i); + // Set color to black. + g->setColor(gcn::Color(0, 0, 0)); + // Draw box border. + g->drawRectangle( + gcn::Rectangle(itemX, itemY, BOX_WIDTH, BOX_HEIGHT)); - if (!item || item->getQuantity() <= 0) - continue; + Item *item = mInventory->getItem((j * mGridColumns) + i); - int itemX = ((i - 2) % columns) * gridWidth; - int itemY = ((i - 2) / columns) * gridHeight; + if (!item || item->getId() == 0) + continue; - // Draw selection image below selected item - if (mSelectedItemIndex == i) - { - static_cast<Graphics*>(graphics)->drawImage( - mSelImg, itemX, itemY); - } + Image *image = item->getImage(); + if (image) + { + if (item == mSelectedItem) + { + if (mSelectionStatus == SEL_DRAGGING) { + // Reposition the coords to that of the cursor. + itemX = mDragPosX - (BOX_WIDTH / 2); + itemY = mDragPosY - (BOX_HEIGHT / 2); + } + else { + // Draw selected image. + g->drawImage(mSelImg, itemX, itemY); + } + } + g->drawImage(image, itemX, itemY); + } + if (item->getQuantity() > 1) { + // Draw item caption + g->drawText( + toString(item->getQuantity()), + itemX + BOX_WIDTH / 2, + itemY + BOX_HEIGHT - 14, + gcn::Graphics::CENTER); + } - // Draw item icon - Image* image = item->getImage(); - if (image) - { - static_cast<Graphics*>(graphics)->drawImage( - image, itemX, itemY); } - - // Draw item caption - graphics->setFont(getFont()); - graphics->setColor(gcn::Color(0, 0, 0)); - graphics->drawText( - (item->isEquipped() ? "Eq." : toString(item->getQuantity())), - itemX + gridWidth / 2, - itemY + gridHeight - 11, - gcn::Graphics::CENTER); } -} -void ItemContainer::widgetResized(const gcn::Event &event) -{ - recalculateHeight(); + if (isFocused() && mHighlightedItem) { + // Items positions made to overlap on another. + const int i = mHighlightedItem->getInvIndex(); + const int itemX = (i % mGridColumns) * (BOX_WIDTH - 1); + const int itemY = (i / mGridColumns) * (BOX_HEIGHT - 1); + // Set color to orange. + g->setColor(gcn::Color(255, 128, 0)); + // Draw box border. + g->drawRectangle(gcn::Rectangle(itemX, itemY, BOX_WIDTH, BOX_HEIGHT)); + } } -void ItemContainer::recalculateHeight() +void ItemContainer::selectNone() { - int cols = getWidth() / gridWidth; - - if (cols < 1) - cols = 1; - - const int rows = (mMaxItems / cols) + (mMaxItems % cols > 0 ? 1 : 0); - const int height = rows * gridHeight + 8; - - if (height != getHeight()) - setHeight(height); + setSelectedItem(NULL); } -Item *ItemContainer::getSelectedItem() +void ItemContainer::setSelectedItem(Item *item) { - refindSelectedItem(); // Make sure that we're still current - - if (mSelectedItemIndex == NO_ITEM) - return NULL; - - return mInventory->getItem(mSelectedItemIndex); + if (mSelectedItem != item) + { + mSelectedItem = item; + distributeValueChangedEvent(); + } } -void ItemContainer::selectNone() +void ItemContainer::distributeValueChangedEvent() { - setSelectedItemIndex(NO_ITEM); -} + SelectionListenerIterator iter; -void ItemContainer::refindSelectedItem() -{ - if (mSelectedItemIndex != NO_ITEM) + for (iter = mSelectionListeners.begin(); iter != mSelectionListeners.end(); + ++iter) { - if (mInventory->getItem(mSelectedItemIndex) && - mInventory->getItem(mSelectedItemIndex)->getId() == mLastSelectedItemId) - return; // we're already fine - - // Otherwise ensure the invariant: we must point to an item of the specified last ID, - // or nowhere at all. - - for (int i = 0; i <= mMaxItems + 1; i++) - if (mInventory->getItem(i) && - mInventory->getItem(i)->getId() == mLastSelectedItemId) - { - mSelectedItemIndex = i; - return; - } + gcn::SelectionEvent event(this); + (*iter)->valueChanged(event); } - - mLastSelectedItemId = mSelectedItemIndex = NO_ITEM; } -void ItemContainer::setSelectedItemIndex(int index) +void ItemContainer::keyPressed(gcn::KeyEvent &event) { - int newSelectedItemIndex; - - /* - * mOffset is used to compensate for some weirdness that eAthena inherited from - * Ragnarok Online. Inventory slots and cart slots are +2 from their actual index, - * while storage slots are +1. - */ - if (index < 0 || index > mMaxItems + mOffset || mInventory->getItem(index) == NULL) - newSelectedItemIndex = NO_ITEM; - else - newSelectedItemIndex = index; - - if (mSelectedItemIndex != newSelectedItemIndex) + switch (event.getKey().getValue()) { - mSelectedItemIndex = newSelectedItemIndex; - - if (mSelectedItemIndex == NO_ITEM) - mLastSelectedItemId = NO_ITEM; - else - mLastSelectedItemId = mInventory->getItem(index)->getId(); - - distributeValueChangedEvent(); + case Key::LEFT: + moveHighlight(MOVE_SELECTED_LEFT); + break; + case Key::RIGHT: + moveHighlight(MOVE_SELECTED_RIGHT); + break; + case Key::UP: + moveHighlight(MOVE_SELECTED_UP); + break; + case Key::DOWN: + moveHighlight(MOVE_SELECTED_DOWN); + break; + case Key::SPACE: + keyAction(); + break; + case Key::LEFT_ALT: + case Key::RIGHT_ALT: + mSwapItems = true; + break; + case Key::RIGHT_CONTROL: + mDescItems = true; + break; } } -void ItemContainer::distributeValueChangedEvent() +void ItemContainer::keyReleased(gcn::KeyEvent &event) { - gcn::SelectionEvent event(this); - std::list<gcn::SelectionListener*>::iterator i_end = mListeners.end(); - std::list<gcn::SelectionListener*>::iterator i; - - for (i = mListeners.begin(); i != i_end; ++i) + switch (event.getKey().getValue()) { - (*i)->valueChanged(event); + case Key::LEFT_ALT: + case Key::RIGHT_ALT: + mSwapItems = false; + break; + case Key::RIGHT_CONTROL: + mDescItems = false; + break; } } void ItemContainer::mousePressed(gcn::MouseEvent &event) { const int button = event.getButton(); - if (button == gcn::MouseEvent::LEFT || button == gcn::MouseEvent::RIGHT) { - int columns = getWidth() / gridWidth; - int mx = event.getX(); - int my = event.getY(); - int index = mx / gridWidth + ((my / gridHeight) * columns) + mOffset; - - itemShortcut->setItemSelected(-1); - setSelectedItemIndex(index); + const int index = getSlotIndex(event.getX(), event.getY()); + if (index == Inventory::NO_SLOT_INDEX) { + return; + } Item *item = mInventory->getItem(index); - if (item) + // put item name into chat window + if (mDescItems) + { + chatWindow->addItemText(item->getInfo().getName()); + } + + if (mSelectedItem && mSelectedItem == item) + { + mSelectionStatus = SEL_DESELECTING; + } + else if (item && item->getId()) + { + setSelectedItem(item); + mSelectionStatus = SEL_SELECTING; + itemShortcut->setItemSelected(item->getId()); + } + else + { + setSelectedItem(NULL); + mSelectionStatus = SEL_NONE; + } + } +} + +void ItemContainer::mouseDragged(gcn::MouseEvent &event) +{ + if (mSelectionStatus != SEL_NONE) + { + mSelectionStatus = SEL_DRAGGING; + mDragPosX = event.getX(); + mDragPosY = event.getY(); } } +void ItemContainer::mouseReleased(gcn::MouseEvent &event) +{ + switch (mSelectionStatus) + { + case SEL_SELECTING: + mSelectionStatus = SEL_SELECTED; + return; + case SEL_DESELECTING: + setSelectedItem(NULL); + mSelectionStatus = SEL_NONE; + return; + case SEL_DRAGGING: + mSelectionStatus = SEL_SELECTED; + break; + default: + return; + }; + + int index = getSlotIndex(event.getX(), event.getY()); + if (index == Inventory::NO_SLOT_INDEX) return; + Item *item = mInventory->getItem(index); + if (item == mSelectedItem) return; + player_node->moveInvItem(mSelectedItem, index); + setSelectedItem(NULL); + mSelectionStatus = SEL_NONE; +} + + // Show ItemTooltip void ItemContainer::mouseMoved(gcn::MouseEvent &event) { @@ -277,11 +325,97 @@ void ItemContainer::mouseExited(gcn::MouseEvent &event) mItemPopup->setVisible(false); } -int ItemContainer::getSlotIndex(int posX, int posY) const +int ItemContainer::getSlotIndex(const int posX, const int posY) const +{ + if (getDimension().isPointInRect(posX, posY)) + { + // Takes into account, boxes are overlapping each other. + return (posY / (BOX_HEIGHT - 1)) * mGridColumns + (posX / (BOX_WIDTH - 1)); + } + return Inventory::NO_SLOT_INDEX; +} + +void ItemContainer::keyAction() { - int columns = getWidth() / gridWidth; - int index = posX / gridWidth + ((posY / gridHeight) * columns) + mOffset; + // If there is no highlight then return. + if (!mHighlightedItem) + return; - return (index); + // If the highlight is on the selected item, then deselect it. + if (mHighlightedItem == mSelectedItem) + { + setSelectedItem(NULL); + mSelectionStatus = SEL_NONE; + } + // Check and swap items if necessary. + else if (mSwapItems && + mSelectedItem && + mHighlightedItem->getId()) + { + player_node->moveInvItem( + mSelectedItem, mHighlightedItem->getInvIndex()); + setSelectedItem(mHighlightedItem); + } + // If the highlight is on an item then select it. + else if (mHighlightedItem->getId()) + { + setSelectedItem(mHighlightedItem); + mSelectionStatus = SEL_SELECTED; + } + // If the highlight is on a blank space then move it. + else if (mSelectedItem) + { + player_node->moveInvItem( + mSelectedItem, mHighlightedItem->getInvIndex()); + setSelectedItem(NULL); + mSelectionStatus = SEL_NONE; + } } +void ItemContainer::moveHighlight(int direction) +{ + if (!mHighlightedItem) + { + if (mSelectedItem) { + mHighlightedItem = mSelectedItem; + } + else { + mHighlightedItem = mInventory->getItem(0); + } + return; + } + + switch (direction) + { + case MOVE_SELECTED_LEFT: + if (mHighlightedItem->getInvIndex() % mGridColumns == 0) + { + mHighlightedItem += mGridColumns; + } + mHighlightedItem--; + break; + case MOVE_SELECTED_RIGHT: + if ((mHighlightedItem->getInvIndex() % mGridColumns) == + (mGridColumns - 1)) + { + mHighlightedItem -= mGridColumns; + } + mHighlightedItem++; + break; + case MOVE_SELECTED_UP: + if (mHighlightedItem->getInvIndex() / mGridColumns == 0) + { + mHighlightedItem += (mGridColumns * mGridRows); + } + mHighlightedItem -= mGridColumns; + break; + case MOVE_SELECTED_DOWN: + if ((mHighlightedItem->getInvIndex() / mGridColumns) == + (mGridRows - 1)) + { + mHighlightedItem -= (mGridColumns * mGridRows); + } + mHighlightedItem += mGridColumns; + break; + } +} diff --git a/src/gui/itemcontainer.h b/src/gui/itemcontainer.h index 5ad140be..38eaba01 100644 --- a/src/gui/itemcontainer.h +++ b/src/gui/itemcontainer.h @@ -24,9 +24,9 @@ #include <list> +#include <guichan/keylistener.hpp> #include <guichan/mouselistener.hpp> #include <guichan/widget.hpp> -#include <guichan/widgetlistener.hpp> class Image; class Inventory; @@ -43,14 +43,19 @@ namespace gcn { * \ingroup GUI */ class ItemContainer : public gcn::Widget, - public gcn::MouseListener, - public gcn::WidgetListener + public gcn::KeyListener, + public gcn::MouseListener { public: /** * Constructor. Initializes the graphic. + * @param inventory + * @param gridColumns Amount of columns in grid. + * @param gridRows Amount of rows in grid. + * @param offset Index offset */ - ItemContainer(Inventory *inventory, int offset); + ItemContainer(Inventory *inventory, int gridColumns, int gridRows, + int offset = 0); /** * Destructor. @@ -58,19 +63,19 @@ class ItemContainer : public gcn::Widget, virtual ~ItemContainer(); /** - * Handles the logic of the ItemContainer + * Draws the items. */ - void logic(); + void draw(gcn::Graphics *graphics); /** - * Draws the items. + * Handles the key presses. */ - void draw(gcn::Graphics *graphics); + void keyPressed(gcn::KeyEvent &event); /** - * Called whenever the widget changes size. + * Handles the key releases. */ - void widgetResized(const gcn::Event &event); + void keyReleased(gcn::KeyEvent &event); /** * Handles mouse click. @@ -78,9 +83,20 @@ class ItemContainer : public gcn::Widget, void mousePressed(gcn::MouseEvent &event); /** + * Handles mouse dragged. + */ + void mouseDragged(gcn::MouseEvent &event); + + /** + * Handles mouse released. + */ + void mouseReleased(gcn::MouseEvent &event); + + /** * Returns the selected item. */ - Item* getSelectedItem(); + Item* getSelectedItem() const + { return mSelectedItem; } /** * Sets selected item to NULL. @@ -93,7 +109,7 @@ class ItemContainer : public gcn::Widget, */ void addSelectionListener(gcn::SelectionListener *listener) { - mListeners.push_back(listener); + mSelectionListeners.push_back(listener); } /** @@ -102,18 +118,35 @@ class ItemContainer : public gcn::Widget, */ void removeSelectionListener(gcn::SelectionListener *listener) { - mListeners.remove(listener); + mSelectionListeners.remove(listener); } + enum { + MOVE_SELECTED_LEFT, // 0 + MOVE_SELECTED_RIGHT, // 1 + MOVE_SELECTED_UP, // 2 + MOVE_SELECTED_DOWN // 3 + }; private: + /** + * Execute all the functionality associated with the action key. + */ + void keyAction(); + void mouseExited(gcn::MouseEvent &event); void mouseMoved(gcn::MouseEvent &event); /** + * Moves the highlight in the direction specified. + * + * @param direction The move direction of the highlighter. + */ + void moveHighlight(int direction); - * Sets the currently selected item. Invalid (e.g., negative) indices set `no item'. + /** + * Sets the currently selected item. */ - void setSelectedItemIndex(int index); + void setSelectedItem(Item *item); /** * Find the current item index by the most recently used item ID @@ -140,19 +173,21 @@ class ItemContainer : public gcn::Widget, int getSlotIndex(int posX, int posY) const; Inventory *mInventory; - Image *mSelImg; - - int mSelectedItemIndex; - int mLastSelectedItemId; // last selected item ID. If we lose the item, find again by ID. - int mMaxItems; + int mGridColumns, mGridRows; int mOffset; + Image *mSelImg; + Item *mSelectedItem, *mHighlightedItem; + int mSelectionStatus; + bool mSwapItems; + bool mDescItems; + int mDragPosX, mDragPosY; ItemPopup *mItemPopup; - std::list<gcn::SelectionListener*> mListeners; + typedef std::list<gcn::SelectionListener*> SelectionListenerList; + typedef SelectionListenerList::iterator SelectionListenerIterator; - static const int gridWidth; - static const int gridHeight; + SelectionListenerList mSelectionListeners; }; #endif diff --git a/src/gui/itempopup.cpp b/src/gui/itempopup.cpp index 48aa7c8c..25e6e78e 100644 --- a/src/gui/itempopup.cpp +++ b/src/gui/itempopup.cpp @@ -106,7 +106,9 @@ ItemPopup::~ItemPopup() void ItemPopup::setItem(const ItemInfo &item) { mItemName->setCaption(item.getName()); +#ifdef EATHENA_SUPPORT mItemName->setForegroundColor(getColor(item.getType())); +#endif mItemName->setWidth(boldFont->getWidth(item.getName())); mItemDesc->setTextWrapped(item.getDescription(), 196); mItemEffect->setTextWrapped(item.getEffect(), 196); diff --git a/src/gui/itemshortcutcontainer.cpp b/src/gui/itemshortcutcontainer.cpp index e5990a28..8864cbd9 100644 --- a/src/gui/itemshortcutcontainer.cpp +++ b/src/gui/itemshortcutcontainer.cpp @@ -102,14 +102,18 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) Item *item = player_node->getInventory()->findItem(itemShortcut->getItem(i)); - if (item) { + if (item) + { // Draw item icon. - const std::string label = - item->isEquipped() ? "Eq." : toString(item->getQuantity()); Image* image = item->getImage(); - if (image) { + + if (image) + { const std::string label = - item->isEquipped() ? "Eq." : toString(item->getQuantity()); +#ifdef EATHENA_SUPPORT + item->isEquipped() ? "Eq." : +#endif + toString(item->getQuantity()); g->drawImage(image, itemX, itemY); g->drawText( label, diff --git a/src/gui/itemshortcutwindow.cpp b/src/gui/itemshortcutwindow.cpp new file mode 100644 index 00000000..e21f421b --- /dev/null +++ b/src/gui/itemshortcutwindow.cpp @@ -0,0 +1,71 @@ +/* + * 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() +{ + setWindowName("ItemShortcut"); + // 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; + + const int border = SCROLL_PADDING * 2 + getPadding() * 2; + setMinWidth(mItems->getBoxWidth() + border); + setMinHeight(mItems->getBoxHeight() + border); + setMaxWidth(mItems->getBoxWidth() * mItems->getMaxItems() + border); + setMaxHeight(mItems->getBoxHeight() * mItems->getMaxItems() + border); + + mScrollArea = new ScrollArea(mItems); + mScrollArea->setPosition(SCROLL_PADDING, SCROLL_PADDING); + mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mScrollArea->setOpaque(false); + + add(mScrollArea); + + loadWindowState(); +} + +ItemShortcutWindow::~ItemShortcutWindow() +{ + delete mItems; + delete mScrollArea; +} + +void ItemShortcutWindow::widgetResized(const gcn::Event &event) +{ + Window::widgetResized(event); + + const gcn::Rectangle &area = getChildrenArea(); + + mScrollArea->setSize( + area.width - SCROLL_PADDING, + area.height - SCROLL_PADDING); +} diff --git a/src/gui/itemshortcutwindow.h b/src/gui/itemshortcutwindow.h new file mode 100644 index 00000000..017df5ec --- /dev/null +++ b/src/gui/itemshortcutwindow.h @@ -0,0 +1,61 @@ +/* + * 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" + +class ItemShortcutContainer; +class ScrollArea; + +/** + * A window around the ItemShortcutContainer. + * + * \ingroup Interface + */ +class ItemShortcutWindow : public Window +{ + public: + /** + * Constructor. + */ + ItemShortcutWindow(); + + /** + * Destructor. + */ + ~ItemShortcutWindow(); + + /** + * Called whenever the widget changes size. + */ + void widgetResized(const gcn::Event &event); + + private: + ItemShortcutContainer *mItems; + + ScrollArea *mScrollArea; +}; + +extern ItemShortcutWindow *itemShortcutWindow; + +#endif diff --git a/src/gui/linkhandler.h b/src/gui/linkhandler.h index ecc05b13..fc9da6da 100644 --- a/src/gui/linkhandler.h +++ b/src/gui/linkhandler.h @@ -24,6 +24,8 @@ #include <string> +#include <string> + /** * A simple interface to windows that need to handle links from BrowserBox * widget. diff --git a/src/gui/login.cpp b/src/gui/login.cpp index e91f7616..e68a8da1 100644 --- a/src/gui/login.cpp +++ b/src/gui/login.cpp @@ -50,6 +50,7 @@ LoginDialog::LoginDialog(LoginData *loginData): { gcn::Label *userLabel = new gcn::Label(_("Name:")); gcn::Label *passLabel = new gcn::Label(_("Password:")); +#ifdef EATHENA_SUPPORT gcn::Label *serverLabel = new gcn::Label(_("Server:")); gcn::Label *portLabel = new gcn::Label(_("Port:")); gcn::Label *dropdownLabel = new gcn::Label(_("Recent:")); @@ -61,15 +62,18 @@ LoginDialog::LoginDialog(LoginData *loginData): MAX_SERVER_LIST_SIZE); mServerListBox = new ListBox(mServerList); mServerScrollArea = new ScrollArea; +#endif mUserField = new TextField(mLoginData->username); mPassField = new PasswordField(mLoginData->password); +#ifdef EATHENA_SUPPORT mServerField = new TextField(mServerList->getServerAt(0)); mPortField = new TextField(mServerList->getPortAt(0)); mServerDropDown = new DropDown(mServerList, mServerScrollArea, mServerListBox); mServerDropDown->setOpaque(false); +#endif mKeepCheck = new CheckBox(_("Remember Username"), mLoginData->remember); mOkButton = new Button(_("OK"), "ok", this); @@ -78,32 +82,42 @@ LoginDialog::LoginDialog(LoginData *loginData): mUserField->setActionEventId("ok"); mPassField->setActionEventId("ok"); +#ifdef EATHENA_SUPPORT mServerField->setActionEventId("ok"); mPortField->setActionEventId("ok"); mServerDropDown->setActionEventId("changeSelection"); +#endif mUserField->addKeyListener(this); mPassField->addKeyListener(this); +#ifdef EATHENA_SUPPORT mServerField->addKeyListener(this); mPortField->addKeyListener(this); mServerDropDown->addKeyListener(this); +#endif mUserField->addActionListener(this); mPassField->addActionListener(this); +#ifdef EATHENA_SUPPORT mServerField->addActionListener(this); mPortField->addActionListener(this); mServerDropDown->addActionListener(this); mKeepCheck->addActionListener(this); +#endif place(0, 0, userLabel); place(0, 1, passLabel); +#ifdef EATHENA_SUPPORT place(0, 2, serverLabel); place(0, 3, portLabel); place(0, 4, dropdownLabel); +#endif place(1, 0, mUserField, 3).setPadding(1); place(1, 1, mPassField, 3).setPadding(1); +#ifdef EATHENA_SUPPORT place(1, 2, mServerField, 3).setPadding(1); place(1, 3, mPortField, 3).setPadding(1); place(1, 4, mServerDropDown, 3).setPadding(1); +#endif place(0, 5, mKeepCheck, 4); place(0, 6, mRegisterButton).setHAlign(LayoutCell::LEFT); place(2, 6, mCancelButton); @@ -126,8 +140,10 @@ void LoginDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "ok" && canSubmit()) { +#ifdef EATHENA_SUPPORT mLoginData->hostname = mServerField->getText(); mLoginData->port = getUShort(mPortField->getText()); +#endif mLoginData->username = mUserField->getText(); mLoginData->password = mPassField->getText(); mLoginData->remember = mKeepCheck->isSelected(); @@ -135,21 +151,32 @@ void LoginDialog::action(const gcn::ActionEvent &event) mOkButton->setEnabled(false); mRegisterButton->setEnabled(false); +#ifdef EATHENA_SUPPORT mServerList->save(mServerField->getText(), mPortField->getText()); - state = ACCOUNT_STATE; + state = STATE_ACCOUNT; +#else + state = STATE_LOGIN_ATTEMPT; +#endif } +#ifdef EATHENA_SUPPORT else if (event.getId() == "changeSelection") { int selected = mServerListBox->getSelected(); mServerField->setText(mServerList->getServerAt(selected)); mPortField->setText(mServerList->getPortAt(selected)); } +#endif else if (event.getId() == "cancel") { - state = EXIT_STATE; +#ifdef TMWSERV_SUPPORT + state = STATE_SWITCH_ACCOUNTSERVER; +#else + state = STATE_EXIT; +#endif } else if (event.getId() == "register") { +#ifdef EATHENA_SUPPORT // Transfer these fields on to the register dialog mLoginData->hostname = mServerField->getText(); if (isUShort(mPortField->getText())) @@ -160,10 +187,11 @@ void LoginDialog::action(const gcn::ActionEvent &event) { mLoginData->port = 6901; } +#endif mLoginData->username = mUserField->getText(); mLoginData->password = mPassField->getText(); - state = REGISTER_STATE; + state = STATE_REGISTER; } } @@ -176,11 +204,14 @@ bool LoginDialog::canSubmit() { return !mUserField->getText().empty() && !mPassField->getText().empty() && +#ifdef EATHENA_SUPPORT !mServerField->getText().empty() && isUShort(mPortField->getText()) && - state == LOGIN_STATE; +#endif + state == STATE_LOGIN; } +#ifdef EATHENA_SUPPORT bool LoginDialog::isUShort(const std::string &str) { if (str.empty()) @@ -312,3 +343,4 @@ std::string LoginDialog::DropDownList::getPortAt(int i) } return mPorts.at(i); } +#endif diff --git a/src/gui/login.h b/src/gui/login.h index 1f0f2ddb..9a97cd4d 100644 --- a/src/gui/login.h +++ b/src/gui/login.h @@ -27,13 +27,17 @@ #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> +#ifdef EATHENA_SUPPORT #include <guichan/listmodel.hpp> +#endif #include "window.h" -class DropDown; class LoginData; +#ifdef EATHENA_SUPPORT +class DropDown; class ScrollArea; +#endif /** * The login dialog. @@ -68,6 +72,7 @@ class LoginDialog : public Window, public gcn::ActionListener, */ bool canSubmit(); +#ifdef EATHENA_SUPPORT /** * Function to decide whether string is an unsigned short or not * @@ -86,11 +91,14 @@ class LoginDialog : public Window, public gcn::ActionListener, */ static unsigned short getUShort(const std::string &str); - DropDown *mServerDropDown; +#endif gcn::TextField *mUserField; gcn::TextField *mPassField; +#ifdef EATHENA_SUPPORT gcn::TextField *mServerField; gcn::TextField *mPortField; + DropDown *mServerDropDown; +#endif gcn::CheckBox *mKeepCheck; gcn::Button *mOkButton; gcn::Button *mCancelButton; @@ -98,6 +106,7 @@ class LoginDialog : public Window, public gcn::ActionListener, LoginData *mLoginData; +#ifdef EATHENA_SUPPORT /** * Helper class to keep a list of all the recent entries for the * dropdown @@ -125,6 +134,7 @@ class LoginDialog : public Window, public gcn::ActionListener, DropDownList *mServerList; gcn::ListBox *mServerListBox; gcn::ScrollArea *mServerScrollArea; +#endif }; #endif diff --git a/src/gui/magic.cpp b/src/gui/magic.cpp new file mode 100644 index 00000000..0e56e853 --- /dev/null +++ b/src/gui/magic.cpp @@ -0,0 +1,92 @@ +/* + * 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 + */ + +#include <guichan/widgets/label.hpp> +#include <guichan/widgets/container.hpp> + +#include "magic.h" + +#include "button.h" + +#include "../localplayer.h" + +#include "../utils/dtor.h" +#include "../utils/gettext.h" + +MagicDialog::MagicDialog(): + Window(_("Magic")) +{ + setWindowName("Magic"); + setCloseButton(true); + setDefaultSize(255, 30, 175, 225); + + gcn::Button *spellButton1 = new Button(_("Cast Test Spell 1"), "spell_1", this); + gcn::Button *spellButton2 = new Button(_("Cast Test Spell 2"), "spell_2", this); + gcn::Button *spellButton3 = new Button(_("Cast Test Spell 3"), "spell_3", this); + + spellButton1->setPosition(10, 30); + spellButton2->setPosition(10, 60); + spellButton3->setPosition(10, 90); + + add(spellButton1); + add(spellButton2); + add(spellButton3); + + update(); + + setLocationRelativeTo(getParent()); + loadWindowState(); +} + +MagicDialog::~MagicDialog() +{ +} + +void MagicDialog::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "spell_1") + { + player_node->useSpecial(1); + } + if (event.getId() == "spell_2") + { + player_node->useSpecial(2); + } + if (event.getId() == "spell_3") + { + player_node->useSpecial(3); + } + else if (event.getId() == "close") + { + setVisible(false); + } +} + +void MagicDialog::draw(gcn::Graphics *g) +{ + update(); + + Window::draw(g); +} + +void MagicDialog::update() +{ +} diff --git a/src/gui/magic.h b/src/gui/magic.h new file mode 100644 index 00000000..48146b9c --- /dev/null +++ b/src/gui/magic.h @@ -0,0 +1,75 @@ +/* + * 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 + */ + +#ifndef _TMW_MAGIC_H +#define _TMW_MAGIC_H + +#include <guichan/actionlistener.hpp> + +#include "window.h" +#include "gccontainer.h" + +#include "../guichanfwd.h" + + +/** + * The skill dialog. + * + * \ingroup Interface + */ +class MagicDialog : public Window, public gcn::ActionListener +{ + public: + /** + * Constructor. + */ + MagicDialog(); + + /** + * Destructor. + */ + ~MagicDialog(); + + /** + * Called when receiving actions from widget. + */ + void action(const gcn::ActionEvent &event); + + /** + * Update the tabs in this dialog + */ + void update(); + + /** + * Draw this window. + */ + void draw(gcn::Graphics *g); + + private: + +}; + + + + +extern MagicDialog *magicDialog; + +#endif diff --git a/src/gui/menuwindow.cpp b/src/gui/menuwindow.cpp index 0dcc999f..25ece461 100644 --- a/src/gui/menuwindow.cpp +++ b/src/gui/menuwindow.cpp @@ -37,6 +37,11 @@ extern Window *emoteWindow; extern Window *setupWindow; extern Window *skillDialog; extern Window *statusWindow; +#ifdef TMWSERV_SUPPORT +extern Window *buddyWindow; +extern Window *guildWindow; +extern Window *magicDialog; +#endif namespace { struct MenuWindowListener : public gcn::ActionListener @@ -49,7 +54,7 @@ namespace { } MenuWindow::MenuWindow(): - Window("") + Window() { setResizable(false); setWindowName("Menu"); @@ -59,14 +64,19 @@ MenuWindow::MenuWindow(): // Buttons static const char *buttonNames[] = { - _("Chat"), - _("Status"), - _("Equipment"), - _("Inventory"), - _("Skills"), - _("Shortcut"), - _("Emote"), - _("Setup"), + N_("Chat"), + N_("Status"), + N_("Equipment"), + N_("Inventory"), + N_("Skills"), +#ifdef TMWSERV_SUPPORT + N_("Magic"), + N_("Guilds"), + N_("Buddys"), +#endif + N_("Shortcut"), + N_("Emote"), + N_("Setup"), 0 }; int x = 0, h = 0; @@ -94,35 +104,49 @@ void MenuWindowListener::action(const gcn::ActionEvent &event) { Window *window = NULL; - if (event.getId() == _("Chat")) + if (event.getId() == "Chat") { window = chatWindow; } - else if (event.getId() == _("Status")) + else if (event.getId() == "Status") { window = statusWindow; } - else if (event.getId() == _("Equipment")) + else if (event.getId() == "Equipment") { window = equipmentWindow; } - else if (event.getId() == _("Inventory")) + else if (event.getId() == "Inventory") { window = inventoryWindow; } - else if (event.getId() == _("Skills")) + else if (event.getId() == "Skills") { window = skillDialog; } - else if (event.getId() == _("Shortcut")) +#ifdef TMWSERV_SUPPORT + else if (event.getId() == "Magic") + { + window = magicDialog; + } + else if (event.getId() == "Guilds") + { + window = guildWindow; + } + else if (event.getId() == "Buddys") + { + window = buddyWindow; + } +#endif + else if (event.getId() == "Shortcut") { window = itemShortcutWindow; } - else if (event.getId() == _("Emote")) + else if (event.getId() == "Emote") { window = emoteWindow; } - else if (event.getId() == _("Setup")) + else if (event.getId() == "Setup") { window = setupWindow; } diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index 55c6b3c5..4347c9cc 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -129,8 +129,14 @@ void Minimap::draw(gcn::Graphics *graphics) if (mMapImage->getWidth() > a.width || mMapImage->getHeight() > a.height) { +#ifdef TMWSERV_SUPPORT + const Vector &p = player_node->getPosition(); + mapOriginX = (int) (((a.width) / 2) - (int) (p.x * mProportion) / 32); + mapOriginY = (int) (((a.height) / 2) - (int) (p.y * mProportion) / 32); +#else mapOriginX = (int) (((a.width) / 2) - (player_node->mX * mProportion)); mapOriginY = (int) (((a.height) / 2) - (player_node->mY * mProportion)); +#endif const int minOriginX = a.width - mMapImage->getWidth(); const int minOriginY = a.height - mMapImage->getHeight(); @@ -181,10 +187,11 @@ void Minimap::draw(gcn::Graphics *graphics) } const int offset = (int) ((dotSize - 1) * mProportion); + const Vector &pos = being->getPosition(); graphics->fillRectangle(gcn::Rectangle( - (int) (being->mX * mProportion) + mapOriginX - offset, - (int) (being->mY * mProportion) + mapOriginY - offset, + (int) (pos.x * mProportion) / 32 + mapOriginX - offset, + (int) (pos.x * mProportion) / 32 + mapOriginY - offset, dotSize, dotSize)); } diff --git a/src/gui/ministatus.cpp b/src/gui/ministatus.cpp index 25f17bd3..5bc25bdb 100644 --- a/src/gui/ministatus.cpp +++ b/src/gui/ministatus.cpp @@ -30,8 +30,7 @@ #include "../utils/stringutils.h" -MiniStatusWindow::MiniStatusWindow(): - Window("") +MiniStatusWindow::MiniStatusWindow() { setWindowName("MiniStatus"); setResizable(false); @@ -39,19 +38,30 @@ MiniStatusWindow::MiniStatusWindow(): setTitleBarHeight(0); mHpBar = new ProgressBar(1.0f, 100, 20, 0, 171, 34); +#ifdef EATHENA_SUPPORT mMpBar = new ProgressBar(1.0f, 100, 20, 26, 102, 230); mXpBar = new ProgressBar(1.0f, 100, 20, 143, 192, 211); +#endif mHpBar->setPosition(0, 3); +#ifdef EATHENA_SUPPORT mMpBar->setPosition(mHpBar->getWidth() + 3, 3); mXpBar->setPosition(mMpBar->getX() + mMpBar->getWidth() + 3, 3); +#endif add(mHpBar); +#ifdef EATHENA_SUPPORT add(mMpBar); add(mXpBar); +#endif +#ifdef EATHENA_SUPPORT setContentSize(mXpBar->getX() + mXpBar->getWidth(), mXpBar->getY() + mXpBar->getHeight()); +#else + setContentSize(mHpBar->getX() + mHpBar->getWidth(), + mHpBar->getY() + mHpBar->getHeight()); +#endif setDefaultSize(0, 0, getWidth(), getHeight()); loadWindowState(); } @@ -77,11 +87,13 @@ extern volatile int tick_time; void MiniStatusWindow::update() { // HP Bar coloration - if (player_node->mHp < int(player_node->mMaxHp / 3)) + int maxHp = player_node->getMaxHp(); + int hp = player_node->getHp(); + if (hp < int(maxHp / 3)) { mHpBar->setColor(223, 32, 32); // Red } - else if (player_node->mHp < int((player_node->mMaxHp / 3) * 2)) + else if (hp < int((maxHp / 3) * 2)) { mHpBar->setColor(230, 171, 34); // Orange } @@ -90,18 +102,23 @@ void MiniStatusWindow::update() mHpBar->setColor(0, 171, 34); // Green } +#ifdef EATHENA_SUPPORT float xp = (float) player_node->getXp() / player_node->mXpForNextLevel; if (xp != xp) xp = 0.0f; // check for NaN if (xp < 0.0f) xp = 0.0f; // make sure the experience isn't negative (uninitialized pointer most likely) if (xp > 1.0f) xp = 1.0f; +#endif - mHpBar->setProgress((float) player_node->mHp / player_node->mMaxHp); + mHpBar->setProgress((float) hp / maxHp); +#ifdef EATHENA_SUPPORT mMpBar->setProgress((float) player_node->mMp / player_node->mMaxMp); mXpBar->setProgress(xp); +#endif // Update labels - mHpBar->setText(toString(player_node->mHp)); + mHpBar->setText(toString(player_node->getHp())); +#ifdef EATHENA_SUPPORT mMpBar->setText(toString(player_node->mMp)); std::stringstream updatedText; @@ -121,11 +138,11 @@ void MiniStatusWindow::update() */ mXpBar->setText(updatedText.str()); +#endif for (unsigned int i = 0; i < mIcons.size(); i++) if (mIcons[i]) mIcons[i]->update(tick_time * 10); - } void MiniStatusWindow::draw(gcn::Graphics *graphics) @@ -137,7 +154,11 @@ void MiniStatusWindow::draw(gcn::Graphics *graphics) void MiniStatusWindow::drawIcons(Graphics *graphics) { // Draw icons +#ifdef TMWSERV_SUPPORT + int icon_x = mHpBar->getX() + mHpBar->getWidth() + 4; +#else int icon_x = mXpBar->getX() + mXpBar->getWidth() + 4; +#endif for (unsigned int i = 0; i < mIcons.size(); i++) { if (mIcons[i]) { mIcons[i]->draw(graphics, icon_x, 3); diff --git a/src/gui/ministatus.h b/src/gui/ministatus.h index f262a2a0..b69f9a14 100644 --- a/src/gui/ministatus.h +++ b/src/gui/ministatus.h @@ -66,8 +66,10 @@ class MiniStatusWindow : public Window * Mini Status Bars */ ProgressBar *mHpBar; +#ifdef EATHENA_SUPPORT ProgressBar *mMpBar; ProgressBar *mXpBar; +#endif std::vector<AnimatedSprite *> mIcons; }; diff --git a/src/gui/npc_text.cpp b/src/gui/npc_text.cpp index e6f039a0..48f2adb7 100644 --- a/src/gui/npc_text.cpp +++ b/src/gui/npc_text.cpp @@ -95,7 +95,7 @@ void NpcTextDialog::action(const gcn::ActionEvent &event) current_npc->nextDialog(); addText("\n> Next\n"); } else if (mState == NPC_TEXT_STATE_CLOSE || - mState == NPC_TEXT_STATE_NEXT && !current_npc) { + (mState == NPC_TEXT_STATE_NEXT && !current_npc)) { setText(""); setVisible(false); if (current_npc) current_npc->handleDeath(); diff --git a/src/gui/npclistdialog.cpp b/src/gui/npclistdialog.cpp index 78d43fd2..82e05fd5 100644 --- a/src/gui/npclistdialog.cpp +++ b/src/gui/npclistdialog.cpp @@ -76,6 +76,11 @@ std::string NpcListDialog::getElementAt(int i) return mItems[i]; } +void NpcListDialog::addItem(const std::string &item) +{ + mItems.push_back(item); +} + void NpcListDialog::parseItems(const std::string &itemString) { std::istringstream iss(itemString); diff --git a/src/gui/npclistdialog.h b/src/gui/npclistdialog.h index 57d6247a..7e37c7e6 100644 --- a/src/gui/npclistdialog.h +++ b/src/gui/npclistdialog.h @@ -61,6 +61,11 @@ class NpcListDialog : public Window, public gcn::ActionListener, std::string getElementAt(int i); /** + * Adds an item to the option list. + */ + void addItem(const std::string &); + + /** * Fills the options list for an NPC dialog. * * @param itemString A string with the options separated with colons. diff --git a/src/gui/npcpostdialog.cpp b/src/gui/npcpostdialog.cpp new file mode 100644 index 00000000..3a72b21b --- /dev/null +++ b/src/gui/npcpostdialog.cpp @@ -0,0 +1,101 @@ +/* + * The Mana World + * Copyright 2008 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 "npcpostdialog.h" +#include "textbox.h" +#include "textfield.h" +#include "button.h" +#include "scrollarea.h" +#include "chat.h" + +#include "../net/gameserver/player.h" +#include "../utils/gettext.h" + +#include <guichan/widgets/label.hpp> + +NpcPostDialog::NpcPostDialog(): + Window(_("NPC")) +{ + setContentSize(400, 180); + + // create text field for receiver + gcn::Label *senderText = new gcn::Label("To:"); + senderText->setPosition(5, 5); + mSender = new TextField(); + mSender->setPosition(senderText->getWidth() + 5, 5); + mSender->setWidth(65); + + // create button for sending + Button *sendButton = new Button(_("Send"), "send", this); + sendButton->setPosition(400-sendButton->getWidth(), + 170-sendButton->getHeight()); + Button *cancelButton = new Button(_("Cancel"), "cancel", this); + cancelButton->setPosition(sendButton->getX() - (cancelButton->getWidth() + 2), + sendButton->getY()); + + // create textfield for letter + mText = new TextBox(); + mText->setHeight(400 - (mSender->getHeight() + sendButton->getHeight())); + mText->setEditable(true); + + // create scroll box for letter text + ScrollArea *scrollArea = new ScrollArea(mText); + scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + scrollArea->setDimension(gcn::Rectangle( + 5, mSender->getHeight() + 5, + 380, 140 - (mSender->getHeight() + sendButton->getHeight()))); + + add(senderText); + add(mSender); + add(scrollArea); + add(sendButton); + add(cancelButton); + + setLocationRelativeTo(getParent()); +} + +void NpcPostDialog::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "send") + { + if (mSender->getText().empty() || mText->getText().empty()) + { + chatWindow->chatLog("Failed to send as sender or letter invalid"); + } + else + { + Net::GameServer::Player::sendLetter(mSender->getText(), mText->getText()); + } + setVisible(false); + clear(); + } + else if (event.getId() == "cancel") + { + setVisible(false); + clear(); + } +} + +void NpcPostDialog::clear() +{ + mSender->setText(""); + mText->setText(""); +} diff --git a/src/gui/npcpostdialog.h b/src/gui/npcpostdialog.h new file mode 100644 index 00000000..1956c877 --- /dev/null +++ b/src/gui/npcpostdialog.h @@ -0,0 +1,55 @@ +/* + * The Mana World + * Copyright 2008 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_GUI_NPCPOSTDIALOG_H +#define _TMW_GUI_NPCPOSTDIALOG_H + +#include <guichan/actionlistener.hpp> + +#include "window.h" + +class TextBox; +class TextField; + +class NpcPostDialog : public Window, public gcn::ActionListener +{ +public: + /** + * Constructor + */ + NpcPostDialog(); + + /** + * Called when receiving actions from the widgets. + */ + void action(const gcn::ActionEvent &event); + + /** + * Clear the contents of the dialog + */ + void clear(); + +private: + TextBox *mText; + TextField *mSender; +}; + +#endif diff --git a/src/gui/partywindow.cpp b/src/gui/partywindow.cpp new file mode 100644 index 00000000..c4a1c780 --- /dev/null +++ b/src/gui/partywindow.cpp @@ -0,0 +1,145 @@ +/* + * The Mana World + * Copyright 2008 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 "partywindow.h" +#include "chat.h" + +#include "widgets/avatar.h" + +#include "../utils/gettext.h" +#include "../net/chatserver/party.h" + +PartyWindow::PartyWindow() : Window(_("Party")) +{ + setWindowName("Party"); + setVisible(false); + setResizable(false); + setCaption(_("Party")); + setCloseButton(true); + setMinWidth(110); + setMinHeight(200); + setDefaultSize(620, 300, 110, 200); + + loadWindowState(); +} + +PartyWindow::~PartyWindow() +{ + mPartyMembers.clear(); +} + +void PartyWindow::draw(gcn::Graphics *graphics) +{ + Window::draw(graphics); +} + +void PartyWindow::addPartyMember(const std::string &memberName) +{ + // check to see if player is already in the party + PartyList::iterator itr = mPartyMembers.begin(), + itr_end = mPartyMembers.end(); + + while (itr != itr_end) + { + if ((*itr).name == memberName) + { + // already in the party, dont add + return; + } + ++itr; + } + + // create new party member + PartyMember player; + player.name = memberName; + mPartyMembers.push_back(player); + + // add avatar of the new member to window + Avatar *avatar = new Avatar(memberName); + add(avatar, 0, (mPartyMembers.size() - 1)*14); + + // show the window + if (mPartyMembers.size() > 0) + { + setVisible(true); + } +} + +void PartyWindow::removePartyMember(const std::string &memberName) +{ + // remove the party member + PartyList::iterator itr = mPartyMembers.begin(), + itr_end = mPartyMembers.end(); + + while (itr != itr_end) + { + if ((*itr).name == memberName) + { + mPartyMembers.erase(itr); + break; + } + ++itr; + } + + // if no-one left, remove the party window + if (mPartyMembers.size() < 1) + { + setVisible(false); + } +} + +void PartyWindow::showPartyInvite(const std::string &inviter) +{ + // check there isnt already an invite showing + if (mPartyInviter != "") + { + chatWindow->chatLog("Received party request, but one already exists", + BY_SERVER); + return; + } + + // log invite + std::string msg = inviter + " has invited you to join their party"; + chatWindow->chatLog(msg, BY_SERVER); + + // show invite + acceptDialog = new ConfirmDialog("Accept Party Invite", msg, this); + acceptDialog->addActionListener(this); + + mPartyInviter = inviter; +} + +void PartyWindow::action(const gcn::ActionEvent &event) +{ + const std::string &eventId = event.getId(); + + // check if they accepted the invite + if (eventId == "yes") + { + chatWindow->chatLog("Accepted invite from " + mPartyInviter); + Net::ChatServer::Party::acceptInvite(mPartyInviter); + mPartyInviter = ""; + } + else if (eventId == "no") + { + mPartyInviter = ""; + } +} diff --git a/src/gui/partywindow.h b/src/gui/partywindow.h new file mode 100644 index 00000000..e79bf392 --- /dev/null +++ b/src/gui/partywindow.h @@ -0,0 +1,96 @@ +/* + * The Mana World + * Copyright 2008 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_PARTYWINDOW_H +#define _TMW_PARTYWINDOW_H + +#include "window.h" +#include "confirm_dialog.h" + +#include <string> +#include <vector> + +#include <guichan/actionevent.hpp> +#include <guichan/actionlistener.hpp> + +/** + * Party Member + * Used for storing players in the party + */ +struct PartyMember +{ + std::string name; + int vitality; +}; + +/** + * Party Window. + * + * \ingroup Interface + */ +class PartyWindow : public Window, gcn::ActionListener +{ + public: + /** + * Constructor. + */ + PartyWindow(); + + /** + * Release all the players created + */ + ~PartyWindow(); + + /** + * Draws the party window + */ + void draw(gcn::Graphics *graphics); + + /** + * Add party member + */ + void addPartyMember(const std::string &memberName); + + /** + * Remove party member + */ + void removePartyMember(const std::string &memberName); + + /** + * Show party invite + */ + void showPartyInvite(const std::string &inviter); + + /** + * Handle events + */ + void action(const gcn::ActionEvent &event); + + private: + typedef std::vector<PartyMember> PartyList; + PartyList mPartyMembers; + std::string mPartyInviter; + ConfirmDialog *acceptDialog; +}; + +extern PartyWindow *partyWindow; + +#endif diff --git a/src/gui/passwordfield.h b/src/gui/passwordfield.h index 42f8d187..86195bd1 100644 --- a/src/gui/passwordfield.h +++ b/src/gui/passwordfield.h @@ -22,8 +22,6 @@ #ifndef PASSWORDFIELD_H #define PASSWORDFIELD_H -#include <string> - #include "textfield.h" /** diff --git a/src/gui/playerbox.cpp b/src/gui/playerbox.cpp index f99ce6ef..2bfa798c 100644 --- a/src/gui/playerbox.cpp +++ b/src/gui/playerbox.cpp @@ -81,10 +81,14 @@ void PlayerBox::draw(gcn::Graphics *graphics) if (mPlayer) { // Draw character - int x, y, bs; - bs = getFrameSize(); - x = getWidth() / 2 - 16 + bs; - y = getHeight() / 2 + bs; + const int bs = getFrameSize(); +#ifdef TMWSERV_SUPPORT + const int x = getWidth() / 2 + bs; + const int y = getHeight() - bs - 8; + mPlayer->draw(static_cast<Graphics*>(graphics), x, y); +#else + const int x = getWidth() / 2 - 16 + bs; + const int y = getHeight() / 2 + bs; for (int i = 0; i < Being::VECTOREND_SPRITE; i++) { if (mPlayer->getSprite(i)) @@ -92,6 +96,7 @@ void PlayerBox::draw(gcn::Graphics *graphics) mPlayer->getSprite(i)->draw(static_cast<Graphics*>(graphics), x, y); } } +#endif } if (config.getValue("guialpha", 0.8) != mAlpha) diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 62e44794..ced44f42 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -36,8 +36,10 @@ #include "../npc.h" #include "../player_relations.h" +#ifdef EATHENA_SUPPORT #include "../net/messageout.h" -#include "../net/protocol.h" +#include "../net/ea/protocol.h" +#endif #include "../resources/itemdb.h" @@ -102,6 +104,8 @@ void PopupMenu::showPopup(int x, int y, Being *being) //mBrowserBox->addRow(_("@@follow|Follow ") + name + "@@"); //mBrowserBox->addRow(_("@@buddy|Add ") + name + " to Buddy List@@"); + mBrowserBox->addRow(strprintf(_("@@guild|Invite %s@@"), name.c_str())); + mBrowserBox->addRow(strprintf(_("@@party|Invite %s to join your party@@"), name.c_str())); mBrowserBox->addRow("##3---"); mBrowserBox->addRow(strprintf(_("@@party-invite|Invite %s to party@@"), name.c_str())); @@ -163,7 +167,7 @@ void PopupMenu::handleLink(const std::string& link) player_node->trade(being); tradePartnerName = being->getName(); } - +#ifdef EATHENA_SUPPORT // Attack action else if (link == "attack" && being && @@ -171,7 +175,7 @@ void PopupMenu::handleLink(const std::string& link) { player_node->attack(being, true); } - +#endif else if (link == "unignore" && being && being->getType() == Being::PLAYER) @@ -199,7 +203,21 @@ void PopupMenu::handleLink(const std::string& link) { player_relations.setRelation(being->getName(), PlayerRelation::FRIEND); } +#ifdef TMWSERV_SUPPORT + // Guild action + else if (link == "guild" && + being != NULL && + being->getType() == Being::PLAYER) + { + player_node->inviteToGuild(being); + } + // Add player to your party + else if (link == "party") + { + player_node->inviteToParty(being->getName()); + } +#endif /* // Follow Player action else if (link == "follow") @@ -232,6 +250,9 @@ void PopupMenu::handleLink(const std::string& link) assert(mItem); if (mItem->isEquipment()) { +#ifdef TMWSERV_SUPPORT + player_node->equipItem(mItem); +#else if (mItem->isEquipped()) { player_node->unequipItem(mItem); @@ -240,10 +261,15 @@ void PopupMenu::handleLink(const std::string& link) { player_node->equipItem(mItem); } +#endif } else { +#ifdef TMWSERV_SUPPORT + player_node->useItem(mItem->getInvIndex()); +#else player_node->useItem(mItem); +#endif } } @@ -252,10 +278,15 @@ void PopupMenu::handleLink(const std::string& link) chatWindow->addItemText(mItem->getInfo().getName()); } + else if (link == "split") + { + new ItemAmountWindow(AMOUNT_ITEM_SPLIT, inventoryWindow, mItem); + } else if (link == "drop") { new ItemAmountWindow(AMOUNT_ITEM_DROP, inventoryWindow, mItem); } +#ifdef EATHENA_SUPPORT else if (link == "party-invite" && being && being->getType() == Being::PLAYER) @@ -264,6 +295,7 @@ void PopupMenu::handleLink(const std::string& link) outMsg.writeInt16(CMSG_PARTY_INVITE); outMsg.writeInt32(being->getId()); } +#endif // Unknown actions else @@ -286,15 +318,23 @@ void PopupMenu::showPopup(int x, int y, Item *item) if (item->isEquipment()) { +#ifdef TMWSERV_SUPPORT + mBrowserBox->addRow(_("@@use|Equip@@")); +#else if (item->isEquipped()) mBrowserBox->addRow(_("@@use|Unequip@@")); else mBrowserBox->addRow(_("@@use|Equip@@")); +#endif } else mBrowserBox->addRow(_("@@use|Use@@")); mBrowserBox->addRow(_("@@drop|Drop@@")); +#ifdef TMWSERV_SUPPORT + if (!item->isEquipment()) + mBrowserBox->addRow(_("@@split|Split@@")); +#endif mBrowserBox->addRow(_("@@chat|Add to Chat@@")); mBrowserBox->addRow("##3---"); mBrowserBox->addRow(_("@@cancel|Cancel@@")); diff --git a/src/gui/progressbar.cpp b/src/gui/progressbar.cpp index 0f59371a..85f21604 100644 --- a/src/gui/progressbar.cpp +++ b/src/gui/progressbar.cpp @@ -41,6 +41,9 @@ ProgressBar::ProgressBar(float progress, mRed(red), mGreen(green), mBlue(blue), mRedToGo(red), mGreenToGo(green), mBlueToGo(blue) { + mProgressToGo = mProgress = 0.0f; + mSmoothProgress = mSmoothColorChange = true; + setProgress(progress); setWidth(width); setHeight(height); @@ -90,13 +93,31 @@ ProgressBar::~ProgressBar() void ProgressBar::logic() { - // Smoothly changing the color for a nicer effect. - if (mRedToGo > mRed) mRed++; - if (mRedToGo < mRed) mRed--; - if (mGreenToGo > mGreen) mGreen++; - if (mGreenToGo < mGreen) mGreen--; - if (mBlueToGo > mBlue) mBlue++; - if (mBlueToGo < mBlue) mBlue--; + if (mSmoothColorChange) + { + // Smoothly changing the color for a nicer effect. + if (mRedToGo > mRed) mRed++; + if (mRedToGo < mRed) mRed--; + if (mGreenToGo > mGreen) mGreen++; + if (mGreenToGo < mGreen) mGreen--; + if (mBlueToGo > mBlue) mBlue++; + if (mBlueToGo < mBlue) mBlue--; + } + else + { + mRed = mRedToGo; + mGreen = mGreenToGo; + mBlue = mBlueToGo; + } + + if (mSmoothProgress) + { + // Smoothly showing the progressbar changes. + if (mProgressToGo > mProgress) mProgress = mProgress + 0.005f; + if (mProgressToGo < mProgress) mProgress = mProgress - 0.005f; + } + else + mProgress = mProgressToGo; } void ProgressBar::draw(gcn::Graphics *graphics) @@ -149,9 +170,9 @@ void ProgressBar::draw(gcn::Graphics *graphics) void ProgressBar::setProgress(float progress) { - if (progress < 0.0f) mProgress = 0.0; - else if (progress > 1.0f) mProgress = 1.0; - else mProgress = progress; + if (progress < 0.0f) mProgressToGo = 0.0; + else if (progress > 1.0f) mProgressToGo = 1.0; + else mProgressToGo = progress; } void ProgressBar::setColor(Uint8 red, Uint8 green, Uint8 blue) diff --git a/src/gui/progressbar.h b/src/gui/progressbar.h index 2c1b22da..49bc3edd 100644 --- a/src/gui/progressbar.h +++ b/src/gui/progressbar.h @@ -102,10 +102,27 @@ class ProgressBar : public gcn::Widget const std::string &text() const { return mText; } + /** + * Set wether the progress is moved smoothly. + */ + void setSmoothProgress(bool smoothProgress) + { mSmoothProgress = smoothProgress; } + + /** + * Set wether the color changing is made smoothly. + */ + void setSmoothColorChange(bool smoothColorChange) + { mSmoothColorChange = smoothColorChange; } + + private: - float mProgress; + float mProgress, mProgressToGo; + bool mSmoothProgress; + Uint8 mRed, mGreen, mBlue; Uint8 mRedToGo, mGreenToGo, mBlueToGo; + bool mSmoothColorChange; + std::string mText; static ImageRect mBorder; diff --git a/src/gui/quitdialog.cpp b/src/gui/quitdialog.cpp new file mode 100644 index 00000000..563ed34a --- /dev/null +++ b/src/gui/quitdialog.cpp @@ -0,0 +1,135 @@ +/* + * 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 + */ + +#include "quitdialog.h" +#include <iostream> +#include <string> + +#include <guichan/widgets/label.hpp> + +#include "../main.h" + +#include "button.h" +#include "radiobutton.h" + +#include "../utils/gettext.h" + +QuitDialog::QuitDialog(bool* quitGame, QuitDialog** pointerToMe): + Window(_("Quit"), true, NULL), mQuitGame(quitGame), mMyPointer(pointerToMe) +{ + + mLogoutQuit = new RadioButton(_("Quit"), "quitdialog"); + mForceQuit = new RadioButton(_("Quit"), "quitdialog"); + mSwitchAccountServer = new RadioButton(_("Switch server"), "quitdialog"); + mSwitchCharacter = new RadioButton(_("Switch character"), "quitdialog"); + mOkButton = new Button(_("Ok"), "ok", this); + mCancelButton = new Button(_("Cancel"), "cancel", this); + + setContentSize(200, 91); + + mLogoutQuit->setPosition(5, 5); + mForceQuit->setPosition(5, 5); + mSwitchAccountServer->setPosition(5, 14 + mLogoutQuit->getHeight()); + mSwitchCharacter->setPosition(5, + 23 + mLogoutQuit->getHeight() + mSwitchAccountServer->getHeight()); + mCancelButton->setPosition( + 200 - mCancelButton->getWidth() - 5, + 91 - mCancelButton->getHeight() - 5); + mOkButton->setPosition( + mCancelButton->getX() - mOkButton->getWidth() - 5, + 91 - mOkButton->getHeight() - 5); + + //All states, when we're not logged in to someone. + if (state == STATE_CHOOSE_SERVER || + state == STATE_CONNECT_ACCOUNT || + state == STATE_LOGIN || + state == STATE_LOGIN_ATTEMPT || + state == STATE_UPDATE) + { + mForceQuit->setSelected(true); + add(mForceQuit); + } + else + { + // Only added if we are connected to an accountserver or gameserver + mLogoutQuit->setSelected(true); + add(mLogoutQuit); + add(mSwitchAccountServer); + + // Only added if we are connected to a gameserver + if (state == STATE_GAME) add(mSwitchCharacter); + } + + add(mOkButton); + add(mCancelButton); + + setLocationRelativeTo(getParent()); + setVisible(true); + + mOkButton->requestFocus(); + +} + +QuitDialog::~QuitDialog() +{ + if (mMyPointer) *mMyPointer = NULL; + // Optional widgets, so delete them by hand. + delete mForceQuit; + delete mLogoutQuit; + delete mSwitchAccountServer; + delete mSwitchCharacter; +} + +void +QuitDialog::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "ok") + { + if (mForceQuit->isSelected()) + { + state = STATE_FORCE_QUIT; + } + else if (mLogoutQuit->isSelected()) + { + if ((state == STATE_GAME) && (mQuitGame)) + { + *mQuitGame = true; + } + state = STATE_EXIT; + } + else if (mSwitchAccountServer->isSelected()) + { + if ((state == STATE_GAME) && (mQuitGame)) + { + *mQuitGame = true; + } + state = STATE_SWITCH_ACCOUNTSERVER_ATTEMPT; + } + else if (mSwitchCharacter->isSelected()) + { + if (mQuitGame) *mQuitGame = true; + + state = STATE_SWITCH_CHARACTER; + } + + } + scheduleDelete(); +} diff --git a/src/gui/quitdialog.h b/src/gui/quitdialog.h new file mode 100644 index 00000000..018f1e52 --- /dev/null +++ b/src/gui/quitdialog.h @@ -0,0 +1,70 @@ +/* + * 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 + */ + +#ifndef _TMW_QUITDIALOG_H +#define _TMW_QUITDIALOG_H + +#include <iosfwd> +#include <guichan/actionlistener.hpp> + +#include "window.h" +#include "../guichanfwd.h" +#include "../main.h" + +/** + * The quit dialog. + * + * \ingroup Interface + */ +class QuitDialog : public Window, public gcn::ActionListener { + public: + /** + * Constructor + * + * @quitGame; to be used for getting out of the while loop in Game + * @pointerToMe; will be set to NULL when the QuitDialog is destroyed + */ + QuitDialog(bool* quitGame, QuitDialog** pointerToMe); + + /** + * Destructor + */ + ~QuitDialog(); + + /** + * Called when receiving actions from the widgets. + */ + void action(const gcn::ActionEvent &event); + + private: + gcn::RadioButton *mLogoutQuit; + gcn::RadioButton *mForceQuit; + gcn::RadioButton *mSwitchAccountServer; + gcn::RadioButton *mSwitchCharacter; + gcn::Button *mOkButton; + gcn::Button *mCancelButton; + + bool* mQuitGame; + QuitDialog** mMyPointer; + +}; + +#endif diff --git a/src/gui/register.cpp b/src/gui/register.cpp index c2190501..216ac211 100644 --- a/src/gui/register.cpp +++ b/src/gui/register.cpp @@ -41,19 +41,6 @@ #include "../utils/strprintf.h" #include "../utils/stringutils.h" -/** - * Listener used while dealing with wrong data. It is used to direct the focus - * to the field which contained wrong data when the Ok button was pressed on - * the error notice. - */ -class WrongDataNoticeListener : public gcn::ActionListener { - public: - void setTarget(gcn::TextField *textField); - void action(const gcn::ActionEvent &event); - private: - gcn::TextField *mTarget; -}; - void WrongDataNoticeListener::setTarget(gcn::TextField *textField) { mTarget = textField; @@ -76,15 +63,19 @@ RegisterDialog::RegisterDialog(LoginData *loginData): gcn::Label *userLabel = new gcn::Label(_("Name:")); gcn::Label *passwordLabel = new gcn::Label(_("Password:")); gcn::Label *confirmLabel = new gcn::Label(_("Confirm:")); +#ifdef EATHENA_SUPPORT gcn::Label *serverLabel = new gcn::Label(_("Server:")); gcn::Label *portLabel = new gcn::Label(_("Port:")); +#endif mUserField = new TextField(loginData->username); mPasswordField = new PasswordField(loginData->password); mConfirmField = new PasswordField; +#ifdef EATHENA_SUPPORT mServerField = new TextField(loginData->hostname); mPortField = new TextField(toString(loginData->port)); mMaleButton = new RadioButton(_("Male"), "sex", true); mFemaleButton = new RadioButton(_("Female"), "sex", false); +#endif mRegisterButton = new Button(_("Register"), "register", this); mCancelButton = new Button(_("Cancel"), "cancel", this); @@ -93,15 +84,19 @@ RegisterDialog::RegisterDialog(LoginData *loginData): place(0, 0, userLabel); place(0, 1, passwordLabel); place(0, 2, confirmLabel); +#ifdef EATHENA_SUPPORT place(1, 3, mMaleButton); place(2, 3, mFemaleButton); place(0, 4, serverLabel); place(0, 5, portLabel); +#endif place(1, 0, mUserField, 3).setPadding(2); place(1, 1, mPasswordField, 3).setPadding(2); place(1, 2, mConfirmField, 3).setPadding(2); +#ifdef EATHENA_SUPPORT place(1, 4, mServerField, 3).setPadding(2); place(1, 5, mPortField, 3).setPadding(2); +#endif place = getPlacer(0, 2); place(1, 0, mRegisterButton); place(2, 0, mCancelButton); @@ -110,8 +105,10 @@ RegisterDialog::RegisterDialog(LoginData *loginData): mUserField->addKeyListener(this); mPasswordField->addKeyListener(this); mConfirmField->addKeyListener(this); +#ifdef EATHENA_SUPPORT mServerField->addKeyListener(this); mPortField->addKeyListener(this); +#endif /* TODO: * This is a quick and dirty way to respond to the ENTER key, regardless of @@ -121,14 +118,18 @@ RegisterDialog::RegisterDialog(LoginData *loginData): mUserField->setActionEventId("register"); mPasswordField->setActionEventId("register"); mConfirmField->setActionEventId("register"); - mServerField->setActionEventId("register"); - mPortField->setActionEventId("register"); mUserField->addActionListener(this); mPasswordField->addActionListener(this); mConfirmField->addActionListener(this); + +#ifdef EATHENA_SUPPORT + mServerField->setActionEventId("register"); + mPortField->setActionEventId("register"); + mServerField->addActionListener(this); mPortField->addActionListener(this); +#endif setLocationRelativeTo(getParent()); setVisible(true); @@ -147,7 +148,7 @@ void RegisterDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "cancel") { - state = LOGIN_STATE; + state = STATE_LOGIN; } else if (event.getId() == "register" && canSubmit()) { @@ -196,6 +197,8 @@ void RegisterDialog::action(const gcn::ActionEvent &event) error = 2; } + // TODO: Check if a valid email address was given + if (error > 0) { if (error == 1) @@ -211,23 +214,30 @@ void RegisterDialog::action(const gcn::ActionEvent &event) mWrongDataNoticeListener->setTarget(this->mPasswordField); } - OkDialog *mWrongRegisterNotice = - new OkDialog(_("Error"), errorMsg); - mWrongRegisterNotice->addActionListener(mWrongDataNoticeListener); + OkDialog *dlg = new OkDialog(_("Error"), errorMsg); + dlg->addActionListener(mWrongDataNoticeListener); } else { // No errors detected, register the new user. mRegisterButton->setEnabled(false); - mLoginData->hostname = mServerField->getText(); - mLoginData->port = getUShort(mPortField->getText()); mLoginData->username = mUserField->getText(); mLoginData->password = mPasswordField->getText(); +#ifdef EATHENA_SUPPORT + mLoginData->hostname = mServerField->getText(); + mLoginData->port = getUShort(mPortField->getText()); mLoginData->username += mFemaleButton->isSelected() ? "_F" : "_M"; +#else + mLoginData->email = mEmailField->getText(); +#endif mLoginData->registerLogin = true; - state = ACCOUNT_STATE; +#ifdef TMWSERV_SUPPORT + state = STATE_REGISTER_ATTEMPT; +#else + state = STATE_ACCOUNT; +#endif } } } @@ -242,11 +252,14 @@ bool RegisterDialog::canSubmit() const return !mUserField->getText().empty() && !mPasswordField->getText().empty() && !mConfirmField->getText().empty() && +#ifdef EATHENA_SUPPORT !mServerField->getText().empty() && isUShort(mPortField->getText()) && - state == REGISTER_STATE; +#endif + state == STATE_REGISTER; } +#ifdef EATHENA_SUPPORT bool RegisterDialog::isUShort(const std::string &str) { if (str.empty()) @@ -280,3 +293,4 @@ unsigned short RegisterDialog::getUShort(const std::string &str) } return static_cast<unsigned short>(l); } +#endif diff --git a/src/gui/register.h b/src/gui/register.h index 9588e07e..fde82a40 100644 --- a/src/gui/register.h +++ b/src/gui/register.h @@ -31,7 +31,19 @@ class LoginData; class OkDialog; -class WrongDataNoticeListener; + +/** + * Listener used while dealing with wrong data. It is used to direct the focus + * to the field which contained wrong data when the Ok button was pressed on + * the error notice. + */ +class WrongDataNoticeListener : public gcn::ActionListener { + public: + void setTarget(gcn::TextField *textField); + void action(const gcn::ActionEvent &event); + private: + gcn::TextField *mTarget; +}; /** * The registration dialog. @@ -72,6 +84,7 @@ class RegisterDialog : public Window, public gcn::ActionListener, */ bool canSubmit() const; +#ifdef EATHENA_SUPPORT /** * Function to decide whether string is an unsigned short or not * @@ -89,17 +102,24 @@ class RegisterDialog : public Window, public gcn::ActionListener, * @return the value str represents */ static unsigned short getUShort(const std::string &str); +#endif gcn::TextField *mUserField; gcn::TextField *mPasswordField; gcn::TextField *mConfirmField; +#ifdef EATHENA_SUPPORT gcn::TextField *mServerField; gcn::TextField *mPortField; +#else + gcn::TextField *mEmailField; +#endif gcn::Button *mRegisterButton; gcn::Button *mCancelButton; +#ifdef EATHENA_SUPPORT gcn::RadioButton *mMaleButton; gcn::RadioButton *mFemaleButton; +#endif WrongDataNoticeListener *mWrongDataNoticeListener; diff --git a/src/gui/sell.cpp b/src/gui/sell.cpp index dad6af21..957ff861 100644 --- a/src/gui/sell.cpp +++ b/src/gui/sell.cpp @@ -34,14 +34,23 @@ #include "../units.h" #include "../net/messageout.h" -#include "../net/protocol.h" +#ifdef TMWSERV_SUPPORT +#include "../net/gameserver/player.h" +#else +#include "../net/ea/protocol.h" +#endif #include "../utils/gettext.h" #include "../utils/strprintf.h" +#ifdef TMWSERV_SUPPORT +SellDialog::SellDialog(): + Window(_("Sell")), +#else SellDialog::SellDialog(Network *network): Window(_("Sell")), mNetwork(network), +#endif mMaxItems(0), mAmountItems(0) { setWindowName("Sell"); @@ -112,6 +121,16 @@ void SellDialog::reset() updateButtonsAndLabels(); } +#ifdef TMWSERV_SUPPORT + +void SellDialog::addItem(int item, int amount, int price) +{ + mShopItems->addItem(item, amount, price); + mShopItemList->adjustSize(); +} + +#else + void SellDialog::addItem(const Item *item, int price) { if (!item) @@ -124,6 +143,8 @@ void SellDialog::addItem(const Item *item, int price) mShopItemList->adjustSize(); } +#endif + void SellDialog::action(const gcn::ActionEvent &event) { int selectedItem = mShopItemList->getSelected(); @@ -162,12 +183,17 @@ void SellDialog::action(const gcn::ActionEvent &event) else if (event.getId() == "sell" && mAmountItems > 0 && mAmountItems <= mMaxItems) { +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::tradeWithNPC + (mShopItems->at(selectedItem)->getId(), mAmountItems); +#else // Attempt sell MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_NPC_SELL_REQUEST); outMsg.writeInt16(8); outMsg.writeInt16(mShopItems->at(selectedItem)->getInvIndex()); outMsg.writeInt16(mAmountItems); +#endif mMaxItems -= mAmountItems; mShopItems->getShop()->at(selectedItem)->setQuantity(mMaxItems); diff --git a/src/gui/sell.h b/src/gui/sell.h index c11a7b7c..f64a6fd5 100644 --- a/src/gui/sell.h +++ b/src/gui/sell.h @@ -30,7 +30,9 @@ #include "window.h" class Item; +#ifdef EATHENA_SUPPORT class Network; +#endif class ShopItems; class ShopListBox; @@ -47,7 +49,11 @@ class SellDialog : public Window, gcn::ActionListener, gcn::SelectionListener * * @see Window::Window */ +#ifdef TMWSERV_SUPPORT + SellDialog(); +#else SellDialog(Network *network); +#endif /** * Destructor @@ -62,7 +68,11 @@ class SellDialog : public Window, gcn::ActionListener, gcn::SelectionListener /** * Adds an item to the inventory. */ +#ifdef TMWSERV_SUPPORT + void addItem(int item, int amount, int price); +#else void addItem(const Item *item, int price); +#endif /** * Called when receiving actions from the widgets. @@ -87,7 +97,9 @@ class SellDialog : public Window, gcn::ActionListener, gcn::SelectionListener */ void updateButtonsAndLabels(); +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif gcn::Button *mSellButton; gcn::Button *mQuitButton; gcn::Button *mIncreaseButton; diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp new file mode 100644 index 00000000..a8074b0c --- /dev/null +++ b/src/gui/serverdialog.cpp @@ -0,0 +1,214 @@ +/* + * 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 + */ + +#include <cstdlib> +#include <iostream> +#include <string> + +#include <guichan/widgets/label.hpp> + +#include "serverdialog.h" + +#include "button.h" +#include "listbox.h" +#include "ok_dialog.h" +#include "scrollarea.h" +#include "textfield.h" + +#include "widgets/layout.h" + +#include "../configuration.h" +#include "../log.h" +#include "../logindata.h" +#include "../main.h" + +#include "../utils/gettext.h" +#include "../utils/stringutils.h" + +const short MAX_SERVERLIST = 5; + +int ServersListModel::getNumberOfElements() +{ + return servers.size(); +} + +std::string ServersListModel::getElementAt(int elementIndex) +{ + std::string myServer = ""; + myServer = servers.at(elementIndex).serverName; + myServer += ":"; + myServer += toString(servers.at(elementIndex).port); + return myServer; +} + +void ServersListModel::addFirstElement(Server server) +{ + // Equivalent to push_front + std::vector<Server>::iterator MyIterator = servers.begin(); + servers.insert(MyIterator, 1, server); +} + +void ServersListModel::addElement(Server server) +{ + servers.push_back(server); +} + +ServerDialog::ServerDialog(LoginData *loginData): + Window(_("Choose your server")), mLoginData(loginData) +{ + gcn::Label *serverLabel = new gcn::Label(_("Server:")); + gcn::Label *portLabel = new gcn::Label(_("Port:")); + mServerNameField = new TextField(mLoginData->hostname); + mPortField = new TextField(toString(mLoginData->port)); + + // Add the most used servers from config + mMostUsedServersListModel = new ServersListModel(); + Server currentServer; + std::string currentConfig = ""; + for (int i=0; i<=MAX_SERVERLIST; i++) + { + currentServer.serverName = ""; + currentServer.port = 0; + + currentConfig = "MostUsedServerName" + toString(i); + currentServer.serverName = config.getValue(currentConfig, ""); + + currentConfig = "MostUsedServerPort" + toString(i); + currentServer.port = (short)atoi(config.getValue(currentConfig, "").c_str()); + if (!currentServer.serverName.empty() || currentServer.port != 0) + { + mMostUsedServersListModel->addElement(currentServer); + } + } + + mMostUsedServersListBox = new ListBox(NULL); + mMostUsedServersListBox->setListModel(mMostUsedServersListModel); + mMostUsedServersScrollArea = new ScrollArea(); + mMostUsedServersDropDown = new DropDown(mMostUsedServersListModel, + mMostUsedServersScrollArea, mMostUsedServersListBox); + + mOkButton = new Button(_("Ok"), "connect", this); + mCancelButton = new Button(_("Cancel"), "cancel", this); + + mServerNameField->setActionEventId("connect"); + mPortField->setActionEventId("connect"); + mMostUsedServersDropDown->setActionEventId("changeSelection"); + + mServerNameField->addActionListener(this); + mPortField->addActionListener(this); + mMostUsedServersDropDown->addActionListener(this); + + place(0, 0, serverLabel); + place(0, 1, portLabel); + place(1, 0, mServerNameField, 3).setPadding(2); + place(1, 1, mPortField, 3).setPadding(2); + place(0, 2, mMostUsedServersDropDown, 4).setPadding(2); + place(2, 3, mOkButton); + place(3, 3, mCancelButton); + reflowLayout(250, 0); + + setLocationRelativeTo(getParent()); + setVisible(true); + + if (mServerNameField->getText().empty()) { + mServerNameField->requestFocus(); + } else { + if (mPortField->getText().empty()) { + mPortField->requestFocus(); + } else { + mOkButton->requestFocus(); + } + } +} + +ServerDialog::~ServerDialog() +{ + delete mMostUsedServersListModel; + delete mMostUsedServersScrollArea; +} + +void +ServerDialog::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "ok") + { + // Give focus back to the server dialog. + mServerNameField->requestFocus(); + } + else if (event.getId() == "changeSelection") + { + // Change the textField Values according to new selection + Server myServer = mMostUsedServersListModel->getServer + (mMostUsedServersListBox->getSelected()); + mServerNameField->setText(myServer.serverName); + mPortField->setText(toString(myServer.port)); + } + else if (event.getId() == "connect") + { + // Check login + if (mServerNameField->getText().empty() || mPortField->getText().empty()) + { + OkDialog *dlg = new OkDialog(_("Error"), + _("Please type both the address and the port of a server.")); + dlg->addActionListener(this); + } + else + { + mLoginData->hostname = mServerNameField->getText(); + mLoginData->port = (short) atoi(mPortField->getText().c_str()); + mOkButton->setEnabled(false); + mCancelButton->setEnabled(false); + + // First, look if the entry is a new one. + Server currentServer; + bool newEntry = true; + for (int i = 0; i < mMostUsedServersListModel->getNumberOfElements(); i++) + { + currentServer = mMostUsedServersListModel->getServer(i); + if (currentServer.serverName == mLoginData->hostname && + currentServer.port == mLoginData->port) + newEntry = false; + } + // Then, add it to config if it's really new + currentServer.serverName = mLoginData->hostname; + currentServer.port = mLoginData->port; + if (newEntry) + mMostUsedServersListModel->addFirstElement(currentServer); + // Write the entry in config + std::string currentConfig = ""; + for (int i = 0; i < mMostUsedServersListModel->getNumberOfElements(); i++) + { + currentServer = mMostUsedServersListModel->getServer(i); + + currentConfig = "MostUsedServerName" + toString(i); + config.setValue(currentConfig, currentServer.serverName); + + currentConfig = "MostUsedServerPort" + toString(i); + config.setValue(currentConfig, toString(currentServer.port)); + } + state = STATE_CONNECT_ACCOUNT; + } + } + else if (event.getId() == "cancel") + { + state = STATE_FORCE_QUIT; + } +} diff --git a/src/gui/serverdialog.h b/src/gui/serverdialog.h new file mode 100644 index 00000000..268b1baf --- /dev/null +++ b/src/gui/serverdialog.h @@ -0,0 +1,123 @@ +/* + * 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 + */ + +#ifndef _TMW_SERVERDIALOG_H +#define _TMW_SERVERDIALOG_H + +#include <iosfwd> +#include <vector> + +#include <guichan/actionlistener.hpp> +#include <guichan/listmodel.hpp> +#include "./widgets/dropdown.h" + +#include "login.h" +#include "window.h" + +#include "../guichanfwd.h" + +#include "../net/network.h" + +class LoginData; + +/** + * A server structure to keep pairs of servers and ports. + */ +struct Server { + Server(): + serverName(""), + port(0) {}; + + std::string serverName; + short port; +}; + +/** + * Server and Port List Model + */ +class ServersListModel : public gcn::ListModel +{ + public: + /** + * Used to get number of line in the list + */ + int getNumberOfElements(); + /** + * Used to get an element from the list + */ + std::string getElementAt(int elementIndex); + /** + * Used to get the corresponding Server struct + */ + Server getServer(int elementIndex) { return servers[elementIndex]; }; + /** + * Add an Element at the end of the server list + */ + void addElement(Server server); + /** + * Add an Element at the beginning of the server list + */ + void addFirstElement(Server server); + + private: + std::vector<Server> servers; +}; + +/** + * The server choice dialog. + * + * \ingroup Interface + */ +class ServerDialog : public Window, public gcn::ActionListener +{ + public: + /** + * Constructor + * + * @see Window::Window + */ + ServerDialog(LoginData *loginData); + + /** + * Destructor + */ + ~ServerDialog(); + + /** + * Called when receiving actions from the widgets. + */ + void action(const gcn::ActionEvent &event); + + private: + gcn::TextField *mServerNameField; + gcn::TextField *mPortField; + gcn::Button *mOkButton; + gcn::Button *mCancelButton; + + DropDown *mMostUsedServersDropDown; + gcn::ListBox *mMostUsedServersListBox; + gcn::ScrollArea *mMostUsedServersScrollArea; + ServersListModel *mMostUsedServersListModel; + + LoginData *mLoginData; +}; + +#endif diff --git a/src/gui/setup.cpp b/src/gui/setup.cpp index 148e8b75..c8b7f900 100644 --- a/src/gui/setup.cpp +++ b/src/gui/setup.cpp @@ -44,6 +44,10 @@ extern Window *itemShortcutWindow; extern Window *emoteShortcutWindow; extern Window *emoteWindow; extern Window *tradeWindow; +#ifdef TMWSERV_SUPPORT +extern Window *magicDialog; +extern Window *guildWindow; +#endif Setup::Setup(): Window(_("Setup")) @@ -138,5 +142,9 @@ void Setup::action(const gcn::ActionEvent &event) emoteShortcutWindow->resetToDefaultSize(); emoteWindow->resetToDefaultSize(); tradeWindow->resetToDefaultSize(); +#ifdef TMWSERV_SUPPORT + magicDialog->resetToDefaultSize(); + guildWindow->resetToDefaultSize(); +#endif } } diff --git a/src/gui/setup_audio.cpp b/src/gui/setup_audio.cpp index a4bc05ae..43cc28e6 100644 --- a/src/gui/setup_audio.cpp +++ b/src/gui/setup_audio.cpp @@ -43,6 +43,7 @@ Setup_Audio::Setup_Audio(): mMusicSlider(new Slider(0, 128)) { setOpaque(false); + setDimension(gcn::Rectangle(0, 0, 250, 200)); gcn::Label *sfxLabel = new gcn::Label(_("Sfx volume")); gcn::Label *musicLabel = new gcn::Label(_("Music volume")); diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index aa39dd36..526c67ce 100644 --- a/src/gui/setup_video.cpp +++ b/src/gui/setup_video.cpp @@ -107,6 +107,7 @@ Setup_Video::Setup_Video(): mFullScreenEnabled(config.getValue("screen", false)), mOpenGLEnabled(config.getValue("opengl", false)), mCustomCursorEnabled(config.getValue("customcursor", true)), + mVisibleNamesEnabled(config.getValue("visiblenames", 1)), mParticleEffectsEnabled(config.getValue("particleeffects", true)), mNameEnabled(config.getValue("showownname", false)), mOpacity(config.getValue("guialpha", 0.8)), @@ -117,6 +118,7 @@ Setup_Video::Setup_Video(): mFsCheckBox(new CheckBox(_("Full screen"), mFullScreenEnabled)), mOpenGLCheckBox(new CheckBox(_("OpenGL"), mOpenGLEnabled)), mCustomCursorCheckBox(new CheckBox(_("Custom cursor"), mCustomCursorEnabled)), + mVisibleNamesCheckBox(new CheckBox(_("Visible names"), mVisibleNamesEnabled)), mParticleEffectsCheckBox(new CheckBox(_("Particle effects"), mParticleEffectsEnabled)), mNameCheckBox(new CheckBox(_("Show name"), mNameEnabled)), mSpeechSlider(new Slider(0, 3)), @@ -168,6 +170,7 @@ Setup_Video::Setup_Video(): mModeList->setActionEventId("videomode"); mCustomCursorCheckBox->setActionEventId("customcursor"); + mVisibleNamesCheckBox->setActionEventId("visiblenames"); mParticleEffectsCheckBox->setActionEventId("particleeffects"); mNameCheckBox->setActionEventId("showownname"); mAlphaSlider->setActionEventId("guialpha"); @@ -185,6 +188,7 @@ Setup_Video::Setup_Video(): mModeList->addActionListener(this); mCustomCursorCheckBox->addActionListener(this); + mVisibleNamesCheckBox->addActionListener(this); mParticleEffectsCheckBox->addActionListener(this); mNameCheckBox->addActionListener(this); mAlphaSlider->addActionListener(this); @@ -263,8 +267,9 @@ Setup_Video::Setup_Video(): place(1, 0, mFsCheckBox, 3); place(1, 1, mOpenGLCheckBox, 3); place(1, 2, mCustomCursorCheckBox, 3); - place(1, 3, mNameCheckBox, 3); - place(1, 4, mParticleEffectsCheckBox, 3); + place(1, 3, mVisibleNamesCheckBox, 3); + place(1, 4, mNameCheckBox, 3); + place(1, 5, mParticleEffectsCheckBox, 3); place(0, 6, mAlphaSlider); place(0, 7, mFpsSlider); @@ -348,6 +353,7 @@ void Setup_Video::apply() // We sync old and new values at apply time mFullScreenEnabled = config.getValue("screen", false); mCustomCursorEnabled = config.getValue("customcursor", true); + mVisibleNamesEnabled = config.getValue("visiblenames", 1); mParticleEffectsEnabled = config.getValue("particleeffects", true); mNameEnabled = config.getValue("showownname", false); mSpeechMode = (int) config.getValue("speech", 3); @@ -381,6 +387,7 @@ void Setup_Video::cancel() mFsCheckBox->setSelected(mFullScreenEnabled); mOpenGLCheckBox->setSelected(mOpenGLEnabled); mCustomCursorCheckBox->setSelected(mCustomCursorEnabled); + mVisibleNamesCheckBox->setSelected(mVisibleNamesEnabled); mParticleEffectsCheckBox->setSelected(mParticleEffectsEnabled); mSpeechSlider->setValue(mSpeechMode); mNameCheckBox->setSelected(mNameEnabled); @@ -395,6 +402,7 @@ void Setup_Video::cancel() config.setValue("screen", mFullScreenEnabled ? true : false); config.setValue("customcursor", mCustomCursorEnabled ? true : false); + config.setValue("visiblenames", mVisibleNamesEnabled ? 1 : 0); config.setValue("particleeffects", mParticleEffectsEnabled ? true : false); config.setValue("speech", mSpeechMode); config.setValue("showownname", mNameEnabled ? true : false); @@ -426,6 +434,11 @@ void Setup_Video::action(const gcn::ActionEvent &event) config.setValue("customcursor", mCustomCursorCheckBox->isSelected() ? true : false); } + else if (event.getId() == "visiblenames") + { + config.setValue("visiblenames", + mVisibleNamesCheckBox->isSelected() ? 1 : 0); + } else if (event.getId() == "particleeffects") { config.setValue("particleeffects", diff --git a/src/gui/setup_video.h b/src/gui/setup_video.h index e57e33cc..44ecdfe5 100644 --- a/src/gui/setup_video.h +++ b/src/gui/setup_video.h @@ -50,6 +50,7 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, bool mFullScreenEnabled; bool mOpenGLEnabled; bool mCustomCursorEnabled; + bool mVisibleNamesEnabled; bool mParticleEffectsEnabled; bool mNameEnabled; double mOpacity; @@ -69,6 +70,7 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, gcn::CheckBox *mFsCheckBox; gcn::CheckBox *mOpenGLCheckBox; gcn::CheckBox *mCustomCursorCheckBox; + gcn::CheckBox *mVisibleNamesCheckBox; gcn::CheckBox *mParticleEffectsCheckBox; gcn::CheckBox *mNameCheckBox; diff --git a/src/gui/shop.cpp b/src/gui/shop.cpp index a5f59bac..7b28cef4 100644 --- a/src/gui/shop.cpp +++ b/src/gui/shop.cpp @@ -38,17 +38,19 @@ std::string ShopItems::getElementAt(int i) return mShopItems.at(i)->getDisplayName(); } -void ShopItems::addItem(int inventoryIndex, short id, int amount, int price) +void ShopItems::addItem(int id, int amount, int price) { - ShopItem *item = new ShopItem(id, amount, price); - item->setInvIndex(inventoryIndex); - mShopItems.push_back(item); + mShopItems.push_back(new ShopItem(id, amount, price)); } -void ShopItems::addItem(short id, int price) +#ifdef EATHENA_SUPPORT +void ShopItems::addItem(int inventoryIndex, int id, int amount, int price) { - mShopItems.push_back(new ShopItem(id, 0, price)); + ShopItem *item = new ShopItem(id, amount, price); + item->setInvIndex(inventoryIndex); + mShopItems.push_back(item); } +#endif ShopItem* ShopItems::at(int i) const { diff --git a/src/gui/shop.h b/src/gui/shop.h index e0db4c59..aa72bf2a 100644 --- a/src/gui/shop.h +++ b/src/gui/shop.h @@ -40,14 +40,16 @@ class ShopItems : public gcn::ListModel ~ShopItems(); /** - * Adds an item to the list (used by sell dialog). + * Adds an item to the list. */ - void addItem(int inventoryIndex, short id, int amount, int price); + void addItem(int id, int amount, int price); +#ifdef EATHENA_SUPPORT /** - * Adds an item to the list (used by buy dialog). + * Adds an item to the list (used by eAthena sell dialog). */ - void addItem(short id, int price); + void addItem(int inventoryIndex, int id, int amount, int price); +#endif /** * Returns the number of items in the shop. diff --git a/src/gui/skilldialog.cpp b/src/gui/skilldialog.cpp new file mode 100644 index 00000000..22d1db60 --- /dev/null +++ b/src/gui/skilldialog.cpp @@ -0,0 +1,263 @@ +/* + * 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 + */ + +#include <guichan/widgets/label.hpp> +#include <guichan/widgets/container.hpp> +#include <guichan/widgets/icon.hpp> + +#include "skilldialog.h" + +#include "icon.h" +#include "button.h" +#include "listbox.h" +#include "scrollarea.h" +#include "windowcontainer.h" +#include "progressbar.h" + +#include "widgets/tabbedarea.h" + +#include "../localplayer.h" + +#include "../utils/dtor.h" +#include "../utils/gettext.h" +#include "../utils/stringutils.h" + +SkillDialog::SkillDialog(): + Window(_("Skills")) +{ + setWindowName("Skills"); + setCloseButton(true); + setDefaultSize(windowContainer->getWidth() - 280, 30, 275, 425); + + TabbedArea *panel = new TabbedArea(); + panel->setDimension(gcn::Rectangle(5, 5, 270, 420)); + + Skill_Tab* tab; + + // Add each type of skill tab to the panel + tab = new Skill_Tab("Weapon"); + panel->addTab(_("Weapons"), tab); + mTabs.push_back(tab); + + tab = new Skill_Tab("Magic"); + panel->addTab(_("Magic"), tab); + mTabs.push_back(tab); + + tab = new Skill_Tab("Craft"); + panel->addTab(_("Crafts"), tab); + mTabs.push_back(tab); + + add(panel); + + update(); + + setLocationRelativeTo(getParent()); + loadWindowState(); +} + +SkillDialog::~SkillDialog() +{ + delete_all(mTabs); +} + +void SkillDialog::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "skill") + { + } + else if (event.getId() == "close") + { + setVisible(false); + } +} + +void SkillDialog::draw(gcn::Graphics *g) +{ + update(); + + Window::draw(g); +} + +void SkillDialog::update() +{ + for(std::list<Skill_Tab*>::const_iterator i = mTabs.begin(); + i != mTabs.end(); ++i) + { + (*i)->update(); + } +} + +Skill_Tab::Skill_Tab(const std::string &type): type(type) +{ + setOpaque(false); + setDimension(gcn::Rectangle(0, 0, 270, 420)); + int skillNum = getSkillNum(); + + mSkillIcons.resize(skillNum); + mSkillNameLabels.resize(skillNum); + mSkillLevelLabels.resize(skillNum); + mSkillExpLabels.resize(skillNum); + mSkillProgress.resize(skillNum); + + // Set the initial positions of the skill information + for (int a=0; a < skillNum; a++) + { + mSkillIcons.at(a) = getIcon(a); + mSkillIcons.at(a)->setPosition(1, a*32); + add(mSkillIcons.at(a)); + + mSkillNameLabels.at(a) = new gcn::Label(""); + mSkillNameLabels.at(a)->setPosition(35, a*32 ); + add(mSkillNameLabels.at(a)); + + mSkillProgress.at(a) = new ProgressBar(0.0f, 200, 20, 150, 150, 150); + mSkillProgress.at(a)->setPosition(35, a*32 + 13); + add(mSkillProgress.at(a)); + + mSkillExpLabels.at(a) = new gcn::Label(""); + mSkillExpLabels.at(a)->setPosition(45, a*32 + 16); + add(mSkillExpLabels.at(a)); + + mSkillLevelLabels.at(a) = new gcn::Label(""); + mSkillLevelLabels.at(a)->setPosition(165, a*32); + add(mSkillLevelLabels.at(a)); + } + + update(); + +} + +int Skill_Tab::getSkillNum() +{ + int skillNum = 0; + + if (type == "Weapon") + { + skillNum = CHAR_SKILL_WEAPON_NB; + return skillNum; + } + else if (type == "Magic") + { + skillNum = CHAR_SKILL_MAGIC_NB; + return skillNum; + } + else if (type == "Craft") + { + skillNum = CHAR_SKILL_CRAFT_NB; + return skillNum; + } + else return skillNum; +} + +int Skill_Tab::getSkillBegin() +{ + int skillBegin = 0; + + if (type == "Weapon") + { + skillBegin = CHAR_SKILL_WEAPON_BEGIN - CHAR_SKILL_BEGIN; + return skillBegin; + } + else if (type == "Magic") + { + skillBegin = CHAR_SKILL_MAGIC_BEGIN - CHAR_SKILL_BEGIN; + return skillBegin; + } + else if (type == "Craft") + { + skillBegin = CHAR_SKILL_CRAFT_BEGIN - CHAR_SKILL_BEGIN; + return skillBegin; + } + else return skillBegin; +} + +Icon* Skill_Tab::getIcon(int index) +{ + int skillBegin = getSkillBegin(); + std::string icon = LocalPlayer::getSkillInfo(index + skillBegin).icon; + return new Icon(icon); +} + +void Skill_Tab::updateSkill(int index) +{ + int skillBegin = getSkillBegin(); + + int baseLevel = player_node->getAttributeBase(index + + skillBegin + + CHAR_SKILL_BEGIN); + + int effLevel = player_node->getAttributeEffective(index + + skillBegin + + CHAR_SKILL_BEGIN); + if(baseLevel <= 0) + { + mSkillProgress.at(index)->setVisible(false); + mSkillExpLabels.at(index)->setVisible(false); + mSkillLevelLabels.at(index)->setVisible(false); + mSkillNameLabels.at(index)->setVisible(false); + mSkillIcons.at(index)->setVisible(false); + } + else + { + mSkillProgress.at(index)->setVisible(true); + mSkillExpLabels.at(index)->setVisible(true); + mSkillLevelLabels.at(index)->setVisible(true); + mSkillNameLabels.at(index)->setVisible(true); + mSkillIcons.at(index)->setVisible(true); + std::string skillLevel("Lvl: " + toString(baseLevel)); + if (effLevel < baseLevel) + { + skillLevel.append(" - " + toString(baseLevel - effLevel)); + } + else if (effLevel > baseLevel) + { + skillLevel.append(" + " + toString(effLevel - baseLevel)); + } + mSkillLevelLabels.at(index)->setCaption(skillLevel); + + std::pair<int, int> exp = player_node->getExperience(index + skillBegin); + std::string sExp (toString(exp.first) + " / " + toString(exp.second)); + + + mSkillNameLabels.at(index)->setCaption(LocalPlayer::getSkillInfo(index + skillBegin).name); + mSkillNameLabels.at(index)->adjustSize(); + mSkillLevelLabels.at(index)->adjustSize(); + mSkillExpLabels.at(index)->setCaption(sExp); + mSkillExpLabels.at(index)->adjustSize(); + mSkillExpLabels.at(index)->setAlignment(gcn::Graphics::RIGHT); + + // More intense red as exp grows + int color = 150 - (int)(150 * ((float) exp.first / exp.second)); + mSkillProgress.at(index)->setColor(244, color, color); + mSkillProgress.at(index)->setProgress((float) exp.first / exp.second); + } +} + +void Skill_Tab::update() +{ + int skillNum = getSkillNum(); + + // Update the skill information for reach skill + for (int a = 0; a < skillNum; a++) + { + updateSkill(a); + } +} diff --git a/src/gui/skilldialog.h b/src/gui/skilldialog.h new file mode 100644 index 00000000..3d010daa --- /dev/null +++ b/src/gui/skilldialog.h @@ -0,0 +1,139 @@ +/* + * 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 + */ + +#ifndef _TMW_SKILL_H +#define _TMW_SKILL_H + +#include <vector> +#include <list> + +#include <guichan/listmodel.hpp> +#include <guichan/actionlistener.hpp> + +#include "window.h" +#include "gccontainer.h" + +#include "../guichanfwd.h" + +class ProgressBar; +class Icon; + +class Skill_Tab : public GCContainer, public gcn::ActionListener +{ + public: + /** + * The type of this skill tab + */ + const std::string type; + + /** + * Constructor + */ + Skill_Tab(const std::string &type); + + /** + * Update this tab + */ + void update(); + + /** + * Called when receiving actions from widget. + */ + void action(const gcn::ActionEvent &event) {} + + private: + /** + * Update the information of a skill at + * the given index + */ + void updateSkill(int index); + + /** + * Gets the number of skills in this particular + * type of tab. + */ + int getSkillNum(); + + /** + * Get the first enumeration of this skill tab's + * skill type. + */ + int getSkillBegin(); + + /** + * Get the icon associated with the given index + */ + Icon* getIcon(int index); + + std::vector<Icon *> mSkillIcons; + std::vector<gcn::Label *> mSkillNameLabels; + std::vector<gcn::Label *> mSkillLevelLabels; + std::vector<gcn::Label *> mSkillExpLabels; + std::vector<ProgressBar *> mSkillProgress; +}; + + +/** + * The skill dialog. + * + * \ingroup Interface + */ +class SkillDialog : public Window, public gcn::ActionListener +{ + public: + /** + * Constructor. + */ + SkillDialog(); + + /** + * Destructor. + */ + ~SkillDialog(); + + /** + * Called when receiving actions from widget. + */ + void action(const gcn::ActionEvent &event); + + /** + * Update the tabs in this dialog + */ + void update(); + + /** + * Draw this window. + */ + void draw(gcn::Graphics *g); + + private: + + + std::list<Skill_Tab*> mTabs; + +}; + + + + +extern SkillDialog *skillDialog; + +#endif diff --git a/src/gui/speechbubble.cpp b/src/gui/speechbubble.cpp index f63b02c3..165d216f 100644 --- a/src/gui/speechbubble.cpp +++ b/src/gui/speechbubble.cpp @@ -1,6 +1,7 @@ /* * Speech bubbles * Copyright (C) 2008 The Legend of Mazzeroth Development Team + * Copyright (C) 2008 The Mana World Development Team * * This file is part of The Mana World. * diff --git a/src/gui/speechbubble.h b/src/gui/speechbubble.h index 6daff8fb..5d582b1d 100644 --- a/src/gui/speechbubble.h +++ b/src/gui/speechbubble.h @@ -1,6 +1,7 @@ /* * Speech bubbles * Copyright (C) 2008 The Legend of Mazzeroth Development Team + * Copyright (C) 2008 The Mana World Development Team * * This file is part of The Mana World. * diff --git a/src/gui/status.cpp b/src/gui/status.cpp index b1a973fe..3c48d045 100644 --- a/src/gui/status.cpp +++ b/src/gui/status.cpp @@ -168,21 +168,21 @@ void StatusWindow::update() { // Status Part // ----------- - mLvlLabel->setCaption(strprintf(_("Level: %d"), mPlayer->mLevel)); + mLvlLabel->setCaption(strprintf(_("Level: %d"), mPlayer->getLevel())); mLvlLabel->adjustSize(); mJobLvlLabel->setCaption(strprintf(_("Job: %d"), mPlayer->mJobLevel)); mJobLvlLabel->adjustSize(); - if (mCurrency != mPlayer->mGp) { - mCurrency = mPlayer->mGp; + if (mCurrency != mPlayer->getMoney()) { + mCurrency = mPlayer->getMoney(); mGpLabel->setCaption(strprintf(_("Money: %s"), Units::formatCurrency(mCurrency).c_str())); mGpLabel->adjustSize(); } - mHpBar->setText(toString(mPlayer->mHp) + - "/" + toString(mPlayer->mMaxHp)); + mHpBar->setText(toString(mPlayer->getHp()) + + "/" + toString(mPlayer->getMaxHp())); mMpBar->setText(toString(mPlayer->mMp) + "/" + toString(mPlayer->mMaxMp)); @@ -194,11 +194,11 @@ void StatusWindow::update() "/" + toString(mPlayer->mJobXpForNextLevel)); // HP Bar coloration - if (mPlayer->mHp < int(mPlayer->mMaxHp / 3)) + if (mPlayer->getHp() < int(mPlayer->getMaxHp() / 3)) { mHpBar->setColor(223, 32, 32); // Red } - else if (mPlayer->mHp < int((mPlayer->mMaxHp / 3) * 2)) + else if (mPlayer->getHp() < int((mPlayer->getMaxHp() / 3) * 2)) { mHpBar->setColor(230, 171, 34); // Orange } @@ -207,7 +207,7 @@ void StatusWindow::update() mHpBar->setColor(0, 171, 34); // Green } - mHpBar->setProgress((float) mPlayer->mHp / (float) mPlayer->mMaxHp); + mHpBar->setProgress((float) mPlayer->getHp() / (float) mPlayer->getMaxHp()); mMpBar->setProgress((float) mPlayer->mMp / (float) mPlayer->mMaxMp); mXpBar->setProgress( diff --git a/src/gui/status.h b/src/gui/status.h index 136c6c3f..9e4de3fd 100644 --- a/src/gui/status.h +++ b/src/gui/status.h @@ -65,7 +65,7 @@ class StatusWindow : public Window, public gcn::ActionListener */ gcn::Label *mLvlLabel, *mJobLvlLabel; gcn::Label *mGpLabel; - Uint32 mCurrency; + int mCurrency; gcn::Label *mHpLabel, *mMpLabel, *mXpLabel, *mJobLabel; ProgressBar *mHpBar, *mMpBar; ProgressBar *mXpBar, *mJobBar; diff --git a/src/gui/statuswindow.cpp b/src/gui/statuswindow.cpp new file mode 100644 index 00000000..bcac0a90 --- /dev/null +++ b/src/gui/statuswindow.cpp @@ -0,0 +1,370 @@ +/* + * 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 + */ + +#include "statuswindow.h" + +#include <guichan/widgets/label.hpp> + +#include "button.h" +#include "progressbar.h" +#include "windowcontainer.h" + +#include "../localplayer.h" + +#include "../utils/strprintf.h" +#include "../utils/stringutils.h" + +StatusWindow::StatusWindow(LocalPlayer *player): + Window(player->getName()), + mPlayer(player) +{ + setWindowName("Status"); + setResizable(true); + setCloseButton(true); + setDefaultSize((windowContainer->getWidth() - 365) / 2, + (windowContainer->getHeight() - 255) / 2, 365, 275); + loadWindowState(); + + // ---------------------- + // Status Part + // ---------------------- + + mLvlLabel = new gcn::Label("Level:"); + mMoneyLabel = new gcn::Label("Money:"); + + mHpLabel = new gcn::Label("HP:"); + mHpBar = new ProgressBar(1.0f, 80, 15, 0, 171, 34); + mHpValueLabel = new gcn::Label(""); + + int y = 3; + int x = 5; + + mLvlLabel->setPosition(x, y); + x += mLvlLabel->getWidth() + 40; + mMoneyLabel->setPosition(x, y); + + y += mLvlLabel->getHeight() + 5; // Next Row + x = 5; + + mHpLabel->setPosition(x, y); + x += mHpLabel->getWidth() + 5; + mHpBar->setPosition(x, y); + x += mHpBar->getWidth() + 5; + mHpValueLabel->setPosition(x, y); + + y += mHpLabel->getHeight() + 5; // Next Row + x = 5; + + add(mLvlLabel); + add(mMoneyLabel); + add(mHpLabel); + add(mHpValueLabel); + add(mHpBar); + + // ---------------------- + // Stats Part + // ---------------------- + + // Static Labels + gcn::Label *mStatsTitleLabel = new gcn::Label("Stats"); + gcn::Label *mStatsTotalLabel = new gcn::Label("Total"); + + // Derived Stats +/* + mStatsAttackLabel = new gcn::Label("Attack:"); + mStatsDefenseLabel= new gcn::Label("Defense:"); + mStatsMagicAttackLabel = new gcn::Label("M.Attack:"); + mStatsMagicDefenseLabel = new gcn::Label("M.Defense:"); + mStatsAccuracyLabel = new gcn::Label("% Accuracy:"); + mStatsEvadeLabel = new gcn::Label("% Evade:"); + mStatsReflexLabel = new gcn::Label("% Reflex:"); + + mStatsAttackPoints = new gcn::Label(""); + mStatsDefensePoints = new gcn::Label(""); + mStatsMagicAttackPoints = new gcn::Label(""); + mStatsMagicDefensePoints = new gcn::Label(""); + mStatsAccuracyPoints = new gcn::Label("% Accuracy:"); + mStatsEvadePoints = new gcn::Label("% Evade:"); + mStatsReflexPoints = new gcn::Label("% Reflex:"); +*/ + // New labels + for (int i = 0; i < 6; i++) { + mStatsLabel[i] = new gcn::Label(); + mStatsDisplayLabel[i] = new gcn::Label(); + } + mCharacterPointsLabel = new gcn::Label(); + mCorrectionPointsLabel = new gcn::Label(); + + // Set button events Id + mStatsPlus[0] = new Button("+", "STR+", this); + mStatsPlus[1] = new Button("+", "AGI+", this); + mStatsPlus[2] = new Button("+", "DEX+", this); + mStatsPlus[3] = new Button("+", "VIT+", this); + mStatsPlus[4] = new Button("+", "INT+", this); + mStatsPlus[5] = new Button("+", "WIL+", this); + + mStatsMinus[0] = new Button("-", "STR-", this); + mStatsMinus[1] = new Button("-", "AGI-", this); + mStatsMinus[2] = new Button("-", "DEX-", this); + mStatsMinus[3] = new Button("-", "VIT-", this); + mStatsMinus[4] = new Button("-", "INT-", this); + mStatsMinus[5] = new Button("-", "WIL-", this); + + + + // Set position + mStatsTitleLabel->setPosition(mHpLabel->getX(), mHpLabel->getY() + 23 ); + mStatsTotalLabel->setPosition(110, mStatsTitleLabel->getY() + 15); + int totalLabelY = mStatsTotalLabel->getY(); + + for (int i = 0; i < 6; i++) + { + mStatsLabel[i]->setPosition(5, + mStatsTotalLabel->getY() + (i * 23) + 15); + mStatsMinus[i]->setPosition(85, totalLabelY + (i * 23) + 15); + mStatsDisplayLabel[i]->setPosition(125, + totalLabelY + (i * 23) + 15); + mStatsPlus[i]->setPosition(185, totalLabelY + (i * 23) + 15); + } + + mCharacterPointsLabel->setPosition(5, mStatsDisplayLabel[5]->getY() + 25); + mCorrectionPointsLabel->setPosition(5, mStatsDisplayLabel[5]->getY() + 35); +/* + mStatsAttackLabel->setPosition(220, mStatsLabel[0]->getY()); + mStatsDefenseLabel->setPosition(220, mStatsLabel[1]->getY()); + mStatsMagicAttackLabel->setPosition(220, mStatsLabel[2]->getY()); + mStatsMagicDefenseLabel->setPosition(220, mStatsLabel[3]->getY()); + mStatsAccuracyLabel->setPosition(220, mStatsLabel[4]->getY()); + mStatsEvadeLabel->setPosition(220, mStatsLabel[5]->getY()); + mStatsReflexLabel->setPosition(220, mStatsLabel[6]->getY()); + + mStatsAttackPoints->setPosition(310, mStatsLabel[0]->getY()); + mStatsDefensePoints->setPosition(310, mStatsLabel[1]->getY()); + mStatsMagicAttackPoints->setPosition(310, mStatsLabel[2]->getY()); + mStatsMagicDefensePoints->setPosition(310, mStatsLabel[3]->getY()); + mStatsAccuracyPoints->setPosition(310, mStatsLabel[4]->getY()); + mStatsEvadePoints->setPosition(310, mStatsLabel[5]->getY()); + mStatsReflexPoints->setPosition(310, mStatsLabel[6]->getY()); +*/ + // Assemble + add(mStatsTitleLabel); + add(mStatsTotalLabel); + for(int i = 0; i < 6; i++) + { + add(mStatsLabel[i]); + add(mStatsDisplayLabel[i]); + add(mStatsPlus[i]); + add(mStatsMinus[i]); + }/* + add(mStatsAttackLabel); + add(mStatsDefenseLabel); + add(mStatsMagicAttackLabel); + add(mStatsMagicDefenseLabel); + add(mStatsAccuracyLabel); + add(mStatsEvadeLabel); + add(mStatsReflexLabel); + + add(mStatsAttackPoints); + add(mStatsDefensePoints); + add(mStatsMagicAttackPoints); + add(mStatsMagicDefensePoints); + add(mStatsAccuracyPoints); + add(mStatsEvadePoints); + add(mStatsReflexPoints);*/ + + add(mCharacterPointsLabel); + add(mCorrectionPointsLabel); +} + +void StatusWindow::update() +{ + // Status Part + // ----------- + mLvlLabel->setCaption( "Level: " + + toString(mPlayer->getLevel()) + + " (" + + toString(mPlayer->getLevelProgress()) + + "%)"); + mLvlLabel->adjustSize(); + + mMoneyLabel->setCaption("Money: " + toString(mPlayer->getMoney()) + " GP"); + mMoneyLabel->adjustSize(); + + int hp = mPlayer->getHp(); + int maxHp = mPlayer->getMaxHp(); + + mHpValueLabel->setCaption(toString(hp) + + " / " + toString(maxHp)); + mHpValueLabel->adjustSize(); + + // HP Bar coloration + if (hp < int(maxHp / 3)) + { + mHpBar->setColor(223, 32, 32); // Red + } + else if (hp < int((maxHp / 3) * 2)) + { + mHpBar->setColor(230, 171, 34); // Orange + } + else + { + mHpBar->setColor(0, 171, 34); // Green + } + + mHpBar->setProgress((float) hp / maxHp); + + // Stats Part + // ---------- + const std::string attrNames[6] = { + "Strength", + "Agility", + "Dexterity", + "Vitality", + "Intelligence", + "Willpower" + }; + int characterPoints = mPlayer->getCharacterPoints(); + int correctionPoints = mPlayer->getCorrectionPoints(); + // Update labels + for (int i = 0; i < 6; i++) + { + mStatsLabel[i]->setCaption(attrNames[i]); + mStatsDisplayLabel[i]->setCaption( + strprintf("%d / %d", + mPlayer->getAttributeEffective(CHAR_ATTR_BEGIN + i), + mPlayer->getAttributeBase(CHAR_ATTR_BEGIN + i))); + + mStatsLabel[i]->adjustSize(); + mStatsDisplayLabel[i]->adjustSize(); + + mStatsPlus[i]->setEnabled(characterPoints); + mStatsMinus[i]->setEnabled(correctionPoints); + } + mCharacterPointsLabel->setCaption("Character Points: " + + toString(characterPoints)); + mCharacterPointsLabel->adjustSize(); + + mCorrectionPointsLabel->setCaption("Correction Points: " + + toString(correctionPoints)); + mCorrectionPointsLabel->adjustSize(); +/* + // Derived Stats Points + + // Attack TODO: Count equipped Weapons and items attack bonuses + mStatsAttackPoints->setCaption( + toString(mPlayer->ATK + mPlayer->ATK_BONUS)); + mStatsAttackPoints->adjustSize(); + + // Defense TODO: Count equipped Armors and items defense bonuses + mStatsDefensePoints->setCaption( + toString(mPlayer->DEF + mPlayer->DEF_BONUS)); + mStatsDefensePoints->adjustSize(); + + // Magic Attack TODO: Count equipped items M.Attack bonuses + mStatsMagicAttackPoints->setCaption( + toString(mPlayer->MATK + mPlayer->MATK_BONUS)); + mStatsMagicAttackPoints->adjustSize(); + + // Magic Defense TODO: Count equipped items M.Defense bonuses + mStatsMagicDefensePoints->setCaption( + toString(mPlayer->MDEF + mPlayer->MDEF_BONUS)); + mStatsMagicDefensePoints->adjustSize(); + + // Accuracy % + mStatsAccuracyPoints->setCaption(toString(mPlayer->HIT)); + mStatsAccuracyPoints->adjustSize(); + + // Evasion % + mStatsEvadePoints->setCaption(toString(mPlayer->FLEE)); + mStatsEvadePoints->adjustSize(); + + // Reflex % + mStatsReflexPoints->setCaption(toString(mPlayer->DEX / 4)); // + counter + mStatsReflexPoints->adjustSize(); +*/ + // Update Second column widgets position + mMoneyLabel->setPosition(mLvlLabel->getX() + mLvlLabel->getWidth() + 20, + mLvlLabel->getY()); + +} + +void StatusWindow::draw(gcn::Graphics *g) +{ + update(); + + Window::draw(g); +} + +void StatusWindow::action(const gcn::ActionEvent &event) +{ + const std::string &eventId = event.getId(); + + // Stats Part + if (eventId == "STR+") + { + mPlayer->raiseAttribute(LocalPlayer::STR); + } + else if (eventId == "AGI+") + { + mPlayer->raiseAttribute(LocalPlayer::AGI); + } + else if (eventId == "DEX+") + { + mPlayer->raiseAttribute(LocalPlayer::DEX); + } + else if (eventId == "VIT+") + { + mPlayer->raiseAttribute(LocalPlayer::VIT); + } + else if (eventId == "INT+") + { + mPlayer->raiseAttribute(LocalPlayer::INT); + } + else if (eventId == "WIL+") + { + mPlayer->raiseAttribute(LocalPlayer::WIL); + } + + else if (eventId == "STR-") + { + mPlayer->lowerAttribute(LocalPlayer::STR); + } + else if (eventId == "AGI-") + { + mPlayer->lowerAttribute(LocalPlayer::AGI); + } + else if (eventId == "DEX-") + { + mPlayer->lowerAttribute(LocalPlayer::DEX); + } + else if (eventId == "VIT-") + { + mPlayer->lowerAttribute(LocalPlayer::VIT); + } + else if (eventId == "INT-") + { + mPlayer->lowerAttribute(LocalPlayer::INT); + } + else if (eventId == "WIL-") + { + mPlayer->lowerAttribute(LocalPlayer::WIL); + } +} diff --git a/src/gui/statuswindow.h b/src/gui/statuswindow.h new file mode 100644 index 00000000..262b89f6 --- /dev/null +++ b/src/gui/statuswindow.h @@ -0,0 +1,105 @@ +/* + * 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 + */ + +#ifndef _TMW_STATUS_H +#define _TMW_STATUS_H + +#include <iosfwd> + +#include <guichan/actionlistener.hpp> + +#include "window.h" + +#include "../guichanfwd.h" + +class LocalPlayer; +class ProgressBar; + + +/** + * The player status dialog. + * + * \ingroup Interface + */ +class StatusWindow : public Window, public gcn::ActionListener +{ + public: + /** + * Constructor. + */ + StatusWindow(LocalPlayer *player); + + /** + * Called when receiving actions from widget. + */ + void action(const gcn::ActionEvent &event); + + /** + * Draw this window + */ + void draw(gcn::Graphics *graphics); + + /** + * Updates this dialog with values from PLAYER_INFO *char_info + */ + void update(); + + private: + LocalPlayer *mPlayer; + + /** + * Status Part + */ + gcn::Label *mLvlLabel, *mMoneyLabel, *mHpLabel, *mHpValueLabel; + ProgressBar *mHpBar; + + /** + * Derived Statistics captions + */ +/* + gcn::Label *mStatsAttackLabel, *mStatsDefenseLabel; + gcn::Label *mStatsMagicAttackLabel, *mStatsMagicDefenseLabel; + gcn::Label *mStatsAccuracyLabel, *mStatsEvadeLabel; + gcn::Label *mStatsReflexLabel; + + gcn::Label *mStatsAttackPoints, *mStatsDefensePoints; + gcn::Label *mStatsMagicAttackPoints, *mStatsMagicDefensePoints; + gcn::Label *mStatsAccuracyPoints, *mStatsEvadePoints; + gcn::Label *mStatsReflexPoints; +*/ + /** + * Stats captions. + */ + gcn::Label *mStatsLabel[6]; + gcn::Label *mStatsDisplayLabel[6]; + gcn::Label *mCharacterPointsLabel; + gcn::Label *mCorrectionPointsLabel; + + /** + * Stats buttons. + */ + gcn::Button *mStatsPlus[6]; + gcn::Button *mStatsMinus[6]; +}; + +extern StatusWindow *statusWindow; + +#endif diff --git a/src/gui/textdialog.cpp b/src/gui/textdialog.cpp new file mode 100644 index 00000000..05e43906 --- /dev/null +++ b/src/gui/textdialog.cpp @@ -0,0 +1,91 @@ +/* + * 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 + */ + +#include "textdialog.h" + +#include <guichan/widgets/label.hpp> +#include <guichan/widgets/textfield.hpp> + +#include "button.h" + +TextDialog::TextDialog(const std::string &title, const std::string &msg, + Window *parent): + Window(title, true, parent), + textField(new TextField("")) +{ + gcn::Label *textLabel = new gcn::Label(msg); + okButton = new Button("OK", "OK", this); + gcn::Button *cancelButton = new Button("Cancel", "CANCEL", this); + + int w = textLabel->getWidth() + 20; + int inWidth = okButton->getWidth() + cancelButton->getWidth() + 5; + int h = textLabel->getHeight() + 25 + okButton->getHeight() + textField->getHeight(); + + if (w < inWidth + 10) { + w = inWidth + 10; + } + + setContentSize(w, h); + textLabel->setPosition(10, 10); + textField->setWidth(85); + textField->setPosition(10,20 + textLabel->getHeight()); + okButton->setPosition((w - inWidth) / 2, + h - 5 - cancelButton->getHeight()); + cancelButton->setPosition(okButton->getX() + okButton->getWidth() + 5, + h - 5 - cancelButton->getHeight()); + + add(textLabel); + add(textField); + add(okButton); + add(cancelButton); + + if (getParent()) { + setLocationRelativeTo(getParent()); + getParent()->moveToTop(this); + } + setVisible(true); + textField->requestFocus(); +} + +void TextDialog::action(const gcn::ActionEvent &event) +{ + // Proxy button events to our listeners + ActionListenerIterator i; + for (i = mActionListeners.begin(); i != mActionListeners.end(); ++i) + { + (*i)->action(event); + } + + if(event.getId() == "CANCEL" || event.getId() == "OK") + { + scheduleDelete(); + } +} + +const std::string& TextDialog::getText() const +{ + return textField->getText(); +} + +void TextDialog::setOKButtonActionId(const std::string &name) +{ + okButton->setActionEventId(name); +} diff --git a/src/gui/textdialog.h b/src/gui/textdialog.h new file mode 100644 index 00000000..8b4e2cc3 --- /dev/null +++ b/src/gui/textdialog.h @@ -0,0 +1,66 @@ +/* + * 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 + */ + +#ifndef _TMW_GUI_GUILD_DIALOG_H +#define _TMW_GUI_GUILD_DIALOG_H + +#include <guichan/actionlistener.hpp> +#include "textfield.h" + +#include "window.h" + + +/** +* An option dialog. + * + * \ingroup GUI + */ +class TextDialog : public Window, public gcn::ActionListener { +public: + /** + * Constructor. + * + * @see Window::Window + */ + TextDialog(const std::string &title, const std::string &msg, + Window *parent = NULL); + + /** + * Called when receiving actions from the widgets. + */ + void action(const gcn::ActionEvent &event); + + /** + * Get the text in the textfield + */ + const std::string& getText() const; + + /** + * Set the OK button action id + */ + void setOKButtonActionId(const std::string &name); + +private: + TextField *textField; + gcn::Button *okButton; +}; + +#endif diff --git a/src/gui/trade.cpp b/src/gui/trade.cpp index 2f6007a9..98214a79 100644 --- a/src/gui/trade.cpp +++ b/src/gui/trade.cpp @@ -21,6 +21,7 @@ #include <sstream> +#include <guichan/font.hpp> #include <guichan/widgets/label.hpp> #include "button.h" @@ -39,49 +40,67 @@ #include "../localplayer.h" #include "../units.h" +#ifdef TMWSERV_SUPPORT +#include "../net/gameserver/player.h" +#else #include "../net/messageout.h" -#include "../net/protocol.h" +#include "../net/ea/protocol.h" +#endif #include "../utils/gettext.h" -#include "../utils/strprintf.h" #include "../utils/stringutils.h" +#include "../utils/strprintf.h" +#ifdef TMWSERV_SUPPORT +TradeWindow::TradeWindow(): +#else TradeWindow::TradeWindow(Network *network): +#endif Window(_("Trade: You")), +#ifdef EATHENA_SUPPORT mNetwork(network), +#endif mMyInventory(new Inventory(INVENTORY_SIZE)), mPartnerInventory(new Inventory(INVENTORY_SIZE)) +#ifdef TMWSERV_SUPPORT + , mStatus(PREPARING) +#endif { setWindowName("Trade"); - setDefaultSize(115, 227, 342, 209); setResizable(true); + setDefaultSize(115, 197, 332, 209); - setMinWidth(342); - setMinHeight(209); - - mAddButton = new Button(_("Add"), "add", this); + Button *mAddButton = new Button(_("Add"), "add", this); +#ifdef EATHENA_SUPPORT mOkButton = new Button(_("Ok"), "ok", this); - mCancelButton = new Button(_("Cancel"), "cancel", this); - mTradeButton = new Button(_("Trade"), "trade", this); - - mTradeButton->setEnabled(false); - - mMyItemContainer = new ItemContainer(mMyInventory.get(), 2); - mMyItemContainer->setWidth(160); +#endif + Button *mCancelButton = new Button(_("Cancel"), "cancel", this); + mTradeButton = new Button(_("Propose trade"), "trade", this); + mTradeButton->setWidth(8 + std::max( + mTradeButton->getFont()->getWidth(_("Propose trade")), + mTradeButton->getFont()->getWidth(_("Confirm trade")))); + +#ifdef TMWSERV_SUPPORT + mMyItemContainer = new ItemContainer(mMyInventory.get(), 4, 3, 0); +#else + mMyItemContainer = new ItemContainer(mMyInventory.get(), 4, 3, 2); +#endif mMyItemContainer->addSelectionListener(this); + ScrollArea *mMyScroll = new ScrollArea(mMyItemContainer); - mMyScroll = new ScrollArea(mMyItemContainer); - - mPartnerItemContainer = new ItemContainer(mPartnerInventory.get(), 2); - mPartnerItemContainer->setWidth(160); +#ifdef TMWSERV_SUPPORT + mPartnerItemContainer = new ItemContainer(mPartnerInventory.get(), 4, 3, 0); +#else + mPartnerItemContainer = new ItemContainer(mPartnerInventory.get(), 4, 3, 2); +#endif mPartnerItemContainer->addSelectionListener(this); + ScrollArea *mPartnerScroll = new ScrollArea(mPartnerItemContainer); - mPartnerScroll = new ScrollArea(mPartnerItemContainer); - - mMoneyLabel = new gcn::Label(strprintf(_("You get %s."), "")); - mMoneyLabel2 = new gcn::Label(_("You give:")); + mMoneyLabel = new gcn::Label(strprintf(_("You get %d GP."), 0)); + gcn::Label *mMoneyLabel2 = new gcn::Label(_("You give:")); mMoneyField = new TextField; - mMoneyField->setWidth(50); + mMoneyField->setWidth(40); + Button *mMoneyChange = new Button(_("Change"), "money", this); place(1, 0, mMoneyLabel); place(0, 1, mMyScroll).setPadding(3); @@ -90,9 +109,12 @@ TradeWindow::TradeWindow(Network *network): place = getPlacer(0, 0); place(0, 0, mMoneyLabel2); place(1, 0, mMoneyField); + place(2, 0, mMoneyChange).setHAlign(LayoutCell::LEFT); place = getPlacer(0, 2); place(0, 0, mAddButton); +#ifdef EATHENA_SUPPORT place(1, 0, mOkButton); +#endif place(2, 0, mTradeButton); place(3, 0, mCancelButton); Layout &layout = getLayout(); @@ -109,44 +131,37 @@ TradeWindow::~TradeWindow() { } -void TradeWindow::widgetResized(const gcn::Event &event) -{ - mMyItemContainer->setWidth(mMyScroll->getWidth()); - mPartnerItemContainer->setWidth(mPartnerScroll->getWidth()); - - Window::widgetResized(event); -} - - -void TradeWindow::addMoney(int amount) +void TradeWindow::setMoney(int amount) { mMoneyLabel->setCaption(strprintf(_("You get %s."), Units::formatCurrency(amount).c_str())); mMoneyLabel->adjustSize(); +#ifdef TMWSERV_SUPPORT + setStatus(PREPARING); +#endif } +#ifdef TMWSERV_SUPPORT +void TradeWindow::addItem(int id, bool own, int quantity) +{ + (own ? mMyInventory : mPartnerInventory)->addItem(id, quantity); + setStatus(PREPARING); +} +#endif + +#ifdef EATHENA_SUPPORT void TradeWindow::addItem(int id, bool own, int quantity, bool equipment) { if (own) { - mMyItemContainer->setWidth(mMyScroll->getWidth()); mMyInventory->addItem(id, quantity, equipment); } else { - mPartnerItemContainer->setWidth(mPartnerScroll->getWidth()); mPartnerInventory->addItem(id, quantity, equipment); } } -void TradeWindow::removeItem(int id, bool own) -{ - if (own) - mMyInventory->removeItem(id); - else - mPartnerInventory->removeItem(id); -} - void TradeWindow::changeQuantity(int index, bool own, int quantity) { if (own) @@ -162,20 +177,35 @@ void TradeWindow::increaseQuantity(int index, bool own, int quantity) else mPartnerInventory->getItem(index)->increaseQuantity(quantity); } +#endif void TradeWindow::reset() { mMyInventory->clear(); mPartnerInventory->clear(); +#ifdef EATHENA_SUPPORT mTradeButton->setEnabled(false); mOkButton->setEnabled(true); mOkOther = false; mOkMe = false; +#endif mMoneyLabel->setCaption(strprintf(_("You get %s."), "")); mMoneyField->setEnabled(true); mMoneyField->setText(""); +#ifdef TMWSERV_SUPPORT + setStatus(PREPARING); +#endif } +#ifdef TMWSERV_SUPPORT + +void TradeWindow::receivedOk() +{ + setStatus(ACCEPTING); +} + +#else + void TradeWindow::setTradeButton(bool enabled) { mTradeButton->setEnabled(enabled); @@ -213,12 +243,20 @@ void TradeWindow::receivedOk(bool own) } } +#endif + void TradeWindow::tradeItem(Item *item, int quantity) { +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::tradeItem(item->getInvIndex(), quantity); + addItem(item->getId(), true, quantity); + item->increaseQuantity(-quantity); +#else MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_ITEM_ADD_REQUEST); outMsg.writeInt16(item->getInvIndex()); outMsg.writeInt32(quantity); +#endif } void TradeWindow::valueChanged(const gcn::SelectionEvent &event) @@ -235,6 +273,18 @@ void TradeWindow::valueChanged(const gcn::SelectionEvent &event) mMyItemContainer->selectNone(); } +#ifdef TMWSERV_SUPPORT +void TradeWindow::setStatus(Status s) +{ + if (s == mStatus) return; + mStatus = s; + + mTradeButton->setCaption + (s == PREPARING ? _("Propose trade") : _("Confirm trade")); + mTradeButton->setEnabled(s != PROPOSING); +} +#endif + void TradeWindow::action(const gcn::ActionEvent &event) { Item *item = inventoryWindow->getSelectedItem(); @@ -247,10 +297,9 @@ void TradeWindow::action(const gcn::ActionEvent &event) if (mMyInventory->getFreeSlot() < 1) return; - if (mMyInventory->contains(item)) - { - chatWindow->chatLog(_("Failed adding item. You can not " - "overlap one kind of item on the window."), BY_SERVER); + if (mMyInventory->contains(item)) { + chatWindow->chatLog("Failed adding item. You can not " + "overlap one kind of item on the window.", BY_SERVER); return; } @@ -263,12 +312,24 @@ void TradeWindow::action(const gcn::ActionEvent &event) // Choose amount of items to trade new ItemAmountWindow(AMOUNT_TRADE_ADD, this, item); } + +#ifdef TMWSERV_SUPPORT + setStatus(PREPARING); +#endif } else if (event.getId() == "cancel") { + setVisible(false); + reset(); + player_node->setTrading(false); +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::acceptTrade(false); +#else MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_CANCEL_REQUEST); +#endif } +#ifdef EATHENA_SUPPORT else if (event.getId() == "ok") { std::stringstream tempMoney(mMoneyField->getText()); @@ -290,9 +351,24 @@ void TradeWindow::action(const gcn::ActionEvent &event) MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_ADD_COMPLETE); } +#endif else if (event.getId() == "trade") { +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::acceptTrade(true); + setStatus(PROPOSING); +#else MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_OK); +#endif + } +#ifdef TMWSERV_SUPPORT + else if (event.getId() == "money") + { + int v = atoi(mMoneyField->getText().c_str()); + Net::GameServer::Player::tradeMoney(v); + mMoneyField->setText(strprintf("%d", v)); + setStatus(PREPARING); } +#endif } diff --git a/src/gui/trade.h b/src/gui/trade.h index df724038..bde0481c 100644 --- a/src/gui/trade.h +++ b/src/gui/trade.h @@ -34,7 +34,9 @@ class Inventory; class Item; class ItemContainer; +#ifdef EATHENA_SUPPORT class Network; +#endif class ScrollArea; /** @@ -48,7 +50,11 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener /** * Constructor. */ +#ifdef TMWSERV_SUPPORT + TradeWindow(); +#else TradeWindow(Network *network); +#endif /** * Destructor. @@ -56,31 +62,25 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener ~TradeWindow(); /** - * Called when resizing the window. - * - * @param event The calling event - */ - void widgetResized(const gcn::Event &event); - - /** - * Add money to the trade window. + * Displays expected money in the trade window. */ - void addMoney(int quantity); + void setMoney(int quantity); /** * Add an item to the trade window. */ - void addItem(int id, bool own, int quantity, bool equipment); + void addItem(int id, bool own, int quantity); /** - * Remove a item from the trade window. + * Reset both item containers */ - void removeItem(int id, bool own); + void reset(); +#ifdef EATHENA_SUPPORT /** - * Reset both item containers + * Add an item to the trade window. */ - void reset(); + void addItem(int id, bool own, int quantity, bool equipment); /** * Change quantity of an item. @@ -96,11 +96,16 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener * Set trade Button disabled */ void setTradeButton(bool enabled); +#endif /** * Player received ok message from server */ +#ifdef TMWSERV_SUPPORT + void receivedOk(); +#else void receivedOk(bool own); +#endif /** * Send trade packet. @@ -119,7 +124,23 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener void action(const gcn::ActionEvent &event); private: +#ifdef TMWSERV_SUPPORT + enum Status + { + PREPARING, /**< Players are adding items. */ + PROPOSING, /**< Local player is proposing a trade. */ + ACCEPTING /**< Distant player is proposing a trade. */ + }; + + /** + * Sets the current status of the trade. + */ + void setStatus(Status); +#endif + +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif typedef const std::auto_ptr<Inventory> InventoryPtr; InventoryPtr mMyInventory; @@ -129,11 +150,17 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener ItemContainer *mPartnerItemContainer; gcn::Label *mMoneyLabel; - gcn::Label *mMoneyLabel2; - gcn::Button *mAddButton, *mOkButton, *mCancelButton, *mTradeButton; - ScrollArea *mMyScroll, *mPartnerScroll; + gcn::Button *mTradeButton; +#ifdef EATHENA_SUPPORT + gcn::Button *mOkButton; +#endif gcn::TextField *mMoneyField; + +#ifdef TMWSERV_SUPPORT + Status mStatus; +#else bool mOkOther, mOkMe; +#endif }; extern TradeWindow *tradeWindow; diff --git a/src/gui/unregisterdialog.cpp b/src/gui/unregisterdialog.cpp new file mode 100644 index 00000000..1e09ca23 --- /dev/null +++ b/src/gui/unregisterdialog.cpp @@ -0,0 +1,141 @@ +/* + * 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 + */ + +#include "unregisterdialog.h" + +#include <string> +#include <sstream> + +#include <guichan/widgets/label.hpp> + +#include "../main.h" +#include "../log.h" +#include "../logindata.h" + +#include "button.h" +#include "checkbox.h" +#include "register.h" +#include "passwordfield.h" +#include "textfield.h" +#include "ok_dialog.h" + +#include "../utils/gettext.h" +#include "../utils/strprintf.h" + +UnRegisterDialog::UnRegisterDialog(Window *parent, LoginData *loginData): + Window("Unregister", true, parent), + mWrongDataNoticeListener(new WrongDataNoticeListener()), + mLoginData(loginData) +{ + gcn::Label *userLabel = new gcn::Label(strprintf(_("Name: %s"), mLoginData->username.c_str())); + gcn::Label *passwordLabel = new gcn::Label(_("Password:")); + mPasswordField = new PasswordField(mLoginData->password); + mUnRegisterButton = new Button(_("Unregister"), "unregister", this); + mCancelButton = new Button(_("Cancel"), "cancel", this); + + const int width = 210; + const int height = 80; + setContentSize(width, height); + + userLabel->setPosition(5, 5); + userLabel->setWidth(width - 5); + mPasswordField->setPosition( + 68, userLabel->getY() + userLabel->getHeight() + 7); + mPasswordField->setWidth(130); + + passwordLabel->setPosition(5, mPasswordField->getY() + 1); + + mCancelButton->setPosition( + width - 5 - mCancelButton->getWidth(), + height - 5 - mCancelButton->getHeight()); + mUnRegisterButton->setPosition( + mCancelButton->getX() - 5 - mUnRegisterButton->getWidth(), + mCancelButton->getY()); + + add(userLabel); + add(passwordLabel); + add(mPasswordField); + add(mUnRegisterButton); + add(mCancelButton); + + setLocationRelativeTo(getParent()); + setVisible(true); + mPasswordField->requestFocus(); + mPasswordField->setActionEventId("cancel"); +} + +UnRegisterDialog::~UnRegisterDialog() +{ + delete mWrongDataNoticeListener; +} + +void +UnRegisterDialog::action(const gcn::ActionEvent &event) +{ + if (event.getId() == "cancel") + { + scheduleDelete(); + } + else if (event.getId() == "unregister") + { + const std::string username = mLoginData->username.c_str(); + const std::string password = mPasswordField->getText(); + logger->log("UnregisterDialog::unregistered, Username is %s", + username.c_str()); + + std::stringstream errorMsg; + bool error = false; + + // Check password + if (password.length() < LEN_MIN_PASSWORD) + { + // Pass too short + errorMsg << "The password needs to be at least " + << LEN_MIN_PASSWORD + << " characters long."; + error = true; + } + else if (password.length() > LEN_MAX_PASSWORD - 1 ) + { + // Pass too long + errorMsg << "The password needs to be less than " + << LEN_MAX_PASSWORD + << " characters long."; + error = true; + } + + if (error) + { + mWrongDataNoticeListener->setTarget(this->mPasswordField); + + OkDialog *dlg = new OkDialog("Error", errorMsg.str()); + dlg->addActionListener(mWrongDataNoticeListener); + } + else + { + // No errors detected, unregister the new user. + mUnRegisterButton->setEnabled(false); + mLoginData->password = password; + state = STATE_UNREGISTER_ATTEMPT; + scheduleDelete(); + } + } +} diff --git a/src/gui/unregisterdialog.h b/src/gui/unregisterdialog.h new file mode 100644 index 00000000..1e3cc88f --- /dev/null +++ b/src/gui/unregisterdialog.h @@ -0,0 +1,70 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef UNREGISTERDIALOG_H +#define UNREGISTERDIALOG_H + +#include <iosfwd> +#include <guichan/actionlistener.hpp> + +#include "window.h" +#include "../guichanfwd.h" + +class LoginData; +class OkDialog; +class WrongDataNoticeListener; + +/** + * The Unregister dialog. + * + * \ingroup Interface + */ +class UnRegisterDialog : public Window, public gcn::ActionListener { + public: + /** + * Constructor + * + * @see Window::Window + */ + UnRegisterDialog(Window *parent,LoginData *loginData); + + /** + * Destructor + */ + ~UnRegisterDialog(); + + /** + * Called when receiving actions from the widgets. + */ + void action(const gcn::ActionEvent &event); + + private: + gcn::TextField *mPasswordField; + + gcn::Button *mUnRegisterButton; + gcn::Button *mCancelButton; + + WrongDataNoticeListener *mWrongDataNoticeListener; + + LoginData *mLoginData; +}; + +#endif diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index 6ec62337..ca41dbda 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -185,7 +185,7 @@ void UpdaterWindow::action(const gcn::ActionEvent &event) } else if (event.getId() == "play") { - state = LOADDATA_STATE; + state = STATE_LOADDATA; } } @@ -232,7 +232,7 @@ int UpdaterWindow::updateProgress(void *ptr, uw->mCurrentFile + " (" + toString((int) (progress * 100)) + "%)"); uw->setProgress(progress); - if (state != UPDATE_STATE || uw->mDownloadStatus == UPDATE_ERROR) + if (state != STATE_UPDATE || uw->mDownloadStatus == UPDATE_ERROR) { // If the action was canceled return an error code to stop the mThread return -1; diff --git a/src/gui/updatewindow.h b/src/gui/updatewindow.h index 4ada3c3a..ace398b4 100644 --- a/src/gui/updatewindow.h +++ b/src/gui/updatewindow.h @@ -31,6 +31,8 @@ #include "../utils/mutex.h" +#include "../utils/mutex.h" + class BrowserBox; class Button; class ProgressBar; diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index a1a02e1e..cbd1f3f7 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -26,6 +26,7 @@ #include "../beingmanager.h" #include "../configuration.h" #include "../flooritemmanager.h" +#include "../game.h" #include "../graphics.h" #include "../keyboardconfig.h" #include "../localplayer.h" @@ -48,8 +49,13 @@ Viewport::Viewport(): mTileViewX(0), mTileViewY(0), mShowDebugPath(false), + mVisibleNames(false), mPlayerFollowMouse(false), +#ifdef TMWSERV_SUPPORT + mLocalWalkTime(-1) +#else mWalkTime(0) +#endif { setOpaque(false); addMouseListener(this); @@ -58,9 +64,11 @@ Viewport::Viewport(): mScrollRadius = (int) config.getValue("ScrollRadius", 0); mScrollCenterOffsetX = (int) config.getValue("ScrollCenterOffsetX", 0); mScrollCenterOffsetY = (int) config.getValue("ScrollCenterOffsetY", 0); + mVisibleNames = config.getValue("visiblenames", 1); config.addListener("ScrollLaziness", this); config.addListener("ScrollRadius", this); + config.addListener("visiblenames", this); mPopupMenu = new PopupMenu; } @@ -68,6 +76,8 @@ Viewport::Viewport(): Viewport::~Viewport() { delete mPopupMenu; + + config.removeListener("visiblenames", this); } void Viewport::setMap(Map *map) @@ -97,6 +107,14 @@ void Viewport::draw(gcn::Graphics *gcnGraphics) } // Calculate viewpoint +#ifdef TMWSERV_SUPPORT + int midTileX = (graphics->getWidth() + mScrollCenterOffsetX) / 2; + int midTileY = (graphics->getHeight() + mScrollCenterOffsetX) / 2; + + const Vector &playerPos = player_node->getPosition(); + const int player_x = (int) playerPos.x - midTileX; + const int player_y = (int) playerPos.y - midTileY; +#else int midTileX = (graphics->getWidth() + mScrollCenterOffsetX) / 32 / 2; int midTileY = (graphics->getHeight() + mScrollCenterOffsetY) / 32 / 2; @@ -104,6 +122,7 @@ void Viewport::draw(gcn::Graphics *gcnGraphics) player_node->getXOffset(); int player_y = (player_node->mY - midTileY) * 32 + player_node->getYOffset(); +#endif if (mScrollLaziness < 1) mScrollLaziness = 1; // Avoids division by zero @@ -142,8 +161,10 @@ void Viewport::draw(gcn::Graphics *gcnGraphics) }; // Don't move camera so that the end of the map is on screen - int viewXmax = (mMap->getWidth() * 32) - graphics->getWidth(); - int viewYmax = (mMap->getHeight() * 32) - graphics->getHeight(); + const int viewXmax = + mMap->getWidth() * mMap->getTileWidth() - graphics->getWidth(); + const int viewYmax = + mMap->getHeight() * mMap->getTileHeight() - graphics->getHeight(); if (mMap) { if (mPixelViewX < 0) { @@ -168,28 +189,13 @@ void Viewport::draw(gcn::Graphics *gcnGraphics) { mMap->draw(graphics, (int) mPixelViewX, (int) mPixelViewY); - // Find a path from the player to the mouse, and draw it. This is for debug - // purposes. - if (mShowDebugPath) - { - // Get the current mouse position - int mouseX, mouseY; - SDL_GetMouseState(&mouseX, &mouseY); - - int mouseTileX = mouseX / 32 + mTileViewX; - int mouseTileY = mouseY / 32 + mTileViewY; - - Path debugPath = mMap->findPath(player_node->mX, player_node->mY, mouseTileX, mouseTileY); - - graphics->setColor(gcn::Color(255, 0, 0)); - for (PathIterator i = debugPath.begin(); i != debugPath.end(); i++) - { - int squareX = i->x * 32 - (int) mPixelViewX + 12; - int squareY = i->y * 32 - (int) mPixelViewY + 12; - - graphics->fillRectangle(gcn::Rectangle(squareX, squareY, 8, 8)); - graphics->drawText(toString(mMap->getMetaTile(i->x, i->y)->Gcost), squareX + 4, squareY + 12, gcn::Graphics::CENTER); - } + if (mShowDebugPath) { + mMap->drawCollision(graphics, + (int) mPixelViewX, + (int) mPixelViewY); +#if 0 + drawDebugPath(graphics); +#endif } } @@ -232,11 +238,52 @@ void Viewport::logic() Uint8 button = SDL_GetMouseState(&mouseX, &mouseY); if (mPlayerFollowMouse && button & SDL_BUTTON(1) && +#ifdef TMWSERV_SUPPORT + get_elapsed_time(mLocalWalkTime) >= walkingMouseDelay) + { + mLocalWalkTime = tick_time; + player_node->setDestination(mouseX + (int) mPixelViewX, + mouseY + (int) mPixelViewY); +#else mWalkTime != player_node->mWalkTime) { player_node->setDestination(mouseX / 32 + mTileViewX, mouseY / 32 + mTileViewY); mWalkTime = player_node->mWalkTime; +#endif + } +} + +void Viewport::drawDebugPath(Graphics *graphics) +{ + // Get the current mouse position + int mouseX, mouseY; + SDL_GetMouseState(&mouseX, &mouseY); + + const int mouseTileX = (mouseX + (int) mPixelViewX) / 32; + const int mouseTileY = (mouseY + (int) mPixelViewY) / 32; + const Vector &playerPos = player_node->getPosition(); + + Path debugPath = mMap->findPath( + (int) playerPos.x / 32, + (int) playerPos.y / 32, + mouseTileX, mouseTileY, 0xFF); + + drawPath(graphics, debugPath); +} + +void Viewport::drawPath(Graphics *graphics, const Path &path) +{ + graphics->setColor(gcn::Color(255, 0, 0)); + for (Path::const_iterator i = path.begin(); i != path.end(); ++i) + { + int squareX = i->x * 32 - (int) mPixelViewX + 12; + int squareY = i->y * 32 - (int) mPixelViewY + 12; + + graphics->fillRectangle(gcn::Rectangle(squareX, squareY, 8, 8)); + graphics->drawText( + toString(mMap->getMetaTile(i->x, i->y)->Gcost), + squareX + 4, squareY + 12, gcn::Graphics::CENTER); } } @@ -252,10 +299,10 @@ void Viewport::mousePressed(gcn::MouseEvent &event) mPlayerFollowMouse = false; - const int tilex = event.getX() / 32 + mTileViewX; - const int tiley = event.getY() / 32 + mTileViewY; - const int x = (int)((float) event.getX() + mPixelViewX); - const int y = (int)((float) event.getY() + mPixelViewY); + const int pixelx = event.getX() + (int) mPixelViewX; + const int pixely = event.getY() + (int) mPixelViewY; + const int tilex = pixelx / mMap->getTileWidth(); + const int tiley = pixely / mMap->getTileHeight(); // Right click might open a popup if (event.getButton() == gcn::MouseEvent::RIGHT) @@ -263,7 +310,7 @@ void Viewport::mousePressed(gcn::MouseEvent &event) Being *being; FloorItem *floorItem; - if ((being = beingManager->findBeingByPixel(x, y)) && + if ((being = beingManager->findBeingByPixel(pixelx, pixely)) && being != player_node) { mPopupMenu->showPopup(event.getX(), event.getY(), being); @@ -287,12 +334,13 @@ void Viewport::mousePressed(gcn::MouseEvent &event) // Left click can cause different actions if (event.getButton() == gcn::MouseEvent::LEFT) { - Being *being; FloorItem *item; +#ifdef EATHENA_SUPPORT + Being *being; // Interact with some being // if ((being = beingManager->findBeing(tilex, tiley))) - if ((being = beingManager->findBeingByPixel(x, y))) + if ((being = beingManager->findBeingByPixel(pixelx, pixely))) { switch (being->getType()) { @@ -321,26 +369,42 @@ void Viewport::mousePressed(gcn::MouseEvent &event) } } // Pick up some item - else if ((item = floorItemManager->findByCoordinates(tilex, tiley))) + else +#endif + if ((item = floorItemManager->findByCoordinates(tilex, tiley))) { player_node->pickUp(item); } // Just walk around else { +#ifdef TMWSERV_SUPPORT + // FIXME: REALLY UGLY! + Uint8 *keys = SDL_GetKeyState(NULL); + if (!(keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT]) && + get_elapsed_time(mLocalWalkTime) >= walkingMouseDelay) + { + mLocalWalkTime = tick_time; + player_node->setDestination(event.getX() + (int) mPixelViewX, + event.getY() + (int) mPixelViewY); + } +#else player_node->stopAttack(); player_node->setDestination(tilex, tiley); +#endif mPlayerFollowMouse = true; } } else if (event.getButton() == gcn::MouseEvent::MIDDLE) { // Find the being nearest to the clicked position - Being *target = beingManager->findBeingByPixel(x, y); + Being *target = beingManager->findNearestLivingBeing( + tilex, tiley, + 20, Being::MONSTER); if (target) { - player_node->setTarget(target); + player_node->setTarget(target); } } } @@ -350,12 +414,22 @@ void Viewport::mouseDragged(gcn::MouseEvent &event) if (!mMap || !player_node) return; +#ifdef TMWSERV_SUPPORT + if (mPlayerFollowMouse + && get_elapsed_time(mLocalWalkTime) >= walkingMouseDelay) + { + mLocalWalkTime = tick_time; + player_node->setDestination(event.getX() + (int) mPixelViewX, + event.getY() + (int) mPixelViewY); + } +#else if (mPlayerFollowMouse && mWalkTime == player_node->mWalkTime) { int destX = event.getX() / 32 + mTileViewX; int destY = event.getY() / 32 + mTileViewY; player_node->setDestination(destX, destY); } +#endif } void Viewport::mouseReleased(gcn::MouseEvent &event) @@ -372,4 +446,20 @@ void Viewport::optionChanged(const std::string &name) { mScrollLaziness = (int) config.getValue("ScrollLaziness", 32); mScrollRadius = (int) config.getValue("ScrollRadius", 32); + + if (name == "visiblenames") { + mVisibleNames = config.getValue("visiblenames", 1); + } +} + +void Viewport::mouseMoved(gcn::MouseEvent &event) +{ + // Check if we are on the map + if (!mMap || !player_node) + return; + + const int tilex = (event.getX() + (int) mPixelViewX) / 32; + const int tiley = (event.getY() + (int) mPixelViewY) / 32; + + mSelectedBeing = beingManager->findBeing(tilex, tiley); } diff --git a/src/gui/viewport.h b/src/gui/viewport.h index 522ea734..a097a4ac 100644 --- a/src/gui/viewport.h +++ b/src/gui/viewport.h @@ -29,6 +29,7 @@ #include "../configlistener.h" #include "../position.h" +class Being; class FloorItem; class Graphics; class ImageSet; @@ -36,6 +37,9 @@ class Item; class Map; class PopupMenu; +/** Delay between two mouse calls when dragging mouse and move the player */ +const int walkingMouseDelay = 500; + /** * The viewport on the map. Displays the current map and handles mouse input * and the popup menu. @@ -94,6 +98,11 @@ class Viewport : public WindowContainer, public gcn::MouseListener, void mouseReleased(gcn::MouseEvent &event); /** + * Handles mouse move on map. + */ + void mouseMoved(gcn::MouseEvent &event); + + /** * Shows a popup for an item. * TODO Find some way to get rid of Item here */ @@ -120,6 +129,17 @@ class Viewport : public WindowContainer, public gcn::MouseListener, void scrollBy(float x, float y) { mPixelViewX += x; mPixelViewY += y; } private: + /** + * Finds a path from the player to the mouse, and draws it. This is for + * debug purposes. + */ + void drawDebugPath(Graphics *graphics); + + /** + * Draws the given path. + */ + void drawPath(Graphics *graphics, const Path &path); + Map *mMap; /**< The current map. */ int mScrollRadius; @@ -131,11 +151,18 @@ class Viewport : public WindowContainer, public gcn::MouseListener, int mTileViewX; /**< Current viewpoint in tiles. */ int mTileViewY; /**< Current viewpoint in tiles. */ bool mShowDebugPath; /**< Show a path from player to pointer. */ + bool mVisibleNames; /**< Show target names. */ bool mPlayerFollowMouse; +#ifdef TMWSERV_SUPPORT + int mLocalWalkTime; /**< Timestamp before the next walk can be sent. */ +#else int mWalkTime; +#endif PopupMenu *mPopupMenu; /**< Popup menu. */ + Being *mSelectedBeing; /**< Current selected being. */ + }; extern Viewport *viewport; /**< The viewport */ diff --git a/src/gui/widgets/avatar.cpp b/src/gui/widgets/avatar.cpp new file mode 100644 index 00000000..9fcd00a6 --- /dev/null +++ b/src/gui/widgets/avatar.cpp @@ -0,0 +1,53 @@ +/* + * The Mana World + * Copyright 2008 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 "avatar.h" + +#include "../icon.h" + +#include "../../resources/image.h" +#include "../../resources/resourcemanager.h" + +Avatar::Avatar(const std::string &name): + mName(name) +{ + setSize(110, 12); + mLabel = new gcn::Label(name); + mLabel->setSize(85, 12); + mLabel->setPosition(25, 0); + ResourceManager *resman = ResourceManager::getInstance(); + mStatusOffline = resman->getImage("graphics/gui/circle-gray.png"); + mStatusOnline = resman->getImage("graphics/gui/circle-green.png"); + mStatus = new Icon(mStatusOffline); + mStatus->setSize(25, 12); + mStatus->setPosition(0, 0); +} + +void Avatar::setOnline(bool online) +{ + mStatus->setImage(online ? mStatusOnline : mStatusOffline); +} + +void Avatar::draw(gcn::Graphics *g) +{ + mLabel->draw(g); + mStatus->draw(g); +} diff --git a/src/gui/widgets/avatar.h b/src/gui/widgets/avatar.h new file mode 100644 index 00000000..c6151020 --- /dev/null +++ b/src/gui/widgets/avatar.h @@ -0,0 +1,59 @@ +/* + * The Mana World + * Copyright 2008 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_AVATAR_H +#define _TMW_AVATAR_H + +#include <string> +#include <guichan/widget.hpp> +#include <guichan/widgets/label.hpp> + +class Image; +class Icon; + +class Avatar : public gcn::Widget +{ +public: + /** + * Constructor. + * @param name Character name + */ + Avatar(const std::string &name); + + /** + * Set the avatar online status. + */ + void setOnline(bool online); + + /** + * Draws the avatar. + */ + void draw(gcn::Graphics *g); + +private: + std::string mName; + Icon *mStatus; + Image *mStatusOnline; + Image *mStatusOffline; + gcn::Label *mLabel; +}; + +#endif diff --git a/src/gui/window.cpp b/src/gui/window.cpp index ee5613bc..58439316 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -534,24 +534,24 @@ void Window::setGuiAlpha() mAlphaChanged = false; } -void Window::loadSkin(const std::string &filename) +void Window::loadSkin(const std::string &fileName) { const std::string windowId = Window::getId(); ResourceManager *resman = ResourceManager::getInstance(); - logger->log("Loading Window Skin '%s'.", filename.c_str()); + logger->log("Loading Window Skin '%s'.", fileName.c_str()); logger->log("Loading Window ID '%s'.", windowId.c_str()); - if (filename.empty()) + if (fileName.empty()) logger->error("Window::loadSkin(): Invalid File Name."); // TODO: // If there is an error loading the specified file, we should try to revert // to a 'default' skin file. Only if the 'default' skin file can't be loaded // should we have a terminating error. - XML::Document doc(filename); + XML::Document doc(fileName); xmlNodePtr rootNode = doc.rootNode(); if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "skinset")) diff --git a/src/gui/window.h b/src/gui/window.h index 518de6e9..3806342a 100644 --- a/src/gui/window.h +++ b/src/gui/window.h @@ -263,7 +263,7 @@ class Window : public gcn::Window, gcn::WidgetListener /** * Loads a window skin */ - void loadSkin(const std::string &filename); + void loadSkin(const std::string &fileName); /** * Adds a widget to the window and sets it at given cell. |