+ for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
+ {
+ if ( i ) {
+ 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;
+ }
+ g_map.m_resource->attach( g_map );
+ if ( format ) {
+ if ( !format->wrongFormat ) {
+ break;
+ }
+ }
+ }
+
+ Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
+ }
+
+ globalOutputStream() << "--- LoadMapFile ---\n";
+ globalOutputStream() << g_map.m_name.c_str() << "\n";
+
+ globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
+ globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
+
+ //GlobalEntityCreator().printStatistics();
+
+ //
+ // move the view to a start position
+ //
+ Map_StartPosition();
+
+ g_currentMap = &g_map;
+
+ // restart VFS to apply new pak filtering based on mapname
+ // needed for daemon DPK VFS
+ VFS_Restart();
+}
+
+class Excluder
+{
+public:
+virtual bool excluded( scene::Node& node ) const = 0;
+};
+
+class ExcludeWalker : public scene::Traversable::Walker
+{
+const scene::Traversable::Walker& m_walker;
+const Excluder* m_exclude;
+mutable bool m_skip;
+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;
+ return false;
+ }
+ else
+ {
+ m_walker.pre( node );
+ }
+ return true;
+}
+void post( scene::Node& node ) const {
+ if ( m_skip ) {
+ m_skip = false;
+ }
+ else
+ {
+ m_walker.post( node );
+ }
+}
+};
+
+class AnyInstanceSelected : public scene::Instantiable::Visitor
+{
+bool& m_selected;
+public:
+AnyInstanceSelected( bool& selected ) : m_selected( selected ){
+ m_selected = false;
+}
+void visit( scene::Instance& instance ) const {
+ Selectable* selectable = Instance_getSelectable( instance );
+ if ( selectable != 0
+ && selectable->isSelected() ) {
+ m_selected = true;
+ }
+}
+};
+
+bool Node_instanceSelected( scene::Node& node ){
+ scene::Instantiable* instantiable = Node_getInstantiable( node );
+ ASSERT_NOTNULL( instantiable );
+ bool selected;
+ instantiable->forEachInstance( AnyInstanceSelected( selected ) );
+ return selected;
+}
+
+class SelectedDescendantWalker : public scene::Traversable::Walker
+{
+bool& m_selected;
+public:
+SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
+ m_selected = false;
+}
+
+bool pre( scene::Node& node ) const {
+ if ( node.isRoot() ) {
+ return false;
+ }
+
+ if ( Node_instanceSelected( node ) ) {
+ m_selected = true;
+ }
+
+ return true;
+}
+};
+
+bool Node_selectedDescendant( scene::Node& node ){
+ bool selected;
+ Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
+ return selected;
+}
+
+class SelectionExcluder : public Excluder
+{
+public:
+bool excluded( scene::Node& node ) const {
+ return !Node_selectedDescendant( node );
+}
+};
+
+class IncludeSelectedWalker : public scene::Traversable::Walker
+{
+const scene::Traversable::Walker& m_walker;
+mutable std::size_t m_selected;
+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 )
+ if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
+ if ( Node_instanceSelected( node ) ) {
+ ++m_selected;
+ }
+ m_walker.pre( node );
+ return true;
+ }
+ else
+ {
+ m_skip = true;
+ return false;
+ }
+}
+void post( scene::Node& node ) const {
+ if ( m_skip ) {
+ m_skip = false;
+ }
+ else
+ {
+ if ( Node_instanceSelected( node ) ) {
+ --m_selected;
+ }
+ m_walker.post( node );
+ }
+}
+};
+
+void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
+ scene::Traversable* traversable = Node_getTraversable( root );
+ if ( traversable != 0 ) {
+#if 0
+ traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
+#else
+ traversable->traverse( IncludeSelectedWalker( walker ) );
+#endif
+ }
+}
+
+void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
+ format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
+}
+
+void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
+ scene::Traversable* traversable = Node_getTraversable( root );
+ if ( traversable != 0 ) {
+ traversable->traverse( walker );
+ }
+}
+
+class RegionExcluder : public Excluder
+{
+public:
+bool excluded( scene::Node& node ) const {
+ return node.excluded();
+}
+};
+
+void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
+ scene::Traversable* traversable = Node_getTraversable( root );
+ if ( traversable != 0 ) {
+ traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
+ }
+}
+
+bool Map_SaveRegion( const char *filename ){
+ AddRegionBrushes();
+
+ bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
+
+ RemoveRegionBrushes();
+
+ return success;
+}
+
+
+void Map_RenameAbsolute( const char* absolute ){
+ Resource* resource = GlobalReferenceCache().capture( absolute );
+ NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
+ resource->setNode( clone.get_pointer() );
+
+ {
+ //ScopeTimer timer("clone subgraph");
+ Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
+ }
+
+ g_map.m_resource->detach( g_map );
+ GlobalReferenceCache().release( g_map.m_name.c_str() );
+
+ g_map.m_resource = resource;
+
+ g_map.m_name = absolute;
+ Map_UpdateTitle( g_map );
+
+ g_map.m_resource->attach( g_map );
+ // refresh VFS to apply new pak filtering based on mapname
+ // needed for daemon DPK VFS
+ VFS_Refresh();
+}
+
+void Map_Rename( const char* filename ){
+ if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
+ ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
+
+ Map_RenameAbsolute( filename );
+
+ SceneChangeNotify();
+ }
+ else
+ {
+ SaveReferences();
+ }
+}
+
+bool Map_Save(){
+ Pointfile_Clear();
+
+ ScopeTimer timer( "map save" );
+ SaveReferences();
+ return true; // assume success..
+}
+
+/*
+ ===========
+ Map_New
+
+ ===========
+ */
+void Map_New(){
+ //globalOutputStream() << "Map_New\n";
+
+ g_map.m_name = "unnamed.map";
+ Map_UpdateTitle( g_map );
+
+ {
+ g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
+// ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
+ g_map.m_resource->attach( g_map );
+
+ SceneChangeNotify();
+ }
+
+ FocusViews( g_vector3_identity, 0 );
+
+ g_currentMap = &g_map;
+
+ // restart VFS to apply new pak filtering based on mapname
+ // needed for daemon DPK VFS
+ VFS_Restart();
+}
+
+extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
+
+void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
+ /*!
+ \todo we need to make sure that the player start IS inside the region and bail out if it's not
+ the compiler will refuse to compile a map with a player_start somewhere in empty space..
+ for now, let's just print an error
+ */
+
+ Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
+
+ for ( int i = 0 ; i < 3 ; i++ )
+ {
+ if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
+ globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
+ break;
+ }
+ }
+
+ // write the info_playerstart
+ char sTmp[1024];
+ sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
+ Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
+ sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
+ Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
+}
+
+/*
+ ===========================================================
+
+ REGION
+
+ ===========================================================
+ */
+bool region_active;
+Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
+Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
+
+scene::Node* region_sides[6];
+scene::Node* region_startpoint = 0;
+
+/*
+ ===========
+ AddRegionBrushes
+ a regioned map will have temp walls put up at the region boundary
+ \todo TODO TTimo old implementation of region brushes
+ we still add them straight in the worldspawn and take them out after the map is saved
+ with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
+ ===========
+ */
+void AddRegionBrushes( void ){
+ int i;
+
+ for ( i = 0; i < 6; i++ )
+ {
+ region_sides[i] = &GlobalBrushCreator().createBrush();
+ Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
+ }
+
+ region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
+
+ ConstructRegionBrushes( region_sides, region_mins, region_maxs );
+ ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
+
+ Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
+}
+
+void RemoveRegionBrushes( void ){
+ for ( std::size_t i = 0; i < 6; i++ )
+ {
+ Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
+ }
+ Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
+}
+
+inline void exclude_node( scene::Node& node, bool exclude ){
+ exclude
+ ? node.enable( scene::Node::eExcluded )
+ : node.disable( scene::Node::eExcluded );
+}
+
+class ExcludeAllWalker : public scene::Graph::Walker
+{
+bool m_exclude;
+public:
+ExcludeAllWalker( bool exclude )
+ : m_exclude( exclude ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+ exclude_node( path.top(), m_exclude );
+
+ return true;
+}
+};
+
+void Scene_Exclude_All( bool exclude ){
+ GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
+}
+
+bool Instance_isSelected( const scene::Instance& instance ){
+ const Selectable* selectable = Instance_getSelectable( instance );
+ return selectable != 0 && selectable->isSelected();
+}
+
+class ExcludeSelectedWalker : public scene::Graph::Walker
+{
+bool m_exclude;
+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;
+}
+};
+
+void Scene_Exclude_Selected( bool exclude ){
+ GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
+}
+
+class ExcludeRegionedWalker : public scene::Graph::Walker
+{
+bool m_exclude;
+public:
+ExcludeRegionedWalker( bool exclude )
+ : m_exclude( exclude ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+ exclude_node(
+ path.top(),
+ !(
+ (
+ aabb_intersects_aabb(
+ instance.worldAABB(),
+ aabb_for_minmax( region_mins, region_maxs )
+ ) != 0
+ ) ^ m_exclude
+ )
+ );
+
+ return true;
+}
+};
+
+void Scene_Exclude_Region( bool exclude ){
+ GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
+}
+
+/*
+ ===========
+ Map_RegionOff
+
+ Other filtering options may still be on
+ ===========
+ */
+void Map_RegionOff(){
+ region_active = false;
+
+ region_maxs[0] = g_MaxWorldCoord - 64;
+ region_mins[0] = g_MinWorldCoord + 64;
+ region_maxs[1] = g_MaxWorldCoord - 64;
+ region_mins[1] = g_MinWorldCoord + 64;
+ region_maxs[2] = g_MaxWorldCoord - 64;
+ region_mins[2] = g_MinWorldCoord + 64;
+
+ Scene_Exclude_All( false );
+}
+
+void Map_ApplyRegion( void ){
+ region_active = true;
+
+ Scene_Exclude_Region( false );
+}
+
+
+/*
+ ========================
+ Map_RegionSelectedBrushes
+ ========================
+ */
+void Map_RegionSelectedBrushes( void ){
+ Map_RegionOff();
+
+ if ( GlobalSelectionSystem().countSelected() != 0
+ && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
+ region_active = true;
+ Select_GetBounds( region_mins, region_maxs );
+
+ Scene_Exclude_Selected( false );
+
+ GlobalSelectionSystem().setSelectedAll( false );
+ }
+}
+
+
+/*
+ ===========
+ Map_RegionXY
+ ===========
+ */
+void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
+ Map_RegionOff();
+
+ region_mins[0] = x_min;
+ region_maxs[0] = x_max;
+ region_mins[1] = y_min;
+ region_maxs[1] = y_max;
+ region_mins[2] = g_MinWorldCoord + 64;
+ region_maxs[2] = g_MaxWorldCoord - 64;
+
+ Map_ApplyRegion();
+}
+
+void Map_RegionBounds( const AABB& bounds ){
+ Map_RegionOff();
+
+ region_mins = vector3_subtracted( bounds.origin, bounds.extents );
+ region_maxs = vector3_added( bounds.origin, bounds.extents );
+
+ deleteSelection();
+
+ Map_ApplyRegion();
+}
+
+/*
+ ===========
+ Map_RegionBrush
+ ===========
+ */
+void Map_RegionBrush( void ){
+ if ( GlobalSelectionSystem().countSelected() != 0 ) {
+ scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
+ Map_RegionBounds( instance.worldAABB() );
+ }
+}
+
+//
+//================
+//Map_ImportFile
+//================
+//
+bool Map_ImportFile( const char* filename ){
+ ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
+
+ g_strLastFolder = g_path_get_dirname( filename );
+
+ bool success = false;
+
+ if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
+ goto tryDecompile;
+ }
+
+ {
+ const MapFormat* format = NULL;
+ const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
+ if ( string_not_empty( moduleName ) ) {
+ format = ReferenceAPI_getMapModules().findModule( moduleName );
+ }
+
+ if ( format ) {
+ format->wrongFormat = false;
+ }
+ Resource* resource = GlobalReferenceCache().capture( filename );
+ resource->refresh(); // avoid loading old version if map has changed on disk since last import
+ if ( !resource->load() ) {
+ GlobalReferenceCache().release( filename );
+ goto tryDecompile;
+ }
+ if ( format ) {
+ if ( format->wrongFormat ) {
+ GlobalReferenceCache().release( filename );
+ goto tryDecompile;
+ }
+ }
+ NodeSmartReference clone( NewMapRoot( "" ) );
+ Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
+ Map_gatherNamespaced( clone );
+ Map_mergeClonedNames();
+ MergeMap( clone );
+ success = true;
+ GlobalReferenceCache().release( filename );
+ }
+
+ SceneChangeNotify();
+
+ return success;
+
+tryDecompile:
+
+ const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "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" );
+ if ( extension_equal( path_get_extension( filename ), "map" ) ) {
+ output.push_string( " -readmap " );
+ }
+ output.push_string( " \"" );
+ output.push_string( filename );
+ output.push_string( "\"" );
+
+ // 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" );
+ filename = output.c_str();
+
+ // open
+ Resource* resource = GlobalReferenceCache().capture( filename );
+ resource->refresh(); // avoid loading old version if map has changed on disk since last import
+ if ( !resource->load() ) {
+ GlobalReferenceCache().release( filename );
+ goto tryDecompile;
+ }
+ NodeSmartReference clone( NewMapRoot( "" ) );
+ Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
+ Map_gatherNamespaced( clone );
+ Map_mergeClonedNames();
+ MergeMap( clone );
+ success = true;
+ GlobalReferenceCache().release( filename );
+ }
+
+ SceneChangeNotify();
+ return success;
+}
+
+/*
+ ===========
+ Map_SaveFile
+ ===========
+ */
+bool Map_SaveFile( const char* filename ){
+ ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
+ bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
+ if ( success ) {
+ // refresh VFS to apply new pak filtering based on mapname
+ // needed for daemon DPK VFS
+ VFS_Refresh();
+ }
+ return success;
+}
+
+//
+//===========
+//Map_SaveSelected
+//===========
+//
+// Saves selected world brushes and whole entities with partial/full selections
+//
+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;
+public:
+ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+ if ( path.top().get_pointer() != &m_parent
+ && Node_isPrimitive( path.top() ) ) {
+ Selectable* selectable = Instance_getSelectable( instance );
+ if ( selectable != 0
+ && 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() ) ) {
+ Selectable* selectable = Instance_getSelectable( instance );
+ if ( selectable != 0
+ && selectable->isSelected()
+ && path.size() > 1 ) {
+ scene::Node& parent = path.parent();
+ if ( &parent != &m_parent ) {
+ NodeSmartReference node( path.top().get() );
+ Node_getTraversable( parent )->erase( node );
+ Node_getTraversable( m_parent )->insert( node );
+ }
+ }
+ }
+}
+};
+
+void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
+ graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
+}
+
+class CountSelectedBrushes : public scene::Graph::Walker
+{
+std::size_t& m_count;
+mutable std::size_t m_depth;
+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;
+ }
+ Selectable* selectable = Instance_getSelectable( instance );
+ if ( selectable != 0
+ && selectable->isSelected()
+ && Node_isPrimitive( path.top() ) ) {
+ ++m_count;
+ }
+ return true;
+}
+void post( const scene::Path& path, scene::Instance& instance ) const {
+ --m_depth;
+}
+};
+
+std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
+ std::size_t count;
+ graph.traverse( CountSelectedBrushes( count ) );
+ return count;
+}
+
+enum ENodeType
+{
+ eNodeUnknown,
+ eNodeMap,
+ eNodeEntity,
+ eNodePrimitive,
+};
+
+const char* nodetype_get_name( ENodeType type ){
+ if ( type == eNodeMap ) {
+ return "map";
+ }
+ if ( type == eNodeEntity ) {
+ return "entity";
+ }
+ if ( type == eNodePrimitive ) {
+ return "primitive";
+ }
+ return "unknown";
+}
+
+ENodeType node_get_nodetype( scene::Node& node ){
+ if ( Node_isEntity( node ) ) {
+ return eNodeEntity;
+ }
+ if ( Node_isPrimitive( node ) ) {
+ return eNodePrimitive;
+ }
+ return eNodeUnknown;
+}
+
+bool contains_entity( scene::Node& node ){
+ return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
+}
+
+bool contains_primitive( scene::Node& node ){
+ return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
+}
+
+ENodeType node_get_contains( scene::Node& node ){
+ if ( contains_entity( node ) ) {
+ return eNodeEntity;
+ }
+ if ( contains_primitive( node ) ) {
+ return eNodePrimitive;
+ }
+ return eNodeUnknown;
+}
+
+void Path_parent( const scene::Path& parent, const scene::Path& child ){
+ ENodeType contains = node_get_contains( parent.top() );
+ ENodeType type = node_get_nodetype( child.top() );
+
+ if ( contains != eNodeUnknown && contains == type ) {
+ NodeSmartReference node( child.top().get() );
+ Path_deleteTop( child );
+ Node_getTraversable( parent.top() )->insert( node );
+ SceneChangeNotify();
+ }
+ else
+ {
+ globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
+ }
+}
+
+void Scene_parentSelected(){
+ UndoableCommand undo( "parentSelected" );
+
+ if ( GlobalSelectionSystem().countSelected() > 1 ) {
+ class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
+ {
+ const scene::Path& m_parent;
+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() );
+ }
+ }
+ };
+
+ ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
+ GlobalSelectionSystem().foreachSelected( visitor );
+ }
+ else
+ {
+ globalOutputStream() << "failed - did not find two selected nodes.\n";
+ }
+}
+
+
+
+void NewMap(){
+ if ( ConfirmModified( "New Map" ) ) {
+ Map_RegionOff();
+ Map_Free();
+ Map_New();
+ }
+}
+
+CopiedString g_mapsPath;
+
+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;
+ }
+ }
+ return g_strLastFolder.c_str();
+}
+
+const char* map_open( const char* title ){
+ return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), 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 );
+}
+
+const char* map_save( const char* title ){
+ return MainFrame_getWindow().file_dialog( FALSE, title, getLastFolderPath(), MapFormat::Name(), false, false, true );
+}
+
+void OpenMap(){
+ if ( !ConfirmModified( "Open Map" ) ) {
+ return;
+ }
+
+ const char* filename = map_open( "Open Map" );
+
+ if ( filename != NULL ) {
+ MRU_AddFile( filename );
+ Map_RegionOff();
+ Map_Free();
+ Map_LoadFile( filename );
+ }
+}
+
+void ImportMap(){
+ const char* filename = map_import( "Import Map" );
+
+ if ( filename != NULL ) {
+ UndoableCommand undo( "mapImport" );
+ Map_ImportFile( filename );
+ }
+}
+
+bool Map_SaveAs(){
+ const char* filename = map_save( "Save Map" );
+
+ if ( filename != NULL ) {
+ g_strLastFolder = g_path_get_dirname( filename );
+ MRU_AddFile( filename );
+ Map_Rename( filename );
+ return Map_Save();
+ }
+ return false;
+}
+
+void SaveMapAs(){
+ Map_SaveAs();
+}
+
+void SaveMap(){
+ if ( Map_Unnamed( g_map ) ) {
+ SaveMapAs();
+ }
+ else if ( Map_Modified( g_map ) ) {
+ Map_Save();
+ }
+}
+
+void ExportMap(){
+ const char* filename = map_save( "Export Selection" );
+
+ if ( filename != NULL ) {
+ g_strLastFolder = g_path_get_dirname( filename );
+ Map_SaveSelected( filename );
+ }
+}
+
+void SaveRegion(){
+ const char* filename = map_save( "Export Region" );
+
+ if ( filename != NULL ) {
+ g_strLastFolder = g_path_get_dirname( filename );
+ Map_SaveRegion( filename );
+ }
+}
+
+
+void RegionOff(){
+ Map_RegionOff();
+ SceneChangeNotify();
+}
+
+void RegionXY(){
+ Map_RegionXY(
+ g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
+ g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
+ g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
+ g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
+ );
+ SceneChangeNotify();
+}
+
+void RegionBrush(){
+ Map_RegionBrush();
+ SceneChangeNotify();
+}
+
+void RegionSelected(){
+ Map_RegionSelectedBrushes();
+ SceneChangeNotify();
+}
+
+
+
+
+
+class BrushFindByIndexWalker : public scene::Traversable::Walker