2022-10-13 01:51:55 +04: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 03:23:17 +04:00
#include "StdInc.h"
#include "validator.h"
2023-04-17 03:01:29 +04:00
#include "mapcontroller.h"
2022-09-18 03:23:17 +04:00
#include "ui_validator.h"
2023-05-24 02:05:59 +03:00
#include "../lib/mapping/CMap.h"
2022-09-18 03:23:17 +04:00
#include "../lib/mapObjects/MapObjects.h"
2023-07-30 20:12:25 +03:00
#include "../lib/modding/CModHandler.h"
#include "../lib/modding/CModInfo.h"
2023-09-04 03:33:01 +04:00
#include "../lib/spells/CSpellHandler.h"
2022-09-18 03:23:17 +04:00
#include "../lib/CHeroHandler.h"
Validator :: Validator ( const CMap * map , QWidget * parent ) :
QDialog ( parent ),
ui ( new Ui :: Validator )
{
ui -> setupUi ( this );
show ();
setAttribute ( Qt :: WA_DeleteOnClose );
std :: array < QString , 2 > icons { "mapeditor/icons/mod-update.png" , "mapeditor/icons/mod-delete.png" };
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 ;
}
std :: list < Validator :: Issue > Validator :: validate ( const CMap * map )
{
std :: list < Validator :: Issue > issues ;
if ( ! map )
{
2023-07-27 20:01:20 +03:00
issues . emplace_back ( tr ( "Map is not loaded" ), true );
2022-09-18 03:23:17 +04:00
return issues ;
}
try
{
//check player settings
int hplayers = 0 ;
int cplayers = 0 ;
std :: map < int , int > amountOfCastles ;
for ( int i = 0 ; i < map -> players . size (); ++ i )
{
auto & p = map -> players [ i ];
if ( p . canAnyonePlay ())
amountOfCastles [ i ] = 0 ;
if ( p . canComputerPlay )
++ cplayers ;
if ( p . canHumanPlay )
++ hplayers ;
if ( p . allowedFactions . empty ())
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "No factions allowed for player %1" )). arg ( i ), true );
2022-09-18 03:23:17 +04:00
}
if ( hplayers + cplayers == 0 )
2023-07-27 20:01:20 +03:00
issues . emplace_back ( tr ( "No players allowed to play this map" ), true );
2022-09-18 03:23:17 +04:00
if ( hplayers + cplayers == 1 )
2023-07-27 20:01:20 +03:00
issues . emplace_back ( tr ( "Map is allowed for one player and cannot be started" ), true );
2022-09-18 03:23:17 +04:00
if ( ! hplayers )
2023-07-27 20:01:20 +03:00
issues . emplace_back ( tr ( "No human players allowed to play this map" ), true );
2022-09-18 03:23:17 +04:00
2023-04-22 20:08:52 +04:00
std :: set < CHero *> allHeroesOnMap ; //used to find hero duplicated
2022-09-18 03:23:17 +04:00
//checking all objects in the map
for ( auto o : map -> objects )
{
//owners for objects
if ( o -> getOwner () == PlayerColor :: UNFLAGGABLE )
{
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 ()))
{
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "Armored instance %1 is UNFLAGGABLE but must have NEUTRAL or player owner" )). arg ( o -> instanceName . c_str ()), true );
2022-09-18 03:23:17 +04:00
}
}
2022-09-20 02:38:10 +04:00
if ( o -> getOwner () != PlayerColor :: NEUTRAL && o -> getOwner (). getNum () < map -> players . size ())
{
if ( ! map -> players [ o -> getOwner (). getNum ()]. canAnyonePlay ())
2023-09-04 22:21:02 +03:00
issues . emplace_back ( QString ( tr ( "Object %1 is assigned to non-playable player %2" )). arg ( o -> instanceName . c_str (), o -> getOwner (). toString (). c_str ()), true );
2022-09-20 02:38:10 +04:00
}
2022-09-18 03:23:17 +04:00
//checking towns
if ( auto * ins = dynamic_cast < CGTownInstance *> ( o . get ()))
{
bool has = amountOfCastles . count ( ins -> getOwner (). getNum ());
if ( ! has && ins -> getOwner () != PlayerColor :: NEUTRAL )
2023-09-04 22:21:02 +03:00
issues . emplace_back ( tr ( "Town %1 has undefined owner %2" ). arg ( ins -> instanceName . c_str (), ins -> getOwner (). toString (). c_str ()), true );
2022-09-18 03:23:17 +04:00
if ( has )
++ amountOfCastles [ ins -> getOwner (). getNum ()];
}
//checking heroes and prisons
if ( auto * ins = dynamic_cast < CGHeroInstance *> ( o . get ()))
{
if ( ins -> ID == Obj :: PRISON )
{
if ( ins -> getOwner () != PlayerColor :: NEUTRAL )
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "Prison %1 must be a NEUTRAL" )). arg ( ins -> instanceName . c_str ()), true );
2022-09-18 03:23:17 +04:00
}
else
{
bool has = amountOfCastles . count ( ins -> getOwner (). getNum ());
if ( ! has )
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "Hero %1 must have an owner" )). arg ( ins -> instanceName . c_str ()), true );
2022-09-18 03:23:17 +04:00
}
if ( ins -> type )
{
if ( ! map -> allowedHeroes [ ins -> type -> getId (). getNum ()])
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "Hero %1 is prohibited by map settings" )). arg ( ins -> type -> getNameTranslated (). c_str ()), false );
2023-04-22 20:08:52 +04:00
if ( ! allHeroesOnMap . insert ( ins -> type ). second )
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "Hero %1 has duplicate on map" )). arg ( ins -> type -> getNameTranslated (). c_str ()), false );
2022-09-18 03:23:17 +04:00
}
2023-09-10 16:57:41 +02:00
else if ( ins -> ID != Obj :: RANDOM_HERO )
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "Hero %1 has an empty type and must be removed" )). arg ( ins -> instanceName . c_str ()), true );
2022-09-18 03:23:17 +04:00
}
//checking for arts
if ( auto * ins = dynamic_cast < CGArtifact *> ( o . get ()))
{
if ( ins -> ID == Obj :: SPELL_SCROLL )
{
if ( ins -> storedArtifact )
{
2023-09-04 03:33:01 +04:00
if ( ! map -> allowedSpells [ ins -> storedArtifact -> getScrollSpellID ()])
issues . emplace_back ( QString ( tr ( "Spell scroll %1 is prohibited by map settings" )). arg ( ins -> storedArtifact -> getScrollSpellID (). toSpell ( VLC -> spells ()) -> getNameTranslated (). c_str ()), false );
2022-09-18 03:23:17 +04:00
}
else
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "Spell scroll %1 doesn't have instance assigned and must be removed" )). arg ( ins -> instanceName . c_str ()), true );
2022-09-18 03:23:17 +04:00
}
else
{
if ( ins -> ID == Obj :: ARTIFACT && ! map -> allowedArtifact [ ins -> subID ])
{
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "Artifact %1 is prohibited by map settings" )). arg ( ins -> getObjectName (). c_str ()), false );
2022-09-18 03:23:17 +04:00
}
}
}
}
//verification of starting towns
for ( auto & mp : amountOfCastles )
if ( mp . second == 0 )
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "Player %1 doesn't have any starting town" )). arg ( mp . first ), false );
2022-09-18 03:23:17 +04:00
//verification of map name and description
if ( map -> name . empty ())
2023-07-27 20:01:20 +03:00
issues . emplace_back ( tr ( "Map name is not specified" ), false );
2022-09-18 03:23:17 +04:00
if ( map -> description . empty ())
2023-07-27 20:01:20 +03:00
issues . emplace_back ( tr ( "Map description is not specified" ), false );
2023-04-17 03:01:29 +04:00
//verificationfor mods
for ( auto & mod : MapController :: modAssessmentMap ( * map ))
{
if ( ! map -> mods . count ( mod . first ))
{
2023-09-21 04:31:08 +02:00
issues . emplace_back ( QString ( 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 03:01:29 +04:00
}
}
2022-09-18 03:23:17 +04:00
}
catch ( const std :: exception & e )
{
2023-07-27 20:01:20 +03:00
issues . emplace_back ( QString ( tr ( "Exception occurs during validation: %1" )). arg ( e . what ()), true );
2022-09-18 03:23:17 +04:00
}
catch (...)
{
2023-07-27 20:01:20 +03:00
issues . emplace_back ( tr ( "Unknown exception occurs during validation" ), true );
2022-09-18 03:23:17 +04:00
}
return issues ;
}