Skip to content

Commit

Permalink
[creatureview] add: Drag-Drop functionality to Equips/Inventory panel
Browse files Browse the repository at this point in the history
  • Loading branch information
jd28 committed Nov 5, 2024
1 parent 226aa3d commit 20061f6
Show file tree
Hide file tree
Showing 9 changed files with 366 additions and 16 deletions.
76 changes: 71 additions & 5 deletions src/widgets/CreatureView/creatureequipview.cpp
Original file line number Diff line number Diff line change
@@ -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 <QByteArray>
#include <QImage>
Expand Down Expand Up @@ -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<nw::EquipIndex>(i);

if (creature->equipment.equips[i].is<nw::Item*>()) {
auto it = creature->equipment.equips[i].as<nw::Item*>();
if (creature_->equipment.equips[i].is<nw::Item*>()) {
auto it = creature_->equipment.equips[i].as<nw::Item*>();
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();
}
10 changes: 10 additions & 0 deletions src/widgets/CreatureView/creatureequipview.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@

namespace nw {
struct Creature;
enum struct EquipSlot;
struct Item;
}

class CreatureInventoryView;

namespace Ui {
class CreatureEquipView;
}
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/widgets/CreatureView/creatureinventorypanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 2 additions & 0 deletions src/widgets/CreatureView/creatureinventorypanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

namespace nw {
struct Creature;
enum struct EquipSlot;
struct Item;
}

namespace Ui {
Expand Down
154 changes: 149 additions & 5 deletions src/widgets/CreatureView/creatureinventoryview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <nw/objects/Creature.hpp>
#include <nw/objects/Item.hpp>
#include <QDrag>
#include <QMimeData>
#include <QMouseEvent>

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<nw::Item*>(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<QPixmap>();
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<nw::Item>(item_handle);
if (!item) { return; }

auto m = static_cast<InventoryModel*>(model());
int i = 0;
for (const auto& it : m->creature()->equipment.equips) {
if (it.is<nw::Item*>() && it.as<nw::Item*>() == item) { break; }
++i;
}

auto slot = static_cast<nw::EquipSlot>(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<nw::Item*>();
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
Expand Down Expand Up @@ -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 ===================================================
Expand All @@ -146,13 +254,19 @@ 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()
{
delete ui;
}

void CreatureInventoryView::connectSlots(CreatureEquipView* equips)
{
connect(ui->inventoryView, &InventoryTable::unequipItem, equips, &CreatureEquipView::unequipItem);
}

void CreatureInventoryView::setCreature(nw::Creature* creature)
{
creature_ = creature;
Expand All @@ -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<nw::Item*>(index.internalPointer());
model_->removeItem(item);
nw::kernel::objects().destroy(item->handle());
}
}
}
Loading

0 comments on commit 20061f6

Please sign in to comment.