- Incremented version to next major release # so new work can begin.

- some initial work to assist units from getting stuck when given commands. This is for both AI and human players and deals with the pathfinder and units getting constantly blocked from their destination.
This commit is contained in:
Mark Vejvoda 2010-10-17 06:34:42 +00:00
parent 2a805240b4
commit 55b4f0bd2b
7 changed files with 182 additions and 53 deletions

View File

@ -19,6 +19,7 @@
#include "unit.h"
#include "unit_type.h"
#include "platform_common.h"
#include "command.h"
#include "leak_dumper.h"
using namespace std;
@ -35,24 +36,26 @@ namespace Glest{ namespace Game{
// ===================== PUBLIC ========================
const int PathFinder::maxFreeSearchRadius= 10;
const int PathFinder::pathFindNodesMax= 400;
const int PathFinder::pathFindRefresh= 10;
//const int PathFinder::pathFindNodesMax= 400;
const int PathFinder::pathFindNodesMax= 500;
//const int PathFinder::pathFindRefresh= 10;
const int PathFinder::pathFindRefresh= 5;
PathFinder::PathFinder(){
PathFinder::PathFinder() {
//nodePool= NULL;
nodePool.clear();
map=NULL;
}
PathFinder::PathFinder(const Map *map){
PathFinder::PathFinder(const Map *map) {
//nodePool= NULL;
nodePool.clear();
map=NULL;
init(map);
}
void PathFinder::init(const Map *map){
void PathFinder::init(const Map *map) {
//if(nodePool != NULL) {
// delete [] nodePool;
// nodePool = NULL;
@ -70,16 +73,28 @@ PathFinder::~PathFinder(){
map=NULL;
}
TravelState PathFinder::findPath(Unit *unit, const Vec2i &finalPos){
TravelState PathFinder::findPath(Unit *unit, const Vec2i &finalPos) {
if(map == NULL) {
throw runtime_error("map == NULL");
}
//route cache
UnitPathInterface *path= unit->getPath();
if(finalPos==unit->getPos()) {
if(finalPos == unit->getPos()) {
//if arrived
unit->setCurrSkill(scStop);
if(SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled == true) {
string commandDesc = "none";
Command *command= unit->getCurrCommand();
if(command != NULL && command->getCommandType() != NULL) {
commandDesc = command->getCommandType()->toString();
}
char szBuf[1024]="";
sprintf(szBuf,"State: arrived#1 at pos: %s, command [%s]",finalPos.getString().c_str(),commandDesc.c_str());
unit->setCurrentUnitTitle(szBuf);
}
return tsArrived;
}
else {
@ -113,9 +128,56 @@ TravelState PathFinder::findPath(Unit *unit, const Vec2i &finalPos){
TravelState ts= aStar(unit, finalPos);
//post actions
switch(ts){
switch(ts) {
case tsBlocked:
case tsArrived:
// The unit is stuck (not only blocked but unable to go anywhere for a while)
// We will try to bail out of the immediate area
if( ts == tsBlocked && unit->getInBailOutAttempt() == false &&
path->isStuck() == true) {
unit->setInBailOutAttempt(true);
// Try to bail out up to 20 cells away
for(int bailoutX = -20; bailoutX <= 20 && ts == tsBlocked; ++bailoutX) {
for(int bailoutY = -20; bailoutY <= 20 && ts == tsBlocked; ++bailoutY) {
const Vec2i newFinalPos = finalPos + Vec2i(bailoutX,bailoutY);
if(map->canMove(unit, unit->getPos(), newFinalPos)) {
ts= aStar(unit, newFinalPos);
if(ts == tsMoving) {
unit->setInBailOutAttempt(false);
if(dynamic_cast<UnitPathBasic *>(path) != NULL) {
UnitPathBasic *basicPath = dynamic_cast<UnitPathBasic *>(path);
Vec2i pos= basicPath->pop();
if(map->canMove(unit, unit->getPos(), pos)) {
unit->setTargetPos(pos);
}
else {
unit->setCurrSkill(scStop);
return tsBlocked;
}
}
else if(dynamic_cast<UnitPath *>(path) != NULL) {
UnitPath *advPath = dynamic_cast<UnitPath *>(path);
Vec2i pos= advPath->peek();
if(map->canMove(unit, unit->getPos(), pos)) {
advPath->pop();
unit->setTargetPos(pos);
}
else {
unit->setCurrSkill(scStop);
return tsBlocked;
}
}
else {
throw runtime_error("unsupported or missing path finder detected!");
}
}
}
}
}
unit->setInBailOutAttempt(false);
}
unit->setCurrSkill(scStop);
break;
case tsMoving:
@ -167,7 +229,17 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){
const Vec2i finalPos= computeNearestFreePos(unit, targetPos);
//if arrived
if(finalPos==unit->getPos()){
if(finalPos == unit->getPos()) {
if(SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled == true) {
string commandDesc = "none";
Command *command= unit->getCurrCommand();
if(command != NULL && command->getCommandType() != NULL) {
commandDesc = command->getCommandType()->toString();
}
char szBuf[1024]="";
sprintf(szBuf,"State: arrived#2 at pos: %s, command [%s]",targetPos.getString().c_str(),commandDesc.c_str());
unit->setCurrentUnitTitle(szBuf);
}
return tsArrived;
}
@ -175,7 +247,11 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){
//a) push starting pos into openNodes
Node *firstNode= newNode();
assert(firstNode!=NULL);;
assert(firstNode != NULL);
if(firstNode == NULL) {
throw runtime_error("firstNode == NULL");
}
firstNode->next= NULL;
firstNode->prev= NULL;
const Vec2i unitPos = unit->getPos();
@ -191,10 +267,10 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){
//if(chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
while(!nodeLimitReached){
while(nodeLimitReached == false) {
//b1) is open nodes is empty => failed to find the path
if(openNodes.empty()){
if(openNodes.empty() == true) {
pathFound= false;
break;
}
@ -213,13 +289,13 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){
//add all succesors that are not in closedNodes or openNodes to openNodes
closedNodes.push_back(node);
openNodes.erase(it);
for(int i=-1; i<=1 && !nodeLimitReached; ++i){
for(int j=-1; j<=1 && !nodeLimitReached; ++j){
for(int i = -1; i <= 1 && nodeLimitReached == false; ++i) {
for(int j = -1; j <= 1 && nodeLimitReached == false; ++j) {
Vec2i sucPos= node->pos + Vec2i(i, j);
if(openPos(sucPos) == false && map->aproxCanMove(unit, node->pos, sucPos)){
if(openPos(sucPos) == false && map->aproxCanMove(unit, node->pos, sucPos)) {
//if node is not open and canMove then generate another node
Node *sucNode= newNode();
if(sucNode!=NULL){
if(sucNode != NULL) {
sucNode->pos= sucPos;
sucNode->heuristic= heuristic(sucNode->pos, finalPos);
sucNode->prev= node;
@ -227,7 +303,7 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){
sucNode->exploredCell= map->getSurfaceCell(Map::toSurfCoords(sucPos))->isExplored(unit->getTeam());
openNodes.push_back(sucNode);
}
else{
else {
nodeLimitReached= true;
}
}
@ -240,9 +316,9 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){
Node *lastNode= node;
//if consumed all nodes find best node (to avoid strange behaviour)
if(nodeLimitReached){
for(Nodes::iterator it= closedNodes.begin(); it!=closedNodes.end(); ++it){
if((*it)->heuristic < lastNode->heuristic){
if(nodeLimitReached == true) {
for(Nodes::iterator it= closedNodes.begin(); it != closedNodes.end(); ++it) {
if((*it)->heuristic < lastNode->heuristic) {
lastNode= *it;
}
}
@ -251,10 +327,22 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){
//if(chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
//check results of path finding
TravelState ts;
TravelState ts = tsImpossible;
UnitPathInterface *path= unit->getPath();
if(pathFound==false || lastNode==firstNode){
if(pathFound == false || lastNode == firstNode) {
//blocked
if(SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled == true) {
string commandDesc = "none";
Command *command= unit->getCurrCommand();
if(command != NULL && command->getCommandType() != NULL) {
commandDesc = command->getCommandType()->toString();
}
char szBuf[1024]="";
sprintf(szBuf,"State: blocked, cmd [%s] pos: %s, dest pos: %s, reason A= %d, B= %d, C= %d, D= %d, E= %d, F = %d",commandDesc.c_str(),unit->getPos().getString().c_str(), targetPos.getString().c_str(),pathFound,(lastNode == firstNode),path->getBlockCount(), path->isBlocked(), nodeLimitReached,path->isStuck());
unit->setCurrentUnitTitle(szBuf);
}
ts= tsBlocked;
path->incBlockCount();
}
@ -264,7 +352,7 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){
//build next pointers
Node *currNode= lastNode;
while(currNode->prev!=NULL){
while(currNode->prev != NULL) {
currNode->prev->next= currNode;
currNode= currNode->prev;
}
@ -272,9 +360,21 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){
path->clear();
currNode= firstNode;
for(int i=0; currNode->next!=NULL && i<pathFindRefresh; currNode= currNode->next, i++){
for(int i=0; currNode->next != NULL && i < pathFindRefresh; currNode= currNode->next, i++) {
path->add(currNode->next->pos);
}
if(SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled == true) {
string commandDesc = "none";
Command *command= unit->getCurrCommand();
if(command != NULL && command->getCommandType() != NULL) {
commandDesc = command->getCommandType()->toString();
}
char szBuf[1024]="";
sprintf(szBuf,"State: moving, cmd [%s] pos: %s dest pos: %s, Queue= %d",commandDesc.c_str(),unit->getPos().getString().c_str(), targetPos.getString().c_str(),path->getQueueCount());
unit->setCurrentUnitTitle(szBuf);
}
}
if(chrono.getMillis() > 2) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
@ -286,8 +386,8 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){
return ts;
}
PathFinder::Node *PathFinder::newNode(){
if(nodePoolCount < pathFindNodesMax){
PathFinder::Node *PathFinder::newNode() {
if(nodePoolCount < pathFindNodesMax) {
Node *node= &nodePool[nodePoolCount];
nodePoolCount++;
return node;
@ -295,39 +395,39 @@ PathFinder::Node *PathFinder::newNode(){
return NULL;
}
Vec2i PathFinder::computeNearestFreePos(const Unit *unit, const Vec2i &finalPos){
Vec2i PathFinder::computeNearestFreePos(const Unit *unit, const Vec2i &finalPos) {
if(map == NULL) {
throw runtime_error("map == NULL");
}
//unit data
Vec2i unitPos= unit->getPos();
int size= unit->getType()->getSize();
Field field= unit->getCurrField();
int teamIndex= unit->getTeam();
//if finalPos is free return it
if(map->isAproxFreeCells(finalPos, size, field, teamIndex)){
if(map->isAproxFreeCells(finalPos, size, field, teamIndex)) {
return finalPos;
}
//find nearest pos
Vec2i unitPos= unit->getPos();
Vec2i nearestPos= unitPos;
float nearestDist= unitPos.dist(finalPos);
for(int i= -maxFreeSearchRadius; i<=maxFreeSearchRadius; ++i){
for(int j= -maxFreeSearchRadius; j<=maxFreeSearchRadius; ++j){
for(int i= -maxFreeSearchRadius; i <= maxFreeSearchRadius; ++i) {
for(int j= -maxFreeSearchRadius; j <= maxFreeSearchRadius; ++j) {
Vec2i currPos= finalPos + Vec2i(i, j);
if(map->isAproxFreeCells(currPos, size, field, teamIndex)){
if(map->isAproxFreeCells(currPos, size, field, teamIndex)) {
float dist= currPos.dist(finalPos);
//if nearer from finalPos
if(dist<nearestDist){
if(dist < nearestDist){
nearestPos= currPos;
nearestDist= dist;
}
//if the distance is the same compare distance to unit
else if(dist==nearestDist){
if(currPos.dist(unitPos)<nearestPos.dist(unitPos)){
else if(dist == nearestDist){
if(currPos.dist(unitPos) < nearestPos.dist(unitPos)) {
nearestPos= currPos;
}
}
@ -337,18 +437,21 @@ Vec2i PathFinder::computeNearestFreePos(const Unit *unit, const Vec2i &finalPos)
return nearestPos;
}
float PathFinder::heuristic(const Vec2i &pos, const Vec2i &finalPos){
float PathFinder::heuristic(const Vec2i &pos, const Vec2i &finalPos) {
return pos.dist(finalPos);
}
//returns an iterator to the lowest heuristic node
PathFinder::Nodes::iterator PathFinder::minHeuristic(){
PathFinder::Nodes::iterator PathFinder::minHeuristic() {
Nodes::iterator minNodeIt= openNodes.begin();
assert(openNodes.empty() == false);
if(openNodes.empty() == true) {
throw runtime_error("openNodes.empty() == true");
}
assert(!openNodes.empty());
Nodes::iterator minNodeIt = openNodes.begin();
for(Nodes::iterator it= openNodes.begin(); it!=openNodes.end(); ++it){
for(Nodes::iterator it= openNodes.begin(); it != openNodes.end(); ++it) {
if((*it)->heuristic < (*minNodeIt)->heuristic){
minNodeIt= it;
}
@ -357,17 +460,17 @@ PathFinder::Nodes::iterator PathFinder::minHeuristic(){
return minNodeIt;
}
bool PathFinder::openPos(const Vec2i &sucPos){
bool PathFinder::openPos(const Vec2i &sucPos) {
for(Nodes::reverse_iterator it= closedNodes.rbegin(); it!=closedNodes.rend(); ++it){
if(sucPos==(*it)->pos){
for(Nodes::reverse_iterator it= closedNodes.rbegin(); it != closedNodes.rend(); ++it) {
if(sucPos == (*it)->pos) {
return true;
}
}
//use reverse iterator to find a node faster
for(Nodes::reverse_iterator it= openNodes.rbegin(); it!=openNodes.rend(); ++it){
if(sucPos==(*it)->pos){
for(Nodes::reverse_iterator it= openNodes.rbegin(); it != openNodes.rend(); ++it) {
if(sucPos == (*it)->pos) {
return true;
}
}

View File

@ -26,7 +26,7 @@ using namespace Shared::Platform;
namespace Glest { namespace Game {
const string mailString = "contact_game@glest.org";
const string glestVersionString = "v3.3.7.2";
const string glestVersionString = "v3.4.0-dev";
const string SVN_Rev = "$Rev$";
string getCrashDumpFileName(){

View File

@ -1602,9 +1602,9 @@ void Game::render2d(){
}
renderer.renderUnitTitles(coreData.getMenuFontNormal(),Vec3f(1.0f));
}
else if(renderer.getAllowRenderUnitTitles() == true) {
renderer.setAllowRenderUnitTitles(false);
}
//else if(renderer.getAllowRenderUnitTitles() == true) {
// renderer.setAllowRenderUnitTitles(false);
//}
//network status
if(renderNetworkStatus == true) {

View File

@ -758,6 +758,11 @@ int glestMain(int argc, char** argv){
Renderer &renderer= Renderer::getInstance();
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] OpenGL Info:\n%s\n",__FILE__,__FUNCTION__,__LINE__,renderer.getGlInfo().c_str());
if(SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled == true) {
renderer.setAllowRenderUnitTitles(SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled);
SystemFlags::OutputDebug(SystemFlags::debugPathFinder,"In [%s::%s Line: %d] renderer.setAllowRenderUnitTitles = %d\n",__FILE__,__FUNCTION__,__LINE__,SystemFlags::getSystemSettingType(SystemFlags::debugPathFinder).enabled);
}
if(hasCommandArgument(argc, argv,GAME_ARGS[GAME_ARG_OPENGL_INFO]) == true) {
//Renderer &renderer= Renderer::getInstance();
printf("%s",renderer.getGlInfo().c_str());

View File

@ -49,6 +49,10 @@ bool UnitPathBasic::isBlocked() const {
return blockCount >= maxBlockCount;
}
bool UnitPathBasic::isStuck() const {
return (isBlocked() == true && blockCount >= (maxBlockCount * 2));
}
void UnitPathBasic::clear() {
pathQueue.clear();
blockCount= 0;
@ -178,6 +182,7 @@ Unit::Unit(int id, UnitPathInterface *unitpath, const Vec2i &pos, const UnitType
this->visible = true;
this->retryCurrCommandCount=0;
this->screenPos = Vec3f(0.0);
this->inBailOutAttempt = false;
level= NULL;
loadType= NULL;
@ -1784,6 +1789,8 @@ std::string Unit::toString() const {
result += "currentUnitTitle = " + currentUnitTitle + "\n";
result += "inBailOutAttempt = " + intToStr(inBailOutAttempt) + "\n";
//SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
return result;

View File

@ -104,12 +104,15 @@ public:
virtual bool isBlocked() const = 0;
virtual bool isEmpty() const = 0;
virtual bool isStuck() const = 0;
virtual void clear() = 0;
virtual void clearBlockCount() = 0;
virtual void incBlockCount() = 0;
virtual void add(const Vec2i &path) = 0;
//virtual Vec2i pop() = 0;
virtual int getBlockCount() const = 0;
virtual int getQueueCount() const = 0;
virtual std::string toString() const = 0;
};
@ -126,12 +129,15 @@ public:
UnitPathBasic();
virtual bool isBlocked() const;
virtual bool isEmpty() const;
virtual bool isStuck() const;
virtual void clear();
virtual void clearBlockCount() { blockCount = 0; }
virtual void incBlockCount();
virtual void add(const Vec2i &path);
Vec2i pop();
virtual int getBlockCount() const { return blockCount; }
virtual int getQueueCount() const { return pathQueue.size(); }
virtual std::string toString() const;
};
@ -152,7 +158,9 @@ private:
public:
UnitPath() : blockCount(0) {} /**< Construct path object */
virtual bool isBlocked() const {return blockCount >= maxBlockCount;} /**< is this path blocked */
virtual bool isEmpty() const {return list<Vec2i>::empty();} /**< is path empty */
virtual bool isEmpty() const {return list<Vec2i>::empty();} /**< is path empty */
virtual bool isStuck() const {return false; }
int size() const {return list<Vec2i>::size();} /**< size of path */
virtual void clear() {list<Vec2i>::clear(); blockCount = 0;} /**< clear the path */
virtual void clearBlockCount() { blockCount = 0; }
@ -172,7 +180,8 @@ public:
//virtual Vec2i pop() { Vec2i p= front(); erase(begin()); return p; } /**< pop the next position off the path */
void pop() { erase(begin()); } /**< pop the next position off the path */
#endif
int getBlockCount() const { return blockCount; }
virtual int getBlockCount() const { return blockCount; }
virtual int getQueueCount() const { return this->size(); }
virtual std::string toString() const;
};
@ -271,6 +280,8 @@ private:
Vec3f screenPos;
string currentUnitTitle;
bool inBailOutAttempt;
static Game *game;
public:
@ -412,6 +423,9 @@ public:
void exploreCells();
bool getInBailOutAttempt() const { return inBailOutAttempt; }
void setInBailOutAttempt(bool value) { inBailOutAttempt = value; }
std::string toString() const;
private:

View File

@ -66,8 +66,8 @@ namespace Shared { namespace PlatformCommon {
namespace Private {
bool shouldBeFullscreen = false;
int ScreenWidth;
int ScreenHeight;
int ScreenWidth = 800;
int ScreenHeight = 600;
}