diff --git a/src/widgets/CreatureView/creatureequipview.cpp b/src/widgets/CreatureView/creatureequipview.cpp index f232e5a..50f5c05 100644 --- a/src/widgets/CreatureView/creatureequipview.cpp +++ b/src/widgets/CreatureView/creatureequipview.cpp @@ -1,8 +1,11 @@ #include "creatureequipview.h" #include "ui_creatureequipview.h" +#include "creatureinventoryview.h" + #include "nw/objects/Creature.hpp" #include "nw/objects/Item.hpp" +#include "nw/profiles/nwn1/functions.hpp" #include #include @@ -74,74 +77,137 @@ CreatureEquipView::~CreatureEquipView() delete ui; } -void CreatureEquipView::setCreature(nw::Creature* creature) +void CreatureEquipView::connectSlots(CreatureInventoryView* inventory) +{ +#define CONNECT_SLOTS(name) \ + connect(ui->name, &InventorySlot::addItemToInventory, inventory, &CreatureInventoryView::addItemToInventory); \ + connect(ui->name, &InventorySlot::removeItemFromInventory, inventory, &CreatureInventoryView::removeItemFromInventory); \ + connect(ui->name, &InventorySlot::equipItem, this, &CreatureEquipView::equipItem); \ + connect(ui->name, &InventorySlot::unequipItem, this, &CreatureEquipView::unequipItem) + + CONNECT_SLOTS(slotRight); + CONNECT_SLOTS(slotArmor); + CONNECT_SLOTS(slotArrow); + CONNECT_SLOTS(slotSling); + CONNECT_SLOTS(slotBolt); + CONNECT_SLOTS(slotLeft); + CONNECT_SLOTS(slotBelt); + CONNECT_SLOTS(slotHelmet); + CONNECT_SLOTS(slotGloves); + CONNECT_SLOTS(slotRing1); + CONNECT_SLOTS(slotRing2); + CONNECT_SLOTS(slotAmulet); + CONNECT_SLOTS(slotCloak); + CONNECT_SLOTS(slotBoots); + CONNECT_SLOTS(slotCreature1); + CONNECT_SLOTS(slotCreature2); + CONNECT_SLOTS(slotCreature3); + +#undef CONNECT_SLOTS +} + +void CreatureEquipView::updateEquips() { + if (!creature_) { return; } for (size_t i = 0; i < 18; ++i) { nw::EquipIndex idx = static_cast(i); - - if (creature->equipment.equips[i].is()) { - auto it = creature->equipment.equips[i].as(); + if (creature_->equipment.equips[i].is()) { + auto it = creature_->equipment.equips[i].as(); switch (idx) { default: break; - case nw::EquipIndex::head: + ui->slotHelmet->setCreature(creature_); ui->slotHelmet->setItem(it); break; case nw::EquipIndex::chest: + ui->slotArmor->setCreature(creature_); ui->slotArmor->setItem(it); break; case nw::EquipIndex::boots: + ui->slotBoots->setCreature(creature_); ui->slotBoots->setItem(it); break; case nw::EquipIndex::arms: + ui->slotGloves->setCreature(creature_); ui->slotGloves->setItem(it); break; case nw::EquipIndex::righthand: + ui->slotRight->setCreature(creature_); ui->slotRight->setItem(it); break; case nw::EquipIndex::lefthand: + ui->slotLeft->setCreature(creature_); ui->slotLeft->setItem(it); break; case nw::EquipIndex::cloak: + ui->slotCloak->setCreature(creature_); ui->slotCloak->setItem(it); break; case nw::EquipIndex::leftring: + ui->slotRing2->setCreature(creature_); ui->slotRing2->setItem(it); break; case nw::EquipIndex::rightring: + ui->slotRing1->setCreature(creature_); ui->slotRing1->setItem(it); break; case nw::EquipIndex::neck: + ui->slotAmulet->setCreature(creature_); ui->slotAmulet->setItem(it); break; case nw::EquipIndex::belt: + ui->slotBolt->setCreature(creature_); ui->slotBolt->setItem(it); break; case nw::EquipIndex::arrows: + ui->slotArrow->setCreature(creature_); ui->slotArrow->setItem(it); break; case nw::EquipIndex::bullets: + ui->slotSling->setCreature(creature_); ui->slotSling->setItem(it); break; case nw::EquipIndex::bolts: + ui->slotBolt->setCreature(creature_); ui->slotBolt->setItem(it); break; case nw::EquipIndex::creature_left: + ui->slotCreature1->setCreature(creature_); ui->slotCreature1->setItem(it); break; case nw::EquipIndex::creature_right: + ui->slotCreature2->setCreature(creature_); ui->slotCreature2->setItem(it); break; case nw::EquipIndex::creature_bite: + ui->slotCreature3->setCreature(creature_); ui->slotCreature3->setItem(it); break; case nw::EquipIndex::creature_skin: + ui->slotCreatureSkin->setCreature(creature_); ui->slotCreatureSkin->setItem(it); break; } } } +} +void CreatureEquipView::setCreature(nw::Creature* creature) +{ creature_ = creature; + updateEquips(); +} + +void CreatureEquipView::equipItem(nw::Item* item, nw::EquipSlot slot) +{ + nwn1::equip_item(creature_, item, nw::equip_slot_to_index(slot)); + updateEquips(); +} + +void CreatureEquipView::unequipItem(nw::Item* item, nw::EquipSlot slot) +{ + Q_UNUSED(item); + nwn1::unequip_item(creature_, nw::equip_slot_to_index(slot)); + updateEquips(); } diff --git a/src/widgets/CreatureView/creatureequipview.h b/src/widgets/CreatureView/creatureequipview.h index cd62511..0bcaab9 100644 --- a/src/widgets/CreatureView/creatureequipview.h +++ b/src/widgets/CreatureView/creatureequipview.h @@ -5,8 +5,12 @@ namespace nw { struct Creature; +enum struct EquipSlot; +struct Item; } +class CreatureInventoryView; + namespace Ui { class CreatureEquipView; } @@ -18,7 +22,13 @@ class CreatureEquipView : public QWidget { explicit CreatureEquipView(QWidget* parent = nullptr); ~CreatureEquipView(); + void connectSlots(CreatureInventoryView* inventory); void setCreature(nw::Creature* creature); + void updateEquips(); + +public slots: + void equipItem(nw::Item* item, nw::EquipSlot slot); + void unequipItem(nw::Item* item, nw::EquipSlot slot); private: Ui::CreatureEquipView* ui; diff --git a/src/widgets/CreatureView/creatureinventorypanel.cpp b/src/widgets/CreatureView/creatureinventorypanel.cpp index 8adf711..3bb83a7 100644 --- a/src/widgets/CreatureView/creatureinventorypanel.cpp +++ b/src/widgets/CreatureView/creatureinventorypanel.cpp @@ -6,6 +6,8 @@ CreatureInventoryPanel::CreatureInventoryPanel(QWidget* parent) , ui(new Ui::CreatureInventoryPanel) { ui->setupUi(this); + ui->equips->connectSlots(ui->inventory); + ui->inventory->connectSlots(ui->equips); } CreatureInventoryPanel::~CreatureInventoryPanel() diff --git a/src/widgets/CreatureView/creatureinventorypanel.h b/src/widgets/CreatureView/creatureinventorypanel.h index 06e90f5..609338f 100644 --- a/src/widgets/CreatureView/creatureinventorypanel.h +++ b/src/widgets/CreatureView/creatureinventorypanel.h @@ -5,6 +5,8 @@ namespace nw { struct Creature; +enum struct EquipSlot; +struct Item; } namespace Ui { diff --git a/src/widgets/CreatureView/creatureinventoryview.cpp b/src/widgets/CreatureView/creatureinventoryview.cpp index d2d703b..a026d3c 100644 --- a/src/widgets/CreatureView/creatureinventoryview.cpp +++ b/src/widgets/CreatureView/creatureinventoryview.cpp @@ -5,16 +5,104 @@ #include "ZFontIcon/ZFontIcon.h" #include "ZFontIcon/ZFont_fa6.h" +#include "nw/kernel/Objects.hpp" +#include "nw/objects/Creature.hpp" +#include "nw/objects/Item.hpp" -#include -#include +#include +#include +#include +InventoryTable::InventoryTable(QWidget* parent) + : QTableView(parent) +{ + setDragEnabled(true); + setAcceptDrops(true); + setDropIndicatorShown(true); + setSelectionBehavior(QAbstractItemView::SelectRows); +} + +void InventoryTable::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) { + QModelIndex index = indexAt(event->pos()); + auto idx = model()->index(index.row(), 0, index.parent()); + if (index.isValid()) { + QMimeData* mimeData = new QMimeData(); + nw::Item* item = reinterpret_cast(idx.internalPointer()); + LOG_F(INFO, "item id: {}", uint32_t(item->handle().id)); + mimeData->setData("application/x-inventory-item", serialize_obj_handle(item->handle())); + + QPixmap img = model()->data(idx, Qt::DecorationRole).value(); + QPoint hotspot = QPoint(img.width() / 2, img.height() / 2); + + QDrag* drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setPixmap(img); + drag->setHotSpot(hotspot); + drag->exec(Qt::MoveAction); + } + } + QTableView::mousePressEvent(event); +} + +void InventoryTable::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->source() == this) { + event->ignore(); + } else if (event->mimeData()->hasFormat("application/x-equip-item")) { + event->acceptProposedAction(); + } +} + +void InventoryTable::dragMoveEvent(QDragMoveEvent* event) +{ + if (event->source() == this) { + event->ignore(); + } else if (event->mimeData()->hasFormat("application/x-equip-item")) { + event->acceptProposedAction(); + } +} + +void InventoryTable::dropEvent(QDropEvent* event) +{ + if (event->mimeData()->hasFormat("application/x-equip-item")) { + QByteArray itemData = event->mimeData()->data("application/x-equip-item"); + auto item_handle = deserialize_obj_handle(itemData); + auto item = nw::kernel::objects().get(item_handle); + if (!item) { return; } + + auto m = static_cast(model()); + int i = 0; + for (const auto& it : m->creature()->equipment.equips) { + if (it.is() && it.as() == item) { break; } + ++i; + } + + auto slot = static_cast(1 << i); + emit unequipItem(item, slot); + + m->addItem(item); + resizeRowsToContents(); + event->acceptProposedAction(); + } +} + +QModelIndex InventoryModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + nw::Item* item = creature_->inventory.items[row].item.as(); + return createIndex(row, column, item); +} // == InventoryItemDelegate =================================================== // ============================================================================ InventoryItemDelegate::InventoryItemDelegate(QObject* parent) : QStyledItemDelegate(parent) { + // Create a QModelIndex with the pointer as the internal data } void InventoryItemDelegate::initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const @@ -131,9 +219,29 @@ Qt::ItemFlags InventoryModel::flags(const QModelIndex& index) const void InventoryModel::addItem(nw::Item* item) { - // beginInsertRows(QModelIndex(), items.size(), items.size()); - // items.append({image, name, stackSize, stackable}); - // endInsertRows(); + beginInsertRows(QModelIndex(), int(creature_->inventory.items.size()), int(creature_->inventory.items.size())); + nw::InventoryItem ii; + ii.item = item; + creature_->inventory.items.push_back(ii); + endInsertRows(); +} + +nw::Creature* InventoryModel::creature() const noexcept +{ + return creature_; +} + +void InventoryModel::removeItem(nw::Item* item) +{ + auto it = std::find_if(creature_->inventory.items.begin(), creature_->inventory.items.end(), + [item](const nw::InventoryItem& ii) { return ii.item == item; }); + + if (it != std::end(creature_->inventory.items)) { + int index = int(std::distance(creature_->inventory.items.begin(), it)); + beginRemoveRows(QModelIndex(), index, index); + creature_->inventory.items.erase(it); + endRemoveRows(); + } } // == CreatureInventoryView =================================================== @@ -146,6 +254,7 @@ CreatureInventoryView::CreatureInventoryView(QWidget* parent) ui->setupUi(this); ui->add->setIcon(ZFontIcon::icon(Fa6::FAMILY, Fa6::fa_plus, Qt::green)); ui->remove->setIcon(ZFontIcon::icon(Fa6::FAMILY, Fa6::fa_minus, Qt::red)); + connect(ui->remove, &QPushButton::clicked, this, &CreatureInventoryView::onRemove); } CreatureInventoryView::~CreatureInventoryView() @@ -153,6 +262,11 @@ CreatureInventoryView::~CreatureInventoryView() delete ui; } +void CreatureInventoryView::connectSlots(CreatureEquipView* equips) +{ + connect(ui->inventoryView, &InventoryTable::unequipItem, equips, &CreatureEquipView::unequipItem); +} + void CreatureInventoryView::setCreature(nw::Creature* creature) { creature_ = creature; @@ -168,3 +282,33 @@ void CreatureInventoryView::setCreature(nw::Creature* creature) ui->inventoryView->resizeRowsToContents(); ui->inventoryView->setSelectionBehavior(QAbstractItemView::SelectRows); } + +void CreatureInventoryView::removeItemFromInventory(nw::Item* item) +{ + model_->removeItem(item); +} + +void CreatureInventoryView::addItemToInventory(nw::Item* item) +{ + model_->addItem(item); +} + +void CreatureInventoryView::onRemove(bool checked) +{ + Q_UNUSED(checked); + if (!model_) { return; } + + auto selmodel = ui->inventoryView->selectionModel(); + if (!selmodel) { return; } + + auto indices = selmodel->selectedRows(); + std::sort(indices.rbegin(), indices.rend()); + + for (const auto& index : indices) { + if (index.isValid()) { + auto item = reinterpret_cast(index.internalPointer()); + model_->removeItem(item); + nw::kernel::objects().destroy(item->handle()); + } + } +} diff --git a/src/widgets/CreatureView/creatureinventoryview.h b/src/widgets/CreatureView/creatureinventoryview.h index 476757e..d776930 100644 --- a/src/widgets/CreatureView/creatureinventoryview.h +++ b/src/widgets/CreatureView/creatureinventoryview.h @@ -6,13 +6,36 @@ #include #include #include +#include #include namespace nw { struct Creature; +enum struct EquipSlot; struct Item; } +class CreatureEquipView; + +// == InventoryTable ========================================================== +// ============================================================================ + +class InventoryTable : public QTableView { + Q_OBJECT + +public: + explicit InventoryTable(QWidget* parent = nullptr); + +signals: + void unequipItem(nw::Item* item, nw::EquipSlot slot); + +protected: + void mousePressEvent(QMouseEvent* event) override; + void dragEnterEvent(QDragEnterEvent* event) override; + void dragMoveEvent(QDragMoveEvent* event) override; + void dropEvent(QDropEvent* event) override; +}; + // == InventoryItemDelegate =================================================== // ============================================================================ @@ -36,13 +59,16 @@ class InventoryModel : public QAbstractTableModel { public: InventoryModel(nw::Creature* creature, QObject* parent = nullptr); - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + QModelIndex index(int row, int column, const QModelIndex& parent) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; void addItem(nw::Item* item); + nw::Creature* creature() const noexcept; + void removeItem(nw::Item* item); private: nw::Creature* creature_; @@ -59,8 +85,15 @@ class CreatureInventoryView : public QWidget { explicit CreatureInventoryView(QWidget* parent = nullptr); ~CreatureInventoryView(); + void connectSlots(CreatureEquipView* equips); + InventoryModel* model() const noexcept { return model_; } void setCreature(nw::Creature* creature); +public slots: + void removeItemFromInventory(nw::Item* item); + void addItemToInventory(nw::Item* item); + void onRemove(bool checked = false); + private: Ui::CreatureInventoryView* ui; nw::Creature* creature_ = nullptr; diff --git a/src/widgets/CreatureView/creatureinventoryview.ui b/src/widgets/CreatureView/creatureinventoryview.ui index 7920859..864c7f9 100644 --- a/src/widgets/CreatureView/creatureinventoryview.ui +++ b/src/widgets/CreatureView/creatureinventoryview.ui @@ -21,7 +21,7 @@ - + @@ -80,6 +80,13 @@ + + + InventoryTable + QTableView +
creatureinventoryview.h
+
+
diff --git a/src/widgets/CreatureView/inventoryslot.cpp b/src/widgets/CreatureView/inventoryslot.cpp index a5f53c0..044a9a9 100644 --- a/src/widgets/CreatureView/inventoryslot.cpp +++ b/src/widgets/CreatureView/inventoryslot.cpp @@ -2,14 +2,82 @@ #include "../util/objects.h" +#include "nw/kernel/Objects.hpp" +#include "nw/kernel/Rules.hpp" #include "nw/kernel/Strings.hpp" #include "nw/objects/Item.hpp" +#include "nw/profiles/nwn1/functions.hpp" +#include +#include +#include #include InventorySlot::InventorySlot(QWidget* parent) : QLabel(parent) { + this->setAcceptDrops(true); +} + +void InventorySlot::dragEnterEvent(QDragEnterEvent* event) +{ + QByteArray ba; + + if (event->mimeData()->hasFormat("application/x-inventory-item")) { + ba = event->mimeData()->data("application/x-inventory-item"); + } else if (event->mimeData()->hasFormat("application/x-equip-item")) { + ba = event->mimeData()->data("application/x-equip-item"); + } + if (ba.size() == 0) { return; } + + auto item_handle = deserialize_obj_handle(ba); + nw::Item* item = nw::kernel::objects().get(item_handle); + if (!item || item == item_) { return; } + if (nwn1::can_equip_item(creature_, item, nw::equip_slot_to_index(slot_))) { + event->acceptProposedAction(); + } +} + +void InventorySlot::dropEvent(QDropEvent* event) +{ + QByteArray ba; + bool inv = false; + if (event->mimeData()->hasFormat("application/x-inventory-item")) { + ba = event->mimeData()->data("application/x-inventory-item"); + inv = true; + } else if (event->mimeData()->hasFormat("application/x-equip-item")) { + ba = event->mimeData()->data("application/x-equip-item"); + } + if (ba.size() == 0) { return; } + + auto item_handle = deserialize_obj_handle(ba); + auto item = nw::kernel::objects().get(item_handle); + if (!item || item == item_) { return; } + + if (item_) { + emit unequipItem(item_, slot_); + if (inv) { emit addItemToInventory(item_); } + } + + emit equipItem(item, slot_); + if (inv) { emit removeItemFromInventory(item); } + event->acceptProposedAction(); + setItem(item); +} + +void InventorySlot::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton && item_) { + QMimeData* mimeData = new QMimeData(); + // Optionally, include item data such as item ID or index + mimeData->setData("application/x-equip-item", serialize_obj_handle(item_->handle())); + + QDrag* drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setHotSpot(event->pos()); + drag->setPixmap(pixmap()); + drag->exec(Qt::MoveAction); + } } void InventorySlot::setDefaults(QPixmap image, QString tip) @@ -27,11 +95,16 @@ nw::Item* InventorySlot::item() const noexcept return item_; } -void InventorySlot::setItem(nw::Item* item, bool female) +void InventorySlot::setCreature(nw::Creature* creature) +{ + creature_ = creature; +} + +void InventorySlot::setItem(nw::Item* item) { item_ = nullptr; if (item) { - auto img = item_to_image(item, female); + auto img = item_to_image(item, creature_->gender == 1); if (!img.isNull()) { item_ = item; setPixmap(prepareImage(QPixmap::fromImage(img))); diff --git a/src/widgets/CreatureView/inventoryslot.h b/src/widgets/CreatureView/inventoryslot.h index 981226f..af579ea 100644 --- a/src/widgets/CreatureView/inventoryslot.h +++ b/src/widgets/CreatureView/inventoryslot.h @@ -13,11 +13,24 @@ class InventorySlot : public QLabel { void setDefaults(QPixmap image, QString tip); nw::Item* item() const noexcept; - void setItem(nw::Item* item, bool female = false); + void setCreature(nw::Creature* creature); + void setItem(nw::Item* item); nw::EquipSlot slot() const noexcept; void setSlot(nw::EquipSlot slot); +signals: + void equipItem(nw::Item* item, nw::EquipSlot slot); + void unequipItem(nw::Item* item, nw::EquipSlot slot); + void addItemToInventory(nw::Item* item); + void removeItemFromInventory(nw::Item* item); + +protected: + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + private: + nw::Creature* creature_ = nullptr; nw::Item* item_ = nullptr; nw::EquipSlot slot_; QPixmap default_image_;