X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fnetradiant.git;a=blobdiff_plain;f=radiant%2Fmap.cpp;h=939ce40ea44ea51fcb71babbec5d716ae1652f9a;hp=f4a23c16d3c57b1c85a74eb421073a7864c53847;hb=7c469116587a352454b7740764c5756484ac843a;hpb=12b372f89ce109a4db9d510884fbe7d05af79870 diff --git a/radiant/map.cpp b/radiant/map.cpp index f4a23c16..939ce40e 100644 --- a/radiant/map.cpp +++ b/radiant/map.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 1999-2006 Id Software, Inc. and contributors. +Copyright (C) 1999-2007 id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. @@ -19,2279 +19,1302 @@ along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "map.h" - -#include "debugging/debugging.h" - -#include "imap.h" -#include "iselection.h" -#include "iundo.h" -#include "ibrush.h" -#include "ifilter.h" -#include "ireference.h" -#include "ifiletypes.h" -#include "ieclass.h" -#include "irender.h" -#include "ientity.h" -#include "editable.h" -#include "ifilesystem.h" -#include "namespace.h" -#include "moduleobserver.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "scenelib.h" -#include "transformlib.h" -#include "selectionlib.h" -#include "instancelib.h" -#include "traverselib.h" -#include "maplib.h" -#include "eclasslib.h" -#include "cmdlib.h" -#include "stream/textfilestream.h" -#include "os/path.h" -#include "uniquenames.h" -#include "modulesystem/singletonmodule.h" -#include "modulesystem/moduleregistry.h" -#include "stream/stringstream.h" - -#include "gtkutil/filechooser.h" -#include "timer.h" -#include "select.h" -#include "plugin.h" -#include "filetypes.h" -#include "gtkdlgs.h" -#include "entityinspector.h" -#include "points.h" -#include "qe3.h" -#include "camwindow.h" -#include "xywindow.h" -#include "mainframe.h" +#include "stdafx.h" +#include +#if defined (__linux__) || defined (__APPLE__) +#include +#endif #include "preferences.h" -#include "referencecache.h" -#include "mru.h" -#include "commands.h" -#include "autosave.h" +#include "mainframe.h" +#include "gtkmisc.h" +#include "filters.h" -class NameObserver -{ - UniqueNames& m_names; - CopiedString m_name; +extern MainFrame* g_pParentWnd; - void construct() - { - if(!empty()) - { - //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n"; - m_names.insert(name_read(c_str())); - } - } - void destroy() - { - if(!empty()) - { - //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n"; - m_names.erase(name_read(c_str())); - } - } +int modified; // for quit confirmation (0 = clean, 1 = unsaved, + // 2 = autosaved, but not regular saved) - NameObserver& operator=(const NameObserver& other); -public: - NameObserver(UniqueNames& names) : m_names(names) - { - construct(); - } - 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; -}; +char currentmap[1024]; -class BasicNamespace : public Namespace +brush_t active_brushes; // brushes currently being displayed +brush_t selected_brushes; // highlighted + +face_t *selected_face; +brush_t *selected_face_brush; + +brush_t filtered_brushes; // brushes that have been filtered or regioned + +entity_t entities; // head/tail of doubly linked list + +entity_t *world_entity = NULL; // "classname" "worldspawn" ! + +void Map_Init() { - typedef std::map Names; - Names m_names; - UniqueNames m_uniqueNames; -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"); - //globalOutputStream() << "detach: " << reinterpret_cast(setName) << "\n"; - detachObserver(NameObserver::NameChangedCaller((*i).second)); - m_names.erase(i); - } + Map_Free(); +} - void makeUnique(const char* name, const NameCallback& setName) const - { - char buffer[1024]; - name_write(buffer, m_uniqueNames.make_unique(name_read(name))); - setName(buffer); - } - void mergeNames(const BasicNamespace& other) const - { - typedef std::list SetNameCallbacks; - typedef std::map NameGroups; - NameGroups groups; +bool g_bCancel_Map_LoadFile; // Hydra: moved this here - UniqueNames uniqueNames(other.m_uniqueNames); +// TTimo +// need that in a variable, will have to tweak depending on the game +int g_MaxWorldCoord = 64*1024; +int g_MinWorldCoord = -64*1024; - for(Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i) - { - groups[(*i).second.c_str()].push_back((*i).first); - } +// the max size we allow on brushes, this is dependant on world coords too +// makes more sense to say smaller I think? +int g_MaxBrushSize = (g_MaxWorldCoord-1)*2; - for(NameGroups::iterator i = groups.begin(); i != groups.end(); ++i) - { - name_t uniqueName(uniqueNames.make_unique(name_read((*i).first.c_str()))); - uniqueNames.insert(uniqueName); +void AddRegionBrushes (void); +void RemoveRegionBrushes (void); - char buffer[1024]; - name_write(buffer, uniqueName); +/* +============================================================= - //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n"; + Cross map selection saving - SetNameCallbacks& setNameCallbacks = (*i).second; + this could fuck up if you have only part of a complex entity selected... +============================================================= +*/ - for(SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j) - { - (*j)(buffer); - } - } - } -}; +brush_t between_brushes; +entity_t between_entities; -BasicNamespace g_defaultNamespace; -BasicNamespace g_cloneNamespace; +bool g_bRestoreBetween = false; -class NamespaceAPI +void Map_SaveBetween (void) { - Namespace* m_namespace; -public: - typedef Namespace Type; - STRING_CONSTANT(Name, "*"); - - NamespaceAPI() - { - m_namespace = &g_defaultNamespace; - } - Namespace* getTable() + if (g_pParentWnd->ActiveXY()) { - return m_namespace; + g_bRestoreBetween = true; + g_pParentWnd->ActiveXY()->Copy(); } -}; - -typedef SingletonModule NamespaceModule; -typedef Static StaticNamespaceModule; -StaticRegisterModule staticRegisterDefaultNamespace(StaticNamespaceModule::instance()); + return; +} +void Map_RestoreBetween (void) +{ + if (g_pParentWnd->ActiveXY() && g_bRestoreBetween) + g_pParentWnd->ActiveXY()->Paste(); +} -std::list g_cloned; +//============================================================================ -inline Namespaced* Node_getNamespaced(scene::Node& node) +bool CheckForTinyBrush(brush_t* b, int n, float fSize) { - return NodeTypeCast::cast(node); + bool bTiny = false; + for (int i=0 ; i<3 ; i++) + { + if (b->maxs[i] - b->mins[i] < fSize) + bTiny = true; + } + if (bTiny) + Sys_Printf("Possible problem brush (too small) #%i ", n); + return bTiny; } -void Node_gatherNamespaced(scene::Node& node) +void Map_BuildBrushData(void) { - Namespaced* namespaced = Node_getNamespaced(node); - if(namespaced != 0) + brush_t *b, *next; + + if (active_brushes.next == NULL) + return; + + Sys_BeginWait (); // this could take a while + + int n = 0; + for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=next) { - g_cloned.push_back(namespaced); + next = b->next; + Brush_Build( b, true, false, false ); + if (!b->brush_faces || (g_PrefsDlg.m_bCleanTiny && CheckForTinyBrush(b, n++, g_PrefsDlg.m_fTinySize))) + { + Brush_Free (b); + Sys_Printf ("Removed degenerate brush\n"); + } } + Sys_EndWait(); } -class GatherNamespaced : public scene::Traversable::Walker +entity_t *Map_FindClass (const char *cname) { -public: - bool pre(scene::Node& node) const - { - Node_gatherNamespaced(node); - return true; - } -}; + entity_t *ent; -void Map_gatherNamespaced(scene::Node& root) + for (ent = entities.next ; ent != &entities ; ent=ent->next) + { + if (!strcmp(cname, ValueForKey (ent, "classname"))) + return ent; + } + return NULL; +} + +/* +================ +Map_Free +free all map elements, reinitialize the structures that depend on them +================ +*/ +void Map_Free (void) { - Node_traverseSubgraph(root, GatherNamespaced()); + g_bRestoreBetween = false; + if (selected_brushes.next && + (selected_brushes.next != &selected_brushes)) + { + if (gtk_MessageBox (g_pParentWnd->m_pWidget, "Copy selection?", " ", MB_YESNO) == IDYES) + Map_SaveBetween (); + } + + QERApp_ActiveShaders_SetInUse( false ); + Pointfile_Clear (); + g_qeglobals.d_num_entities = 0; + + if (!active_brushes.next) + { + // first map + active_brushes.prev = active_brushes.next = &active_brushes; + selected_brushes.prev = selected_brushes.next = &selected_brushes; + filtered_brushes.prev = filtered_brushes.next = &filtered_brushes; + entities.prev = entities.next = &entities; + } + else + { + // free selected faces array + g_ptrSelectedFaces.RemoveAll(); + g_ptrSelectedFaceBrushes.RemoveAll(); + while (active_brushes.next != &active_brushes) + Brush_Free (active_brushes.next); + while (selected_brushes.next != &selected_brushes) + Brush_Free (selected_brushes.next); + while (filtered_brushes.next != &filtered_brushes) + Brush_Free (filtered_brushes.next); + while (entities.next != &entities) + Entity_Free (entities.next); + } + + if (world_entity) + Entity_Free(world_entity); + world_entity = NULL; } -void Map_mergeClonedNames() +entity_t *AngledEntity() { - for(std::list::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i) + entity_t *ent = Map_FindClass ("info_player_start"); + if (!ent) { - (*i)->setNamespace(g_cloneNamespace); + ent = Map_FindClass ("info_player_deathmatch"); } - g_cloneNamespace.mergeNames(g_defaultNamespace); - for(std::list::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i) + if (!ent) { - (*i)->setNamespace(g_defaultNamespace); + ent = Map_FindClass ("info_player_deathmatch"); } - - g_cloned.clear(); + if (!ent) + { + ent = Map_FindClass ("team_CTF_redplayer"); + } + if (!ent) + { + ent = Map_FindClass ("team_CTF_blueplayer"); + } + if (!ent) + { + ent = Map_FindClass ("team_CTF_redspawn"); + } + if (!ent) + { + ent = Map_FindClass ("team_CTF_bluespawn"); + } + return ent; } -class WorldNode +// +// move the view to a start position +// +void Map_StartPosition() { - scene::Node* m_node; -public: - WorldNode() - : m_node(0) - { - } - void set(scene::Node* node) + entity_t *ent = AngledEntity(); + + g_pParentWnd->GetCamWnd()->Camera()->angles[PITCH] = 0; + if (ent) { - if(m_node != 0) - m_node->DecRef(); - m_node = node; - if(m_node != 0) - m_node->IncRef(); + GetVectorForKey (ent, "origin", g_pParentWnd->GetCamWnd()->Camera()->origin); + GetVectorForKey (ent, "origin", g_pParentWnd->GetXYWnd()->GetOrigin()); + g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = FloatForKey (ent, "angle"); } - scene::Node* get() const + else { - return m_node; + g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = 0; + VectorCopy (vec3_origin, g_pParentWnd->GetCamWnd()->Camera()->origin); + VectorCopy (vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin()); } -}; +} -class Map; -void Map_SetValid(Map& map, bool valid); -void Map_UpdateTitle(const Map& map); -void Map_SetWorldspawn(Map& map, scene::Node* node); +void Map_FreeEntities(CPtrArray *ents) +{ + int i, j, num_ents, num_brushes; + entity_t* e; + CPtrArray* brushes; + num_ents = ents->GetSize(); + for(i=0; iGetAt(i); + brushes = (CPtrArray*)e->pData; + num_brushes = brushes->GetSize(); + for(j=0; jGetAt(j)); + brushes->RemoveAll(); + delete (CPtrArray*)e->pData; + e->pData = NULL; + Entity_Free(e); + } + ents->RemoveAll(); +} -class Map : public ModuleObserver +/*!\todo Possibly make the import Undo-friendly by calling Undo_End for new brushes and ents */ +void Map_ImportEntities(CPtrArray *ents, bool bAddSelected = false) { -public: - CopiedString m_name; - Resource* m_resource; - bool m_valid; + int num_ents, num_brushes; + CPtrArray *brushes; + vec3_t mins, maxs; + entity_t *e; + brush_t *b; + face_t *f; + int i,j; - bool m_modified; - void (*m_modified_changed)(const Map&); + GPtrArray *new_ents = g_ptr_array_new(); - typedef std::vector MapValidCallbacks; - MapValidCallbacks m_mapValidCallbacks; + g_qeglobals.bPrimitBrushes = false; - WorldNode m_world_node; // "classname" "worldspawn" ! + brush_t *pBrushList = (bAddSelected) ? &selected_brushes : &active_brushes; - Map() : m_resource(0), m_valid(false), m_modified_changed(Map_UpdateTitle) + bool bDoneBPCheck = false; + g_qeglobals.bNeedConvert = false; + // HACK: find out if this map file was a BP one + // check the first brush in the file that is NOT a patch + // this will not be necessary when we allow both formats in the same file + num_ents = ents->GetSize(); + for(i=0; !bDoneBPCheck && iGetAt(i); + brushes = (CPtrArray*)e->pData; + num_brushes = brushes->GetSize(); + for(j=0; !bDoneBPCheck && jGetAt(j); + if(b->patchBrush) continue; + bDoneBPCheck = true; + int BP_param = -1; + if(b->bBrushDef && !g_qeglobals.m_bBrushPrimitMode) + BP_param = 0; + else if(!b->bBrushDef && g_qeglobals.m_bBrushPrimitMode) + BP_param = 1; + + if(BP_param != -1) + { + switch(BP_MessageBox(BP_param)) + { + case 0: + Map_FreeEntities(ents); + return; + case 1: + g_qeglobals.bNeedConvert = true; + break; + case 2: + g_qeglobals.bNeedConvert = false; + break; + } + } + } } - void realise() + // process the entities into the world geometry + num_ents = ents->GetSize(); + for(i=0; iGetAt(i); + brushes = (CPtrArray*)e->pData; + + num_brushes = brushes->GetSize(); + // link brushes into entity + for(j=0; jGetAt(j)); + g_qeglobals.d_parsed_brushes++; + } + brushes->RemoveAll(); + delete brushes; + e->pData = NULL; + + // set entity origin + GetVectorForKey (e, "origin", e->origin); + // set entity eclass + /*!\todo Make SetKeyValue check for "classname" change and assign appropriate eclass */ + e->eclass = Eclass_ForName (ValueForKey (e, "classname"), + (e->brushes.onext != &e->brushes)); + + // go through all parsed brushes and build stuff + for(b = e->brushes.onext; b!=&e->brushes; b=b->onext) { - if(Map_Unnamed(*this)) + for(f = b->brush_faces; f != NULL; f = f->next) { - g_map.m_resource->setNode(NewMapRoot("").get_pointer()); - MapFile* map = Node_getMapFile(*g_map.m_resource->getNode()); - if(map != 0) - { - map->save(); - } + f->pShader = QERApp_Shader_ForName(f->texdef.GetName()); + f->d_texture = f->pShader->getTexture(); } - else + + // when brushes are in final state, build the planes and windings + // NOTE: also converts BP brushes if g_qeglobals.bNeedConvert is true + Brush_Build(b); + } + +//#define TERRAIN_HACK +#undef TERRAIN_HACK + +#ifdef TERRAIN_HACK + if ((strcmp(ValueForKey(e, "terrain"),"1") == 0 && strcmp(e->eclass->name,"func_group") == 0)) + { + + // two aux pointers to the shaders used in the terrain entity + // we don't keep refcount on them since they are only temporary + // this avoids doing expensive lookups by name for all faces + IShader *pTerrainShader, *pCaulk; + + pTerrainShader = NULL; + pCaulk = QERApp_Shader_ForName(SHADER_CAULK); + + for(b = e->brushes.onext; b!=&e->brushes; b=b->onext) { - m_resource->load(); + if (pTerrainShader == NULL) + for(f = b->brush_faces; f != NULL; f = f->next) + if (strcmp(f->texdef.GetName(), SHADER_CAULK)!=0) + pTerrainShader = f->pShader; + + if (pTerrainShader) + { + for(f = b->brush_faces; f != NULL; f = f->next) + { + if (strcmp(f->texdef.GetName(), SHADER_CAULK)!=0) // not caulk + Face_SetShader(f, pTerrainShader->getName()); + else + Face_SetShader(f, pCaulk->getName()); + } + } + else + Sys_Printf("WARNING: no terrain shader found for brush\n"); } + } +#endif - GlobalSceneGraph().insert_root(*m_resource->getNode()); +#define PATCH_HACK +#ifdef PATCH_HACK + for(b = e->brushes.onext; b!=&e->brushes; b=b->onext) + { + // patch hack, to be removed when dependency on brush_faces is removed + if (b->patchBrush) + { + Patch_CalcBounds(b->pPatch, mins, maxs); + for (int i=0; i<3; i++) + { + if ((int)mins[i] == (int)maxs[i]) + { + mins[i] -= 4; + maxs[i] += 4; + } + } + Brush_Resize(b, mins, maxs); + Brush_Build(b); + } + } +#endif + // add brush for fixedsize entity + if (e->eclass->fixedsize) + { + vec3_t mins, maxs; + VectorAdd (e->eclass->mins, e->origin, mins); + VectorAdd (e->eclass->maxs, e->origin, maxs); + b = Brush_Create (mins, maxs, &e->eclass->texdef); + Entity_LinkBrush(e, b); + Brush_Build(b); + } - AutoSave_clear(); + for(b = e->brushes.onext; b!=&e->brushes; b=b->onext) + Brush_AddToList(b, pBrushList); - Map_SetValid(g_map, true); + if (strcmp(e->eclass->name, "worldspawn") == 0) + { + if (world_entity) + { + while(e->brushes.onext != &e->brushes) + { + b = e->brushes.onext; + Entity_UnlinkBrush(b); + Entity_LinkBrush(world_entity, b); + } + Entity_Free(e); + } + else + { + world_entity = e; + } } - } - void unrealise() - { - if(m_resource != 0) + else if (strcmp(e->eclass->name, "group_info") == 0) { - Map_SetValid(g_map, false); - Map_SetWorldspawn(g_map, 0); + // it's a group thing! + Group_Add(e); + Entity_Free(e); + } + else + { + // fix target/targetname collisions + if ((g_PrefsDlg.m_bDoTargetFix) && (strcmp(ValueForKey(e, "target"), "") != 0)) + { + GPtrArray *t_ents = g_ptr_array_new(); + entity_t *e_target; + const char *target = ValueForKey(e, "target"); + qboolean bCollision=FALSE; + // check the current map entities for an actual collision + for (e_target = entities.next; e_target != &entities; e_target = e_target->next) + { + if(!strcmp(target, ValueForKey(e_target, "target"))) + { + bCollision = TRUE; + // make sure the collision is not between two imported entities + for(j=0; j<(int)new_ents->len; j++) + { + if(e_target == g_ptr_array_index(new_ents, j)) + bCollision = FALSE; + } + } + } + + // find the matching targeted entity(s) + if(bCollision) + { + for(j=num_ents-1; j>0; j--) + { + e_target = (entity_t*)ents->GetAt(j); + if(e_target != NULL && e_target != e) + { + const char *targetname = ValueForKey(e_target, "targetname"); + if( (targetname != NULL) && (strcmp(target, targetname) == 0) ) + g_ptr_array_add(t_ents, (gpointer)e_target); + } + } + if(t_ents->len > 0) + { + // link the first to get a unique target/targetname + Entity_Connect(e, (entity_t*)g_ptr_array_index(t_ents,0)); + // set the targetname of the rest of them manually + for(j = 1; j < (int)t_ents->len; j++) + SetKeyValue( (entity_t*)g_ptr_array_index(t_ents, j), "targetname", ValueForKey(e, "target") ); + } + g_ptr_array_free(t_ents, FALSE); + } + } - GlobalUndoSystem().clear(); + // add the entity to the end of the entity list + Entity_AddToList(e, &entities); + g_qeglobals.d_num_entities++; - GlobalSceneGraph().erase_root(); + // keep a list of ents added to avoid testing collisions against them + g_ptr_array_add(new_ents, (gpointer)e); } } -}; + g_ptr_array_free(new_ents, FALSE); -Map g_map; -Map* g_currentMap = 0; + ents->RemoveAll(); -void Map_addValidCallback(Map& map, const Callback& callback) -{ - map.m_mapValidCallbacks.push_back(callback); + g_qeglobals.bNeedConvert = false; } -bool Map_Valid(const Map& map) +void Map_Import(IDataStream *in, const char *type, bool bAddSelected) { - return map.m_valid; + CPtrArray ents; + + g_pParentWnd->GetSynapseClient().ImportMap(in, &ents, type); + Map_ImportEntities(&ents, bAddSelected); } -void Map_SetValid(Map& map, bool valid) +/* +================ +Map_LoadFile +================ +*/ +void Map_LoadFile (const char *filename) { - map.m_valid = valid; - std::for_each(map.m_mapValidCallbacks.begin(), map.m_mapValidCallbacks.end(), CallbackInvoke()); -} + clock_t start, finish; + double elapsed_time; + start = clock(); + Sys_BeginWait (); + Select_Deselect(); + /*! + \todo FIXME TTimo why is this commented out? + stability issues maybe? or duplicate feature? + forcing to show the console during map load was a good thing IMO + */ + //SetInspectorMode(W_CONSOLE); + Sys_Printf ("Loading map from %s\n", filename ); -const char* Map_Name(const Map& map) -{ - return map.m_name.c_str(); -} + Map_Free (); + //++timo FIXME: maybe even easier to have Group_Init called from Map_Free? + Group_Init(); + g_qeglobals.d_num_entities = 0; + g_qeglobals.d_parsed_brushes = 0; -bool Map_Unnamed(const Map& map) -{ - return string_equal(Map_Name(map), "unnamed.map"); -} -inline const MapFormat& MapFormat_forFile(const char* filename) -{ - const char* moduleName = findModuleName(GetFileTypeRegistry(), MapFormat::Name(), path_get_extension(filename)); - MapFormat* format = Radiant_getMapModules().findModule(moduleName); - ASSERT_MESSAGE(format != 0, "map format not found for file " << makeQuoted(filename)); - return *format; -} + // cancel the map loading process + // used when conversion between standard map format and BP format is required and the user cancels the process + g_bCancel_Map_LoadFile = false; -const MapFormat& Map_getFormat(const Map& map) -{ - return MapFormat_forFile(Map_Name(map)); -} + strcpy (currentmap, filename); + g_bScreenUpdates = false; // leo: avoid redraws while loading the map (see fenris:1952) -bool Map_Modified(const Map& map) -{ - return map.m_modified; -} + // prepare to let the map module do the parsing + FileStream file; + const char* type = strrchr(filename,'.'); + if(type!=NULL) type++; + // NOTE TTimo opening has binary doesn't make a lot of sense + // but opening as text confuses the scriptlib parser + // this may be a problem if we "rb" and use the XML parser, might have an incompatibility + if (file.Open(filename, "rb")) + Map_Import(&file, type); + else + Sys_FPrintf(SYS_ERR, "ERROR: failed to open %s for read\n", filename); + file.Close(); -void Map_SetModified(Map& map, bool modified) -{ - if(map.m_modified ^ modified) + g_bScreenUpdates = true; + + if (g_bCancel_Map_LoadFile) { - map.m_modified = modified; + Sys_Printf("Map_LoadFile canceled\n"); + Map_New(); + Sys_EndWait(); + return; + } - map.m_modified_changed(map); + if (!world_entity) + { + Sys_Printf ("No worldspawn in map.\n"); + Map_New (); + Sys_EndWait(); + return; } -} + finish = clock(); + elapsed_time = (double)(finish - start) / CLOCKS_PER_SEC; -void Map_UpdateTitle(const Map& map) -{ - Sys_SetTitle(map.m_name.c_str(), Map_Modified(map)); -} + Sys_Printf ("--- LoadMapFile ---\n"); + Sys_Printf ("%s\n", filename ); + Sys_Printf ("%5i brushes\n", g_qeglobals.d_parsed_brushes ); + Sys_Printf ("%5i entities\n", g_qeglobals.d_num_entities); + Sys_Printf ("%5.2f second(s) load time\n", elapsed_time ); + Sys_EndWait(); -scene::Node* Map_GetWorldspawn(const Map& map) -{ - return map.m_world_node.get(); -} + Map_RestoreBetween (); -void Map_SetWorldspawn(Map& map, scene::Node* node) -{ - map.m_world_node.set(node); -} + // + // move the view to a start position + // + Map_StartPosition(); + Map_RegionOff (); -// TTimo -// need that in a variable, will have to tweak depending on the game -float g_MaxWorldCoord = 64*1024; -float g_MinWorldCoord = -64*1024; + modified = false; + Sys_SetTitle (filename); -void AddRegionBrushes (void); -void RemoveRegionBrushes (void); + Texture_ShowInuse (); + QERApp_SortActiveShaders(); + Sys_UpdateWindows (W_ALL); +} -/* -================ -Map_Free -free all map elements, reinitialize the structures that depend on them -================ +/*! +=========== +Supporting functions for Map_SaveFile, builds a CPtrArray with the filtered / non filtered brushes +=========== */ -void Map_Free() +void CleanFilter(entity_t *ent) { - Pointfile_Clear(); - - g_map.m_resource->detach(g_map); - GlobalReferenceCache().release(g_map.m_name.c_str()); - g_map.m_resource = 0; - - FlushReferences(); - - g_currentMap = 0; + if (ent->pData) + { + delete static_cast(ent->pData); + ent->pData = NULL; + } } -class EntityFindByClassname : public scene::Graph::Walker +/*! +filters out the region brushes if necessary +returns true if this entity as a whole is out of the region +(if all brushes are filtered out, then the entity will be completely dropped .. except if it's worldspawn of course) +*/ +bool FilterChildren(entity_t *ent, bool bRegionOnly = false, bool bSelectedOnly = false) { - 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(ent->brushes.onext == &ent->brushes) + return false; + // entity without a brush, ignore it... this can be caused by Undo + + // filter fixedsize ents by their eclass bounding box + // don't add their brushes + if (ent->eclass->fixedsize) { - if(m_entity == 0) + if(bSelectedOnly && !IsBrushSelected(ent->brushes.onext)) + return false; + + if(bRegionOnly && region_active) { - Entity* entity = Node_getEntity(path.top()); - if(entity != 0 - && string_equal(m_name, entity->getKeyValue("classname"))) - { - m_entity = entity; - } + for (int i=0 ; i<3 ; i++) + { + if ((ent->origin[i] + ent->eclass->mins[i]) > region_maxs[i]) + return false; + if ((ent->origin[i] + ent->eclass->maxs[i]) < region_mins[i]) + return false; + } } - return true; } -}; - -Entity* Scene_FindEntityByClass(const char* name) -{ - Entity* entity; - GlobalSceneGraph().traverse(EntityFindByClassname(name, entity)); - return entity; -} - -Entity *Scene_FindPlayerStart() -{ - typedef const char* StaticString; - StaticString strings[] = { - "info_player_start", - "info_player_deathmatch", - "team_CTF_redplayer", - "team_CTF_blueplayer", - "team_CTF_redspawn", - "team_CTF_bluespawn", - }; - typedef const StaticString* StaticStringIterator; - for(StaticStringIterator i = strings, end = strings+(sizeof(strings)/sizeof(StaticString)); i != end; ++i) + else { - Entity* entity = Scene_FindEntityByClass(*i); - if(entity != 0) + for (brush_t *b = ent->brushes.onext ; b != &ent->brushes ; b=b->onext) { - return entity; + // set flag to use brushprimit_texdef + if(g_qeglobals.m_bBrushPrimitMode) + b->bBrushDef = true; + else + b->bBrushDef = false; + + // add brush, unless it's excluded by region + if ( !(bRegionOnly && Map_IsBrushFiltered(b)) && + !(bSelectedOnly && !IsBrushSelected(b)) ) + ((CPtrArray*)ent->pData)->Add(b); } + + if (((CPtrArray*)ent->pData)->GetSize() <= 0) + return false; } - return 0; + return true; } -// -// move the view to a start position -// +entity_t *region_startpoint = NULL; +void Map_ExportEntities(CPtrArray* ents, bool bRegionOnly = false, bool bSelectedOnly = false) +{ + int i; + entity_t *e; + /*! + \todo the entity_t needs to be reworked and asbtracted some more -void FocusViews(const Vector3& point, float angle) -{ - CamWnd& camwnd = *g_pParentWnd->GetCamWnd(); - Camera_setOrigin(camwnd, point); - Vector3 angles(Camera_getAngles(camwnd)); - angles[CAMERA_PITCH] = 0; - angles[CAMERA_YAW] = angle; - Camera_setAngles(camwnd, angles); - - XYWnd* xywnd = g_pParentWnd->GetXYWnd(); - xywnd->SetOrigin(point); -} + keeping the entity_t as the struct providing access to a list of map objects, a list of epairs and various other info? + but separating some more the data that belongs to the entity_t and the 'sons' data + on a side note, I don't think that doing that with linked list would be a good thing -#include "stringio.h" + for now, we use the blind void* in entity_t casted to a CPtrArray of brush_t* to hand out a list of the brushes for map write + the next step is very likely to be a change of the brush_t* to a more abstract object? + */ -void Map_StartPosition() -{ - Entity* entity = Scene_FindPlayerStart(); + FilterChildren(world_entity, bRegionOnly, bSelectedOnly); + ents->Add(world_entity); - if (entity) + for (e=entities.next ; e!=&entities ; e=e->next) { - Vector3 origin; - string_parse_vector3(entity->getKeyValue("origin"), origin); - FocusViews(origin, string_read_float(entity->getKeyValue("angle"))); + // not sure this still happens, probably safe to leave it in + if ((!strcmp(ValueForKey (e, "classname"), "worldspawn")) && (e!=world_entity)) + { + Sys_FPrintf(SYS_ERR, "Dropping parasite worldspawn entity\n"); + continue; + } + + // entities which brushes are completely filtered out by regioning are not printed to the map + if (FilterChildren(e, bRegionOnly, bSelectedOnly)) + ents->Add(e); } - else + + if (bRegionOnly && region_active) { - FocusViews(g_vector3_identity, 0); + for(i=0; i<6; i++) + ((CPtrArray*)world_entity->pData)->Add(region_sides[i]); + + ents->Add(region_startpoint); } } - -inline bool node_is_worldspawn(scene::Node& node) +void Map_Export(IDataStream *out, const char *type, bool bRegionOnly, bool bSelectedOnly) { - Entity* entity = Node_getEntity(node); - return entity != 0 && string_equal(entity->getKeyValue("classname"), "worldspawn"); -} + entity_t *e; + CPtrArray ents; -// use first worldspawn -class entity_updateworldspawn : public scene::Traversable::Walker -{ -public: - bool pre(scene::Node& node) const - { - if(node_is_worldspawn(node)) - { - if(Map_GetWorldspawn(g_map) == 0) - { - Map_SetWorldspawn(g_map, &node); - } - } - return false; - } -}; + if (bRegionOnly && region_active) + AddRegionBrushes(); -scene::Node* Map_FindWorldspawn(Map& map) -{ - Map_SetWorldspawn(map, 0); + // create the filters + world_entity->pData = new CPtrArray(); + for(e = entities.next; e != &entities; e = e->next) + e->pData = new CPtrArray(); - Node_getTraversable(GlobalSceneGraph().root())->traverse(entity_updateworldspawn()); + Map_ExportEntities(&ents, bRegionOnly, bSelectedOnly); - return Map_GetWorldspawn(map); -} + g_pParentWnd->GetSynapseClient().ExportMap(&ents, out, type); + // cleanup the filters + CleanFilter(world_entity); + for (e=entities.next ; e!=&entities ; e=e->next) + CleanFilter(e); -class CollectAllWalker : public scene::Traversable::Walker -{ - scene::Node& m_root; - 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); - return false; - } -}; - -void Node_insertChildFirst(scene::Node& parent, scene::Node& child) -{ - UnsortedNodeSet nodes; - Node_getTraversable(parent)->traverse(CollectAllWalker(parent, nodes)); - Node_getTraversable(parent)->insert(child); - - for(UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i) - { - Node_getTraversable(parent)->insert((*i)); - } -} - -scene::Node& createWorldspawn() -{ - NodeSmartReference worldspawn(GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert("worldspawn", true))); - Node_insertChildFirst(GlobalSceneGraph().root(), worldspawn); - return worldspawn; -} - -void Map_UpdateWorldspawn(Map& map) -{ - if(Map_FindWorldspawn(map) == 0) - { - Map_SetWorldspawn(map, &createWorldspawn()); - } -} - -scene::Node& Map_FindOrInsertWorldspawn(Map& map) -{ - Map_UpdateWorldspawn(map); - return *Map_GetWorldspawn(map); -} - - -class MapMergeAll : public scene::Traversable::Walker -{ - mutable scene::Path m_path; -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(); - } -}; - -class MapMergeEntities : public scene::Traversable::Walker -{ - mutable scene::Path m_path; -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); - if(world_node == 0) - { - Map_SetWorldspawn(g_map, &node); - Node_getTraversable(m_path.top().get())->insert(node); - m_path.push(makeReference(node)); - Node_getTraversable(node)->traverse(SelectChildren(m_path)); - } - else - { - m_path.push(makeReference(*world_node)); - Node_getTraversable(node)->traverse(MapMergeAll(m_path)); - } - } - else - { - Node_getTraversable(m_path.top())->insert(node); - m_path.push(makeReference(node)); - if(node_is_group(node)) - { - Node_getTraversable(node)->traverse(SelectChildren(m_path)); - } - else - { - selectPath(m_path, true); - } - } - return false; - } - void post(scene::Node& node) const - { - m_path.pop(); - } -}; - -class BasicContainer : public scene::Node::Symbiot -{ - class TypeCasts - { - NodeTypeCastTable m_casts; - public: - TypeCasts() - { - NodeContainedCast::install(m_casts); - } - NodeTypeCastTable& get() - { - return m_casts; - } - }; - - scene::Node m_node; - TraversableNodeSet m_traverse; -public: - - typedef LazyStatic StaticTypeCasts; - - scene::Traversable& get(NullType) - { - return m_traverse; - } - - BasicContainer() : m_node(this, this, StaticTypeCasts::instance().get()) - { - } - void release() - { - delete this; - } - scene::Node& node() - { - return m_node; - } -}; - -/// Merges the map graph rooted at \p node into the global scene-graph. -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()); - Map_gatherNamespaced(node); - Map_mergeClonedNames(); - MergeMap(node); -} - -inline scene::Cloneable* Node_getCloneable(scene::Node& node) -{ - return NodeTypeCast::cast(node); -} - -inline scene::Node& node_clone(scene::Node& node) -{ - scene::Cloneable* cloneable = Node_getCloneable(node); - if(cloneable != 0) - { - return cloneable->clone(); - } - - return (new scene::NullNode)->node(); -} - -class CloneAll : public scene::Traversable::Walker -{ - mutable scene::Path m_path; -public: - CloneAll(scene::Node& root) - : m_path(makeReference(root)) - { - } - bool pre(scene::Node& node) const - { - if(node.isRoot()) - { - return false; - } - - m_path.push(makeReference(node_clone(node))); - m_path.top().get().IncRef(); - - return true; - } - void post(scene::Node& node) const - { - if(node.isRoot()) - { - return; - } - - Node_getTraversable(m_path.parent())->insert(m_path.top()); - - m_path.top().get().DecRef(); - m_path.pop(); - } -}; - -scene::Node& Node_Clone(scene::Node& node) -{ - scene::Node& clone = node_clone(node); - scene::Traversable* traversable = Node_getTraversable(node); - if(traversable != 0) - { - traversable->traverse(CloneAll(clone)); - } - return clone; -} - - -typedef std::map EntityBreakdown; - -class EntityBreakdownWalker : public scene::Graph::Walker -{ - EntityBreakdown& m_entitymap; -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()]; - } - return true; - } -}; - -void Scene_EntityBreakdown(EntityBreakdown& entitymap) -{ - GlobalSceneGraph().traverse(EntityBreakdownWalker(entitymap)); -} - - -WindowPosition g_posMapInfoWnd(c_default_window_pos); - -void DoMapInfo() -{ - ModalDialog dialog; - GtkEntry* brushes_entry; - GtkEntry* entities_entry; - GtkListStore* EntityBreakdownWalker; - - GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Map Info", G_CALLBACK(dialog_delete_callback), &dialog); - - window_set_position(window, g_posMapInfoWnd); - - { - GtkVBox* vbox = create_dialog_vbox(4, 4); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox)); - - { - GtkHBox* hbox = create_dialog_hbox(4); - gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, TRUE, 0); - - { - GtkTable* table = create_dialog_table(2, 2, 4, 4); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(table), TRUE, TRUE, 0); - - { - GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); - gtk_widget_show(GTK_WIDGET(entry)); - gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_entry_set_editable(entry, FALSE); - - brushes_entry = entry; - } - { - GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); - gtk_widget_show(GTK_WIDGET(entry)); - gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_entry_set_editable(entry, FALSE); - - entities_entry = entry; - } - { - GtkWidget* label = gtk_label_new ("Total Brushes"); - gtk_widget_show (label); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - } - { - GtkWidget* label = gtk_label_new ("Total Entities"); - gtk_widget_show (label); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - } - } - { - GtkVBox* vbox2 = create_dialog_vbox(4); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox2), FALSE, FALSE, 0); - - { - GtkButton* button = create_dialog_button("Close", G_CALLBACK(dialog_button_ok), &dialog); - gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(button), FALSE, FALSE, 0); - } - } - } - { - GtkWidget* label = gtk_label_new ("Entity breakdown"); - gtk_widget_show (label); - gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, TRUE, 0); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - } - { - GtkScrolledWindow* scr = create_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4); - gtk_box_pack_start(GTK_BOX (vbox), GTK_WIDGET(scr), TRUE, TRUE, 0); - - { - GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); - - GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(view), TRUE); - - { - GtkCellRenderer* renderer = gtk_cell_renderer_text_new(); - GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Entity", renderer, "text", 0, 0); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); - gtk_tree_view_column_set_sort_column_id(column, 0); - } - - { - GtkCellRenderer* renderer = gtk_cell_renderer_text_new(); - GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Count", renderer, "text", 1, 0); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); - gtk_tree_view_column_set_sort_column_id(column, 1); - } - - gtk_widget_show(view); - - gtk_container_add(GTK_CONTAINER(scr), view); - - EntityBreakdownWalker = store; - } - } - } - - // Initialize fields - - { - EntityBreakdown entitymap; - Scene_EntityBreakdown(entitymap); - - for(EntityBreakdown::iterator i=entitymap.begin(); i != entitymap.end(); ++i) - { - char tmp[16]; - sprintf (tmp, "%u", Unsigned((*i).second)); - GtkTreeIter iter; - gtk_list_store_append(GTK_LIST_STORE(EntityBreakdownWalker), &iter); - gtk_list_store_set(GTK_LIST_STORE(EntityBreakdownWalker), &iter, 0, (*i).first.c_str(), 1, tmp, -1); - } - } - - g_object_unref(G_OBJECT(EntityBreakdownWalker)); - - char tmp[16]; - sprintf (tmp, "%u", Unsigned(g_brushCount.get())); - gtk_entry_set_text (GTK_ENTRY (brushes_entry), tmp); - sprintf (tmp, "%u", Unsigned(g_entityCount.get())); - gtk_entry_set_text (GTK_ENTRY (entities_entry), tmp); - - modal_dialog_show(window, dialog); - - // save before exit - window_get_position(window, g_posMapInfoWnd); - - gtk_widget_destroy(GTK_WIDGET(window)); -} - - - -class ScopeTimer -{ - Timer m_timer; - const char* m_message; -public: - 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"; - } -}; - -/* -================ -Map_LoadFile -================ -*/ - -void Map_LoadFile (const char *filename) -{ - globalOutputStream() << "Loading map from " << filename << "\n"; - ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map"); - - g_map.m_name = filename; - Map_UpdateTitle(g_map); - - { - ScopeTimer timer("map load"); - - g_map.m_resource = GlobalReferenceCache().capture(g_map.m_name.c_str()); - g_map.m_resource->attach(g_map); - - Node_getTraversable(GlobalSceneGraph().root())->traverse(entity_updateworldspawn()); - } - - globalOutputStream() << "--- LoadMapFile ---\n"; - globalOutputStream() << g_map.m_name.c_str() << "\n"; - - globalOutputStream() << makeLeftJustified(Unsigned(g_brushCount.get()), 5) << " primitive\n"; - globalOutputStream() << makeLeftJustified(Unsigned(g_entityCount.get()), 5) << " entities\n"; - - // - // move the view to a start position - // - Map_StartPosition(); - - g_currentMap = &g_map; -} - -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); -} - -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; + if (bRegionOnly && region_active) + RemoveRegionBrushes(); } -extern void ConstructRegionBrushes(scene::Node* brushes[6], const Vector3& region_mins, const Vector3& region_maxs); - -void ConstructRegionStartpoint(scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs) +const char* filename_get_extension(const char* filename) { - /*! - \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); + const char* type = strrchr(filename,'.'); + if(type != NULL) + return ++type; + return ""; } -/* -=========================================================== - - 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 +Map_SaveFile +\todo FIXME remove the use_region, this is broken .. work with a global flag to set region mode or not =========== */ -void AddRegionBrushes (void) -{ - int i; - - for(i=0; i<6; i++) - { - region_sides[i] = &GlobalBrushCreator().createBrush(); - Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(*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(*region_startpoint); -} - -void RemoveRegionBrushes (void) +void Map_SaveFile (const char *filename, qboolean use_region ) { - 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; - } -}; + clock_t start, finish; + double elapsed_time; + start = clock(); + Sys_Printf("Saving map to %s\n",filename); -void Scene_Exclude_All(bool exclude) -{ - GlobalSceneGraph().traverse(ExcludeAllWalker(exclude)); -} + Pointfile_Clear (); -bool Instance_isSelected(const scene::Instance& instance) -{ - const Selectable* selectable = Instance_getSelectable(instance); - return selectable != 0 && selectable->isSelected(); -} + if (!use_region) + { + char backup[1024]; -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; - } -}; + // rename current to .bak + strcpy (backup, filename); + StripExtension (backup); + strcat (backup, ".bak"); + unlink (backup); + rename (filename, backup); + } -void Scene_Exclude_Selected(bool exclude) -{ - GlobalSceneGraph().traverse(ExcludeSelectedWalker(exclude)); -} + Sys_Printf ("Map_SaveFile: %s\n", filename); -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 + // build the out data stream + FileStream file; + if (!file.Open(filename,"w")) { - exclude_node( - path.top(), - !( - ( - aabb_intersects_aabb( - instance.worldAABB(), - aabb_for_minmax(region_mins, region_maxs) - ) != 0 - ) ^ m_exclude - ) - ); - - return true; + Sys_FPrintf(SYS_ERR, "ERROR: couldn't open %s for write\n", filename); + return; } -}; - -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); - } -} + // extract filetype + Map_Export(&file, filename_get_extension(filename), use_region); + file.Close(); -/* -=========== -Map_RegionXY -=========== -*/ -void Map_RegionXY(float x_min, float y_min, float x_max, float y_max) -{ - Map_RegionOff(); + finish = clock(); + elapsed_time = (double)(finish - start) / CLOCKS_PER_SEC; - 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; + Sys_Printf ("Saved in %-.2f second(s).\n",elapsed_time); + modified = false; - Map_ApplyRegion(); -} + if ( !strstr( filename, "autosave" ) ) + Sys_SetTitle (filename); -void Map_RegionBounds(const AABB& bounds) -{ - Map_RegionOff(); + if (!use_region) + { + time_t timer; - region_mins = vector3_subtracted(bounds.origin, bounds.extents); - region_maxs = vector3_added(bounds.origin, bounds.extents); + time (&timer); - deleteSelection(); + Sys_Beep (); - Map_ApplyRegion(); + Sys_Status ("Saved.", 0); + } } /* =========== -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"); - - bool success = false; - { - Resource* resource = GlobalReferenceCache().capture(filename); - resource->refresh(); // avoid loading old version if map has changed on disk since last import - if(resource->load()) - { - NodeSmartReference clone(NewMapRoot("")); - - { - //ScopeTimer timer("clone subgraph"); - Node_getTraversable(*resource->getNode())->traverse(CloneAll(clone)); - } - - Map_gatherNamespaced(clone); - Map_mergeClonedNames(); - MergeMap(clone); - success = true; - } - GlobalReferenceCache().release(filename); - } - - SceneChangeNotify(); - - return success; -} +Map_New -/* -=========== -Map_SaveFile =========== */ -bool Map_SaveFile(const char* filename) -{ - ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map"); - return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse, filename); -} - -// -//=========== -//Map_SaveSelected -//=========== -// -// Saves selected world brushes and whole entities with partial/full selections -// -bool Map_SaveSelected(const char* filename) +void Map_New (void) { - return MapResource_saveFile(MapFormat_forFile(filename), GlobalSceneGraph().root(), Map_Traverse_Selected, filename); -} + Sys_Printf ("Map_New\n"); + Map_Free (); + strcpy (currentmap, "unnamed.map"); + Sys_SetTitle (currentmap); -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)); -} + world_entity = (entity_s*)qmalloc(sizeof(*world_entity)); + world_entity->brushes.onext = + world_entity->brushes.oprev = &world_entity->brushes; + SetKeyValue (world_entity, "classname", "worldspawn"); + world_entity->eclass = Eclass_ForName ("worldspawn", true); -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; - } -}; + g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = 0; + g_pParentWnd->GetCamWnd()->Camera()->angles[PITCH] = 0; + VectorCopy (vec3_origin, g_pParentWnd->GetCamWnd()->Camera()->origin); + g_pParentWnd->GetCamWnd()->Camera()->origin[2] = 48; + VectorCopy (vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin()); -std::size_t Scene_countSelectedBrushes(scene::Graph& graph) -{ - std::size_t count; - graph.traverse(CountSelectedBrushes(count)); - return count; -} + Map_RestoreBetween (); -enum ENodeType -{ - eNodeUnknown, - eNodeMap, - eNodeEntity, - eNodePrimitive, -}; + Group_Init(); -const char* nodetype_get_name(ENodeType type) -{ - if(type == eNodeMap) - return "map"; - if(type == eNodeEntity) - return "entity"; - if(type == eNodePrimitive) - return "primitive"; - return "unknown"; + Sys_UpdateWindows (W_ALL); + modified = false; } -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); -} + REGION -bool contains_primitive(scene::Node& node) -{ - return Node_isEntity(node) && Node_getTraversable(node) != 0 && Node_getEntity(node)->isContainer(); -} +=========================================================== +*/ +qboolean region_active; +vec3_t region_mins = {g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord}; +vec3_t region_maxs = {g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord}; -ENodeType node_get_contains(scene::Node& node) -{ - if(contains_entity(node)) - { - return eNodeEntity; - } - if(contains_primitive(node)) - { - return eNodePrimitive; - } - return eNodeUnknown; -} +brush_t *region_sides[6]; -void Path_parent(const scene::Path& parent, const scene::Path& child) +/* +=========== +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) { - ENodeType contains = node_get_contains(parent.top()); - ENodeType type = node_get_nodetype(child.top()); + vec3_t mins, maxs; + int i; + texdef_t td; - if(contains != eNodeUnknown && contains == type) + if (!region_active) { - 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"; +#ifdef _DEBUG + Sys_FPrintf( SYS_WRN, "Unexpected AddRegionBrushes call.\n"); +#endif + return; } -} -void Scene_parentSelected() -{ - UndoableCommand undo("parentSelected"); + memset (&td, 0, sizeof(td)); + td.SetName(SHADER_NOT_FOUND); - if(GlobalSelectionSystem().countSelected() > 1) + // set mins + VectorSet(mins, region_mins[0]-32, region_mins[1]-32, region_mins[2]-32); + + // vary maxs + for(i=0; i<3; i++) { - 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); + VectorSet(maxs, region_maxs[0]+32, region_maxs[1]+32, region_maxs[2]+32); + maxs[i] = region_mins[i]; + region_sides[i] = Brush_Create (mins, maxs, &td); } - else + + // set maxs + VectorSet(maxs, region_maxs[0]+32, region_maxs[1]+32, region_maxs[2]+32); + + // vary mins + for(i=0; i<3; i++) { - globalOutputStream() << "failed - did not find two selected nodes.\n"; + VectorSet(mins, region_mins[0]-32, region_mins[1]-32, region_mins[2]-32); + mins[i] = region_maxs[i]; + region_sides[i+3] = Brush_Create (mins, maxs, &td); } -} + // this is a safe check, but it should not really happen anymore + vec3_t vOrig; + VectorSet(vOrig, + (int)g_pParentWnd->GetCamWnd()->Camera()->origin[0], + (int)g_pParentWnd->GetCamWnd()->Camera()->origin[1], + (int)g_pParentWnd->GetCamWnd()->Camera()->origin[2]); -void NewMap() -{ - if (ConfirmModified("New Map")) + for (i=0 ; i<3 ; i++) { - Map_RegionOff(); - Map_Free(); - Map_New(); + if (vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i]) + { + Sys_FPrintf(SYS_ERR, "Camera is NOT in the region, it's likely that the region won't compile correctly\n"); + } } + + // write the info_playerstart + region_startpoint = Entity_Alloc(); + SetKeyValue(region_startpoint, "classname", "info_player_start"); + region_startpoint->eclass = Eclass_ForName ("info_player_start", false); + char sTmp[1024]; + sprintf(sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2]); + SetKeyValue(region_startpoint, "origin", sTmp); + sprintf(sTmp, "%d", (int)g_pParentWnd->GetCamWnd()->Camera()->angles[YAW]); + SetKeyValue(region_startpoint, "angle", sTmp); + // empty array of children + region_startpoint->pData = new CPtrArray; } -void maps_directory(StringOutputStream& buffer) +void RemoveRegionBrushes (void) { - ASSERT_MESSAGE(!string_empty(g_qeglobals.m_userGamePath.c_str()), "maps_directory: user-game-path is empty"); - buffer << g_qeglobals.m_userGamePath.c_str() << "maps/"; - Q_mkdir(buffer.c_str()); + int i; + + if (!region_active) + return; + for (i=0 ; i<6 ; i++) + Brush_Free (region_sides[i]); + + CleanFilter(region_startpoint); + Entity_Free(region_startpoint); } -const char* map_open(const char* title) +qboolean Map_IsBrushFiltered (brush_t *b) { - StringOutputStream buf(256); - maps_directory(buf); - return file_dialog(GTK_WIDGET(MainFrame_getWindow()), TRUE, title, buf.c_str(), MapFormat::Name()); + int i; + + for (i=0 ; i<3 ; i++) + { + if (b->mins[i] > region_maxs[i]) + return true; + if (b->maxs[i] < region_mins[i]) + return true; + } + return false; } -const char* map_save(const char* title) +/* +=========== +Map_RegionOff + +Other filtering options may still be on +=========== +*/ +void Map_RegionOff (void) { - StringOutputStream buf(256); - maps_directory(buf); - return file_dialog(GTK_WIDGET(MainFrame_getWindow()), FALSE, title, buf.c_str(), MapFormat::Name()); + brush_t *b, *next; + int i; + + region_active = false; + for (i=0 ; i<3 ; i++) + { + region_maxs[i] = g_MaxWorldCoord-64; + region_mins[i] = g_MinWorldCoord+64; + } + + for (b=filtered_brushes.next ; b != &filtered_brushes ; b=next) + { + next = b->next; + if (Map_IsBrushFiltered (b)) + continue; // still filtered + Brush_RemoveFromList (b); + if (active_brushes.next == NULL || active_brushes.prev == NULL) + { + active_brushes.next = &active_brushes; + active_brushes.prev = &active_brushes; + } + Brush_AddToList (b, &active_brushes); + b->bFiltered = FilterBrush(b); + } + Sys_UpdateWindows (W_ALL); } -void OpenMap() +void Map_ApplyRegion (void) { - if (!ConfirmModified("Open Map")) - return; + brush_t *b, *next; - const char* filename = map_open("Open Map"); + region_active = true; + for (b=active_brushes.next ; b != &active_brushes ; b=next) + { + next = b->next; + if (!Map_IsBrushFiltered (b)) + continue; // still filtered + Brush_RemoveFromList (b); + Brush_AddToList (b, &filtered_brushes); + } - if (filename != 0) - { - MRU_AddFile(filename); - Map_RegionOff(); - Map_Free(); - Map_LoadFile(filename); - } + Sys_UpdateWindows (W_ALL); } -void ImportMap() + +/* +======================== +Map_RegionSelectedBrushes +======================== +*/ +void Map_RegionSelectedBrushes (void) { - const char* filename = map_open("Import Map"); + Map_RegionOff (); - if(filename != 0) + if (selected_brushes.next == &selected_brushes) // nothing selected { - UndoableCommand undo("mapImport"); - Map_ImportFile(filename); + Sys_Printf("Tried to region with no selection...\n"); + return; } -} + region_active = true; + Select_GetBounds (region_mins, region_maxs); -bool Map_SaveAs() -{ - const char* filename = map_save("Save Map"); - - if(filename != 0) - { - MRU_AddFile(filename); - Map_Rename(filename); - return Map_Save(); - } - return false; -} +#ifdef _DEBUG + if (filtered_brushes.next != &filtered_brushes) + Sys_Printf("WARNING: filtered_brushes list may not be empty in Map_RegionSelectedBrushes\n"); +#endif -void SaveMapAs() -{ - Map_SaveAs(); -} + if (active_brushes.next == &active_brushes) + { + // just have an empty filtered_brushes list + // this happens if you set region after selecting all the brushes in your map (some weird people do that, ask MrE!) + filtered_brushes.next = filtered_brushes.prev = &filtered_brushes; + } + else + { + // move the entire active_brushes list to filtered_brushes + filtered_brushes.next = active_brushes.next; + filtered_brushes.prev = active_brushes.prev; + filtered_brushes.next->prev = &filtered_brushes; + filtered_brushes.prev->next = &filtered_brushes; + } -void SaveMap() -{ - if(Map_Unnamed(g_map)) - { - SaveMapAs(); - } - else if(Map_Modified(g_map)) - { - Map_Save(); - } -} + // move the entire selected_brushes list to active_brushes + active_brushes.next = selected_brushes.next; + active_brushes.prev = selected_brushes.prev; + active_brushes.next->prev = &active_brushes; + active_brushes.prev->next = &active_brushes; -void ExportMap() -{ - const char* filename = map_save("Export Selection"); + // deselect patches + for (brush_t *b = active_brushes.next; b != &active_brushes; b = b->next) + if (b->patchBrush) + b->pPatch->bSelected = false; - if(filename != 0) - { - Map_SaveSelected(filename); - } -} + // clear selected_brushes + selected_brushes.next = selected_brushes.prev = &selected_brushes; -void SaveRegion() -{ - const char* filename = map_save("Export Region"); - - if(filename != 0) - { - Map_SaveRegion(filename); - } + Sys_UpdateWindows (W_ALL); } -void RegionOff() +/* +=========== +Map_RegionXY +=========== +*/ +void Map_RegionXY (void) { - Map_RegionOff(); - SceneChangeNotify(); -} + Map_RegionOff (); -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(); + region_mins[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5 * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(); + region_maxs[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5 * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(); + region_mins[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5 * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(); + region_maxs[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5 * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(); + region_mins[2] = g_MinWorldCoord+64; + region_maxs[2] = g_MaxWorldCoord-64; + Map_ApplyRegion (); } -void RegionBrush() +/* +=========== +Map_RegionTallBrush +=========== +*/ +void Map_RegionTallBrush (void) { - Map_RegionBrush(); - SceneChangeNotify(); -} + brush_t *b; -void RegionSelected() -{ - Map_RegionSelectedBrushes(); - SceneChangeNotify(); -} + if (!QE_SingleBrush ()) + return; + b = selected_brushes.next; + Map_RegionOff (); + VectorCopy (b->mins, region_mins); + VectorCopy (b->maxs, region_maxs); + region_mins[2] = g_MinWorldCoord+64; + region_maxs[2] = g_MaxWorldCoord-64; + Undo_Start("delete"); + Undo_AddBrushList(&selected_brushes); + Undo_AddEntity(b->owner); + Select_Delete (); + Undo_EndBrushList(&selected_brushes); + Undo_End(); -class BrushFindByIndexWalker : public scene::Traversable::Walker -{ - mutable std::size_t m_index; - scene::Path& m_path; -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)); - } - return false; - } -}; + Map_ApplyRegion (); +} -class EntityFindByIndexWalker : public scene::Traversable::Walker +/* +=========== +Map_RegionBrush +=========== +*/ +void Map_RegionBrush (void) { - mutable std::size_t m_index; - scene::Path& m_path; -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)); - } - return false; - } -}; + brush_t *b; -void Scene_FindEntityBrush(std::size_t entity, std::size_t brush, scene::Path& path) -{ - path.push(makeReference(GlobalSceneGraph().root())); - { - Node_getTraversable(path.top())->traverse(EntityFindByIndexWalker(entity, path)); - } - if(path.size() == 2) - { - scene::Traversable* traversable = Node_getTraversable(path.top()); - if(traversable != 0) - { - traversable->traverse(BrushFindByIndexWalker(brush, path)); - } - } -} + if (!QE_SingleBrush ()) + return; -inline bool Node_hasChildren(scene::Node& node) -{ - scene::Traversable* traversable = Node_getTraversable(node); - return traversable != 0 && !traversable->empty(); -} + b = selected_brushes.next; -void SelectBrush (int entitynum, int brushnum) -{ - scene::Path path; - Scene_FindEntityBrush(entitynum, brushnum, path); - if(path.size() == 3 || (path.size() == 2 && !Node_hasChildren(path.top()))) - { - scene::Instance* instance = GlobalSceneGraph().find(path); - ASSERT_MESSAGE(instance != 0, "SelectBrush: path not found in scenegraph"); - Selectable* selectable = Instance_getSelectable(*instance); - ASSERT_MESSAGE(selectable != 0, "SelectBrush: path not selectable"); - selectable->setSelected(true); - g_pParentWnd->GetXYWnd()->PositionView(instance->worldAABB().origin); - } -} + Map_RegionOff (); + VectorCopy (b->mins, region_mins); + VectorCopy (b->maxs, region_maxs); -class BrushFindIndexWalker : public scene::Graph::Walker -{ - mutable const scene::Node* m_node; - std::size_t& m_count; -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()) - { - m_node = 0; - } - if(m_node) - { - ++m_count; - } - } - return true; - } -}; + Undo_Start("delete"); + Undo_AddBrushList(&selected_brushes); + Undo_AddEntity(b->owner); + Select_Delete (); + Undo_EndBrushList(&selected_brushes); + Undo_End(); -class EntityFindIndexWalker : public scene::Graph::Walker -{ - mutable const scene::Node* m_node; - std::size_t& m_count; -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()) - { - m_node = 0; - } - if(m_node) - { - ++m_count; - } - } - return true; - } -}; + Map_ApplyRegion (); +} -static void GetSelectionIndex (int *ent, int *brush) +GList *find_string(GList *glist, const char *buf) { - std::size_t count_brush = 0; - std::size_t count_entity = 0; - if(GlobalSelectionSystem().countSelected() != 0) + while (glist) { - const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path(); - - GlobalSceneGraph().traverse(BrushFindIndexWalker(path.top(), count_brush)); - GlobalSceneGraph().traverse(EntityFindIndexWalker(path.parent(), count_entity)); + if (strcmp((char *)glist->data, buf) == 0) + break; // this name is in our list already + glist = glist->next; } - *brush = int(count_brush); - *ent = int(count_entity); + return glist; } -void DoFind() +void Map_ImportBuffer(char *buf) { - ModalDialog dialog; - GtkEntry* entity; - GtkEntry* brush; + Select_Deselect(); - GtkWindow* window = create_dialog_window(MainFrame_getWindow(), "Find Brush", G_CALLBACK(dialog_delete_callback), &dialog); + Undo_Start("import buffer"); - GtkAccelGroup* accel = gtk_accel_group_new(); - gtk_window_add_accel_group(window, accel); + MemStream stream; - { - GtkVBox* vbox = create_dialog_vbox(4, 4); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox)); - { - GtkTable* table = create_dialog_table(2, 2, 4, 4); - gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), TRUE, TRUE, 0); - { - GtkWidget* label = gtk_label_new ("Entity number"); - gtk_widget_show (label); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, - (GtkAttachOptions) (0), - (GtkAttachOptions) (0), 0, 0); - } - { - GtkWidget* label = gtk_label_new ("Brush number"); - gtk_widget_show (label); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, - (GtkAttachOptions) (0), - (GtkAttachOptions) (0), 0, 0); - } - { - GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); - gtk_widget_show(GTK_WIDGET(entry)); - gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 0, 1, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_grab_focus(GTK_WIDGET(entry)); - entity = entry; - } - { - GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); - gtk_widget_show(GTK_WIDGET(entry)); - gtk_table_attach(table, GTK_WIDGET(entry), 1, 2, 1, 2, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); + stream.Write(buf, strlen(buf)); + Map_Import(&stream, "xmap"); + stream.Close(); - brush = entry; - } - } - { - GtkHBox* hbox = create_dialog_hbox(4); - gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), TRUE, TRUE, 0); - { - GtkButton* button = create_dialog_button("Find", G_CALLBACK(dialog_button_ok), &dialog); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0); - widget_make_default(GTK_WIDGET(button)); - gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0); - } - { - GtkButton* button = create_dialog_button("Close", G_CALLBACK(dialog_button_cancel), &dialog); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0); - gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0); - } - } - } + Sys_UpdateWindows (W_ALL); + Sys_MarkMapModified(); + + Undo_End(); +} - // Initialize dialog - char buf[16]; - int ent, br; - GetSelectionIndex (&ent, &br); - sprintf (buf, "%i", ent); - gtk_entry_set_text(entity, buf); - sprintf (buf, "%i", br); - gtk_entry_set_text(brush, buf); +// +//================ +//Map_ImportFile +//================ +// +void Map_ImportFile (const char *filename) +{ + FileStream file; + Sys_BeginWait (); - if(modal_dialog_show(window, dialog) == eIDOK) - { - const char *entstr = gtk_entry_get_text(entity); - const char *brushstr = gtk_entry_get_text(brush); - SelectBrush (atoi(entstr), atoi(brushstr)); - } + Sys_Printf("Importing map from %s\n",filename); - gtk_widget_destroy(GTK_WIDGET(window)); -} + const char* type = strrchr(filename,'.'); + if(type!=NULL) type++; + /*!\todo Resolve "r" problem in scriptlib" */ + if(file.Open(filename, "rb")) + Map_Import(&file, type, true); + else + Sys_FPrintf(SYS_ERR, "ERROR: couldn't open %s for read\n", filename); + + file.Close(); + Sys_UpdateWindows (W_ALL); + modified = true; + Sys_EndWait(); +} -class MapEntityClasses : public ModuleObserver +// +//=========== +//Map_SaveSelected +//=========== +// +// Saves selected world brushes and whole entities with partial/full selections +// +void Map_SaveSelected(const char* filename) { - std::size_t m_unrealised; -public: - MapEntityClasses() : m_unrealised(1) - { - } - void realise() - { - if(--m_unrealised == 0) - { - if(g_map.m_resource != 0) - { - ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Loading Map"); - g_map.m_resource->realise(); - } - } - } - void unrealise() - { - if(++m_unrealised == 1) - { - if(g_map.m_resource != 0) - { - g_map.m_resource->flush(); - g_map.m_resource->unrealise(); - } - } - } -}; + FileStream file; -MapEntityClasses g_MapEntityClasses; + Sys_Printf("Saving selection to %s\n",filename); + const char* type = strrchr(filename,'.'); + if(type!=NULL) type++; + if(file.Open(filename, "w")) + Map_Export (&file, type, false, true); + else + Sys_FPrintf(SYS_ERR, "ERROR: failed to open %s for write\n", filename); + + file.Close(); -void Map_constructPreferences(PreferencesPage& page) -{ - page.appendCheckBox("", "Load last map on open", g_bLoadLastMap); } -#include "preferencesystem.h" +// +//=========== +//Map_SaveSelected +//=========== +// +// Saves selected world brushes and whole entities with partial/full selections +// +void Map_SaveSelected (MemStream* pMemFile, MemStream* pPatchFile) +{ + Map_Export (pMemFile, "xmap", false, true); + + /* + // write world entity first + Entity_WriteSelected(world_entity, pMemFile); + + // then write all other ents + count = 1; + for (e=entities.next ; e != &entities ; e=next) + { + MemFile_fprintf(pMemFile, "// entity %i\n", count); + count++; + Entity_WriteSelected(e, pMemFile); + next = e->next; + } + + //if (pPatchFile) + // Patch_WriteFile(pPatchFile); + */ +} -CopiedString g_strLastMap; -bool g_bLoadLastMap = false; -void Map_Construct() +void MemFile_fprintf(MemStream* pMemFile, const char* pText, ...) { - 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()); - - GlobalEntityClassManager().attach(g_MapEntityClasses); + char Buffer[4096]; + va_list args; + va_start (args,pText); + vsprintf(Buffer, pText, args); + pMemFile->Write(Buffer, strlen(Buffer)); } -void Map_Destroy() -{ - GlobalEntityClassManager().detach(g_MapEntityClasses); +/*! +============== +Region_SpawnPoint +push the region spawn point +\todo FIXME TTimo this was in the #1 MAP module implementation (in the core) +not sure it has any use anymore, should prolly drop it +============== +*/ +void Region_SpawnPoint(FILE *f) +{ + // write the info_player_start, we use the camera position + fprintf (f, "{\n"); + fprintf (f, "\"classname\" \"info_player_start\"\n"); + fprintf (f, "\"origin\" \"%i %i %i\"\n", + (int)g_pParentWnd->GetCamWnd()->Camera()->origin[0], + (int)g_pParentWnd->GetCamWnd()->Camera()->origin[1], + (int)g_pParentWnd->GetCamWnd()->Camera()->origin[2]); + fprintf (f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamWnd()->Camera()->angles[YAW]); + fprintf (f, "}\n"); }