map.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. #ifdef SYSTEM_WIN32
  2. #pragma warning(disable:4996)
  3. #endif
  4. #pragma warning(disable:4018) // '<' : signed/unsigned mismatch
  5. #include "csg.h"
  6. int g_nummapbrushes;
  7. brush_t g_mapbrushes[MAX_MAP_BRUSHES];
  8. int g_numbrushsides;
  9. side_t g_brushsides[MAX_MAP_SIDES];
  10. int g_nMapFileVersion;
  11. static const vec3_t s_baseaxis[18] = {
  12. {0, 0, 1}, {1, 0, 0}, {0, -1, 0}, // floor
  13. {0, 0, -1}, {1, 0, 0}, {0, -1, 0}, // ceiling
  14. {1, 0, 0}, {0, 1, 0}, {0, 0, -1}, // west wall
  15. {-1, 0, 0}, {0, 1, 0}, {0, 0, -1}, // east wall
  16. {0, 1, 0}, {1, 0, 0}, {0, 0, -1}, // south wall
  17. {0, -1, 0}, {1, 0, 0}, {0, 0, -1}, // north wall
  18. };
  19. // =====================================================================================
  20. // TextureAxisFromPlane
  21. // =====================================================================================
  22. void TextureAxisFromPlane(const plane_t* const pln, vec3_t xv, vec3_t yv)
  23. {
  24. int bestaxis;
  25. vec_t dot, best;
  26. int i;
  27. best = 0;
  28. bestaxis = 0;
  29. for (i = 0; i < 6; i++)
  30. {
  31. dot = DotProduct(pln->normal, s_baseaxis[i * 3]);
  32. if (dot > best)
  33. {
  34. best = dot;
  35. bestaxis = i;
  36. }
  37. }
  38. VectorCopy(s_baseaxis[bestaxis * 3 + 1], xv);
  39. VectorCopy(s_baseaxis[bestaxis * 3 + 2], yv);
  40. }
  41. #define ScaleCorrection (1.0/128.0)
  42. // =====================================================================================
  43. // CopySKYtoCLIP
  44. // clips a particluar sky brush
  45. // =====================================================================================
  46. static void CopySKYtoCLIP(const brush_t* const b)
  47. {
  48. int i;
  49. entity_t* mapent;
  50. brush_t* newbrush;
  51. if (b->contents != CONTENTS_SKY)
  52. Error("[MOD] CopySKYtoCLIP: Got a NON-SKY for passed brush! (%s)",b->contents );
  53. hlassert(b->contents == CONTENTS_SKY); // Only SKY brushes should be passed down to this function(sanity check)
  54. hlassert(b->entitynum == 0); // SKY must be in worldspawn entity
  55. mapent = &g_entities[b->entitynum];
  56. mapent->numbrushes++;
  57. newbrush = &g_mapbrushes[g_nummapbrushes];
  58. newbrush->entitynum = b->entitynum;
  59. newbrush->brushnum = g_nummapbrushes - mapent->firstbrush;
  60. newbrush->firstside = g_numbrushsides;
  61. newbrush->numsides = b->numsides;
  62. newbrush->contents = CONTENTS_CLIP;
  63. newbrush->noclip = 0;
  64. for (i = 0; i < b->numsides; i++)
  65. {
  66. int j;
  67. side_t* side = &g_brushsides[g_numbrushsides];
  68. *side = g_brushsides[b->firstside + i];
  69. safe_strncpy(side->td.name, "CLIP", sizeof(side->td.name));
  70. for (j = 0; j < NUM_HULLS; j++)
  71. {
  72. newbrush->hulls[j].faces = NULL;
  73. newbrush->hulls[j].bounds = b->hulls[j].bounds;
  74. }
  75. g_numbrushsides++;
  76. hlassume(g_numbrushsides < MAX_MAP_SIDES, assume_MAX_MAP_SIDES);
  77. }
  78. g_nummapbrushes++;
  79. hlassume(g_nummapbrushes < MAX_MAP_BRUSHES, assume_MAX_MAP_BRUSHES);
  80. }
  81. // =====================================================================================
  82. // HandleSKYCLIP
  83. // clips the whole sky, unconditional of g_skyclip
  84. // =====================================================================================
  85. static void HandleSKYCLIP()
  86. {
  87. int i;
  88. int last;
  89. entity_t* e = &g_entities[0];
  90. for (i = e->firstbrush, last = e->firstbrush + e->numbrushes; i < last; i++)
  91. {
  92. if (g_mapbrushes[i].contents == CONTENTS_SKY)
  93. {
  94. CopySKYtoCLIP(&g_mapbrushes[i]);
  95. }
  96. }
  97. }
  98. // =====================================================================================
  99. // CheckForInvisible
  100. // see if a brush is part of an invisible entity (KGP)
  101. // =====================================================================================
  102. #ifdef HLCSG_NULLIFY_INVISIBLE
  103. static bool CheckForInvisible(entity_t* mapent)
  104. {
  105. using namespace std;
  106. string keyval(ValueForKey(mapent,"classname"));
  107. if(g_invisible_items.count(keyval))
  108. { return true; }
  109. keyval.assign(ValueForKey(mapent,"targetname"));
  110. if(g_invisible_items.count(keyval))
  111. { return true; }
  112. keyval.assign(ValueForKey(mapent,"zhlt_invisible"));
  113. if(!keyval.empty() && strcmp(keyval.c_str(),"0"))
  114. { return true; }
  115. return false;
  116. }
  117. #endif
  118. // =====================================================================================
  119. // ParseBrush
  120. // parse a brush from script
  121. // =====================================================================================
  122. static contents_t ParseBrush(entity_t* mapent)
  123. {
  124. brush_t* b;
  125. int i, j;
  126. side_t* side;
  127. contents_t contents;
  128. bool ok;
  129. #ifdef HLCSG_NULLIFY_INVISIBLE // KGP
  130. bool nullify = CheckForInvisible(mapent);
  131. #endif
  132. hlassume(g_nummapbrushes < MAX_MAP_BRUSHES, assume_MAX_MAP_BRUSHES);
  133. b = &g_mapbrushes[g_nummapbrushes];
  134. g_nummapbrushes++;
  135. b->firstside = g_numbrushsides;
  136. b->entitynum = g_numentities - 1;
  137. b->brushnum = g_nummapbrushes - mapent->firstbrush - 1;
  138. #ifdef HLCSG_CLIPECONOMY // AJM
  139. b->noclip = 0;
  140. #endif
  141. mapent->numbrushes++;
  142. ok = GetToken(true);
  143. while (ok)
  144. {
  145. g_TXcommand = 0;
  146. if (!strcmp(g_token, "}"))
  147. {
  148. break;
  149. }
  150. hlassume(g_numbrushsides < MAX_MAP_SIDES, assume_MAX_MAP_SIDES);
  151. side = &g_brushsides[g_numbrushsides];
  152. g_numbrushsides++;
  153. b->numsides++;
  154. // read the three point plane definition
  155. for (i = 0; i < 3; i++)
  156. {
  157. if (i != 0)
  158. {
  159. GetToken(true);
  160. }
  161. if (strcmp(g_token, "("))
  162. {
  163. Error("Parsing Entity %i, Brush %i, Side %i : Expecting '(' got '%s'",
  164. b->entitynum, b->brushnum, b->numsides, g_token);
  165. }
  166. for (j = 0; j < 3; j++)
  167. {
  168. GetToken(false);
  169. side->planepts[i][j] = atof(g_token);
  170. }
  171. GetToken(false);
  172. if (strcmp(g_token, ")"))
  173. {
  174. Error("Parsing Entity %i, Brush %i, Side %i : Expecting ')' got '%s'",
  175. b->entitynum, b->brushnum, b->numsides, g_token);
  176. }
  177. }
  178. // read the texturedef
  179. GetToken(false);
  180. _strupr(g_token);
  181. #ifdef HLCSG_NULLIFY_INVISIBLE
  182. if(nullify && strncmp(g_token,"BEVEL",5) && strncmp(g_token,"ORIGIN",6))
  183. { safe_strncpy(g_token,"NULL",sizeof(g_token)); }
  184. #endif
  185. safe_strncpy(side->td.name, g_token, sizeof(side->td.name));
  186. if (g_nMapFileVersion < 220) // Worldcraft 2.1-, Radiant
  187. {
  188. GetToken(false);
  189. side->td.vects.valve.shift[0] = atof(g_token);
  190. GetToken(false);
  191. side->td.vects.valve.shift[1] = atof(g_token);
  192. GetToken(false);
  193. side->td.vects.valve.rotate = atof(g_token);
  194. GetToken(false);
  195. side->td.vects.valve.scale[0] = atof(g_token);
  196. GetToken(false);
  197. side->td.vects.valve.scale[1] = atof(g_token);
  198. }
  199. else // Worldcraft 2.2+
  200. {
  201. // texture U axis
  202. GetToken(false);
  203. if (strcmp(g_token, "["))
  204. {
  205. hlassume(false, assume_MISSING_BRACKET_IN_TEXTUREDEF);
  206. }
  207. GetToken(false);
  208. side->td.vects.valve.UAxis[0] = atof(g_token);
  209. GetToken(false);
  210. side->td.vects.valve.UAxis[1] = atof(g_token);
  211. GetToken(false);
  212. side->td.vects.valve.UAxis[2] = atof(g_token);
  213. GetToken(false);
  214. side->td.vects.valve.shift[0] = atof(g_token);
  215. GetToken(false);
  216. if (strcmp(g_token, "]"))
  217. {
  218. Error("missing ']' in texturedef (U)");
  219. }
  220. // texture V axis
  221. GetToken(false);
  222. if (strcmp(g_token, "["))
  223. {
  224. Error("missing '[' in texturedef (V)");
  225. }
  226. GetToken(false);
  227. side->td.vects.valve.VAxis[0] = atof(g_token);
  228. GetToken(false);
  229. side->td.vects.valve.VAxis[1] = atof(g_token);
  230. GetToken(false);
  231. side->td.vects.valve.VAxis[2] = atof(g_token);
  232. GetToken(false);
  233. side->td.vects.valve.shift[1] = atof(g_token);
  234. GetToken(false);
  235. if (strcmp(g_token, "]"))
  236. {
  237. Error("missing ']' in texturedef (V)");
  238. }
  239. // Texture rotation is implicit in U/V axes.
  240. GetToken(false);
  241. side->td.vects.valve.rotate = 0;
  242. // texure scale
  243. GetToken(false);
  244. side->td.vects.valve.scale[0] = atof(g_token);
  245. GetToken(false);
  246. side->td.vects.valve.scale[1] = atof(g_token);
  247. }
  248. ok = GetToken(true); // Done with line, this reads the first item from the next line
  249. if ((g_TXcommand == '1' || g_TXcommand == '2'))
  250. {
  251. // We are QuArK mode and need to translate some numbers to align textures its way
  252. // from QuArK, the texture vectors are given directly from the three points
  253. vec3_t TexPt[2];
  254. int k;
  255. float dot22, dot23, dot33, mdet, aa, bb, dd;
  256. k = g_TXcommand - '0';
  257. for (j = 0; j < 3; j++)
  258. {
  259. TexPt[1][j] = (side->planepts[k][j] - side->planepts[0][j]) * ScaleCorrection;
  260. }
  261. k = 3 - k;
  262. for (j = 0; j < 3; j++)
  263. {
  264. TexPt[0][j] = (side->planepts[k][j] - side->planepts[0][j]) * ScaleCorrection;
  265. }
  266. dot22 = DotProduct(TexPt[0], TexPt[0]);
  267. dot23 = DotProduct(TexPt[0], TexPt[1]);
  268. dot33 = DotProduct(TexPt[1], TexPt[1]);
  269. mdet = dot22 * dot33 - dot23 * dot23;
  270. if (mdet < 1E-6 && mdet > -1E-6)
  271. {
  272. aa = bb = dd = 0;
  273. Warning
  274. ("Degenerate QuArK-style brush texture : Entity %i, Brush %i @ (%f,%f,%f) (%f,%f,%f) (%f,%f,%f)",
  275. b->entitynum, b->brushnum, side->planepts[0][0], side->planepts[0][1], side->planepts[0][2],
  276. side->planepts[1][0], side->planepts[1][1], side->planepts[1][2], side->planepts[2][0],
  277. side->planepts[2][1], side->planepts[2][2]);
  278. }
  279. else
  280. {
  281. mdet = 1.0 / mdet;
  282. aa = dot33 * mdet;
  283. bb = -dot23 * mdet;
  284. //cc = -dot23*mdet; // cc = bb
  285. dd = dot22 * mdet;
  286. }
  287. for (j = 0; j < 3; j++)
  288. {
  289. side->td.vects.quark.vects[0][j] = aa * TexPt[0][j] + bb * TexPt[1][j];
  290. side->td.vects.quark.vects[1][j] = -( /*cc */ bb * TexPt[0][j] + dd * TexPt[1][j]);
  291. }
  292. side->td.vects.quark.vects[0][3] = -DotProduct(side->td.vects.quark.vects[0], side->planepts[0]);
  293. side->td.vects.quark.vects[1][3] = -DotProduct(side->td.vects.quark.vects[1], side->planepts[0]);
  294. }
  295. side->td.txcommand = g_TXcommand; // Quark stuff, but needs setting always
  296. };
  297. b->contents = contents = CheckBrushContents(b);
  298. //
  299. // origin brushes are removed, but they set
  300. // the rotation origin for the rest of the brushes
  301. // in the entity
  302. //
  303. if (contents == CONTENTS_ORIGIN)
  304. {
  305. char string[MAXTOKEN];
  306. vec3_t origin;
  307. b->contents = CONTENTS_SOLID;
  308. CreateBrush(mapent->firstbrush + b->brushnum); // to get sizes
  309. b->contents = contents;
  310. for (i = 0; i < NUM_HULLS; i++)
  311. {
  312. b->hulls[i].faces = NULL;
  313. }
  314. if (b->entitynum != 0) // Ignore for WORLD (code elsewhere enforces no ORIGIN in world message)
  315. {
  316. VectorAdd(b->hulls[0].bounds.m_Mins, b->hulls[0].bounds.m_Maxs, origin);
  317. VectorScale(origin, 0.5, origin);
  318. safe_snprintf(string, MAXTOKEN, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
  319. SetKeyValue(&g_entities[b->entitynum], "origin", string);
  320. }
  321. }
  322. return contents;
  323. }
  324. // =====================================================================================
  325. // ParseMapEntity
  326. // parse an entity from script
  327. // =====================================================================================
  328. bool ParseMapEntity()
  329. {
  330. bool all_clip = true;
  331. int this_entity;
  332. entity_t* mapent;
  333. epair_t* e;
  334. if (!GetToken(true))
  335. {
  336. return false;
  337. }
  338. this_entity = g_numentities;
  339. if (strcmp(g_token, "{"))
  340. {
  341. Error("Parsing Entity %i, expected '{' got '%s'", this_entity, g_token);
  342. }
  343. hlassume(g_numentities < MAX_MAP_ENTITIES, assume_MAX_MAP_ENTITIES);
  344. g_numentities++;
  345. mapent = &g_entities[this_entity];
  346. mapent->firstbrush = g_nummapbrushes;
  347. mapent->numbrushes = 0;
  348. while (1)
  349. {
  350. if (!GetToken(true))
  351. Error("ParseEntity: EOF without closing brace");
  352. if (!strcmp(g_token, "}")) // end of our context
  353. break;
  354. if (!strcmp(g_token, "{")) // must be a brush
  355. {
  356. contents_t contents = ParseBrush(mapent);
  357. if ((contents != CONTENTS_CLIP) && (contents != CONTENTS_ORIGIN))
  358. all_clip = false;
  359. }
  360. else // else assume an epair
  361. {
  362. e = ParseEpair();
  363. if (!strcmp(e->key, "mapversion"))
  364. {
  365. g_nMapFileVersion = atoi(e->value);
  366. }
  367. e->next = mapent->epairs;
  368. mapent->epairs = e;
  369. }
  370. }
  371. if (mapent->numbrushes && all_clip)
  372. Fatal(assume_NO_VISIBILE_BRUSHES, "Entity %i has no visible brushes\n", this_entity);
  373. CheckFatal();
  374. #ifdef ZHLT_DETAIL // AJM
  375. if (!strcmp(ValueForKey(mapent, "classname"), "info_detail") && g_bDetailBrushes && this_entity != 0)
  376. {
  377. // mark all of the brushes in this entity as contents_detail
  378. for (int i = mapent->firstbrush; i < mapent->firstbrush + mapent->numbrushes; i++)
  379. {
  380. g_mapbrushes[i].contents = CONTENTS_DETAIL;
  381. }
  382. // move these brushes to worldspawn
  383. {
  384. brush_t* temp;
  385. int newbrushes;
  386. int worldbrushes;
  387. int i;
  388. newbrushes = mapent->numbrushes;
  389. worldbrushes = g_entities[0].numbrushes;
  390. temp = (brush_t*)Alloc(newbrushes * sizeof(brush_t));
  391. memcpy(temp, g_mapbrushes + mapent->firstbrush, newbrushes * sizeof(brush_t));
  392. for (i = 0; i < newbrushes; i++)
  393. {
  394. temp[i].entitynum = 0;
  395. }
  396. // make space to move the brushes (overlapped copy)
  397. memmove(g_mapbrushes + worldbrushes + newbrushes,
  398. g_mapbrushes + worldbrushes, sizeof(brush_t) * (g_nummapbrushes - worldbrushes - newbrushes));
  399. // copy the new brushes down
  400. memcpy(g_mapbrushes + worldbrushes, temp, sizeof(brush_t) * newbrushes);
  401. // fix up indexes
  402. g_numentities--;
  403. g_entities[0].numbrushes += newbrushes;
  404. for (i = 1; i < g_numentities; i++)
  405. {
  406. g_entities[i].firstbrush += newbrushes;
  407. }
  408. memset(mapent, 0, sizeof(*mapent));
  409. Free(temp);
  410. }
  411. // delete this entity
  412. g_numentities--;
  413. return true;
  414. }
  415. #endif
  416. #ifdef ZHLT_INFO_COMPILE_PARAMETERS // AJM
  417. if (!strcmp(ValueForKey(mapent, "classname"), "info_compile_parameters"))
  418. {
  419. GetParamsFromEnt(mapent);
  420. }
  421. #endif
  422. // if its the worldspawn entity and we need to skyclip, then do it
  423. if ((this_entity == 0) && g_skyclip) // first entitiy
  424. {
  425. HandleSKYCLIP();
  426. }
  427. // if the given entity only has one brush and its an origin brush
  428. if ((mapent->numbrushes == 1) && (g_mapbrushes[mapent->firstbrush].contents == CONTENTS_ORIGIN))
  429. {
  430. brushhull_t* hull = g_mapbrushes[mapent->firstbrush].hulls;
  431. Error("Entity %i, contains ONLY an origin brush near (%.0f,%.0f,%.0f)\n",
  432. this_entity, hull->bounds.m_Mins[0], hull->bounds.m_Mins[1], hull->bounds.m_Mins[2]);
  433. }
  434. GetVectorForKey(mapent, "origin", mapent->origin);
  435. // group entities are just for editor convenience
  436. // toss all brushes into the world entity
  437. if (!g_onlyents && !strcmp("func_group", ValueForKey(mapent, "classname")))
  438. {
  439. // this is pretty gross, because the brushes are expected to be
  440. // in linear order for each entity
  441. brush_t* temp;
  442. int newbrushes;
  443. int worldbrushes;
  444. int i;
  445. newbrushes = mapent->numbrushes;
  446. worldbrushes = g_entities[0].numbrushes;
  447. temp = (brush_t*)Alloc(newbrushes * sizeof(brush_t));
  448. memcpy(temp, g_mapbrushes + mapent->firstbrush, newbrushes * sizeof(brush_t));
  449. for (i = 0; i < newbrushes; i++)
  450. {
  451. temp[i].entitynum = 0;
  452. }
  453. // make space to move the brushes (overlapped copy)
  454. memmove(g_mapbrushes + worldbrushes + newbrushes,
  455. g_mapbrushes + worldbrushes, sizeof(brush_t) * (g_nummapbrushes - worldbrushes - newbrushes));
  456. // copy the new brushes down
  457. memcpy(g_mapbrushes + worldbrushes, temp, sizeof(brush_t) * newbrushes);
  458. // fix up indexes
  459. g_numentities--;
  460. g_entities[0].numbrushes += newbrushes;
  461. for (i = 1; i < g_numentities; i++)
  462. {
  463. g_entities[i].firstbrush += newbrushes;
  464. }
  465. memset(mapent, 0, sizeof(*mapent));
  466. Free(temp);
  467. }
  468. return true;
  469. }
  470. // =====================================================================================
  471. // CountEngineEntities
  472. // =====================================================================================
  473. unsigned int CountEngineEntities()
  474. {
  475. unsigned int x;
  476. unsigned num_engine_entities = 0;
  477. entity_t* mapent = g_entities;
  478. // for each entity in the map
  479. for (x=0; x<g_numentities; x++, mapent++)
  480. {
  481. const char* classname = ValueForKey(mapent, "classname");
  482. // if its a light_spot or light_env, dont include it as an engine entity!
  483. if (classname)
  484. {
  485. if ( !strncasecmp(classname, "light", 5)
  486. || !strncasecmp(classname, "light_spot", 10)
  487. || !strncasecmp(classname, "light_environment", 17)
  488. )
  489. {
  490. const char* style = ValueForKey(mapent, "style");
  491. const char* targetname = ValueForKey(mapent, "targetname");
  492. // lightspots and lightenviroments dont have a targetname or style
  493. if (!strlen(targetname) && !atoi(style))
  494. {
  495. continue;
  496. }
  497. }
  498. }
  499. num_engine_entities++;
  500. }
  501. return num_engine_entities;
  502. }
  503. // =====================================================================================
  504. // LoadMapFile
  505. // wrapper for LoadScriptFile
  506. // parse in script entities
  507. // =====================================================================================
  508. const char* ContentsToString(const contents_t type);
  509. void LoadMapFile(const char* const filename)
  510. {
  511. unsigned num_engine_entities;
  512. LoadScriptFile(filename);
  513. g_numentities = 0;
  514. while (ParseMapEntity())
  515. {
  516. }
  517. // AJM debug
  518. /*
  519. for (int i = 0; i < g_numentities; i++)
  520. {
  521. Log("entity: %i - %i brushes - %s\n", i, g_entities[i].numbrushes, ValueForKey(&g_entities[i], "classname"));
  522. }
  523. Log("total entities: %i\ntotal brushes: %i\n\n", g_numentities, g_nummapbrushes);
  524. for (i = g_entities[0].firstbrush; i < g_entities[0].firstbrush + g_entities[0].numbrushes; i++)
  525. {
  526. Log("worldspawn brush %i: contents %s\n", i, ContentsToString((contents_t)g_mapbrushes[i].contents));
  527. }
  528. */
  529. num_engine_entities = CountEngineEntities();
  530. hlassume(num_engine_entities < MAX_ENGINE_ENTITIES, assume_MAX_ENGINE_ENTITIES);
  531. CheckFatal();
  532. Verbose("Load map:%s\n", filename);
  533. Verbose("%5i brushes\n", g_nummapbrushes);
  534. Verbose("%5i map entities \n", g_numentities - num_engine_entities);
  535. Verbose("%5i engine entities\n", num_engine_entities);
  536. // AJM: added in
  537. #ifdef HLCSG_AUTOWAD
  538. GetUsedTextures();
  539. #endif
  540. }