From 35c8086802278b830bff30dbd1e11cbeee49b8e2 Mon Sep 17 00:00:00 2001 From: fgenesis Date: Mon, 21 Jul 2014 22:20:08 +0200 Subject: [PATCH] Some changed to pathfinding. This hopefully gets rid of spikes in very short paths. Some untested changes. --- Aquaria/DSQ.h | 3 - Aquaria/Entity.cpp | 13 ++-- Aquaria/Entity.h | 1 + Aquaria/PathFinding.cpp | 67 +++++++++++++++++- Aquaria/PathFinding.h | 13 +++- Aquaria/ScriptInterface.cpp | 132 ++++++++++++++++++++++++++++-------- 6 files changed, 187 insertions(+), 42 deletions(-) diff --git a/Aquaria/DSQ.h b/Aquaria/DSQ.h index 773e212..03b189a 100644 --- a/Aquaria/DSQ.h +++ b/Aquaria/DSQ.h @@ -31,8 +31,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "AquariaMenuItem.h" #include "ScriptInterface.h" -#include "PathFinding.h" - #include "TTFFont.h" #include "tinyxml2.h" @@ -1397,7 +1395,6 @@ public: void jumpToSection(InStream &inFile, const std::string §ion); - PathFinding pathFinding; void runGesture(const std::string &line); void generateCollisionMask(RenderObject *r); void toggleRenderCollisionShapes(); diff --git a/Aquaria/Entity.cpp b/Aquaria/Entity.cpp index b34b0af..d4e2ee9 100644 --- a/Aquaria/Entity.cpp +++ b/Aquaria/Entity.cpp @@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "Avatar.h" #include "ScriptedEntity.h" #include "Shot.h" +#include "PathFinding.h" //Shader Entity::blurShader; @@ -485,10 +486,10 @@ void Entity::moveToNode(Path *path, int speedType, int dieOnPathEnd, bool swim) swimPath = swim; //debugLog("Generating path to: " + path->name); - dsq->pathFinding.generatePath(this, TileVector(start), TileVector(dest)); - int sz = position.data->path.getNumPathNodes(); - position.data->path.addPathNode(path->nodes[0].position, 1); - VectorPath old = position.data->path; + PathFinding::generatePath(this, TileVector(start), TileVector(dest)); + //int sz = position.data->path.getNumPathNodes(); + //position.data->path.addPathNode(path->nodes[0].position, 1); + //VectorPath old = position.data->path; /*std::ostringstream os; os << "Path length: " << sz; debugLog(os.str());*/ @@ -509,12 +510,12 @@ void Entity::moveToNode(Path *path, int speedType, int dieOnPathEnd, bool swim) //debugLog("Molesting Path"); - dsq->pathFinding.molestPath(position.data->path); + PathFinding::molestPath(position.data->path); //position.data->path.realPercentageCalc(); //position.data->path.cut(4); //debugLog("forcing path to minimum 2 nodes"); - dsq->pathFinding.forceMinimumPath(position.data->path, start, dest); + PathFinding::forceMinimumPath(position.data->path, start, dest); //debugLog("Done"); //debugLog("Calculating Time"); diff --git a/Aquaria/Entity.h b/Aquaria/Entity.h index e7d8d97..9bc9ee4 100644 --- a/Aquaria/Entity.h +++ b/Aquaria/Entity.h @@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "DSQ.h" #include "Path.h" #include "Hair.h" +#include "TileVector.h" #include "tinyxml2.h" using namespace tinyxml2; diff --git a/Aquaria/PathFinding.cpp b/Aquaria/PathFinding.cpp index 3a0f69e..ee450b7 100644 --- a/Aquaria/PathFinding.cpp +++ b/Aquaria/PathFinding.cpp @@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "DSQ.h" #include "Game.h" + class SearchGridRaw { public: @@ -33,11 +34,19 @@ public: return (game->getGridRaw(TileVector(x, y)) & blockingObsBits) == OT_EMPTY; } -private: - const ObsType blockingObsBits; + ObsType blockingObsBits; const Game *game; }; +class PathFinding::State : public ScriptObject +{ +public: + State(ObsType obs) : grid(obs), searcher(grid), result(JPS::NO_PATH) {} + SearchGridRaw grid; + JPS::Searcher searcher; + JPS::Result result; +}; + static void generateVectorPath(const JPS::PathVector& rawpath, VectorPath& vp, int offx, int offy) { for(JPS::PathVector::const_iterator it = rawpath.begin(); it != rawpath.end(); ++it) @@ -124,7 +133,7 @@ void PathFinding::molestPath(VectorPath &path) lastSuccessNode = 0; hadSuccess = false; Vector node = path.getPathNode(i)->value; - for (int j = sz-3; j >= i+adjust; j--) + for (int j = sz-1; j >= i+adjust; j--) { Vector target = path.getPathNode(j)->value; if (dsq->game->trace(node, target)) @@ -183,3 +192,55 @@ bool PathFinding::generatePathSimple(VectorPath& path, const Vector& start, cons molestPath(path); return true; } + +PathFinding::State *PathFinding::initFindPath() +{ + State *state = new PathFinding::State(OT_BLOCKING); + return state; +} + +void PathFinding::deleteFindPath(State *state) +{ + delete state; +} + +void PathFinding::beginFindPath(PathFinding::State *state, const Vector& start, const Vector& end, unsigned int obs /* = 0 */) +{ + if(obs == OT_EMPTY) + obs = OT_BLOCKING; + + state->grid.blockingObsBits = (ObsType)obs; + JPS::Position istart = JPS::Pos(start.x, start.y); + JPS::Position iend = JPS::Pos(end.x, end.y); + state->result = state->searcher.findPathInit(istart, iend); +} + +bool PathFinding::updateFindPath(PathFinding::State *state, int limit) +{ + int oldres = state->result; + if(oldres == JPS::NEED_MORE_STEPS) + { + state->result = state->searcher.findPathStep(limit); + return oldres != state->result; + } + return true; // done +} + +bool PathFinding::finishFindPath(PathFinding::State *state, VectorPath& path, unsigned step /* = 0 */) +{ + if(state->result != JPS::FOUND_PATH) + return false; + + JPS::PathVector rawpath; + state->searcher.findPathFinish(rawpath, step); + generateVectorPath(rawpath, path, 0, 0); + molestPath(path); + return true; +} + +void PathFinding::getStats(PathFinding::State *state, unsigned& stepsDone, unsigned& nodesExpanded) +{ + stepsDone = (unsigned)state->searcher.getStepsDone(); + nodesExpanded = (unsigned)state->searcher.getNodesExpanded(); +} + diff --git a/Aquaria/PathFinding.h b/Aquaria/PathFinding.h index 7920cbb..1193afb 100644 --- a/Aquaria/PathFinding.h +++ b/Aquaria/PathFinding.h @@ -30,14 +30,23 @@ class RenderObject; class SearchGrid; class Game; -class PathFinding +namespace PathFinding { -public: + class State; + void forceMinimumPath(VectorPath &path, const Vector &start, const Vector &dest); 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, unsigned int obs = 0); + + State *initFindPath(); + void deleteFindPath(State *state); + + void beginFindPath(State *state, const Vector& start, const Vector& end, unsigned int obs = 0); + bool updateFindPath(State *state, int limit); + bool finishFindPath(State *state, VectorPath& path, unsigned step = 0); + void getStats(State *state, unsigned& stepsDone, unsigned& nodesExpanded); }; #endif diff --git a/Aquaria/ScriptInterface.cpp b/Aquaria/ScriptInterface.cpp index 18d96c8..75ff6b6 100644 --- a/Aquaria/ScriptInterface.cpp +++ b/Aquaria/ScriptInterface.cpp @@ -36,6 +36,7 @@ extern "C" #include "Web.h" #include "GridRender.h" #include "AfterEffect.h" +#include "PathFinding.h" #include #include "../BBGE/MathFunctions.h" @@ -8729,6 +8730,43 @@ luaFunc(isShuttingDownGameState) luaReturnBool(dsq->game->isShuttingDownGameState()); } +static void _fillPathfindTables(lua_State *L, VectorPath& path, int xs_idx, int ys_idx) +{ + const unsigned num = path.getNumPathNodes(); + + if(lua_istable(L, xs_idx)) + lua_pushvalue(L, xs_idx); + else + lua_createtable(L, num, 0); + + if(lua_istable(L, ys_idx)) + lua_pushvalue(L, ys_idx); + else + lua_createtable(L, num, 0); + + // [..., xs, yx] + for(unsigned i = 0; i < num; ++i) + { + const VectorPathNode *n = path.getPathNode(i); + lua_pushnumber(L, n->value.x); // [..., xs, ys, x] + lua_rawseti(L, -3, i+1); // [..., xs, ys] + lua_pushnumber(L, n->value.y); // [..., xs, ys, y] + lua_rawseti(L, -2, i+1); // [..., xs, ys] + } + // terminate tables + lua_pushnil(L); // [..., xs, ys, nil] + lua_rawseti(L, -3, num+1); // [..., xs, ys] + lua_pushnil(L); // [..., xs, ys, nil] + lua_rawseti(L, -2, num+1); // [..., xs, ys] +} + +static int _pathfindDelete(lua_State *L) +{ + PathFinding::State *state = *(PathFinding::State**)luaL_checkudata(L, 1, "pathfinder"); + PathFinding::deleteFindPath(state); + return 0; +} + // startx, starty, endx, endy [, step, xtab, ytab, obsMask] luaFunc(findPath) { @@ -8736,55 +8774,81 @@ luaFunc(findPath) Vector start(lua_tonumber(L, 1), lua_tonumber(L, 2)); Vector end(lua_tonumber(L, 3), lua_tonumber(L, 4)); ObsType obs = ObsType(lua_tointeger(L, 8)); - if(!dsq->pathFinding.generatePathSimple(path, start, end, lua_tointeger(L, 5), obs)) + if(!PathFinding::generatePathSimple(path, start, end, lua_tointeger(L, 5), obs)) luaReturnBool(false); const unsigned num = path.getNumPathNodes(); lua_pushinteger(L, num); - if(lua_istable(L, 6)) - lua_pushvalue(L, 6); - else - lua_createtable(L, num, 0); - - if(lua_istable(L, 7)) - lua_pushvalue(L, 7); - else - lua_createtable(L, num, 0); - - // [true, xs, yx] - - for(unsigned i = 0; i < num; ++i) - { - const VectorPathNode *n = path.getPathNode(i); - lua_pushnumber(L, n->value.x); // [num, xs, ys, x] - lua_rawseti(L, -3, i+1); // [num, xs, ys] - lua_pushnumber(L, n->value.y); // [num, xs, ys, y] - lua_rawseti(L, -2, i+1); // [num, xs, ys] - } - // terminate tables - lua_pushnil(L); // [num xs, ys, nil] - lua_rawseti(L, -3, num+1); // [num, xs, ys] - lua_pushnil(L); // [num, xs, ys, nil] - lua_rawseti(L, -2, num+1); // [num, xs, ys] + _fillPathfindTables(L, path, 6, 7); return 3; // found path?, x positions, y positions } +luaFunc(createFindPath) +{ + PathFinding::State **statep = (PathFinding::State**)lua_newuserdata(L, sizeof(void*)); + *statep = PathFinding::initFindPath(); + luaL_getmetatable(L, "pathfinder"); + lua_setmetatable(L, -2); + return 1; +} + +luaFunc(findPathBegin) +{ + PathFinding::State *state = *(PathFinding::State**)luaL_checkudata(L, 1, "pathfinder"); + Vector start(lua_tonumber(L, 1), lua_tonumber(L, 2)); + Vector end(lua_tonumber(L, 3), lua_tonumber(L, 4)); + ObsType obs = ObsType(lua_tointeger(L, 8)); + PathFinding::beginFindPath(state, start, end, obs); + luaReturnNil(); +} + +luaFunc(findPathUpdate) +{ + PathFinding::State *state = *(PathFinding::State**)luaL_checkudata(L, 1, "pathfinder"); + int limit = lua_tointeger(L, 2); + bool done = PathFinding::updateFindPath(state, limit); + luaReturnBool(done); +} + +luaFunc(findPathFinish) +{ + PathFinding::State *state = *(PathFinding::State**)luaL_checkudata(L, 1, "pathfinder"); + VectorPath path; + bool found = PathFinding::finishFindPath(state, path); + if(!found) + luaReturnBool(false); + + lua_pushinteger(L, (int)path.getNumPathNodes()); + _fillPathfindTables(L, path, 2, 3); + return 3; +} + +luaFunc(findPathGetStats) +{ + PathFinding::State *state = *(PathFinding::State**)luaL_checkudata(L, 1, "pathfinder"); + unsigned stepsDone, nodesExpanded; + PathFinding::getStats(state, stepsDone, nodesExpanded); + lua_pushinteger(L, stepsDone); + lua_pushinteger(L, nodesExpanded); + return 2; +} + luaFunc(castLine) { Vector v(lua_tonumber(L, 1), lua_tonumber(L, 2)); Vector end(lua_tonumber(L, 3), lua_tonumber(L, 4)); int tiletype = lua_tointeger(L, 5); if(!tiletype) - tiletype = -1; + tiletype = OT_BLOCKING; Vector step = end - v; int steps = step.getLength2D() / TILE_SIZE; step.setLength2D(TILE_SIZE); for(int i = 0; i < steps; ++i) { - if(dsq->game->isObstructed(TileVector(v), tiletype)) + if(dsq->game->getGridRaw(TileVector(v)) & tiletype) { lua_pushinteger(L, dsq->game->getGrid(TileVector(v))); lua_pushnumber(L, v.x); @@ -9628,6 +9692,11 @@ static const struct { luaRegister(getObstruction), luaRegister(getGridRaw), luaRegister(findPath), + luaRegister(createFindPath), + luaRegister(findPathBegin), + luaRegister(findPathUpdate), + luaRegister(findPathFinish), + luaRegister(findPathGetStats), luaRegister(castLine), luaRegister(getUserInputString), luaRegister(getMaxCameraValues), @@ -10881,6 +10950,13 @@ lua_State *ScriptInterface::createLuaVM() // In case a script errors outside of any protected environment, report and exit. lua_atpanic(state, l_panicHandler); + // Register Lua classes + luaL_newmetatable(state, "pathfinder"); + lua_pushliteral(state, "__gc"); + lua_pushcfunction(state, _pathfindDelete); + lua_settable(state, -3); + lua_pop(state, 1); + // All done, return the new state. return state; }