pointtree: Kill mapgrid.c. Reincarnate as mapgrid.cpp, using the new PointTree.
git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/trunk@9261 4a71c877-e1ca-e34f-864e-861f7616d084master
parent
5c9585e8cb
commit
d6d0070530
|
@ -230,7 +230,7 @@ warzone2100_SOURCES = \
|
|||
main.c \
|
||||
map.c \
|
||||
mapdisplay.c \
|
||||
mapgrid.c \
|
||||
mapgrid.cpp \
|
||||
mechanics.c \
|
||||
message.c \
|
||||
message_lexer.lex.c \
|
||||
|
|
599
src/mapgrid.c
599
src/mapgrid.c
|
@ -1,599 +0,0 @@
|
|||
/*
|
||||
This file is part of Warzone 2100.
|
||||
Copyright (C) 1999-2004 Eidos Interactive
|
||||
Copyright (C) 2005-2009 Warzone Resurrection Project
|
||||
|
||||
Warzone 2100 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.
|
||||
|
||||
Warzone 2100 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 Warzone 2100; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
/*
|
||||
* MapGrid.c
|
||||
*
|
||||
* Functions for storing objects in a grid over the map.
|
||||
* The objects are stored in every grid over which they might
|
||||
* have some influence.
|
||||
*
|
||||
*/
|
||||
#include "lib/framework/frame.h"
|
||||
#include "objects.h"
|
||||
#include "map.h"
|
||||
|
||||
#include "mapgrid.h"
|
||||
|
||||
// The number of world units per grid
|
||||
#define GRID_UNITS (GRID_SIZE * TILE_UNITS)
|
||||
|
||||
static UDWORD gridWidth, gridHeight;
|
||||
|
||||
// The map grid
|
||||
static GRID_ARRAY *apsMapGrid[GRID_MAXAREA];
|
||||
#define GridIndex(a,b) (((b)*gridWidth) + (a))
|
||||
|
||||
// which grid to garbage collect on next
|
||||
static SDWORD garbageX, garbageY;
|
||||
|
||||
// the current state of the iterator
|
||||
static GRID_ARRAY *psIterateGrid;
|
||||
static SDWORD iterateEntry;
|
||||
|
||||
// what to do when calculating the coverage of an object
|
||||
typedef enum _coverage_mode
|
||||
{
|
||||
GRID_ADDOBJECT,
|
||||
GRID_REMOVEOBJECT,
|
||||
} COVERAGE_MODE;
|
||||
|
||||
// Function prototypes
|
||||
static BOOL gridIntersect(const int x1, const int y1, const int x2, const int y2,
|
||||
const int cx, const int cy, const int Rad);
|
||||
static void gridAddArrayObject(SDWORD x, SDWORD y, BASE_OBJECT *psObj);
|
||||
static void gridRemoveArrayObject(SDWORD x, SDWORD y, BASE_OBJECT *psObj);
|
||||
static void gridCompactArray(SDWORD x, SDWORD y);
|
||||
static int gridObjRange(const BASE_OBJECT* psObj);
|
||||
|
||||
// initialise the grid system
|
||||
BOOL gridInitialise(void)
|
||||
{
|
||||
memset(apsMapGrid, 0, sizeof(GRID_ARRAY *) * GRID_MAXAREA);
|
||||
|
||||
garbageX = 0;
|
||||
garbageY = 0;
|
||||
|
||||
psIterateGrid = NULL;
|
||||
iterateEntry = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//clear the grid of everything on it
|
||||
void gridClear(void)
|
||||
{
|
||||
GRID_ARRAY *psCurr, *psNext;
|
||||
unsigned int x, y;
|
||||
|
||||
debug( LOG_NEVER, "gridClear %d %d\n", gridWidth, gridHeight );
|
||||
for(x = 0; x < gridWidth; ++x)
|
||||
{
|
||||
for(y = 0; y < gridHeight; ++y)
|
||||
{
|
||||
for(psCurr = apsMapGrid[GridIndex(x,y)]; psCurr != NULL; psCurr = psNext)
|
||||
{
|
||||
psNext = psCurr->psNext;
|
||||
free(psCurr);
|
||||
}
|
||||
apsMapGrid[GridIndex(x,y)] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset the grid system
|
||||
void gridReset(void)
|
||||
{
|
||||
STRUCTURE *psStruct;
|
||||
DROID *psDroid;
|
||||
FEATURE *psFeature;
|
||||
UBYTE inc;
|
||||
|
||||
// Setup the grid dimensions.
|
||||
gridWidth = (mapWidth+GRID_SIZE-1) / GRID_SIZE;
|
||||
gridHeight = (mapHeight+GRID_SIZE-1) / GRID_SIZE;
|
||||
|
||||
gridClear();
|
||||
|
||||
//put all the existing objects into the grid
|
||||
for (inc = 0; inc < MAX_PLAYERS; inc++)
|
||||
{
|
||||
for (psDroid = apsDroidLists[inc]; psDroid != NULL; psDroid =
|
||||
psDroid->psNext)
|
||||
{
|
||||
gridAddObject((BASE_OBJECT *)psDroid);
|
||||
}
|
||||
for (psStruct = apsStructLists[inc]; psStruct != NULL; psStruct =
|
||||
psStruct->psNext)
|
||||
{
|
||||
gridAddObject((BASE_OBJECT *)psStruct);
|
||||
}
|
||||
for (psFeature = apsFeatureLists[inc]; psFeature != NULL; psFeature =
|
||||
psFeature->psNext)
|
||||
{
|
||||
gridAddObject((BASE_OBJECT *)psFeature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// shutdown the grid system
|
||||
void gridShutDown(void)
|
||||
{
|
||||
gridClear();
|
||||
}
|
||||
|
||||
|
||||
// find the grid's that are covered by the object and either
|
||||
// add or remove the object
|
||||
static void gridCalcCoverage(BASE_OBJECT *psObj, SDWORD objx, SDWORD objy, COVERAGE_MODE mode)
|
||||
{
|
||||
SDWORD range, x,y, minx,maxx, miny,maxy;
|
||||
|
||||
range = gridObjRange(psObj);
|
||||
|
||||
// calculate the range of grids that could be covered by the object
|
||||
objx = (objx & ~TILE_MASK) + TILE_UNITS/2;
|
||||
objy = (objy & ~TILE_MASK) + TILE_UNITS/2;
|
||||
minx = objx - range;
|
||||
maxx = objx + range;
|
||||
miny = objy - range;
|
||||
maxy = objy + range;
|
||||
|
||||
minx = map_coord(minx) / GRID_SIZE;
|
||||
maxx = map_coord(maxx) / GRID_SIZE;
|
||||
miny = map_coord(miny) / GRID_SIZE;
|
||||
maxy = map_coord(maxy) / GRID_SIZE;
|
||||
|
||||
// see which ones are covered by the object
|
||||
for (x=minx; x<=maxx; x++)
|
||||
{
|
||||
if ( (x >= 0) && (x < gridWidth) )
|
||||
{
|
||||
for(y=miny; y<=maxy; y++)
|
||||
{
|
||||
if ( (y >= 0) && (y < gridHeight) &&
|
||||
gridIntersect( x * GRID_UNITS, y * GRID_UNITS,
|
||||
(x+1) * GRID_UNITS, (y+1) * GRID_UNITS,
|
||||
objx, objy, range ) )
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case GRID_ADDOBJECT:
|
||||
gridAddArrayObject(x,y, psObj);
|
||||
break;
|
||||
case GRID_REMOVEOBJECT:
|
||||
gridRemoveArrayObject(x,y, psObj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// add an object to the grid system
|
||||
void gridAddObject(BASE_OBJECT *psObj)
|
||||
{
|
||||
ASSERT_OR_RETURN(, psObj != NULL, "Attempted to add a NULL pointer");
|
||||
ASSERT_OR_RETURN(, !isDead(psObj), "Attempted to add dead object %s(%d) to the map grid!", objInfo(psObj), (int)psObj->id);
|
||||
gridCalcCoverage(psObj, (SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y, GRID_ADDOBJECT);
|
||||
}
|
||||
|
||||
// move an object within the grid
|
||||
// oldX,oldY are the old position of the object in world coords
|
||||
void gridMoveDroid(DROID* psDroid, SDWORD oldX, SDWORD oldY)
|
||||
{
|
||||
if (map_coord(psDroid->pos.x) == map_coord(oldX)
|
||||
&& map_coord(psDroid->pos.y) == map_coord(oldY))
|
||||
{
|
||||
// havn't changed the tile the object is on, don't bother updating
|
||||
return;
|
||||
}
|
||||
|
||||
gridCalcCoverage((BASE_OBJECT*)psDroid, oldX,oldY, GRID_REMOVEOBJECT);
|
||||
gridCalcCoverage((BASE_OBJECT*)psDroid, psDroid->pos.x, psDroid->pos.y, GRID_ADDOBJECT);
|
||||
}
|
||||
|
||||
|
||||
// remove an object from the grid system
|
||||
void gridRemoveObject(BASE_OBJECT *psObj)
|
||||
{
|
||||
gridCalcCoverage(psObj, (SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y, GRID_REMOVEOBJECT);
|
||||
|
||||
#if defined(DEBUG)
|
||||
{
|
||||
GRID_ARRAY *psCurr;
|
||||
unsigned int i,x,y;
|
||||
|
||||
for (x = 0; x < gridWidth; x++)
|
||||
{
|
||||
for(y = 0; y < gridHeight; y++)
|
||||
{
|
||||
for (psCurr = apsMapGrid[GridIndex(x,y)]; psCurr; psCurr = psCurr->psNext)
|
||||
{
|
||||
for (i = 0; i < MAX_GRID_ARRAY_CHUNK; i++)
|
||||
{
|
||||
if (psCurr->apsObjects[i] == psObj)
|
||||
{
|
||||
ASSERT(false, "Grid out of sync at (%u,%u):%u removing %s(%d)", x, y, i, objInfo(psObj), (int)psObj->id);
|
||||
psCurr->apsObjects[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// initialise the grid system to start iterating through units that
|
||||
// could affect a location (x,y in world coords)
|
||||
void gridStartIterate(SDWORD x, SDWORD y)
|
||||
{
|
||||
const int nx = x / GRID_UNITS;
|
||||
const int ny = y / GRID_UNITS;
|
||||
|
||||
ASSERT_OR_RETURN(, nx >= 0 && nx < gridWidth && ny >= 0 && ny < gridHeight, "Coordinates(%d, %d) off grid(%u, %u)", nx, ny, gridWidth, gridHeight);
|
||||
|
||||
psIterateGrid = apsMapGrid[GridIndex(nx, ny)];
|
||||
iterateEntry = 0;
|
||||
}
|
||||
|
||||
// get the next object that could affect a location,
|
||||
// should only be called after gridStartIterate
|
||||
BASE_OBJECT *gridIterate(void)
|
||||
{
|
||||
BASE_OBJECT *psRet;
|
||||
|
||||
while (psIterateGrid != NULL)
|
||||
{
|
||||
if (psIterateGrid->apsObjects[iterateEntry] != NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
iterateEntry += 1;
|
||||
if (iterateEntry >= MAX_GRID_ARRAY_CHUNK)
|
||||
{
|
||||
psIterateGrid = psIterateGrid->psNext;
|
||||
iterateEntry = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (psIterateGrid)
|
||||
{
|
||||
psRet = psIterateGrid->apsObjects[iterateEntry];
|
||||
iterateEntry += 1;
|
||||
if (iterateEntry >= MAX_GRID_ARRAY_CHUNK)
|
||||
{
|
||||
psIterateGrid = psIterateGrid->psNext;
|
||||
iterateEntry = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
psRet = NULL;
|
||||
}
|
||||
|
||||
return psRet;
|
||||
}
|
||||
|
||||
|
||||
// compact some of the grid arrays
|
||||
void gridGarbageCollect(void)
|
||||
{
|
||||
gridCompactArray(garbageX,garbageY);
|
||||
|
||||
++garbageX;
|
||||
if (garbageX >= gridWidth)
|
||||
{
|
||||
garbageX = 0;
|
||||
++garbageY;
|
||||
|
||||
if (garbageY >= gridHeight)
|
||||
{
|
||||
garbageX = 0;
|
||||
garbageY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//#ifdef DEBUG
|
||||
#if 0
|
||||
// integrity check the array
|
||||
{
|
||||
GRID_ARRAY *psCurr, *psCheck;
|
||||
SDWORD curr, check;
|
||||
BASE_OBJECT *psObj;
|
||||
|
||||
check = 0;
|
||||
psCheck = apsMapGrid[GridIndex(garbageX,garbageY)];
|
||||
while (psCheck != NULL)
|
||||
{
|
||||
psObj = psCheck->apsObjects[check];
|
||||
if (psObj != NULL)
|
||||
{
|
||||
// see if there is a duplicate element in the array
|
||||
curr = 0;
|
||||
psCurr = apsMapGrid[GridIndex(garbageX,garbageY)];
|
||||
while ( psCurr != NULL )
|
||||
{
|
||||
if ( !((psCurr == psCheck) && (curr == check)) &&
|
||||
(psCurr->apsObjects[curr] == psObj) )
|
||||
{
|
||||
ASSERT( false, "mapGrid integrity check failed" );
|
||||
|
||||
psCurr->apsObjects[curr] = NULL;
|
||||
}
|
||||
|
||||
curr += 1;
|
||||
if (curr >= MAX_GRID_ARRAY_CHUNK)
|
||||
{
|
||||
psCurr=psCurr->psNext;
|
||||
curr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check += 1;
|
||||
if (check >= MAX_GRID_ARRAY_CHUNK)
|
||||
{
|
||||
psCheck = psCheck->psNext;
|
||||
check = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// add an object to a grid array
|
||||
static void gridAddArrayObject(SDWORD x, SDWORD y, BASE_OBJECT *psObj)
|
||||
{
|
||||
GRID_ARRAY *psPrev, *psCurr, *psNew;
|
||||
SDWORD i;
|
||||
|
||||
// see if there is an empty slot in the currently allocated array
|
||||
psPrev = NULL;
|
||||
for (psCurr = apsMapGrid[GridIndex(x,y)]; psCurr; psCurr=psCurr->psNext)
|
||||
{
|
||||
for(i=0; i<MAX_GRID_ARRAY_CHUNK; i++)
|
||||
{
|
||||
if (psCurr->apsObjects[i] == NULL)
|
||||
{
|
||||
// found an empty slot
|
||||
psCurr->apsObjects[i] = psObj;
|
||||
return;
|
||||
}
|
||||
}
|
||||
psPrev = psCurr;
|
||||
}
|
||||
|
||||
// allocate a new array chunk
|
||||
psNew = malloc(sizeof(GRID_ARRAY));
|
||||
if (psNew == NULL)
|
||||
{
|
||||
debug(LOG_ERROR, "gridAddArrayObject: Out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
// store the object
|
||||
memset(psNew, 0, sizeof(GRID_ARRAY));
|
||||
psNew->apsObjects[0] = psObj;
|
||||
|
||||
// add the chunk to the end of the list
|
||||
if (psPrev == NULL)
|
||||
{
|
||||
apsMapGrid[GridIndex(x,y)] = psNew;
|
||||
}
|
||||
else
|
||||
{
|
||||
psPrev->psNext = psNew;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// remove an object from a grid array
|
||||
static void gridRemoveArrayObject(SDWORD x, SDWORD y, BASE_OBJECT *psObj)
|
||||
{
|
||||
GRID_ARRAY *psCurr;
|
||||
SDWORD i;
|
||||
|
||||
for (psCurr = apsMapGrid[GridIndex(x,y)]; psCurr; psCurr = psCurr->psNext)
|
||||
{
|
||||
for (i=0; i<MAX_GRID_ARRAY_CHUNK; i++)
|
||||
{
|
||||
if (psCurr->apsObjects[i] == psObj)
|
||||
{
|
||||
psCurr->apsObjects[i] = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compact a grid array to remove any NULL objects
|
||||
static void gridCompactArray(SDWORD x, SDWORD y)
|
||||
{
|
||||
GRID_ARRAY *psDone, *psMove, *psPrev, *psNext;
|
||||
SDWORD done, move;
|
||||
|
||||
psDone = psMove = apsMapGrid[GridIndex(x,y)];
|
||||
done = move = 0;
|
||||
|
||||
// move every entry down in the array
|
||||
psPrev = NULL;
|
||||
while (psMove != NULL)
|
||||
{
|
||||
if (psMove->apsObjects[move] != NULL)
|
||||
{
|
||||
psDone->apsObjects[done] = psMove->apsObjects[move];
|
||||
|
||||
done += 1;
|
||||
if (done >= MAX_GRID_ARRAY_CHUNK)
|
||||
{
|
||||
psPrev = psDone;
|
||||
psDone = psDone->psNext;
|
||||
done = 0;
|
||||
}
|
||||
}
|
||||
|
||||
move += 1;
|
||||
if (move >= MAX_GRID_ARRAY_CHUNK)
|
||||
{
|
||||
psMove = psMove->psNext;
|
||||
move = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// if the compacted array finishes half way through, NULL the rest of it
|
||||
if ( (psDone != NULL) && (done != 0) )
|
||||
{
|
||||
memset(&psDone->apsObjects[done], 0, sizeof(BASE_OBJECT *) * (MAX_GRID_ARRAY_CHUNK - done) );
|
||||
|
||||
psPrev = psDone;
|
||||
psDone = psDone->psNext;
|
||||
}
|
||||
|
||||
// now release any unused chunks
|
||||
if (psPrev == NULL)
|
||||
{
|
||||
apsMapGrid[GridIndex(x,y)] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
psPrev->psNext = NULL;
|
||||
}
|
||||
while (psDone)
|
||||
{
|
||||
psNext = psDone->psNext;
|
||||
free(psDone);
|
||||
psDone = psNext;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Display all the grid's an object is a member of
|
||||
void gridDisplayCoverage(BASE_OBJECT *psObj)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
{
|
||||
unsigned int x, y, i;
|
||||
GRID_ARRAY *psCurr;
|
||||
|
||||
debug(LOG_ERROR, "Grid coverage for object %d (%d,%d) - range %d", psObj->id, psObj->pos.x, psObj->pos.y, gridObjRange(psObj) );
|
||||
for (x = 0; x < gridWidth; x++)
|
||||
{
|
||||
for(y = 0; y < gridHeight; y++)
|
||||
{
|
||||
psCurr = apsMapGrid[GridIndex(x, y)];
|
||||
i = 0;
|
||||
while (psCurr != NULL)
|
||||
{
|
||||
if (psCurr->apsObjects[i] == psObj)
|
||||
{
|
||||
debug(LOG_ERROR, " %d,%d [ %d,%d -> %d,%d ]", x, y, x*GRID_UNITS, y*GRID_UNITS, (x+1)*GRID_UNITS, (y+1)*GRID_UNITS );
|
||||
}
|
||||
|
||||
++i;
|
||||
if (i >= MAX_GRID_ARRAY_CHUNK)
|
||||
{
|
||||
psCurr = psCurr->psNext;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Fast circle rectangle intersection, taken from Graphics Gems I, p51
|
||||
// by Clifford A Shaffer
|
||||
/* Return true iff rectangle R intersects circle with centerpoint C and
|
||||
radius Rad. */
|
||||
static BOOL gridIntersect(const int x1, const int y1, const int x2, const int y2,
|
||||
const int cx, const int cy, const int Rad)
|
||||
{
|
||||
// Translate coordinates, placing C at the origin
|
||||
const struct
|
||||
{
|
||||
int x1, y1,
|
||||
x2, y2;
|
||||
} rect = { x1 - cx, y1 - cy,
|
||||
x2 - cx, y2 - cy };
|
||||
|
||||
const int Rad2 = Rad * Rad;
|
||||
|
||||
if (rect.x2 < 0) /* R to left of circle center */
|
||||
{
|
||||
if (rect.y2 < 0) /* R in lower left corner */
|
||||
{
|
||||
return ((rect.x2 * rect.x2 + rect.y2 * rect.y2) < Rad2);
|
||||
}
|
||||
else if (rect.y1 > 0) /* R in upper left corner */
|
||||
{
|
||||
return ((rect.x2 * rect.x2 + rect.y1 * rect.y1) < Rad2);
|
||||
}
|
||||
else /* R due West of circle */
|
||||
{
|
||||
return(abs(rect.x2) < Rad);
|
||||
}
|
||||
}
|
||||
else if (rect.x1 > 0) /* R to right of circle center */
|
||||
{
|
||||
if (rect.y2 < 0) /* R in lower right corner */
|
||||
{
|
||||
return ((rect.x1 * rect.x1 + rect.y2 * rect.y2) < Rad2);
|
||||
}
|
||||
else if (rect.y1 > 0) /* R in upper right corner */
|
||||
{
|
||||
return ((rect.x1 * rect.x1 + rect.y1 * rect.y1) < Rad2);
|
||||
}
|
||||
else /* R due East of circle */
|
||||
{
|
||||
return (rect.x1 < Rad);
|
||||
}
|
||||
}
|
||||
else /* R on circle vertical centerline */
|
||||
{
|
||||
if (rect.y2 < 0) /* R due South of circle */
|
||||
{
|
||||
return (abs(rect.y2) < Rad);
|
||||
}
|
||||
else if (rect.y1 > 0) /* R due North of circle */
|
||||
{
|
||||
return (rect.y1 < Rad);
|
||||
}
|
||||
else /* R contains circle centerpoint */
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the range of effect of an object
|
||||
static int gridObjRange(WZ_DECL_UNUSED const BASE_OBJECT* psObj)
|
||||
{
|
||||
return world_coord(20);
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
This file is part of Warzone 2100.
|
||||
Copyright (C) 1999-2004 Eidos Interactive
|
||||
Copyright (C) 2005-2010 Warzone Resurrection Project
|
||||
|
||||
Warzone 2100 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.
|
||||
|
||||
Warzone 2100 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 Warzone 2100; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
/*
|
||||
* mapgrid.cpp
|
||||
*
|
||||
* Functions for storing objects in a quad-tree like object over the map.
|
||||
* The objects are stored in the quad-tree.
|
||||
*
|
||||
*/
|
||||
#include "lib/framework/frame.h"
|
||||
#include "objects.h"
|
||||
#include "map.h"
|
||||
|
||||
#include "mapgrid.h"
|
||||
#include "pointtree.h"
|
||||
|
||||
// the current state of the iterator
|
||||
void **gridIterator;
|
||||
|
||||
PointTree *gridPointTree = NULL; // A quad-tree-like object.
|
||||
|
||||
// initialise the grid system
|
||||
BOOL gridInitialise(void)
|
||||
{
|
||||
ASSERT(gridPointTree == NULL, "gridInitialise already called, without calling gridShutDown.");
|
||||
gridPointTree = new PointTree;
|
||||
|
||||
return true; // Yay, nothing failed!
|
||||
}
|
||||
|
||||
// reset the grid system
|
||||
void gridReset(void)
|
||||
{
|
||||
gridPointTree->clear();
|
||||
|
||||
// Put all existing objects into the point tree.
|
||||
for (unsigned player = 0; player < MAX_PLAYERS; player++)
|
||||
{
|
||||
BASE_OBJECT *start[3] = {(BASE_OBJECT *)apsDroidLists[player], (BASE_OBJECT *)apsStructLists[player], (BASE_OBJECT *)apsFeatureLists[player]};
|
||||
for (unsigned type = 0; type != sizeof(start)/sizeof(*start); ++type)
|
||||
{
|
||||
for (BASE_OBJECT *psObj = start[type]; psObj != NULL; psObj = psObj->psNext)
|
||||
{
|
||||
gridPointTree->insert(psObj, psObj->pos.x, psObj->pos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gridPointTree->sort();
|
||||
}
|
||||
|
||||
// shutdown the grid system
|
||||
void gridShutDown(void)
|
||||
{
|
||||
delete gridPointTree;
|
||||
gridPointTree = NULL;
|
||||
}
|
||||
|
||||
// add an object to the grid system
|
||||
void gridAddObject(BASE_OBJECT *psObj)
|
||||
{
|
||||
ASSERT_OR_RETURN(, psObj != NULL, "Attempted to add a NULL pointer");
|
||||
ASSERT_OR_RETURN(, !isDead(psObj), "Attempted to add dead object %s(%d) to the map grid!", objInfo(psObj), (int)psObj->id);
|
||||
//gridCalcCoverage(psObj, (SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y, GRID_ADDOBJECT);
|
||||
}
|
||||
|
||||
// move an object within the grid
|
||||
// oldX,oldY are the old position of the object in world coords
|
||||
void gridMoveDroid(DROID* psDroid, SDWORD oldX, SDWORD oldY)
|
||||
{
|
||||
if (map_coord(psDroid->pos.x) == map_coord(oldX)
|
||||
&& map_coord(psDroid->pos.y) == map_coord(oldY))
|
||||
{
|
||||
// havn't changed the tile the object is on, don't bother updating
|
||||
return;
|
||||
}
|
||||
|
||||
//gridCalcCoverage((BASE_OBJECT*)psDroid, oldX,oldY, GRID_REMOVEOBJECT);
|
||||
//gridCalcCoverage((BASE_OBJECT*)psDroid, psDroid->pos.x, psDroid->pos.y, GRID_ADDOBJECT);
|
||||
}
|
||||
|
||||
// remove an object from the grid system
|
||||
void gridRemoveObject(BASE_OBJECT *psObj)
|
||||
{
|
||||
//gridCalcCoverage(psObj, (SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y, GRID_REMOVEOBJECT);
|
||||
}
|
||||
|
||||
// initialise the grid system to start iterating through units that
|
||||
// could affect a location (x,y in world coords)
|
||||
void gridStartIterate(SDWORD x, SDWORD y/*, uint32_t radius*/)
|
||||
{
|
||||
uint32_t radius = 20*TILE_UNITS;
|
||||
|
||||
gridIterator = pointTreeQuery(gridPointTree, x, y, radius); // Use the C interface, for NULL termination.
|
||||
/*
|
||||
// In case you are curious.
|
||||
int len = 0;
|
||||
for(void **x = gridIterator; *x != NULL; ++x)
|
||||
++len;
|
||||
debug(LOG_WARNING, "gridStartIterate found %d objects", len);
|
||||
*/
|
||||
}
|
||||
|
||||
// compact some of the grid arrays
|
||||
void gridGarbageCollect(void)
|
||||
{
|
||||
gridReset();
|
||||
}
|
||||
|
||||
// Display all the grid's an object is a member of
|
||||
void gridDisplayCoverage(BASE_OBJECT *psObj)
|
||||
{
|
||||
}
|
|
@ -18,64 +18,57 @@
|
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
/** @file
|
||||
* Definitions for storing objects in a grid over the map.
|
||||
* The objects are stored in every grid over which they might have some influence.
|
||||
* Allows querying which objects are within a given radius of a given location.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDED_SRC_MAPGRID_H__
|
||||
#define __INCLUDED_SRC_MAPGRID_H__
|
||||
|
||||
/** Number of objects in each chunk of the grid array. */
|
||||
#define MAX_GRID_ARRAY_CHUNK 32
|
||||
|
||||
// Objects are stored in an extensible array for each grid
|
||||
typedef struct _grid_array
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
BASE_OBJECT *apsObjects[MAX_GRID_ARRAY_CHUNK];
|
||||
#endif //__cplusplus
|
||||
|
||||
struct _grid_array *psNext;
|
||||
} GRID_ARRAY;
|
||||
extern void **gridIterator; ///< The iterator.
|
||||
|
||||
|
||||
/** The number of tiles per grid. */
|
||||
#define GRID_SIZE 8
|
||||
|
||||
#define GRID_MAXAREA (MAP_MAXAREA/(GRID_SIZE*GRID_SIZE))
|
||||
|
||||
// initialise the grid system
|
||||
extern BOOL gridInitialise(void);
|
||||
|
||||
// shutdown the grid system
|
||||
extern void gridShutDown(void);
|
||||
|
||||
//clear the grid of everything on it
|
||||
extern void gridClear(void);
|
||||
|
||||
// reset the grid system
|
||||
extern void gridReset(void);
|
||||
|
||||
// add an object to the grid system
|
||||
// TODO THIS FUNCTION DOES NOT DO ANYTHING ANYMORE. Delete this function
|
||||
extern void gridAddObject(BASE_OBJECT *psObj);
|
||||
|
||||
// move a DROID within the grid
|
||||
// oldX,oldY are the old position of the object in world coords
|
||||
// TODO THIS FUNCTION DOES NOT DO ANYTHING ANYMORE. Delete this function
|
||||
extern void gridMoveDroid(DROID* psDroid, SDWORD oldX, SDWORD oldY);
|
||||
|
||||
// remove an object from the grid system
|
||||
// TODO THIS FUNCTION DOES NOT DO ANYTHING ANYMORE. Delete this function
|
||||
extern void gridRemoveObject(BASE_OBJECT *psObj);
|
||||
|
||||
// compact some of the grid arrays
|
||||
// Calls gridReset().
|
||||
// TODO Remove this function, call gridReset() directly.
|
||||
extern void gridGarbageCollect(void);
|
||||
|
||||
// Display all the grid's an object is a member of
|
||||
// TODO This function doesn't do anything, should probably be deleted.
|
||||
extern void gridDisplayCoverage(BASE_OBJECT *psObj);
|
||||
|
||||
// initialise the grid system to start iterating through units that
|
||||
// could affect a location (x,y in world coords)
|
||||
extern void gridStartIterate(SDWORD x, SDWORD y);
|
||||
/// Find all objects within radius r = 20*TILE_UNITS. Call gridIterate() to get the search results.
|
||||
extern void gridStartIterate(int32_t x, int32_t y/*, uint32_t radius*/);
|
||||
|
||||
// get the next object that could affect a location,
|
||||
// should only be called after gridStartIterate
|
||||
extern BASE_OBJECT *gridIterate(void);
|
||||
/// Get the next search result from gridStartIterate, or NULL if finished.
|
||||
static inline BASE_OBJECT *gridIterate(void)
|
||||
{
|
||||
return (BASE_OBJECT *)*gridIterator++;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif // __INCLUDED_SRC_MAPGRID_H__
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
#include "function.h"
|
||||
#include "stats.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif //__cplusplus
|
||||
|
||||
/* Initialise the object system */
|
||||
extern BOOL objInitialise(void);
|
||||
|
||||
|
@ -48,4 +53,8 @@ extern void reverseObjectList(BASE_OBJECT **ppsList);
|
|||
/** Output an informative string about this object. For debugging. */
|
||||
const char *objInfo(const BASE_OBJECT *psObj);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif // __INCLUDED_SRC_OBJECTS_H__
|
||||
|
|
|
@ -17,16 +17,6 @@ The coloured areas are the points in the ranges (note that each range also conta
|
|||
outside the rectangles). The orange points are the search results.
|
||||
*/
|
||||
|
||||
typedef std::pair<uint64_t, void *> POINT;
|
||||
|
||||
struct _pointTree
|
||||
{
|
||||
std::vector<POINT> points;
|
||||
|
||||
std::vector<void *> lastQueryResults;
|
||||
};
|
||||
|
||||
|
||||
// Expands bit pattern abcd efgh to 0a0b 0c0d 0e0f 0g0h
|
||||
static uint64_t expand(uint32_t x)
|
||||
{
|
||||
|
@ -66,29 +56,19 @@ static uint64_t interleave(int32_t x, int32_t y)
|
|||
return expandX(x) | expandY(y);
|
||||
}
|
||||
|
||||
POINT_TREE *pointTreeCreate(void)
|
||||
void PointTree::insert(void *pointData, int32_t x, int32_t y)
|
||||
{
|
||||
return new POINT_TREE;
|
||||
points.push_back(Point(interleave(x, y), pointData));
|
||||
}
|
||||
|
||||
void pointTreeDestroy(POINT_TREE *pointTree)
|
||||
void PointTree::clear()
|
||||
{
|
||||
delete pointTree;
|
||||
points.clear();
|
||||
}
|
||||
|
||||
void pointTreeInsert(POINT_TREE *pointTree, void *point, int32_t x, int32_t y)
|
||||
void PointTree::sort()
|
||||
{
|
||||
pointTree->points.push_back(POINT(interleave(x, y), point));
|
||||
}
|
||||
|
||||
void pointTreeClear(POINT_TREE *pointTree)
|
||||
{
|
||||
pointTree->points.clear();
|
||||
}
|
||||
|
||||
void pointTreeSort(POINT_TREE *pointTree)
|
||||
{
|
||||
std::sort(pointTree->points.begin(), pointTree->points.end());
|
||||
std::sort(points.begin(), points.end());
|
||||
}
|
||||
|
||||
//#define DUMP_IMAGE // All x and y coordinates must be in range -500 to 499, if dumping an image.
|
||||
|
@ -98,7 +78,12 @@ uint8_t ppm[1000][1000][3];
|
|||
int doDump = false;
|
||||
#endif //DUMP_IMAGE
|
||||
|
||||
void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radius)
|
||||
struct PointTreeRange
|
||||
{
|
||||
uint64_t a, z;
|
||||
};
|
||||
|
||||
PointTree::ResultVector &PointTree::query(int32_t x, int32_t y, uint32_t radius)
|
||||
{
|
||||
int32_t minXo = x - radius;
|
||||
int32_t maxXo = x + radius;
|
||||
|
@ -117,11 +102,11 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
|
|||
uint64_t splitY1 = expandY(splitYo - 1);
|
||||
uint64_t splitY2 = expandY(splitYo);
|
||||
|
||||
uint64_t ranges[4][2] = {{minX | minY, splitX1 | splitY1},
|
||||
{splitX2 | minY, maxX | splitY1},
|
||||
{minX | splitY2, splitX1 | maxY},
|
||||
{splitX2 | splitY2, maxX | maxY}
|
||||
};
|
||||
PointTreeRange ranges[4] = {{minX | minY, splitX1 | splitY1},
|
||||
{splitX2 | minY, maxX | splitY1},
|
||||
{minX | splitY2, splitX1 | maxY},
|
||||
{splitX2 | splitY2, maxX | maxY}
|
||||
};
|
||||
int numRanges = 4;
|
||||
|
||||
#ifdef DUMP_IMAGE
|
||||
|
@ -130,8 +115,7 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
|
|||
{
|
||||
f = fopen("pointtree.ppm", "wb");
|
||||
fprintf(f, "P6\n1000 1000\n255\n");
|
||||
int px, py;
|
||||
for (py = 0; py != 1000; ++py) for (px = 0; px != 1000; ++px)
|
||||
for (int py = 0; py != 1000; ++py) for (int px = 0; px != 1000; ++px)
|
||||
{
|
||||
int ax = px - 500, ay = py-500;
|
||||
double dx = ax - x, dy = ay - y;
|
||||
|
@ -152,19 +136,19 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
|
|||
ppm[py][px][2] /= 2;
|
||||
}
|
||||
uint64_t nn = expandX(ax) | expandY(ay);
|
||||
if (ranges[0][0] <= nn && nn <= ranges[0][1])
|
||||
if (ranges[0].a <= nn && nn <= ranges[0].z)
|
||||
{
|
||||
ppm[py][px][0] /= 2;
|
||||
}
|
||||
if (ranges[1][0] <= nn && nn <= ranges[1][1])
|
||||
if (ranges[1].a <= nn && nn <= ranges[1].z)
|
||||
{
|
||||
ppm[py][px][1] /= 2;
|
||||
}
|
||||
if (ranges[2][0] <= nn && nn <= ranges[2][1])
|
||||
if (ranges[2].a <= nn && nn <= ranges[2].z)
|
||||
{
|
||||
ppm[py][px][2] /= 2;
|
||||
}
|
||||
if (ranges[3][0] <= nn && nn <= ranges[3][1] && ((ax ^ ay) & 2))
|
||||
if (ranges[3].a <= nn && nn <= ranges[3].z && ((ax ^ ay) & 2))
|
||||
{
|
||||
ppm[py][px][0] /= 2;
|
||||
ppm[py][px][1] /= 2;
|
||||
|
@ -174,45 +158,43 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
|
|||
}
|
||||
#endif //DUMP_IMAGE
|
||||
|
||||
if (ranges[1][0] > ranges[2][0])
|
||||
// Sort ranges ready to be merged.
|
||||
if (ranges[1].a > ranges[2].a)
|
||||
{
|
||||
std::swap(ranges[1][0], ranges[2][0]);
|
||||
std::swap(ranges[1][1], ranges[2][1]);
|
||||
std::swap(ranges[1], ranges[2]);
|
||||
}
|
||||
if (ranges[2][1] + 1 >= ranges[3][0])
|
||||
// Merge ranges if needed.
|
||||
if (ranges[2].z + 1 >= ranges[3].a)
|
||||
{
|
||||
ranges[2][1] = ranges[3][1];
|
||||
ranges[2].z = ranges[3].z;
|
||||
--numRanges;
|
||||
}
|
||||
if (ranges[1][1] + 1 >= ranges[2][0])
|
||||
if (ranges[1].z + 1 >= ranges[2].a)
|
||||
{
|
||||
ranges[1][1] = ranges[2][1];
|
||||
ranges[2][0] = ranges[3][0];
|
||||
ranges[2][1] = ranges[3][1];
|
||||
ranges[1].z = ranges[2].z;
|
||||
ranges[2] = ranges[3];
|
||||
--numRanges;
|
||||
}
|
||||
if (ranges[0][1] + 1 >= ranges[1][0])
|
||||
if (ranges[0].z + 1 >= ranges[1].a)
|
||||
{
|
||||
ranges[0][1] = ranges[1][1];
|
||||
ranges[1][0] = ranges[2][0];
|
||||
ranges[1][1] = ranges[2][1];
|
||||
ranges[2][0] = ranges[3][0];
|
||||
ranges[2][1] = ranges[3][1];
|
||||
ranges[0].z = ranges[1].z;
|
||||
ranges[1] = ranges[2];
|
||||
ranges[2] = ranges[3];
|
||||
--numRanges;
|
||||
}
|
||||
|
||||
pointTree->lastQueryResults.clear();
|
||||
lastQueryResults.clear();
|
||||
for (unsigned r = 0; r != numRanges; ++r)
|
||||
{
|
||||
std::vector<POINT>::iterator i1 = std::lower_bound(pointTree->points.begin(), pointTree->points.end(), POINT(ranges[r][0], NULL));
|
||||
std::vector<POINT>::iterator i2 = std::lower_bound(i1, pointTree->points.end(), POINT(ranges[r][1] + 1, NULL));
|
||||
for (std::vector<POINT>::const_iterator i = i1; i != i2; ++i)
|
||||
Vector::iterator i1 = std::lower_bound(points.begin(), points.end(), Point(ranges[r].a, NULL));
|
||||
Vector::iterator i2 = std::lower_bound(i1, points.end(), Point(ranges[r].z + 1, NULL));
|
||||
for (Vector::const_iterator i = i1; i != i2; ++i)
|
||||
{
|
||||
uint64_t px = i->first & 0xAAAAAAAAAAAAAAAA;
|
||||
uint64_t py = i->first & 0x5555555555555555;
|
||||
if (px >= minX && px <= maxX && py >= minY && py <= maxY) // Only add point if it's at least in the desired square.
|
||||
{
|
||||
pointTree->lastQueryResults.push_back(i->second);
|
||||
lastQueryResults.push_back(i->second);
|
||||
#ifdef DUMP_IMAGE
|
||||
if (doDump)
|
||||
{
|
||||
|
@ -225,8 +207,6 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
|
|||
}
|
||||
}
|
||||
|
||||
pointTree->lastQueryResults.push_back(NULL); // So it's possible to know when the list ends.
|
||||
|
||||
#ifdef DUMP_IMAGE
|
||||
if (doDump)
|
||||
{
|
||||
|
@ -235,5 +215,41 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
|
|||
}
|
||||
#endif //DUMP_IMAGE
|
||||
|
||||
return lastQueryResults;
|
||||
}
|
||||
|
||||
/////////////////
|
||||
// C interface //
|
||||
/////////////////
|
||||
|
||||
PointTree *pointTreeCreate(void)
|
||||
{
|
||||
return new PointTree;
|
||||
}
|
||||
|
||||
void pointTreeDestroy(PointTree *pointTree)
|
||||
{
|
||||
delete pointTree;
|
||||
}
|
||||
|
||||
void pointTreeInsert(PointTree *pointTree, void *pointData, int32_t x, int32_t y)
|
||||
{
|
||||
pointTree->insert(pointData, x, y);
|
||||
}
|
||||
|
||||
void pointTreeClear(POINT_TREE *pointTree)
|
||||
{
|
||||
pointTree->clear();
|
||||
}
|
||||
|
||||
void pointTreeSort(POINT_TREE *pointTree)
|
||||
{
|
||||
pointTree->sort();
|
||||
}
|
||||
|
||||
void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, uint32_t radius)
|
||||
{
|
||||
pointTree->query(x, y, radius);
|
||||
pointTree->lastQueryResults.push_back(NULL); // So it's possible to know when the list ends.
|
||||
return &pointTree->lastQueryResults[0];
|
||||
}
|
||||
|
|
|
@ -3,13 +3,37 @@
|
|||
|
||||
#include "lib/framework/types.h"
|
||||
|
||||
struct _pointTree;
|
||||
typedef struct _pointTree POINT_TREE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <vector>
|
||||
|
||||
class PointTree
|
||||
{
|
||||
public:
|
||||
typedef std::vector<void *> ResultVector;
|
||||
|
||||
void insert(void *pointData, int32_t x, int32_t y); ///< Inserts a point into the point tree.
|
||||
void clear(); ///< Clears the PointTree.
|
||||
void sort(); ///< Must be done between inserting and querying, to get meaningful results.
|
||||
/// Returns all points less than or equal to radius from (x, y), possibly plus some extra nearby points.
|
||||
/// (More specifically, returns all objects in a square with edge length 2*radius.)
|
||||
ResultVector &query(int32_t x, int32_t y, uint32_t radius);
|
||||
|
||||
ResultVector lastQueryResults;
|
||||
|
||||
private:
|
||||
typedef std::pair<uint64_t, void *> Point;
|
||||
typedef std::vector<Point> Vector;
|
||||
|
||||
Vector points;
|
||||
};
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#endif //_cplusplus
|
||||
|
||||
struct PointTree;
|
||||
typedef struct PointTree POINT_TREE;
|
||||
|
||||
POINT_TREE *pointTreeCreate(void);
|
||||
void pointTreeDestroy(POINT_TREE *pointTree);
|
||||
|
@ -21,10 +45,10 @@ void pointTreeClear();
|
|||
void pointTreeSort(POINT_TREE *pointTree);
|
||||
|
||||
// Returns all points less than or equal to radius from (x, y), possibly plus some extra nearby points.
|
||||
void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radius);
|
||||
void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, uint32_t radius);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif //_cplusplus
|
||||
|
||||
#endif //_point_tree_h
|
||||
|
|
|
@ -223,7 +223,6 @@ static void proj_Free(PROJECTILE *psObj)
|
|||
/* Decrement any reference counts the projectile may have increased */
|
||||
setProjectileDamaged(psObj, NULL);
|
||||
setProjectileSource(psObj, NULL);
|
||||
setProjectileDestination(psObj, NULL);
|
||||
|
||||
free(psObj);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue