2022-09-24 22:55:05 +02:00
/*
* mainwindow . 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
*
*/
2022-09-18 01:23:17 +02:00
# include "StdInc.h"
# include "mainwindow.h"
# include "ui_mainwindow.h"
# include <QFileDialog>
# include <QFile>
# include <QMessageBox>
# include <QFileInfo>
# include "../lib/VCMIDirs.h"
# include "../lib/VCMI_Lib.h"
# include "../lib/logging/CBasicLogConfigurator.h"
# include "../lib/CConfigHandler.h"
# include "../lib/filesystem/Filesystem.h"
# include "../lib/GameConstants.h"
2023-06-02 20:47:37 +02:00
# include "../lib/mapObjectConstructors/AObjectTypeHandler.h"
# include "../lib/mapObjectConstructors/CObjectClassesHandler.h"
# include "../lib/mapObjects/ObjectTemplate.h"
2022-09-18 01:23:17 +02:00
# include "../lib/mapping/CMapService.h"
# include "../lib/mapping/CMap.h"
# include "../lib/mapping/CMapEditManager.h"
2023-05-24 00:14:06 +02:00
# include "../lib/mapping/MapFormat.h"
2023-07-30 19:12:25 +02:00
# include "../lib/modding/ModIncompatibility.h"
2023-01-11 15:17:24 +02:00
# include "../lib/RoadHandler.h"
# include "../lib/RiverHandler.h"
2023-01-09 01:17:37 +02:00
# include "../lib/TerrainHandler.h"
2022-09-18 01:23:17 +02:00
# include "../lib/filesystem/CFilesystemLoader.h"
# include "maphandler.h"
# include "graphics.h"
# include "windownewmap.h"
# include "objectbrowser.h"
# include "inspector/inspector.h"
2023-09-05 01:26:38 +02:00
# include "mapsettings/mapsettings.h"
2023-09-28 15:29:43 +02:00
# include "mapsettings/translations.h"
2022-09-18 01:23:17 +02:00
# include "playersettings.h"
# include "validator.h"
static CBasicLogConfigurator * logConfig ;
2022-11-24 22:52:31 +02:00
2022-09-18 01:23:17 +02:00
QJsonValue jsonFromPixmap ( const QPixmap & p )
{
QBuffer buffer ;
buffer . open ( QIODevice : : WriteOnly ) ;
p . save ( & buffer , " PNG " ) ;
auto const encoded = buffer . data ( ) . toBase64 ( ) ;
return { QLatin1String ( encoded ) } ;
}
QPixmap pixmapFromJson ( const QJsonValue & val )
{
auto const encoded = val . toString ( ) . toLatin1 ( ) ;
QPixmap p ;
p . loadFromData ( QByteArray : : fromBase64 ( encoded ) , " PNG " ) ;
return p ;
}
void init ( )
{
loadDLLClasses ( ) ;
2023-09-16 18:34:50 +02:00
Settings config = settings . write [ " session " ] [ " editor " ] ;
config - > Bool ( ) = true ;
2022-09-18 01:23:17 +02:00
logGlobal - > info ( " Initializing VCMI_Lib " ) ;
}
void MainWindow : : loadUserSettings ( )
{
//load window settings
QSettings s ( Ui : : teamName , Ui : : appName ) ;
auto size = s . value ( mainWindowSizeSetting ) . toSize ( ) ;
if ( size . isValid ( ) )
{
resize ( size ) ;
}
auto position = s . value ( mainWindowPositionSetting ) . toPoint ( ) ;
if ( ! position . isNull ( ) )
{
move ( position ) ;
}
2024-05-16 00:01:02 +02:00
lastSavingDir = s . value ( lastDirectorySetting ) . toString ( ) ;
if ( lastSavingDir . isEmpty ( ) )
lastSavingDir = QString : : fromStdString ( VCMIDirs : : get ( ) . userDataPath ( ) . make_preferred ( ) . string ( ) ) ;
2022-09-18 01:23:17 +02:00
}
void MainWindow : : saveUserSettings ( )
{
QSettings s ( Ui : : teamName , Ui : : appName ) ;
s . setValue ( mainWindowSizeSetting , size ( ) ) ;
s . setValue ( mainWindowPositionSetting , pos ( ) ) ;
2024-05-16 00:01:02 +02:00
s . setValue ( lastDirectorySetting , lastSavingDir ) ;
2022-09-18 01:23:17 +02:00
}
2022-11-27 19:14:14 +02:00
void MainWindow : : parseCommandLine ( ExtractionOptions & extractionOptions )
2022-11-20 18:48:31 +02:00
{
QCommandLineParser parser ;
parser . addHelpOption ( ) ;
parser . addPositionalArgument ( " map " , QCoreApplication : : translate ( " main " , " Filepath of the map to open. " ) ) ;
parser . addOptions ( {
{ " e " , QCoreApplication : : translate ( " main " , " Extract original H3 archives into a separate folder. " ) } ,
{ " s " , QCoreApplication : : translate ( " main " , " From an extracted archive, it Splits TwCrPort, CPRSMALL, FlagPort, ITPA, ITPt, Un32 and Un44 into individual PNG's. " ) } ,
{ " c " , QCoreApplication : : translate ( " main " , " From an extracted archive, Converts single Images (found in Images folder) from .pcx to png. " ) } ,
2023-10-18 01:41:02 +02:00
{ " d " , QCoreApplication : : translate ( " main " , " Delete original files, for the ones split / converted. " ) } ,
2022-11-20 18:48:31 +02:00
} ) ;
parser . process ( qApp - > arguments ( ) ) ;
const QStringList positionalArgs = parser . positionalArguments ( ) ;
2022-11-27 19:02:33 +02:00
if ( ! positionalArgs . isEmpty ( ) )
2022-11-20 18:48:31 +02:00
mapFilePath = positionalArgs . at ( 0 ) ;
2022-11-27 19:02:33 +02:00
extractionOptions = {
parser . isSet ( " e " ) , {
parser . isSet ( " s " ) ,
parser . isSet ( " c " ) ,
parser . isSet ( " d " ) } } ;
2022-11-20 18:48:31 +02:00
}
2022-12-26 23:29:42 +02:00
void MainWindow : : loadTranslation ( )
{
# ifdef ENABLE_QT_TRANSLATIONS
2024-07-18 17:29:46 +02:00
const std : : string translationFile = settings [ " general " ] [ " language " ] . String ( ) + " .qm " ;
QString translationFileResourcePath = QString { " :/translation/%1 " } . arg ( translationFile . c_str ( ) ) ;
2022-12-26 23:29:42 +02:00
2024-07-18 17:29:46 +02:00
logGlobal - > info ( " Loading translation %s " , translationFile ) ;
if ( ! QFile : : exists ( translationFileResourcePath ) )
{
2024-07-18 20:32:17 +02:00
logGlobal - > debug ( " Translation file %s does not exist " , translationFileResourcePath . toStdString ( ) ) ;
2024-07-18 17:29:46 +02:00
return ;
}
if ( ! translator . load ( translationFileResourcePath ) )
2022-12-29 17:45:32 +02:00
{
2024-07-18 17:29:46 +02:00
logGlobal - > error ( " Failed to load translation file %s " , translationFileResourcePath . toStdString ( ) ) ;
return ;
}
if ( translationFile = = " english.qm " )
{
// translator doesn't need to be installed for English
2024-06-05 14:46:42 +02:00
return ;
2022-12-29 17:45:32 +02:00
}
2024-06-05 14:46:42 +02:00
if ( ! qApp - > installTranslator ( & translator ) )
2024-07-18 17:29:46 +02:00
{
logGlobal - > error ( " Failed to install translator for translation file %s " , translationFileResourcePath . toStdString ( ) ) ;
}
2022-12-26 23:29:42 +02:00
# endif
}
2022-11-25 00:21:24 +02:00
MainWindow : : MainWindow ( QWidget * parent ) :
2022-09-18 01:23:17 +02:00
QMainWindow ( parent ) ,
ui ( new Ui : : MainWindow ) ,
controller ( this )
{
2022-12-20 17:11:49 +02:00
// Set current working dir to executable folder.
// This is important on Mac for relative paths to work inside DMG.
QDir : : setCurrent ( QApplication : : applicationDirPath ( ) ) ;
2024-06-05 06:52:17 +02:00
2023-03-05 22:54:52 +02:00
new QShortcut ( QKeySequence ( " Backspace " ) , this , SLOT ( on_actionErase_triggered ( ) ) ) ;
2024-06-05 06:52:17 +02:00
2022-11-27 19:14:14 +02:00
ExtractionOptions extractionOptions ;
parseCommandLine ( extractionOptions ) ;
2022-11-20 18:48:31 +02:00
2022-09-18 01:23:17 +02:00
//configure logging
2022-10-08 20:55:15 +02:00
const boost : : filesystem : : path logPath = VCMIDirs : : get ( ) . userLogsPath ( ) / " VCMI_Editor_log.txt " ;
2022-09-18 01:23:17 +02:00
console = new CConsoleHandler ( ) ;
logConfig = new CBasicLogConfigurator ( logPath , console ) ;
logConfig - > configureDefault ( ) ;
2024-10-26 13:48:12 +02:00
logGlobal - > info ( " Starting map editor of '%s' " , GameConstants : : VCMI_VERSION ) ;
2022-09-18 01:23:17 +02:00
logGlobal - > info ( " The log file will be saved to %s " , logPath ) ;
2022-11-20 18:48:31 +02:00
2022-09-18 01:23:17 +02:00
//init
2024-01-04 23:48:58 +02:00
preinitDLL ( : : console , extractionOptions . extractArchives ) ;
2022-11-25 00:21:24 +02:00
2022-09-18 01:23:17 +02:00
// Initialize logging based on settings
logConfig - > configure ( ) ;
2024-02-12 01:22:16 +02:00
logGlobal - > debug ( " settings = %s " , settings . toJsonNode ( ) . toString ( ) ) ;
2022-11-25 00:21:24 +02:00
2022-09-18 01:23:17 +02:00
// Some basic data validation to produce better error messages in cases of incorrect install
auto testFile = [ ] ( std : : string filename , std : : string message ) - > bool
{
2023-08-23 14:07:50 +02:00
if ( CResourceHandler : : get ( ) - > existsResource ( ResourcePath ( filename ) ) )
2022-09-18 01:23:17 +02:00
return true ;
2022-11-25 00:21:24 +02:00
2022-09-18 01:23:17 +02:00
logGlobal - > error ( " Error: %s was not found! " , message ) ;
return false ;
} ;
2022-11-25 00:21:24 +02:00
if ( ! testFile ( " DATA/HELP.TXT " , " Heroes III data " ) | |
! testFile ( " MODS/VCMI/MOD.JSON " , " VCMI data " ) )
2022-09-18 01:23:17 +02:00
{
QApplication : : quit ( ) ;
}
2022-11-25 00:21:24 +02:00
2022-12-26 23:29:42 +02:00
loadTranslation ( ) ;
ui - > setupUi ( this ) ;
2024-06-05 10:56:35 +02:00
2024-06-05 06:52:17 +02:00
setWindowIcon ( QIcon { " :/icons/menu-game.png " } ) ;
2024-06-05 10:56:35 +02:00
ui - > toolBrush - > setIcon ( QIcon { " :/icons/brush-1.png " } ) ;
ui - > toolBrush2 - > setIcon ( QIcon { " :/icons/brush-2.png " } ) ;
ui - > toolBrush4 - > setIcon ( QIcon { " :/icons/brush-4.png " } ) ;
2024-06-06 04:04:36 +02:00
ui - > toolLasso - > setIcon ( QIcon { " :/icons/tool-lasso.png " } ) ;
ui - > toolLine - > setIcon ( QIcon { " :/icons/tool-line.png " } ) ;
ui - > toolArea - > setIcon ( QIcon { " :/icons/tool-area.png " } ) ;
ui - > toolFill - > setIcon ( QIcon { " :/icons/tool-fill.png " } ) ;
ui - > toolSelect - > setIcon ( QIcon { " :/icons/tool-select.png " } ) ;
2024-06-05 10:56:35 +02:00
ui - > actionOpen - > setIcon ( QIcon { " :/icons/document-open.png " } ) ;
ui - > actionSave - > setIcon ( QIcon { " :/icons/document-save.png " } ) ;
ui - > actionNew - > setIcon ( QIcon { " :/icons/document-new.png " } ) ;
ui - > actionLevel - > setIcon ( QIcon { " :/icons/toggle-underground.png " } ) ;
ui - > actionPass - > setIcon ( QIcon { " :/icons/toggle-pass.png " } ) ;
ui - > actionCut - > setIcon ( QIcon { " :/icons/edit-cut.png " } ) ;
ui - > actionCopy - > setIcon ( QIcon { " :/icons/edit-copy.png " } ) ;
ui - > actionPaste - > setIcon ( QIcon { " :/icons/edit-paste.png " } ) ;
ui - > actionFill - > setIcon ( QIcon { " :/icons/fill-obstacles.png " } ) ;
ui - > actionGrid - > setIcon ( QIcon { " :/icons/toggle-grid.png " } ) ;
ui - > actionUndo - > setIcon ( QIcon { " :/icons/edit-undo.png " } ) ;
ui - > actionRedo - > setIcon ( QIcon { " :/icons/edit-redo.png " } ) ;
ui - > actionErase - > setIcon ( QIcon { " :/icons/edit-clear.png " } ) ;
ui - > actionTranslations - > setIcon ( QIcon { " :/icons/translations.png " } ) ;
ui - > actionLock - > setIcon ( QIcon { " :/icons/lock-closed.png " } ) ;
ui - > actionUnlock - > setIcon ( QIcon { " :/icons/lock-open.png " } ) ;
ui - > actionZoom_in - > setIcon ( QIcon { " :/icons/zoom_plus.png " } ) ;
ui - > actionZoom_out - > setIcon ( QIcon { " :/icons/zoom_minus.png " } ) ;
ui - > actionZoom_reset - > setIcon ( QIcon { " :/icons/zoom_zero.png " } ) ;
2024-06-05 06:52:17 +02:00
2022-12-26 23:29:42 +02:00
loadUserSettings ( ) ; //For example window size
setTitle ( ) ;
2022-09-18 01:23:17 +02:00
init ( ) ;
2022-11-25 00:21:24 +02:00
2022-09-18 01:23:17 +02:00
graphics = new Graphics ( ) ; // should be before curh->init()
graphics - > load ( ) ; //must be after Content loading but should be in main thread
2022-11-20 18:48:31 +02:00
2022-11-25 00:21:24 +02:00
if ( extractionOptions . extractArchives )
ResourceConverter : : convertExtractedResourceFiles ( extractionOptions . conversionOptions ) ;
2022-09-18 01:23:17 +02:00
ui - > mapView - > setScene ( controller . scene ( 0 ) ) ;
ui - > mapView - > setController ( & controller ) ;
ui - > mapView - > setOptimizationFlags ( QGraphicsView : : DontSavePainterState | QGraphicsView : : DontAdjustForAntialiasing ) ;
connect ( ui - > mapView , & MapView : : openObjectProperties , this , & MainWindow : : loadInspector ) ;
2023-10-08 20:25:59 +02:00
connect ( ui - > mapView , & MapView : : currentCoordinates , this , & MainWindow : : currentCoordinatesChanged ) ;
2022-09-18 01:23:17 +02:00
ui - > minimapView - > setScene ( controller . miniScene ( 0 ) ) ;
ui - > minimapView - > setController ( & controller ) ;
connect ( ui - > minimapView , & MinimapView : : cameraPositionChanged , ui - > mapView , & MapView : : cameraChanged ) ;
scenePreview = new QGraphicsScene ( this ) ;
ui - > objectPreview - > setScene ( scenePreview ) ;
//loading objects
loadObjectsTree ( ) ;
ui - > tabWidget - > setCurrentIndex ( 0 ) ;
2022-09-24 22:55:05 +02:00
for ( int i = 0 ; i < PlayerColor : : PLAYER_LIMIT . getNum ( ) ; + + i )
2022-09-18 01:23:17 +02:00
{
connect ( getActionPlayer ( PlayerColor ( i ) ) , & QAction : : toggled , this , [ & , i ] ( ) { switchDefaultPlayer ( PlayerColor ( i ) ) ; } ) ;
}
connect ( getActionPlayer ( PlayerColor : : NEUTRAL ) , & QAction : : toggled , this , [ & ] ( ) { switchDefaultPlayer ( PlayerColor : : NEUTRAL ) ; } ) ;
onPlayersChanged ( ) ;
show ( ) ;
//Load map from command line
2022-11-20 18:48:31 +02:00
if ( ! mapFilePath . isEmpty ( ) )
openMap ( mapFilePath ) ;
2022-09-18 01:23:17 +02:00
}
MainWindow : : ~ MainWindow ( )
{
saveUserSettings ( ) ; //save window size etc.
2022-10-08 20:55:15 +02:00
delete ui ;
2022-09-18 01:23:17 +02:00
}
bool MainWindow : : getAnswerAboutUnsavedChanges ( )
{
if ( unsaved )
{
2023-07-27 19:01:20 +02:00
auto sure = QMessageBox : : question ( this , tr ( " Confirmation " ) , tr ( " Unsaved changes will be lost, are you sure? " ) ) ;
2022-09-18 01:23:17 +02:00
if ( sure = = QMessageBox : : No )
{
return false ;
}
}
return true ;
}
void MainWindow : : closeEvent ( QCloseEvent * event )
{
if ( getAnswerAboutUnsavedChanges ( ) )
QMainWindow : : closeEvent ( event ) ;
else
event - > ignore ( ) ;
}
void MainWindow : : setStatusMessage ( const QString & status )
{
statusBar ( ) - > showMessage ( status ) ;
}
void MainWindow : : setTitle ( )
{
2024-10-26 13:48:12 +02:00
QString title = QString ( " %1%2 - %3 (%4) " ) . arg ( filename , unsaved ? " * " : " " , VCMI_EDITOR_NAME , GameConstants : : VCMI_VERSION . c_str ( ) ) ;
2022-09-18 01:23:17 +02:00
setWindowTitle ( title ) ;
}
void MainWindow : : mapChanged ( )
{
unsaved = true ;
setTitle ( ) ;
}
void MainWindow : : initializeMap ( bool isNew )
{
unsaved = isNew ;
if ( isNew )
filename . clear ( ) ;
setTitle ( ) ;
mapLevel = 0 ;
ui - > mapView - > setScene ( controller . scene ( mapLevel ) ) ;
ui - > minimapView - > setScene ( controller . miniScene ( mapLevel ) ) ;
ui - > minimapView - > dimensions ( ) ;
2023-10-16 22:24:12 +02:00
if ( initialScale . isValid ( ) )
on_actionZoom_reset_triggered ( ) ;
2023-10-13 05:21:09 +02:00
initialScale = ui - > mapView - > mapToScene ( ui - > mapView - > viewport ( ) - > geometry ( ) ) . boundingRect ( ) ;
2022-09-18 01:23:17 +02:00
//enable settings
ui - > actionMapSettings - > setEnabled ( true ) ;
ui - > actionPlayers_settings - > setEnabled ( true ) ;
2023-09-28 15:29:43 +02:00
ui - > actionTranslations - > setEnabled ( true ) ;
2023-10-08 20:25:59 +02:00
ui - > actionLevel - > setEnabled ( controller . map ( ) - > twoLevel ) ;
2022-09-18 01:23:17 +02:00
2023-04-02 12:31:57 +02:00
//set minimal players count
if ( isNew )
{
controller . map ( ) - > players [ 0 ] . canComputerPlay = true ;
controller . map ( ) - > players [ 0 ] . canHumanPlay = true ;
}
2022-09-18 01:23:17 +02:00
onPlayersChanged ( ) ;
}
2023-09-30 03:42:29 +02:00
std : : unique_ptr < CMap > MainWindow : : openMapInternal ( const QString & filenameSelect )
2022-09-18 01:23:17 +02:00
{
QFileInfo fi ( filenameSelect ) ;
std : : string fname = fi . fileName ( ) . toStdString ( ) ;
std : : string fdir = fi . dir ( ) . path ( ) . toStdString ( ) ;
2023-08-23 14:07:50 +02:00
ResourcePath resId ( " MAPEDITOR/ " + fname , EResType : : MAP ) ;
2022-09-18 01:23:17 +02:00
//addFilesystem takes care about memory deallocation if case of failure, no memory leak here
auto * mapEditorFilesystem = new CFilesystemLoader ( " MAPEDITOR/ " , fdir , 0 ) ;
CResourceHandler : : removeFilesystem ( " local " , " mapEditor " ) ;
CResourceHandler : : addFilesystem ( " local " , " mapEditor " , mapEditorFilesystem ) ;
if ( ! CResourceHandler : : get ( " mapEditor " ) - > existsResource ( resId ) )
2023-09-30 03:42:29 +02:00
throw std : : runtime_error ( " Cannot open map from this folder " ) ;
2022-09-18 01:23:17 +02:00
CMapService mapService ;
2023-09-30 03:42:29 +02:00
if ( auto header = mapService . loadMapHeader ( resId ) )
{
auto missingMods = CMapService : : verifyMapHeaderMods ( * header ) ;
2024-04-19 11:46:50 +02:00
ModIncompatibility : : ModList modList ;
2023-09-30 03:42:29 +02:00
for ( const auto & m : missingMods )
2024-04-19 11:46:50 +02:00
modList . push_back ( m . second . name ) ;
2023-09-30 03:42:29 +02:00
if ( ! modList . empty ( ) )
throw ModIncompatibility ( modList ) ;
2024-01-01 16:37:48 +02:00
return mapService . loadMap ( resId , nullptr ) ;
2023-09-30 03:42:29 +02:00
}
else
throw std : : runtime_error ( " Corrupted map " ) ;
}
bool MainWindow : : openMap ( const QString & filenameSelect )
{
2022-09-18 01:23:17 +02:00
try
{
2023-09-30 03:42:29 +02:00
controller . setMap ( openMapInternal ( filenameSelect ) ) ;
2023-04-17 01:01:29 +02:00
}
2023-07-30 19:12:25 +02:00
catch ( const ModIncompatibility & e )
2023-04-17 01:01:29 +02:00
{
2023-09-23 00:32:48 +02:00
assert ( e . whatExcessive ( ) . empty ( ) ) ;
2023-10-18 01:41:02 +02:00
QMessageBox : : warning ( this , " Mods are required " , QString : : fromStdString ( e . whatMissing ( ) ) ) ;
2023-04-17 01:01:29 +02:00
return false ;
2022-09-18 01:23:17 +02:00
}
catch ( const std : : exception & e )
{
2023-09-30 03:42:29 +02:00
QMessageBox : : critical ( this , " Failed to open map " , tr ( e . what ( ) ) ) ;
2022-09-18 01:23:17 +02:00
return false ;
}
filename = filenameSelect ;
initializeMap ( controller . map ( ) - > version ! = EMapFormat : : VCMI ) ;
return true ;
}
void MainWindow : : on_actionOpen_triggered ( )
{
if ( ! getAnswerAboutUnsavedChanges ( ) )
return ;
2022-10-08 20:55:15 +02:00
auto filenameSelect = QFileDialog : : getOpenFileName ( this , tr ( " Open map " ) ,
2024-05-16 00:01:02 +02:00
QString : : fromStdString ( VCMIDirs : : get ( ) . userDataPath ( ) . make_preferred ( ) . string ( ) ) ,
2022-10-08 20:55:15 +02:00
tr ( " All supported maps (*.vmap *.h3m);;VCMI maps(*.vmap);;HoMM3 maps(*.h3m) " ) ) ;
if ( filenameSelect . isEmpty ( ) )
2022-09-18 01:23:17 +02:00
return ;
openMap ( filenameSelect ) ;
}
void MainWindow : : saveMap ( )
{
if ( ! controller . map ( ) )
return ;
if ( ! unsaved )
return ;
//validate map
auto issues = Validator : : validate ( controller . map ( ) ) ;
bool critical = false ;
for ( auto & issue : issues )
critical | = issue . critical ;
if ( ! issues . empty ( ) )
{
if ( critical )
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 " ) ;
else
QMessageBox : : information ( this , " Map validation " , " Map has some errors. Open Validator from the Map menu to see issues found " ) ;
}
2023-09-28 23:15:36 +02:00
Translations : : cleanupRemovedItems ( * controller . map ( ) ) ;
2022-09-18 01:23:17 +02:00
2024-08-22 16:53:43 +02:00
for ( auto obj : controller . map ( ) - > objects )
{
if ( obj - > ID = = Obj : : HERO_PLACEHOLDER )
{
auto hero = dynamic_cast < CGHeroPlaceholder * > ( obj . get ( ) ) ;
if ( hero - > heroType . has_value ( ) )
{
controller . map ( ) - > reservedCampaignHeroes . insert ( hero - > heroType . value ( ) ) ;
}
}
}
2022-09-18 01:23:17 +02:00
CMapService mapService ;
try
{
mapService . saveMap ( controller . getMapUniquePtr ( ) , filename . toStdString ( ) ) ;
}
catch ( const std : : exception & e )
{
QMessageBox : : critical ( this , " Failed to save map " , e . what ( ) ) ;
return ;
}
unsaved = false ;
setTitle ( ) ;
}
void MainWindow : : on_actionSave_as_triggered ( )
{
if ( ! controller . map ( ) )
return ;
2023-09-30 03:42:29 +02:00
auto filenameSelect = QFileDialog : : getSaveFileName ( this , tr ( " Save map " ) , lastSavingDir , tr ( " VCMI maps (*.vmap) " ) ) ;
2022-09-18 01:23:17 +02:00
if ( filenameSelect . isNull ( ) )
return ;
2024-05-16 00:01:02 +02:00
QFileInfo fileInfo ( filenameSelect ) ;
lastSavingDir = fileInfo . dir ( ) . path ( ) ;
if ( fileInfo . suffix ( ) . toLower ( ) ! = " vmap " )
filenameSelect + = " .vmap " ;
2022-09-18 01:23:17 +02:00
filename = filenameSelect ;
saveMap ( ) ;
}
void MainWindow : : on_actionNew_triggered ( )
{
if ( getAnswerAboutUnsavedChanges ( ) )
new WindowNewMap ( this ) ;
}
void MainWindow : : on_actionSave_triggered ( )
{
if ( ! controller . map ( ) )
return ;
if ( filename . isNull ( ) )
2023-09-30 03:42:29 +02:00
on_actionSave_as_triggered ( ) ;
else
saveMap ( ) ;
2022-09-18 01:23:17 +02:00
}
2023-10-08 20:25:59 +02:00
void MainWindow : : currentCoordinatesChanged ( int x , int y )
{
setStatusMessage ( QString ( " x: %1 y: %2 " ) . arg ( x ) . arg ( y ) ) ;
}
2022-10-08 21:54:45 +02:00
void MainWindow : : terrainButtonClicked ( TerrainId terrain )
2022-09-18 01:23:17 +02:00
{
controller . commitTerrainChange ( mapLevel , terrain ) ;
}
2022-10-08 22:11:29 +02:00
void MainWindow : : roadOrRiverButtonClicked ( ui8 type , bool isRoad )
2022-09-18 01:23:17 +02:00
{
controller . commitRoadOrRiverChange ( mapLevel , type , isRoad ) ;
}
void MainWindow : : addGroupIntoCatalog ( const std : : string & groupName , bool staticOnly )
{
auto knownObjects = VLC - > objtypeh - > knownObjects ( ) ;
for ( auto ID : knownObjects )
{
if ( catalog . count ( ID ) )
continue ;
addGroupIntoCatalog ( groupName , true , staticOnly , ID ) ;
}
}
void MainWindow : : addGroupIntoCatalog ( const std : : string & groupName , bool useCustomName , bool staticOnly , int ID )
{
QStandardItem * itemGroup = nullptr ;
auto itms = objectsModel . findItems ( QString : : fromStdString ( groupName ) ) ;
if ( itms . empty ( ) )
{
itemGroup = new QStandardItem ( QString : : fromStdString ( groupName ) ) ;
objectsModel . appendRow ( itemGroup ) ;
}
else
{
itemGroup = itms . front ( ) ;
}
2023-01-10 01:30:01 +02:00
if ( VLC - > objtypeh - > knownObjects ( ) . count ( ID ) = = 0 )
return ;
2022-09-18 01:23:17 +02:00
auto knownSubObjects = VLC - > objtypeh - > knownSubObjects ( ID ) ;
for ( auto secondaryID : knownSubObjects )
{
auto factory = VLC - > objtypeh - > getHandlerFor ( ID , secondaryID ) ;
auto templates = factory - > getTemplates ( ) ;
bool singleTemplate = templates . size ( ) = = 1 ;
if ( staticOnly & & ! factory - > isStaticObject ( ) )
continue ;
2023-02-09 21:33:33 +02:00
auto subGroupName = QString : : fromStdString ( VLC - > objtypeh - > getObjectName ( ID , secondaryID ) ) ;
2022-09-18 01:23:17 +02:00
auto * itemType = new QStandardItem ( subGroupName ) ;
for ( int templateId = 0 ; templateId < templates . size ( ) ; + + templateId )
{
auto templ = templates [ templateId ] ;
//selecting file
2023-08-23 14:07:50 +02:00
const AnimationPath & afile = templ - > editorAnimationFile . empty ( ) ? templ - > animationFile : templ - > editorAnimationFile ;
2022-09-18 01:23:17 +02:00
//creating picture
QPixmap preview ( 128 , 128 ) ;
preview . fill ( QColor ( 255 , 255 , 255 ) ) ;
QPainter painter ( & preview ) ;
2023-08-23 14:07:50 +02:00
Animation animation ( afile . getOriginalName ( ) ) ;
2022-09-18 01:23:17 +02:00
animation . preload ( ) ;
auto picture = animation . getImage ( 0 ) ;
if ( picture & & picture - > width ( ) & & picture - > height ( ) )
{
2024-01-10 02:30:35 +02:00
qreal xscale = static_cast < qreal > ( 128 ) / static_cast < qreal > ( picture - > width ( ) ) ;
qreal yscale = static_cast < qreal > ( 128 ) / static_cast < qreal > ( picture - > height ( ) ) ;
2022-09-18 01:23:17 +02:00
qreal scale = std : : min ( xscale , yscale ) ;
painter . scale ( scale , scale ) ;
painter . drawImage ( QPoint ( 0 , 0 ) , * picture ) ;
}
2023-10-14 03:16:17 +02:00
//create object to extract name
2024-01-01 16:37:48 +02:00
std : : unique_ptr < CGObjectInstance > temporaryObj ( factory - > create ( nullptr , templ ) ) ;
2023-10-14 03:16:17 +02:00
QString translated = useCustomName ? QString : : fromStdString ( temporaryObj - > getObjectName ( ) . c_str ( ) ) : subGroupName ;
itemType - > setText ( translated ) ;
2022-09-18 01:23:17 +02:00
//add parameters
QJsonObject data { { " id " , QJsonValue ( ID ) } ,
{ " subid " , QJsonValue ( secondaryID ) } ,
{ " template " , QJsonValue ( templateId ) } ,
2023-08-23 14:07:50 +02:00
{ " animationEditor " , QString : : fromStdString ( templ - > editorAnimationFile . getOriginalName ( ) ) } ,
{ " animation " , QString : : fromStdString ( templ - > animationFile . getOriginalName ( ) ) } ,
2023-10-14 03:16:17 +02:00
{ " preview " , jsonFromPixmap ( preview ) } ,
{ " typeName " , QString : : fromStdString ( factory - > getJsonKey ( ) ) }
} ;
2022-09-18 01:23:17 +02:00
//do not have extra level
if ( singleTemplate )
{
itemType - > setIcon ( QIcon ( preview ) ) ;
itemType - > setData ( data ) ;
}
else
{
auto * item = new QStandardItem ( QIcon ( preview ) , QString : : fromStdString ( templ - > stringID ) ) ;
item - > setData ( data ) ;
itemType - > appendRow ( item ) ;
}
}
itemGroup - > appendRow ( itemType ) ;
catalog . insert ( ID ) ;
}
}
void MainWindow : : loadObjectsTree ( )
{
try
{
ui - > terrainFilterCombo - > addItem ( " " ) ;
//adding terrains
2022-12-20 16:14:06 +02:00
for ( auto & terrain : VLC - > terrainTypeHandler - > objects )
2022-09-18 01:23:17 +02:00
{
2024-01-16 23:40:48 +02:00
auto * b = new QPushButton ( QString : : fromStdString ( terrain - > getNameTranslated ( ) ) ) ;
2022-09-18 01:23:17 +02:00
ui - > terrainLayout - > addWidget ( b ) ;
2024-05-17 00:05:51 +02:00
connect ( b , & QPushButton : : clicked , this , [ this , terrainID = terrain - > getId ( ) ] { terrainButtonClicked ( terrainID ) ; } ) ;
2022-09-18 01:23:17 +02:00
//filter
2023-04-10 13:33:53 +02:00
QString displayName = QString : : fromStdString ( terrain - > getNameTranslated ( ) ) ;
QString uniqueName = QString : : fromStdString ( terrain - > getJsonKey ( ) ) ;
ui - > terrainFilterCombo - > addItem ( displayName , QVariant ( uniqueName ) ) ;
2022-09-18 01:23:17 +02:00
}
//add spacer to keep terrain button on the top
ui - > terrainLayout - > addItem ( new QSpacerItem ( 20 , 20 , QSizePolicy : : Minimum , QSizePolicy : : Expanding ) ) ;
//adding roads
2022-12-20 16:14:06 +02:00
for ( auto & road : VLC - > roadTypeHandler - > objects )
2022-09-18 01:23:17 +02:00
{
2024-01-16 23:40:48 +02:00
auto * b = new QPushButton ( QString : : fromStdString ( road - > getNameTranslated ( ) ) ) ;
2022-09-18 01:23:17 +02:00
ui - > roadLayout - > addWidget ( b ) ;
2024-05-17 00:05:51 +02:00
connect ( b , & QPushButton : : clicked , this , [ this , roadID = road - > getIndex ( ) ] { roadOrRiverButtonClicked ( roadID , true ) ; } ) ;
2022-09-18 01:23:17 +02:00
}
//add spacer to keep terrain button on the top
ui - > roadLayout - > addItem ( new QSpacerItem ( 20 , 20 , QSizePolicy : : Minimum , QSizePolicy : : Expanding ) ) ;
//adding rivers
2022-12-20 16:14:06 +02:00
for ( auto & river : VLC - > riverTypeHandler - > objects )
2022-09-18 01:23:17 +02:00
{
2024-01-16 23:40:48 +02:00
auto * b = new QPushButton ( QString : : fromStdString ( river - > getNameTranslated ( ) ) ) ;
2022-09-18 01:23:17 +02:00
ui - > riverLayout - > addWidget ( b ) ;
2024-05-17 00:05:51 +02:00
connect ( b , & QPushButton : : clicked , this , [ this , riverID = river - > getIndex ( ) ] { roadOrRiverButtonClicked ( riverID , false ) ; } ) ;
2022-09-18 01:23:17 +02:00
}
//add spacer to keep terrain button on the top
ui - > riverLayout - > addItem ( new QSpacerItem ( 20 , 20 , QSizePolicy : : Minimum , QSizePolicy : : Expanding ) ) ;
if ( objectBrowser )
throw std : : runtime_error ( " object browser exists " ) ;
//model
2022-10-08 20:55:15 +02:00
objectsModel . setHorizontalHeaderLabels ( QStringList ( ) < < tr ( " Type " ) ) ;
2022-12-04 04:45:39 +02:00
objectBrowser = new ObjectBrowserProxyModel ( this ) ;
2022-09-18 01:23:17 +02:00
objectBrowser - > setSourceModel ( & objectsModel ) ;
objectBrowser - > setDynamicSortFilter ( false ) ;
objectBrowser - > setRecursiveFilteringEnabled ( true ) ;
ui - > treeView - > setModel ( objectBrowser ) ;
ui - > treeView - > setSelectionBehavior ( QAbstractItemView : : SelectItems ) ;
ui - > treeView - > setSelectionMode ( QAbstractItemView : : SingleSelection ) ;
connect ( ui - > treeView - > selectionModel ( ) , SIGNAL ( currentChanged ( const QModelIndex & , const QModelIndex & ) ) , this , SLOT ( treeViewSelected ( const QModelIndex & , const QModelIndex & ) ) ) ;
//adding objects
addGroupIntoCatalog ( " TOWNS " , false , false , Obj : : TOWN ) ;
addGroupIntoCatalog ( " TOWNS " , false , false , Obj : : RANDOM_TOWN ) ;
addGroupIntoCatalog ( " TOWNS " , true , false , Obj : : SHIPYARD ) ;
addGroupIntoCatalog ( " TOWNS " , true , false , Obj : : GARRISON ) ;
addGroupIntoCatalog ( " TOWNS " , true , false , Obj : : GARRISON2 ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : ARENA ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : BUOY ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : CARTOGRAPHER ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : SWAN_POND ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : COVER_OF_DARKNESS ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : CORPSE ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : FAERIE_RING ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : FOUNTAIN_OF_FORTUNE ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : FOUNTAIN_OF_YOUTH ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : GARDEN_OF_REVELATION ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : HILL_FORT ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : IDOL_OF_FORTUNE ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : LIBRARY_OF_ENLIGHTENMENT ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : LIGHTHOUSE ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : SCHOOL_OF_MAGIC ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : MAGIC_SPRING ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : MAGIC_WELL ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : MERCENARY_CAMP ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : MERMAID ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : MYSTICAL_GARDEN ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : OASIS ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : LEAN_TO ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : OBELISK ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : REDWOOD_OBSERVATORY ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : PILLAR_OF_FIRE ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : STAR_AXIS ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : RALLY_FLAG ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : WATERING_HOLE ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : SCHOLAR ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : SHRINE_OF_MAGIC_INCANTATION ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : SHRINE_OF_MAGIC_GESTURE ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : SHRINE_OF_MAGIC_THOUGHT ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : SIRENS ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : STABLES ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : TAVERN ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : TEMPLE ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : DEN_OF_THIEVES ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : LEARNING_STONE ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : TREE_OF_KNOWLEDGE ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : WAGON ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : SCHOOL_OF_WAR ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : WAR_MACHINE_FACTORY ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : WARRIORS_TOMB ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : WITCH_HUT ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : SANCTUARY ) ;
addGroupIntoCatalog ( " OBJECTS " , true , false , Obj : : MARLETTO_TOWER ) ;
addGroupIntoCatalog ( " HEROES " , true , false , Obj : : PRISON ) ;
addGroupIntoCatalog ( " HEROES " , false , false , Obj : : HERO ) ;
addGroupIntoCatalog ( " HEROES " , false , false , Obj : : RANDOM_HERO ) ;
addGroupIntoCatalog ( " HEROES " , false , false , Obj : : HERO_PLACEHOLDER ) ;
addGroupIntoCatalog ( " HEROES " , false , false , Obj : : BOAT ) ;
addGroupIntoCatalog ( " ARTIFACTS " , true , false , Obj : : ARTIFACT ) ;
addGroupIntoCatalog ( " ARTIFACTS " , false , false , Obj : : RANDOM_ART ) ;
addGroupIntoCatalog ( " ARTIFACTS " , false , false , Obj : : RANDOM_TREASURE_ART ) ;
addGroupIntoCatalog ( " ARTIFACTS " , false , false , Obj : : RANDOM_MINOR_ART ) ;
addGroupIntoCatalog ( " ARTIFACTS " , false , false , Obj : : RANDOM_MAJOR_ART ) ;
addGroupIntoCatalog ( " ARTIFACTS " , false , false , Obj : : RANDOM_RELIC_ART ) ;
addGroupIntoCatalog ( " ARTIFACTS " , true , false , Obj : : SPELL_SCROLL ) ;
addGroupIntoCatalog ( " ARTIFACTS " , true , false , Obj : : PANDORAS_BOX ) ;
addGroupIntoCatalog ( " RESOURCES " , true , false , Obj : : RANDOM_RESOURCE ) ;
addGroupIntoCatalog ( " RESOURCES " , false , false , Obj : : RESOURCE ) ;
addGroupIntoCatalog ( " RESOURCES " , true , false , Obj : : SEA_CHEST ) ;
addGroupIntoCatalog ( " RESOURCES " , true , false , Obj : : TREASURE_CHEST ) ;
addGroupIntoCatalog ( " RESOURCES " , true , false , Obj : : CAMPFIRE ) ;
addGroupIntoCatalog ( " RESOURCES " , true , false , Obj : : SHIPWRECK_SURVIVOR ) ;
addGroupIntoCatalog ( " RESOURCES " , true , false , Obj : : FLOTSAM ) ;
addGroupIntoCatalog ( " BANKS " , true , false , Obj : : CREATURE_BANK ) ;
addGroupIntoCatalog ( " BANKS " , true , false , Obj : : DRAGON_UTOPIA ) ;
addGroupIntoCatalog ( " BANKS " , true , false , Obj : : CRYPT ) ;
addGroupIntoCatalog ( " BANKS " , true , false , Obj : : DERELICT_SHIP ) ;
addGroupIntoCatalog ( " BANKS " , true , false , Obj : : PYRAMID ) ;
addGroupIntoCatalog ( " BANKS " , true , false , Obj : : SHIPWRECK ) ;
addGroupIntoCatalog ( " DWELLINGS " , true , false , Obj : : CREATURE_GENERATOR1 ) ;
addGroupIntoCatalog ( " DWELLINGS " , true , false , Obj : : CREATURE_GENERATOR2 ) ;
addGroupIntoCatalog ( " DWELLINGS " , true , false , Obj : : CREATURE_GENERATOR3 ) ;
addGroupIntoCatalog ( " DWELLINGS " , true , false , Obj : : CREATURE_GENERATOR4 ) ;
addGroupIntoCatalog ( " DWELLINGS " , true , false , Obj : : REFUGEE_CAMP ) ;
addGroupIntoCatalog ( " DWELLINGS " , false , false , Obj : : RANDOM_DWELLING ) ;
addGroupIntoCatalog ( " DWELLINGS " , false , false , Obj : : RANDOM_DWELLING_LVL ) ;
addGroupIntoCatalog ( " DWELLINGS " , false , false , Obj : : RANDOM_DWELLING_FACTION ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : CURSED_GROUND1 ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : MAGIC_PLAINS1 ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : CLOVER_FIELD ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : CURSED_GROUND2 ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : EVIL_FOG ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : FAVORABLE_WINDS ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : FIERY_FIELDS ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : HOLY_GROUNDS ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : LUCID_POOLS ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : MAGIC_CLOUDS ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : MAGIC_PLAINS2 ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : ROCKLANDS ) ;
addGroupIntoCatalog ( " GROUNDS " , true , false , Obj : : HOLE ) ;
addGroupIntoCatalog ( " TELEPORTS " , true , false , Obj : : MONOLITH_ONE_WAY_ENTRANCE ) ;
addGroupIntoCatalog ( " TELEPORTS " , true , false , Obj : : MONOLITH_ONE_WAY_EXIT ) ;
addGroupIntoCatalog ( " TELEPORTS " , true , false , Obj : : MONOLITH_TWO_WAY ) ;
addGroupIntoCatalog ( " TELEPORTS " , true , false , Obj : : SUBTERRANEAN_GATE ) ;
addGroupIntoCatalog ( " TELEPORTS " , true , false , Obj : : WHIRLPOOL ) ;
addGroupIntoCatalog ( " MINES " , true , false , Obj : : MINE ) ;
addGroupIntoCatalog ( " MINES " , false , false , Obj : : ABANDONED_MINE ) ;
addGroupIntoCatalog ( " MINES " , true , false , Obj : : WINDMILL ) ;
addGroupIntoCatalog ( " MINES " , true , false , Obj : : WATER_WHEEL ) ;
addGroupIntoCatalog ( " TRIGGERS " , true , false , Obj : : EVENT ) ;
addGroupIntoCatalog ( " TRIGGERS " , true , false , Obj : : GRAIL ) ;
addGroupIntoCatalog ( " TRIGGERS " , true , false , Obj : : SIGN ) ;
addGroupIntoCatalog ( " TRIGGERS " , true , false , Obj : : OCEAN_BOTTLE ) ;
addGroupIntoCatalog ( " MONSTERS " , false , false , Obj : : MONSTER ) ;
addGroupIntoCatalog ( " MONSTERS " , true , false , Obj : : RANDOM_MONSTER ) ;
addGroupIntoCatalog ( " MONSTERS " , true , false , Obj : : RANDOM_MONSTER_L1 ) ;
addGroupIntoCatalog ( " MONSTERS " , true , false , Obj : : RANDOM_MONSTER_L2 ) ;
addGroupIntoCatalog ( " MONSTERS " , true , false , Obj : : RANDOM_MONSTER_L3 ) ;
addGroupIntoCatalog ( " MONSTERS " , true , false , Obj : : RANDOM_MONSTER_L4 ) ;
addGroupIntoCatalog ( " MONSTERS " , true , false , Obj : : RANDOM_MONSTER_L5 ) ;
addGroupIntoCatalog ( " MONSTERS " , true , false , Obj : : RANDOM_MONSTER_L6 ) ;
addGroupIntoCatalog ( " MONSTERS " , true , false , Obj : : RANDOM_MONSTER_L7 ) ;
addGroupIntoCatalog ( " QUESTS " , true , false , Obj : : SEER_HUT ) ;
addGroupIntoCatalog ( " QUESTS " , true , false , Obj : : BORDER_GATE ) ;
addGroupIntoCatalog ( " QUESTS " , true , false , Obj : : QUEST_GUARD ) ;
addGroupIntoCatalog ( " QUESTS " , true , false , Obj : : HUT_OF_MAGI ) ;
addGroupIntoCatalog ( " QUESTS " , true , false , Obj : : EYE_OF_MAGI ) ;
addGroupIntoCatalog ( " QUESTS " , true , false , Obj : : BORDERGUARD ) ;
addGroupIntoCatalog ( " QUESTS " , true , false , Obj : : KEYMASTER ) ;
addGroupIntoCatalog ( " wog object " , true , false , Obj : : WOG_OBJECT ) ;
addGroupIntoCatalog ( " OBSTACLES " , true ) ;
addGroupIntoCatalog ( " OTHER " , false ) ;
}
2022-12-08 23:20:42 +02:00
catch ( const std : : exception & )
2022-09-18 01:23:17 +02:00
{
QMessageBox : : critical ( this , " Mods loading problem " , " Critical error during Mods loading. Disable invalid mods and restart. " ) ;
}
}
void MainWindow : : on_actionLevel_triggered ( )
{
if ( controller . map ( ) & & controller . map ( ) - > twoLevel )
{
mapLevel = mapLevel ? 0 : 1 ;
ui - > mapView - > setScene ( controller . scene ( mapLevel ) ) ;
ui - > minimapView - > setScene ( controller . miniScene ( mapLevel ) ) ;
if ( mapLevel = = 0 )
{
ui - > actionLevel - > setToolTip ( tr ( " View underground " ) ) ;
}
else
{
ui - > actionLevel - > setToolTip ( tr ( " View surface " ) ) ;
}
}
}
void MainWindow : : on_actionUndo_triggered ( )
{
QString str ( " Undo clicked " ) ;
statusBar ( ) - > showMessage ( str , 1000 ) ;
if ( controller . map ( ) )
{
controller . undo ( ) ;
}
}
void MainWindow : : on_actionRedo_triggered ( )
{
QString str ( " Redo clicked " ) ;
displayStatus ( str ) ;
if ( controller . map ( ) )
{
controller . redo ( ) ;
}
}
void MainWindow : : on_actionPass_triggered ( bool checked )
{
QString str ( " Passability clicked " ) ;
displayStatus ( str ) ;
if ( controller . map ( ) )
{
controller . scene ( 0 ) - > passabilityView . show ( checked ) ;
controller . scene ( 1 ) - > passabilityView . show ( checked ) ;
}
}
void MainWindow : : on_actionGrid_triggered ( bool checked )
{
QString str ( " Grid clicked " ) ;
displayStatus ( str ) ;
if ( controller . map ( ) )
{
controller . scene ( 0 ) - > gridView . show ( checked ) ;
2022-09-24 22:55:05 +02:00
controller . scene ( 1 ) - > gridView . show ( checked ) ;
2022-09-18 01:23:17 +02:00
}
}
void MainWindow : : changeBrushState ( int idx )
{
}
void MainWindow : : on_actionErase_triggered ( )
{
if ( controller . map ( ) )
{
controller . commitObjectErase ( mapLevel ) ;
}
}
2022-12-04 12:51:01 +02:00
void MainWindow : : preparePreview ( const QModelIndex & index )
2022-09-18 01:23:17 +02:00
{
scenePreview - > clear ( ) ;
auto data = objectsModel . itemFromIndex ( objectBrowser - > mapToSource ( index ) ) - > data ( ) . toJsonObject ( ) ;
if ( ! data . empty ( ) )
{
auto preview = data [ " preview " ] ;
if ( preview ! = QJsonValue : : Undefined )
{
QPixmap objPreview = pixmapFromJson ( preview ) ;
scenePreview - > addPixmap ( objPreview ) ;
}
}
2023-10-12 15:33:09 +02:00
ui - > objectPreview - > fitInView ( scenePreview - > itemsBoundingRect ( ) , Qt : : KeepAspectRatio ) ;
2022-09-18 01:23:17 +02:00
}
void MainWindow : : treeViewSelected ( const QModelIndex & index , const QModelIndex & deselected )
{
2023-10-14 02:58:13 +02:00
ui - > toolSelect - > setChecked ( true ) ;
2022-09-18 01:23:17 +02:00
ui - > mapView - > selectionTool = MapView : : SelectionTool : : None ;
2022-12-04 12:51:01 +02:00
preparePreview ( index ) ;
2022-09-18 01:23:17 +02:00
}
2023-04-10 13:33:53 +02:00
void MainWindow : : on_terrainFilterCombo_currentIndexChanged ( int index )
2022-09-18 01:23:17 +02:00
{
if ( ! objectBrowser )
return ;
2023-04-10 13:33:53 +02:00
QString uniqueName = ui - > terrainFilterCombo - > itemData ( index ) . toString ( ) ;
2022-12-20 18:35:40 +02:00
objectBrowser - > terrain = TerrainId ( ETerrainId : : ANY_TERRAIN ) ;
2023-04-10 13:33:53 +02:00
if ( ! uniqueName . isEmpty ( ) )
2022-12-20 18:35:40 +02:00
{
for ( auto const & terrain : VLC - > terrainTypeHandler - > objects )
2023-04-10 13:33:53 +02:00
if ( terrain - > getJsonKey ( ) = = uniqueName . toStdString ( ) )
2023-01-01 17:10:47 +02:00
objectBrowser - > terrain = terrain - > getId ( ) ;
2022-12-20 18:35:40 +02:00
}
2022-09-18 01:23:17 +02:00
objectBrowser - > invalidate ( ) ;
objectBrowser - > sort ( 0 ) ;
}
void MainWindow : : on_filter_textChanged ( const QString & arg1 )
{
if ( ! objectBrowser )
return ;
objectBrowser - > filter = arg1 ;
objectBrowser - > invalidate ( ) ;
objectBrowser - > sort ( 0 ) ;
}
void MainWindow : : on_actionFill_triggered ( )
{
QString str ( " Fill clicked " ) ;
displayStatus ( str ) ;
if ( ! controller . map ( ) )
return ;
controller . commitObstacleFill ( mapLevel ) ;
}
void MainWindow : : loadInspector ( CGObjectInstance * obj , bool switchTab )
{
if ( switchTab )
ui - > tabWidget - > setCurrentIndex ( 1 ) ;
2023-10-09 01:24:00 +02:00
Inspector inspector ( controller , obj , ui - > inspectorWidget ) ;
2022-09-18 01:23:17 +02:00
inspector . updateProperties ( ) ;
}
void MainWindow : : on_inspectorWidget_itemChanged ( QTableWidgetItem * item )
{
2023-09-10 22:05:57 +02:00
if ( ! item - > isSelected ( ) & & ! ( item - > flags ( ) & Qt : : ItemIsUserCheckable ) )
2022-09-18 01:23:17 +02:00
return ;
int r = item - > row ( ) ;
int c = item - > column ( ) ;
if ( c < 1 )
return ;
auto * tableWidget = item - > tableWidget ( ) ;
//get identifier
auto identifier = tableWidget - > item ( 0 , 1 ) - > text ( ) ;
2022-09-19 01:17:39 +02:00
CGObjectInstance * obj = data_cast < CGObjectInstance > ( identifier . toLongLong ( ) ) ;
2022-09-18 01:23:17 +02:00
//get parameter name
auto param = tableWidget - > item ( r , c - 1 ) - > text ( ) ;
//set parameter
2023-10-09 01:24:00 +02:00
Inspector inspector ( controller , obj , tableWidget ) ;
2023-09-10 22:05:57 +02:00
inspector . setProperty ( param , item ) ;
2022-09-18 01:23:17 +02:00
controller . commitObjectChange ( mapLevel ) ;
}
void MainWindow : : on_actionMapSettings_triggered ( )
{
auto settingsDialog = new MapSettings ( controller , this ) ;
settingsDialog - > setWindowModality ( Qt : : WindowModal ) ;
settingsDialog - > setModal ( true ) ;
}
void MainWindow : : on_actionPlayers_settings_triggered ( )
{
auto settingsDialog = new PlayerSettings ( controller , this ) ;
settingsDialog - > setWindowModality ( Qt : : WindowModal ) ;
settingsDialog - > setModal ( true ) ;
connect ( settingsDialog , & QDialog : : finished , this , & MainWindow : : onPlayersChanged ) ;
}
QAction * MainWindow : : getActionPlayer ( const PlayerColor & player )
{
if ( player . getNum ( ) = = 0 ) return ui - > actionPlayer_1 ;
if ( player . getNum ( ) = = 1 ) return ui - > actionPlayer_2 ;
if ( player . getNum ( ) = = 2 ) return ui - > actionPlayer_3 ;
if ( player . getNum ( ) = = 3 ) return ui - > actionPlayer_4 ;
if ( player . getNum ( ) = = 4 ) return ui - > actionPlayer_5 ;
if ( player . getNum ( ) = = 5 ) return ui - > actionPlayer_6 ;
if ( player . getNum ( ) = = 6 ) return ui - > actionPlayer_7 ;
if ( player . getNum ( ) = = 7 ) return ui - > actionPlayer_8 ;
return ui - > actionNeutral ;
}
void MainWindow : : switchDefaultPlayer ( const PlayerColor & player )
{
if ( controller . defaultPlayer = = player )
return ;
ui - > actionNeutral - > blockSignals ( true ) ;
ui - > actionNeutral - > setChecked ( PlayerColor : : NEUTRAL = = player ) ;
ui - > actionNeutral - > blockSignals ( false ) ;
2022-10-12 23:40:52 +02:00
for ( int i = 0 ; i < PlayerColor : : PLAYER_LIMIT . getNum ( ) ; + + i )
2022-09-18 01:23:17 +02:00
{
getActionPlayer ( PlayerColor ( i ) ) - > blockSignals ( true ) ;
getActionPlayer ( PlayerColor ( i ) ) - > setChecked ( PlayerColor ( i ) = = player ) ;
getActionPlayer ( PlayerColor ( i ) ) - > blockSignals ( false ) ;
}
controller . defaultPlayer = player ;
}
void MainWindow : : onPlayersChanged ( )
{
if ( controller . map ( ) )
{
getActionPlayer ( PlayerColor : : NEUTRAL ) - > setEnabled ( true ) ;
for ( int i = 0 ; i < controller . map ( ) - > players . size ( ) ; + + i )
getActionPlayer ( PlayerColor ( i ) ) - > setEnabled ( controller . map ( ) - > players . at ( i ) . canAnyonePlay ( ) ) ;
if ( ! getActionPlayer ( controller . defaultPlayer ) - > isEnabled ( ) | | controller . defaultPlayer = = PlayerColor : : NEUTRAL )
switchDefaultPlayer ( PlayerColor : : NEUTRAL ) ;
}
else
{
for ( int i = 0 ; i < PlayerColor : : PLAYER_LIMIT . getNum ( ) ; + + i )
getActionPlayer ( PlayerColor ( i ) ) - > setEnabled ( false ) ;
getActionPlayer ( PlayerColor : : NEUTRAL ) - > setEnabled ( false ) ;
}
}
void MainWindow : : enableUndo ( bool enable )
{
ui - > actionUndo - > setEnabled ( enable ) ;
}
void MainWindow : : enableRedo ( bool enable )
{
ui - > actionRedo - > setEnabled ( enable ) ;
}
void MainWindow : : onSelectionMade ( int level , bool anythingSelected )
{
if ( level = = mapLevel )
{
ui - > actionErase - > setEnabled ( anythingSelected ) ;
}
}
void MainWindow : : displayStatus ( const QString & message , int timeout /* = 2000 */ )
{
statusBar ( ) - > showMessage ( message , timeout ) ;
}
void MainWindow : : on_actionValidate_triggered ( )
{
new Validator ( controller . map ( ) , this ) ;
}
void MainWindow : : on_actionUpdate_appearance_triggered ( )
{
if ( ! controller . map ( ) )
return ;
if ( controller . scene ( mapLevel ) - > selectionObjectsView . getSelection ( ) . empty ( ) )
{
2023-07-27 19:01:20 +02:00
QMessageBox : : information ( this , tr ( " Update appearance " ) , tr ( " No objects selected " ) ) ;
2022-09-18 01:23:17 +02:00
return ;
}
2023-07-27 19:01:20 +02:00
if ( QMessageBox : : Yes ! = QMessageBox : : question ( this , tr ( " Update appearance " ) , tr ( " This operation is irreversible. Do you want to continue? " ) ) )
2022-09-18 01:23:17 +02:00
return ;
controller . scene ( mapLevel ) - > selectionTerrainView . clear ( ) ;
int errors = 0 ;
std : : set < CGObjectInstance * > staticObjects ;
for ( auto * obj : controller . scene ( mapLevel ) - > selectionObjectsView . getSelection ( ) )
{
auto handler = VLC - > objtypeh - > getHandlerFor ( obj - > ID , obj - > subID ) ;
if ( ! controller . map ( ) - > isInTheMap ( obj - > visitablePos ( ) ) )
{
+ + errors ;
continue ;
}
2022-10-08 21:54:45 +02:00
auto * terrain = controller . map ( ) - > getTile ( obj - > visitablePos ( ) ) . terType ;
2022-09-18 01:23:17 +02:00
if ( handler - > isStaticObject ( ) )
{
staticObjects . insert ( obj ) ;
2023-01-01 17:10:47 +02:00
if ( obj - > appearance - > canBePlacedAt ( terrain - > getId ( ) ) )
2022-09-18 01:23:17 +02:00
{
controller . scene ( mapLevel ) - > selectionObjectsView . deselectObject ( obj ) ;
continue ;
}
for ( auto & offset : obj - > appearance - > getBlockedOffsets ( ) )
controller . scene ( mapLevel ) - > selectionTerrainView . select ( obj - > pos + offset ) ;
}
else
{
2023-01-01 17:10:47 +02:00
auto app = handler - > getOverride ( terrain - > getId ( ) , obj ) ;
2022-09-18 01:23:17 +02:00
if ( ! app )
{
2023-01-01 17:10:47 +02:00
if ( obj - > appearance - > canBePlacedAt ( terrain - > getId ( ) ) )
2022-09-18 01:23:17 +02:00
continue ;
2023-01-01 17:10:47 +02:00
auto templates = handler - > getTemplates ( terrain - > getId ( ) ) ;
2022-09-18 01:23:17 +02:00
if ( templates . empty ( ) )
{
+ + errors ;
continue ;
}
app = templates . front ( ) ;
}
obj - > appearance = app ;
controller . mapHandler ( ) - > invalidate ( obj ) ;
controller . scene ( mapLevel ) - > selectionObjectsView . deselectObject ( obj ) ;
}
}
controller . commitObjectChange ( mapLevel ) ;
controller . commitObjectErase ( mapLevel ) ;
controller . commitObstacleFill ( mapLevel ) ;
if ( errors )
2023-10-18 01:41:02 +02:00
QMessageBox : : warning ( this , tr ( " Update appearance " ) , QString ( tr ( " Errors occurred. %1 objects were not updated " ) ) . arg ( errors ) ) ;
2022-09-18 01:23:17 +02:00
}
void MainWindow : : on_actionRecreate_obstacles_triggered ( )
{
}
2022-12-04 23:32:50 +02:00
void MainWindow : : on_actionCut_triggered ( )
{
if ( controller . map ( ) )
{
controller . copyToClipboard ( mapLevel ) ;
controller . commitObjectErase ( mapLevel ) ;
}
}
void MainWindow : : on_actionCopy_triggered ( )
{
if ( controller . map ( ) )
{
controller . copyToClipboard ( mapLevel ) ;
}
}
void MainWindow : : on_actionPaste_triggered ( )
{
if ( controller . map ( ) )
{
controller . pasteFromClipboard ( mapLevel ) ;
}
}
2023-04-22 18:41:23 +02:00
void MainWindow : : on_actionExport_triggered ( )
{
2023-09-30 03:44:13 +02:00
QString fileName = QFileDialog : : getSaveFileName ( this , tr ( " Save to image " ) , lastSavingDir , " BMP (*.bmp);;JPEG (*.jpeg);;PNG (*.png) " ) ;
2023-04-22 18:41:23 +02:00
if ( ! fileName . isNull ( ) )
{
2023-04-29 19:33:23 +02:00
QImage image ( ui - > mapView - > scene ( ) - > sceneRect ( ) . size ( ) . toSize ( ) , QImage : : Format_RGB888 ) ;
QPainter painter ( & image ) ;
ui - > mapView - > scene ( ) - > render ( & painter ) ;
image . save ( fileName ) ;
2023-04-22 18:41:23 +02:00
}
}
2023-09-28 15:29:43 +02:00
void MainWindow : : on_actionTranslations_triggered ( )
{
auto translationsDialog = new Translations ( * controller . map ( ) , this ) ;
translationsDialog - > show ( ) ;
}
2023-10-01 02:39:03 +02:00
void MainWindow : : on_actionh3m_converter_triggered ( )
2023-09-30 03:42:29 +02:00
{
2023-10-01 02:39:03 +02:00
auto mapFiles = QFileDialog : : getOpenFileNames ( this , tr ( " Select maps to convert " ) ,
2024-05-16 00:01:02 +02:00
QString : : fromStdString ( VCMIDirs : : get ( ) . userDataPath ( ) . make_preferred ( ) . string ( ) ) ,
2023-09-30 03:42:29 +02:00
tr ( " HoMM3 maps(*.h3m) " ) ) ;
if ( mapFiles . empty ( ) )
return ;
2023-10-01 02:39:03 +02:00
auto saveDirectory = QFileDialog : : getExistingDirectory ( this , tr ( " Choose directory to save converted maps " ) , QCoreApplication : : applicationDirPath ( ) ) ;
2023-09-30 03:42:29 +02:00
if ( saveDirectory . isEmpty ( ) )
return ;
try
{
for ( auto & m : mapFiles )
{
CMapService mapService ;
2023-10-01 13:32:35 +02:00
auto map = openMapInternal ( m ) ;
controller . repairMap ( map . get ( ) ) ;
mapService . saveMap ( map , ( saveDirectory + ' / ' + QFileInfo ( m ) . completeBaseName ( ) + " .vmap " ) . toStdString ( ) ) ;
2023-09-30 03:42:29 +02:00
}
2023-10-01 13:32:35 +02:00
QMessageBox : : information ( this , tr ( " Operation completed " ) , tr ( " Successfully converted %1 maps " ) . arg ( mapFiles . size ( ) ) ) ;
2023-09-30 03:42:29 +02:00
}
catch ( const std : : exception & e )
{
QMessageBox : : critical ( this , tr ( " Failed to convert the map. Abort operation " ) , tr ( e . what ( ) ) ) ;
}
}
2023-10-13 05:21:09 +02:00
void MainWindow : : on_actionLock_triggered ( )
{
if ( controller . map ( ) )
{
if ( controller . scene ( mapLevel ) - > selectionObjectsView . getSelection ( ) . empty ( ) )
{
for ( auto obj : controller . map ( ) - > objects )
{
controller . scene ( mapLevel ) - > selectionObjectsView . setLockObject ( obj , true ) ;
controller . scene ( mapLevel ) - > objectsView . setLockObject ( obj , true ) ;
}
}
else
{
for ( auto * obj : controller . scene ( mapLevel ) - > selectionObjectsView . getSelection ( ) )
{
controller . scene ( mapLevel ) - > selectionObjectsView . setLockObject ( obj , true ) ;
controller . scene ( mapLevel ) - > objectsView . setLockObject ( obj , true ) ;
}
controller . scene ( mapLevel ) - > selectionObjectsView . clear ( ) ;
}
controller . scene ( mapLevel ) - > objectsView . update ( ) ;
controller . scene ( mapLevel ) - > selectionObjectsView . update ( ) ;
}
}
void MainWindow : : on_actionUnlock_triggered ( )
{
if ( controller . map ( ) )
{
controller . scene ( mapLevel ) - > selectionObjectsView . unlockAll ( ) ;
controller . scene ( mapLevel ) - > objectsView . unlockAll ( ) ;
}
controller . scene ( mapLevel ) - > objectsView . update ( ) ;
}
void MainWindow : : on_actionZoom_in_triggered ( )
{
auto rect = ui - > mapView - > mapToScene ( ui - > mapView - > viewport ( ) - > geometry ( ) ) . boundingRect ( ) ;
rect - = QMargins { 32 + 1 , 32 + 1 , 32 + 2 , 32 + 2 } ; //compensate bounding box
ui - > mapView - > fitInView ( rect , Qt : : KeepAspectRatioByExpanding ) ;
}
void MainWindow : : on_actionZoom_out_triggered ( )
{
auto rect = ui - > mapView - > mapToScene ( ui - > mapView - > viewport ( ) - > geometry ( ) ) . boundingRect ( ) ;
rect + = QMargins { 32 - 1 , 32 - 1 , 32 - 2 , 32 - 2 } ; //compensate bounding box
ui - > mapView - > fitInView ( rect , Qt : : KeepAspectRatioByExpanding ) ;
}
void MainWindow : : on_actionZoom_reset_triggered ( )
{
auto center = ui - > mapView - > mapToScene ( ui - > mapView - > viewport ( ) - > geometry ( ) . center ( ) ) ;
ui - > mapView - > fitInView ( initialScale , Qt : : KeepAspectRatioByExpanding ) ;
ui - > mapView - > centerOn ( center ) ;
}
2023-10-14 02:58:13 +02:00
void MainWindow : : on_toolLine_toggled ( bool checked )
{
if ( checked )
{
ui - > mapView - > selectionTool = MapView : : SelectionTool : : Line ;
ui - > tabWidget - > setCurrentIndex ( 0 ) ;
}
}
void MainWindow : : on_toolBrush2_toggled ( bool checked )
{
if ( checked )
{
ui - > mapView - > selectionTool = MapView : : SelectionTool : : Brush2 ;
ui - > tabWidget - > setCurrentIndex ( 0 ) ;
}
}
void MainWindow : : on_toolBrush_toggled ( bool checked )
{
if ( checked )
{
ui - > mapView - > selectionTool = MapView : : SelectionTool : : Brush ;
ui - > tabWidget - > setCurrentIndex ( 0 ) ;
}
}
void MainWindow : : on_toolBrush4_toggled ( bool checked )
{
if ( checked )
{
ui - > mapView - > selectionTool = MapView : : SelectionTool : : Brush4 ;
ui - > tabWidget - > setCurrentIndex ( 0 ) ;
}
}
void MainWindow : : on_toolLasso_toggled ( bool checked )
{
if ( checked )
{
ui - > mapView - > selectionTool = MapView : : SelectionTool : : Lasso ;
ui - > tabWidget - > setCurrentIndex ( 0 ) ;
}
}
void MainWindow : : on_toolArea_toggled ( bool checked )
{
if ( checked )
{
ui - > mapView - > selectionTool = MapView : : SelectionTool : : Area ;
ui - > tabWidget - > setCurrentIndex ( 0 ) ;
}
}
void MainWindow : : on_toolFill_toggled ( bool checked )
{
if ( checked )
{
ui - > mapView - > selectionTool = MapView : : SelectionTool : : Fill ;
ui - > tabWidget - > setCurrentIndex ( 0 ) ;
}
}
void MainWindow : : on_toolSelect_toggled ( bool checked )
{
if ( checked )
{
ui - > mapView - > selectionTool = MapView : : SelectionTool : : None ;
ui - > tabWidget - > setCurrentIndex ( 0 ) ;
}
}