Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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