diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/button.cpp | 55 | ||||
-rw-r--r-- | src/gui/inventorywindow.cpp | 135 | ||||
-rw-r--r-- | src/gui/inventorywindow.h | 31 | ||||
-rw-r--r-- | src/gui/item_amount.cpp | 29 | ||||
-rw-r--r-- | src/gui/item_amount.h | 1 | ||||
-rw-r--r-- | src/gui/itemcontainer.cpp | 338 | ||||
-rw-r--r-- | src/gui/itemcontainer.h | 71 | ||||
-rw-r--r-- | src/gui/popupmenu.cpp | 10 | ||||
-rw-r--r-- | src/gui/trade.cpp | 5 | ||||
-rw-r--r-- | src/inventory.cpp | 14 | ||||
-rw-r--r-- | src/inventory.h | 8 | ||||
-rw-r--r-- | src/localplayer.cpp | 32 | ||||
-rw-r--r-- | src/localplayer.h | 11 | ||||
-rw-r--r-- | src/net/gameserver/player.cpp | 17 | ||||
-rw-r--r-- | src/net/gameserver/player.h | 2 | ||||
-rw-r--r-- | src/net/protocol.h | 3 |
16 files changed, 564 insertions, 198 deletions
diff --git a/src/gui/button.cpp b/src/gui/button.cpp index 83d4657c..eec2e4b2 100644 --- a/src/gui/button.cpp +++ b/src/gui/button.cpp @@ -34,9 +34,32 @@ #include "../utils/dtor.h" -ImageRect Button::button[4]; int Button::mInstances = 0; +enum{ + BUTTON_STANDARD, // 0 + BUTTON_HIGHLIGHTED, // 1 + BUTTON_PRESSED, // 2 + BUTTON_DISABLED, // 3 + BUTTON_COUNT // 4 - Must be last. +}; + +struct ButtonData +{ + char *const file; + int gridX; + int gridY; +}; + +static ButtonData const data[BUTTON_COUNT] = { + {"graphics/gui/button.png", 0, 0}, + {"graphics/gui/buttonhi.png", 9, 4}, + {"graphics/gui/buttonpress.png", 16, 19}, + {"graphics/gui/button_disabled.png", 25, 24} +}; + +ImageRect Button::button[BUTTON_COUNT]; + Button::Button(const std::string& caption, const std::string &actionEventId, gcn::ActionListener *listener): gcn::Button(caption), @@ -48,24 +71,20 @@ Button::Button(const std::string& caption, const std::string &actionEventId, { // Load the skin ResourceManager *resman = ResourceManager::getInstance(); - Image *btn[4]; - btn[0] = resman->getImage("graphics/gui/button.png"); - btn[1] = resman->getImage("graphics/gui/buttonhi.png"); - btn[2] = resman->getImage("graphics/gui/buttonpress.png"); - btn[3] = resman->getImage("graphics/gui/button_disabled.png"); - int bgridx[4] = {0, 9, 16, 25}; - int bgridy[4] = {0, 4, 19, 24}; + Image *btn[BUTTON_COUNT]; + int a, x, y, mode; - for (mode = 0; mode < 4; mode++) + for (mode = 0; mode < BUTTON_COUNT; mode++) { + btn[mode] = resman->getImage(data[mode].file); a = 0; for (y = 0; y < 3; y++) { for (x = 0; x < 3; x++) { button[mode].grid[a] = btn[mode]->getSubImage( - bgridx[x], bgridy[y], - bgridx[x + 1] - bgridx[x] + 1, - bgridy[y + 1] - bgridy[y] + 1); + data[x].gridX, data[y].gridY, + data[x + 1].gridX - data[x].gridX + 1, + data[y + 1].gridY - data[y].gridY + 1); a++; } } @@ -86,7 +105,7 @@ Button::~Button() if (mInstances == 0) { - for (int mode = 0; mode < 4; mode++) + for (int mode = 0; mode < BUTTON_COUNT; mode++) { for_each(button[mode].grid, button[mode].grid + 9, dtor<Image*>()); } @@ -99,16 +118,16 @@ Button::draw(gcn::Graphics *graphics) int mode; if (!isEnabled()) { - mode = 3; + mode = BUTTON_DISABLED; } else if (isPressed() || mIsLogged) { - mode = 2; + mode = BUTTON_PRESSED; } - else if (mHasMouse) { - mode = 1; + else if (mHasMouse || isFocused()) { + mode = BUTTON_HIGHLIGHTED; } else { - mode = 0; + mode = BUTTON_STANDARD; } static_cast<Graphics*>(graphics)-> diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index 7b199be4..32c18da6 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -28,6 +28,7 @@ #include <guichan/mouseinput.hpp> #include <guichan/widgets/label.hpp> +#include <guichan/widgets/checkbox.hpp> #include "button.h" #include "gui.h" @@ -38,6 +39,7 @@ #include "../item.h" #include "../localplayer.h" +#include "../log.h" #include "../resources/iteminfo.h" @@ -51,40 +53,60 @@ InventoryWindow::InventoryWindow(): setMinWidth(240); setMinHeight(172); // If you adjust these defaults, don't forget to adjust the trade window's. - setDefaultSize(115, 25, 322, 172); + setDefaultSize(115, 25, 368, 326); + addKeyListener(this); mUseButton = new Button(_("Use"), "use", this); mDropButton = new Button(_("Drop"), "drop", this); - mItems = new ItemContainer(player_node->mInventory.get()); + mSplitBox = new gcn::CheckBox(_("Split")); + + mItems = new ItemContainer(player_node->mInventory.get(), 10, 5); mItems->addSelectionListener(this); mInvenScroll = new ScrollArea(mItems); mInvenScroll->setPosition(8, 8); - mInvenScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); mItemNameLabel = new gcn::Label(strprintf(_("Name: %s"), "")); - mItemDescriptionLabel = new gcn::Label(strprintf(_("Description: %s"), "")); + mItemDescriptionLabel = new gcn::Label( + strprintf(_("Description: %s"), "")); mItemEffectLabel = new gcn::Label(strprintf(_("Effect: %s"), "")); - mWeightLabel = new gcn::Label(strprintf(_("Total Weight: %d - Maximum Weight: %d"), 0, 0)); + mWeightLabel = new gcn::Label( + strprintf(_("Total Weight: %d - Maximum Weight: %d"), 0, 0)); mWeightLabel->setPosition(8, 8); mInvenScroll->setPosition(8, mWeightLabel->getY() + mWeightLabel->getHeight() + 5); add(mUseButton); add(mDropButton); + add(mSplitBox); add(mInvenScroll); add(mItemNameLabel); add(mItemDescriptionLabel); add(mItemEffectLabel); add(mWeightLabel); - mUseButton->setSize(48, mUseButton->getHeight()); + mUseButton->setWidth(48); + mDropButton->setWidth(48); loadWindowState("Inventory"); updateContentSize(); } +InventoryWindow::~InventoryWindow() +{ + delete mUseButton; + delete mDropButton; + delete mSplitBox; + delete mItems; + delete mInvenScroll; + + delete mItemNameLabel; + delete mItemDescriptionLabel; + delete mItemEffectLabel; + delete mWeightLabel; +} + void InventoryWindow::logic() { Window::logic(); @@ -94,7 +116,8 @@ void InventoryWindow::logic() updateButtons(); // Update weight information - mWeightLabel->setCaption(strprintf(_("Total Weight: %d - Maximum Weight: %d"), + mWeightLabel->setCaption( + strprintf(_("Total Weight: %d - Maximum Weight: %d"), player_node->getTotalWeight(), player_node->getMaxWeight())); mWeightLabel->adjustSize(); } @@ -102,23 +125,29 @@ void InventoryWindow::logic() void InventoryWindow::action(const gcn::ActionEvent &event) { Item *item = mItems->getItem(); - if (!item) { return; } - if (event.getId() == "use") { + if (event.getId() == "use") + { if (item->isEquipment()) { player_node->equipItem(item); } else { - player_node->useItem(item); + player_node->useItem(item->getInvIndex()); } } else if (event.getId() == "drop") { - // Choose amount of items to drop - new ItemAmountWindow(AMOUNT_ITEM_DROP, this, item); + 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(); } } @@ -127,16 +156,24 @@ void InventoryWindow::selectionChanged(const SelectionEvent &event) Item *item = mItems->getItem(); ItemInfo const *info = item ? &item->getInfo() : NULL; - mItemNameLabel->setCaption - (strprintf(_("Name: %s"), info ? info->getName().c_str() : "")); - mItemEffectLabel->setCaption - (strprintf(_("Effect: %s"), info ? info->getEffect().c_str() : "")); - mItemDescriptionLabel->setCaption - (strprintf(_("Description: %s"), info ? info->getDescription().c_str() : "")); + mItemNameLabel->setCaption(strprintf(_("Name: %s"), + info ? info->getName().c_str() : "")); + mItemEffectLabel->setCaption(strprintf(_("Effect: %s"), + info ? info->getEffect().c_str() : "")); + mItemDescriptionLabel->setCaption(strprintf(_("Description: %s"), + info ? info->getDescription().c_str() : "")); mItemNameLabel->adjustSize(); mItemEffectLabel->adjustSize(); mItemDescriptionLabel->adjustSize(); + + if (mSplitBox->isMarked()) + { + if (item && !item->isEquipment() && item->getQuantity() > 1) { + mSplitBox->setMarked(false); + new ItemAmountWindow(AMOUNT_ITEM_SPLIT, this, item); + } + } } void InventoryWindow::mouseClicked(gcn::MouseEvent &event) @@ -147,54 +184,47 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event) { Item *item = mItems->getItem(); - if (!item) return; + if (!item) { + return; + } /* Convert relative to the window coordinates to absolute screen * coordinates. */ - int mx = event.getX() + getX(); - int my = event.getY() + getY(); + const int mx = event.getX() + getX(); + const int my = event.getY() + getY(); viewport->showPopup(mx, my, item); } } void InventoryWindow::updateContentSize() { - gcn::Rectangle area = getChildrenArea(); - int width = area.width; - int height = area.height; - int columns = width / 24; - - if (columns < 1) - { - columns = 1; - } + const gcn::Rectangle area = getChildrenArea(); // Resize widgets - mUseButton->setPosition(8, height - 24); - mDropButton->setPosition(48 + 16, height - 24); - mInvenScroll->setSize(width - 16, height - 110); + mUseButton->setPosition(8, area.height - 24); + mDropButton->setPosition(64, area.height - 24); // 8 + 48 + 8 = 64. + mSplitBox->setPosition(128, area.height - 20); + mInvenScroll->setSize(area.width - 16, area.height - 110); mItemNameLabel->setPosition(8, - mInvenScroll->getY() + mInvenScroll->getHeight() + 4); + mInvenScroll->getY() + mInvenScroll->getHeight() + 4); mItemEffectLabel->setPosition(8, - mItemNameLabel->getY() + mItemNameLabel->getHeight() + 4); + mItemNameLabel->getY() + mItemNameLabel->getHeight() + 4); mItemDescriptionLabel->setPosition(8, - mItemEffectLabel->getY() + mItemEffectLabel->getHeight() + 4); + mItemEffectLabel->getY() + mItemEffectLabel->getHeight() + 4); } void InventoryWindow::updateButtons() { - Item *item; + Item *item = mItems->getItem(); - if ((item = mItems->getItem()) && item->isEquipment()) - { + if (item && item->isEquipment()) { mUseButton->setCaption(_("Equip")); } else { - mUseButton ->setCaption(_("Use")); + mUseButton->setCaption(_("Use")); } - mUseButton->setEnabled(!!item); mDropButton->setEnabled(!!item); } @@ -204,3 +234,26 @@ Item* InventoryWindow::getItem() return mItems->getItem(); } +void +InventoryWindow::keyPressed(gcn::KeyEvent &event) +{ + switch (event.getKey().getValue()) + { + case gcn::Key::LEFT_SHIFT: + case gcn::Key::RIGHT_SHIFT: + mSplitBox->setMarked(true); + break; + } +} + +void +InventoryWindow::keyReleased(gcn::KeyEvent &event) +{ + switch (event.getKey().getValue()) + { + case gcn::Key::LEFT_SHIFT: + case gcn::Key::RIGHT_SHIFT: + mSplitBox->setMarked(false); + break; + } +} diff --git a/src/gui/inventorywindow.h b/src/gui/inventorywindow.h index b153bb39..450d6565 100644 --- a/src/gui/inventorywindow.h +++ b/src/gui/inventorywindow.h @@ -25,6 +25,9 @@ #define _TMW_INVENTORYWINDOW_H #include <guichan/actionlistener.hpp> +#include <guichan/keylistener.hpp> + +#include <guichan/widgets/checkbox.hpp> #include "window.h" #include "selectionlistener.h" @@ -39,7 +42,10 @@ class ItemContainer; * * \ingroup Interface */ -class InventoryWindow : public Window, gcn::ActionListener, SelectionListener +class InventoryWindow : public Window, + public gcn::ActionListener, + public gcn::KeyListener, + public SelectionListener { public: /** @@ -48,6 +54,11 @@ class InventoryWindow : public Window, gcn::ActionListener, SelectionListener InventoryWindow(); /** + * Destructor. + */ + ~InventoryWindow(); + + /** * Logic (updates buttons and weight information). */ void logic(); @@ -57,8 +68,21 @@ class InventoryWindow : public Window, gcn::ActionListener, SelectionListener */ void action(const gcn::ActionEvent &event); + /** + * Handles the mouse clicks. + */ void mouseClicked(gcn::MouseEvent &event); + /** + * Handles the key presses. + */ + void keyPressed(gcn::KeyEvent &event); + + /** + * Handles the key releases. + */ + void keyReleased(gcn::KeyEvent &event); + Item* getItem(); /** @@ -75,8 +99,9 @@ class InventoryWindow : public Window, gcn::ActionListener, SelectionListener ItemContainer *mItems; - gcn::Button *mUseButton, *mDropButton; - gcn::ScrollArea *mInvenScroll; + gcn::Button *mUseButton, *mDropButton; /**< Use, Drop Item Buttons. */ + gcn::CheckBox *mSplitBox; /**< Split item checkbox. */ + gcn::ScrollArea *mInvenScroll; /**< Inventory Scroll Area. */ gcn::Label *mItemNameLabel; gcn::Label *mItemDescriptionLabel; gcn::Label *mItemEffectLabel; diff --git a/src/gui/item_amount.cpp b/src/gui/item_amount.cpp index 2e3ac4ad..22c219f2 100644 --- a/src/gui/item_amount.cpp +++ b/src/gui/item_amount.cpp @@ -83,6 +83,10 @@ ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item): setCaption(_("Select amount of items to drop.")); okButton->setActionEventId("Drop"); break; + case AMOUNT_ITEM_SPLIT: + setCaption(_("Select amount of items to split.")); + okButton->setActionEventId("Split"); + break; default: break; } @@ -105,16 +109,6 @@ void ItemAmountWindow::action(const gcn::ActionEvent &event) { scheduleDelete(); } - else if (event.getId() == "Drop") - { - player_node->dropItem(mItem, mItemAmountTextBox->getInt()); - scheduleDelete(); - } - else if (event.getId() == "AddTrade") - { - tradeWindow->tradeItem(mItem, mItemAmountTextBox->getInt()); - scheduleDelete(); - } else if (event.getId() == "Plus") { amount++; @@ -127,6 +121,21 @@ void ItemAmountWindow::action(const gcn::ActionEvent &event) { amount = static_cast<int>(mItemAmountSlide->getValue()); } + else if (event.getId() == "Drop") + { + player_node->dropItem(mItem, mItemAmountTextBox->getInt()); + scheduleDelete(); + } + else if (event.getId() == "AddTrade") + { + tradeWindow->tradeItem(mItem, mItemAmountTextBox->getInt()); + scheduleDelete(); + } + else if (event.getId() == "Split") + { + player_node->splitItem(mItem, mItemAmountTextBox->getInt()); + scheduleDelete(); + } mItemAmountTextBox->setInt(amount); mItemAmountSlide->setValue(amount); } diff --git a/src/gui/item_amount.h b/src/gui/item_amount.h index 01319012..227f6a87 100644 --- a/src/gui/item_amount.h +++ b/src/gui/item_amount.h @@ -37,6 +37,7 @@ class Item; #define AMOUNT_TRADE_ADD 1 #define AMOUNT_ITEM_DROP 2 +#define AMOUNT_ITEM_SPLIT 3 /** * Window used for selecting the amount of items to drop. diff --git a/src/gui/itemcontainer.cpp b/src/gui/itemcontainer.cpp index 444be2a2..2f65d157 100644 --- a/src/gui/itemcontainer.cpp +++ b/src/gui/itemcontainer.cpp @@ -30,7 +30,7 @@ #include "../graphics.h" #include "../inventory.h" #include "../item.h" -#include "../log.h" +#include "../localplayer.h" #include "../resources/image.h" #include "../resources/iteminfo.h" @@ -38,18 +38,30 @@ #include "../utils/tostring.h" -ItemContainer::ItemContainer(Inventory *inventory): +static const int BOX_WIDTH = 36; +static const int BOX_HEIGHT = 44; + +ItemContainer::ItemContainer(Inventory *inventory, + int gridColumns = 1, int gridRows = 1): mInventory(inventory), - mSelectedItem(NULL) + mGridColumns(gridColumns), + mGridRows(gridRows), + mSelectedItem(NULL), + mHighlightedItem(NULL), + mDragged(false), + mSwapItems(false) { + setFocusable(true); + ResourceManager *resman = ResourceManager::getInstance(); mSelImg = resman->getImage("graphics/gui/selection.png"); - if (!mSelImg) logger->error("Unable to load selection.png"); - - mMaxItems = mInventory->getLastUsedSlot() + 1; + addKeyListener(this); addMouseListener(this); + + setSize((BOX_WIDTH - 1) * mGridColumns + 1, + (BOX_HEIGHT - 1) * mGridRows + 1); } ItemContainer::~ItemContainer() @@ -58,140 +70,284 @@ ItemContainer::~ItemContainer() } void -ItemContainer::logic() +ItemContainer::draw(gcn::Graphics *graphics) { - gcn::Widget::logic(); - - int i = mInventory->getLastUsedSlot() + 1; + Graphics *g = static_cast<Graphics*>(graphics); - if (i != mMaxItems) + for (int i = 0; i < mGridColumns; i++) { - mMaxItems = i; - setWidth(getWidth()); + 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); + + // Set color to black. + g->setColor(gcn::Color(0, 0, 0)); + // Draw box border. + g->drawRectangle( + gcn::Rectangle(itemX, itemY, BOX_WIDTH, BOX_HEIGHT)); + + Item *item = mInventory->getItem((j * mGridColumns) + i); + + if (!item) + return; + Image *image = item->getInfo().getImage(); + if (image) + { + if (item == mSelectedItem) + { + if (mDragged) { + // 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 (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::draw(gcn::Graphics *graphics) +ItemContainer::selectNone() { - int gridWidth = 36; //(item icon width + 4) - int gridHeight = 42; //(item icon height + 10) - int columns = getWidth() / gridWidth; + setSelectedItem(NULL); +} - // Have at least 1 column - if (columns < 1) - { - columns = 1; +void +ItemContainer::setSelectedItem(Item *item) +{ + if (mSelectedItem != item) { + mSelectedItem = item; + fireSelectionChangedEvent(); } +} - // Reset selected item when quantity not above 0 (should probably be made - // sure somewhere else) - if (mSelectedItem && mSelectedItem->getQuantity() <= 0) +void +ItemContainer::fireSelectionChangedEvent() +{ + SelectionEvent event(this); + SelectionListeners::iterator i_end = mListeners.end(); + SelectionListeners::iterator i; + + for (i = mListeners.begin(); i != i_end; ++i) { + (*i)->selectionChanged(event); + } +} + +void +ItemContainer::keyPressed(gcn::KeyEvent &event) +{ + switch (event.getKey().getValue()) { - selectNone(); + case gcn::Key::LEFT: + moveHighlight(ItemContainer::MOVE_SELECTED_LEFT); + break; + case gcn::Key::RIGHT: + moveHighlight(ItemContainer::MOVE_SELECTED_RIGHT); + break; + case gcn::Key::UP: + moveHighlight(ItemContainer::MOVE_SELECTED_UP); + break; + case gcn::Key::DOWN: + moveHighlight(ItemContainer::MOVE_SELECTED_DOWN); + break; + case gcn::Key::SPACE: + keyAction(); + break; + case gcn::Key::LEFT_ALT: + case gcn::Key::RIGHT_ALT: + mSwapItems = true; } +} - for (int i = 0; i < INVENTORY_SIZE; i++) +void +ItemContainer::keyReleased(gcn::KeyEvent &event) +{ + switch (event.getKey().getValue()) { - Item *item = mInventory->getItem(i); + case gcn::Key::LEFT_ALT: + case gcn::Key::RIGHT_ALT: + mSwapItems = false; + } +} - if (item->getQuantity() <= 0) { - continue; +void +ItemContainer::mousePressed(gcn::MouseEvent &event) +{ + + const int button = event.getButton(); + if (button == gcn::MouseEvent::LEFT || button == gcn::MouseEvent::RIGHT) + { + const int index = getSlotIndex(event.getX(), event.getY()); + if (index == Inventory::NO_SLOT_INDEX) { + return; } - int itemX = (i % columns) * gridWidth; - int itemY = (i / columns) * gridHeight; + Item *item = mInventory->getItem(index); - // Draw selection image below selected item - if (mSelectedItem == item) - { - static_cast<Graphics*>(graphics)->drawImage(mSelImg, itemX, itemY); + if (mSelectedItem && mSelectedItem == item) { + setSelectedItem(NULL); } - - // Draw item icon - if (Image *image = item->getInfo().getImage()) - { - static_cast<Graphics*>(graphics)->drawImage(image, itemX, itemY); + else if (item->getId()) { + setSelectedItem(item); + } + else { + setSelectedItem(NULL); } - - // Draw item caption - graphics->drawText( - toString(item->getQuantity()), - itemX + gridWidth / 2, - itemY + gridHeight - 11, - gcn::Graphics::CENTER); } } void -ItemContainer::setWidth(int width) +ItemContainer::mouseDragged(gcn::MouseEvent &event) { - gcn::Widget::setWidth(width); - - int gridWidth = 36; //item icon width + 4 - int gridHeight = 46; //item icon height + 14 - int columns = getWidth() / gridWidth; - - if (columns < 1) + if (mSelectedItem) { - columns = 1; + if (!mDragged) { + mDragged = true; + } + mDragPosX = event.getX(); + mDragPosY = event.getY(); } - - setHeight((mMaxItems + columns - 1) / columns * gridHeight); -} - -Item* -ItemContainer::getItem() -{ - return mSelectedItem; } void -ItemContainer::selectNone() +ItemContainer::mouseReleased(gcn::MouseEvent &event) { - setSelectedItem(NULL); + if (mDragged) + { + mDragged = false; + + const int index = getSlotIndex(event.getX(), event.getY()); + if (index == Inventory::NO_SLOT_INDEX) { + return; + } + + if (mSelectedItem) { + player_node->moveInvItem(mSelectedItem, index); + setSelectedItem(NULL); + } + } } -void -ItemContainer::setSelectedItem(Item *item) +int +ItemContainer::getSlotIndex(const int posX, const int posY) const { - if (mSelectedItem != item) + if(gcn::Rectangle(0, 0, (getWidth() - 1), (getHeight() - 1)) + .isPointInRect(posX, posY)) { - mSelectedItem = item; - fireSelectionChangedEvent(); + // Takes into account, boxes are overlapping each other. + return (posY / (BOX_HEIGHT - 1)) * mGridColumns + + (posX / (BOX_WIDTH - 1)); } + return Inventory::NO_SLOT_INDEX; } void -ItemContainer::fireSelectionChangedEvent() +ItemContainer::keyAction() { - SelectionEvent event(this); - SelectionListeners::iterator i_end = mListeners.end(); - SelectionListeners::iterator i; + // If there is no highlight then return. + if (!mHighlightedItem) + return; - for (i = mListeners.begin(); i != i_end; ++i) + // If the highlight is on the selected item, then deselect it. + if (mHighlightedItem == mSelectedItem) { - (*i)->selectionChanged(event); + setSelectedItem(NULL); + } + // 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 the select it. + else if (mHighlightedItem->getId()) + { + setSelectedItem(mHighlightedItem); + } + // If the highlight is on a blank space then move it. + else if (mSelectedItem) + { + player_node->moveInvItem( + mSelectedItem, mHighlightedItem->getInvIndex()); + setSelectedItem(NULL); } } void -ItemContainer::mousePressed(gcn::MouseEvent &event) +ItemContainer::moveHighlight(int direction) { - int button = event.getButton(); - - if (button == gcn::MouseEvent::LEFT || button == gcn::MouseEvent::RIGHT) + if (!mHighlightedItem) { - int gridWidth = 36; //(item icon width + 4) - int gridHeight = 42; //(item icon height + 10) - int columns = getWidth() / gridWidth; - int mx = event.getX(); - int my = event.getY(); - int index = mx / gridWidth + ((my / gridHeight) * columns); - - if (index > INVENTORY_SIZE) - index = INVENTORY_SIZE - 1; + if (mSelectedItem) { + mHighlightedItem = mSelectedItem; + } + else { + mHighlightedItem = mInventory->getItem(0); + } + return; + } - setSelectedItem(mInventory->getItem(index)); + 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 8c548fcd..38dc9f6e 100644 --- a/src/gui/itemcontainer.h +++ b/src/gui/itemcontainer.h @@ -24,7 +24,9 @@ #ifndef _TMW_ITEMCONTAINER_H__ #define _TMW_ITEMCONTAINER_H__ +#include <guichan/keylistener.hpp> #include <guichan/mouselistener.hpp> + #include <guichan/widget.hpp> #include <list> @@ -39,13 +41,18 @@ class SelectionListener; * * \ingroup GUI */ -class ItemContainer : public gcn::Widget, public gcn::MouseListener +class ItemContainer : public gcn::Widget, + 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. */ - ItemContainer(Inventory *inventory); + ItemContainer(Inventory *inventory, int gridColumns, int gridRows); /** * Destructor. @@ -53,20 +60,19 @@ class ItemContainer : public gcn::Widget, public gcn::MouseListener 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); /** - * Sets the width of the container. This is used to determine the new - * height of the container. + * Handles the key releases. */ - void setWidth(int width); + void keyReleased(gcn::KeyEvent &event); /** * Handles mouse click. @@ -74,9 +80,20 @@ class ItemContainer : public gcn::Widget, public gcn::MouseListener 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* getItem(); + Item* getItem() const + { return mSelectedItem; } /** * Sets selected item to NULL. @@ -101,8 +118,26 @@ class ItemContainer : public gcn::Widget, public gcn::MouseListener mListeners.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(); + + /** + * Moves the highlight in the direction specified. + * + * @param direction The move direction of the highlighter. + */ + void moveHighlight(int direction); + + /** * Sets the currently selected item. */ void setSelectedItem(Item *item); @@ -112,11 +147,21 @@ class ItemContainer : public gcn::Widget, public gcn::MouseListener */ void fireSelectionChangedEvent(); + /** + * Gets the slot index based on the cursor position. + * + * @param posX The X Coordinate position. + * @param posY The Y Coordinate position. + * @return The slot index on success, -1 on failure. + */ + int getSlotIndex(const int posX, const int posY) const; + Inventory *mInventory; + int mGridColumns, mGridRows; Image *mSelImg; - Item *mSelectedItem; - - int mMaxItems; + Item *mSelectedItem, *mHighlightedItem; + bool mDragged, mSwapItems; + int mDragPosX, mDragPosY; std::list<SelectionListener*> mListeners; }; diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 65162d35..48bbd3d0 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -174,10 +174,13 @@ void PopupMenu::handleLink(const std::string& link) } else { - player_node->useItem(mItem); + player_node->useItem(mItem->getInvIndex()); } } - + else if (link == "split") + { + new ItemAmountWindow(AMOUNT_ITEM_SPLIT, inventoryWindow, mItem); + } else if (link == "drop") { new ItemAmountWindow(AMOUNT_ITEM_DROP, inventoryWindow, mItem); @@ -220,9 +223,10 @@ void PopupMenu::showPopup(int x, int y, Item *item) } else mBrowserBox->addRow(_("@@use|Use@@")); - mBrowserBox->addRow(_("@@drop|Drop@@")); mBrowserBox->addRow(_("@@description|Description@@")); + if (!item->isEquipment()) + { mBrowserBox->addRow(_("@@split|Split@@")); } mBrowserBox->addRow("##3---"); mBrowserBox->addRow(_("@@cancel|Cancel@@")); diff --git a/src/gui/trade.cpp b/src/gui/trade.cpp index 724f6d5b..78204120 100644 --- a/src/gui/trade.cpp +++ b/src/gui/trade.cpp @@ -57,14 +57,15 @@ TradeWindow::TradeWindow(): mCancelButton = new Button("Cancel", "cancel", this); mTradeButton = new Button("Trade", "trade", this); - mMyItemContainer = new ItemContainer(mMyInventory.get()); + mMyItemContainer = new ItemContainer(mMyInventory.get(), 5, 3); mMyItemContainer->addSelectionListener(this); mMyItemContainer->setPosition(2, 2); mMyScroll = new ScrollArea(mMyItemContainer); mMyScroll->setPosition(8, 8); - mPartnerItemContainer = new ItemContainer(mPartnerInventory.get()); + mPartnerItemContainer = new ItemContainer( + mPartnerInventory.get(), 5, 3); mPartnerItemContainer->addSelectionListener(this); mPartnerItemContainer->setPosition(2, 58); diff --git a/src/inventory.cpp b/src/inventory.cpp index 11c52421..d9bd79f8 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -30,7 +30,7 @@ struct SlotUsed : public std::unary_function<Item, bool> { bool operator()(const Item &item) const { - return (item.getId() != -1 && item.getQuantity() > 0); + return (item.getId() && item.getQuantity()); } }; @@ -72,8 +72,7 @@ void Inventory::addItem(int index, int id, int quantity) void Inventory::clear() { for (int i = 0; i < INVENTORY_SIZE; i++) { - mItems[i].setId(-1); - mItems[i].setQuantity(0); + removeItemIndex(i); } } @@ -81,12 +80,17 @@ void Inventory::removeItem(int id) { for (int i = 0; i < INVENTORY_SIZE; i++) { if (mItems[i].getId() == id) { - mItems[i].setId(-1); - mItems[i].setQuantity(0); + removeItemIndex(i); } } } +void Inventory::removeItemIndex(int index) +{ + mItems[index].setId(0); + mItems[index].setQuantity(0); +} + bool Inventory::contains(Item *item) { for (int i = 0; i < INVENTORY_SIZE; i++) { diff --git a/src/inventory.h b/src/inventory.h index ce537f34..f0d3e4d7 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -26,7 +26,7 @@ class Item; -#define INVENTORY_SIZE 100 +#define INVENTORY_SIZE 50 class Inventory { @@ -62,6 +62,11 @@ class Inventory void removeItem(int id); /** + * Remove a item from the inventory, specified by the index. + */ + void removeItemIndex(int index); + + /** * Checks if the given item is in the inventory */ bool contains(Item *item); @@ -86,6 +91,7 @@ class Inventory */ int getLastUsedSlot(); + static const int NO_SLOT_INDEX = -1; /**< Slot has no index. */ protected: Item *mItems; /**< The holder of items */ }; diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 478603b8..035c31f6 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -101,6 +101,17 @@ Item* LocalPlayer::getInvItem(int index) return mInventory->getItem(index); } +void +LocalPlayer::moveInvItem(Item *item, int newIndex) +{ + // special case, the old and new cannot copy over each other. + if (item->getInvIndex() == newIndex) + return; + + Net::GameServer::Player::moveItem( + item->getInvIndex(), newIndex, item->getQuantity()); +} + void LocalPlayer::equipItem(Item *item) { Net::GameServer::Player::equip(item->getInvIndex()); @@ -114,15 +125,9 @@ void LocalPlayer::unequipItem(int slot) mEquipment->setEquipment(slot, 0); } -void LocalPlayer::useItem(Item *item) +void LocalPlayer::useItem(int slot) { - // XXX Convert for new server - /* - MessageOut outMsg(CMSG_PLAYER_INVENTORY_USE); - outMsg.writeShort(item->getInvIndex()); - outMsg.writeLong(item->getId()); - // Note: id is dest of item, usually player_node->account_ID ?? - */ + Net::GameServer::Player::useItem(slot); } void LocalPlayer::dropItem(Item *item, int quantity) @@ -130,6 +135,17 @@ void LocalPlayer::dropItem(Item *item, int quantity) Net::GameServer::Player::drop(item->getInvIndex(), quantity); } +void LocalPlayer::splitItem(Item *item, int quantity) +{ + int newIndex = mInventory->getFreeSlot(); + if (newIndex > Inventory::NO_SLOT_INDEX) + { + Net::GameServer::Player::moveItem( + item->getInvIndex(), newIndex, quantity); + } + +} + void LocalPlayer::pickUp(FloorItem *item) { int dx = item->getX() - mX / 32; diff --git a/src/localplayer.h b/src/localplayer.h index edadd7a0..d2aef38b 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -137,6 +137,11 @@ class LocalPlayer : public Player Item* getInvItem(int index); /** + * Move the Inventory item from the old slot to the new slot. + */ + void moveInvItem(Item *item, int newIndex); + + /** * Equips an item. */ void equipItem(Item *item); @@ -146,8 +151,12 @@ class LocalPlayer : public Player */ void unequipItem(int slot); - void useItem(Item *item); + void useItem(int slot); + void dropItem(Item *item, int quantity); + + void splitItem(Item *item, int quantity); + void pickUp(FloorItem *item); /** diff --git a/src/net/gameserver/player.cpp b/src/net/gameserver/player.cpp index e93fff46..29d711d7 100644 --- a/src/net/gameserver/player.cpp +++ b/src/net/gameserver/player.cpp @@ -52,6 +52,15 @@ void Net::GameServer::Player::pickUp(int x, int y) Net::GameServer::connection->send(msg); } +void Net::GameServer::Player::moveItem(int oldSlot, int newSlot, int amount) +{ + MessageOut msg(PGMSG_MOVE_ITEM); + msg.writeByte(oldSlot); + msg.writeByte(newSlot); + msg.writeByte(amount); + Net::GameServer::connection->send(msg); +} + void Net::GameServer::Player::drop(int slot, int amount) { MessageOut msg(PGMSG_DROP); @@ -74,6 +83,13 @@ void Net::GameServer::Player::unequip(int slot) Net::GameServer::connection->send(msg); } +void Net::GameServer::Player::useItem(int slot) +{ + MessageOut msg(PGMSG_USE_ITEM); + msg.writeByte(slot); + Net::GameServer::connection->send(msg); +} + void Net::GameServer::Player::attack(int direction) { MessageOut msg(PGMSG_ATTACK); @@ -123,4 +139,3 @@ void Net::GameServer::Player::tradeItem(int slot, int amount) msg.writeByte(amount); Net::GameServer::connection->send(msg); } - diff --git a/src/net/gameserver/player.h b/src/net/gameserver/player.h index 4c27d4d0..d21e656e 100644 --- a/src/net/gameserver/player.h +++ b/src/net/gameserver/player.h @@ -37,9 +37,11 @@ namespace Net void say(const std::string &text); void walk(int x, int y); void pickUp(int x, int y); + void moveItem(int oldSlot, int newSlot, int amount); void drop(int slot, int amount); void equip(int slot); void unequip(int slot); + void useItem(int slot); void attack(int direction); void changeAction(Being::Action action); void talkToNPC(int id, bool restart); diff --git a/src/net/protocol.h b/src/net/protocol.h index 6bbe7919..1dde518e 100644 --- a/src/net/protocol.h +++ b/src/net/protocol.h @@ -79,6 +79,7 @@ enum { PGMSG_DROP = 0x0111, // B slot, B amount PGMSG_EQUIP = 0x0112, // B slot PGMSG_UNEQUIP = 0x0113, // B slot + PGMSG_MOVE_ITEM = 0x0114, // B slot1, B slot2, B amount GPMSG_INVENTORY = 0x0120, // { B slot, W item id [, B amount] }* GPMSG_INVENTORY_FULL = 0x0121, // { B slot, W item id [, B amount] }* GPMSG_PLAYER_ATTRIBUTE_UPDATE = 0x0130, // { W attribute, W value }* @@ -116,7 +117,7 @@ enum { GPMSG_TRADE_ACCEPT = 0x02C7, // - PGMSG_TRADE_ADD_ITEM = 0x02C8, // B slot, B amount GPMSG_TRADE_ADD_ITEM = 0x02C9, // W item id, B amount - PGMSG_USE_ITEM = 0x0300, // L item id + PGMSG_USE_ITEM = 0x0300, // B slot GPMSG_USE_RESPONSE = 0x0301, // B error GPMSG_BEINGS_DAMAGE = 0x0310, // { W being id, W amount }* |