diff --git a/Aquaria/AquariaCompileConfig.h b/Aquaria/AquariaCompileConfig.h index 4dd79e3..150ee25 100644 --- a/Aquaria/AquariaCompileConfig.h +++ b/Aquaria/AquariaCompileConfig.h @@ -12,7 +12,7 @@ #define AQUARIA_CUSTOM_BUILD_ID (" Build " __DATE__ " - " __TIME__) // If defined, this is shown instead of "Aquaria vx.x.x ..." on the title screen. - //#define AQUARIA_OVERRIDE_VERSION_STRING "Aquaria OSE v1.000" + //#define AQUARIA_OVERRIDE_VERSION_STRING "Aquaria OSE v1.001" #endif diff --git a/Aquaria/ScriptInterface.cpp b/Aquaria/ScriptInterface.cpp index d56835a..d7bfb1e 100644 --- a/Aquaria/ScriptInterface.cpp +++ b/Aquaria/ScriptInterface.cpp @@ -1538,7 +1538,7 @@ luaFunc(obj_setUpdateCull) { RenderObject *r = robj(L);; if (r) - r->updateCull = lua_tonumber(L, 2); + r->updateCull = lua_tointeger(L, 2); luaReturnNil(); } diff --git a/ExternalLibs/JPS.h b/ExternalLibs/JPS.h index ff86c08..8dada40 100644 --- a/ExternalLibs/JPS.h +++ b/ExternalLibs/JPS.h @@ -2,21 +2,22 @@ #define JUMP_POINT_SEARCH_H // Public domain Jump Point Search implementation by False.Genesis +// Very fast pathfinding for uniform cost grids. +// Supports incremental pathfinding. + // Please keep the following source information intact when you use this file in your own projects: // This file originates from: https://github.com/fgenesis/jps // Based on the paper http://users.cecs.anu.edu.au/~dharabor/data/papers/harabor-grastien-aaai11.pdf // by Daniel Harabor & Alban Grastien. // Jumper (https://github.com/Yonaba/Jumper) and PathFinding.js (https://github.com/qiao/PathFinding.js) // served as reference for this implementation. +// If you use this, attribution would be nice, but is not necessary. // ====== COMPILE CONFIG ====== // If this is defined, compare all jumps against recursive reference implementation (only if _DEBUG is defined) //#define JPS_VERIFY -// NYI -//#define JPS_USE_HASHMAP - // ============================ // Usage: @@ -74,15 +75,36 @@ while(true) search.freeMemory(); } -// Further remarks can be found at the bottom of this file. +// Further remarks about the super lazy single-call function can be found at the bottom of this file. + +// ------------------------------- +// --- Incremental pathfinding --- +// ------------------------------- + +First, call findPathInit(Position start, Position end). Don't forget to check the return value, +as it may return NO_PATH if one or both of the points are obstructed, +or FOUND_PATH if the points are equal and not obstructed. +If it returns NEED_MORE_STEPS then the next part can start. + +Repeatedly call findPathStep(int limit) until it returns NO_PATH or FOUND_PATH. +For consistency, you will want to ensure that the grid does not change between subsequent calls; +if the grid changes, parts of the path may go through a now obstructed area. +If limit is 0, it will perform the pathfinding in one go. Values > 0 abort the search +as soon as possible after the number of steps was exceeded, returning NEED_MORE_STEPS. +Use getStepsDone() after some test runs to find a good value for the limit. + +Lastly, generate the actual path points from a successful run via findPathFinish(PathVector& path, unsigned step = 0). +Like described above, path points are appended, and granularity can be adjusted with the step parameter. +Returns false if the pathfinding did not finish or generating the path failed. + */ +#include #include #include #include #include -#include #ifdef _DEBUG #include @@ -91,12 +113,16 @@ while(true) #define JPS_ASSERT(cond) #endif -#ifdef JPS_USE_HASHMAP -#include "JPSUtilHashmap.h" -#endif namespace JPS { +enum Result +{ + NO_PATH = 0, + FOUND_PATH = 1, + NEED_MORE_STEPS = 2 +}; + struct Position { unsigned x, y; @@ -215,25 +241,32 @@ template class Searcher { public: Searcher(const GRID& g) - : grid(g), skip(1) + : grid(g), endNode(NULL), skip(1), stepsRemain(0), stepsDone(0) {} - void freeMemory(); - + // single-call bool findPath(PathVector& path, Position start, Position end, unsigned step = 0); + + // incremental pathfinding + Result findPathInit(Position start, Position end); + Result findPathStep(int limit); + bool findPathFinish(PathVector& path, unsigned step = 0); + + // misc + void freeMemory(); inline void setSkip(int s) { skip = std::max(1, s); } + inline size_t getStepsDone() const { return stepsDone; } + inline size_t getNodesExpanded() const { return nodegrid.size(); } private: -#ifdef JPS_USE_HASHMAP - typedef Util::HashMap NodeGrid; -#else typedef std::map NodeGrid; -#endif const GRID& grid; Node *endNode; int skip; + int stepsRemain; + size_t stepsDone; OpenList open; NodeGrid nodegrid; @@ -241,11 +274,11 @@ private: Node *getNode(const Position& pos); void identifySuccessors(const Node *n); unsigned findNeighbors(const Node *n, Position *wptr) const; - Position jumpP(const Position& p, const Position& src) const; - Position jumpD(Position p, int dx, int dy) const; - Position jumpX(Position p, int dx) const; - Position jumpY(Position p, int dy) const; - void generatePath(PathVector& path, unsigned step) const; + Position jumpP(const Position& p, const Position& src); + Position jumpD(Position p, int dx, int dy); + Position jumpX(Position p, int dx); + Position jumpY(Position p, int dy); + bool generatePath(PathVector& path, unsigned step) const; #ifdef JPS_VERIFY Position jumpPRec(const Position& p, const Position& src) const; #endif @@ -257,7 +290,7 @@ template inline Node *Searcher::getNode(const Position& po return &nodegrid.insert(std::make_pair(pos, Node(pos))).first->second; } -template Position Searcher::jumpP(const Position &p, const Position& src) const +template Position Searcher::jumpP(const Position &p, const Position& src) { JPS_ASSERT(grid(p.x, p.y)); @@ -277,32 +310,34 @@ template Position Searcher::jumpP(const Position &p, const return npos; } -template Position Searcher::jumpD(Position p, int dx, int dy) const +template Position Searcher::jumpD(Position p, int dx, int dy) { JPS_ASSERT(grid(p.x, p.y)); JPS_ASSERT(dx && dy); const Position& endpos = endNode->pos; + int steps = 0; while(true) { if(p == endpos) - return p; + break; + ++steps; const unsigned x = p.x; const unsigned y = p.y; if( (grid(x-dx, y+dy) && !grid(x-dx, y)) || (grid(x+dx, y-dy) && !grid(x, y-dy)) ) - return p; + break; const bool gdx = grid(x+dx, y); const bool gdy = grid(x, y+dy); if(gdx && jumpX(Pos(x+dx, y), dx).isValid()) - return p; + break; if(gdy && jumpY(Pos(x, y+dy), dy).isValid()) - return p; + break; if((gdx || gdy) && grid(x+dx, y+dy)) { @@ -310,13 +345,17 @@ template Position Searcher::jumpD(Position p, int dx, int p.y += dy; } else + { + p = npos; break; + } } - - return npos; + stepsDone += (unsigned)steps; + stepsRemain -= steps; + return p; } -template inline Position Searcher::jumpX(Position p, int dx) const +template inline Position Searcher::jumpX(Position p, int dx) { JPS_ASSERT(dx); JPS_ASSERT(grid(p.x, p.y)); @@ -324,6 +363,7 @@ template inline Position Searcher::jumpX(Position p, int d const unsigned y = p.y; const Position& endpos = endNode->pos; const int skip = this->skip; + int steps = 0; unsigned a = ~((!!grid(p.x, y+skip)) | ((!!grid(p.x, y-skip)) << 1)); @@ -333,16 +373,24 @@ template inline Position Searcher::jumpX(Position p, int d const unsigned b = (!!grid(xx, y+skip)) | ((!!grid(xx, y-skip)) << 1); if((b & a) || p == endpos) - return p; + break; if(!grid(xx, y)) - return npos; + { + p = npos; + break; + } p.x += dx; a = ~b; + ++steps; } + + stepsDone += (unsigned)steps; + stepsRemain -= steps; + return p; } -template inline Position Searcher::jumpY(Position p, int dy) const +template inline Position Searcher::jumpY(Position p, int dy) { JPS_ASSERT(dy); JPS_ASSERT(grid(p.x, p.y)); @@ -350,6 +398,7 @@ template inline Position Searcher::jumpY(Position p, int d const unsigned x = p.x; const Position& endpos = endNode->pos; const int skip = this->skip; + int steps = 0; unsigned a = ~((!!grid(x+skip, p.y)) | ((!!grid(x-skip, p.y)) << 1)); @@ -359,13 +408,20 @@ template inline Position Searcher::jumpY(Position p, int d const unsigned b = (!!grid(x+skip, yy)) | ((!!grid(x-skip, yy)) << 1); if((a & b) || p == endpos) - return p; + break; if(!grid(x, yy)) - return npos; + { + p = npos; + break; + } p.y += dy; a = ~b; } + + stepsDone += (unsigned)steps; + stepsRemain -= steps; + return p; } #ifdef JPS_VERIFY @@ -547,13 +603,17 @@ template void Searcher::identifySuccessors(const Node *n) } } -template void Searcher::generatePath(PathVector& path, unsigned step) const +template bool Searcher::generatePath(PathVector& path, unsigned step) const { + if(!endNode) + return false; size_t offset = path.size(); if(step) { const Node *next = endNode; const Node *prev = endNode->parent; + if(!prev) + return false; do { const unsigned x = next->pos.x, y = next->pos.y; @@ -580,6 +640,8 @@ template void Searcher::generatePath(PathVector& path, uns else { const Node *next = endNode; + if(!next->parent) + return false; do { path.push_back(next->pos); @@ -588,12 +650,42 @@ template void Searcher::generatePath(PathVector& path, uns while (next->parent); } std::reverse(path.begin() + offset, path.end()); + return true; } template bool Searcher::findPath(PathVector& path, Position start, Position end, unsigned step /* = 0 */) +{ + Result res = findPathInit(start, end); + + // If this is true, the resulting path is empty (findPathFinish() would fail, so this needs to be checked before) + if(res == FOUND_PATH) + return true; + + while(true) + { + switch(res) + { + case NEED_MORE_STEPS: + res = findPathStep(0); + break; // the switch + + case FOUND_PATH: + return findPathFinish(path, step); + + case NO_PATH: + default: + return false; + } + } +} + +template Result Searcher::findPathInit(Position start, Position end) { for(NodeGrid::iterator it = nodegrid.begin(); it != nodegrid.end(); ++it) it->second.clearState(); + open.clear(); + endNode = NULL; + stepsDone = 0; // If skip is > 1, make sure the points are aligned so that the search will always hit them start.x = (start.x / skip) * skip; @@ -605,37 +697,48 @@ template bool Searcher::findPath(PathVector& path, Positio { // There is only a path if this single position is walkable. // But since the starting position is omitted, there is nothing to do here. - return grid(end.x, end.y); + return grid(end.x, end.y) ? FOUND_PATH : NO_PATH; } // If start or end point are obstructed, don't even start if(!grid(start.x, start.y) || !grid(end.x, end.y)) - return false; + return NO_PATH; open.push(getNode(start)); endNode = getNode(end); JPS_ASSERT(endNode); + return NEED_MORE_STEPS; +} + +template Result Searcher::findPathStep(int limit) +{ + stepsRemain = limit; do { + if(open.empty()) + return NO_PATH; Node *n = open.pop(); n->setClosed(); if(n == endNode) - { - open.clear(); - generatePath(path, step); - return true; - } + return FOUND_PATH; identifySuccessors(n); } - while (!open.empty()); - return false; + while(stepsRemain >= 0); + return NEED_MORE_STEPS; +} + +template bool Searcher::findPathFinish(PathVector& path, unsigned step /* = 0 */) +{ + return generatePath(path, step); } template void Searcher::freeMemory() { NodeGrid v; nodegrid.swap(v); + endNode = NULL; + open.clear(); // other containers known to be empty. } @@ -643,7 +746,6 @@ template void Searcher::freeMemory() using Internal::Searcher; - // Single-call convenience function // // path: If the function returns true, the path is stored in this vector. @@ -667,12 +769,19 @@ using Internal::Searcher; // Warning: Start and end positions will be rounded down to the nearest -aligned position, // so make sure to give appropriate positions so they do not end up in a wall. // This will also skip through walls if they are less than blocks thick at any reachable position. -template bool findPath(PathVector& path, const GRID& grid, unsigned startx, unsigned starty, unsigned endx, unsigned endy, unsigned step = 0, int skip = 1) +template bool findPath(PathVector& path, const GRID& grid, unsigned startx, unsigned starty, unsigned endx, unsigned endy, + unsigned step = 0, int skip = 0, // optional params + size_t *stepsDone = NULL, size_t *nodesExpanded = NULL // for information + ) { - JPS_ASSERT(skip >= 1); Searcher search(grid); search.setSkip(skip); - return search.findPath(path, Pos(startx, starty), Pos(endx, endy), step); + bool found = search.findPath(path, Pos(startx, starty), Pos(endx, endy), step); + if(stepsDone) + *stepsDone = search.getStepsDone(); + if(nodesExpanded) + *nodesExpanded = search.getNodesExpanded(); + return found; }