* applied patch by StefanV (from mailinglist) that fixes an error in config.py (broke...
[xonotic/netradiant.git] / contrib / bobtoolz / DEntity.cpp
1 /*
2 BobToolz plugin for GtkRadiant
3 Copyright (C) 2001 Gordon Biggans
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 // DEntity.cpp: implementation of the DEntity class.
21 //
22 //////////////////////////////////////////////////////////////////////
23
24 #include "StdAfx.h"
25
26 #ifdef _WIN32
27 #pragma warning(disable : 4786)
28 #endif
29
30 #include "DEntity.h"
31
32 #include "dialogs/dialogs-gtk.h"
33 #include "misc.h"
34 #include "CPortals.h"
35
36 const char* brushEntityList[] = {
37         "worldspawn",
38         "trigger_always",
39         "trigger_hurt",
40         "trigger_multiple",
41         "trigger_push",
42         "trigger_teleport",
43         "func_bobbing",
44         "func_button",
45         "func_door",
46         "func_group",
47         "func_pendulum",
48         "func_plat",
49         "func_rotating",
50         "func_static",
51         "func_timer",
52         "func_train",
53         0
54 };
55
56 //////////////////////////////////////////////////////////////////////
57 // Construction/Destruction
58 //////////////////////////////////////////////////////////////////////
59
60 DEntity::DEntity(char *classname, int ID)
61 {
62         SetClassname(classname);
63         m_nID = ID;
64         QER_Entity = NULL;
65 }
66
67 DEntity::~DEntity()
68 {
69         ClearPatches();
70         ClearBrushes();
71         ClearEPairs();
72 }
73
74 //////////////////////////////////////////////////////////////////////
75 // Implementation
76 //////////////////////////////////////////////////////////////////////
77
78 void DEntity::ClearBrushes()
79 {
80         for(list<DBrush *>::const_iterator deadBrush=brushList.begin(); deadBrush!=brushList.end(); deadBrush++)
81         {
82                 delete *deadBrush;
83         }
84         brushList.clear();
85 }
86
87 void DEntity::ClearPatches()
88 {
89         for(list<DPatch *>::const_iterator deadPatch=patchList.begin(); deadPatch!=patchList.end(); deadPatch++)
90         {
91                 delete *deadPatch;
92         }
93         patchList.clear();
94 }
95
96 DPatch* DEntity::NewPatch()
97 {
98         DPatch* newPatch = new DPatch;
99
100         patchList.push_back(newPatch);
101
102         return newPatch;
103 }
104
105 DBrush* DEntity::NewBrush(int ID)
106 {
107         DBrush* newBrush = new DBrush(ID);
108
109         brushList.push_back(newBrush);
110
111         return newBrush;
112 }
113
114 char* getNextBracket(char* s)
115 {
116         char* p = s;
117         while(*p)
118         {
119                 p++;
120                 if(*p == '(')
121                         break;
122         }
123
124         return p;
125 }
126
127 bool DEntity::LoadFromPrt(char *filename)
128 {
129         CPortals portals;
130         strcpy(portals.fn, filename);
131         portals.Load();
132
133         if(portals.node_count == 0)
134                 return FALSE;
135
136         ClearBrushes();
137         ClearEPairs();
138
139   bool build = false;
140         for(unsigned int i = 0; i < portals.node_count; i++)
141         {
142     build = false;
143                 DBrush* brush = NewBrush();
144
145                 for(unsigned int j = 0; j < portals.node[i].portal_count; j++)
146                 {
147       for(unsigned int k = 0; k < portals.node[i].portal[j].point_count-2; k++)
148       {
149               vec3_t v1, v2, normal, n;
150               VectorSubtract(portals.node[i].portal[j].point[k+2].p, portals.node[i].portal[j].point[k+1].p, v1);
151               VectorSubtract(portals.node[i].portal[j].point[k].p, portals.node[i].portal[j].point[k+1].p, v2);
152               CrossProduct(v1, v2, n);
153         VectorNormalize(n, v2);
154
155         if(k == 0)
156         {
157           VectorCopy(v2, normal);
158         }
159         else
160         {
161           VectorSubtract(v2, normal, v1);
162           if(VectorLength(v1) > 0.01)
163           {
164             build = true;
165             break;
166           }
167         }
168       }
169
170       if(!build)
171                           brush->AddFace(portals.node[i].portal[j].point[2].p, portals.node[i].portal[j].point[1].p, portals.node[i].portal[j].point[0].p, "textures/common/caulk", FALSE);
172       else
173                           brush->AddFace(portals.node[i].portal[j].point[0].p, portals.node[i].portal[j].point[1].p, portals.node[i].portal[j].point[2].p, "textures/common/caulk", FALSE);
174                 }
175     if(build)
176       brush->BuildInRadiant(FALSE, NULL);
177         }
178
179         return TRUE;
180 }
181
182 DPlane* DEntity::AddFaceToBrush(vec3_t va, vec3_t vb, vec3_t vc, _QERFaceData* faceData, int ID)
183 {
184         DBrush* buildBrush = GetBrushForID(ID);
185         return buildBrush->AddFace(va, vb, vc, faceData);
186         // slow, dont use much
187 }
188
189 DBrush* DEntity::GetBrushForID(int ID)
190 {
191         DBrush* buildBrush = NULL;
192
193         for(list<DBrush *>::const_iterator chkBrush=brushList.begin(); chkBrush!=brushList.end(); chkBrush++)
194         {
195                 if((*chkBrush)->m_nBrushID == ID)
196                 {
197                         buildBrush = (*chkBrush);
198                         break;
199                 }
200         }
201
202         if(!buildBrush)
203                 buildBrush = NewBrush(ID);
204
205         return buildBrush;
206 }
207
208 void DEntity::LoadSelectedBrushes()
209 {
210         ClearBrushes();
211         ClearEPairs();
212
213         int count = g_FuncTable.m_pfnAllocateSelectedBrushHandles();
214
215         for(int i = 0; i < count; i++) {
216                 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(i);
217
218                 if(brush->pPatch)
219                         continue;
220
221                 DBrush* loadBrush = NewBrush(i);
222                 loadBrush->LoadFromBrush_t(brush, TRUE);
223         }
224
225         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
226 }
227
228 void DEntity::LoadSelectedPatches()
229 {
230         ClearPatches();
231         ClearEPairs();
232
233   int count = g_FuncTable.m_pfnAllocateSelectedPatchHandles();
234
235         for(int i = 0; i < count; i++)
236         {
237     //$ FIXME: m_pfnGetPatchHandle
238                 patchMesh_t *pmesh = (patchMesh_t*)g_FuncTable.m_pfnGetPatchData(i);
239
240                 DPatch* loadPatch = NewPatch();
241                 loadPatch->LoadFromBrush_t(pmesh->pSymbiot);
242         }
243
244   g_FuncTable.m_pfnReleasePatchHandles();
245 }
246
247 bool* DEntity::BuildIntersectList()
248 {
249         int max = GetIDMax();
250         if(max == 0)
251                 return NULL;
252
253         bool* pbIntList = new bool[max];
254         memset(pbIntList, 0, sizeof(bool)*(max));
255
256         for(list<DBrush *>::const_iterator pB1=brushList.begin(); pB1!=brushList.end(); pB1++)
257         {
258                 list<DBrush *>::const_iterator pB2=pB1;
259                 for(pB2++; pB2!=brushList.end(); pB2++)
260                 {
261                         if((*pB1)->IntersectsWith((*pB2)))
262                         {
263                                 pbIntList[(*pB1)->m_nBrushID] = TRUE;
264                                 pbIntList[(*pB2)->m_nBrushID] = TRUE;
265                         }
266                 }
267         }
268
269         return pbIntList;
270 }
271
272 bool* DEntity::BuildDuplicateList()
273 {
274         int max = GetIDMax();
275         if(max == 0)
276                 return NULL;
277
278         bool* pbDupList = new bool[max];
279         memset(pbDupList, 0, sizeof(bool)*(max));
280
281         for(list<DBrush *>::const_iterator pB1=brushList.begin(); pB1!=brushList.end(); pB1++)
282         {
283                 list<DBrush *>::const_iterator pB2=pB1;
284                 for(pB2++; pB2!=brushList.end(); pB2++)
285                 {
286                         if(**pB1 == *pB2)
287                         {
288                                 pbDupList[(*pB1)->m_nBrushID] = TRUE;
289                                 pbDupList[(*pB2)->m_nBrushID] = TRUE;
290                         }
291                 }
292         }
293
294         return pbDupList;
295 }
296
297 void DEntity::SelectBrushes(bool *selectList)
298 {
299         if(selectList == NULL)
300                 return;
301
302         g_FuncTable.m_pfnDeselectAllBrushes();
303
304         g_FuncTable.m_pfnAllocateActiveBrushHandles();
305
306         for(std::list<DBrush *>::const_iterator pBrush=brushList.begin(); pBrush!=brushList.end(); pBrush++)
307         {
308                 if(selectList[(*pBrush)->m_nBrushID])
309                         g_FuncTable.m_pfnSelectBrush((*pBrush)->QER_brush);
310         }
311         g_FuncTable.m_pfnReleaseActiveBrushHandles();
312 }
313
314 bool DEntity::LoadFromEntity(int id, bool bLoadPatches) {
315         return LoadFromEntity((entity_t*)g_FuncTable.m_pfnGetEntityHandle(id), bLoadPatches);
316 }
317
318 bool DEntity::LoadFromEntity(entity_t* ent, bool bLoadPatches) {
319         ClearPatches();
320         ClearBrushes();
321         ClearEPairs();
322
323         QER_Entity = ent;
324
325         epair_t* epl = *g_EntityTable.m_pfnGetEntityKeyValList(QER_Entity);
326         LoadEPairList(epl);
327
328         bool keep = FALSE;
329         int i;
330         for(i = 0; brushEntityList[i]; i++)
331         {
332                 if(!stricmp(brushEntityList[i], m_Classname))
333                 {
334                         keep = TRUE;
335                         break;
336                 }
337         }
338
339         if(!keep)
340                 return FALSE;
341
342         int count = g_FuncTable.m_pfnAllocateEntityBrushHandles(QER_Entity);
343
344         for(i = 0; i < count; i++)
345         {
346
347                 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetEntityBrushHandle(i);
348
349     if(brush == NULL) {
350                         DoMessageBox("GTKRadiant returned a NULL pointer, NOT a good sign", "WARNING!!!", MB_OK);
351       continue;
352     }
353
354                 if(brush->pPatch)
355                 {
356                         if(bLoadPatches)
357                         {
358                                 DPatch* loadPatch = NewPatch();
359                                 loadPatch->LoadFromBrush_t(brush);
360                         }
361                 }
362                 else
363                 {
364                         DBrush* loadBrush = NewBrush(i);
365                         loadBrush->LoadFromBrush_t(brush, TRUE);
366                 }
367         }
368
369         g_FuncTable.m_pfnReleaseEntityBrushHandles();
370
371         return TRUE;
372 }
373
374 void DEntity::RemoveNonCheckBrushes(list<Str>* exclusionList, bool useDetail)
375 {
376         list<DBrush *>::iterator chkBrush=brushList.begin();
377
378         while( chkBrush!=brushList.end() )
379         {
380                 if(!useDetail)
381                 {
382                         if((*chkBrush)->IsDetail())
383                         {
384                                 delete *chkBrush;
385                                 chkBrush = brushList.erase(chkBrush);
386                                 continue;
387                         }
388                 }
389
390                 list<Str>::iterator eTexture;
391
392                 for( eTexture=exclusionList->begin(); eTexture!=exclusionList->end(); eTexture++ )
393                 {
394                         if((*chkBrush)->HasTexture((*eTexture).GetBuffer()))
395                         {
396                                 delete *chkBrush;
397                                 chkBrush = brushList.erase(chkBrush);
398                                 break;
399                         }
400                 }
401
402                 if( eTexture == exclusionList->end() )
403                         chkBrush++;
404         }
405 }
406
407 void DEntity::ResetChecks(list<Str>* exclusionList)
408 {
409         for(list<DBrush *>::const_iterator resetBrush=brushList.begin(); resetBrush!=brushList.end(); resetBrush++)
410         {
411                 (*resetBrush)->ResetChecks(exclusionList);
412         }
413 }
414
415 int DEntity::FixBrushes(bool rebuild)
416 {
417         g_FuncTable.m_pfnAllocateActiveBrushHandles();
418
419         int cnt = 0;
420
421         for(list<DBrush *>::const_iterator fixBrush=brushList.begin(); fixBrush!=brushList.end(); fixBrush++)
422         {
423                 int count = (*fixBrush)->RemoveRedundantPlanes();
424                 if(count)
425                 {
426                         cnt += count;
427                         if(rebuild)
428                         {
429                                 g_FuncTable.m_pfnDeleteBrushHandle((*fixBrush)->QER_brush);
430
431                                 (*fixBrush)->BuildInRadiant(FALSE, NULL);
432                         }
433                 }
434         }
435
436         g_FuncTable.m_pfnReleaseActiveBrushHandles();
437
438         return cnt;
439 }
440
441 void DEntity::BuildInRadiant(bool allowDestruction)
442 {
443         bool makeEntity = strcmp(m_Classname, "worldspawn") ? true : false;
444
445         if(makeEntity)
446         {
447                 entity_t* pE = (entity_t*)g_FuncTable.m_pfnCreateEntityHandle();
448
449                 epair_t* pEpS = GetNextChainItem(NULL, "classname", m_Classname);
450
451                 epair_t* pEp = pEpS;
452
453                 for(list<DEPair* >::const_iterator buildEPair=epairList.begin(); buildEPair!=epairList.end(); buildEPair++)
454                 {
455                         pEp = GetNextChainItem(pEp, (*buildEPair)->key, (*buildEPair)->value);
456                 }
457
458                 g_EntityTable.m_pfnSetEntityKeyValList(pE, pEpS);
459
460                 g_FuncTable.m_pfnCommitEntityHandleToMap(pE);
461
462                 for(list<DBrush *>::const_iterator buildBrush=brushList.begin(); buildBrush!=brushList.end(); buildBrush++)
463                         (*buildBrush)->BuildInRadiant(allowDestruction, NULL, pE);
464
465                 for(list<DPatch *>::const_iterator buildPatch=patchList.begin(); buildPatch!=patchList.end(); buildPatch++)
466                         (*buildPatch)->BuildInRadiant(pE);
467
468                 QER_Entity = pE;
469         }
470         else
471         {
472                 for(list<DBrush *>::const_iterator buildBrush=brushList.begin(); buildBrush!=brushList.end(); buildBrush++)
473                         (*buildBrush)->BuildInRadiant(allowDestruction, NULL);
474
475                 for(list<DPatch *>::const_iterator buildPatch=patchList.begin(); buildPatch!=patchList.end(); buildPatch++)
476                         (*buildPatch)->BuildInRadiant();
477         }
478 }
479
480
481
482 int DEntity::GetIDMax( void ) {
483         int max = -1;
484         for(list<DBrush *>::const_iterator cntBrush=brushList.begin(); cntBrush!=brushList.end(); cntBrush++) {
485                 if((*cntBrush)->m_nBrushID > max)
486                         max = (*cntBrush)->m_nBrushID;
487         }
488         return max+1;
489 }
490
491 void DEntity::SetClassname( char *classname ) {
492         m_Classname = classname;
493 }
494
495 void DEntity::SaveToFile(FILE *pFile)
496 {
497         fprintf(pFile, "{\n");
498
499         fprintf(pFile, "\"classname\" \"%s\"\n", (const char *)m_Classname);
500
501         for(list<DEPair *>::const_iterator ep=epairList.begin(); ep!=epairList.end(); ep++)
502         {
503                 fprintf(pFile, "\"%s\" \"%s\"\n", (const char *)(*ep)->key, (const char *)(*ep)->value);
504         }
505
506         for(list<DBrush *>::const_iterator bp=brushList.begin(); bp!=brushList.end(); bp++)
507         {
508                 (*bp)->SaveToFile(pFile);
509         }
510
511         fprintf(pFile, "}\n");
512 }
513
514 void DEntity::ClearEPairs()
515 {
516         for(list<DEPair *>::const_iterator deadEPair=epairList.begin(); deadEPair!=epairList.end(); deadEPair++)
517         {
518                 delete (*deadEPair);
519         }
520         epairList.clear();
521 }
522
523 void DEntity::AddEPair(const char *key, const char *value) {
524         DEPair* newEPair;
525         newEPair = FindEPairByKey( key );
526         if(!newEPair) {
527                 newEPair = new DEPair;
528                 newEPair->Build(key, value);
529                 epairList.push_back(newEPair);
530         } else {
531                 newEPair->Build(key, value);
532         }
533 }
534
535 void DEntity::LoadEPairList(epair_t *epl)
536 {
537         epair_t* ep = epl;
538         while(ep)
539         {
540                 if(!strcmp(ep->key, "classname"))
541                         SetClassname(ep->value);
542                 else
543                         AddEPair(ep->key, ep->value);
544
545                 ep = ep->next;
546         }
547 }
548
549 bool DEntity::ResetTextures(const char* textureName, float fScale[2],     float fShift[2],    int rotation, const char* newTextureName,
550                             int bResetTextureName,    int bResetScale[2], int bResetShift[2], int bResetRotation, bool rebuild)
551 {
552         g_FuncTable.m_pfnDeselectAllBrushes();
553
554         g_FuncTable.m_pfnAllocateActiveBrushHandles();
555
556         bool reset = FALSE;
557
558         for(list<DBrush *>::const_iterator resetBrush=brushList.begin(); resetBrush!=brushList.end(); resetBrush++)
559         {
560                 bool tmp = (*resetBrush)->ResetTextures(textureName,        fScale,       fShift,       rotation, newTextureName,
561                                             bResetTextureName,  bResetScale,  bResetShift,  bResetRotation);
562
563                 if(tmp)
564                 {
565                         reset = TRUE;
566
567                         if(rebuild)
568                         {
569         entity_t *pE = (*resetBrush)->QER_brush->owner;
570                                 g_FuncTable.m_pfnDeleteBrushHandle((*resetBrush)->QER_brush);
571         (*resetBrush)->BuildInRadiant(FALSE, NULL, pE->entityId == 0 ? NULL : pE);
572
573         if( pE->entityId == 0 ? NULL : pE )
574         {
575         }
576                         }
577                 }
578         }
579
580   if(bResetTextureName)
581   {
582           for(list<DPatch *>::const_iterator resetPatch=patchList.begin(); resetPatch!=patchList.end(); resetPatch++)
583           {
584                   bool tmp = (*resetPatch)->ResetTextures(textureName, newTextureName);
585
586                   if(tmp)
587                   {
588                           reset = TRUE;
589
590                           if(rebuild)
591                           {
592           entity_t *pE = (*resetPatch)->QER_brush->owner;
593                                   g_FuncTable.m_pfnDeleteBrushHandle((*resetPatch)->QER_brush);
594           (*resetPatch)->BuildInRadiant(pE->entityId == 0 ? NULL : pE);
595                           }
596                   }
597           }
598   }
599
600         g_FuncTable.m_pfnReleaseActiveBrushHandles();
601
602         return reset;
603 }
604
605 DEPair* DEntity::FindEPairByKey(const char* keyname)
606 {
607         for(list<DEPair *>::const_iterator ep=epairList.begin(); ep!=epairList.end(); ep++)
608         {
609                 char* c = (*ep)->key;
610                 if(!strcmp(c, keyname))
611                         return *ep;
612         }
613         return NULL;
614 }
615
616 void DEntity::RemoveFromRadiant()
617 {
618         g_EntityTable.m_pfnEntity_Free( (entity_t*)QER_Entity );
619
620         QER_Entity = NULL;
621 }
622
623 void DEntity::SpawnString(const char* key, const char* defaultstring, const char** out)
624 {
625         DEPair* pEP = FindEPairByKey(key);
626         if(pEP) {
627                 *out = pEP->value;
628         } else {
629                 *out = defaultstring;
630         }
631 }
632
633 void DEntity::SpawnInt(const char* key, const char* defaultstring, int* out)
634 {
635         DEPair* pEP = FindEPairByKey(key);
636         if(pEP) {
637                 *out = atoi(pEP->value);
638         } else {
639                 *out = atoi(defaultstring);
640         }
641 }
642
643 void DEntity::SpawnFloat(const char* key, const char* defaultstring, float* out)
644 {
645         DEPair* pEP = FindEPairByKey(key);
646         if(pEP) {
647                 *out = static_cast< float >( atof( pEP->value ) );
648         } else {
649                 *out = static_cast< float >( atof(defaultstring) );
650         }
651 }
652
653 void DEntity::SpawnVector(const char* key, const char* defaultstring, vec_t* out)
654 {
655         DEPair* pEP = FindEPairByKey(key);
656         if(pEP) {
657                 sscanf(pEP->value, "%f %f %f", &out[0], &out[1], &out[2]);
658         } else {
659                 sscanf(defaultstring, "%f %f %f", &out[0], &out[1], &out[2]);
660         }
661 }
662
663 int DEntity::GetBrushCount( void ) {
664         return brushList.size();
665 }
666
667 DBrush* DEntity::FindBrushByPointer( brush_t* brush ) {
668         for(list<DBrush *>::const_iterator listBrush = brushList.begin(); listBrush != brushList.end(); listBrush++) {
669                 DBrush* pBrush = (*listBrush);
670                 if(pBrush->QER_brush == brush) {
671                         return pBrush;
672                 }
673         }
674         return NULL;
675 }