diff options
Diffstat (limited to 'src/gui')
101 files changed, 6210 insertions, 1051 deletions
diff --git a/src/gui/browserbox.cpp b/src/gui/browserbox.cpp index 596174cc..2f667237 100644 --- a/src/gui/browserbox.cpp +++ b/src/gui/browserbox.cpp @@ -418,4 +418,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 c5c37601..0b572a23 100644 --- a/src/gui/buy.cpp +++ b/src/gui/buy.cpp @@ -32,14 +32,25 @@ #include "../npc.h" #include "../units.h" +#ifdef TMWSERV_SUPPORT +#include "../net/tmwserv/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"); @@ -128,9 +139,9 @@ void BuyDialog::reset() setMoney(0); } -void BuyDialog::addItem(int id, int price) +void BuyDialog::addItem(int id, int amount, int price) { - mShopItems->addItem(id, price); + mShopItems->addItem(id, amount, price); mShopItemList->adjustSize(); } @@ -180,11 +191,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 2e6b5377..200394b9 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(int id, int price); + void addItem(int id, int amount, int price); /** * Called when receiving actions from the widgets. @@ -110,7 +116,9 @@ class BuyDialog : public Window, public gcn::ActionListener, */ void close(); private: +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif gcn::Button *mBuyButton; gcn::Button *mQuitButton; gcn::Button *mAddMaxButton; @@ -126,9 +134,9 @@ class BuyDialog : public Window, public gcn::ActionListener, ShopItems *mShopItems; - Uint32 mMoney; - Uint32 mAmountItems; - Uint32 mMaxItems; + int mMoney; + int mAmountItems; + int mMaxItems; }; extern BuyDialog *buyDialog; diff --git a/src/gui/buysell.cpp b/src/gui/buysell.cpp index 04a27b8c..c56f6435 100644 --- a/src/gui/buysell.cpp +++ b/src/gui/buysell.cpp @@ -25,7 +25,9 @@ #include "../npc.h" #include "../net/messageout.h" -#include "../net/protocol.h" +#ifdef EATHENA_SUPPORT +#include "../net/ea/protocol.h" +#endif #include "../utils/gettext.h" @@ -93,8 +95,10 @@ void BuySellDialog::action(const gcn::ActionEvent &event) return; } +#ifdef EATHENA_SUPPORT MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_NPC_BUY_SELL_REQUEST); outMsg.writeInt32(current_npc); outMsg.writeInt8(action); +#endif } diff --git a/src/gui/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 aaf4e8c4..7c0e2ab2 100644 --- a/src/gui/char_select.cpp +++ b/src/gui/char_select.cpp @@ -31,6 +31,22 @@ #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/tmwserv/accountserver/account.h" +#include "../net/tmwserv/charserverhandler.h" +#else +#include "../net/ea/charserverhandler.h" +#endif + #include "widgets/layout.h" #include "../game.h" @@ -38,7 +54,6 @@ #include "../main.h" #include "../units.h" -#include "../net/charserverhandler.h" #include "../net/messageout.h" #include "../resources/colordb.h" @@ -47,6 +62,8 @@ #include "../utils/strprintf.h" #include "../utils/stringutils.h" +#define MAX_SLOT 2 + // Defined in main.cpp, used here for setting the char create dialog extern CharServerHandler charServerHandler; @@ -79,22 +96,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 Label(strprintf(_("Name: %s"), "")); + mLevelLabel = new Label(strprintf(_("Level: %d"), 0)); +#ifdef TMWSERV_SUPPORT + mNewCharButton = new Button(_("New"), "new", this); + mDelCharButton = new Button(_("Delete"), "delete", this); + 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 Label(strprintf(_("Account: %s"), mLoginData->username.c_str())); + mNameLabel = new Label(strprintf(_("Name: %s"), "")); + mLevelLabel = new Label(strprintf(_("Level: %d"), 0)); + mMoneyLabel = new Label(strprintf(_("Money: %d"), 0)); + + // Control that shows the Player + mPlayerBox = new PlayerBox; + 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 Label(strprintf(_("Name: %s"), "")); - mLevelLabel = new Label(strprintf(_("Level: %d"), 0)); mJobLevelLabel = new Label(strprintf(_("Job Level: %d"), 0)); mMoneyLabel = new Label(strprintf(_("Money: %s"), mMoney.c_str())); @@ -102,11 +169,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); @@ -125,6 +188,7 @@ CharSelectDialog::CharSelectDialog(Network *network, place(5, 0, mSelectButton); reflowLayout(250, 0); +#endif center(); setVisible(true); @@ -134,10 +198,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); @@ -146,8 +221,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 @@ -164,20 +266,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() @@ -188,13 +305,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); } } @@ -202,9 +326,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); } @@ -213,20 +344,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(); } @@ -258,16 +397,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 Label(_("Name:")); @@ -279,6 +431,19 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, mHairStyleLabel = new 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); @@ -286,6 +451,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); @@ -304,6 +544,7 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, place(5, 0, mCreateButton); reflowLayout(225, 0); +#endif center(); setVisible(true); @@ -327,12 +568,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") @@ -351,6 +615,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() @@ -360,11 +636,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 @@ -381,3 +706,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 22b0e7aa..5cfcef4d 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 94a554e8..597eddfd 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -31,25 +31,42 @@ #include "windowcontainer.h" #include "widgets/layout.h" +#include "widgets/tab.h" +#include "widgets/tabbedarea.h" #include "../beingmanager.h" +#include "../commandhandler.h" +#include "../channelmanager.h" +#include "../channel.h" #include "../configuration.h" #include "../game.h" #include "../localplayer.h" -#include "../party.h" +#ifdef TMWSERV_SUPPORT +#include "../net/tmwserv/chatserver/chatserver.h" +#include "../net/tmwserv/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 +81,10 @@ 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); + mChatTabs = new TabbedArea(); + createNewChannelTab("General"); - mScrollArea = new ScrollArea(mTextOutput); - mScrollArea->setScrollPolicy(gcn::ScrollArea::SHOW_NEVER, - gcn::ScrollArea::SHOW_ALWAYS); - mScrollArea->setScrollAmount(0, 1); - mScrollArea->setOpaque(false); - - 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,28 +97,33 @@ 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)); mReturnToggles = config.getValue("ReturnToggles", "0") == "1"; mRecorder = new Recorder(this); - mParty = new Party(this, mNetwork); // If the player had @assert on in the last session, ask the server to // run the @assert command for the player again. Convenience for GMs. - if (config.getValue(player_node->getName() + "GMassert", 0)) - chatSend(player_node->getName(), "@assert"); + if (config.getValue(player_node->getName() + "GMassert", 0)) { + std::string cmd = "@assert"; + chatSend(cmd); + } +#endif } ChatWindow::~ChatWindow() { +#ifdef EATHENA_SUPPORT char partyPrefix[2] = "."; *partyPrefix = mPartyPrefix; config.setValue("PartyPrefix", partyPrefix); config.setValue("ReturnToggles", mReturnToggles ? "1" : "0"); delete mRecorder; - delete mItemLinkHandler; delete mParty; +#endif + delete mItemLinkHandler; } void ChatWindow::resetToDefaultSize() @@ -118,8 +132,65 @@ void ChatWindow::resetToDefaultSize() Window::resetToDefaultSize(); } -void ChatWindow::chatLog(std::string line, int own, bool ignoreRecord) +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, std::string channelName, + bool ignoreRecord) +{ + if(channelName.empty()) +#ifdef TMWSERV_SUPPORT + channelName = getFocused(); +#else + channelName = "General"; +#endif + + 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); @@ -188,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 += " "; @@ -214,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; @@ -272,24 +352,30 @@ 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)); } -void ChatWindow::chatLog(CHATSKILL act) +const std::string &ChatWindow::getFocused() const { - chatLog(const_msg(act), BY_SERVER); + 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) @@ -309,7 +395,7 @@ void ChatWindow::action(const gcn::ActionEvent &event) mCurHist = mHistory.end(); // Send the message to the server - chatSend(player_node->getName(), message); + chatSend(message); // Clear the text from the chat input mChatInput->setText(""); @@ -358,62 +444,79 @@ bool ChatWindow::isInputFocused() return mChatInput->isFocused(); } -void ChatWindow::whisper(const std::string &nick, std::string msg) +void ChatWindow::removeChannel(short channelId) { - std::string recvnick = ""; - - if (msg.substr(0, 1) == "\"") - { - const std::string::size_type pos = msg.find('"', 1); - if (pos != std::string::npos) - { - recvnick = msg.substr(1, pos - 1); - msg.erase(0, pos + 2); - } - } - else - { - const std::string::size_type pos = msg.find(" "); - if (pos != std::string::npos) - { - recvnick = msg.substr(0, pos); - msg.erase(0, pos + 1); - } - } - - trim(msg); - - std::string playerName = player_node->getName(); - std::string tempNick = recvnick; + removeChannel(channelManager->findById(channelId)); +} - toLower(playerName); - toLower(tempNick); +void ChatWindow::removeChannel(const std::string &channelName) +{ + removeChannel(channelManager->findByName(channelName)); +} - if (tempNick.compare(playerName) == 0 || msg.empty()) - return; +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); - MessageOut outMsg(mNetwork); - outMsg.writeInt16(CMSG_CHAT_WHISPER); - outMsg.writeInt16(msg.length() + 28); - outMsg.writeString(recvnick, 24); - outMsg.writeString(msg, msg.length()); + logic(); + } +} - chatLog(strprintf(_("Whispering to %s: %s"), - recvnick.c_str(), msg.c_str()), - BY_PLAYER); +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::chatSend(const std::string &nick, std::string msg) +void ChatWindow::sendToChannel(short channelId, + const std::string &user, + const std::string &msg) { - /* Some messages are managed client side, while others - * require server handling by proper packet. Probably - * those if elses should be replaced by protocol calls */ + 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); + } +} +void ChatWindow::chatSend(std::string &msg) +{ trim(msg); - if (msg.compare("") == 0) - return; + if (msg.empty()) return; +#ifdef EATHENA_SUPPORT // Send party message if (msg.at(0) == mPartyPrefix) { @@ -432,11 +535,26 @@ void ChatWindow::chatSend(const std::string &nick, std::string msg) outMsg.writeString(msg, length); return; } +#endif // Prepare ordinary message - if (msg.substr(0, 1) != "/") + if (msg[0] != '/') { - msg = nick + " : " + msg; +#ifdef TMWSERV_SUPPORT + if (getFocused() == "General") + { + Net::GameServer::Player::say(msg); + } + else + { + Channel *channel = channelManager->findByName(getFocused()); + if (channel) + { + Net::ChatServer::chat(channel->getId(), msg); + } + } +#else + msg = player_node->getName() + " : " + msg; MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_CHAT_MESSAGE); @@ -444,290 +562,54 @@ void ChatWindow::chatSend(const std::string &nick, std::string msg) outMsg.writeInt16(msg.length() + 4 + 1); outMsg.writeString(msg, msg.length() + 1); return; - } - - msg.erase(0, 1); - trim(msg); - - std::size_t space = msg.find(" "); - std::string command = msg.substr(0, space); - - if (space == std::string::npos) - { - msg = ""; +#endif } else { - msg = msg.substr(space); - trim(msg); - } - - if (command == "announce") - { - MessageOut outMsg(mNetwork); - outMsg.writeInt16(0x0099); - outMsg.writeInt16(msg.length() + 4); - outMsg.writeString(msg, msg.length()); - } - else if (command == "help") - { - trim(msg); - std::size_t space = msg.find(" "); - std::string msg1; - - if (space == std::string::npos) - { - msg1 = ""; - } - else - { - msg1 = msg.substr(space + 1, msg.length()); - msg = msg.substr(0, space); - } - - if (!msg.empty() && msg.at(0) == '/') - { - msg.erase(0, 1); - } - - trim(msg1); - help(msg, msg1); - } - else if (command == "where") - { - // Display the current map, X, and Y - std::ostringstream where; - where << map_path << " " << player_node->mX << "," << player_node->mY; - chatLog(where.str(), BY_SERVER); - } - else if (command == "who") - { - MessageOut outMsg(mNetwork); - outMsg.writeInt16(0x00c1); - } - else if (command == "clear") - mTextOutput->clearRows(); - else if (command == "whisper" || command == "msg" || command == "w") - whisper(nick, msg); - else if (command == "record") - mRecorder->changeRecordingStatus(msg); - else if (command == "toggle") - { - if (msg.empty()) - { - chatLog(mReturnToggles ? _("Return toggles chat.") - : _("Message closes chat."), BY_SERVER); - return; - } - - msg = msg.substr(0, 1); - - if (msg == "1" || - msg == "y" || msg == "Y" || - msg == "t" || msg == "T") - { - chatLog(_("Return now toggles chat."), BY_SERVER); - mReturnToggles = true; - return; - } - else if (msg == "0" || - msg == "n" || msg == "N" || - msg == "f" || msg == "F") - { - chatLog(_("Message now closes chat."), BY_SERVER); - mReturnToggles = false; - return; - } - else - chatLog(_("Options to /toggle are \"yes\", \"no\", \"true\", " - "\"false\", \"1\", \"0\"."), BY_SERVER); + commandHandler->handleCommand(std::string(msg, 1)); } - else if (command == "party") - { - if (msg.empty()) - { - chatLog(_("Unknown party command... Type \"/help\" party for more " - "information."), BY_SERVER); - return; - } - - const std::string::size_type space = msg.find(" "); - std::string rest = (space == std::string::npos ? "" - : msg.substr(space + 1, msg.length())); +} - if (!rest.empty()) - { - msg = msg.substr(0, space); - trim(msg); - } +void ChatWindow::doPresent() +{ + Beings & beings = beingManager->getAll(); + std::string response = ""; - party(msg, rest); - return; - } - else if (command == "cast") + for (BeingIterator bi = beings.begin(), be = beings.end(); + bi != be; ++bi) { - /* - * This will eventually be replaced by a GUI, so - * we don't need to get too sophisticated - */ - MessageOut outMsg(mNetwork); - if (msg == "heal") - { - outMsg.writeInt16(0x03f3); - outMsg.writeInt16(0x01); - outMsg.writeInt32(0); - outMsg.writeInt8(0); - outMsg.writeInt8(0); - outMsg.writeString("", 24); - } - else if (msg == "gather") + if ((*bi)->getType() == Being::PLAYER) { - outMsg.writeInt16(0x03f3); - outMsg.writeInt16(0x02); - outMsg.writeInt32(0); - outMsg.writeInt8(0); - outMsg.writeInt8(0); - outMsg.writeString("", 24); - } - else - chatLog(_("No such spell!"), BY_SERVER); - } - else if (command == "present") - { - Beings & beings = beingManager->getAll(); - std::string response = ""; - - for (BeingIterator bi = beings.begin(), be = beings.end(); - bi != be; ++bi) - { - if ((*bi)->getType() == Being::PLAYER) + if (!response.empty()) { - if (!response.empty()) - { - response += ", "; - } - response += (*bi)->getName(); + response += ", "; } - } - - if (mRecorder->isRecording()) - { - // Get the current system time - time_t t; - time(&t); - - // Format the time string properly - std::stringstream timeStr; - timeStr << "[" << ((((t / 60) / 60) % 24 < 10) ? "0" : "") - << (int) (((t / 60) / 60) % 24) - << ":" << (((t / 60) % 60 < 10) ? "0" : "") - << (int) ((t / 60) % 60) - << "] "; - - - mRecorder->record(timeStr.str() + _("Present: ") + response + "."); - chatLog(_("Attendance written to record log."), BY_SERVER, true); - } - else - { - chatLog(_("Present: ") + response, BY_SERVER); + response += (*bi)->getName(); } } - else if (command == "me") - { - std::stringstream actionStr; - actionStr << "*" << msg << "*"; - chatSend(player_node->getName(), actionStr.str()); - } - else - { - chatLog(_("Unknown command"), BY_SERVER); - } -} -std::string ChatWindow::const_msg(CHATSKILL act) -{ - std::string msg; - if (act.success == SKILL_FAILED && act.skill == SKILL_BASIC) + if (mRecorder->isRecording()) { - switch (act.bskill) - { - case BSKILL_TRADE: - msg = _("Trade failed!"); - break; - case BSKILL_EMOTE: - msg = _("Emote failed!"); - break; - case BSKILL_SIT: - msg = _("Sit failed!"); - break; - case BSKILL_CREATECHAT: - msg = _("Chat creating failed!"); - break; - case BSKILL_JOINPARTY: - msg = _("Could not join party!"); - break; - case BSKILL_SHOUT: - msg = _("Cannot shout!"); - break; - } + // Get the current system time + time_t t; + time(&t); - msg += " "; + // Format the time string properly + std::stringstream timeStr; + timeStr << "[" << ((((t / 60) / 60) % 24 < 10) ? "0" : "") + << (int) (((t / 60) / 60) % 24) + << ":" << (((t / 60) % 60 < 10) ? "0" : "") + << (int) ((t / 60) % 60) + << "] "; - switch (act.reason) - { - case RFAIL_SKILLDEP: - msg += _("You have not yet reached a high enough lvl!"); - break; - case RFAIL_INSUFHP: - msg += _("Insufficient HP!"); - break; - case RFAIL_INSUFSP: - msg += _("Insufficient SP!"); - break; - case RFAIL_NOMEMO: - msg += _("You have no memos!"); - break; - case RFAIL_SKILLDELAY: - msg += _("You cannot do that right now!"); - break; - case RFAIL_ZENY: - msg += _("Seems you need more money... ;-)"); - break; - case RFAIL_WEAPON: - msg += _("You cannot use this skill with that kind of weapon!"); - break; - case RFAIL_REDGEM: - msg += _("You need another red gem!"); - break; - case RFAIL_BLUEGEM: - msg += _("You need another blue gem!"); - break; - case RFAIL_OVERWEIGHT: - msg += _("You're carrying to much to do this!"); - break; - default: - msg += _("Huh? What's that?"); - break; - } + + mRecorder->record(timeStr.str() + _("Present: ") + response + "."); + chatLog(_("Attendance written to record log."), BY_SERVER, std::string(), true); } else { - switch (act.skill) - { - case SKILL_WARP : - msg = _("Warp failed..."); - break; - case SKILL_STEAL : - msg = _("Could not steal anything..."); - break; - case SKILL_ENVENOM : - msg = _("Poison had no effect..."); - break; - } + chatLog(_("Present: ") + response, BY_SERVER); } - - return msg; } void ChatWindow::scroll(int amount) @@ -735,11 +617,18 @@ 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) @@ -792,150 +681,16 @@ void ChatWindow::setVisible(bool isVisible) * For whatever reason, if setVisible is called, the mTmpVisible effect * should be disabled. */ - mTmpVisible = false; } -void ChatWindow::party(const std::string & command, const std::string & rest) +bool ChatWindow::tabExists(const std::string &tabName) { - if (command == "prefix") - { - if (rest.empty()) - { - char temp[2] = "."; - *temp = mPartyPrefix; - chatLog(_("The current party prefix is ") + std::string(temp), - BY_SERVER); - } - else if (rest.length() != 1) - { - chatLog(_("Party prefix must be one character long."), BY_SERVER); - } - else - { - if (rest == "/") - { - chatLog(_("Cannot use a '/' as the prefix."), BY_SERVER); - } - else - { - mPartyPrefix = rest.at(0); - chatLog(_("Changing prefix to ") + rest, BY_SERVER); - } - } - } - else - mParty->respond(command, rest); + Tab *tab = mChatTabs->getTab(tabName); + return tab != 0; } -void ChatWindow::help(const std::string & msg1, const std::string & msg2) +void ChatWindow::setRecordingFile(const std::string &msg) { - chatLog(_("-- Help --"), BY_SERVER); - if (msg1.empty()) - { - chatLog(_("/announce: Global announcement (GM only)"), BY_SERVER); - chatLog(_("/clear: Clears this window"), BY_SERVER); - chatLog(_("/help: Display this help"), BY_SERVER); - chatLog(_("/me <message>: Tell something about yourself"), BY_SERVER); - chatLog(_("/msg <nick> <message>: Alternate form for /whisper"), - BY_SERVER); - chatLog(_("/party <command> <params>: Party commands"), BY_SERVER); - chatLog(_("/present: Get list of players present"), BY_SERVER); - chatLog(_("/record <filename>: Start recording the chat to an " - "external file"), BY_SERVER); - chatLog(_("/toggle: Determine whether <return> toggles the chat log"), - BY_SERVER); - chatLog(_("/w <nick> <message>: Short form for /whisper"), BY_SERVER); - chatLog(_("/where: Display map name"), BY_SERVER); - chatLog(_("/whisper <nick> <message>: Sends a private <message> " - "to <nick>"), BY_SERVER); - chatLog(_("/who: Display number of online users"), BY_SERVER); - chatLog(_("For more information, type /help <command>"), BY_SERVER); - } - else if (msg1 == "announce") - { - chatLog(_("Command: /announce <msg>"), BY_SERVER); - chatLog(_("*** only available to a GM ***"), BY_SERVER); - chatLog(_("This command sends the message <msg> to " - "all players currently online."), BY_SERVER); - } - else if (msg1 == "clear") - { - chatLog(_("Command: /clear"), BY_SERVER); - chatLog(_("This command clears the chat log of previous chat."), - BY_SERVER); - } - else if (msg1 == "help") - { - chatLog(_("Command: /help"), BY_SERVER); - chatLog(_("This command displays a list of all commands available."), - BY_SERVER); - chatLog(_("Command: /help <command>"), BY_SERVER); - chatLog(_("This command displays help on <command>."), BY_SERVER); - } - else if (msg1 == "me") - { - chatLog(_("Command: /me <msg>"), BY_SERVER); - chatLog(_("This command tell others you are (doing) <msg>."), - BY_SERVER); - } - else if (msg1 == "party") - { - mParty->help(msg2); - } - else if (msg1 == "present") - { - chatLog(_("Command: /present"), BY_SERVER); - chatLog(_("This command gets a list of players within hearing and " - "sends it to either the record log if recording, or the chat " - "log otherwise."), BY_SERVER); - } - else if (msg1 == "record") - { - chatLog(_("Command: /record <filename>"), BY_SERVER); - chatLog(_("This command starts recording the chat log to the file " - "<filename>."), BY_SERVER); - chatLog(_("Command: /record"), BY_SERVER); - chatLog(_("This command finishes a recording session."), BY_SERVER); - } - else if (msg1 == "toggle") - { - chatLog(_("Command: /toggle <state>"), BY_SERVER); - chatLog(_("This command sets whether the return key should toggle the " - "chat log, or whether the chat log turns off automatically."), - BY_SERVER); - chatLog(_("<state> can be one of \"1\", \"yes\", \"true\" to " - "turn the toggle on, or \"0\", \"no\", \"false\" to turn the " - "toggle off."), BY_SERVER); - chatLog(_("Command: /toggle"), BY_SERVER); - chatLog(_("This command displays the return toggle status."), - BY_SERVER); - } - else if (msg1 == "where") - { - chatLog(_("Command: /where"), BY_SERVER); - chatLog(_("This command displays the name of the current map."), - BY_SERVER); - } - else if (msg1 == "whisper" || msg1 == "msg" || msg1 == "w") - { - chatLog(_("Command: /msg <nick> <msg>"), BY_SERVER); - chatLog(_("Command: /whisper <nick> <msg>"), BY_SERVER); - chatLog(_("Command: /w <nick> <msg>"), BY_SERVER); - chatLog(_("This command sends the message <msg> to <nick>."), - BY_SERVER); - chatLog(_("If the <nick> has spaces in it, enclose it in " - "double quotes (\")."), BY_SERVER); - } - else if (msg1 == "who") - { - chatLog(_("Command: /who"), BY_SERVER); - chatLog(_("This command displays the number of players currently " - "online."), BY_SERVER); - } - else - { - chatLog(_("Unknown command."), BY_SERVER); - chatLog(_("Type /help for a list of commands."), BY_SERVER); - } + mRecorder->setRecordingFile(msg); } diff --git a/src/gui/chat.h b/src/gui/chat.h index 09f3260b..3c553c67 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,62 +65,26 @@ class ItemLinkHandler; #define CAT_IS "" #define CAT_WHISPER " whispers: " -/** job dependend identifiers (?) */ -#define SKILL_BASIC 0x0001 -#define SKILL_WARP 0x001b -#define SKILL_STEAL 0x0032 -#define SKILL_ENVENOM 0x0034 - -/** basic skills identifiers */ -#define BSKILL_TRADE 0x0000 -#define BSKILL_EMOTE 0x0001 -#define BSKILL_SIT 0x0002 -#define BSKILL_CREATECHAT 0x0003 -#define BSKILL_JOINPARTY 0x0004 -#define BSKILL_SHOUT 0x0005 -#define BSKILL_PK 0x0006 // ?? -#define BSKILL_SETALLIGN 0x0007 // ?? - -/** reasons why action failed */ -#define RFAIL_SKILLDEP 0x00 -#define RFAIL_INSUFHP 0x01 -#define RFAIL_INSUFSP 0x02 -#define RFAIL_NOMEMO 0x03 -#define RFAIL_SKILLDELAY 0x04 -#define RFAIL_ZENY 0x05 -#define RFAIL_WEAPON 0x06 -#define RFAIL_REDGEM 0x07 -#define RFAIL_BLUEGEM 0x08 -#define RFAIL_OVERWEIGHT 0x09 -#define RFAIL_GENERIC 0x0a - -/** 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; - short bskill; - short unused; - char success; - char reason; -}; - /** * 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,6 +92,14 @@ 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(); + + /** * Reset the chat window and recorder window attached to it to their * default positions. */ @@ -128,14 +109,24 @@ class ChatWindow : public Window, public gcn::ActionListener, * Adds a line of text to our message list. Parameters: * * @param line Text message. - * @parem own Type of message (usually the owner-type). + * @param own Type of message (usually the owner-type). + * @param channelName which channel to send the message to. + * @param ignoreRecord should this not be recorded? */ - 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); /** - * Calls original chat_log() after processing the packet. + * Gets the focused tab's name */ - void chatLog(CHATSKILL); + const std::string& getFocused() const; + + /** + * Clear the tab with the given name + */ + void clearTab(const std::string &tab); /** * Performs action. @@ -155,31 +146,30 @@ class ChatWindow : public Window, public gcn::ActionListener, */ bool isInputFocused(); + /** 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); + /** - * Determines whether to send a command or an ordinary message, then - * contructs packets & sends them. - * - * @param nick The character's name to display in front. - * @param msg The message text which is to be send. + * 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 * - * NOTE: - * The nickname is required by the server, if not specified - * the message may not be sent unless a command was intended - * which requires another packet to be constructed! you can - * achieve this by putting a slash ("/") infront of the - * message followed by the command name and the message. - * of course all slash-commands need implemented handler- - * routines. ;-) - * remember, a line starting with "@" is not a command that needs - * to be parsed rather is sent using the normal chat-packet. + * @param msg The message text which is to be sent. * - * EXAMPLE: - * // for an global announcement /- command - * chatlog.chat_send("", "/announce Hello to all logged in users!"); - * // for simple message by a user /- message - * chatlog.chat_send("Zaeiru", "Hello to all users on the screen!"); */ - void chatSend(const std::string &nick, std::string msg); + void chatSend(std::string &msg); /** Called when key is pressed */ void keyPressed(gcn::KeyEvent &event); @@ -193,37 +183,43 @@ 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. - */ - void scroll(int amount); + /** Check if tab with that name already exists */ + bool tabExists(const std::string &tabName); /** - * party implements the partying chat commands + * Scrolls the chat window * - * @param command is the party command to perform - * @param msg is the remainder of the message + * @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 party(const std::string &command, const std::string &msg); + void scroll(int amount); + +#ifdef EATHENA_SUPPORT + char getPartyPrefix() const { return mPartyPrefix; } + void setPartyPrefix(char prefix) { mPartyPrefix = prefix; } +#endif /** - * help implements the /help command + * Sets the file being recorded to * - * @param msg1 is the command that the player needs help on - * @param msg2 is the sub-command relating to the command + * @param msg The file to write out to. If null, then stop recording. */ - void help(const std::string &msg1, const std::string &msg2); + void setRecordingFile(const std::string &msg); - private: + bool getReturnTogglesChat() const { return mReturnToggles; } + void setReturnTogglesChat(bool toggles) { mReturnToggles = toggles; } + void doPresent(); + + private: +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif bool mTmpVisible; - void whisper(const std::string &nick, std::string msg); + int mItems; + int mItemsKeep; /** One item in the chat log */ struct CHATLOG @@ -233,25 +229,46 @@ class ChatWindow : public Window, public gcn::ActionListener, int own; }; - /** Constructs failed messages for actions */ - std::string const_msg(CHATSKILL); + /** + * 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 */ 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 658eade7..5fb21ff2 100644 --- a/src/gui/connection.cpp +++ b/src/gui/connection.cpp @@ -19,31 +19,22 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/actionlistener.hpp> - #include "button.h" #include "connection.h" #include "label.h" #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 Label(_("Connecting...")); @@ -59,6 +50,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 856d7d03..7ac9051f 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -37,6 +37,7 @@ #include "../item.h" #include "../localplayer.h" +#include "../resources/image.h" #include "../resources/iteminfo.h" #include "../resources/resourcemanager.h" @@ -58,8 +59,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; @@ -83,19 +92,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) @@ -106,13 +127,15 @@ 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 { if (i == mSelected) { @@ -130,14 +153,19 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) g->drawRectangle(gcn::Rectangle(mEquipBox[i].posX, mEquipBox[i].posY, BOX_WIDTH, BOX_HEIGHT)); - 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(guiPalette->getColor(Palette::TEXT)); @@ -146,6 +174,7 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) mEquipBox[i].posY - getFont()->getHeight(), gcn::Graphics::CENTER); } +#endif } } } @@ -154,26 +183,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; @@ -191,11 +232,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 83441e1b..87ce74fa 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -104,7 +104,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); @@ -118,7 +119,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..37d739e4 --- /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/tmwserv/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 50fae483..0b554469 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -31,6 +31,7 @@ #include "label.h" #include "progressbar.h" #include "scrollarea.h" +#include "sdlinput.h" #include "viewport.h" #include "widgets/layout.h" @@ -43,20 +44,25 @@ #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(375, 300, ImageRect::CENTER); + addKeyListener(this); std::string longestUseString = getFont()->getWidth(_("Equip")) > getFont()->getWidth(_("Use")) ? @@ -70,8 +76,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,12 +107,15 @@ 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(); } @@ -120,21 +136,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 } @@ -146,8 +162,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", @@ -165,6 +181,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()) @@ -174,19 +198,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); @@ -207,15 +245,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 @@ -223,9 +301,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 e1196f78..95a47bdb 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -27,6 +27,7 @@ #include "../inventory.h" #include <guichan/actionlistener.hpp> +#include <guichan/keylistener.hpp> #include <guichan/selectionlistener.hpp> class Item; @@ -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 a8242aae..0f6aa593 100644 --- a/src/gui/item_amount.cpp +++ b/src/gui/item_amount.cpp @@ -23,7 +23,9 @@ #include "item_amount.h" #include "label.h" #include "slider.h" +#ifdef EATHENA_SUPPORT #include "storagewindow.h" +#endif #include "trade.h" #include "widgets/layout.h" @@ -34,12 +36,16 @@ #include "../utils/gettext.h" #include "../utils/strprintf.h" -ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item): +ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item, + int maxRange): Window("", true, parent), mItem(item), - mMax(item->getQuantity()), + mMax(maxRange), mUsage(usage) { + if (!mMax) + mMax = mItem->getQuantity(); + setCloseButton(true); // Integer field @@ -93,6 +99,9 @@ ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item): case AMOUNT_STORE_REMOVE: setCaption(_("Select amount of items to retrieve.")); break; + case AMOUNT_ITEM_SPLIT: + setCaption(_("Select amount of items to split.")); + break; default: break; } @@ -139,12 +148,18 @@ void ItemAmountWindow::action(const gcn::ActionEvent &event) case AMOUNT_ITEM_DROP: player_node->dropItem(mItem, amount); break; +#ifdef TMWSERV_SUPPORT + case AMOUNT_ITEM_SPLIT: + player_node->splitItem(mItem, amount); + break; +#else case AMOUNT_STORE_ADD: storageWindow->addStore(mItem, amount); break; case AMOUNT_STORE_REMOVE: storageWindow->removeStore(mItem, amount); break; +#endif default: return; break; diff --git a/src/gui/item_amount.h b/src/gui/item_amount.h index d8253e3c..344f8c28 100644 --- a/src/gui/item_amount.h +++ b/src/gui/item_amount.h @@ -33,9 +33,10 @@ class Item; #define AMOUNT_ITEM_DROP 2 #define AMOUNT_STORE_ADD 3 #define AMOUNT_STORE_REMOVE 4 +#define AMOUNT_ITEM_SPLIT 5 /** - * Window used for selecting the amount of items to drop or trade. + * Window used for selecting the amount of items to drop, trade or split. * * \ingroup Interface */ @@ -45,7 +46,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 8a780eb4..38a41e0e 100644 --- a/src/gui/itemcontainer.cpp +++ b/src/gui/itemcontainer.cpp @@ -19,12 +19,15 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include <guichan/mouseinput.hpp> #include <guichan/selectionlistener.hpp> +#include "chat.h" #include "itemcontainer.h" #include "itempopup.h" #include "palette.h" +#include "sdlinput.h" #include "viewport.h" #include "../graphics.h" @@ -39,29 +42,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->setOpaque(false); + 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() - (mOffset - 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,185 +92,214 @@ ItemContainer::~ItemContainer() delete mItemPopup; } -void ItemContainer::logic() -{ - if (!isVisible()) - return; - - gcn::Widget::logic(); - - int i = mInventory->getLastUsedSlot() - (mOffset - 1); // Count from 0, usage from 2 - - if (i != mMaxItems) - { - mMaxItems = i; - recalculateHeight(); - } -} - void ItemContainer::draw(gcn::Graphics *graphics) { - if (!isVisible()) - return; - - int columns = getWidth() / gridWidth; + Graphics *g = static_cast<Graphics*>(graphics); - // Have at least 1 column - if (columns < 1) - columns = 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++) + for (int i = 0; i < mGridColumns; i++) { - Item *item = mInventory->getItem(i); + 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); - if (!item || item->getQuantity() <= 0) - continue; + // Set color to black. + g->setColor(gcn::Color(0, 0, 0)); + // Draw box border. + g->drawRectangle( + gcn::Rectangle(itemX, itemY, BOX_WIDTH, BOX_HEIGHT)); - int itemX = ((i - mOffset) % columns) * gridWidth; - int itemY = ((i - mOffset) / columns) * gridHeight; + Item *item = mInventory->getItem((j * mGridColumns) + i); - // Draw selection image below selected item - if (mSelectedItemIndex == i) - static_cast<Graphics*>(graphics)->drawImage(mSelImg, itemX, itemY); + if (!item || item->getId() == 0) + continue; - // Draw item icon - Image* image = item->getImage(); + 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); + } - if (image) - static_cast<Graphics*>(graphics)->drawImage(image, itemX, itemY); + } + } - // Draw item caption - graphics->setFont(getFont()); - graphics->setColor(guiPalette->getColor(Palette::TEXT)); - graphics->drawText( - (item->isEquipped() ? "Eq." : toString(item->getQuantity())), - itemX + gridWidth / 2, itemY + gridHeight - 11, - gcn::Graphics::CENTER); + 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::widgetResized(const gcn::Event &event) +void ItemContainer::selectNone() { - recalculateHeight(); + setSelectedItem(NULL); } -void ItemContainer::recalculateHeight() +void ItemContainer::setSelectedItem(Item *item) { - 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); -} - -Item *ItemContainer::getSelectedItem() -{ - 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) { @@ -273,11 +324,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 { - int columns = getWidth() / gridWidth; - int index = posX / gridWidth + ((posY / gridHeight) * columns) + mOffset; + 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; +} - return (index); +void ItemContainer::keyAction() +{ + // If there is no highlight then return. + if (!mHighlightedItem) + return; + + // 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 61cb18fb..1b0a2bb2 100644 --- a/src/gui/itempopup.cpp +++ b/src/gui/itempopup.cpp @@ -110,7 +110,9 @@ void ItemPopup::setItem(const ItemInfo &item) mItemEffect->setTextWrapped(item.getEffect(), 196); mItemWeight->setTextWrapped(_("Weight: ") + Units::formatWeight(item.getWeight()), 196); +#ifdef EATHENA_SUPPORT mItemType = item.getType(); +#endif int minWidth = mItemName->getWidth(); @@ -165,7 +167,9 @@ void ItemPopup::setItem(const ItemInfo &item) void ItemPopup::updateColors() { +#ifdef EATHENA_SUPPORT mItemName->setForegroundColor(getColor(mItemType)); +#endif graphics->setColor(guiPalette->getColor(Palette::TEXT)); } diff --git a/src/gui/itemshortcutcontainer.cpp b/src/gui/itemshortcutcontainer.cpp index 9f77fd65..45a5ffa0 100644 --- a/src/gui/itemshortcutcontainer.cpp +++ b/src/gui/itemshortcutcontainer.cpp @@ -108,7 +108,10 @@ void ItemShortcutContainer::draw(gcn::Graphics *graphics) 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, itemX + mBoxWidth / 2, itemY + mBoxHeight - 14, gcn::Graphics::CENTER); 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 5eaf6626..281a25a2 100644 --- a/src/gui/login.cpp +++ b/src/gui/login.cpp @@ -49,6 +49,7 @@ LoginDialog::LoginDialog(LoginData *loginData): { gcn::Label *userLabel = new Label(_("Name:")); gcn::Label *passLabel = new Label(_("Password:")); +#ifdef EATHENA_SUPPORT gcn::Label *serverLabel = new Label(_("Server:")); gcn::Label *portLabel = new Label(_("Port:")); gcn::Label *dropdownLabel = new Label(_("Recent:")); @@ -60,15 +61,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); @@ -77,32 +81,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); @@ -124,8 +138,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(); @@ -133,21 +149,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(); @@ -155,11 +182,12 @@ void LoginDialog::action(const gcn::ActionEvent &event) mLoginData->port = getUShort(mPortField->getText()); else mLoginData->port = 6901; +#endif mLoginData->username = mUserField->getText(); mLoginData->password = mPassField->getText(); - state = REGISTER_STATE; + state = STATE_REGISTER; } } @@ -172,11 +200,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()) @@ -301,3 +332,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 62f3766f..e6ae2d3b 100644 --- a/src/gui/menuwindow.cpp +++ b/src/gui/menuwindow.cpp @@ -39,6 +39,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 @@ -56,14 +61,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; @@ -90,35 +100,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 d8c5d742..93a55688 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -119,8 +119,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(); @@ -171,10 +177,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 9f789a9d..95577e69 100644 --- a/src/gui/ministatus.cpp +++ b/src/gui/ministatus.cpp @@ -35,19 +35,30 @@ MiniStatusWindow::MiniStatusWindow(): Popup("MiniStatus") { 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 } void MiniStatusWindow::setIcon(int index, AnimatedSprite *sprite) @@ -71,6 +82,7 @@ extern volatile int tick_time; void MiniStatusWindow::update() { StatusWindow::updateHPBar(mHpBar); +#ifdef EATHENA_SUPPORT StatusWindow::updateMPBar(mMpBar); StatusWindow::updateXPBar(mXpBar); @@ -86,11 +98,11 @@ void MiniStatusWindow::update() << config.getValue("xpBarMonsterCounterName", "Monsters") <<" left..."; } */ +#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) @@ -102,7 +114,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 6e47f490..f6d80ca7 100644 --- a/src/gui/ministatus.h +++ b/src/gui/ministatus.h @@ -66,8 +66,10 @@ class MiniStatusWindow : public Popup * 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 5bde7f36..5158e966 100644 --- a/src/gui/npc_text.cpp +++ b/src/gui/npc_text.cpp @@ -29,13 +29,24 @@ #include "../npc.h" #include "../net/messageout.h" -#include "../net/protocol.h" +#ifdef TMWSERV_SUPPORT +#include "../net/tmwserv/gameserver/player.h" +#else +#include "../net/ea/protocol.h" +#endif #include "../utils/gettext.h" -NpcTextDialog::NpcTextDialog(Network *network): - Window(_("NPC")), mNetwork(network), - mState(NPC_TEXT_STATE_WAITING) +#ifdef TMWSERV_SUPPORT +NpcTextDialog::NpcTextDialog() +#else +NpcTextDialog::NpcTextDialog(Network *network) +#endif + : Window(_("NPC")) +#ifdef EATHENA_SUPPORT + , mNetwork(network) +#endif + , mState(NPC_TEXT_STATE_WAITING) { setWindowName("NPCText"); setResizable(true); @@ -122,16 +133,22 @@ void NpcTextDialog::action(const gcn::ActionEvent &event) void NpcTextDialog::nextDialog(int npcID) { +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::talkToNPC(npcID, false); +#else MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_NPC_NEXT_REQUEST); outMsg.writeInt32(npcID); +#endif } void NpcTextDialog::closeDialog(int npcID) { +#ifdef EATHENA_SUPPORT MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_NPC_CLOSE); outMsg.writeInt32(npcID); +#endif } void NpcTextDialog::widgetResized(const gcn::Event &event) diff --git a/src/gui/npc_text.h b/src/gui/npc_text.h index 011c7bcf..4c0c31e3 100644 --- a/src/gui/npc_text.h +++ b/src/gui/npc_text.h @@ -30,7 +30,9 @@ #include "../npc.h" +#ifdef EATHENA_SUPPORT class Network; +#endif class TextBox; /** @@ -46,7 +48,11 @@ class NpcTextDialog : public Window, public gcn::ActionListener * * @see Window::Window */ +#ifdef TMWSERV_SUPPORT + NpcTextDialog(); +#else NpcTextDialog(Network *network); +#endif /** * Called when receiving actions from the widgets. @@ -102,7 +108,9 @@ class NpcTextDialog : public Window, public gcn::ActionListener void widgetResized(const gcn::Event &event); private: +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif gcn::ScrollArea *mScrollArea; TextBox *mTextBox; gcn::Button *mButton; diff --git a/src/gui/npcintegerdialog.cpp b/src/gui/npcintegerdialog.cpp index 896f3380..a7ae2748 100644 --- a/src/gui/npcintegerdialog.cpp +++ b/src/gui/npcintegerdialog.cpp @@ -29,13 +29,22 @@ #include "../npc.h" #include "../net/messageout.h" -#include "../net/protocol.h" +#ifdef EATHENA_SUPPORT +#include "../net/ea/protocol.h" +#endif #include "../utils/gettext.h" #include "../utils/strprintf.h" -NpcIntegerDialog::NpcIntegerDialog(Network *network): - Window(_("NPC Number Request")), mNetwork(network) +#ifdef TMWSERV_SUPPORT +NpcIntegerDialog::NpcIntegerDialog() +#else +NpcIntegerDialog::NpcIntegerDialog(Network *network) +#endif + : Window(_("NPC Number Request")) +#ifdef EATHENA_SUPPORT + , mNetwork(network) +#endif { setWindowName("NPCInteger"); mValueField = new IntTextField; @@ -117,10 +126,12 @@ void NpcIntegerDialog::action(const gcn::ActionEvent &event) setVisible(false); NPC::isTalking = false; +#ifdef EATHENA_SUPPORT MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_NPC_INT_RESPONSE); outMsg.writeInt32(current_npc); outMsg.writeInt32(mValueField->getValue()); +#endif mValueField->reset(); } diff --git a/src/gui/npcintegerdialog.h b/src/gui/npcintegerdialog.h index 58f6970b..df74c904 100644 --- a/src/gui/npcintegerdialog.h +++ b/src/gui/npcintegerdialog.h @@ -26,7 +26,9 @@ #include "window.h" +#ifdef EATHENA_SUPPORT class Network; +#endif class IntTextField; /** @@ -42,7 +44,11 @@ class NpcIntegerDialog : public Window, public gcn::ActionListener * * @see Window::Window */ +#ifdef TMWSERV_SUPPORT + NpcIntegerDialog(); +#else NpcIntegerDialog(Network *network); +#endif /** * Called when receiving actions from the widgets. @@ -87,7 +93,9 @@ class NpcIntegerDialog : public Window, public gcn::ActionListener void setVisible(bool visible); private: +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif gcn::Button *mDecButton; gcn::Button *mIncButton; IntTextField *mValueField; diff --git a/src/gui/npclistdialog.cpp b/src/gui/npclistdialog.cpp index ef8b0627..968e2514 100644 --- a/src/gui/npclistdialog.cpp +++ b/src/gui/npclistdialog.cpp @@ -32,13 +32,24 @@ #include "../npc.h" #include "../net/messageout.h" -#include "../net/protocol.h" +#ifdef TMWSERV_SUPPORT +#include "../net/tmwserv/gameserver/player.h" +#else +#include "../net/ea/protocol.h" +#endif #include "../utils/gettext.h" #include "../utils/strprintf.h" -NpcListDialog::NpcListDialog(Network *network): - Window("NPC"), mNetwork(network) +#ifdef TMWSERV_SUPPORT +NpcListDialog::NpcListDialog() +#else +NpcListDialog::NpcListDialog(Network *network) +#endif + : Window("NPC") +#ifdef EATHENA_SUPPORT + , mNetwork(network) +#endif { setWindowName("NPCList"); setResizable(true); @@ -80,6 +91,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); @@ -124,10 +140,14 @@ void NpcListDialog::action(const gcn::ActionEvent &event) saveWindowState(); reset(); +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::selectFromNPC(current_npc, choice); +#else MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_NPC_LIST_CHOICE); outMsg.writeInt32(current_npc); outMsg.writeInt8(choice); +#endif } } diff --git a/src/gui/npclistdialog.h b/src/gui/npclistdialog.h index f5f2147b..6c1e02e3 100644 --- a/src/gui/npclistdialog.h +++ b/src/gui/npclistdialog.h @@ -29,7 +29,9 @@ #include <vector> +#ifdef EATHENA_SUPPORT class Network; +#endif /** * The npc list dialog. @@ -45,7 +47,11 @@ class NpcListDialog : public Window, public gcn::ActionListener, * * @see Window::Window */ +#ifdef TMWSERV_SUPPORT + NpcListDialog(); +#else NpcListDialog(Network *network); +#endif /** * Called when receiving actions from the widgets. @@ -63,6 +69,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. @@ -83,7 +94,9 @@ class NpcListDialog : public Window, public gcn::ActionListener, void requestFocus(); private: +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif gcn::ListBox *mItemList; std::vector<std::string> mItems; diff --git a/src/gui/npcpostdialog.cpp b/src/gui/npcpostdialog.cpp new file mode 100644 index 00000000..f8c0f4b2 --- /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/tmwserv/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/npcstringdialog.cpp b/src/gui/npcstringdialog.cpp index c2379e7a..c84de015 100644 --- a/src/gui/npcstringdialog.cpp +++ b/src/gui/npcstringdialog.cpp @@ -29,13 +29,22 @@ #include "../npc.h" #include "../net/messageout.h" -#include "../net/protocol.h" +#ifdef EATHENA_SUPPORT +#include "../net/ea/protocol.h" +#endif #include "../utils/gettext.h" #include "../utils/strprintf.h" -NpcStringDialog::NpcStringDialog(Network *network): - Window(_("NPC Text Request")), mNetwork(network) +#ifdef TMWSERV_SUPPORT +NpcStringDialog::NpcStringDialog() +#else +NpcStringDialog::NpcStringDialog(Network *network) +#endif + : Window(_("NPC Text Request")) +#ifdef EATHENA_SUPPORT + , mNetwork(network) +#endif { setWindowName("NPCString"); mValueField = new TextField(""); @@ -85,12 +94,14 @@ void NpcStringDialog::action(const gcn::ActionEvent &event) std::string text = mValueField->getText(); mValueField->setText(""); +#ifdef EATHENA_SUPPORT MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_NPC_STR_RESPONSE); outMsg.writeInt16(text.length() + 9); outMsg.writeInt32(current_npc); outMsg.writeString(text, text.length()); outMsg.writeInt8(0); +#endif } bool NpcStringDialog::isInputFocused() diff --git a/src/gui/npcstringdialog.h b/src/gui/npcstringdialog.h index 0c552baa..94cd59b2 100644 --- a/src/gui/npcstringdialog.h +++ b/src/gui/npcstringdialog.h @@ -26,9 +26,9 @@ #include <guichan/actionlistener.hpp> +#ifdef EATHENA_SUPPORT class Network; - -class Network; +#endif /** * The npc integer input dialog. @@ -43,7 +43,11 @@ class NpcStringDialog : public Window, public gcn::ActionListener * * @see Window::Window */ +#ifdef TMWSERV_SUPPORT + NpcStringDialog(); +#else NpcStringDialog(Network *network); +#endif /** * Called when receiving actions from the widgets. @@ -75,7 +79,9 @@ class NpcStringDialog : public Window, public gcn::ActionListener void setVisible(bool visible); private: +#ifdef EATHENA_SUPPORT Network *mNetwork; +#endif gcn::TextField *mValueField; std::string mDefault; }; diff --git a/src/gui/partywindow.cpp b/src/gui/partywindow.cpp new file mode 100644 index 00000000..d60974a8 --- /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/tmwserv/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 7223c453..b7e553dc 100644 --- a/src/gui/playerbox.cpp +++ b/src/gui/playerbox.cpp @@ -84,10 +84,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)) @@ -95,6 +99,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 7627c1ad..9ff9b23f 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 58d7d815..02ddab16 100644 --- a/src/gui/progressbar.cpp +++ b/src/gui/progressbar.cpp @@ -43,6 +43,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); @@ -92,13 +95,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) @@ -141,9 +162,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 aa36e02b..e75b1d44 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; bool mUpdated; 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/recorder.cpp b/src/gui/recorder.cpp index 479ab76b..0536188c 100644 --- a/src/gui/recorder.cpp +++ b/src/gui/recorder.cpp @@ -66,7 +66,7 @@ void Recorder::record(const std::string &msg) } } -void Recorder::changeRecordingStatus(const std::string &msg) +void Recorder::setRecordingFile(const std::string &msg) { std::string msgCopy = msg; trim(msgCopy); @@ -114,5 +114,5 @@ void Recorder::changeRecordingStatus(const std::string &msg) void Recorder::action(const gcn::ActionEvent &event) { - changeRecordingStatus(""); + setRecordingFile(""); } diff --git a/src/gui/recorder.h b/src/gui/recorder.h index 0bbab012..39d00c2c 100644 --- a/src/gui/recorder.h +++ b/src/gui/recorder.h @@ -41,26 +41,26 @@ class Recorder : public Window, public gcn::ActionListener virtual ~Recorder(); - /* + /** * Outputs the message to the recorder file * * @param msg the line to write to the recorded file. */ void record(const std::string &msg); - /* - * Outputs the message to the recorder file + /** + * Sets the file being recorded to * * @param msg The file to write out to. If null, then stop recording. */ - void changeRecordingStatus(const std::string &msg); + void setRecordingFile(const std::string &msg); - /* + /** * Whether or not the recorder is in use. */ - bool isRecording() {return (bool) mStream.is_open();} + bool isRecording() { return (bool) mStream.is_open(); } - /* + /** * called when the button is pressed * * @param event is the event that is generated diff --git a/src/gui/register.cpp b/src/gui/register.cpp index 0e8a99ca..5fb8b579 100644 --- a/src/gui/register.cpp +++ b/src/gui/register.cpp @@ -40,20 +40,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; @@ -73,15 +59,19 @@ RegisterDialog::RegisterDialog(LoginData *loginData): gcn::Label *userLabel = new Label(_("Name:")); gcn::Label *passwordLabel = new Label(_("Password:")); gcn::Label *confirmLabel = new Label(_("Confirm:")); +#ifdef EATHENA_SUPPORT gcn::Label *serverLabel = new Label(_("Server:")); gcn::Label *portLabel = new 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); @@ -90,15 +80,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); @@ -107,8 +101,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 @@ -118,14 +114,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 center(); setVisible(true); @@ -144,7 +144,7 @@ void RegisterDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "cancel") { - state = LOGIN_STATE; + state = STATE_LOGIN; } else if (event.getId() == "register" && canSubmit()) { @@ -193,6 +193,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) @@ -208,23 +210,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 } } } @@ -239,11 +248,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()) @@ -277,3 +289,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..c37305e4 100644 --- a/src/gui/register.h +++ b/src/gui/register.h @@ -31,7 +31,20 @@ 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 +85,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 +103,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 fe391636..22f56195 100644 --- a/src/gui/sell.cpp +++ b/src/gui/sell.cpp @@ -33,14 +33,23 @@ #include "../units.h" #include "../net/messageout.h" -#include "../net/protocol.h" +#ifdef TMWSERV_SUPPORT +#include "../net/tmwserv/gameserver/player.h" +#else +#include "../net/ea/protocol.h" +#endif #include "../utils/gettext.h" #include "../utils/strprintf.h" +#ifdef TMWSERV_SUPPORT +SellDialog::SellDialog(): + Window(_("Sell")), +#else SellDialog::SellDialog(Network *network): Window(_("Sell")), mNetwork(network), +#endif mMaxItems(0), mAmountItems(0) { setWindowName("Sell"); @@ -123,6 +132,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) @@ -134,6 +153,8 @@ void SellDialog::addItem(const Item *item, int price) mShopItemList->adjustSize(); } +#endif + void SellDialog::action(const gcn::ActionEvent &event) { if (event.getId() == "quit") @@ -177,6 +198,10 @@ 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); @@ -195,6 +220,7 @@ void SellDialog::action(const gcn::ActionEvent &event) mAmountItems -= sellCount; outMsg.writeInt16(sellCount); } +#endif mPlayerMoney += mAmountItems * mShopItems->at(selectedItem)->getPrice(); diff --git a/src/gui/sell.h b/src/gui/sell.h index 45b6a704..b6388a1f 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. @@ -101,7 +111,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 *mAddMaxButton; 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..16965571 --- /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/tmwserv/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 849a30c2..72dfbce5 100644 --- a/src/gui/setup.cpp +++ b/src/gui/setup.cpp @@ -50,7 +50,12 @@ extern Window *helpWindow; extern Window *debugWindow; extern Window *itemShortcutWindow; extern Window *emoteShortcutWindow; +#ifdef TMWSERV_SUPPORT +extern Window *magicDialog; +extern Window *guildWindow; +#else extern Window *storageWindow; +#endif Setup::Setup(): Window(_("Setup")) @@ -140,7 +145,9 @@ void Setup::action(const gcn::ActionEvent &event) statusWindow->resetToDefaultSize(); buyDialog->resetToDefaultSize(); sellDialog->resetToDefaultSize(); +#ifdef EATHENA_SUPPORT buySellDialog->resetToDefaultSize(); +#endif inventoryWindow->resetToDefaultSize(); emoteWindow->resetToDefaultSize(); npcTextDialog->resetToDefaultSize(); @@ -153,7 +160,12 @@ void Setup::action(const gcn::ActionEvent &event) debugWindow->resetToDefaultSize(); itemShortcutWindow->resetToDefaultSize(); emoteShortcutWindow->resetToDefaultSize(); +#ifdef TMWSERV_SUPPORT + magicDialog->resetToDefaultSize(); + guildWindow->resetToDefaultSize(); +#else storageWindow->resetToDefaultSize(); +#endif } } diff --git a/src/gui/setup_audio.cpp b/src/gui/setup_audio.cpp index 2ca39282..28a80b3d 100644 --- a/src/gui/setup_audio.cpp +++ b/src/gui/setup_audio.cpp @@ -42,6 +42,7 @@ Setup_Audio::Setup_Audio(): mMusicSlider(new Slider(0, 128)) { setOpaque(false); + setDimension(gcn::Rectangle(0, 0, 250, 200)); gcn::Label *sfxLabel = new Label(_("Sfx volume")); gcn::Label *musicLabel = new Label(_("Music volume")); diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index 74574ec7..07f073db 100644 --- a/src/gui/setup_video.cpp +++ b/src/gui/setup_video.cpp @@ -108,6 +108,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)), mPickupChatEnabled(config.getValue("showpickupchat", true)), @@ -120,6 +121,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)), mPickupNotifyLabel(new Label(_("Show pickup notification"))), @@ -175,6 +177,7 @@ Setup_Video::Setup_Video(): mModeList->setActionEventId("videomode"); mCustomCursorCheckBox->setActionEventId("customcursor"); + mVisibleNamesCheckBox->setActionEventId("visiblenames"); mParticleEffectsCheckBox->setActionEventId("particleeffects"); mPickupChatCheckBox->setActionEventId("pickupchat"); mPickupParticleCheckBox->setActionEventId("pickupparticle"); @@ -194,6 +197,7 @@ Setup_Video::Setup_Video(): mModeList->addActionListener(this); mCustomCursorCheckBox->addActionListener(this); + mVisibleNamesCheckBox->addActionListener(this); mParticleEffectsCheckBox->addActionListener(this); mPickupChatCheckBox->addActionListener(this); mPickupParticleCheckBox->addActionListener(this); @@ -274,7 +278,8 @@ Setup_Video::Setup_Video(): place(1, 0, mFsCheckBox, 2); place(3, 0, mOpenGLCheckBox, 1); place(1, 1, mCustomCursorCheckBox, 3); - place(1, 2, mNameCheckBox, 3); + place(1, 2, mVisibleNamesCheckBox, 3); + place(3, 2, mNameCheckBox, 1); place(1, 3, mParticleEffectsCheckBox, 3); place(1, 4, mPickupNotifyLabel, 3); place(1, 5, mPickupChatCheckBox, 1); @@ -364,6 +369,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); @@ -399,6 +405,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); @@ -413,6 +420,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); @@ -449,6 +457,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 7d9732a1..d2680fa8 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; bool mPickupChatEnabled; @@ -71,6 +72,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 85f238c0..3cdc304c 100644 --- a/src/gui/shop.cpp +++ b/src/gui/shop.cpp @@ -43,6 +43,12 @@ std::string ShopItems::getElementAt(int i) return mShopItems.at(i)->getDisplayName(); } +void ShopItems::addItem(int id, int amount, int price) +{ + mShopItems.push_back(new ShopItem(-1, id, amount, price)); +} + +#ifdef EATHENA_SUPPORT void ShopItems::addItem(int inventoryIndex, int id, int quantity, int price) { ShopItem* item = 0; @@ -61,11 +67,7 @@ void ShopItems::addItem(int inventoryIndex, int id, int quantity, int price) mShopItems.push_back(item); } } - -void ShopItems::addItem(int id, int price) -{ - addItem(-1, id, 0, price); -} +#endif ShopItem* ShopItems::at(int i) const { diff --git a/src/gui/shop.h b/src/gui/shop.h index 0c900d9d..6c3031af 100644 --- a/src/gui/shop.h +++ b/src/gui/shop.h @@ -57,6 +57,12 @@ class ShopItems : public gcn::ListModel ~ShopItems(); /** + * Adds an item to the list. + */ + void addItem(int id, int amount, int price); + +#ifdef EATHENA_SUPPORT + /** * Adds an item to the list (used by sell dialog). Looks for * duplicate entries, if mergeDuplicates was turned on. * @@ -66,15 +72,7 @@ class ShopItems : public gcn::ListModel * @param price price of the item */ void addItem(int inventoryIndex, int id, int amount, int price); - - /** - * Adds an item to the list (used by buy dialog). Looks for - * duplicate entries, if mergeDuplicates was turned on. - * - * @param id the id of the item - * @param price price of the item - */ - void addItem(int id, 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 de0c8406..73ada2e1 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 8bb0e5ea..3eead884 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 a9af2ab4..2f95f1c8 100644 --- a/src/gui/status.cpp +++ b/src/gui/status.cpp @@ -166,14 +166,14 @@ 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(); @@ -284,17 +284,17 @@ void StatusWindow::action(const gcn::ActionEvent &event) void StatusWindow::updateHPBar(ProgressBar *bar, bool showMax) { if (showMax) - bar->setText(toString(player_node->mHp) + - "/" + toString(player_node->mMaxHp)); + bar->setText(toString(player_node->getHp()) + + "/" + toString(player_node->getMaxHp())); else - bar->setText(toString(player_node->mHp)); + bar->setText(toString(player_node->getHp())); // HP Bar coloration - if (player_node->mHp < player_node->mMaxHp / 3) + if (player_node->getHp() < player_node->getMaxHp() / 3) { bar->setColor(223, 32, 32); // Red } - else if (player_node->mHp < (player_node->mMaxHp / 3) * 2) + else if (player_node->getHp() < (player_node->getMaxHp() / 3) * 2) { bar->setColor(230, 171, 34); // Orange } @@ -303,7 +303,7 @@ void StatusWindow::updateHPBar(ProgressBar *bar, bool showMax) bar->setColor(0, 171, 34); // Green } - bar->setProgress((float) player_node->mHp / (float) player_node->mMaxHp); + bar->setProgress((float) player_node->getHp() / (float) player_node->getMaxHp()); } void StatusWindow::updateMPBar(ProgressBar *bar, bool showMax) diff --git a/src/gui/status.h b/src/gui/status.h index f3f54f4e..1425fe12 100644 --- a/src/gui/status.h +++ b/src/gui/status.h @@ -70,7 +70,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..8d4363d9 --- /dev/null +++ b/src/gui/statuswindow.cpp @@ -0,0 +1,376 @@ +/* + * 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(); + + updateHPBar(mHpBar, true); + + // 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); + } +} + +// WARNING: Duplicated method! + +void StatusWindow::updateHPBar(ProgressBar *bar, bool showMax) +{ + if (showMax) + bar->setText(toString(player_node->getHp()) + + "/" + toString(player_node->getMaxHp())); + else + bar->setText(toString(player_node->getHp())); + + // HP Bar coloration + if (player_node->getHp() < player_node->getMaxHp() / 3) + { + bar->setColor(223, 32, 32); // Red + } + else if (player_node->getHp() < (player_node->getMaxHp() / 3) * 2) + { + bar->setColor(230, 171, 34); // Orange + } + else + { + bar->setColor(0, 171, 34); // Green + } + + bar->setProgress((float) player_node->getHp() / (float) player_node->getMaxHp()); +} diff --git a/src/gui/statuswindow.h b/src/gui/statuswindow.h new file mode 100644 index 00000000..05bac4e5 --- /dev/null +++ b/src/gui/statuswindow.h @@ -0,0 +1,107 @@ +/* + * 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(); + + static void updateHPBar(ProgressBar *bar, bool showMax = false); + + 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/storagewindow.cpp b/src/gui/storagewindow.cpp index 8484093a..81212ae8 100644 --- a/src/gui/storagewindow.cpp +++ b/src/gui/storagewindow.cpp @@ -42,7 +42,9 @@ #include "../units.h" #include "../net/messageout.h" -#include "../net/protocol.h" +#ifdef EATHENA_SUPPORT +#include "../net/ea/protocol.h" +#endif #include "../resources/iteminfo.h" @@ -66,7 +68,7 @@ StorageWindow::StorageWindow(Network *network, int invSize): mStoreButton = new Button(_("Store"), "store", this); mRetrieveButton = new Button(_("Retrieve"), "retrieve", this); - mItems = new ItemContainer(player_node->getStorage(), 1); + mItems = new ItemContainer(player_node->getStorage(), 10, 5, 1); mItems->addSelectionListener(this); mInvenScroll = new ScrollArea(mItems); 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 d7554de8..5be71a6f 100644 --- a/src/gui/trade.cpp +++ b/src/gui/trade.cpp @@ -40,23 +40,37 @@ #include "../localplayer.h" #include "../units.h" +#ifdef TMWSERV_SUPPORT +#include "../net/tmwserv/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): - Window("Trade"), +#endif + Window(_("Trade: You")), +#ifdef EATHENA_SUPPORT mNetwork(network), mMyInventory(new Inventory(INVENTORY_SIZE, 2)), mPartnerInventory(new Inventory(INVENTORY_SIZE, 2)) +#else + mMyInventory(new Inventory(INVENTORY_SIZE)), + mPartnerInventory(new Inventory(INVENTORY_SIZE)), + mStatus(PREPARING) +#endif { - setWindowName(_("Trade")); - setDefaultSize(342, 209, ImageRect::CENTER); + setWindowName("Trade"); setResizable(true); setCloseButton(true); + setDefaultSize(342, 209, ImageRect::CENTER); setMinWidth(342); setMinHeight(209); @@ -65,25 +79,40 @@ TradeWindow::TradeWindow(Network *network): getFont()->getWidth(_("Trade")) ? _("OK") : _("Trade"); - mAddButton = new Button(_("Add"), "add", this); + Button *mAddButton = new Button(_("Add"), "add", this); +#ifdef EATHENA_SUPPORT mOkButton = new Button(longestName, "ok", this); - - 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); - mMyScroll = new ScrollArea(mMyItemContainer); + ScrollArea *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); - mPartnerScroll = new ScrollArea(mPartnerItemContainer); + ScrollArea *mPartnerScroll = new ScrollArea(mPartnerItemContainer); mMoneyLabel = new Label(strprintf(_("You get %s."), "")); - mMoneyLabel2 = new Label(_("You give:")); + gcn::Label *mMoneyLabel2 = new 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); @@ -92,9 +121,15 @@ 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(6, 0, mAddButton); - place(7, 0, mOkButton); + place(0, 0, mAddButton); +#ifdef EATHENA_SUPPORT + place(1, 0, mOkButton); +#else + place(2, 0, mTradeButton); + place(3, 0, mCancelButton); +#endif Layout &layout = getLayout(); layout.extend(0, 2, 2, 1); layout.setRowHeight(1, Layout::AUTO_SET); @@ -102,7 +137,9 @@ TradeWindow::TradeWindow(Network *network): layout.setColWidth(0, Layout::AUTO_SET); layout.setColWidth(1, Layout::AUTO_SET); +#ifdef EATHENA_SUPPORT mOkButton->setCaption(_("OK")); +#endif loadWindowState(); } @@ -111,35 +148,37 @@ TradeWindow::~TradeWindow() { } -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) @@ -155,21 +194,36 @@ 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 mOkButton->setCaption(_("OK")); mOkButton->setActionEventId("ok"); 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::receivedOk(bool own) { if (own) @@ -192,8 +246,15 @@ 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 // TODO: Our newer version of eAthena doesn't register this following // function. Detect the actual server version, and re-enable this // for that version only. @@ -202,6 +263,7 @@ void TradeWindow::tradeItem(Item *item, int quantity) outMsg.writeInt16(CMSG_TRADE_ITEM_ADD_REQUEST); outMsg.writeInt16(item->getInvIndex()); outMsg.writeInt32(quantity); +#endif } void TradeWindow::valueChanged(const gcn::SelectionEvent &event) @@ -218,6 +280,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(); @@ -249,12 +323,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()); @@ -276,15 +362,34 @@ 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 } void TradeWindow::close() { +#ifdef TMWSERV_SUPPORT + Net::GameServer::Player::acceptTrade(false); +#else MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_CANCEL_REQUEST); +#endif } diff --git a/src/gui/trade.h b/src/gui/trade.h index 67138c24..4c215ba6 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,24 +62,25 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener ~TradeWindow(); /** - * 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. @@ -84,11 +91,16 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener * Increase quantity of an item. */ void increaseQuantity(int index, bool own, int quantity); +#endif /** * Player received ok message from server */ +#ifdef TMWSERV_SUPPORT + void receivedOk(); +#else void receivedOk(bool own); +#endif /** * Send trade packet. @@ -113,7 +125,23 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener void close(); 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; @@ -123,11 +151,18 @@ class TradeWindow : public Window, gcn::ActionListener, gcn::SelectionListener ItemContainer *mPartnerItemContainer; gcn::Label *mMoneyLabel; - gcn::Label *mMoneyLabel2; - gcn::Button *mAddButton, *mOkButton; - ScrollArea *mMyScroll, *mPartnerScroll; + gcn::Button *mTradeButton; +#ifdef EATHENA_SUPPORT + gcn::Button *mAddButton; + 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 295b752e..8c903c28 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -183,7 +183,7 @@ void UpdaterWindow::action(const gcn::ActionEvent &event) } else if (event.getId() == "play") { - state = LOADDATA_STATE; + state = STATE_LOADDATA; } } @@ -230,7 +230,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 7f97d98c..c840e456 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" @@ -50,8 +51,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); @@ -60,9 +66,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; } @@ -70,6 +78,8 @@ Viewport::Viewport(): Viewport::~Viewport() { delete mPopupMenu; + + config.removeListener("visiblenames", this); } void Viewport::setMap(Map *map) @@ -99,6 +109,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; @@ -106,6 +124,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 @@ -148,8 +167,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) @@ -170,30 +191,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 - SDL_GetMouseState(&mMouseX, &mMouseY); - - int mouseTileX = mMouseX / 32 + mTileViewX; - int mouseTileY = mMouseY / 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 } } @@ -234,11 +238,51 @@ void Viewport::logic() Uint8 button = SDL_GetMouseState(&mMouseX, &mMouseY); if (mPlayerFollowMouse && button & SDL_BUTTON(1) && +#ifdef TMWSERV_SUPPORT + get_elapsed_time(mLocalWalkTime) >= walkingMouseDelay) + { + mLocalWalkTime = tick_time; + player_node->setDestination(mMouseX + (int) mPixelViewX, + mMouseY + (int) mPixelViewY); +#else mWalkTime != player_node->mWalkTime) { player_node->setDestination(mMouseX / 32 + mTileViewX, mMouseY / 32 + mTileViewY); mWalkTime = player_node->mWalkTime; +#endif + } +} + +void Viewport::drawDebugPath(Graphics *graphics) +{ + // Get the current mouse position + SDL_GetMouseState(&mMouseX, &mMouseY); + + const int mouseTileX = (mMouseX + (int) mPixelViewX) / 32; + const int mouseTileY = (mMouseY + (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); } } @@ -254,10 +298,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) @@ -265,7 +309,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); @@ -289,12 +333,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()) { @@ -325,23 +370,38 @@ 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->findNearestLivingBeing(tilex, tiley, 20, - Being::MONSTER); + Being *target = beingManager->findNearestLivingBeing( + tilex, tiley, + 20, Being::MONSTER); if (target) player_node->setTarget(target); @@ -353,12 +413,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) @@ -375,4 +445,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 6e7a2370..c051e5a2 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 */ @@ -130,6 +139,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; @@ -143,11 +163,17 @@ 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 |