spawnBots accept now name and vpx, addedspawnGroup_ffsssffff_c
[ryzomcore.git] / ryzom / server / src / ai_service / ai_instance.cpp
blob6db8caac81c5a8d2420bc090fe72cd0f20f46611
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdpch.h"
18 #include "server_share/r2_vision.h"
19 #include "ai_instance.h"
20 #include "ais_actions.h"
22 #include "ai_player.h"
23 #include "ai_grp_npc.h"
24 #include "ai_bot_fauna.h"
25 #include "ai_grp_fauna.h"
26 #include "ai_bot_easter_egg.h"
28 #include "ai_instance_inline.h"
30 #include "nel/ligo/primitive_utils.h"
32 #include "commands.h"
33 #include "messages.h"
35 using namespace std;
36 using namespace NLMISC;
37 using namespace NLNET;
38 using namespace MULTI_LINE_FORMATER;
39 using namespace NLLIGO;
41 NLLIGO::CLigoConfig CAIInstance::_LigoConfig;
44 CAIInstance::CAIInstance(CAIS* owner)
45 : CChild<CAIS>(owner)
47 _PlayerManager = new CManagerPlayer(this);
48 _PetManager = static_cast<CMgrPet*>(newMgr(AITYPES::MgrTypePet, 0, "Pet Manager", "MapName: Pet Manager", ""));
49 _EventNpcManager = static_cast<CMgrNpc*>(newMgr(AITYPES::MgrTypeNpc, 0, "Event NPCs Manager", "MapName: Event NPCs Manager", ""));
50 _EasterEggManager = static_cast<CMgrNpc*>(newMgr(AITYPES::MgrTypeNpc, 0, "Easter Eggs Manager", "MapName: Easter Eggs Manager", ""));
51 _SquadFamily = new COutpostSquadFamily(this, 0, "Squads");
54 CAIInstance::~CAIInstance()
56 // delete managers
57 if (_PlayerManager != NULL)
59 delete _PlayerManager;
60 _PlayerManager = NULL;
62 _PetManager = NULL;
63 _EventNpcManager = NULL;
64 _Managers.clear();
66 // check if ref pointers were notified of the destruction
67 nlassert(_EasterEggGroup == NULL);
68 nlassert(_EasterEggManager == NULL);
70 _SquadFamily = NULL;
71 _SquadVariantNameToGroupDesc.clear();
73 // delete continents
74 _Continents.clear();
77 static bool zoneHaveError = false;
79 /// map of lastCreatedNpcGroup by player id
80 static std::map<CEntityId, string> _PlayersLastCreatedNpcGroup;
82 void CAIInstance::addZone(string const& zoneName, CNpcZone* zone)
84 #if !FINAL_VERSION
85 TZoneList::iterator it = zoneList.find(CStringMapper::map(zoneName));
86 if (it!=zoneList.end())
88 nlwarning("this NpcZone have the same name than another: %s", zoneName.c_str());
89 zoneHaveError = true;
91 #endif
92 zoneList[CStringMapper::map(zoneName)] = zone;
95 void CAIInstance::removeZone(string const& zoneName, CNpcZone* zone)
97 TZoneList::iterator it = zoneList.find(CStringMapper::map(zoneName));
98 zoneList.erase(it);
101 void CAIInstance::updateZoneTrigger(CBotPlayer* player)
103 std::list<CMgrNpc*> zoneTriggerManager;
105 FOREACH(it, CCont<CManager>, _Managers)
107 std::string name = it->getName();
108 uint32 size = (uint32)name.size();
109 const uint32 extensionSize = 13; // strlen(".zone_trigger");
110 if (size >= 13 && name.substr(size - extensionSize, extensionSize) == ".zone_trigger" )
112 CMgrNpc* npcManager = dynamic_cast<CMgrNpc*>(*it);
113 if (npcManager && npcManager->getStateMachine())
115 zoneTriggerManager.push_back(npcManager);
119 if ( zoneTriggerManager.empty() )
121 return;
124 uint64 whoSeesMe = CMirrors::whoSeesMe(player->dataSetRow());
125 if (!R2_VISION::isEntityVisibleToPlayers(whoSeesMe))
127 // The player is invisible, its a Dm / Gm: no zone event must be triggered
128 return;
131 std::set<uint32> insideZone;
132 std::vector<uint32> enterZone;
133 std::vector<uint32> leaveZone;
135 FOREACH(it, std::list<CMgrNpc*>, zoneTriggerManager)
137 CMgrNpc* npcManager = *it;
138 CAliasCont<CGroup>& groups = npcManager->groups();
140 CAliasCont<CGroup>::iterator first2(groups.begin()), last2(groups.end());
141 for ( ; first2 != last2 ; ++first2)
143 CAIState* startState = first2->getPersistentStateInstance()->getStartState();
144 if (startState->isPositional())
146 const CAIStatePositional* posit = dynamic_cast<const CAIStatePositional*>(startState);
147 if (posit)
149 CAIVector pos( player->pos() );
150 bool inside = posit->contains(pos);
151 if (inside)
153 insideZone.insert(first2->getAlias());
160 player->updateInsideTriggerZones(insideZone, enterZone, leaveZone);
163 static NLMISC::TStringId nbPlayer = CStringMapper::map("NbPlayer");
165 FOREACH(zoneIt, std::vector<uint32>, enterZone)
167 FOREACH(it, std::list<CMgrNpc*>, zoneTriggerManager)
169 CGroup* grp = (*it)->groups().getChildByAlias(*zoneIt);
170 if (grp)
173 CGroupNpc* grpNpc = dynamic_cast<CGroupNpc*>(grp);
174 CPersistentStateInstance* sa = grpNpc->getPersistentStateInstance();
175 if (sa)
177 sa->setLogicVar(nbPlayer, sa->getLogicVar(nbPlayer) + 1);
179 grpNpc->processStateEvent(grpNpc->getEventContainer().EventPlayerEnterTriggerZone);
184 FOREACH(zoneIt, std::vector<uint32>, leaveZone)
186 FOREACH(it, std::list<CMgrNpc*>, zoneTriggerManager)
188 CGroup* grp = (*it)->groups().getChildByAlias(*zoneIt);
189 if (grp)
191 CGroupNpc* grpNpc = dynamic_cast<CGroupNpc*>(grp);
192 CPersistentStateInstance* sa = grpNpc->getPersistentStateInstance();
193 if (sa)
195 sa->setLogicVar(nbPlayer, sa->getLogicVar(nbPlayer) - 1);
197 grpNpc->processStateEvent(grpNpc->getEventContainer().EventPlayerLeaveTriggerZone);
204 CNpcZone* CAIInstance::getZone(TStringId zoneName)
206 return zoneList[zoneName];
209 std::string CAIInstance::getManagerIndexString(CManager const* manager) const
211 return getIndexString()+NLMISC::toString(":m%u", manager->getChildIndex());
214 void CAIInstance::initInstance(string const& continentName, uint32 instanceNumber)
216 _ContinentName = continentName;
217 _InstanceNumber = instanceNumber;
219 _LastSpawnAlias = (900 + _InstanceNumber) << LigoConfig.getDynamicAliasSize();
220 _LastStateAlias = 0;
221 _LastGroupAlias = (900 + _InstanceNumber) << LigoConfig.getDynamicAliasSize();
223 sendInstanceInfoToEGS();
225 if (!EGSHasMirrorReady)
226 return;
228 // check all player in mirror to insert them in this instance.
229 TEntityIdToEntityIndexMap::const_iterator it, itEnd(TheDataset.entityEnd());
230 for (it = TheDataset.entityBegin(); it!=itEnd; ++it)
232 if (it->first.getType()!=RYZOMID::player)
233 continue;
235 TDataSetRow const row = TheDataset.getCurrentDataSetRow(it->second);
236 if (!row.isValid())
237 continue;
239 // this is a player, check the instance number
240 CMirrorPropValueRO<uint32> playerInstance(TheDataset, row, DSPropertyAI_INSTANCE);
241 if (playerInstance != instanceNumber)
242 continue;
244 // ok, add this player
245 getPlayerMgr()->addSpawnedPlayer(row, it->first);
249 void CAIInstance::serviceEvent(CServiceEvent const& info)
251 if (info.getServiceName() == "EGS")
252 sendInstanceInfoToEGS();
254 FOREACH(it, CCont<CManager>, managers())
255 it->serviceEvent(info);
257 if (CMgrPet* petManager = getPetMgr())
258 petManager->serviceEvent(info);
260 if (_EventNpcManager)
261 _EventNpcManager->serviceEvent(info);
263 FOREACH(first, CCont<CContinent>, continents())
264 (*first)->serviceEvent(info);
267 void CAIInstance::sendInstanceInfoToEGS()
269 // send message to the EGS about this new instance
270 if (EGSHasMirrorReady)
272 CReportStaticAIInstanceMsg msg;
273 msg.InstanceNumber = _InstanceNumber;
274 msg.InstanceContinent = _ContinentName;
276 msg.send("EGS");
280 bool CAIInstance::advanceUserTimer(uint32 nbTicks)
282 // for each manager, look for a timer event
283 FOREACH(it, CCont<CManager>, _Managers)
285 CGroup* grp = (*it)->getNextValidGroupChild();
286 while (grp)
288 grp->getPersistentStateInstance()->advanceUserTimer(nbTicks);
289 grp=(*it)->getNextValidGroupChild(grp); // next group
292 return true;
295 void CAIInstance::addGroupInfo(CGroup* grp)
297 string const& name = grp->aliasTreeOwner()->getName();
298 uint32 alias = grp->aliasTreeOwner()->getAlias();
299 if (!name.empty())
300 _GroupFromNames[name].push_back(grp);
301 if (alias)
302 _GroupFromAlias[alias] = grp;
305 void CAIInstance::addGroupInfo(CGroup* grp, const string &name, uint32 alias)
307 if (!name.empty())
308 _GroupFromNames[name].push_back(grp);
309 if (alias)
310 _GroupFromAlias[alias] = grp;
314 void CAIInstance::removeGroupInfo(CGroup* grp, CAliasTreeOwner* grpAliasTreeOwner)
316 string const& name = grpAliasTreeOwner->getName();
317 uint32 alias = grpAliasTreeOwner->getAlias();
319 if (!name.empty())
321 std::map< std::string, std::vector<NLMISC::CDbgPtr<CGroup> > >::iterator it=_GroupFromNames.find(name);
323 // remove from name
324 if (it!=_GroupFromNames.end())
326 std::vector<NLMISC::CDbgPtr<CGroup> > &v = it->second; // _GroupFromNames[name];
327 std::vector<NLMISC::CDbgPtr<CGroup> >::iterator itGrp(find(v.begin(), v.end(), NLMISC::CDbgPtr<CGroup>(grp) ));
328 if (itGrp != v.end())
329 v.erase(itGrp);
332 // remove from alias
333 if (alias)
334 _GroupFromAlias.erase(alias);
337 CGroup* CAIInstance::findGroup(uint32 alias)
339 std::map<uint32, NLMISC::CDbgPtr<CGroup> >::iterator it(_GroupFromAlias.find(alias));
340 if (it != _GroupFromAlias.end())
341 return it->second;
342 return NULL;
345 void CAIInstance::findGroup(std::vector<CGroup*>& result, std::string const& name)
347 std::map<std::string, std::vector<NLMISC::CDbgPtr<CGroup> > >::iterator it(_GroupFromNames.find(name));
348 if (it != _GroupFromNames.end()) {
349 result.insert(result.end(), it->second.begin(), it->second.end());
353 void CAIInstance::addMissionInfo(std::string const& missionName, uint32 alias)
355 std::string const* otherName;
357 while (true)
359 otherName = &findMissionName(alias);
360 if (otherName->empty())
361 break;
362 nlwarning("Replacing mission name for alias %u from '%s' to '%s'", alias, otherName->c_str(), missionName.c_str());
363 vector<uint32> &aliases = _MissionToAlias[*otherName];
364 aliases.erase(find(aliases.begin(), aliases.end(), alias));
367 vector<uint32>& aliases = _MissionToAlias[missionName];
368 if (std::find(aliases.begin(), aliases.end(), alias) == aliases.end())
369 aliases.push_back(alias);
372 std::string const& CAIInstance::findMissionName(uint32 alias)
374 map<string, vector<uint32> >::iterator it, itEnd(_MissionToAlias.end());
375 for (it=_MissionToAlias.begin(); it!=itEnd; ++it)
377 vector<uint32>::iterator it2, itEnd2(it->second.end());
378 for (it2=it->second.begin(); it2!=itEnd2; ++it2)
380 if (*it2 == alias)
381 return it->first;
384 static string emptyString;
385 return emptyString;
388 void CAIInstance::update()
390 // Call the player manager's updates
392 H_AUTO(PlayerMgrUpdate)
393 getPlayerMgr()->update();
396 // Call the managers' updates
398 H_AUTO(ManagersUpdate)
399 FOREACH(it, CCont<CManager>, _Managers)
400 it->CManager::update();
403 // call continent's update
405 H_AUTO(ContinentsUpdate)
406 FOREACH(it, CCont<CContinent>, _Continents)
407 it->CContinent::update();
411 CManager* CAIInstance::tryToGetManager(char const* str)
413 return dynamic_cast<CManager*>(tryToGetEntity(str, CAIS::AI_MANAGER));
416 CGroup* CAIInstance::tryToGetGroup(char const* str)
418 return dynamic_cast<CGroup*>(tryToGetEntity(str, CAIS::AI_GROUP));
421 // :TODO: Document that function
422 CAIEntity* CAIInstance::tryToGetEntity(char const* str, CAIS::TSearchType searchType)
424 char ident[512];
425 char* id = ident;
427 strcpy(ident,str);
429 char *mgr=NULL;
430 char *grp=NULL;
431 char *bot=NULL;
432 char lastChar;
433 CManager *mgrPtr=NULL;
434 CGroup *grpPtr=NULL;
435 CBot *botPtr=NULL;
436 uint32 localIndex = std::numeric_limits<uint32>::max();
438 mgr = id;
439 while((*id!=':')&&(*id!=0)) id++;
440 lastChar = *id;
441 *id=0;
442 id++;
444 localIndex=getInt64FromStr(mgr);
445 if (localIndex<managers().size())
446 mgrPtr = managers()[localIndex];
447 if (!mgrPtr)
448 goto tryWithEntityId;
449 if ( lastChar==0
450 || searchType==CAIS::AI_MANAGER)
451 return dynamic_cast<CAIEntity*> (mgrPtr);
453 grp = id;
454 while((*id!=':')&&(*id!=0)) id++;
455 lastChar = *id;
456 *id=0;
457 id++;
459 grpPtr = mgrPtr->getGroup(getInt64FromStr(grp));
460 if (!grpPtr)
461 goto tryWithEntityId;
462 if ( lastChar==0
463 || searchType==CAIS::AI_GROUP)
464 return dynamic_cast<CAIEntity*> (grpPtr);
466 bot = id;
467 while((*id!=':')&&(*id!=0)) id++;
468 lastChar = *id;
469 *id=0;
471 botPtr = grpPtr->getBot(getInt64FromStr(bot));
472 if (!botPtr)
473 goto tryWithEntityId;
474 if ( lastChar==0
475 || searchType==CAIS::AI_BOT)
476 return dynamic_cast<CAIEntity*> (botPtr);
478 return NULL;
480 tryWithEntityId:
482 CEntityId entityId;
483 entityId.fromString(str);
485 if (entityId.isUnknownId())
486 return NULL;
488 CCont<CAIInstance>::iterator instanceIt=CAIS::instance().AIList().begin(), instanceItEnd=CAIS::instance().AIList().end();
490 while (instanceIt!=instanceItEnd)
492 CAIInstance *instancePtr=*instanceIt;
494 CCont<CManager>::iterator it=instancePtr->managers().begin(), itEnd=instancePtr->managers().end();
496 while (it!=itEnd)
498 mgrPtr = *it;
500 grpPtr = mgrPtr->getNextValidGroupChild ();
501 while (grpPtr)
503 botPtr = grpPtr->getNextValidBotChild();
504 while (botPtr)
506 if (botPtr->isSpawned() && botPtr->getSpawnObj()->getEntityId() == entityId)
507 // if (botPtr->createEntityId()==entityId)
508 return dynamic_cast<CAIEntity*> (botPtr);
509 botPtr = grpPtr->getNextValidBotChild (botPtr);
511 grpPtr=mgrPtr->getNextValidGroupChild (grpPtr);
513 ++it;
515 ++instanceIt;
518 return NULL;
521 //--------------------------------------------------------------------------
522 // factory method newMgr()
523 //--------------------------------------------------------------------------
525 // factory for creating new managers and for adding them to the _Managers map
526 // asserts if the id is invalid (<0 or >1023)
527 // asserts if the id is already in use
528 CManager* CAIInstance::newMgr(AITYPES::TMgrType type, uint32 alias, std::string const& name, std::string const& mapName, std::string const& filename)
531 CManager* mgr = CManager::createManager(type, this, alias, name, "", filename);
532 // add the manager into the singleton's managers vector
533 if (mgr)
535 _Managers.addChild(mgr);
536 mgr->init ();
538 return mgr;
541 bool CAIInstance::spawn()
543 // Spawn instance managers
544 CCont<CManager>::iterator itManager, itManagerEnd(_Managers.end());
545 for (itManager=_Managers.begin(); itManager!=itManagerEnd; ++itManager)
547 CManager* manager = *itManager;
548 manager->spawn();
550 // Spawn continents
551 CCont<CContinent>::iterator itContinent, itContinentEnd(continents().end());
552 for (itContinent=continents().begin(); itContinent!=itContinentEnd; ++itContinent)
554 CContinent* continent = *itContinent;
555 continent->spawn();
557 // We should check individual errors, but fake success here :)
558 return true;
561 bool CAIInstance::despawn()
563 // Despawn instance managers
564 CCont<CManager>::iterator itManager, itManagerEnd(_Managers.end());
565 for (itManager=_Managers.begin(); itManager!=itManagerEnd; ++itManager)
567 CManager* manager = *itManager;
568 manager->despawnMgr();
570 // Despawn continents
571 CCont<CContinent>::iterator itContinent, itContinentEnd(_Continents.end());
572 for (itContinent=_Continents.begin(); itContinent!=itContinentEnd; ++itContinent)
574 CContinent* continent = *itContinent;
575 continent->despawn();
577 // We should check individual errors, but fake success here :)
578 return true;
581 //----------------------------------------------------------------------------
582 // deleteMgr()
583 //----------------------------------------------------------------------------
585 // erase a manager (free resources, free id, etc, etc
586 // asserts if the id is invalid (<0 or >1023)
587 // displays a warning and returns cleanly if the id is unused
588 void CAIInstance::deleteMgr(sint mgrId)
590 _Managers.removeChildByIndex(mgrId);
593 //----------------------------------------------------------------------------
594 // Interface to kami management
595 //----------------------------------------------------------------------------
597 void CAIInstance::registerKamiDeposit(uint32 alias, CGroupNpc* grp)
599 if (_KamiDeposits.find(alias) !=_KamiDeposits.end() && _KamiDeposits[alias]!=grp)
601 nlwarning("Failed to register Kami deposit '%s' with alias '%u' because alias already assigned to deposit '%s'",
602 (grp==NULL || grp->getAliasNode()==NULL) ? "NULL" : grp->getAliasNode()->fullName().c_str(),
603 alias,
604 (_KamiDeposits[alias]==NULL || _KamiDeposits[alias]->getAliasNode()==NULL)? "NULL": _KamiDeposits[alias]->getAliasNode()->fullName().c_str()
606 return;
608 if (grp!=NULL)
609 _KamiDeposits[alias] = grp;
610 else
611 nlwarning("Attempt to assign Kami deposit alias '%u' to an inexistant group",alias);
614 void CAIInstance::unregisterKamiDeposit(uint32 alias)
616 if (_KamiDeposits.find(alias) !=_KamiDeposits.end())
617 _KamiDeposits.erase(_KamiDeposits.find(alias));
620 //----------------------------------------------------------------------------
622 std::string CAIInstance::getIndexString() const
624 return getOwner()->getIndexString()+NLMISC::toString(":i%u", getChildIndex());
627 std::string CAIInstance::getOneLineInfoString() const
629 return "AI instance number " + NLMISC::toString("%u", getInstanceNumber()) + ", continent '" + getContinentName() + "'";
632 std::vector<std::string> CAIInstance::getMultiLineInfoString() const
634 std::vector<std::string> container;
637 pushTitle(container, "CAIInstance");
638 pushEntry(container, "id=" + getIndexString());
639 container.back() += " num=" + NLMISC::toString("%u", getInstanceNumber());
640 container.back() += " continent=" + getContinentName();
641 pushFooter(container);
644 return container;
647 //----------------------------------------------------------------------------
649 #include "ai_bot_npc.h"
650 #include "ai_profile_npc.h"
652 extern CAIVector randomPos(double dispersionRadius);
654 CAIVector randomPos(double dispersionRadius)
656 if (dispersionRadius<=0.)
658 return CAIVector(0., 0.);
660 const uint32 maxLimit = std::numeric_limits<uint32>::max() >> 1;
661 double rval = (double)CAIS::rand32(maxLimit)/(double)maxLimit; // [0-1[
662 double r = dispersionRadius*sqrt(rval);
663 rval = (double)CAIS::rand32(maxLimit)/(double)maxLimit; // [0-1[
664 double t = 2.0*NLMISC::Pi*rval;
665 double dx = cos(t)*r;
666 double dy = sin(t)*r;
667 return CAIVector(dx, dy);
670 inline
671 static float randomAngle()
673 uint32 const maxLimit = CAngle::PI*2;
674 float val = (float)CAIS::rand32(maxLimit);
675 return val;
678 CGroupNpc* CAIInstance::eventCreateNpcGroup(uint nbBots, NLMISC::CSheetId const& sheetId, CAIVector const& pos, double dispersionRadius, bool spawnBots, double orientation, const std::string &grpName, const std::string &look, sint32 cell, const std::string &botsName, const std::string &vpx) {
679 if (!_EventNpcManager)
680 return NULL;
682 AISHEETS::ICreatureCPtr sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
683 if (!sheet)
685 nlwarning("invalid sheet while creating event npc group");
686 return NULL;
689 _LastGroupAlias++;
690 string name = grpName.empty() ? NLMISC::toString("event_group_%u", _LastGroupAlias):grpName;
691 string botname = botsName.empty() ? name : botsName;
692 // Create a group
693 CGroupNpc* grp = new CGroupNpc(_EventNpcManager, _LastGroupAlias, name, RYAI_MAP_CRUNCH::Nothing);
694 // Register it in the manager
695 _EventNpcManager->groups().addAliasChild(grp);
696 // Set the group parameters
697 grp->setAutoSpawn(false);
699 grp->botVpx = vpx;
700 grp->botName = botname;
701 grp->setName(name);
702 grp->clearParameters();
703 grp->setPlayerAttackable(true);
704 grp->setBotAttackable(true);
706 grp->autoDestroy(true);
708 grp->clrBotsAreNamedFlag();
710 if (nbBots > 0)
711 eventCreateNpcBot(grp, nbBots, false, sheetId, pos, "", orientation, dispersionRadius, look, botname, vpx);
713 grp->spawn();
714 CSpawnGroupNpc* spawnGroup = grp->getSpawnObj();
715 if (!spawnGroup)
717 // the spawning has failed, delete the useless object
718 nlwarning("Failed to spawn the event group");
719 _EventNpcManager->groups().removeChildByIndex(grp->getChildIndex());
720 return NULL;
723 spawnGroup->setCell(cell);
725 NLMISC::CSmartPtr<CNpcZonePlaceNoPrim> destZone = NLMISC::CSmartPtr<CNpcZonePlaceNoPrim>(new CNpcZonePlaceNoPrim());
726 destZone->setPosAndRadius(AITYPES::vp_auto, CAIPos(pos, 0, 0), (uint32)(dispersionRadius*1000.));
727 spawnGroup->movingProfile().setAIProfile(new CGrpProfileWanderNoPrim(spawnGroup, destZone));
729 if (!grpName.empty())
731 CStateMachine* sm = _EventNpcManager->getStateMachine();
732 uint32 stateAlias = grp->getStateAlias("start");
733 CAIStatePositional* statePositional;
734 if (stateAlias == 0)
736 _LastStateAlias++;
737 statePositional = new CAIStatePositional(sm, _LastStateAlias, "start");
738 grp->setStateAlias("start", statePositional->getAlias());
739 sm->states().addChild(statePositional);
741 else
743 statePositional = safe_cast<CAIStatePositional*>(sm->states().getChildByAlias(stateAlias));
745 grp->setNextState(statePositional);
748 if (spawnBots)
749 grp->getSpawnObj()->spawnBots(botname, vpx);
751 return grp;
754 bool CAIInstance::eventCreateNpcBot(CGroupNpc* grp, uint nbBots, bool spawnBots, NLMISC::CSheetId const& sheetId, CAIVector const& pos, const std::string &name, double orientation, double dispersionRadius, const std::string &look, const std::string &botname, const std::string &vpx)
756 uint32 offset = grp->bots().size();
757 for (uint i=0; i<nbBots; ++i)
759 _LastSpawnAlias++;
760 grp->bots().addChild(new CBotNpc(grp, _LastSpawnAlias, grp->getName()), offset+i);
762 CBotNpc* const bot = NLMISC::safe_cast<CBotNpc*>(grp->bots()[offset+i]);
764 AISHEETS::ICreatureCPtr sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
765 if (!sheet)
767 nlwarning("invalid sheet while creating event npc group");
768 return false;
771 bot->setSheet(sheet);
772 bot->setPrimAlias(900+_InstanceNumber);
773 if (!look.empty())
774 bot->setClientSheet(look);
775 bot->equipmentInit();
776 bot->initEnergy(/*groupEnergyCoef()*/0);
777 CAIVector rpos(pos);
778 if (dispersionRadius == 0)
779 bot->setStuck(true);
781 float angle = 0.f;
782 if (orientation < (NLMISC::Pi * 2.0) && orientation > (-NLMISC::Pi * 2.0))
783 angle = (float)orientation;
784 else
785 angle = randomAngle();
787 // Spawn all randomly except if only 1 bot
788 if (nbBots > 1 || dispersionRadius > 1)
790 bot->saveFirstPosition(pos, dispersionRadius);
791 RYAI_MAP_CRUNCH::CWorldMap const& worldMap = CWorldContainer::getWorldMap();
792 RYAI_MAP_CRUNCH::CWorldPosition wp;
793 uint32 maxTries = 100;
796 rpos = pos;
797 rpos += randomPos(dispersionRadius);
798 --maxTries;
800 while (!worldMap.setWorldPosition(AITYPES::vp_auto, wp, rpos) && maxTries>0);
801 if (maxTries<=0)
802 rpos = pos;
804 bot->setStartPos(pos.x().asDouble(), pos.y().asDouble(), angle, AITYPES::vp_auto);
806 else
807 bot->setStartPos(rpos.x().asDouble(),rpos.y().asDouble(), angle, AITYPES::vp_auto);
810 if (spawnBots)
811 grp->getSpawnObj()->spawnBots(botname, vpx);
813 return true;
819 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)
821 if (_EasterEggManager == NULL)
822 return NULL;
824 if (_EasterEggGroup == NULL)
826 // create the easter egg group
827 CGroupNpc* grp = new CGroupNpc(_EasterEggManager, NULL, RYAI_MAP_CRUNCH::Nothing);
828 _EasterEggManager->groups().addAliasChild(grp);
830 // set the group parameters
831 grp->setAutoSpawn(false);
832 grp->setName("easter_egg_group");
833 grp->clearParameters();
834 grp->setPlayerAttackable(true);
835 grp->setBotAttackable(true); // if false the bot will not be attackable by players...
836 grp->autoDestroy(false);
838 // spawn the group
839 grp->spawn();
841 CSpawnGroupNpc* spawnGroup = grp->getSpawnObj();
842 if (spawnGroup == NULL)
844 // the spawning has failed, delete the useless object
845 nlwarning("Failed to spawn the easter egg group");
846 _EasterEggManager->groups().removeChildByIndex(grp->getChildIndex());
847 return NULL;
850 // easter eggs do not move
851 spawnGroup->movingProfile().setAIProfile(new CGrpProfileIdle(spawnGroup));
853 // keep a pointer on the easter egg group
854 _EasterEggGroup = grp;
856 nlassert(_EasterEggGroup != NULL);
858 if (getEasterEgg(easterEggId) != NULL)
860 nlwarning("An easter egg with ID %u already exists in instance %u", easterEggId, getInstanceNumber());
861 return NULL;
864 AISHEETS::ICreatureCPtr sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
865 if (!sheet)
867 nlwarning("Invalid sheet '%s' while creating an easter egg", sheetId.toString().c_str());
868 return NULL;
871 // build the easter egg bot
872 CBotEasterEgg* bot = new CBotEasterEgg(_EasterEggGroup, 0, botName, easterEggId);
873 _EasterEggGroup->bots().addChild(bot);
875 bot->setSheet(sheet);
876 if (!look.empty()) { bot->setClientSheet(look); }
877 bot->equipmentInit();
878 bot->initEnergy(0);
879 bot->setStartPos(x, y, float(heading), AITYPES::vp_auto);
881 bot->spawn();
882 if (!bot->isSpawned())
884 // the spawning has failed, delete the useless object
885 nlwarning("Failed to spawn the easter egg bot");
886 _EasterEggGroup->bots().removeChildByIndex(bot->getChildIndex());
887 return NULL;
890 return bot;
893 void CAIInstance::destroyEasterEgg(uint32 easterEggId)
895 CBotEasterEgg* bot = getEasterEgg(easterEggId);
896 if (bot != NULL)
898 bot->despawnBot();
899 _EasterEggGroup->bots().removeChildByIndex(bot->getChildIndex());
901 else
903 nlwarning("Cannot destroy easter egg %u because not found", easterEggId);
907 CBotEasterEgg* CAIInstance::getEasterEgg(uint32 easterEggId)
909 if (_EasterEggGroup == NULL)
910 return NULL;
912 FOREACH(itBot, CCont<CBot>, _EasterEggGroup->bots())
914 CBotEasterEgg* bot = dynamic_cast<CBotEasterEgg*>(*itBot);
915 #if !FINAL_VERSION
916 nlassert(bot != NULL);
917 #endif
918 if (bot != NULL && bot->getEasterEggId() == easterEggId)
919 return bot;
922 return NULL;
925 void cbEventCreateNpcGroup( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
927 uint32 messageVersion;
928 uint32 instanceNumber;
929 sint32 x;
930 sint32 y;
931 sint32 z;
932 sint32 orientation;
933 uint32 nbBots;
934 NLMISC::CSheetId sheetId;
935 CEntityId playerId;
936 double dispersionRadius;
937 bool spawnBots;
938 std::string botsName;
939 std::string look;
940 sint32 cell;
941 msgin.serial(messageVersion);
942 nlassert(messageVersion==1);
943 msgin.serial(instanceNumber);
944 msgin.serial(playerId);
945 msgin.serial(x);
946 msgin.serial(y);
947 msgin.serial(z);
948 msgin.serial(orientation);
949 msgin.serial(nbBots);
950 msgin.serial(sheetId);
951 msgin.serial(dispersionRadius);
952 msgin.serial(spawnBots);
953 msgin.serial(botsName);
954 msgin.serial(look);
955 msgin.serial(cell);
956 CAIInstance* instance = CAIS::instance().getAIInstance(instanceNumber);
957 if (instance)
959 CGroupNpc* npcGroup = instance->eventCreateNpcGroup(nbBots, sheetId, CAIVector(((double)x)/1000.0, ((double)y)/1000.0), dispersionRadius, spawnBots, ((double)orientation)/1000.0, botsName, look, cell);
960 if (npcGroup != NULL)
962 _PlayersLastCreatedNpcGroup[playerId] = npcGroup->getName();
964 FOREACH(botIt, CCont<CBot>, npcGroup->bots())
966 CSpawnBot* pbot = botIt->getSpawnObj();
967 if (pbot!=NULL)
969 CEntityId id = pbot->getEntityId();
970 float t = 0;
971 uint8 cont = 0;
972 uint8 one = 1;
973 NLMISC::TGameCycle tick = CTickEventHandler::getGameCycle() + 1;
974 CMessage msgout2("ENTITY_TELEPORTATION");
975 msgout2.serial( id );
976 msgout2.serial( x );
977 msgout2.serial( y );
978 msgout2.serial( z );
979 msgout2.serial( t );
980 msgout2.serial( tick );
981 msgout2.serial( cont );
982 msgout2.serial( cell );
983 msgout2.serial( one );
985 sendMessageViaMirror("GPMS", msgout2);
992 extern vector<vector<string> > scriptCommands2;
994 void cbEventNpcGroupScript( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
996 uint32 messageVersion;
997 string botId;
998 uint32 nbString;
999 vector<string> strings;
1000 msgin.serial(messageVersion);
1001 nlassert(messageVersion==1);
1002 msgin.serial(nbString);
1004 string eid;
1005 string firstCommand;
1006 msgin.serial(eid); // Player or boteid
1007 msgin.serial(firstCommand); // Player or boteid
1009 if (firstCommand[0] == '(') // Old eventNpcGroupScript command : (boteid, commands...)
1011 strings.resize(nbString);
1012 strings[0] = eid;
1013 strings[1] = firstCommand;
1014 for (uint32 i=2; i<nbString-2; ++i)
1016 msgin.serial(strings[i]);
1019 else
1021 nlinfo("Event group script with %d strings :", nbString-1);
1022 CEntityId playerId;
1023 if (!eid.empty())
1024 playerId = CEntityId(eid);
1026 strings.resize(nbString-1);
1027 CSString groupname = CSString(firstCommand);
1028 if (firstCommand[0] == '#' && firstCommand[1] == '(')
1030 CEntityId botEId = CEntityId(firstCommand.substr(1));
1031 if (botEId==CEntityId::Unknown)
1032 return;
1033 CAIEntityPhysical* entity = CAIEntityPhysicalLocator::getInstance()->getEntity(botEId);
1034 CSpawnBotNpc* bot = dynamic_cast<CSpawnBotNpc*>(entity);
1035 if (!bot)
1036 return;
1037 if (!bot->getPersistent().getOwner())
1038 return;
1040 strings[0] = bot->getPersistent().getOwner()->getName();
1042 else
1044 if (!eid.empty())
1045 strings[0] = (string)groupname.replace("#last", _PlayersLastCreatedNpcGroup[playerId].c_str());
1046 else
1047 strings[0] = (string)groupname;
1049 for (uint32 i=1; i<nbString-1; ++i)
1051 msgin.serial(strings[i]);
1054 scriptCommands2.push_back(strings);
1057 void cbEventFaunaBotSetRadii( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
1059 uint32 messageVersion;
1060 string botName;
1061 float notHungryRadius;
1062 float hungryRadius;
1063 float huntingRadius;
1065 msgin.serial(messageVersion);
1066 nlassert(messageVersion==1);
1067 msgin.serial(botName);
1068 msgin.serial(notHungryRadius);
1069 msgin.serial(hungryRadius);
1070 msgin.serial(huntingRadius);
1072 vector<CBot*> bots;
1073 /// try to find the bot name
1074 buildFilteredBotList(bots, botName);
1075 if (bots.empty())
1077 nlwarning("No bot correspond to name %s", botName.c_str());
1078 return;
1080 else
1082 FOREACH(itBot, vector<CBot*>, bots)
1084 CBot* bot = *itBot;
1085 CBotFauna* botFauna = dynamic_cast<CBotFauna*>(bot);
1086 if (botFauna)
1088 if (notHungryRadius>=0.f)
1089 botFauna->setAggroRadiusNotHungry(notHungryRadius);
1090 if (hungryRadius>=0.f)
1091 botFauna->setAggroRadiusHungry(hungryRadius);
1092 if (huntingRadius>=0.f)
1093 botFauna->setAggroRadiusHunting(huntingRadius);
1099 void cbEventFaunaBotResetRadii( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
1101 uint32 messageVersion;
1102 string botName;
1104 msgin.serial(messageVersion);
1105 nlassert(messageVersion==1);
1106 msgin.serial(botName);
1108 vector<CBot*> bots;
1109 /// try to find the bot name
1110 buildFilteredBotList(bots, botName);
1111 if (bots.empty())
1113 nlwarning("No bot correspond to name %s", botName.c_str());
1114 return;
1116 else
1118 FOREACH(itBot, vector<CBot*>, bots)
1120 CBot* bot = *itBot;
1121 CBotFauna* botFauna = dynamic_cast<CBotFauna*>(bot);
1122 if (botFauna)
1123 botFauna->resetAggroRadius();
1128 void cbEventBotCanAggro( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
1130 uint32 messageVersion;
1131 string botName;
1132 bool canAggro;
1134 msgin.serial(messageVersion);
1135 nlassert(messageVersion==1);
1136 msgin.serial(botName);
1137 msgin.serial(canAggro);
1139 vector<CBot*> bots;
1140 /// try to find the bot name
1141 buildFilteredBotList(bots, botName);
1142 if (bots.empty())
1144 nlwarning("No bot correspond to name %s", botName.c_str());
1145 return;
1147 else
1149 FOREACH(itBot, vector<CBot*>, bots)
1151 CBot* bot = *itBot;
1152 if (bot)
1154 CSpawnBot* spBot = bot->getSpawnObj();
1155 if (spBot)
1156 spBot->setCanAggro(canAggro);
1162 void cbEventBotSheet( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
1164 uint32 messageVersion;
1165 string botName;
1166 NLMISC::CSheetId sheetId;
1167 bool autoSpawnDespawn;
1168 string customName;
1170 msgin.serial(messageVersion);
1171 nlassert(messageVersion==3);
1172 msgin.serial(botName);
1173 msgin.serial(sheetId);
1174 msgin.serial(autoSpawnDespawn);
1175 msgin.serial(customName);
1177 vector<CBot*> bots;
1178 /// try to find the bot name
1179 buildFilteredBotList(bots, botName);
1180 if (bots.empty())
1182 nlwarning("No bot correspond to name %s", botName.c_str());
1183 return;
1185 else
1187 AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
1188 if (sheetId!=NLMISC::CSheetId::Unknown && !sheet.isNull())
1190 FOREACH(itBot, vector<CBot*>, bots)
1192 CBot* bot = *itBot;
1193 if (bot)
1195 bot->setCustomName(customName);
1196 bot->triggerSetSheet(sheet);
1197 if (autoSpawnDespawn && !bot->isSpawned())
1198 bot->spawn();
1202 else if (autoSpawnDespawn)
1204 FOREACH(itBot, vector<CBot*>, bots)
1206 CBot* bot = *itBot;
1207 if (bot && bot->isSpawned())
1208 bot->despawnBot();
1214 extern vector<vector<string> > scriptCommandsBotById;
1216 void cbR2NpcBotScriptById(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
1218 uint32 messageVersion;
1219 string botId;
1220 uint32 nbString;
1221 vector<string> strings;
1222 msgin.serial(messageVersion);
1223 nlassert(messageVersion==1);
1224 msgin.serial(nbString);
1225 strings.resize(nbString);
1226 for (uint32 i=0; i<nbString; ++i)
1227 msgin.serial(strings[i]);
1228 scriptCommandsBotById.push_back(strings);
1231 extern vector<vector<string> > scriptCommandsGroupByName;
1233 void cbR2NpcGroupScriptByName(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
1235 uint32 messageVersion;
1236 string botId;
1237 uint32 nbString;
1238 vector<string> strings;
1239 msgin.serial(messageVersion);
1240 nlassert(messageVersion==1);
1241 msgin.serial(nbString);
1242 strings.resize(nbString);
1243 for (uint32 i=0; i<nbString; ++i)
1244 msgin.serial(strings[i]);
1245 scriptCommandsGroupByName.push_back(strings);
1248 //////////////////////////////////////////////////////////////////////////////
1249 // CBotDespawnNotification //
1250 //////////////////////////////////////////////////////////////////////////////
1252 class CBotDespawnNotification : private CBot::IObserver
1254 public:
1255 /// get the singleton instance
1256 static CBotDespawnNotification* getInstance();
1258 /// register a service to be notified when a bot will despawn
1259 void registerService(NLNET::TServiceId serviceId, CBot* bot, const NLMISC::CEntityId& botId);
1261 /// dump in a log
1262 void dump(CLog& log) const;
1264 private:
1265 /// private ctor
1266 CBotDespawnNotification();
1268 /// callback of the observer
1269 void notifyBotDespawn(CBot* bot);
1270 void notifyBotDeath(CBot* bot);
1271 void notifyStopNpcControl(CBot* bot);
1273 /// the singleton instance
1274 static CBotDespawnNotification* _Instance;
1276 class CRegisteredService
1278 public:
1279 CRegisteredService(NLNET::TServiceId serviceId, const CEntityId& botId)
1280 : _ServiceId(serviceId)
1281 , _BotId(botId)
1285 NLNET::TServiceId getServiceId() const { return _ServiceId; }
1286 const CEntityId& getBotId() const { return _BotId; }
1288 bool operator==(const CRegisteredService& other) const
1290 return (_ServiceId == other._ServiceId && _BotId == other._BotId);
1293 private:
1294 NLNET::TServiceId _ServiceId;
1295 CEntityId _BotId;
1298 /// registered services by bot alias
1299 typedef std::multimap<uint32, CRegisteredService> TRegisteredServiceMap;
1300 TRegisteredServiceMap _RegisteredServices;
1303 CBotDespawnNotification* CBotDespawnNotification::_Instance = NULL;
1305 CBotDespawnNotification::CBotDespawnNotification()
1309 CBotDespawnNotification* CBotDespawnNotification::getInstance()
1311 if (_Instance == NULL)
1312 _Instance = new CBotDespawnNotification;
1313 return _Instance;
1316 void CBotDespawnNotification::registerService(NLNET::TServiceId serviceId, CBot* bot, const NLMISC::CEntityId& botId)
1318 nlassert(bot != NULL);
1320 TRegisteredServiceMap::const_iterator first = _RegisteredServices.lower_bound(bot->getAlias());
1321 TRegisteredServiceMap::const_iterator last = _RegisteredServices.upper_bound(bot->getAlias());
1323 if (first == last)
1324 bot->attachObserver(this);
1326 CRegisteredService rs(serviceId, botId);
1328 for (; first != last; ++first)
1330 if ((*first).second == rs)
1331 break;
1333 if (first == last)
1334 _RegisteredServices.insert( make_pair(bot->getAlias(), rs) );
1337 void CBotDespawnNotification::dump(CLog& log) const
1339 FOREACHC(it, TRegisteredServiceMap, _RegisteredServices)
1341 const CRegisteredService& rs = (*it).second;
1342 log.displayNL("BotAlias %s => ServiceId %hu, BotId %s",
1343 LigoConfig.aliasToString((*it).first).c_str(),
1344 rs.getServiceId().get(),
1345 rs.getBotId().toString().c_str()
1350 void CBotDespawnNotification::notifyBotDespawn(CBot* bot)
1352 nlassert(bot != NULL);
1354 // catch this event only once for current services registered for this bot
1355 bot->detachObserver(this);
1357 TRegisteredServiceMap::iterator first = _RegisteredServices.lower_bound(bot->getAlias());
1358 TRegisteredServiceMap::iterator last = _RegisteredServices.upper_bound(bot->getAlias());
1359 if (first == last)
1361 nlwarning("No service requested despawn notification for bot %s",
1362 LigoConfig.aliasToString(bot->getAlias()).c_str()
1364 DEBUG_STOP;
1365 return;
1368 // notify all services registered for this bot
1369 while (first != last)
1371 const CRegisteredService& rs = (*first).second;
1372 CMessages::notifyBotDespawn(rs.getServiceId(), bot->getAlias(), rs.getBotId());
1374 TRegisteredServiceMap::iterator tmp = first;
1375 ++first;
1376 _RegisteredServices.erase(tmp);
1380 void CBotDespawnNotification::notifyStopNpcControl(CBot* bot)
1382 nlassert(bot != NULL);
1384 // catch this event only once for current services registered for this bot
1385 //bot->detachObserver(this);
1387 TRegisteredServiceMap::iterator first = _RegisteredServices.lower_bound(bot->getAlias());
1388 TRegisteredServiceMap::iterator last = _RegisteredServices.upper_bound(bot->getAlias());
1391 // notify all services registered for this bot
1392 while (first != last)
1394 const CRegisteredService& rs = (*first).second;
1395 CMessages::notifyBotStopNpcControl(rs.getServiceId(), bot->getAlias(), rs.getBotId());
1397 TRegisteredServiceMap::iterator tmp = first;
1398 ++first;
1399 // _RegisteredServices.erase(tmp);
1402 void CBotDespawnNotification::notifyBotDeath(CBot* bot)
1404 nlassert(bot != NULL);
1406 // catch this event only once for current services registered for this bot
1407 bot->detachObserver(this);
1409 TRegisteredServiceMap::iterator first = _RegisteredServices.lower_bound(bot->getAlias());
1410 TRegisteredServiceMap::iterator last = _RegisteredServices.upper_bound(bot->getAlias());
1411 if (first == last)
1413 nlwarning("No service requested death notification for bot %s",
1414 LigoConfig.aliasToString(bot->getAlias()).c_str()
1416 DEBUG_STOP;
1417 return;
1420 // notify all services registered for this bot
1421 while (first != last)
1423 const CRegisteredService& rs = (*first).second;
1424 CMessages::notifyBotDeath(rs.getServiceId(), bot->getAlias(), rs.getBotId());
1426 TRegisteredServiceMap::iterator tmp = first;
1427 ++first;
1428 _RegisteredServices.erase(tmp);
1433 void cbAskBotDespawnNotification(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
1435 uint32 messageVersion;
1436 uint32 botAlias;
1437 CEntityId botId;
1439 msgin.serial(messageVersion);
1440 nlassert(messageVersion == 1);
1441 msgin.serial(botAlias);
1442 msgin.serial(botId);
1444 TDataSetRow botRowId = CMirrors::getDataSetRow(botId);
1445 if (botRowId.isNull())
1447 CMessages::notifyBotDespawn(serviceId, botAlias, botId);
1448 return;
1451 CAIEntityPhysical* entity = CAIS::instance().getEntityPhysical(botRowId);
1452 if (entity == NULL)
1454 CMessages::notifyBotDespawn(serviceId, botAlias, botId);
1455 return;
1458 #if !FINAL_VERSION
1459 nlassert(entity->getEntityId() == botId);
1460 #endif
1462 CSpawnBot* bot = NULL;
1463 switch (entity->getRyzomType())
1465 case RYZOMID::creature:
1466 case RYZOMID::npc:
1467 bot = NLMISC::safe_cast<CSpawnBot*>(entity);
1468 break;
1469 default:
1470 break;
1473 // check if the bot has been replaced by another entity in mirror
1474 if (bot == NULL || bot->getPersistent().getAlias() != botAlias)
1476 CMessages::notifyBotDespawn(serviceId, botAlias, botId);
1477 return;
1480 // register the service to be notified when the bot will despawn
1481 CBotDespawnNotification::getInstance()->registerService(serviceId, &bot->getPersistent(), botId);
1484 void cbSpawnEasterEgg(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
1486 uint32 messageVersion;
1487 uint32 instanceNumber;
1488 uint32 easterEggId;
1489 CSheetId sheetId;
1490 string botName;
1491 string look;
1493 sint32 x, y, z;
1494 float heading;
1496 msgin.serial(messageVersion);
1497 nlassert(messageVersion==1);
1498 msgin.serial(instanceNumber);
1499 msgin.serial(easterEggId);
1500 msgin.serial(sheetId);
1501 msgin.serial(botName);
1502 msgin.serial(look);
1503 msgin.serial(x, y, z, heading);
1505 CAIInstance* instance = CAIS::instance().getAIInstance(instanceNumber);
1506 if (instance != NULL)
1508 CBotEasterEgg* bot = instance->createEasterEgg(easterEggId, sheetId, botName, float(x)/1000., float(y)/1000., float(z)/1000., heading, look );
1509 if (bot != NULL)
1511 nlassert(bot->getSpawn() != NULL);
1512 CEntityId botId = bot->getSpawn()->getEntityId();
1514 NLNET::CMessage msgout("EASTER_EGG_SPAWNED");
1515 msgout.serial(botId);
1516 msgout.serial(easterEggId);
1517 sendMessageViaMirror(serviceId, msgout);
1519 else
1521 nlwarning("Cannot spawn easter egg %u!", easterEggId);
1524 else
1526 nlwarning("Cannot spawn easter egg %u, AI instance %u not found!", easterEggId, instanceNumber);
1530 void cbDespawnEasterEgg(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
1532 uint32 messageVersion;
1533 uint32 instanceNumber;
1534 uint32 easterEggId;
1536 msgin.serial(messageVersion);
1537 nlassert(messageVersion==1);
1538 msgin.serial(instanceNumber);
1539 msgin.serial(easterEggId);
1541 CAIInstance* instance = CAIS::instance().getAIInstance(instanceNumber);
1542 if (instance != NULL)
1544 instance->destroyEasterEgg(easterEggId);
1548 NLMISC_COMMAND(simulateMsgAskBotDespawnNotification, "", "<service_id> <bot_alias> <bot_id>")
1550 if (args.size() != 3)
1551 return false;
1553 uint16 id;
1554 NLMISC::fromString(args[0], id);
1555 NLNET::TServiceId serviceId(id);
1556 uint32 botAlias = LigoConfig.aliasFromString(args[1]);
1557 CEntityId botId(args[2].c_str());
1559 NLNET::CMessage msg("ASK_BOT_DESPAWN_NOTIFICATION");
1560 uint32 messageVersion = 1;
1561 msg.serial(messageVersion, botAlias, botId);
1562 msg.invert();
1563 cbAskBotDespawnNotification(msg, "FAKE", serviceId);
1565 return true;
1568 NLMISC_COMMAND(dumpBotDespawnNotification, "", "")
1570 if (args.size() != 0)
1571 return false;
1573 CBotDespawnNotification::getInstance()->dump(log);
1574 return true;
1577 NLMISC_COMMAND(simulateMsgSpawnEasterEgg, "", "<service_id> <ai_instance> <easter_egg_id> <sheet> <bot_name> <x> <y>")
1579 if (args.size() != 7)
1580 return false;
1582 uint16 id;
1583 NLMISC::fromString(args[0], id);
1584 NLNET::TServiceId serviceId(id);
1585 uint32 instanceNumber;
1586 NLMISC::fromString(args[1], instanceNumber);
1587 uint32 easterEggId;
1588 NLMISC::fromString(args[2], easterEggId);
1589 CSheetId sheetId(args[3]);
1590 string botName = args[4];
1591 sint32 x;
1592 NLMISC::fromString(args[5], x);
1593 sint32 y;
1594 NLMISC::fromString(args[6], y);
1595 sint32 z = 0;
1596 sint32 heading = 0;
1597 std::string look = "";
1599 NLNET::CMessage msg("SPAWN_EASTER_EGG");
1600 uint32 messageVersion = 1;
1601 msg.serial(messageVersion);
1602 msg.serial(instanceNumber);
1603 msg.serial(easterEggId);
1604 msg.serial(sheetId);
1605 msg.serial(botName);
1606 msg.serial(look);
1608 msg.serial(x, y, z, heading);
1609 msg.invert();
1610 cbSpawnEasterEgg(msg, "FAKE", serviceId);
1612 return true;
1615 NLMISC_COMMAND(simulateMsgDespawnEasterEgg, "", "<service_id> <ai_instance> <easter_egg_id>")
1617 if (args.size() != 3)
1618 return false;
1620 uint16 id;
1621 NLMISC::fromString(args[0], id);
1622 NLNET::TServiceId serviceId(id);
1623 uint32 instanceNumber;
1624 NLMISC::fromString(args[1], instanceNumber);
1625 uint32 easterEggId;
1626 NLMISC::fromString(args[2], easterEggId);
1628 NLNET::CMessage msg("DESPAWN_EASTER_EGG");
1629 uint32 messageVersion = 1;
1630 msg.serial(messageVersion);
1631 msg.serial(instanceNumber);
1632 msg.serial(easterEggId);
1633 msg.invert();
1634 cbDespawnEasterEgg(msg, "FAKE", serviceId);
1636 return true;