2022-10-12 23:51:55 +02:00
/*
* validator . 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 "validator.h"
2023-04-17 01:01:29 +02:00
# include "mapcontroller.h"
2022-09-18 01:23:17 +02:00
# include "ui_validator.h"
2024-10-11 18:30:16 +02:00
# include "../lib/entities/hero/CHero.h"
2023-05-24 01:05:59 +02:00
# include "../lib/mapping/CMap.h"
2022-09-18 01:23:17 +02:00
# include "../lib/mapObjects/MapObjects.h"
2023-07-30 19:12:25 +02:00
# include "../lib/modding/CModHandler.h"
2024-11-09 22:29:07 +02:00
# include "../lib/modding/ModDescription.h"
2023-09-04 01:33:01 +02:00
# include "../lib/spells/CSpellHandler.h"
2022-09-18 01:23:17 +02:00
Validator : : Validator ( const CMap * map , QWidget * parent ) :
QDialog ( parent ) ,
ui ( new Ui : : Validator )
{
ui - > setupUi ( this ) ;
show ( ) ;
setAttribute ( Qt : : WA_DeleteOnClose ) ;
2024-06-05 06:52:17 +02:00
std : : array < QString , 2 > icons { " :/icons/mod-update.png " , " :/icons/mod-delete.png " } ;
2022-09-18 01:23:17 +02:00
for ( auto & issue : Validator : : validate ( map ) )
{
auto * item = new QListWidgetItem ( QIcon ( icons [ issue . critical ? 1 : 0 ] ) , issue . message ) ;
ui - > listWidget - > addItem ( item ) ;
}
}
Validator : : ~ Validator ( )
{
delete ui ;
}
2024-08-20 00:53:48 +02:00
std : : set < Validator : : Issue > Validator : : validate ( const CMap * map )
2022-09-18 01:23:17 +02:00
{
2024-08-20 00:53:48 +02:00
std : : set < Validator : : Issue > issues ;
2022-09-18 01:23:17 +02:00
if ( ! map )
{
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Map is not loaded " ) , true } ) ;
2022-09-18 01:23:17 +02:00
return issues ;
}
try
{
//check player settings
int hplayers = 0 ;
int cplayers = 0 ;
2024-08-19 00:21:11 +02:00
std : : map < PlayerColor , int > amountOfTowns ;
std : : map < PlayerColor , int > amountOfHeroes ;
2022-09-18 01:23:17 +02:00
for ( int i = 0 ; i < map - > players . size ( ) ; + + i )
{
auto & p = map - > players [ i ] ;
2024-08-20 00:53:48 +02:00
if ( p . canAnyonePlay ( ) )
amountOfTowns [ PlayerColor ( i ) ] = 0 ;
2022-09-18 01:23:17 +02:00
if ( p . canComputerPlay )
+ + cplayers ;
if ( p . canHumanPlay )
+ + hplayers ;
if ( p . allowedFactions . empty ( ) )
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " No factions allowed for player %1 " ) . arg ( i ) , true } ) ;
2022-09-18 01:23:17 +02:00
}
if ( hplayers + cplayers = = 0 )
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " No players allowed to play this map " ) , true } ) ;
2022-09-18 01:23:17 +02:00
if ( hplayers + cplayers = = 1 )
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Map is allowed for one player and cannot be started " ) , true } ) ;
2022-09-18 01:23:17 +02:00
if ( ! hplayers )
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " No human players allowed to play this map " ) , true } ) ;
2022-09-18 01:23:17 +02:00
2024-08-20 00:53:48 +02:00
std : : set < const CHero * > allHeroesOnMap ; //used to find hero duplicated
2023-04-22 18:08:52 +02:00
2022-09-18 01:23:17 +02:00
//checking all objects in the map
for ( auto o : map - > objects )
{
//owners for objects
if ( o - > getOwner ( ) = = PlayerColor : : UNFLAGGABLE )
{
2024-08-20 00:53:48 +02:00
if ( dynamic_cast < CGMine * > ( o . get ( ) ) | |
dynamic_cast < CGDwelling * > ( o . get ( ) ) | |
dynamic_cast < CGTownInstance * > ( o . get ( ) ) | |
dynamic_cast < CGGarrison * > ( o . get ( ) ) | |
dynamic_cast < CGHeroInstance * > ( o . get ( ) ) )
2022-09-18 01:23:17 +02:00
{
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Armored instance %1 is UNFLAGGABLE but must have NEUTRAL or player owner " ) . arg ( o - > instanceName . c_str ( ) ) , true } ) ;
2022-09-18 01:23:17 +02:00
}
}
2022-09-20 00:38:10 +02:00
if ( o - > getOwner ( ) ! = PlayerColor : : NEUTRAL & & o - > getOwner ( ) . getNum ( ) < map - > players . size ( ) )
{
if ( ! map - > players [ o - > getOwner ( ) . getNum ( ) ] . canAnyonePlay ( ) )
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Object %1 is assigned to non-playable player %2 " ) . arg ( o - > instanceName . c_str ( ) , o - > getOwner ( ) . toString ( ) . c_str ( ) ) , true } ) ;
2022-09-20 00:38:10 +02:00
}
2024-08-19 00:21:11 +02:00
//count towns
2024-08-20 00:53:48 +02:00
if ( auto * ins = dynamic_cast < CGTownInstance * > ( o . get ( ) ) )
2022-09-18 01:23:17 +02:00
{
2024-08-19 00:21:11 +02:00
+ + amountOfTowns [ ins - > getOwner ( ) ] ;
2022-09-18 01:23:17 +02:00
}
2024-08-19 00:21:11 +02:00
//checking and counting heroes and prisons
2024-08-20 00:53:48 +02:00
if ( auto * ins = dynamic_cast < CGHeroInstance * > ( o . get ( ) ) )
2022-09-18 01:23:17 +02:00
{
if ( ins - > ID = = Obj : : PRISON )
{
if ( ins - > getOwner ( ) ! = PlayerColor : : NEUTRAL )
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Prison %1 must be a NEUTRAL " ) . arg ( ins - > instanceName . c_str ( ) ) , true } ) ;
2022-09-18 01:23:17 +02:00
}
else
{
2024-08-19 00:21:11 +02:00
if ( ins - > getOwner ( ) = = PlayerColor : : NEUTRAL )
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Hero %1 must have an owner " ) . arg ( ins - > instanceName . c_str ( ) ) , true } ) ;
2024-08-19 00:21:11 +02:00
+ + amountOfHeroes [ ins - > getOwner ( ) ] ;
2022-09-18 01:23:17 +02:00
}
2024-10-05 21:37:52 +02:00
if ( ins - > getHeroTypeID ( ) . hasValue ( ) )
2022-09-18 01:23:17 +02:00
{
2024-10-05 21:37:52 +02:00
if ( map - > allowedHeroes . count ( ins - > getHeroTypeID ( ) ) = = 0 )
issues . insert ( { tr ( " Hero %1 is prohibited by map settings " ) . arg ( ins - > getHeroType ( ) - > getNameTranslated ( ) . c_str ( ) ) , false } ) ;
2023-04-22 18:08:52 +02:00
2024-10-05 21:37:52 +02:00
if ( ! allHeroesOnMap . insert ( ins - > getHeroType ( ) ) . second )
issues . insert ( { tr ( " Hero %1 has duplicate on map " ) . arg ( ins - > getHeroType ( ) - > getNameTranslated ( ) . c_str ( ) ) , false } ) ;
2022-09-18 01:23:17 +02:00
}
2023-09-10 16:57:41 +02:00
else if ( ins - > ID ! = Obj : : RANDOM_HERO )
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Hero %1 has an empty type and must be removed " ) . arg ( ins - > instanceName . c_str ( ) ) , true } ) ;
2022-09-18 01:23:17 +02:00
}
//checking for arts
2024-08-20 00:53:48 +02:00
if ( auto * ins = dynamic_cast < CGArtifact * > ( o . get ( ) ) )
2022-09-18 01:23:17 +02:00
{
if ( ins - > ID = = Obj : : SPELL_SCROLL )
{
2024-08-20 00:53:48 +02:00
if ( ins - > storedArtifact )
2022-09-18 01:23:17 +02:00
{
2024-08-20 00:53:48 +02:00
if ( map - > allowedSpells . count ( ins - > storedArtifact - > getScrollSpellID ( ) ) = = 0 )
issues . insert ( { tr ( " Spell scroll %1 is prohibited by map settings " ) . arg ( ins - > storedArtifact - > getScrollSpellID ( ) . toEntity ( VLC - > spells ( ) ) - > getNameTranslated ( ) . c_str ( ) ) , false } ) ;
2022-09-18 01:23:17 +02:00
}
else
2024-09-13 14:02:09 +02:00
issues . insert ( { tr ( " Spell scroll %1 doesn't have instance assigned and must be removed " ) . arg ( ins - > instanceName . c_str ( ) ) , true } ) ;
2022-09-18 01:23:17 +02:00
}
else
{
2023-11-05 16:35:51 +02:00
if ( ins - > ID = = Obj : : ARTIFACT & & map - > allowedArtifact . count ( ins - > getArtifact ( ) ) = = 0 )
2022-09-18 01:23:17 +02:00
{
2024-09-13 14:02:09 +02:00
issues . insert ( { tr ( " Artifact %1 is prohibited by map settings " ) . arg ( ins - > getObjectName ( ) . c_str ( ) ) , false } ) ;
2022-09-18 01:23:17 +02:00
}
}
}
}
//verification of starting towns
2024-08-20 00:53:48 +02:00
for ( const auto & [ player , counter ] : amountOfTowns )
{
if ( counter = = 0 )
{
// FIXME: heroesNames are empty even though heroes are on the map
// if(map->players[playerTCounter.first].heroesNames.empty())
if ( amountOfHeroes . count ( player ) = = 0 )
issues . insert ( { tr ( " Player %1 has no towns and heroes assigned " ) . arg ( player + 1 ) , true } ) ;
else
issues . insert ( { tr ( " Player %1 doesn't have any starting town " ) . arg ( player + 1 ) , false } ) ;
}
}
2022-09-18 01:23:17 +02:00
//verification of map name and description
if ( map - > name . empty ( ) )
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Map name is not specified " ) , false } ) ;
2022-09-18 01:23:17 +02:00
if ( map - > description . empty ( ) )
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Map description is not specified " ) , false } ) ;
2023-04-17 01:01:29 +02:00
//verificationfor mods
for ( auto & mod : MapController : : modAssessmentMap ( * map ) )
{
if ( ! map - > mods . count ( mod . first ) )
{
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Map contains object from mod \" %1 \" , but doesn't require it " ) . arg ( QString : : fromStdString ( VLC - > modh - > getModInfo ( mod . first ) . getVerificationInfo ( ) . name ) ) , true } ) ;
2023-04-17 01:01:29 +02:00
}
}
2022-09-18 01:23:17 +02:00
}
catch ( const std : : exception & e )
{
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Exception occurs during validation: %1 " ) . arg ( e . what ( ) ) , true } ) ;
2022-09-18 01:23:17 +02:00
}
catch ( . . . )
{
2024-08-20 00:53:48 +02:00
issues . insert ( { tr ( " Unknown exception occurs during validation " ) , true } ) ;
2022-09-18 01:23:17 +02:00
}
return issues ;
}