Initial commit
This commit is contained in:
74
engines/hpl1/engine/ai/AI.cpp
Normal file
74
engines/hpl1/engine/ai/AI.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2010 - Frictional Games
|
||||
*
|
||||
* This file is part of HPL1 Engine.
|
||||
*/
|
||||
|
||||
#include "hpl1/engine/ai/AI.h"
|
||||
|
||||
#include "hpl1/engine/ai/AINodeGenerator.h"
|
||||
#include "hpl1/engine/system/MemoryManager.h"
|
||||
#include "hpl1/engine/system/low_level_system.h"
|
||||
|
||||
namespace hpl {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CONSTRUCTORS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAI::cAI() : iUpdateable("HPL_AI") {
|
||||
mpAINodeGenerator = hplNew(cAINodeGenerator, ());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAI::~cAI() {
|
||||
hplDelete(mpAINodeGenerator);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC METHODS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAI::Reset() {
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAI::Update(float afTimeStep) {
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAI::Init() {
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
} // namespace hpl
|
||||
58
engines/hpl1/engine/ai/AI.h
Normal file
58
engines/hpl1/engine/ai/AI.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2010 - Frictional Games
|
||||
*
|
||||
* This file is part of HPL1 Engine.
|
||||
*/
|
||||
|
||||
#ifndef HPL_AI_H
|
||||
#define HPL_AI_H
|
||||
|
||||
#include "hpl1/engine/game/GameTypes.h"
|
||||
#include "hpl1/engine/system/SystemTypes.h"
|
||||
|
||||
#include "hpl1/engine/game/Updateable.h"
|
||||
|
||||
namespace hpl {
|
||||
|
||||
class cAINodeGenerator;
|
||||
|
||||
class cAI : public iUpdateable {
|
||||
public:
|
||||
cAI();
|
||||
~cAI();
|
||||
|
||||
void Reset();
|
||||
void Update(float afTimeStep);
|
||||
|
||||
void Init();
|
||||
|
||||
cAINodeGenerator *GetNodeGenerator() { return mpAINodeGenerator; }
|
||||
|
||||
private:
|
||||
cAINodeGenerator *mpAINodeGenerator;
|
||||
};
|
||||
|
||||
} // namespace hpl
|
||||
|
||||
#endif // HPL_AI_H
|
||||
619
engines/hpl1/engine/ai/AINodeContainer.cpp
Normal file
619
engines/hpl1/engine/ai/AINodeContainer.cpp
Normal file
@@ -0,0 +1,619 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2010 - Frictional Games
|
||||
*
|
||||
* This file is part of HPL1 Engine.
|
||||
*/
|
||||
|
||||
#include "hpl1/engine/ai/AINodeContainer.h"
|
||||
|
||||
#include "hpl1/engine/physics/PhysicsBody.h"
|
||||
#include "hpl1/engine/scene/World3D.h"
|
||||
#include "hpl1/engine/system/String.h"
|
||||
#include "hpl1/engine/system/low_level_system.h"
|
||||
|
||||
#include "hpl1/engine/math/Math.h"
|
||||
|
||||
#include "hpl1/engine/impl/tinyXML/tinyxml.h"
|
||||
|
||||
#include "common/algorithm.h"
|
||||
|
||||
namespace hpl {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AI NODE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAINode::cAINode() {
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAINode::~cAINode() {
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAINode::AddEdge(cAINode *pNode) {
|
||||
cAINodeEdge Edge;
|
||||
|
||||
Edge.mpNode = pNode;
|
||||
Edge.mfDistance = cMath::Vector3Dist(mvPosition, pNode->mvPosition);
|
||||
Edge.mfSqrDistance = cMath::Vector3DistSqr(mvPosition, pNode->mvPosition);
|
||||
|
||||
mvEdges.push_back(Edge);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// RAY INTERSECT
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAINodeRayCallback::Reset() {
|
||||
mbIntersected = false;
|
||||
mpCallback = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool cAINodeRayCallback::Intersected() {
|
||||
return mbIntersected;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool cAINodeRayCallback::BeforeIntersect(iPhysicsBody *pBody) {
|
||||
if (pBody->GetCollideCharacter() == false)
|
||||
return false;
|
||||
|
||||
if ((mFlags & eAIFreePathFlag_SkipStatic) && pBody->GetMass() == 0)
|
||||
return false;
|
||||
|
||||
if ((mFlags & eAIFreePathFlag_SkipDynamic) &&
|
||||
(pBody->GetMass() > 0 || pBody->IsCharacter()))
|
||||
return false;
|
||||
|
||||
if ((mFlags & eAIFreePathFlag_SkipVolatile) && pBody->IsVolatile())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool cAINodeRayCallback::OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams) {
|
||||
if (mpCallback) {
|
||||
if (mpCallback->Intersects(pBody, apParams)) {
|
||||
mbIntersected = true;
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
mbIntersected = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CONSTRUCTORS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAINodeIterator::cAINodeIterator(cAINodeContainer *apContainer, const cVector3f &avPos, float afRadius) {
|
||||
mpContainer = apContainer;
|
||||
mvPosition = avPos;
|
||||
mfRadius = afRadius;
|
||||
|
||||
// Calculate local position
|
||||
cVector2f vLocalPos(mvPosition.x, mvPosition.z);
|
||||
vLocalPos -= mpContainer->mvMinGridPos;
|
||||
|
||||
cVector2f vLocalStart = vLocalPos - cVector2f(afRadius);
|
||||
cVector2f vLocalEnd = vLocalPos + cVector2f(afRadius);
|
||||
|
||||
mvStartGridPos = mpContainer->GetGridPosFromLocal(vLocalStart);
|
||||
mvEndGridPos = mpContainer->GetGridPosFromLocal(vLocalEnd);
|
||||
mvGridPos = mvStartGridPos;
|
||||
|
||||
mpNodeList = &mpContainer->GetGrid(mvGridPos)->mlstNodes;
|
||||
while (mpNodeList->empty()) {
|
||||
if (IncGridPos()) {
|
||||
mpNodeList = &mpContainer->GetGrid(mvGridPos)->mlstNodes;
|
||||
} else {
|
||||
mpNodeList = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mpNodeList) {
|
||||
mNodeIt = mpNodeList->begin();
|
||||
}
|
||||
|
||||
// Log("--------------------------------------\n");
|
||||
// Log("Iterating (%d %d) -> (%d %d)\n", mvStartGridPos.x,mvStartGridPos.y,
|
||||
// mvEndGridPos.x,mvEndGridPos.y);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool cAINodeIterator::HasNext() {
|
||||
if (mpNodeList == NULL || mpNodeList->empty())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAINode *cAINodeIterator::Next() {
|
||||
cAINode *pNode = *mNodeIt;
|
||||
|
||||
++mNodeIt;
|
||||
if (mNodeIt == mpNodeList->end()) {
|
||||
if (IncGridPos()) {
|
||||
mpNodeList = &mpContainer->GetGrid(mvGridPos)->mlstNodes;
|
||||
while (mpNodeList->empty()) {
|
||||
if (IncGridPos()) {
|
||||
mpNodeList = &mpContainer->GetGrid(mvGridPos)->mlstNodes;
|
||||
} else {
|
||||
mpNodeList = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mpNodeList = NULL;
|
||||
}
|
||||
|
||||
if (mpNodeList)
|
||||
mNodeIt = mpNodeList->begin();
|
||||
}
|
||||
|
||||
return pNode;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool cAINodeIterator::IncGridPos() {
|
||||
mvGridPos.x++;
|
||||
if (mvGridPos.x > mvEndGridPos.x) {
|
||||
mvGridPos.x = mvStartGridPos.x;
|
||||
mvGridPos.y++;
|
||||
if (mvGridPos.y > mvEndGridPos.y) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CONSTRUCTORS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAINodeContainer::cAINodeContainer(const tString &asName, const tString &asNodeName,
|
||||
cWorld3D *apWorld, const cVector3f &avCollideSize) {
|
||||
mpWorld = apWorld;
|
||||
mvSize = avCollideSize;
|
||||
msName = asName;
|
||||
msNodeName = asNodeName;
|
||||
|
||||
mpRayCallback = hplNew(cAINodeRayCallback, ());
|
||||
|
||||
mlMaxNodeEnds = 5;
|
||||
mlMinNodeEnds = 2;
|
||||
mfMaxEndDistance = 3.0f;
|
||||
mfMaxHeight = 0.1f;
|
||||
|
||||
mlNodesPerGrid = 6;
|
||||
|
||||
mbNodeIsAtCenter = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAINodeContainer::~cAINodeContainer() {
|
||||
hplDelete(mpRayCallback);
|
||||
|
||||
STLDeleteAll(mvNodes);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC METHODS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAINodeContainer::ReserveSpace(size_t alReserveSpace) {
|
||||
mvNodes.reserve(alReserveSpace);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAINodeContainer::AddNode(const tString &asName, const cVector3f &avPosition, void *apUserData) {
|
||||
cAINode *pNode = hplNew(cAINode, ());
|
||||
pNode->msName = asName;
|
||||
pNode->mvPosition = avPosition;
|
||||
pNode->mpUserData = apUserData;
|
||||
|
||||
mvNodes.push_back(pNode);
|
||||
m_mapNodes.insert(tAINodeMap::value_type(asName, pNode));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
int cAINodeContainer::GetNodeNum() const {
|
||||
return (int)mvNodes.size();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAINode *cAINodeContainer::GetNodeFromName(const tString &asName) {
|
||||
tAINodeMapIt it = m_mapNodes.find(asName);
|
||||
if (it == m_mapNodes.end()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
|
||||
// return (cAINode*)STLFindByName(mvNodes,asName);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
class cSortEndNodes {
|
||||
public:
|
||||
bool operator()(const cAINodeEdge &aEndA, const cAINodeEdge &aEndB) {
|
||||
return aEndA.mfDistance < aEndB.mfDistance;
|
||||
}
|
||||
};
|
||||
|
||||
void cAINodeContainer::Compile() {
|
||||
BuildNodeGridMap();
|
||||
|
||||
tAINodeVecIt CurrentNodeIt = mvNodes.begin();
|
||||
for (; CurrentNodeIt != mvNodes.end(); ++CurrentNodeIt) {
|
||||
cAINode *pNode = *CurrentNodeIt;
|
||||
|
||||
////////////////////////////////////////
|
||||
// Add the ends that are connected to the node.
|
||||
/*Log("Node %s checks: ",pNode->GetName().c_str());
|
||||
tAINodeVecIt EndNodeIt = mvNodes.begin();
|
||||
for(; EndNodeIt != mvNodes.end(); ++EndNodeIt)
|
||||
{
|
||||
cAINode *pEndNode = *EndNodeIt;
|
||||
|
||||
if(pEndNode == pNode) continue;
|
||||
float fDist = cMath::Vector3Dist(pNode->mvPosition, pEndNode->mvPosition);
|
||||
if(fDist > mfMaxEndDistance*2) continue;
|
||||
Log("'%s'(%f) ",pEndNode->GetName().c_str(),fDist);
|
||||
|
||||
float fHeight = fabs(pNode->mvPosition.y - pEndNode->mvPosition.y);
|
||||
|
||||
if( fHeight <= mfMaxHeight &&
|
||||
FreePath(pNode->mvPosition, pEndNode->mvPosition,-1,eAIFreePathFlag_SkipDynamic))
|
||||
{
|
||||
pNode->AddEdge(pEndNode);
|
||||
Log("Added!");
|
||||
}
|
||||
Log(", ");
|
||||
}
|
||||
Log("\n");*/
|
||||
// Log("Node %s checks: ",pNode->GetName().c_str());
|
||||
cAINodeIterator nodeIt = GetNodeIterator(pNode->mvPosition, mfMaxEndDistance * 1.5f);
|
||||
while (nodeIt.HasNext()) {
|
||||
cAINode *pEndNode = nodeIt.Next();
|
||||
|
||||
if (pEndNode == pNode)
|
||||
continue;
|
||||
float fDist = cMath::Vector3Dist(pNode->mvPosition, pEndNode->mvPosition);
|
||||
if (fDist > mfMaxEndDistance * 2)
|
||||
continue;
|
||||
// Log("'%s'(%f) ",pEndNode->GetName().c_str(),fDist);
|
||||
|
||||
float fHeight = fabs(pNode->mvPosition.y - pEndNode->mvPosition.y);
|
||||
|
||||
tAIFreePathFlag flag = eAIFreePathFlag_SkipDynamic | eAIFreePathFlag_SkipVolatile;
|
||||
if (fHeight <= mfMaxHeight &&
|
||||
FreePath(pNode->mvPosition, pEndNode->mvPosition, -1, flag)) {
|
||||
pNode->AddEdge(pEndNode);
|
||||
// Log("Added!");
|
||||
}
|
||||
// Log(", ");
|
||||
}
|
||||
// Log("\n");
|
||||
|
||||
///////////////////////////////////////
|
||||
// Sort nodes and remove unwanted ones.
|
||||
|
||||
Common::sort(pNode->mvEdges.data(), pNode->mvEdges.data() + pNode->mvEdges.size(), cSortEndNodes());
|
||||
|
||||
// Resize if to too large
|
||||
if (mlMaxNodeEnds > 0 && (int)pNode->mvEdges.size() > mlMaxNodeEnds) {
|
||||
pNode->mvEdges.resize(mlMaxNodeEnds);
|
||||
}
|
||||
|
||||
// Remove ends to far, but skip if min nodes is not met
|
||||
for (size_t i = 0; i < pNode->mvEdges.size(); ++i) {
|
||||
if (pNode->mvEdges[i].mfDistance > mfMaxEndDistance && (int)i >= mlMinNodeEnds) {
|
||||
pNode->mvEdges.resize(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Log(" Final edge count: %d\n",pNode->mvEdges.size());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAINodeContainer::BuildNodeGridMap() {
|
||||
bool bLog = false;
|
||||
|
||||
if (bLog)
|
||||
Log("Nodes: %d\n", mvNodes.size());
|
||||
|
||||
////////////////////////////////////
|
||||
// Calculate min and max
|
||||
cVector2f vMin(mvNodes[0]->GetPosition().x, mvNodes[0]->GetPosition().z);
|
||||
cVector2f vMax(mvNodes[0]->GetPosition().x, mvNodes[0]->GetPosition().z);
|
||||
|
||||
for (size_t i = 1; i < mvNodes.size(); ++i) {
|
||||
cAINode *pNode = mvNodes[i];
|
||||
|
||||
if (vMin.x > pNode->GetPosition().x)
|
||||
vMin.x = pNode->GetPosition().x;
|
||||
if (vMin.y > pNode->GetPosition().z)
|
||||
vMin.y = pNode->GetPosition().z;
|
||||
|
||||
if (vMax.x < pNode->GetPosition().x)
|
||||
vMax.x = pNode->GetPosition().x;
|
||||
if (vMax.y < pNode->GetPosition().z)
|
||||
vMax.y = pNode->GetPosition().z;
|
||||
}
|
||||
|
||||
mvMinGridPos = vMin;
|
||||
mvMaxGridPos = vMax;
|
||||
|
||||
////////////////////////////////////
|
||||
// Determine size of grids
|
||||
int lGridNum = (int)(sqrt((float)mvNodes.size() / (float)mlNodesPerGrid) + 0.5f) + 1;
|
||||
|
||||
if (bLog)
|
||||
Log("Grid Num: %d\n", lGridNum);
|
||||
|
||||
mvGridMapSize.x = lGridNum;
|
||||
mvGridMapSize.y = lGridNum;
|
||||
|
||||
//+1 to fix so that nodes on the border has a grid)
|
||||
mvGrids.resize((lGridNum + 1) * (lGridNum + 1));
|
||||
|
||||
mvGridSize = (mvMaxGridPos - mvMinGridPos);
|
||||
mvGridSize.x /= (float)mvGridMapSize.x;
|
||||
mvGridSize.y /= (float)mvGridMapSize.y;
|
||||
|
||||
if (bLog)
|
||||
Log("GridSize: %f : %f\n", mvGridSize.x, mvGridSize.y);
|
||||
if (bLog)
|
||||
Log("MinPos: %s\n", mvMinGridPos.ToString().c_str());
|
||||
if (bLog)
|
||||
Log("MaxPos: %s\n", mvMaxGridPos.ToString().c_str());
|
||||
|
||||
////////////////////////////////////
|
||||
// Add nodes to grid
|
||||
for (size_t i = 0; i < mvNodes.size(); ++i) {
|
||||
cAINode *pNode = mvNodes[i];
|
||||
|
||||
cVector2f vLocalPos(pNode->GetPosition().x, pNode->GetPosition().z);
|
||||
vLocalPos -= mvMinGridPos;
|
||||
|
||||
cVector2l vGridPos(0);
|
||||
// Have checks so we are sure there is no division by zero.
|
||||
if (mvGridSize.x > 0)
|
||||
vGridPos.x = (int)(vLocalPos.x / mvGridSize.x);
|
||||
if (mvGridSize.y > 0)
|
||||
vGridPos.y = (int)(vLocalPos.y / mvGridSize.y);
|
||||
|
||||
if (false)
|
||||
Log("Adding node %d, world: (%s) local (%s), at %d : %d\n", i,
|
||||
pNode->GetPosition().ToString().c_str(),
|
||||
vLocalPos.ToString().c_str(),
|
||||
vGridPos.x, vGridPos.y);
|
||||
|
||||
mvGrids[vGridPos.y * (mvGridMapSize.x + 1) + vGridPos.x].mlstNodes.push_back(pNode);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAINodeIterator cAINodeContainer::GetNodeIterator(const cVector3f &avPosition, float afRadius) {
|
||||
return cAINodeIterator(this, avPosition, afRadius);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
static const cVector2f gvPosAdds[] = {cVector2f(0, 0),
|
||||
cVector2f(1, 0),
|
||||
cVector2f(-1, 0),
|
||||
cVector2f(0, 1),
|
||||
cVector2f(0, -1)};
|
||||
|
||||
bool cAINodeContainer::FreePath(const cVector3f &avStart, const cVector3f &avEnd, int alRayNum,
|
||||
tAIFreePathFlag aFlags, iAIFreePathCallback *apCallback) {
|
||||
iPhysicsWorld *pPhysicsWorld = mpWorld->GetPhysicsWorld();
|
||||
if (pPhysicsWorld == NULL)
|
||||
return true;
|
||||
|
||||
if (alRayNum < 0 || alRayNum > 5)
|
||||
alRayNum = 5;
|
||||
|
||||
/////////////////////////////
|
||||
// Calculate the right vector
|
||||
const cVector3f vForward = cMath::Vector3Normalize(avEnd - avStart);
|
||||
const cVector3f vUp = cVector3f(0, 1.0f, 0);
|
||||
const cVector3f vRight = cMath::Vector3Cross(vForward, vUp);
|
||||
|
||||
// Get the center
|
||||
const cVector3f vStartCenter = mbNodeIsAtCenter ? avStart : avStart + cVector3f(0, mvSize.y / 2, 0);
|
||||
const cVector3f vEndCenter = mbNodeIsAtCenter ? avEnd : avEnd + cVector3f(0, mvSize.y / 2, 0);
|
||||
|
||||
// Get the half with and height. Make them a little smaller so that player can slide over funk on floor.
|
||||
const float fHalfWidth = mvSize.x * 0.4f;
|
||||
const float fHalfHeight = mvSize.y * 0.4f;
|
||||
|
||||
// Setup ray callback
|
||||
mpRayCallback->SetFlags(aFlags);
|
||||
|
||||
// Iterate through all the rays.
|
||||
for (int i = 0; i < alRayNum; ++i) {
|
||||
cVector3f vAdd = vRight * (gvPosAdds[i].x * fHalfWidth) + vUp * (gvPosAdds[i].y * fHalfHeight);
|
||||
cVector3f vStart = vStartCenter + vAdd;
|
||||
cVector3f vEnd = vEndCenter + vAdd;
|
||||
|
||||
mpRayCallback->Reset();
|
||||
mpRayCallback->mpCallback = apCallback;
|
||||
|
||||
pPhysicsWorld->CastRay(mpRayCallback, vStart, vEnd, false, false, false, true);
|
||||
|
||||
if (mpRayCallback->Intersected())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAINodeContainer::SaveToFile(const tString &asFile) {
|
||||
TiXmlDocument *pXmlDoc = hplNew(TiXmlDocument, (asFile.c_str()));
|
||||
|
||||
TiXmlElement *pRootElem = static_cast<TiXmlElement *>(pXmlDoc->InsertEndChild(TiXmlElement("AINodes")));
|
||||
|
||||
for (size_t i = 0; i < mvNodes.size(); ++i) {
|
||||
cAINode *pNode = mvNodes[i];
|
||||
TiXmlElement *pNodeElem = static_cast<TiXmlElement *>(pRootElem->InsertEndChild(TiXmlElement("Node")));
|
||||
|
||||
pNodeElem->SetAttribute("Name", pNode->GetName().c_str());
|
||||
|
||||
for (int edge = 0; edge < pNode->GetEdgeNum(); ++edge) {
|
||||
cAINodeEdge *pEdge = pNode->GetEdge(edge);
|
||||
TiXmlElement *pEdgeElem = static_cast<TiXmlElement *>(pNodeElem->InsertEndChild(TiXmlElement("Edge")));
|
||||
|
||||
pEdgeElem->SetAttribute("Node", pEdge->mpNode->GetName().c_str());
|
||||
tString sDistance = cString::ToString(pEdge->mfDistance);
|
||||
pEdgeElem->SetAttribute("Distance", sDistance.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (pXmlDoc->SaveFile() == false) {
|
||||
Error("Couldn't save XML file %s\n", asFile.c_str());
|
||||
}
|
||||
hplDelete(pXmlDoc);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAINodeContainer::LoadFromFile(const tString &asFile) {
|
||||
BuildNodeGridMap();
|
||||
|
||||
TiXmlDocument *pXmlDoc = hplNew(TiXmlDocument, (asFile.c_str()));
|
||||
if (pXmlDoc->LoadFile() == false) {
|
||||
Warning("Couldn't open XML file %s\n", asFile.c_str());
|
||||
hplDelete(pXmlDoc);
|
||||
return;
|
||||
}
|
||||
|
||||
TiXmlElement *pRootElem = pXmlDoc->RootElement();
|
||||
|
||||
TiXmlElement *pNodeElem = pRootElem->FirstChildElement("Node");
|
||||
for (; pNodeElem != NULL; pNodeElem = pNodeElem->NextSiblingElement("Node")) {
|
||||
tString sName = cString::ToString(pNodeElem->Attribute("Name"), "");
|
||||
|
||||
cAINode *pNode = GetNodeFromName(sName);
|
||||
|
||||
TiXmlElement *pEdgeElem = pNodeElem->FirstChildElement("Edge");
|
||||
for (; pEdgeElem != NULL; pEdgeElem = pEdgeElem->NextSiblingElement("Edge")) {
|
||||
tString sNodeName = cString::ToString(pEdgeElem->Attribute("Node"), "");
|
||||
cAINode *pEdgeNode = GetNodeFromName(sNodeName);
|
||||
|
||||
cAINodeEdge Edge;
|
||||
Edge.mpNode = pEdgeNode;
|
||||
Edge.mfDistance = cString::ToFloat(pEdgeElem->Attribute("Distance"), 0);
|
||||
Edge.mfSqrDistance = Edge.mfDistance * Edge.mfDistance;
|
||||
|
||||
pNode->mvEdges.push_back(Edge);
|
||||
}
|
||||
}
|
||||
|
||||
hplDelete(pXmlDoc);
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE METHODS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cVector2l cAINodeContainer::GetGridPosFromLocal(const cVector2f &avLocalPos) {
|
||||
cVector2l vGridPos;
|
||||
vGridPos.x = (int)(avLocalPos.x / mvGridSize.x);
|
||||
vGridPos.y = (int)(avLocalPos.y / mvGridSize.y);
|
||||
|
||||
if (vGridPos.x < 0)
|
||||
vGridPos.x = 0;
|
||||
if (vGridPos.y < 0)
|
||||
vGridPos.y = 0;
|
||||
if (vGridPos.x > mvGridMapSize.x)
|
||||
vGridPos.x = mvGridMapSize.x;
|
||||
if (vGridPos.y > mvGridMapSize.y)
|
||||
vGridPos.y = mvGridMapSize.y;
|
||||
|
||||
return vGridPos;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAIGridNode *cAINodeContainer::GetGrid(const cVector2l &avPos) {
|
||||
int lIndex = avPos.y * (mvGridMapSize.x + 1) + avPos.x;
|
||||
// Log(" index: %d Max: %d\n",lIndex,(int)mvGrids.size());
|
||||
|
||||
return &mvGrids[lIndex];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
} // namespace hpl
|
||||
303
engines/hpl1/engine/ai/AINodeContainer.h
Normal file
303
engines/hpl1/engine/ai/AINodeContainer.h
Normal file
@@ -0,0 +1,303 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2010 - Frictional Games
|
||||
*
|
||||
* This file is part of HPL1 Engine.
|
||||
*/
|
||||
|
||||
#ifndef HPL_AI_NODE_CONTAINER_H
|
||||
#define HPL_AI_NODE_CONTAINER_H
|
||||
|
||||
#include "hpl1/engine/math/MathTypes.h"
|
||||
#include "hpl1/engine/system/SystemTypes.h"
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/list.h"
|
||||
#include "hpl1/engine/physics/PhysicsWorld.h"
|
||||
|
||||
namespace hpl {
|
||||
|
||||
class cWorld3D;
|
||||
|
||||
//--------------------------------
|
||||
|
||||
typedef tFlag tAIFreePathFlag;
|
||||
|
||||
#define eAIFreePathFlag_SkipStatic (0x00000001)
|
||||
#define eAIFreePathFlag_SkipDynamic (0x00000002)
|
||||
#define eAIFreePathFlag_SkipVolatile (0x00000004)
|
||||
|
||||
//--------------------------------
|
||||
class cAINode;
|
||||
|
||||
class cAINodeEdge {
|
||||
public:
|
||||
float mfDistance;
|
||||
float mfSqrDistance;
|
||||
cAINode *mpNode;
|
||||
};
|
||||
|
||||
typedef Common::Array<cAINodeEdge> tAINodeEdgeVec;
|
||||
typedef tAINodeEdgeVec::iterator tAINodeEdgeVecIt;
|
||||
|
||||
//--------------------------------
|
||||
|
||||
class cAINode {
|
||||
friend class cAINodeContainer;
|
||||
|
||||
public:
|
||||
cAINode();
|
||||
~cAINode();
|
||||
|
||||
void AddEdge(cAINode *pNode);
|
||||
|
||||
int GetEdgeNum() const { return (int)mvEdges.size(); }
|
||||
inline cAINodeEdge *GetEdge(int alIdx) { return &mvEdges[alIdx]; }
|
||||
|
||||
const cVector3f &GetPosition() { return mvPosition; }
|
||||
|
||||
const tString &GetName() { return msName; }
|
||||
|
||||
private:
|
||||
tString msName;
|
||||
cVector3f mvPosition;
|
||||
void *mpUserData;
|
||||
|
||||
tAINodeEdgeVec mvEdges;
|
||||
};
|
||||
|
||||
typedef Common::Array<cAINode *> tAINodeVec;
|
||||
typedef tAINodeVec::iterator tAINodeVecIt;
|
||||
|
||||
typedef Common::List<cAINode *> tAINodeList;
|
||||
typedef tAINodeList::iterator tAINodeListIt;
|
||||
|
||||
typedef Common::StableMap<tString, cAINode *> tAINodeMap;
|
||||
typedef tAINodeMap::iterator tAINodeMapIt;
|
||||
|
||||
//--------------------------------
|
||||
|
||||
class iAIFreePathCallback {
|
||||
public:
|
||||
virtual ~iAIFreePathCallback() = default;
|
||||
virtual bool Intersects(iPhysicsBody *pBody, cPhysicsRayParams *apParams) = 0;
|
||||
};
|
||||
|
||||
//--------------------------------
|
||||
|
||||
class cAINodeRayCallback : public iPhysicsRayCallback {
|
||||
public:
|
||||
void Reset();
|
||||
void SetFlags(tAIFreePathFlag aFlags) { mFlags = aFlags; }
|
||||
|
||||
bool BeforeIntersect(iPhysicsBody *pBody);
|
||||
bool OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams);
|
||||
|
||||
bool Intersected();
|
||||
|
||||
iAIFreePathCallback *mpCallback;
|
||||
|
||||
private:
|
||||
bool mbIntersected;
|
||||
tAIFreePathFlag mFlags;
|
||||
};
|
||||
|
||||
//--------------------------------
|
||||
|
||||
class cAIGridNode {
|
||||
public:
|
||||
tAINodeList mlstNodes;
|
||||
};
|
||||
|
||||
//--------------------------------
|
||||
class cAINodeContainer;
|
||||
|
||||
class cAINodeIterator {
|
||||
public:
|
||||
cAINodeIterator(cAINodeContainer *apContainer, const cVector3f &avPos, float afRadius);
|
||||
|
||||
bool HasNext();
|
||||
cAINode *Next();
|
||||
|
||||
private:
|
||||
bool IncGridPos();
|
||||
|
||||
cAINodeContainer *mpContainer;
|
||||
cVector3f mvPosition;
|
||||
float mfRadius;
|
||||
cVector2l mvStartGridPos;
|
||||
cVector2l mvEndGridPos;
|
||||
cVector2l mvGridPos;
|
||||
|
||||
tAINodeList *mpNodeList;
|
||||
tAINodeListIt mNodeIt;
|
||||
};
|
||||
|
||||
//--------------------------------
|
||||
|
||||
class cAINodeContainer {
|
||||
friend class cAINodeIterator;
|
||||
|
||||
public:
|
||||
cAINodeContainer(const tString &asName, const tString &asNodeName,
|
||||
cWorld3D *apWorld, const cVector3f &avCollideSize);
|
||||
~cAINodeContainer();
|
||||
|
||||
const tString &GetNodeName() { return msNodeName; }
|
||||
const tString &GetName() { return msName; }
|
||||
|
||||
const cVector3f &GetCollideSize() { return mvSize; }
|
||||
|
||||
/**
|
||||
* Reserves spaces for nodes.
|
||||
* \param alReserveSpace Number of nodes to reserve space for.
|
||||
*/
|
||||
void ReserveSpace(size_t alReserveSpace);
|
||||
|
||||
/**
|
||||
* Adds a new node to the container.
|
||||
* \param &asName Name of the node
|
||||
* \param &avPosition Position of the node.
|
||||
* \param *apUserData Data supplied by user.
|
||||
*/
|
||||
void AddNode(const tString &asName, const cVector3f &avPosition, void *apUserData = NULL);
|
||||
|
||||
/**
|
||||
* Get the number of nodes.
|
||||
*/
|
||||
int GetNodeNum() const;
|
||||
|
||||
/**
|
||||
* Get a node.
|
||||
* \param alIdx index of node.
|
||||
*/
|
||||
inline cAINode *GetNode(int alIdx) { return mvNodes[alIdx]; }
|
||||
|
||||
/**
|
||||
* Gets a node based on the name.
|
||||
* \param &asName Name of the node.
|
||||
*/
|
||||
cAINode *GetNodeFromName(const tString &asName);
|
||||
|
||||
/**
|
||||
* Compile the added nodes.
|
||||
*/
|
||||
void Compile();
|
||||
|
||||
/**
|
||||
* Build a grid map for nodes. (Used internally mostly)
|
||||
*/
|
||||
void BuildNodeGridMap();
|
||||
|
||||
/**
|
||||
* Returns a node iterator. Note that the radius is not checked, some nodes may lie outside.
|
||||
* \param &avPosition
|
||||
* \param afRadius
|
||||
* \return
|
||||
*/
|
||||
cAINodeIterator GetNodeIterator(const cVector3f &avPosition, float afRadius);
|
||||
|
||||
/**
|
||||
* Checks for a free path using the containers collide size.
|
||||
* \param &avStart
|
||||
* \param &avEnd
|
||||
* \param alRayNum The max number of rays cast, -1 = maximum
|
||||
* \param alFlags Set Flags for the ray casting.
|
||||
* \param apCallback Check for every body and overrides alFlags.
|
||||
* \return
|
||||
*/
|
||||
bool FreePath(const cVector3f &avStart, const cVector3f &avEnd, int alRayNum = -1,
|
||||
tAIFreePathFlag aFlags = 0, iAIFreePathCallback *apCallback = NULL);
|
||||
|
||||
/**
|
||||
* Sets the max number of end node added to a node.
|
||||
* \param alX The max number, -1 = unlimited
|
||||
*/
|
||||
void SetMaxEdges(int alX) { mlMaxNodeEnds = alX; }
|
||||
|
||||
/**
|
||||
* Sets the min number of end node added to a node. This overrides max distance when needed.
|
||||
*/
|
||||
void SetMinEdges(int alX) { mlMinNodeEnds = alX; }
|
||||
|
||||
/**
|
||||
* Sets the max distance for an end node.
|
||||
* \param afX
|
||||
*/
|
||||
void SetMaxEdgeDistance(float afX) { mfMaxEndDistance = afX; }
|
||||
|
||||
float GetMaxEdgeDistance() const { return mfMaxEndDistance; }
|
||||
|
||||
void SetMaxHeight(float afX) { mfMaxHeight = afX; }
|
||||
float GetMaxHeight() const { return mfMaxHeight; }
|
||||
|
||||
/**
|
||||
* When calculating if there is a free path between two nodes. Is the node position the center of the collider.
|
||||
* If not the position is the feet position.
|
||||
*/
|
||||
void SetNodeIsAtCenter(bool abX) { mbNodeIsAtCenter = abX; }
|
||||
bool GetNodeIsAtCenter() { return mbNodeIsAtCenter; }
|
||||
|
||||
/**
|
||||
* Saves all the node connections to file.
|
||||
*/
|
||||
void SaveToFile(const tString &asFile);
|
||||
/**
|
||||
* Loads all node connections from file. Only to be done after all nodes are loaded.
|
||||
*/
|
||||
void LoadFromFile(const tString &asFile);
|
||||
|
||||
private:
|
||||
cVector2l GetGridPosFromLocal(const cVector2f &avLocalPos);
|
||||
cAIGridNode *GetGrid(const cVector2l &avPos);
|
||||
|
||||
tString msName;
|
||||
tString msNodeName;
|
||||
|
||||
cWorld3D *mpWorld;
|
||||
cVector3f mvSize;
|
||||
|
||||
cAINodeRayCallback *mpRayCallback;
|
||||
tAINodeVec mvNodes;
|
||||
tAINodeMap m_mapNodes;
|
||||
|
||||
bool mbNodeIsAtCenter;
|
||||
|
||||
cVector2l mvGridMapSize;
|
||||
cVector2f mvGridSize;
|
||||
cVector2f mvMinGridPos;
|
||||
cVector2f mvMaxGridPos;
|
||||
int mlNodesPerGrid;
|
||||
|
||||
Common::Array<cAIGridNode> mvGrids;
|
||||
|
||||
// properties
|
||||
int mlMaxNodeEnds;
|
||||
int mlMinNodeEnds;
|
||||
float mfMaxEndDistance;
|
||||
float mfMaxHeight;
|
||||
};
|
||||
|
||||
} // namespace hpl
|
||||
|
||||
#endif // HPL_AI_NODE_CONTAINER_H
|
||||
342
engines/hpl1/engine/ai/AINodeGenerator.cpp
Normal file
342
engines/hpl1/engine/ai/AINodeGenerator.cpp
Normal file
@@ -0,0 +1,342 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2010 - Frictional Games
|
||||
*
|
||||
* This file is part of HPL1 Engine.
|
||||
*/
|
||||
|
||||
#include "hpl1/engine/ai/AINodeGenerator.h"
|
||||
|
||||
#include "hpl1/engine/scene/World3D.h"
|
||||
|
||||
#include "hpl1/engine/system/String.h"
|
||||
#include "hpl1/engine/system/System.h"
|
||||
#include "hpl1/engine/system/low_level_system.h"
|
||||
|
||||
#include "hpl1/engine/resources/FileSearcher.h"
|
||||
#include "hpl1/engine/resources/Resources.h"
|
||||
|
||||
#include "hpl1/engine/physics/PhysicsBody.h"
|
||||
#include "hpl1/engine/physics/PhysicsWorld.h"
|
||||
|
||||
#include "hpl1/engine/impl/tinyXML/tinyxml.h"
|
||||
|
||||
namespace hpl {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PARAMS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAINodeGeneratorParams::cAINodeGeneratorParams() {
|
||||
msNodeType = "node";
|
||||
|
||||
mfHeightFromGround = 0.1f;
|
||||
mfMinWallDist = 0.4f;
|
||||
|
||||
mvMinPos = cVector3f(-10000, -10000, -10000);
|
||||
mvMaxPos = cVector3f(10000, 10000, 10000);
|
||||
|
||||
mfGridSize = 0.4f;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool cCollideRayCallback::OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams) {
|
||||
if (pBody->GetMass() != 0)
|
||||
return true;
|
||||
|
||||
mbIntersected = true;
|
||||
mvPos = apParams->mvPoint;
|
||||
mfDist = apParams->mfDist;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CONSTRUCTORS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
unsigned generatorInstances = 0;
|
||||
|
||||
cAINodeGenerator::cAINodeGenerator() {
|
||||
++generatorInstances;
|
||||
assert(generatorInstances == 1);
|
||||
}
|
||||
|
||||
cAINodeGenerator::~cAINodeGenerator() {
|
||||
--generatorInstances;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC METHODS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
// static cCollideRayCallback gCollideRayCallback;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAINodeGenerator::Generate(cWorld3D *apWorld, cAINodeGeneratorParams *apParams) {
|
||||
mpWorld = apWorld;
|
||||
mpParams = apParams;
|
||||
|
||||
iPhysicsWorld *pPhysicsWorld = apWorld->GetPhysicsWorld();
|
||||
|
||||
// bool mbLoadFromFile = false;
|
||||
|
||||
/*cSystem *pSystem = */ apWorld->GetSystem();
|
||||
cResources *pResources = apWorld->GetResources();
|
||||
cFileSearcher *pFileSearcher = pResources->GetFileSearcher();
|
||||
|
||||
mpNodeList = apWorld->GetAINodeList(mpParams->msNodeType);
|
||||
|
||||
if (mpWorld->GetFileName() != "") {
|
||||
tString sPath = pFileSearcher->GetFilePath(mpWorld->GetFileName());
|
||||
tWString sSaveFile = cString::To16Char(cString::SetFileExt(sPath, "ainodes"));
|
||||
|
||||
if (sPath != "" && FileExists(sSaveFile)) {
|
||||
cDate mapDate = FileModifiedDate(cString::To16Char(sPath));
|
||||
cDate saveDate = FileModifiedDate(sSaveFile);
|
||||
|
||||
// If the save file is newer than the map load from it.
|
||||
if (saveDate > mapDate) {
|
||||
LoadFromFile();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
// Get the size of the world
|
||||
cPhysicsBodyIterator it = pPhysicsWorld->GetBodyIterator();
|
||||
cVector3f vWorldMax(-100000, -100000, -100000);
|
||||
cVector3f vWorldMin(100000, 100000, 100000);
|
||||
|
||||
while (it.HasNext()) {
|
||||
iPhysicsBody *pBody = it.Next();
|
||||
|
||||
cVector3f vMin = pBody->GetBV()->GetMin();
|
||||
cVector3f vMax = pBody->GetBV()->GetMax();
|
||||
|
||||
// X
|
||||
if (vWorldMin.x > vMin.x)
|
||||
vWorldMin.x = vMin.x;
|
||||
if (vWorldMax.x < vMax.x)
|
||||
vWorldMax.x = vMax.x;
|
||||
|
||||
// Y
|
||||
if (vWorldMin.y > vMin.y)
|
||||
vWorldMin.y = vMin.y;
|
||||
if (vWorldMax.y < vMax.y)
|
||||
vWorldMax.y = vMax.y;
|
||||
|
||||
// Z
|
||||
if (vWorldMin.z > vMin.z)
|
||||
vWorldMin.z = vMin.z;
|
||||
if (vWorldMax.z < vMax.z)
|
||||
vWorldMax.z = vMax.z;
|
||||
}
|
||||
|
||||
// Make the world small according to grid size.
|
||||
vWorldMin.x += mpParams->mfGridSize;
|
||||
vWorldMin.z += mpParams->mfGridSize;
|
||||
vWorldMax.x -= mpParams->mfGridSize;
|
||||
vWorldMax.z -= mpParams->mfGridSize;
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Check against the user set min and max
|
||||
if (vWorldMin.x < mpParams->mvMinPos.x)
|
||||
vWorldMin.x = mpParams->mvMinPos.x;
|
||||
if (vWorldMax.x > mpParams->mvMaxPos.x)
|
||||
vWorldMax.x = mpParams->mvMaxPos.x;
|
||||
|
||||
if (vWorldMin.y < mpParams->mvMinPos.y)
|
||||
vWorldMin.y = mpParams->mvMinPos.y;
|
||||
if (vWorldMax.y > mpParams->mvMaxPos.y)
|
||||
vWorldMax.y = mpParams->mvMaxPos.y;
|
||||
|
||||
if (vWorldMin.z < mpParams->mvMinPos.z)
|
||||
vWorldMin.z = mpParams->mvMinPos.z;
|
||||
if (vWorldMax.z > mpParams->mvMaxPos.z)
|
||||
vWorldMax.z = mpParams->mvMaxPos.z;
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Place the nodes in the world
|
||||
cVector3f vPos(vWorldMin.x, 0, vWorldMin.z);
|
||||
|
||||
while (vPos.z <= vWorldMax.z) {
|
||||
cVector3f vStart(vPos.x, vWorldMax.y, vPos.z);
|
||||
cVector3f vEnd(vPos.x, vWorldMin.y, vPos.z);
|
||||
|
||||
pPhysicsWorld->CastRay(this, vStart, vEnd, false, false, true);
|
||||
|
||||
// Log("Pos: %s Min: %s Max: %s\n",vPos.ToString().c_str(),
|
||||
// vWorldMin.ToString().c_str(),
|
||||
// vWorldMax.ToString().c_str());
|
||||
|
||||
vPos.x += apParams->mfGridSize;
|
||||
if (vPos.x > vWorldMax.x) {
|
||||
vPos.x = vWorldMin.x;
|
||||
vPos.z += apParams->mfGridSize;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Check so that the nodes are not too close to walls
|
||||
cVector3f vEnds[4] = {cVector3f(mpParams->mfMinWallDist, 0, 0),
|
||||
cVector3f(-mpParams->mfMinWallDist, 0, 0),
|
||||
cVector3f(0, 0, mpParams->mfMinWallDist),
|
||||
cVector3f(0, 0, -mpParams->mfMinWallDist)};
|
||||
|
||||
cVector3f vPushBackDirs[4] = {cVector3f(-1, 0, 0),
|
||||
cVector3f(1, 0, 0),
|
||||
cVector3f(0, 0, -1),
|
||||
cVector3f(0, 0, 1)};
|
||||
|
||||
tTempAiNodeListIt nodeIt = mpNodeList->begin();
|
||||
for (; nodeIt != mpNodeList->end(); ++nodeIt) {
|
||||
cTempAiNode &Node = *nodeIt;
|
||||
|
||||
// Check if there are any walls close by.
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
_rayCallback.mbIntersected = false;
|
||||
pPhysicsWorld->CastRay(&_rayCallback, Node.mvPos, Node.mvPos + vEnds[i], true, false, true);
|
||||
|
||||
if (_rayCallback.mbIntersected) {
|
||||
// Log("Walldistance %f : Add: (%s) Push (%s) Min: %f\n",gCollideRayCallback.mfDist,
|
||||
// vEnds[i].ToString().c_str(),
|
||||
// vPushBackDirs[i].ToString().c_str(),
|
||||
// mpParams->mfMinWallDist);
|
||||
if (_rayCallback.mfDist < mpParams->mfMinWallDist) {
|
||||
Node.mvPos += vPushBackDirs[i] * (mpParams->mfMinWallDist - _rayCallback.mfDist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Save to file
|
||||
|
||||
SaveToFile();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool cAINodeGenerator::OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams) {
|
||||
if (pBody->GetMass() != 0)
|
||||
return true;
|
||||
|
||||
/*iPhysicsWorld *pPhysicsWorld = */ mpWorld->GetPhysicsWorld();
|
||||
|
||||
cVector3f vPosition = apParams->mvPoint + cVector3f(0, mpParams->mfHeightFromGround, 0);
|
||||
|
||||
mpNodeList->push_back(cTempAiNode(vPosition, ""));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAINodeGenerator::SaveToFile() {
|
||||
if (mpWorld->GetFileName() == "")
|
||||
return;
|
||||
|
||||
/*cSystem *pSystem = */ mpWorld->GetSystem();
|
||||
cResources *pResources = mpWorld->GetResources();
|
||||
cFileSearcher *pFileSearcher = pResources->GetFileSearcher();
|
||||
|
||||
tString sMapPath = pFileSearcher->GetFilePath(mpWorld->GetFileName());
|
||||
tString sSaveFile = cString::SetFileExt(sMapPath, "ainodes");
|
||||
|
||||
TiXmlDocument *pXmlDoc = hplNew(TiXmlDocument, (sSaveFile.c_str()));
|
||||
|
||||
TiXmlElement *pRootElem = static_cast<TiXmlElement *>(pXmlDoc->InsertEndChild(TiXmlElement("AiNodes")));
|
||||
|
||||
tTempAiNodeListIt nodeIt = mpNodeList->begin();
|
||||
for (; nodeIt != mpNodeList->end(); ++nodeIt) {
|
||||
cTempAiNode &Node = *nodeIt;
|
||||
TiXmlElement *pNodeElem = static_cast<TiXmlElement *>(pRootElem->InsertEndChild(TiXmlElement("Node")));
|
||||
|
||||
tString sPos = cString::ToString(Node.mvPos.x) + " " +
|
||||
cString::ToString(Node.mvPos.y) + " " +
|
||||
cString::ToString(Node.mvPos.z);
|
||||
pNodeElem->SetAttribute("Pos", sPos.c_str());
|
||||
pNodeElem->SetAttribute("Name", Node.msName.c_str());
|
||||
}
|
||||
|
||||
if (pXmlDoc->SaveFile() == false) {
|
||||
Error("Couldn't save XML file %s\n", sSaveFile.c_str());
|
||||
}
|
||||
hplDelete(pXmlDoc);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAINodeGenerator::LoadFromFile() {
|
||||
if (mpWorld->GetFileName() == "")
|
||||
return;
|
||||
|
||||
/*cSystem *pSystem = */ mpWorld->GetSystem();
|
||||
cResources *pResources = mpWorld->GetResources();
|
||||
cFileSearcher *pFileSearcher = pResources->GetFileSearcher();
|
||||
|
||||
tString sMapPath = pFileSearcher->GetFilePath(mpWorld->GetFileName());
|
||||
tString sSaveFile = cString::SetFileExt(sMapPath, "ainodes");
|
||||
|
||||
TiXmlDocument *pXmlDoc = hplNew(TiXmlDocument, (sSaveFile.c_str()));
|
||||
if (pXmlDoc->LoadFile() == false) {
|
||||
Warning("Couldn't open XML file %s\n", sSaveFile.c_str());
|
||||
hplDelete(pXmlDoc);
|
||||
return;
|
||||
}
|
||||
|
||||
TiXmlElement *pRootElem = pXmlDoc->RootElement();
|
||||
|
||||
TiXmlElement *pNodeElem = pRootElem->FirstChildElement("Node");
|
||||
for (; pNodeElem != NULL; pNodeElem = pNodeElem->NextSiblingElement("Node")) {
|
||||
cVector3f vPos = cString::ToVector3f(pNodeElem->Attribute("Pos"), 0);
|
||||
tString sName = cString::ToString(pNodeElem->Attribute("Name"), "");
|
||||
|
||||
mpNodeList->push_back(cTempAiNode(vPos, sName));
|
||||
}
|
||||
|
||||
hplDelete(pXmlDoc);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
} // namespace hpl
|
||||
91
engines/hpl1/engine/ai/AINodeGenerator.h
Normal file
91
engines/hpl1/engine/ai/AINodeGenerator.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2010 - Frictional Games
|
||||
*
|
||||
* This file is part of HPL1 Engine.
|
||||
*/
|
||||
|
||||
#ifndef HPL_AI_NODE_GENERATOR_H
|
||||
#define HPL_AI_NODE_GENERATOR_H
|
||||
|
||||
#include "hpl1/engine/game/GameTypes.h"
|
||||
#include "hpl1/engine/system/SystemTypes.h"
|
||||
|
||||
#include "hpl1/engine/physics/PhysicsWorld.h"
|
||||
#include "hpl1/engine/scene/World3D.h"
|
||||
|
||||
namespace hpl {
|
||||
|
||||
class cWorld3D;
|
||||
|
||||
class cCollideRayCallback : public iPhysicsRayCallback {
|
||||
public:
|
||||
virtual ~cCollideRayCallback() {}
|
||||
bool OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams);
|
||||
|
||||
bool mbIntersected;
|
||||
cVector3f mvPos;
|
||||
float mfDist;
|
||||
};
|
||||
|
||||
//-------------------------------
|
||||
|
||||
class cAINodeGeneratorParams {
|
||||
public:
|
||||
cAINodeGeneratorParams();
|
||||
|
||||
tString msNodeType;
|
||||
|
||||
float mfHeightFromGround;
|
||||
float mfMinWallDist;
|
||||
|
||||
cVector3f mvMinPos;
|
||||
cVector3f mvMaxPos;
|
||||
|
||||
float mfGridSize;
|
||||
};
|
||||
|
||||
//-------------------------------
|
||||
|
||||
class cAINodeGenerator : public iPhysicsRayCallback {
|
||||
public:
|
||||
cAINodeGenerator();
|
||||
virtual ~cAINodeGenerator();
|
||||
|
||||
void Generate(cWorld3D *apWorld, cAINodeGeneratorParams *apParams);
|
||||
|
||||
private:
|
||||
bool OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams);
|
||||
|
||||
void SaveToFile();
|
||||
void LoadFromFile();
|
||||
|
||||
cAINodeGeneratorParams *mpParams;
|
||||
cWorld3D *mpWorld;
|
||||
tTempAiNodeList *mpNodeList;
|
||||
cCollideRayCallback _rayCallback;
|
||||
};
|
||||
|
||||
} // namespace hpl
|
||||
|
||||
#endif // HPL_AI_NODE_GENERATOR_H
|
||||
323
engines/hpl1/engine/ai/AStar.cpp
Normal file
323
engines/hpl1/engine/ai/AStar.cpp
Normal file
@@ -0,0 +1,323 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2010 - Frictional Games
|
||||
*
|
||||
* This file is part of HPL1 Engine.
|
||||
*/
|
||||
|
||||
#include "hpl1/engine/ai/AStar.h"
|
||||
|
||||
#include "hpl1/engine/ai/AINodeContainer.h"
|
||||
|
||||
#include "hpl1/engine/math/Math.h"
|
||||
|
||||
#include "hpl1/engine/system/low_level_system.h"
|
||||
|
||||
namespace hpl {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// NODE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAStarNode::cAStarNode(cAINode *apAINode) {
|
||||
mpParent = NULL;
|
||||
mpAINode = apAINode;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool cAStarNodeCompare::operator()(cAStarNode *apNodeA, cAStarNode *apNodeB) const {
|
||||
return apNodeA->mpAINode < apNodeB->mpAINode;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CONSTRUCTORS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAStarHandler::cAStarHandler(cAINodeContainer *apContainer) {
|
||||
mlMaxIterations = -1;
|
||||
|
||||
mpContainer = apContainer;
|
||||
|
||||
mpCallback = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAStarHandler::~cAStarHandler() {
|
||||
STLDeleteAll(m_setClosedList);
|
||||
STLDeleteAll(m_setOpenList);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC METHODS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool cAStarHandler::GetPath(const cVector3f &avStart, const cVector3f &avGoal, tAINodeList *apNodeList) {
|
||||
/////////////////////////////////////////////////
|
||||
// check if there is free path from start to goal
|
||||
if (mpContainer->FreePath(avStart, avGoal, 3)) {
|
||||
mpGoalNode = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Reset all variables
|
||||
// These should just be cleared and pools used instead.
|
||||
STLDeleteAll(m_setClosedList);
|
||||
STLDeleteAll(m_setOpenList);
|
||||
m_setGoalNodes.clear();
|
||||
mpGoalNode = NULL;
|
||||
|
||||
// Set goal position
|
||||
mvGoal = avGoal;
|
||||
|
||||
float fMaxHeight = mpContainer->GetMaxHeight() * 1.5f;
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Find nodes reachable from the start and goal position (use double 2*2 distance)
|
||||
float fMaxDist = mpContainer->GetMaxEdgeDistance() * 2; // float fMaxDist = mpContainer->GetMaxEdgeDistance()*mpContainer->GetMaxEdgeDistance()*4;
|
||||
|
||||
/////////////////////
|
||||
// Check with Start
|
||||
// Log(" Get Start\n");
|
||||
cAINodeIterator startNodeIt = mpContainer->GetNodeIterator(avStart, fMaxDist);
|
||||
while (startNodeIt.HasNext()) {
|
||||
cAINode *pAINode = startNodeIt.Next();
|
||||
// Log("Check node: %s\n",pAINode->GetName().c_str());
|
||||
|
||||
float fHeight = fabs(avStart.y - pAINode->GetPosition().y);
|
||||
float fDist = cMath::Vector3Dist(avStart, pAINode->GetPosition()); // float fDist = cMath::Vector3DistSqr(avStart,pAINode->GetPosition());
|
||||
if (fDist < fMaxDist && fHeight <= fMaxHeight) {
|
||||
// Check if path is clear
|
||||
if (mpContainer->FreePath(avStart, pAINode->GetPosition(), -1,
|
||||
eAIFreePathFlag_SkipDynamic)) {
|
||||
AddOpenNode(pAINode, NULL, fDist);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Log(" Found start\n");
|
||||
|
||||
////////////////////////////////
|
||||
// Check with Goal
|
||||
// Log(" Get Goal\n");
|
||||
cAINodeIterator goalNodeIt = mpContainer->GetNodeIterator(avGoal, fMaxDist);
|
||||
while (goalNodeIt.HasNext()) {
|
||||
cAINode *pAINode = goalNodeIt.Next();
|
||||
// Log("Check node: %s\n",pAINode->GetName().c_str());
|
||||
|
||||
float fHeight = fabs(avGoal.y - pAINode->GetPosition().y);
|
||||
float fDist = cMath::Vector3Dist(avGoal, pAINode->GetPosition()); // fDist = cMath::Vector3DistSqr(avGoal,pAINode->GetPosition());
|
||||
if (fDist < fMaxDist && fHeight <= fMaxHeight) {
|
||||
// Check if path is clear
|
||||
if (mpContainer->FreePath(avGoal, pAINode->GetPosition(), 3)) {
|
||||
m_setGoalNodes.insert(pAINode);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Log(" Found goal\n");
|
||||
|
||||
/*for(int i=0; i<mpContainer->GetNodeNum(); ++i)
|
||||
{
|
||||
cAINode *pAINode = mpContainer->GetNode(i);
|
||||
|
||||
////////////////////////////////
|
||||
//Check with Start
|
||||
float fHeight = fabs(avStart.y - pAINode->GetPosition().y);
|
||||
float fDist = cMath::Vector3Dist(avStart,pAINode->GetPosition());
|
||||
//float fDist = cMath::Vector3DistSqr(avStart,pAINode->GetPosition());
|
||||
if(fDist < fMaxDist && fHeight <= fMaxHeight)
|
||||
{
|
||||
//Check if path is clear
|
||||
if(mpContainer->FreePath(avStart,pAINode->GetPosition(),-1))
|
||||
{
|
||||
AddOpenNode(pAINode,NULL,fDist);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//Check with Goal
|
||||
fHeight = fabs(avGoal.y - pAINode->GetPosition().y);
|
||||
fDist = cMath::Vector3Dist(avGoal,pAINode->GetPosition());
|
||||
//fDist = cMath::Vector3DistSqr(avGoal,pAINode->GetPosition());
|
||||
if(fDist < fMaxDist && fHeight <= fMaxHeight)
|
||||
{
|
||||
//Check if path is clear
|
||||
if(mpContainer->FreePath(avGoal,pAINode->GetPosition(),3))
|
||||
{
|
||||
m_setGoalNodes.insert(pAINode);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Iterate the algorithm
|
||||
IterateAlgorithm();
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Check if goal was found, if so build path.
|
||||
if (mpGoalNode) {
|
||||
if (apNodeList) {
|
||||
cAStarNode *pParentNode = mpGoalNode;
|
||||
while (pParentNode != NULL) {
|
||||
apNodeList->push_back(pParentNode->mpAINode);
|
||||
pParentNode = pParentNode->mpParent;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE METHODS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAStarHandler::IterateAlgorithm() {
|
||||
int lIterationCount = 0;
|
||||
while (m_setOpenList.empty() == false && (mlMaxIterations < 0 || lIterationCount < mlMaxIterations)) {
|
||||
cAStarNode *pNode = GetBestNode();
|
||||
cAINode *pAINode = pNode->mpAINode;
|
||||
|
||||
//////////////////////
|
||||
// Check if current node can reach goal
|
||||
if (IsGoalNode(pAINode)) {
|
||||
mpGoalNode = pNode;
|
||||
break;
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Add nodes connected to current
|
||||
int lEdgeCount = pAINode->GetEdgeNum();
|
||||
for (int i = 0; i < lEdgeCount; ++i) {
|
||||
cAINodeEdge *pEdge = pAINode->GetEdge(i);
|
||||
|
||||
if (mpCallback == NULL || mpCallback->CanAddNode(pAINode, pEdge->mpNode)) {
|
||||
AddOpenNode(pEdge->mpNode, pNode, pNode->mfDistance + pEdge->mfDistance);
|
||||
// AddOpenNode(pEdge->mpNode, pNode, pNode->mfDistance + pEdge->mfSqrDistance);
|
||||
}
|
||||
}
|
||||
|
||||
++lIterationCount;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cAStarHandler::AddOpenNode(cAINode *apAINode, cAStarNode *apParent, float afDistance) {
|
||||
// TODO: free path check with dynamic objects here.
|
||||
|
||||
// TODO: Some pooling here would be good.
|
||||
cAStarNode *pNode = hplNew(cAStarNode, (apAINode));
|
||||
|
||||
// Check if it is in closed list.
|
||||
tAStarNodeSetIt it = m_setClosedList.find(pNode);
|
||||
if (it != m_setClosedList.end()) {
|
||||
hplDelete(pNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add it if it wasn't already inserted
|
||||
const auto test = m_setOpenList.find(pNode);
|
||||
if (test != m_setOpenList.end()) {
|
||||
hplDelete(pNode);
|
||||
return;
|
||||
}
|
||||
m_setOpenList.insert(pNode);
|
||||
|
||||
pNode->mfDistance = afDistance;
|
||||
pNode->mfCost = Cost(afDistance, apAINode, apParent) + Heuristic(pNode->mpAINode->GetPosition(), mvGoal);
|
||||
pNode->mpParent = apParent;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cAStarNode *cAStarHandler::GetBestNode() {
|
||||
tAStarNodeSetIt it = m_setOpenList.begin();
|
||||
tAStarNodeSetIt bestIt = it;
|
||||
cAStarNode *pBestNode = *it;
|
||||
++it;
|
||||
|
||||
// Iterate open list and find the best node.
|
||||
for (; it != m_setOpenList.end(); ++it) {
|
||||
cAStarNode *pNode = *it;
|
||||
if (pBestNode->mfCost > pNode->mfCost) {
|
||||
pBestNode = pNode;
|
||||
bestIt = it;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove node from open
|
||||
m_setOpenList.erase(bestIt);
|
||||
|
||||
// Add to closed list
|
||||
m_setClosedList.insert(pBestNode);
|
||||
|
||||
return pBestNode;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
float cAStarHandler::Cost(float afDistance, cAINode *apAINode, cAStarNode *apParent) {
|
||||
if (apParent) {
|
||||
float fHeight = (1 + fabs(apAINode->GetPosition().y - apParent->mpAINode->GetPosition().y));
|
||||
return afDistance * fHeight;
|
||||
} else
|
||||
return afDistance;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
float cAStarHandler::Heuristic(const cVector3f &avStart, const cVector3f &avGoal) {
|
||||
// return cMath::Vector3DistSqr(avStart, avGoal);
|
||||
return cMath::Vector3Dist(avStart, avGoal);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool cAStarHandler::IsGoalNode(cAINode *apAINode) {
|
||||
tAINodeSetIt it = m_setGoalNodes.find(apAINode);
|
||||
if (it == m_setGoalNodes.end())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
} // namespace hpl
|
||||
128
engines/hpl1/engine/ai/AStar.h
Normal file
128
engines/hpl1/engine/ai/AStar.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2010 - Frictional Games
|
||||
*
|
||||
* This file is part of HPL1 Engine.
|
||||
*/
|
||||
|
||||
#ifndef HPL_A_STAR_H
|
||||
#define HPL_A_STAR_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "hpl1/engine/game/GameTypes.h"
|
||||
#include "hpl1/engine/math/MathTypes.h"
|
||||
#include "hpl1/engine/system/SystemTypes.h"
|
||||
|
||||
namespace hpl {
|
||||
|
||||
class cAINodeContainer;
|
||||
class cAINode;
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
typedef Hpl1::Std::set<cAINode *> tAINodeSet;
|
||||
typedef tAINodeSet::iterator tAINodeSetIt;
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
typedef Common::List<cAINode *> tAINodeList;
|
||||
typedef tAINodeList::iterator tAINodeListIt;
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
class cAStarNode {
|
||||
public:
|
||||
cAStarNode(cAINode *apAINode);
|
||||
|
||||
float mfCost;
|
||||
float mfDistance;
|
||||
|
||||
cAStarNode *mpParent;
|
||||
cAINode *mpAINode;
|
||||
};
|
||||
|
||||
class cAStarNodeCompare {
|
||||
public:
|
||||
bool operator()(cAStarNode *apNodeA, cAStarNode *apNodeB) const;
|
||||
};
|
||||
|
||||
typedef Hpl1::Std::set<cAStarNode *, cAStarNodeCompare> tAStarNodeSet;
|
||||
typedef tAStarNodeSet::iterator tAStarNodeSetIt;
|
||||
|
||||
//--------------------------------------
|
||||
class cAStarHandler;
|
||||
|
||||
class iAStarCallback {
|
||||
public:
|
||||
virtual ~iAStarCallback() {}
|
||||
|
||||
virtual bool CanAddNode(cAINode *apParentNode, cAINode *apChildNode) = 0;
|
||||
};
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
class cAStarHandler {
|
||||
public:
|
||||
cAStarHandler(cAINodeContainer *apContainer);
|
||||
~cAStarHandler();
|
||||
|
||||
bool GetPath(const cVector3f &avStart, const cVector3f &avGoal, tAINodeList *apNodeList);
|
||||
|
||||
/**
|
||||
* Set max number of times the algorithm is iterated.
|
||||
* \param alX -1 = until OpenList is empty
|
||||
*/
|
||||
void SetMaxIterations(int alX) { mlMaxIterations = alX; }
|
||||
|
||||
void SetCallback(iAStarCallback *apCallback) { mpCallback = apCallback; }
|
||||
|
||||
private:
|
||||
void IterateAlgorithm();
|
||||
|
||||
void AddOpenNode(cAINode *apAINode, cAStarNode *apParent, float afDistance);
|
||||
|
||||
cAStarNode *GetBestNode();
|
||||
|
||||
float Cost(float afDistance, cAINode *apAINode, cAStarNode *apParent);
|
||||
float Heuristic(const cVector3f &avStart, const cVector3f &avGoal);
|
||||
|
||||
bool IsGoalNode(cAINode *apAINode);
|
||||
|
||||
cVector3f mvGoal;
|
||||
|
||||
cAStarNode *mpGoalNode;
|
||||
tAINodeSet m_setGoalNodes;
|
||||
|
||||
cAINodeContainer *mpContainer;
|
||||
|
||||
int mlMaxIterations;
|
||||
|
||||
iAStarCallback *mpCallback;
|
||||
|
||||
tAStarNodeSet m_setOpenList;
|
||||
tAStarNodeSet m_setClosedList;
|
||||
};
|
||||
|
||||
} // namespace hpl
|
||||
|
||||
#endif // HPL_A_STAR_H
|
||||
143
engines/hpl1/engine/ai/StateMachine.cpp
Normal file
143
engines/hpl1/engine/ai/StateMachine.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2010 - Frictional Games
|
||||
*
|
||||
* This file is part of HPL1 Engine.
|
||||
*/
|
||||
|
||||
#include "hpl1/engine/ai/StateMachine.h"
|
||||
|
||||
#include "hpl1/engine/system/low_level_system.h"
|
||||
|
||||
namespace hpl {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// STATE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
iAIState::iAIState() {
|
||||
mfTimeCount = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void iAIState::Sleep(float afTime) {
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void iAIState::Update(float afTime) {
|
||||
// Update according to the time step
|
||||
mfTimeCount += afTime;
|
||||
while (mfTimeCount >= mfUpdateStep) {
|
||||
OnUpdate(mfUpdateStep);
|
||||
mfTimeCount -= mfUpdateStep;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CONSTRUCTORS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cStateMachine::cStateMachine() {
|
||||
mbActive = true;
|
||||
|
||||
mpCurrentState = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
cStateMachine::~cStateMachine() {
|
||||
STLMapDeleteAll(m_mapStates);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC METHODS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cStateMachine::Update(float afTime) {
|
||||
if (mbActive == false || mpCurrentState == NULL)
|
||||
return;
|
||||
|
||||
mpCurrentState->Update(afTime);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cStateMachine::AddState(iAIState *apState, const tString &asName, int alId, float afUpdateStep) {
|
||||
apState->SetStateMachine(this);
|
||||
apState->mlId = alId;
|
||||
apState->msName = asName;
|
||||
apState->mfUpdateStep = afUpdateStep;
|
||||
|
||||
if (m_mapStates.empty())
|
||||
mpCurrentState = apState;
|
||||
|
||||
m_mapStates.insert(tAIStateMap::value_type(alId, apState));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void cStateMachine::ChangeState(int alId) {
|
||||
if (alId == mpCurrentState->GetId())
|
||||
return;
|
||||
|
||||
iAIState *pState = GetState(alId);
|
||||
if (pState == NULL) {
|
||||
Warning("State %d does not exist!\n", alId);
|
||||
return;
|
||||
}
|
||||
|
||||
mpCurrentState->OnLeaveState(pState->GetId());
|
||||
pState->OnEnterState(mpCurrentState == NULL ? -1 : mpCurrentState->GetId());
|
||||
|
||||
mpCurrentState = pState;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
iAIState *cStateMachine::GetState(int alId) {
|
||||
tAIStateMapIt it = m_mapStates.find(alId);
|
||||
if (it == m_mapStates.end())
|
||||
return NULL;
|
||||
|
||||
return it->second;
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
iAIState *cStateMachine::CurrentState() {
|
||||
return mpCurrentState;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
} // namespace hpl
|
||||
108
engines/hpl1/engine/ai/StateMachine.h
Normal file
108
engines/hpl1/engine/ai/StateMachine.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2010 - Frictional Games
|
||||
*
|
||||
* This file is part of HPL1 Engine.
|
||||
*/
|
||||
|
||||
#ifndef HPL_STATE_MACHINE_H
|
||||
#define HPL_STATE_MACHINE_H
|
||||
|
||||
#include "hpl1/engine/game/GameTypes.h"
|
||||
#include "hpl1/engine/system/SystemTypes.h"
|
||||
|
||||
#include "common/stablemap.h"
|
||||
|
||||
namespace hpl {
|
||||
|
||||
//-----------------------------------------
|
||||
class cStateMachine;
|
||||
|
||||
class iAIState {
|
||||
friend class cStateMachine;
|
||||
|
||||
public:
|
||||
iAIState();
|
||||
virtual ~iAIState() {}
|
||||
|
||||
virtual void OnUpdate(float afTime) = 0;
|
||||
|
||||
virtual void OnEnterState(int alLastState) = 0;
|
||||
virtual void OnLeaveState(int alNextState) = 0;
|
||||
|
||||
int GetId() { return mlId; }
|
||||
const tString &GetName() { return msName; }
|
||||
float GetUpdateStep() { return mfUpdateStep; }
|
||||
|
||||
void Sleep(float afTime);
|
||||
|
||||
protected:
|
||||
int mlId;
|
||||
tString msName;
|
||||
float mfUpdateStep;
|
||||
cStateMachine *mpStateMachine;
|
||||
|
||||
private:
|
||||
void Update(float afTime);
|
||||
void SetStateMachine(cStateMachine *apStateMachine) { mpStateMachine = apStateMachine; }
|
||||
|
||||
float mfTimeCount;
|
||||
};
|
||||
|
||||
typedef Common::StableMap<int, iAIState *> tAIStateMap;
|
||||
typedef tAIStateMap::iterator tAIStateMapIt;
|
||||
|
||||
//-----------------------------------------
|
||||
|
||||
class cStateMachine {
|
||||
public:
|
||||
cStateMachine();
|
||||
virtual ~cStateMachine();
|
||||
|
||||
void Update(float afTime);
|
||||
|
||||
/**
|
||||
* Adds a new state to the state machine. The state machine will destroy them when deleted.
|
||||
*/
|
||||
void AddState(iAIState *apState, const tString &asName, int alId, float afUpdateStep);
|
||||
|
||||
void ChangeState(int alId);
|
||||
|
||||
void SetActive(bool abX) { mbActive = abX; }
|
||||
bool IsActive() { return mbActive; }
|
||||
|
||||
iAIState *GetState(int alId);
|
||||
|
||||
iAIState *CurrentState();
|
||||
|
||||
private:
|
||||
bool mbActive;
|
||||
|
||||
tAIStateMap m_mapStates;
|
||||
|
||||
iAIState *mpCurrentState;
|
||||
};
|
||||
|
||||
} // namespace hpl
|
||||
|
||||
#endif // HPL_STATE_MACHINE_H
|
||||
Reference in New Issue
Block a user