/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
- // Emacs style mode select -*- C++ -*-
- //-----------------------------------------------------------------------------
- //
- // $Id: b_node.c 696 2010-07-09 03:12:42Z wesleyjohnson $
- //
- // Copyright (C) 2002 by DooM Legacy Team.
- //
- // This program 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 2
- // of the License, or (at your option) any later version.
- //
- // This program 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.
- //
- //
- // $Log: b_node.c,v $
- // Revision 1.3 2002/09/28 06:53:11 tonyd
- // fixed CR problem, fixed game options crash
- //
- // Revision 1.2 2002/09/27 16:40:08 tonyd
- // First commit of acbot
- //
- #include "b_bot.h"
- #include "b_node.h"
- #include "z_zone.h"
- #include "g_game.h"
- #include "r_state.h"
- #include "p_maputl.h"
- #include "r_main.h"
- #include "p_local.h"
- #include "p_setup.h"
- #define x2ClosestPosX(a) ((fixed_t)((float)(a)/(float)BOTNODEGRIDSIZE + 0.5) - xOffset)
- #define y2ClosestPosY(a) ((fixed_t)((float)(a)/(float)BOTNODEGRIDSIZE + 0.5) - yOffset)
- #define x2PosX(a) ((fixed_t)((float)(a)/(float)BOTNODEGRIDSIZE) - xOffset)
- #define y2PosY(a) ((fixed_t)((float)(a)/(float)BOTNODEGRIDSIZE) - yOffset)
- sector_t *oksector = NULL;
- boolean botdoorfound = false,
- botteledestfound = false;
- int botteledestx,
- botteledesty,
- botteletype,
- numbotnodes,
- xOffset,
- xSize,
- yOffset,
- ySize;
- SearchNode_t *** botNodeArray;
- static sector_t *last_s;
- SearchNode_t* B_FindClosestNode(fixed_t x, fixed_t y)
- {
- int i, j,
- depth = 0;
- botdirtype_t dir = BDI_SOUTH;
- SearchNode_t *closestNode = NULL;
- i = x = x2ClosestPosX(x);
- j = y = y2ClosestPosY(y);
- while (!closestNode && (depth < 50))
- {
- if ((i >= 0) && (i < xSize) && (j >= 0) && (j < ySize))
- {
- if (botNodeArray[i][j])
- closestNode = botNodeArray[i][j];
- }
- switch (dir)
- {
- case (BDI_EAST):
- if (++i > x + depth)
- {
- i--; //change it back
- dir = BDI_NORTH; //go in the new direction
- }
- break;
- case (BDI_NORTH):
- if (++j > y + depth)
- {
- j--; //change it back
- dir = BDI_WEST;
- }
- break;
- case (BDI_WEST):
- if (--i < x - depth)
- {
- i++; //change it back
- dir = BDI_SOUTH;
- }
- break;
- case (BDI_SOUTH):
- if (--j < y - depth)
- {
- j++; //change it back
- dir = BDI_EAST;
- depth++;
- }
- break;
- default: //shouldn't ever happen
- break;
- }
- }
- return closestNode;
- }
- SearchNode_t* B_GetClosestReachableNode(fixed_t x, fixed_t y)
- {
- int i,j, nx, ny;
- SearchNode_t *tempNode = NULL;
- nx = x2PosX(x);
- ny = y2PosY(y);
- if ((nx >= 0) && (nx < xSize) && (ny >= 0) && (ny < ySize))
- {
- tempNode = botNodeArray[nx][ny];
- if (tempNode && !B_NodeReachable(NULL, x, y, posX2x(tempNode->x), posY2y(tempNode->y)))
- tempNode = NULL;
- }
- for (i = nx-1; !tempNode && (i <= nx+1); i++)
- {
- for (j = ny-1; !tempNode && (j <= ny+1); j++)
- {
- if ((i >= 0) && (i < xSize) && (j >= 0) && (j < ySize))
- {
- tempNode = botNodeArray[i][j];
- if (tempNode)
- {
- if (!B_NodeReachable(NULL, x, y, posX2x(tempNode->x), posY2y(tempNode->y)))
- tempNode = NULL;
- }
- }
- }
- }
- if (!tempNode)
- tempNode = B_FindClosestNode(x, y);
- return tempNode;
- }
- SearchNode_t* B_GetNodeAt(fixed_t x, fixed_t y)
- {
- int i,j, nx, ny;
- SearchNode_t *tempNode = NULL;
- nx = x2PosX(x);
- ny = y2PosY(y);
- if ((nx >= 0) && (nx < xSize) && (ny >= 0) && (ny < ySize))
- {
- tempNode = botNodeArray[nx][ny];
- if (tempNode && !B_NodeReachable(NULL, posX2x(tempNode->x), posY2y(tempNode->y), x, y))
- tempNode = NULL;
- }
- for (i = nx-1; !tempNode && (i <= nx+1); i++)
- {
- for (j = ny-1; !tempNode && (j <= ny+1); j++)
- {
- if ((i >= 0) && (i < xSize) && (j >= 0) && (j < ySize) && ((i != nx) && (j != ny)))
- {
- tempNode = botNodeArray[i][j];
- if (tempNode)
- {
- if (!B_NodeReachable(NULL, posX2x(tempNode->x), posY2y(tempNode->y), x, y))
- tempNode = NULL;
- }
- }
- }
- }
- return tempNode;
- }
- boolean B_PTRPathTraverse (intercept_t *in)
- {
- fixed_t floorheight, ceilingheight;
- line_t *line;
- sector_t *s;
- if (in->isaline)
- {
- line = in->d.line;
- if (!(line->flags & ML_TWOSIDED) || (line->flags & ML_BLOCKING))
- return false;
- else
- {
- int i;
- mobj_t* m;
- thinker_t* th;
- switch(line->special)
- {
- case 1: // Vertical Door
- case 26: // Blue Door/Locked
- case 27: // Yellow Door /Locked
- case 28: // Red Door /Locked
- case 31: // Manual door open
- case 32: // Blue locked door open
- case 33: // Red locked door open
- case 34: // Yellow locked door open
- case 62: //SR slow lift
- case 123: //SR blazing lift
- case 117: // Blazing door raise
- case 118: // Blazing door open
- //Determine if looking at backsector/frontsector.
- oksector = (line->backsector == last_s) ? line->frontsector : line->backsector;
- botdoorfound = true;
- break;
- //case 39: // TELEPORT TRIGGER useful only once anyway so forget it
- case 97: // TELEPORT RETRIGGER
- case 208: //boom Silent thing teleporters
- case 207:
- for (i = -1; (i = P_FindSectorFromLineTag(line, i)) >= 0;)
- {
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
- {
- if (th->function.acp1 == (actionf_p1) P_MobjThinker &&
- (m = (mobj_t *) th)->type == MT_TELEPORTMAN &&
- m->subsector->sector-sectors == i)
- {
- botteledestfound = true;
- botteledestx = m->x;
- botteledesty = m->y;
- botteletype = line->special;
- //CONS_Printf("found a teleport thing going to x:%d, y:%d\n", botteledestx, botteledesty);
- }
- }
- }
- break;
- // boom linedef types.
- case 243: //Same as below but trigger once.
- case 244: //Silent line to line teleporter
- case 262: //Same as 243 but reversed
- case 263: //Same as 244 but reversed
- if (!botteledestfound)
- {
- for (i = -1; (i = P_FindLineFromLineTag(line, i)) >= 0;)
- {
- if (&lines[i] != line)
- {
- botteledestfound = true;
- botteledestx = (lines[i].v1->x+lines[i].v2->x)/2 - ((line->v1->x+line->v2->x)/2 - botteledestx);
- botteledesty = (lines[i].v1->y+lines[i].v2->y)/2 - ((line->v1->y+line->v2->y)/2 - botteledesty);
- botteletype = line->special;
- //CONS_Printf("found a teleporter line going to x:%d, y:%d\n", botteledestx, botteledesty);
- }
- break;
- }
- }
- default: //not a special type
- botteledestfound = false;
- //Determine if looking at backsector/frontsector.
- s = (line->backsector == last_s) ? line->frontsector : line->backsector;
- ceilingheight = s->ceilingheight;
- floorheight = s->floorheight;
- if (s != oksector)
- {
- if (((ceilingheight - floorheight) < mobjinfo[MT_PLAYER].height) && !s->tag)
- return false; //can't fit
- if (((floorheight > (last_s->floorheight+(45<<FRACBITS))) || (((floorheight > (last_s->floorheight+(37<<FRACBITS))) && (last_s->floortype == FLOOR_WATER)))))
- return false; //i can't jump or reach there
- }
- } // switch
- return true;
- }
- }
- else if ((in->d.thing->flags & MF_SOLID) && !(in->d.thing->flags & MF_SHOOTABLE))
- return false;
- return true;
- }
- boolean PIT_NodeReachable (line_t* ld)
- {
- if (tm_bbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
- || tm_bbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
- || tm_bbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
- || tm_bbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
- return true;
- if (P_BoxOnLineSide (tm_bbox, ld) != -1)
- return true;
- if (ld->flags & ML_BLOCKING)
- return false;
- return true;
- }
- boolean B_CheckNodePosition(mobj_t* thing, fixed_t x, fixed_t y)
- {
- int xl, xh;
- int yl, yh;
- int bx, by;
- // subsector_t* newsubsec;
- tm_bbox[BOXTOP] = y + thing->radius;
- tm_bbox[BOXBOTTOM] = y - thing->radius;
- tm_bbox[BOXRIGHT] = x + thing->radius;
- tm_bbox[BOXLEFT] = x - thing->radius;
- validcount++;
- numspechit = 0;
-
- /* xl = (tm_bbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
- xh = (tm_bbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
- yl = (tm_bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
- yh = (tm_bbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
- for (bx=xl ; bx<=xh ; bx++)
- for (by=yl ; by<=yh ; by++)
- if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
- return false;
- */
- // check lines
- xl = (tm_bbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
- xh = (tm_bbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
- yl = (tm_bbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
- yh = (tm_bbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
- for (bx=xl ; bx<=xh ; bx++)
- {
- for (by=yl ; by<=yh ; by++)
- if (!P_BlockLinesIterator (bx,by,PIT_NodeReachable))
- return false;
- }
-
- return true;
- }
- boolean B_NodeReachable(mobj_t* mo, fixed_t x, fixed_t y, fixed_t destx, fixed_t desty)
- {
- fixed_t nx = x2PosX(destx),
- ny = y2PosY(desty);
- botdoorfound = false;
- botteledestfound = false;
- if ((nx >= 0) && (nx < xSize) && (ny >= 0) && (ny < ySize))
- {
- botteledestx = destx;
- botteledesty = desty;
- last_s = R_PointInSubsector(x, y)->sector;
- if (mo)
- {
- if (B_CheckNodePosition(mo, destx, desty)
- && P_PathTraverse (x, y, destx - 1, desty + 1, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
- && P_PathTraverse (x, y, destx + 1, desty + 1, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
- && P_PathTraverse (x, y, destx - 1, desty - 1, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
- && P_PathTraverse (x, y, destx + 1, desty - 1, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
- && P_PathTraverse (x - 1, y + 1, destx, desty, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
- && P_PathTraverse (x + 1, y + 1, destx, desty, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
- && P_PathTraverse (x - 1, y - 1, destx, desty, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
- && P_PathTraverse (x + 1, y - 1, destx, desty, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse)
- && P_PathTraverse (x, y, destx, desty, PT_ADDLINES|PT_ADDTHINGS, B_PTRPathTraverse))
- {
- return true;
- }
- else
- {
- botteledestfound = false;
- return false;
- }
- }
- else
- return P_PathTraverse (x, y, destx, desty, PT_ADDLINES, B_PTRPathTraverse);
- }
- return false;
- }
- int B_GetNodeCost(SearchNode_t* node)
- {
- int cost = 10000;
- sector_t* sector;
- if (node)
- {
- sector = R_PointInSubsector(node->x, node->y)->sector;
- if (sector->floortype == FLOOR_LAVA)
- cost = 50000;
- else if (sector->floortype == FLOOR_LAVA)
- cost = 40000;
- }
- return cost;
- }
- SearchNode_t* B_CreateNode(fixed_t x, fixed_t y)
- {
- SearchNode_t* newnode = Z_Malloc(sizeof(SearchNode_t), PU_LEVEL, 0);
- newnode->cost = 0;
- newnode->f = 0;
- newnode->heuristic = 0;
- newnode->vnext = NULL;
- newnode->pprevious = NULL;
- newnode->pnext = NULL;
- newnode->visited = false;
- newnode->x = x;
- newnode->y = y;
- //CONS_Printf("Created node at x:%d, y:%d.\n", (x>>FRACBITS), (y>>FRACBITS));
- botNodeArray[x][y] = newnode;
- /*newnode->mo = P_SpawnMobj(posX2x(newnode->x), posY2y(newnode->y), R_PointInSubsector(posX2x(newnode->x), posY2y(newnode->y))->sector->floorheight, MT_MISC49);*/
- return newnode;
- }
- void B_DeleteNode(SearchNode_t* node)
- {
- Z_Free(node);
- // CONS_Printf("deleted a node\n");
- }
- void B_SetNodeTeleDest(SearchNode_t* node)
- {
- fixed_t x = x2ClosestPosX(botteledestx),
- y = y2ClosestPosY(botteledesty);
- //CONS_Printf("trying to make a tele node at x:%d, y:%d\n", botteledestx>>FRACBITS, botteledesty>>FRACBITS);
- if( x >= 0 && x < xSize && y >=0 && y <= ySize )
- {
-
- if (!botNodeArray[x][y])
- {
- node->dir[BDI_TELEPORT] = B_CreateNode(x, y);
- //CONS_Printf("created teleporter node at x:%d, y:%d\n", botteledestx>>FRACBITS, botteledesty>>FRACBITS);
- numbotnodes++;
- B_BuildNodes(node->dir[BDI_TELEPORT]);
- }
- else
- node->dir[BDI_TELEPORT] = botNodeArray[x][y];
- node->costDir[BDI_TELEPORT] = 20000;//B_GetNodeCost(node->dir[TELEPORT]);
- }
- else
- {
- I_SoftError( "Tele node bad pos, x:%d, y:%d\n", botteledestx>>FRACBITS, botteledesty>>FRACBITS);
- }
- }
- void B_BuildNodes(SearchNode_t* node)
- {
- fixed_t angle,
- nx, ny,
- extraCost;
- LinkedList_t *queue = B_LLCreate();
- B_LLInsertFirstNode(queue, node);
- // [WDJ] 1/22/2009 FreeDoom MAP10 can get stuck in this loop, sometimes.
- // It keeps cycling forever through the queue, putting entries back in,
- // because botNodeArray[nx][ny] == NULL.
- // Started happening after changes to Z_ALLOC to reduce fragmentation.
- // Resolved by removing code that searched twice to get less fragmentation.
- // Some of the purgable memory blocks deleted were probably still in use
- // in this routine. The handling of such memory by this routine is fragile,
- // it is suspicious, and probably needs a more robust fix.
- while (!B_LLIsEmpty(queue))
- {
- node = B_LLRemoveLastNode(queue);
- node->dir[BDI_TELEPORT] = NULL;
- for (angle = BDI_EAST; angle <= BDI_SOUTHEAST; angle++)
- {
- extraCost = 0;
- switch(angle)
- {
- case (BDI_EAST):
- nx = node->x + 1;
- ny = node->y;
- break;
- case (BDI_NORTHEAST):
- nx = node->x + 1;
- ny = node->y + 1;
- extraCost = 5000; //because diaginal
- break;
- case (BDI_NORTH):
- nx = node->x;
- ny = node->y + 1;
- break;
- case (BDI_NORTHWEST):
- nx = node->x - 1;
- ny = node->y + 1;
- extraCost = 5000; //because diaginal
- break;
- case (BDI_WEST):
- nx = node->x - 1;
- ny = node->y;
- break;
- case (BDI_SOUTHWEST):
- nx = node->x - 1;
- ny = node->y - 1;
- extraCost = 5000; //because diaginal
- break;
- case (BDI_SOUTH):
- nx = node->x;
- ny = node->y - 1;
- break;
- case (BDI_SOUTHEAST):
- nx = node->x + 1;
- ny = node->y - 1;
- extraCost = 5000; //because diaginal
- break;
- default: //shouldn't ever happen
- nx = ny = node->x; //shut compiler up
- break;
- }
- if (B_NodeReachable(players[0].mo, posX2x(node->x), posY2y(node->y), posX2x(nx), posY2y(ny)))
- {
- if (!botNodeArray[nx][ny])
- {
- node->dir[angle] = B_CreateNode(nx, ny);
- numbotnodes++;
- B_LLInsertFirstNode(queue, node->dir[angle]);
- }
- else
- node->dir[angle] = botNodeArray[nx][ny];
- node->costDir[angle] = B_GetNodeCost(node->dir[angle]) + extraCost;
- if (botteledestfound)
- B_SetNodeTeleDest(node->dir[angle]);
- }
- else
- node->dir[angle] = NULL;
- }
- }
- B_LLDelete(queue);
- }
- void B_InitNodes( void )
- {
- int i, j, px, py,
- xMax = -1000000000,
- xMin = 1000000000,
- yMax = -1000000000,
- yMin = 1000000000;
- SearchNode_t* tempNode;
- for (i=0; i<numvertexes; i++)
- {
- if (vertexes[i].x < xMin)
- xMin = vertexes[i].x;
- else if (vertexes[i].x > xMax)
- xMax = vertexes[i].x;
- if (vertexes[i].y < yMin)
- yMin = vertexes[i].y;
- else if (vertexes[i].y > yMax)
- yMax = vertexes[i].y;
- }
- // xMin-=BOTNODEGRIDSIZE; xMax+=2*BOTNODEGRIDSIZE;
- // yMin-=BOTNODEGRIDSIZE; yMax+=2*BOTNODEGRIDSIZE;
- // [WDJ] On overly-large map max-min will overflow signed
- xOffset = xMin/BOTNODEGRIDSIZE;
- xSize = ceil( ((double)xMax - (double)xMin)/(double)BOTNODEGRIDSIZE) + 1;
- yOffset = yMin/BOTNODEGRIDSIZE;
- ySize = ceil( ((double)yMax - (double)yMin)/(double)BOTNODEGRIDSIZE) + 1;
- botNodeArray = (SearchNode_t***)Z_Malloc(xSize*sizeof(SearchNode_t*),PU_LEVEL,0);
- for (i=0; i<xSize; i++)
- {
- botNodeArray[i] = (SearchNode_t**)Z_Malloc(ySize*sizeof(SearchNode_t*),PU_LEVEL,0);
- for (j=0; j<ySize; j++)
- botNodeArray[i][j] = NULL;
- }
- numbotnodes = 0;
- CONS_Printf("Building nodes for acbot.....\n");
- for (i = 0; i < MAXPLAYERS; i++)
- if (playerstarts[i])
- {
- px = playerstarts[i]->x/(BOTNODEGRIDSIZE>>FRACBITS) - xOffset;
- py = playerstarts[i]->y/(BOTNODEGRIDSIZE>>FRACBITS) - yOffset;
- if (((px >= 0) && (px < xSize) && (py >= 0) && (py < ySize)) && (!botNodeArray[px][py]))
- {
- tempNode = B_CreateNode(px, py);
- numbotnodes++;
- B_BuildNodes(tempNode);
- }
- }
- for (i = 0; i < MAX_DM_STARTS; i++)
- if (deathmatchstarts[i])
- {
- px = deathmatchstarts[i]->x/(BOTNODEGRIDSIZE>>FRACBITS) - xOffset;
- py = deathmatchstarts[i]->y/(BOTNODEGRIDSIZE>>FRACBITS) - yOffset;
- if (((px >= 0) && (px < xSize) && (py >= 0) && (py < ySize)) && (!botNodeArray[px][py]))
- {
- tempNode = B_CreateNode(px, py);
- numbotnodes++;
- B_BuildNodes(tempNode);
- }
- }
- CONS_Printf("Completed building %d nodes.\n", numbotnodes);
- }