Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / family_profile_tribe.cpp
blob292a090101a98640c2a1c41672e70214d099b8a5
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/>.
19 #include "stdpch.h"
20 #include "nel/misc/smart_ptr.h"
21 #include "family_profile.h"
22 #include "continent.h"
23 #include "ai_grp_fauna.h"
24 #include "ai_grp_npc.h"
25 #include "ai_mgr_fauna.h"
26 #include "ai_mgr_npc.h"
27 #include "group_profile.h"
28 #include "family_behavior.h"
29 #include "family_profile_tribe.h"
31 #include "continent_inline.h"
33 using namespace std;
34 using namespace NLMISC;
35 using namespace AITYPES;
36 using namespace RYAI_MAP_CRUNCH;
38 CAiFactory<IFamilyProfile, CFamilyProfileTribe> _singleProfileTribe;
39 IAiFactory<IFamilyProfile> *_ProfileTribe=&_singleProfileTribe;
43 //CPropertyId act_fz_spawn("activity_fauna_spawn");
45 //CPropertyId act_fz_food_herb("activity_fauna_food_herb");
46 //CPropertyId act_fz_food_carn("activity_fauna_food_carn");
47 //CPropertyId act_fz_rest_herb("activity_fauna_rest_herb");
48 //CPropertyId act_fz_rest_carn("activity_fauna_rest_carn");
50 //CPropertyId act_nz_harvest("activity_npc_harvest");
51 //CPropertyId act_nz_ambush("activity_npc_ambush");
52 //CPropertyId act_nz_rest("activity_npc_rest");
53 //CPropertyId act_nz_outpost("activity_npc_outpost");
54 //CPropertyId act_nz_spawn("activity_npc_spawn");
55 //CPropertyId act_nz_outpost_def("activity_npc_outpost_def");
56 //CPropertyId act_nz_outpost_atk("activity_npc_outpost_atk");
57 //CPropertyId act_nz_kami_wander("activity_npc_kami_wander");
58 //CPropertyId act_nz_escort("activity_npc_escort");
59 //CPropertyId act_nz_convoy("activity_npc_convoy");
60 //CPropertyId act_nz_contact("activity_npc_contact");
61 //CPropertyId act_nz_fight("activity_npc_fight");
63 //CPropertyId act_nz_contact_camp("activity_npc_contact_camp");
64 //CPropertyId act_nz_contact_outpost("activity_npc_contact_outpost");
65 //CPropertyId act_nz_contact_city("activity_npc_contact_city");
66 //// CPropertyId act_nz_contact_city("activity_npc_fight_boss"); problem ?
67 //CPropertyId act_nz_fight_boss("act_nz_fight_boss");
69 //CPropertyId act_fz_food_kitin("act_fz_food_kitin");
70 //CPropertyId act_fz_food_kitin_invasion("act_fz_food_kitin_invasion");
71 //CPropertyId act_fz_rest_kitin_invasion("act_fz_rest_kitin_invasion");
72 //CPropertyId act_fz_food_degen("act_fz_food_degen");
73 //CPropertyId act_fz_plant("act_fz_plant");
74 //CPropertyId act_fz_rest_kitin("act_fz_rest_kitin");
75 //CPropertyId act_fz_rest_degen("act_fz_rest_degen");
77 // Todo:
79 // +faire gaffe à l'init du CGrpProfileFollowRoute dans le cadre statique .. :\ (adapter?)//
80 // (done)+ dynCamping n'a pas une bonne current zone, -> à mettre.
81 // (done)+ problème d'ordre des points pour le suivit de chemin.
82 // + vérifier que les comportements statiques fonctionnent toujours avec le nouveau code (régression).
84 // - Choisir le comportement au spawn des groupes.
85 // - Mettre des activités dans les événements d'OutPosts. (pas de chianlie..).
86 // - Vérifer la logique des comportements (pas de trous).
87 // - Vérifier que currentZone est bien géré dans les classes.
89 // - Death report / OutPosts.
90 // - getGeometry (forced by the unconceptualized finite state machine script implementation legacy in the leveldesign :( ).
92 // - mettre un coup de boule dans le mur pour se calmer.
94 void COutpostInfo::checkDespawnGroupList ()
97 while (_DespawnList.size()>0)
99 CGroupNpc *grpNpc=_DespawnList.back();
100 _DespawnList.pop_back();
103 TGroupList::iterator it=_FightGroup.begin();
104 const TGroupList::iterator itEnd=_FightGroup.end();
106 for (;(it!=itEnd) && ((*it).ptr()!=grpNpc);++it);
108 if (it!=itEnd)
110 (*it)->despawnGrp();
111 _FightGroup.erase(it);
112 continue;
118 TGroupList::iterator it=_ContactGroups.begin();
119 const TGroupList::iterator itEnd=_ContactGroups.end();
121 for (;(it!=itEnd) && ((*it).ptr()!=grpNpc);++it);
123 if (it!=itEnd)
125 (*it)->despawnGrp();
126 _ContactGroups.erase(it);
127 continue;
131 #ifdef NL_DEBUG
132 nlassert(true==false); // unknown group.
133 #endif
138 // Not Implemented.
139 void COutpostInfo::spawnBoss ()
143 void COutpostInfo::outpostEvent (ZCSTATE::TZcState state)
145 if (_ZoneNpc.isNULL())
146 return;
148 _State=state;
150 // sets group presence in the outpost.
151 switch (_State)
153 case ZCSTATE::Tribe: // no charge
154 fightGroups(false);
155 bossGroups(false);
156 contactGroups(true);
157 break;
159 case ZCSTATE::TribeInWar: // tribe got the outpost, tribe is in war
160 case ZCSTATE::GuildInWar: // Guild got the outpost, Guild is in war
161 fightGroups(true);
162 bossGroups(true);
163 contactGroups(false);
164 break;
166 case ZCSTATE::GuildInPeace: // Guild got the outpost, Guild is in peace
167 fightGroups(false);
168 bossGroups(false);
169 contactGroups(false);
170 break;
175 void COutpostInfo::fightGroups(bool exist)
177 return;
178 // TODO
179 // if (exist)
180 // {
181 // if (_FightGroupExist) // if no groups, spawn them ..
182 // return;
184 // CCellZone &cellZone=_FamilyBehavior->getOwner();
186 // for (uint i=0; i<10; ++i)
187 // {
188 // // look for a fight group
189 // const CGroupDesc *const gd = cellZone.getOwner()->getProportionalGroupDesc(_FamilyBehavior, act_nz_fight, act_nz_fight_boss);
190 // if (!gd)
191 // continue;
193 // // set the npc of the group attackable
194 // gd->setAttackable(true);
196 // // look for a spawn point
197 // const CNpcZone *const spawnZone = cellZone.lookupNpcZone(/*_FamilyBehavior->getFamily(),*/ act_nz_spawn);
198 // if (!spawnZone)
199 // continue;
201 // // Look for a defense zone
202 // const CNpcZone *const defZone = cellZone.lookupNpcZone(/*_FamilyBehavior->getFamily(),*/ act_nz_outpost_def);
203 // if (!defZone)
204 // continue;
206 // CGroupNpc *const grp = _FamilyProfileTribe->createNpcGroup(spawnZone,gd);
207 // if (!grp)
208 // continue;
210 // grp->setDiscardable (false);
211 // // this group will run !
212 // grp->mergeProfileParameter (CProfileParameters::TProfileParameter("running", "", 0));
214 // CGrpProfileDynFight *dynFight=new CGrpProfileDynFight(grp->getSpawnObj(),_FamilyProfileTribe, spawnZone, this);
215 // grp->getSpawnObj()->activityProfile().setAIProfile (dynFight);
216 // dynFight->gotoZone(defZone, act_nz_outpost_atk+act_nz_contact_camp);
217 // _FightGroup.push_back(grp);
218 // }
220 // }
221 // else
222 // {
223 // if (!_FightGroupExist)
224 // return;
226 // for (TGroupList::iterator it=_FightGroup.begin(), itEnd=_FightGroup.end();it!=itEnd;++it)
227 // {
228 // NLMISC::CDbgPtr<CGroupNpc> &dbgPtr=*it;
229 //#ifdef NL_DEBUG
230 // nlassert(!dbgPtr.isNULL());
231 //#endif
232 // CGrpProfileDynFight *dynFight=safe_cast<CGrpProfileDynFight*>(dbgPtr->getSpawnObj()->activityProfile().getAIProfile());
233 // dynFight->gotoZone(_FamilyProfileTribe->getCampZone(),CPropertySet());
234 // }
236 // }
237 // _FightGroupExist=exist;
241 void COutpostInfo::bossGroups(bool exist) // not implemented yet.
243 _BossGroupExist=exist;
246 void COutpostInfo::contactGroups(bool exist) // contact groups ..
248 // TODO
249 // if (exist)
250 // {
251 // if (_ContactGroupExist)
252 // return;
254 // // try to create a contact group
255 // breakable
256 // {
257 // const CGroupDesc *gd = _FamilyBehavior->getOwner()->getOwner()->getProportionalGroupDesc(_FamilyBehavior, act_nz_contact+act_nz_contact_camp, act_nz_escort+act_nz_convoy+act_nz_fight_boss);
258 // if (!gd)
259 // break;
261 // CGroupNpc *const grp=_FamilyProfileTribe->createNpcGroup(_FamilyProfileTribe->getCampZone(),gd);
262 // if (!grp)
263 // break;
265 // grp->getSpawnObj()->activityProfile().setAIProfile(new CGrpProfileDynContact(grp->getSpawnObj(), _FamilyProfileTribe, this, true));
266 // _ContactGroups.push_back(grp);
267 // }
269 // // 2 other contacts
270 // for (uint32 i=0;i<2;i++)
271 // {
272 // // try to create a contact group
273 // const CGroupDesc *gd = _FamilyBehavior->getOwner()->getOwner()->getProportionalGroupDesc(_FamilyBehavior, act_nz_contact+act_nz_contact_outpost, act_nz_escort+act_nz_convoy);
274 // if (!gd)
275 // continue;
277 // CGroupNpc *const grp=_FamilyProfileTribe->createNpcGroup(_FamilyProfileTribe->getCampZone(),gd);
278 // if (!grp)
279 // continue;
281 // grp->getSpawnObj()->activityProfile().setAIProfile(new CGrpProfileDynContact(grp->getSpawnObj(), _FamilyProfileTribe, this));
282 // _ContactGroups.push_back(grp);
284 // }
286 // }
287 // else
288 // {
289 // if (!_ContactGroupExist)
290 // return;
292 // for (TGroupList::iterator it=_ContactGroups.begin(), itEnd=_ContactGroups.end();it!=itEnd;++it)
293 // {
294 // NLMISC::CDbgPtr<CGroupNpc> &dbgPtr=*it;
295 // CGrpProfileDynFight *dynFight=safe_cast<CGrpProfileDynFight*>(dbgPtr->getSpawnObj()->activityProfile().getAIProfile());
296 // dynFight->gotoZone(_FamilyProfileTribe->getCampZone(),CPropertySet());
297 // }
299 // }
300 // _ContactGroupExist=exist;
304 void COutpostInfo::updateOutPostInfo ()
306 checkDespawnGroupList ();
311 CFamilyProfileTribe::CFamilyProfileTribe (const CtorParam &ctorParam)
312 :IFamilyProfile(ctorParam)
314 // choose a camp zone.
315 // need to choose a camp zone
316 static CPropertyId act_nz_rest("activity_rest");
317 _CampZone = _FamilyBehavior->getOwner()->lookupNpcZone(_FamilyBehavior->getFamilyTag()+act_nz_rest, _FamilyBehavior->grpFamily()->getSubstitutionId());
320 // get params.
321 const NLMISC::CVirtualRefCount*const param=ctorParam.familyBehavior()->grpFamily()->getProfileParams("aggro_groups");
322 const CAggroGroupContainer *const aggroGroupContainer=type_cast<const CAggroGroupContainer*>(param);
323 if (aggroGroupContainer)
324 _AggroGroupIds=aggroGroupContainer->aggroGroupIds;
329 void CFamilyProfileTribe::setDefaultProfile(const CNpcZone *const zone, CGroupNpc *grp)
331 #ifdef NL_DEBUG
332 nlassert(grp);
333 #endif
334 static CPropertyId act_nz_harvest("activity_harvest");
335 CSpawnGroupNpc *const spawnGrp=grp->getSpawnObj();
336 const CNpcZone *const dest = getFamilyBehavior()->getOwner()->lookupNpcZone(act_nz_harvest, getFamilyBehavior()->grpFamily()->getSubstitutionId());
337 if ( !dest
338 || dest==zone)
340 spawnGrp->activityProfile().setAIProfile(new CGrpProfileDynWaitInZone(spawnGrp,zone));
341 return;
343 spawnGrp->activityProfile().setAIProfile(new CGrpProfileDynHarvest(spawnGrp,this,dest,zone));
346 void CFamilyProfileTribe::outpostAdd(NLMISC::TStringId outpostName)
348 if (_OutpostInfos.find(outpostName) != _OutpostInfos.end())
350 return;
353 if (LogOutpostDebug)
354 nldebug("OUTPOST: adding outpost '%s' to tribe '%s' control in '%s'",
355 CStringMapper::unmap(outpostName).c_str(),
356 _FamilyBehavior->getName().c_str(),
357 _FamilyBehavior->getOwner()->getAliasFullName().c_str());
360 CSmartPtr<COutpostInfo> outPost=COutpostInfo::createOutpost(_FamilyBehavior,this,outpostName);
361 if (!outPost)
363 static map<CFamilyProfileTribe*, set<TStringId> > warnOnce;
364 if (warnOnce[this].find(outpostName) == warnOnce[this].end())
366 nlwarning("OUTPOST: no zone found for outpost '%s'", CStringMapper::unmap(outpostName).c_str());
367 warnOnce[this].insert(outpostName);
369 return;
371 _OutpostInfos.insert(make_pair(outpostName, outPost));
374 void CFamilyProfileTribe::outpostRemove(NLMISC::TStringId outpostName)
376 TOutpostContainer::iterator it(_OutpostInfos.find(outpostName));
377 if (it==_OutpostInfos.end())
378 return;
380 // nlassert(it != _OutpostInfos.end());
382 if (LogOutpostDebug)
383 nldebug("OUTPOST: Removing outpost '%s' to tribe 'ss' control in '%s'",
384 CStringMapper::unmap(outpostName).c_str(),
385 /*_FamilyBehavior->getFamily().getFamilyName().c_str(),*/
386 _FamilyBehavior->getOwner()->getAliasFullName().c_str());
388 _OutpostInfos.erase(it);
391 void CFamilyProfileTribe::spawnBoss(NLMISC::TStringId outpostName)
393 TOutpostContainer::iterator it(_OutpostInfos.find(outpostName));
394 if (it==_OutpostInfos.end())
395 return;
397 // nlassert(it != _OutpostInfos.end());
398 it->second->spawnBoss();
401 void CFamilyProfileTribe::spawnGroup()
403 H_AUTO(FamilySpawnTribe)
404 static CPropertyId act_nz_spawn("activity_spawn");
406 if (getCampZone().isNull())
407 return;
409 static CPropertyId act_nz_rest("activity_rest");
410 const CNpcZone *const spawn = _FamilyBehavior->getOwner()->lookupNpcZone(/*_FamilyBehavior->getFamily(),*/ act_nz_spawn+act_nz_rest, _FamilyBehavior->grpFamily()->getSubstitutionId());
411 if (!spawn)
412 return;
415 static CPropertyId act_nz_escort("activity_escort");
416 static CPropertyId act_nz_contact("activity_contact");
417 static CPropertyId act_nz_fight_boss("act_nz_fight_boss");
419 // CCellZone &cellZone=_FamilyBehavior->getOwner();
420 const CGroupDesc<CGroupFamily> *const gd = _FamilyBehavior->grpFamily()->getProportionalGroupDesc(_FamilyBehavior, CPropertySet(), act_nz_contact+act_nz_fight_boss+act_nz_escort);
422 if (!gd)
423 return;
425 // set the npc of the group attackable
426 // gd->setAttackable(true);
427 // set the npc of the group vulnerable
428 // gd->setVulnerable(true);
430 const CGroupNpc *const grp=createNpcGroup(spawn,gd);
432 if (!grp)
433 return;
435 grp->getSpawnObj()->spawnBotOfGroup();
439 /// The main update for the profile. Called aprox every 10 s (100 ticks)
440 void CFamilyProfileTribe::update()
442 H_AUTO(FamilyTribeUpdate)
444 // update outposts
445 TOutpostContainer::iterator first(_OutpostInfos.begin()), last(_OutpostInfos.end());
446 for (; first != last; ++first)
447 first->second->updateOutPostInfo();
451 /************************************************************************/
452 /* Profiles */
453 /************************************************************************/
455 //---------------------------------------------------------------------------------
456 // CGrpProfileDynContact
457 //---------------------------------------------------------------------------------
459 void CGrpProfileDynContact::beginProfile()
461 static CPropertyId act_nz_rest("activity_rest");
462 static CPropertyId act_nz_outpost("activity_outpost");
463 _Grp->movingProfile().setAIProfile(new CGrpProfileDynFollowPath(_Grp, _CurrentZone, _FamilyProfile->getCampZone(), act_nz_outpost + act_nz_rest));
466 // routine called just before bot starts to use a new profile or when a bot dies
467 void CGrpProfileDynContact::endProfile()
471 // routine called every time the bot is updated (frequency depends on player proximity, etc)
473 // there's a coherence problem about the activity.
474 void CGrpProfileDynContact::updateProfile(uint ticksSinceLastUpdate)
477 // this is a contact group, special treatment
478 switch (_Grp->movingProfile().getAIProfileType())
480 case ZONE_WAIT:
481 // we must be in a city or in an outpost, just check the date to return to camp.
482 if (CTimeInterface::timeOfDay()!=CRyzomTime::nightfall)
483 break;
485 // go back to the camp.
486 CGrpProfileDynWaitInZone const* const waitProfile = safe_cast<CGrpProfileDynWaitInZone*>(_Grp->movingProfile().getAIProfile());
487 _Grp->movingProfile().setAIProfile(new CGrpProfileDynFollowPath(_Grp, waitProfile->currentZone(), _FamilyProfile->getCampZone(), CPropertySet()));
489 break;
491 case MOVE_CAMPING:
492 // the group is camping, if the day rise, send them to an outpost or city
493 if (CTimeInterface::timeOfDay()!=CRyzomTime::dawn)
494 break;
497 static CPropertyId act_nz_outpost("activity_outpost");
498 // time to go work childrens!
499 const CNpcZone *dest = _FamilyProfile->getFamilyBehavior()->getOwner()->lookupNpcZone(/*_FamilyProfile->getFamilyBehavior()->getFamily(),*/ act_nz_outpost, _FamilyProfile->getFamilyBehavior()->grpFamily()->getSubstitutionId());
500 if (dest)
502 CGrpProfileDynCamping const* const campingProfile = safe_cast<CGrpProfileDynCamping*>(_Grp->movingProfile().getAIProfile());
503 _Grp->movingProfile().setAIProfile(new CGrpProfileDynFollowPath(_Grp, campingProfile->currentZone(), dest, CPropertySet()));
506 break;
508 case MOVE_DYN_FOLLOW_PATH:
510 CGrpProfileDynFollowPath* profile = NLMISC::safe_cast<CGrpProfileDynFollowPath*>(_Grp->movingProfile().getAIProfile());
512 // if arrived.
513 if (!profile->destinationReach())
514 break;
516 if (!_OutPostInfo->_ContactGroupExist)
518 _OutPostInfo->addToDespawnGroupList(_Grp);
519 return;
522 // ok, we are at the road end, what should we do now ?
523 if (_isTheContactGroup)
524 _Grp->activityProfile().setAIProfile(new CGrpProfileDynCamping(_Grp,profile->currentZone()));
525 else
526 _Grp->activityProfile().setAIProfile(new CGrpProfileDynWaitInZone(_Grp,profile->currentZone()));
528 break;
529 default:
530 #ifdef NL_DEBUG
531 nlassert(true==false);
532 #endif
533 break;
536 CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
539 void CGrpProfileDynContact::gotoZone(const CNpcZone *const zone, const CPropertySet &flags)
541 _Grp->movingProfile().setAIProfile(new CGrpProfileDynFollowPath(_Grp, _CurrentZone, zone, flags));
544 string CGrpProfileDynContact::buildDebugString() const
546 return string();
549 //---------------------------------------------------------------------------------
550 // CGrpProfileDynHarvest
551 //---------------------------------------------------------------------------------
554 void CGrpProfileDynHarvest::beginProfile()
556 static CPropertyId act_nz_rest("activity_rest");
557 static CPropertyId act_nz_harvest("activity_harvest");
558 static CPropertyId act_nz_outpost("activity_outpost");
559 _Grp->movingProfile().setAIProfile(new CGrpProfileDynFollowPath(_Grp, _CurrentZone, _HarvestZone, act_nz_outpost + act_nz_rest));
562 // routine called just before bot starts to use a new profile or when a bot dies
563 void CGrpProfileDynHarvest::endProfile()
567 // routine called every time the bot is updated (frequency depends on player proximity, etc)
570 void CGrpProfileDynHarvest::checkTargetsAround ()
572 if (!_checkTargetTimer.test())
573 return;
575 _checkTargetTimer.set(10+CAIS::rand16(2)); // every 11 seconds.
577 if (_FamilyProfile->_AggroGroupIds.size()==0)
578 return;
580 // check if we have a player property.
582 const std::vector<uint32> &aggroList=_FamilyProfile->_AggroGroupIds;
583 if (std::find(aggroList.begin(), aggroList.end(), AISHEETS::CSheets::getInstance()->playerGroupIndex())==aggroList.end())
584 return;
587 CAIVision<CPersistentOfPhysical> Vision;
589 breakable
591 CAIVector centerPos;
592 if (!_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
593 break;
595 const uint32 playerRadius=30; // _AggroRange);
596 const uint32 botRadius=0; // _AggroRange);
598 const uint32 minRadius=playerRadius>botRadius?botRadius:playerRadius;
600 Vision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), centerPos, playerRadius, botRadius);
604 const std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> > &players = Vision.players();
606 std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::const_iterator first(players.begin()), last(players.end());
607 for (; first != last; ++first)
609 const CPersistentOfPhysical*const player = (*first);
610 const CAIEntityPhysical*const ep = player->getSpawnObj();
611 if ( !ep
612 || !ep->isAlive()
613 || ep->currentHitPoints()<=0.f )
614 continue;
616 const CRootCell *const rootCell=ep->wpos().getRootCell();
617 if ( rootCell
618 && rootCell->getFlag()!=0 ) // Safe Zone ?
619 continue;
621 _Grp->setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
626 // there's a coherence problem about the activity.
627 void CGrpProfileDynHarvest::updateProfile(uint ticksSinceLastUpdate)
629 static CPropertyId act_nz_harvest("activity_harvest");
630 CProfilePtr &movingProfile=_Grp->movingProfile();
632 // this is a contact group, special treatment
633 switch (movingProfile.getAIProfileType())
635 // Camping -> Moving(Wandering(Harvesting))
636 case MOVE_CAMPING:
638 // the group is resting in the camp, check activity timeout
639 CGrpProfileDynCamping const* const profile = safe_cast<CGrpProfileDynCamping*>(movingProfile.getAIProfile());
640 if (!profile->timeOut())
641 break;
643 // send the group to a harvest point
644 const CNpcZone *const dest = _FamilyProfile->getFamilyBehavior()->getOwner()->lookupNpcZone(/*_FamilyProfile->getFamilyBehavior()->getFamily(),*/ act_nz_harvest, _FamilyProfile->getFamilyBehavior()->grpFamily()->getSubstitutionId());
646 if (dest==profile->currentZone()) // are we already at the right place ?
648 // then lets camp again ..
649 _Grp->movingProfile().setAIProfile(new CGrpProfileDynCamping(_Grp,dest));
651 else
653 // send the group to an harvest site.
654 movingProfile.setAIProfile(new CGrpProfileDynFollowPath(_Grp, profile->currentZone(), dest, CPropertySet()));
656 break;
658 break;
660 // Moving -> Wandering(Harvesting)|Camping
661 case MOVE_DYN_FOLLOW_PATH:
663 CGrpProfileDynFollowPath const* const fp = safe_cast<CGrpProfileDynFollowPath*>(movingProfile.getAIProfile());
665 if (!fp->destinationReach())
666 break;
668 if (fp->currentZone()==_FamilyProfile->getCampZone())
670 // the group just enter the camp, do a camping for some time
671 _Grp->movingProfile().setAIProfile(new CGrpProfileDynCamping(_Grp,fp->currentZone()));
672 break;
675 // ok the group has reach a destination that is not the camp ..
676 // lets forage ..
677 // set the group in wander/forage mode (with zone in param)
678 CGrpProfileWander *profile=new CGrpProfileWander(_Grp,fp->currentZone());
679 _Grp->movingProfile().setAIProfile(profile);
681 // do some foraging
682 profile->setBotStandProfile(BOT_FORAGE, &BotProfileForageFactory);
683 profile->setTimer(60*10+CAIS::rand32(30*10)); // between 1 to 1:30 minute long ..
685 break;
687 // Harvesting -> Moving(Camping)
688 case MOVE_WANDER:
690 // this is a harvest group doing foraging
691 const CGrpProfileWander * const profile=safe_cast<CGrpProfileWander*>(movingProfile.getAIProfile());
692 if (!profile->testTimer())
693 break;
695 // send the group to a harvest point
696 const CNpcZone *dest=NULL;
698 while ( !dest
699 || dest==profile->currentZone())
701 if (CAIS::rand32(9)==0)
702 dest=_FamilyProfile->getCampZone();
703 else
704 dest=_FamilyProfile->getFamilyBehavior()->getOwner()->lookupNpcZone(/*_FamilyProfile->getFamilyBehavior()->getFamily(),*/ act_nz_harvest, _FamilyProfile->getFamilyBehavior()->grpFamily()->getSubstitutionId());
707 // send the group to the camp.
708 movingProfile.setAIProfile(new CGrpProfileDynFollowPath(_Grp, profile->currentZone(), dest, CPropertySet()));
710 break;
712 default: // don't think we have to be there ..
713 #ifdef NL_DEBUG
714 nlassert(true==false);
715 #endif
716 break;
718 checkTargetsAround ();
719 CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
722 string CGrpProfileDynHarvest::buildDebugString() const
724 return string();
729 //---------------------------------------------------------------------------------
730 // CGrpProfileDynFight
731 //---------------------------------------------------------------------------------
733 void CGrpProfileDynFight::beginProfile()
735 static CPropertyId act_nz_rest("activity_rest");
736 static CPropertyId act_nz_harvest("activity_harvest");
737 static CPropertyId act_nz_outpost("activity_outpost");
739 const CNpcZone *const dest = _FamilyProfile->getFamilyBehavior()->getOwner()->lookupNpcZone(/*_FamilyProfile->getFamilyBehavior()->getFamily(),*/ act_nz_harvest, _FamilyProfile->getFamilyBehavior()->grpFamily()->getSubstitutionId());
740 _Grp->movingProfile().setAIProfile(new CGrpProfileDynFollowPath(_Grp, _CurrentZone, dest, act_nz_outpost + act_nz_rest + act_nz_harvest));
743 // routine called just before bot starts to use a new profile or when a bot dies
744 void CGrpProfileDynFight::endProfile()
748 // routine called every time the bot is updated (frequency depends on player proximity, etc)
750 // there's a coherence problem about the activity.
751 void CGrpProfileDynFight::updateProfile(uint ticksSinceLastUpdate)
753 static CPropertyId act_nz_outpost("activity_outpost");
754 CProfilePtr &movingProfile=_Grp->movingProfile();
756 // this is a contact group, special treatment
757 switch (movingProfile.getAIProfileType())
759 // Camping -> Moving(Wandering(Fighting))
760 case MOVE_CAMPING:
762 // the group is resting in the camp, check activity timeout
763 CGrpProfileDynCamping const* const profile = safe_cast<CGrpProfileDynCamping*>(movingProfile.getAIProfile());
764 #ifdef NL_DEBUG
765 nlassert(_CurrentZone==profile->currentZone());
766 #endif
767 if (!profile->timeOut())
768 break;
770 // send the group to a harvest point
771 const CNpcZone *const dest = _FamilyProfile->getFamilyBehavior()->getOwner()->lookupNpcZone(/*_FamilyProfile->getFamilyBehavior()->getFamily(),*/ act_nz_outpost, _FamilyProfile->getFamilyBehavior()->grpFamily()->getSubstitutionId());
772 // send the group to an harvest site.
773 movingProfile.setAIProfile(new CGrpProfileDynFollowPath(_Grp, profile->currentZone(), dest, CPropertySet()));
774 break;
776 break;
778 // Moving -> Wandering(Fighting)|Camping
779 case MOVE_DYN_FOLLOW_PATH:
781 CGrpProfileDynFollowPath const* const fp = safe_cast<CGrpProfileDynFollowPath*>(movingProfile.getAIProfile());
783 _CurrentZone=fp->currentZone();
785 if (!fp->destinationReach())
786 break;
788 if (!_OutPostInfo->_FightGroupExist)
790 _OutPostInfo->addToDespawnGroupList(_Grp);
791 return;
794 if (fp->currentZone()==_FamilyProfile->getCampZone())
796 // the group just enter the camp, do a camping for some time
797 _Grp->movingProfile().setAIProfile(new CGrpProfileDynCamping(_Grp,fp->currentZone()));
798 break;
801 // ok the group has reach a destination that is not the camp ..
802 // lets forage ..
803 // set the group in wander/forage mode (with zone in param)
804 CGrpProfileWander* profile = new CGrpProfileWander(_Grp,fp->currentZone());
805 _Grp->movingProfile().setAIProfile(profile);
807 profile->setTimer(30*60*10+CAIS::rand32(15*50*10));
809 break;
811 // Fighting -> Moving(Camping)
812 case MOVE_WANDER:
814 // this is a harvest group doing foraging
815 CGrpProfileWander const* const profile = safe_cast<CGrpProfileWander*>(movingProfile.getAIProfile());
816 #ifdef NL_DEBUG
817 nlassert(_CurrentZone==profile->currentZone());
818 #endif
820 if (!profile->testTimer())
821 break;
823 // send the group to the camp.
824 movingProfile.setAIProfile(new CGrpProfileDynFollowPath(_Grp, profile->currentZone(), _FamilyProfile->getCampZone(), CPropertySet()));
826 break;
828 default: // don't think we have to be there ..
829 #ifdef NL_DEBUG
830 nlassert(true==false);
831 #endif
832 break;
834 CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
838 void CGrpProfileDynFight::gotoZone(const CNpcZone *const zone, const CPropertySet &flags)
840 _Grp->movingProfile().setAIProfile(new CGrpProfileDynFollowPath(_Grp, _CurrentZone, zone, flags));
843 string CGrpProfileDynFight::buildDebugString() const
845 return string();