/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see .
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "qe3.h"
int mapModified; // for quit confirmation (0 = clean, 1 = unsaved,
// 2 = autosaved, but not regular saved)
char currentmap[1024];
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 AddRegionBrushes(void);
void RemoveRegionBrushes(void);
/*
=======================================================================================================================
=======================================================================================================================
*/
void DupLists() {
DWORD dw = GetTickCount();
}
/*
* Cross map selection saving this could mess this up if you have only part of a
* complex entity selected...
*/
brush_t between_brushes;
entity_t between_entities;
bool g_bRestoreBetween = false;
/*
=======================================================================================================================
=======================================================================================================================
*/
void Map_SaveBetween(void) {
if (g_pParentWnd->ActiveXY()) {
g_bRestoreBetween = true;
g_pParentWnd->ActiveXY()->Copy();
}
return;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void Map_RestoreBetween(void) {
if (g_pParentWnd->ActiveXY() && g_bRestoreBetween) {
g_pParentWnd->ActiveXY()->Paste();
}
return;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
bool CheckForTinyBrush(brush_t *b, int n, float fSize) {
bool bTiny = false;
for (int i = 0; i < 3; i++) {
if (b->maxs[i] - b->mins[i] < fSize) {
bTiny = true;
}
}
if (bTiny) {
common->Printf("Possible problem brush (too small) #%i ", n);
}
return bTiny;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void Map_BuildBrushData(void) {
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) {
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);
common->Printf("Removed degenerate brush\n");
}
}
Sys_EndWait();
}
/*
=======================================================================================================================
=======================================================================================================================
*/
entity_t *Map_FindClass(char *cname) {
entity_t *ent;
for (ent = entities.next; ent != &entities; ent = ent->next) {
if (!strcmp(cname, ValueForKey(ent, "classname"))) {
return ent;
}
}
return NULL;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
int Map_GetUniqueEntityID(const char *prefix, const char *eclass) {
entity_t *ent;
int id = 0;
for (ent = entities.next; ent != &entities; ent = ent->next) {
if (!strcmp(eclass, ValueForKey(ent, "classname"))) {
const char *name = ValueForKey(ent, "name");
if (name && name[0]) {
const char *buf;
if (prefix && *prefix) {
buf = va("%s_%s_", prefix, eclass);
} else {
buf = va("%s_", eclass);
}
int len = strlen(buf);
if ( idStr::Cmpn(name, buf, len) == 0 ) {
int j = atoi(name + len);
if (j > id) {
id = j;
}
}
}
}
}
return id + 1;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
bool Entity_NameIsUnique(const char *name) {
entity_t *ent;
if (name == NULL) {
return false;
}
for (ent = entities.next; ent != &entities; ent = ent->next) {
const char *testName = ValueForKey(ent, "name");
if (testName) {
if ( idStr::Icmp(name, testName) == 0 ) {
return false;
}
}
}
return true;
}
/*
=======================================================================================================================
Map_Free
=======================================================================================================================
*/
void Map_Free(void) {
g_bRestoreBetween = false;
if (selected_brushes.next && (selected_brushes.next != &selected_brushes)) {
if (g_pParentWnd->MessageBox("Copy selection?", "", MB_YESNO) == IDYES) {
Map_SaveBetween();
}
}
// clear all the render and sound system data
g_qeglobals.rw->InitFromMap( NULL );
g_qeglobals.sw->ClearAllSoundEmitters();
Texture_ClearInuse();
Pointfile_Clear();
strcpy(currentmap, "unnamed.map");
Sys_SetTitle(currentmap);
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 {
while (active_brushes.next != &active_brushes) {
Brush_Free(active_brushes.next, false);
}
while (selected_brushes.next != &selected_brushes) {
Brush_Free(selected_brushes.next, false);
}
while (filtered_brushes.next != &filtered_brushes) {
Brush_Free(filtered_brushes.next, false);
}
while (entities.next != &entities) {
Entity_Free(entities.next);
}
}
if (world_entity) {
Entity_Free(world_entity);
}
world_entity = NULL;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
entity_t *AngledEntity() {
entity_t *ent = Map_FindClass("info_player_start");
if (!ent) {
ent = Map_FindClass("info_player_deathmatch");
}
if (!ent) {
ent = Map_FindClass("info_player_deathmatch");
}
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;
}
brush_t *BrushFromMapPatch(idMapPatch *mappatch, idVec3 origin) {
patchMesh_t *pm = MakeNewPatch(mappatch->GetWidth(), mappatch->GetHeight());
pm->d_texture = Texture_ForName(mappatch->GetMaterial());
for (int i = 0; i < mappatch->GetWidth(); i++) {
for (int j = 0; j < mappatch->GetHeight(); j++) {
pm->ctrl(i, j).xyz = (*mappatch)[j * mappatch->GetWidth() + i].xyz + origin;
pm->ctrl(i, j).st = (*mappatch)[j * mappatch->GetWidth() + i].st;
}
}
pm->horzSubdivisions = mappatch->GetHorzSubdivisions();
pm->vertSubdivisions = mappatch->GetVertSubdivisions();
pm->explicitSubdivisions = mappatch->GetExplicitlySubdivided();
if (mappatch->epairs.GetNumKeyVals()) {
pm->epairs = new idDict;
*pm->epairs = mappatch->epairs;
}
brush_t *b = AddBrushForPatch(pm, false);
return b;
}
brush_t *BrushFromMapBrush(idMapBrush *mapbrush, idVec3 origin) {
brush_t *b = NULL;
if (mapbrush) {
b = Brush_Alloc();
int count = mapbrush->GetNumSides();
for (int i = 0; i < count; i++) {
idMapBrushSide *side = mapbrush->GetSide(i);
face_t *f = Face_Alloc();
f->next = NULL;
if (!b->brush_faces) {
b->brush_faces = f;
}
else {
face_t *scan;
for (scan = b->brush_faces; scan->next; scan = scan->next) {
;
}
scan->next = f;
}
f->plane = side->GetPlane();
f->originalPlane = f->plane;
f->dirty = false;
idWinding w;
w.BaseForPlane(f->plane);
for (int j = 0; j < 3; j++) {
f->planepts[j].x = w[j].x + origin.x;
f->planepts[j].y = w[j].y + origin.y;
f->planepts[j].z = w[j].z + origin.z;
}
idVec3 mat[2];
side->GetTextureMatrix(mat[0], mat[1]);
f->brushprimit_texdef.coords[0][0] = mat[0][0];
f->brushprimit_texdef.coords[0][1] = mat[0][1];
f->brushprimit_texdef.coords[0][2] = mat[0][2];
f->brushprimit_texdef.coords[1][0] = mat[1][0];
f->brushprimit_texdef.coords[1][1] = mat[1][1];
f->brushprimit_texdef.coords[1][2] = mat[1][2];
f->texdef.SetName(side->GetMaterial());
}
}
return b;
}
entity_t *EntityFromMapEntity(idMapEntity *mapent, CWaitDlg *dlg) {
entity_t *ent = NULL;
if (mapent) {
ent = Entity_New();
ent->brushes.onext = ent->brushes.oprev = &ent->brushes;
ent->origin.Zero();
ent->epairs = mapent->epairs;
GetVectorForKey(ent, "origin", ent->origin);
int count = mapent->GetNumPrimitives();
long lastUpdate = 0;
idStr status;
for (int i = 0; i < count; i++) {
idMapPrimitive *prim = mapent->GetPrimitive(i);
if (prim) {
// update 20 times a second
if ( (GetTickCount() - lastUpdate) > 50 ) {
lastUpdate = GetTickCount();
if (prim->GetType() == idMapPrimitive::TYPE_BRUSH) {
sprintf(status, "Reading primitive %i (brush)", i);
} else if (prim->GetType() == idMapPrimitive::TYPE_PATCH) {
sprintf(status, "Reading primitive %i (patch)", i);
}
dlg->SetText(status, true);
}
if ( dlg->CancelPressed() ) {
return ent;
}
brush_t *b = NULL;
if (prim->GetType() == idMapPrimitive::TYPE_BRUSH) {
idMapBrush *mapbrush = reinterpret_cast(prim);
b = BrushFromMapBrush(mapbrush, ent->origin);
} else if (prim->GetType() == idMapPrimitive::TYPE_PATCH) {
idMapPatch *mappatch = reinterpret_cast(prim);
b = BrushFromMapPatch(mappatch, ent->origin);
}
if (b) {
b->owner = ent;
// add to the end of the entity chain
b->onext = &ent->brushes;
b->oprev = ent->brushes.oprev;
ent->brushes.oprev->onext = b;
ent->brushes.oprev = b;
}
}
}
}
return ent;
}
extern entity_t *Entity_PostParse(entity_t *ent, brush_t *pList);
/*
=======================================================================================================================
Map_LoadFile
=======================================================================================================================
*/
void Map_LoadFile(const char *filename) {
entity_t *ent;
CWaitDlg dlg;
idStr fileStr, status;
idMapFile mapfile;
Sys_BeginWait();
Select_Deselect();
dlg.AllowCancel( true );
idStr( filename ).ExtractFileName( fileStr );
sprintf( status, "Loading %s...", fileStr.c_str() );
dlg.SetWindowText( status );
sprintf( status, "Reading file %s...", fileStr.c_str() );
dlg.SetText( status );
// SetInspectorMode(W_CONSOLE);
fileStr = filename;
fileStr.BackSlashesToSlashes();
common->Printf( "Map_LoadFile: %s\n", fileStr.c_str() );
Map_Free();
g_qeglobals.d_parsed_brushes = 0;
strcpy( currentmap, filename );
if(mapfile.Parse(filename, true, true)) {
g_qeglobals.bNeedConvert = false;
g_qeglobals.bOldBrushes = false;
g_qeglobals.bPrimitBrushes = false;
g_qeglobals.mapVersion = 1.0;
long lastUpdate = 0;
int count = mapfile.GetNumEntities();
for (int i = 0; i < count; i++) {
idMapEntity *mapent = mapfile.GetEntity(i);
if (mapent) {
idStr classname = mapent->epairs.GetString("classname");
// Update 20 times a second
if ( (GetTickCount() - lastUpdate) > 50 ) {
lastUpdate = GetTickCount();
sprintf(status, "Loading entity %i (%s)...", i, classname.c_str());
dlg.SetText(status);
}
if ( dlg.CancelPressed() ) {
Sys_Status("Map load cancelled.\n");
Map_New();
return;
}
if (classname == "worldspawn") {
world_entity = EntityFromMapEntity(mapent, &dlg);
Entity_PostParse(world_entity, &active_brushes);
} else {
ent = EntityFromMapEntity(mapent, &dlg);
Entity_PostParse(ent, &active_brushes);
Entity_Name(ent, true);
// add the entity to the end of the entity list
ent->next = &entities;
ent->prev = entities.prev;
entities.prev->next = ent;
entities.prev = ent;
g_qeglobals.d_num_entities++;
}
}
}
}
if (!world_entity) {
Sys_Status("No worldspawn in map.\n");
Map_New();
return;
}
common->Printf("--- LoadMapFile ---\n");
common->Printf("%s\n", fileStr.c_str());
common->Printf("%5i brushes\n", g_qeglobals.d_parsed_brushes);
common->Printf("%5i entities\n", g_qeglobals.d_num_entities);
dlg.SetText("Restoring Between");
Map_RestoreBetween();
dlg.SetText("Building Brush Data");
common->Printf("Map_BuildAllDisplayLists\n");
Map_BuildBrushData();
//
// reset the "need conversion" flag conversion to the good format done in
// Map_BuildBrushData
//
g_qeglobals.bNeedConvert = false;
// move the view to a start position
ent = AngledEntity();
g_pParentWnd->GetCamera()->Camera().angles[PITCH] = 0;
if (ent) {
GetVectorForKey(ent, "origin", g_pParentWnd->GetCamera()->Camera().origin);
GetVectorForKey(ent, "origin", g_pParentWnd->GetXYWnd()->GetOrigin());
g_pParentWnd->GetCamera()->Camera().angles[YAW] = FloatForKey(ent, "angle");
}
else {
g_pParentWnd->GetCamera()->Camera().angles[YAW] = 0;
VectorCopy(vec3_origin, g_pParentWnd->GetCamera()->Camera().origin);
VectorCopy(vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin());
}
Map_RegionOff();
mapModified = 0;
if (GetFileAttributes(filename) & FILE_ATTRIBUTE_READONLY) {
fileStr += " (read only) ";
}
Sys_SetTitle(fileStr);
Texture_ShowInuse();
if (g_pParentWnd->GetCamera()->GetRenderMode()) {
g_pParentWnd->GetCamera()->BuildRendererState();
}
Sys_EndWait();
Sys_UpdateWindows(W_ALL);
}
void Map_VerifyCurrentMap(const char *map) {
if ( idStr::Icmp( map, currentmap ) != 0 ) {
Map_LoadFile( map );
}
}
idMapPrimitive *BrushToMapPrimitive( const brush_t *b, const idVec3 &origin ) {
if ( b->pPatch ) {
idMapPatch *patch = new idMapPatch( b->pPatch->width * 6, b->pPatch->height * 6 );
patch->SetSize( b->pPatch->width, b->pPatch->height );
for ( int i = 0; i < b->pPatch->width; i++ ) {
for ( int j = 0; j < b->pPatch->height; j++ ) {
(*patch)[j*patch->GetWidth()+i].xyz = b->pPatch->ctrl(i, j).xyz - origin;
(*patch)[j*patch->GetWidth()+i].st = b->pPatch->ctrl(i, j).st;
}
}
patch->SetExplicitlySubdivided( b->pPatch->explicitSubdivisions );
if ( b->pPatch->explicitSubdivisions ) {
patch->SetHorzSubdivisions( b->pPatch->horzSubdivisions );
patch->SetVertSubdivisions( b->pPatch->vertSubdivisions );
}
patch->SetMaterial( b->pPatch->d_texture->GetName() );
if ( b->pPatch->epairs ) {
patch->epairs = *b->pPatch->epairs;
}
return patch;
}
else {
idMapBrush *mapbrush = new idMapBrush;
for ( face_t *f = b->brush_faces; f; f = f->next ) {
idMapBrushSide *side = new idMapBrushSide;
idPlane plane;
if ( f->dirty ) {
f->planepts[0] -= origin;
f->planepts[1] -= origin;
f->planepts[2] -= origin;
plane.FromPoints( f->planepts[0], f->planepts[1], f->planepts[2], false );
f->planepts[0] += origin;
f->planepts[1] += origin;
f->planepts[2] += origin;
} else {
plane = f->originalPlane;
}
side->SetPlane( plane );
side->SetMaterial( f->d_texture->GetName() );
idVec3 mat[2];
mat[0][0] = f->brushprimit_texdef.coords[0][0];
mat[0][1] = f->brushprimit_texdef.coords[0][1];
mat[0][2] = f->brushprimit_texdef.coords[0][2];
mat[1][0] = f->brushprimit_texdef.coords[1][0];
mat[1][1] = f->brushprimit_texdef.coords[1][1];
mat[1][2] = f->brushprimit_texdef.coords[1][2];
side->SetTextureMatrix(mat);
mapbrush->AddSide(side);
mapbrush->epairs = b->epairs;
}
return mapbrush;
}
}
idMapEntity *EntityToMapEntity(entity_t *e, bool use_region, CWaitDlg *dlg) {
idMapEntity *mapent = new idMapEntity;
mapent->epairs = e->epairs;
idStr status;
int count = 0;
long lastUpdate = 0;
if ( !EntityHasModel( e ) ) {
for ( brush_t *b = e->brushes.onext; b != &e->brushes; b = b->onext ) {
count++;
if ( e->eclass->fixedsize && !b->entityModel ) {
continue;
}
if ( !use_region || !Map_IsBrushFiltered( b ) ) {
// Update 20 times a second
if ( GetTickCount() - lastUpdate > 50 ) {
lastUpdate = GetTickCount();
if ( b->pPatch ) {
sprintf( status, "Adding primitive %i (patch)", count );
dlg->SetText( status, true );
} else {
sprintf( status, "Adding primitive %i (brush)", count );
dlg->SetText( status, true );
}
}
idMapPrimitive *prim = BrushToMapPrimitive( b, e->origin );
if ( prim ) {
mapent->AddPrimitive( prim );
}
}
}
}
return mapent;
}
/*
=======================================================================================================================
Map_SaveFile
=======================================================================================================================
*/
bool Map_SaveFile(const char *filename, bool use_region, bool autosave) {
entity_t *e, *next;
idStr temp;
int count;
brush_t *b;
idStr status;
int len = strlen(filename);
WIN32_FIND_DATA FileData;
if (FindFirstFile(filename, &FileData) != INVALID_HANDLE_VALUE) {
// the file exists;
if (len > 0 && GetFileAttributes(filename) & FILE_ATTRIBUTE_READONLY) {
g_pParentWnd->MessageBox("File is read only", "Read Only", MB_OK);
return false;
}
}
if (filename == NULL || len == 0 || (filename && stricmp(filename, "unnamed.map") == 0)) {
CFileDialog dlgSave(FALSE,"map",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"Map Files (*.map)|*.map||",AfxGetMainWnd());
if (dlgSave.DoModal() == IDOK) {
filename = dlgSave.m_ofn.lpstrFile;
strcpy(currentmap, filename);
}
else {
return false;
}
}
MEMORYSTATUSEX statex;
statex.dwLength = sizeof (statex);
GlobalMemoryStatusEx (&statex);
if ( statex.dwMemoryLoad > 95 ) {
g_pParentWnd->MessageBox("Physical memory is over 95% utilized. Consider saving and restarting", "Memory");
}
CWaitDlg dlg;
Pointfile_Clear();
temp = filename;
temp.BackSlashesToSlashes();
if ( !use_region ) {
idStr backup;
backup = temp;
backup.StripFileExtension();
backup.SetFileExtension( ".bak" );
if ( _unlink(backup) != 0 && errno != 2 ) { // errno 2 means the file doesn't exist, which we don't care about
g_pParentWnd->MessageBox( va("Unable to delete %s: %s", backup.c_str(), strerror(errno) ), "File Error" );
}
if ( rename(filename, backup) != 0 ) {
g_pParentWnd->MessageBox( va("Unable to rename %s to %s: %s", filename, backup.c_str(), strerror(errno) ), "File Error" );
}
}
common->Printf("Map_SaveFile: %s\n", filename);
idStr mapFile;
bool localFile = (strstr(filename, ":") != NULL);
if (autosave || localFile) {
mapFile = filename;
} else {
mapFile = fileSystem->OSPathToRelativePath( filename );
}
if (use_region) {
AddRegionBrushes();
}
idMapFile map;
world_entity->origin.Zero();
idMapEntity *mapentity = EntityToMapEntity(world_entity, use_region, &dlg);
dlg.SetText("Saving worldspawn...");
map.AddEntity(mapentity);
if ( use_region ) {
idStr buf;
sprintf( buf, "{\n\"classname\" \"info_player_start\"\n\"origin\"\t \"%i %i %i\"\n\"angle\"\t \"%i\"\n}\n",
(int)g_pParentWnd->GetCamera()->Camera().origin[0],
(int)g_pParentWnd->GetCamera()->Camera().origin[1],
(int)g_pParentWnd->GetCamera()->Camera().origin[2],
(int)g_pParentWnd->GetCamera()->Camera().angles[YAW] );
idLexer src( LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
src.LoadMemory( buf, buf.Length(), "regionbuf" );
idMapEntity *playerstart = idMapEntity::Parse( src );
map.AddEntity( playerstart );
}
count = -1;
for ( e = entities.next; e != &entities; e = next ) {
count++;
next = e->next;
if (e->brushes.onext == &e->brushes) {
Entity_Free(e); // no brushes left, so remove it
}
else {
if (use_region) {
for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
if (!Map_IsBrushFiltered(b)) {
break; // got one
}
}
if (b == &e->brushes) {
continue; // nothing visible
}
}
idVec3 origin;
if (!GetVectorForKey(e, "origin", origin)) {
idStr text;
VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
SetKeyValue(e, "origin", text);
}
if (use_region && !idStr::Icmp(ValueForKey(e, "classname"), "info_player_start")) {
continue;
}
idStr classname = e->epairs.GetString("classname");
sprintf(status, "Saving entity %i (%s)...", count, classname.c_str());
dlg.SetText(status);
map.AddEntity(EntityToMapEntity(e, use_region, &dlg));
count++;
}
}
mapFile.StripFileExtension();
idStr mapExt = (use_region) ? ".reg" : ".map";
sprintf(status, "Writing file %s.%s...", mapFile.c_str(), mapExt.c_str());
dlg.SetText(status);
map.Write(mapFile, mapExt, !(autosave || localFile));
mapModified = 0;
if (use_region) {
RemoveRegionBrushes();
}
if (!strstr(temp, "autosave")) {
Sys_SetTitle(temp);
}
Sys_Status("Saved.\n", 0);
return true;
}
/*
=======================================================================================================================
Map_New
=======================================================================================================================
*/
void Map_New(void) {
common->Printf("Map_New\n");
Map_Free();
Patch_Cleanup();
g_Inspectors->entityDlg.SetEditEntity ( NULL );
world_entity = Entity_New();
world_entity->brushes.onext = world_entity->brushes.oprev = &world_entity->brushes;
SetKeyValue(world_entity, "classname", "worldspawn");
world_entity->eclass = Eclass_ForName("worldspawn", true);
g_pParentWnd->GetCamera()->Camera().angles[YAW] = 0;
g_pParentWnd->GetCamera()->Camera().angles[PITCH] = 0;
VectorCopy(vec3_origin, g_pParentWnd->GetCamera()->Camera().origin);
g_pParentWnd->GetCamera()->Camera().origin[2] = 48;
VectorCopy(vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin());
Map_RestoreBetween();
Sys_UpdateWindows(W_ALL);
mapModified = 0;
g_qeglobals.mapVersion = MAP_VERSION;
}
bool region_active;
idVec3 region_mins(MIN_WORLD_COORD, MIN_WORLD_COORD, MIN_WORLD_COORD);
idVec3 region_maxs(MAX_WORLD_COORD, MAX_WORLD_COORD, MAX_WORLD_COORD);
brush_t *region_sides[6];
/*
=======================================================================================================================
AddRegionBrushes a regioned map will have temp walls put up at the region boundary
=======================================================================================================================
*/
void AddRegionBrushes(void) {
idVec3 mins, maxs;
int i;
texdef_t td;
if (!region_active) {
return;
}
memset(&td, 0, sizeof(td));
td = g_qeglobals.d_texturewin.texdef;
// strcpy (td.name, "REGION");
td.SetName("textures/REGION");
const int REGION_WIDTH = 1024;
mins[0] = region_mins[0] - REGION_WIDTH;
maxs[0] = region_mins[0] + 1;
mins[1] = region_mins[1] - REGION_WIDTH;
maxs[1] = region_maxs[1] + REGION_WIDTH;
mins[2] = MIN_WORLD_COORD;
maxs[2] = MAX_WORLD_COORD;
region_sides[0] = Brush_Create(mins, maxs, &td);
mins[0] = region_maxs[0] - 1;
maxs[0] = region_maxs[0] + REGION_WIDTH;
region_sides[1] = Brush_Create(mins, maxs, &td);
mins[0] = region_mins[0] - REGION_WIDTH;
maxs[0] = region_maxs[0] + REGION_WIDTH;
mins[1] = region_mins[1] - REGION_WIDTH;
maxs[1] = region_mins[1] + 1;
region_sides[2] = Brush_Create(mins, maxs, &td);
mins[1] = region_maxs[1] - 1;
maxs[1] = region_maxs[1] + REGION_WIDTH;
region_sides[3] = Brush_Create(mins, maxs, &td);
mins = region_mins;
maxs = region_maxs;
maxs[2] = mins[2] + REGION_WIDTH;
region_sides[4] = Brush_Create(mins, maxs, &td);
mins = region_mins;
maxs = region_maxs;
mins[2] = maxs[2] - REGION_WIDTH;
region_sides[5] = Brush_Create(mins, maxs, &td);
for (i = 0; i < 6; i++) {
Brush_AddToList(region_sides[i], &selected_brushes);
Entity_LinkBrush(world_entity, region_sides[i]);
Brush_Build(region_sides[i]);
}
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void RemoveRegionBrushes(void) {
int i;
if (!region_active) {
return;
}
for (i = 0; i < 6; i++) {
Brush_Free(region_sides[i]);
}
}
/*
=======================================================================================================================
=======================================================================================================================
*/
bool Map_IsBrushFiltered(brush_t *b) {
int i;
if (!region_active) {
return false;
}
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;
}
/*
=======================================================================================================================
Map_RegionOff Other filtering options may still be on
=======================================================================================================================
*/
void Map_RegionOff(void) {
brush_t *b, *next;
int i;
region_active = false;
for (i = 0; i < 3; i++) {
region_maxs[i] = MAX_WORLD_COORD; // 4096;
region_mins[i] = MIN_WORLD_COORD; // -4096;
}
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);
}
Sys_UpdateWindows(W_ALL);
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void Map_ApplyRegion(void) {
brush_t *b, *next;
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);
}
Sys_UpdateWindows(W_ALL);
}
/*
=======================================================================================================================
Map_RegionSelectedBrushes
=======================================================================================================================
*/
void Map_RegionSelectedBrushes(void) {
Map_RegionOff();
if (selected_brushes.next == &selected_brushes) { // nothing selected
Sys_Status("Tried to region with no selection...\n");
return;
}
region_active = true;
Select_GetBounds(region_mins, region_maxs);
// 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;
Patch_Deselect();
// 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;
// clear selected_brushes
selected_brushes.next = selected_brushes.prev = &selected_brushes;
Sys_UpdateWindows(W_ALL);
}
/*
=======================================================================================================================
Map_RegionXY
=======================================================================================================================
*/
void Map_RegionXY(void) {
Map_RegionOff();
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] = MIN_WORLD_COORD;
region_maxs[2] = MAX_WORLD_COORD;
Map_ApplyRegion();
}
/*
=======================================================================================================================
Map_RegionTallBrush
=======================================================================================================================
*/
void Map_RegionTallBrush(void) {
brush_t *b;
if (!QE_SingleBrush()) {
return;
}
b = selected_brushes.next;
Map_RegionOff();
VectorCopy(b->mins, region_mins);
VectorCopy(b->maxs, region_maxs);
region_mins[2] = MIN_WORLD_COORD;
region_maxs[2] = MAX_WORLD_COORD;
Select_Delete();
Map_ApplyRegion();
}
/*
=======================================================================================================================
Map_RegionBrush
=======================================================================================================================
*/
void Map_RegionBrush(void) {
brush_t *b;
if (!QE_SingleBrush()) {
return;
}
b = selected_brushes.next;
Map_RegionOff();
VectorCopy(b->mins, region_mins);
VectorCopy(b->maxs, region_maxs);
Select_Delete();
Map_ApplyRegion();
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void UniqueTargetName(idStr &rStr) {
// make a unique target value
int maxtarg = 0;
for (entity_t * e = entities.next; e != &entities; e = e->next) {
const char *tn = ValueForKey(e, "name");
if (tn && tn[0]) {
int targetnum = atoi(tn + 1);
if (targetnum > maxtarg) {
maxtarg = targetnum;
}
}
else {
tn = ValueForKey(e, "target");
if (tn && tn[0]) {
int targetnum = atoi(tn + 1);
if (targetnum > maxtarg) {
maxtarg = targetnum;
}
}
}
}
sprintf(rStr, "t%i", maxtarg + 1);
}
//
// =======================================================================================================================
// Map_ImportFile Timo 09/01/99:: called by CXYWnd::Paste & Map_ImportFile if Map_ImportFile ( prefab ), the buffer
// may contain brushes in old format ( conversion needed )
// =======================================================================================================================
//
void Map_ImportBuffer(char *buf, bool renameEntities) {
entity_t *ent;
brush_t *b = NULL;
CPtrArray ptrs;
Select_Deselect();
Undo_Start("import buffer");
g_qeglobals.d_parsed_brushes = 0;
if (buf) {
CMapStringToString mapStr;
StartTokenParsing(buf);
g_qeglobals.d_num_entities = 0;
//
// Timo will be used in Entity_Parse to detect if a conversion between brush
// formats is needed
//
g_qeglobals.bNeedConvert = false;
g_qeglobals.bOldBrushes = false;
g_qeglobals.bPrimitBrushes = false;
g_qeglobals.mapVersion = 1.0;
if (GetToken(true)) {
if (stricmp(token, "Version") == 0) {
GetToken(false);
g_qeglobals.mapVersion = atof(token);
common->Printf("Map version: %1.2f\n", g_qeglobals.mapVersion);
} else {
UngetToken();
}
}
idDict RemappedNames; // since I can't use "map "... sigh. So much for STL...
while (1) {
//
// use the selected brushes list as it's handy ent = Entity_Parse (false,
// &selected_brushes);
//
ent = Entity_Parse(false, &active_brushes);
if (!ent) {
break;
}
// end entity for undo
Undo_EndEntity(ent);
// end brushes for undo
for (b = ent->brushes.onext; b && b != &ent->brushes; b = b->onext) {
Undo_EndBrush(b);
}
if (!strcmp(ValueForKey(ent, "classname"), "worldspawn")) {
// world brushes need to be added to the current world entity
b = ent->brushes.onext;
while (b && b != &ent->brushes) {
brush_t *bNext = b->onext;
Entity_UnlinkBrush(b);
Entity_LinkBrush(world_entity, b);
ptrs.Add(b);
b = bNext;
}
}
else {
// the following bit remaps conflicting target/targetname key/value pairs
CString str = ValueForKey(ent, "target");
CString strKey;
CString strTarget("");
if (str.GetLength() > 0) {
if (FindEntity("target", str.GetBuffer(0))) {
if (!mapStr.Lookup(str, strKey)) {
idStr key;
UniqueTargetName(key);
strKey = key;
mapStr.SetAt(str, strKey);
}
strTarget = strKey;
SetKeyValue(ent, "target", strTarget.GetBuffer(0));
}
}
/*
* str = ValueForKey(ent, "name"); if (str.GetLength() > 0) { if
* (FindEntity("name", str.GetBuffer(0))) { if (!mapStr.Lookup(str, strKey)) {
* UniqueTargetName(strKey); mapStr.SetAt(str, strKey); } Entity_SetName(ent,
* strKey.GetBuffer(0)); } }
*/
CString cstrNameOld = ValueForKey(ent, "name");
Entity_Name(ent, renameEntities);
CString cstrNameNew = ValueForKey(ent, "name");
if (cstrNameOld != cstrNameNew)
{
RemappedNames.Set(cstrNameOld, cstrNameNew);
}
//
// if (strTarget.GetLength() > 0) SetKeyValue(ent, "target",
// strTarget.GetBuffer(0));
// add the entity to the end of the entity list
//
ent->next = &entities;
ent->prev = entities.prev;
entities.prev->next = ent;
entities.prev = ent;
g_qeglobals.d_num_entities++;
for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
ptrs.Add(b);
}
}
}
// now iterate through the remapped names, and see if there are any target-connections that need remaking...
//
// (I could probably write this in half the size with STL, but WTF, work with what we have...)
//
int iNumKeyVals = RemappedNames.GetNumKeyVals();
for (int iKeyVal=0; iKeyVal < iNumKeyVals; iKeyVal++)
{
const idKeyValue *pKeyVal = RemappedNames.GetKeyVal( iKeyVal );
LPCSTR psOldName = pKeyVal->GetKey().c_str();
LPCSTR psNewName = pKeyVal->GetValue().c_str();
entity_t *pEntOld = FindEntity("name", psOldName); // original ent we cloned from
entity_t *pEntNew = FindEntity("name", psNewName); // cloned ent
if (pEntOld && pEntNew)
{
CString cstrTargetNameOld = ValueForKey(pEntOld, "target");
if (!cstrTargetNameOld.IsEmpty())
{
// ok, this ent was targeted at another ent, so it's clone needs updating to point to
// the clone of that target, so...
//
entity_t *pEntOldTarget = FindEntity("name", cstrTargetNameOld);
if ( pEntOldTarget )
{
LPCSTR psNewTargetName = RemappedNames.GetString( cstrTargetNameOld );
if (psNewTargetName && psNewTargetName[0])
{
SetKeyValue(pEntNew, "target", psNewTargetName);
}
}
}
}
}
}
//
// ::ShowWindow(g_qeglobals.d_hwndEntity, FALSE);
// ::LockWindowUpdate(g_qeglobals.d_hwndEntity);
//
g_bScreenUpdates = false;
for (int i = 0; i < ptrs.GetSize(); i++) {
Brush_Build(reinterpret_cast < brush_t * > (ptrs[i]), true, false);
Select_Brush(reinterpret_cast < brush_t * > (ptrs[i]), true, false);
}
// ::LockWindowUpdate(NULL);
g_bScreenUpdates = true;
ptrs.RemoveAll();
//
// reset the "need conversion" flag conversion to the good format done in
// Map_BuildBrushData
//
g_qeglobals.bNeedConvert = false;
Sys_UpdateWindows(W_ALL);
// Sys_MarkMapModified();
mapModified = 1;
Undo_End();
}
//
// =======================================================================================================================
// Map_ImportFile
// =======================================================================================================================
//
void Map_ImportFile(char *fileName) {
char *buf;
idStr temp;
Sys_BeginWait();
temp = fileName;
temp.BackSlashesToSlashes();
if (LoadFile( temp, (void **) &buf) != -1) {
Map_ImportBuffer(buf);
Mem_Free( buf );
Map_BuildBrushData();
}
Sys_UpdateWindows(W_ALL);
mapModified = 1;
Sys_EndWait();
}
//
// =======================================================================================================================
// Map_SaveSelected Saves selected world brushes and whole entities with partial/full selections
// =======================================================================================================================
//
void Map_SaveSelected(char *fileName) {
entity_t *e, *next;
FILE *f;
idStr temp;
int count;
temp = fileName;
temp.BackSlashesToSlashes();
f = fopen(temp, "w");
if ( !f ) {
common->Printf( "ERROR!!!! Couldn't open %s\n", temp.c_str() );
return;
}
// write version
g_qeglobals.mapVersion = MAP_VERSION;
fprintf( f, "Version %1.2f\n", MAP_VERSION );
// write world entity second
world_entity->origin.Zero();
Entity_WriteSelected( world_entity, f );
// then write all other ents
count = 1;
for ( e = entities.next; e != &entities; e = next ) {
fprintf( f, "// entity %i\n", count );
count++;
Entity_WriteSelected( e, f );
next = e->next;
}
fclose( f );
}
//
// =======================================================================================================================
// Map_SaveSelected Saves selected world brushes and whole entities with partial/full selections
// =======================================================================================================================
//
void Map_SaveSelected(CMemFile *pMemFile, CMemFile *pPatchFile) {
entity_t *e, *next;
int count;
CString strTemp;
// write version
g_qeglobals.mapVersion = MAP_VERSION;
MemFile_fprintf(pMemFile, "Version %1.2f\n", MAP_VERSION);
// write world entity first
world_entity->origin.Zero();
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);
}
/*
=======================================================================================================================
=======================================================================================================================
*/
/*
================
WriteFileString
================
*/
bool WriteFileString( FILE *fp, char *string, ... ) {
long i;
unsigned long u;
double f;
char *str;
idStr buf;
va_list argPtr;
va_start( argPtr, string );
while( *string ) {
switch( *string ) {
case '%':
string++;
while ( (*string >= '0' && *string <= '9') ||
*string == '.' || *string == '-' || *string == '+' || *string == '#') {
string++;
}
switch( *string ) {
case 'f':
case 'e':
case 'E':
case 'g':
case 'G':
f = va_arg( argPtr, double );
sprintf( buf, "%1.10f", f );
buf.StripTrailing( '0' );
buf.StripTrailing( '.' );
fprintf( fp, "%s", buf.c_str() );
break;
case 'd':
case 'i':
i = va_arg( argPtr, long );
fprintf( fp, "%d", i );
break;
case 'u':
u = va_arg( argPtr, unsigned long );
fprintf( fp, "%u", u );
break;
case 'o':
u = va_arg( argPtr, unsigned long );
fprintf( fp, "%o", u );
break;
case 'x':
u = va_arg( argPtr, unsigned long );
fprintf( fp, "%x", u );
break;
case 'X':
u = va_arg( argPtr, unsigned long );
fprintf( fp, "%X", u );
break;
case 'c':
i = va_arg( argPtr, long );
fprintf( fp, "%c", (char) i );
break;
case 's':
str = va_arg( argPtr, char * );
fprintf( fp, "%s", str );
break;
case '%':
fprintf( fp, "%%" );
break;
default:
common->Error( "WriteFileString: invalid %%%c", *string );
break;
}
string++;
break;
case '\\':
string++;
switch( *string ) {
case 't':
fprintf( fp, "\t" );
break;
case 'n':
fprintf( fp, "\n" );
default:
common->Error( "WriteFileString: unknown escape character \'%c\'", *string );
break;
}
string++;
break;
default:
fprintf( fp, "%c", *string );
string++;
break;
}
}
va_end( argPtr );
return true;
}
/*
================
MemFile_fprintf
================
*/
void MemFile_fprintf( CMemFile *pMemFile, const char *string, ... ) {
char Buffer[4096];
long i;
unsigned long u;
double f;
char *str;
idStr buf, out;
va_list argPtr;
char *buff = Buffer;
va_start( argPtr, string );
while( *string ) {
switch( *string ) {
case '%':
string++;
while ( (*string >= '0' && *string <= '9') ||
*string == '.' || *string == '-' || *string == '+' || *string == '#') {
string++;
}
switch( *string ) {
case 'f':
case 'e':
case 'E':
case 'g':
case 'G':
f = va_arg( argPtr, double );
sprintf( buf, "%1.10f", f );
buf.StripTrailing( '0' );
buf.StripTrailing( '.' );
sprintf( buff, "%s", buf.c_str() );
break;
case 'd':
case 'i':
i = va_arg( argPtr, long );
sprintf( buff, "%d", i );
break;
case 'u':
u = va_arg( argPtr, unsigned long );
sprintf( buff, "%u", u );
break;
case 'o':
u = va_arg( argPtr, unsigned long );
sprintf( buff, "%o", u );
break;
case 'x':
u = va_arg( argPtr, unsigned long );
sprintf( buff, "%x", u );
break;
case 'X':
u = va_arg( argPtr, unsigned long );
sprintf( buff, "%X", u );
break;
case 'c':
i = va_arg( argPtr, long );
sprintf( buff, "%c", (char) i );
break;
case 's':
str = va_arg( argPtr, char * );
sprintf( buff, "%s", str );
break;
case '%':
sprintf( buff, "%%" );
break;
default:
common->Error( "MemFile_fprintf: invalid %%%c", *string );
break;
}
string++;
break;
case '\\':
string++;
switch( *string ) {
case 't':
sprintf( buff, "\t" );
break;
case 'n':
sprintf( buff, "\n" );
default:
common->Error( "MemFile_fprintf: unknown escape character \'%c\'", *string );
break;
}
string++;
break;
default:
sprintf( buff, "%c", *string );
string++;
break;
}
buff = Buffer + strlen(Buffer);
}
va_end( argPtr );
out = Buffer;
pMemFile->Write( out.c_str(), out.Length() );
}