/* * windownewmap.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 "../lib/mapping/CMap.h" #include "../lib/rmg/CRmgTemplateStorage.h" #include "../lib/rmg/CRmgTemplate.h" #include "../lib/rmg/CMapGenerator.h" #include "../lib/VCMI_Lib.h" #include "../lib/mapping/CMapEditManager.h" #include "../lib/mapping/MapFormat.h" #include "../lib/texts/CGeneralTextHandler.h" #include "../lib/CRandomGenerator.h" #include "../lib/serializer/JsonSerializer.h" #include "../lib/serializer/JsonDeserializer.h" #include "jsonutils.h" #include "windownewmap.h" #include "ui_windownewmap.h" #include "mainwindow.h" #include "generatorprogress.h" WindowNewMap::WindowNewMap(QWidget *parent) : QDialog(parent), ui(new Ui::WindowNewMap) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); setWindowModality(Qt::ApplicationModal); for(auto * combo : {ui->humanCombo, ui->cpuCombo, ui->humanTeamsCombo, ui->cpuTeamsCombo}) combo->clear(); //prepare human players combo box for(int i = 0; i <= PlayerColor::PLAYER_LIMIT_I; ++i) { ui->humanCombo->addItem(!i ? randomString : QString::number(players.at(i))); ui->humanCombo->setItemData(i, QVariant(players.at(i))); ui->cpuCombo->addItem(!i ? randomString : QString::number(cpuPlayers.at(i))); ui->cpuCombo->setItemData(i, QVariant(cpuPlayers.at(i))); ui->humanTeamsCombo->addItem(!i ? randomString : QString::number(cpuPlayers.at(i))); ui->humanTeamsCombo->setItemData(i, QVariant(cpuPlayers.at(i))); ui->cpuTeamsCombo->addItem(!i ? randomString : QString::number(cpuPlayers.at(i))); ui->cpuTeamsCombo->setItemData(i, QVariant(cpuPlayers.at(i))); } bool useLoaded = loadUserSettings(); if (!useLoaded) { for(auto * combo : {ui->humanCombo, ui->cpuCombo, ui->humanTeamsCombo, ui->cpuTeamsCombo}) combo->setCurrentIndex(0); } show(); if (!useLoaded) { //setup initial parameters int width = ui->widthTxt->text().toInt(); int height = ui->heightTxt->text().toInt(); mapGenOptions.setWidth(width ? width : 1); mapGenOptions.setHeight(height ? height : 1); bool twoLevel = ui->twoLevelCheck->isChecked(); mapGenOptions.setHasTwoLevels(twoLevel); updateTemplateList(); } } WindowNewMap::~WindowNewMap() { delete ui; } bool WindowNewMap::loadUserSettings() { bool ret = false; CRmgTemplate * templ = nullptr; QSettings s(Ui::teamName, Ui::appName); auto generateRandom = s.value(newMapGenerateRandom); if (generateRandom.isValid()) { ui->randomMapCheck->setChecked(generateRandom.toBool()); } auto settings = s.value(newMapWindow); if (settings.isValid()) { auto node = JsonUtils::toJson(settings); JsonDeserializer handler(nullptr, node); handler.serializeStruct("lastSettings", mapGenOptions); templ = const_cast(mapGenOptions.getMapTemplate()); // Remember for later ui->widthTxt->setText(QString::number(mapGenOptions.getWidth())); ui->heightTxt->setText(QString::number(mapGenOptions.getHeight())); for(const auto & sz : mapSizes) { if(sz.second.first == mapGenOptions.getWidth() && sz.second.second == mapGenOptions.getHeight()) { ui->sizeCombo->setCurrentIndex(sz.first); break; } } ui->twoLevelCheck->setChecked(mapGenOptions.getHasTwoLevels()); ui->humanCombo->setCurrentIndex(mapGenOptions.getHumanOrCpuPlayerCount()); ui->cpuCombo->setCurrentIndex(mapGenOptions.getCompOnlyPlayerCount()); ui->humanTeamsCombo->setCurrentIndex(mapGenOptions.getTeamCount()); ui->cpuTeamsCombo->setCurrentIndex(mapGenOptions.getCompOnlyTeamCount()); switch (mapGenOptions.getWaterContent()) { case EWaterContent::RANDOM: ui->waterOpt1->setChecked(true); break; case EWaterContent::NONE: ui->waterOpt2->setChecked(true); break; case EWaterContent::NORMAL: ui->waterOpt3->setChecked(true); break; case EWaterContent::ISLANDS: ui->waterOpt4->setChecked(true); break; } switch (mapGenOptions.getMonsterStrength()) { case EMonsterStrength::RANDOM: ui->monsterOpt1->setChecked(true); break; case EMonsterStrength::GLOBAL_WEAK: ui->monsterOpt2->setChecked(true); break; case EMonsterStrength::GLOBAL_NORMAL: ui->monsterOpt3->setChecked(true); break; case EMonsterStrength::GLOBAL_STRONG: ui->monsterOpt4->setChecked(true); break; } ui->roadDirt->setChecked(mapGenOptions.isRoadEnabled(Road::DIRT_ROAD)); ui->roadGravel->setChecked(mapGenOptions.isRoadEnabled(Road::GRAVEL_ROAD)); ui->roadCobblestone->setChecked(mapGenOptions.isRoadEnabled(Road::COBBLESTONE_ROAD)); ret = true; } updateTemplateList(); mapGenOptions.setMapTemplate(templ); // Can be null if (templ) { std::string name = templ->getName(); for (size_t i = 0; i < ui->templateCombo->count(); i++) { if (ui->templateCombo->itemText(i).toStdString() == name) { ui->templateCombo->setCurrentIndex(i); break; } } ret = true; } return ret; } void WindowNewMap::saveUserSettings() { QSettings s(Ui::teamName, Ui::appName); JsonNode data; JsonSerializer ser(nullptr, data); ser.serializeStruct("lastSettings", mapGenOptions); auto variant = JsonUtils::toVariant(data); s.setValue(newMapWindow, variant); s.setValue(newMapGenerateRandom, ui->randomMapCheck->isChecked()); } void WindowNewMap::on_cancelButton_clicked() { saveUserSettings(); close(); } void generateRandomMap(CMapGenerator & gen, MainWindow * window) { window->controller.setMap(gen.generate()); } std::unique_ptr generateEmptyMap(CMapGenOptions & options) { auto map = std::make_unique(nullptr); map->version = EMapFormat::VCMI; map->creationDateTime = std::time(nullptr); map->width = options.getWidth(); map->height = options.getHeight(); map->twoLevel = options.getHasTwoLevels(); map->initTerrain(); map->getEditManager()->clearTerrain(&CRandomGenerator::getDefault()); return map; } void WindowNewMap::on_okButton_clicked() { EWaterContent::EWaterContent water = EWaterContent::RANDOM; EMonsterStrength::EMonsterStrength monster = EMonsterStrength::RANDOM; if(ui->waterOpt1->isChecked()) water = EWaterContent::RANDOM; if(ui->waterOpt2->isChecked()) water = EWaterContent::NONE; if(ui->waterOpt3->isChecked()) water = EWaterContent::NORMAL; if(ui->waterOpt4->isChecked()) water = EWaterContent::ISLANDS; if(ui->monsterOpt1->isChecked()) monster = EMonsterStrength::RANDOM; if(ui->monsterOpt2->isChecked()) monster = EMonsterStrength::GLOBAL_WEAK; if(ui->monsterOpt3->isChecked()) monster = EMonsterStrength::GLOBAL_NORMAL; if(ui->monsterOpt4->isChecked()) monster = EMonsterStrength::GLOBAL_STRONG; mapGenOptions.setWaterContent(water); mapGenOptions.setMonsterStrength(monster); mapGenOptions.setRoadEnabled(Road::DIRT_ROAD, ui->roadDirt->isChecked()); mapGenOptions.setRoadEnabled(Road::GRAVEL_ROAD, ui->roadGravel->isChecked()); mapGenOptions.setRoadEnabled(Road::COBBLESTONE_ROAD, ui->roadCobblestone->isChecked()); saveUserSettings(); std::unique_ptr nmap; if(ui->randomMapCheck->isChecked()) { //verify map template if(mapGenOptions.getPossibleTemplates().empty()) { QMessageBox::warning(this, tr("No template"), tr("No template for parameters specified. Random map cannot be generated.")); return; } int seed = std::time(nullptr); if(ui->checkSeed->isChecked() && !ui->lineSeed->text().isEmpty()) seed = ui->lineSeed->text().toInt(); CMapGenerator generator(mapGenOptions, nullptr, seed); auto progressBarWnd = new GeneratorProgress(generator, this); progressBarWnd->show(); try { auto f = std::async(std::launch::async, &CMapGenerator::generate, &generator); progressBarWnd->update(); nmap = f.get(); } catch(const std::exception & e) { QMessageBox::critical(this, tr("RMG failure"), e.what()); } } else { auto f = std::async(std::launch::async, &::generateEmptyMap, std::ref(mapGenOptions)); nmap = f.get(); } nmap->mods = MapController::modAssessmentAll(); static_cast(parent())->controller.setMap(std::move(nmap)); static_cast(parent())->initializeMap(true); close(); } void WindowNewMap::on_sizeCombo_activated(int index) { ui->widthTxt->setText(QString::number(mapSizes.at(index).first)); ui->heightTxt->setText(QString::number(mapSizes.at(index).second)); } void WindowNewMap::on_twoLevelCheck_stateChanged(int arg1) { bool twoLevel = ui->twoLevelCheck->isChecked(); mapGenOptions.setHasTwoLevels(twoLevel); updateTemplateList(); } void WindowNewMap::on_humanCombo_activated(int index) { int humans = ui->humanCombo->currentData().toInt(); if(humans > PlayerColor::PLAYER_LIMIT_I) { humans = PlayerColor::PLAYER_LIMIT_I; ui->humanCombo->setCurrentIndex(humans); } mapGenOptions.setHumanOrCpuPlayerCount(humans); int teams = mapGenOptions.getTeamCount(); if(teams > humans - 1) { teams = humans > 0 ? humans - 1 : CMapGenOptions::RANDOM_SIZE; ui->humanTeamsCombo->setCurrentIndex(teams + 1); //skip one element because first is random } int cpu = mapGenOptions.getCompOnlyPlayerCount(); if(cpu > PlayerColor::PLAYER_LIMIT_I - humans) { cpu = PlayerColor::PLAYER_LIMIT_I - humans; ui->cpuCombo->setCurrentIndex(cpu + 1); //skip one element because first is random } int cpuTeams = mapGenOptions.getCompOnlyTeamCount(); //comp only players - 1 if(cpuTeams > cpu - 1) { cpuTeams = cpu > 0 ? cpu - 1 : CMapGenOptions::RANDOM_SIZE; ui->cpuTeamsCombo->setCurrentIndex(cpuTeams + 1); //skip one element because first is random } updateTemplateList(); } void WindowNewMap::on_cpuCombo_activated(int index) { int humans = mapGenOptions.getHumanOrCpuPlayerCount(); int cpu = ui->cpuCombo->currentData().toInt(); // FIXME: Use mapGenOption method only to calculate actual number of players for current template if(cpu > PlayerColor::PLAYER_LIMIT_I - humans) { cpu = PlayerColor::PLAYER_LIMIT_I - humans; ui->cpuCombo->setCurrentIndex(cpu + 1); //skip one element because first is random } mapGenOptions.setCompOnlyPlayerCount(cpu); int cpuTeams = mapGenOptions.getCompOnlyTeamCount(); //comp only players - 1 if(cpuTeams > cpu - 1) { cpuTeams = cpu > 0 ? cpu - 1 : CMapGenOptions::RANDOM_SIZE; ui->cpuTeamsCombo->setCurrentIndex(cpuTeams + 1); //skip one element because first is random } updateTemplateList(); } void WindowNewMap::on_randomMapCheck_stateChanged(int arg1) { randomMap = ui->randomMapCheck->isChecked(); ui->templateCombo->setEnabled(randomMap); updateTemplateList(); } void WindowNewMap::on_templateCombo_activated(int index) { if(index == 0) { mapGenOptions.setMapTemplate(nullptr); return; } auto * templ = data_cast(ui->templateCombo->currentData().toLongLong()); mapGenOptions.setMapTemplate(templ); } void WindowNewMap::on_widthTxt_textChanged(const QString &arg1) { int sz = arg1.toInt(); if(sz > 1) { mapGenOptions.setWidth(arg1.toInt()); updateTemplateList(); } } void WindowNewMap::on_heightTxt_textChanged(const QString &arg1) { int sz = arg1.toInt(); if(sz > 1) { mapGenOptions.setHeight(arg1.toInt()); updateTemplateList(); } } void WindowNewMap::updateTemplateList() { ui->templateCombo->clear(); ui->templateCombo->setCurrentIndex(-1); if(!randomMap) return; mapGenOptions.setMapTemplate(nullptr); auto templates = mapGenOptions.getPossibleTemplates(); if(templates.empty()) return; ui->templateCombo->addItem("[default]", 0); for(auto * templ : templates) { ui->templateCombo->addItem(QString::fromStdString(templ->getName()), data_cast(templ)); } ui->templateCombo->setCurrentIndex(0); } void WindowNewMap::on_checkSeed_toggled(bool checked) { ui->lineSeed->setEnabled(checked); } void WindowNewMap::on_humanTeamsCombo_activated(int index) { int humans = mapGenOptions.getHumanOrCpuPlayerCount(); int teams = ui->humanTeamsCombo->currentData().toInt(); if(teams >= humans) { teams = humans > 0 ? humans - 1 : CMapGenOptions::RANDOM_SIZE; ui->humanTeamsCombo->setCurrentIndex(teams + 1); //skip one element because first is random } mapGenOptions.setTeamCount(teams); updateTemplateList(); } void WindowNewMap::on_cpuTeamsCombo_activated(int index) { int cpu = mapGenOptions.getCompOnlyPlayerCount(); int cpuTeams = ui->cpuTeamsCombo->currentData().toInt(); if(cpuTeams >= cpu) { cpuTeams = cpu > 0 ? cpu - 1 : CMapGenOptions::RANDOM_SIZE; ui->cpuTeamsCombo->setCurrentIndex(cpuTeams + 1); //skip one element because first is random } mapGenOptions.setCompOnlyTeamCount(cpuTeams); updateTemplateList(); }