PageRenderTime 71ms CodeModel.GetById 42ms RepoModel.GetById 0ms app.codeStats 0ms

/legacy_one/trunk/b_node.c

#
C | 628 lines | 488 code | 81 blank | 59 comment | 140 complexity | 8661f4f8e0d3ed745ad4b53e4d43c38d MD5 | raw file
Possible License(s): GPL-2.0
  1. // Emacs style mode select -*- C++ -*-
  2. //-----------------------------------------------------------------------------
  3. //
  4. // $Id: b_node.c 696 2010-07-09 03:12:42Z wesleyjohnson $
  5. //
  6. // Copyright (C) 2002 by DooM Legacy Team.
  7. //
  8. // This program is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU General Public License
  10. // as published by the Free Software Foundation; either version 2
  11. // of the License, or (at your option) any later version.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU General Public License for more details.
  17. //
  18. //
  19. // $Log: b_node.c,v $
  20. // Revision 1.3 2002/09/28 06:53:11 tonyd
  21. // fixed CR problem, fixed game options crash
  22. //
  23. // Revision 1.2 2002/09/27 16:40:08 tonyd
  24. // First commit of acbot
  25. //
  26. #include "b_bot.h"
  27. #include "b_node.h"
  28. #include "z_zone.h"
  29. #include "g_game.h"
  30. #include "r_state.h"
  31. #include "p_maputl.h"
  32. #include "r_main.h"
  33. #include "p_local.h"
  34. #include "p_setup.h"
  35. #define x2ClosestPosX(a) ((fixed_t)((float)(a)/(float)BOTNODEGRIDSIZE + 0.5) - xOffset)
  36. #define y2ClosestPosY(a) ((fixed_t)((float)(a)/(float)BOTNODEGRIDSIZE + 0.5) - yOffset)
  37. #define x2PosX(a) ((fixed_t)((float)(a)/(float)BOTNODEGRIDSIZE) - xOffset)
  38. #define y2PosY(a) ((fixed_t)((float)(a)/(float)BOTNODEGRIDSIZE) - yOffset)
  39. sector_t *oksector = NULL;
  40. boolean botdoorfound = false,
  41. botteledestfound = false;
  42. int botteledestx,
  43. botteledesty,
  44. botteletype,
  45. numbotnodes,
  46. xOffset,
  47. xSize,
  48. yOffset,
  49. ySize;
  50. SearchNode_t *** botNodeArray;
  51. static sector_t *last_s;
  52. SearchNode_t* B_FindClosestNode(fixed_t x, fixed_t y)
  53. {
  54. int i, j,
  55. depth = 0;
  56. botdirtype_t dir = BDI_SOUTH;
  57. SearchNode_t *closestNode = NULL;
  58. i = x = x2ClosestPosX(x);
  59. j = y = y2ClosestPosY(y);
  60. while (!closestNode && (depth < 50))
  61. {
  62. if ((i >= 0) && (i < xSize) && (j >= 0) && (j < ySize))
  63. {
  64. if (botNodeArray[i][j])
  65. closestNode = botNodeArray[i][j];
  66. }
  67. switch (dir)
  68. {
  69. case (BDI_EAST):
  70. if (++i > x + depth)
  71. {
  72. i--; //change it back
  73. dir = BDI_NORTH; //go in the new direction
  74. }
  75. break;
  76. case (BDI_NORTH):
  77. if (++j > y + depth)
  78. {
  79. j--; //change it back
  80. dir = BDI_WEST;
  81. }
  82. break;
  83. case (BDI_WEST):
  84. if (--i < x - depth)
  85. {
  86. i++; //change it back
  87. dir = BDI_SOUTH;
  88. }
  89. break;
  90. case (BDI_SOUTH):
  91. if (--j < y - depth)
  92. {
  93. j++; //change it back
  94. dir = BDI_EAST;
  95. depth++;
  96. }
  97. break;
  98. default: //shouldn't ever happen
  99. break;
  100. }
  101. }
  102. return closestNode;
  103. }
  104. SearchNode_t* B_GetClosestReachableNode(fixed_t x, fixed_t y)
  105. {
  106. int i,j, nx, ny;
  107. SearchNode_t *tempNode = NULL;
  108. nx = x2PosX(x);
  109. ny = y2PosY(y);
  110. if ((nx >= 0) && (nx < xSize) && (ny >= 0) && (ny < ySize))
  111. {
  112. tempNode = botNodeArray[nx][ny];
  113. if (tempNode && !B_NodeReachable(NULL, x, y, posX2x(tempNode->x), posY2y(tempNode->y)))
  114. tempNode = NULL;
  115. }
  116. for (i = nx-1; !tempNode && (i <= nx+1); i++)
  117. {
  118. for (j = ny-1; !tempNode && (j <= ny+1); j++)
  119. {
  120. if ((i >= 0) && (i < xSize) && (j >= 0) && (j < ySize))
  121. {
  122. tempNode = botNodeArray[i][j];
  123. if (tempNode)
  124. {
  125. if (!B_NodeReachable(NULL, x, y, posX2x(tempNode->x), posY2y(tempNode->y)))
  126. tempNode = NULL;
  127. }
  128. }
  129. }
  130. }
  131. if (!tempNode)
  132. tempNode = B_FindClosestNode(x, y);
  133. return tempNode;
  134. }
  135. SearchNode_t* B_GetNodeAt(fixed_t x, fixed_t y)
  136. {
  137. int i,j, nx, ny;
  138. SearchNode_t *tempNode = NULL;
  139. nx = x2PosX(x);
  140. ny = y2PosY(y);
  141. if ((nx >= 0) && (nx < xSize) && (ny >= 0) && (ny < ySize))
  142. {
  143. tempNode = botNodeArray[nx][ny];
  144. if (tempNode && !B_NodeReachable(NULL, posX2x(tempNode->x), posY2y(tempNode->y), x, y))
  145. tempNode = NULL;
  146. }
  147. for (i = nx-1; !tempNode && (i <= nx+1); i++)
  148. {
  149. for (j = ny-1; !tempNode && (j <= ny+1); j++)
  150. {
  151. if ((i >= 0) && (i < xSize) && (j >= 0) && (j < ySize) && ((i != nx) && (j != ny)))
  152. {
  153. tempNode = botNodeArray[i][j];
  154. if (tempNode)
  155. {
  156. if (!B_NodeReachable(NULL, posX2x(tempNode->x), posY2y(tempNode->y), x, y))
  157. tempNode = NULL;
  158. }
  159. }
  160. }
  161. }
  162. return tempNode;
  163. }
  164. boolean B_PTRPathTraverse (intercept_t *in)
  165. {
  166. fixed_t floorheight, ceilingheight;
  167. line_t *line;
  168. sector_t *s;
  169. if (in->isaline)
  170. {
  171. line = in->d.line;
  172. if (!(line->flags & ML_TWOSIDED) || (line->flags & ML_BLOCKING))
  173. return false;
  174. else
  175. {
  176. int i;
  177. mobj_t* m;
  178. thinker_t* th;
  179. switch(line->special)
  180. {
  181. case 1: // Vertical Door
  182. case 26: // Blue Door/Locked
  183. case 27: // Yellow Door /Locked
  184. case 28: // Red Door /Locked
  185. case 31: // Manual door open
  186. case 32: // Blue locked door open
  187. case 33: // Red locked door open
  188. case 34: // Yellow locked door open
  189. case 62: //SR slow lift
  190. case 123: //SR blazing lift
  191. case 117: // Blazing door raise
  192. case 118: // Blazing door open
  193. //Determine if looking at backsector/frontsector.
  194. oksector = (line->backsector == last_s) ? line->frontsector : line->backsector;
  195. botdoorfound = true;
  196. break;
  197. //case 39: // TELEPORT TRIGGER useful only once anyway so forget it
  198. case 97: // TELEPORT RETRIGGER
  199. case 208: //boom Silent thing teleporters
  200. case 207:
  201. for (i = -1; (i = P_FindSectorFromLineTag(line, i)) >= 0;)
  202. {
  203. for (th = thinkercap.next; th != &thinkercap; th = th->next)
  204. {
  205. if (th->function.acp1 == (actionf_p1) P_MobjThinker &&
  206. (m = (mobj_t *) th)->type == MT_TELEPORTMAN &&
  207. m->subsector->sector-sectors == i)
  208. {
  209. botteledestfound = true;
  210. botteledestx = m->x;
  211. botteledesty = m->y;
  212. botteletype = line->special;
  213. //CONS_Printf("found a teleport thing going to x:%d, y:%d\n", botteledestx, botteledesty);
  214. }
  215. }
  216. }
  217. break;
  218. // boom linedef types.
  219. case 243: //Same as below but trigger once.
  220. case 244: //Silent line to line teleporter
  221. case 262: //Same as 243 but reversed
  222. case 263: //Same as 244 but reversed
  223. if (!botteledestfound)
  224. {
  225. for (i = -1; (i = P_FindLineFromLineTag(line, i)) >= 0;)
  226. {
  227. if (&lines[i] != line)
  228. {
  229. botteledestfound = true;
  230. botteledestx = (lines[i].v1->x+lines[i].v2->x)/2 - ((line->v1->x+line->v2->x)/2 - botteledestx);
  231. botteledesty = (lines[i].v1->y+lines[i].v2->y)/2 - ((line->v1->y+line->v2->y)/2 - botteledesty);
  232. botteletype = line->special;
  233. //CONS_Printf("found a teleporter line going to x:%d, y:%d\n", botteledestx, botteledesty);
  234. }
  235. break;
  236. }
  237. }
  238. default: //not a special type
  239. botteledestfound = false;
  240. //Determine if looking at backsector/frontsector.
  241. s = (line->backsector == last_s) ? line->frontsector : line->backsector;
  242. ceilingheight = s->ceilingheight;
  243. floorheight = s->floorheight;
  244. if (s != oksector)
  245. {
  246. if (((ceilingheight - floorheight) < mobjinfo[MT_PLAYER].height) && !s->tag)
  247. return false; //can't fit
  248. if (((floorheight > (last_s->floorheight+(45<<FRACBITS))) || (((floorheight > (last_s->floorheight+(37<<FRACBITS))) && (last_s->floortype == FLOOR_WATER)))))
  249. return false; //i can't jump or reach there
  250. }
  251. } // switch
  252. return true;
  253. }
  254. }
  255. else if ((in->d.thing->flags & MF_SOLID) && !(in->d.thing->flags & MF_SHOOTABLE))
  256. return false;
  257. return true;
  258. }
  259. boolean PIT_NodeReachable (line_t* ld)
  260. {
  261. if (tm_bbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
  262. || tm_bbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
  263. || tm_bbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
  264. || tm_bbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
  265. return true;
  266. if (P_BoxOnLineSide (tm_bbox, ld) != -1)
  267. return true;
  268. if (ld->flags & ML_BLOCKING)
  269. return false;
  270. return true;
  271. }
  272. boolean B_CheckNodePosition(mobj_t* thing, fixed_t x, fixed_t y)
  273. {
  274. int xl, xh;
  275. int yl, yh;
  276. int bx, by;
  277. // subsector_t* newsubsec;
  278. tm_bbox[BOXTOP] = y + thing->radius;
  279. tm_bbox[BOXBOTTOM] = y - thing->radius;
  280. tm_bbox[BOXRIGHT] = x + thing->radius;
  281. tm_bbox[BOXLEFT] = x - thing->radius;
  282. validcount++;
  283. numspechit = 0;
  284. /* xl = (tm_bbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
  285. xh = (tm_bbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
  286. yl = (tm_bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
  287. yh = (tm_bbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
  288. for (bx=xl ; bx<=xh ; bx++)
  289. for (by=yl ; by<=yh ; by++)
  290. if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
  291. return false;
  292. */
  293. // check lines
  294. xl = (tm_bbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
  295. xh = (tm_bbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
  296. yl = (tm_bbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
  297. yh = (tm_bbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
  298. for (bx=xl ; bx<=xh ; bx++)
  299. {
  300. for (by=yl ; by<=yh ; by++)
  301. if (!P_BlockLinesIterator (bx,by,PIT_NodeReachable))
  302. return false;
  303. }
  304. return true;
  305. }
  306. boolean B_NodeReachable(mobj_t* mo, fixed_t x, fixed_t y, fixed_t destx, fixed_t desty)
  307. {
  308. fixed_t nx = x2PosX(destx),
  309. ny = y2PosY(desty);
  310. botdoorfound = false;
  311. botteledestfound = false;
  312. if ((nx >= 0) && (nx < xSize) && (ny >= 0) && (ny < ySize))
  313. {
  314. botteledestx = destx;
  315. botteledesty = desty;
  316. last_s = R_PointInSubsector(x, y)->sector;
  317. if (mo)
  318. {
  319. if (B_CheckNodePosition(mo, destx, desty)
  320. && P_PathTraverse (x, y, destx - 1, desty + 1, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
  321. && P_PathTraverse (x, y, destx + 1, desty + 1, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
  322. && P_PathTraverse (x, y, destx - 1, desty - 1, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
  323. && P_PathTraverse (x, y, destx + 1, desty - 1, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
  324. && P_PathTraverse (x - 1, y + 1, destx, desty, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
  325. && P_PathTraverse (x + 1, y + 1, destx, desty, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
  326. && P_PathTraverse (x - 1, y - 1, destx, desty, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
  327. && P_PathTraverse (x + 1, y - 1, destx, desty, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
  328. && P_PathTraverse (x, y, destx, desty, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse))
  329. {
  330. return true;
  331. }
  332. else
  333. {
  334. botteledestfound = false;
  335. return false;
  336. }
  337. }
  338. else
  339. return P_PathTraverse (x, y, destx, desty, PT_ADDLINES, B_PTRPathTraverse);
  340. }
  341. return false;
  342. }
  343. int B_GetNodeCost(SearchNode_t* node)
  344. {
  345. int cost = 10000;
  346. sector_t* sector;
  347. if (node)
  348. {
  349. sector = R_PointInSubsector(node->x, node->y)->sector;
  350. if (sector->floortype == FLOOR_LAVA)
  351. cost = 50000;
  352. else if (sector->floortype == FLOOR_LAVA)
  353. cost = 40000;
  354. }
  355. return cost;
  356. }
  357. SearchNode_t* B_CreateNode(fixed_t x, fixed_t y)
  358. {
  359. SearchNode_t* newnode = Z_Malloc(sizeof(SearchNode_t), PU_LEVEL, 0);
  360. newnode->cost = 0;
  361. newnode->f = 0;
  362. newnode->heuristic = 0;
  363. newnode->vnext = NULL;
  364. newnode->pprevious = NULL;
  365. newnode->pnext = NULL;
  366. newnode->visited = false;
  367. newnode->x = x;
  368. newnode->y = y;
  369. //CONS_Printf("Created node at x:%d, y:%d.\n", (x>>FRACBITS), (y>>FRACBITS));
  370. botNodeArray[x][y] = newnode;
  371. /*newnode->mo = P_SpawnMobj(posX2x(newnode->x), posY2y(newnode->y), R_PointInSubsector(posX2x(newnode->x), posY2y(newnode->y))->sector->floorheight, MT_MISC49);*/
  372. return newnode;
  373. }
  374. void B_DeleteNode(SearchNode_t* node)
  375. {
  376. Z_Free(node);
  377. // CONS_Printf("deleted a node\n");
  378. }
  379. void B_SetNodeTeleDest(SearchNode_t* node)
  380. {
  381. fixed_t x = x2ClosestPosX(botteledestx),
  382. y = y2ClosestPosY(botteledesty);
  383. //CONS_Printf("trying to make a tele node at x:%d, y:%d\n", botteledestx>>FRACBITS, botteledesty>>FRACBITS);
  384. if( x >= 0 && x < xSize && y >=0 && y <= ySize )
  385. {
  386. if (!botNodeArray[x][y])
  387. {
  388. node->dir[BDI_TELEPORT] = B_CreateNode(x, y);
  389. //CONS_Printf("created teleporter node at x:%d, y:%d\n", botteledestx>>FRACBITS, botteledesty>>FRACBITS);
  390. numbotnodes++;
  391. B_BuildNodes(node->dir[BDI_TELEPORT]);
  392. }
  393. else
  394. node->dir[BDI_TELEPORT] = botNodeArray[x][y];
  395. node->costDir[BDI_TELEPORT] = 20000;//B_GetNodeCost(node->dir[TELEPORT]);
  396. }
  397. else
  398. {
  399. I_SoftError( "Tele node bad pos, x:%d, y:%d\n", botteledestx>>FRACBITS, botteledesty>>FRACBITS);
  400. }
  401. }
  402. void B_BuildNodes(SearchNode_t* node)
  403. {
  404. fixed_t angle,
  405. nx, ny,
  406. extraCost;
  407. LinkedList_t *queue = B_LLCreate();
  408. B_LLInsertFirstNode(queue, node);
  409. // [WDJ] 1/22/2009 FreeDoom MAP10 can get stuck in this loop, sometimes.
  410. // It keeps cycling forever through the queue, putting entries back in,
  411. // because botNodeArray[nx][ny] == NULL.
  412. // Started happening after changes to Z_ALLOC to reduce fragmentation.
  413. // Resolved by removing code that searched twice to get less fragmentation.
  414. // Some of the purgable memory blocks deleted were probably still in use
  415. // in this routine. The handling of such memory by this routine is fragile,
  416. // it is suspicious, and probably needs a more robust fix.
  417. while (!B_LLIsEmpty(queue))
  418. {
  419. node = B_LLRemoveLastNode(queue);
  420. node->dir[BDI_TELEPORT] = NULL;
  421. for (angle = BDI_EAST; angle <= BDI_SOUTHEAST; angle++)
  422. {
  423. extraCost = 0;
  424. switch(angle)
  425. {
  426. case (BDI_EAST):
  427. nx = node->x + 1;
  428. ny = node->y;
  429. break;
  430. case (BDI_NORTHEAST):
  431. nx = node->x + 1;
  432. ny = node->y + 1;
  433. extraCost = 5000; //because diaginal
  434. break;
  435. case (BDI_NORTH):
  436. nx = node->x;
  437. ny = node->y + 1;
  438. break;
  439. case (BDI_NORTHWEST):
  440. nx = node->x - 1;
  441. ny = node->y + 1;
  442. extraCost = 5000; //because diaginal
  443. break;
  444. case (BDI_WEST):
  445. nx = node->x - 1;
  446. ny = node->y;
  447. break;
  448. case (BDI_SOUTHWEST):
  449. nx = node->x - 1;
  450. ny = node->y - 1;
  451. extraCost = 5000; //because diaginal
  452. break;
  453. case (BDI_SOUTH):
  454. nx = node->x;
  455. ny = node->y - 1;
  456. break;
  457. case (BDI_SOUTHEAST):
  458. nx = node->x + 1;
  459. ny = node->y - 1;
  460. extraCost = 5000; //because diaginal
  461. break;
  462. default: //shouldn't ever happen
  463. nx = ny = node->x; //shut compiler up
  464. break;
  465. }
  466. if (B_NodeReachable(players[0].mo, posX2x(node->x), posY2y(node->y), posX2x(nx), posY2y(ny)))
  467. {
  468. if (!botNodeArray[nx][ny])
  469. {
  470. node->dir[angle] = B_CreateNode(nx, ny);
  471. numbotnodes++;
  472. B_LLInsertFirstNode(queue, node->dir[angle]);
  473. }
  474. else
  475. node->dir[angle] = botNodeArray[nx][ny];
  476. node->costDir[angle] = B_GetNodeCost(node->dir[angle]) + extraCost;
  477. if (botteledestfound)
  478. B_SetNodeTeleDest(node->dir[angle]);
  479. }
  480. else
  481. node->dir[angle] = NULL;
  482. }
  483. }
  484. B_LLDelete(queue);
  485. }
  486. void B_InitNodes( void )
  487. {
  488. int i, j, px, py,
  489. xMax = -1000000000,
  490. xMin = 1000000000,
  491. yMax = -1000000000,
  492. yMin = 1000000000;
  493. SearchNode_t* tempNode;
  494. for (i=0; i<numvertexes; i++)
  495. {
  496. if (vertexes[i].x < xMin)
  497. xMin = vertexes[i].x;
  498. else if (vertexes[i].x > xMax)
  499. xMax = vertexes[i].x;
  500. if (vertexes[i].y < yMin)
  501. yMin = vertexes[i].y;
  502. else if (vertexes[i].y > yMax)
  503. yMax = vertexes[i].y;
  504. }
  505. // xMin-=BOTNODEGRIDSIZE; xMax+=2*BOTNODEGRIDSIZE;
  506. // yMin-=BOTNODEGRIDSIZE; yMax+=2*BOTNODEGRIDSIZE;
  507. // [WDJ] On overly-large map max-min will overflow signed
  508. xOffset = xMin/BOTNODEGRIDSIZE;
  509. xSize = ceil( ((double)xMax - (double)xMin)/(double)BOTNODEGRIDSIZE) + 1;
  510. yOffset = yMin/BOTNODEGRIDSIZE;
  511. ySize = ceil( ((double)yMax - (double)yMin)/(double)BOTNODEGRIDSIZE) + 1;
  512. botNodeArray = (SearchNode_t***)Z_Malloc(xSize*sizeof(SearchNode_t*),PU_LEVEL,0);
  513. for (i=0; i<xSize; i++)
  514. {
  515. botNodeArray[i] = (SearchNode_t**)Z_Malloc(ySize*sizeof(SearchNode_t*),PU_LEVEL,0);
  516. for (j=0; j<ySize; j++)
  517. botNodeArray[i][j] = NULL;
  518. }
  519. numbotnodes = 0;
  520. CONS_Printf("Building nodes for acbot.....\n");
  521. for (i = 0; i < MAXPLAYERS; i++)
  522. if (playerstarts[i])
  523. {
  524. px = playerstarts[i]->x/(BOTNODEGRIDSIZE>>FRACBITS) - xOffset;
  525. py = playerstarts[i]->y/(BOTNODEGRIDSIZE>>FRACBITS) - yOffset;
  526. if (((px >= 0) && (px < xSize) && (py >= 0) && (py < ySize)) && (!botNodeArray[px][py]))
  527. {
  528. tempNode = B_CreateNode(px, py);
  529. numbotnodes++;
  530. B_BuildNodes(tempNode);
  531. }
  532. }
  533. for (i = 0; i < MAX_DM_STARTS; i++)
  534. if (deathmatchstarts[i])
  535. {
  536. px = deathmatchstarts[i]->x/(BOTNODEGRIDSIZE>>FRACBITS) - xOffset;
  537. py = deathmatchstarts[i]->y/(BOTNODEGRIDSIZE>>FRACBITS) - yOffset;
  538. if (((px >= 0) && (px < xSize) && (py >= 0) && (py < ySize)) && (!botNodeArray[px][py]))
  539. {
  540. tempNode = B_CreateNode(px, py);
  541. numbotnodes++;
  542. B_BuildNodes(tempNode);
  543. }
  544. }
  545. CONS_Printf("Completed building %d nodes.\n", numbotnodes);
  546. }