khanat-opennel-code/code/ryzom/server/src/ai_service/ai.cpp

906 lines
28 KiB
C++

// 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 <functional>
#include "child_container.h"
#include "ai_mgr.h"
#include "ai_entity_matrix.h"
#include "ai_player.h"
#include "ai_mgr_pet.h"
#include "ai_grp.h"
#include "ai_mgr_fauna.h"
#include "ai_mgr_npc.h"
#include "ai_bot_npc.h"
#include "ai_grp_npc.h"
#include "ai_grp_pet.h"
#include "ai_script_data_manager.h"
#include "ais_user_models.h"
#include "continent.h"
#include "client_message.h"
#include "ai_outpost.h"
// Georges
#include "nel/georges/u_form_loader.h"
#include "nel/georges/u_form_elm.h"
#include "nel/georges/u_form.h"
#include "nel/georges/u_form_dfn.h"
// Game share
#include "game_share/emote_list_parser.h"
#include "game_share/backup_service_interface.h"
#include "ai_variables.h"
#include "server_share/r2_variables.h"
using namespace NLMISC;
using namespace NLNET;
using namespace std;
using namespace NLGEORGES;
//--------------------------------------------------------------------------
// SINGLETON DATA
//--------------------------------------------------------------------------
CAIS *CAIS::_Instance = NULL;
CRandom CAIS::_random;
const std::string disengageString("DISENGAGE");
const std::string egsString("EGS");
const uint32 Default_MaxPlayers=5000;
const uint32 Default_MaxBotsPet=Default_MaxPlayers*4;
const uint32 Default_MaxBotsFauna=40000;
const uint32 Default_MaxBotsNpc=20000;
const uint32 Default_MaxBotsFx=200;
CAIS &CAIS::instance()
{
if (_Instance == NULL)
{
_Instance = new CAIS();
// init the AI engine
_Instance->initAI();
}
return *_Instance;
}
CAIS::CAIS()
: _PetBotCounter(TotalMaxPet),
_FaunaBotCounter(TotalMaxFauna),
_NpcBotCounter(TotalMaxNpc)
{
// _initialised=false;
_TotalBotsSpawned = 0;
_ClientCreatureDebug=false;
}
void setMaxPetCallBack(IVariable &var)
{
uint32 newMax=NLMISC::safe_cast<CVariable<uint32>*>(&var)->get();
if (CAIS::instanceCreated())
CAIS::instance()._PetBotCounter.setMax(newMax);
}
void setMaxFaunaCallBack(IVariable &var)
{
uint32 newMax=NLMISC::safe_cast<CVariable<uint32>*>(&var)->get();
if (CAIS::instanceCreated())
CAIS::instance()._FaunaBotCounter.setMax(newMax);
}
void setMaxNpcCallBack(IVariable &var)
{
uint32 newMax=NLMISC::safe_cast<CVariable<uint32>*>(&var)->get();
if (CAIS::instanceCreated())
CAIS::instance()._NpcBotCounter.setMax(newMax);
}
CVariable<uint32> TotalMaxPlayer("ai", "NbPlayersLimit", "Security absolute limit to the number of Player", Default_MaxPlayers, 0, true );
CVariable<uint32> TotalMaxPet("ai", "NbPetLimit", "Security absolute limit to the number of Pets", Default_MaxBotsPet, 0, true, setMaxPetCallBack );
CVariable<uint32> TotalMaxFauna("ai", "NbFaunaLimit", "Security absolute limit to the number of Faunas", Default_MaxBotsFauna, 0, true, setMaxFaunaCallBack );
CVariable<uint32> TotalMaxNpc("ai", "NbNpcLimit", "Security absolute limit to the number of Npcs", Default_MaxBotsNpc, 0, true, setMaxNpcCallBack );
CVariable<uint32> TotalMaxFx("ai", "NbFxLimit", "Security absolute limit to the number of Fx", Default_MaxBotsFx, 0, true );
CVariable<string> BotRepopFx("ai", "BotRepopFx", "Fx sheet to use when changing the sheet of a bot", string(), 0, true );
//--------------------------------------------------------------------------
// DATA TABLES FOR ENTITY MATRIX
//--------------------------------------------------------------------------
// a series of tables giving the minimum iterator table forms for entity matrix iterators for all sizes up to 127m
static uint32 EntityMatrixTbl0[] = { 3, 3, 3};
static uint32 EntityMatrixTbl16[] = { 3, 5, 5, 5, 3};
static uint32 EntityMatrixTbl23[] = { 5, 5, 5, 5, 5};
static uint32 EntityMatrixTbl32[] = { 3, 5, 7, 7, 7, 5, 3};
static uint32 EntityMatrixTbl36[] = { 5, 7, 7, 7, 7, 7, 5};
static uint32 EntityMatrixTbl46[] = { 7, 7, 7, 7, 7, 7, 7};
static uint32 EntityMatrixTbl48[] = { 3, 7, 7, 9, 9, 9, 7, 7, 3};
static uint32 EntityMatrixTbl51[] = { 5, 7, 9, 9, 9, 9, 9, 7, 5};
static uint32 EntityMatrixTbl58[] = { 7, 9, 9, 9, 9, 9, 9, 9, 7};
static uint32 EntityMatrixTbl64[] = { 3, 7, 9, 9, 11, 11, 11, 9, 9, 7, 3};
static uint32 EntityMatrixTbl66[] = { 5, 7, 9, 11, 11, 11, 11, 11, 9, 7, 5};
static uint32 EntityMatrixTbl68[] = { 5, 9, 9, 11, 11, 11, 11, 11, 9, 9, 5};
static uint32 EntityMatrixTbl72[] = { 7, 9, 11, 11, 11, 11, 11, 11, 11, 9, 7};
static uint32 EntityMatrixTbl80[] = { 3, 9, 11, 11, 11, 13, 13, 13, 11, 11, 11, 9, 3};
static uint32 EntityMatrixTbl82[] = { 5, 9, 11, 11, 13, 13, 13, 13, 13, 11, 11, 9, 5};
static uint32 EntityMatrixTbl87[] = { 7, 9, 11, 13, 13, 13, 13, 13, 13, 13, 11, 9, 7};
static uint32 EntityMatrixTbl91[] = { 7, 11, 11, 13, 13, 13, 13, 13, 13, 13, 11, 11, 7};
static uint32 EntityMatrixTbl94[] = { 9, 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, 11, 9};
static uint32 EntityMatrixTbl96[] = { 3, 9, 11, 13, 13, 13, 15, 15, 15, 13, 13, 13, 11, 9, 3};
static uint32 EntityMatrixTbl98[] = { 5, 9, 11, 13, 13, 15, 15, 15, 15, 15, 13, 13, 11, 9, 5};
static uint32 EntityMatrixTbl102[] = { 7, 9, 11, 13, 15, 15, 15, 15, 15, 15, 15, 13, 11, 9, 7};
static uint32 EntityMatrixTbl103[] = { 7, 11, 13, 13, 15, 15, 15, 15, 15, 15, 15, 13, 13, 11, 7};
static uint32 EntityMatrixTbl108[] = { 9, 11, 13, 15, 15, 15, 15, 15, 15, 15, 15, 15, 13, 11, 9};
static uint32 EntityMatrixTbl112[] = { 3, 9, 11, 13, 15, 15, 15, 17, 17, 17, 15, 15, 15, 13, 11, 9, 3};
static uint32 EntityMatrixTbl114[] = { 5, 9, 13, 13, 15, 15, 17, 17, 17, 17, 17, 15, 15, 13, 13, 9, 5};
static uint32 EntityMatrixTbl116[] = { 5, 11, 13, 15, 15, 15, 17, 17, 17, 17, 17, 15, 15, 15, 13, 11, 5};
static uint32 EntityMatrixTbl117[] = { 7, 11, 13, 15, 15, 17, 17, 17, 17, 17, 17, 17, 15, 15, 13, 11, 7};
static uint32 EntityMatrixTbl122[] = { 9, 11, 13, 15, 17, 17, 17, 17, 17, 17, 17, 17, 17, 15, 13, 11, 9};
static uint32 EntityMatrixTbl125[] = { 9, 13, 15, 15, 17, 17, 17, 17, 17, 17, 17, 17, 17, 15, 15, 13, 9};
// a few larger special case matrices
static uint32 EntityMatrixTblUpTo150[] = { 7, 11, 15, 17, 17, 19, 19, 21, 21, 21, 21, 21, 21, 21, 19, 19, 17, 17, 15, 11, 7};
static uint32 EntityMatrixTblUpTo200[] = { 9, 13, 17, 19, 21, 23, 23, 25, 25, 27, 27, 27, 27, 27, 27, 27, 27, 27, 25, 25, 23, 23, 21, 19, 17, 13, 9};
static uint32 EntityMatrixTblUpTo250[] = {11, 15, 19, 23, 25, 27, 27, 29, 29, 31, 31, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 31, 31, 29, 29, 27, 27, 25, 23, 19, 15, 11};
static void initLinearMatrixIteratorTables(std::vector<CAIEntityMatrixIteratorTblLinear *> &vect)
{
// initialise the vector with the first table
vect.push_back(new CAIEntityMatrixIteratorTblLinear(&(EntityMatrixTbl0[0]),3));
// local macro undefined at end of function
#define ADD_TBL(d) \
{ \
while (vect.size()<d) \
vect.push_back(vect[vect.size()-1]); \
vect.push_back(new CAIEntityMatrixIteratorTblLinear(EntityMatrixTbl##d,sizeof(EntityMatrixTbl##d)/sizeof(EntityMatrixTbl##d[0]))); \
}
// setup the tables ...
ADD_TBL(16) ADD_TBL(23)
ADD_TBL(32) ADD_TBL(36) ADD_TBL(46)
ADD_TBL(48) ADD_TBL(51) ADD_TBL(58)
ADD_TBL(64) ADD_TBL(66) ADD_TBL(68) ADD_TBL(72)
ADD_TBL(80) ADD_TBL(82) ADD_TBL(87) ADD_TBL(91) ADD_TBL(94)
ADD_TBL(96) ADD_TBL(98) ADD_TBL(102) ADD_TBL(103) ADD_TBL(108)
ADD_TBL(112) ADD_TBL(114) ADD_TBL(116) ADD_TBL(117) ADD_TBL(122) ADD_TBL(125)
#undef ADD_TBL
}
bool CAIS::markTagForDelete(const std::string &filename)
{
const TStringId fileId = CStringMapper::map(filename);
for (CCont<CAIInstance>::iterator it=_AIInstances.begin(), itEnd=_AIInstances.end(); it!=itEnd;++it)
{
// first: tag the dynamic regions in the continents
for_each(it->continents().begin(), it->continents().end(),
bind2nd(mem_fun(&CContinent::markTagForDelete), fileId));
for_each(it->managers().begin(),it->managers().end(),
CAliasTreeRoot::CMarkTagForDelete(fileId));
}
return true;
}
void CAIS::deleteTaggedAlias(const std::string &filename)
{
const TStringId fileId = CStringMapper::map(filename);
FOREACH(it, CCont<CAIInstance>, _AIInstances)
{
// first: tag the dynamic regions in the continents
for_each(it->continents().begin(), it->continents().end(),
bind2nd(mem_fun(&CContinent::deleteTaggedAlias),fileId));
for_each(it->managers().begin(),it->managers().end(),
CAliasTreeRoot::CDeleteTagged<CManager>(it->managers()));
}
}
uint32 CAIS::getEmotNumber(const std::string &name)
{
std::map<std::string, uint32>::iterator it(_EmotNames.find(name));
if (it==_EmotNames.end())
return ~0;
return it->second;
}
bool CAIS::advanceUserTimer (uint32 nbTicks)
{
// for each manager, look for a timer event
for_each(AIList().begin(), AIList().end(), bind2nd(mem_fun(&CAIInstance::advanceUserTimer),nbTicks) );
return true;
}
// initialise the singleton
void CAIS::initAI()
{
// if (_initialised)
// return;
// _initialised=true;
nlinfo("---------- Initialising AI Singleton ----------");
// setup the random number generator
_random.srand( (sint32)NLMISC::CTime::getLocalTime() );
// allocate RAM for the players
// setup the standard iterator tables for scanning the entity matrices
_matrixIterator2x2.push_back(-1,-1); _matrixIterator2x2.push_back(1,0);
_matrixIterator2x2.push_back(-1, 1); _matrixIterator2x2.push_back(1,0);
_matrixIterator3x3.push_back(-1,-1); _matrixIterator3x3.push_back(1,0); _matrixIterator3x3.push_back(1,0);
_matrixIterator3x3.push_back(-2, 1); _matrixIterator3x3.push_back(1,0); _matrixIterator3x3.push_back(1,0);
_matrixIterator3x3.push_back(-2, 1); _matrixIterator3x3.push_back(1,0); _matrixIterator3x3.push_back(1,0);
// setup the set of linear iterator tables for generating visions of given distances
initLinearMatrixIteratorTables(_matrixIteratorsByDistance);
EMOTE_LIST_PARSER::initEmoteList(_EmotNames);
// init the client message callbacks
CAIClientMessages::init();
}
uint32 CAIS::createAIInstance(const std::string &continentName, uint32 instanceNumber)
{
// first, check that an instance with this number is not already running
for (CCont<CAIInstance>::iterator it=_AIInstances.begin(), itEnd=_AIInstances.end();it!=itEnd;++it)
{
if (it->getInstanceNumber()!=instanceNumber)
continue;
nlwarning("CAIS::createAIInstance: instance number %u is already in use, can't create new instance.", instanceNumber);
return ~0;
}
CAIInstance *aii = _AIInstances.addChild(new CAIInstance(this));
// ok, set the continent name and instance number
aii->initInstance(continentName, instanceNumber);
return aii->getChildIndex();
}
void CAIS::destroyAIInstance(uint32 instanceNumber, bool displayWarningIfInstanceNotExist)
{
// this method is not fully tested for a Ryzom shard
// but it should work as expected for a Ring shard
nlassert(IsRingShard.get());
CRefPtr<CAIInstance> aii = getAIInstance(instanceNumber);
if (aii == NULL)
{
if (displayWarningIfInstanceNotExist)
{
nlwarning("AI instance %u does not exist but it was asked to delete here", instanceNumber);
}
return;
}
aii->despawn();
_AIInstances.removeChildByIndex(aii->getChildIndex());
nlassert(aii == NULL);
// notify the EGS
if (EGSHasMirrorReady)
{
CReportAIInstanceDespawnMsg msg;
msg.InstanceNumbers.push_back(instanceNumber);
msg.send("EGS");
}
}
// release the singleton before program exit
void CAIS::release ()
{
// force an update to save the persistent var if needed
updatePersistentVariables();
// erase all ai instance.
AIList().clear();
CAIUserModelManager::getInstance()->destroyInstance();
// release the client message callbacks
CAIClientMessages::release();
// free up the vision matrix iterator tables
if (!_matrixIteratorsByDistance.empty())
{
for (uint i=0;i<_matrixIteratorsByDistance.size();)
{
// erase the iterator table
delete _matrixIteratorsByDistance[i];
// run i forwards past repeated refs to the iterator tbl that we just deleted
for (++i;i<_matrixIteratorsByDistance.size() && _matrixIteratorsByDistance[i]==_matrixIteratorsByDistance[i-1];++i) {}
}
_matrixIteratorsByDistance.clear();
}
_Instance = NULL;
delete this;
}
void CAIS::serviceEvent (const CServiceEvent &info)
{
if (info.getEventType() == CServiceEvent::SERVICE_UP && info.getServiceName() == "EGS")
{
// send the list of available collision data
CReportAICollisionAvailableMsg msg;
msg.ContinentsCollision = CWorldContainer::getContinentList();
msg.send(info.getServiceId());
}
// event on all ai instance
// for_each(_AIInstances.begin(), _AIInstances.end(), bind2nd(mem_fun1(&CAIInstance::serviceEvent), info));
// don't compile coz we need to pass an object and not a reference (info). have to build an object that represents the reference.
FOREACH(it, CCont<CAIInstance>, _AIInstances)
it->serviceEvent (info);
}
//--------------------------------------------------------------------------
// update() & save()
//--------------------------------------------------------------------------
// the update routine called once per tick
// this is the routine that calls the managers' updates
extern void execBufferedCommands();
extern void execNamedEntityChanges();
void CAIS::update()
{
if (!EGSHasMirrorReady)
return;
H_AUTO(AIUpdate);
// Init stat counters
AISStat::countersBegin();
// Execute buffered Task
uint32 tick = CTimeInterface::gameCycle();
_TickedTaskList.execute(tick);
// Execute buffered functions that need to be executed in the correct context
execBufferedCommands();
execNamedEntityChanges();
// Update AI instances
FOREACH(it, CCont<CAIInstance>, CAIS::instance().AIList())
(*it)->CAIInstance::update();
// Send systematic messages to EGS
if (EGSHasMirrorReady)
{
// send the fauna description message to EGS then clear the content.
if (!_FaunaDescriptionList.Bots.empty())
{
nlassert(_FaunaDescriptionList.Bots.size() == _FaunaDescriptionList.GrpAlias.size());
_FaunaDescriptionList.send("EGS");
_FaunaDescriptionList.Bots.clear();
_FaunaDescriptionList.GrpAlias.clear();
}
// send agglomerated hp changes
if (!_CreatureChangeHPList.Entities.empty())
{
nlassert(_CreatureChangeHPList.Entities.size()==_CreatureChangeHPList.DeltaHp.size());
_CreatureChangeHPList.send("EGS");
_CreatureChangeHPList.Entities.clear();
_CreatureChangeHPList.DeltaHp.clear();
}
}
//
// update persistent variables every 1024 tick if AI script data manager is flagged
if ((tick & 0x3FF) == 0)
{
updatePersistentVariables();
}
//TODO: UserModelManager must send UserModels to EGS if not done yet
//CAIUserModelManager::getInstance()->sendUserModels();
// Terminate counters and store stats in an accessible place
AISStat::countersEnd();
}
// provoke a general 'save to backup' across the whole service
void CAIS::save()
{
nlinfo("*** save() NOT IMPLEMENTED YET ***");
}
//--------------------------------------------------------------------------
// management of iterator tables for vision matrices
//--------------------------------------------------------------------------
const CAIEntityMatrixIteratorTblLinear* CAIS::bestLinearMatrixIteratorTbl(uint32 distInMeters)
{
#if !FINAL_VERSION
nlassert(!_matrixIteratorsByDistance.empty());
#endif
if (distInMeters >= _matrixIteratorsByDistance.size())
{
//nlwarning("Try to access to a Vision Matrix to far %u the farest is only %u", distInMeters, _matrixIteratorsByDistance.size());
return _matrixIteratorsByDistance.back();
}
return _matrixIteratorsByDistance[distInMeters];
}
int getInt64FromStr (const char* str)
{
if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
return (int) atoiInt64(str+2,16);
return (int) atoiInt64(str,10);
}
// all these dynamics casts can be throwned away ..
CAIInstance* CAIS::tryToGetAIInstance(const char* str)
{
return dynamic_cast<CAIInstance*> (tryToGetEntity(str,CAIS::AI_INSTANCE));
}
CContinent *CAIS::tryToGetContinent (const char *str)
{
return dynamic_cast<CContinent*> (tryToGetEntity(str,CAIS::AI_CONTINENT));
}
CRegion *CAIS::tryToGetRegion (const char *str)
{
return dynamic_cast<CRegion*> (tryToGetEntity(str,CAIS::AI_REGION));
}
CCellZone *CAIS::tryToGetCellZone (const char *str)
{
return dynamic_cast<CCellZone*> (tryToGetEntity(str,CAIS::AI_CELL_ZONE));
}
CFamilyBehavior *CAIS::tryToGetFamilyBehavior (const char *str)
{
return dynamic_cast<CFamilyBehavior*> (tryToGetEntity(str,CAIS::AI_FAMILY_BEHAVIOR));
}
CManager* CAIS::tryToGetManager(const char* str)
{
return dynamic_cast<CManager*> (tryToGetEntity(str,CAIS::AI_MANAGER));
}
CGroup* CAIS::tryToGetGroup(const char* str)
{
return dynamic_cast<CGroup*> (tryToGetEntity(str,CAIS::AI_GROUP));
}
CBot* CAIS::tryToGetBot(const char* str)
{
return dynamic_cast<CBot*> (tryToGetEntity(str,CAIS::AI_BOT));
}
CAIEntity* CAIS::tryToGetAIEntity(const char* str)
{
return dynamic_cast<CAIEntity*> (tryToGetEntity(str));
}
CAIEntityPhysical* CAIS::tryToGetEntityPhysical(const char* str)
{
CEntityId entityId;
entityId.fromString(str);
return CAIS::getEntityPhysical(CMirrors::DataSet->getDataSetRow(entityId));
}
CAIEntity* CAIS::tryToGetEntity(const char* str, TSearchType searchType)
{
CAIInstance *aii = NULL;
CManager *mgr = NULL;
CBot *bot = NULL;
CGroup *grp = NULL;
vector<string> parts;
explode(string(str), string(":"), parts, false);
if (parts.empty() || parts[0].empty())
return NULL;
// skip AIS number if any
if (parts[0].substr(0, 4) == "AIS_")
parts.erase(parts.begin());
// check
if (parts.empty() || parts[0].empty()) return NULL;
// instance index
uint32 index = atoui(parts[0].c_str());
if (index >= CAIS::instance().AIList().size())
goto tryWithEntityId;
aii = CAIS::AIList()[index];
if (!aii)
goto tryWithEntityId;
if (searchType==CAIS::AI_INSTANCE
|| (searchType == CAIS::AI_UNDEFINED && parts.size() == 1))
return aii;
parts.erase(parts.begin());
// check
if (parts.empty() || parts[0].empty()) return NULL;
// branch on static or dynamic system
// manager index
if (parts[0].find("dyn_") == 0)
{
// parse dynamic id
// continent index
index = atoui(parts[0].substr(4).c_str());
if (index >= aii->continents().size())
return NULL;
CContinent *continent = aii->continents()[index];
if (searchType==CAIS::AI_CONTINENT
|| (searchType == CAIS::AI_UNDEFINED && parts.size() == 1))
return continent;
parts.erase(parts.begin());
// check
if (parts.empty() || parts[0].empty()) return NULL;
// region index
index = atoui(parts[0].c_str());
if (index >= continent->regions().size())
return NULL;
CRegion *region = continent->regions()[index];
if (searchType==CAIS::AI_REGION
|| (searchType == CAIS::AI_UNDEFINED && parts.size() == 1))
return region;
parts.erase(parts.begin());
// check
if (parts.empty() || parts[0].empty()) return NULL;
// cellzone index
index = atoui(parts[0].c_str());
if (index >= region->cellZones().size())
return NULL;
CCellZone *cz = region->cellZones()[index];
if (searchType==CAIS::AI_CELL_ZONE
|| (searchType == CAIS::AI_UNDEFINED && parts.size() == 1))
return cz;
parts.erase(parts.begin());
// check
if (parts.empty() || parts[0].empty()) return NULL;
// family behavior index
index = atoui(parts[0].c_str());
if (index >= cz->familyBehaviors().size())
return NULL;
CFamilyBehavior *fb = cz->familyBehaviors()[index];
if (searchType==CAIS::AI_FAMILY_BEHAVIOR
|| (searchType == CAIS::AI_UNDEFINED && parts.size() == 1))
return fb;
parts.erase(parts.begin());
// check
if (parts.empty() || parts[0].empty())
return NULL;
// manager index
if (parts[0] == "npc")
mgr = fb->mgrNpc();
else if (parts[0] == "fauna")
mgr = fb->mgrFauna();
if (!mgr)
return NULL;
if (searchType==CAIS::AI_MANAGER
|| (searchType == CAIS::AI_UNDEFINED && parts.size() == 1)
|| mgr == NULL)
return mgr;
parts.erase(parts.begin());
// check
if (parts.empty() || parts[0].empty()) return NULL;
}
else
{
// parse static id
// Manager index
index = atoui(parts[0].c_str());
if (index >= aii->managers().size())
return NULL;
mgr = aii->managers()[index];
if (searchType==CAIS::AI_MANAGER
|| (searchType == CAIS::AI_UNDEFINED && parts.size() == 1)
|| mgr == NULL)
return mgr;
parts.erase(parts.begin());
// check
if (parts.empty() || parts[0].empty()) return NULL;
}
// group index
index = atoui(parts[0].c_str());
if (index >= mgr->groups().size())
return NULL;
grp = mgr->groups()[index];
if (searchType==CAIS::AI_GROUP
|| (searchType == CAIS::AI_UNDEFINED && parts.size() == 1))
return grp;
parts.erase(parts.begin());
// check
if (parts.empty() || parts[0].empty()) return NULL;
// bot index
index = atoui(parts[0].c_str());
if (index >= grp->bots().size())
return NULL;
bot = grp->bots()[index];
if (searchType==CAIS::AI_BOT
|| (searchType == CAIS::AI_UNDEFINED && parts.size() == 1))
return bot;
// what ?
return NULL;
tryWithEntityId:
CEntityId entityId;
entityId.fromString(str);
if (entityId.isUnknownId())
return NULL;
CCont<CAIInstance>::iterator instanceIt=CAIS::instance().AIList().begin(), instanceItEnd=CAIS::instance().AIList().end();
while (instanceIt!=instanceItEnd)
{
CAIInstance *instancePtr=*instanceIt;
CCont<CManager>::iterator it=instancePtr->managers().begin(), itEnd=instancePtr->managers().end();
while (it!=itEnd)
{
CManager *mgrPtr = *it;
CGroup *grpPtr = mgrPtr->getNextValidGroupChild ();
while (grpPtr)
{
CBot *botPtr = grpPtr->getNextValidBotChild();
while (botPtr)
{
if ( botPtr->isSpawned()
&& botPtr->getSpawnObj()->getEntityId() == entityId)
return dynamic_cast<CAIEntity*> (botPtr);
botPtr = grpPtr->getNextValidBotChild (botPtr);
}
grpPtr=mgrPtr->getNextValidGroupChild (grpPtr);
}
++it;
}
++instanceIt;
}
return NULL;
}
//--------------------------------------------------------------------------
// manageing the set of maps
//--------------------------------------------------------------------------
CAIEntityPhysical *CAIS::getEntityPhysical(const TDataSetRow& row)
{
CHashMap<int,NLMISC::CDbgPtr<CAIEntityPhysical> >::iterator it(_CAIEntityByDataSetRow.find(row.getIndex()));
if (it!=_CAIEntityByDataSetRow.end())
return (*it).second;
else
return NULL;
// the code below generates an error .. :( hu !
}
//-------------------------------------------------------------------
// Interface to bot chat - callbacks called when bots start or
// stop chatting with player(s)
//-------------------------------------------------------------------
void CAIS::beginBotChat(const TDataSetRow &bot,const TDataSetRow &player)
{
#ifdef NL_DEBUG
/// Is this still true?
nlwarning("Chat can't work now as bot are now splitted in persistent and spawnable part. Have to rework on this part.");
#endif
// get a pointer to the bot
CSpawnBotNpc* botNpc=dynamic_cast<CSpawnBotNpc*>(CAIS::getEntityPhysical(bot));
if (!botNpc)
{
// nlwarning("CAIS::beginBotChat(): Bot chat message identifies an entity that isn't an NPC!!!");
return;
}
// get a pointer to the player
CBotPlayer* plrPtr = dynamic_cast<CBotPlayer*>(CAIS::getEntityPhysical(player));
if (plrPtr==NULL)
{
// nlwarning("CAIS::beginBotChat(): Bot chat message identifies an unknown player!!!");
return;
}
// have the bot register the chat
botNpc->beginBotChat(plrPtr);
}
void CAIS::endBotChat(const TDataSetRow &bot, const TDataSetRow &player)
{
// get a pointer to the bot
CSpawnBotNpc* botNpc=dynamic_cast<CSpawnBotNpc*>(CAIS::getEntityPhysical(bot));
if (!botNpc)
{
// nlwarning("CAIS::endBotChat(): Bot chat message identifies an entity that isn't an NPC!!!");
return;
}
// get a pointer to the player
CBotPlayer* plrPtr = dynamic_cast<CBotPlayer*>(CAIS::getEntityPhysical(player));
if (!plrPtr)
{
// nlwarning("CAIS::endBotChat(): Bot chat message identifies an unknown player!!!");
return;
}
// have the bot register the chat end
botNpc->endBotChat(plrPtr);
}
void CAIS::beginDynChat(const TDataSetRow &bot)
{
// get a pointer to the bot
CSpawnBotNpc* botNpc=dynamic_cast<CSpawnBotNpc*>(CAIS::getEntityPhysical(bot));
if (!botNpc)
{
// nlwarning("CAIS::beginBotChat(): Bot chat message identifies an entity that isn't an NPC!!!");
return;
}
// have the bot register the chat
botNpc->beginDynChat();
nldebug( "DYNCHT: E%u: %u dyn chats", bot.getIndex(), botNpc->getNbActiveDynChats() );
}
void CAIS::endDynChat(const TDataSetRow &bot)
{
// get a pointer to the bot
CSpawnBotNpc* botNpc=dynamic_cast<CSpawnBotNpc*>(CAIS::getEntityPhysical(bot));
if (!botNpc)
{
// nlwarning("CAIS::endBotChat(): Bot chat message identifies an entity that isn't an NPC!!!");
return;
}
// have the bot register the chat end
botNpc->endDynChat();
nldebug( "DYNCHT: E%u: %u dyn chats", bot.getIndex(), botNpc->getNbActiveDynChats() );
}
void CAIPlaceXYR::display(CStringWriter &stringWriter) const
{
stringWriter.append("XYR: ("+_pos.x().toString()
+" "
+_pos.y().toString()
+" "+toString(_pos.h())
+") Radius "
+toString(_radius)
+" "
+getName());
// nlinfo("XYR: (%s,%s,%d) x %f :%s",_pos.x().toString().c_str(),_pos.y().toString().c_str(),_pos.h(),_radius,getName().c_str());
}
void CAIS::warnBadInstanceMsgImp(const std::string &serviceName, TServiceId serviceId, CWarnBadInstanceMsgImp &msg)
{
// EGS says that an instance is spoofing an instance number or using a bad static instance number/continent name association.
// we must despawn/delete the aiinstance
FOREACH(it, CCont<CAIInstance>, _AIInstances)
{
if ((*it)->getInstanceNumber()!=msg.InstanceNumber)
continue;
// ok, we found the bad guy !
nlwarning("CAIS::warnBadInstanceMsgImp: despawning AIInstance %u, instance number %u, continent '%s'",
(*it)->getChildIndex(), msg.InstanceNumber, (*it)->getContinentName().c_str());
_AIInstances.removeChildByIndex((*it)->getChildIndex());
return;
}
// not found
nlwarning("CAIS::warnBadInstanceMsgImp: can't find AIInstance with instance number %u ! Can't despawn it", msg.InstanceNumber);
}
void CAIS::updatePersistentVariables()
{
if (CAIScriptDataManager::getInstance()->needsPersistentVarUpdate() == true)
{
// sending data to bs
CPersistentDataRecord pdr("AiTokenFamily");
CAIScriptDataManager::getInstance()->getPersistentVariables().store(pdr);
uint32 bufSize= pdr.totalDataSize();
vector<char> buffer;
buffer.resize(bufSize);
pdr.toBuffer(&buffer[0],bufSize);
CBackupMsgSaveFile msg( CAIScriptDataManager::getInstance()->makePdrFileName(), CBackupMsgSaveFile::SaveFile, Bsi );
msg.DataMsg.serialBuffer((uint8*)&buffer[0], bufSize);
Bsi.sendFile( msg );
CAIScriptDataManager::getInstance()->clearDirtyFlag();
}
}
CAIInstance *CAIS::getAIInstance(uint32 instanceNumber)
{
FOREACH(it, CCont<CAIInstance>, _AIInstances)
{
if ((*it)->getInstanceNumber()==instanceNumber)
return *it;
}
return NULL;
}