1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
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
)
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()
57 if (_PlayerManager
!= NULL
)
59 delete _PlayerManager
;
60 _PlayerManager
= NULL
;
63 _EventNpcManager
= NULL
;
66 // check if ref pointers were notified of the destruction
67 nlassert(_EasterEggGroup
== NULL
);
68 nlassert(_EasterEggManager
== NULL
);
71 _SquadVariantNameToGroupDesc
.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
)
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());
92 zoneList
[CStringMapper::map(zoneName
)] = zone
;
95 void CAIInstance::removeZone(string
const& zoneName
, CNpcZone
* zone
)
97 TZoneList::iterator it
= zoneList
.find(CStringMapper::map(zoneName
));
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() )
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
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
);
149 CAIVector
pos( player
->pos() );
150 bool inside
= posit
->contains(pos
);
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
);
173 CGroupNpc
* grpNpc
= dynamic_cast<CGroupNpc
*>(grp
);
174 CPersistentStateInstance
* sa
= grpNpc
->getPersistentStateInstance();
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
);
191 CGroupNpc
* grpNpc
= dynamic_cast<CGroupNpc
*>(grp
);
192 CPersistentStateInstance
* sa
= grpNpc
->getPersistentStateInstance();
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();
221 _LastGroupAlias
= (900 + _InstanceNumber
) << LigoConfig
.getDynamicAliasSize();
223 sendInstanceInfoToEGS();
225 if (!EGSHasMirrorReady
)
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
)
235 TDataSetRow
const row
= TheDataset
.getCurrentDataSetRow(it
->second
);
239 // this is a player, check the instance number
240 CMirrorPropValueRO
<uint32
> playerInstance(TheDataset
, row
, DSPropertyAI_INSTANCE
);
241 if (playerInstance
!= instanceNumber
)
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
;
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();
288 grp
->getPersistentStateInstance()->advanceUserTimer(nbTicks
);
289 grp
=(*it
)->getNextValidGroupChild(grp
); // next group
295 void CAIInstance::addGroupInfo(CGroup
* grp
)
297 string
const& name
= grp
->aliasTreeOwner()->getName();
298 uint32 alias
= grp
->aliasTreeOwner()->getAlias();
300 _GroupFromNames
[name
].push_back(grp
);
302 _GroupFromAlias
[alias
] = grp
;
305 void CAIInstance::addGroupInfo(CGroup
* grp
, const string
&name
, uint32 alias
)
308 _GroupFromNames
[name
].push_back(grp
);
310 _GroupFromAlias
[alias
] = grp
;
314 void CAIInstance::removeGroupInfo(CGroup
* grp
, CAliasTreeOwner
* grpAliasTreeOwner
)
316 string
const& name
= grpAliasTreeOwner
->getName();
317 uint32 alias
= grpAliasTreeOwner
->getAlias();
321 std::map
< std::string
, std::vector
<NLMISC::CDbgPtr
<CGroup
> > >::iterator it
=_GroupFromNames
.find(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())
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())
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
;
359 otherName
= &findMissionName(alias
);
360 if (otherName
->empty())
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
)
384 static string 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
)
433 CManager
*mgrPtr
=NULL
;
436 uint32 localIndex
= std::numeric_limits
<uint32
>::max();
439 while((*id
!=':')&&(*id
!=0)) id
++;
444 localIndex
=getInt64FromStr(mgr
);
445 if (localIndex
<managers().size())
446 mgrPtr
= managers()[localIndex
];
448 goto tryWithEntityId
;
450 || searchType
==CAIS::AI_MANAGER
)
451 return dynamic_cast<CAIEntity
*> (mgrPtr
);
454 while((*id
!=':')&&(*id
!=0)) id
++;
459 grpPtr
= mgrPtr
->getGroup(getInt64FromStr(grp
));
461 goto tryWithEntityId
;
463 || searchType
==CAIS::AI_GROUP
)
464 return dynamic_cast<CAIEntity
*> (grpPtr
);
467 while((*id
!=':')&&(*id
!=0)) id
++;
471 botPtr
= grpPtr
->getBot(getInt64FromStr(bot
));
473 goto tryWithEntityId
;
475 || searchType
==CAIS::AI_BOT
)
476 return dynamic_cast<CAIEntity
*> (botPtr
);
483 entityId
.fromString(str
);
485 if (entityId
.isUnknownId())
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();
500 grpPtr
= mgrPtr
->getNextValidGroupChild ();
503 botPtr
= grpPtr
->getNextValidBotChild();
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
);
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
535 _Managers
.addChild(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
;
551 CCont
<CContinent
>::iterator itContinent
, itContinentEnd(continents().end());
552 for (itContinent
=continents().begin(); itContinent
!=itContinentEnd
; ++itContinent
)
554 CContinent
* continent
= *itContinent
;
557 // We should check individual errors, but fake success here :)
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 :)
581 //----------------------------------------------------------------------------
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(),
604 (_KamiDeposits
[alias
]==NULL
|| _KamiDeposits
[alias
]->getAliasNode()==NULL
)? "NULL": _KamiDeposits
[alias
]->getAliasNode()->fullName().c_str()
609 _KamiDeposits
[alias
] = grp
;
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
);
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
);
671 static float randomAngle()
673 uint32
const maxLimit
= CAngle::PI
*2;
674 float val
= (float)CAIS::rand32(maxLimit
);
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
)
682 AISHEETS::ICreatureCPtr sheet
= AISHEETS::CSheets::getInstance()->lookup(sheetId
);
685 nlwarning("invalid sheet while creating event npc group");
690 string name
= botsName
.empty() ? NLMISC::toString("event_group_%u", _LastGroupAlias
):botsName
;
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);
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
);
711 CSpawnGroupNpc
* spawnGroup
= grp
->getSpawnObj();
714 // the spawning has failed, delete the useless object
715 nlwarning("Failed to spawn the event group");
716 _EventNpcManager
->groups().removeChildByIndex(grp
->getChildIndex());
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
;
734 statePositional
= new CAIStatePositional(sm
, _LastStateAlias
, "start");
735 grp
->setStateAlias("start", statePositional
->getAlias());
736 sm
->states().addChild(statePositional
);
740 statePositional
= safe_cast
<CAIStatePositional
*>(sm
->states().getChildByAlias(stateAlias
));
742 grp
->setNextState(statePositional
);
746 grp
->getSpawnObj()->spawnBots();
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
)
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
);
764 nlwarning("invalid sheet while creating event npc group");
768 bot
->setSheet(sheet
);
769 bot
->setPrimAlias(900+_InstanceNumber
);
771 bot
->setClientSheet(look
);
772 bot
->equipmentInit();
773 bot
->initEnergy(/*groupEnergyCoef()*/0);
775 if (dispersionRadius
== 0)
779 if (orientation
< (NLMISC::Pi
* 2.0) && orientation
> (-NLMISC::Pi
* 2.0))
780 angle
= (float)orientation
;
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;
794 rpos
+= randomPos(dispersionRadius
);
797 while (!worldMap
.setWorldPosition(AITYPES::vp_auto
, wp
, rpos
) && maxTries
>0);
801 bot
->setStartPos(pos
.x().asDouble(), pos
.y().asDouble(), angle
, AITYPES::vp_auto
);
804 bot
->setStartPos(rpos
.x().asDouble(),rpos
.y().asDouble(), angle
, AITYPES::vp_auto
);
808 grp
->getSpawnObj()->spawnBots(name
);
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
)
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);
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());
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());
861 AISHEETS::ICreatureCPtr sheet
= AISHEETS::CSheets::getInstance()->lookup(sheetId
);
864 nlwarning("Invalid sheet '%s' while creating an easter egg", sheetId
.toString().c_str());
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();
876 bot
->setStartPos(x
, y
, float(heading
), AITYPES::vp_auto
);
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());
890 void CAIInstance::destroyEasterEgg(uint32 easterEggId
)
892 CBotEasterEgg
* bot
= getEasterEgg(easterEggId
);
896 _EasterEggGroup
->bots().removeChildByIndex(bot
->getChildIndex());
900 nlwarning("Cannot destroy easter egg %u because not found", easterEggId
);
904 CBotEasterEgg
* CAIInstance::getEasterEgg(uint32 easterEggId
)
906 if (_EasterEggGroup
== NULL
)
909 FOREACH(itBot
, CCont
<CBot
>, _EasterEggGroup
->bots())
911 CBotEasterEgg
* bot
= dynamic_cast<CBotEasterEgg
*>(*itBot
);
913 nlassert(bot
!= NULL
);
915 if (bot
!= NULL
&& bot
->getEasterEggId() == easterEggId
)
922 void cbEventCreateNpcGroup( NLNET::CMessage
& msgin
, const std::string
&serviceName
, NLNET::TServiceId serviceId
)
924 uint32 messageVersion
;
925 uint32 instanceNumber
;
931 NLMISC::CSheetId sheetId
;
933 double dispersionRadius
;
935 std::string botsName
;
938 msgin
.serial(messageVersion
);
939 nlassert(messageVersion
==1);
940 msgin
.serial(instanceNumber
);
941 msgin
.serial(playerId
);
945 msgin
.serial(orientation
);
946 msgin
.serial(nbBots
);
947 msgin
.serial(sheetId
);
948 msgin
.serial(dispersionRadius
);
949 msgin
.serial(spawnBots
);
950 msgin
.serial(botsName
);
953 CAIInstance
* instance
= CAIS::instance().getAIInstance(instanceNumber
);
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();
966 CEntityId id
= pbot
->getEntityId();
970 NLMISC::TGameCycle tick
= CTickEventHandler::getGameCycle() + 1;
971 CMessage
msgout2("ENTITY_TELEPORTATION");
972 msgout2
.serial( id
);
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
;
996 vector
<string
> strings
;
997 msgin
.serial(messageVersion
);
998 nlassert(messageVersion
==1);
999 msgin
.serial(nbString
);
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
);
1010 strings
[1] = firstCommand
;
1011 for (uint32 i
=2; i
<nbString
-2; ++i
)
1013 msgin
.serial(strings
[i
]);
1018 nlinfo("Event group script with %d strings :", nbString
-1);
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
)
1030 CAIEntityPhysical
* entity
= CAIEntityPhysicalLocator::getInstance()->getEntity(botEId
);
1031 CSpawnBotNpc
* bot
= dynamic_cast<CSpawnBotNpc
*>(entity
);
1034 if (!bot
->getPersistent().getOwner())
1037 strings
[0] = bot
->getPersistent().getOwner()->getName();
1042 strings
[0] = (string
)groupname
.replace("#last", _PlayersLastCreatedNpcGroup
[playerId
].c_str());
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
;
1058 float notHungryRadius
;
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
);
1070 /// try to find the bot name
1071 buildFilteredBotList(bots
, botName
);
1074 nlwarning("No bot correspond to name %s", botName
.c_str());
1079 FOREACH(itBot
, vector
<CBot
*>, bots
)
1082 CBotFauna
* botFauna
= dynamic_cast<CBotFauna
*>(bot
);
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
;
1101 msgin
.serial(messageVersion
);
1102 nlassert(messageVersion
==1);
1103 msgin
.serial(botName
);
1106 /// try to find the bot name
1107 buildFilteredBotList(bots
, botName
);
1110 nlwarning("No bot correspond to name %s", botName
.c_str());
1115 FOREACH(itBot
, vector
<CBot
*>, bots
)
1118 CBotFauna
* botFauna
= dynamic_cast<CBotFauna
*>(bot
);
1120 botFauna
->resetAggroRadius();
1125 void cbEventBotCanAggro( NLNET::CMessage
& msgin
, const std::string
&serviceName
, NLNET::TServiceId serviceId
)
1127 uint32 messageVersion
;
1131 msgin
.serial(messageVersion
);
1132 nlassert(messageVersion
==1);
1133 msgin
.serial(botName
);
1134 msgin
.serial(canAggro
);
1137 /// try to find the bot name
1138 buildFilteredBotList(bots
, botName
);
1141 nlwarning("No bot correspond to name %s", botName
.c_str());
1146 FOREACH(itBot
, vector
<CBot
*>, bots
)
1151 CSpawnBot
* spBot
= bot
->getSpawnObj();
1153 spBot
->setCanAggro(canAggro
);
1159 void cbEventBotSheet( NLNET::CMessage
& msgin
, const std::string
&serviceName
, NLNET::TServiceId serviceId
)
1161 uint32 messageVersion
;
1163 NLMISC::CSheetId sheetId
;
1164 bool autoSpawnDespawn
;
1167 msgin
.serial(messageVersion
);
1168 nlassert(messageVersion
==3);
1169 msgin
.serial(botName
);
1170 msgin
.serial(sheetId
);
1171 msgin
.serial(autoSpawnDespawn
);
1172 msgin
.serial(customName
);
1175 /// try to find the bot name
1176 buildFilteredBotList(bots
, botName
);
1179 nlwarning("No bot correspond to name %s", botName
.c_str());
1184 AISHEETS::ICreatureCPtr
const sheet
= AISHEETS::CSheets::getInstance()->lookup(sheetId
);
1185 if (sheetId
!=NLMISC::CSheetId::Unknown
&& !sheet
.isNull())
1187 FOREACH(itBot
, vector
<CBot
*>, bots
)
1192 bot
->setCustomName(customName
);
1193 bot
->triggerSetSheet(sheet
);
1194 if (autoSpawnDespawn
&& !bot
->isSpawned())
1199 else if (autoSpawnDespawn
)
1201 FOREACH(itBot
, vector
<CBot
*>, bots
)
1204 if (bot
&& bot
->isSpawned())
1211 extern vector
<vector
<string
> > scriptCommandsBotById
;
1213 void cbR2NpcBotScriptById(NLNET::CMessage
& msgin
, const std::string
& serviceName
, NLNET::TServiceId serviceId
)
1215 uint32 messageVersion
;
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
;
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
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
);
1259 void dump(CLog
& log
) const;
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
1276 CRegisteredService(NLNET::TServiceId serviceId
, const CEntityId
& botId
)
1277 : _ServiceId(serviceId
)
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
);
1291 NLNET::TServiceId _ServiceId
;
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
;
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());
1321 bot
->attachObserver(this);
1323 CRegisteredService
rs(serviceId
, botId
);
1325 for (; first
!= last
; ++first
)
1327 if ((*first
).second
== rs
)
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());
1358 nlwarning("No service requested despawn notification for bot %s",
1359 LigoConfig
.aliasToString(bot
->getAlias()).c_str()
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
;
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
;
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());
1410 nlwarning("No service requested death notification for bot %s",
1411 LigoConfig
.aliasToString(bot
->getAlias()).c_str()
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
;
1425 _RegisteredServices
.erase(tmp
);
1430 void cbAskBotDespawnNotification(NLNET::CMessage
& msgin
, const std::string
& serviceName
, NLNET::TServiceId serviceId
)
1432 uint32 messageVersion
;
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
);
1448 CAIEntityPhysical
* entity
= CAIS::instance().getEntityPhysical(botRowId
);
1451 CMessages::notifyBotDespawn(serviceId
, botAlias
, botId
);
1456 nlassert(entity
->getEntityId() == botId
);
1459 CSpawnBot
* bot
= NULL
;
1460 switch (entity
->getRyzomType())
1462 case RYZOMID::creature
:
1464 bot
= NLMISC::safe_cast
<CSpawnBot
*>(entity
);
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
);
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
;
1493 msgin
.serial(messageVersion
);
1494 nlassert(messageVersion
==1);
1495 msgin
.serial(instanceNumber
);
1496 msgin
.serial(easterEggId
);
1497 msgin
.serial(sheetId
);
1498 msgin
.serial(botName
);
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
);
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
);
1518 nlwarning("Cannot spawn easter egg %u!", easterEggId
);
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
;
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)
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
);
1560 cbAskBotDespawnNotification(msg
, "FAKE", serviceId
);
1565 NLMISC_COMMAND(dumpBotDespawnNotification
, "", "")
1567 if (args
.size() != 0)
1570 CBotDespawnNotification::getInstance()->dump(log
);
1574 NLMISC_COMMAND(simulateMsgSpawnEasterEgg
, "", "<service_id> <ai_instance> <easter_egg_id> <sheet> <bot_name> <x> <y>")
1576 if (args
.size() != 7)
1580 NLMISC::fromString(args
[0], id
);
1581 NLNET::TServiceId
serviceId(id
);
1582 uint32 instanceNumber
;
1583 NLMISC::fromString(args
[1], instanceNumber
);
1585 NLMISC::fromString(args
[2], easterEggId
);
1586 CSheetId
sheetId(args
[3]);
1587 string botName
= args
[4];
1589 NLMISC::fromString(args
[5], x
);
1591 NLMISC::fromString(args
[6], y
);
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
);
1605 msg
.serial(x
, y
, z
, heading
);
1607 cbSpawnEasterEgg(msg
, "FAKE", serviceId
);
1612 NLMISC_COMMAND(simulateMsgDespawnEasterEgg
, "", "<service_id> <ai_instance> <easter_egg_id>")
1614 if (args
.size() != 3)
1618 NLMISC::fromString(args
[0], id
);
1619 NLNET::TServiceId
serviceId(id
);
1620 uint32 instanceNumber
;
1621 NLMISC::fromString(args
[1], instanceNumber
);
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
);
1631 cbDespawnEasterEgg(msg
, "FAKE", serviceId
);