Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / ai_instance.cpp
blob8f3fce9fb23e72d12107e9712dbc16cb84b9ff91
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 &botsName, const std::string &look, sint32 cell) {
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 = botsName.empty() ? NLMISC::toString("event_group_%u", _LastGroupAlias):botsName;
691 // Create a group
692 CGroupNpc* grp = new CGroupNpc(_EventNpcManager, _LastGroupAlias, name, RYAI_MAP_CRUNCH::Nothing);
693 // Register it in the manager
694 _EventNpcManager->groups().addAliasChild(grp);
695 // Set the group parameters
696 grp->setAutoSpawn(false);
699 grp->setName(name);
700 grp->clearParameters();
701 grp->setPlayerAttackable(true);
702 grp->setBotAttackable(true);
704 grp->autoDestroy(true);
706 grp->clrBotsAreNamedFlag();
708 eventCreateNpcBot(grp, nbBots, false, sheetId, pos, "", orientation, dispersionRadius, look);
710 grp->spawn();
711 CSpawnGroupNpc* spawnGroup = grp->getSpawnObj();
712 if (!spawnGroup)
714 // the spawning has failed, delete the useless object
715 nlwarning("Failed to spawn the event group");
716 _EventNpcManager->groups().removeChildByIndex(grp->getChildIndex());
717 return NULL;
720 spawnGroup->setCell(cell);
722 NLMISC::CSmartPtr<CNpcZonePlaceNoPrim> destZone = NLMISC::CSmartPtr<CNpcZonePlaceNoPrim>(new CNpcZonePlaceNoPrim());
723 destZone->setPosAndRadius(AITYPES::vp_auto, CAIPos(pos, 0, 0), (uint32)(dispersionRadius*1000.));
724 spawnGroup->movingProfile().setAIProfile(new CGrpProfileWanderNoPrim(spawnGroup, destZone));
726 if (!botsName.empty())
728 CStateMachine* sm = _EventNpcManager->getStateMachine();
729 uint32 stateAlias = grp->getStateAlias("start");
730 CAIStatePositional* statePositional;
731 if (stateAlias == 0)
733 _LastStateAlias++;
734 statePositional = new CAIStatePositional(sm, _LastStateAlias, "start");
735 grp->setStateAlias("start", statePositional->getAlias());
736 sm->states().addChild(statePositional);
738 else
740 statePositional = safe_cast<CAIStatePositional*>(sm->states().getChildByAlias(stateAlias));
742 grp->setNextState(statePositional);
745 if (spawnBots)
746 grp->getSpawnObj()->spawnBots();
748 return grp;
751 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)
753 uint32 offset = grp->bots().size();
754 for (uint i=0; i<nbBots; ++i)
756 _LastSpawnAlias++;
757 grp->bots().addChild(new CBotNpc(grp, _LastSpawnAlias, grp->getName()), offset+i);
759 CBotNpc* const bot = NLMISC::safe_cast<CBotNpc*>(grp->bots()[offset+i]);
761 AISHEETS::ICreatureCPtr sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
762 if (!sheet)
764 nlwarning("invalid sheet while creating event npc group");
765 return false;
768 bot->setSheet(sheet);
769 bot->setPrimAlias(900+_InstanceNumber);
770 if (!look.empty())
771 bot->setClientSheet(look);
772 bot->equipmentInit();
773 bot->initEnergy(/*groupEnergyCoef()*/0);
774 CAIVector rpos(pos);
775 if (dispersionRadius == 0)
776 bot->setStuck(true);
778 float angle = 0.f;
779 if (orientation < (NLMISC::Pi * 2.0) && orientation > (-NLMISC::Pi * 2.0))
780 angle = (float)orientation;
781 else
782 angle = randomAngle();
784 // Spawn all randomly except if only 1 bot
785 if (nbBots > 1 || dispersionRadius > 1)
787 bot->saveFirstPosition(pos, dispersionRadius);
788 RYAI_MAP_CRUNCH::CWorldMap const& worldMap = CWorldContainer::getWorldMap();
789 RYAI_MAP_CRUNCH::CWorldPosition wp;
790 uint32 maxTries = 100;
793 rpos = pos;
794 rpos += randomPos(dispersionRadius);
795 --maxTries;
797 while (!worldMap.setWorldPosition(AITYPES::vp_auto, wp, rpos) && maxTries>0);
798 if (maxTries<=0)
799 rpos = pos;
801 bot->setStartPos(pos.x().asDouble(), pos.y().asDouble(), angle, AITYPES::vp_auto);
803 else
804 bot->setStartPos(rpos.x().asDouble(),rpos.y().asDouble(), angle, AITYPES::vp_auto);
807 if (spawnBots)
808 grp->getSpawnObj()->spawnBots(name);
810 return true;
816 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)
818 if (_EasterEggManager == NULL)
819 return NULL;
821 if (_EasterEggGroup == NULL)
823 // create the easter egg group
824 CGroupNpc* grp = new CGroupNpc(_EasterEggManager, NULL, RYAI_MAP_CRUNCH::Nothing);
825 _EasterEggManager->groups().addAliasChild(grp);
827 // set the group parameters
828 grp->setAutoSpawn(false);
829 grp->setName("easter_egg_group");
830 grp->clearParameters();
831 grp->setPlayerAttackable(true);
832 grp->setBotAttackable(true); // if false the bot will not be attackable by players...
833 grp->autoDestroy(false);
835 // spawn the group
836 grp->spawn();
838 CSpawnGroupNpc* spawnGroup = grp->getSpawnObj();
839 if (spawnGroup == NULL)
841 // the spawning has failed, delete the useless object
842 nlwarning("Failed to spawn the easter egg group");
843 _EasterEggManager->groups().removeChildByIndex(grp->getChildIndex());
844 return NULL;
847 // easter eggs do not move
848 spawnGroup->movingProfile().setAIProfile(new CGrpProfileIdle(spawnGroup));
850 // keep a pointer on the easter egg group
851 _EasterEggGroup = grp;
853 nlassert(_EasterEggGroup != NULL);
855 if (getEasterEgg(easterEggId) != NULL)
857 nlwarning("An easter egg with ID %u already exists in instance %u", easterEggId, getInstanceNumber());
858 return NULL;
861 AISHEETS::ICreatureCPtr sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
862 if (!sheet)
864 nlwarning("Invalid sheet '%s' while creating an easter egg", sheetId.toString().c_str());
865 return NULL;
868 // build the easter egg bot
869 CBotEasterEgg* bot = new CBotEasterEgg(_EasterEggGroup, 0, botName, easterEggId);
870 _EasterEggGroup->bots().addChild(bot);
872 bot->setSheet(sheet);
873 if (!look.empty()) { bot->setClientSheet(look); }
874 bot->equipmentInit();
875 bot->initEnergy(0);
876 bot->setStartPos(x, y, float(heading), AITYPES::vp_auto);
878 bot->spawn();
879 if (!bot->isSpawned())
881 // the spawning has failed, delete the useless object
882 nlwarning("Failed to spawn the easter egg bot");
883 _EasterEggGroup->bots().removeChildByIndex(bot->getChildIndex());
884 return NULL;
887 return bot;
890 void CAIInstance::destroyEasterEgg(uint32 easterEggId)
892 CBotEasterEgg* bot = getEasterEgg(easterEggId);
893 if (bot != NULL)
895 bot->despawnBot();
896 _EasterEggGroup->bots().removeChildByIndex(bot->getChildIndex());
898 else
900 nlwarning("Cannot destroy easter egg %u because not found", easterEggId);
904 CBotEasterEgg* CAIInstance::getEasterEgg(uint32 easterEggId)
906 if (_EasterEggGroup == NULL)
907 return NULL;
909 FOREACH(itBot, CCont<CBot>, _EasterEggGroup->bots())
911 CBotEasterEgg* bot = dynamic_cast<CBotEasterEgg*>(*itBot);
912 #if !FINAL_VERSION
913 nlassert(bot != NULL);
914 #endif
915 if (bot != NULL && bot->getEasterEggId() == easterEggId)
916 return bot;
919 return NULL;
922 void cbEventCreateNpcGroup( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
924 uint32 messageVersion;
925 uint32 instanceNumber;
926 sint32 x;
927 sint32 y;
928 sint32 z;
929 sint32 orientation;
930 uint32 nbBots;
931 NLMISC::CSheetId sheetId;
932 CEntityId playerId;
933 double dispersionRadius;
934 bool spawnBots;
935 std::string botsName;
936 std::string look;
937 sint32 cell;
938 msgin.serial(messageVersion);
939 nlassert(messageVersion==1);
940 msgin.serial(instanceNumber);
941 msgin.serial(playerId);
942 msgin.serial(x);
943 msgin.serial(y);
944 msgin.serial(z);
945 msgin.serial(orientation);
946 msgin.serial(nbBots);
947 msgin.serial(sheetId);
948 msgin.serial(dispersionRadius);
949 msgin.serial(spawnBots);
950 msgin.serial(botsName);
951 msgin.serial(look);
952 msgin.serial(cell);
953 CAIInstance* instance = CAIS::instance().getAIInstance(instanceNumber);
954 if (instance)
956 CGroupNpc* npcGroup = instance->eventCreateNpcGroup(nbBots, sheetId, CAIVector(((double)x)/1000.0, ((double)y)/1000.0), dispersionRadius, spawnBots, ((double)orientation)/1000.0, botsName, look, cell);
957 if (npcGroup != NULL)
959 _PlayersLastCreatedNpcGroup[playerId] = npcGroup->getName();
961 FOREACH(botIt, CCont<CBot>, npcGroup->bots())
963 CSpawnBot* pbot = botIt->getSpawnObj();
964 if (pbot!=NULL)
966 CEntityId id = pbot->getEntityId();
967 float t = 0;
968 uint8 cont = 0;
969 uint8 one = 1;
970 NLMISC::TGameCycle tick = CTickEventHandler::getGameCycle() + 1;
971 CMessage msgout2("ENTITY_TELEPORTATION");
972 msgout2.serial( id );
973 msgout2.serial( x );
974 msgout2.serial( y );
975 msgout2.serial( z );
976 msgout2.serial( t );
977 msgout2.serial( tick );
978 msgout2.serial( cont );
979 msgout2.serial( cell );
980 msgout2.serial( one );
982 sendMessageViaMirror("GPMS", msgout2);
989 extern vector<vector<string> > scriptCommands2;
991 void cbEventNpcGroupScript( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
993 uint32 messageVersion;
994 string botId;
995 uint32 nbString;
996 vector<string> strings;
997 msgin.serial(messageVersion);
998 nlassert(messageVersion==1);
999 msgin.serial(nbString);
1001 string eid;
1002 string firstCommand;
1003 msgin.serial(eid); // Player or boteid
1004 msgin.serial(firstCommand); // Player or boteid
1006 if (firstCommand[0] == '(') // Old eventNpcGroupScript command : (boteid, commands...)
1008 strings.resize(nbString);
1009 strings[0] = eid;
1010 strings[1] = firstCommand;
1011 for (uint32 i=2; i<nbString-2; ++i)
1013 msgin.serial(strings[i]);
1016 else
1018 nlinfo("Event group script with %d strings :", nbString-1);
1019 CEntityId playerId;
1020 if (!eid.empty())
1021 playerId = CEntityId(eid);
1023 strings.resize(nbString-1);
1024 CSString groupname = CSString(firstCommand);
1025 if (firstCommand[0] == '#' && firstCommand[1] == '(')
1027 CEntityId botEId = CEntityId(firstCommand.substr(1));
1028 if (botEId==CEntityId::Unknown)
1029 return;
1030 CAIEntityPhysical* entity = CAIEntityPhysicalLocator::getInstance()->getEntity(botEId);
1031 CSpawnBotNpc* bot = dynamic_cast<CSpawnBotNpc*>(entity);
1032 if (!bot)
1033 return;
1034 if (!bot->getPersistent().getOwner())
1035 return;
1037 strings[0] = bot->getPersistent().getOwner()->getName();
1039 else
1041 if (!eid.empty())
1042 strings[0] = (string)groupname.replace("#last", _PlayersLastCreatedNpcGroup[playerId].c_str());
1043 else
1044 strings[0] = (string)groupname;
1046 for (uint32 i=1; i<nbString-1; ++i)
1048 msgin.serial(strings[i]);
1051 scriptCommands2.push_back(strings);
1054 void cbEventFaunaBotSetRadii( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
1056 uint32 messageVersion;
1057 string botName;
1058 float notHungryRadius;
1059 float hungryRadius;
1060 float huntingRadius;
1062 msgin.serial(messageVersion);
1063 nlassert(messageVersion==1);
1064 msgin.serial(botName);
1065 msgin.serial(notHungryRadius);
1066 msgin.serial(hungryRadius);
1067 msgin.serial(huntingRadius);
1069 vector<CBot*> bots;
1070 /// try to find the bot name
1071 buildFilteredBotList(bots, botName);
1072 if (bots.empty())
1074 nlwarning("No bot correspond to name %s", botName.c_str());
1075 return;
1077 else
1079 FOREACH(itBot, vector<CBot*>, bots)
1081 CBot* bot = *itBot;
1082 CBotFauna* botFauna = dynamic_cast<CBotFauna*>(bot);
1083 if (botFauna)
1085 if (notHungryRadius>=0.f)
1086 botFauna->setAggroRadiusNotHungry(notHungryRadius);
1087 if (hungryRadius>=0.f)
1088 botFauna->setAggroRadiusHungry(hungryRadius);
1089 if (huntingRadius>=0.f)
1090 botFauna->setAggroRadiusHunting(huntingRadius);
1096 void cbEventFaunaBotResetRadii( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
1098 uint32 messageVersion;
1099 string botName;
1101 msgin.serial(messageVersion);
1102 nlassert(messageVersion==1);
1103 msgin.serial(botName);
1105 vector<CBot*> bots;
1106 /// try to find the bot name
1107 buildFilteredBotList(bots, botName);
1108 if (bots.empty())
1110 nlwarning("No bot correspond to name %s", botName.c_str());
1111 return;
1113 else
1115 FOREACH(itBot, vector<CBot*>, bots)
1117 CBot* bot = *itBot;
1118 CBotFauna* botFauna = dynamic_cast<CBotFauna*>(bot);
1119 if (botFauna)
1120 botFauna->resetAggroRadius();
1125 void cbEventBotCanAggro( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
1127 uint32 messageVersion;
1128 string botName;
1129 bool canAggro;
1131 msgin.serial(messageVersion);
1132 nlassert(messageVersion==1);
1133 msgin.serial(botName);
1134 msgin.serial(canAggro);
1136 vector<CBot*> bots;
1137 /// try to find the bot name
1138 buildFilteredBotList(bots, botName);
1139 if (bots.empty())
1141 nlwarning("No bot correspond to name %s", botName.c_str());
1142 return;
1144 else
1146 FOREACH(itBot, vector<CBot*>, bots)
1148 CBot* bot = *itBot;
1149 if (bot)
1151 CSpawnBot* spBot = bot->getSpawnObj();
1152 if (spBot)
1153 spBot->setCanAggro(canAggro);
1159 void cbEventBotSheet( NLNET::CMessage& msgin, const std::string &serviceName, NLNET::TServiceId serviceId )
1161 uint32 messageVersion;
1162 string botName;
1163 NLMISC::CSheetId sheetId;
1164 bool autoSpawnDespawn;
1165 string customName;
1167 msgin.serial(messageVersion);
1168 nlassert(messageVersion==3);
1169 msgin.serial(botName);
1170 msgin.serial(sheetId);
1171 msgin.serial(autoSpawnDespawn);
1172 msgin.serial(customName);
1174 vector<CBot*> bots;
1175 /// try to find the bot name
1176 buildFilteredBotList(bots, botName);
1177 if (bots.empty())
1179 nlwarning("No bot correspond to name %s", botName.c_str());
1180 return;
1182 else
1184 AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
1185 if (sheetId!=NLMISC::CSheetId::Unknown && !sheet.isNull())
1187 FOREACH(itBot, vector<CBot*>, bots)
1189 CBot* bot = *itBot;
1190 if (bot)
1192 bot->setCustomName(customName);
1193 bot->triggerSetSheet(sheet);
1194 if (autoSpawnDespawn && !bot->isSpawned())
1195 bot->spawn();
1199 else if (autoSpawnDespawn)
1201 FOREACH(itBot, vector<CBot*>, bots)
1203 CBot* bot = *itBot;
1204 if (bot && bot->isSpawned())
1205 bot->despawnBot();
1211 extern vector<vector<string> > scriptCommandsBotById;
1213 void cbR2NpcBotScriptById(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
1215 uint32 messageVersion;
1216 string botId;
1217 uint32 nbString;
1218 vector<string> strings;
1219 msgin.serial(messageVersion);
1220 nlassert(messageVersion==1);
1221 msgin.serial(nbString);
1222 strings.resize(nbString);
1223 for (uint32 i=0; i<nbString; ++i)
1224 msgin.serial(strings[i]);
1225 scriptCommandsBotById.push_back(strings);
1228 extern vector<vector<string> > scriptCommandsGroupByName;
1230 void cbR2NpcGroupScriptByName(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
1232 uint32 messageVersion;
1233 string botId;
1234 uint32 nbString;
1235 vector<string> strings;
1236 msgin.serial(messageVersion);
1237 nlassert(messageVersion==1);
1238 msgin.serial(nbString);
1239 strings.resize(nbString);
1240 for (uint32 i=0; i<nbString; ++i)
1241 msgin.serial(strings[i]);
1242 scriptCommandsGroupByName.push_back(strings);
1245 //////////////////////////////////////////////////////////////////////////////
1246 // CBotDespawnNotification //
1247 //////////////////////////////////////////////////////////////////////////////
1249 class CBotDespawnNotification : private CBot::IObserver
1251 public:
1252 /// get the singleton instance
1253 static CBotDespawnNotification* getInstance();
1255 /// register a service to be notified when a bot will despawn
1256 void registerService(NLNET::TServiceId serviceId, CBot* bot, const NLMISC::CEntityId& botId);
1258 /// dump in a log
1259 void dump(CLog& log) const;
1261 private:
1262 /// private ctor
1263 CBotDespawnNotification();
1265 /// callback of the observer
1266 void notifyBotDespawn(CBot* bot);
1267 void notifyBotDeath(CBot* bot);
1268 void notifyStopNpcControl(CBot* bot);
1270 /// the singleton instance
1271 static CBotDespawnNotification* _Instance;
1273 class CRegisteredService
1275 public:
1276 CRegisteredService(NLNET::TServiceId serviceId, const CEntityId& botId)
1277 : _ServiceId(serviceId)
1278 , _BotId(botId)
1282 NLNET::TServiceId getServiceId() const { return _ServiceId; }
1283 const CEntityId& getBotId() const { return _BotId; }
1285 bool operator==(const CRegisteredService& other) const
1287 return (_ServiceId == other._ServiceId && _BotId == other._BotId);
1290 private:
1291 NLNET::TServiceId _ServiceId;
1292 CEntityId _BotId;
1295 /// registered services by bot alias
1296 typedef std::multimap<uint32, CRegisteredService> TRegisteredServiceMap;
1297 TRegisteredServiceMap _RegisteredServices;
1300 CBotDespawnNotification* CBotDespawnNotification::_Instance = NULL;
1302 CBotDespawnNotification::CBotDespawnNotification()
1306 CBotDespawnNotification* CBotDespawnNotification::getInstance()
1308 if (_Instance == NULL)
1309 _Instance = new CBotDespawnNotification;
1310 return _Instance;
1313 void CBotDespawnNotification::registerService(NLNET::TServiceId serviceId, CBot* bot, const NLMISC::CEntityId& botId)
1315 nlassert(bot != NULL);
1317 TRegisteredServiceMap::const_iterator first = _RegisteredServices.lower_bound(bot->getAlias());
1318 TRegisteredServiceMap::const_iterator last = _RegisteredServices.upper_bound(bot->getAlias());
1320 if (first == last)
1321 bot->attachObserver(this);
1323 CRegisteredService rs(serviceId, botId);
1325 for (; first != last; ++first)
1327 if ((*first).second == rs)
1328 break;
1330 if (first == last)
1331 _RegisteredServices.insert( make_pair(bot->getAlias(), rs) );
1334 void CBotDespawnNotification::dump(CLog& log) const
1336 FOREACHC(it, TRegisteredServiceMap, _RegisteredServices)
1338 const CRegisteredService& rs = (*it).second;
1339 log.displayNL("BotAlias %s => ServiceId %hu, BotId %s",
1340 LigoConfig.aliasToString((*it).first).c_str(),
1341 rs.getServiceId().get(),
1342 rs.getBotId().toString().c_str()
1347 void CBotDespawnNotification::notifyBotDespawn(CBot* bot)
1349 nlassert(bot != NULL);
1351 // catch this event only once for current services registered for this bot
1352 bot->detachObserver(this);
1354 TRegisteredServiceMap::iterator first = _RegisteredServices.lower_bound(bot->getAlias());
1355 TRegisteredServiceMap::iterator last = _RegisteredServices.upper_bound(bot->getAlias());
1356 if (first == last)
1358 nlwarning("No service requested despawn notification for bot %s",
1359 LigoConfig.aliasToString(bot->getAlias()).c_str()
1361 DEBUG_STOP;
1362 return;
1365 // notify all services registered for this bot
1366 while (first != last)
1368 const CRegisteredService& rs = (*first).second;
1369 CMessages::notifyBotDespawn(rs.getServiceId(), bot->getAlias(), rs.getBotId());
1371 TRegisteredServiceMap::iterator tmp = first;
1372 ++first;
1373 _RegisteredServices.erase(tmp);
1377 void CBotDespawnNotification::notifyStopNpcControl(CBot* bot)
1379 nlassert(bot != NULL);
1381 // catch this event only once for current services registered for this bot
1382 //bot->detachObserver(this);
1384 TRegisteredServiceMap::iterator first = _RegisteredServices.lower_bound(bot->getAlias());
1385 TRegisteredServiceMap::iterator last = _RegisteredServices.upper_bound(bot->getAlias());
1388 // notify all services registered for this bot
1389 while (first != last)
1391 const CRegisteredService& rs = (*first).second;
1392 CMessages::notifyBotStopNpcControl(rs.getServiceId(), bot->getAlias(), rs.getBotId());
1394 TRegisteredServiceMap::iterator tmp = first;
1395 ++first;
1396 // _RegisteredServices.erase(tmp);
1399 void CBotDespawnNotification::notifyBotDeath(CBot* bot)
1401 nlassert(bot != NULL);
1403 // catch this event only once for current services registered for this bot
1404 bot->detachObserver(this);
1406 TRegisteredServiceMap::iterator first = _RegisteredServices.lower_bound(bot->getAlias());
1407 TRegisteredServiceMap::iterator last = _RegisteredServices.upper_bound(bot->getAlias());
1408 if (first == last)
1410 nlwarning("No service requested death notification for bot %s",
1411 LigoConfig.aliasToString(bot->getAlias()).c_str()
1413 DEBUG_STOP;
1414 return;
1417 // notify all services registered for this bot
1418 while (first != last)
1420 const CRegisteredService& rs = (*first).second;
1421 CMessages::notifyBotDeath(rs.getServiceId(), bot->getAlias(), rs.getBotId());
1423 TRegisteredServiceMap::iterator tmp = first;
1424 ++first;
1425 _RegisteredServices.erase(tmp);
1430 void cbAskBotDespawnNotification(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
1432 uint32 messageVersion;
1433 uint32 botAlias;
1434 CEntityId botId;
1436 msgin.serial(messageVersion);
1437 nlassert(messageVersion == 1);
1438 msgin.serial(botAlias);
1439 msgin.serial(botId);
1441 TDataSetRow botRowId = CMirrors::getDataSetRow(botId);
1442 if (botRowId.isNull())
1444 CMessages::notifyBotDespawn(serviceId, botAlias, botId);
1445 return;
1448 CAIEntityPhysical* entity = CAIS::instance().getEntityPhysical(botRowId);
1449 if (entity == NULL)
1451 CMessages::notifyBotDespawn(serviceId, botAlias, botId);
1452 return;
1455 #if !FINAL_VERSION
1456 nlassert(entity->getEntityId() == botId);
1457 #endif
1459 CSpawnBot* bot = NULL;
1460 switch (entity->getRyzomType())
1462 case RYZOMID::creature:
1463 case RYZOMID::npc:
1464 bot = NLMISC::safe_cast<CSpawnBot*>(entity);
1465 break;
1466 default:
1467 break;
1470 // check if the bot has been replaced by another entity in mirror
1471 if (bot == NULL || bot->getPersistent().getAlias() != botAlias)
1473 CMessages::notifyBotDespawn(serviceId, botAlias, botId);
1474 return;
1477 // register the service to be notified when the bot will despawn
1478 CBotDespawnNotification::getInstance()->registerService(serviceId, &bot->getPersistent(), botId);
1481 void cbSpawnEasterEgg(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
1483 uint32 messageVersion;
1484 uint32 instanceNumber;
1485 uint32 easterEggId;
1486 CSheetId sheetId;
1487 string botName;
1488 string look;
1490 sint32 x, y, z;
1491 float heading;
1493 msgin.serial(messageVersion);
1494 nlassert(messageVersion==1);
1495 msgin.serial(instanceNumber);
1496 msgin.serial(easterEggId);
1497 msgin.serial(sheetId);
1498 msgin.serial(botName);
1499 msgin.serial(look);
1500 msgin.serial(x, y, z, heading);
1502 CAIInstance* instance = CAIS::instance().getAIInstance(instanceNumber);
1503 if (instance != NULL)
1505 CBotEasterEgg* bot = instance->createEasterEgg(easterEggId, sheetId, botName, float(x)/1000., float(y)/1000., float(z)/1000., heading, look );
1506 if (bot != NULL)
1508 nlassert(bot->getSpawn() != NULL);
1509 CEntityId botId = bot->getSpawn()->getEntityId();
1511 NLNET::CMessage msgout("EASTER_EGG_SPAWNED");
1512 msgout.serial(botId);
1513 msgout.serial(easterEggId);
1514 sendMessageViaMirror(serviceId, msgout);
1516 else
1518 nlwarning("Cannot spawn easter egg %u!", easterEggId);
1521 else
1523 nlwarning("Cannot spawn easter egg %u, AI instance %u not found!", easterEggId, instanceNumber);
1527 void cbDespawnEasterEgg(NLNET::CMessage& msgin, const std::string& serviceName, NLNET::TServiceId serviceId)
1529 uint32 messageVersion;
1530 uint32 instanceNumber;
1531 uint32 easterEggId;
1533 msgin.serial(messageVersion);
1534 nlassert(messageVersion==1);
1535 msgin.serial(instanceNumber);
1536 msgin.serial(easterEggId);
1538 CAIInstance* instance = CAIS::instance().getAIInstance(instanceNumber);
1539 if (instance != NULL)
1541 instance->destroyEasterEgg(easterEggId);
1545 NLMISC_COMMAND(simulateMsgAskBotDespawnNotification, "", "<service_id> <bot_alias> <bot_id>")
1547 if (args.size() != 3)
1548 return false;
1550 uint16 id;
1551 NLMISC::fromString(args[0], id);
1552 NLNET::TServiceId serviceId(id);
1553 uint32 botAlias = LigoConfig.aliasFromString(args[1]);
1554 CEntityId botId(args[2].c_str());
1556 NLNET::CMessage msg("ASK_BOT_DESPAWN_NOTIFICATION");
1557 uint32 messageVersion = 1;
1558 msg.serial(messageVersion, botAlias, botId);
1559 msg.invert();
1560 cbAskBotDespawnNotification(msg, "FAKE", serviceId);
1562 return true;
1565 NLMISC_COMMAND(dumpBotDespawnNotification, "", "")
1567 if (args.size() != 0)
1568 return false;
1570 CBotDespawnNotification::getInstance()->dump(log);
1571 return true;
1574 NLMISC_COMMAND(simulateMsgSpawnEasterEgg, "", "<service_id> <ai_instance> <easter_egg_id> <sheet> <bot_name> <x> <y>")
1576 if (args.size() != 7)
1577 return false;
1579 uint16 id;
1580 NLMISC::fromString(args[0], id);
1581 NLNET::TServiceId serviceId(id);
1582 uint32 instanceNumber;
1583 NLMISC::fromString(args[1], instanceNumber);
1584 uint32 easterEggId;
1585 NLMISC::fromString(args[2], easterEggId);
1586 CSheetId sheetId(args[3]);
1587 string botName = args[4];
1588 sint32 x;
1589 NLMISC::fromString(args[5], x);
1590 sint32 y;
1591 NLMISC::fromString(args[6], y);
1592 sint32 z = 0;
1593 sint32 heading = 0;
1594 std::string look = "";
1596 NLNET::CMessage msg("SPAWN_EASTER_EGG");
1597 uint32 messageVersion = 1;
1598 msg.serial(messageVersion);
1599 msg.serial(instanceNumber);
1600 msg.serial(easterEggId);
1601 msg.serial(sheetId);
1602 msg.serial(botName);
1603 msg.serial(look);
1605 msg.serial(x, y, z, heading);
1606 msg.invert();
1607 cbSpawnEasterEgg(msg, "FAKE", serviceId);
1609 return true;
1612 NLMISC_COMMAND(simulateMsgDespawnEasterEgg, "", "<service_id> <ai_instance> <easter_egg_id>")
1614 if (args.size() != 3)
1615 return false;
1617 uint16 id;
1618 NLMISC::fromString(args[0], id);
1619 NLNET::TServiceId serviceId(id);
1620 uint32 instanceNumber;
1621 NLMISC::fromString(args[1], instanceNumber);
1622 uint32 easterEggId;
1623 NLMISC::fromString(args[2], easterEggId);
1625 NLNET::CMessage msg("DESPAWN_EASTER_EGG");
1626 uint32 messageVersion = 1;
1627 msg.serial(messageVersion);
1628 msg.serial(instanceNumber);
1629 msg.serial(easterEggId);
1630 msg.invert();
1631 cbDespawnEasterEgg(msg, "FAKE", serviceId);
1633 return true;