From acceca0139b8695668e65030e21bca1866112bb4 Mon Sep 17 00:00:00 2001 From: godric3 Date: Sun, 24 Mar 2024 10:01:20 +0100 Subject: [PATCH] map editor: Allow to customize hero spells --- lib/constants/EntityIdentifiers.cpp | 8 + lib/modding/IdentifierStorage.cpp | 2 + mapeditor/CMakeLists.txt | 3 + mapeditor/inspector/herospellwidget.cpp | 133 ++++++++++++ mapeditor/inspector/herospellwidget.h | 56 +++++ mapeditor/inspector/herospellwidget.ui | 264 ++++++++++++++++++++++++ mapeditor/inspector/inspector.cpp | 2 + mapeditor/mapcontroller.cpp | 8 +- 8 files changed, 469 insertions(+), 7 deletions(-) create mode 100644 mapeditor/inspector/herospellwidget.cpp create mode 100644 mapeditor/inspector/herospellwidget.h create mode 100644 mapeditor/inspector/herospellwidget.ui diff --git a/lib/constants/EntityIdentifiers.cpp b/lib/constants/EntityIdentifiers.cpp index d1d2b66c6..44ae4d0e3 100644 --- a/lib/constants/EntityIdentifiers.cpp +++ b/lib/constants/EntityIdentifiers.cpp @@ -362,6 +362,10 @@ const HeroType * HeroTypeID::toEntity(const Services * services) const si32 SpellID::decode(const std::string & identifier) { + if (identifier == "preset") + return SpellID::PRESET; + if (identifier == "spellbook_preset") + return SpellID::SPELLBOOK_PRESET; return resolveIdentifier("spell", identifier); } @@ -369,6 +373,10 @@ std::string SpellID::encode(const si32 index) { if (index == -1) return ""; + if (index == SpellID::PRESET) + return "preset"; + if (index == SpellID::SPELLBOOK_PRESET) + return "spellbook_preset"; return VLC->spells()->getByIndex(index)->getJsonKey(); } diff --git a/lib/modding/IdentifierStorage.cpp b/lib/modding/IdentifierStorage.cpp index 589a79f44..4220687fa 100644 --- a/lib/modding/IdentifierStorage.cpp +++ b/lib/modding/IdentifierStorage.cpp @@ -83,6 +83,8 @@ CIdentifierStorage::CIdentifierStorage() registerObject(ModScope::scopeBuiltin(), "bonusSubtype", "creatureLevel5", 5); registerObject(ModScope::scopeBuiltin(), "bonusSubtype", "creatureLevel6", 6); registerObject(ModScope::scopeBuiltin(), "bonusSubtype", "creatureLevel7", 7); + registerObject(ModScope::scopeBuiltin(), "spell", "preset", SpellID::PRESET); + registerObject(ModScope::scopeBuiltin(), "spell", "spellbook_preset", SpellID::SPELLBOOK_PRESET); } void CIdentifierStorage::checkIdentifier(std::string & ID) diff --git a/mapeditor/CMakeLists.txt b/mapeditor/CMakeLists.txt index 629534604..c93b1ff53 100644 --- a/mapeditor/CMakeLists.txt +++ b/mapeditor/CMakeLists.txt @@ -34,6 +34,7 @@ set(editor_SRCS inspector/rewardswidget.cpp inspector/questwidget.cpp inspector/heroskillswidget.cpp + inspector/herospellwidget.cpp inspector/PickObjectDelegate.cpp inspector/portraitwidget.cpp resourceExtractor/ResourceConverter.cpp @@ -74,6 +75,7 @@ set(editor_HEADERS inspector/rewardswidget.h inspector/questwidget.h inspector/heroskillswidget.h + inspector/herospellwidget.h inspector/PickObjectDelegate.h inspector/portraitwidget.h resourceExtractor/ResourceConverter.h @@ -101,6 +103,7 @@ set(editor_FORMS inspector/rewardswidget.ui inspector/questwidget.ui inspector/heroskillswidget.ui + inspector/herospellwidget.ui inspector/portraitwidget.ui ) diff --git a/mapeditor/inspector/herospellwidget.cpp b/mapeditor/inspector/herospellwidget.cpp new file mode 100644 index 000000000..296968b59 --- /dev/null +++ b/mapeditor/inspector/herospellwidget.cpp @@ -0,0 +1,133 @@ +/* + * herosspellwidget.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "herospellwidget.h" +#include "ui_herospellwidget.h" +#include "inspector.h" +#include "../../lib/constants/StringConstants.h" +#include "../../lib/spells/CSpellHandler.h" + +HeroSpellWidget::HeroSpellWidget(CGHeroInstance & h, QWidget * parent) : + QDialog(parent), + ui(new Ui::HeroSpellWidget), + hero(h) +{ + ui->setupUi(this); +} + +HeroSpellWidget::~HeroSpellWidget() +{ + delete ui; +} + + +void HeroSpellWidget::obtainData() +{ + initSpellLists(); + if (hero.spellbookContainsSpell(SpellID::PRESET)) { + ui->customizeSpells->setChecked(true); + } + else + { + ui->customizeSpells->setChecked(false); + ui->tabWidget->setEnabled(false); + } +} + +void HeroSpellWidget::initSpellLists() +{ + QListWidget * spellLists[] = { ui->spellList1, ui->spellList2, ui->spellList3, ui->spellList4, ui->spellList5 }; + auto spells = VLC->spellh->objects; + for (int i = 0; i < 5; i++) + { + std::vector> spellsByLevel; + auto getSpellsByLevel = [i](auto spell) { + return spell->getLevel() == i + 1 && !spell->isSpecial() && !spell->isCreatureAbility(); + }; + vstd::copy_if(spells, std::back_inserter(spellsByLevel), getSpellsByLevel); + spellLists[i]->clear(); + for (auto spell : spellsByLevel) + { + auto* item = new QListWidgetItem(QString::fromStdString(spell->getNameTranslated())); + item->setData(Qt::UserRole, QVariant::fromValue(spell->getIndex())); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(hero.spellbookContainsSpell(spell->getId()) ? Qt::Checked : Qt::Unchecked); + spellLists[i]->addItem(item); + } + } +} + +void HeroSpellWidget::commitChanges() +{ + QListWidget * spellLists[] = { ui->spellList1, ui->spellList2, ui->spellList3, ui->spellList4, ui->spellList5 }; + for (auto spellList : spellLists) + { + for (int i = 0; i < spellList->count(); i++) + { + auto * item = spellList->item(i); + if (item->checkState() == Qt::Checked) + { + hero.addSpellToSpellbook(item->data(Qt::UserRole).toInt()); + } + else + { + hero.removeSpellFromSpellbook(item->data(Qt::UserRole).toInt()); + } + } + } +} + +void HeroSpellWidget::on_customizeSpells_toggled(bool checked) +{ + if (checked) + { + hero.addSpellToSpellbook(SpellID::PRESET); + } + else + { + hero.removeSpellFromSpellbook(SpellID::PRESET); + hero.removeSpellbook(); + } + ui->tabWidget->setEnabled(checked); + initSpellLists(); +} + +HeroSpellDelegate::HeroSpellDelegate(CGHeroInstance & h) : hero(h), QStyledItemDelegate() +{ +} + +QWidget * HeroSpellDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const +{ + return new HeroSpellWidget(hero, parent); +} + +void HeroSpellDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const +{ + if (auto * ed = qobject_cast(editor)) + { + ed->obtainData(); + } + else + { + QStyledItemDelegate::setEditorData(editor, index); + } +} + +void HeroSpellDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const +{ + if (auto * ed = qobject_cast(editor)) + { + ed->commitChanges(); + } + else + { + QStyledItemDelegate::setModelData(editor, model, index); + } +} \ No newline at end of file diff --git a/mapeditor/inspector/herospellwidget.h b/mapeditor/inspector/herospellwidget.h new file mode 100644 index 000000000..83864eb12 --- /dev/null +++ b/mapeditor/inspector/herospellwidget.h @@ -0,0 +1,56 @@ +/* + * herospellwidget.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include +#include "../../lib/mapObjects/CGHeroInstance.h" + +namespace Ui { + class HeroSpellWidget; +} + + +class HeroSpellWidget : public QDialog +{ + Q_OBJECT + +public: + explicit HeroSpellWidget(CGHeroInstance &, QWidget * parent = nullptr); + ~HeroSpellWidget(); + + void obtainData(); + void commitChanges(); + +private slots: + void on_customizeSpells_toggled(bool checked); + +private: + Ui::HeroSpellWidget * ui; + + CGHeroInstance & hero; + + void initSpellLists(); +}; + +class HeroSpellDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + using QStyledItemDelegate::QStyledItemDelegate; + + HeroSpellDelegate(CGHeroInstance &); + + QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex& index) const override; + void setEditorData(QWidget * editor, const QModelIndex & index) const override; + void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override; + +private: + CGHeroInstance & hero; +}; diff --git a/mapeditor/inspector/herospellwidget.ui b/mapeditor/inspector/herospellwidget.ui new file mode 100644 index 000000000..8eb5cdb87 --- /dev/null +++ b/mapeditor/inspector/herospellwidget.ui @@ -0,0 +1,264 @@ + + + HeroSpellWidget + + + Qt::NonModal + + + + 0 + 0 + 480 + 635 + + + + + 0 + 0 + + + + + 480 + 480 + + + + Spells + + + Qt::LeftToRight + + + true + + + + 10 + + + 5 + + + + + Customize spells + + + false + + + + + + + + 0 + 0 + + + + 0 + + + true + + + + + 0 + 0 + + + + Level 1 + + + + 12 + + + 12 + + + 12 + + + + + + 0 + 0 + + + + QAbstractItemView::NoEditTriggers + + + true + + + + + + + + + 0 + 0 + + + + Level 2 + + + + 12 + + + 12 + + + 12 + + + + + + 0 + 0 + + + + QAbstractItemView::NoEditTriggers + + + true + + + + + + + + + 0 + 0 + + + + Level 3 + + + + 12 + + + 12 + + + 12 + + + + + + 0 + 0 + + + + QAbstractItemView::NoEditTriggers + + + true + + + + + + + + + 0 + 0 + + + + Level 4 + + + + 12 + + + 12 + + + 12 + + + + + + 0 + 0 + + + + QAbstractItemView::NoEditTriggers + + + true + + + + + + + + + 0 + 0 + + + + Level 5 + + + + 12 + + + 12 + + + 12 + + + + + + 0 + 0 + + + + QAbstractItemView::NoEditTriggers + + + true + + + + + + + + + + + + diff --git a/mapeditor/inspector/inspector.cpp b/mapeditor/inspector/inspector.cpp index 5ae80af4d..42dc8c2d6 100644 --- a/mapeditor/inspector/inspector.cpp +++ b/mapeditor/inspector/inspector.cpp @@ -26,6 +26,7 @@ #include "rewardswidget.h" #include "questwidget.h" #include "heroskillswidget.h" +#include "herospellwidget.h" #include "portraitwidget.h" #include "PickObjectDelegate.h" #include "../mapcontroller.h" @@ -314,6 +315,7 @@ void Inspector::updateProperties(CGHeroInstance * o) auto * delegate = new HeroSkillsDelegate(*o); addProperty("Skills", PropertyEditorPlaceholder(), delegate, false); + addProperty("Spells", PropertyEditorPlaceholder(), new HeroSpellDelegate(*o), false); if(o->type) { //Hero type diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index e768b3130..1e6da2589 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -157,13 +157,7 @@ void MapController::repairMap(CMap * map) const if(nih->ID == Obj::HERO) //not prison nih->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front(); - //fix spells - if(nih->spellbookContainsSpell(SpellID::PRESET)) - { - nih->removeSpellFromSpellbook(SpellID::PRESET); - for(auto spellID : type->spells) - nih->addSpellToSpellbook(spellID); - } + //fix spellbook if(nih->spellbookContainsSpell(SpellID::SPELLBOOK_PRESET)) { nih->removeSpellFromSpellbook(SpellID::SPELLBOOK_PRESET);