Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / ai_profile_npc.cpp
blob4608bcdbf466fc0c1286751429d7ad75c3a74178
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 "profile.h"
19 #include "ai_bot_npc.h"
20 #include "ai_grp_npc.h"
21 #include "ai_mgr_npc.h"
22 #include "ai_grp_fauna.h"
24 #include "ai_bot_fauna.h"
26 #include "states.h"
27 #include "path_behaviors.h"
28 #include "ai_aggro.h"
29 #include "ai_player.h"
30 #include "ai_generic_fight.h"
31 #include "ai_profile_npc.h"
32 #include "group_profile.h"
34 extern bool simulateBug(int bugId);
36 #include "dyn_grp_inline.h"
38 using namespace std;
39 using namespace RYAI_MAP_CRUNCH;
40 using namespace NLMISC;
41 using namespace NLNET;
42 using namespace AITYPES;
44 // Global configuration variables
45 extern CVariable<int> DefaultWanderMinTimer;
46 extern CVariable<int> DefaultWanderMaxTimer;
47 extern CVariable<sint32> FameForGuardAttack;
48 extern CVariable<sint32> FameForGuardHelp;
50 // Stuff used for management of log messages
51 bool ai_profile_npc_VerboseLog = false;
53 void ai_profile_npc_LOG(std::string const& type, std::string const& profile, std::string const& step, std::string const& object)
55 static size_t maxType = 0;
56 static size_t maxProfile = 0;
57 static size_t maxStep = 0;
58 static size_t maxObject = 0;
59 if (ai_profile_npc_VerboseLog)
61 maxType = std::max(maxType, type.length());
62 maxProfile = std::max(maxProfile, profile.length());
63 maxStep = std::max(maxStep, step.length());
64 maxObject = std::max(maxObject, object.length());
65 std::string log = "profile";
66 log += " " + type + std::string(maxType - type.length(), ' ');
67 log += " " + profile + std::string(maxProfile - profile.length(), ' ');
68 log += " " + step + std::string(maxStep - step.length(), ' ');
69 log += " " + object + std::string(maxObject - object.length(), ' ');
70 nlinfo("%s", log.c_str());
73 #define PROFILE_LOG(type,profile,step,object) ai_profile_npc_LOG(type,profile,step,object)
75 #define LOG if (!ai_profile_npc_VerboseLog) {} else nlinfo
77 NLMISC_COMMAND(verboseAIProfiles,"Turn on or off or check the state of verbose ai profile info logging","")
79 if(args.size()>1)
80 return false;
82 if(args.size()==1)
83 StrToBool (ai_profile_npc_VerboseLog, args[0]);
85 nlinfo("VerboseLogging is %s",ai_profile_npc_VerboseLog?"ON":"OFF");
86 return true;
89 /****************************************************************************/
90 /* Local classes */
91 /****************************************************************************/
93 //////////////////////////////////////////////////////////////////////////////
94 // CBotProfileFightNpc //
95 //////////////////////////////////////////////////////////////////////////////
97 class CBotProfileFightNpc
98 : public CBotProfileFight
100 public:
101 CBotProfileFightNpc(CProfileOwner* owner, CAIEntityPhysical* ennemy);
102 virtual ~CBotProfileFightNpc();
104 virtual std::string getOneLineInfoString() const { return "fight npc bot profile"; }
106 void noMoreTarget();
108 void eventBeginFight();
109 void eventTargetKilled();
112 //////////////////////////////////////////////////////////////////////////////
113 // CBotProfileHealNpc //
114 //////////////////////////////////////////////////////////////////////////////
116 class CBotProfileHealNpc
117 : public CBotProfileHeal
118 , public IAIEntityPhysicalHealer
120 public:
121 CBotProfileHealNpc(CAIEntityPhysical* target, CProfileOwner* owner);
122 virtual ~CBotProfileHealNpc();
124 virtual std::string getOneLineInfoString() const { return "heal npc bot profile"; }
126 void noMoreTarget();
128 virtual void healerAdded(CAIEntityPhysical* entity);
129 virtual void healerRemoved(CAIEntityPhysical* entity);
131 // void eventBeginFight();
132 // void eventTargetKilled();
133 private:
134 CAIEntityPhysical* _Target;
137 //////////////////////////////////////////////////////////////////////////////
138 // CBotProfileReturnAfterFightNpc //
139 //////////////////////////////////////////////////////////////////////////////
141 class CBotProfileReturnAfterFightNpc
142 : public CBotProfileReturnAfterFight
144 public:
145 CBotProfileReturnAfterFightNpc(CSpawnBotNpc* owner);
146 ~CBotProfileReturnAfterFightNpc();
147 virtual void beginProfile();
148 virtual void endProfile();
149 virtual void updateProfile(uint ticksSinceLastUpdate);
150 virtual std::string getOneLineInfoString() const;
152 // virtual NLMISC::CSmartPtr<CMovementMagnet> const& getMovementMagnet() const { return _MovementMagnet; }
154 private:
155 CPathCont _PathCont;
156 NLMISC::CSmartPtr<CBotProfileFollowPos> _MoveProfile;
159 //////////////////////////////////////////////////////////////////////////////
160 // CGrpProfileTribu //
161 //////////////////////////////////////////////////////////////////////////////
163 class CGrpProfileTribu : public CGrpProfileNormal
165 public:
166 CGrpProfileTribu(CProfileOwner *owner);
167 virtual ~CGrpProfileTribu();
169 virtual void beginProfile();
170 virtual void endProfile();
171 virtual void updateProfile(uint ticksSinceLastUpdate);
173 virtual std::string getOneLineInfoString() const;
175 virtual TProfiles getAIProfileType() const { return ACTIVITY_TRIBU; }
177 private:
178 CAIVector _CenterPos;
181 //////////////////////////////////////////////////////////////////////////////
182 // CGrpProfileStandOnVertices //
183 //////////////////////////////////////////////////////////////////////////////
185 class CGrpProfileStandAtStartPoint
186 : public CMoveProfile
188 public:
189 CGrpProfileStandAtStartPoint(CProfileOwner* owner);
190 virtual ~CGrpProfileStandAtStartPoint();
192 class CBotPositionner : public CRefCount
194 public:
195 CBotPositionner(RYAI_MAP_CRUNCH::TAStarFlag flags);
196 CBotPositionner(TVerticalPos verticalPos, CAIPos position, RYAI_MAP_CRUNCH::TAStarFlag flag);
197 virtual ~CBotPositionner();
198 void setBotAtDest(bool atDest = true);
199 bool isBotAtDest() const;
201 CPathCont _PathCont;
202 CAIPos _Position;
203 TVerticalPos _VerticalPos;
204 private:
205 bool _BotAtDest;
208 CPathCont* getPathCont(CBot const* bot);
210 virtual void beginProfile();
211 void resumeProfile();
212 virtual void endProfile();
213 virtual void updateProfile(uint ticksSinceLastUpdate);
215 void addBot(CBot* bot);
216 void removeBot(CBot* bot);
218 void setCurrentValidPos();
220 virtual TProfiles getAIProfileType() const { return MOVE_STAND_ON_VERTICES; }
221 virtual std::string getOneLineInfoString() const;
223 private:
224 typedef std::map<CBot const*, CSmartPtr<CBotPositionner> > TNpcBotPositionnerMap;
226 TNpcBotPositionnerMap _NpcList;
227 bool _Finished;
230 /****************************************************************************/
231 /* Local profile factories */
232 /****************************************************************************/
234 //- Simple profile factories (based on generic factory) ----------------------
236 // CGrpProfileFightFactory
237 typedef CAIGenericProfileFactory<CGrpProfileFight> CGrpProfileFightFactory;
239 // CGrpProfileTribuFactory
240 typedef CAIGenericProfileFactory<CGrpProfileTribu> CGrpProfileTribuFactory;
242 // CGrpProfileIdleFactory
243 typedef CAIGenericProfileFactory<CGrpProfileIdle> CGrpProfileIdleFactory;
245 // CGrpProfileStandAtStartPointFactory
246 typedef CAIGenericProfileFactory<CGrpProfileStandAtStartPoint> CGrpProfileStandAtStartPointFactory;
248 //- Singleton profiles (stateless ones) --------------------------------------
249 extern CGrpProfileFightFactory GrpProfileFightFactory;
251 /****************************************************************************/
252 /* Implementations */
253 /****************************************************************************/
255 //////////////////////////////////////////////////////////////////////////////
256 // CSpawnGroupNpc //
257 //////////////////////////////////////////////////////////////////////////////
259 void CSpawnGroupNpc::botHaveDied(CBotNpc* bot)
261 // check bot profile type and update group profile bot list.
262 if (fightProfile().getAIProfile())
264 CSlaveProfile* const profile = NLMISC::type_cast<CSlaveProfile*>(fightProfile().getAIProfile());
265 if (profile)
266 profile->removeBot(bot);
268 if (movingProfile().getAIProfile())
270 CSlaveProfile* const profile = NLMISC::type_cast<CSlaveProfile*>(movingProfile().getAIProfile());
271 if (profile)
272 profile->removeBot(bot);
276 CSpawnBotNpc* const spawn = bot->getSpawn();
277 if (spawn)
278 spawn->setAIProfile(BotProfileStandAtPosFactory.createAIProfile(spawn));
282 void CSpawnGroupNpc::botHaveDespawn(CBotNpc* bot)
284 CSpawnGroupNpc::botHaveDied(bot);
287 void CSpawnGroupNpc::botHaveSpawn(CBotNpc* bot)
289 if (movingProfile().getAIProfile())
291 NLMISC::safe_cast<CSlaveProfile*>(movingProfile().getAIProfile())->addBot(bot);
293 if (fightProfile().getAIProfile())
295 NLMISC::safe_cast<CSlaveProfile*>(fightProfile().getAIProfile())->addBot(bot);
299 //////////////////////////////////////////////////////////////////////////////
300 // CBotProfileFightNpc //
301 //////////////////////////////////////////////////////////////////////////////
303 CBotProfileFightNpc::CBotProfileFightNpc(CProfileOwner* owner, CAIEntityPhysical* ennemy)
304 : CBotProfileFight(owner, ennemy)
306 PROFILE_LOG("bot", "fight_npc", "ctor", "");
309 CBotProfileFightNpc::~CBotProfileFightNpc()
311 PROFILE_LOG("bot", "fight_npc", "dtor", "");
314 void CBotProfileFightNpc::noMoreTarget()
316 _Bot->setAIProfile(BotProfileStandAtPosFactory.createAIProfile(_Bot.ptr()));
319 void CBotProfileFightNpc::eventBeginFight()
321 TempSpeaker = _Bot;
322 CSpawnBotNpc* spawnable = NLMISC::safe_cast<CSpawnBotNpc*>(_Bot.ptr());
323 CGroupNpc* grpNpc = static_cast<CGroupNpc*>(spawnable->getPersistent().getOwner());
324 grpNpc->processStateEvent(grpNpc->getEventContainer().EventBotBeginFight);
325 TempSpeaker = NULL;
327 void CBotProfileFightNpc::eventTargetKilled()
329 TempSpeaker = _Bot;
330 CSpawnBotNpc* spawnable= NLMISC::safe_cast<CSpawnBotNpc*>(_Bot.ptr());
331 CGroupNpc* grpNpc = static_cast<CGroupNpc*>(spawnable->getPersistent().getOwner());
332 grpNpc->processStateEvent(grpNpc->getEventContainer().EventBotTargetKilled);
333 TempSpeaker = NULL;
336 //////////////////////////////////////////////////////////////////////////////
337 // CBotProfileHealNpc //
338 //////////////////////////////////////////////////////////////////////////////
340 CBotProfileHealNpc::CBotProfileHealNpc(CAIEntityPhysical* target, CProfileOwner* owner)
341 : CBotProfileHeal(target->dataSetRow(), owner)
342 , _Target(target)
344 PROFILE_LOG("bot", "heal_npc", "ctor", "");
345 if (_Target)
346 _Target->addHealer(this);
349 CBotProfileHealNpc::~CBotProfileHealNpc()
351 PROFILE_LOG("bot", "heal_npc", "dtor", "");
352 if (_Target)
353 _Target->delHealer(this);
356 void CBotProfileHealNpc::healerAdded(CAIEntityPhysical* entity)
360 void CBotProfileHealNpc::healerRemoved(CAIEntityPhysical* entity)
362 if (_Target == entity)
363 _Target = NULL;
366 void CBotProfileHealNpc::noMoreTarget()
368 _Bot->setAIProfile(BotProfileStandAtPosFactory.createAIProfile(_Bot.ptr()));
371 //////////////////////////////////////////////////////////////////////////////
372 // CBotProfileReturnAfterFightNpc //
373 //////////////////////////////////////////////////////////////////////////////
375 CBotProfileReturnAfterFightNpc::CBotProfileReturnAfterFightNpc(CSpawnBotNpc* owner)
376 : CBotProfileReturnAfterFight(owner)
377 , _PathCont(owner->getPersistent().getOwner()->getAStarFlag())
379 PROFILE_LOG("bot_npc", "return_after_fight", "ctor", "");
380 // CSpawnBotNpc* bot = NLMISC::safe_cast<CSpawnBotNpc*>(owner);
381 // RYAI_MAP_CRUNCH::TAStarFlag denyFlags = bot->getAStarFlag();
382 _PathCont.setDestination(owner->getReturnPos());
383 _MoveProfile = NLMISC::CSmartPtr<CBotProfileFollowPos>(new CBotProfileFollowPos(&_PathCont, owner));
386 CBotProfileReturnAfterFightNpc::~CBotProfileReturnAfterFightNpc()
388 PROFILE_LOG("bot_npc", "return_after_fight", "dtor", "");
391 void CBotProfileReturnAfterFightNpc::beginProfile()
393 PROFILE_LOG("bot_npc", "return_after_fight", "begin", "");
394 CBotProfileReturnAfterFight::beginProfile();
395 _Bot->ignoreReturnAggro(true);
396 _MoveProfile->beginProfile();
399 void CBotProfileReturnAfterFightNpc::endProfile()
401 PROFILE_LOG("bot_npc", "return_after_fight", "end", "");
402 _MoveProfile->endProfile();
403 _Bot->ignoreReturnAggro(false);
404 CBotProfileReturnAfterFight::endProfile();
407 void CBotProfileReturnAfterFightNpc::updateProfile(uint ticksSinceLastUpdate)
409 H_AUTO(CBotProfileReturnAfterFightFaunaUpdate);
410 CBotProfileReturnAfterFight::updateProfile(ticksSinceLastUpdate);
411 _MoveProfile->updateProfile(ticksSinceLastUpdate);
414 std::string CBotProfileReturnAfterFightNpc::getOneLineInfoString() const
416 std::string info = "return_after_fight npc bot profile";
417 info += " (";
418 info += _MoveProfile?_MoveProfile->getOneLineInfoString():std::string("<no move profile>");
419 info += ")";
420 return info;
423 //////////////////////////////////////////////////////////////////////////////
424 // CGrpProfileFight //
425 //////////////////////////////////////////////////////////////////////////////
427 CGrpProfileFight::CGrpProfileFight(CProfileOwner *owner)
428 : CFightProfile(owner)
429 , CFightOrganizer()
430 , _WasRunning(false)
432 PROFILE_LOG("group", "fight", "ctor", "");
435 CGrpProfileFight::~CGrpProfileFight()
437 PROFILE_LOG("group", "fight", "dtor", "");
438 for (CCont<CBot >::iterator it=_Grp->bots().begin(), itEnd=_Grp->bots().end();it!=itEnd;++it)
440 CBot *bot=*it;
441 removeBot (bot);
445 void CGrpProfileFight::beginProfile()
447 PROFILE_LOG("group", "fight", "begin", "");
448 if (_Grp->getPersistent().getGrpDynBase() != /*(CDynGrpBase*)*/NULL
449 && !_Grp->getPersistent().getGrpDynBase()->getFamilyBehavior().isNULL())
451 // this is a dynamic bots groups, activate assist between groups
452 _CheckAround.set(CHECK_AROUND_PERIOD);
455 _HaveEnnemy=true;
456 for (CCont<CBot >::iterator it=_Grp->getPersistent().bots().begin(), itEnd=_Grp->getPersistent().bots().end();it!=itEnd;++it)
458 addBot (*it);
460 _WasRunning = _Grp->checkProfileParameter("running");
461 if (!_WasRunning)
462 _Grp->addProfileParameter("running", "", 0.f);
465 void CGrpProfileFight::endProfile()
467 PROFILE_LOG("group", "fight", "end", "");
468 if (!_WasRunning)
469 _Grp->removeProfileParameter("running");
472 void CGrpProfileFight::addBot(CBot* bot)
474 vector<CBot*>::iterator it = find(_NpcList.begin(), _NpcList.end(), bot);
475 if (it==_NpcList.end())
476 _NpcList.push_back(bot);
479 void CGrpProfileFight::removeBot(CBot* bot)
481 vector<CBot*>::iterator it = find(_NpcList.begin(), _NpcList.end(), bot);
482 if (it!=_NpcList.end())
483 _NpcList.erase(it);
486 void CGrpProfileFight::setFight(CSpawnBot* bot, CAIEntityPhysical* ennemy)
488 bot->setAIProfile(new CBotProfileFightNpc(bot, ennemy));
491 void CGrpProfileFight::setHeal(CSpawnBot* bot, CAIEntityPhysical* target)
493 bot->setAIProfile(new CBotProfileHealNpc(target, bot));
496 void CGrpProfileFight::setNoFight(CSpawnBot* bot)
498 if (!bot->getTarget().isNULL())
499 bot->setTarget(NULL);
500 if ( bot->getAIProfileType()==BOT_FLEE
501 || bot->getAIProfileType()==BOT_FIGHT
502 || bot->getAIProfileType()==BOT_HEAL
503 || bot->getAIProfileType()==BOT_RETURN_AFTER_FIGHT )
505 bot->setAIProfile(new CBotProfileStandAtPos(bot));
509 void CGrpProfileFight::setFlee(CSpawnBot* bot, CAIVector& fleeVect)
511 bot->setMoveDecalage(fleeVect);
512 if (bot->getAIProfileType()!=BOT_FLEE)
514 bot->setAIProfile(new CBotProfileFlee(bot));
518 void CGrpProfileFight::setReturnAfterFight(CSpawnBot* bot)
520 if (bot->getAIProfileType()!=BOT_RETURN_AFTER_FIGHT)
522 bot->setAIProfile(new CBotProfileReturnAfterFightNpc(NLMISC::safe_cast<CSpawnBotNpc*>(bot)));
526 bool CGrpProfileFight::stillHaveEnnemy() const
528 return _HaveEnnemy;
531 void CGrpProfileFight::updateProfile(uint ticksSinceLastUpdate)
533 H_AUTO(GrpFightProfileUpdate);
534 CFollowPathContext fpcGrpFightProfileUpdate("GrpFightProfileUpdate");
536 // check if some bots died or are despawned.
538 for(uint i = 0; i < _NpcList.size();)
540 CSpawnBot *spawnBot=_NpcList[i]->getSpawnObj();
541 if ( !spawnBot
542 || !spawnBot->isAlive())
544 _NpcList.erase(_NpcList.begin()+i);
545 continue;
547 i++;
549 reorganize(_NpcList.begin(), _NpcList.end());
551 // check groups around
552 if (_CheckAround.test())
554 _CheckAround.set(CHECK_AROUND_PERIOD);
556 FOREACH(itBot, vector<CBot*>, _NpcList)
558 CBot* pBot = *itBot;
559 if (pBot)
561 CSpawnBot* bot = pBot->getSpawnObj();
562 if (bot)
563 bot->propagateAggro();
569 std::string CGrpProfileFight::getOneLineInfoString() const
571 return "fight profile";
574 std::vector<CBot*>& CGrpProfileFight::npcList()
576 return _NpcList;
579 //////////////////////////////////////////////////////////////////////////////
580 // CGrpProfileNormal //
581 //////////////////////////////////////////////////////////////////////////////
583 void CGrpProfileNormal::beginProfile()
585 PROFILE_LOG("group", "normal", "begin", "");
586 _GroupFighting=false;
589 void CGrpProfileNormal::endProfile()
591 PROFILE_LOG("group", "normal", "end", "");
592 setGroupFighting(false);
595 void CGrpProfileNormal::updateProfile(uint ticksSinceLastUpdate)
597 H_AUTO(GrpNormalProfileUpdate);
598 CFollowPathContext fpcGrpNormalProfileUpdate("GrpNormalProfileUpdate");
600 if (_GroupFighting)
602 if (!_Grp->fightProfile().getAIProfile())
603 _Grp->fightProfile().setAIProfile(new CGrpProfileFight(_Grp));
605 _Grp->fightProfile().mayUpdateProfile(ticksSinceLastUpdate);
607 CFightProfile* profile = NLMISC::safe_cast<CFightProfile*>(_Grp->fightProfile().getAIProfile());
608 if (!profile->stillHaveEnnemy ())
610 // :TODO: Verify if it's needed to erase bots aggro too/instead
611 // _Grp->clearAggroList(); // this erase all agro.
613 setGroupFighting(false);
614 _Grp->fightProfile().setAIProfile(NULL);
616 CSlaveProfile* moveProfile = NLMISC::type_cast<CSlaveProfile*>(_Grp->movingProfile().getAIProfile());
617 if (moveProfile)
618 moveProfile->resumeProfile();
621 else
623 if (_Grp->haveAggroOrReturnPlace())
625 if(_Grp->isGroupAlive())
627 // set the fighting comportment.
628 if (!_Grp->fightProfile().getAIProfile())
629 // _Grp->fightProfile().setAIProfile(new CGrpProfileFight(_Grp));
630 _Grp->fightProfile().setAIProfile(_Grp.ptr(), &GrpProfileFightFactory, false);
632 setGroupFighting(true);
635 else
637 _Grp->movingProfile().mayUpdateProfile(ticksSinceLastUpdate);
642 std::string CGrpProfileNormal::getOneLineInfoString() const
644 std::string info = "normal profile";
645 info += " group_fighting=" + NLMISC::toString(_GroupFighting);
646 return info;
649 //////////////////////////////////////////////////////////////////////////////
650 // CGrpProfileBandit //
651 //////////////////////////////////////////////////////////////////////////////
653 CGrpProfileBandit::CGrpProfileBandit(CProfileOwner *owner)
654 : CGrpProfileNormal(owner)
656 PROFILE_LOG("group", "bandit", "ctor", "");
659 CGrpProfileBandit::~CGrpProfileBandit()
661 PROFILE_LOG("group", "bandit", "dtor", "");
664 void CGrpProfileBandit::beginProfile()
666 PROFILE_LOG("group", "bandit", "begin", "");
667 CGrpProfileNormal::beginProfile();
670 CGroupNpc &persGrp=_Grp->getPersistent();
672 if (persGrp.isRingGrp())
674 _AggroRange = persGrp.getAggroDist();
676 else
678 // look for aggro range parameter or set a default value
679 float aggroRangeFloat = 0.f;
681 if (!_Grp->getProfileParameter("aggro range", aggroRangeFloat))
682 _AggroRange =static_cast<uint32>( CGrpProfileBanditFactory::getDefaultBanditAggroRange() );
683 else
684 _AggroRange = static_cast<uint32>(aggroRangeFloat);
686 bool resendInfo = false;
689 if (!persGrp.getPlayerAttackable ())
691 persGrp.setPlayerAttackable (true);
692 resendInfo = true;
694 if (!persGrp.getBotAttackable ())
696 persGrp.setBotAttackable (true);
697 resendInfo = true;
699 if (resendInfo)
700 _Grp->sendInfoToEGS ();
706 void CGrpProfileBandit::endProfile()
708 PROFILE_LOG("group", "bandit", "end", "");
709 CGrpProfileNormal::endProfile();
712 void CGrpProfileBandit::updateProfile(uint ticksSinceLastUpdate)
714 H_AUTO(GrpBanditProfileUpdate);
715 CFollowPathContext fpcGrpBanditProfileUpdate("GrpBanditProfileUpdate");
717 CAIVision<CPersistentOfPhysical> BanditVision;
719 breakable
721 CAIVector centerPos;
722 if (!_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
723 break;
724 _Grp->setCenterPos(centerPos);
726 uint32 playerRadius=_AggroRange;
727 uint32 botRadius=_AggroRange;
728 uint32 groupPlayerRadius=playerRadius*2;
729 uint32 groupBotRadius=botRadius*2;
731 uint32 minRadius=playerRadius>botRadius?botRadius:playerRadius;
733 CFightProfile* fightProfile=static_cast<CFightProfile*>(_Grp->fightProfile().getAIProfile());
735 if (fightProfile)
737 CAIVision<CPersistentOfPhysical> localBanditVision;
739 for (vector<CBot*>::iterator it=fightProfile->npcList().begin(), itEnd=fightProfile->npcList().end();it!=itEnd;++it)
741 CBot *bot=(*it);
742 CSpawnBot *spawnBot=bot->getSpawnObj();
744 if (!spawnBot)
745 continue;
747 double distToCenter=centerPos.quickDistTo(spawnBot->pos());
748 if (distToCenter>minRadius) // (minRadius*2) - minRadius
750 const CAIVector spawnBotPos(spawnBot->pos());
751 // bot vision update.
752 localBanditVision.updateBotsAndPlayers(spawnBot->getAIInstance(), spawnBotPos, playerRadius, botRadius);
754 // bandits don't like guards nor escorted people
756 const std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> > &bots = localBanditVision.bots();
757 std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::const_iterator first(bots.begin()), last(bots.end());
758 for (; first != last; ++first)
760 CAIEntityPhysical *ep = (*first)->getSpawnObj();
761 if ( ep
762 && ep->getRyzomType()==RYZOMID::npc
763 && ep->isAlive())
765 CSpawnBotNpc *botNpc = NLMISC::safe_cast<CSpawnBotNpc *>(ep);
767 if ( botNpc->spawnGrp().activityProfile().getAIProfileType() == ACTIVITY_GUARD
768 || botNpc->spawnGrp().activityProfile().getAIProfileType() == ACTIVITY_GUARD_ESCORTED
769 || botNpc->spawnGrp().activityProfile().getAIProfileType() == ACTIVITY_ESCORTED )
771 spawnBot->setAggroMinimumFor(ep->dataSetRow(), 0.8f, false);
777 // bandits don't like players.
779 const std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> > &players = localBanditVision.players();
780 std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::const_iterator first(players.begin()), last(players.end());
781 for (; first != last; ++first)
783 CPersistentOfPhysical *player = (*first);
784 CAIEntityPhysical *ep = player->getSpawnObj();
785 if ( ep
786 && ep->isAlive()
787 && ep->currentHitPoints()>0.f)
789 const CRootCell *const rootCell=ep->wpos().getRootCell();
790 if ( rootCell
791 && rootCell->getFlag()!=0 ) // Safe Zone ?
792 continue;
794 spawnBot->setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
801 // group vision update.
802 BanditVision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), centerPos, playerRadius, botRadius);
805 CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
807 // check if we are in war and if some bot are waiting for a bus.
808 if (_GroupFighting)
810 // check if some bots are not fighting.
811 for (CCont<CBot >::iterator it=_Grp->getPersistent().bots().begin(), itEnd=_Grp->getPersistent().bots().end();it!=itEnd;++it)
813 CBot* bot=*it;
814 CSpawnBot *spawnBot=bot->getSpawnObj();
815 if ( spawnBot
816 && spawnBot->isAlive()
817 && spawnBot->getAIProfileType()==BOT_STAND_AT_POS)
819 // :KLUDGE: We verify here that we have a moving profile, to prevent some crashes
820 // :TODO: Remove that check and make sure a group always have a moving profile (even if none is defined in primitives)
821 if (_Grp->movingProfile().getAIProfile())
823 CMoveProfile *moveProf=NLMISC::safe_cast<CMoveProfile*>(_Grp->movingProfile().getAIProfile());
824 moveProf->resumeBot(bot);
830 // bandits don't like guards nor escorted people
832 const std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> > &bots = BanditVision.bots();
833 std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::const_iterator first(bots.begin()), last(bots.end());
834 for (; first != last; ++first)
836 CAIEntityPhysical *const ep = (*first)->getSpawnObj();
837 if ( !ep
838 || ep->getRyzomType()!=RYZOMID::npc
839 || !ep->isAlive())
840 continue;
842 const CSpawnBot *const bot = NLMISC::safe_cast<const CSpawnBot *>(ep);
844 const TProfiles profileType=bot->spawnGrp().activityProfile().getAIProfileType();
845 if ( profileType != ACTIVITY_GUARD
846 && profileType != ACTIVITY_GUARD_ESCORTED
847 && profileType != ACTIVITY_ESCORTED )
848 continue;
850 _Grp->setAggroMinimumFor(ep->dataSetRow(), 0.8f, false);
854 // bandits don't like players.
856 const std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> > &players = BanditVision.players();
858 std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::const_iterator first(players.begin()), last(players.end());
859 for (; first != last; ++first)
861 CPersistentOfPhysical*const player = (*first);
862 CAIEntityPhysical*const ep = player->getSpawnObj();
864 if ( ep
865 && ep->isAlive()
866 && ep->currentHitPoints()>0.f) // not in safe zone.
868 const CRootCell *const rootCell=ep->wpos().getRootCell();
869 if ( rootCell
870 && rootCell->getFlag()!=0 ) // Safe Zone ?
871 continue;
873 _Grp->setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
879 std::string CGrpProfileBandit::getOneLineInfoString() const
881 std::string info = "bandit profile";
882 info += " aggro_range=" + NLMISC::toString(_AggroRange);
883 return info;
886 //////////////////////////////////////////////////////////////////////////////
887 // CGrpProfileGuard //
888 //////////////////////////////////////////////////////////////////////////////
890 CGrpProfileGuard::CGrpProfileGuard(CProfileOwner *owner)
891 : CGrpProfileNormal(owner)
893 PROFILE_LOG("group", "guard", "ctor", "");
896 CGrpProfileGuard::~CGrpProfileGuard()
898 PROFILE_LOG("group", "guard", "dtor", "");
901 void CGrpProfileGuard::beginProfile()
903 PROFILE_LOG("group", "guard", "begin", "");
904 CGrpProfileNormal::beginProfile();
905 CGroupNpc &persGrp=_Grp->getPersistent();
907 if (persGrp.isRingGrp())
909 _AggroRange = persGrp.getAggroDist();
911 else
913 _AggroRange = 0;
917 void CGrpProfileGuard::endProfile()
919 PROFILE_LOG("group", "guard", "end", "");
920 CGrpProfileNormal::endProfile();
923 void CGrpProfileGuard::updateProfile(uint ticksSinceLastUpdate)
925 H_AUTO(GrpGuardProfileUpdate);
926 CFollowPathContext fpcGrpGuardProfileUpdate("GrpGuardProfileUpdate");
928 CAIVision<CPersistentOfPhysical> GuardVision;
929 uint32 aggroSize;
931 float f = 0.f;
932 if (_Grp->getPersistent().getProfileParameter("aggro_range", f)) // look for persistent aggro range
933 aggroSize = (uint32)f;
934 else
935 aggroSize = _Grp->getPersistent()._AggroRange;
937 if (aggroSize == 0)
938 aggroSize = 25;
940 TTicks startVisionTime = CTime::getPerformanceTime();
943 H_AUTO(GrpGuardProfileVision);
944 CAIVector centerPos;
945 if (_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
947 _Grp->setCenterPos(centerPos);
948 if (!_GroupFighting)
949 _CenterPos=_Grp->getCenterPos();
950 GuardVision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), _CenterPos, aggroSize, aggroSize);
954 TTicks endVisionTime = CTime::getPerformanceTime();
956 static uint32 s_maxBotsVisible = 0;
957 static double s_maxBotsVisionTime = 0.0;
959 uint32 numBotsVisible = (uint32)GuardVision.bots().size();
960 double deltaVisionTime = CTime::ticksToSecond(endVisionTime-startVisionTime);
961 bool bTellUs = false;
962 if( s_maxBotsVisible < numBotsVisible )
964 s_maxBotsVisible = numBotsVisible;
965 bTellUs = true;
968 if( s_maxBotsVisionTime < deltaVisionTime )
970 s_maxBotsVisionTime = deltaVisionTime;
971 bTellUs = true;
974 if( bTellUs )
976 nldebug( "==> max bots visible is now %u", s_maxBotsVisible );
977 nldebug( "vision time: %.2f", (float)(deltaVisionTime * 1000.0) );
980 uint32 factionIndex=CStaticFames::INVALID_FACTION_INDEX;
981 sint32 fameForGuardAttack = FameForGuardAttack;
983 H_AUTO(GrpGuardProfileFaction);
984 CAliasCont<CBot> &bots = _Grp->getPersistent().bots();
985 if (!bots.size() != 0 && bots.begin() != bots.end())
987 CBot* bot = *(bots.begin());
988 if (bot != NULL)
990 factionIndex=bot->getSheet()->FactionIndex();
991 if (bot->getSheet()->FameForGuardAttack() != AISHEETS::ICreature::InvalidFameForGuardAttack)
992 fameForGuardAttack = bot->getSheet()->FameForGuardAttack();
994 /* if (factionIndex == CStaticFames::INVALID_FACTION_INDEX)
996 nlwarning("Bot sheet '%s' have invalid faction index (guard profile)", bot->getSheet()->SheetId().toString().c_str());
1003 string s;
1004 f = 0.f;
1005 if (_Grp->getProfileParameter("faction", s) && !s.empty())
1007 factionIndex = CStaticFames::getInstance().getFactionIndex(s);
1010 if (_Grp->getPersistent().getProfileParameter("fame_for_guard_attack", f))
1012 fameForGuardAttack = (sint32)f;
1014 else if (_Grp->getProfileParameter("fame_for_guard_attack", f))
1016 fameForGuardAttack = (sint32)f;
1021 CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
1023 // check if we are in war and if some bot are waiting for a bus.
1024 if (_GroupFighting)
1026 H_AUTO(GrpGuardProfileFighting);
1027 const CAIVector centerPos(_Grp->getCenterPos());
1029 // check if some bots are not fighting.
1030 for (CCont<CBot >::iterator it=_Grp->getPersistent().bots().begin(), itEnd=_Grp->getPersistent().bots().end();it!=itEnd;++it)
1032 const CBot*const bot=*it;
1033 CSpawnBot *const spawnBot=bot->getSpawnObj();
1034 if ( spawnBot
1035 && spawnBot->isAlive())
1037 switch(spawnBot->getAIProfileType())
1039 case BOT_STAND_AT_POS:
1041 CMoveProfile*const moveProf=NLMISC::type_cast<CMoveProfile*>(_Grp->movingProfile().getAIProfile());
1042 if (moveProf)
1043 moveProf->resumeBot(bot);
1045 break;
1046 case BOT_FIGHT:
1048 // This system is now managed by CBotAggroOwner itself
1050 const CAIEntityPhysical*const target=spawnBot->getTarget();
1051 if (target)
1053 // if target is out of range, then forget the aggro.
1054 if (centerPos.quickDistTo(target->pos())>50)
1056 spawnBot->forgetAggroFor(target->dataSetRow());
1057 _Grp->forgetAggroFor(target->dataSetRow());
1062 break;
1063 default:
1064 break;
1070 // guards don't like bandits.
1072 H_AUTO(GrpGuardProfileBandits);
1073 const std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> > &bots = GuardVision.bots();
1074 std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::const_iterator first(bots.begin()), last(bots.end());
1075 for (; first != last; ++first)
1077 const CAIEntityPhysical *const ep = (*first)->getSpawnObj();
1078 if (ep->isAlive())
1080 switch (ep->getRyzomType())
1082 case RYZOMID::npc:
1084 const CSpawnBotNpc *const botNpc = NLMISC::safe_cast<const CSpawnBotNpc*>(ep);
1086 if (botNpc->spawnGrp().activityProfile().getAIProfileType() == ACTIVITY_BANDIT)
1088 if (_CenterPos.quickDistTo(botNpc->pos())<aggroSize)
1090 _Grp->setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
1094 break;
1095 case RYZOMID::creature:
1097 const CSpawnBotFauna *const botFauna = NLMISC::safe_cast<const CSpawnBotFauna*>(ep);
1099 if (botFauna->getPersistent().faunaType()==FaunaTypePredator)
1101 if (_CenterPos.quickDistTo(botFauna->pos())<aggroSize)
1103 _Grp->setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
1107 break;
1108 default:
1109 break;
1115 // guards don't like bots that attack players.
1117 H_AUTO(GrpGuardProfileAttack);
1118 const std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> > &players = GuardVision.players();
1119 LOG("%u players in group vision with aggrosize %d", players.size(), aggroSize);
1121 const CAIVector &centerPos=_Grp->getCenterPos();
1123 std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::const_iterator first(players.begin()), last(players.end());
1124 for (; first != last; ++first)
1126 const CPersistentOfPhysical *const player = (*first);
1127 CAIEntityPhysical *const ep = player->getSpawnObj();
1128 if ( !ep
1129 || !ep->isAlive()
1130 || ep->currentHitPoints()<=0.f
1131 || ep->wpos().toAIVector().quickDistTo(centerPos)>aggroSize)
1132 continue;
1134 // check Fame before choosing what to do ..
1135 sint32 const fame = ep->getFameIndexed(factionIndex);
1137 // if player is kos attack him (only if bot is attackable by player)
1138 if ((_Grp->getPersistent().getPlayerAttackable() || (factionIndex!=CStaticFames::INVALID_FACTION_INDEX && _Grp->getPersistent().isFactionAttackable(CStaticFames::getInstance().getFactionName(factionIndex), fame))) && ((factionIndex!=CStaticFames::INVALID_FACTION_INDEX && fame<fameForGuardAttack) || OUTPOSTHELPERS::isAttackingFaction(factionIndex, ep)))
1140 // the guard attack the player !
1141 _Grp->setAggroMinimumFor(ep->dataSetRow(), 1.f, false);
1142 continue;
1145 // check if player is attacked and assist him).
1146 CAIEntityPhysical const* phys = ep->firstTargeter();
1147 while (phys)
1149 switch(phys->getRyzomType())
1151 case RYZOMID::player:
1152 break;
1153 case RYZOMID::npc:
1155 const CSpawnBotNpc *const botNpc=dynamic_cast<const CSpawnBotNpc*>(phys);
1156 if ( botNpc
1157 && botNpc->getPersistent().getGroup().getSpawnObj()->activityProfile().getAIProfileType() == ACTIVITY_GUARD)
1159 break;
1162 // guard don't attack npc of the same faction, rather, they attack the player !
1163 if (factionIndex != CStaticFames::INVALID_FACTION_INDEX && botNpc->getPersistent().getSheet()->FactionIndex() == factionIndex)
1165 // the guard attack the player !
1166 _Grp->setAggroMinimumFor(ep->dataSetRow(), 1.f, false);
1167 break;
1170 default:
1171 // guard defend only player with a not too bad fame
1172 if (fame >= FameForGuardHelp && !OUTPOSTHELPERS::isAttackingFaction(factionIndex, ep))
1173 _Grp->setAggroMinimumFor(phys->dataSetRow(), 1.f, false);
1174 break;
1176 phys = phys->nextTargeter();
1182 std::string CGrpProfileGuard::getOneLineInfoString() const
1184 return "guard profile";
1187 //////////////////////////////////////////////////////////////////////////////
1188 // CGrpProfileTribu //
1189 //////////////////////////////////////////////////////////////////////////////
1191 CGrpProfileTribu::CGrpProfileTribu(CProfileOwner *owner)
1192 : CGrpProfileNormal(owner)
1194 PROFILE_LOG("group", "tribe", "ctor", "");
1197 CGrpProfileTribu::~CGrpProfileTribu()
1199 PROFILE_LOG("group", "tribe", "dtor", "");
1202 void CGrpProfileTribu::beginProfile()
1204 PROFILE_LOG("group", "tribe", "begin", "");
1205 CGrpProfileNormal::beginProfile();
1208 void CGrpProfileTribu::endProfile()
1210 PROFILE_LOG("group", "tribe", "end", "");
1211 CGrpProfileNormal::endProfile();
1214 void CGrpProfileTribu::updateProfile(uint ticksSinceLastUpdate)
1216 H_AUTO(GrpTribuProfileUpdate);
1217 CFollowPathContext fpcGrpTribuProfileUpdate("GrpTribuProfileUpdate");
1219 CAIVision<CPersistentOfPhysical> TribuVision;
1220 const uint32 aggroSize=40;
1222 breakable
1224 CAIVector centerPos;
1225 if (!_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
1226 break;
1228 _Grp->setCenterPos(centerPos);
1229 if (!_GroupFighting)
1230 _CenterPos=_Grp->getCenterPos();
1231 TribuVision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), _CenterPos, aggroSize, aggroSize);
1234 uint32 factionIndex=CStaticFames::INVALID_FACTION_INDEX;
1236 CBotNpc* bot = static_cast<CBotNpc*>(*_Grp->getPersistent().bots().begin());
1237 factionIndex = bot->getSheet()->FactionIndex();
1240 CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
1242 // check if we are in war and if some bot are waiting for a bus.
1243 if (_GroupFighting)
1245 CAIVector centerPos(_Grp->getCenterPos());
1247 // check if some bots are not fighting.
1248 for (CCont<CBot >::iterator it=_Grp->getPersistent().bots().begin(), itEnd=_Grp->getPersistent().bots().end();it!=itEnd;++it)
1250 const CBot*const bot=*it;
1251 CSpawnBot *const spawnBot=bot->getSpawnObj();
1252 if ( !spawnBot
1253 || !spawnBot->isAlive())
1254 continue;
1256 switch(spawnBot->getAIProfileType())
1258 case BOT_STAND_AT_POS:
1260 CMoveProfile *moveProf=NLMISC::safe_cast<CMoveProfile*>(_Grp->movingProfile().getAIProfile());
1261 moveProf->resumeBot(bot);
1263 break;
1264 case BOT_FIGHT:
1266 // This system is managed by CBotAggroOwner now
1268 const CAIEntityPhysical *const target=spawnBot->getTarget();
1269 if (!target)
1270 break;
1272 // if target is out of range, then forget the aggro.
1273 if (centerPos.quickDistTo(target->pos())<=50)
1274 break;
1276 spawnBot->forgetAggroFor(target->dataSetRow());
1277 _Grp->forgetAggroFor(target->dataSetRow());
1280 break;
1281 default:
1282 break;
1287 // Tribus don't like players with bad fame.
1289 const std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> > &players = TribuVision.players();
1291 LOG("%u players in group vision", players.size());
1293 std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::const_iterator first(players.begin()), last(players.end());
1294 for (; first != last; ++first)
1296 CPersistentOfPhysical *const player = (*first);
1297 CAIEntityPhysical *const ep = player->getSpawnObj();
1298 if ( !ep
1299 || !ep->isAlive()
1300 || ep->currentHitPoints()<=0.f)
1301 continue;
1303 const CRootCell *const rootCell=ep->wpos().getRootCell();
1304 if ( rootCell
1305 && rootCell->getFlag()!=0 ) // Safe Zone ?
1306 continue;
1308 // check Fame before choosing what to do ..
1309 sint32 const fame = ep->getFameIndexed(factionIndex);
1311 // check if player is attacked.
1312 if (fame<-10000 || OUTPOSTHELPERS::isAttackingFaction(factionIndex, ep))
1314 _Grp->setAggroMinimumFor(ep->dataSetRow(), 1.f, false);
1320 std::string CGrpProfileTribu::getOneLineInfoString() const
1322 return "tribu profile";
1325 //////////////////////////////////////////////////////////////////////////////
1326 // CGrpProfileFollowRoute //
1327 //////////////////////////////////////////////////////////////////////////////
1329 CGrpProfileGoToPoint::CGrpProfileGoToPoint(CProfileOwner *owner, RYAI_MAP_CRUNCH::CWorldPosition const& startPos, RYAI_MAP_CRUNCH::CWorldPosition const& endPos, bool dontSendEvent)
1330 : CMoveProfile(owner)
1331 , _StartPos(startPos)
1332 , _EndPos(endPos)
1333 , _PathCont(NLMISC::safe_cast<CSpawnGroup*>(owner)->getPersistent().getAStarFlag())
1334 , _DontSendEvent(dontSendEvent)
1336 PROFILE_LOG("group", "go_to_point", "ctor", "");
1337 _GlobalOrient.setX(1);
1338 _GlobalOrient.setY(0);
1339 _FollowForward=true;
1340 _ValidPosInit=false;
1341 _StopNpc = false;
1344 void CGrpProfileGoToPoint::setDirection(bool forward)
1346 if ( _FollowForward==forward
1347 && _ValidPosInit )
1348 return;
1350 _ValidPosInit=true;
1351 _FollowForward=forward;
1354 void CGrpProfileGoToPoint::beginProfile()
1356 PROFILE_LOG("group", "go_to_point", "begin", "");
1357 _ProfileTerminated = false;
1358 CMoveProfile::beginProfile();
1360 setCurrentDestination(_EndPos); // *
1363 void CGrpProfileGoToPoint::stateChangeProfile()
1365 setCurrentDestination(_EndPos); // *
1367 // set a stand at pos profile on every bots
1368 FOREACH(it, CAliasCont<CBot>, _Grp->bots())
1370 CSpawnBot *sb = (*it)->getSpawnObj();
1371 if (sb)
1372 sb->setAIProfile(new CBotProfileStandAtPos(sb));
1375 resumeProfile();
1378 void CGrpProfileGoToPoint::endProfile()
1380 PROFILE_LOG("group", "go_to_point", "end", "");
1383 void CGrpProfileGoToPoint::resumeProfile()
1385 PROFILE_LOG("group", "go_to_point", "resume", "");
1386 FOREACH(it, TBotFollowerMap, _NpcList)
1388 const CBot *const bot=it->first;
1389 CSpawnBot *const sbot=bot->getSpawnObj();
1390 if (!sbot)
1391 continue;
1393 switch (sbot->getAIProfileType())
1395 case BOT_FOLLOW_POS:
1396 break;
1397 default: // push the correct comportment.
1398 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
1399 break;
1403 setCurrentDestination(_EndPos);
1406 void CGrpProfileGoToPoint::addBot(CBot* bot)
1408 _NpcList[bot] = CBotFollower();
1410 CSpawnBot *sbot=bot->getSpawnObj();
1411 if (sbot)
1412 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
1414 _MustCalcRatios = true;
1417 void CGrpProfileGoToPoint::removeBot(CBot* bot)
1419 TBotFollowerMap::iterator it=_NpcList.find(bot);
1421 if (it==_NpcList.end())
1422 return;
1424 CSpawnBotNpc *const spawnBot=NLMISC::safe_cast<CBotNpc*>(bot)->getSpawn();
1425 if (spawnBot)
1426 spawnBot->setAIProfile(BotProfileStandAtPosFactory.createAIProfile(spawnBot));
1428 _NpcList.erase (it);
1429 _MustCalcRatios = true;
1432 void CGrpProfileGoToPoint::setCurrentDestination(RYAI_MAP_CRUNCH::CWorldPosition const& dest)
1434 _PathCont.setDestination(dest);
1436 FOREACH(it, TBotFollowerMap, _NpcList)
1437 it->second.setBotAtDest(false);
1440 void CGrpProfileGoToPoint::calcRatios()
1442 _MustCalcRatios = false;
1444 // loop to compute max speeds
1445 _MaxRunSpeed = FLT_MAX;
1446 _MaxWalkSpeed = FLT_MAX;
1447 FOREACH(it, TBotFollowerMap, _NpcList)
1449 CBot *bot = it->first; //static_cast<CBotNpc*>(_Grp->bots()[botFollower.getIndex()]);
1450 CSpawnBot *sbot = bot->getSpawnObj();
1451 if ( !sbot
1452 || !sbot->isAlive())
1453 continue;
1455 _MaxRunSpeed = std::min(sbot->runSpeed(), _MaxRunSpeed);
1456 _MaxWalkSpeed = std::min(sbot->walkSpeed(), _MaxWalkSpeed);
1460 if (_Shape!=SHAPE_RECTANGLE)
1461 return;
1463 const uint32 nbbots=(uint32)_NpcList.size();
1465 _NbRange = (uint32) sqrt(_Ratio*nbbots);
1466 if (_NbRange==0)
1467 _NbRange=1;
1468 _NbLines = nbbots/_NbRange;
1469 _NbBotInNormalShape = _NbLines*_NbRange;
1470 _Rest = nbbots-_NbBotInNormalShape;
1472 _Cx=(double(_NbRange)-1.0)*0.5;
1473 _Cy=(double(_NbLines)-1.0)*0.5;
1474 _Cy=(_Cy*_NbBotInNormalShape+double(_NbLines)*_Rest)/double(nbbots);
1477 void CGrpProfileGoToPoint::updateProfile(uint ticksSinceLastUpdate)
1479 H_AUTO(CGrpGoToPointProfileUpdate);
1480 CFollowPathContext fpcCGrpGoToPointProfileUpdate("CGrpGoToPointProfileUpdate");
1482 CMoveProfile::updateProfile(ticksSinceLastUpdate);
1484 CSpawnGroupNpc *NpcGrp=NLMISC::safe_cast<CSpawnGroupNpc*>(_Grp.ptr());
1485 CGroupNpc &persGrp=NpcGrp->getPersistent();
1487 CAIVector groupPosition = NpcGrp->getCenterPos();
1488 CAIVector perpGlobalOrient;
1489 NpcGrp->calcCenterPos(groupPosition);
1491 uint32 nbAtDest=0;
1492 uint32 nbNewAtDest=0;
1494 uint32 botIndex=0;
1495 uint32 xIndex=0;
1496 uint32 yIndex=0;
1498 double dx=0;
1499 double dy=0;
1502 if (_Shape==SHAPE_RECTANGLE)
1504 perpGlobalOrient.setX(-_GlobalOrient.y());
1505 perpGlobalOrient.setY(_GlobalOrient.x());
1508 //////////////////////////////////////////////////////////////////////////
1509 // Calcs the correct gravity grid position (must be done only when bot are removed or add to the group.
1510 if (_MustCalcRatios)
1511 calcRatios ();
1514 FOREACH(it, TBotFollowerMap, _NpcList)
1516 CBotFollower &botFollower=it->second;
1517 if (botFollower.isBotAtDest())
1519 nbAtDest++;
1520 continue;
1523 CBot *bot=it->first;
1524 CSpawnBot *sbot=bot->getSpawnObj();
1525 if ( !sbot
1526 || !sbot->isAlive())
1527 continue;
1529 // verify if the bot has a correct profile and if he reached the destination position.
1530 switch (sbot->getAIProfileType())
1532 case BOT_FOLLOW_POS:
1534 if (_ProfileTerminated)
1536 // remove the profile
1537 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
1539 else
1541 CBotProfileFollowPos* prof = NLMISC::safe_cast<CBotProfileFollowPos*>(sbot->getAIProfile());
1543 // flag the sub profile to stop the npc
1544 prof->setStop(_StopNpc);
1545 if (!_StopNpc)
1547 if (prof->_Status==CFollowPath::FOLLOW_ARRIVED)
1549 botFollower.setBotAtDest();
1550 nbNewAtDest++;
1551 nbAtDest++;
1553 else
1555 // update speeds
1556 prof->setMaxSpeeds(_MaxWalkSpeed, _MaxRunSpeed);
1561 break;
1562 default: // push the correct comportment.
1564 if (!_ProfileTerminated)
1566 sbot->setAIProfile(new CBotProfileFollowPos(&_PathCont, sbot));
1567 CBotProfileFollowPos* prof = NLMISC::safe_cast<CBotProfileFollowPos*>(sbot->getAIProfile());
1568 // update speeds
1569 prof->setMaxSpeeds(_MaxWalkSpeed, _MaxRunSpeed);
1572 break;
1575 if (_Shape==SHAPE_RECTANGLE)
1577 NLMISC::CVector2d dir=sbot->theta().asVector2d();
1578 dx+=dir.x;
1579 dy+=dir.y;
1581 // 4 rows
1582 CAIVector idealPos=groupPosition;
1583 if (botIndex>=_NbBotInNormalShape)
1585 idealPos += perpGlobalOrient * (_XSize*(_Cx-double(xIndex)-(_NbRange-_Rest)*0.5));
1587 else
1589 idealPos += perpGlobalOrient * (_XSize*(_Cx-double(xIndex)));
1592 idealPos+=_GlobalOrient*(_YSize*(_Cy-(double)yIndex));
1593 idealPos-=CAIVector(sbot->pos());
1595 botIndex++;
1596 xIndex++;
1597 if (xIndex>=_NbRange)
1599 xIndex=0;
1600 yIndex++;
1602 sbot->setMoveDecalage(idealPos);
1606 if (_Shape==SHAPE_RECTANGLE)
1608 _GlobalOrient.setX(dx/botIndex);
1609 _GlobalOrient.setY(dy/botIndex);
1612 // first to arrived ?
1613 if (nbAtDest>0 && !_ProfileTerminated)
1615 if (!_DontSendEvent)
1617 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
1618 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
1620 _ProfileTerminated = true;
1624 CGrpProfileGoToPoint::~CGrpProfileGoToPoint()
1626 for (CCont<CBot >::iterator it=_Grp->bots().begin(), itEnd=_Grp->bots().end();it!=itEnd;++it)
1627 removeBot(*it);
1630 CGrpProfileGoToPoint::CBotFollower::CBotFollower()
1631 : _BotAtDest(false)
1635 CGrpProfileGoToPoint::CBotFollower::~CBotFollower()
1639 void CGrpProfileGoToPoint::CBotFollower::setBotAtDest(bool atDest)
1641 _BotAtDest = atDest;
1644 const bool& CGrpProfileGoToPoint::CBotFollower::isBotAtDest() const
1646 return _BotAtDest;
1649 bool CGrpProfileGoToPoint::getDirection()
1651 return _FollowForward;
1655 CPathCont* CGrpProfileGoToPoint::getPathCont(CBot const* bot)
1657 return &_PathCont;
1660 std::string CGrpProfileGoToPoint::getOneLineInfoString() const
1662 std::string info = "go_to_point profile";
1663 info += " stop_npc=" + NLMISC::toString(_StopNpc);
1664 return info;
1667 bool CGrpProfileGoToPoint::profileTerminated() const
1669 return _ProfileTerminated;
1672 void CGrpProfileGoToPoint::stopNpc(bool stop)
1674 _StopNpc = stop;
1677 //////////////////////////////////////////////////////////////////////////////
1678 // CGrpProfileGoToPoint //
1679 //////////////////////////////////////////////////////////////////////////////
1681 //RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileGoToPoint, "goto_point");
1683 //////////////////////////////////////////////////////////////////////////////
1684 // CGrpProfileFollowRoute //
1685 //////////////////////////////////////////////////////////////////////////////
1687 CGrpProfileFollowRoute::CGrpProfileFollowRoute(CProfileOwner *owner)
1688 : CMoveProfile(owner)
1689 , _PathCont(NLMISC::safe_cast<CSpawnGroup*>(owner)->getPersistent().getAStarFlag())
1690 , _DontSendEvent(false)
1692 PROFILE_LOG("group", "follow_route", "ctor", "");
1693 _GlobalOrient.setX(1);
1694 _GlobalOrient.setY(0);
1695 _FollowForward = true;
1696 _StopNpc = false;
1698 CGroupNpc &grp=*safe_cast<CGroupNpc*>(&_Grp->getPersistent());
1699 if (grp.getState())
1701 const CAIState *const state = grp.getCAIState();
1702 if (state->isPositional())
1704 const CAIStatePositional *const statePositionnal=static_cast<const CAIStatePositional*>(state);
1705 _Geometry=&statePositionnal->shape().getGeometry();
1706 _GeometryComeFromState = true;
1707 _VerticalPos=statePositionnal->shape().getVerticalPos();
1708 return;
1711 _GeometryComeFromState = false;
1712 _Geometry = NULL;
1713 _VerticalPos = TVerticalPos();
1714 _GeomIndex = 0;
1715 #ifdef NL_DEBUG
1716 nlassert(true==false); // Cannot use this constructor outside machine state context.
1717 #endif
1721 CGrpProfileFollowRoute::CGrpProfileFollowRoute(CProfileOwner *owner,const std::vector<CShape::TPosition> &geometry,const TVerticalPos &verticalPos, bool dontSendEvent)
1722 : CMoveProfile(owner)
1723 , _PathCont(NLMISC::safe_cast<CSpawnGroup*>(owner)->getPersistent().getAStarFlag())
1724 , _GeometryComeFromState(false)
1725 , _Geometry(&geometry)
1726 , _VerticalPos(verticalPos)
1727 , _DontSendEvent(dontSendEvent)
1729 PROFILE_LOG("group", "follow_route", "ctor2", "");
1730 _GlobalOrient.setX(1);
1731 _GlobalOrient.setY(0);
1732 _FollowForward=true;
1733 _ValidPosInit=false;
1734 _GeomIndex=0;
1735 _StopNpc = false;
1738 void CGrpProfileFollowRoute::setDirection(bool forward)
1740 if ( _FollowForward==forward
1741 && _ValidPosInit )
1742 return;
1744 _ValidPosInit=true;
1745 _FollowForward=forward;
1746 #ifdef NL_DEBUG
1747 nlassert(_Geometry);
1748 #endif
1749 setCurrentValidPos(_VerticalPos);
1752 void CGrpProfileFollowRoute::beginProfile()
1754 PROFILE_LOG("group", "follow_route", "begin", "");
1755 _ProfileTerminated = false;
1756 CMoveProfile::beginProfile();
1758 if (_GeometryComeFromState)
1759 assignGeometryFromState();
1762 void CGrpProfileFollowRoute::assignGeometryFromState()
1764 _ProfileTerminated = false;
1766 // default value initialization.
1767 std::string shape;
1768 _Shape = SHAPE_NOTHING;
1769 _XSize = 1;
1770 _YSize = 1;
1771 _Ratio = 1;
1773 if (_Grp->getProfileParameter("shape", shape)
1774 && shape=="rectangle")
1776 _Shape = SHAPE_RECTANGLE;
1779 _Grp->getProfileParameter("ratio", _Ratio);
1780 _Grp->getProfileParameter("xsize", _XSize);
1781 _Grp->getProfileParameter("ysize", _YSize);
1783 _GeomIndex=0;
1785 CGroup &persGrp=NLMISC::safe_cast<CSpawnGroup*>(_Grp.ptr())->getPersistent();
1786 // R2_PRIMITIVE_LAXITY
1787 if (IsRingShard.get())
1789 nlassertex(_Geometry, ("CGrpProfileFollowRoute : NULL geometry data for group '%s'", persGrp.getFullName().c_str()));
1790 if (_Geometry->empty())
1792 nlwarning("CGrpProfileFollowRoute : missing geometry data for group '%s'", persGrp.getFullName().c_str());
1795 else
1797 nlassertex(_Geometry && _Geometry->size()>0, ("CGrpProfileFollowRoute : missing geometry data for group '%s'", persGrp.getFullName().c_str()));
1801 CSpawnGroupNpc *grp = static_cast<CSpawnGroupNpc*>(static_cast<CSpawnGroup *>(_Grp));
1802 CGroupNpc &pgrp = grp->getPersistent();
1803 const CAIState *const state = pgrp.getActiveState();
1805 if ( !state
1806 || !state->isPositional())
1807 return;
1809 const CAIStatePositional *const sp = static_cast<const CAIStatePositional *const>(state);
1810 if (!sp->shape().hasPoints() && (_GeometryComeFromState))
1812 nlwarning("Error, no position in state '%s'%s",
1813 sp->getAliasFullName().c_str(),
1814 sp->getAliasString().c_str());
1816 else
1818 _Geometry = &(sp->shape().getGeometry());
1819 _GeometryComeFromState = true;
1820 _MustCalcRatios = true;
1823 setCurrentValidPos (_VerticalPos); // static_cast<CSpawnGroupNpc*>(_Grp.ptr())->getPersistent().getGeometryVerticalPos(), geometry);
1826 void CGrpProfileFollowRoute::stateChangeProfile()
1828 assignGeometryFromState();
1830 // set a stand at pos profile on every bots
1831 FOREACH(it, CAliasCont<CBot>, _Grp->bots())
1833 CSpawnBot *sb = (*it)->getSpawnObj();
1834 if (sb)
1835 sb->setAIProfile(new CBotProfileStandAtPos(sb));
1837 // Reset stop flag to false
1838 stopNpc(false);
1840 resumeProfile();
1843 void CGrpProfileFollowRoute::endProfile()
1845 PROFILE_LOG("group", "follow_route", "end", "");
1848 void CGrpProfileFollowRoute::resumeProfile()
1850 PROFILE_LOG("group", "follow_route", "resume", "");
1851 FOREACH(it, TBotFollowerMap, _NpcList)
1853 const CBot *const bot=it->first;
1854 CSpawnBot *const sbot=bot->getSpawnObj();
1855 if (!sbot)
1856 continue;
1858 switch (sbot->getAIProfileType())
1860 case BOT_FOLLOW_POS:
1861 break;
1862 default: // push the correct comportment.
1863 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
1864 break;
1868 // setCurrentValidPos (NLMISC::safe_cast<CAIStatePositional *>(NLMISC::safe_cast<CSpawnGroupNpc*>(_Grp)->getPersistent().getCAIState()));
1869 #ifdef NL_DEBUG
1870 nlassert(_Geometry);
1871 #endif
1872 setCurrentValidPos (_VerticalPos);
1875 void CGrpProfileFollowRoute::addBot (CBot *bot)
1877 _NpcList[bot]=CBotFollower();
1879 CSpawnBot *sbot=bot->getSpawnObj();
1880 if (sbot)
1881 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
1883 _MustCalcRatios = true;
1886 void CGrpProfileFollowRoute::removeBot (CBot *bot)
1888 TBotFollowerMap::iterator it=_NpcList.find(bot);
1890 if (it==_NpcList.end())
1891 return;
1893 CSpawnBotNpc *const spawnBot=NLMISC::safe_cast<CBotNpc*>(bot)->getSpawn();
1894 if (spawnBot)
1895 spawnBot->setAIProfile(BotProfileStandAtPosFactory.createAIProfile(spawnBot));
1897 _NpcList.erase (it);
1898 _MustCalcRatios = true;
1901 void CGrpProfileFollowRoute::setCurrentValidPos (TVerticalPos verticalPos)
1903 if (_Geometry->size()>0)
1905 #if !FINAL_VERSION
1906 nlassert(_Geometry!=NULL);
1907 #endif
1908 size_t index = getDirection()?_GeomIndex:(_Geometry->size()-_GeomIndex-1);
1909 #if !FINAL_VERSION
1910 nlassert(index < _Geometry->size());
1911 #endif
1912 _PathCont.setDestination (verticalPos, (*_Geometry)[index]);
1915 FOREACH(it, TBotFollowerMap, _NpcList)
1916 it->second.setBotAtDest(false);
1919 void CGrpProfileFollowRoute::calcRatios ()
1921 _MustCalcRatios = false;
1923 // loop to compute max speeds
1924 _MaxRunSpeed = FLT_MAX;
1925 _MaxWalkSpeed = FLT_MAX;
1926 FOREACH(it, TBotFollowerMap, _NpcList)
1928 CBot *bot = it->first; //static_cast<CBotNpc*>(_Grp->bots()[botFollower.getIndex()]);
1929 CSpawnBot *sbot = bot->getSpawnObj();
1930 if ( !sbot
1931 || !sbot->isAlive())
1932 continue;
1934 _MaxRunSpeed = std::min(sbot->runSpeed(), _MaxRunSpeed);
1935 _MaxWalkSpeed = std::min(sbot->walkSpeed(), _MaxWalkSpeed);
1939 if (_Shape!=SHAPE_RECTANGLE)
1940 return;
1942 const uint32 nbbots=(uint32)_NpcList.size();
1944 _NbRange = (uint32) sqrt(_Ratio*nbbots);
1945 if (_NbRange==0)
1946 _NbRange=1;
1947 _NbLines = nbbots/_NbRange;
1948 _NbBotInNormalShape = _NbLines*_NbRange;
1949 _Rest = nbbots-_NbBotInNormalShape;
1951 _Cx=(double(_NbRange)-1.0)*0.5;
1952 _Cy=(double(_NbLines)-1.0)*0.5;
1953 _Cy=(_Cy*_NbBotInNormalShape+double(_NbLines)*_Rest)/double(nbbots);
1956 void CGrpProfileFollowRoute::updateProfile(uint ticksSinceLastUpdate)
1958 H_AUTO(GrpFollowRouteProfileUpdate);
1959 CFollowPathContext fpcGrpFollowRouteProfileUpdate("GrpFollowRouteProfileUpdate");
1961 CMoveProfile::updateProfile(ticksSinceLastUpdate);
1963 CSpawnGroupNpc *NpcGrp=NLMISC::safe_cast<CSpawnGroupNpc*>(_Grp.ptr());
1964 CGroupNpc &persGrp=NpcGrp->getPersistent();
1966 CAIVector groupPosition = NpcGrp->getCenterPos();
1967 CAIVector perpGlobalOrient;
1968 NpcGrp->calcCenterPos(groupPosition);
1970 uint32 nbAtDest=0;
1971 uint32 nbNewAtDest=0;
1973 uint32 botIndex=0;
1974 uint32 xIndex=0;
1975 uint32 yIndex=0;
1977 double dx=0;
1978 double dy=0;
1980 // R2_PRIMITIVE_LAXITY
1981 if (IsRingShard.get())
1983 if (!_ProfileTerminated && _Geometry->empty())
1984 _ProfileTerminated = true;
1987 if (_Shape==SHAPE_RECTANGLE)
1989 perpGlobalOrient.setX(-_GlobalOrient.y());
1990 perpGlobalOrient.setY(_GlobalOrient.x());
1993 //////////////////////////////////////////////////////////////////////////
1994 // Calcs the correct gravity grid position (must be done only when bot are removed or add to the group.
1995 if (_MustCalcRatios)
1996 calcRatios ();
1999 FOREACH(it, TBotFollowerMap, _NpcList)
2001 CBotFollower &botFollower=it->second;
2002 if (botFollower.isBotAtDest())
2004 nbAtDest++;
2005 continue;
2008 CBot *bot=it->first;
2009 CSpawnBot *sbot=bot->getSpawnObj();
2010 if ( !sbot
2011 || !sbot->isAlive())
2012 continue;
2014 // verify if the bot has a correct profile and if he reached the destination position.
2015 switch (sbot->getAIProfileType())
2017 case BOT_FOLLOW_POS:
2019 if (_ProfileTerminated)
2021 // remove the profile
2022 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
2024 else
2026 CBotProfileFollowPos* prof = NLMISC::safe_cast<CBotProfileFollowPos*>(sbot->getAIProfile());
2028 // flag the sub profile to stop the npc
2029 prof->setStop(_StopNpc);
2030 if (!_StopNpc)
2032 if (prof->_Status==CFollowPath::FOLLOW_ARRIVED)
2034 botFollower.setBotAtDest();
2035 nbNewAtDest++;
2036 if (simulateBug(2))
2038 /* Following statement was missing */
2040 else
2042 nbAtDest++;
2045 else
2047 // update speeds
2048 prof->setMaxSpeeds(_MaxWalkSpeed, _MaxRunSpeed);
2053 break;
2054 default: // push the correct comportment.
2056 if (!_ProfileTerminated)
2058 sbot->setAIProfile(new CBotProfileFollowPos(&_PathCont, sbot));
2059 CBotProfileFollowPos* prof = NLMISC::safe_cast<CBotProfileFollowPos*>(sbot->getAIProfile());
2060 // update speeds
2061 prof->setMaxSpeeds(_MaxWalkSpeed, _MaxRunSpeed);
2064 break;
2067 if (_Shape==SHAPE_RECTANGLE)
2069 NLMISC::CVector2d dir=sbot->theta().asVector2d();
2070 dx+=dir.x;
2071 dy+=dir.y;
2073 // 4 rows
2074 CAIVector idealPos=groupPosition;
2075 if (botIndex>=_NbBotInNormalShape)
2077 idealPos += perpGlobalOrient * (_XSize*(_Cx-double(xIndex)-(_NbRange-_Rest)*0.5));
2079 else
2081 idealPos += perpGlobalOrient * (_XSize*(_Cx-double(xIndex)));
2084 idealPos+=_GlobalOrient*(_YSize*(_Cy-(double)yIndex));
2085 idealPos-=CAIVector(sbot->pos());
2087 botIndex++;
2088 xIndex++;
2089 if (xIndex>=_NbRange)
2091 xIndex=0;
2092 yIndex++;
2094 sbot->setMoveDecalage(idealPos);
2098 if (_Shape==SHAPE_RECTANGLE)
2100 _GlobalOrient.setX(dx/botIndex);
2101 _GlobalOrient.setY(dy/botIndex);
2104 // first to arrived ?
2105 if (nbAtDest>0 && !_ProfileTerminated)
2107 // oh la la (la, let 's go dancing)..
2108 _GeomIndex++;
2109 #ifdef NL_DEBUG
2110 nlassert(_Geometry);
2111 #endif
2113 if (_GeomIndex>=_Geometry->size()) // we reach the end.
2115 _GeomIndex=0;
2117 if (!_DontSendEvent)
2119 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
2120 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
2122 _ProfileTerminated = true;
2124 else
2126 setCurrentValidPos(_VerticalPos);
2131 //////////////////////////////////////////////////////////////////////////////
2132 // CGrpProfileStandOnVertices //
2133 //////////////////////////////////////////////////////////////////////////////
2135 CPathCont* CGrpProfileStandOnVertices::getPathCont(CBot const* bot)
2137 TNpcBotPositionnerMap::const_iterator it=_NpcList.find(bot);
2138 if (it==_NpcList.end())
2139 return NULL;
2140 return &it->second->_PathCont;
2143 void CGrpProfileStandOnVertices::beginProfile()
2145 PROFILE_LOG("group", "stand_on_vertices", "begin", "");
2146 CMoveProfile::beginProfile();
2147 CSpawnGroupNpc *NpcGrp=NLMISC::safe_cast<CSpawnGroupNpc*>(_Grp.ptr());
2149 CAIStatePositional *grpState=NLMISC::safe_cast<CAIStatePositional *>(NpcGrp->getPersistent().getCAIState());
2151 if ( !grpState
2152 || grpState->shape().numPoints() == 0
2153 || !grpState->isPositional())
2155 if (grpState)
2156 nlwarning("CGrpProfileStandOnVertices::beginProfile : grpState without valid points %s for group %s", grpState->getAliasFullName().c_str(), NpcGrp->getPersistent().getFullName().c_str());
2157 else
2158 nlwarning("CGrpProfileStandOnVertices::beginProfile : invalid no grpState for group %s", NpcGrp->getPersistent().getFullName().c_str());
2160 setCurrentValidPos (grpState);
2161 _Finished=false;
2164 void CGrpProfileStandOnVertices::setCurrentValidPos(CAIStatePositional *grpState)
2166 for (TNpcBotPositionnerMap::iterator it=_NpcList.begin(), itEnd=_NpcList.end();it!=itEnd;++it)
2168 CBotPositionner *botPos=(*it).second;
2169 botPos->setBotAtDest(false);
2170 botPos->_PathCont.setDestination(grpState->shape().getVerticalPos(), *grpState->shape().point(botPos->_GeomIndex));
2174 void CGrpProfileStandOnVertices::resumeProfile()
2176 PROFILE_LOG("group", "stand_on_vertices", "resume", "");
2177 for (TNpcBotPositionnerMap::iterator it=_NpcList.begin(), itEnd=_NpcList.end();it!=itEnd;++it)
2179 const CBot*const bot=(*it).first;
2180 CSpawnBot *sbot=bot->getSpawnObj();
2181 if (!sbot)
2182 continue;
2184 switch (sbot->getAIProfileType())
2186 case BOT_FOLLOW_POS:
2187 break;
2188 default: // push the correct behaviour
2190 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
2192 break;
2197 CSpawnGroupNpc *NpcGrp=NLMISC::safe_cast<CSpawnGroupNpc*>(_Grp.ptr());
2198 CAIStatePositional *grpState=NLMISC::safe_cast<CAIStatePositional *>(NpcGrp->getPersistent().getCAIState());
2199 nlassert( grpState->shape().numPoints()>0
2200 && grpState
2201 && grpState->isPositional());
2202 setCurrentValidPos (grpState);
2203 _Finished=false;
2206 void CGrpProfileStandOnVertices::addBot (CBot *bot)
2208 CGroupNpc& grp=NLMISC::safe_cast<CBotNpc*>(bot)->grp();
2209 #ifdef NL_DEBUG
2210 nlassert(grp.getSpawnObj());
2211 #endif
2213 CAIStatePositional *grpState=NLMISC::safe_cast<CAIStatePositional *>(grp.getCAIState());
2215 CSpawnBot *const sbot=bot->getSpawnObj();
2217 if ( grpState->shape().numPoints() < 1 )
2219 nlwarning("CGrpProfileStandOnVertices : group state '%s'%s: no vertice !", grpState->getAliasFullName().c_str(), grpState->getAliasString().c_str());
2220 return;
2223 if (sbot)
2224 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
2226 _NpcList[bot]=new CBotPositionner (bot->getChildIndex()%grpState->shape().numPoints(), bot->getGroup().getAStarFlag());
2229 void CGrpProfileStandOnVertices::removeBot (CBot *bot)
2231 TNpcBotPositionnerMap::iterator it=_NpcList.find(bot);
2232 if (it!=_NpcList.end())
2234 CSpawnBotNpc *spawnBot=NLMISC::safe_cast<CBotNpc*>(bot)->getSpawn();
2235 if (spawnBot)
2236 spawnBot->setAIProfile(new CBotProfileStandAtPos(spawnBot));
2238 _NpcList.erase (it);
2243 void CGrpProfileStandOnVertices::updateProfile(uint ticksSinceLastUpdate)
2245 H_AUTO(GrpStandProfileUpdate);
2246 CFollowPathContext fpcGrpStandProfileUpdate("GrpStandProfileUpdate");
2248 CMoveProfile::updateProfile(ticksSinceLastUpdate);
2250 CGroupNpc &persGrp=NLMISC::safe_cast<CSpawnGroupNpc*>(_Grp.ptr())->getPersistent();
2251 CAIStatePositional *grpState=static_cast<CAIStatePositional*>(persGrp.getCAIState());
2253 uint32 nbAtDest=0;
2254 uint32 nbNewAtDest=0;
2256 for (TNpcBotPositionnerMap::iterator it=_NpcList.begin(), itEnd=_NpcList.end();it!=itEnd;++it)
2258 CBotPositionner *botPos=(*it).second;
2259 if (!botPos->isBotAtDest())
2261 const CBot*const bot=(*it).first;
2262 CSpawnBot *sbot=bot->getSpawnObj();
2263 if ( sbot
2264 && sbot->isAlive())
2266 switch (sbot->getAIProfileType())
2268 case BOT_FOLLOW_POS:
2270 CBotProfileFollowPos* prof = NLMISC::safe_cast<CBotProfileFollowPos*>(sbot->getAIProfile());
2272 if (prof->_Status==CFollowPath::FOLLOW_ARRIVED)
2274 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
2275 botPos->setBotAtDest();
2276 nbNewAtDest++;
2277 if (simulateBug(2))
2279 /* Following statement was missing */
2281 else
2283 nbAtDest++;
2287 break;
2288 default: // push the correct comportment.
2290 sbot->setAIProfile(new CBotProfileFollowPos(&botPos->_PathCont, sbot));
2292 break;
2296 else
2298 nbAtDest++;
2302 // first to arrived ?
2303 if ( nbNewAtDest==nbAtDest
2304 && nbAtDest>0 )
2306 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
2309 // all arrived ?
2310 if ( nbAtDest==_NpcList.size()
2311 && !_Finished )
2313 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
2314 _Finished=true;
2318 //////////////////////////////////////////////////////////////////////////////
2319 // CGrpProfileFollowPlayer //
2320 //////////////////////////////////////////////////////////////////////////////
2321 CGrpProfileFollowPlayer::CGrpProfileFollowPlayer(CProfileOwner* owner, TDataSetRow const& playerRow, uint32 dispersionRadius)
2322 : CMoveProfile(owner)
2323 , _PlayerRow(playerRow)
2324 , _DispersionRadius(dispersionRadius)
2325 , _PathPos(CAngle(0))
2326 , _PathCont(NLMISC::safe_cast<CSpawnBotNpc*>(owner)->getAStarFlag())
2328 PROFILE_LOG("group", "follow player", "ctor", "");
2329 _Status = CFollowPath::FOLLOWING;
2332 bool CGrpProfileFollowPlayer::destinationReach() const
2334 return _Status == CFollowPath::FOLLOW_ARRIVED
2335 || _Status==CFollowPath::FOLLOW_NO_PATH;
2338 void CGrpProfileFollowPlayer::beginProfile()
2340 _Status = CFollowPath::FOLLOWING;
2343 // TODO: this doesn't work very well at all...
2344 void CGrpProfileFollowPlayer::updateProfile(uint ticksSinceLastUpdate)
2346 H_AUTO(CGrpProfileFollowPlayerUpdate);
2347 CFollowPathContext fpcGrpFollowPlayerUpdate("CGrpProfileFollowPlayerUpdate");
2349 // check all bot to see if there need to move
2350 CSpawnGroupNpc* grp = static_cast<CSpawnGroupNpc*>(static_cast<CSpawnGroup*>(_Grp));
2351 CGroupNpc &pgrp = grp->getPersistent();
2353 CBotPlayer* plrPtr = dynamic_cast<CBotPlayer*>(CAIS::instance().getEntityPhysical(_PlayerRow));
2355 if ( ! plrPtr) {
2356 nlwarning("CGrpProfileFollowPlayer: No valid player position to follow");
2357 return;
2360 _PathCont.setDestination(plrPtr->wpos());
2361 _PathPos._Angle = plrPtr->theta();
2363 for (uint i = 0; i < pgrp.bots().size(); ++i)
2365 CBotNpc* bot = static_cast<CBotNpc*>(pgrp.bots()[i]);
2366 if (!bot)
2367 continue;
2369 // check current bot state
2370 CSpawnBotNpc *sbot = bot->getSpawn();
2371 if (!sbot)
2372 continue;
2374 // Need to wait for a correct position before moving?
2375 CAIVector const& dest = _PathCont.getDestination();
2376 if (dest.x()==0 || dest.y()==0)
2377 return;
2379 static const std::string runParameter("running");
2380 float dist;
2381 if (sbot->getPersistent().getOwner()->getSpawnObj()->checkProfileParameter(runParameter))
2382 dist = sbot->runSpeed()*ticksSinceLastUpdate;
2383 else
2384 dist = sbot->walkSpeed()*ticksSinceLastUpdate;
2386 // Move
2387 CFollowPath::TFollowStatus const status = CFollowPath::getInstance()->followPath(
2388 sbot,
2389 _PathPos,
2390 _PathCont,
2391 dist,
2392 0.f,
2393 0.5f);
2395 if (status==CFollowPath::FOLLOW_NO_PATH)
2397 nlwarning("Problem with following player");
2406 //////////////////////////////////////////////////////////////////////////////
2407 // CGrpProfileIdle //
2408 //////////////////////////////////////////////////////////////////////////////
2410 CGrpProfileIdle::CGrpProfileIdle(CProfileOwner* owner)
2411 : CMoveProfile(owner)
2413 PROFILE_LOG("group", "idle", "ctor", "");
2416 CGrpProfileIdle::~CGrpProfileIdle()
2418 PROFILE_LOG("group", "idle", "dtor", "");
2419 FOREACH(it, CCont<CBot>, _Grp->bots())
2421 CBot* bot = *it;
2422 removeBot(bot);
2426 CGrpProfileIdle::CBotPositionner::CBotPositionner()
2430 CGrpProfileIdle::CBotPositionner::~CBotPositionner()
2434 void CGrpProfileIdle::beginProfile()
2436 PROFILE_LOG("group", "idle", "begin", "");
2437 CMoveProfile::beginProfile();
2440 CPathCont* CGrpProfileIdle::getPathCont(CBot const* bot)
2442 return NULL;
2445 void CGrpProfileIdle::resumeProfile()
2447 PROFILE_LOG("group", "idle", "resume", "");
2448 typedef std::map<CBot*,CBotPositionner> TCont;
2449 FOREACH(it, TCont, _NpcList)
2451 CBot* bot = (*it).first;
2452 CSpawnBot* sbot = bot->getSpawnObj();
2453 if (sbot)
2455 switch (sbot->getAIProfileType())
2457 case BOT_STAND_AT_POS:
2458 break;
2459 default: // push the correct comportment.
2460 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
2461 break;
2467 void CGrpProfileIdle::addBot(CBot* bot)
2469 #ifdef NL_DEBUG
2470 CGroupNpc& grp=NLMISC::safe_cast<CBotNpc*>(bot)->grp();
2471 nlassert(grp.getSpawnObj());
2472 #endif
2473 _NpcList[bot]=CBotPositionner ();
2474 CSpawnBot *sbot=bot->getSpawnObj();
2475 if (sbot)
2476 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
2479 void CGrpProfileIdle::removeBot(CBot* bot)
2481 std::map<CBot*, CBotPositionner>::iterator it=_NpcList.find(bot);
2482 if (it!=_NpcList.end())
2484 _NpcList.erase (it);
2489 void CGrpProfileIdle::endProfile()
2491 PROFILE_LOG("group", "idle", "end", "");
2494 void CGrpProfileIdle::updateProfile(uint ticksSinceLastUpdate)
2496 CMoveProfile::updateProfile(ticksSinceLastUpdate);
2499 std::string CGrpProfileIdle::getOneLineInfoString() const
2501 return "idle profile";
2504 //////////////////////////////////////////////////////////////////////////////
2505 // CGrpProfileWander //
2506 //////////////////////////////////////////////////////////////////////////////
2508 CGrpProfileWander::CGrpProfileWander(CProfileOwner* owner, CNpcZone const* npcZone)
2509 : CMoveProfile(owner)
2510 , _Social(false)
2511 , _NpcZone(npcZone)
2513 PROFILE_LOG("group", "wander", "ctor", "");
2514 _BotStandProfileType = BOT_STAND_AT_POS;
2515 _BotStandProfileFactory = &BotProfileStandAtPosFactory;
2516 _RandomPos=&npcZone->getPlaceRandomPos();
2519 CGrpProfileWander::CGrpProfileWander(CProfileOwner* owner)
2520 : CMoveProfile(owner)
2521 , _Social(false)
2523 PROFILE_LOG("group", "wander", "ctor2", "");
2524 affectZoneFromStateMachine();
2525 #if !FINAL_VERSION
2526 // R2_PRIMITIVE_LAXITY
2527 if (!IsRingShard.get())
2529 nlassert(!_RandomPos.isNULL());
2531 #endif
2533 // default to stand apt pos profile
2534 _BotStandProfileType = BOT_STAND_AT_POS;
2535 _BotStandProfileFactory = &BotProfileStandAtPosFactory;
2538 CGrpProfileWander::~CGrpProfileWander()
2540 PROFILE_LOG("group", "wander", "dtor", "");
2543 void CGrpProfileWander::stateChangeProfile()
2545 affectZoneFromStateMachine();
2546 resetDestinationReachedData();
2549 void CGrpProfileWander::affectZoneFromStateMachine()
2551 CSpawnGroupNpc* grp = static_cast<CSpawnGroupNpc*>(static_cast<CSpawnGroup *>(_Grp));
2552 CGroupNpc& pgrp = grp->getPersistent();
2553 CAIState const* const state = pgrp.getActiveState();
2555 if ( !state
2556 || !state->isPositional())
2557 return;
2559 CAIStatePositional const* const sp = static_cast<CAIStatePositional const* const>(state);
2560 if (!sp->shape().hasPoints())
2562 if (sp->getAliasNode())
2563 nlwarning("Error, no position in state %s", sp->getAliasNode()->fullName().c_str());
2564 else
2565 nlwarning("Error, no position in state %s", sp->getName().c_str());
2567 if (sp->shape().hasPatat())
2568 _RandomPos=&(sp->shape());
2571 void CGrpProfileWander::resetDestinationReachedData()
2573 _DestinationReachedFirst = false;
2574 _DestinationReachedAll = false;
2575 std::fill(_NpcDestinationReached.begin(), _NpcDestinationReached.end(), false);
2578 void CGrpProfileWander::setBotStandProfile(TProfiles botStandProfileType, IAIProfileFactory *botStandProfileFactory)
2580 _BotStandProfileType = botStandProfileType;
2581 _BotStandProfileFactory = botStandProfileFactory;
2584 void CGrpProfileWander::beginProfile()
2586 PROFILE_LOG("group", "wander", "begin", "");
2587 CMoveProfile::beginProfile();
2589 CSpawnGroupNpc *grp = static_cast<CSpawnGroupNpc*>(static_cast<CSpawnGroup *>(_Grp));
2591 if (grp->checkProfileParameter("forage"))
2593 _BotStandProfileType = BOT_FORAGE;
2594 _BotStandProfileFactory = &BotProfileForageFactory;
2596 else if (grp->checkProfileParameter("social"))
2598 _Social = true;
2601 _NpcDestinationReached.resize( grp->getPersistent().bots().size());
2602 resetDestinationReachedData();
2606 void CGrpProfileWander::addBot(CBot* bot)
2608 CSpawnBot *const spawnBot=bot->getSpawnObj();
2609 if (!spawnBot)
2610 return;
2612 if (_BotStandProfileType == BOT_FORAGE)
2614 // special case, only set the forage activity and let the bot live there life
2615 spawnBot->setAIProfile(_BotStandProfileFactory->createAIProfile(spawnBot));
2616 return;
2619 CBotProfileStandAtPos* const profile = new CBotProfileStandAtPos(spawnBot);
2620 #ifdef NL_DEBUG
2621 nlassert(profile!=NULL);
2622 #endif
2623 spawnBot->setAIProfile(profile);
2625 void CGrpProfileWander::removeBot(CBot* bot)
2628 CPathCont* CGrpProfileWander::getPathCont(CBot const* bot)
2630 return NULL;
2633 void CGrpProfileWander::endProfile()
2635 PROFILE_LOG("group", "wander", "end", "");
2638 void CGrpProfileWander::updateProfile(uint ticksSinceLastUpdate)
2640 H_AUTO(GrpWanderProfileUpdate);
2641 CFollowPathContext fpcGrpWanderProfileUpdate("GrpWanderProfileUpdate");
2643 if (_BotStandProfileType == BOT_FORAGE)
2645 // special case, only set the forage activity and let the bot live there life
2646 return;
2649 // check all bot to see if there need to move
2650 CSpawnGroupNpc* grp = static_cast<CSpawnGroupNpc*>(static_cast<CSpawnGroup*>(_Grp));
2651 CGroupNpc &pgrp = grp->getPersistent();
2652 bool aNpcHasReachDestination = false;
2653 for (uint i=0; i<pgrp.bots().size(); ++i)
2655 CBotNpc* bot = static_cast<CBotNpc*>(pgrp.bots()[i]);
2656 if (!bot)
2657 continue;
2659 // check current bot state
2660 CSpawnBotNpc *sbot = bot->getSpawn();
2661 if (!sbot)
2662 continue;
2664 IAIProfile *profile = sbot->getAIProfile();
2666 if (!profile)
2668 // init a profile on the bot
2669 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
2670 continue;
2673 if (profile->getAIProfileType() == BOT_MOVE_TO)
2675 // check if we are arrived
2676 CBotProfileMoveTo* mt = static_cast<CBotProfileMoveTo*>(profile);
2677 if (mt->destinationReach())
2679 if (!_DestinationReachedAll)
2682 uint32 npcSize = (uint32)pgrp.bots().size();
2683 uint32 reachedSize = (uint32)_NpcDestinationReached.size();
2684 if (reachedSize!= npcSize)
2686 _NpcDestinationReached.resize(npcSize);
2687 // invalid the vector a new bot has arrived
2688 if (npcSize>reachedSize){ std::fill(_NpcDestinationReached.begin(), _NpcDestinationReached.end(), false); }
2691 if ( !_NpcDestinationReached[i])
2693 _NpcDestinationReached[i] = true;
2694 aNpcHasReachDestination = true;
2699 // look arround for interesting target
2700 CAIVision<CPersistentOfPhysical> vision;
2701 vision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), CAIVector(sbot->pos()), 10, 10);
2703 CPersistentOfPhysical *target = NULL;
2705 if (!vision.players().empty())
2707 // there are some player near, look at one if it is not behin us
2708 uint index = CAIS::rand16((uint32)vision.players().size());
2709 CAngle angle(CAngle::pi());
2711 while (index < vision.players().size() && !target)
2713 CPersistentOfPhysical *pop = vision.players()[index++];
2715 angle = sbot->pos().angleTo(pop->getSpawnObj()->pos());
2717 if (angle < CAngle::pi()/2 && angle > CAngle::pi()/-2)
2719 target = pop;
2720 break;
2724 if (!target && !vision.bots().empty())
2726 // there are some bots near, look at one if it is not behin us
2727 uint index = CAIS::rand16((uint32)vision.bots().size());
2728 CAngle angle(CAngle::pi());
2730 while (index < vision.bots().size() && !target)
2732 CPersistentOfPhysical *pop = vision.bots()[index++];
2734 angle = sbot->pos().angleTo(pop->getSpawnObj()->pos());
2736 if (angle < CAngle::pi()/2 && angle > CAngle::pi()/-2)
2738 target = pop;
2739 break;
2743 // set the visual target
2744 if (target && target->isSpawned())
2745 sbot->setVisualTarget(target->getSpawnObj());
2746 else
2747 sbot->setVisualTarget(NULL);
2749 // now, set the idle activity with a random timer
2751 sbot->setAIProfile(_BotStandProfileFactory->createAIProfile(sbot));
2752 CBotProfileWanderBase *wbs = safe_cast<CBotProfileWanderBase*>(sbot->getAIProfile());
2753 if (wbs)
2755 float waitMin;
2756 float waitMax;
2757 static string waitMinStr("wait min");
2758 static string waitMaxStr("wait max");
2759 if (grp->getProfileParameter(waitMinStr, waitMin))
2761 if (!grp->getProfileParameter(waitMaxStr, waitMax))
2762 waitMax = waitMin;
2763 else
2764 waitMax = waitMax > waitMin ? waitMax : waitMin;
2766 else
2768 waitMin = float(DefaultWanderMinTimer);
2769 waitMax = float(DefaultWanderMaxTimer);
2771 wbs->setTimer(uint32(waitMin+CAIS::rand32(uint32(waitMax-waitMin))));
2774 continue;
2777 if (profile->getAIProfileType()==_BotStandProfileType)
2779 const CBotProfileWanderBase*const wbs = static_cast<CBotProfileStandAtPos *>(sbot->getAIProfile());
2780 #ifdef NL_DEBUG
2781 nlassert(wbs);
2782 #endif
2783 if (!wbs->testTimer())
2784 continue;
2787 #ifdef NL_DEBUG
2788 nlassert(_RandomPos != NULL && _RandomPos->getRandomPosCount() != 0);
2789 #endif
2790 // R2_PRIMITIVE_LAXITY
2791 // for a Ring shard stop here and only do a warning to avoid asserting later
2792 // primitives generated by players may be incorrect and should not crash the service
2793 if (IsRingShard.get())
2795 if ( _RandomPos == NULL
2796 || _RandomPos->getRandomPosCount() == 0)
2798 string stateName = "NULL state!";
2799 CAIState const* const state = pgrp.getActiveState();
2800 if (state != NULL)
2801 stateName = state->getAliasFullName();
2802 nlwarning("No valid wander position for state '%s'", stateName.c_str());
2803 return;
2807 // time out, move to another point in the geometry
2808 RYAI_MAP_CRUNCH::CWorldPosition wp;
2810 if (_Social && CAIS::rand32(3) == 0)
2812 // this time, we try to reach an npc in the neighbour
2813 //TODO : implemente this behavior
2815 else
2817 // standard random move
2818 _RandomPos->getRandomPos(wp);
2820 CBotProfileMoveTo* mts = new CBotProfileMoveTo(_RandomPos->getVerticalPos(), wp, sbot);
2821 sbot->setAIProfile(mts);
2825 if (aNpcHasReachDestination && !_DestinationReachedAll)
2828 if (!_DestinationReachedFirst)
2830 _DestinationReachedFirst = true;
2831 pgrp.processStateEvent(pgrp.mgr().EventDestinationReachedFirst);
2834 uint32 first=0, last=(uint32)_NpcDestinationReached.size();
2835 for ( ; first != last && _NpcDestinationReached[first]; ++first) {}
2837 if (first == last)
2839 _DestinationReachedAll = true;
2840 pgrp.processStateEvent(pgrp.mgr().EventDestinationReachedAll);
2845 std::string CGrpProfileWander::getOneLineInfoString() const
2847 return "wander group profile";
2850 //////////////////////////////////////////////////////////////////////////////
2851 // CGrpProfileWanderNoPrim //
2852 //////////////////////////////////////////////////////////////////////////////
2854 CGrpProfileWanderNoPrim::CGrpProfileWanderNoPrim(CProfileOwner* owner, NLMISC::CSmartPtr<CNpcZonePlaceNoPrim> const& npcZone)
2855 : CMoveProfile(owner)
2856 , _Social(false)
2857 , _NpcZone(npcZone)
2859 PROFILE_LOG("group", "wander", "ctor", "");
2860 _BotStandProfileType = BOT_STAND_AT_POS;
2861 _BotStandProfileFactory = &BotProfileStandAtPosFactory;
2862 // _RandomPos = &npcZone->getPlaceRandomPos();
2865 CGrpProfileWanderNoPrim::CGrpProfileWanderNoPrim(CProfileOwner* owner)
2866 : CMoveProfile(owner)
2867 , _Social(false)
2869 PROFILE_LOG("group", "wander", "ctor2", "");
2870 affectZoneFromStateMachine();
2871 #if !FINAL_VERSION
2872 nlassert(!_RandomPos.isNULL());
2873 #endif
2875 // default to stand apt pos profile
2876 _BotStandProfileType = BOT_STAND_AT_POS;
2877 _BotStandProfileFactory = &BotProfileStandAtPosFactory;
2880 void CGrpProfileWanderNoPrim::stateChangeProfile()
2882 // affectZoneFromStateMachine();
2883 // resetDestinationReachedData();
2886 void CGrpProfileWanderNoPrim::affectZoneFromStateMachine()
2888 CSpawnGroupNpc* grp = static_cast<CSpawnGroupNpc*>(static_cast<CSpawnGroup *>(_Grp));
2889 CGroupNpc& pgrp = grp->getPersistent();
2890 CAIState const* const state = pgrp.getActiveState();
2892 if ( !state
2893 || !state->isPositional())
2894 return;
2896 CAIStatePositional const* const sp = static_cast<CAIStatePositional const* const>(state);
2897 if (!sp->shape().hasPoints())
2899 if (sp->getAliasNode())
2900 nlwarning("Error, no position in state %s", sp->getAliasNode()->fullName().c_str());
2901 else
2902 nlwarning("Error, no position in state %s", sp->getName().c_str());
2904 if (sp->shape().hasPatat())
2905 _RandomPos=&(sp->shape());
2909 CGrpProfileWanderNoPrim::~CGrpProfileWanderNoPrim()
2911 PROFILE_LOG("group", "wander", "dtor", "");
2914 void CGrpProfileWanderNoPrim::setBotStandProfile(TProfiles botStandProfileType, IAIProfileFactory *botStandProfileFactory)
2916 _BotStandProfileType = botStandProfileType;
2917 _BotStandProfileFactory = botStandProfileFactory;
2920 void CGrpProfileWanderNoPrim::beginProfile()
2922 PROFILE_LOG("group", "wander", "begin", "");
2923 CMoveProfile::beginProfile();
2925 CSpawnGroupNpc *grp = static_cast<CSpawnGroupNpc*>(static_cast<CSpawnGroup *>(_Grp));
2927 if (grp->checkProfileParameter("forage"))
2929 _BotStandProfileType = BOT_FORAGE;
2930 _BotStandProfileFactory = &BotProfileForageFactory;
2932 else if (grp->checkProfileParameter("social"))
2934 _Social = true;
2938 void CGrpProfileWanderNoPrim::addBot(CBot* bot)
2940 CSpawnBot *const spawnBot=bot->getSpawnObj();
2941 if (!spawnBot)
2942 return;
2944 if (_BotStandProfileType == BOT_FORAGE)
2946 // special case, only set the forage activity and let the bot live there life
2947 spawnBot->setAIProfile(_BotStandProfileFactory->createAIProfile(spawnBot));
2948 return;
2951 CBotProfileStandAtPos* const profile = new CBotProfileStandAtPos(spawnBot);
2952 #ifdef NL_DEBUG
2953 nlassert(profile!=NULL);
2954 #endif
2955 spawnBot->setAIProfile(profile);
2957 void CGrpProfileWanderNoPrim::removeBot(CBot* bot)
2960 CPathCont* CGrpProfileWanderNoPrim::getPathCont(CBot const* bot)
2962 return NULL;
2965 void CGrpProfileWanderNoPrim::endProfile()
2967 PROFILE_LOG("group", "wander", "end", "");
2970 void CGrpProfileWanderNoPrim::updateProfile(uint ticksSinceLastUpdate)
2972 H_AUTO(GrpWanderProfileUpdate);
2973 CFollowPathContext fpcGrpWanderProfileUpdate("GrpWanderProfileUpdate");
2975 if (_BotStandProfileType == BOT_FORAGE)
2977 // special case, only set the forage activity and let the bot live there life
2978 return;
2981 // check all bot to see if there need to move
2982 CSpawnGroupNpc* grp = static_cast<CSpawnGroupNpc*>(static_cast<CSpawnGroup*>(_Grp));
2983 CGroupNpc &pgrp = grp->getPersistent();
2985 for (uint i=0; i<pgrp.bots().size(); ++i)
2987 CBotNpc* bot = static_cast<CBotNpc*>(pgrp.bots()[i]);
2988 if (!bot)
2989 continue;
2991 // check current bot state
2992 CSpawnBotNpc *sbot = bot->getSpawn();
2993 if (!sbot)
2994 continue;
2996 IAIProfile *profile = sbot->getAIProfile();
2998 if (!profile)
3000 // init a profile on the bot
3001 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
3002 continue;
3005 if (profile->getAIProfileType() == BOT_MOVE_TO)
3007 // check if we are arrived
3008 CBotProfileMoveTo* mt = static_cast<CBotProfileMoveTo*>(profile);
3009 if (mt->destinationReach())
3011 // look arround for interesting target
3012 CAIVision<CPersistentOfPhysical> vision;
3013 vision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), CAIVector(sbot->pos()), 10, 10);
3015 CPersistentOfPhysical *target = NULL;
3017 if (!vision.players().empty())
3019 // there are some player near, look at one if it is not behin us
3020 uint index = CAIS::rand16((uint32)vision.players().size());
3021 CAngle angle(CAngle::pi());
3023 while (index < vision.players().size() && !target)
3025 CPersistentOfPhysical *pop = vision.players()[index++];
3027 angle = sbot->pos().angleTo(pop->getSpawnObj()->pos());
3029 if (angle < CAngle::pi()/2 && angle > CAngle::pi()/-2)
3031 target = pop;
3032 break;
3036 if (!target && !vision.bots().empty())
3038 // there are some bots near, look at one if it is not behin us
3039 uint index = CAIS::rand16((uint32)vision.bots().size());
3040 CAngle angle(CAngle::pi());
3042 while (index < vision.bots().size() && !target)
3044 CPersistentOfPhysical *pop = vision.bots()[index++];
3046 angle = sbot->pos().angleTo(pop->getSpawnObj()->pos());
3048 if (angle < CAngle::pi()/2 && angle > CAngle::pi()/-2)
3050 target = pop;
3051 break;
3055 // set the visual target
3056 if (target && target->isSpawned())
3057 sbot->setVisualTarget(target->getSpawnObj());
3058 else
3059 sbot->setVisualTarget(NULL);
3061 // now, set the idle activity with a random timer
3063 sbot->setAIProfile(_BotStandProfileFactory->createAIProfile(sbot));
3064 CBotProfileWanderBase *wbs = safe_cast<CBotProfileWanderBase*>(sbot->getAIProfile());
3065 if (wbs)
3067 float waitMin;
3068 float waitMax;
3069 static string waitMinStr("wait min");
3070 static string waitMaxStr("wait max");
3071 if (grp->getProfileParameter(waitMinStr, waitMin))
3073 if (!grp->getProfileParameter(waitMaxStr, waitMax))
3074 waitMax = waitMin;
3075 else
3076 waitMax = waitMax > waitMin ? waitMax : waitMin;
3078 else
3080 waitMin = float(DefaultWanderMinTimer);
3081 waitMax = float(DefaultWanderMaxTimer);
3083 wbs->setTimer(uint32(waitMin+CAIS::rand32(uint32(waitMax-waitMin))));
3085 pgrp.processStateEvent(pgrp.mgr().EventDestinationReachedFirst);
3086 pgrp.processStateEvent(pgrp.mgr().EventDestinationReachedAll);
3088 continue;
3091 if (profile->getAIProfileType()==_BotStandProfileType)
3093 const CBotProfileWanderBase*const wbs = static_cast<CBotProfileStandAtPos *>(sbot->getAIProfile());
3094 #ifdef NL_DEBUG
3095 nlassert(wbs);
3096 #endif
3097 if (!wbs->testTimer())
3098 continue;
3101 #ifdef NL_DEBUG
3102 nlassert(_NpcZone->getRandomPosCount());
3103 #endif
3104 // time out, move to another point in the geometry
3105 RYAI_MAP_CRUNCH::CWorldPosition wp;
3107 if (_Social && CAIS::rand32(3) == 0)
3109 // this time, we try to reach an npc in the neighbour
3110 //TODO : implemente this behavior
3112 else
3114 // standard random move
3115 _NpcZone->getRandomPos(wp);
3117 CBotProfileMoveTo* mts = new CBotProfileMoveTo(_NpcZone->getVerticalPos(), wp, sbot);
3118 sbot->setAIProfile(mts);
3122 std::string CGrpProfileWanderNoPrim::getOneLineInfoString() const
3124 return "wander group profile (without primitive)";
3127 //////////////////////////////////////////////////////////////////////////////
3128 // CGrpProfileStandOnVertices //
3129 //////////////////////////////////////////////////////////////////////////////
3131 CGrpProfileStandAtStartPoint::CGrpProfileStandAtStartPoint(CProfileOwner* owner)
3132 : CMoveProfile(owner)
3134 PROFILE_LOG("group", "stand_at_start_point", "ctor", "");
3137 CGrpProfileStandAtStartPoint::~CGrpProfileStandAtStartPoint()
3139 PROFILE_LOG("group", "stand_at_start_point", "dtor", "");
3140 for (CCont<CBot >::iterator it=_Grp->bots().begin(), itEnd=_Grp->bots().end();it!=itEnd;++it)
3142 CBot *bot=*it;
3143 removeBot (bot);
3147 CGrpProfileStandAtStartPoint::CBotPositionner::CBotPositionner(RYAI_MAP_CRUNCH::TAStarFlag flags)
3148 : _PathCont(flags)
3152 CGrpProfileStandAtStartPoint::CBotPositionner::CBotPositionner(TVerticalPos verticalPos, CAIPos position, RYAI_MAP_CRUNCH::TAStarFlag flag)
3153 : _PathCont(flag)
3154 , _Position(position)
3155 , _VerticalPos(verticalPos)
3156 , _BotAtDest(false)
3158 _PathCont.setDestination(verticalPos, position);
3161 CGrpProfileStandAtStartPoint::CBotPositionner::~CBotPositionner()
3165 inline
3166 void CGrpProfileStandAtStartPoint::CBotPositionner::setBotAtDest(bool atDest)
3168 _BotAtDest = atDest;
3171 inline
3172 bool CGrpProfileStandAtStartPoint::CBotPositionner::isBotAtDest() const
3174 return _BotAtDest;
3177 CPathCont* CGrpProfileStandAtStartPoint::getPathCont(CBot const* bot)
3179 TNpcBotPositionnerMap::const_iterator it = _NpcList.find(bot);
3180 if (it==_NpcList.end())
3181 return NULL;
3182 return &it->second->_PathCont;
3185 void CGrpProfileStandAtStartPoint::beginProfile()
3187 PROFILE_LOG("group", "stand_at_start_point", "begin", "");
3188 CMoveProfile::beginProfile();
3190 setCurrentValidPos();
3191 _Finished = false;
3194 void CGrpProfileStandAtStartPoint::resumeProfile()
3196 PROFILE_LOG("group", "stand_at_start_point", "resume", "");
3197 for (TNpcBotPositionnerMap::iterator it=_NpcList.begin(), itEnd=_NpcList.end();it!=itEnd;++it)
3199 const CBot*const bot=(*it).first;
3200 CSpawnBot *sbot=bot->getSpawnObj();
3201 if (!sbot)
3202 continue;
3204 switch (sbot->getAIProfileType())
3206 case BOT_FOLLOW_POS:
3207 break;
3208 default: // push the correct comportment.
3209 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
3210 break;
3215 setCurrentValidPos();
3216 _Finished = false;
3219 void CGrpProfileStandAtStartPoint::addBot(CBot* bot)
3221 CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
3223 CGroupNpc& grp = botNpc->grp();
3224 #ifdef NL_DEBUG
3225 nlassert(grp.getSpawnObj());
3226 #endif
3227 CAIStatePositional *grpState=NLMISC::safe_cast<CAIStatePositional *>(grp.getCAIState());
3228 _NpcList[bot]=new CBotPositionner (botNpc->getStartVerticalPos(), botNpc->getStartPos(), botNpc->getGroup().getAStarFlag());
3230 CSpawnBot *const sbot=bot->getSpawnObj();
3231 if (sbot)
3232 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
3235 void CGrpProfileStandAtStartPoint::removeBot(CBot* bot)
3237 TNpcBotPositionnerMap::iterator it=_NpcList.find(bot);
3239 if (it==_NpcList.end())
3240 return;
3242 CSpawnBotNpc* const spawnBot = NLMISC::safe_cast<CBotNpc*>(bot)->getSpawn();
3243 if (spawnBot)
3244 spawnBot->setAIProfile(new CBotProfileStandAtPos(spawnBot));
3245 _NpcList.erase (it);
3248 void CGrpProfileStandAtStartPoint::setCurrentValidPos()
3250 FOREACH(it, TNpcBotPositionnerMap, _NpcList)
3252 CBotPositionner *botPos=(*it).second;
3253 botPos->setBotAtDest(false);
3254 botPos->_PathCont.setDestination(botPos->_VerticalPos, botPos->_Position);
3259 void CGrpProfileStandAtStartPoint::endProfile()
3261 PROFILE_LOG("group", "stand_at_start_point", "end", "");
3264 void CGrpProfileStandAtStartPoint::updateProfile(uint ticksSinceLastUpdate)
3266 H_AUTO(GrpStandStartPointProfileUpdate);
3267 CFollowPathContext fpcGrpStandStartPointProfileUpdate("GrpStandStartPointProfileUpdate");
3269 CMoveProfile::updateProfile(ticksSinceLastUpdate);
3271 CGroupNpc &persGrp=NLMISC::safe_cast<CSpawnGroupNpc*>(_Grp.ptr())->getPersistent();
3272 CAIStatePositional *grpState=static_cast<CAIStatePositional*>(persGrp.getCAIState());
3274 uint32 nbAtDest=0;
3275 uint32 nbNewAtDest=0;
3277 for (TNpcBotPositionnerMap::iterator it=_NpcList.begin(), itEnd=_NpcList.end();it!=itEnd;++it)
3279 CBotPositionner*const botPos=(*it).second;
3280 if (botPos->isBotAtDest())
3282 nbAtDest++;
3283 continue;
3286 const CBot*const bot=(*it).first;
3287 CSpawnBot *sbot=bot->getSpawnObj();
3288 if ( !sbot
3289 || !sbot->isAlive())
3290 continue;
3292 switch (sbot->getAIProfileType())
3294 case BOT_FOLLOW_POS:
3296 CBotProfileFollowPos* prof = NLMISC::safe_cast<CBotProfileFollowPos*>(sbot->getAIProfile());
3298 if (prof->_Status==CFollowPath::FOLLOW_ARRIVED)
3300 sbot->setTheta(botPos->_Position.theta());
3301 sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
3302 botPos->setBotAtDest();
3303 nbNewAtDest++;
3304 if (simulateBug(2))
3306 /* Following statement was missing */
3308 else
3310 nbAtDest++;
3315 break;
3316 default: // push the correct comportment.
3318 sbot->setAIProfile(new CBotProfileFollowPos(&botPos->_PathCont, sbot));
3320 break;
3325 // first to arrived ?
3326 if ( nbNewAtDest==nbAtDest
3327 && nbAtDest>0 )
3329 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
3332 // all arrived ?
3333 if ( nbAtDest==_NpcList.size()
3334 && !_Finished )
3336 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
3337 _Finished=true;
3341 std::string CGrpProfileStandAtStartPoint::getOneLineInfoString() const
3343 std::string info = "stand_at_start_point group profile";
3344 info += " finished=" + NLMISC::toString(_Finished);
3345 return info;
3348 //////////////////////////////////////////////////////////////////////////////
3349 // CGrpProfileEscorted //
3350 //////////////////////////////////////////////////////////////////////////////
3352 CGrpProfileEscorted::CGrpProfileEscorted(CProfileOwner *owner)
3353 : CGrpProfileNormal(owner)
3355 PROFILE_LOG("group", "escorted", "ctor", "");
3358 CGrpProfileEscorted::~CGrpProfileEscorted()
3360 PROFILE_LOG("group", "escorted", "dtor", "");
3363 void CGrpProfileEscorted::beginProfile()
3365 PROFILE_LOG("group", "escorted", "begin", "");
3366 CGrpProfileNormal::beginProfile();
3367 _EscortTeamInRange = false;
3370 void CGrpProfileEscorted::endProfile()
3372 PROFILE_LOG("group", "escorted", "end", "");
3373 if (_Grp->movingProfile().getAIProfileType() == AITYPES::MOVE_FOLLOW_ROUTE)
3375 CGrpProfileFollowRoute* prof = NLMISC::safe_cast<CGrpProfileFollowRoute*>(_Grp->movingProfile().getAIProfile());
3376 if (prof)
3377 prof->stopNpc(false);
3381 void CGrpProfileEscorted::stateChangeProfile()
3383 CGrpProfileNormal::beginProfile();
3386 void CGrpProfileEscorted::updateProfile(uint ticksSinceLastUpdate)
3388 H_AUTO(GrpEscortedProfileUpdate);
3389 CFollowPathContext fpcGrpEscortedProfileUpdate("GrpEscortedProfileUpdate");
3391 const uint32 ESCORT_RANGE_DELTA = 3;
3392 CGroupNpc &GrpRef = _Grp->getPersistent();
3393 const uint16 teamId = GrpRef.getEscortTeamId();
3395 if (teamId == CTEAM::InvalidTeamId)
3397 // no escort team assigned, can't move!
3398 _EscortTeamInRange = false;
3399 return;
3402 // we need to have some member of the escort team in range to allow movement
3403 // look arround for an escort member
3404 CAIVector centerPos;
3405 bool escortAway = true;
3406 bool escortBack = false;
3407 float escortRange=GrpRef.getEscortRange();
3408 double distAway = escortRange+ESCORT_RANGE_DELTA;
3409 double distBack = escortRange-ESCORT_RANGE_DELTA;
3410 // square the dist to speedup test
3411 distAway *= distAway;
3412 distBack *= distBack;
3413 if (_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
3415 CAIVision<CPersistentOfPhysical> vision;
3417 // group vision update.
3418 vision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), centerPos, uint(escortRange+ESCORT_RANGE_DELTA), 0);
3420 if (vision.players().empty())
3422 // no player, no need to check, the escort is away !
3423 escortAway = true;
3425 else
3427 // loop on each player until we know if escort is away or back
3428 vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::const_iterator first(vision.players().begin()), last(vision.players().end());
3429 for (;first != last && (escortAway || !escortBack); ++first)
3431 CBotPlayer *player = safe_cast<CBotPlayer*>((*first).ptr()); // (CPersistentOfPhysical*)
3432 if (player->isAlive())
3434 if (CMirrors::getTeamId(player->getSpawnObj()->dataSetRow()) == teamId)
3436 // on of our escort girl ;)
3437 // check the distance
3438 double sqrDist = (centerPos - player->getSpawnObj()->pos()).sqrnorm();
3439 if (sqrDist < distBack)
3441 escortBack = true;
3442 // the escort is back
3444 if (sqrDist < distAway)
3446 escortAway = false;
3447 // the escort is not away
3455 if (_EscortTeamInRange)
3457 if (escortAway)
3459 // switch to 'wait for escort' mode and send event 'escort away'
3460 _EscortTeamInRange = false;
3461 GrpRef.processStateEvent(GrpRef.mgr().getStateMachine()->EventEscortAway);
3464 else
3466 if (escortBack)
3468 // switch to 'escort in range' mode and send event 'escort back'
3469 _EscortTeamInRange = true;
3470 GrpRef.processStateEvent(GrpRef.mgr().getStateMachine()->EventEscortBack);
3474 if (_GroupFighting)
3476 if (!_Grp->fightProfile().getAIProfile())
3477 _Grp->fightProfile().setAIProfile(_Grp.ptr(), &GrpProfileFightFactory, false);
3479 _Grp->fightProfile().mayUpdateProfile(ticksSinceLastUpdate);
3481 CFightProfile* profile = NLMISC::safe_cast<CFightProfile*>(_Grp->fightProfile().getAIProfile());
3482 if (!profile->stillHaveEnnemy())
3484 // :TODO: Verify if it's needed to erase bots aggro too/instead
3485 // _Grp->clearAggroList(); // this erase all aggro.
3486 setGroupFighting (false);
3488 _Grp->fightProfile().setAIProfile(NULL);
3489 (NLMISC::safe_cast<CMoveProfile*>(_Grp->movingProfile().getAIProfile()))->resumeProfile ();
3492 else
3494 if (!_Grp->checkProfileParameter("defenceless"))
3496 if (_Grp->haveAggroOrReturnPlace())
3498 if(_Grp->isGroupAlive())
3500 // set the fighting comportment.
3501 if (!_Grp->fightProfile().getAIProfile())
3502 _Grp->fightProfile().setAIProfile(_Grp.ptr(), &GrpProfileFightFactory, false);
3504 setGroupFighting(true);
3510 if (_Grp->movingProfile().getAIProfileType() == AITYPES::MOVE_FOLLOW_ROUTE && !_GroupFighting)
3512 CGrpProfileFollowRoute* prof = NLMISC::safe_cast<CGrpProfileFollowRoute*>(_Grp->movingProfile().getAIProfile());
3514 if (prof)
3516 prof->stopNpc(!_EscortTeamInRange);
3517 _Grp->movingProfile().mayUpdateProfile(ticksSinceLastUpdate);
3522 std::string CGrpProfileEscorted::getOneLineInfoString() const
3524 std::string info = "escorted profile";
3525 info += " escort_team_in_range=" + NLMISC::toString(_EscortTeamInRange);
3526 uint16 teamId = _Grp->getPersistent().getEscortTeamId();
3527 info += " team_id=" + (teamId==CTEAM::InvalidTeamId)?"InvalidTeamId":NLMISC::toString(teamId);
3528 return info;
3531 //////////////////////////////////////////////////////////////////////////////
3532 // CGrpProfileGuardEscorted //
3533 //////////////////////////////////////////////////////////////////////////////
3535 CGrpProfileGuardEscorted::CGrpProfileGuardEscorted(CProfileOwner *owner)
3536 : CGrpProfileNormal(owner)
3538 PROFILE_LOG("group", "guard_escorted", "ctor", "");
3539 _GuardProfile = new CGrpProfileGuard(owner);
3540 _EscortedProfile = new CGrpProfileEscorted(owner);
3543 CGrpProfileGuardEscorted::~CGrpProfileGuardEscorted()
3545 PROFILE_LOG("group", "guard_escorted", "dtor", "");
3548 void CGrpProfileGuardEscorted::beginProfile()
3550 PROFILE_LOG("group", "guard_escorted", "begin", "");
3551 CGrpProfileNormal::beginProfile();
3553 _GuardProfile->beginProfile();
3554 _EscortedProfile->beginProfile();
3557 void CGrpProfileGuardEscorted::endProfile()
3559 PROFILE_LOG("group", "guard_escorted", "end", "");
3560 _EscortedProfile->endProfile();
3561 _GuardProfile->endProfile();
3564 void CGrpProfileGuardEscorted::stateChangeProfile()
3566 CGrpProfileNormal::beginProfile();
3567 _EscortedProfile->stateChangeProfile();
3568 _GuardProfile->stateChangeProfile();
3571 void CGrpProfileGuardEscorted::updateProfile(uint ticksSinceLastUpdate)
3573 H_AUTO(GrpEscortedGuardProfileUpdate);
3574 CFollowPathContext fpcGrpEscortedGuardProfileUpdate("GrpEscortedGuardProfileUpdate");
3576 _GuardProfile->updateProfile(ticksSinceLastUpdate);
3577 if (!_EscortedProfile->isGroupFighting())
3578 _EscortedProfile->updateProfile(ticksSinceLastUpdate);
3581 std::string CGrpProfileGuardEscorted::getOneLineInfoString() const
3583 std::string info = "guard_escorted profile";
3584 info += " (";
3585 info += _GuardProfile?_GuardProfile->getOneLineInfoString():std::string("<no guard profile>");
3586 info += ") (";
3587 info += _EscortedProfile?_EscortedProfile->getOneLineInfoString():std::string("<no escorted profile>");
3588 info += ")";
3589 return info;
3592 //////////////////////////////////////////////////////////////////////////////
3593 // CGrpProfileSquad //
3594 //////////////////////////////////////////////////////////////////////////////
3596 CGrpProfileSquad::CGrpProfileSquad(CProfileOwner* const owner)
3597 : CGrpProfileFaction(owner)
3599 PROFILE_LOG("group", "squad", "ctor", "");
3602 CGrpProfileSquad::~CGrpProfileSquad()
3604 PROFILE_LOG("group", "squad", "dtor", "");
3607 void CGrpProfileSquad::beginProfile()
3609 PROFILE_LOG("group", "squad", "begin", "");
3610 CGrpProfileFaction::beginProfile();
3611 CGroupNpc& thisGrpNpc = _Grp->getPersistent();
3613 // Set aggro parameters
3614 thisGrpNpc._AggroRange = 25;
3615 thisGrpNpc._UpdateNbTicks = 10;
3618 string CGrpProfileSquad::getOneLineInfoString() const
3620 return "squad profile";
3623 void CGrpProfileSquad::aggroEntity(CAIEntityPhysical const* entity)
3625 // COutpost* outpost = getDefendedOutpost();
3626 // if (!outpost || outpost->getShape()->atPlace(CAIVector(entity->pos())))
3627 _Grp->setAggroMinimumFor(entity->dataSetRow(), 0.5f, false);
3630 NLMISC::CSmartPtr<CAIPlace const> CGrpProfileSquad::buildFirstHitPlace(TDataSetRow const& aggroBot)
3632 COutpost* outpost = getDefendedOutpost();
3633 if (outpost)
3634 return &*(outpost->getShape());
3635 else
3636 return NULL;
3639 void CGrpProfileSquad::aggroEntity(CAIEntityPhysical const* entity)
3641 COutpost* outpost = getDefendedOutpost();
3642 if (outpost)
3644 // Check that the (player) entity is in the outpost zone (according to the rules of the EGS with timers)
3645 CMirrorPropValueRO<TYPE_IN_OUTPOST_ZONE_ALIAS> entityInOutpostAlias(TheDataset, entity->dataSetRow(), DSPropertyIN_OUTPOST_ZONE_ALIAS);
3646 if (entityInOutpostAlias != outpost->getAlias())
3647 return;
3649 _Grp->setBotAggroMinimum(entity->dataSetRow(), -0.5f);
3652 //////////////////////////////////////////////////////////////////////////////
3653 // CGrpProfileFaction //
3654 //////////////////////////////////////////////////////////////////////////////
3656 CGrpProfileFaction::CGrpProfileFaction(CProfileOwner* const owner)
3657 : CGrpProfileNormal(owner)
3659 PROFILE_LOG("group", "faction", "ctor", "");
3660 bNoAssist = false;
3663 CGrpProfileFaction::~CGrpProfileFaction()
3665 PROFILE_LOG("group", "faction", "dtor", "");
3668 void CGrpProfileFaction::beginProfile()
3670 PROFILE_LOG("group", "faction", "begin", "");
3673 void CGrpProfileFaction::endProfile()
3675 PROFILE_LOG("group", "faction", "end", "");
3678 AITYPES::CPropertySet CGrpProfileFaction::_FameFactions;
3680 void CGrpProfileFaction::initFameFactions()
3682 static bool inited = false;
3683 if (!inited)
3685 inited = true;
3686 std::map<std::string, NLMISC::TStringId> factionTranslator;
3687 std::vector<NLMISC::TStringId> const& factionNames = CStaticFames::getInstance().getFactionNames();
3688 std::vector<NLMISC::TStringId>::const_iterator it, end=factionNames.end();
3689 for (it=factionNames.begin(); it!=end; ++it)
3691 std::string fameFaction = CStringMapper::unmap(*it);
3692 std::string scriptFaction = fameFactionToScriptFaction(fameFaction);
3693 factionTranslator.insert(std::make_pair(scriptFaction, *it));
3694 _FameFactions.addProperty(scriptFaction);
3696 _FameFactions.addProperty(CPropertyId("Player"));
3697 #ifdef NL_DEBUG_FACTION_NAME_CONVERTER
3698 nldebug("Testing script faction name converters");
3699 std::string names[][2] = {
3700 { "matis", "FamousMatis" },
3701 { "fyros", "FamousFyros" },
3702 { "tryker", "FamousTryker" },
3703 { "zorai", "FamousZorai" },
3704 { "kami", "FamousKami" },
3705 { "black_kami", "FamousBlackKami" },
3706 { "karavan", "FamousKaravan" },
3707 { "white_karavan", "FamousWhiteKaravan" },
3708 { "tribe_ancient_dryads", "FamousTribeAncientDryads" },
3709 { "tribe_antikamis", "FamousTribeAntikamis" },
3710 { "tribe_barkers", "FamousTribeBarkers" },
3711 { "tribe_beachcombers", "FamousTribeBeachcombers" },
3712 { "tribe_black_circle", "FamousTribeBlackCircle" },
3713 { "tribe_cholorogoos", "FamousTribeCholorogoos" },
3714 { "tribe_cockroaches", "FamousTribeCockroaches" },
3715 { "tribe_company_of_the_eternal_tree", "FamousTribeCompanyOfTheEternalTree" },
3716 { "tribe_corsair", "FamousTribeCorsair" },
3717 { "tribe_cute", "FamousTribeCute" },
3718 { "tribe_darkening_sap", "FamousTribeDarkeningSap" },
3719 { "tribe_dune_riders", "FamousTribeDuneRiders" },
3720 { "tribe_ecowarriors", "FamousTribeEcowarriors" },
3721 { "tribe_firebrands", "FamousTribeFirebrands" },
3722 { "tribe_first_deserter", "FamousTribeFirstDeserter" },
3723 { "tribe_frahar", "FamousTribeFrahar" },
3724 { "tribe_frahar_hunters", "FamousTribeFraharHunters" },
3725 { "tribe_gibbay", "FamousTribeGibbay" },
3726 { "tribe_goo_heads", "FamousTribeGooHeads" },
3727 { "tribe_green_seed", "FamousTribeGreenSeed" },
3728 { "tribe_hamazans_of_the_dead_seed", "FamousTribeHamazansOfTheDeadSeed" },
3729 { "tribe_icon_workshipers", "FamousTribeIconWorkshipers" },
3730 { "tribe_keepers", "FamousTribeKeepers" },
3731 { "tribe_kitin_gatheres", "FamousTribeKitinGatheres" },
3732 { "tribe_lagoon_brothers", "FamousTribeLagoonBrothers" },
3733 { "tribe_lawless", "FamousTribeLawless" },
3734 { "tribe_leviers", "FamousTribeLeviers" },
3735 { "tribe_master_of_the_goo", "FamousTribeMasterOfTheGoo" },
3736 { "tribe_matisian_border_guards", "FamousTribeMatisianBorderGuards" },
3737 { "tribe_night_turners", "FamousTribeNightTurners" },
3738 { "tribe_oasis_diggers", "FamousTribeOasisDiggers" },
3739 { "tribe_pyromancers", "FamousTribePyromancers" },
3740 { "tribe_recoverers", "FamousTribeRecoverers" },
3741 { "tribe_renegades", "FamousTribeRenegades" },
3742 { "tribe_restorers", "FamousTribeRestorers" },
3743 { "tribe_root_tappers", "FamousTribeRootTappers" },
3744 { "tribe_sacred_sap", "FamousTribeSacredSap" },
3745 { "tribe_sap_gleaners", "FamousTribeSapGleaners" },
3746 { "tribe_sap_slaves", "FamousTribeSapSlaves" },
3747 { "tribe_scorchers", "FamousTribeScorchers" },
3748 { "tribe_shadow_runners", "FamousTribeShadowRunners" },
3749 { "tribe_siblings_of_the_weeds", "FamousTribeSiblingsOfTheWeeds" },
3750 { "tribe_silt_sculptors", "FamousTribeSiltSculptors" },
3751 { "tribe_slavers", "FamousTribeSlavers" },
3752 { "tribe_smuglers", "FamousTribeSmuglers" },
3753 { "tribe_the_arid_matis", "FamousTribeTheAridMatis" },
3754 { "tribe_the_kuilde", "FamousTribeTheKuilde" },
3755 { "tribe_the_slash_and_burn", "FamousTribeTheSlashAndBurn" },
3756 { "tribe_tutors", "FamousTribeTutors" },
3757 { "tribe_water_breakers", "FamousTribeWaterBreakers" },
3758 { "tribe_woven_bridles", "FamousTribeWovenBridles" }
3760 size_t size = sizeof(names)/sizeof(names[0]);
3761 for (size_t i=0; i<size; ++i)
3763 nldebug("%s -> %s", names[i][0].c_str(), fameFactionToScriptFaction(names[i][0]).c_str());
3764 nlassert( names[i][1] == fameFactionToScriptFaction(names[i][0]) );
3765 nldebug("%s -> %s", names[i][1].c_str(), scriptFactionToFameFaction(names[i][1]).c_str());
3766 nlassert( names[i][0] == scriptFactionToFameFaction(names[i][1]) );
3768 #endif
3772 bool CGrpProfileFaction::entityHavePartOfFactions(CAIEntityPhysical const* entity, AITYPES::CPropertySetWithExtraList<TAllianceId> const& factions)
3774 H_AUTO(CGrpProfileFaction_entityHavePartOfFactions);
3775 switch (entity->getRyzomType())
3777 case RYZOMID::player:
3779 // Test Player
3780 if (factions.have(AITYPES::CPropertyId("Player")))
3782 // nldebug("Entity has faction Player");
3783 return true;
3785 // Test if the entity is involved in an outpost war
3786 if (entity->outpostAlias()!=0)
3788 if (factions.have(NLMISC::toString("outpost:%s:%s", LigoConfig.aliasToString(entity->outpostAlias()).c_str(), entity->outpostSide()?"attacker":"defender")))
3789 return true;
3791 // Test entity fame value
3792 std::set<TStringId> factionsSet = factions.properties();
3793 std::set<TStringId>::const_iterator it, end = factionsSet.end();
3794 for (it=factionsSet.begin(); it!=end; ++it)
3796 string factionInfos = CStringMapper::unmap(*it);
3797 string fameFaction = scriptFactionToFameFaction(factionInfos);
3798 // sint32 fame = CFameInterface::getInstance().getFameOrCivilisationFame(entity->getEntityId(), CStringMapper::map(fameFaction));
3799 sint32 const fame = entity->getFame(fameFaction);
3800 sint32 const value = scriptFactionToFameFactionValue(factionInfos);
3801 bool gt = scriptFactionToFameFactionGreaterThan(factionInfos);
3802 if ((fame != NO_FAME && gt && fame > value) ||
3803 (fame != NO_FAME && !gt && fame < value))
3805 // nldebug("Entity has faction %s", CStringMapper::unmap(*it).c_str());
3806 return true;
3809 return false;
3811 case RYZOMID::npc:
3813 CSpawnBotNpc const* const sbnEntity = NLMISC::safe_cast<CSpawnBotNpc const*>(entity);
3814 CGroupNpc *const gnEntityGroup = NLMISC::safe_cast<CGroupNpc*>(&sbnEntity->getPersistent().getGroup());
3815 return gnEntityGroup->faction().containsPartOfStrict(factions);
3817 case RYZOMID::creature:
3819 CSpawnBotFauna const* const sbnEntity = NLMISC::safe_cast<CSpawnBotFauna const*>(entity);
3820 CGrpFauna* const gnEntityGroup = NLMISC::safe_cast<CGrpFauna*>(&sbnEntity->getPersistent().getGroup());
3821 return gnEntityGroup->faction().containsPartOfStrict(factions);
3823 default:
3825 return false;
3830 std::string CGrpProfileFaction::scriptFactionToFameFaction(std::string name)
3832 if (name.find("Famous")!=0)
3833 return name;
3834 std::string ret = "";
3835 for (size_t i=6; i<name.length(); ++i)
3837 if (i==6 && name[i]>='A' && name[i]<='Z')
3838 ret += name[i]-'A'+'a';
3839 else if (name[i]>='A' && name[i]<='Z')
3841 ret += "_";
3842 ret += name[i]-'A'+'a';
3844 else if (name[i] == '>' || name[i] == '<')
3846 return ret;
3848 else
3850 ret += name[i];
3853 return ret;
3856 bool CGrpProfileFaction::scriptFactionToFameFactionGreaterThan(string name)
3858 if (name.find("<") != string::npos)
3859 return false;
3861 return true;
3864 sint32 CGrpProfileFaction::scriptFactionToFameFactionValue(string name)
3866 size_t start = name.find(">");
3867 if (start == string::npos)
3869 start = name.find("<");
3870 if (start == string::npos)
3871 return 0;
3874 sint32 value;
3875 NLMISC::fromString(name.substr(start+1), value);
3876 return value*6000;
3879 std::string CGrpProfileFaction::fameFactionToScriptFaction(std::string name)
3881 std::string ret = "Famous";
3882 for (size_t i=0; i<name.length(); ++i)
3884 if (i==0 && name[i]>='a' && name[i]<='z')
3885 ret += name[i]-'a'+'A';
3886 else if (name[i]=='_' && name[i+1]>='a' && name[i+1]<='z')
3887 ret += name[++i]-'a'+'A';
3888 else
3889 ret += name[i];
3891 return ret;
3894 void CGrpProfileFaction::aggroEntity(CAIEntityPhysical const* entity)
3896 _Grp->setAggroMinimumFor(entity->dataSetRow(), 0.5f, false);
3899 void CGrpProfileFaction::checkTargetsAround()
3901 if (!_checkTargetTimer.test())
3902 return;
3904 CGroupNpc& thisGrpNpc = _Grp->getPersistent();
3906 _checkTargetTimer.set(thisGrpNpc._UpdateNbTicks+CAIS::rand16(2)); // every _UpdateNbTicks+1 seconds.
3908 initFameFactions();
3909 CPropertySetWithExtraList<TAllianceId> const& thisFaction = thisGrpNpc.faction();
3910 CPropertySetWithExtraList<TAllianceId> const& thisFriendFactions = thisGrpNpc.friendFaction();
3911 CPropertySetWithExtraList<TAllianceId> const& thisEnnemyFactions = thisGrpNpc.ennemyFaction();
3913 // We don't assist or attack players if our friends/ennemies are not in factions
3914 bool const assistPlayers = (thisFriendFactions.containsPartOfStrictFilter("Famous*") || thisFriendFactions.have(AITYPES::CPropertyId("Player")));
3915 bool const assistBots = !thisFriendFactions.empty() && !bNoAssist;
3916 bool const attackPlayers = (!thisEnnemyFactions.extraSetEmpty()) || thisEnnemyFactions.containsPartOfStrictFilter("Famous*") || thisEnnemyFactions.have(AITYPES::CPropertyId("Player")) || thisEnnemyFactions.containsPartOfStrictFilter("outpost:*");
3917 bool const attackBots = !thisEnnemyFactions.empty();
3919 CAIVision<CPersistentOfPhysical> Vision;
3920 breakable
3922 CAIVector centerPos;
3923 if (!_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
3924 break;
3926 const uint32 playerRadius=assistPlayers||attackPlayers?thisGrpNpc._AggroRange:0;
3927 const uint32 botRadius=assistBots||attackBots?thisGrpNpc._AggroRange:0;
3930 for (CCont<CBot>::iterator itBot=_Grp->bots().begin(), itEnd=_Grp->bots().end(); itBot!=itEnd; ++itBot)
3932 uint32 cellValue = 0;
3933 CBot* bot = *itBot;
3934 if (bot)
3936 CSpawnBot* spawnBot = bot->getSpawnObj();
3937 if (spawnBot)
3939 CMirrorPropValueRO<uint32> cell( TheDataset, spawnBot->dataSetRow(), DSPropertyCELL );
3940 cellValue = cell();
3941 Vision.updateBotsAndPlayers(thisGrpNpc.getAIInstance(), centerPos, playerRadius, botRadius, cellValue);
3942 break;
3948 // Assist players
3949 if (assistPlayers)
3951 // For each player
3952 FOREACHC (itAssisted, std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >, Vision.players())
3954 // Get assisted entity
3955 CPersistentOfPhysical const* const popAssisted = (*itAssisted);
3956 CAIEntityPhysical* const epAssisted = popAssisted->getSpawnObj();
3957 // If entity is not alive skip it
3958 if (!epAssisted || !epAssisted->isAlive() || epAssisted->currentHitPoints()<=0.f)
3959 continue;
3960 // If entity is not a friend skip it
3961 if (!entityHavePartOfFactions(epAssisted, thisFriendFactions))
3962 continue;
3963 // For each targeter of the assisted entity
3964 CAIEntityPhysical const* epAttacker = epAssisted->firstTargeter();
3965 while (epAttacker)
3967 // If attacker is not in our faction attack him
3968 if (!entityHavePartOfFactions(epAttacker, thisFaction))
3969 aggroEntity(epAttacker);
3970 epAttacker = epAttacker->nextTargeter();
3974 // Assist bots
3975 if (assistBots)
3977 FOREACHC (itAssisted, std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >, Vision.bots())
3979 // Get assisted entity
3980 const CPersistentOfPhysical *const popAssisted = (*itAssisted);
3981 CAIEntityPhysical *const epAssisted = popAssisted->getSpawnObj();
3982 // If entity is not alive skip it
3983 if (!epAssisted || !epAssisted->isAlive() || epAssisted->currentHitPoints()<=0.f)
3984 continue;
3985 // If entity is not a npc skip it
3986 if (epAssisted->getRyzomType()!=RYZOMID::npc)
3987 continue;
3988 // If entity is not a friend skip it
3989 if (!entityHavePartOfFactions(epAssisted, thisFriendFactions))
3990 continue;
3991 // For each targeter of the assisted entity
3992 CAIEntityPhysical const* epAttacker = epAssisted->firstTargeter();
3993 while (epAttacker)
3995 // If attacker is not in our faction attack him
3996 if (!entityHavePartOfFactions(epAttacker, thisFaction))
3998 const CSpawnBot * spwBot = dynamic_cast<const CSpawnBot *>(epAssisted);
3999 if(spwBot)
4001 if (spwBot->haveAggroWithEntity(epAttacker->dataSetRow()))
4002 aggroEntity(epAttacker);
4005 epAttacker = epAttacker->nextTargeter();
4009 // Attack players
4010 if (attackPlayers)
4012 FOREACHC (itAttacked, TPersistentList, Vision.players())
4014 // Get attacked entity
4015 CPersistentOfPhysical const* const popAttacked = (*itAttacked);
4016 CAIEntityPhysical const* const epAttacked = popAttacked->getSpawnObj();
4017 // If entity is not alive skip it
4018 if (!epAttacked || !epAttacked->isAlive() || epAttacked->currentHitPoints()<=0.f)
4019 continue;
4020 // If entity is not an ennemy skip it
4021 if (!entityHavePartOfFactions(epAttacked, thisEnnemyFactions))
4022 continue;
4023 // If entity is a friend skip it
4024 if (entityHavePartOfFactions(epAttacked, thisFriendFactions))
4025 continue;
4026 // If entity is in our faction skip it
4027 if (entityHavePartOfFactions(epAttacked, thisFaction))
4028 continue;
4029 // If player is in safe zone skip it
4030 CRootCell const* const rootCell = epAttacked->wpos().getRootCell();
4031 if (rootCell && rootCell->getFlag()!=0) // Safe Zone ?
4032 continue;
4033 // Attack the rest
4034 aggroEntity(epAttacked);
4037 // Attack bots
4038 if (attackBots)
4040 FOREACHC (itAttacked, TPersistentList, Vision.bots())
4042 // Get attacked entity
4043 CPersistentOfPhysical const* const popAttacked = (*itAttacked);
4044 CAIEntityPhysical const* const epAttacked = popAttacked->getSpawnObj();
4045 // If entity is not alive skip it
4046 if (!epAttacked || !epAttacked->isAlive() || epAttacked->currentHitPoints()<=0.f)
4047 continue;
4048 // If entity is not an ennemy skip it
4049 if (!entityHavePartOfFactions(epAttacked, thisEnnemyFactions))
4050 continue;
4051 // If entity is a friend skip it
4052 if (entityHavePartOfFactions(epAttacked, thisFriendFactions))
4053 continue;
4054 // If entity is in our faction skip it
4055 if (entityHavePartOfFactions(epAttacked, thisFaction))
4056 continue;
4057 // Attack the rest
4058 aggroEntity(epAttacked);
4063 void CGrpProfileFaction::updateProfile(uint ticksSinceLastUpdate)
4065 checkTargetsAround();
4066 CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
4069 string CGrpProfileFaction::getOneLineInfoString() const
4071 return "faction profile";
4074 //////////////////////////////////////////////////////////////////////////////
4075 // Profile factories implementation //
4076 //////////////////////////////////////////////////////////////////////////////
4078 //- Singleton profiles (stateless ones) --------------------------------------
4080 CBotProfileStandAtPosFactory BotProfileStandAtPosFactory;
4081 CBotProfileForageFactory BotProfileForageFactory;
4082 CGrpProfileFightFactory GrpProfileFightFactory;
4084 //- Factory registering ------------------------------------------------------
4086 // CGrpProfileGuardFactory
4087 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileGuardFactory, "guard");
4089 // CGrpProfileTribuFactory
4090 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileTribuFactory, "tribu");
4092 // CGrpProfileIdleFactory
4093 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileIdleFactory, "idle");
4095 // CGrpProfileStandAtStartPointFactory
4096 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileStandAtStartPointFactory, "stand_on_start_point");
4098 // CGrpProfileEscortedFactory
4099 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileEscortedFactory, "escorted");
4101 // CGrpProfileGuardEscorted Factory
4102 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileGuardEscortedFactory, "guard_escorted");
4104 // CGrpProfileSquadFactory
4105 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileSquadFactory, "squad");
4107 // CGrpProfileFactionFactory
4108 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileFactionFactory, "faction");
4110 // CGrpProfileStandOnVerticesFactory
4111 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileStandOnVerticesFactory, "stand_on_vertices");
4113 // CGrpProfileWanderFactory
4114 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileWanderFactory, "wander");
4116 // CGrpProfileNormalFactory
4117 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileNormalFactory, "normal");
4119 // CGrpProfileFollowRouteFactory
4120 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileFollowRouteFactory, "follow_route");
4122 // CGrpProfileNoChangeFactory
4123 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileNoChangeFactory, "no_change");
4125 // CGrpProfileBanditFactory
4126 NLMISC::CSmartPtr<IAIProfile> CGrpProfileBanditFactory::createAIProfile(CProfileOwner* owner)
4128 LOG("bandit group profile factory create");
4129 static bool firstCall = true;
4130 if (firstCall)
4132 CConfigFile::CVar *v = IService::getInstance()->ConfigFile.getVarPtr("DefaultBanditAggroRange");
4133 if (v)
4134 _DefaultAggroRange = v->asFloat();
4135 else
4136 _DefaultAggroRange = 15.0f;
4139 return new CGrpProfileBandit(owner);
4141 float CGrpProfileBanditFactory::getDefaultBanditAggroRange()
4143 return _DefaultAggroRange;
4145 float CGrpProfileBanditFactory::_DefaultAggroRange;
4146 RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileBanditFactory, "bandit");
4148 // Global profile factory stuff. This should be put in some profile.cpp since it's common with fauna profiles.
4149 IAIProfileFactory* lookupAIGrpProfile(const char *name)
4151 IAIProfileFactory *ret = CAiFactoryIndirect<IAIProfileFactory, std::string>::instance().getFactory(std::string(name));
4152 if (!ret)
4153 nlwarning("Can't find activity '%s', returning NULL", name);
4154 return ret;
4156 RYAI_IMPLEMENT_FACTORY_INDIRECT(IAIProfileFactory, std::string);
4158 //***************************************************************************/
4159 /* Below that line is magical ununderstandable stuff */
4160 //***************************************************************************/
4162 float getDistBetWeen(CAIEntityPhysical& creat1, CAIEntityPhysical& creat2)
4164 // coz player position is not updated really 'goodly' as it can be in a invalid ai map position.
4165 float angTo = (creat1.pos().angleTo(creat2.pos())).asRadians();
4167 return creat1.getCollisionDist(angTo) + creat2.getCollisionDist(-angTo);
4170 //////////////////////////////////////////////////////////////////////////////
4171 // CBotProfileMoveTo //
4172 //////////////////////////////////////////////////////////////////////////////
4174 CBotProfileMoveTo::CBotProfileMoveTo(AITYPES::TVerticalPos verticalPos, RYAI_MAP_CRUNCH::CWorldPosition const& dest, CProfileOwner* owner)
4175 : CAIBaseProfile()
4176 , _VerticalPos(verticalPos)
4177 , _Dest(dest)
4178 , _PathCont(NLMISC::safe_cast<CSpawnBotNpc*>(owner)->getAStarFlag())
4179 , _PathPos(NLMISC::safe_cast<CSpawnBotNpc*>(owner)->theta())
4180 , _Bot(NLMISC::safe_cast<CSpawnBotNpc*>(owner))
4182 PROFILE_LOG("bot", "move_to", "ctor", "");
4183 #ifdef NL_DEBUG_PTR
4184 _Bot.setData(this);
4185 #endif
4186 #if !FINAL_VERSION
4187 nlassert(dest.x()!=0 || dest.y()!=0);
4188 #endif
4191 void CBotProfileMoveTo::beginProfile()
4193 PROFILE_LOG("bot", "move_to", "begin", "");
4194 _LastPos = CAIVector(_Bot->pos());
4195 _PathCont.setDestination(_VerticalPos, _Dest);
4196 _Status = CFollowPath::FOLLOWING;
4199 void CBotProfileMoveTo::endProfile()
4201 PROFILE_LOG("bot", "move_to", "end", "");
4204 void CBotProfileMoveTo::updateProfile(uint ticksSinceLastUpdate)
4206 H_AUTO(BotMoveToProfileUpdate);
4207 CFollowPathContext fpcBotMoveToProfileUpdate("BotMoveToProfileUpdate");
4209 if (!_Bot->canMove())
4210 return;
4212 if (_Status != CFollowPath::FOLLOW_ARRIVED)
4214 static const std::string runParameter("running");
4215 float dist;
4216 if (_Bot->getPersistent().getOwner()->getSpawnObj()->checkProfileParameter(runParameter))
4217 dist =_Bot->runSpeed()*ticksSinceLastUpdate;
4218 else
4219 dist =_Bot->walkSpeed()*ticksSinceLastUpdate;
4221 _Status = CFollowPath::getInstance()->followPath(
4222 _Bot,
4223 _PathPos,
4224 _PathCont,
4225 dist,
4226 0.f,
4227 .5f);
4228 if (_Status==CFollowPath::FOLLOW_NO_PATH)
4230 // get a base pointer to allow virtual call to work
4232 //nlwarning("Follow No Path : %s", _Bot->getPersistent().getOneLineInfoString().c_str());
4237 bool CBotProfileMoveTo::destinationReach() const
4239 return _Status == CFollowPath::FOLLOW_ARRIVED
4240 || _Status==CFollowPath::FOLLOW_NO_PATH;
4243 std::string CBotProfileMoveTo::getOneLineInfoString() const
4245 return "move_to bot profile";
4248 //////////////////////////////////////////////////////////////////////////////
4249 // CBotProfileFollowPos //
4250 //////////////////////////////////////////////////////////////////////////////
4252 CBotProfileFollowPos::CBotProfileFollowPos(CBotProfileFollowPos const& other)
4253 : CAIBaseProfile()
4254 , _PathPos(const_cast<CBotProfileFollowPos&>(other)._PathPos._Angle) // Just to debug...
4255 , _Bot(const_cast<CBotProfileFollowPos&>(other)._Bot)
4256 , _PathCont(const_cast<CBotProfileFollowPos&>(other)._PathCont)
4257 , _MaxWalkSpeed(FLT_MAX)
4258 , _MaxRunSpeed(FLT_MAX)
4259 , _Stop(false)
4261 PROFILE_LOG("bot", "follow_pos", "ctor", "");
4262 #ifdef NL_DEBUG_PTR
4263 _Bot.setData(this);
4264 #endif
4267 CBotProfileFollowPos::CBotProfileFollowPos(CPathCont* pathCont, CProfileOwner* owner)
4268 : CAIBaseProfile()
4269 , _PathPos(NLMISC::safe_cast<CSpawnBotNpc*>(owner)->theta())
4270 , _Bot(NLMISC::safe_cast<CSpawnBotNpc*>(owner))
4271 , _PathCont(pathCont)
4272 , _MaxWalkSpeed(FLT_MAX)
4273 , _MaxRunSpeed(FLT_MAX)
4274 , _Stop(false)
4276 PROFILE_LOG("bot", "follow_pos", "ctor", "");
4277 #ifdef NL_DEBUG
4278 nlassert(pathCont);
4279 #endif
4282 void CBotProfileFollowPos::beginProfile()
4284 PROFILE_LOG("bot", "follow_pos", "begin", "");
4285 _Status = CFollowPath::FOLLOWING;
4288 void CBotProfileFollowPos::endProfile()
4290 PROFILE_LOG("bot", "follow_pos", "end", "");
4293 void CBotProfileFollowPos::updateProfile(uint ticksSinceLastUpdate)
4295 H_AUTO(BotFollowPosProfileUpdate);
4296 CFollowPathContext fpcBotFollowPosProfileUpdate("BotFollowPosProfileUpdate");
4298 if (!_Bot->canMove() || _Stop)
4299 return;
4301 static const std::string runParameter("running");
4302 float dist;
4303 float speed;
4304 if (_Bot->getPersistent().getOwner()->getSpawnObj()->checkProfileParameter(runParameter))
4305 speed = std::min(_Bot->runSpeed(), _MaxRunSpeed);
4306 else
4307 speed = std::min(_Bot->walkSpeed(), _MaxWalkSpeed);
4309 dist = speed*ticksSinceLastUpdate;
4311 CPathCont &pathContRef=*_PathCont;
4312 if (_Status!=CFollowPath::FOLLOW_NO_PATH)
4314 _Status = CFollowPath::getInstance()->followPath(
4315 _Bot,
4316 _PathPos,
4317 pathContRef,
4318 dist,
4320 .5f);
4322 if (_Status==CFollowPath::FOLLOW_NO_PATH)
4324 // R2_PRIMITIVE_LAXITY
4325 if (!IsRingShard.get())
4327 nlwarning("Follow No Path for '%s'%s",
4328 _Bot->getPersistent().getAliasFullName().c_str(),
4329 _Bot->getPersistent().getAliasString().c_str());
4334 std::string CBotProfileFollowPos::getOneLineInfoString() const
4336 std::string info = "follow_pos bot profile";
4337 info += " stop=" + NLMISC::toString(_Stop);
4338 info += " max_walk_speed=" + NLMISC::toString(_MaxWalkSpeed);
4339 info += " max_run_speed=" + NLMISC::toString(_MaxRunSpeed);
4340 return info;
4343 void CBotProfileFollowPos::setMaxSpeeds(float walkSpeed, float runSpeed)
4345 _MaxWalkSpeed = walkSpeed;
4346 _MaxRunSpeed = runSpeed;
4349 void CBotProfileFollowPos::setStop(bool stop)
4351 _Stop = stop;
4354 //////////////////////////////////////////////////////////////////////////////
4355 // CBotProfileWanderBase //
4356 //////////////////////////////////////////////////////////////////////////////
4358 CBotProfileWanderBase::CBotProfileWanderBase()
4359 : CAIBaseProfile()
4361 PROFILE_LOG("bot", "wander_base", "ctor", "");
4364 void CBotProfileWanderBase::setTimer(uint32 ticks)
4366 _Timer.set(ticks);
4369 bool CBotProfileWanderBase::testTimer() const
4371 return _Timer.test();
4374 //////////////////////////////////////////////////////////////////////////////
4375 // CBotProfileStandAtPos //
4376 //////////////////////////////////////////////////////////////////////////////
4378 CBotProfileStandAtPos::CBotProfileStandAtPos(CProfileOwner* owner)
4379 : CBotProfileWanderBase()
4380 , _Bot(NLMISC::safe_cast<CSpawnBotNpc*>(owner))
4382 PROFILE_LOG("bot", "stand_at_pos", "ctor", "");
4383 #ifdef NL_DEBUG_PTR
4384 _Bot.setData(this);
4385 #endif
4388 void CBotProfileStandAtPos::beginProfile()
4390 PROFILE_LOG("bot", "stand_at_pos", "begin", "");
4393 void CBotProfileStandAtPos::endProfile()
4395 PROFILE_LOG("bot", "stand_at_pos", "end", "");
4398 void CBotProfileStandAtPos::updateProfile(uint ticksSinceLastUpdate)
4402 std::string CBotProfileStandAtPos::getOneLineInfoString() const
4404 return "stand_at_pos bot profile";
4407 //////////////////////////////////////////////////////////////////////////////
4408 // CBotProfileForage //
4409 //////////////////////////////////////////////////////////////////////////////
4411 CBotProfileForage::CBotProfileForage(CProfileOwner* owner)
4412 : CBotProfileWanderBase()
4413 , _Bot(NLMISC::safe_cast<CSpawnBotNpc*>(owner))
4414 , _TemporarySheetUsed(false)
4416 PROFILE_LOG("bot", "forage", "ctor", "");
4417 #ifdef NL_DEBUG_PTR
4418 _Bot.setData(this);
4419 #endif
4420 _OldSheet = 0;
4423 CBotProfileForage::~CBotProfileForage()
4425 PROFILE_LOG("bot", "forage", "dtor", "");
4426 #ifdef NL_DEBUG
4427 nlassert(_TemporarySheetUsed==false);
4428 #endif
4429 if (_TemporarySheetUsed)
4431 setOldSheet(); // if this leads to a virtual pure call somewhere,
4432 // try to ask profileOwner to flush its profile in Bot dtor,
4433 // which is a proper way to delete objects.
4437 void CBotProfileForage::beginProfile()
4439 PROFILE_LOG("bot", "forage", "begin", "");
4440 // begin first forage in 3-10 second
4441 _ForageTimer.set(30+CAIS::rand32(70));
4443 static const NLMISC::CSheetId forageTool("itforage.sitem");
4445 AISHEETS::ICreature *cs = new AISHEETS::CCreature(*_Bot->getPersistent().getSheet());
4446 _OldSheet = _Bot->getPersistent().getSheet();
4447 cs->LeftItem = NLMISC::CSheetId();
4448 cs->RightItem = forageTool;
4449 _Bot->getPersistent().setSheet(cs);
4450 _Bot->getPersistent().sendVPA();
4451 _TemporarySheetUsed = true;
4453 nlerror("This profile has been broken (above block commented), it shouldn't be used");
4455 // begin propecting
4456 _Bot->setBehaviour(MBEHAV::PROSPECTING);
4459 void CBotProfileForage::endProfile()
4461 PROFILE_LOG("bot", "forage", "end", "");
4462 // stop foraging if needed
4463 if (_Bot->getBehaviour() == MBEHAV::EXTRACTING)
4464 _Bot->setBehaviour(MBEHAV::EXTRACTING_END);
4465 else if (_Bot->getBehaviour() == MBEHAV::PROSPECTING)
4466 _Bot->setBehaviour(MBEHAV::PROSPECTING_END);
4468 setOldSheet();
4470 nlerror("This profile has been broken (above block commented), it shouldn't be used");
4473 void CBotProfileForage::updateProfile(uint ticksSinceLastUpdate)
4475 H_AUTO(BotForageProfileUpdate);
4476 CFollowPathContext fpcBotForageProfileUpdate("BotForageProfileUpdate");
4478 if (_ForageTimer.test())
4480 // somethink to do :)
4481 if (_Bot->getBehaviour() != MBEHAV::EXTRACTING)
4483 // do a little turn
4484 CAngle a = _Bot->theta();
4485 CAngle newAngle(CAIS::randPlusMinus(CAngle::PI/4));
4486 _Bot->setTheta(a+newAngle);
4488 // begin foraging
4489 _Bot->setBehaviour(MBEHAV::EXTRACTING);
4491 // forage for 10 to 15 sec
4492 _ForageTimer.set(100+CAIS::rand32(50));
4494 else if (_Bot->getBehaviour() == MBEHAV::EXTRACTING)
4496 // end foraging
4497 _Bot->setBehaviour(MBEHAV::PROSPECTING);
4499 // wait 20-30s before next forage
4500 _ForageTimer.set(250+CAIS::rand32(50));
4505 void CBotProfileForage::setOldSheet()
4507 AISHEETS::ICreatureCPtr cs = _Bot->getPersistent().getSheet();
4508 _Bot->getPersistent().setSheet(_OldSheet);
4509 _Bot->getPersistent().sendVPA();
4510 _TemporarySheetUsed = false;
4511 // delete const_cast<AISHEETS::ICreature*>(cs);
4512 nlerror("This profile has been broken (above line commented), it shouldn't be used");
4515 std::string CBotProfileForage::getOneLineInfoString() const
4517 return "forage bot profile";
4520 //////////////////////////////////////////////////////////////////////////////
4521 // CGrpProfileNormal //
4522 //////////////////////////////////////////////////////////////////////////////
4524 CGrpProfileNormal::CGrpProfileNormal(CProfileOwner* owner)
4525 : _Grp(NLMISC::safe_cast<CSpawnGroupNpc*>(owner))
4526 //, _GroupFighting(false) // :KLUDGE: Replaced by a bug simulation
4528 PROFILE_LOG("group", "normal", "ctor", "");
4529 // This should be part of init list, but can't do conditionnal init
4530 if (simulateBug(7))
4532 /* Following statement was missing in initialization list. */
4534 else
4536 _GroupFighting = false;
4540 bool CGrpProfileNormal::isGroupFighting() const
4542 return _GroupFighting;
4545 void CGrpProfileNormal::setGroupFighting(bool groupFighting)
4547 if (_GroupFighting!=groupFighting)
4549 _GroupFighting=groupFighting;
4551 CGroupNpc &grp=_Grp->getPersistent();
4552 if (groupFighting)
4553 grp.processStateEvent(grp.getEventContainer().EventGroupBeginFight);
4554 else
4555 grp.processStateEvent(grp.getEventContainer().EventGroupEndFight);
4559 //////////////////////////////////////////////////////////////////////////////
4560 // CSlaveProfile //
4561 //////////////////////////////////////////////////////////////////////////////
4563 CSlaveProfile::CSlaveProfile(CProfileOwner* owner)
4564 : _Grp(NLMISC::safe_cast<CSpawnGroupNpc*>(owner))
4566 PROFILE_LOG("base", "slave", "ctor", "");
4569 void CSlaveProfile::beginProfile()
4571 PROFILE_LOG("base", "slave", "begin", "");
4572 FOREACH(itBot, CCont<CBot>, _Grp->bots())
4574 CBot* bot = *itBot;
4575 CSpawnBot* spawnBot = bot->getSpawnObj();
4576 if (!spawnBot || !spawnBot->isAlive())
4577 continue;
4578 addBot(bot);
4582 void CSlaveProfile::updateProfile(uint ticksSinceLastUpdate)
4586 //////////////////////////////////////////////////////////////////////////////
4587 // CMoveProfile //
4588 //////////////////////////////////////////////////////////////////////////////
4590 CMoveProfile::CMoveProfile(CProfileOwner* owner)
4591 : CSlaveProfile(owner)
4592 , _MaxRunSpeed(FLT_MAX)
4593 , _MaxWalkSpeed(FLT_MAX)
4595 PROFILE_LOG("base", "move", "ctor", "");
4598 void CMoveProfile::resumeBot(CBot const* bot)
4600 CSpawnBot* spawnBot = bot->getSpawnObj();
4601 if (!spawnBot)
4602 return;
4604 CPathCont* pathCont = getPathCont(bot);
4605 if (!pathCont)
4606 return;
4608 spawnBot->setAIProfile(new CBotProfileFollowPos(pathCont, spawnBot));
4611 //////////////////////////////////////////////////////////////////////////////
4612 // CFightProfile //
4613 //////////////////////////////////////////////////////////////////////////////
4615 CFightProfile::CFightProfile(CProfileOwner* owner)
4616 : CSlaveProfile(owner)
4618 PROFILE_LOG("base", "fight", "ctor", "");
4621 //////////////////////////////////////////////////////////////////////////////
4622 // CGrpProfileFollowRoute //
4623 //////////////////////////////////////////////////////////////////////////////
4625 CGrpProfileFollowRoute::~CGrpProfileFollowRoute()
4627 PROFILE_LOG("group", "follow_route", "dtor", "");
4628 FOREACH(itBot, CCont<CBot>, _Grp->bots())
4629 removeBot(*itBot);
4632 CGrpProfileFollowRoute::CBotFollower::CBotFollower()
4633 : _BotAtDest(false)
4637 CGrpProfileFollowRoute::CBotFollower::~CBotFollower()
4641 void CGrpProfileFollowRoute::CBotFollower::setBotAtDest(bool atDest)
4643 _BotAtDest = atDest;
4646 const bool& CGrpProfileFollowRoute::CBotFollower::isBotAtDest() const
4648 return _BotAtDest;
4651 bool CGrpProfileFollowRoute::getDirection()
4653 return _FollowForward;
4657 CPathCont* CGrpProfileFollowRoute::getPathCont(CBot const* bot)
4659 return &_PathCont;
4662 std::string CGrpProfileFollowRoute::getOneLineInfoString() const
4664 std::string info = "follow_route group profile";
4665 info += " stop_npc=" + NLMISC::toString(_StopNpc);
4666 return info;
4669 bool CGrpProfileFollowRoute::profileTerminated() const
4671 return _ProfileTerminated;
4674 void CGrpProfileFollowRoute::stopNpc(bool stop)
4676 _StopNpc = stop;
4679 //////////////////////////////////////////////////////////////////////////////
4680 // CGrpProfileStandOnVertices //
4681 //////////////////////////////////////////////////////////////////////////////
4683 CGrpProfileStandOnVertices::CBotPositionner::CBotPositionner(RYAI_MAP_CRUNCH::TAStarFlag flags)
4684 : _PathCont(flags)
4688 CGrpProfileStandOnVertices::CBotPositionner::CBotPositionner(uint32 geomIndex, RYAI_MAP_CRUNCH::TAStarFlag flags)
4689 : _PathCont(flags)
4690 , _GeomIndex(geomIndex)
4691 , _BotAtDest(false)
4695 void CGrpProfileStandOnVertices::CBotPositionner::setBotAtDest(bool atDest)
4697 _BotAtDest=atDest;
4700 bool CGrpProfileStandOnVertices::CBotPositionner::isBotAtDest() const
4702 return _BotAtDest;
4705 CGrpProfileStandOnVertices::CGrpProfileStandOnVertices(CProfileOwner* owner)
4706 : CMoveProfile(owner)
4707 , _DenyFlags(NLMISC::safe_cast<CSpawnGroup*>(owner)->getPersistent().getAStarFlag())
4709 PROFILE_LOG("group", "stand_on_vertices", "ctor", "");
4712 CGrpProfileStandOnVertices::~CGrpProfileStandOnVertices()
4714 PROFILE_LOG("group", "stand_on_vertices", "dtor", "");
4715 for (CCont<CBot >::iterator it=_Grp->bots().begin(), itEnd=_Grp->bots().end();it!=itEnd;++it)
4717 removeBot(*it);
4721 void CGrpProfileStandOnVertices::endProfile()
4723 PROFILE_LOG("group", "stand_on_vertices", "end", "");
4726 std::string CGrpProfileStandOnVertices::getOneLineInfoString() const
4728 std::string info = "stand_on_vertices group profile";
4729 info += " finished=" + NLMISC::toString(_Finished);
4730 return info;
4734 //- Complex profile factories ------------------------------------------------
4736 // CBotProfileMoveToFactory (with specialization)
4737 typedef CAIGenericProfileFactory<CBotProfileMoveTo> CBotProfileMoveToFactory;
4738 template <>
4739 NLMISC::CSmartPtr<IAIProfile> CAIGenericProfileFactory<CBotProfileMoveTo>::createAIProfile(CProfileOwner* owner)
4741 nlerror("This profile factory (CBotProfileMoveToFactory) can't be used");
4742 return NULL;
4745 // CBotProfileFollowPosFactory (with specialization)
4746 typedef CAIGenericProfileFactory<CBotProfileFollowPos> CBotProfileFollowPosFactory;
4747 template <>
4748 NLMISC::CSmartPtr<IAIProfile> CAIGenericProfileFactory<CBotProfileFollowPos>::createAIProfile(CProfileOwner* owner)
4750 nlerror("This profile factory (CBotProfileFollowPosFactory) can't be used");
4751 return NULL;
4754 #include "event_reaction_include.h"