1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-12-24 21:55:42 +00:00

Pathfinding & map grid improvements.

There were still only 6 of 8 bits of the map grid in use.
The last 2 bits are now available as non-colliding user bits,
and are ignored unless specially treated.
The findPath() function can now be told which bits to respect,
which allows to prevent pathfinding to pass through fish tunnels,
for example.
A function for fast user bit map dilation is added as well.
This commit is contained in:
fgenesis 2014-05-16 00:11:52 +02:00
parent b98e2532ed
commit 3db8c9e13a
8 changed files with 185 additions and 124 deletions

View file

@ -3561,7 +3561,7 @@ void Avatar::lockToWall()
while(0);
}
if (dsq->game->getGrid(t)==OT_HURT && isDamageTarget(DT_WALLHURT))
if (dsq->game->isObstructed(t, OT_HURT) && isDamageTarget(DT_WALLHURT))
{
good = false;
}
@ -3612,16 +3612,16 @@ void Avatar::lockToWall()
Vector offdiff = t.worldVector() - position;
if (!offdiff.isZero())
{
if (tileType != OT_INVISIBLEIN)
if (tileType & OT_INVISIBLEIN)
{
Vector adjust = offdiff;
adjust.setLength2D(TILE_SIZE*2);
adjust.setLength2D(TILE_SIZE/2);
offdiff -= adjust;
}
else
{
Vector adjust = offdiff;
adjust.setLength2D(TILE_SIZE/2);
adjust.setLength2D(TILE_SIZE*2);
offdiff -= adjust;
}
}

View file

@ -1509,13 +1509,13 @@ bool Entity::isSittingOnInvisibleIn()
{
for (int y = 0; y < 4; y++)
{
if (dsq->game->getGrid(TileVector(t.x+x, t.y+y))==OT_INVISIBLEIN)
if (dsq->game->isObstructed(TileVector(t.x+x, t.y+y), OT_INVISIBLEIN))
return true;
if (dsq->game->getGrid(TileVector(t.x-x, t.y+y))==OT_INVISIBLEIN)
if (dsq->game->isObstructed(TileVector(t.x-x, t.y+y), OT_INVISIBLEIN))
return true;
if (dsq->game->getGrid(TileVector(t.x+x, t.y-y))==OT_INVISIBLEIN)
if (dsq->game->isObstructed(TileVector(t.x+x, t.y-y), OT_INVISIBLEIN))
return true;
if (dsq->game->getGrid(TileVector(t.x-x, t.y-y))==OT_INVISIBLEIN)
if (dsq->game->isObstructed(TileVector(t.x-x, t.y-y), OT_INVISIBLEIN))
return true;
}
}
@ -3055,6 +3055,7 @@ bool Entity::doCollisionAvoidance(float dt, int search, float mod, Vector *vp, i
int minDist=-1;
TileVector t(position);
TileVector useTile;
const int blockObs = ~ignoreObs;
float totalDist = sqrtf(float(sqr((search*2)*TILE_SIZE)+sqr((search*2)*TILE_SIZE)));
for (int x = -search; x <= search; x++)
@ -3069,7 +3070,7 @@ bool Entity::doCollisionAvoidance(float dt, int search, float mod, Vector *vp, i
}
if (waterBlocked || dsq->game->isObstructed(checkT))
{
if (dsq->game->getGrid(checkT) != ignoreObs)
if (dsq->game->isObstructed(checkT, blockObs))
{
Vector vtc(t.x+x, t.y+y);
Vector vt(t.x, t.y);

View file

@ -2075,8 +2075,8 @@ void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim)
//dsq->game->setGrid(TileVector(tpos.x+(w2*TILE_SIZE)+(x/TILE_SIZE), tpos.y+(h2*TILE_SIZE)+(y/TILE_SIZE)), obsType);
TileVector tvec(tpos.x+w2+x, tpos.y+h2+y);
if (dsq->game->getGrid(tvec) == OT_EMPTY)
dsq->game->setGrid(tvec, obsType);
if (!dsq->game->isObstructed(tvec))
dsq->game->addGrid(tvec, obsType);
}
glPopMatrix();
@ -2103,7 +2103,7 @@ void Game::clearDynamicGrid(unsigned char maskbyte /* = OT_MASK_BLACK */)
// otherwise there is a chance to write a few bytes over the end of the buffer -- FG
compile_assert(sizeof(grid) % sizeof(uint32) == 0);
signed char *gridstart = &grid[0][0];
unsigned char *gridstart = &grid[0][0];
uint32 *gridend = (uint32*)(gridstart + sizeof(grid));
uint32 *gridptr = (uint32*)gridstart;
// mask out specific bytes
@ -2165,9 +2165,9 @@ void Game::trimGrid()
// from beeing drawn. (The maps were designed with this mind...)
for (int x = 0; x < MAX_GRID; x++)
{
const signed char *curCol = dsq->game->getGridColumn(x);
const signed char *leftCol = dsq->game->getGridColumn(x-1);
const signed char *rightCol = dsq->game->getGridColumn(x+1);
const unsigned char *curCol = grid[x]; // safe
const unsigned char *leftCol = dsq->game->getGridColumn(x-1); // unsafe
const unsigned char *rightCol = dsq->game->getGridColumn(x+1); // unsafe
for (int y = 0; y < MAX_GRID; y++)
{
if (curCol[y] & OT_MASK_BLACK)
@ -2179,6 +2179,66 @@ void Game::trimGrid()
}
}
void Game::dilateGrid(unsigned int radius, ObsType test, ObsType set, ObsType allowOverwrite)
{
if(!radius)
return;
const int lim = MAX_GRID - radius;
const unsigned int denyOverwrite = ~allowOverwrite;
// Box dilation is separable, so we do a two-pass by axis
int dilate = 0;
// dilate rows
for (int y = 0; y < MAX_GRID; ++y)
{
for (int x = radius; x < lim; ++x)
{
if (grid[x][y] & test)
{
dilate = 2 * radius;
goto doDilate1;
}
if(dilate)
{
--dilate;
doDilate1:
if((grid[x - radius][y] & denyOverwrite) == OT_EMPTY)
grid[x - radius][y] |= set;
}
}
assert(lim + dilate < MAX_GRID);
for(int x = 0; x < dilate; ++x)
if(!(grid[x][y - radius] & test))
grid[x][y - radius] |= set;
}
// dilate colums
dilate = 0;
for (int x = 0; x < MAX_GRID; ++x)
{
unsigned char * const curCol = grid[x];
for (int y = radius; y < lim; ++y)
{
if (curCol[y] & test)
{
dilate = 2 * radius;
goto doDilate2;
}
if(dilate)
{
--dilate;
doDilate2:
if((curCol[y - radius] & denyOverwrite) == OT_EMPTY)
curCol[y - radius] |= set;
}
}
assert(lim + dilate < MAX_GRID);
for(int y = 0; y < dilate; ++y)
if(!(curCol[y - radius] & test))
curCol[y - radius] |= set;
}
}
float Game::getCoverage(Vector pos, int sampleArea)
{
TileVector t(pos);
@ -6451,30 +6511,46 @@ void Game::applyState()
addRenderObject(songLineRender, LR_HUD);
gridRender = new GridRender(OT_INVISIBLE);
gridRender->color = Vector(1, 0, 0);
addRenderObject(gridRender, LR_DEBUG_TEXT);
gridRender->alpha = 0;
gridRender2 = new GridRender(OT_HURT);
gridRender2->color = Vector(1, 1, 0);
addRenderObject(gridRender2, LR_DEBUG_TEXT);
gridRender2->alpha = 0;
gridRender3 = new GridRender(OT_INVISIBLEIN);
gridRender3->color = Vector(1, 0.5f, 0);
addRenderObject(gridRender3, LR_DEBUG_TEXT);
gridRender3->alpha = 0;
edgeRender = new GridRender(OT_BLACKINVIS);
edgeRender->color = Vector(0.3f, 0, 0.6f);
addRenderObject(edgeRender, LR_DEBUG_TEXT);
edgeRender->alpha = 0;
gridRenderEnt = new GridRender(OT_INVISIBLEENT);
gridRenderEnt->color = Vector(0, 1, 0.5);
addRenderObject(gridRenderEnt, LR_DEBUG_TEXT);
gridRenderEnt->alpha = 0;
gridRenderUser1 = new GridRender(OT_USER1);
addRenderObject(gridRenderUser1, LR_DEBUG_TEXT);
gridRenderUser1->color = Vector(1, 0, 1);
gridRenderUser1->alpha = 0;
gridRenderUser2 = new GridRender(OT_USER2);
addRenderObject(gridRenderUser2, LR_DEBUG_TEXT);
gridRenderUser2->color = Vector(1, 1, 1);
gridRenderUser2->alpha = 0;
waterSurfaceRender = new WaterSurfaceRender();
//waterSurfaceRender->setRenderPass(-1);
addRenderObject(waterSurfaceRender, LR_WATERSURFACE);
GridRender *blackRender = new GridRender(OT_BLACK);
blackRender->color = Vector(0, 0, 0);
//blackRender->alpha = 0;
blackRender->blendEnabled = false;
addRenderObject(blackRender, LR_ELEMENTS4);
@ -8515,22 +8591,17 @@ void Game::toggleMiniMapRender(int v)
void Game::toggleGridRender()
{
float t = 0;
float a = 0;
if (gridRender->alpha == 0)
{
gridRender->alpha.interpolateTo(0.5, t);
gridRender2->alpha.interpolateTo(0.5, t);
gridRender3->alpha.interpolateTo(0.5, t);
edgeRender->alpha.interpolateTo(0.5, t);
gridRenderEnt->alpha.interpolateTo(0.5, t);
}
else if (gridRender->alpha == 0.5)
{
gridRender->alpha.interpolateTo(0, t);
gridRender2->alpha.interpolateTo(0, t);
gridRender3->alpha.interpolateTo(0, t);
edgeRender->alpha.interpolateTo(0, t);
gridRenderEnt->alpha.interpolateTo(0, t);
}
a = 0.5f;
gridRender->alpha.interpolateTo(a, t);
gridRender2->alpha.interpolateTo(a, t);
gridRender3->alpha.interpolateTo(a, t);
edgeRender->alpha.interpolateTo(a, t);
gridRenderEnt->alpha.interpolateTo(a, t);
gridRenderUser1->alpha.interpolateTo(a, t);
gridRenderUser2->alpha.interpolateTo(a, t);
}
Vector Game::getCameraPositionFor(const Vector &pos)
@ -10624,7 +10695,7 @@ void Game::setGrid(ElementTemplate *et, Vector position, float rot360)
}
TileVector s(t.x+x, t.y+y);
//Vector v = Vector(position.x+et->grid[i].x*TILE_SIZE, position.y+et->grid[i].y*TILE_SIZE);
setGrid(s, 1);
setGrid(s, OT_INVISIBLE);
}
}
@ -10752,6 +10823,12 @@ void Game::removeState()
miniMapRender = 0;
gridRender = 0;
gridRender2 = 0;
gridRender3 = 0;
edgeRender = 0;
gridRenderEnt = 0;
gridRenderUser1 = 0;
gridRenderUser2 = 0;
worldMapRender = 0;
//core->sound->stopStreamingOgg();
@ -10922,44 +10999,6 @@ bool Game::collideCircleWithGrid(const Vector& position, float r)
return false;
}
bool Game::collideBoxWithGrid(const Vector& position, int hw, int hh)
{
Vector tile = position;
TileVector t(tile);
tile.x = t.x;
tile.y = t.y;
float hsz = TILE_SIZE/2;
int xrange=1,yrange=1;
xrange = (hw/TILE_SIZE)+1;
yrange = (hh/TILE_SIZE)+1;
for (int x = tile.x-xrange; x <= tile.x+xrange; x++)
{
for (int y = tile.y-yrange; y <= tile.y+yrange; y++)
{
int v = this->getGrid(TileVector(x, y));
if (v == 1)
{
if (tile.x == x && tile.y == y) return true;
float rx = (x*TILE_SIZE)+TILE_SIZE/2;
float ry = (y*TILE_SIZE)+TILE_SIZE/2;
if (isBoxIn(position, Vector(hw, hh), Vector(rx, ry), Vector(hsz, hsz)))
{
return true;
}
if (isBoxIn(Vector(rx, ry), Vector(hsz, hsz), position, Vector(hw, hh)))
{
return true;
}
}
}
}
return false;
}
void Game::learnedRecipe(Recipe *r, bool effects)
{
if (nocasecmp(dsq->getTopStateData()->name,"Game")==0 && !applyingState)

View file

@ -587,6 +587,14 @@ enum ObsType
// set by entities
OT_INVISIBLEENT = 0x20,
// mask for all bits that block
OT_BLOCKING = OT_MASK_BLACK | OT_INVISIBLE | OT_INVISIBLEIN | OT_HURT | OT_INVISIBLEENT,
// free for use, not colliding by default
OT_USER1 = 0x40,
OT_USER2 = 0x80,
OT_USER_MASK = OT_USER1 | OT_USER2,
};
struct EntitySaveData
@ -620,12 +628,14 @@ public:
std::string getSelectedChoice() { return selectedChoice; }
int getGrid(const TileVector &tile) const;
int getGridRaw(unsigned int x, unsigned int y) const;
const signed char *getGridColumn(int tileX);
void setGrid(const TileVector &tile, int v);
bool isObstructed(const TileVector &tile, int t = -1) const;
ObsType getGrid(const TileVector &tile) const;
ObsType getGridRaw(const TileVector &tile) const;
unsigned char *getGridColumn(int tileX);
void setGrid(const TileVector &tile, ObsType v);
void addGrid(const TileVector &tile, ObsType v);
bool isObstructed(const TileVector &tile, int t = OT_BLOCKING) const;
void trimGrid();
void dilateGrid(unsigned int radius, ObsType test, ObsType set, ObsType allowOverwrite);
void clearPointers();
@ -655,7 +665,6 @@ public:
void registerSporeDrop(const Vector &pos, int t);
bool collideBoxWithGrid(const Vector& position, int w, int h);
bool collideCircleWithGrid(const Vector& position, float r);
bool collideHairVsCircle(Entity *a, int num, const Vector &pos2, float radius, float perc=0, int *colSegment=0);
@ -959,7 +968,7 @@ public:
void createGradient();
std::string saveMusic;
GridRender *gridRender, *gridRender2, *gridRender3, *edgeRender, *gridRenderEnt;
GridRender *gridRender, *gridRender2, *gridRender3, *edgeRender, *gridRenderEnt, *gridRenderUser1, *gridRenderUser2;
void toggleGridRender();
ElementUpdateList elementUpdateList;
ElementUpdateList elementInteractionList;
@ -1174,7 +1183,7 @@ protected:
void toggleSceneEditor();
#endif
signed char grid[MAX_GRID][MAX_GRID];
unsigned char grid[MAX_GRID][MAX_GRID];
Quad *bg, *bg2;
@ -1200,21 +1209,23 @@ extern Game *game;
// INLINE FUNCTIONS
inline
int Game::getGridRaw(unsigned int x, unsigned int y) const
ObsType Game::getGridRaw(const TileVector &tile) const
{
return grid[x][y];
return (unsigned(tile.x) < unsigned(MAX_GRID) && unsigned(tile.y) < unsigned(MAX_GRID))
? ObsType(grid[tile.x][tile.y])
: OT_INVISIBLE;
}
inline
int Game::getGrid(const TileVector &tile) const
ObsType Game::getGrid(const TileVector &tile) const
{
//if (tile.x < 0 || tile.x >= MAX_GRID || tile.y < 0 || tile.y >= MAX_GRID) return OT_INVISIBLE;
//return grid[tile.x][tile.y];
return (unsigned(tile.x) < unsigned(MAX_GRID) && unsigned(tile.y) < unsigned(MAX_GRID)) ? grid[tile.x][tile.y] : OT_INVISIBLE;
return (unsigned(tile.x) < unsigned(MAX_GRID) && unsigned(tile.y) < unsigned(MAX_GRID))
? ObsType(grid[tile.x][tile.y] & OT_BLOCKING)
: OT_INVISIBLE;
}
inline
const signed char *Game::getGridColumn(int tileX)
unsigned char *Game::getGridColumn(int tileX)
{
if (tileX < 0)
return grid[0];
@ -1225,14 +1236,21 @@ const signed char *Game::getGridColumn(int tileX)
}
inline
void Game::setGrid(const TileVector &tile, int v)
void Game::setGrid(const TileVector &tile, ObsType v)
{
if (tile.x < 0 || tile.x >= MAX_GRID || tile.y < 0 || tile.y >= MAX_GRID) return;
grid[tile.x][tile.y] = v;
if (unsigned(tile.x) < unsigned(MAX_GRID) && unsigned(tile.y) < unsigned(MAX_GRID))
grid[tile.x][tile.y] = v;
}
inline
bool Game::isObstructed(const TileVector &tile, int t /* = -1 */) const
void Game::addGrid(const TileVector &tile, ObsType v)
{
if (unsigned(tile.x) < unsigned(MAX_GRID) && unsigned(tile.y) < unsigned(MAX_GRID))
grid[tile.x][tile.y] |= v;
}
inline
bool Game::isObstructed(const TileVector &tile, int t /* = OT_BLOCKING */) const
{
return (getGrid(tile) & t);
}

View file

@ -66,30 +66,6 @@ inline static void doRenderGrid(int x, int startCol, int endCol)
void GridRender::onRender()
{
switch(obsType)
{
case OT_INVISIBLE:
core->setColor(1, 0, 0, alpha.getValue());
break;
case OT_INVISIBLEIN:
core->setColor(1, 0.5, 0, alpha.getValue());
break;
case OT_INVISIBLEENT:
core->setColor(0, 1, 0.5, alpha.getValue());
break;
case OT_BLACK:
core->setColor(0, 0, 0, 1);
break;
case OT_BLACKINVIS:
core->setColor(0.3, 0, 0.6, alpha.getValue());
break;
case OT_HURT:
core->setColor(1, 1, 0, alpha.getValue());
break;
default:
break;
}
const signed char obsType = this->obsType;
Vector camPos = core->cameraPos;
camPos.x -= core->getVirtualOffX() * (core->invGlobalScale);
@ -112,11 +88,11 @@ void GridRender::onRender()
return;
for (int x = startX; x <= endX; ++x)
{
const signed char *gridColumn = dsq->game->getGridColumn(x);
const unsigned char *gridColumn = dsq->game->getGridColumn(x);
int startCol = -1, y;
// fast-forward to next drawable byte
if(const signed char *next = (const signed char*)memchr(gridColumn + startY, obsType, endY - startY + 1)) // find next byte with correct obs type
if(const unsigned char *next = (const unsigned char*)memchr(gridColumn + startY, obsType, endY - startY + 1)) // find next byte with correct obs type
{
y = next - gridColumn; // will get incremented right away, which is okay, because we alrady set startCol
startCol = y;
@ -131,7 +107,7 @@ void GridRender::onRender()
doRenderGrid(x, startCol, y - 1);
// fast-forward to next drawable byte
if(const signed char *next = (const signed char*)memchr(gridColumn + y, obsType, endY - y)) // find next byte with correct obs type
if(const unsigned char *next = (const unsigned char*)memchr(gridColumn + y, obsType, endY - y)) // find next byte with correct obs type
{
y = next - gridColumn; // will get incremented right away, which is okay, because we alrady set startCol
startCol = y;

View file

@ -24,16 +24,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "DSQ.h"
#include "Game.h"
class SearchGrid
class SearchGridRaw
{
public:
SearchGrid() : game(dsq->game) {}
SearchGridRaw(ObsType blocking) : game(dsq->game), blockingObsBits(blocking) {}
inline bool operator()(unsigned x, unsigned y) const
{
return game->getGrid(TileVector(x, y)) == OT_EMPTY;
return (game->getGridRaw(TileVector(x, y)) & blockingObsBits) == OT_EMPTY;
}
private:
const ObsType blockingObsBits;
const Game *game;
};
@ -157,7 +158,7 @@ void PathFinding::generatePath(RenderObject *ro, TileVector start, TileVector go
VectorPath& vp = ro->position.data->path;
vp.clear();
SearchGrid grid;
SearchGridRaw grid(OT_BLOCKING);
JPS::PathVector path;
if(JPS::findPath(path, grid, start.x, start.y, goal.x, goal.y, 1))
{
@ -166,9 +167,12 @@ void PathFinding::generatePath(RenderObject *ro, TileVector start, TileVector go
}
}
bool PathFinding::generatePathSimple(VectorPath& path, const Vector& start, const Vector& end, unsigned int step /* = 0 */)
bool PathFinding::generatePathSimple(VectorPath& path, const Vector& start, const Vector& end, unsigned int step /* = 0 */, unsigned int obs /* = 0 */)
{
SearchGrid grid;
if(obs == OT_EMPTY)
obs = OT_BLOCKING;
SearchGridRaw grid((ObsType)obs);
JPS::PathVector p;
TileVector tstart(start);
TileVector tend(end);

View file

@ -37,7 +37,7 @@ public:
void molestPath(VectorPath &path);
void generatePath(RenderObject *go, TileVector g1, TileVector g2, int offx=0, int offy=0);
bool generatePathSimple(VectorPath& path, const Vector& start, const Vector& end, unsigned int step = 0);
bool generatePathSimple(VectorPath& path, const Vector& start, const Vector& end, unsigned int step = 0, unsigned int obs = 0);
};
#endif

View file

@ -2898,6 +2898,16 @@ luaFunc(reconstructEntityGrid)
luaReturnNil();
}
luaFunc(dilateGrid)
{
unsigned int radius = lua_tointeger(L, 1);
ObsType test = (ObsType)lua_tointeger(L, 2);
ObsType set = (ObsType)lua_tointeger(L, 3);
ObsType allowOverwrite = (ObsType)lua_tointeger(L, 4);
dsq->game->dilateGrid(radius, test, set, allowOverwrite);
luaReturnNil();
}
luaFunc(entity_setCanLeaveWater)
{
Entity *e = entity(L);
@ -8347,6 +8357,11 @@ luaFunc(getObstruction)
luaReturnInt(dsq->game->getGrid(TileVector(Vector(lua_tonumber(L, 1), lua_tonumber(L, 2)))));
}
luaFunc(getGridRaw)
{
luaReturnInt(dsq->game->getGridRaw(TileVector(Vector(lua_tonumber(L, 1), lua_tonumber(L, 2)))));
}
luaFunc(isObstructedBlock)
{
float x = lua_tonumber(L, 1);
@ -8568,13 +8583,14 @@ luaFunc(isShuttingDownGameState)
luaReturnBool(dsq->game->isShuttingDownGameState());
}
// startx, starty, endx, endy [, step, xtab, ytab]
// startx, starty, endx, endy [, step, xtab, ytab, obsMask]
luaFunc(findPath)
{
VectorPath path;
Vector start(lua_tonumber(L, 1), lua_tonumber(L, 2));
Vector end(lua_tonumber(L, 3), lua_tonumber(L, 4));
if(!dsq->pathFinding.generatePathSimple(path, start, end, lua_tointeger(L, 5)))
ObsType obs = ObsType(lua_tointeger(L, 8));
if(!dsq->pathFinding.generatePathSimple(path, start, end, lua_tointeger(L, 5), obs))
luaReturnBool(false);
const unsigned num = path.getNumPathNodes();
@ -8964,6 +8980,7 @@ static const struct {
luaRegister(reconstructGrid),
luaRegister(reconstructEntityGrid),
luaRegister(dilateGrid),
luaRegister(ing_hasIET),
luaRegister(ing_getIngredientName),
@ -9463,6 +9480,7 @@ static const struct {
luaRegister(isObstructed),
luaRegister(isObstructedBlock),
luaRegister(getObstruction),
luaRegister(getGridRaw),
luaRegister(findPath),
luaRegister(castLine),
luaRegister(getUserInputString),
@ -10597,6 +10615,11 @@ static const struct {
luaConstant(OT_INVISIBLEIN),
luaConstant(OT_HURT),
luaConstant(OT_INVISIBLEENT),
luaConstant(OT_USER1),
luaConstant(OT_USER2),
luaConstant(OT_MASK_BLACK),
luaConstant(OT_BLOCKING),
luaConstant(OT_USER_MASK),
luaConstant(SEE_MAP_NEVER),
luaConstant(SEE_MAP_DEFAULT),