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
&grpName
, const std::string
&look
, sint32 cell
, const std::string
&botsName
, const std::string
&vpx
) {
679 if (!_EventNpcManager
)
682 AISHEETS::ICreatureCPtr sheet
= AISHEETS::CSheets::getInstance()->lookup(sheetId
);
685 nlwarning("invalid sheet while creating event npc group");
690 string name
= grpName
.empty() ? NLMISC::toString("event_group_%u", _LastGroupAlias
):grpName
;
691 string botname
= botsName
.empty() ? name
: botsName
;
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);
700 grp
->botName
= botname
;
702 grp
->clearParameters();
703 grp
->setPlayerAttackable(true);
704 grp
->setBotAttackable(true);
706 grp
->autoDestroy(true);
708 grp
->clrBotsAreNamedFlag();
711 eventCreateNpcBot(grp
, nbBots
, false, sheetId
, pos
, "", orientation
, dispersionRadius
, look
, botname
, vpx
);
714 CSpawnGroupNpc
* spawnGroup
= grp
->getSpawnObj();
717 // the spawning has failed, delete the useless object
718 nlwarning("Failed to spawn the event group");
719 _EventNpcManager
->groups().removeChildByIndex(grp
->getChildIndex());
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
;
737 statePositional
= new CAIStatePositional(sm
, _LastStateAlias
, "start");
738 grp
->setStateAlias("start", statePositional
->getAlias());
739 sm
->states().addChild(statePositional
);
743 statePositional
= safe_cast
<CAIStatePositional
*>(sm
->states().getChildByAlias(stateAlias
));
745 grp
->setNextState(statePositional
);
749 grp
->getSpawnObj()->spawnBots(botname
, vpx
);
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
)
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
);
767 nlwarning("invalid sheet while creating event npc group");
771 bot
->setSheet(sheet
);
772 bot
->setPrimAlias(900+_InstanceNumber
);
774 bot
->setClientSheet(look
);
775 bot
->equipmentInit();
776 bot
->initEnergy(/*groupEnergyCoef()*/0);
778 if (dispersionRadius
== 0)
782 if (orientation
< (NLMISC::Pi
* 2.0) && orientation
> (-NLMISC::Pi
* 2.0))
783 angle
= (float)orientation
;
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;
797 rpos
+= randomPos(dispersionRadius
);
800 while (!worldMap
.setWorldPosition(AITYPES::vp_auto
, wp
, rpos
) && maxTries
>0);
804 bot
->setStartPos(pos
.x().asDouble(), pos
.y().asDouble(), angle
, AITYPES::vp_auto
);
807 bot
->setStartPos(rpos
.x().asDouble(),rpos
.y().asDouble(), angle
, AITYPES::vp_auto
);
811 grp
->getSpawnObj()->spawnBots(botname
, vpx
);
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
)
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);
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());
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());
864 AISHEETS::ICreatureCPtr sheet
= AISHEETS::CSheets::getInstance()->lookup(sheetId
);
867 nlwarning("Invalid sheet '%s' while creating an easter egg", sheetId
.toString().c_str());
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();
879 bot
->setStartPos(x
, y
, float(heading
), AITYPES::vp_auto
);
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());
893 void CAIInstance::destroyEasterEgg(uint32 easterEggId
)
895 CBotEasterEgg
* bot
= getEasterEgg(easterEggId
);
899 _EasterEggGroup
->bots().removeChildByIndex(bot
->getChildIndex());
903 nlwarning("Cannot destroy easter egg %u because not found", easterEggId
);
907 CBotEasterEgg
* CAIInstance::getEasterEgg(uint32 easterEggId
)
909 if (_EasterEggGroup
== NULL
)
912 FOREACH(itBot
, CCont
<CBot
>, _EasterEggGroup
->bots())
914 CBotEasterEgg
* bot
= dynamic_cast<CBotEasterEgg
*>(*itBot
);
916 nlassert(bot
!= NULL
);
918 if (bot
!= NULL
&& bot
->getEasterEggId() == easterEggId
)
925 void cbEventCreateNpcGroup( NLNET::CMessage
& msgin
, const std::string
&serviceName
, NLNET::TServiceId serviceId
)
927 uint32 messageVersion
;
928 uint32 instanceNumber
;
934 NLMISC::CSheetId sheetId
;
936 double dispersionRadius
;
938 std::string botsName
;
941 msgin
.serial(messageVersion
);
942 nlassert(messageVersion
==1);
943 msgin
.serial(instanceNumber
);
944 msgin
.serial(playerId
);
948 msgin
.serial(orientation
);
949 msgin
.serial(nbBots
);
950 msgin
.serial(sheetId
);
951 msgin
.serial(dispersionRadius
);
952 msgin
.serial(spawnBots
);
953 msgin
.serial(botsName
);
956 CAIInstance
* instance
= CAIS::instance().getAIInstance(instanceNumber
);
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();
969 CEntityId id
= pbot
->getEntityId();
973 NLMISC::TGameCycle tick
= CTickEventHandler::getGameCycle() + 1;
974 CMessage
msgout2("ENTITY_TELEPORTATION");
975 msgout2
.serial( id
);
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
;
999 vector
<string
> strings
;
1000 msgin
.serial(messageVersion
);
1001 nlassert(messageVersion
==1);
1002 msgin
.serial(nbString
);
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
);
1013 strings
[1] = firstCommand
;
1014 for (uint32 i
=2; i
<nbString
-2; ++i
)
1016 msgin
.serial(strings
[i
]);
1021 nlinfo("Event group script with %d strings :", nbString
-1);
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
)
1033 CAIEntityPhysical
* entity
= CAIEntityPhysicalLocator::getInstance()->getEntity(botEId
);
1034 CSpawnBotNpc
* bot
= dynamic_cast<CSpawnBotNpc
*>(entity
);
1037 if (!bot
->getPersistent().getOwner())
1040 strings
[0] = bot
->getPersistent().getOwner()->getName();
1045 strings
[0] = (string
)groupname
.replace("#last", _PlayersLastCreatedNpcGroup
[playerId
].c_str());
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
;
1061 float notHungryRadius
;
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
);
1073 /// try to find the bot name
1074 buildFilteredBotList(bots
, botName
);
1077 nlwarning("No bot correspond to name %s", botName
.c_str());
1082 FOREACH(itBot
, vector
<CBot
*>, bots
)
1085 CBotFauna
* botFauna
= dynamic_cast<CBotFauna
*>(bot
);
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
;
1104 msgin
.serial(messageVersion
);
1105 nlassert(messageVersion
==1);
1106 msgin
.serial(botName
);
1109 /// try to find the bot name
1110 buildFilteredBotList(bots
, botName
);
1113 nlwarning("No bot correspond to name %s", botName
.c_str());
1118 FOREACH(itBot
, vector
<CBot
*>, bots
)
1121 CBotFauna
* botFauna
= dynamic_cast<CBotFauna
*>(bot
);
1123 botFauna
->resetAggroRadius();
1128 void cbEventBotCanAggro( NLNET::CMessage
& msgin
, const std::string
&serviceName
, NLNET::TServiceId serviceId
)
1130 uint32 messageVersion
;
1134 msgin
.serial(messageVersion
);
1135 nlassert(messageVersion
==1);
1136 msgin
.serial(botName
);
1137 msgin
.serial(canAggro
);
1140 /// try to find the bot name
1141 buildFilteredBotList(bots
, botName
);
1144 nlwarning("No bot correspond to name %s", botName
.c_str());
1149 FOREACH(itBot
, vector
<CBot
*>, bots
)
1154 CSpawnBot
* spBot
= bot
->getSpawnObj();
1156 spBot
->setCanAggro(canAggro
);
1162 void cbEventBotSheet( NLNET::CMessage
& msgin
, const std::string
&serviceName
, NLNET::TServiceId serviceId
)
1164 uint32 messageVersion
;
1166 NLMISC::CSheetId sheetId
;
1167 bool autoSpawnDespawn
;
1170 msgin
.serial(messageVersion
);
1171 nlassert(messageVersion
==3);
1172 msgin
.serial(botName
);
1173 msgin
.serial(sheetId
);
1174 msgin
.serial(autoSpawnDespawn
);
1175 msgin
.serial(customName
);
1178 /// try to find the bot name
1179 buildFilteredBotList(bots
, botName
);
1182 nlwarning("No bot correspond to name %s", botName
.c_str());
1187 AISHEETS::ICreatureCPtr
const sheet
= AISHEETS::CSheets::getInstance()->lookup(sheetId
);
1188 if (sheetId
!=NLMISC::CSheetId::Unknown
&& !sheet
.isNull())
1190 FOREACH(itBot
, vector
<CBot
*>, bots
)
1195 bot
->setCustomName(customName
);
1196 bot
->triggerSetSheet(sheet
);
1197 if (autoSpawnDespawn
&& !bot
->isSpawned())
1202 else if (autoSpawnDespawn
)
1204 FOREACH(itBot
, vector
<CBot
*>, bots
)
1207 if (bot
&& bot
->isSpawned())
1214 extern vector
<vector
<string
> > scriptCommandsBotById
;
1216 void cbR2NpcBotScriptById(NLNET::CMessage
& msgin
, const std::string
& serviceName
, NLNET::TServiceId serviceId
)
1218 uint32 messageVersion
;
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
;
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
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
);
1262 void dump(CLog
& log
) const;
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
1279 CRegisteredService(NLNET::TServiceId serviceId
, const CEntityId
& botId
)
1280 : _ServiceId(serviceId
)
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
);
1294 NLNET::TServiceId _ServiceId
;
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
;
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());
1324 bot
->attachObserver(this);
1326 CRegisteredService
rs(serviceId
, botId
);
1328 for (; first
!= last
; ++first
)
1330 if ((*first
).second
== rs
)
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());
1361 nlwarning("No service requested despawn notification for bot %s",
1362 LigoConfig
.aliasToString(bot
->getAlias()).c_str()
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
;
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
;
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());
1413 nlwarning("No service requested death notification for bot %s",
1414 LigoConfig
.aliasToString(bot
->getAlias()).c_str()
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
;
1428 _RegisteredServices
.erase(tmp
);
1433 void cbAskBotDespawnNotification(NLNET::CMessage
& msgin
, const std::string
& serviceName
, NLNET::TServiceId serviceId
)
1435 uint32 messageVersion
;
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
);
1451 CAIEntityPhysical
* entity
= CAIS::instance().getEntityPhysical(botRowId
);
1454 CMessages::notifyBotDespawn(serviceId
, botAlias
, botId
);
1459 nlassert(entity
->getEntityId() == botId
);
1462 CSpawnBot
* bot
= NULL
;
1463 switch (entity
->getRyzomType())
1465 case RYZOMID::creature
:
1467 bot
= NLMISC::safe_cast
<CSpawnBot
*>(entity
);
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
);
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
;
1496 msgin
.serial(messageVersion
);
1497 nlassert(messageVersion
==1);
1498 msgin
.serial(instanceNumber
);
1499 msgin
.serial(easterEggId
);
1500 msgin
.serial(sheetId
);
1501 msgin
.serial(botName
);
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
);
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
);
1521 nlwarning("Cannot spawn easter egg %u!", easterEggId
);
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
;
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)
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
);
1563 cbAskBotDespawnNotification(msg
, "FAKE", serviceId
);
1568 NLMISC_COMMAND(dumpBotDespawnNotification
, "", "")
1570 if (args
.size() != 0)
1573 CBotDespawnNotification::getInstance()->dump(log
);
1577 NLMISC_COMMAND(simulateMsgSpawnEasterEgg
, "", "<service_id> <ai_instance> <easter_egg_id> <sheet> <bot_name> <x> <y>")
1579 if (args
.size() != 7)
1583 NLMISC::fromString(args
[0], id
);
1584 NLNET::TServiceId
serviceId(id
);
1585 uint32 instanceNumber
;
1586 NLMISC::fromString(args
[1], instanceNumber
);
1588 NLMISC::fromString(args
[2], easterEggId
);
1589 CSheetId
sheetId(args
[3]);
1590 string botName
= args
[4];
1592 NLMISC::fromString(args
[5], x
);
1594 NLMISC::fromString(args
[6], y
);
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
);
1608 msg
.serial(x
, y
, z
, heading
);
1610 cbSpawnEasterEgg(msg
, "FAKE", serviceId
);
1615 NLMISC_COMMAND(simulateMsgDespawnEasterEgg
, "", "<service_id> <ai_instance> <easter_egg_id>")
1617 if (args
.size() != 3)
1621 NLMISC::fromString(args
[0], id
);
1622 NLNET::TServiceId
serviceId(id
);
1623 uint32 instanceNumber
;
1624 NLMISC::fromString(args
[1], instanceNumber
);
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
);
1634 cbDespawnEasterEgg(msg
, "FAKE", serviceId
);