mainwindow.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237
  1. /*
  2. * mainwindow.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "mainwindow.h"
  12. #include "ui_mainwindow.h"
  13. #include <QFileDialog>
  14. #include <QFile>
  15. #include <QMessageBox>
  16. #include <QFileInfo>
  17. #include "../lib/VCMIDirs.h"
  18. #include "../lib/VCMI_Lib.h"
  19. #include "../lib/logging/CBasicLogConfigurator.h"
  20. #include "../lib/CConfigHandler.h"
  21. #include "../lib/CModHandler.h"
  22. #include "../lib/filesystem/Filesystem.h"
  23. #include "../lib/GameConstants.h"
  24. #include "../lib/mapping/CMapService.h"
  25. #include "../lib/mapping/CMap.h"
  26. #include "../lib/mapping/CMapEditManager.h"
  27. #include "../lib/RoadHandler.h"
  28. #include "../lib/RiverHandler.h"
  29. #include "../lib/TerrainHandler.h"
  30. #include "../lib/mapObjects/CObjectClassesHandler.h"
  31. #include "../lib/filesystem/CFilesystemLoader.h"
  32. #include "maphandler.h"
  33. #include "graphics.h"
  34. #include "windownewmap.h"
  35. #include "objectbrowser.h"
  36. #include "inspector/inspector.h"
  37. #include "mapsettings.h"
  38. #include "playersettings.h"
  39. #include "validator.h"
  40. static CBasicLogConfigurator * logConfig;
  41. QJsonValue jsonFromPixmap(const QPixmap &p)
  42. {
  43. QBuffer buffer;
  44. buffer.open(QIODevice::WriteOnly);
  45. p.save(&buffer, "PNG");
  46. auto const encoded = buffer.data().toBase64();
  47. return {QLatin1String(encoded)};
  48. }
  49. QPixmap pixmapFromJson(const QJsonValue &val)
  50. {
  51. auto const encoded = val.toString().toLatin1();
  52. QPixmap p;
  53. p.loadFromData(QByteArray::fromBase64(encoded), "PNG");
  54. return p;
  55. }
  56. void init()
  57. {
  58. loadDLLClasses();
  59. logGlobal->info("Initializing VCMI_Lib");
  60. }
  61. void MainWindow::loadUserSettings()
  62. {
  63. //load window settings
  64. QSettings s(Ui::teamName, Ui::appName);
  65. auto size = s.value(mainWindowSizeSetting).toSize();
  66. if (size.isValid())
  67. {
  68. resize(size);
  69. }
  70. auto position = s.value(mainWindowPositionSetting).toPoint();
  71. if (!position.isNull())
  72. {
  73. move(position);
  74. }
  75. }
  76. void MainWindow::saveUserSettings()
  77. {
  78. QSettings s(Ui::teamName, Ui::appName);
  79. s.setValue(mainWindowSizeSetting, size());
  80. s.setValue(mainWindowPositionSetting, pos());
  81. }
  82. void MainWindow::parseCommandLine(ExtractionOptions & extractionOptions)
  83. {
  84. QCommandLineParser parser;
  85. parser.addHelpOption();
  86. parser.addPositionalArgument("map", QCoreApplication::translate("main", "Filepath of the map to open."));
  87. parser.addOptions({
  88. {"e", QCoreApplication::translate("main", "Extract original H3 archives into a separate folder.")},
  89. {"s", QCoreApplication::translate("main", "From an extracted archive, it Splits TwCrPort, CPRSMALL, FlagPort, ITPA, ITPt, Un32 and Un44 into individual PNG's.")},
  90. {"c", QCoreApplication::translate("main", "From an extracted archive, Converts single Images (found in Images folder) from .pcx to png.")},
  91. {"d", QCoreApplication::translate("main", "Delete original files, for the ones splitted / converted.")},
  92. });
  93. parser.process(qApp->arguments());
  94. const QStringList positionalArgs = parser.positionalArguments();
  95. if(!positionalArgs.isEmpty())
  96. mapFilePath = positionalArgs.at(0);
  97. extractionOptions = {
  98. parser.isSet("e"), {
  99. parser.isSet("s"),
  100. parser.isSet("c"),
  101. parser.isSet("d")}};
  102. }
  103. void MainWindow::loadTranslation()
  104. {
  105. #ifdef ENABLE_QT_TRANSLATIONS
  106. std::string translationFile = settings["general"]["language"].String() + ".qm";
  107. QVector<QString> searchPaths;
  108. for(auto const & string : VCMIDirs::get().dataPaths())
  109. searchPaths.push_back(pathToQString(string / "mapeditor" / "translation" / translationFile));
  110. searchPaths.push_back(pathToQString(VCMIDirs::get().userDataPath() / "mapeditor" / "translation" / translationFile));
  111. for(auto const & string : boost::adaptors::reverse(searchPaths))
  112. {
  113. if (translator.load(string))
  114. {
  115. if (!qApp->installTranslator(&translator))
  116. logGlobal->error("Failed to install translator");
  117. return;
  118. }
  119. }
  120. logGlobal->error("Failed to find translation");
  121. #endif
  122. }
  123. MainWindow::MainWindow(QWidget* parent) :
  124. QMainWindow(parent),
  125. ui(new Ui::MainWindow),
  126. controller(this)
  127. {
  128. // Set current working dir to executable folder.
  129. // This is important on Mac for relative paths to work inside DMG.
  130. QDir::setCurrent(QApplication::applicationDirPath());
  131. for(auto & string : VCMIDirs::get().dataPaths())
  132. QDir::addSearchPath("icons", pathToQString(string / "mapeditor" / "icons"));
  133. QDir::addSearchPath("icons", pathToQString(VCMIDirs::get().userDataPath() / "mapeditor" / "icons"));
  134. new QShortcut(QKeySequence("Backspace"), this, SLOT(on_actionErase_triggered()));
  135. ExtractionOptions extractionOptions;
  136. parseCommandLine(extractionOptions);
  137. //configure logging
  138. const boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Editor_log.txt";
  139. console = new CConsoleHandler();
  140. logConfig = new CBasicLogConfigurator(logPath, console);
  141. logConfig->configureDefault();
  142. logGlobal->info("The log file will be saved to %s", logPath);
  143. //init
  144. preinitDLL(::console, false, extractionOptions.extractArchives);
  145. // Initialize logging based on settings
  146. logConfig->configure();
  147. logGlobal->debug("settings = %s", settings.toJsonNode().toJson());
  148. // Some basic data validation to produce better error messages in cases of incorrect install
  149. auto testFile = [](std::string filename, std::string message) -> bool
  150. {
  151. if (CResourceHandler::get()->existsResource(ResourceID(filename)))
  152. return true;
  153. logGlobal->error("Error: %s was not found!", message);
  154. return false;
  155. };
  156. if (!testFile("DATA/HELP.TXT", "Heroes III data") ||
  157. !testFile("MODS/VCMI/MOD.JSON", "VCMI data"))
  158. {
  159. QApplication::quit();
  160. }
  161. loadTranslation();
  162. ui->setupUi(this);
  163. loadUserSettings(); //For example window size
  164. setTitle();
  165. init();
  166. graphics = new Graphics(); // should be before curh->init()
  167. graphics->load();//must be after Content loading but should be in main thread
  168. if (extractionOptions.extractArchives)
  169. ResourceConverter::convertExtractedResourceFiles(extractionOptions.conversionOptions);
  170. ui->mapView->setScene(controller.scene(0));
  171. ui->mapView->setController(&controller);
  172. ui->mapView->setOptimizationFlags(QGraphicsView::DontSavePainterState | QGraphicsView::DontAdjustForAntialiasing);
  173. connect(ui->mapView, &MapView::openObjectProperties, this, &MainWindow::loadInspector);
  174. ui->minimapView->setScene(controller.miniScene(0));
  175. ui->minimapView->setController(&controller);
  176. connect(ui->minimapView, &MinimapView::cameraPositionChanged, ui->mapView, &MapView::cameraChanged);
  177. scenePreview = new QGraphicsScene(this);
  178. ui->objectPreview->setScene(scenePreview);
  179. //loading objects
  180. loadObjectsTree();
  181. ui->tabWidget->setCurrentIndex(0);
  182. for(int i = 0; i < PlayerColor::PLAYER_LIMIT.getNum(); ++i)
  183. {
  184. connect(getActionPlayer(PlayerColor(i)), &QAction::toggled, this, [&, i](){switchDefaultPlayer(PlayerColor(i));});
  185. }
  186. connect(getActionPlayer(PlayerColor::NEUTRAL), &QAction::toggled, this, [&](){switchDefaultPlayer(PlayerColor::NEUTRAL);});
  187. onPlayersChanged();
  188. show();
  189. //Load map from command line
  190. if(!mapFilePath.isEmpty())
  191. openMap(mapFilePath);
  192. }
  193. MainWindow::~MainWindow()
  194. {
  195. saveUserSettings(); //save window size etc.
  196. delete ui;
  197. }
  198. bool MainWindow::getAnswerAboutUnsavedChanges()
  199. {
  200. if(unsaved)
  201. {
  202. auto sure = QMessageBox::question(this, "Confirmation", "Unsaved changes will be lost, are you sure?");
  203. if(sure == QMessageBox::No)
  204. {
  205. return false;
  206. }
  207. }
  208. return true;
  209. }
  210. void MainWindow::closeEvent(QCloseEvent *event)
  211. {
  212. if(getAnswerAboutUnsavedChanges())
  213. QMainWindow::closeEvent(event);
  214. else
  215. event->ignore();
  216. }
  217. void MainWindow::setStatusMessage(const QString & status)
  218. {
  219. statusBar()->showMessage(status);
  220. }
  221. void MainWindow::setTitle()
  222. {
  223. QString title = QString("%1%2 - %3 (v%4)").arg(filename, unsaved ? "*" : "", VCMI_EDITOR_NAME, VCMI_EDITOR_VERSION);
  224. setWindowTitle(title);
  225. }
  226. void MainWindow::mapChanged()
  227. {
  228. unsaved = true;
  229. setTitle();
  230. }
  231. void MainWindow::initializeMap(bool isNew)
  232. {
  233. unsaved = isNew;
  234. if(isNew)
  235. filename.clear();
  236. setTitle();
  237. mapLevel = 0;
  238. ui->mapView->setScene(controller.scene(mapLevel));
  239. ui->minimapView->setScene(controller.miniScene(mapLevel));
  240. ui->minimapView->dimensions();
  241. setStatusMessage(QString("Scene objects: %1").arg(ui->mapView->scene()->items().size()));
  242. //enable settings
  243. ui->actionMapSettings->setEnabled(true);
  244. ui->actionPlayers_settings->setEnabled(true);
  245. //set minimal players count
  246. if(isNew)
  247. {
  248. controller.map()->players[0].canComputerPlay = true;
  249. controller.map()->players[0].canHumanPlay = true;
  250. }
  251. onPlayersChanged();
  252. }
  253. bool MainWindow::openMap(const QString & filenameSelect)
  254. {
  255. QFileInfo fi(filenameSelect);
  256. std::string fname = fi.fileName().toStdString();
  257. std::string fdir = fi.dir().path().toStdString();
  258. ResourceID resId("MAPEDITOR/" + fname, EResType::MAP);
  259. //addFilesystem takes care about memory deallocation if case of failure, no memory leak here
  260. auto * mapEditorFilesystem = new CFilesystemLoader("MAPEDITOR/", fdir, 0);
  261. CResourceHandler::removeFilesystem("local", "mapEditor");
  262. CResourceHandler::addFilesystem("local", "mapEditor", mapEditorFilesystem);
  263. if(!CResourceHandler::get("mapEditor")->existsResource(resId))
  264. {
  265. QMessageBox::warning(this, "Failed to open map", "Cannot open map from this folder");
  266. return false;
  267. }
  268. CMapService mapService;
  269. try
  270. {
  271. if(auto header = mapService.loadMapHeader(resId))
  272. {
  273. auto missingMods = CMapService::verifyMapHeaderMods(*header);
  274. CModHandler::Incompatibility::ModList modList;
  275. for(const auto & m : missingMods)
  276. modList.push_back({m.first, m.second.toString()});
  277. if(!modList.empty())
  278. throw CModHandler::Incompatibility(std::move(modList));
  279. controller.setMap(mapService.loadMap(resId));
  280. }
  281. }
  282. catch(const CModHandler::Incompatibility & e)
  283. {
  284. QMessageBox::warning(this, "Mods requiered", e.what());
  285. return false;
  286. }
  287. catch(const std::exception & e)
  288. {
  289. QMessageBox::critical(this, "Failed to open map", e.what());
  290. return false;
  291. }
  292. filename = filenameSelect;
  293. initializeMap(controller.map()->version != EMapFormat::VCMI);
  294. return true;
  295. }
  296. void MainWindow::on_actionOpen_triggered()
  297. {
  298. if(!getAnswerAboutUnsavedChanges())
  299. return;
  300. auto filenameSelect = QFileDialog::getOpenFileName(this, tr("Open map"),
  301. QString::fromStdString(VCMIDirs::get().userCachePath().make_preferred().string()),
  302. tr("All supported maps (*.vmap *.h3m);;VCMI maps(*.vmap);;HoMM3 maps(*.h3m)"));
  303. if(filenameSelect.isEmpty())
  304. return;
  305. openMap(filenameSelect);
  306. }
  307. void MainWindow::saveMap()
  308. {
  309. if(!controller.map())
  310. return;
  311. if(!unsaved)
  312. return;
  313. //validate map
  314. auto issues = Validator::validate(controller.map());
  315. bool critical = false;
  316. for(auto & issue : issues)
  317. critical |= issue.critical;
  318. if(!issues.empty())
  319. {
  320. if(critical)
  321. QMessageBox::warning(this, "Map validation", "Map has critical problems and most probably will not be playable. Open Validator from the Map menu to see issues found");
  322. else
  323. QMessageBox::information(this, "Map validation", "Map has some errors. Open Validator from the Map menu to see issues found");
  324. }
  325. CMapService mapService;
  326. try
  327. {
  328. mapService.saveMap(controller.getMapUniquePtr(), filename.toStdString());
  329. }
  330. catch(const std::exception & e)
  331. {
  332. QMessageBox::critical(this, "Failed to save map", e.what());
  333. return;
  334. }
  335. unsaved = false;
  336. setTitle();
  337. }
  338. void MainWindow::on_actionSave_as_triggered()
  339. {
  340. if(!controller.map())
  341. return;
  342. auto filenameSelect = QFileDialog::getSaveFileName(this, tr("Save map"), "", tr("VCMI maps (*.vmap)"));
  343. if(filenameSelect.isNull())
  344. return;
  345. if(filenameSelect == filename)
  346. return;
  347. filename = filenameSelect;
  348. saveMap();
  349. }
  350. void MainWindow::on_actionNew_triggered()
  351. {
  352. if(getAnswerAboutUnsavedChanges())
  353. new WindowNewMap(this);
  354. }
  355. void MainWindow::on_actionSave_triggered()
  356. {
  357. if(!controller.map())
  358. return;
  359. if(filename.isNull())
  360. {
  361. auto filenameSelect = QFileDialog::getSaveFileName(this, tr("Save map"), "", tr("VCMI maps (*.vmap)"));
  362. if(filenameSelect.isNull())
  363. return;
  364. filename = filenameSelect;
  365. }
  366. saveMap();
  367. }
  368. void MainWindow::terrainButtonClicked(TerrainId terrain)
  369. {
  370. controller.commitTerrainChange(mapLevel, terrain);
  371. }
  372. void MainWindow::roadOrRiverButtonClicked(ui8 type, bool isRoad)
  373. {
  374. controller.commitRoadOrRiverChange(mapLevel, type, isRoad);
  375. }
  376. void MainWindow::addGroupIntoCatalog(const std::string & groupName, bool staticOnly)
  377. {
  378. auto knownObjects = VLC->objtypeh->knownObjects();
  379. for(auto ID : knownObjects)
  380. {
  381. if(catalog.count(ID))
  382. continue;
  383. addGroupIntoCatalog(groupName, true, staticOnly, ID);
  384. }
  385. }
  386. void MainWindow::addGroupIntoCatalog(const std::string & groupName, bool useCustomName, bool staticOnly, int ID)
  387. {
  388. QStandardItem * itemGroup = nullptr;
  389. auto itms = objectsModel.findItems(QString::fromStdString(groupName));
  390. if(itms.empty())
  391. {
  392. itemGroup = new QStandardItem(QString::fromStdString(groupName));
  393. objectsModel.appendRow(itemGroup);
  394. }
  395. else
  396. {
  397. itemGroup = itms.front();
  398. }
  399. if (VLC->objtypeh->knownObjects().count(ID) == 0)
  400. return;
  401. auto knownSubObjects = VLC->objtypeh->knownSubObjects(ID);
  402. for(auto secondaryID : knownSubObjects)
  403. {
  404. auto factory = VLC->objtypeh->getHandlerFor(ID, secondaryID);
  405. auto templates = factory->getTemplates();
  406. bool singleTemplate = templates.size() == 1;
  407. if(staticOnly && !factory->isStaticObject())
  408. continue;
  409. auto subGroupName = QString::fromStdString(VLC->objtypeh->getObjectName(ID, secondaryID));
  410. auto * itemType = new QStandardItem(subGroupName);
  411. for(int templateId = 0; templateId < templates.size(); ++templateId)
  412. {
  413. auto templ = templates[templateId];
  414. //selecting file
  415. const std::string & afile = templ->editorAnimationFile.empty() ? templ->animationFile : templ->editorAnimationFile;
  416. //creating picture
  417. QPixmap preview(128, 128);
  418. preview.fill(QColor(255, 255, 255));
  419. QPainter painter(&preview);
  420. Animation animation(afile);
  421. animation.preload();
  422. auto picture = animation.getImage(0);
  423. if(picture && picture->width() && picture->height())
  424. {
  425. qreal xscale = qreal(128) / qreal(picture->width()), yscale = qreal(128) / qreal(picture->height());
  426. qreal scale = std::min(xscale, yscale);
  427. painter.scale(scale, scale);
  428. painter.drawImage(QPoint(0, 0), *picture);
  429. }
  430. //add parameters
  431. QJsonObject data{{"id", QJsonValue(ID)},
  432. {"subid", QJsonValue(secondaryID)},
  433. {"template", QJsonValue(templateId)},
  434. {"animationEditor", QString::fromStdString(templ->editorAnimationFile)},
  435. {"animation", QString::fromStdString(templ->animationFile)},
  436. {"preview", jsonFromPixmap(preview)}};
  437. //create object to extract name
  438. std::unique_ptr<CGObjectInstance> temporaryObj(factory->create(templ));
  439. QString translated = useCustomName ? tr(temporaryObj->getObjectName().c_str()) : subGroupName;
  440. itemType->setText(translated);
  441. //do not have extra level
  442. if(singleTemplate)
  443. {
  444. itemType->setIcon(QIcon(preview));
  445. itemType->setData(data);
  446. }
  447. else
  448. {
  449. auto * item = new QStandardItem(QIcon(preview), QString::fromStdString(templ->stringID));
  450. item->setData(data);
  451. itemType->appendRow(item);
  452. }
  453. }
  454. itemGroup->appendRow(itemType);
  455. catalog.insert(ID);
  456. }
  457. }
  458. void MainWindow::loadObjectsTree()
  459. {
  460. try
  461. {
  462. ui->terrainFilterCombo->addItem("");
  463. //adding terrains
  464. for(auto & terrain : VLC->terrainTypeHandler->objects)
  465. {
  466. QPushButton *b = new QPushButton(QString::fromStdString(terrain->getNameTranslated()));
  467. ui->terrainLayout->addWidget(b);
  468. connect(b, &QPushButton::clicked, this, [this, terrain]{ terrainButtonClicked(terrain->getId()); });
  469. //filter
  470. QString displayName = QString::fromStdString(terrain->getNameTranslated());
  471. QString uniqueName = QString::fromStdString(terrain->getJsonKey());
  472. ui->terrainFilterCombo->addItem(displayName, QVariant(uniqueName));
  473. }
  474. //add spacer to keep terrain button on the top
  475. ui->terrainLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
  476. //adding roads
  477. for(auto & road : VLC->roadTypeHandler->objects)
  478. {
  479. QPushButton *b = new QPushButton(QString::fromStdString(road->getNameTranslated()));
  480. ui->roadLayout->addWidget(b);
  481. connect(b, &QPushButton::clicked, this, [this, road]{ roadOrRiverButtonClicked(road->getIndex(), true); });
  482. }
  483. //add spacer to keep terrain button on the top
  484. ui->roadLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
  485. //adding rivers
  486. for(auto & river : VLC->riverTypeHandler->objects)
  487. {
  488. QPushButton *b = new QPushButton(QString::fromStdString(river->getNameTranslated()));
  489. ui->riverLayout->addWidget(b);
  490. connect(b, &QPushButton::clicked, this, [this, river]{ roadOrRiverButtonClicked(river->getIndex(), false); });
  491. }
  492. //add spacer to keep terrain button on the top
  493. ui->riverLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
  494. if(objectBrowser)
  495. throw std::runtime_error("object browser exists");
  496. //model
  497. objectsModel.setHorizontalHeaderLabels(QStringList() << tr("Type"));
  498. objectBrowser = new ObjectBrowserProxyModel(this);
  499. objectBrowser->setSourceModel(&objectsModel);
  500. objectBrowser->setDynamicSortFilter(false);
  501. objectBrowser->setRecursiveFilteringEnabled(true);
  502. ui->treeView->setModel(objectBrowser);
  503. ui->treeView->setSelectionBehavior(QAbstractItemView::SelectItems);
  504. ui->treeView->setSelectionMode(QAbstractItemView::SingleSelection);
  505. connect(ui->treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(treeViewSelected(const QModelIndex &, const QModelIndex &)));
  506. //adding objects
  507. addGroupIntoCatalog("TOWNS", false, false, Obj::TOWN);
  508. addGroupIntoCatalog("TOWNS", false, false, Obj::RANDOM_TOWN);
  509. addGroupIntoCatalog("TOWNS", true, false, Obj::SHIPYARD);
  510. addGroupIntoCatalog("TOWNS", true, false, Obj::GARRISON);
  511. addGroupIntoCatalog("TOWNS", true, false, Obj::GARRISON2);
  512. addGroupIntoCatalog("OBJECTS", true, false, Obj::ARENA);
  513. addGroupIntoCatalog("OBJECTS", true, false, Obj::BUOY);
  514. addGroupIntoCatalog("OBJECTS", true, false, Obj::CARTOGRAPHER);
  515. addGroupIntoCatalog("OBJECTS", true, false, Obj::SWAN_POND);
  516. addGroupIntoCatalog("OBJECTS", true, false, Obj::COVER_OF_DARKNESS);
  517. addGroupIntoCatalog("OBJECTS", true, false, Obj::CORPSE);
  518. addGroupIntoCatalog("OBJECTS", true, false, Obj::FAERIE_RING);
  519. addGroupIntoCatalog("OBJECTS", true, false, Obj::FOUNTAIN_OF_FORTUNE);
  520. addGroupIntoCatalog("OBJECTS", true, false, Obj::FOUNTAIN_OF_YOUTH);
  521. addGroupIntoCatalog("OBJECTS", true, false, Obj::GARDEN_OF_REVELATION);
  522. addGroupIntoCatalog("OBJECTS", true, false, Obj::HILL_FORT);
  523. addGroupIntoCatalog("OBJECTS", true, false, Obj::IDOL_OF_FORTUNE);
  524. addGroupIntoCatalog("OBJECTS", true, false, Obj::LIBRARY_OF_ENLIGHTENMENT);
  525. addGroupIntoCatalog("OBJECTS", true, false, Obj::LIGHTHOUSE);
  526. addGroupIntoCatalog("OBJECTS", true, false, Obj::SCHOOL_OF_MAGIC);
  527. addGroupIntoCatalog("OBJECTS", true, false, Obj::MAGIC_SPRING);
  528. addGroupIntoCatalog("OBJECTS", true, false, Obj::MAGIC_WELL);
  529. addGroupIntoCatalog("OBJECTS", true, false, Obj::MERCENARY_CAMP);
  530. addGroupIntoCatalog("OBJECTS", true, false, Obj::MERMAID);
  531. addGroupIntoCatalog("OBJECTS", true, false, Obj::MYSTICAL_GARDEN);
  532. addGroupIntoCatalog("OBJECTS", true, false, Obj::OASIS);
  533. addGroupIntoCatalog("OBJECTS", true, false, Obj::LEAN_TO);
  534. addGroupIntoCatalog("OBJECTS", true, false, Obj::OBELISK);
  535. addGroupIntoCatalog("OBJECTS", true, false, Obj::REDWOOD_OBSERVATORY);
  536. addGroupIntoCatalog("OBJECTS", true, false, Obj::PILLAR_OF_FIRE);
  537. addGroupIntoCatalog("OBJECTS", true, false, Obj::STAR_AXIS);
  538. addGroupIntoCatalog("OBJECTS", true, false, Obj::RALLY_FLAG);
  539. addGroupIntoCatalog("OBJECTS", true, false, Obj::WATERING_HOLE);
  540. addGroupIntoCatalog("OBJECTS", true, false, Obj::SCHOLAR);
  541. addGroupIntoCatalog("OBJECTS", true, false, Obj::SHRINE_OF_MAGIC_INCANTATION);
  542. addGroupIntoCatalog("OBJECTS", true, false, Obj::SHRINE_OF_MAGIC_GESTURE);
  543. addGroupIntoCatalog("OBJECTS", true, false, Obj::SHRINE_OF_MAGIC_THOUGHT);
  544. addGroupIntoCatalog("OBJECTS", true, false, Obj::SIRENS);
  545. addGroupIntoCatalog("OBJECTS", true, false, Obj::STABLES);
  546. addGroupIntoCatalog("OBJECTS", true, false, Obj::TAVERN);
  547. addGroupIntoCatalog("OBJECTS", true, false, Obj::TEMPLE);
  548. addGroupIntoCatalog("OBJECTS", true, false, Obj::DEN_OF_THIEVES);
  549. addGroupIntoCatalog("OBJECTS", true, false, Obj::LEARNING_STONE);
  550. addGroupIntoCatalog("OBJECTS", true, false, Obj::TREE_OF_KNOWLEDGE);
  551. addGroupIntoCatalog("OBJECTS", true, false, Obj::WAGON);
  552. addGroupIntoCatalog("OBJECTS", true, false, Obj::SCHOOL_OF_WAR);
  553. addGroupIntoCatalog("OBJECTS", true, false, Obj::WAR_MACHINE_FACTORY);
  554. addGroupIntoCatalog("OBJECTS", true, false, Obj::WARRIORS_TOMB);
  555. addGroupIntoCatalog("OBJECTS", true, false, Obj::WITCH_HUT);
  556. addGroupIntoCatalog("OBJECTS", true, false, Obj::SANCTUARY);
  557. addGroupIntoCatalog("OBJECTS", true, false, Obj::MARLETTO_TOWER);
  558. addGroupIntoCatalog("HEROES", true, false, Obj::PRISON);
  559. addGroupIntoCatalog("HEROES", false, false, Obj::HERO);
  560. addGroupIntoCatalog("HEROES", false, false, Obj::RANDOM_HERO);
  561. addGroupIntoCatalog("HEROES", false, false, Obj::HERO_PLACEHOLDER);
  562. addGroupIntoCatalog("HEROES", false, false, Obj::BOAT);
  563. addGroupIntoCatalog("ARTIFACTS", true, false, Obj::ARTIFACT);
  564. addGroupIntoCatalog("ARTIFACTS", false, false, Obj::RANDOM_ART);
  565. addGroupIntoCatalog("ARTIFACTS", false, false, Obj::RANDOM_TREASURE_ART);
  566. addGroupIntoCatalog("ARTIFACTS", false, false, Obj::RANDOM_MINOR_ART);
  567. addGroupIntoCatalog("ARTIFACTS", false, false, Obj::RANDOM_MAJOR_ART);
  568. addGroupIntoCatalog("ARTIFACTS", false, false, Obj::RANDOM_RELIC_ART);
  569. addGroupIntoCatalog("ARTIFACTS", true, false, Obj::SPELL_SCROLL);
  570. addGroupIntoCatalog("ARTIFACTS", true, false, Obj::PANDORAS_BOX);
  571. addGroupIntoCatalog("RESOURCES", true, false, Obj::RANDOM_RESOURCE);
  572. addGroupIntoCatalog("RESOURCES", false, false, Obj::RESOURCE);
  573. addGroupIntoCatalog("RESOURCES", true, false, Obj::SEA_CHEST);
  574. addGroupIntoCatalog("RESOURCES", true, false, Obj::TREASURE_CHEST);
  575. addGroupIntoCatalog("RESOURCES", true, false, Obj::CAMPFIRE);
  576. addGroupIntoCatalog("RESOURCES", true, false, Obj::SHIPWRECK_SURVIVOR);
  577. addGroupIntoCatalog("RESOURCES", true, false, Obj::FLOTSAM);
  578. addGroupIntoCatalog("BANKS", true, false, Obj::CREATURE_BANK);
  579. addGroupIntoCatalog("BANKS", true, false, Obj::DRAGON_UTOPIA);
  580. addGroupIntoCatalog("BANKS", true, false, Obj::CRYPT);
  581. addGroupIntoCatalog("BANKS", true, false, Obj::DERELICT_SHIP);
  582. addGroupIntoCatalog("BANKS", true, false, Obj::PYRAMID);
  583. addGroupIntoCatalog("BANKS", true, false, Obj::SHIPWRECK);
  584. addGroupIntoCatalog("DWELLINGS", true, false, Obj::CREATURE_GENERATOR1);
  585. addGroupIntoCatalog("DWELLINGS", true, false, Obj::CREATURE_GENERATOR2);
  586. addGroupIntoCatalog("DWELLINGS", true, false, Obj::CREATURE_GENERATOR3);
  587. addGroupIntoCatalog("DWELLINGS", true, false, Obj::CREATURE_GENERATOR4);
  588. addGroupIntoCatalog("DWELLINGS", true, false, Obj::REFUGEE_CAMP);
  589. addGroupIntoCatalog("DWELLINGS", false, false, Obj::RANDOM_DWELLING);
  590. addGroupIntoCatalog("DWELLINGS", false, false, Obj::RANDOM_DWELLING_LVL);
  591. addGroupIntoCatalog("DWELLINGS", false, false, Obj::RANDOM_DWELLING_FACTION);
  592. addGroupIntoCatalog("GROUNDS", true, false, Obj::CURSED_GROUND1);
  593. addGroupIntoCatalog("GROUNDS", true, false, Obj::MAGIC_PLAINS1);
  594. addGroupIntoCatalog("GROUNDS", true, false, Obj::CLOVER_FIELD);
  595. addGroupIntoCatalog("GROUNDS", true, false, Obj::CURSED_GROUND2);
  596. addGroupIntoCatalog("GROUNDS", true, false, Obj::EVIL_FOG);
  597. addGroupIntoCatalog("GROUNDS", true, false, Obj::FAVORABLE_WINDS);
  598. addGroupIntoCatalog("GROUNDS", true, false, Obj::FIERY_FIELDS);
  599. addGroupIntoCatalog("GROUNDS", true, false, Obj::HOLY_GROUNDS);
  600. addGroupIntoCatalog("GROUNDS", true, false, Obj::LUCID_POOLS);
  601. addGroupIntoCatalog("GROUNDS", true, false, Obj::MAGIC_CLOUDS);
  602. addGroupIntoCatalog("GROUNDS", true, false, Obj::MAGIC_PLAINS2);
  603. addGroupIntoCatalog("GROUNDS", true, false, Obj::ROCKLANDS);
  604. addGroupIntoCatalog("GROUNDS", true, false, Obj::HOLE);
  605. addGroupIntoCatalog("TELEPORTS", true, false, Obj::MONOLITH_ONE_WAY_ENTRANCE);
  606. addGroupIntoCatalog("TELEPORTS", true, false, Obj::MONOLITH_ONE_WAY_EXIT);
  607. addGroupIntoCatalog("TELEPORTS", true, false, Obj::MONOLITH_TWO_WAY);
  608. addGroupIntoCatalog("TELEPORTS", true, false, Obj::SUBTERRANEAN_GATE);
  609. addGroupIntoCatalog("TELEPORTS", true, false, Obj::WHIRLPOOL);
  610. addGroupIntoCatalog("MINES", true, false, Obj::MINE);
  611. addGroupIntoCatalog("MINES", false, false, Obj::ABANDONED_MINE);
  612. addGroupIntoCatalog("MINES", true, false, Obj::WINDMILL);
  613. addGroupIntoCatalog("MINES", true, false, Obj::WATER_WHEEL);
  614. addGroupIntoCatalog("TRIGGERS", true, false, Obj::EVENT);
  615. addGroupIntoCatalog("TRIGGERS", true, false, Obj::GRAIL);
  616. addGroupIntoCatalog("TRIGGERS", true, false, Obj::SIGN);
  617. addGroupIntoCatalog("TRIGGERS", true, false, Obj::OCEAN_BOTTLE);
  618. addGroupIntoCatalog("MONSTERS", false, false, Obj::MONSTER);
  619. addGroupIntoCatalog("MONSTERS", true, false, Obj::RANDOM_MONSTER);
  620. addGroupIntoCatalog("MONSTERS", true, false, Obj::RANDOM_MONSTER_L1);
  621. addGroupIntoCatalog("MONSTERS", true, false, Obj::RANDOM_MONSTER_L2);
  622. addGroupIntoCatalog("MONSTERS", true, false, Obj::RANDOM_MONSTER_L3);
  623. addGroupIntoCatalog("MONSTERS", true, false, Obj::RANDOM_MONSTER_L4);
  624. addGroupIntoCatalog("MONSTERS", true, false, Obj::RANDOM_MONSTER_L5);
  625. addGroupIntoCatalog("MONSTERS", true, false, Obj::RANDOM_MONSTER_L6);
  626. addGroupIntoCatalog("MONSTERS", true, false, Obj::RANDOM_MONSTER_L7);
  627. addGroupIntoCatalog("QUESTS", true, false, Obj::SEER_HUT);
  628. addGroupIntoCatalog("QUESTS", true, false, Obj::BORDER_GATE);
  629. addGroupIntoCatalog("QUESTS", true, false, Obj::QUEST_GUARD);
  630. addGroupIntoCatalog("QUESTS", true, false, Obj::HUT_OF_MAGI);
  631. addGroupIntoCatalog("QUESTS", true, false, Obj::EYE_OF_MAGI);
  632. addGroupIntoCatalog("QUESTS", true, false, Obj::BORDERGUARD);
  633. addGroupIntoCatalog("QUESTS", true, false, Obj::KEYMASTER);
  634. addGroupIntoCatalog("wog object", true, false, Obj::WOG_OBJECT);
  635. addGroupIntoCatalog("OBSTACLES", true);
  636. addGroupIntoCatalog("OTHER", false);
  637. }
  638. catch(const std::exception &)
  639. {
  640. QMessageBox::critical(this, "Mods loading problem", "Critical error during Mods loading. Disable invalid mods and restart.");
  641. }
  642. }
  643. void MainWindow::on_actionLevel_triggered()
  644. {
  645. if(controller.map() && controller.map()->twoLevel)
  646. {
  647. mapLevel = mapLevel ? 0 : 1;
  648. ui->mapView->setScene(controller.scene(mapLevel));
  649. ui->minimapView->setScene(controller.miniScene(mapLevel));
  650. if (mapLevel == 0)
  651. {
  652. ui->actionLevel->setToolTip(tr("View underground"));
  653. }
  654. else
  655. {
  656. ui->actionLevel->setToolTip(tr("View surface"));
  657. }
  658. }
  659. }
  660. void MainWindow::on_actionUndo_triggered()
  661. {
  662. QString str("Undo clicked");
  663. statusBar()->showMessage(str, 1000);
  664. if (controller.map())
  665. {
  666. controller.undo();
  667. }
  668. }
  669. void MainWindow::on_actionRedo_triggered()
  670. {
  671. QString str("Redo clicked");
  672. displayStatus(str);
  673. if (controller.map())
  674. {
  675. controller.redo();
  676. }
  677. }
  678. void MainWindow::on_actionPass_triggered(bool checked)
  679. {
  680. QString str("Passability clicked");
  681. displayStatus(str);
  682. if(controller.map())
  683. {
  684. controller.scene(0)->passabilityView.show(checked);
  685. controller.scene(1)->passabilityView.show(checked);
  686. }
  687. }
  688. void MainWindow::on_actionGrid_triggered(bool checked)
  689. {
  690. QString str("Grid clicked");
  691. displayStatus(str);
  692. if(controller.map())
  693. {
  694. controller.scene(0)->gridView.show(checked);
  695. controller.scene(1)->gridView.show(checked);
  696. }
  697. }
  698. void MainWindow::changeBrushState(int idx)
  699. {
  700. }
  701. void MainWindow::on_toolBrush_clicked(bool checked)
  702. {
  703. //ui->toolBrush->setChecked(false);
  704. ui->toolBrush2->setChecked(false);
  705. ui->toolBrush4->setChecked(false);
  706. ui->toolArea->setChecked(false);
  707. ui->toolLasso->setChecked(false);
  708. if(checked)
  709. ui->mapView->selectionTool = MapView::SelectionTool::Brush;
  710. else
  711. ui->mapView->selectionTool = MapView::SelectionTool::None;
  712. ui->tabWidget->setCurrentIndex(0);
  713. }
  714. void MainWindow::on_toolBrush2_clicked(bool checked)
  715. {
  716. ui->toolBrush->setChecked(false);
  717. //ui->toolBrush2->setChecked(false);
  718. ui->toolBrush4->setChecked(false);
  719. ui->toolArea->setChecked(false);
  720. ui->toolLasso->setChecked(false);
  721. if(checked)
  722. ui->mapView->selectionTool = MapView::SelectionTool::Brush2;
  723. else
  724. ui->mapView->selectionTool = MapView::SelectionTool::None;
  725. ui->tabWidget->setCurrentIndex(0);
  726. }
  727. void MainWindow::on_toolBrush4_clicked(bool checked)
  728. {
  729. ui->toolBrush->setChecked(false);
  730. ui->toolBrush2->setChecked(false);
  731. //ui->toolBrush4->setChecked(false);
  732. ui->toolArea->setChecked(false);
  733. ui->toolLasso->setChecked(false);
  734. if(checked)
  735. ui->mapView->selectionTool = MapView::SelectionTool::Brush4;
  736. else
  737. ui->mapView->selectionTool = MapView::SelectionTool::None;
  738. ui->tabWidget->setCurrentIndex(0);
  739. }
  740. void MainWindow::on_toolArea_clicked(bool checked)
  741. {
  742. ui->toolBrush->setChecked(false);
  743. ui->toolBrush2->setChecked(false);
  744. ui->toolBrush4->setChecked(false);
  745. //ui->toolArea->setChecked(false);
  746. ui->toolLasso->setChecked(false);
  747. if(checked)
  748. ui->mapView->selectionTool = MapView::SelectionTool::Area;
  749. else
  750. ui->mapView->selectionTool = MapView::SelectionTool::None;
  751. ui->tabWidget->setCurrentIndex(0);
  752. }
  753. void MainWindow::on_toolLasso_clicked(bool checked)
  754. {
  755. ui->toolBrush->setChecked(false);
  756. ui->toolBrush2->setChecked(false);
  757. ui->toolBrush4->setChecked(false);
  758. ui->toolArea->setChecked(false);
  759. //ui->toolLasso->setChecked(false);
  760. if(checked)
  761. ui->mapView->selectionTool = MapView::SelectionTool::Lasso;
  762. else
  763. ui->mapView->selectionTool = MapView::SelectionTool::None;
  764. ui->tabWidget->setCurrentIndex(0);
  765. }
  766. void MainWindow::on_actionErase_triggered()
  767. {
  768. on_toolErase_clicked();
  769. }
  770. void MainWindow::on_toolErase_clicked()
  771. {
  772. if(controller.map())
  773. {
  774. controller.commitObjectErase(mapLevel);
  775. }
  776. ui->tabWidget->setCurrentIndex(0);
  777. }
  778. void MainWindow::preparePreview(const QModelIndex &index)
  779. {
  780. scenePreview->clear();
  781. auto data = objectsModel.itemFromIndex(objectBrowser->mapToSource(index))->data().toJsonObject();
  782. if(!data.empty())
  783. {
  784. auto preview = data["preview"];
  785. if(preview != QJsonValue::Undefined)
  786. {
  787. QPixmap objPreview = pixmapFromJson(preview);
  788. scenePreview->addPixmap(objPreview);
  789. }
  790. }
  791. }
  792. void MainWindow::treeViewSelected(const QModelIndex & index, const QModelIndex & deselected)
  793. {
  794. ui->toolBrush->setChecked(false);
  795. ui->toolBrush2->setChecked(false);
  796. ui->toolBrush4->setChecked(false);
  797. ui->toolArea->setChecked(false);
  798. ui->toolLasso->setChecked(false);
  799. ui->mapView->selectionTool = MapView::SelectionTool::None;
  800. preparePreview(index);
  801. }
  802. void MainWindow::on_terrainFilterCombo_currentIndexChanged(int index)
  803. {
  804. if(!objectBrowser)
  805. return;
  806. QString uniqueName = ui->terrainFilterCombo->itemData(index).toString();
  807. objectBrowser->terrain = TerrainId(ETerrainId::ANY_TERRAIN);
  808. if (!uniqueName.isEmpty())
  809. {
  810. for (auto const & terrain : VLC->terrainTypeHandler->objects)
  811. if (terrain->getJsonKey() == uniqueName.toStdString())
  812. objectBrowser->terrain = terrain->getId();
  813. }
  814. objectBrowser->invalidate();
  815. objectBrowser->sort(0);
  816. }
  817. void MainWindow::on_filter_textChanged(const QString &arg1)
  818. {
  819. if(!objectBrowser)
  820. return;
  821. objectBrowser->filter = arg1;
  822. objectBrowser->invalidate();
  823. objectBrowser->sort(0);
  824. }
  825. void MainWindow::on_actionFill_triggered()
  826. {
  827. QString str("Fill clicked");
  828. displayStatus(str);
  829. if(!controller.map())
  830. return;
  831. controller.commitObstacleFill(mapLevel);
  832. }
  833. void MainWindow::loadInspector(CGObjectInstance * obj, bool switchTab)
  834. {
  835. if(switchTab)
  836. ui->tabWidget->setCurrentIndex(1);
  837. Inspector inspector(controller.map(), obj, ui->inspectorWidget);
  838. inspector.updateProperties();
  839. }
  840. void MainWindow::on_inspectorWidget_itemChanged(QTableWidgetItem *item)
  841. {
  842. if(!item->isSelected())
  843. return;
  844. int r = item->row();
  845. int c = item->column();
  846. if(c < 1)
  847. return;
  848. auto * tableWidget = item->tableWidget();
  849. //get identifier
  850. auto identifier = tableWidget->item(0, 1)->text();
  851. CGObjectInstance * obj = data_cast<CGObjectInstance>(identifier.toLongLong());
  852. //get parameter name
  853. auto param = tableWidget->item(r, c - 1)->text();
  854. //set parameter
  855. Inspector inspector(controller.map(), obj, tableWidget);
  856. inspector.setProperty(param, item->text());
  857. controller.commitObjectChange(mapLevel);
  858. }
  859. void MainWindow::on_actionMapSettings_triggered()
  860. {
  861. auto settingsDialog = new MapSettings(controller, this);
  862. settingsDialog->setWindowModality(Qt::WindowModal);
  863. settingsDialog->setModal(true);
  864. }
  865. void MainWindow::on_actionPlayers_settings_triggered()
  866. {
  867. auto settingsDialog = new PlayerSettings(controller, this);
  868. settingsDialog->setWindowModality(Qt::WindowModal);
  869. settingsDialog->setModal(true);
  870. connect(settingsDialog, &QDialog::finished, this, &MainWindow::onPlayersChanged);
  871. }
  872. QAction * MainWindow::getActionPlayer(const PlayerColor & player)
  873. {
  874. if(player.getNum() == 0) return ui->actionPlayer_1;
  875. if(player.getNum() == 1) return ui->actionPlayer_2;
  876. if(player.getNum() == 2) return ui->actionPlayer_3;
  877. if(player.getNum() == 3) return ui->actionPlayer_4;
  878. if(player.getNum() == 4) return ui->actionPlayer_5;
  879. if(player.getNum() == 5) return ui->actionPlayer_6;
  880. if(player.getNum() == 6) return ui->actionPlayer_7;
  881. if(player.getNum() == 7) return ui->actionPlayer_8;
  882. return ui->actionNeutral;
  883. }
  884. void MainWindow::switchDefaultPlayer(const PlayerColor & player)
  885. {
  886. if(controller.defaultPlayer == player)
  887. return;
  888. ui->actionNeutral->blockSignals(true);
  889. ui->actionNeutral->setChecked(PlayerColor::NEUTRAL == player);
  890. ui->actionNeutral->blockSignals(false);
  891. for(int i = 0; i < PlayerColor::PLAYER_LIMIT.getNum(); ++i)
  892. {
  893. getActionPlayer(PlayerColor(i))->blockSignals(true);
  894. getActionPlayer(PlayerColor(i))->setChecked(PlayerColor(i) == player);
  895. getActionPlayer(PlayerColor(i))->blockSignals(false);
  896. }
  897. controller.defaultPlayer = player;
  898. }
  899. void MainWindow::onPlayersChanged()
  900. {
  901. if(controller.map())
  902. {
  903. getActionPlayer(PlayerColor::NEUTRAL)->setEnabled(true);
  904. for(int i = 0; i < controller.map()->players.size(); ++i)
  905. getActionPlayer(PlayerColor(i))->setEnabled(controller.map()->players.at(i).canAnyonePlay());
  906. if(!getActionPlayer(controller.defaultPlayer)->isEnabled() || controller.defaultPlayer == PlayerColor::NEUTRAL)
  907. switchDefaultPlayer(PlayerColor::NEUTRAL);
  908. }
  909. else
  910. {
  911. for(int i = 0; i < PlayerColor::PLAYER_LIMIT.getNum(); ++i)
  912. getActionPlayer(PlayerColor(i))->setEnabled(false);
  913. getActionPlayer(PlayerColor::NEUTRAL)->setEnabled(false);
  914. }
  915. }
  916. void MainWindow::enableUndo(bool enable)
  917. {
  918. ui->actionUndo->setEnabled(enable);
  919. }
  920. void MainWindow::enableRedo(bool enable)
  921. {
  922. ui->actionRedo->setEnabled(enable);
  923. }
  924. void MainWindow::onSelectionMade(int level, bool anythingSelected)
  925. {
  926. if (level == mapLevel)
  927. {
  928. auto info = QString::asprintf("Selection on layer %d: %s", level, anythingSelected ? "true" : "false");
  929. setStatusMessage(info);
  930. ui->actionErase->setEnabled(anythingSelected);
  931. ui->toolErase->setEnabled(anythingSelected);
  932. }
  933. }
  934. void MainWindow::displayStatus(const QString& message, int timeout /* = 2000 */)
  935. {
  936. statusBar()->showMessage(message, timeout);
  937. }
  938. void MainWindow::on_actionValidate_triggered()
  939. {
  940. new Validator(controller.map(), this);
  941. }
  942. void MainWindow::on_actionUpdate_appearance_triggered()
  943. {
  944. if(!controller.map())
  945. return;
  946. if(controller.scene(mapLevel)->selectionObjectsView.getSelection().empty())
  947. {
  948. QMessageBox::information(this, "Update appearance", "No objects selected");
  949. return;
  950. }
  951. if(QMessageBox::Yes != QMessageBox::question(this, "Update appearance", "This operation is irreversible. Do you want to continue?"))
  952. return;
  953. controller.scene(mapLevel)->selectionTerrainView.clear();
  954. int errors = 0;
  955. std::set<CGObjectInstance*> staticObjects;
  956. for(auto * obj : controller.scene(mapLevel)->selectionObjectsView.getSelection())
  957. {
  958. auto handler = VLC->objtypeh->getHandlerFor(obj->ID, obj->subID);
  959. if(!controller.map()->isInTheMap(obj->visitablePos()))
  960. {
  961. ++errors;
  962. continue;
  963. }
  964. auto * terrain = controller.map()->getTile(obj->visitablePos()).terType;
  965. if(handler->isStaticObject())
  966. {
  967. staticObjects.insert(obj);
  968. if(obj->appearance->canBePlacedAt(terrain->getId()))
  969. {
  970. controller.scene(mapLevel)->selectionObjectsView.deselectObject(obj);
  971. continue;
  972. }
  973. for(auto & offset : obj->appearance->getBlockedOffsets())
  974. controller.scene(mapLevel)->selectionTerrainView.select(obj->pos + offset);
  975. }
  976. else
  977. {
  978. auto app = handler->getOverride(terrain->getId(), obj);
  979. if(!app)
  980. {
  981. if(obj->appearance->canBePlacedAt(terrain->getId()))
  982. continue;
  983. auto templates = handler->getTemplates(terrain->getId());
  984. if(templates.empty())
  985. {
  986. ++errors;
  987. continue;
  988. }
  989. app = templates.front();
  990. }
  991. auto tiles = controller.mapHandler()->getTilesUnderObject(obj);
  992. obj->appearance = app;
  993. controller.mapHandler()->invalidate(tiles);
  994. controller.mapHandler()->invalidate(obj);
  995. controller.scene(mapLevel)->selectionObjectsView.deselectObject(obj);
  996. }
  997. }
  998. controller.commitObjectChange(mapLevel);
  999. controller.commitObjectErase(mapLevel);
  1000. controller.commitObstacleFill(mapLevel);
  1001. if(errors)
  1002. QMessageBox::warning(this, "Update appearance", QString("Errors occured. %1 objects were not updated").arg(errors));
  1003. }
  1004. void MainWindow::on_actionRecreate_obstacles_triggered()
  1005. {
  1006. }
  1007. void MainWindow::on_actionCut_triggered()
  1008. {
  1009. if(controller.map())
  1010. {
  1011. controller.copyToClipboard(mapLevel);
  1012. controller.commitObjectErase(mapLevel);
  1013. }
  1014. }
  1015. void MainWindow::on_actionCopy_triggered()
  1016. {
  1017. if(controller.map())
  1018. {
  1019. controller.copyToClipboard(mapLevel);
  1020. }
  1021. }
  1022. void MainWindow::on_actionPaste_triggered()
  1023. {
  1024. if(controller.map())
  1025. {
  1026. controller.pasteFromClipboard(mapLevel);
  1027. }
  1028. }
  1029. void MainWindow::on_actionExport_triggered()
  1030. {
  1031. QString fileName = QFileDialog::getSaveFileName(this, "Save to image", QCoreApplication::applicationDirPath(), "BMP (*.bmp);;JPEG (*.jpeg);;PNG (*.png)");
  1032. if(!fileName.isNull())
  1033. {
  1034. QImage image(ui->mapView->scene()->sceneRect().size().toSize(), QImage::Format_RGB888);
  1035. QPainter painter(&image);
  1036. ui->mapView->scene()->render(&painter);
  1037. image.save(fileName);
  1038. }
  1039. }