/* * The ManaPlus Client * Copyright (C) 2007-2009 The Mana World Development Team * Copyright (C) 2009-2010 The Mana Developers * Copyright (C) 2011-2012 The ManaPlus Developers * * This file is part of The ManaPlus Client. * * 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, see <http://www.gnu.org/licenses/>. */ #include "gui/outfitwindow.h" #include "configuration.h" #include "equipment.h" #include "graphics.h" #include "inventory.h" #include "item.h" #include "localplayer.h" #include "logger.h" #include "playerinfo.h" #include "gui/chatwindow.h" #include "gui/theme.h" #include "gui/viewport.h" #include "gui/widgets/button.h" #include "gui/widgets/checkbox.h" #include "gui/widgets/chattab.h" #include "gui/widgets/label.h" #include "gui/widgets/layout.h" #include "net/inventoryhandler.h" #include "net/net.h" #include "resources/image.h" #include "resources/resourcemanager.h" #include "utils/gettext.h" #include "utils/stringutils.h" #include <vector> #include "debug.h" float OutfitWindow::mAlpha = 1.0; OutfitWindow::OutfitWindow(): Window(_("Outfits"), false, nullptr, "outfits.xml"), mBoxWidth(33), mBoxHeight(33), mGridWidth(4), mGridHeight(3), mItemClicked(false), mItemMoved(nullptr), mItemSelected(-1), mItemColorSelected(1), mCurrentOutfit(0), mAwayOutfit(0) { setWindowName("Outfits"); setResizable(true); setCloseButton(true); setStickyButtonLock(true); setDefaultSize(250, 400, 150, 230); setMinWidth(145); setMinHeight(220); mBorderColor = Theme::getThemeColor(Theme::BORDER, 64); mBackgroundColor = Theme::getThemeColor(Theme::BACKGROUND, 32); mPreviousButton = new Button(_("<"), "previous", this); mNextButton = new Button(_(">"), "next", this); mCurrentLabel = new Label(strprintf(_("Outfit: %d"), 1)); mCurrentLabel->setAlignment(gcn::Graphics::CENTER); mKeyLabel = new Label(strprintf(_("Key: %s"), keyName(mCurrentOutfit).c_str())); mKeyLabel->setAlignment(gcn::Graphics::CENTER); mUnequipCheck = new CheckBox(_("Unequip first"), serverConfig.getValueBool("OutfitUnequip0", true)); mAwayOutfitCheck = new CheckBox(_("Away outfit"), serverConfig.getValue("OutfitAwayIndex", OUTFITS_COUNT - 1)); mUnequipCheck->setActionEventId("unequip"); mUnequipCheck->addActionListener(this); mAwayOutfitCheck->setActionEventId("away"); mAwayOutfitCheck->addActionListener(this); place(0, 3, mKeyLabel, 4); place(0, 4, mPreviousButton, 1); place(1, 4, mCurrentLabel, 2); place(3, 4, mNextButton, 1); place(0, 5, mUnequipCheck, 4); place(0, 6, mAwayOutfitCheck, 4); Layout &layout = getLayout(); layout.setRowHeight(0, Layout::AUTO_SET); layout.setColWidth(4, Layout::CENTER); loadWindowState(); load(); } OutfitWindow::~OutfitWindow() { save(); } void OutfitWindow::load(bool oldConfig) { Configuration *cfg; if (oldConfig) cfg = &config; else cfg = &serverConfig; memset(mItems, -1, sizeof(mItems)); memset(mItemColors, 1, sizeof(mItemColors)); for (int o = 0; o < OUTFITS_COUNT; o++) { std::string outfit = cfg->getValue("Outfit" + toString(o), "-1"); std::string buf; std::stringstream ss(outfit); std::vector<int> tokens; while (ss >> buf) tokens.push_back(atoi(buf.c_str())); for (int i = 0; i < static_cast<int>(tokens.size()) && i < OUTFIT_ITEM_COUNT; i++) { mItems[o][i] = tokens[i]; } outfit = cfg->getValue("OutfitColor" + toString(o), "1"); std::stringstream ss2(outfit); tokens.clear(); while (ss2 >> buf) tokens.push_back(atoi(buf.c_str())); for (int i = 0; i < static_cast<int>(tokens.size()) && i < OUTFIT_ITEM_COUNT; i++) { mItemColors[o][i] = tokens[i]; } mItemsUnequip[o] = cfg->getValueBool("OutfitUnequip" + toString(o), true); } mAwayOutfit = cfg->getValue("OutfitAwayIndex", OUTFITS_COUNT - 1); if (mAwayOutfit >= OUTFITS_COUNT) mAwayOutfit = OUTFITS_COUNT - 1; if (mAwayOutfitCheck) mAwayOutfitCheck->setSelected(mAwayOutfit == mCurrentOutfit); } void OutfitWindow::save() { std::string outfitStr; std::string outfitColorsStr; for (int o = 0; o < OUTFITS_COUNT; o++) { bool good = false; for (int i = 0; i < OUTFIT_ITEM_COUNT; i++) { int res = mItems[o][i] ? mItems[o][i] : -1; if (res != -1) good = true; outfitStr += toString(res); if (i < OUTFIT_ITEM_COUNT - 1) outfitStr += " "; outfitColorsStr += toString((int)mItemColors[o][i]); if (i < OUTFIT_ITEM_COUNT - 1) outfitColorsStr += " "; } if (good) { serverConfig.setValue("Outfit" + toString(o), outfitStr); serverConfig.setValue("OutfitColor" + toString(o), outfitColorsStr); } else { serverConfig.deleteKey("Outfit" + toString(o)); serverConfig.deleteKey("OutfitColor" + toString(o)); } if (mItemsUnequip[o]) { serverConfig.deleteKey("OutfitUnequip" + toString(o)); } else { serverConfig.setValue("OutfitUnequip" + toString(o), mItemsUnequip[o]); } outfitStr = ""; outfitColorsStr = ""; } serverConfig.setValue("OutfitAwayIndex", mAwayOutfit); } void OutfitWindow::action(const gcn::ActionEvent &event) { if (event.getId() == "next") { next(); } else if (event.getId() == "previous") { previous(); } else if (event.getId() == "unequip") { if (mCurrentOutfit < OUTFITS_COUNT) mItemsUnequip[mCurrentOutfit] = mUnequipCheck->isSelected(); } else if (event.getId() == "away") { mAwayOutfit = mCurrentOutfit; if (!mAwayOutfitCheck->isSelected()) mAwayOutfitCheck->setSelected(true); } } void OutfitWindow::wearOutfit(int outfit, bool unwearEmpty, bool select) { bool isEmpty = true; Item *item; if (outfit < 0 || outfit > OUTFITS_COUNT) return; for (int i = 0; i < OUTFIT_ITEM_COUNT; i++) { item = PlayerInfo::getInventory()->findItem( mItems[outfit][i], mItemColors[outfit][i]); if (item && !item->isEquipped() && item->getQuantity()) { if (item->isEquipment()) { Net::getInventoryHandler()->equipItem(item); isEmpty = false; } } } if ((!isEmpty || unwearEmpty) && outfit < OUTFITS_COUNT && mItemsUnequip[outfit]) { unequipNotInOutfit(outfit); } if (select) { mCurrentOutfit = outfit; showCurrentOutfit(); } } void OutfitWindow::copyOutfit(int outfit) { copyOutfit(outfit, mCurrentOutfit); } void OutfitWindow::copyOutfit(int src, int dst) { if (src < 0 || src > OUTFITS_COUNT || dst < 0 || dst > OUTFITS_COUNT) { return; } for (int i = 0; i < OUTFIT_ITEM_COUNT; i++) { mItems[dst][i] = mItems[src][i]; } } void OutfitWindow::draw(gcn::Graphics *graphics) { Window::draw(graphics); Graphics *g = static_cast<Graphics*>(graphics); for (int i = 0; i < OUTFIT_ITEM_COUNT; i++) { const int itemX = 10 + ((i % mGridWidth) * mBoxWidth); const int itemY = 25 + ((i / mGridWidth) * mBoxHeight); graphics->setColor(mBorderColor); graphics->drawRectangle(gcn::Rectangle(itemX, itemY, 32, 32)); graphics->setColor(mBackgroundColor); graphics->fillRectangle(gcn::Rectangle(itemX, itemY, 32, 32)); if (mItems[mCurrentOutfit][i] < 0) continue; bool foundItem = false; Inventory *inv = PlayerInfo::getInventory(); if (inv) { Item *item = inv->findItem(mItems[mCurrentOutfit][i], mItemColors[mCurrentOutfit][i]); if (item) { // Draw item icon. Image* image = item->getImage(); if (image) { g->drawImage(image, itemX, itemY); foundItem = true; } } } if (!foundItem) { Image *image = Item::getImage(mItems[mCurrentOutfit][i], mItemColors[mCurrentOutfit][i]); if (image) g->drawImage(image, itemX, itemY); } } if (mItemMoved) { // Draw the item image being dragged by the cursor. Image* image = mItemMoved->getImage(); if (image) { const int tPosX = mCursorPosX - (image->mBounds.w / 2); const int tPosY = mCursorPosY - (image->mBounds.h / 2); g->drawImage(image, tPosX, tPosY); } } } void OutfitWindow::mouseDragged(gcn::MouseEvent &event) { if (event.getButton() == gcn::MouseEvent::LEFT) { if (!mItemMoved && mItemClicked) { const int index = getIndexFromGrid(event.getX(), event.getY()); if (index == -1) { Window::mouseDragged(event); return; } const int itemId = mItems[mCurrentOutfit][index]; const int itemColor = mItemColors[mCurrentOutfit][index]; if (itemId < 0) { Window::mouseDragged(event); return; } mMoved = false; event.consume(); Inventory *inv = PlayerInfo::getInventory(); if (inv) { Item *item = inv->findItem(itemId, itemColor); if (item) mItemMoved = item; else mItemMoved = nullptr; mItems[mCurrentOutfit][index] = -1; } } if (mItemMoved) { mCursorPosX = event.getX(); mCursorPosY = event.getY(); } } Window::mouseDragged(event); } void OutfitWindow::mousePressed(gcn::MouseEvent &event) { const int index = getIndexFromGrid(event.getX(), event.getY()); if (index == -1) { if (event.getButton() == gcn::MouseEvent::RIGHT && viewport) viewport->showOutfitsPopup(); Window::mousePressed(event); return; } mMoved = false; event.consume(); // Stores the selected item if there is one. if (isItemSelected()) { mItems[mCurrentOutfit][index] = mItemSelected; mItemColors[mCurrentOutfit][index] = mItemColorSelected; // mItemSelected = -1; } else if (mItems[mCurrentOutfit][index]) { mItemClicked = true; } Window::mousePressed(event); } void OutfitWindow::mouseReleased(gcn::MouseEvent &event) { if (event.getButton() == gcn::MouseEvent::LEFT) { // if (isItemSelected()) // mItemSelected = -1; const int index = getIndexFromGrid(event.getX(), event.getY()); if (index == -1) { mItemMoved = nullptr; Window::mouseReleased(event); return; } mMoved = false; event.consume(); if (mItemMoved) { mItems[mCurrentOutfit][index] = mItemMoved->getId(); mItemColors[mCurrentOutfit][index] = mItemMoved->getColor(); mItemMoved = nullptr; } if (mItemClicked) mItemClicked = false; } Window::mouseReleased(event); } int OutfitWindow::getIndexFromGrid(int pointX, int pointY) const { const gcn::Rectangle tRect = gcn::Rectangle( 10, 25, mGridWidth * mBoxWidth, mGridHeight * mBoxHeight); if (!tRect.isPointInRect(pointX, pointY)) return -1; const int index = (((pointY - 25) / mBoxHeight) * mGridWidth) + (pointX - 10) / mBoxWidth; if (index >= OUTFIT_ITEM_COUNT || index < 0) return -1; return index; } void OutfitWindow::unequipNotInOutfit(int outfit) { // here we think that outfit is correct index Inventory *inventory = PlayerInfo::getInventory(); if (!inventory) return; for (unsigned i = 0; i < inventory->getSize(); i++) { if (inventory->getItem(i) && inventory->getItem(i)->isEquipped()) { bool found = false; for (unsigned f = 0; f < OUTFIT_ITEM_COUNT; f++) { if (inventory->getItem(i)->getId() == mItems[outfit][f]) { found = true; break; } } if (!found) Net::getInventoryHandler()->unequipItem(inventory->getItem(i)); } } } static const SDLKey numsTbl[] = { SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, SDLK_7, SDLK_8, SDLK_9, SDLK_0, SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_INSERT, SDLK_HOME, SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i, SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_BACKSLASH, SDLK_a, SDLK_s, SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON, SDLK_QUOTE, SDLK_z, SDLK_x, SDLK_c, SDLK_v, SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH }; int OutfitWindow::keyToNumber(SDLKey key) const { for (unsigned f = 0; f < sizeof(numsTbl) / sizeof(SDLKey); f ++) { if (numsTbl[f] == key) return f; } return -1; } SDLKey OutfitWindow::numberToKey(unsigned number) const { if (number >= sizeof(numsTbl) / sizeof(SDLKey)) return SDLK_UNKNOWN; return numsTbl[number]; } std::string OutfitWindow::keyName(int number) { return SDL_GetKeyName(numberToKey(number)); } void OutfitWindow::next() { if (mCurrentOutfit < (OUTFITS_COUNT - 1)) mCurrentOutfit++; else mCurrentOutfit = 0; showCurrentOutfit(); } void OutfitWindow::previous() { if (mCurrentOutfit > 0) mCurrentOutfit--; else mCurrentOutfit = OUTFITS_COUNT - 1; showCurrentOutfit(); } void OutfitWindow::showCurrentOutfit() { mCurrentLabel->setCaption(strprintf(_("Outfit: %d"), mCurrentOutfit + 1)); mUnequipCheck->setSelected(mItemsUnequip[mCurrentOutfit]); mKeyLabel->setCaption(strprintf(_("Key: %s"), keyName(mCurrentOutfit).c_str())); mAwayOutfitCheck->setSelected(mAwayOutfit == mCurrentOutfit); } void OutfitWindow::wearNextOutfit(bool all) { bool fromStart = false; next(); if (!all && mCurrentOutfit < OUTFITS_COUNT) { while (!mItemsUnequip[mCurrentOutfit]) { next(); if (mCurrentOutfit == 0) { if (!fromStart) fromStart = true; else return; } } } wearOutfit(mCurrentOutfit); } void OutfitWindow::wearPreviousOutfit(bool all) { bool fromStart = false; previous(); if (!all && mCurrentOutfit < OUTFITS_COUNT) { while (!mItemsUnequip[mCurrentOutfit]) { previous(); if (mCurrentOutfit == 0) { if (!fromStart) fromStart = true; else return; } } } wearOutfit(mCurrentOutfit); } void OutfitWindow::copyFromEquiped() { copyFromEquiped(mCurrentOutfit); } void OutfitWindow::copyFromEquiped(int dst) { Inventory *inventory = PlayerInfo::getInventory(); if (!inventory) return; int outfitCell = 0; for (unsigned i = 0; i < inventory->getSize(); i++) { if (inventory->getItem(i) && inventory->getItem(i)->isEquipped()) { mItems[dst][outfitCell] = inventory->getItem(i)->getId(); mItemColors[dst][outfitCell++] = inventory->getItem(i)->getColor(); if (outfitCell >= OUTFIT_ITEM_COUNT) break; } } } void OutfitWindow::wearAwayOutfit() { copyFromEquiped(OUTFITS_COUNT); wearOutfit(mAwayOutfit, false); } void OutfitWindow::unwearAwayOutfit() { wearOutfit(OUTFITS_COUNT); } void OutfitWindow::setItemSelected(Item *item) { if (item) { mItemSelected = item->getId(); mItemColorSelected = item->getColor(); } else { mItemSelected = -1; mItemColorSelected = 1; } } void OutfitWindow::clearCurrentOutfit() { for (unsigned f = 0; f < OUTFIT_ITEM_COUNT; f++) { mItems[mCurrentOutfit][f] = -1; mItemColors[mCurrentOutfit][f] = 1; } }