X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fnetradiant.git;a=blobdiff_plain;f=radiant%2Fmap.cpp;h=a5ac53f899e63e2a27e863482a12091851efd4ce;hp=03cbc21d7c1271ec2a4e9a4ab2a154a75b6426be;hb=HEAD;hpb=c9d78eecedad850196f7b2e2b5f5d72e980f3c8e diff --git a/radiant/map.cpp b/radiant/map.cpp index 03cbc21d..c783e035 100644 --- a/radiant/map.cpp +++ b/radiant/map.cpp @@ -26,7 +26,9 @@ #include "debugging/debugging.h" #include "imap.h" + MapModules& ReferenceAPI_getMapModules(); + #include "iselection.h" #include "iundo.h" #include "ibrush.h" @@ -57,6 +59,7 @@ MapModules& ReferenceAPI_getMapModules(); #include "cmdlib.h" #include "stream/textfilestream.h" #include "os/path.h" +#include "os/file.h" #include "uniquenames.h" #include "modulesystem/singletonmodule.h" #include "modulesystem/moduleregistry.h" @@ -83,6 +86,9 @@ MapModules& ReferenceAPI_getMapModules(); #include "autosave.h" #include "brushmodule.h" #include "brush.h" +#include "patch.h" + +bool g_writeMapComments = true; class NameObserver { @@ -95,6 +101,7 @@ void construct(){ m_names.insert( name_read( c_str() ) ); } } + void destroy(){ if ( !empty() ) { //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n"; @@ -103,6 +110,7 @@ void destroy(){ } NameObserver& operator=( const NameObserver& other ); + public: NameObserver( UniqueNames& names ) : m_names( names ){ construct(); @@ -110,21 +118,26 @@ NameObserver( UniqueNames& names ) : m_names( names ){ NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){ construct(); } + ~NameObserver(){ destroy(); } + bool empty() const { return string_empty( c_str() ); } + const char* c_str() const { return m_name.c_str(); } + void nameChanged( const char* name ){ destroy(); m_name = name; construct(); } -typedef MemberCaller1 NameChangedCaller; + +typedef MemberCaller NameChangedCaller; }; class BasicNamespace : public Namespace @@ -136,12 +149,14 @@ public: ~BasicNamespace(){ ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" ); } + void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){ std::pair result = m_names.insert( Names::value_type( setName, m_uniqueNames ) ); ASSERT_MESSAGE( result.second, "cannot attach name" ); attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) ); //globalOutputStream() << "attach: " << reinterpret_cast(setName) << "\n"; } + void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){ Names::iterator i = m_names.find( setName ); ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" ); @@ -196,11 +211,13 @@ class NamespaceAPI Namespace* m_namespace; public: typedef Namespace Type; + STRING_CONSTANT( Name, "*" ); NamespaceAPI(){ m_namespace = &g_defaultNamespace; } + Namespace* getTable(){ return m_namespace; } @@ -258,6 +275,7 @@ public: WorldNode() : m_node( 0 ){ } + void set( scene::Node* node ){ if ( m_node != 0 ) { m_node->DecRef(); @@ -267,6 +285,7 @@ void set( scene::Node* node ){ m_node->IncRef(); } } + scene::Node* get() const { return m_node; } @@ -274,7 +293,9 @@ scene::Node* get() const { class Map; void Map_SetValid( Map& map, bool valid ); + void Map_UpdateTitle( const Map& map ); + void Map_SetWorldspawn( Map& map, scene::Node* node ); @@ -286,6 +307,7 @@ Resource* m_resource; bool m_valid; bool m_modified; + void ( *m_modified_changed )( const Map& ); Signal0 m_mapValidCallbacks; @@ -316,6 +338,7 @@ void realise(){ Map_SetValid( g_map, true ); } } + void unrealise(){ if ( m_resource != 0 ) { Map_SetValid( g_map, false ); @@ -383,7 +406,6 @@ void Map_UpdateTitle( const Map& map ){ } - scene::Node* Map_GetWorldspawn( const Map& map ){ return map.m_world_node.get(); } @@ -399,8 +421,8 @@ float g_MaxWorldCoord = 64 * 1024; float g_MinWorldCoord = -64 * 1024; void AddRegionBrushes( void ); -void RemoveRegionBrushes( void ); +void RemoveRegionBrushes( void ); /* @@ -430,6 +452,7 @@ 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() ); @@ -539,6 +562,7 @@ UnsortedNodeSet& m_nodes; public: CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){ } + bool pre( scene::Node& node ) const { m_nodes.insert( NodeSmartReference( node ) ); Node_getTraversable( m_root )->erase( node ); @@ -582,12 +606,14 @@ public: MapMergeAll( const scene::Path& root ) : m_path( root ){ } + bool pre( scene::Node& node ) const { Node_getTraversable( m_path.top() )->insert( node ); m_path.push( makeReference( node ) ); selectPath( m_path, true ); return false; } + void post( scene::Node& node ) const { m_path.pop(); } @@ -600,6 +626,7 @@ public: MapMergeEntities( const scene::Path& root ) : m_path( root ){ } + bool pre( scene::Node& node ) const { if ( node_is_worldspawn( node ) ) { scene::Node* world_node = Map_FindWorldspawn( g_map ); @@ -629,6 +656,7 @@ bool pre( scene::Node& node ) const { } return false; } + void post( scene::Node& node ) const { m_path.pop(); } @@ -643,6 +671,7 @@ public: TypeCasts(){ NodeContainedCast::install( m_casts ); } + NodeTypeCastTable& get(){ return m_casts; } @@ -660,9 +689,11 @@ scene::Traversable& get( NullType){ BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){ } + void release(){ delete this; } + scene::Node& node(){ return m_node; } @@ -672,6 +703,7 @@ scene::Node& node(){ void MergeMap( scene::Node& node ){ Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) ); } + void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){ NodeSmartReference node( ( new BasicContainer )->node() ); format.readGraph( node, in, GlobalEntityCreator() ); @@ -700,6 +732,7 @@ public: CloneAll( scene::Node& root ) : m_path( makeReference( root ) ){ } + bool pre( scene::Node& node ) const { if ( node.isRoot() ) { return false; @@ -710,6 +743,7 @@ bool pre( scene::Node& node ) const { return true; } + void post( scene::Node& node ) const { if ( node.isRoot() ) { return; @@ -731,6 +765,50 @@ scene::Node& Node_Clone( scene::Node& node ){ return clone; } +bool Node_instanceSelected( scene::Node& node ); + +class CloneAllSelected : public scene::Traversable::Walker +{ +mutable scene::Path m_path; +public: +CloneAllSelected( scene::Node& root ) + : m_path( makeReference( root ) ){ +} +bool pre( scene::Node& node ) const { + if ( node.isRoot() ) { + return false; + } + + if( Node_instanceSelected( node ) ){ + m_path.push( makeReference( node_clone( node ) ) ); + m_path.top().get().IncRef(); + } + + return true; +} +void post( scene::Node& node ) const { + if ( node.isRoot() ) { + return; + } + + if( Node_instanceSelected( node ) ){ + Node_getTraversable( m_path.parent() )->insert( m_path.top() ); + + m_path.top().get().DecRef(); + m_path.pop(); + } +} +}; + +scene::Node& Node_Clone_Selected( scene::Node& node ){ + scene::Node& clone = node_clone( node ); + scene::Traversable* traversable = Node_getTraversable( node ); + if ( traversable != 0 ) { + traversable->traverse( CloneAllSelected( clone ) ); + } + return clone; +} + typedef std::map EntityBreakdown; @@ -741,14 +819,17 @@ public: EntityBreakdownWalker( EntityBreakdown& entitymap ) : m_entitymap( entitymap ){ } + bool pre( const scene::Path& path, scene::Instance& instance ) const { Entity* entity = Node_getEntity( path.top() ); if ( entity != 0 ) { const EntityClass& eclass = entity->getEntityClass(); if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) { m_entitymap[eclass.name()] = 1; + } else + { + ++m_entitymap[eclass.name()]; } - else{ ++m_entitymap[eclass.name()]; } } return true; } @@ -758,13 +839,56 @@ void Scene_EntityBreakdown( EntityBreakdown& entitymap ){ GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) ); } +class CountStuffWalker : public scene::Graph::Walker +{ +int& m_patches; +int& m_ents_ingame; +int& m_groupents; +int& m_groupents_ingame; +public: +CountStuffWalker( int& patches, int& ents_ingame, int& groupents, int& groupents_ingame ) + : m_patches( patches ), m_ents_ingame( ents_ingame ), m_groupents( groupents ), m_groupents_ingame( groupents_ingame ){ +} +bool pre( const scene::Path& path, scene::Instance& instance ) const { + Patch* patch = Node_getPatch( path.top() ); + if( patch != 0 ){ + ++m_patches; + } + Entity* entity = Node_getEntity( path.top() ); + if ( entity != 0 ){ + if( entity->isContainer() ){ + ++m_groupents; + if( !string_equal_nocase( "func_group", entity->getKeyValue( "classname" ) ) && + !string_equal_nocase( "_decal", entity->getKeyValue( "classname" ) ) ){ + ++m_groupents_ingame; + ++m_ents_ingame; + } + return true; + } + if( !string_equal_nocase_n( "light", entity->getKeyValue( "classname" ), 5 ) && + !string_equal_nocase( "misc_model", entity->getKeyValue( "classname" ) ) ){ + ++m_ents_ingame; + } + } + return true; +} +}; + +void Scene_CountStuff( int& patches, int& ents_ingame, int& groupents, int& groupents_ingame ){ + GlobalSceneGraph().traverse( CountStuffWalker( patches, ents_ingame, groupents, groupents_ingame ) ); +} WindowPosition g_posMapInfoWnd( c_default_window_pos ); void DoMapInfo(){ ModalDialog dialog; - ui::Entry brushes_entry{ui::null}; - ui::Entry entities_entry{ui::null}; + ui::Widget w_brushes{ui::null}; + ui::Widget w_patches{ui::null}; + ui::Widget w_ents{ui::null}; + ui::Widget w_ents_ingame{ui::null}; + ui::Widget w_groupents{ui::null}; + ui::Widget w_groupents_ingame{ui::null}; + ui::ListStore EntityBreakdownWalker{ui::null}; ui::Window window = MainFrame_getWindow().create_dialog_window("Map Info", G_CALLBACK(dialog_delete_callback ), &dialog ); @@ -777,40 +901,90 @@ void DoMapInfo(){ { auto hbox = create_dialog_hbox( 4 ); - vbox.pack_start( hbox, FALSE, TRUE, 0 ); + vbox.pack_start( hbox, FALSE, FALSE, 0 ); { - auto table = create_dialog_table( 2, 2, 4, 4 ); + auto table = create_dialog_table( 3, 4, 4, 4 ); hbox.pack_start( table, TRUE, TRUE, 0 ); { - auto entry = ui::Entry(ui::New); - entry.show(); - table.attach(entry, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0}); - gtk_editable_set_editable( GTK_EDITABLE(entry), FALSE ); - - brushes_entry = entry; + auto label = ui::Label( "Total Brushes:" ); + label.show(); + table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0}); + gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); } { - auto entry = ui::Entry(ui::New); - entry.show(); - table.attach(entry, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0}); - gtk_editable_set_editable( GTK_EDITABLE(entry), FALSE ); - - entities_entry = entry; + auto label = ui::Label( "" ); + label.show(); + table.attach(label, {1, 2, 0, 1}, {GTK_FILL | GTK_EXPAND, 0}, {3, 0}); + w_brushes = label; } { - ui::Widget label = ui::Label( "Total Brushes" ); + auto label = ui::Label( "Total Patches" ); label.show(); - table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0}); + table.attach(label, {2, 3, 0, 1}, {GTK_FILL, 0}); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); } { - ui::Widget label = ui::Label( "Total Entities" ); + auto label = ui::Label( "" ); label.show(); - table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0}); + table.attach(label, {3, 4, 0, 1}, {GTK_FILL, 0}, {3, 0}); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); + w_patches = label; + } + { + auto label = ui::Label( "Total Entities:" ); + label.show(); + table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0}); + gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); + } + { + auto label = ui::Label( "" ); + label.show(); + table.attach(label, {1, 2, 1, 2}, {GTK_FILL | GTK_EXPAND, 0}, {3, 0}); + gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); + w_ents = label; } + { + auto label = ui::Label( "Ingame Entities:" ); + label.show(); + table.attach(label, {2, 3, 1, 2}, {GTK_FILL, 0}); + gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); + } + { + auto label = ui::Label( "" ); + label.show(); + table.attach(label, {3, 4, 1, 2}, {GTK_FILL | GTK_EXPAND, 0 }, {3, 0}); + gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); + w_ents_ingame = label; + } + { + auto label = ui::Label( "Group Entities:" ); + label.show(); + table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0}); + gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); + } + { + auto label = ui::Label( "" ); + label.show(); + table.attach(label, {1, 2, 2, 3}, {GTK_FILL | GTK_EXPAND, 0}, {3, 0}); + gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); + w_groupents = label; + } + { + auto label = ui::Label( "Ingame Group Entities:" ); + label.show(); + table.attach(label, {2, 3, 2, 3}, {GTK_FILL, 0}); + gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); + } + { + auto label = ui::Label( "" ); + label.show(); + table.attach(label, {3, 4, 2, 3}, {GTK_FILL | GTK_EXPAND, 0}, {3, 0}); + gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 ); + w_groupents_ingame = label; + } + } { auto vbox2 = create_dialog_vbox( 4 ); @@ -823,7 +997,7 @@ void DoMapInfo(){ } } { - ui::Widget label = ui::Label( "Entity breakdown" ); + ui::Widget label = ui::Label( "*** Entity breakdown ***" ); label.show(); vbox.pack_start( label, FALSE, TRUE, 0 ); gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 ); @@ -833,22 +1007,22 @@ void DoMapInfo(){ vbox.pack_start( scr, TRUE, TRUE, 0 ); { - ui::ListStore store = ui::ListStore(gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING )); + auto store = ui::ListStore::from(gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_UINT )); - ui::Widget view = ui::TreeView(ui::TreeModel(store )); - gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE ); + auto view = ui::TreeView(ui::TreeModel::from(store._handle)); + gtk_tree_view_set_headers_clickable(view, TRUE ); { auto renderer = ui::CellRendererText(ui::New); - GtkTreeViewColumn* column = ui::TreeViewColumn( "Entity", renderer, {{"text", 0}} ); - gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column ); + auto column = ui::TreeViewColumn( "Entity", renderer, {{"text", 0}} ); + gtk_tree_view_append_column(view, column ); gtk_tree_view_column_set_sort_column_id( column, 0 ); } { auto renderer = ui::CellRendererText(ui::New); - GtkTreeViewColumn* column = ui::TreeViewColumn( "Count", renderer, {{"text", 1}} ); - gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column ); + auto column = ui::TreeViewColumn( "Count", renderer, {{"text", 1}} ); + gtk_tree_view_append_column(view, column ); gtk_tree_view_column_set_sort_column_id( column, 1 ); } @@ -869,19 +1043,45 @@ void DoMapInfo(){ for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i ) { - char tmp[16]; - sprintf( tmp, "%u", Unsigned( ( *i ).second ) ); - EntityBreakdownWalker.append(0, (*i).first.c_str(), 1, tmp); + EntityBreakdownWalker.append(0, (*i).first.c_str(), 1, Unsigned( ( *i ).second )); } } EntityBreakdownWalker.unref(); - char tmp[16]; - sprintf( tmp, "%u", Unsigned( g_brushCount.get() ) ); - brushes_entry.text(tmp); - sprintf( tmp, "%u", Unsigned( g_entityCount.get() ) ); - entities_entry.text(tmp); + int n_patches = 0; + int n_ents_ingame = 0; + int n_groupents = 0; + int n_groupents_ingame = 0; + Scene_CountStuff( n_patches, n_ents_ingame, n_groupents, n_groupents_ingame ); + //globalOutputStream() << n_patches << n_ents_ingame << n_groupents << n_groupents_ingame << "\n"; + + char *markup; + + markup = g_markup_printf_escaped( "%u ", Unsigned( g_brushCount.get() ) ); + gtk_label_set_markup( GTK_LABEL( w_brushes ), markup ); + g_free( markup ); + + markup = g_markup_printf_escaped( "%i ", n_patches ); + gtk_label_set_markup( GTK_LABEL( w_patches ), markup ); + g_free( markup ); + + markup = g_markup_printf_escaped( "%u ", Unsigned( g_entityCount.get() ) ); + gtk_label_set_markup( GTK_LABEL( w_ents ), markup ); + g_free( markup ); + + markup = g_markup_printf_escaped( "%i ", n_ents_ingame ); + gtk_label_set_markup( GTK_LABEL( w_ents_ingame ), markup ); + g_free( markup ); + + markup = g_markup_printf_escaped( "%i ", n_groupents ); + gtk_label_set_markup( GTK_LABEL( w_groupents ), markup ); + g_free( markup ); + + markup = g_markup_printf_escaped( "%i ", n_groupents_ingame ); + gtk_label_set_markup( GTK_LABEL( w_groupents_ingame ), markup ); + g_free( markup ); + modal_dialog_show( window, dialog ); @@ -902,13 +1102,14 @@ ScopeTimer( const char* message ) : m_message( message ){ m_timer.start(); } + ~ScopeTimer(){ double elapsed_time = m_timer.elapsed_msec() / 1000.f; globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n"; } }; -CopiedString g_strLastFolder = ""; +CopiedString g_strLastMapFolder = ""; /* ================ @@ -917,11 +1118,19 @@ CopiedString g_strLastFolder = ""; */ void Map_LoadFile( const char *filename ){ + g_map.m_name = filename; + + // refresh VFS to apply new pak filtering based on mapname + // needed for daemon DPK VFS + VFS_Refresh(); + globalOutputStream() << "Loading map from " << filename << "\n"; ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" ); MRU_AddFile( filename ); - g_strLastFolder = g_path_get_dirname( filename ); + g_strLastMapFolder = g_path_get_dirname( filename ); + + bool switch_format = false; { ScopeTimer timer( "map load" ); @@ -938,8 +1147,8 @@ void Map_LoadFile( const char *filename ){ Map_Free(); } Brush_toggleFormat( i ); - g_map.m_name = filename; Map_UpdateTitle( g_map ); + g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() ); if ( format ) { format->wrongFormat = false; @@ -949,6 +1158,7 @@ void Map_LoadFile( const char *filename ){ if ( !format->wrongFormat ) { break; } + switch_format = !switch_format; } } @@ -970,9 +1180,7 @@ void Map_LoadFile( const char *filename ){ g_currentMap = &g_map; - // restart VFS to apply new pak filtering based on mapname - // needed for daemon DPK VFS - VFS_Restart(); + Brush_switchFormat( switch_format ); } class Excluder @@ -990,6 +1198,7 @@ public: ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude ) : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){ } + bool pre( scene::Node& node ) const { if ( m_exclude->excluded( node ) || node.isRoot() ) { m_skip = true; @@ -1001,6 +1210,7 @@ bool pre( scene::Node& node ) const { } return true; } + void post( scene::Node& node ) const { if ( m_skip ) { m_skip = false; @@ -1019,6 +1229,7 @@ public: AnyInstanceSelected( bool& selected ) : m_selected( selected ){ m_selected = false; } + void visit( scene::Instance& instance ) const { Selectable* selectable = Instance_getSelectable( instance ); if ( selectable != 0 @@ -1080,10 +1291,12 @@ mutable bool m_skip; bool selectedParent() const { return m_selected != 0; } + public: IncludeSelectedWalker( const scene::Traversable::Walker& walker ) : m_walker( walker ), m_selected( 0 ), m_skip( false ){ } + bool pre( scene::Node& node ) const { // include node if: // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected ) @@ -1100,6 +1313,7 @@ bool pre( scene::Node& node ) const { return false; } } + void post( scene::Node& node ) const { if ( m_skip ) { m_skip = false; @@ -1126,7 +1340,7 @@ void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& } void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){ - format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out ); + format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out, g_writeMapComments ); } void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){ @@ -1271,7 +1485,17 @@ void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_m =========================================================== */ -bool region_active; +bool region_active = false; + +ConstReferenceCaller &), PropertyImpl::Export> g_region_caller( region_active ); + +ToggleItem g_region_item( g_region_caller ); + +/*void Map_ToggleRegion(){ + region_active = !region_active; + g_region_item.update(); +}*/ + Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord ); Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord ); @@ -1325,6 +1549,7 @@ public: ExcludeAllWalker( bool exclude ) : m_exclude( exclude ){ } + bool pre( const scene::Path& path, scene::Instance& instance ) const { exclude_node( path.top(), m_exclude ); @@ -1348,6 +1573,7 @@ public: ExcludeSelectedWalker( bool exclude ) : m_exclude( exclude ){ } + bool pre( const scene::Path& path, scene::Instance& instance ) const { exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude ); return true; @@ -1365,6 +1591,7 @@ public: ExcludeRegionedWalker( bool exclude ) : m_exclude( exclude ){ } + bool pre( const scene::Path& path, scene::Instance& instance ) const { exclude_node( path.top(), @@ -1395,6 +1622,7 @@ void Scene_Exclude_Region( bool exclude ){ */ void Map_RegionOff(){ region_active = false; + g_region_item.update(); region_maxs[0] = g_MaxWorldCoord - 64; region_mins[0] = g_MinWorldCoord + 64; @@ -1408,6 +1636,7 @@ void Map_RegionOff(){ void Map_ApplyRegion( void ){ region_active = true; + g_region_item.update(); Scene_Exclude_Region( false ); } @@ -1424,6 +1653,7 @@ void Map_RegionSelectedBrushes( void ){ if ( GlobalSelectionSystem().countSelected() != 0 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) { region_active = true; + g_region_item.update(); Select_GetBounds( region_mins, region_maxs ); Scene_Exclude_Selected( false ); @@ -1482,7 +1712,7 @@ void Map_RegionBrush( void ){ bool Map_ImportFile( const char* filename ){ ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" ); - g_strLastFolder = g_path_get_dirname( filename ); + g_strLastMapFolder = g_path_get_dirname( filename ); bool success = false; @@ -1527,37 +1757,58 @@ bool Map_ImportFile( const char* filename ){ tryDecompile: - const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" ); + const char *type = GlobalRadiant().getGameDescriptionKeyValue( "q3map2_type" ); int n = string_length( path_get_extension( filename ) ); if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) { - StringBuffer output; - output.push_string( AppPath_get() ); - output.push_string( "q3map2." ); - output.push_string( RADIANT_EXECUTABLE ); - output.push_string( " -v -game " ); - output.push_string( ( type && *type ) ? type : "quake3" ); - output.push_string( " -fs_basepath \"" ); - output.push_string( EnginePath_get() ); - output.push_string( "\" -fs_homepath \"" ); - output.push_string( g_qeglobals.m_userEnginePath.c_str() ); - output.push_string( "\" -fs_game " ); - output.push_string( gamename_get() ); - output.push_string( " -convert -format " ); - output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" ); + std::string output; + output += AppPath_get(); + output += "q3map2"; + output += GDEF_OS_EXE_EXT; + + output += " -v -game "; + output += ( type && *type ) ? type : "quake3"; + output += " -fs_basepath \""; + output += EnginePath_get(); + output += "\" -fs_homepath \""; + output += g_qeglobals.m_userEnginePath.c_str(); + output += "\""; + + // extra pakpaths + for ( int i = 0; i < g_pakPathCount; i++ ) { + if ( g_strcmp0( g_strPakPath[i].c_str(), "") ) { + output += " -fs_pakpath \""; + output += g_strPakPath[i].c_str(); + output += "\""; + } + } + + // extra switches + if ( g_disableEnginePath ) { + output += " -fs_nobasepath "; + } + + if ( g_disableHomePath ) { + output += " -fs_nohomepath "; + } + + output += " -fs_game "; + output += gamename_get(); + output += " -convert -format "; + output += Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map"; if ( extension_equal( path_get_extension( filename ), "map" ) ) { - output.push_string( " -readmap " ); + output += " -readmap "; } - output.push_string( " \"" ); - output.push_string( filename ); - output.push_string( "\"" ); + output += " \""; + output += filename; + output += "\""; // run Q_Exec( NULL, output.c_str(), NULL, false, true ); // rebuild filename as "filenamewithoutext_converted.map" - output.clear(); - output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) ); - output.push_string( "_converted.map" ); + output = ""; + output.append( filename, string_length( filename ) - ( n + 1 ) ); + output += "_converted.map"; filename = output.c_str(); // open @@ -1607,40 +1858,53 @@ bool Map_SaveSelected( const char* filename ){ return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename ); } - class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker { -scene::Node& m_parent; + scene::Node& m_parent; + mutable bool m_emptyOldParent; + public: -ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){ +ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ), m_emptyOldParent( false ){ } + bool pre( const scene::Path& path, scene::Instance& instance ) const { - if ( path.top().get_pointer() != &m_parent - && Node_isPrimitive( path.top() ) ) { + if ( path.top().get_pointer() != &m_parent && ( Node_isPrimitive( path.top() ) || m_emptyOldParent ) ) { Selectable* selectable = Instance_getSelectable( instance ); - if ( selectable != 0 - && selectable->isSelected() - && path.size() > 1 ) { + if ( selectable && selectable->isSelected() && path.size() > 1 ) { return false; } } return true; } + void post( const scene::Path& path, scene::Instance& instance ) const { - if ( path.top().get_pointer() != &m_parent - && Node_isPrimitive( path.top() ) ) { + if ( path.top().get_pointer() == &m_parent ) + return; + + if ( Node_isPrimitive( path.top() ) ){ + m_emptyOldParent = false; Selectable* selectable = Instance_getSelectable( instance ); - if ( selectable != 0 - && selectable->isSelected() - && path.size() > 1 ) { + + if ( selectable && selectable->isSelected() && path.size() > 1 ){ scene::Node& parent = path.parent(); - if ( &parent != &m_parent ) { + if ( &parent != &m_parent ){ NodeSmartReference node( path.top().get() ); - Node_getTraversable( parent )->erase( node ); + scene::Traversable* traversable_parent = Node_getTraversable( parent ); + traversable_parent->erase( node ); Node_getTraversable( m_parent )->insert( node ); + if ( traversable_parent->empty() ) + m_emptyOldParent = true; } } } + else if ( m_emptyOldParent ){ + m_emptyOldParent = false; + // delete empty entities + Entity* entity = Node_getEntity( path.top() ); + if ( entity != 0 && path.top().get_pointer() != Map_FindWorldspawn( g_map ) && Node_getTraversable( path.top() )->empty() ) { + Path_deleteTop( path ); + } + } } }; @@ -1656,6 +1920,7 @@ public: CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){ m_count = 0; } + bool pre( const scene::Path& path, scene::Instance& instance ) const { if ( ++m_depth != 1 && path.top().get().isRoot() ) { return false; @@ -1668,6 +1933,7 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const { } return true; } + void post( const scene::Path& path, scene::Instance& instance ) const { --m_depth; } @@ -1754,6 +2020,7 @@ void Scene_parentSelected(){ public: ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){ } + void visit( scene::Instance& instance ) const { if ( &m_parent != &instance.path() ) { Path_parent( m_parent, instance.path() ); @@ -1771,7 +2038,6 @@ public: } - void NewMap(){ if ( ConfirmModified( "New Map" ) ) { Map_RegionOff(); @@ -1786,26 +2052,32 @@ const char* getMapsPath(){ return g_mapsPath.c_str(); } -const char* getLastFolderPath(){ - if (g_strLastFolder.empty()) { - GlobalPreferenceSystem().registerPreference( "LastFolder", CopiedStringImportStringCaller( g_strLastFolder ), CopiedStringExportStringCaller( g_strLastFolder ) ); - if (g_strLastFolder.empty()) { - g_strLastFolder = g_qeglobals.m_userGamePath; +const char* getLastMapFolderPath(){ + if (g_strLastMapFolder.empty()) { + GlobalPreferenceSystem().registerPreference( "LastMapFolder", make_property_string( g_strLastMapFolder ) ); + if (g_strLastMapFolder.empty()) { + StringOutputStream buffer( 1024 ); + buffer << getMapsPath(); + if ( !file_readable( buffer.c_str() ) ) { + buffer.clear(); + buffer << g_qeglobals.m_userGamePath.c_str() << "/"; + } + g_strLastMapFolder = buffer.c_str(); } } - return g_strLastFolder.c_str(); + return g_strLastMapFolder.c_str(); } const char* map_open( const char* title ){ - return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), MapFormat::Name(), true, false, false ); + return MainFrame_getWindow().file_dialog( TRUE, title, getLastMapFolderPath(), MapFormat::Name(), true, false, false ); } const char* map_import( const char* title ){ - return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), MapFormat::Name(), false, true, false ); + return MainFrame_getWindow().file_dialog( TRUE, title, getLastMapFolderPath(), MapFormat::Name(), false, true, false ); } const char* map_save( const char* title ){ - return MainFrame_getWindow().file_dialog( FALSE, title, getLastFolderPath(), MapFormat::Name(), false, false, true ); + return MainFrame_getWindow().file_dialog( FALSE, title, getLastMapFolderPath(), MapFormat::Name(), false, false, true ); } void OpenMap(){ @@ -1836,7 +2108,7 @@ bool Map_SaveAs(){ const char* filename = map_save( "Save Map" ); if ( filename != NULL ) { - g_strLastFolder = g_path_get_dirname( filename ); + g_strLastMapFolder = g_path_get_dirname( filename ); MRU_AddFile( filename ); Map_Rename( filename ); return Map_Save(); @@ -1854,6 +2126,7 @@ void SaveMap(){ } else if ( Map_Modified( g_map ) ) { Map_Save(); + MRU_AddFile( g_map.m_name.c_str() ); //add on saving, but not opening via cmd line: spoils the list } } @@ -1861,7 +2134,7 @@ void ExportMap(){ const char* filename = map_save( "Export Selection" ); if ( filename != NULL ) { - g_strLastFolder = g_path_get_dirname( filename ); + g_strLastMapFolder = g_path_get_dirname( filename ); Map_SaveSelected( filename ); } } @@ -1870,7 +2143,7 @@ void SaveRegion(){ const char* filename = map_save( "Export Region" ); if ( filename != NULL ) { - g_strLastFolder = g_path_get_dirname( filename ); + g_strLastMapFolder = g_path_get_dirname( filename ); Map_SaveRegion( filename ); } } @@ -1913,6 +2186,7 @@ public: BrushFindByIndexWalker( std::size_t index, scene::Path& path ) : m_index( index ), m_path( path ){ } + bool pre( scene::Node& node ) const { if ( Node_isPrimitive( node ) && m_index-- == 0 ) { m_path.push( makeReference( node ) ); @@ -1929,6 +2203,7 @@ public: EntityFindByIndexWalker( std::size_t index, scene::Path& path ) : m_index( index ), m_path( path ){ } + bool pre( scene::Node& node ) const { if ( Node_isEntity( node ) && m_index-- == 0 ) { m_path.push( makeReference( node ) ); @@ -1969,7 +2244,7 @@ void SelectBrush( int entitynum, int brushnum ){ } -class BrushFindIndexWalker : public scene::Graph::Walker +class BrushFindIndexWalker : public scene::Traversable::Walker { mutable const scene::Node* m_node; std::size_t& m_count; @@ -1977,9 +2252,10 @@ public: BrushFindIndexWalker( const scene::Node& node, std::size_t& count ) : m_node( &node ), m_count( count ){ } -bool pre( const scene::Path& path, scene::Instance& instance ) const { - if ( Node_isPrimitive( path.top() ) ) { - if ( m_node == path.top().get_pointer() ) { + +bool pre( scene::Node& node ) const { + if ( Node_isPrimitive( node ) ) { + if ( m_node == &node ) { m_node = 0; } if ( m_node ) { @@ -1990,7 +2266,7 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const { } }; -class EntityFindIndexWalker : public scene::Graph::Walker +class EntityFindIndexWalker : public scene::Traversable::Walker { mutable const scene::Node* m_node; std::size_t& m_count; @@ -1998,9 +2274,10 @@ public: EntityFindIndexWalker( const scene::Node& node, std::size_t& count ) : m_node( &node ), m_count( count ){ } -bool pre( const scene::Path& path, scene::Instance& instance ) const { - if ( Node_isEntity( path.top() ) ) { - if ( m_node == path.top().get_pointer() ) { + +bool pre( scene::Node& node ) const { + if ( Node_isEntity( node ) ) { + if ( m_node == &node ) { m_node = 0; } if ( m_node ) { @@ -2017,8 +2294,24 @@ static void GetSelectionIndex( int *ent, int *brush ){ if ( GlobalSelectionSystem().countSelected() != 0 ) { const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path(); - GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) ); - GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) ); + { + scene::Traversable* traversable = Node_getTraversable( path.parent() ); + if ( traversable != 0 && path.size() == 3 ) { + traversable->traverse( BrushFindIndexWalker( path.top(), count_brush ) ); + } + } + + { + scene::Traversable* traversable = Node_getTraversable( GlobalSceneGraph().root() ); + if ( traversable != 0 ) { + if( path.size() == 3 ){ + traversable->traverse( EntityFindIndexWalker( path.parent(), count_entity ) ); + } + else if ( path.size() == 2 ){ + traversable->traverse( EntityFindIndexWalker( path.top(), count_entity ) ); + } + } + } } *brush = int(count_brush); *ent = int(count_entity); @@ -2102,7 +2395,8 @@ void DoFind(){ } void Map_constructPreferences( PreferencesPage& page ){ - page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap ); + page.appendCheckBox( "", "Load last map at startup", g_bLoadLastMap ); + page.appendCheckBox( "", "Add entity and brush number comments on map write", g_writeMapComments ); } @@ -2112,6 +2406,7 @@ std::size_t m_unrealised; public: MapEntityClasses() : m_unrealised( 1 ){ } + void realise(){ if ( --m_unrealised == 0 ) { if ( g_map.m_resource != 0 ) { @@ -2120,6 +2415,7 @@ void realise(){ } } } + void unrealise(){ if ( ++m_unrealised == 1 ) { if ( g_map.m_resource != 0 ) { @@ -2139,6 +2435,7 @@ std::size_t m_unrealised; public: MapModuleObserver() : m_unrealised( 1 ){ } + void realise(){ if ( --m_unrealised == 0 ) { ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" ); @@ -2148,6 +2445,7 @@ void realise(){ g_mapsPath = buffer.c_str(); } } + void unrealise(){ if ( ++m_unrealised == 1 ) { g_mapsPath = ""; @@ -2161,16 +2459,18 @@ CopiedString g_strLastMap; bool g_bLoadLastMap = false; void Map_Construct(){ - GlobalCommands_insert( "RegionOff", FreeCaller() ); - GlobalCommands_insert( "RegionSetXY", FreeCaller() ); - GlobalCommands_insert( "RegionSetBrush", FreeCaller() ); - GlobalCommands_insert( "RegionSetSelection", FreeCaller(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) ); - - GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) ); - GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) ); - GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) ); - - PreferencesDialog_addSettingsPreferences( FreeCaller1() ); + GlobalCommands_insert( "RegionOff", makeCallbackF(RegionOff) ); + GlobalCommands_insert( "RegionSetXY", makeCallbackF(RegionXY) ); + GlobalCommands_insert( "RegionSetBrush", makeCallbackF(RegionBrush) ); + //GlobalCommands_insert( "RegionSetSelection", makeCallbackF(RegionSelected), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) ); + GlobalToggles_insert( "RegionSetSelection", makeCallbackF(RegionSelected), ToggleItem::AddCallbackCaller( g_region_item ), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) ); + + GlobalPreferenceSystem().registerPreference( "LastMap", make_property_string( g_strLastMap ) ); + GlobalPreferenceSystem().registerPreference( "LoadLastMap", make_property_string( g_bLoadLastMap ) ); + GlobalPreferenceSystem().registerPreference( "MapInfoDlg", make_property( g_posMapInfoWnd ) ); + GlobalPreferenceSystem().registerPreference( "WriteMapComments", make_property_string( g_writeMapComments ) ); + + PreferencesDialog_addSettingsPreferences( makeCallbackF(Map_constructPreferences) ); GlobalEntityClassManager().attach( g_MapEntityClasses ); Radiant_attachHomePathsObserver( g_MapModuleObserver );