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

1533 lines
42 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 "server_share/r2_vision.h"
#include "ai_instance.h"
#include "ai_player.h"
#include "ai_grp_npc.h"
#include "ai_bot_fauna.h"
#include "ai_grp_fauna.h"
#include "ai_bot_easter_egg.h"
#include "ai_instance_inline.h"
#include "commands.h"
#include "messages.h"
using namespace std;
using namespace NLMISC;
using namespace MULTI_LINE_FORMATER;
CAIInstance::CAIInstance(CAIS* owner)
: CChild<CAIS>(owner)
{
_PlayerManager = new CManagerPlayer(this);
_PetManager = static_cast<CMgrPet*>(newMgr(AITYPES::MgrTypePet, 0, "Pet Manager", "MapName: Pet Manager", ""));
_EventNpcManager = static_cast<CMgrNpc*>(newMgr(AITYPES::MgrTypeNpc, 0, "Event NPCs Manager", "MapName: Event NPCs Manager", ""));
_EasterEggManager = static_cast<CMgrNpc*>(newMgr(AITYPES::MgrTypeNpc, 0, "Easter Eggs Manager", "MapName: Easter Eggs Manager", ""));
_SquadFamily = new COutpostSquadFamily(this, 0, "Squads");
}
CAIInstance::~CAIInstance()
{
// delete managers
if (_PlayerManager != NULL)
{
delete _PlayerManager;
_PlayerManager = NULL;
}
_PetManager = NULL;
_EventNpcManager = NULL;
_Managers.clear();
// check if ref pointers were notified of the destruction
nlassert(_EasterEggGroup == NULL);
nlassert(_EasterEggManager == NULL);
_SquadFamily = NULL;
_SquadVariantNameToGroupDesc.clear();
// delete continents
_Continents.clear();
}
static bool zoneHaveError = false;
/// map of lastCreatedNpcGroup by player id
static std::map<CEntityId, string> _PlayersLastCreatedNpcGroup;
void CAIInstance::addZone(string const& zoneName, CNpcZone* zone)
{
#if !FINAL_VERSION
TZoneList::iterator it = zoneList.find(CStringMapper::map(zoneName));
if (it!=zoneList.end())
{
nlwarning("this NpcZone have the same name than another: %s", zoneName.c_str());
zoneHaveError = true;
}
#endif
zoneList[CStringMapper::map(zoneName)] = zone;
}
void CAIInstance::removeZone(string const& zoneName, CNpcZone* zone)
{
TZoneList::iterator it = zoneList.find(CStringMapper::map(zoneName));
zoneList.erase(it);
}
void CAIInstance::updateZoneTrigger(CBotPlayer* player)
{
std::list<CMgrNpc*> zoneTriggerManager;
FOREACH(it, CCont<CManager>, _Managers)
{
std::string name = it->getName();
uint32 size = (uint32)name.size();
const uint32 extensionSize = 13; // strlen(".zone_trigger");
if (size >= 13 && name.substr(size - extensionSize, extensionSize) == ".zone_trigger" )
{
CMgrNpc* npcManager = dynamic_cast<CMgrNpc*>(*it);
if (npcManager && npcManager->getStateMachine())
{
zoneTriggerManager.push_back(npcManager);
}
}
}
if ( zoneTriggerManager.empty() )
{
return;
}
uint64 whoSeesMe = CMirrors::whoSeesMe(player->dataSetRow());
if (!R2_VISION::isEntityVisibleToPlayers(whoSeesMe))
{
// The player is invisible, its a Dm / Gm: no zone event must be triggered
return;
}
std::set<uint32> insideZone;
std::vector<uint32> enterZone;
std::vector<uint32> leaveZone;
FOREACH(it, std::list<CMgrNpc*>, zoneTriggerManager)
{
CMgrNpc* npcManager = *it;
CAliasCont<CGroup>& groups = npcManager->groups();
CAliasCont<CGroup>::iterator first2(groups.begin()), last2(groups.end());
for ( ; first2 != last2 ; ++first2)
{
CAIState* startState = first2->getPersistentStateInstance()->getStartState();
if (startState->isPositional())
{
const CAIStatePositional* posit = dynamic_cast<const CAIStatePositional*>(startState);
if (posit)
{
CAIVector pos( player->pos() );
bool inside = posit->contains(pos);
if (inside)
{
insideZone.insert(first2->getAlias());
}
}
}
}
}
player->updateInsideTriggerZones(insideZone, enterZone, leaveZone);
static NLMISC::TStringId nbPlayer = CStringMapper::map("NbPlayer");
FOREACH(zoneIt, std::vector<uint32>, enterZone)
{
FOREACH(it, std::list<CMgrNpc*>, zoneTriggerManager)
{
CGroup* grp = (*it)->groups().getChildByAlias(*zoneIt);
if (grp)
{
CGroupNpc* grpNpc = dynamic_cast<CGroupNpc*>(grp);
CPersistentStateInstance* sa = grpNpc->getPersistentStateInstance();
if (sa)
{
sa->setLogicVar(nbPlayer, sa->getLogicVar(nbPlayer) + 1);
}
grpNpc->processStateEvent(grpNpc->getEventContainer().EventPlayerEnterTriggerZone);
}
}
}
FOREACH(zoneIt, std::vector<uint32>, leaveZone)
{
FOREACH(it, std::list<CMgrNpc*>, zoneTriggerManager)
{
CGroup* grp = (*it)->groups().getChildByAlias(*zoneIt);
if (grp)
{
CGroupNpc* grpNpc = dynamic_cast<CGroupNpc*>(grp);
CPersistentStateInstance* sa = grpNpc->getPersistentStateInstance();
if (sa)
{
sa->setLogicVar(nbPlayer, sa->getLogicVar(nbPlayer) - 1);
}
grpNpc->processStateEvent(grpNpc->getEventContainer().EventPlayerLeaveTriggerZone);
}
}
}
}
CNpcZone* CAIInstance::getZone(TStringId zoneName)
{
return zoneList[zoneName];
}
std::string CAIInstance::getManagerIndexString(CManager const* manager) const
{
return getIndexString()+NLMISC::toString(":m%u", manager->getChildIndex());
}
void CAIInstance::initInstance(string const& continentName, uint32 instanceNumber)
{
_ContinentName = continentName;
_InstanceNumber = instanceNumber;
sendInstanceInfoToEGS();
if (!EGSHasMirrorReady)
return;
// check all player in mirror to insert them in this instance.
TEntityIdToEntityIndexMap::const_iterator it, itEnd(TheDataset.entityEnd());
for (it = TheDataset.entityBegin(); it!=itEnd; ++it)
{
if (it->first.getType()!=RYZOMID::player)
continue;
TDataSetRow const row = TheDataset.getCurrentDataSetRow(it->second);
if (!row.isValid())
continue;
// this is a player, check the instance number
CMirrorPropValueRO<uint32> playerInstance(TheDataset, row, DSPropertyAI_INSTANCE);
if (playerInstance != instanceNumber)
continue;
// ok, add this player
getPlayerMgr()->addSpawnedPlayer(row, it->first);
}
}
void CAIInstance::serviceEvent(CServiceEvent const& info)
{
if (info.getServiceName() == "EGS")
sendInstanceInfoToEGS();
FOREACH(it, CCont<CManager>, managers())
it->serviceEvent(info);
if (CMgrPet* petManager = getPetMgr())
petManager->serviceEvent(info);
if (_EventNpcManager)
_EventNpcManager->serviceEvent(info);
FOREACH(first, CCont<CContinent>, continents())
(*first)->serviceEvent(info);
}
void CAIInstance::sendInstanceInfoToEGS()
{
// send message to the EGS about this new instance
if (EGSHasMirrorReady)
{
CReportStaticAIInstanceMsg msg;
msg.InstanceNumber = _InstanceNumber;
msg.InstanceContinent = _ContinentName;
msg.send("EGS");
}
}
bool CAIInstance::advanceUserTimer(uint32 nbTicks)
{
// for each manager, look for a timer event
FOREACH(it, CCont<CManager>, _Managers)
{
CGroup* grp = (*it)->getNextValidGroupChild();
while (grp)
{
grp->getPersistentStateInstance()->advanceUserTimer(nbTicks);
grp=(*it)->getNextValidGroupChild(grp); // next group
}
}
return true;
}
void CAIInstance::addGroupInfo(CGroup* grp)
{
string const& name = grp->aliasTreeOwner()->getName();
uint32 alias = grp->aliasTreeOwner()->getAlias();
if (!name.empty())
_GroupFromNames[name].push_back(grp);
if (alias)
_GroupFromAlias[alias] = grp;
}
void CAIInstance::removeGroupInfo(CGroup* grp, CAliasTreeOwner* grpAliasTreeOwner)
{
string const& name = grpAliasTreeOwner->getName();
uint32 alias = grpAliasTreeOwner->getAlias();
if (!name.empty())
{
std::map< std::string, std::vector<NLMISC::CDbgPtr<CGroup> > >::iterator it=_GroupFromNames.find(name);
// remove from name
if (it!=_GroupFromNames.end())
{
std::vector<NLMISC::CDbgPtr<CGroup> > &v = it->second; // _GroupFromNames[name];
std::vector<NLMISC::CDbgPtr<CGroup> >::iterator itGrp(find(v.begin(), v.end(), NLMISC::CDbgPtr<CGroup>(grp) ));
if (itGrp != v.end())
v.erase(itGrp);
}
}
// remove from alias
if (alias)
_GroupFromAlias.erase(alias);
}
CGroup* CAIInstance::findGroup(uint32 alias)
{
std::map<uint32, NLMISC::CDbgPtr<CGroup> >::iterator it(_GroupFromAlias.find(alias));
if (it != _GroupFromAlias.end())
return it->second;
return NULL;
}
void CAIInstance::findGroup(std::vector<CGroup*>& result, std::string const& name)
{
std::map<std::string, std::vector<NLMISC::CDbgPtr<CGroup> > >::iterator it(_GroupFromNames.find(name));
if (it != _GroupFromNames.end())
result.insert(result.end(), it->second.begin(), it->second.end());
}
void CAIInstance::addMissionInfo(std::string const& missionName, uint32 alias)
{
std::string const* otherName;
while (true)
{
otherName = &findMissionName(alias);
if (otherName->empty())
break;
nlwarning("Replacing mission name for alias %u from '%s' to '%s'", alias, otherName->c_str(), missionName.c_str());
vector<uint32> &aliases = _MissionToAlias[*otherName];
aliases.erase(find(aliases.begin(), aliases.end(), alias));
}
vector<uint32>& aliases = _MissionToAlias[missionName];
if (std::find(aliases.begin(), aliases.end(), alias) == aliases.end())
aliases.push_back(alias);
}
std::string const& CAIInstance::findMissionName(uint32 alias)
{
map<string, vector<uint32> >::iterator it, itEnd(_MissionToAlias.end());
for (it=_MissionToAlias.begin(); it!=itEnd; ++it)
{
vector<uint32>::iterator it2, itEnd2(it->second.end());
for (it2=it->second.begin(); it2!=itEnd2; ++it2)
{
if (*it2 == alias)
return it->first;
}
}
static string emptyString;
return emptyString;
}
void CAIInstance::update()
{
// Call the player manager's updates
{
H_AUTO(PlayerMgrUpdate)
getPlayerMgr()->update();
}
// Call the managers' updates
{
H_AUTO(ManagersUpdate)
FOREACH(it, CCont<CManager>, _Managers)
it->CManager::update();
}
// call continent's update
{
H_AUTO(ContinentsUpdate)
FOREACH(it, CCont<CContinent>, _Continents)
it->CContinent::update();
}
}
CManager* CAIInstance::tryToGetManager(char const* str)
{
return dynamic_cast<CManager*>(tryToGetEntity(str, CAIS::AI_MANAGER));
}
CGroup* CAIInstance::tryToGetGroup(char const* str)
{
return dynamic_cast<CGroup*>(tryToGetEntity(str, CAIS::AI_GROUP));
}
// :TODO: Document that function
CAIEntity* CAIInstance::tryToGetEntity(char const* str, CAIS::TSearchType searchType)
{
char ident[512];
char* id = ident;
strcpy(ident,str);
char *mgr=NULL;
char *grp=NULL;
char *bot=NULL;
char lastChar;
CManager *mgrPtr=NULL;
CGroup *grpPtr=NULL;
CBot *botPtr=NULL;
uint32 localIndex=~0;
mgr = id;
while((*id!=':')&&(*id!=0)) id++;
lastChar = *id;
*id=0;
id++;
localIndex=getInt64FromStr(mgr);
if (localIndex<managers().size())
mgrPtr = managers()[localIndex];
if (!mgrPtr)
goto tryWithEntityId;
if ( lastChar==0
|| searchType==CAIS::AI_MANAGER)
return dynamic_cast<CAIEntity*> (mgrPtr);
grp = id;
while((*id!=':')&&(*id!=0)) id++;
lastChar = *id;
*id=0;
id++;
grpPtr = mgrPtr->getGroup(getInt64FromStr(grp));
if (!grpPtr)
goto tryWithEntityId;
if ( lastChar==0
|| searchType==CAIS::AI_GROUP)
return dynamic_cast<CAIEntity*> (grpPtr);
bot = id;
while((*id!=':')&&(*id!=0)) id++;
lastChar = *id;
*id=0;
botPtr = grpPtr->getBot(getInt64FromStr(bot));
if (!botPtr)
goto tryWithEntityId;
if ( lastChar==0
|| searchType==CAIS::AI_BOT)
return dynamic_cast<CAIEntity*> (botPtr);
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)
{
mgrPtr = *it;
grpPtr = mgrPtr->getNextValidGroupChild ();
while (grpPtr)
{
botPtr = grpPtr->getNextValidBotChild();
while (botPtr)
{
if (botPtr->isSpawned() && botPtr->getSpawnObj()->getEntityId() == entityId)
// if (botPtr->createEntityId()==entityId)
return dynamic_cast<CAIEntity*> (botPtr);
botPtr = grpPtr->getNextValidBotChild (botPtr);
}
grpPtr=mgrPtr->getNextValidGroupChild (grpPtr);
}
++it;
}
++instanceIt;
}
return NULL;
}
//--------------------------------------------------------------------------
// factory method newMgr()
//--------------------------------------------------------------------------
// factory for creating new managers and for adding them to the _Managers map
// asserts if the id is invalid (<0 or >1023)
// asserts if the id is already in use
CManager* CAIInstance::newMgr(AITYPES::TMgrType type, uint32 alias, std::string const& name, std::string const& mapName, std::string const& filename)
{
CManager* mgr = CManager::createManager(type, this, alias, name, "", filename);
// add the manager into the singleton's managers vector
if (mgr)
{
_Managers.addChild(mgr);
mgr->init ();
}
return mgr;
}
bool CAIInstance::spawn()
{
// Spawn instance managers
CCont<CManager>::iterator itManager, itManagerEnd(_Managers.end());
for (itManager=_Managers.begin(); itManager!=itManagerEnd; ++itManager)
{
CManager* manager = *itManager;
manager->spawn();
}
// Spawn continents
CCont<CContinent>::iterator itContinent, itContinentEnd(continents().end());
for (itContinent=continents().begin(); itContinent!=itContinentEnd; ++itContinent)
{
CContinent* continent = *itContinent;
continent->spawn();
}
// We should check individual errors, but fake success here :)
return true;
}
bool CAIInstance::despawn()
{
// Despawn instance managers
CCont<CManager>::iterator itManager, itManagerEnd(_Managers.end());
for (itManager=_Managers.begin(); itManager!=itManagerEnd; ++itManager)
{
CManager* manager = *itManager;
manager->despawnMgr();
}
// Despawn continents
CCont<CContinent>::iterator itContinent, itContinentEnd(_Continents.end());
for (itContinent=_Continents.begin(); itContinent!=itContinentEnd; ++itContinent)
{
CContinent* continent = *itContinent;
continent->despawn();
}
// We should check individual errors, but fake success here :)
return true;
}
//----------------------------------------------------------------------------
// deleteMgr()
//----------------------------------------------------------------------------
// erase a manager (free resources, free id, etc, etc
// asserts if the id is invalid (<0 or >1023)
// displays a warning and returns cleanly if the id is unused
void CAIInstance::deleteMgr(sint mgrId)
{
_Managers.removeChildByIndex(mgrId);
}
//----------------------------------------------------------------------------
// Interface to kami management
//----------------------------------------------------------------------------
void CAIInstance::registerKamiDeposit(uint32 alias, CGroupNpc* grp)
{
if (_KamiDeposits.find(alias) !=_KamiDeposits.end() && _KamiDeposits[alias]!=grp)
{
nlwarning("Failed to register Kami deposit '%s' with alias '%u' because alias already assigned to deposit '%s'",
(grp==NULL || grp->getAliasNode()==NULL) ? "NULL" : grp->getAliasNode()->fullName().c_str(),
alias,
(_KamiDeposits[alias]==NULL || _KamiDeposits[alias]->getAliasNode()==NULL)? "NULL": _KamiDeposits[alias]->getAliasNode()->fullName().c_str()
);
return;
}
if (grp!=NULL)
_KamiDeposits[alias] = grp;
else
nlwarning("Attempt to assign Kami deposit alias '%u' to an inexistant group",alias);
}
void CAIInstance::unregisterKamiDeposit(uint32 alias)
{
if (_KamiDeposits.find(alias) !=_KamiDeposits.end())
_KamiDeposits.erase(_KamiDeposits.find(alias));
}
//----------------------------------------------------------------------------
std::string CAIInstance::getIndexString() const
{
return getOwner()->getIndexString()+NLMISC::toString(":i%u", getChildIndex());
}
std::string CAIInstance::getOneLineInfoString() const
{
return "AI instance number " + NLMISC::toString("%u", getInstanceNumber()) + ", continent '" + getContinentName() + "'";
}
std::vector<std::string> CAIInstance::getMultiLineInfoString() const
{
std::vector<std::string> container;
pushTitle(container, "CAIInstance");
pushEntry(container, "id=" + getIndexString());
container.back() += " num=" + NLMISC::toString("%u", getInstanceNumber());
container.back() += " continent=" + getContinentName();
pushFooter(container);
return container;
}
//----------------------------------------------------------------------------
#include "ai_bot_npc.h"
#include "ai_profile_npc.h"
inline
static CAIVector randomPos(double dispersionRadius)
{
if (dispersionRadius<=0.)
{
return CAIVector(0., 0.);
}
uint32 const maxLimit=((uint32)~0U)>>1;
double rval = (double)CAIS::rand32(maxLimit)/(double)maxLimit; // [0-1[
double r = dispersionRadius*sqrt(rval);
rval = (double)CAIS::rand32(maxLimit)/(double)maxLimit; // [0-1[
double t = 2.0*NLMISC::Pi*rval;
double dx = cos(t)*r;
double dy = sin(t)*r;
return CAIVector(dx, dy);
}
inline
static float randomAngle()
{
uint32 const maxLimit = CAngle::PI*2;
float val = (float)CAIS::rand32(maxLimit);
return val;
}
CGroupNpc* CAIInstance::eventCreateNpcGroup(uint nbBots, NLMISC::CSheetId const& sheetId, CAIVector const& pos, double dispersionRadius, bool spawnBots, double orientation, const std::string &botsName, const std::string &look)
{
if (!_EventNpcManager)
return NULL;
AISHEETS::ICreatureCPtr sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
if (!sheet)
{
nlwarning("invalid sheet while creating event npc group");
return NULL;
}
// Create a group
CGroupNpc* grp = new CGroupNpc(_EventNpcManager, NULL, RYAI_MAP_CRUNCH::Nothing);
// Register it in the manager
_EventNpcManager->groups().addAliasChild(grp);
// Set the group parameters
grp->setAutoSpawn(false);
grp->setName(NLMISC::toString("event_group_%u", grp->getChildIndex()));
grp->clearParameters();
grp->setPlayerAttackable(true);
grp->setBotAttackable(true);
grp->autoDestroy(true);
grp->clrBotsAreNamedFlag();
{
// build unnamed bot
for (uint i=0; i<nbBots; ++i)
{
grp->bots().addChild(new CBotNpc(grp, 0, botsName.empty() ? grp->getName():botsName), i); // Doub: 0 instead of getAlias()+i otherwise aliases are wrong
CBotNpc* const bot = NLMISC::safe_cast<CBotNpc*>(grp->bots()[i]);
bot->setSheet(sheet);
if (!look.empty())
bot->setClientSheet(look);
bot->equipmentInit();
bot->initEnergy(/*groupEnergyCoef()*/0);
CAIVector rpos(pos);
// Spawn all randomly except if only 1 bot
if (nbBots > 1)
{
RYAI_MAP_CRUNCH::CWorldMap const& worldMap = CWorldContainer::getWorldMap();
RYAI_MAP_CRUNCH::CWorldPosition wp;
uint32 maxTries = 100;
do
{
rpos = pos;
rpos += randomPos(dispersionRadius);
--maxTries;
}
while (!worldMap.setWorldPosition(AITYPES::vp_auto, wp, rpos) && maxTries>0);
if (maxTries<=0)
rpos = pos;
}
float angle = 0.f;
if (orientation < (NLMISC::Pi * 2.0) && orientation > (-NLMISC::Pi * 2.0))
angle = (float)orientation;
else
angle = randomAngle();
bot->setStartPos(rpos.x().asDouble(),rpos.y().asDouble(), angle, AITYPES::vp_auto);
}
}
grp->spawn();
CSpawnGroupNpc* spawnGroup = grp->getSpawnObj();
if (!spawnGroup)
{
// the spawning has failed, delete the useless object
nlwarning("Failed to spawn the event group");
_EventNpcManager->groups().removeChildByIndex(grp->getChildIndex());
return NULL;
}
NLMISC::CSmartPtr<CNpcZonePlaceNoPrim> destZone = NLMISC::CSmartPtr<CNpcZonePlaceNoPrim>(new CNpcZonePlaceNoPrim());
destZone->setPosAndRadius(AITYPES::vp_auto, CAIPos(pos, 0, 0), (uint32)(dispersionRadius*1000.));
spawnGroup->movingProfile().setAIProfile(new CGrpProfileWanderNoPrim(spawnGroup, destZone));
if (spawnBots)
grp->getSpawnObj()->spawnBots();
return grp;
}
CBotEasterEgg* CAIInstance::createEasterEgg(uint32 easterEggId, NLMISC::CSheetId const& sheetId, std::string const& botName, double x, double y, double z, double heading, const std::string& look)
{
if (_EasterEggManager == NULL)
return NULL;
if (_EasterEggGroup == NULL)
{
// create the easter egg group
CGroupNpc* grp = new CGroupNpc(_EasterEggManager, NULL, RYAI_MAP_CRUNCH::Nothing);
_EasterEggManager->groups().addAliasChild(grp);
// set the group parameters
grp->setAutoSpawn(false);
grp->setName("easter_egg_group");
grp->clearParameters();
grp->setPlayerAttackable(true);
grp->setBotAttackable(true); // if false the bot will not be attackable by players...
grp->autoDestroy(false);
// spawn the group
grp->spawn();
CSpawnGroupNpc* spawnGroup = grp->getSpawnObj();
if (spawnGroup == NULL)
{
// the spawning has failed, delete the useless object
nlwarning("Failed to spawn the easter egg group");
_EasterEggManager->groups().removeChildByIndex(grp->getChildIndex());
return NULL;
}
// easter eggs do not move
spawnGroup->movingProfile().setAIProfile(new CGrpProfileIdle(spawnGroup));
// keep a pointer on the easter egg group
_EasterEggGroup = grp;
}
nlassert(_EasterEggGroup != NULL);
if (getEasterEgg(easterEggId) != NULL)
{
nlwarning("An easter egg with ID %u already exists in instance %u", easterEggId, getInstanceNumber());
return NULL;
}
AISHEETS::ICreatureCPtr sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
if (!sheet)
{
nlwarning("Invalid sheet '%s' while creating an easter egg", sheetId.toString().c_str());
return NULL;
}
// build the easter egg bot
CBotEasterEgg* bot = new CBotEasterEgg(_EasterEggGroup, 0, botName, easterEggId);
_EasterEggGroup->bots().addChild(bot);
bot->setSheet(sheet);
if (!look.empty()) { bot->setClientSheet(look); }
bot->equipmentInit();
bot->initEnergy(0);
bot->setStartPos(x, y, float(heading), AITYPES::vp_auto);
bot->spawn();
if (!bot->isSpawned())
{
// the spawning has failed, delete the useless object
nlwarning("Failed to spawn the easter egg bot");
_EasterEggGroup->bots().removeChildByIndex(bot->getChildIndex());
return NULL;
}
return bot;
}
void CAIInstance::destroyEasterEgg(uint32 easterEggId)
{
CBotEasterEgg* bot = getEasterEgg(easterEggId);
if (bot != NULL)
{
bot->despawnBot();
_EasterEggGroup->bots().removeChildByIndex(bot->getChildIndex());
}
else
{
nlwarning("Cannot destroy easter egg %u because not found", easterEggId);
}
}
CBotEasterEgg* CAIInstance::getEasterEgg(uint32 easterEggId)
{
if (_EasterEggGroup == NULL)
return NULL;
FOREACH(itBot, CCont<CBot>, _EasterEggGroup->bots())
{
CBotEasterEgg* bot = dynamic_cast<CBotEasterEgg*>(*itBot);
#if !FINAL_VERSION
nlassert(bot != NULL);
#endif
if (bot != NULL && bot->getEasterEggId() == easterEggId)
return bot;
}
return NULL;
}
void cbEventCreateNpcGroup( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
{
uint32 messageVersion;
uint32 instanceNumber;
sint32 x;
sint32 y;
sint32 orientation;
uint32 nbBots;
NLMISC::CSheetId sheetId;
CEntityId playerId;
double dispersionRadius;
bool spawnBots;
std::string botsName;
std::string look;
msgin.serial(messageVersion);
nlassert(messageVersion==1);
msgin.serial(instanceNumber);
msgin.serial(playerId);
msgin.serial(x);
msgin.serial(y);
msgin.serial(orientation);
msgin.serial(nbBots);
msgin.serial(sheetId);
msgin.serial(dispersionRadius);
msgin.serial(spawnBots);
msgin.serial(botsName);
msgin.serial(look);
CAIInstance* instance = CAIS::instance().getAIInstance(instanceNumber);
if (instance)
{
CGroupNpc* npcGroup = instance->eventCreateNpcGroup(nbBots, sheetId, CAIVector((double)x/1000., (double)y/1000.), dispersionRadius, spawnBots, (double)orientation/1000., botsName, look);
if (npcGroup != NULL)
{
_PlayersLastCreatedNpcGroup[playerId] = npcGroup->getName();
}
}
}
extern vector<vector<string> > scriptCommands2;
void cbEventNpcGroupScript( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
{
uint32 messageVersion;
string botId;
uint32 nbString;
vector<string> strings;
msgin.serial(messageVersion);
nlassert(messageVersion==1);
msgin.serial(nbString);
string eid;
string firstCommand;
msgin.serial(eid); // Player or boteid
msgin.serial(firstCommand); // Player or boteid
if (firstCommand[0] == '(') // Old eventNpcGroupScript command : (boteid, commands...)
{
nlinfo("Event group script with %d strings :", nbString);
strings.resize(nbString);
strings[0] = eid;
nlinfo(" %d '%s'", 0, strings[0].c_str());
strings[1] = firstCommand;
nlinfo(" %d '%s'", 1, strings[1].c_str());
for (uint32 i=2; i<nbString-2; ++i)
{
msgin.serial(strings[i]);
nlinfo(" %d '%s'", i, strings[i].c_str());
}
}
else
{
nlinfo("Event group script with %d strings :", nbString-1);
CEntityId playerId(eid);
strings.resize(nbString-1);
NLMISC::CSString groupname = CSString(firstCommand);
if (firstCommand[0] == '#' && firstCommand[1] == '(')
{
NLMISC::CEntityId botEId = NLMISC::CEntityId(firstCommand.substr(1));
if (botEId==NLMISC::CEntityId::Unknown)
return;
CAIEntityPhysical* entity = CAIEntityPhysicalLocator::getInstance()->getEntity(botEId);
CSpawnBotNpc* bot = dynamic_cast<CSpawnBotNpc*>(entity);
if (!bot)
return;
if (!bot->getPersistent().getOwner())
return;
strings[0] = bot->getPersistent().getOwner()->getName();
}
else
{
strings[0] = (string)groupname.replace("#last", _PlayersLastCreatedNpcGroup[playerId].c_str());
}
nlinfo(" %d '%s'", 0, strings[0].c_str());
for (uint32 i=1; i<nbString-1; ++i)
{
msgin.serial(strings[i]);
nlinfo(" %d '%s'", i, strings[i].c_str());
}
}
scriptCommands2.push_back(strings);
}
void cbEventFaunaBotSetRadii( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
{
uint32 messageVersion;
string botName;
float notHungryRadius;
float hungryRadius;
float huntingRadius;
msgin.serial(messageVersion);
nlassert(messageVersion==1);
msgin.serial(botName);
msgin.serial(notHungryRadius);
msgin.serial(hungryRadius);
msgin.serial(huntingRadius);
vector<CBot*> bots;
/// try to find the bot name
buildFilteredBotList(bots, botName);
if (bots.empty())
{
nlwarning("No bot correspond to name %s", botName.c_str());
return;
}
else
{
FOREACH(itBot, vector<CBot*>, bots)
{
CBot* bot = *itBot;
CBotFauna* botFauna = dynamic_cast<CBotFauna*>(bot);
if (botFauna)
{
if (notHungryRadius>=0.f)
botFauna->setAggroRadiusNotHungry(notHungryRadius);
if (hungryRadius>=0.f)
botFauna->setAggroRadiusHungry(hungryRadius);
if (huntingRadius>=0.f)
botFauna->setAggroRadiusHunting(huntingRadius);
}
}
}
}
void cbEventFaunaBotResetRadii( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
{
uint32 messageVersion;
string botName;
msgin.serial(messageVersion);
nlassert(messageVersion==1);
msgin.serial(botName);
vector<CBot*> bots;
/// try to find the bot name
buildFilteredBotList(bots, botName);
if (bots.empty())
{
nlwarning("No bot correspond to name %s", botName.c_str());
return;
}
else
{
FOREACH(itBot, vector<CBot*>, bots)
{
CBot* bot = *itBot;
CBotFauna* botFauna = dynamic_cast<CBotFauna*>(bot);
if (botFauna)
botFauna->resetAggroRadius();
}
}
}
void cbEventBotCanAggro( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
{
uint32 messageVersion;
string botName;
bool canAggro;
msgin.serial(messageVersion);
nlassert(messageVersion==1);
msgin.serial(botName);
msgin.serial(canAggro);
vector<CBot*> bots;
/// try to find the bot name
buildFilteredBotList(bots, botName);
if (bots.empty())
{
nlwarning("No bot correspond to name %s", botName.c_str());
return;
}
else
{
FOREACH(itBot, vector<CBot*>, bots)
{
CBot* bot = *itBot;
if (bot)
{
CSpawnBot* spBot = bot->getSpawnObj();
if (spBot)
spBot->setCanAggro(canAggro);
}
}
}
}
void cbEventBotSheet( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
{
uint32 messageVersion;
string botName;
NLMISC::CSheetId sheetId;
bool autoSpawnDespawn;
string customName;
msgin.serial(messageVersion);
nlassert(messageVersion==3);
msgin.serial(botName);
msgin.serial(sheetId);
msgin.serial(autoSpawnDespawn);
msgin.serial(customName);
vector<CBot*> bots;
/// try to find the bot name
buildFilteredBotList(bots, botName);
if (bots.empty())
{
nlwarning("No bot correspond to name %s", botName.c_str());
return;
}
else
{
AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
if (sheetId!=NLMISC::CSheetId::Unknown && !sheet.isNull())
{
FOREACH(itBot, vector<CBot*>, bots)
{
CBot* bot = *itBot;
if (bot)
{
bot->setCustomName(customName);
bot->triggerSetSheet(sheet);
if (autoSpawnDespawn && !bot->isSpawned())
bot->spawn();
}
}
}
else if (autoSpawnDespawn)
{
FOREACH(itBot, vector<CBot*>, bots)
{
CBot* bot = *itBot;
if (bot && bot->isSpawned())
bot->despawnBot();
}
}
}
}
extern vector<vector<string> > scriptCommandsBotById;
void cbR2NpcBotScriptById(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
{
uint32 messageVersion;
string botId;
uint32 nbString;
vector<string> strings;
msgin.serial(messageVersion);
nlassert(messageVersion==1);
msgin.serial(nbString);
strings.resize(nbString);
for (uint32 i=0; i<nbString; ++i)
msgin.serial(strings[i]);
scriptCommandsBotById.push_back(strings);
}
extern vector<vector<string> > scriptCommandsGroupByName;
void cbR2NpcGroupScriptByName(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
{
uint32 messageVersion;
string botId;
uint32 nbString;
vector<string> strings;
msgin.serial(messageVersion);
nlassert(messageVersion==1);
msgin.serial(nbString);
strings.resize(nbString);
for (uint32 i=0; i<nbString; ++i)
msgin.serial(strings[i]);
scriptCommandsGroupByName.push_back(strings);
}
//////////////////////////////////////////////////////////////////////////////
// CBotDespawnNotification //
//////////////////////////////////////////////////////////////////////////////
class CBotDespawnNotification : private CBot::IObserver
{
public:
/// get the singleton instance
static CBotDespawnNotification* getInstance();
/// register a service to be notified when a bot will despawn
void registerService(NLNET::TServiceId serviceId, CBot* bot, const NLMISC::CEntityId& botId);
/// dump in a log
void dump(CLog& log) const;
private:
/// private ctor
CBotDespawnNotification();
/// callback of the observer
void notifyBotDespawn(CBot* bot);
void notifyBotDeath(CBot* bot);
void notifyStopNpcControl(CBot* bot);
/// the singleton instance
static CBotDespawnNotification* _Instance;
class CRegisteredService
{
public:
CRegisteredService(NLNET::TServiceId serviceId, const CEntityId& botId)
: _ServiceId(serviceId)
, _BotId(botId)
{
}
NLNET::TServiceId getServiceId() const { return _ServiceId; }
const CEntityId& getBotId() const { return _BotId; }
bool operator==(const CRegisteredService& other) const
{
return (_ServiceId == other._ServiceId && _BotId == other._BotId);
}
private:
NLNET::TServiceId _ServiceId;
CEntityId _BotId;
};
/// registered services by bot alias
typedef std::multimap<uint32, CRegisteredService> TRegisteredServiceMap;
TRegisteredServiceMap _RegisteredServices;
};
CBotDespawnNotification* CBotDespawnNotification::_Instance = NULL;
CBotDespawnNotification::CBotDespawnNotification()
{
}
CBotDespawnNotification* CBotDespawnNotification::getInstance()
{
if (_Instance == NULL)
_Instance = new CBotDespawnNotification;
return _Instance;
}
void CBotDespawnNotification::registerService(NLNET::TServiceId serviceId, CBot* bot, const NLMISC::CEntityId& botId)
{
nlassert(bot != NULL);
TRegisteredServiceMap::const_iterator first = _RegisteredServices.lower_bound(bot->getAlias());
TRegisteredServiceMap::const_iterator last = _RegisteredServices.upper_bound(bot->getAlias());
if (first == last)
bot->attachObserver(this);
CRegisteredService rs(serviceId, botId);
for (; first != last; ++first)
{
if ((*first).second == rs)
break;
}
if (first == last)
_RegisteredServices.insert( make_pair(bot->getAlias(), rs) );
}
void CBotDespawnNotification::dump(CLog& log) const
{
FOREACHC(it, TRegisteredServiceMap, _RegisteredServices)
{
const CRegisteredService& rs = (*it).second;
log.displayNL("BotAlias %s => ServiceId %hu, BotId %s",
LigoConfig.aliasToString((*it).first).c_str(),
rs.getServiceId().get(),
rs.getBotId().toString().c_str()
);
}
}
void CBotDespawnNotification::notifyBotDespawn(CBot* bot)
{
nlassert(bot != NULL);
// catch this event only once for current services registered for this bot
bot->detachObserver(this);
TRegisteredServiceMap::iterator first = _RegisteredServices.lower_bound(bot->getAlias());
TRegisteredServiceMap::iterator last = _RegisteredServices.upper_bound(bot->getAlias());
if (first == last)
{
nlwarning("No service requested despawn notification for bot %s",
LigoConfig.aliasToString(bot->getAlias()).c_str()
);
DEBUG_STOP;
return;
}
// notify all services registered for this bot
while (first != last)
{
const CRegisteredService& rs = (*first).second;
CMessages::notifyBotDespawn(rs.getServiceId(), bot->getAlias(), rs.getBotId());
TRegisteredServiceMap::iterator tmp = first;
++first;
_RegisteredServices.erase(tmp);
}
}
void CBotDespawnNotification::notifyStopNpcControl(CBot* bot)
{
nlassert(bot != NULL);
// catch this event only once for current services registered for this bot
//bot->detachObserver(this);
TRegisteredServiceMap::iterator first = _RegisteredServices.lower_bound(bot->getAlias());
TRegisteredServiceMap::iterator last = _RegisteredServices.upper_bound(bot->getAlias());
// notify all services registered for this bot
while (first != last)
{
const CRegisteredService& rs = (*first).second;
CMessages::notifyBotStopNpcControl(rs.getServiceId(), bot->getAlias(), rs.getBotId());
TRegisteredServiceMap::iterator tmp = first;
++first;
// _RegisteredServices.erase(tmp);
}
}
void CBotDespawnNotification::notifyBotDeath(CBot* bot)
{
nlassert(bot != NULL);
// catch this event only once for current services registered for this bot
bot->detachObserver(this);
TRegisteredServiceMap::iterator first = _RegisteredServices.lower_bound(bot->getAlias());
TRegisteredServiceMap::iterator last = _RegisteredServices.upper_bound(bot->getAlias());
if (first == last)
{
nlwarning("No service requested death notification for bot %s",
LigoConfig.aliasToString(bot->getAlias()).c_str()
);
DEBUG_STOP;
return;
}
// notify all services registered for this bot
while (first != last)
{
const CRegisteredService& rs = (*first).second;
CMessages::notifyBotDeath(rs.getServiceId(), bot->getAlias(), rs.getBotId());
TRegisteredServiceMap::iterator tmp = first;
++first;
_RegisteredServices.erase(tmp);
}
}
void cbAskBotDespawnNotification(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
{
uint32 messageVersion;
uint32 botAlias;
CEntityId botId;
msgin.serial(messageVersion);
nlassert(messageVersion == 1);
msgin.serial(botAlias);
msgin.serial(botId);
TDataSetRow botRowId = CMirrors::getDataSetRow(botId);
if (botRowId.isNull())
{
CMessages::notifyBotDespawn(serviceId, botAlias, botId);
return;
}
CAIEntityPhysical* entity = CAIS::instance().getEntityPhysical(botRowId);
if (entity == NULL)
{
CMessages::notifyBotDespawn(serviceId, botAlias, botId);
return;
}
#if !FINAL_VERSION
nlassert(entity->getEntityId() == botId);
#endif
CSpawnBot* bot = NULL;
switch (entity->getRyzomType())
{
case RYZOMID::creature:
case RYZOMID::npc:
bot = NLMISC::safe_cast<CSpawnBot*>(entity);
break;
default:
break;
}
// check if the bot has been replaced by another entity in mirror
if (bot == NULL || bot->getPersistent().getAlias() != botAlias)
{
CMessages::notifyBotDespawn(serviceId, botAlias, botId);
return;
}
// register the service to be notified when the bot will despawn
CBotDespawnNotification::getInstance()->registerService(serviceId, &bot->getPersistent(), botId);
}
void cbSpawnEasterEgg(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
{
uint32 messageVersion;
uint32 instanceNumber;
uint32 easterEggId;
CSheetId sheetId;
string botName;
string look;
sint32 x, y, z;
float heading;
msgin.serial(messageVersion);
nlassert(messageVersion==1);
msgin.serial(instanceNumber);
msgin.serial(easterEggId);
msgin.serial(sheetId);
msgin.serial(botName);
msgin.serial(look);
msgin.serial(x, y, z, heading);
CAIInstance* instance = CAIS::instance().getAIInstance(instanceNumber);
if (instance != NULL)
{
CBotEasterEgg* bot = instance->createEasterEgg(easterEggId, sheetId, botName, float(x)/1000., float(y)/1000., float(z)/1000., heading, look );
if (bot != NULL)
{
nlassert(bot->getSpawn() != NULL);
CEntityId botId = bot->getSpawn()->getEntityId();
NLNET::CMessage msgout("EASTER_EGG_SPAWNED");
msgout.serial(botId);
msgout.serial(easterEggId);
sendMessageViaMirror(serviceId, msgout);
}
else
{
nlwarning("Cannot spawn easter egg %u!", easterEggId);
}
}
else
{
nlwarning("Cannot spawn easter egg %u, AI instance %u not found!", easterEggId, instanceNumber);
}
}
void cbDespawnEasterEgg(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
{
uint32 messageVersion;
uint32 instanceNumber;
uint32 easterEggId;
msgin.serial(messageVersion);
nlassert(messageVersion==1);
msgin.serial(instanceNumber);
msgin.serial(easterEggId);
CAIInstance* instance = CAIS::instance().getAIInstance(instanceNumber);
if (instance != NULL)
{
instance->destroyEasterEgg(easterEggId);
}
}
NLMISC_COMMAND(simulateMsgAskBotDespawnNotification, "", "<service_id> <bot_alias> <bot_id>")
{
if (args.size() != 3)
return false;
uint16 id;
NLMISC::fromString(args[0], id);
NLNET::TServiceId serviceId(id);
uint32 botAlias = LigoConfig.aliasFromString(args[1]);
CEntityId botId(args[2].c_str());
NLNET::CMessage msg("ASK_BOT_DESPAWN_NOTIFICATION");
uint32 messageVersion = 1;
msg.serial(messageVersion, botAlias, botId);
msg.invert();
cbAskBotDespawnNotification(msg, "FAKE", serviceId);
return true;
}
NLMISC_COMMAND(dumpBotDespawnNotification, "", "")
{
if (args.size() != 0)
return false;
CBotDespawnNotification::getInstance()->dump(log);
return true;
}
NLMISC_COMMAND(simulateMsgSpawnEasterEgg, "", "<service_id> <ai_instance> <easter_egg_id> <sheet> <bot_name> <x> <y>")
{
if (args.size() != 7)
return false;
uint16 id;
NLMISC::fromString(args[0], id);
NLNET::TServiceId serviceId(id);
uint32 instanceNumber;
NLMISC::fromString(args[1], instanceNumber);
uint32 easterEggId;
NLMISC::fromString(args[2], easterEggId);
CSheetId sheetId(args[3]);
string botName = args[4];
sint32 x;
NLMISC::fromString(args[5], x);
sint32 y;
NLMISC::fromString(args[6], y);
sint32 z = 0;
sint32 heading = 0;
std::string look = "";
NLNET::CMessage msg("SPAWN_EASTER_EGG");
uint32 messageVersion = 1;
msg.serial(messageVersion);
msg.serial(instanceNumber);
msg.serial(easterEggId);
msg.serial(sheetId);
msg.serial(botName);
msg.serial(look);
msg.serial(x, y, z, heading);
msg.invert();
cbSpawnEasterEgg(msg, "FAKE", serviceId);
return true;
}
NLMISC_COMMAND(simulateMsgDespawnEasterEgg, "", "<service_id> <ai_instance> <easter_egg_id>")
{
if (args.size() != 3)
return false;
uint16 id;
NLMISC::fromString(args[0], id);
NLNET::TServiceId serviceId(id);
uint32 instanceNumber;
NLMISC::fromString(args[1], instanceNumber);
uint32 easterEggId;
NLMISC::fromString(args[2], easterEggId);
NLNET::CMessage msg("DESPAWN_EASTER_EGG");
uint32 messageVersion = 1;
msg.serial(messageVersion);
msg.serial(instanceNumber);
msg.serial(easterEggId);
msg.invert();
cbDespawnEasterEgg(msg, "FAKE", serviceId);
return true;
}