/* Sunplug plugin for GtkRadiant Copyright (C) 2004 Topsun Thanks to SPoG for help! This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "sunplug.h" #include "debugging/debugging.h" #include "iplugin.h" #include "string/string.h" #include "modulesystem/singletonmodule.h" #include "iundo.h" // declaration of undo system #include "ientity.h" // declaration of entity system #include "iscenegraph.h" // declaration of datastructure of the map #include "scenelib.h" // declaration of datastructure of the map #include "qerplugin.h" // declaration to use other interfaces as a plugin #include "generic/vector.h" #include "gtkutil/spinbox.h" #include #include #include #include #include #include void about_plugin_window(); void MapCoordinator(); class SunPlugPluginDependencies : public GlobalRadiantModuleRef, // basic class for all other module refs public GlobalUndoModuleRef, // used to say radiant that something has changed and to undo that public GlobalSceneGraphModuleRef, // necessary to handle data in the mapfile (change, retrieve data) public GlobalEntityModuleRef // to access and modify the entities { public: SunPlugPluginDependencies() : GlobalEntityModuleRef( GlobalRadiant().getRequiredGameDescriptionKeyValue( "entities" ) ){ //, } }; // ************************* // ** standard plugin stuff ** // ************************* namespace SunPlug { QWidget* main_window; char MenuList[100] = ""; const char* init( void* hApp, void* pMainWidget ){ main_window = static_cast( pMainWidget ); return "Initializing SunPlug for GTKRadiant"; } const char* getName(){ return "SunPlug"; // name that is shown in the menue } const char* getCommandList(){ const char about[] = "About..."; const char etMapCoordinator[] = ";ET-MapCoordinator"; strcat( MenuList, about ); //. if ( strncmp( GlobalRadiant().getGameName(), "etmain", 6 ) == 0 ) { strcat( MenuList, etMapCoordinator ); //. } return (const char*)MenuList; } const char* getCommandTitleList(){ return ""; } void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush ){ // message processing if ( string_equal( command, "About..." ) ) { about_plugin_window(); } if ( string_equal( command, "ET-MapCoordinator" ) ) { MapCoordinator(); } } } // namespace class SunPlugModule : public TypeSystemRef { _QERPluginTable m_plugin; public: typedef _QERPluginTable Type; STRING_CONSTANT( Name, "SunPlug" ); SunPlugModule(){ m_plugin.m_pfnQERPlug_Init = &SunPlug::init; m_plugin.m_pfnQERPlug_GetName = &SunPlug::getName; m_plugin.m_pfnQERPlug_GetCommandList = &SunPlug::getCommandList; m_plugin.m_pfnQERPlug_GetCommandTitleList = &SunPlug::getCommandTitleList; m_plugin.m_pfnQERPlug_Dispatch = &SunPlug::dispatch; } _QERPluginTable* getTable(){ return &m_plugin; } }; typedef SingletonModule SingletonSunPlugModule; SingletonSunPlugModule g_SunPlugModule; extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules( ModuleServer& server ){ initialiseModule( server ); g_SunPlugModule.selfRegister(); } // ************************** // ** find entities by class ** from radiant/map.cpp // ************************** class EntityFindByClassname : public scene::Graph::Walker { const char* m_name; Entity*& m_entity; public: EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){ m_entity = 0; } bool pre( const scene::Path& path, scene::Instance& instance ) const { if ( m_entity == 0 ) { Entity* entity = Node_getEntity( path.top() ); if ( entity != 0 && string_equal( m_name, entity->getClassName() ) ) { m_entity = entity; } } return true; } }; Entity* Scene_FindEntityByClass( const char* name ){ Entity* entity; GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) ); return entity; } // ************ // ** my stuff ** // ************ // About dialog void about_plugin_window(){ GlobalRadiant().m_pfnMessageBox( SunPlug::main_window, "SunPlug v1.0 for NetRadiant 1.5\nby Topsun", "About SunPlug", EMessageBoxType::Info, 0 ); } AABB GetMapBounds(){ scene::Path path = makeReference( GlobalSceneGraph().root() ); // get the path to the root element of the graph scene::Instance* instance = GlobalSceneGraph().find( path ); // find the instance to the given path return instance->worldAABB(); // get the bounding box of the level } // get the current bounding box and return the optimal coordinates auto GetOptimalCoordinates(){ const AABB bounds = GetMapBounds(); const float max = std::max( { bounds.extents.x(), bounds.extents.y(), 175.f } ); // the square must be at least 350x350 units return std::pair( BasicVector2( bounds.origin.vec2() - Vector2( max, max ) ), BasicVector2( bounds.origin.vec2() + Vector2( max, max ) ) ); } // MapCoordinator dialog window void MapCoordinator(){ // find the entity worldspawn if ( Entity *theWorldspawn = Scene_FindEntityByClass( "worldspawn" ) ) { // need to have a worldspawn otherwise setting a value crashes the radiant // get the current values of the mapcoords BasicVector2 min( 0, 0 ), max( 0, 0 ); { StringTokeniser tokeniser( theWorldspawn->getKeyValue( "mapcoordsmins" ) ); // upper left corner min.x() = atoi( tokeniser.getToken() ); // minimum of x value min.y() = atoi( tokeniser.getToken() ); // maximum of y value } { StringTokeniser tokeniser( theWorldspawn->getKeyValue( "mapcoordsmaxs" ) ); // lower right corner max.x() = atoi( tokeniser.getToken() ); // maximum of x value max.y() = atoi( tokeniser.getToken() ); // minimum of y value } globalOutputStream() << "SunPlug: calculating optimal coordinates\n"; // write to console that we are calculating the coordinates const auto [ calc_min, calc_max ] = GetOptimalCoordinates(); // calculate optimal mapcoords with the dimensions of the level bounding box globalOutputStream() << "SunPlug: advised mapcoordsmins=" << calc_min.x() << " " << calc_max.y() << "\n"; // console info about mapcoordsmins globalOutputStream() << "SunPlug: advised mapcoordsmaxs=" << calc_max.x() << " " << calc_min.y() << "\n"; // console info about mapcoordsmaxs { QDialog dialog( SunPlug::main_window, Qt::Dialog | Qt::WindowCloseButtonHint ); dialog.setWindowTitle( "ET-MapCoordinator" ); { auto form = new QFormLayout( &dialog ); form->setSizeConstraint( QLayout::SizeConstraint::SetFixedSize ); { auto spin_minX = new SpinBox( -65536, 65536, min.x() ); auto spin_minY = new SpinBox( -65536, 65536, min.y() ); auto spin_maxX = new SpinBox( -65536, 65536, max.x() ); auto spin_maxY = new SpinBox( -65536, 65536, max.y() ); spin_minX->setPrefix( "X: " ); spin_minY->setPrefix( "Y: " ); spin_maxX->setPrefix( "X: " ); spin_maxY->setPrefix( "Y: " ); { auto button = new QPushButton( "Get optimal mapcoords" ); form->addWidget( button ); QObject::connect( button, &QPushButton::clicked, [&, calc_min = calc_min, calc_max = calc_max](){ spin_minX->setValue( calc_min.x() ); spin_minY->setValue( calc_max.y() ); spin_maxX->setValue( calc_max.x() ); spin_maxY->setValue( calc_min.y() ); } ); } { auto line = new QFrame; line->setFrameShape( QFrame::Shape::HLine ); line->setFrameShadow( QFrame::Shadow::Raised ); form->addRow( line ); } { auto hbox = new QHBoxLayout; hbox->addWidget( spin_minX ); hbox->addWidget( spin_minY ); form->addRow( new QLabel( "MapCoordsMins" ), hbox ); } { auto hbox = new QHBoxLayout; hbox->addWidget( spin_maxX ); hbox->addWidget( spin_maxY ); form->addRow( new QLabel( "MapCoordsMaxs" ), hbox ); } { auto buttons = new QDialogButtonBox( QDialogButtonBox::StandardButton::Ok | QDialogButtonBox::StandardButton::Cancel ); form->addWidget( buttons ); QObject::connect( buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept ); QObject::connect( buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject ); buttons->button( QDialogButtonBox::StandardButton::Ok )->setText( "Set" ); } if( dialog.exec() ){ UndoableCommand undo( "SunPlug.entitySetMapcoords" ); theWorldspawn->setKeyValue( "mapcoordsmins", ( spin_minX->cleanText() + " " + spin_minY->cleanText() ).toLatin1().constData() ); theWorldspawn->setKeyValue( "mapcoordsmaxs", ( spin_maxX->cleanText() + " " + spin_maxY->cleanText() ).toLatin1().constData() ); } } } } } else { globalErrorStream() << "SunPlug: no worldspawn found!\n"; // output error to console GlobalRadiant().m_pfnMessageBox( SunPlug::main_window, "No worldspawn was found in the map!\nIn order to use this tool the map must have at least one brush in the worldspawn.", "ET-MapCoordinator", EMessageBoxType::Error, 0 ); } }