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 \
|
main.c \
|
||||||
map.c \
|
map.c \
|
||||||
mapdisplay.c \
|
mapdisplay.c \
|
||||||
mapgrid.c \
|
mapgrid.cpp \
|
||||||
mechanics.c \
|
mechanics.c \
|
||||||
message.c \
|
message.c \
|
||||||
message_lexer.lex.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
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
/** @file
|
/** @file
|
||||||
* Definitions for storing objects in a grid over the map.
|
* Allows querying which objects are within a given radius of a given location.
|
||||||
* The objects are stored in every grid over which they might have some influence.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __INCLUDED_SRC_MAPGRID_H__
|
#ifndef __INCLUDED_SRC_MAPGRID_H__
|
||||||
#define __INCLUDED_SRC_MAPGRID_H__
|
#define __INCLUDED_SRC_MAPGRID_H__
|
||||||
|
|
||||||
/** Number of objects in each chunk of the grid array. */
|
#ifdef __cplusplus
|
||||||
#define MAX_GRID_ARRAY_CHUNK 32
|
extern "C"
|
||||||
|
|
||||||
// Objects are stored in an extensible array for each grid
|
|
||||||
typedef struct _grid_array
|
|
||||||
{
|
{
|
||||||
BASE_OBJECT *apsObjects[MAX_GRID_ARRAY_CHUNK];
|
#endif //__cplusplus
|
||||||
|
|
||||||
struct _grid_array *psNext;
|
extern void **gridIterator; ///< The iterator.
|
||||||
} GRID_ARRAY;
|
|
||||||
|
|
||||||
|
|
||||||
/** The number of tiles per grid. */
|
|
||||||
#define GRID_SIZE 8
|
|
||||||
|
|
||||||
#define GRID_MAXAREA (MAP_MAXAREA/(GRID_SIZE*GRID_SIZE))
|
|
||||||
|
|
||||||
// initialise the grid system
|
// initialise the grid system
|
||||||
extern BOOL gridInitialise(void);
|
extern BOOL gridInitialise(void);
|
||||||
|
|
||||||
// shutdown the grid system
|
// shutdown the grid system
|
||||||
extern void gridShutDown(void);
|
extern void gridShutDown(void);
|
||||||
|
|
||||||
//clear the grid of everything on it
|
|
||||||
extern void gridClear(void);
|
|
||||||
|
|
||||||
// reset the grid system
|
// reset the grid system
|
||||||
extern void gridReset(void);
|
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);
|
extern void gridAddObject(BASE_OBJECT *psObj);
|
||||||
|
|
||||||
// move a DROID within the grid
|
// TODO THIS FUNCTION DOES NOT DO ANYTHING ANYMORE. Delete this function
|
||||||
// oldX,oldY are the old position of the object in world coords
|
|
||||||
extern void gridMoveDroid(DROID* psDroid, SDWORD oldX, SDWORD oldY);
|
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);
|
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);
|
extern void gridGarbageCollect(void);
|
||||||
|
|
||||||
// Display all the grid's an object is a member of
|
// 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);
|
extern void gridDisplayCoverage(BASE_OBJECT *psObj);
|
||||||
|
|
||||||
// initialise the grid system to start iterating through units that
|
/// Find all objects within radius r = 20*TILE_UNITS. Call gridIterate() to get the search results.
|
||||||
// could affect a location (x,y in world coords)
|
extern void gridStartIterate(int32_t x, int32_t y/*, uint32_t radius*/);
|
||||||
extern void gridStartIterate(SDWORD x, SDWORD y);
|
|
||||||
|
|
||||||
// get the next object that could affect a location,
|
/// Get the next search result from gridStartIterate, or NULL if finished.
|
||||||
// should only be called after gridStartIterate
|
static inline BASE_OBJECT *gridIterate(void)
|
||||||
extern BASE_OBJECT *gridIterate(void);
|
{
|
||||||
|
return (BASE_OBJECT *)*gridIterator++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif //__cplusplus
|
||||||
|
|
||||||
#endif // __INCLUDED_SRC_MAPGRID_H__
|
#endif // __INCLUDED_SRC_MAPGRID_H__
|
||||||
|
|
|
@ -35,6 +35,11 @@
|
||||||
#include "function.h"
|
#include "function.h"
|
||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif //__cplusplus
|
||||||
|
|
||||||
/* Initialise the object system */
|
/* Initialise the object system */
|
||||||
extern BOOL objInitialise(void);
|
extern BOOL objInitialise(void);
|
||||||
|
|
||||||
|
@ -48,4 +53,8 @@ extern void reverseObjectList(BASE_OBJECT **ppsList);
|
||||||
/** Output an informative string about this object. For debugging. */
|
/** Output an informative string about this object. For debugging. */
|
||||||
const char *objInfo(const BASE_OBJECT *psObj);
|
const char *objInfo(const BASE_OBJECT *psObj);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif //__cplusplus
|
||||||
|
|
||||||
#endif // __INCLUDED_SRC_OBJECTS_H__
|
#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.
|
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
|
// Expands bit pattern abcd efgh to 0a0b 0c0d 0e0f 0g0h
|
||||||
static uint64_t expand(uint32_t x)
|
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);
|
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));
|
std::sort(points.begin(), points.end());
|
||||||
}
|
|
||||||
|
|
||||||
void pointTreeClear(POINT_TREE *pointTree)
|
|
||||||
{
|
|
||||||
pointTree->points.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void pointTreeSort(POINT_TREE *pointTree)
|
|
||||||
{
|
|
||||||
std::sort(pointTree->points.begin(), pointTree->points.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//#define DUMP_IMAGE // All x and y coordinates must be in range -500 to 499, if dumping an image.
|
//#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;
|
int doDump = false;
|
||||||
#endif //DUMP_IMAGE
|
#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 minXo = x - radius;
|
||||||
int32_t maxXo = x + radius;
|
int32_t maxXo = x + radius;
|
||||||
|
@ -117,7 +102,7 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
|
||||||
uint64_t splitY1 = expandY(splitYo - 1);
|
uint64_t splitY1 = expandY(splitYo - 1);
|
||||||
uint64_t splitY2 = expandY(splitYo);
|
uint64_t splitY2 = expandY(splitYo);
|
||||||
|
|
||||||
uint64_t ranges[4][2] = {{minX | minY, splitX1 | splitY1},
|
PointTreeRange ranges[4] = {{minX | minY, splitX1 | splitY1},
|
||||||
{splitX2 | minY, maxX | splitY1},
|
{splitX2 | minY, maxX | splitY1},
|
||||||
{minX | splitY2, splitX1 | maxY},
|
{minX | splitY2, splitX1 | maxY},
|
||||||
{splitX2 | splitY2, maxX | maxY}
|
{splitX2 | splitY2, maxX | maxY}
|
||||||
|
@ -130,8 +115,7 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
|
||||||
{
|
{
|
||||||
f = fopen("pointtree.ppm", "wb");
|
f = fopen("pointtree.ppm", "wb");
|
||||||
fprintf(f, "P6\n1000 1000\n255\n");
|
fprintf(f, "P6\n1000 1000\n255\n");
|
||||||
int px, py;
|
for (int py = 0; py != 1000; ++py) for (int px = 0; px != 1000; ++px)
|
||||||
for (py = 0; py != 1000; ++py) for (px = 0; px != 1000; ++px)
|
|
||||||
{
|
{
|
||||||
int ax = px - 500, ay = py-500;
|
int ax = px - 500, ay = py-500;
|
||||||
double dx = ax - x, dy = ay - y;
|
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;
|
ppm[py][px][2] /= 2;
|
||||||
}
|
}
|
||||||
uint64_t nn = expandX(ax) | expandY(ay);
|
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;
|
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;
|
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;
|
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][0] /= 2;
|
||||||
ppm[py][px][1] /= 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
|
#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], ranges[2]);
|
||||||
std::swap(ranges[1][1], ranges[2][1]);
|
|
||||||
}
|
}
|
||||||
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;
|
--numRanges;
|
||||||
}
|
}
|
||||||
if (ranges[1][1] + 1 >= ranges[2][0])
|
if (ranges[1].z + 1 >= ranges[2].a)
|
||||||
{
|
{
|
||||||
ranges[1][1] = ranges[2][1];
|
ranges[1].z = ranges[2].z;
|
||||||
ranges[2][0] = ranges[3][0];
|
ranges[2] = ranges[3];
|
||||||
ranges[2][1] = ranges[3][1];
|
|
||||||
--numRanges;
|
--numRanges;
|
||||||
}
|
}
|
||||||
if (ranges[0][1] + 1 >= ranges[1][0])
|
if (ranges[0].z + 1 >= ranges[1].a)
|
||||||
{
|
{
|
||||||
ranges[0][1] = ranges[1][1];
|
ranges[0].z = ranges[1].z;
|
||||||
ranges[1][0] = ranges[2][0];
|
ranges[1] = ranges[2];
|
||||||
ranges[1][1] = ranges[2][1];
|
ranges[2] = ranges[3];
|
||||||
ranges[2][0] = ranges[3][0];
|
|
||||||
ranges[2][1] = ranges[3][1];
|
|
||||||
--numRanges;
|
--numRanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
pointTree->lastQueryResults.clear();
|
lastQueryResults.clear();
|
||||||
for (unsigned r = 0; r != numRanges; ++r)
|
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));
|
Vector::iterator i1 = std::lower_bound(points.begin(), points.end(), Point(ranges[r].a, NULL));
|
||||||
std::vector<POINT>::iterator i2 = std::lower_bound(i1, pointTree->points.end(), POINT(ranges[r][1] + 1, NULL));
|
Vector::iterator i2 = std::lower_bound(i1, points.end(), Point(ranges[r].z + 1, NULL));
|
||||||
for (std::vector<POINT>::const_iterator i = i1; i != i2; ++i)
|
for (Vector::const_iterator i = i1; i != i2; ++i)
|
||||||
{
|
{
|
||||||
uint64_t px = i->first & 0xAAAAAAAAAAAAAAAA;
|
uint64_t px = i->first & 0xAAAAAAAAAAAAAAAA;
|
||||||
uint64_t py = i->first & 0x5555555555555555;
|
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.
|
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
|
#ifdef DUMP_IMAGE
|
||||||
if (doDump)
|
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
|
#ifdef DUMP_IMAGE
|
||||||
if (doDump)
|
if (doDump)
|
||||||
{
|
{
|
||||||
|
@ -235,5 +215,41 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
|
||||||
}
|
}
|
||||||
#endif //DUMP_IMAGE
|
#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];
|
return &pointTree->lastQueryResults[0];
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,37 @@
|
||||||
|
|
||||||
#include "lib/framework/types.h"
|
#include "lib/framework/types.h"
|
||||||
|
|
||||||
struct _pointTree;
|
|
||||||
typedef struct _pointTree POINT_TREE;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#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"
|
extern "C"
|
||||||
{
|
{
|
||||||
#endif
|
#endif //_cplusplus
|
||||||
|
|
||||||
|
struct PointTree;
|
||||||
|
typedef struct PointTree POINT_TREE;
|
||||||
|
|
||||||
POINT_TREE *pointTreeCreate(void);
|
POINT_TREE *pointTreeCreate(void);
|
||||||
void pointTreeDestroy(POINT_TREE *pointTree);
|
void pointTreeDestroy(POINT_TREE *pointTree);
|
||||||
|
@ -21,10 +45,10 @@ void pointTreeClear();
|
||||||
void pointTreeSort(POINT_TREE *pointTree);
|
void pointTreeSort(POINT_TREE *pointTree);
|
||||||
|
|
||||||
// Returns all points less than or equal to radius from (x, y), possibly plus some extra nearby points.
|
// 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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif //_cplusplus
|
||||||
|
|
||||||
#endif //_point_tree_h
|
#endif //_point_tree_h
|
||||||
|
|
|
@ -223,7 +223,6 @@ static void proj_Free(PROJECTILE *psObj)
|
||||||
/* Decrement any reference counts the projectile may have increased */
|
/* Decrement any reference counts the projectile may have increased */
|
||||||
setProjectileDamaged(psObj, NULL);
|
setProjectileDamaged(psObj, NULL);
|
||||||
setProjectileSource(psObj, NULL);
|
setProjectileSource(psObj, NULL);
|
||||||
setProjectileDestination(psObj, NULL);
|
|
||||||
|
|
||||||
free(psObj);
|
free(psObj);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue