Files
noggit-red/src/noggit/ui/ZoneIDBrowser.cpp
2024-08-06 20:23:18 +02:00

962 lines
44 KiB
C++
Executable File

// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#include <noggit/ui/ZoneIDBrowser.h>
#include <noggit/ui/windows/SoundPlayer/SoundEntryPlayer.h>
#include <noggit/ui/windows/EditorWindows/ZoneIntroMusicPickerWindow.h>
#include <noggit/ui/windows/EditorWindows/SoundEntryPickerWindow.h>
#include <noggit/ui/windows/EditorWindows/ZoneMusicPickerWindow.h>
#include <noggit/ui/tools/MapCreationWizard/Ui/MapCreationWizard.hpp>
#include <noggit/DBC.h>
#include <noggit/Log.h>
#include <noggit/Misc.h>
#include <ClientFile.hpp>
#include <noggit/application/NoggitApplication.hpp>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/qpushbutton.h>
#include <QtWidgets/qgroupbox.h>
#include <QtWidgets/qcheckbox.h>
#include <QtWidgets/qlineedit.h>
#include <QtWidgets/QTableView>
#include <QStandardItemModel>
#include <QTableWidgetItem>
#include <QSound>
#include <qtemporaryfile>
#include <QMediaPlayer>
#include <QListWidget>
#include <QToolButton>
#include <noggit/ui/tools/UiCommon/expanderwidget.h>
#include <iostream>
#include <sstream>
#include <string>
#include <bitset>
namespace Noggit
{
namespace Ui
{
zone_id_browser::zone_id_browser(QWidget* parent)
: QWidget(parent)
, _area_tree(new QTreeWidget())
, mapID(-1)
{
auto layout = new QFormLayout(this);
_radius_spin = new QDoubleSpinBox(this);
_radius_spin->setRange(0.0f, 1000.0f);
_radius_spin->setDecimals(2);
_radius_spin->setValue(_radius);
layout->addRow("Radius:", _radius_spin);
_radius_slider = new QSlider(Qt::Orientation::Horizontal, this);
_radius_slider->setRange(0, 250);
_radius_slider->setSliderPosition(_radius);
QPushButton* edit_area_button = new QPushButton("Edit selected Area", this);
edit_area_button->setIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::cog));
QPushButton* add_zone_button = new QPushButton("Add a new Zone(parent Area)", this);
add_zone_button->setIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::plus));
QPushButton* add_subzone_button = new QPushButton("Add a new Subzone(selected as Parent)", this);
add_subzone_button->setIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::plus));
_area_editor = new AreaEditor(this);
layout->addRow(_radius_slider);
layout->addRow(_area_tree);
layout->addRow(edit_area_button);
layout->addRow(add_zone_button);
layout->addRow(add_subzone_button);
setMinimumWidth(250);
connect(_area_tree, &QTreeWidget::itemSelectionChanged
, [this]
{
auto const& selected_items = _area_tree->selectedItems();
if (selected_items.size())
{
emit selected(selected_items.back()->data(0, 1).toInt());
}
}
);
connect(_area_tree, &QTreeWidget::itemDoubleClicked
, [this]
{
open_area_editor();
});
connect(_radius_spin, qOverload<double>(&QDoubleSpinBox::valueChanged)
, [&](double v)
{
_radius = v;
QSignalBlocker const blocker(_radius_slider);
_radius_slider->setSliderPosition((int)std::round(v));
}
);
connect(_radius_slider, &QSlider::valueChanged
, [&](int v)
{
_radius = v;
QSignalBlocker const blocker(_radius_spin);
_radius_spin->setValue(v);
}
);
connect(edit_area_button, &QPushButton::clicked, [=]() {
open_area_editor();
});
connect(add_zone_button, &QPushButton::clicked, [=]() {
add_new_zone();
});
connect(add_subzone_button, &QPushButton::clicked, [=]() {
add_new_subzone();
});
}
void zone_id_browser::setMapID(int id)
{
mapID = id;
for (DBCFile::Iterator i = gMapDB.begin(); i != gMapDB.end(); ++i)
{
if (i->getInt(MapDB::MapID) == id)
{
std::stringstream ss;
ss << id << "-" << i->getString(MapDB::InternalName);
_area_tree->setHeaderLabel(ss.str().c_str());
}
}
buildAreaList();
}
void zone_id_browser::setZoneID(int id)
{
QSignalBlocker const block_area_tree(_area_tree);
if (_items.find(id) != _items.end())
{
_area_tree->selectionModel()->clear();
auto* item = _items.at(id);
item->setSelected(true);
while ((item = item->parent()))
{
item->setExpanded(true);
}
}
}
void zone_id_browser::buildAreaList()
{
QSignalBlocker const block_area_tree(_area_tree);
_area_tree->clear();
_area_tree->setColumnCount(1);
_items.clear();
// Read out Area List.
for (DBCFile::Iterator i = gAreaDB.begin(); i != gAreaDB.end(); ++i)
{
if (i->getInt(AreaDB::Continent) == mapID)
{
add_area(i->getInt(AreaDB::AreaID));
}
}
}
void zone_id_browser::changeRadius(float change)
{
_radius_spin->setValue(_radius + change);
}
void zone_id_browser::setRadius(float radius)
{
_radius_spin->setValue(radius);
}
int zone_id_browser::GetSelectedAreaId()
{
auto const& selected_items = _area_tree->selectedItems();
if (selected_items.size())
{
int selected_area_id = selected_items.back()->data(0, 1).toInt();
return selected_area_id;
}
return 0;
}
QTreeWidgetItem* zone_id_browser::create_or_get_tree_widget_item(int area_id)
{
auto it = _items.find(area_id);
if (it != _items.end())
{
return _items.at(area_id);
}
else
{
QTreeWidgetItem* item = new QTreeWidgetItem();
std::stringstream ss;
// ss << area_id << "-" << gAreaDB.getAreaName(area_id);
std::string areaName = "";
try
{
AreaDB::Record rec = gAreaDB.getByID(area_id);
areaName = rec.getLocalizedString(AreaDB::Name);
}
catch (AreaDB::NotFound)
{
areaName = "Unknown location";
}
ss << area_id << "-" << areaName;
item->setData(0, 1, QVariant(area_id));
item->setText(0, QString(ss.str().c_str()));
_items.emplace(area_id, item);
return item;
}
}
QTreeWidgetItem* zone_id_browser::add_area(int area_id)
{
QTreeWidgetItem* item = create_or_get_tree_widget_item(area_id);
std::uint32_t parent_area_id = gAreaDB.get_area_parent(area_id);
if (parent_area_id && parent_area_id != area_id)
{
QTreeWidgetItem* parent_item = add_area(parent_area_id);
parent_item->addChild(item);
}
else
{
_area_tree->addTopLevelItem(item);
}
return item;
}
void zone_id_browser::open_area_editor()
{
// opens and loads the area editor with the currently selected item in the tree.
int selected_area_id = GetSelectedAreaId();
if (selected_area_id != 0)
{
_area_editor->load_area(selected_area_id);
_area_editor->show();
}
else
{
// No selected area popup
}
}
void zone_id_browser::add_new_zone()
{
// create new zone area id
int new_id = gAreaDB.getEmptyRecordID();
QDialog* zone_create_params = new QDialog(this);
zone_create_params->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint);
zone_create_params->setWindowTitle("New Zone settings");
QVBoxLayout* zone_create_params_layout = new QVBoxLayout(zone_create_params);
zone_create_params_layout->addWidget(new QLabel("AreaTable Id : ", zone_create_params));
QSpinBox* params_id_spinbox = new QSpinBox(zone_create_params);
params_id_spinbox->setRange(1, std::numeric_limits<int>::max());
params_id_spinbox->setValue(new_id);
zone_create_params_layout->addWidget(params_id_spinbox);
zone_create_params_layout->addWidget(new QLabel("Zone Name : ", zone_create_params));
QLineEdit* params_name_ledit = new QLineEdit(zone_create_params);
params_name_ledit->setText("Unnamed Noggit Zone");
zone_create_params_layout->addWidget(params_name_ledit);
QPushButton* params_okay = new QPushButton("Create new Zone", zone_create_params);
zone_create_params_layout->addWidget(params_okay);
connect(params_okay, &QPushButton::clicked
, [=]()
{
// check if ID is valid
if (!gAreaDB.CheckIfIdExists(params_id_spinbox->value()))
{
zone_create_params->accept();
}
else
{
QMessageBox::warning
(nullptr
, "ID already in use"
, "Id is already used, use a different one"
, QMessageBox::Ok
);
}
});
// start popup and wait for ok button
if (zone_create_params->exec() == QDialog::Accepted)
{
auto new_record = gAreaDB.addRecord(params_id_spinbox->value()); // could add a try catch but we already check if id is used before
// init some defaults
new_record.write(AreaDB::Continent, mapID);
// get new areabit
new_record.write(AreaDB::AreaBit, gAreaDB.get_new_areabit());
unsigned int flags = 0;
flags |= (1 << 6); // Allow Dueling
new_record.write(AreaDB::Flags, flags);
new_record.write(AreaDB::UnderwaterSoundProviderPreferences, 11); // underwater sound pref, usually set
// locale stuff
// new_record.writeString(AreaDB::Name, params_name_ledit->text().toStdString()); // only write default name for enUS and enGB ? maybe get the client's locale somehow
int locale_id = Noggit::Application::NoggitApplication::instance()->clientData()->getLocaleId();
new_record.writeLocalizedString(AreaDB::Name, params_name_ledit->text().toStdString(), locale_id);
new_record.write(AreaDB::Name + 16, 16712190); // loc mask, only verified for enUS
new_record.write(AreaDB::MinElevation, -500.0f);
// save dbc instantly ?
gAreaDB.save();
// add to tree
auto areawidgetitem = add_area(params_id_spinbox->value());
// select the new item
_area_tree->clearSelection();
areawidgetitem->setSelected(true);
open_area_editor();// open the editor
}
}
void zone_id_browser::add_new_subzone()
{
// create new subzone area id
// set selected as parent
int selected_areaid = GetSelectedAreaId();
if (!selected_areaid) // no valid item selected
return;
// check if it's a valid parent : it shouldn't have a parent
std::uint32_t selected_parent_area_id = gAreaDB.get_area_parent(selected_areaid); // the selected area's parentid
if (selected_parent_area_id)
{
QMessageBox::information
(nullptr
, "Wrong Parent type"
, "The parent must be a Zone, not a Subzone."
, QMessageBox::Ok
);
return;
}
int new_id = gAreaDB.getEmptyRecordID();
QDialog* zone_create_params = new QDialog(this);
zone_create_params->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint);
zone_create_params->setWindowTitle("New Zone settings");
QVBoxLayout* zone_create_params_layout = new QVBoxLayout(zone_create_params);
zone_create_params_layout->addWidget(new QLabel("AreaTable Id : ", zone_create_params));
QSpinBox* params_id_spinbox = new QSpinBox(zone_create_params);
params_id_spinbox->setRange(1, std::numeric_limits<int>::max());
params_id_spinbox->setValue(new_id);
zone_create_params_layout->addWidget(params_id_spinbox);
zone_create_params_layout->addWidget(new QLabel("Subzone Name : ", zone_create_params));
QLineEdit* params_name_ledit = new QLineEdit(zone_create_params);
params_name_ledit->setText("Unnamed Noggit Subzone");
zone_create_params_layout->addWidget(params_name_ledit);
QPushButton* params_okay = new QPushButton("Create new Subzone", zone_create_params);
zone_create_params_layout->addWidget(params_okay);
connect(params_okay, &QPushButton::clicked
, [=]()
{
// check if ID is valid
int id = params_id_spinbox->value();
bool exists = gAreaDB.CheckIfIdExists(params_id_spinbox->value());
if (exists)
{
QMessageBox::warning
(nullptr
, "ID already in use"
, "Id is already used, use a different one"
, QMessageBox::Ok
);
}
else
{
zone_create_params->accept();
}
});
// start popup and wait for ok button
if (zone_create_params->exec() == QDialog::Accepted)
{
auto new_record = gAreaDB.addRecord(params_id_spinbox->value());
// init some defaults
new_record.write(AreaDB::Continent, mapID);
new_record.write(AreaDB::Region, selected_areaid); // set selecetd area as parent.
// get new areabit
new_record.write(AreaDB::AreaBit, gAreaDB.get_new_areabit());
unsigned int flags = 0;
flags |= (1 << 6); // Allow Dueling
flags |= (1 << 10); // force area on dynamic transport
flags |= (1 << 14); // enable flight bounds
flags |= (1 << 30); // subzone flag
new_record.write(AreaDB::Flags, flags); // allow dueling + force area on dynamic transport + enable flight bounds+ subzone flags
new_record.write(AreaDB::UnderwaterSoundProviderPreferences, 11); // underwater sound pref, usually set
// lcoale stuff
int locale_id = Noggit::Application::NoggitApplication::instance()->clientData()->getLocaleId();
new_record.writeLocalizedString(AreaDB::Name, params_name_ledit->text().toStdString(), locale_id);
new_record.write(AreaDB::Name + 16, 16712190); // loc mask, only verified for enUS
new_record.write(AreaDB::MinElevation, -500.0f); // loc mask, only verified for enUS
// save dbc instantly ?
gAreaDB.save();
// add to tree
auto areawidgetitem = add_area(params_id_spinbox->value());
// select the new item
_area_tree->clearSelection();
areawidgetitem->setSelected(true);
open_area_editor();// open the editor
}
}
AreaEditor::AreaEditor(QWidget* parent)
: QWidget(parent)
{
setWindowTitle("Area Editor");
setWindowFlags(Qt::Dialog);
// setWindowFlags(Qt::Widget);
auto main_layout = new QHBoxLayout(this);
QGroupBox* area_settings_group = new QGroupBox("Area Settings", this);
main_layout->addWidget(area_settings_group);
auto layout = new QFormLayout(area_settings_group);
// main_layout->addLayout(layout);
_area_id_label = new QLabel("0", this);
_area_id_label->setEnabled(false);
_parent_area_label = new QLabel("-NONE-", area_settings_group);
_parent_area_label->setEnabled(false);
// auto parent_set_layout = new QHBoxLayout(this);
_set_parent_button = new QPushButton("Set Selected Zone as Parent", area_settings_group);
// QPushButton* unset_parent_button = new QPushButton("Unset Parent", this);
// parent_set_layout->addWidget(set_parent_button);
// parent_set_layout->addWidget(unset_parent_button);
_area_name = new Tools::MapCreationWizard::Ui::LocaleDBCEntry(area_settings_group);
_exploration_level_spinbox = new QSpinBox(this);
_exploration_level_spinbox->setRange(-1, 255);
_ambiant_multiplier = new QSlider(Qt::Horizontal, this);
_ambiant_multiplier->setRange(0, 100); // 0.0 - 1.0, decimal not supported so *100
_ambiant_multiplier->setTickInterval(5);
_ambiant_multiplier->setSingleStep(5);
// faction group
// read FactionGroup.dbc or just hardcode?
_faction_group_combobox = new QComboBox(this);
_faction_group_combobox->addItem("Contested"); // 0
_faction_group_combobox->addItem("Alliance"); // 2
_faction_group_combobox->addItem("Horde"); // 4
_faction_group_combobox->addItem("Horde & Alliance"); // mask : 2 + 4
// _liquid_type_water_combobox = new QComboBox(this);
// _liquid_type_water_combobox->addItem("None");
// _liquid_type_ocean_combobox = new QComboBox(this);
// _liquid_type_ocean_combobox->addItem("None");
// _liquid_type_magma_combobox = new QComboBox(this);
// _liquid_type_magma_combobox->addItem("None");
// _liquid_type_slime_combobox = new QComboBox(this);
// _liquid_type_slime_combobox->addItem("None");
// for (DBCFile::Iterator i = gLiquidTypeDB.begin(); i != gLiquidTypeDB.end(); ++i)
// {
// std::stringstream ss;
// int liquid_type = i->getInt(LiquidTypeDB::Type);
// ss << i->getInt(LiquidTypeDB::ID) << "-" << i->getString(LiquidTypeDB::Name);
// switch (liquid_type)
// {
// case 0:
// _liquid_type_water_combobox->addItem(i->getString(LiquidTypeDB::Name));
// break;
// case 1:
// _liquid_type_ocean_combobox->addItem(i->getString(LiquidTypeDB::Name));
// break;
// case 2:
// _liquid_type_magma_combobox->addItem(i->getString(LiquidTypeDB::Name));
// break;
// case 3:
// _liquid_type_slime_combobox->addItem(i->getString(LiquidTypeDB::Name));
// }
// }
_sound_provider_preferences_cbbox = new QComboBox(this);
_sound_provider_preferences_cbbox->addItem("None");
_underwater_sound_provider_preferences_cbbox = new QComboBox(this);
_underwater_sound_provider_preferences_cbbox->addItem("None");
for (DBCFile::Iterator i = gSoundProviderPreferencesDB.begin(); i != gSoundProviderPreferencesDB.end(); ++i)
{
std::stringstream ss;
ss << i->getInt(SoundProviderPreferencesDB::ID) << "-" << i->getString(SoundProviderPreferencesDB::Description);
_sound_provider_preferences_cbbox->addItem(ss.str().c_str());
_underwater_sound_provider_preferences_cbbox->addItem(ss.str().c_str());
}
_sound_ambiance_day_button = new QPushButton("-None-", this);
_sound_ambiance_day_button->setProperty("id", 0);
connect(_sound_ambiance_day_button, &QPushButton::clicked, [=]() {
auto window = new SoundEntryPickerWindow(_sound_ambiance_day_button, SoundEntryTypes::ZONE_AMBIENCE, false, this);
window->show();
});
_sound_ambiance_night_button = new QPushButton("-None-", this);
_sound_ambiance_night_button->setProperty("id", 0);
connect(_sound_ambiance_night_button, &QPushButton::clicked, [=]() {
auto window = new SoundEntryPickerWindow(_sound_ambiance_night_button, SoundEntryTypes::ZONE_AMBIENCE, false, this);
window->show();
});
_zone_music_button = new QPushButton("-None-", this);
_zone_music_button->setProperty("id", 0);
connect(_zone_music_button, &QPushButton::clicked, [=]() {
auto window = new ZoneMusicPickerWindow(_zone_music_button, this);
window->show();
});
_zone_intro_music_button = new QPushButton("-None-", this);
_zone_intro_music_button->setProperty("id", 0);
connect(_zone_intro_music_button, &QPushButton::clicked, [=]() {
auto window = new ZoneIntroMusicPickerWindow(_zone_intro_music_button, this);
window->show();
});
// advanced settings group
auto* AdvancedOptionsBox = new ExpanderWidget(this);
AdvancedOptionsBox->setExpanderTitle("Advanced Options");
// selectionOptionsBox->setExpanded(_settings->value("object_editor/movement_options", false).toBool());
auto advancedOptionsBox_content = new QWidget(this);
auto advancedOptions_layout = new QFormLayout(advancedOptionsBox_content);
// advancedOptions_layout->setAlignment(Qt::AlignTop);
AdvancedOptionsBox->addPage(advancedOptionsBox_content);
AdvancedOptionsBox->setExpanded(false);
_min_elevation_spinbox = new QDoubleSpinBox(this);
_min_elevation_spinbox->setRange(-5000, 5000); // only 3 known values : -5000, -500, 1000
_min_elevation_spinbox->setSingleStep(100);
advancedOptions_layout->addRow("Min Elevation:", _min_elevation_spinbox);
advancedOptions_layout->addRow("Ambiant Multiplier:", _ambiant_multiplier);
// advancedOptions_layout->addRow("Water Liquid Type override:", _liquid_type_water_combobox);
// advancedOptions_layout->addRow("Ocean Liquid Type override:", _liquid_type_ocean_combobox);
// advancedOptions_layout->addRow("Magma Liquid Type override:", _liquid_type_magma_combobox);
// advancedOptions_layout->addRow("Slime Liquid Type override:", _liquid_type_slime_combobox);
advancedOptions_layout->addRow("Sound Provider Preference:", _sound_provider_preferences_cbbox);
advancedOptions_layout->addRow("Underwater Sound Provider Preference:", _underwater_sound_provider_preferences_cbbox);
QPushButton* save_area_button = new QPushButton("Save changes (write DBC)", this);
// TODO : unset parent button?
// flags tab **************************//
QGroupBox* area_flags_group = new QGroupBox("Flags", this);
main_layout->addWidget(area_flags_group);
auto flags_layout = new QVBoxLayout(area_flags_group);
_flags_value_spinbox = new QSpinBox(this);
_flags_value_spinbox->setRange(0, INT_MAX); // uint not necessary for wrath because we only have 31 flags.
// for cata+ we'll need all 32 bits instead, might need a line edit
flags_layout->addWidget(_flags_value_spinbox);
// TODO : update checkboxes when value is changed, temporarly disable it from edit
_flags_value_spinbox->setEnabled(false);
for (int i = 0; i < 31; ++i)
{
QCheckBox* flag_checkbox = new QCheckBox(QString::fromStdString(area_flags_names.at(i)), this);
flags_checkboxes[i] = flag_checkbox;
flags_layout->addWidget(flag_checkbox);
connect(flag_checkbox, &QCheckBox::stateChanged, [&, i](bool state) {
// connect(flag_checkbox, &QCheckBox::clicked, [=]() {
// int old_value = _flags_value_spinbox->value();
int new_value = _flags_value_spinbox->value();
if (state) // set bit
new_value |= (1U << (i));
else // remove bit
new_value &= ~(1U << (i));
_flags_value_spinbox->setValue(new_value);
});
}
// hide some useless flags to gain space
flags_checkboxes[30]->setEnabled(false); // user can't set subzone flag.
flags_checkboxes[20]->setHidden(true); // tournement realm thingy, useless for pservers
flags_checkboxes[17]->setHidden(true); // "Area not in use", prob some blizz dev stuff
//************************************//
layout->addRow("Area ID:", _area_id_label);
layout->addRow("Area Name:", _area_name);
layout->addWidget(_set_parent_button);
layout->addRow("Parent Area ID:", _parent_area_label);
layout->addRow("Faction Group:", _faction_group_combobox);
layout->addRow("Exploration Level:", _exploration_level_spinbox);
layout->addRow("Zone Music:", _zone_music_button);
layout->addRow("Zone Intro Music:", _zone_intro_music_button);
layout->addRow("Sound Ambience Day:", _sound_ambiance_day_button);
layout->addRow("Sound Ambience Night:", _sound_ambiance_night_button);
layout->addRow(new QLabel("If only day or night is set but not the other, the other will be saved as the same sound."));
layout->addRow(AdvancedOptionsBox);
layout->addRow(save_area_button);
layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
// main_layout->addStretch();
connect(_set_parent_button, &QPushButton::clicked, [=]() {
// Current design choice : Cannot change a zone to be a subzone or the opposite, only current subzones can have a parent set.
auto parent = static_cast<zone_id_browser*>(this->parentWidget());
auto tree_selected_id = parent->GetSelectedAreaId();
if (!tree_selected_id)
return;
// can only set a zone as parent, not a subzone.
// a zone must be a top level item, and not have a parent or the subzone flag.
// checks :
// 1 : current area must be a subzone
if (!_parent_area_id)
return;
std::uint32_t selected_parent_area_id = gAreaDB.get_area_parent(tree_selected_id); // the selected area's parentid
if (selected_parent_area_id)
{
// error, parent must not have a parent
QMessageBox messagebox;
messagebox.setIcon(QMessageBox::Information);
messagebox.setWindowIcon(QIcon(":/icon"));
messagebox.setWindowTitle("Wrong Parent type");
messagebox.setText("The parent must be a Zone, not a Subzone.");
messagebox.exec();
return;
}
// can't be self
if (tree_selected_id == _parent_area_id)
return;// same parent, ignore
if (tree_selected_id == _area_id_label->text().toInt())
return; // error, can't select self
// save the change for the session (don't write dbc yet)
_parent_area_id = tree_selected_id;
std::stringstream ss;
ss << _parent_area_id << "-" << gAreaDB.getAreaFullName(_parent_area_id);
_parent_area_label->setText(ss.str().c_str());
// _parent_area_label->setText(std::to_string( tree_selected_id).c_str());
try
{
auto curr_record = gAreaDB.getByID(_area_id_label->text().toInt());
curr_record.write(AreaDB::Region, tree_selected_id);
// update the tree
parent->buildAreaList();
// select the item ?
auto item = parent->create_or_get_tree_widget_item(_area_id_label->text().toInt());
// parent->selected(_area_id_label->text().toInt());
}
catch (AreaDB::NotFound)
{
}
});
connect(save_area_button, &QPushButton::clicked, [=]() {
save_area();// save and write DBC
});
connect(_flags_value_spinbox, qOverload<int>(&QSpinBox::valueChanged), [&](int v) {
// std::bitset<32> IntBits(_flags_value_spinbox->value());
//
// for (int i = 0; i < 31; i++)
// flags_checkboxes[i]->setChecked(IntBits[i]);
});
}
void AreaEditor::load_area(int area_id)
{
try
{
DBCFile::Record record = gAreaDB.getByID(area_id);
_area_id_label->setText(QString(std::to_string(area_id).c_str()));
_areabit = record.getInt(AreaDB::AreaBit);
_parent_area_id = record.getInt(AreaDB::Region);
if (_parent_area_id)
{
std::stringstream ss;
ss << _parent_area_id << "-" << gAreaDB.getAreaFullName(_parent_area_id);
_parent_area_label->setText(ss.str().c_str());
}
else
_parent_area_label->setText("-NONE-");
// hide some UI if not subzone
_set_parent_button->setHidden(_parent_area_id ? false : true);
// _area_name_ledit->setText(record.getString(AreaDB::Name));
_area_name->fill(record, AreaDB::Name);
_flags_value_spinbox->setValue(record.getInt(AreaDB::Flags));
std::bitset<32> IntBits(_flags_value_spinbox->value());
for (int i = 0; i < 31; i++)
flags_checkboxes[i]->setChecked(IntBits[i]);
_exploration_level_spinbox->setValue(record.getInt(AreaDB::ExplorationLevel));
int faction_group_mask = record.getInt(AreaDB::FactionGroup);
switch (faction_group_mask) // hardcoded but can be read from factiongroup.dbc
{
case 2:
_faction_group_combobox->setCurrentIndex(1);
break;
case 4:
_faction_group_combobox->setCurrentIndex(2);
break;
case 6:
_faction_group_combobox->setCurrentIndex(3);
break;
default:
_faction_group_combobox->setCurrentIndex(0);
}
_min_elevation_spinbox->setValue(record.getFloat(AreaDB::MinElevation)); // only 3 known values : -5000, -500, 1000
_ambiant_multiplier->setValue(record.getFloat(AreaDB::AmbientMultiplier) * 100);
int sound_provider_id = record.getInt(AreaDB::SoundProviderPreferences);
if (sound_provider_id != 0 && gSoundProviderPreferencesDB.CheckIfIdExists(sound_provider_id))
{
int row_id = gSoundProviderPreferencesDB.getRecordRowId(sound_provider_id);
_sound_provider_preferences_cbbox->setCurrentIndex(row_id + 1); // index 0 = "None"
}
else
_sound_provider_preferences_cbbox->setCurrentIndex(0);
int underwater_sound_provider_id = record.getInt(AreaDB::UnderwaterSoundProviderPreferences);
if (underwater_sound_provider_id != 0 && gSoundProviderPreferencesDB.CheckIfIdExists(underwater_sound_provider_id))
{
int row_id = gSoundProviderPreferencesDB.getRecordRowId(underwater_sound_provider_id);
_underwater_sound_provider_preferences_cbbox->setCurrentIndex(row_id + 1); // index 0 = "None"
}
else
_underwater_sound_provider_preferences_cbbox->setCurrentIndex(0);
int zone_music_id = record.getInt(AreaDB::ZoneMusic);
if (zone_music_id != 0 && gZoneMusicDB.CheckIfIdExists(zone_music_id))
{
_zone_music_button->setProperty("id", zone_music_id);
auto zone_music_record = gZoneMusicDB.getByID(zone_music_id);
std::stringstream ss;
ss << zone_music_id << "-" << zone_music_record.getString(ZoneMusicDB::Name);
_zone_music_button->setText(ss.str().c_str());
}
else
{
_zone_music_button->setText("-NONE-");
_zone_music_button->setProperty("id", 0);
}
int zone_intro_music_id = record.getInt(AreaDB::ZoneIntroMusicTable);
if (zone_intro_music_id != 0 && gZoneIntroMusicTableDB.CheckIfIdExists(zone_intro_music_id))
{
_zone_intro_music_button->setProperty("id", zone_intro_music_id);
auto zone_intro_music_record = gZoneIntroMusicTableDB.getByID(zone_intro_music_id);
std::stringstream ss;
ss << zone_intro_music_id << "-" << zone_intro_music_record.getString(ZoneIntroMusicTableDB::Name);
_zone_intro_music_button->setText(ss.str().c_str());
}
else
{
_zone_intro_music_button->setProperty("id", 0);
_zone_intro_music_button->setText("-NONE-");
}
int sound_ambiance_id = record.getInt(AreaDB::SoundAmbience);
if (sound_ambiance_id != 0 && gSoundAmbienceDB.CheckIfIdExists(sound_ambiance_id))
{
auto sound_ambiance_record = gSoundAmbienceDB.getByID(sound_ambiance_id);
int day_sound_id = sound_ambiance_record.getInt(SoundAmbienceDB::SoundEntry_day);
if (day_sound_id != 0 && gSoundEntriesDB.CheckIfIdExists(day_sound_id))
{
auto sound_entry_day_record = gSoundEntriesDB.getByID(day_sound_id);
std::stringstream ss_day;
ss_day << day_sound_id << "-" << sound_entry_day_record.getString(SoundEntriesDB::Name);
_sound_ambiance_day_button->setText(ss_day.str().c_str());
_sound_ambiance_day_button->setProperty("id", day_sound_id);
}
else
{
_sound_ambiance_day_button->setText("-NONE-");
_sound_ambiance_day_button->setProperty("id", 0);
}
int night_sound_id = sound_ambiance_record.getInt(SoundAmbienceDB::SoundEntry_night);
if (night_sound_id != 0 && gSoundEntriesDB.CheckIfIdExists(night_sound_id))
{
auto sound_entry_night_record = gSoundEntriesDB.getByID(night_sound_id);
std::stringstream ss_night;
ss_night << night_sound_id << "-" << sound_entry_night_record.getString(SoundEntriesDB::Name);
_sound_ambiance_night_button->setText(ss_night.str().c_str());
_sound_ambiance_night_button->setProperty("id", night_sound_id);
}
else
{
_sound_ambiance_night_button->setText("-NONE-");
_sound_ambiance_night_button->setProperty("id", 0);
}
}
else
{
_sound_ambiance_day_button->setProperty("id", 0);
_sound_ambiance_day_button->setText("-NONE-");
_sound_ambiance_night_button->setProperty("id", 0);
_sound_ambiance_night_button->setText("-NONE-");
}
}
catch (AreaDB::NotFound)
{
}
}
void AreaEditor::save_area()
{
try
{
DBCFile::Record record = gAreaDB.getByID(_area_id_label->text().toInt()); // is_new_record ? gLightDB.addRecord(Id) : gLightDB.getByID(Id);
// record.write(AreaDB::ID, entry_id);
// rewrite mapid ?
record.write(AreaDB::Region, _parent_area_id);
record.write(AreaDB::AreaBit, _areabit);
record.write(AreaDB::Flags, _flags_value_spinbox->value());
int SoundProviderPreferences_id = 0;
if (_sound_provider_preferences_cbbox->currentIndex() != 0)
{
auto rec = gSoundProviderPreferencesDB.getRecord(_sound_provider_preferences_cbbox->currentIndex() - 1);
SoundProviderPreferences_id = rec.getInt(SoundProviderPreferencesDB::ID);
}
record.write(AreaDB::SoundProviderPreferences, SoundProviderPreferences_id);
int underwaterSoundProviderPreferences_id = 0;
if (_underwater_sound_provider_preferences_cbbox->currentIndex() != 0)
{
auto rec = gSoundProviderPreferencesDB.getRecord(_underwater_sound_provider_preferences_cbbox->currentIndex() - 1);
underwaterSoundProviderPreferences_id = rec.getInt(SoundProviderPreferencesDB::ID);
}
record.write(AreaDB::UnderwaterSoundProviderPreferences, underwaterSoundProviderPreferences_id);
// Ambiance ID. Blizzard stores those as unamed pair
// Just iterate the DBC to see if an entry with our settings already exists, if not create it.
// The reasoning for not having a selector/picker with the existing pairs is that it has less freedom, is harder to use and it's ugly if they don't have a name.
// This doesn't have the option to edit that entry for all its users though.
int soundambience_day = _sound_ambiance_day_button->property("id").toInt();
int soundambience_night = _sound_ambiance_night_button->property("id").toInt();
if (soundambience_day && !soundambience_night) // if day is set but not night, set night to day
soundambience_night = soundambience_day;
else if (!soundambience_day && soundambience_night) // night to day
soundambience_night = soundambience_day;
if (soundambience_day && soundambience_night) // check if both day and night are set
{
bool sound_ambiance_exists {false};
for (DBCFile::Iterator i = gSoundAmbienceDB.begin(); i != gSoundAmbienceDB.end(); ++i)
{
int day_id = i->getInt(SoundAmbienceDB::SoundEntry_day);
int night_id = i->getInt(SoundAmbienceDB::SoundEntry_night);
if (day_id == soundambience_day && night_id == soundambience_night)
{
record.write(AreaDB::SoundAmbience, i->getInt(SoundAmbienceDB::ID));
sound_ambiance_exists = true;
break;
}
}
if (!sound_ambiance_exists)
{
// create new sond entry record with the two ids
auto new_id = gSoundAmbienceDB.getEmptyRecordID();
auto new_record = gSoundAmbienceDB.addRecord(new_id);
new_record.write(SoundAmbienceDB::SoundEntry_day, soundambience_day);
new_record.write(SoundAmbienceDB::SoundEntry_night, soundambience_night);
gSoundAmbienceDB.save();
record.write(AreaDB::SoundAmbience, new_id);
}
}
else
record.write(AreaDB::SoundAmbience, 0); // if night or day isn't set, set to 0
record.write(AreaDB::ZoneMusic, _zone_music_button->property("id").toInt());
record.write(AreaDB::ZoneIntroMusicTable, _zone_intro_music_button->property("id").toInt());
record.write(AreaDB::ExplorationLevel, _exploration_level_spinbox->value());
_area_name->toRecord(record, AreaDB::Name);
// update name in the tree
auto parent = static_cast<zone_id_browser*>(this->parentWidget());
auto item = parent->create_or_get_tree_widget_item(_area_id_label->text().toInt());
std::stringstream ss;
std::string areaName = record.getLocalizedString(AreaDB::Name);
ss << _area_id_label->text().toInt() << "-" << areaName;
item->setText(0, QString(ss.str().c_str()));
record.write(AreaDB::FactionGroup, _faction_group_combobox->currentIndex() * 2);
record.write(AreaDB::MinElevation, static_cast<float>(_min_elevation_spinbox->value()));
record.write(AreaDB::AmbientMultiplier, _ambiant_multiplier->value() / 100.0f);
record.write(AreaDB::LightId, 0); // never used
gAreaDB.save();
load_area(_area_id_label->text().toInt());// reload ui, especially for night/day ambience
}
catch (AreaDB::NotFound)
{
}
}
}
}