khanat-code-old/code/ryzom/server/src/ai_service/continent.cpp

2494 lines
64 KiB
C++
Raw Normal View History

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdpch.h"
#include "nel/misc/string_mapper.h"
#include "nel/net/service.h"
#include "algorithm"
#include "continent.h"
#include "ai_instance.h"
#include "ai_mgr.h"
#include "ai_grp_fauna.h"
#include "ai_mgr_fauna.h"
#include "ai_grp_npc.h"
#include "ai_mgr_npc.h"
#include "ai_bot_npc.h"
#include "group_profile.h"
//#include "family_behavior.h"
#include "family_profile_tribe.h"
#include "continent_inline.h"
using namespace MULTI_LINE_FORMATER;
using namespace std;
using namespace NLMISC;
using namespace NLNET;
using namespace RYAI_MAP_CRUNCH;
using namespace AITYPES;
CVariable<bool> LogScorerScores("ai", "LogScorerScores", "", false, 0, true);
//////////////////////////////////////////////////////////////////////////////
// CFaunaZone //
//////////////////////////////////////////////////////////////////////////////
CFaunaZone::CFaunaZone(CCell *owner, CAIAliasDescriptionNode *adn)
: CAIPlaceXYR(owner, adn)
{
// nldebug("Creating fauna zone '%s', alias %u", getName().c_str(), getAlias());
}
CFaunaZone::~CFaunaZone()
{
// nldebug("Deleting fauna zone '%s', alias %u", getName().c_str(), getAlias());
}
CCell* CFaunaZone::getOwner() const
{
return static_cast<CCell*>(CAIPlaceXYR::getOwner());
}
//////////////////////////////////////////////////////////////////////////////
// CNpcZone //
//////////////////////////////////////////////////////////////////////////////
CNpcZone::~CNpcZone()
{
unlinkNpcZone();
}
void CNpcZone::unlinkNpcZone()
{
while (!_Roads.empty())
{
CRoad *road = _Roads.back();
if (road->startZone().ptr() == this)
{
road->setStartZone(0);
}
else if (road->endZone().ptr() == this)
{
road->setEndZone(0);
}
else
{
nlassert(false);
}
_Roads.pop_back();
}
}
float CNpcZone::getFreeAreaScore() const
{
const float area = getArea();
return area/((float)getNbUse()+0.001f);
}
std::string CNpcZone::getIndexString() const
{
return getOwner()->getIndexString()+NLMISC::toString(":nz%d", getIndex());
}
AITYPES::CPropertySet& CNpcZone::properties()
{
return _Properties;
}
const AITYPES::CPropertySet& CNpcZone::properties() const
{
return _Properties;
}
std::vector<NLMISC::CDbgPtr<CRoad> >& CNpcZone::roads()
{
return _Roads;
}
uint32 CNpcZone::getNbUse() const
{
const sint nbUse = getRefCount() - 1; // -1 for the container
#ifdef NL_DEBUG
nlassert(nbUse>=0);
#endif
return nbUse;
}
//////////////////////////////////////////////////////////////////////////////
// CNpcZonePlace //
//////////////////////////////////////////////////////////////////////////////
CNpcZonePlace::CNpcZonePlace(CCell *owner, CAIAliasDescriptionNode *adn)
: CAIPlace(owner, adn)
{
owner->getOwner()->getAIInstance()->addZone(getAliasFullName(), this);
// nldebug("Creating npc zone '%s', alias %u", getName().c_str(), getAlias());
}
CNpcZonePlace::~CNpcZonePlace()
{
getOwner()->getOwner()->getAIInstance()->removeZone(getAliasFullName(), this);
// nldebug("Deleting npc zone '%s', alias %u", getName().c_str(), getAlias());
}
CCell* CNpcZonePlace::getOwner() const
{
return static_cast<CCell*>(CAIPlace::getOwner());
}
const CAliasTreeOwner& CNpcZonePlace::getAliasTreeOwner() const
{
return *this;
}
uint32 CNpcZonePlace::getIndex() const
{
return getIndex();
}
CPlaceRandomPos& CNpcZonePlace::getPlaceRandomPos()
{
return *this;
}
const CPlaceRandomPos& CNpcZonePlace::getPlaceRandomPos() const
{
return *this;
}
bool CNpcZonePlace::atPlace(const CAIVector& pos) const
{
return (pos-_pos).sqrnorm() <= ((double)_radius*(double)_radius);
}
bool CNpcZonePlace::atPlace(const CAIVectorMirror& pos) const
{
return (pos-_pos).sqrnorm() <= ((double)_radius*(double)_radius);
}
bool CNpcZonePlace::atPlace(CAIEntityPhysical const* entity) const
{
return atPlace(entity->pos());
}
const CAIPos& CNpcZonePlace::midPos() const
{
return _pos;
}
float CNpcZonePlace::getArea() const
{
const float radius = getRadius();
return (float)(radius*radius*NLMISC::Pi);
}
const RYAI_MAP_CRUNCH::CWorldPosition& CNpcZonePlace::worldValidPos() const
{
return _worldValidPos;
}
float CNpcZonePlace::getRadius() const
{
return _radius;
}
void CNpcZonePlace::display(CStringWriter& stringWriter) const
{
// :TODO: Implement that method
nlassert(false && "not yet implemented!");
}
AITYPES::TVerticalPos CNpcZonePlace::getVerticalPos() const
{
return CPlaceRandomPos::getVerticalPos();
}
void CNpcZonePlace::getRandomPos(RYAI_MAP_CRUNCH::CWorldPosition& pos) const
{
CPlaceRandomPos::getRandomPos(pos);
}
void CNpcZonePlace::setPosAndRadius(AITYPES::TVerticalPos verticalPos, const CAIPos& pos, uint32 radius)
{
_VerticalPos = verticalPos;
_pos = pos;
_radius = float(radius)/1000.0f;
#ifdef NL_DEBUG
nlassert(_radius > 0);
nlassert(pos.x()!=0||pos.y()!=0);
#endif
if ( pos.x()==0
&& pos.y()==0)
{
nlwarning("Null place Position for %s", getAliasFullName().c_str());
}
if (!CWorldContainer::calcNearestWPosFromPosAnRadius(_VerticalPos, _worldValidPos, _pos, _radius, 1000, CWorldContainer::CPosValidatorDefault()))
{
if (LogAcceptablePos)
nlwarning("Unvalid place (no collision free position found) at %d %d ", _pos.x().asInt(), _pos.y().asInt());
}
buildRandomPos(_worldValidPos, _radius);
}
bool CNpcZonePlace::calcRandomPos(CAIPos& pos) const
{
double dx, dy;
const double r = (double)_radius;
const double rSquare = r*r;
// :TODO: Replace that while with a theta/r rand and a space conversion
do
{
dx = CAIS::frandPlusMinus(r);
dy = CAIS::frandPlusMinus(r);
}
while (dx*dx+dy*dy>rSquare);
pos.setX(_pos.x()+dx);
pos.setY(_pos.y()+dy);
pos.setH(_pos.h());
pos.setTheta(pos.angleTo(_pos)+CAngle(NLMISC::Pi/2));
return true;
}
//////////////////////////////////////////////////////////////////////////////
// CNpcZonePlaceNoPrim //
//////////////////////////////////////////////////////////////////////////////
CNpcZonePlaceNoPrim::CNpcZonePlaceNoPrim()
{
}
CNpcZonePlaceNoPrim::~CNpcZonePlaceNoPrim()
{
}
bool CNpcZonePlaceNoPrim::atPlace(const CAIVector& pos) const
{
return (pos-_Pos).sqrnorm() <= ((double)_Radius*(double)_Radius);
}
bool CNpcZonePlaceNoPrim::atPlace(const CAIVectorMirror& pos) const
{
return (pos-_Pos).sqrnorm() <= ((double)_Radius*(double)_Radius);
}
bool CNpcZonePlaceNoPrim::atPlace(CAIEntityPhysical const* entity) const
{
return atPlace(entity->pos());
}
const CAIPos& CNpcZonePlaceNoPrim::midPos() const
{
return _Pos;
}
float CNpcZonePlaceNoPrim::getArea() const
{
return (float)(_Radius*_Radius*NLMISC::Pi);
}
const RYAI_MAP_CRUNCH::CWorldPosition& CNpcZonePlaceNoPrim::worldValidPos() const
{
return _WorldValidPos;
}
float CNpcZonePlaceNoPrim::getRadius() const
{
return _Radius;
}
void CNpcZonePlaceNoPrim::display(CStringWriter& stringWriter) const
{
// :TODO: Implement that method
nlassert(false && "not yet implemented!");
}
AITYPES::TVerticalPos CNpcZonePlaceNoPrim::getVerticalPos() const
{
return AITYPES::vp_auto;
}
void CNpcZonePlaceNoPrim::getRandomPos(RYAI_MAP_CRUNCH::CWorldPosition& pos) const
{
uint maxTries = RandomPosMaxRetry;
CAIPos dummyPos;
bool foundRandomPos = false;
while (!foundRandomPos)
{
if (calcRandomPos(dummyPos))
foundRandomPos = CWorldContainer::getWorldMap().setWorldPosition(_VerticalPos, pos, dummyPos);
--maxTries;
if (maxTries<=0)
break;
}
if (!foundRandomPos)
{
if (!CWorldContainer::calcNearestWPosFromPosAnRadius(_VerticalPos, pos, _WorldValidPos, _Radius, 1000, CWorldContainer::CPosValidatorDefault()))
{
if (LogAcceptablePos)
nlwarning("Unvalid place (no collision free position found) at %d %d", _WorldValidPos.toAIVector().x().asInt(), _WorldValidPos.toAIVector().y().asInt());
}
}
}
void CNpcZonePlaceNoPrim::setPosAndRadius(AITYPES::TVerticalPos verticalPos, const CAIPos& pos, uint32 radius)
{
#ifdef NL_DEBUG
nlassert(radius > 0);
nlassert(pos.x()!=0||pos.y()!=0);
#endif
_VerticalPos = verticalPos;
_Pos = pos;
_Radius = float(radius)/1000.0f;
if (!CWorldContainer::calcNearestWPosFromPosAnRadius(_VerticalPos, _WorldValidPos, _Pos, _Radius, 1000, CWorldContainer::CPosValidatorDefault()))
{
if (LogAcceptablePos)
nlwarning("Unvalid place (no collision free position found) at %d %d ", _Pos.x().asInt(), _Pos.y().asInt());
}
}
bool CNpcZonePlaceNoPrim::calcRandomPos(CAIPos& pos) const
{
double dx, dy;
const double r = (double)_Radius;
const double rSquare = r*r;
// :TODO: Replace that while with a theta/r rand and a space conversion
do
{
dx = CAIS::frandPlusMinus(r);
dy = CAIS::frandPlusMinus(r);
}
while (dx*dx+dy*dy>rSquare);
pos.setX(_Pos.x()+dx);
pos.setY(_Pos.y()+dy);
pos.setH(_Pos.h());
pos.setTheta(pos.angleTo(_Pos)+CAngle(NLMISC::Pi/2));
return true;
}
//////////////////////////////////////////////////////////////////////////////
// CNpcZoneShape //
//////////////////////////////////////////////////////////////////////////////
CNpcZoneShape::CNpcZoneShape(CCell* owner, CAIAliasDescriptionNode* adn)
: CAIPlace(owner, adn)
{
owner->getOwner()->getAIInstance()->addZone(getAliasFullName(), this);
// nldebug("Creating npc zone '%s', alias %u", getName().c_str(), getAlias());
}
CNpcZoneShape::~CNpcZoneShape()
{
getOwner()->getOwner()->getAIInstance()->removeZone(getAliasFullName(), this);
// nldebug("Deleting npc zone '%s', alias %u", getName().c_str(), getAlias());
}
CCell* CNpcZoneShape::getOwner() const
{
return static_cast<CCell*>(CAIPlace::getOwner());
}
const CAliasTreeOwner& CNpcZoneShape::getAliasTreeOwner() const
{
return *this;
}
uint32 CNpcZoneShape::getIndex() const
{
return getIndex();
}
CPlaceRandomPos& CNpcZoneShape::getPlaceRandomPos()
{
return this->_shape;
}
const CPlaceRandomPos& CNpcZoneShape::getPlaceRandomPos() const
{
return this->_shape;
}
bool CNpcZoneShape::atPlace(const CAIVector& pos) const
{
return _shape.contains(pos);
}
bool CNpcZoneShape::atPlace(const CAIVectorMirror &pos) const
{
return _shape.contains(pos);
}
bool CNpcZoneShape::atPlace(CAIEntityPhysical const* entity) const
{
return atPlace(entity->pos());
}
const CAIPos& CNpcZoneShape::midPos() const
{
return _midPos;
}
float CNpcZoneShape::getArea() const
{
const float area = 20.0f;
nlwarning("CNpcZoneShape area asked although it's a fake value.");
return area;
}
const RYAI_MAP_CRUNCH::CWorldPosition& CNpcZoneShape::worldValidPos() const
{
return _worldValidPos;
}
float CNpcZoneShape::getRadius() const
{
const float radius = 1.0f;
return radius;
}
void CNpcZoneShape::display(CStringWriter& stringWriter) const
{
// :TODO: Implement that method
nlassert(false && "not yet implemented!");
}
AITYPES::TVerticalPos CNpcZoneShape::getVerticalPos() const
{
return _shape.getVerticalPos();
}
void CNpcZoneShape::getRandomPos(RYAI_MAP_CRUNCH::CWorldPosition& pos) const
{
_shape.getRandomPos(pos);
}
void CNpcZoneShape::setPatat(AITYPES::TVerticalPos verticalPos, const std::vector<CAIVector>& points)
{
if (!_shape.setPatat(verticalPos, points))
{
nlwarning("CNpcZoneShape::setPatat: error while placing the points of '%s'",
getAliasFullName().c_str());
}
buildMidPos();
}
void CNpcZoneShape::buildMidPos()
{
//nlassert(false && "build appropriate _midPos");
int count = 0;
bool succeeded = _shape.calcRandomPos(_midPos);
while (!succeeded && count<1000)
{
succeeded = _shape.calcRandomPos(_midPos);
++count;
}
}
//////////////////////////////////////////////////////////////////////////////
// CCell //
//////////////////////////////////////////////////////////////////////////////
std::string CCell::getIndexString() const
{
return getOwner()->getIndexString()+NLMISC::toString(":ce%d", getChildIndex());
}
void CCell::connectRoads()
{
CContinent &continent=*getOwner()->getOwner()->getOwner();
vector<CCell*> neighBourgCellList;
getNeighBourgCellList(neighBourgCellList);
for (uint i=0; i<_Roads.size(); ++i)
{
CRoad *road = roads()[i];
if (!road)
continue;
road->calcLength ();
// clear existing link;
road->unlinkRoad();
if (road->coords().size() > 1)
{
// ok, there are 2 points, try to found the zone they belong to
// starting zone try first in the same CCell, after in the neighbourg, and last in the whole continent.
CNpcZone *nz = findNpcZone(road->getLogicStart());
if (!nz)
{
FOREACH(itCell, vector<CCell*>, neighBourgCellList)
{
nz=(*itCell)->findNpcZone(road->getLogicStart());
if (nz)
break;
}
}
if (!nz)
nz = continent.findNpcZone(road->getLogicStart());
if (nz)
{
// ok, we found the first zone
road->setStartZone(nz);
nz->roads().push_back(road);
}
else
{
//#if !FINAL_VERSION
nlwarning("Road '%s'%s end don't reach a zone at %s",
road->getAliasFullName().c_str(),
road->getAliasString().c_str(),
road->getLogicStart().toString().c_str());
//#endif
}
// ending zone
nz = findNpcZone(road->getLogicEnd());
if (!nz)
{
FOREACH(itCell, vector<CCell*>, neighBourgCellList)
{
nz=(*itCell)->findNpcZone(road->getLogicEnd());
if (nz)
break;
}
}
if (!nz)
nz = continent.findNpcZone(road->getLogicEnd());
if (nz)
{
// ok, we found the first zone
road->setEndZone(nz);
nz->roads().push_back(road);
}
else
{
//#ifndef FINAL_VERSION
nlwarning("Road '%s'%s: end don't reach a zone at %s",
road->getAliasFullName().c_str(),
road->getAliasString().c_str(),
road->getLogicEnd().toString().c_str());
//#endif
}
}
else
{
//#ifndef FINAL_VERSION
if (road->coords().size()==1)
{
nlwarning("Road '%s'%s: only one point at %s",
road->getAliasFullName().c_str(),
road->getAliasString().c_str(),
road->getLogicStart().toString().c_str());
}
//#endif
}
}
}
//////////////////////////////////////////////////////////////////////////////
// CContinent //
//////////////////////////////////////////////////////////////////////////////
CContinent::~CContinent()
{
// first, despawn all groups in familyBehaviors managers
FOREACH(itRegion, CAliasCont<CRegion>, _Regions)
{
FOREACH(itCZ, CAliasCont<CCellZone>, itRegion->cellZones())
{
FOREACH(itFB, CCont<CFamilyBehavior>, itCZ->familyBehaviors())
{
// unspawn in the npc manager
CManager *mgrNpc = itFB->mgrNpc();
mgrNpc->groups().setChildSize(0);
CManager *mgrFauna = itFB->mgrFauna();
mgrFauna->groups().setChildSize(0);
}
}
}
}
IAliasCont* CContinent::getAliasCont(TAIType type)
{
switch(type)
{
case AITypeDynamicRegion:
return &_Regions;
case AITypeOutpost:
return &_Outposts;
default:
return NULL;
}
}
std::string CContinent::getIndexString() const
{
return getOwner()->getIndexString()+NLMISC::toString(":c%u", getChildIndex());
}
std::string CContinent::getOneLineInfoString() const
{
return std::string("Continent '") + getName() + "'";
}
std::vector<std::string> CContinent::getMultiLineInfoString() const
{
std::vector<std::string> container;
pushTitle(container, "CContinent");
pushEntry(container, "id=" + getIndexString());
container.back() += " name=" + getName();
pushEntry(container, "fullname=" + getFullName());
pushFooter(container);
return container;
}
CNpcZone* CContinent::findNpcZone(CAIVector const& posInside)
{
for (CCont<CRegion>::iterator itRegion=_Regions.begin(), itEndRegion=_Regions.end(); itRegion!=itEndRegion; ++itRegion)
{
for (CCont<CCellZone>::iterator itCellZone=itRegion->cellZones().begin(), itEndCellZone=itRegion->cellZones().end(); itCellZone!=itEndCellZone; ++itCellZone)
{
for (CCont<CCell>::iterator itCell=itCellZone->cells().begin(), itEndCell=itCellZone->cells().end(); itCell!=itEndCell; ++itCell)
{
CNpcZone* npcZone = itCell->findNpcZone(posInside);
if (npcZone)
return npcZone;
}
}
}
// no zone match the position !
return NULL;
}
bool CContinent::markTagForDelete(NLMISC::TStringId fileId)
{
CAliasTreeRoot::CMarkTagForDelete const deleteMarker(fileId);
for_each(_Regions.begin(),_Regions.end(),deleteMarker);
for_each(_Outposts.begin(),_Outposts.end(),deleteMarker);
return true;
}
bool CContinent::deleteTaggedAlias(NLMISC::TStringId fileId)
{
for_each(_Regions.begin(),_Regions.end(), CAliasTreeRoot::CDeleteTagged<CRegion> (_Regions));
for_each(_Outposts.begin(),_Outposts.end(), CAliasTreeRoot::CDeleteTagged<COutpost> (_Outposts));
return true;
}
void CContinent::updateLazyProcess()
{
if (_LazyProcess.size()<=0)
return;
FOREACH(lazy, TLazyProcessList, _LazyProcess)
(*lazy)->update();
_LazyProcess.clear();
}
void CContinent::pushLazyProcess(CSmartPtr<CLazyProcess> lazyProcess)
{
FOREACH(lazy, TLazyProcessList, _LazyProcess)
if ((*lazy)->absorb(*lazyProcess))
return;
_LazyProcess.push_back(lazyProcess);
}
void CRebuildContinentAndOutPost::update() const
{
nlinfo ("Build Continent %s dependencies ..", _Continent->getFullName().c_str());
// rebuild the bounding box
_Continent->rebuildBoundingBox();
// rebuild the connectivity graph
FOREACH(region,CAliasCont<CRegion>,_Continent->regions())
region->rebuildConnectivity();
/*
// relocate the outpost into the correct cellZone
FOREACH(outpost, CAliasCont<COutpost>, _Continent->outposts())
{
CCellZone *const czone = _Continent->getOwner()->locateCellZoneForPos(outpost->getPosition());
outpost->setCellZone(czone);
// relink outpost and tribe
_Continent->relinkOutpost();
}
*/
}
void CContinent::update()
{
updateLazyProcess();
{
H_AUTO(RegionsUpdate)
// update all the regions
FOREACH(region,CAliasCont<CRegion>,_Regions)
(*region)->update();
}
{
H_AUTO(OutpostsUpdate)
// update all the outpost
FOREACH(outpost, CAliasCont<COutpost>, _Outposts)
(*outpost)->update();
}
}
void CContinent::rebuildBoundingBox()
{
// build the AABB to speedup the task
_BoundingBox.init();
for (uint i=0; i<_Regions.size(); ++i)
{
CRegion *region = _Regions[i];
if (!region)
continue;
region->rebuildBoundingBox();
_BoundingBox.includeAabb(region->_BoundingBox);
}
}
void CContinent::serviceEvent (const CServiceEvent &info)
{
CCont<CRegion>::iterator itRegion=_Regions.begin(), itRegionEnd=_Regions.end();
while (itRegion!=itRegionEnd)
{
itRegion->serviceEvent(info);
++itRegion;
}
CCont<COutpost>::iterator itOutpost=_Outposts.begin(), itOutpostEnd=_Outposts.end();
while (itOutpost!=itOutpostEnd)
{
itOutpost->serviceEvent(info);
++itOutpost;
}
}
bool CContinent::spawn()
{
// Spawn regions
for (size_t i=0; i<_Regions.size(); ++i)
{
CRegion* region = _Regions[(uint32)i];
if (!region)
continue;
region->spawn();
}
// Spawn outposts
for (size_t i=0; i<_Outposts.size(); ++i)
{
COutpost* outpost = _Outposts[(uint32)i];
if (!outpost)
continue;
outpost->spawn();
}
// We should check individual errors, but fake success here :)
return true;
}
bool CContinent::despawn()
{
// Despawn regions
for (size_t i=0; i<_Regions.size(); ++i)
{
CRegion* region = _Regions[(uint32)i];
if (!region)
continue;
region->despawn();
}
// Despawn outposts
for (size_t i=0; i<_Outposts.size(); ++i)
{
COutpost* outpost = _Outposts[(uint32)i];
if (!outpost)
continue;
outpost->despawn();
}
// We should check individual errors, but fake success here :)
return true;
}
//////////////////////////////////////////////////////////////////////////////
// CRegion //
//////////////////////////////////////////////////////////////////////////////
CRegion::CRegion(CContinent* owner, uint32 alias, std::string const& name, std::string const& filename)
: CAliasChild<CContinent>(owner, alias, name)
, CAliasTreeRoot(filename)
{
}
CRegion::~CRegion()
{
_CellZones.clear();
_GroupFamilies.clear();
nldebug("Deleting region '%s'%s", getName().c_str(), getAliasString().c_str());
}
IAliasCont* CRegion::getAliasCont(TAIType type)
{
switch (type)
{
case AITypeCellZone:
return &_CellZones;
case AITypeGroupFamilyProfileFauna:
case AITypeGroupFamilyProfileTribe:
return &_GroupFamilies;
case AITypeGroupFamilyProfileNpc:
return &_GroupFamilies;
default:
return NULL;
}
}
CAliasTreeOwner* CRegion::createChild(IAliasCont* cont, CAIAliasDescriptionNode* aliasTree)
{
if (!cont)
return NULL;
CAliasTreeOwner* child = NULL;
switch(aliasTree->getType())
{
// create the child and adds it to the corresponding position.
case AITypeCellZone:
child = new CCellZone(this, aliasTree->getAlias(), aliasTree->getName());
break;
case AITypeGroupFamilyProfileFauna:
case AITypeGroupFamilyProfileTribe:
child = new CGroupFamily(this, aliasTree->getAlias(), aliasTree->getName());
break;
case AITypeGroupFamilyProfileNpc:
child = new CGroupFamily(this, aliasTree->getAlias(), aliasTree->getName());
break;
default:
break;
}
if (child)
cont->addAliasChild(child);
return child;
}
std::string CRegion::getIndexString() const
{
return getOwner()->getIndexString()+toString(":r%u", getChildIndex());
}
std::string CRegion::getOneLineInfoString() const
{
return std::string("Region '") + getName() + "'";
}
std::vector<std::string> CRegion::getMultiLineInfoString() const
{
std::vector<std::string> container;
pushTitle(container, "CRegion");
pushEntry(container, "id=" + getIndexString());
container.back() += " alias=" + getAliasString();
container.back() += " name=" + getName();
pushEntry(container, "fullname=" + getFullName());
pushFooter(container);
return container;
}
std::string CRegion::getFullName() const
{
return std::string(getOwner()->getFullName() +":"+ getName());
}
void CRegion::rebuildBoundingBox()
{
// build the AABB to speedup the task
_BoundingBox.init();
for (uint i=0; i<_CellZones.size(); ++i)
{
CCellZone *czone = _CellZones[i];
if (!czone)
continue;
// CAIVector vmaxZone(INT_MIN/CAICoord::UNITS_PER_METER, INT_MIN/CAICoord::UNITS_PER_METER);
// CAIVector vminZone(INT_MAX/CAICoord::UNITS_PER_METER, INT_MAX/CAICoord::UNITS_PER_METER);
czone->_BoundingBox.init();
for (uint j=0; j<czone->cells().size(); ++j)
{
CCell *cell = czone->cells()[j];
if (!cell)
continue;
cell->_BoundingBox = CAabb(cell->_Coords);
czone->_BoundingBox.includeAabb(cell->_BoundingBox);
}
_BoundingBox.includeAabb(czone->_BoundingBox);
}
}
void CRegion::rebuildConnectivity()
{
nlinfo ("Build Region '%s' dependencies ..", getFullName().c_str());
// prestep : clear the road and cell connectivity
FOREACH(czone, CAliasCont<CCellZone>, _CellZones)
{
czone->rebuildEnergyLevels();
FOREACH(itCell, CCont<CCell>, czone->cells())
{
itCell->_NeighbourCells.clear();
for (TAliasZonePlaceList::iterator itZone=itCell->npcZonePlaces().begin(),itEndZone=itCell->npcZonePlaces().end();itZone!=itEndZone;++itZone)
itZone->roads().clear();
for (TAliasZoneShapeList::iterator itZone=itCell->npcZoneShapes().begin(),itEndZone=itCell->npcZoneShapes().end();itZone!=itEndZone;++itZone)
itZone->roads().clear();
}
}
// First pass : build the connectivity between cells
const double SCALE_DIAG = 1/10.0;
for (uint i=0; i<_CellZones.size(); ++i)
{
CCellZone *czone = _CellZones[i];
if (!czone)
continue;
for (uint j=0; j<czone->cells().size(); ++j)
{
CCell *cell1 = czone->cells()[j];
if (!cell1)
continue;
// maximum dist for aproximation of contact
double epsilon1 = (cell1->_BoundingBox.vmax() - cell1->_BoundingBox.vmin()).norm()*SCALE_DIAG ;
// for (uint k=i; k<_CellZones.size(); ++k)
{
CCellZone *czone2 = czone; //_CellZones[k];
if (!czone2)
continue;
for (uint l=0; l<czone2->cells().size(); ++l)
{
CCell *cell2 = czone->cells()[l];
if ( !cell2
|| cell2 == cell1
|| cell1->_NeighbourCells.find(cell2) != cell1->_NeighbourCells.end())
continue;
if (cell2->_NeighbourCells.find(cell1) != cell2->_NeighbourCells.end())
{
nlassert(false);
continue;
}
double epsilon2 = (cell2->_BoundingBox.vmax() - cell2->_BoundingBox.vmin()).norm()*SCALE_DIAG;
double epsilon = max(epsilon1, epsilon2);
// check distance between AABB to reduce vertex checking
// check on X
if (cell1->_BoundingBox.vmin().x() < cell2->_BoundingBox.vmin().x())
{
// cell 1 on left of cell 2
if ((cell2->_BoundingBox.vmin().x() - cell1->_BoundingBox.vmax().x()) > epsilon)
continue;
}
else
{
// cell 1 on right of cell 2
if ((cell1->_BoundingBox.vmin().x() - cell2->_BoundingBox.vmax().x()) > epsilon)
continue;
}
// check on Y
if (cell1->_BoundingBox.vmin().y() < cell2->_BoundingBox.vmin().y())
{
// cell 1 on left of cell 2
if ((cell2->_BoundingBox.vmin().y() - cell1->_BoundingBox.vmax().y()) > epsilon)
continue;
}
else
{
// cell 1 on right of cell 2
if ((cell1->_BoundingBox.vmin().y() - cell2->_BoundingBox.vmax().y()) > epsilon)
continue;
}
// ok, if we are there, the bouding box overlaps or are close enough
// run through the points of each cell
{
// square the espilon to check distance faster
epsilon = std::min(epsilon, 4.0);
epsilon = epsilon*epsilon;
uint32 nbCnx = 0;
for (uint i=0; i<cell1->_Coords.size(); ++i)
{
for (uint j=0; j<cell2->_Coords.size(); ++j)
{
if ((cell1->_Coords[i] - cell2->_Coords[j]).sqrnorm() < epsilon)
nbCnx ++;
}
}
if (nbCnx > 1)
{
// ok, the two cells are connected.
bool ok = cell1->_NeighbourCells.insert(cell2).second;
ok = cell2->_NeighbourCells.insert(cell1).second;
// nldebug("Connecting cell '%s' with '%s'", cell1->getName().c_str(), cell2->getName().c_str());
}
}
}
}
}
}
// second pass, link road with cells
FOREACH(czone, CAliasCont<CCellZone>, _CellZones)
{
FOREACH(itCell, CCont<CCell>, czone->cells())
{
itCell->connectRoads();
}
}
}
void CRegion::update()
{
for (uint j=0; j<_CellZones.size(); ++j)
{
CCellZone *czone = _CellZones[j];
if (!czone)
continue;
czone->update();
}
}
void CRegion::serviceEvent (const CServiceEvent &info)
{
CCont<CCellZone>::iterator first(_CellZones.begin()), last(_CellZones.end());
for(; first != last; ++first)
{
(*first)->serviceEvent (info);
}
}
bool CRegion::spawn()
{
// Spawn cellzones
for (size_t j=0; j<_CellZones.size(); ++j)
{
CCellZone* cellZone = _CellZones[(uint32)j];
if (!cellZone)
continue;
cellZone->spawn();
}
// We should check individual errors, but fake success here :)
return true;
}
bool CRegion::despawn()
{
// Despawn cellzones
for (size_t j=0; j<_CellZones.size(); ++j)
{
CCellZone* cellZone = _CellZones[(uint32)j];
if (!cellZone)
continue;
cellZone->despawn();
}
// We should check individual errors, but fake success here :)
return true;
}
//////////////////////////////////////////////////////////////////////////////
// CGroupFamily //
//////////////////////////////////////////////////////////////////////////////
IAliasCont *CGroupFamily::getAliasCont(TAIType type)
{
switch (type)
{
case AITypeGroupTemplate:
case AITypeGroupTemplateMultiLevel:
case AITypeGroupTemplateFauna:
return &_GroupDescs;
break;
case AITypeGroupTemplateNpc:
return &_GroupDescs;
default:
return NULL;
}
}
CAliasTreeOwner *CGroupFamily::createChild(IAliasCont *cont, CAIAliasDescriptionNode *aliasTree)
{
if (!cont)
return NULL;
CAliasTreeOwner* child = NULL;
switch(aliasTree->getType())
{
case AITypeGroupTemplate:
case AITypeGroupTemplateMultiLevel:
case AITypeGroupTemplateFauna:
case AITypeGroupTemplateNpc:
child = new CGroupDesc<CGroupFamily>(this, aliasTree->getAlias(), aliasTree->getName());
break;
default:
break;
}
if (child)
cont->addAliasChild(child);
return child;
}
const CGroupDesc<CGroupFamily> *CGroupFamily::getProportionalGroupDesc(const CFamilyBehavior *const familyBehavior, const CPropertySet &needActFlag, const CPropertySet &maskActFlag)
{
H_AUTO(getProportionalGroupDesc)
// first, build a list of group that match
// the family, energy level and spawn type
// TODO
// const TPopulationFamily &family =familyBehavior->getFamily();
const uint32 level = familyBehavior->getLevelIndex();
vector<const CGroupDesc<CGroupFamily>*> groups;
const bool &isDay = CTimeInterface::isDay();
uint32 totalWeight=0;
uint32 totalSpawnGroup=0;
for (uint i=0; i<_GroupDescs.size(); ++i)
{
const CGroupDesc<CGroupFamily> *const gd = _GroupDescs[i];
if (!gd)
continue;
const uint32 weight=gd->getWeightForEnergy (level);
if (weight<=0)
continue;
if (!gd->isValidForSeason(CTimeInterface::season()))
continue;
if (!gd->isValidForDayOrNight(isDay))
continue;
if (gd->properties().containsPartOfStrict(maskActFlag))
continue;
if (!gd->properties().containsAllOf(needActFlag))
continue;
totalWeight += weight;
totalSpawnGroup += gd->getNbUse();
groups.push_back(gd);
}
if (groups.empty())
{
if (!LogGroupCreationFailure)
return NULL;
return NULL;
}
float maxScore=0;
const CGroupDesc<CGroupFamily> *choosenGroup=NULL;
for (std::vector<const CGroupDesc<CGroupFamily>*>::iterator it=groups.begin(), itEnd=groups.end(); it!=itEnd; ++it)
{
// score to reach theorical rate.
const float score = ((*it)->getWeightForEnergy(level)*totalSpawnGroup)
- ((float)(*it)->getNbUse()*totalWeight);
if (score<maxScore)
continue;
choosenGroup=*it;
maxScore=score;
}
return choosenGroup;
}
//////////////////////////////////////////////////////////////////////////////
// CGroupDesc //
//////////////////////////////////////////////////////////////////////////////
template <> size_t const CGroupDesc<CGroupFamily>::_MultiLevelSheetCount = 20;
//////////////////////////////////////////////////////////////////////////////
// CBotDesc //
//////////////////////////////////////////////////////////////////////////////
template <> size_t const CBotDesc<CGroupFamily>::_MultiLevelSheetCount = 20;
//////////////////////////////////////////////////////////////////////////////
// CRoad //
//////////////////////////////////////////////////////////////////////////////
CRoad::CRoad(CCell *owner, uint32 alias, const std::string &name)
: CAliasChild<CCell>(owner, alias, name),
_StartZone(), _StartExternal(false), _EndZone(), _EndExternal(false)
{
// nldebug("Creating road '%s', alias %u", getName().c_str(), getAlias());
}
CRoad::~CRoad()
{
// nldebug("Deleting road '%s', alias %u", getName().c_str(), getAlias());
// need to unlink the road from the start and end zones
unlinkRoad();
}
void CRoad::unlinkRoad()
{
// unlink the road from the start and end zones
if (!_StartZone.isNULL())
{
std::vector<NLMISC::CDbgPtr<CRoad> > &roads = _StartZone->roads();
roads.erase(std::remove(roads.begin(), roads.end(), CDbgPtr<CRoad>(this)), roads.end());
}
_StartZone = (CNpcZone*)NULL;
if (!_EndZone.isNULL())
{
std::vector<NLMISC::CDbgPtr<CRoad> > &roads = _EndZone->roads();
roads.erase(std::remove(roads.begin(), roads.end(), CDbgPtr<CRoad>(this)), roads.end());
}
_EndZone = (CNpcZone*)NULL;
}
IAliasCont *CRoad::getAliasCont(TAIType type)
{
switch(type)
{
case AITypeRoadTrigger:
return &_RoadTriggers;
default:
return NULL;
}
}
CAliasTreeOwner *CRoad::createChild(IAliasCont *cont, CAIAliasDescriptionNode *aliasTree)
{
if (!cont)
return NULL;
CAliasTreeOwner* child = NULL;
switch(aliasTree->getType())
{
// create the child and adds it to the corresponding position.
case AITypeRoadTrigger:
child = new CRoadTrigger(this, aliasTree->getAlias(), aliasTree->getName());
break;
}
if (child)
cont->addAliasChild(child);
return child;
}
std::string CRoad::getIndexString() const
{
return getOwner()->getIndexString()+NLMISC::toString(":ro%u", getChildIndex());
}
void CRoad::setPathPoints(TVerticalPos verticalPos, const std::vector<CAIVector> &points)
{
_VerticalPos = verticalPos;
for (uint i=0; i<points.size(); ++i)
{
RYAI_MAP_CRUNCH::CWorldPosition newpos;
CWorldContainer::calcNearestWPosFromPosAnRadius(_VerticalPos, newpos, points[i], 0, 1, CWorldContainer::CPosValidatorDefault());
if (!newpos.isValid())
{
CWorldContainer::calcNearestWPosFromPosAnRadius(_VerticalPos, newpos, points[i], 6, 100, CWorldContainer::CPosValidatorDefault());
if (newpos.isValid())
{
if (LogAcceptablePos)
nlinfo("DynRoad '%s'%s: Path pos Error at position %s for size %d, an acceptable position could be %s",
getAliasFullName().c_str(),
getAliasString().c_str(),
points[i].toString().c_str(),
0,
newpos.toString().c_str() );
}
else
nlwarning("Dynroad '%s'%s: Path pos Error at position %s for size %d, no acceptable position found around",
getAliasFullName().c_str(),
getAliasString().c_str(),
points[i].toString().c_str(),
0);
}
_Coords.push_back(newpos);
}
if (points.size()>0)
_Start=points.front();
if (points.size()>1)
_End=points.back();
}
void CRoad::setStartZone(const CNpcZone *const npcZone)
{
_StartZone=(CNpcZone*)npcZone;
if (!npcZone)
{
_StartExternal=false;
}
else
{
_StartExternal=npcZone->getOwner()->getOwner()->getOwner()!=getOwner()->getOwner()->getOwner();
}
}
void CRoad::setEndZone(const CNpcZone *const npcZone)
{
_EndZone=npcZone;
if (!npcZone)
{
_EndExternal=false;
}
else
{
_EndExternal=npcZone->getOwner()->getOwner()->getOwner()!=getOwner()->getOwner()->getOwner();
}
}
//////////////////////////////////////////////////////////////////////////////
// CCellZone //
//////////////////////////////////////////////////////////////////////////////
struct CCellChoice
{
struct CZoneScore
{
float score;
const CFaunaZone* zone;
};
CCellChoice()
{
for (uint32 i=0;i<MAX_ZONE_SCORE;i++)
{
zones[i].score=0;
zones[i].zone=NULL;
}
}
float getTotalScore() const
{
float totalScore=1;
for (uint32 i=0;i<MAX_ZONE_SCORE;i++)
totalScore*=zones[i].score;
return totalScore;
}
enum TZoneScoreType
{
FOOD_ZONE_SCORE=0,
REST_ZONE_SCORE=1,
MAX_ZONE_SCORE=2
};
CZoneScore zones[MAX_ZONE_SCORE];
};
CCellZone::CCellZone(CRegion *owner, uint32 alias, const std::string &name)
: CAliasChild<CRegion>(owner, alias, name)
{
nldebug("Creating cell zone '%s'%s",
getName().c_str(),
getAliasString().c_str());
}
CCellZone::~CCellZone()
{
// clear the families first to despawn all the groups (managers are in the families)
_Families.clear();
_Cells.clear();
nldebug("Deleting cell zone '%s'%s",
getName().c_str(),
getAliasString().c_str());
}
std::string CCellZone::getOneLineInfoString() const
{
return std::string("Cell zone '") + getName() + "'";
}
std::vector<std::string> CCellZone::getMultiLineInfoString() const
{
std::vector<std::string> container;
pushTitle(container, "CCellZone");
pushEntry(container, "id=" + getIndexString());
container.back() += " name=" + getName();
pushEntry(container, "fullname=" + getFullName());
pushFooter(container);
return container;
}
std::string CCellZone::getFullName() const
{
return std::string(getOwner()->getFullName()+":"+getName());
}
IAliasCont* CCellZone::getAliasCont(TAIType type)
{
switch(type)
{
case AITypeCell:
return &_Cells;
default:
return NULL;
}
}
CAliasTreeOwner *CCellZone::createChild(IAliasCont *cont, CAIAliasDescriptionNode *aliasTree)
{
if (!cont)
return NULL;
CAliasTreeOwner* child = NULL;
switch (aliasTree->getType())
{
// create the child and adds it to the corresponding position.
case AITypeCell:
child = new CCell(this, aliasTree->getAlias(), aliasTree->getName());
break;
}
if (child)
cont->addAliasChild(child);
return child;
}
bool CCellZone::findRestAndFoodFaunaZoneInCellList(CFaunaZone const*& rest, CPropertySet const& restActivity, CFaunaZone const*& food, CPropertySet const& foodActivity, std::vector<CCell*> const& cells, TAStarFlag const denyflags)
{
static bool extensiveDebug = false; // That extensive debug has code 0001
if (extensiveDebug) nldebug("ED0001.01: restActivity=%s foodActivity=%s", restActivity.toString().c_str(), foodActivity.toString().c_str());
// Flags topology
typedef CHashMap<uint,CCellChoice> TSearchMapCellChoice;
typedef CHashMap<uint,TSearchMapCellChoice > TSearchMap;
TSearchMap searchMap;
const float minimumScore=/*28*28*/24*24; // :FIXME: Put that in a config thing (like a config file)
// Property sets for all activities
CPropertySet activities[CCellChoice::MAX_ZONE_SCORE];
activities[CCellChoice::FOOD_ZONE_SCORE].merge(foodActivity);
activities[CCellChoice::REST_ZONE_SCORE].merge(restActivity);
// Look for a conveninent zone in a convenient cell.
if (extensiveDebug) nldebug("ED0001.02: cells.size()=%d", cells.size());
// For each cell
FOREACHC(itCell, std::vector<CCell*>, cells)
{
CCell const* const cell = *itCell;
if (!cell)
continue;
if (extensiveDebug) nldebug("ED0001.03: cell->faunaZonesCst().size()=%d", cell->faunaZonesCst().size());
// For each zone
FOREACHC(itFaunaZone, CCont<CFaunaZone>, cell->faunaZonesCst())
{
CFaunaZone const* const faunaZone = *itFaunaZone;
#ifdef NL_DEBUG
nlassert(faunaZone);
#endif
// For each activity
for (uint32 typeZone=0; typeZone<CCellChoice::MAX_ZONE_SCORE; ++typeZone)
{
// Get zone position
CWorldPosition const& wpos = faunaZone->worldValidPos();
// Check it's valid
if (!wpos.isValid())
{
if (extensiveDebug) nldebug("ED0001.04: !wpos.isValid()");
continue;
}
// tmp nico for debug
/*
static bool displayActivities = false;
if (displayActivities)
{
nlinfo("Zone activities");
const AITYPES::CPropertySet &ps = faunaZone->additionalActivities();
const std::set<NLMISC::TStringId> &props = ps.properties();
std::set<NLMISC::TStringId>::const_iterator it;
for (it = props.begin(); it != props.end(); ++it)
{
nlinfo("Prop = %s", NLMISC::CStringMapper::unmap(*it).c_str());
}
nlinfo("Wanted activities");
for (it = activities[typeZone].properties().begin(); it != activities[typeZone].properties().end(); ++it)
{
nlinfo("Prop = %s", NLMISC::CStringMapper::unmap(*it).c_str());
}
}
*/
// Check that zone activity match
if (!faunaZone->haveActivity(activities[typeZone]))
{
if (extensiveDebug) nldebug("ED0001.05: !faunaZone->haveActivity(activities[typeZone])");
continue;
}
TAStarFlag const flags = (TAStarFlag)(wpos.getFlags()&GroundFlags); // Erase unused flags.
float const score = faunaZone->getFreeAreaScore();
for (TAStarFlag possibleFlag=Nothing;possibleFlag<=GroundFlags;possibleFlag=(TAStarFlag)(possibleFlag+2)) // tricky !! -> to replace with a defined list of flags to checks.
{
const uint32 incompatibilityFlags=possibleFlag&denyflags&GroundFlags; // Erase unused flags.
if (incompatibilityFlags)
{
if (extensiveDebug) nldebug("ED0001.06: incompatibilityFlags");
continue;
}
const uint32 masterTopo=wpos.getTopologyRef().getCstTopologyNode().getMasterTopo(possibleFlag);
if (masterTopo==~0)
{
if (extensiveDebug) nldebug("ED0001.07: masterTopo==~0");
continue;
}
if (score<minimumScore)
{
if (extensiveDebug) nldebug("ED0001.08: score<minimumScore");
continue;
}
CCellChoice::CZoneScore &zoneScore=searchMap[possibleFlag][masterTopo].zones[typeZone];
if (score<zoneScore.score)
{
if (extensiveDebug) nldebug("ED0001.09: score<zoneScore.score");
continue;
}
if (score==zoneScore.score)
{
if (extensiveDebug) nldebug("ED0001.10: score==zoneScore.score");
if (CAIS::rand16(1)==0)
zoneScore.zone=faunaZone;
}
else
{
if (extensiveDebug) nldebug("ED0001.11: score!=zoneScore.score");
zoneScore.score=score;
zoneScore.zone=faunaZone;
}
}
}
}
}
// find now the best score :)
{
CCellChoice *selectedCell=NULL;
float minScore=minimumScore;
for (TSearchMap::iterator flagIt=searchMap.begin(), flagItEnd=searchMap.end();flagIt!=flagItEnd;++flagIt)
{
for (TSearchMapCellChoice::iterator topoIt=flagIt->second.begin(), topoItEnd=flagIt->second.end();topoIt!=topoItEnd;++topoIt)
{
if (extensiveDebug) nldebug("ED0001.12: topoIt->second.getTotalScore()=%g minScore=%g", topoIt->second.getTotalScore(), minScore);
float theScore = topoIt->second.getTotalScore();
if (theScore > minScore)
{
selectedCell=&topoIt->second;
minScore=selectedCell->getTotalScore();
}
}
}
if (selectedCell)
{
rest=selectedCell->zones[CCellChoice::REST_ZONE_SCORE].zone;
food=selectedCell->zones[CCellChoice::FOOD_ZONE_SCORE].zone;
#if !FINAL_VERSION
const RYAI_MAP_CRUNCH::TAStarFlag restFlags=rest->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
const RYAI_MAP_CRUNCH::TAStarFlag foodFlags=food->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
nlassert((restFlags&denyflags)==0);
nlassert((foodFlags&denyflags)==0);
#endif
return true;
}
}
return false;
}
const CFaunaZone *CCellZone::lookupFaunaZone(const CPropertySet &activity, TAStarFlag denyflags, size_t replacementGroupFamilyId)
{
float totalScore=28*28;
vector<const CFaunaZone*> candidates;
// prepare a randomly ordered list of cell.
vector<CCell*> cells;
for (CCont<CCell>::iterator it=_Cells.begin(), itEnd=_Cells.end();it!=itEnd;++it)
cells.push_back(*it);
random_shuffle(cells.begin(), cells.end());
// look for a conveninent zone in a convenient cell.
for (uint c = 0; c < cells.size(); ++c)
{
const CCell *const cell = cells[c];
if (!cell)
continue;
// look for a zone that support activity.
for (CCont<CFaunaZone>::const_iterator it=cell->faunaZonesCst().begin(), itEnd=cell->faunaZonesCst().end(); it!=itEnd;++it)
{
const CFaunaZone *const faunaZone=*it;
#ifdef NL_DEBUG
nlassert(faunaZone);
#endif
// tmp nico for debug
/*
static bool displayActivities = false;
if (displayActivities)
{
nlinfo("Zone activities");
const AITYPES::CPropertySet &ps = faunaZone->additionalActivities();
const std::set<NLMISC::TStringId> &props = ps.properties();
std::set<NLMISC::TStringId>::const_iterator it;
for (it = props.begin(); it != props.end(); ++it)
{
nlinfo("Prop = %s", NLMISC::CStringMapper::unmap(*it).c_str());
}
nlinfo("Wanted activities num = %d ", (int) activity.properties().size());
if (activity.properties().size() != 0)
{
for (it = activity.properties().begin(); it != activity.properties().end(); ++it)
{
nlinfo("Prop = %s", NLMISC::CStringMapper::unmap(*it).c_str());
}
}
}
*/
if ( !faunaZone->worldValidPos().isValid()
|| !faunaZone->haveActivity(activity) )
continue;
// if a replacement GroupFamily was asked, it must be in the zone
if (replacementGroupFamilyId != 0)
{
if (!faunaZone->isSubstituted()) continue;
if (!faunaZone->isSubsitutedForGroupFamily(replacementGroupFamilyId)) continue;
}
else
{
if (faunaZone->isSubstituted()) continue; // zone wants a substitution group
}
const RYAI_MAP_CRUNCH::TAStarFlag flags=faunaZone->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
if (flags&denyflags)
continue;
const float score=faunaZone->getFreeAreaScore();
if (score>=totalScore)
{
if (score==totalScore)
{
candidates.push_back(faunaZone);
}
else
{
candidates.clear();
candidates.push_back(faunaZone);
totalScore=score;
}
}
}
}
if (candidates.size()>0)
return candidates[CAIS::rand16((uint32)candidates.size())];
return NULL;
}
struct TLookupLogFilter
{
CPropertySet Properties;
uint32 Alias;
bool operator ==(const TLookupLogFilter &other) const
{
if (Alias != other.Alias)
return false;
return Properties == other.Properties;
}
bool operator <(const TLookupLogFilter &other) const
{
if (Alias != other.Alias)
return Alias < other.Alias;
return Properties < other.Properties;
}
};
const CNpcZone *CCellZone::lookupNpcZone(const CPropertySet &activity, size_t replacementGroupFamilyId)
{
float totalScore=0;
// prepare a randomly ordered list of cell.
vector<CCell*> cells;
for (CCont<CCell>::iterator it=_Cells.begin(), itEnd=_Cells.end();it!=itEnd;++it)
cells.push_back(*it);
random_shuffle(cells.begin(), cells.end());
vector<const CNpcZone*> candidates;
// look for a convenient zone in a convenient cell.
for (uint c=0; c < cells.size(); ++c)
{
const CCell *const cell = cells[c];
if (!cell)
continue;
FOREACHC(it, TAliasZonePlaceList, cell->npcZonePlacesCst())
{
const CNpcZone *npcZone=*it;
#ifdef NL_DEBUG
nlassert(npcZone);
#endif
if ( !activity.empty()
&& !npcZone->properties().containsPartOfNotStrict(activity))
continue;
// if a replacement GroupFamily was asked, it must be in the zone
if (replacementGroupFamilyId != 0)
{
if (!npcZone->isSubstituted()) continue;
if (!npcZone->isSubsitutedForGroupFamily(replacementGroupFamilyId)) continue;
}
else
{
if (npcZone->isSubstituted()) continue; // zone wants a substitution group
}
const float score=npcZone->getFreeAreaScore();
if (score>totalScore)
{
totalScore=score;
candidates.clear();
candidates.push_back(npcZone);
continue;
}
if (score==totalScore)
candidates.push_back(npcZone);
}
FOREACHC(it, TAliasZoneShapeList, cell->npcZoneShapesCst())
{
// :FIXME: Same code than above, cut n paste
const CNpcZone *const npcZone=*it;
#ifdef NL_DEBUG
nlassert(npcZone);
#endif
if ( !activity.empty()
&& !npcZone->properties().containsPartOfNotStrict(activity))
continue;
const float score=npcZone->getFreeAreaScore();
if (score>totalScore)
{
totalScore=score;
candidates.clear();
candidates.push_back(npcZone);
continue;
}
if (score==totalScore)
candidates.push_back(npcZone);
}
}
if (candidates.size()>0)
return candidates[CAIS::rand16((uint32)candidates.size())];
// warning only once
{
static set<TLookupLogFilter> filter;
TLookupLogFilter lf;
lf.Properties = activity;
lf.Alias = getAlias();
if (filter.find(lf) == filter.end())
{
nlwarning("CCellZone::lookupNpcZone can't find zone for activity '%s' in cellZone '%s'%s (display only ONCE)",
activity.toString().c_str(),
getAliasFullName().c_str(),
getAliasString().c_str());
filter.insert(lf);
}
}
return NULL;
}
const CNpcZone *CCellZone::lookupNpcZoneByName(/*const TPopulationFamily &family, */const std::string &zoneName)
{
for (uint i=0; i<_Cells.size(); ++i)
{
CCell *const cell = _Cells[i];
if (!cell)
continue;
// if (!cell->_FamilyFlags.containsFamily(family))
// continue;
for (uint j=0; j<cell->npcZoneCount(); ++j)
{
CNpcZone *const zone = cell->npcZone(j);
if (!zone)
continue;
if (zone->getAliasTreeOwner().getName() == zoneName)
{ // ok, we found it !
return zone;
}
}
}
return NULL;
}
std::string CCellZone::getIndexString() const
{
return getOwner()->getIndexString()+toString(":cz%u", getChildIndex());
}
void CCellZone::rebuildEnergyLevels()
{
CCont<CGroupFamily> &groupFamilies=getOwner()->groupFamilies();
{
CPropertySet groupFamiliesNames;
for (CCont<CGroupFamily>::iterator it=groupFamilies.begin(), itEnd=groupFamilies.end();it!=itEnd;++it)
groupFamiliesNames.addProperty(it->getName());
for (CCont<CFamilyBehavior>::iterator it=_Families.begin(), itEnd=_Families.end();it!=itEnd;)
{
if (!groupFamiliesNames.have(it->getName()))
{
CCont<CFamilyBehavior>::iterator last=it;
++it;
_Families.removeChildByIndex(last->getChildIndex());
continue;
}
++it;
}
}
for (CCont<CGroupFamily>::iterator it=groupFamilies.begin(), itEnd=groupFamilies.end();it!=itEnd;++it)
{
CCont<CFamilyBehavior>::iterator itBehav=_Families.begin(), itBehavEnd=_Families.end();
for (;itBehav!=itBehavEnd;++itBehav)
{
if (itBehav->getName()==it->getName())
break;
}
if (itBehav!=itBehavEnd) // present ?
continue;
// else create the missing Behavior.
CSmartPtr<CFamilyBehavior> fb=new CFamilyBehavior(this, *it);
if (!fb->isFamilyProfileValid())
{
fb=NULL;
continue;
}
fb->setBaseLevel ((uint32)(0.45*(double)ENERGY_SCALE)); // forced to 0 at start instead of 0.5 ..
fb->setEffectiveLevel (fb->baseLevel());
_Families.addChild (fb);
}
}
// predicate to remove old family manager
class CObsoleteFamilyManagerRemover
{
public:
bool operator()(CFamilyBehavior *fb) const
{
nlassert(fb);
if (fb->grpFamily()->getSubstitutionId() != 0) // do the state only dynamically added groups
{
// if all managers are empty, then can remove
if (fb->mgrNpc()->isEmpty() && fb->mgrFauna()->isEmpty())
{
return true;
}
}
return false;
}
};
void CCellZone::update()
{
H_AUTO(CellZoneUpdate);
// update all family managers
for (CCont<CFamilyBehavior>::iterator it=_Families.begin(), itEnd=_Families.end(); it!=itEnd;++it)
{
{
H_AUTO(CellZonesManagersUpdate);
it->updateManagers();
}
{
H_AUTO(FamiliesUpdate);
if (it->needUpdate())
it->update(it->getDt());
}
}
// remove old substitution family manager if they are not referenced any more
_Families.getInternalCont().erase(std::remove_if(_Families.getInternalCont().begin(), _Families.getInternalCont().end(), CObsoleteFamilyManagerRemover()), _Families.getInternalCont().end());
}
void CCellZone::serviceEvent (const CServiceEvent &info)
{
CCont<CFamilyBehavior>::iterator first(_Families.begin()), last(_Families.end());
for(; first != last; ++first)
{
(*first)->serviceEvent (info);
}
}
const CNpcZone *CCellZone::lookupNpcZoneScorer (std::vector<CCell*> cells, const CZoneScorer &scorer)
{
float totalScore=0;
vector<const CNpcZone*> candidates;
// look for a convenient zone in a convenient cell.
for (uint c=0; c < cells.size(); ++c)
{
const CCell *const cell = cells[c];
if (!cell)
continue;
FOREACHC(it, TAliasZonePlaceList, cell->npcZonePlacesCst())
{
const CNpcZone *const npcZone=*it;
#ifdef NL_DEBUG
nlassert(npcZone);
#endif
const float score=scorer.getScore(*npcZone);
float const distance = scorer.getParam(*npcZone);
if (LogScorerScores && score>=0.f)
nldebug("Zone: %s - Score: %f - Distance: %f", npcZone->getAliasTreeOwner().getAliasFullName().c_str(), score, distance);
if (score<totalScore)
continue;
if (score>totalScore)
{
totalScore=score;
candidates.clear();
candidates.push_back(npcZone);
continue;
}
candidates.push_back(npcZone);
}
FOREACHC(it, TAliasZoneShapeList, cell->npcZoneShapesCst())
{
// :FIXME: Same code than above, cut n paste
const CNpcZone *const npcZone=*it;
#ifdef NL_DEBUG
nlassert(npcZone);
#endif
const float score=scorer.getScore(*npcZone);
float const distance = scorer.getParam(*npcZone);
if (LogScorerScores && score>=0.f)
nldebug("Zone: %s - Score: %f - Distance: %f", npcZone->getAliasTreeOwner().getAliasFullName().c_str(), score, distance);
if (score<totalScore)
continue;
if (score>totalScore)
{
totalScore=score;
candidates.clear();
candidates.push_back(npcZone);
continue;
}
candidates.push_back(npcZone);
}
}
if (candidates.size()>0)
return candidates[CAIS::rand16((uint32)candidates.size())];
return NULL;
}
bool CCellZone::spawn()
{
// Spawn families
for (size_t k=0; k<_Families.size(); ++k)
{
CFamilyBehavior* familyBehavior = _Families[(uint32)k];
if (!familyBehavior)
continue;
familyBehavior->spawn();
}
// We should check individual errors, but fake success here :)
return true;
}
bool CCellZone::despawn()
{
// Despawn families
for (size_t k=0; k<_Families.size(); ++k)
{
CFamilyBehavior* familyBehavior = _Families[(uint32)k];
if (!familyBehavior)
continue;
familyBehavior->despawn();
}
// We should check individual errors, but fake success here :)
return true;
}
//////////////////////////////////////////////////////////////////////////////
// CCell //
//////////////////////////////////////////////////////////////////////////////
CCell::CCell(CCellZone *owner, uint32 alias, const std::string &name)
: CAliasChild<CCellZone>(owner, alias, name)
{
}
CCell::~CCell()
{
unlinkCell();
}
std::string CCell::getFullName() const
{
return std::string(getOwner()->getFullName()+":"+getName());
}
void CCell::unlinkCell()
{
while (!_NeighbourCells.empty())
{
CCell *c = *(_NeighbourCells.begin());
c->_NeighbourCells.erase(this);
_NeighbourCells.erase(_NeighbourCells.begin());
}
}
IAliasCont *CCell::getAliasCont(TAIType type)
{
switch(type)
{
case AITypeDynFaunaZone:
return &_FaunaZones;
case AITypeDynNpcZonePlace:
return &_NpcZonePlaces;
case AITypeDynNpcZoneShape:
return &_NpcZoneShapes;
case AITypeDynRoad:
return &_Roads;
default:
return NULL;
}
}
CNpcZone *CCell::findNpcZone(const CAIVector &posInside)
{
// for (CCont<CNpcZone>::iterator itNpcZone=npcZones().begin(), itEndNpcZone=npcZones().end(); itNpcZone!=itEndNpcZone; ++itNpcZone)
FOREACH(itNpcZone, TAliasZonePlaceList, npcZonePlaces())
{
if (!itNpcZone->atPlace(posInside))
continue;
// This zone match the position
return *itNpcZone;
}
FOREACH(itNpcZone, TAliasZoneShapeList, npcZoneShapes())
{
// :FIXME: Same code than above, cut n paste
if (!itNpcZone->atPlace(posInside))
continue;
// This zone match the position
return *itNpcZone;
}
return NULL;
}
CAliasTreeOwner *CCell::createChild(IAliasCont *cont, CAIAliasDescriptionNode *aliasTree)
{
if (!cont)
return NULL;
CAliasTreeOwner* child = NULL;
switch(aliasTree->getType())
{
// create the child and adds it to the corresponding position.
case AITypeDynFaunaZone:
{
CFaunaZone *fzc = new CFaunaZone(this, aliasTree);
child = fzc;
}
break;
case AITypeDynNpcZonePlace:
child = new CNpcZonePlace(this, aliasTree);
break;
case AITypeDynNpcZoneShape:
child = new CNpcZoneShape(this, aliasTree);
break;
case AITypeDynRoad:
child = new CRoad(this, aliasTree->getAlias(), aliasTree->getName());
break;
}
if (child)
cont->addAliasChild(child);
return child;
}
//////////////////////////////////////////////////////////////////////////////
// //
//////////////////////////////////////////////////////////////////////////////
// data structure for path finding
//****************************************
class TListItem
{
public:
// only for search purpose.
TListItem(const CNpcZone *const zone)
: _Zone(zone)
,_MvtCost(0)
,_TargetDist(0)
,_Value(0)
,_Parent(NULL)
,_Road(NULL)
{}
TListItem (const CNpcZone *const zone, const float &mvtCost, const float &targetDist, const float &value, const CNpcZone *const parent, const CRoad *const road)
:_Zone(zone)
,_MvtCost(mvtCost)
,_TargetDist(targetDist)
,_Value(value)
,_Parent(parent)
,_Road(road)
{}
const float &value () const
{
return _Value;
}
const CNpcZone *zone () const
{
return _Zone;
}
const CNpcZone *parent () const
{
return _Parent;
}
const CRoad *road () const
{
return _Road;
}
const float &mvtCost () const
{
return _MvtCost;
}
private:
const CNpcZone *const _Zone;
const CNpcZone *const _Parent;
const CRoad *const _Road;
float _MvtCost;
float _TargetDist;
float _Value;
};
struct TOrderItemOnValue : unary_function<TListItem, bool>
{
bool operator () (const TListItem &item1, const TListItem &item2) const
{
return item1.value() < item2.value();
}
};
struct TOrderItemOnZone : unary_function<TListItem, bool>
{
bool operator () (const TListItem &item1, const TListItem &item2) const
{
return item1.zone()< item2.zone();
}
};
class TFindItemOnZone
{
public:
TFindItemOnZone(const CNpcZone *zone)
: _Zone(zone)
{}
bool operator () (const TListItem &item) const
{
return item.zone()== _Zone;
}
private:
const CNpcZone *_Zone;
};
bool pathFind(const CNpcZone *const start, const CNpcZone *const end, const CPropertySet &zoneFilter, std::vector<NLMISC::CSmartPtr<CRoad> > &path, bool logError)
{
/// check that the start and end are in the same continent.
const CContinent *cont = start->getOwner()->getOwner()->getOwner()->getOwner();
if (end->getOwner()->getOwner()->getOwner()->getOwner() != cont)
{
nlwarning("Searching path for zone in different continent !");
return false;
}
#if !FINAL_VERSION
if (start==end)
{
nlwarning("trying to find a path between same zone %s", start->getAliasTreeOwner().getAliasFullName().c_str());
path.clear(); // ->leads to an assert in the caller ?
return true;
}
#endif
multiset<TListItem, TOrderItemOnValue> openList;
set<TListItem, TOrderItemOnZone> closedList;
const float localDist=(float)(end->midPos() - start->midPos()).norm();
TListItem li(start, 0, localDist, localDist, NULL, NULL);
openList.insert(li);
do
{
const TListItem bestItem = *openList.begin();
openList.erase(openList.begin());
closedList.insert(bestItem);
nlassert(closedList.find(bestItem) != closedList.end());
// check if we are at destination
if (bestItem.zone()== end)
{
// yeee! we found the path! build the resulting path
const TListItem *pli = &bestItem;
while ( pli->parent()
&& pli->road())
{
path.push_back(const_cast<CRoad*>(pli->road()));
set<TListItem, TOrderItemOnZone>::iterator it(closedList.find(pli->parent()));
if (it != closedList.end())
pli = &(*it);
else
{
// look in the open list
multiset<TListItem, TOrderItemOnValue>::iterator it(find_if(openList.begin(), openList.end(), TFindItemOnZone(pli->parent())));
nlassert(it != openList.end());
pli = &(*it);
}
}
// make the path in correct order
std::reverse(path.begin(), path.end());
return true;
}
CNpcZone*const z1 = const_cast<CNpcZone*>(bestItem.zone());
FOREACH(road, vector<CDbgPtr<CRoad> >, z1->roads())
{
CNpcZone *z2 = (*road)->startZone();
if (z2 == z1)
{
z2 = (*road)->endZone();
if (z2==z1)
continue;
}
if ( !z2
|| closedList.find(TListItem(z2)) != closedList.end()
|| ( z2!=end
&& z2->properties().containsPartOfStrict(zoneFilter)))
{
continue;
}
const float localDist=(float)(end->midPos() - z2->midPos()).norm();
// _Length in meters
// _Difficulty between 0 and ??
TListItem li(z2, bestItem.mvtCost()+(*road)->getCost(), localDist, localDist+li.mvtCost(), z1, *road);
// first, check if the zone is already in the open list
multiset<TListItem, TOrderItemOnValue>::iterator it(find_if(openList.begin(), openList.end(), TFindItemOnZone(z2)));
if (it == openList.end())
{
//If it isn<73>t on the open list, add it to the open list.
// Make the current square the parent of this square.
// Record the F, G, and H costs of the square.
openList.insert(li);
}
else
{
// If it is on the open list already, check to see if this path
// to that square is better, using G cost as the measure. A lower
// G cost means that this is a better path. If so, change the parent
// of the square to the current square, and recalculate the G and F
// scores of the square. If you are keeping your open list sorted by F
// score, you may need to resort the list to account for the change.
if (li.mvtCost() < it->mvtCost())
{
openList.erase(it);
openList.insert(li);
}
}
}
} while(!openList.empty());
if (logError)
nlwarning("Could't find path from '%s'%s to '%s'%s",
start->getAliasTreeOwner().getAliasFullName().c_str(),
start->getAliasTreeOwner().getAliasString().c_str(),
end->getAliasTreeOwner().getAliasFullName().c_str(),
end->getAliasTreeOwner().getAliasString().c_str());
return false;
}
//////////////////////////////////////////////////////////////////////////////
// CPopulation //
//////////////////////////////////////////////////////////////////////////////
std::string CPopulation::getIndexString() const
{
return getOwner()->getIndexString()+NLMISC::toString(":po%u", getChildIndex());
}
//////////////////////////////////////////////////////////////////////////////
// CRoadTrigger //
//////////////////////////////////////////////////////////////////////////////
std::string CRoadTrigger::getIndexString() const
{
return getOwner()->getIndexString()+NLMISC::toString(":rt%u", getChildIndex());
}
CGroupFamily::CGroupFamily(CAliasTreeOwner *owner, uint32 alias, std::string const& name)
: CAliasTreeOwner(alias, name),
_Owner(owner),
_ProfileName(0),
_SubstitutionId(0),
_Index(~0)
{
}
bool CAIRefPlaceXYR::atPlace(CAIEntityPhysical const* entity) const
{
return _Zone->atPlace(entity);
}