spawnBots accept now name and vpx, addedspawnGroup_ffsssffff_c
[ryzomcore.git] / ryzom / server / src / ai_service / ai_grp_fauna.cpp
blobd3e5f8c8ddfd1cf4fc78d23ac0e5431d1cf52387
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 "ai_grp_fauna.h"
19 #include "ai_mgr_fauna.h"
21 #include "continent_inline.h"
22 #include "dyn_grp_inline.h"
24 using namespace MULTI_LINE_FORMATER;
26 using namespace NLMISC;
27 using namespace RYAI_MAP_CRUNCH;
28 using namespace AITYPES;
30 static char const* stateName(CSpawnGroupFauna::TState s)
32 switch (s)
34 case CSpawnGroupFauna::StateDespawned: return "DESPAWNED";
35 case CSpawnGroupFauna::StateSpawning: return "SPAWNING";
36 case CSpawnGroupFauna::StateGrazing: return "GRAZING";
37 case CSpawnGroupFauna::StateWandering: return "WANDERING";
38 case CSpawnGroupFauna::StateResting: return "RESTING";
39 default:
40 break;
42 return "UNKNOWN STATE";
46 // helper : get a fauna xyr zone from a base zone or a zone reference
47 static inline const CFaunaGenericPlace *getFaunaGenericPlace(const CAIPlace *place)
49 const CFaunaGenericPlace *faunaPlace = dynamic_cast<const CFaunaGenericPlace *>(place);
50 nlassert(faunaPlace);
51 return faunaPlace;
54 //////////////////////////////////////////////////////////////////////////////
55 // CGrpFauna static data //
56 //////////////////////////////////////////////////////////////////////////////
58 CGrpFauna::CCycleDef const CGrpFauna::cycles[] =
60 { CSpawnGroupFauna::StateSpawning, CGrpFauna::SPAWN_TIME, CGrpFauna::SPAWN_PLACE },
61 { CSpawnGroupFauna::StateGrazing, CGrpFauna::EAT_TIME, CGrpFauna::EAT_PLACE },
62 { CSpawnGroupFauna::StateResting, CGrpFauna::REST_TIME, CGrpFauna::REST_PLACE }
65 uint32 const CGrpFauna::nbCycle = sizeof(CGrpFauna::CCycleDef) / sizeof(CGrpFauna::cycles);
67 //////////////////////////////////////////////////////////////////////////////
68 // CSpawnGroupFauna //
69 //////////////////////////////////////////////////////////////////////////////
71 CSpawnGroupFauna::CSpawnGroupFauna(CPersistent<CSpawnGroup>& owner, RYAI_MAP_CRUNCH::TAStarFlag denyFlag)
72 : CSpawnGroup(owner)
73 , _DespawnImmediately(false)
74 , _PathCont(denyFlag)
76 // variables for CAIMgrFauna's update prioritisation system
77 _UpdatePriority = 0; // 0..15 - priority class (distance based - 0 is highest priority)
79 _Leader = (CBotFauna*)NULL;
81 _Timer.set(0);
83 // pick a spawn place
84 sint spawnPlace = getPersistent().getNextPlace(NULL, CAIPlaceXYRFauna::FLAG_SPAWN);
85 if (spawnPlace == CGrpFauna::INVALID_PLACE)
87 std::vector<uint> candidates;
88 // seek place with the smallest index
89 sint minIndex = INT_MAX;
90 for (uint k = 0; k < getPersistent().places().size(); ++k)
92 const CFaunaGenericPlace *place = getFaunaGenericPlace(getPersistent().places()[k]);
93 minIndex = std::min((sint) place->getIndex(), minIndex);
95 for (uint k = 0; k < getPersistent().places().size(); ++k)
97 const CFaunaGenericPlace *place = getFaunaGenericPlace(getPersistent().places()[k]);
98 if ((sint) place->getIndex() == minIndex)
100 candidates.push_back(k);
103 spawnPlace = (sint) candidates[rand() % candidates.size()];
104 //nlwarning("No spawn place found for group %s, using place with smallest index", getPersistent().getName().c_str());
106 setPlace(spawnPlace);
107 _CenterPos = _TargetPlace->midPos();
109 // make sure memory's been allocated for the bots
110 if (bots().size()==0)
111 getPersistent().allocateBots();
113 if (bots().size()!=0)
115 uint32 const spawnTimer = getPersistent().timer(CGrpFauna::SPAWN_TIME);
116 _Timer.set(spawnTimer);
119 // setup the update priority system variables
120 _LastUpdate = CTimeInterface::gameCycle();
121 _DeltaTime = 1;
123 _CurrentCycle = 0;
125 setMustDespawnBots(false);
128 CSpawnGroupFauna& CSpawnBotFauna::spawnGrp()
130 return static_cast<CSpawnGroupFauna&>(CSpawnBot::spawnGrp());
133 void CSpawnGroupFauna::despawnGrp() // critical code (despawn 'this' object).
135 CGrpFauna* faunaGrpPtr = &getPersistent();
136 faunaGrpPtr->mgr().addToSpawn(faunaGrpPtr);
137 faunaGrpPtr->despawnGrp();
140 void CSpawnGroupFauna::recalcUpdatePriorityDeltaAndGroupPos()
142 // recalculate the priority delta score for the group based on players in vision
143 bool speedUpdate = false;
145 sint32 grpPosx = 0;
146 sint32 grpPosy = 0;
147 sint32 nbBots = 0;
149 FOREACH(it, CCont<CBot>, bots())
151 CBotFauna *bot=NLMISC::safe_cast<CBotFauna*>(*it);
152 CSpawnBotFauna *botFauna=bot->getSpawn();
153 if (botFauna)
155 if (botFauna->isAlive())
157 const CAIPosMirror &pos=botFauna->pos();
158 grpPosx+=(sint32)(pos.x().asInt()*(1.0/1000.0));
159 grpPosy+=(sint32)(pos.y().asInt()*(1.0/1000.0));
160 nbBots++;
163 if (botFauna->havePlayersAround())
165 speedUpdate=true;
166 break;
173 if (nbBots>0)
175 _CenterPos = CAIVector(grpPosx/nbBots,grpPosy/nbBots);
178 if (speedUpdate)
179 _UpdatePriority = 1;
180 else
181 _UpdatePriority = 31;
183 // if players are approaching then crop our move time
184 uint32 curTime = CTimeInterface::gameCycle ();
185 if (((sint32)(curTime-_LastUpdate))>(_UpdatePriority+1))
186 _LastUpdate = curTime-(_UpdatePriority+1);
188 _DeltaTime = curTime-_LastUpdate;
191 CBotFauna* CSpawnGroupFauna::findLeader()
193 CBotFauna* possibleLeader = NULL;
195 CCont<CBot >::iterator it = bots().begin();
196 CCont<CBot >::iterator itEnd = bots().end();
197 while (it!=itEnd)
199 CBotFauna* botPtr = static_cast<CBotFauna*>(*it);
200 if (botPtr->isSpawned()) // if bot is spawned
202 if (botPtr->getSpawnObj()->isAlive()) // is alive
204 possibleLeader=botPtr;
205 if (_TargetPlace->atPlace(possibleLeader->getSpawn()->pos())) // and eventually in place
206 return possibleLeader;
209 ++it;
211 return possibleLeader;
214 void CSpawnGroupFauna::update()
216 H_AUTO(GrpFaunaUpdate);
218 ++AISStat::GrpTotalUpdCtr;
219 ++AISStat::GrpFaunaUpdCtr;
221 getPersistent().updateStateInstance();
223 if (_CurrentCycle==std::numeric_limits<uint32>::max())
224 return;
226 // Respawn
227 // breakable
229 H_AUTO(GrpFaunaUpdateDealWithDead);
230 checkDespawn ();
232 if (nbBotToRespawn()>0)
234 if (nbSpawnedBot()>0) // (getPersistent().bots().size()/2))
236 if (getPersistent().timeAllowSpawn ())
238 checkRespawn();
241 else
243 // critical code (despawn 'this' object).
244 despawnGrp();
245 return; // forced becoz the group is despawn and respawned (will be updated next time).
250 // recalculate our priority rating in function of distance from player
251 // if players are approaching then crop our move time
252 recalcUpdatePriorityDeltaAndGroupPos();
254 // identify the leader, call type-dependent update and calculate group position and radius
256 H_AUTO(GrpFaunaUpdateByType);
258 H_TIME(GrpFaunaUpdateFindLeader, _Leader = findLeader(););
259 // locate the first live bot and treat them as the group leader
261 // update the state variable (think about changing state)
262 H_TIME(GrpFaunaUpdateCheckTimer, checkTimers(););
264 H_TIME(GrpFaunaUpdateGeneralUpdate, generalUpdate(););
266 // re-find leader as it could have been despawn ..
267 _Leader = findLeader();
269 // record the current tick as last update time for the group
270 _LastUpdate = CTimeInterface::gameCycle();
273 void CSpawnGroupFauna::generalUpdate(TState state)
275 H_TIME(GrpFaunaReorganize, reorganize(bots().begin(), bots().end()););
278 H_AUTO(GrpFaunaUpdDespawnTest);
280 if ( !mustDespawnBots()
281 && getUpdatePriority ()>(2<<3)
282 && !getPersistent().timeAllowSpawn() ) // 40*3 -> more than 120 meters far from players
284 setMustDespawnBots (true);
288 if (state==StateUndefined)
289 state=CGrpFauna::cycles[_CurrentCycle]._Activity;
291 // call a type-dependent update
292 switch (getPersistent().getType())
294 case FaunaTypeHerbivore:
295 case FaunaTypePredator:
297 H_AUTO(GrpFaunaUpdNonPlant);
298 switch (state) // updateAnimals.
300 case StateDespawned: break;
301 case StateSpawning: updateSpawning(); break;
302 case StateGrazing: updateActivity(ACTIVITY_GRAZING); break;
303 case StateWandering: updateActivity(ACTIVITY_WANDERING); break;
304 case StateResting: updateActivity(ACTIVITY_RESTING); break;
305 default: nlwarning("CSpawnGroupFauna::updateAnimals FAILED because state not valid: %d",state);
308 break;
310 case FaunaTypePlant:
312 H_AUTO(GrpFaunaUpdPlant);
313 // run the state behaviour code
314 switch (state) // updatePlants.
316 case StateDespawned: break;
317 case StateSpawning: updateSpawning(); break;
318 case StateGrazing: updateActivity(ACTIVITY_PLANTIDLE); break;
319 case StateWandering: updateActivity(ACTIVITY_PLANTIDLE); break;
320 case StateResting: updateActivity(ACTIVITY_PLANTIDLE); break;
321 default: nlwarning("CSpawnGroupFauna::updatePlants FAILED because state not valid: %d",state);
324 break;
326 default: nlwarning("CSpawnGroupFauna::update() FAILED because group type not valid: %d",getPersistent().getType());
330 bool CSpawnGroupFauna::isSpawning()
332 return CGrpFauna::cycles[_CurrentCycle]._Activity == StateSpawning;
335 void CSpawnGroupFauna::updateSpawning()
337 // check if we have some bots to spawn to reach timer (proportionnaly).
338 if ( (!mustDespawnBots())
339 && ( ( _Timer.timeRemaining()==0
340 && nbSpawnedBot()<bots().size())
341 || ( _Timer.totalTime()!=0
342 && nbSpawnedBot()<((bots().size()*_Timer.timeSinceStart())/_Timer.totalTime()))
345 uint i,j;
346 uint targetCount=0;
347 #ifdef NL_DEBUG
348 nlassert(getPersistent().populations()[getPersistent()._CurPopulation]);
349 #endif
350 CPopulation& curPop=*getPersistent().populations()[getPersistent()._CurPopulation];
352 for (i=0;i<curPop.size();++i)
353 targetCount+=curPop[i].getBotCount(getPersistent().getCountMultiplierFlag());
355 // if no more bots to spawn then change state...
356 if (bots()[targetCount-1]->isSpawned())
358 incCurrentCycle();
359 generalUpdate();
360 return;
363 // identify the bot to spawn
364 uint count=0;
366 CBot* faunaPt=NULL;
367 for (i=0;i<curPop.size();++i)
369 CPopulationRecord &popRecord=curPop[i];
370 for (j=0;j<popRecord.getBotCount(getPersistent().getCountMultiplierFlag());++j,++count)
372 if (!bots()[count]->isSpawned())
373 break;
375 if (j<popRecord.getBotCount(getPersistent().getCountMultiplierFlag()))
376 break;
383 // by definition there must be a bot type
384 nlassert(i<curPop.size());
386 // spawn the bot
388 CBotFauna *faunaPt=NLMISC::safe_cast<CBotFauna*>(bots()[count]);
390 faunaPt->setSheet (/*const_cast<AISHEETS::ICreature *>(*/curPop[i].getCreatureSheet()/*)*/);
391 faunaPt->reSpawn ();
394 // if this is the first bot to be spawned in the group then st up the leader pointer to point to it
395 // and set the group type
396 if (count==0)
398 _Leader = findLeader();
402 // to all intent and purpose consider that our bots are wandering
403 generalUpdate(StateWandering);
406 void CSpawnGroupFauna::incCurrentCycle()
408 setCurrentCycle(_CurrentCycle + 1);
411 void CSpawnGroupFauna::setCurrentCycle(uint32 cycle)
413 if (getPersistent().places().isEmpty())
415 nlwarning("No places in fauna group %s", getPersistent().getName().c_str());
416 return;
418 // did we start a new cycle ?
419 if (cycle>=(sizeof(CGrpFauna::cycles)/sizeof(CGrpFauna::CCycleDef)))
420 cycle=1;
422 const CFaunaGenericPlace *targetPlacePtr = getFaunaGenericPlace(targetPlace());
424 sint nextPlace = CGrpFauna::INVALID_PLACE;
425 // search a place that match current cycle
426 // First we search a neighbour place that has wanted activity
427 // otherwise we change activity to the one we found
428 switch(CGrpFauna::cycles[cycle]._Place)
430 case CGrpFauna::EAT_PLACE:
431 nextPlace = getPersistent().getNextPlace(targetPlacePtr, CAIPlaceXYRFauna::FLAG_EAT);
432 if (nextPlace == CGrpFauna::INVALID_PLACE)
434 nextPlace = getPersistent().getNextPlace(targetPlacePtr, CAIPlaceXYRFauna::FLAG_REST);
435 if (nextPlace != CGrpFauna::INVALID_PLACE)
437 cycle = CGrpFauna::REST_PLACE; // force to rest
439 else
441 nextPlace = targetPlace()->getChildIndex();
442 // remains in the same place
443 if (!targetPlacePtr->getFlag(CAIPlaceXYRFauna::FLAG_EAT))
445 // can't eat there, so force to rest
446 cycle = CGrpFauna::REST_PLACE; // force to rest
450 break;
451 case CGrpFauna::REST_PLACE:
452 nextPlace = getPersistent().getNextPlace(targetPlacePtr, CAIPlaceXYRFauna::FLAG_REST);
453 if (nextPlace == CGrpFauna::INVALID_PLACE)
455 nextPlace = getPersistent().getNextPlace(targetPlacePtr, CAIPlaceXYRFauna::FLAG_EAT);
456 if (nextPlace != CGrpFauna::INVALID_PLACE)
458 cycle = CGrpFauna::EAT_PLACE; // force to eat
460 else
462 nextPlace = targetPlace()->getChildIndex();
463 // remains in the same place
464 if (!targetPlacePtr->getFlag(CAIPlaceXYRFauna::FLAG_REST))
466 // can't rest there, so force to eat
467 cycle = CGrpFauna::EAT_PLACE; // force to rest
471 break;
473 _CurrentCycle = cycle;
474 setPlace(nextPlace);
476 if (_CurrentCycle==CGrpFauna::EAT_PLACE)
478 for (CCont<CBot >::iterator it=bots().begin(), itEnd=bots().end();it!=itEnd;++it)
480 CBotFauna *const faunaBot=NLMISC::safe_cast<CBotFauna*>(*it);
481 CSpawnBotFauna *const faunaSpawnBot= faunaBot->getSpawn();
482 if (!faunaSpawnBot)
483 continue;
485 faunaSpawnBot->hungry()=faunaSpawnBot->radius();
490 void CSpawnGroupFauna::updateActivity(TProfiles activity)
492 FOREACH(it, CCont<CBot>, bots())
494 CBotFauna* bot=NLMISC::safe_cast<CBotFauna*>(*it);
495 if (bot->isSpawned())
496 bot->getSpawn()->update(activity,getDt());
500 void CSpawnGroupFauna::checkTimers()
502 if (CGrpFauna::cycles[_CurrentCycle]._Activity==StateSpawning)
503 return;
505 if (!_ArrivedInZone) // if we are changing the current activity zone.
507 if ( !_Leader.isNULL()
508 && _Leader->isSpawned())
510 const CAIPos leaderPos(_Leader->getSpawn()->pos());
511 const CAIPos midPos=_TargetPlace->midPos(); // better is very possible.
513 if (leaderPos.distTo(midPos)<_TargetPlace->getRadius()) // si leader dans la zone.
515 _ArrivedInZone=true; // we desactivate the boolean.
516 const CFaunaGenericPlace *faunaPlace = getFaunaGenericPlace(targetPlace());
517 NLMISC::CRandom rnd;
518 uint32 stayTime = faunaPlace->getMinStayTime() + (sint32) (rnd.frand() * ((sint32) faunaPlace->getMaxStayTime() - (sint32) faunaPlace->getMinStayTime()));
519 _Timer.set(stayTime);
521 nlwarning("Group %s : Setting stay time to %d in place %s with index %d",
522 getPersistent().getName().c_str(),
523 (int) stayTime,
524 faunaPlace->getName().c_str(),
525 (int) faunaPlace->getIndex());
533 else
535 // nlwarning("Group %s : Timer = %d", getPersistent().getName().c_str(), (int) (100 * _Timer.timeRemaining() / _Timer.totalTime()));
536 if (_Timer.test()) // si fin de timer.
537 incCurrentCycle();
541 void CSpawnGroupFauna::spawnBots()
543 setMustDespawnBots(false);
544 _CurrentCycle = 0; // first activity == spawning.
545 nlassert(bots().size()>0);
546 _Timer.set(getPersistent().timer(CGrpFauna::SPAWN_TIME));
549 void CSpawnGroupFauna::spawnBots(const std::string &name)
554 void CSpawnGroupFauna::spawnBots(const std::string &name, const std::string &vpx)
559 void CSpawnGroupFauna::despawnBots(bool immediately)
561 setDespawnImmediately(immediately);
562 setMustDespawnBots();
565 CAIPos const& CSpawnGroupFauna::magnetPos() const
567 return targetPlace()->midPos();
570 float CSpawnGroupFauna::magnetRadiusNear() const
572 return targetPlace()->getRadius()*(7.0f/8.0f);
575 float CSpawnGroupFauna::magnetRadiusFar() const
577 return targetPlace()->getRadius()*(9.0f/8.0f);
580 CGrpFauna& CSpawnGroupFauna::getPersistent()
582 return static_cast<CGrpFauna&>(CSpawnGroup::getPersistent());
585 uint32 CSpawnGroupFauna::getCurrentCycleTime()
587 return getPersistent().timer(CGrpFauna::cycles[_CurrentCycle]._Time);
590 //////////////////////////////////////////////////////////////////////////////
591 // CGrpFauna //
592 //////////////////////////////////////////////////////////////////////////////
595 uint32 CGrpFauna::refTimer(TTime time)
597 switch(time)
599 case EAT_TIME: return 250;
600 case REST_TIME: return 250;
601 case SPAWN_TIME: return 30;
602 case CORPSE_TIME: return 120;
603 case RESPAWN_TIME: return 45; // "Backward" compatibility: 45 seconds after the corpse is despawned (after corpse time, or when looted)
604 default:
605 nlassert(0);
606 break;
608 return 0;
611 CGrpFauna::CGrpFauna(CMgrFauna* mgr, CAIAliasDescriptionNode* aliasTree, RYAI_MAP_CRUNCH::TAStarFlag denyFlags)
612 : CGroup(mgr, denyFlags, aliasTree)
613 , CDynGrpBase()
614 , CPersistentStateInstance(*mgr->getStateMachine())
617 // state
619 _CurPopulation = std::numeric_limits<uint32>::max();
621 _CurrentCycle = std::numeric_limits<uint32>::max();
623 // default values.
624 setTimer(EAT_TIME, refTimer(EAT_TIME));
625 setTimer(REST_TIME, refTimer(REST_TIME));
626 setTimer(SPAWN_TIME, refTimer(SPAWN_TIME));
627 setTimer(CORPSE_TIME, refTimer(CORPSE_TIME));
628 setTimer(RESPAWN_TIME, refTimer(RESPAWN_TIME));
631 void CGrpFauna::stateChange(CAIState const* oldState, CAIState const* newState)
635 std::string CGrpFauna::getOneLineInfoString() const
637 return std::string("Fauna group '") + getName() + "'";
640 std::vector<std::string> CGrpFauna::getMultiLineInfoString() const
642 std::vector<std::string> container;
645 pushTitle(container, "CGrpFauna");
646 pushEntry(container, "id=" + CGroup::getIndexString());
647 container.back() += " alias=" + getAliasString();
648 container.back() += " name=" + getName();
649 pushEntry(container, "fullname=" + CGroup::getFullName());
650 FOREACHC(it, CCont<CPopulation>, _Populations)
652 CPopulation const* pop = *it;
653 uint32 index = pop->getChildIndex();
654 pushEntry(container, "- population["+toString(index)+"]: "+((_CurPopulation==index)? "* ACTIVE *": ""));
656 for (uint j=0; j<pop->size(); ++j)
658 CPopulationRecord& popRecord = (*pop)[j];
659 pushEntry(container, "bots:");
660 container.back() += " count="+toString(popRecord.getBotCount(getCountMultiplierFlag()));
661 if (popRecord.getCreatureSheet()==NULL)
662 container.back() += " <no sheet>";
663 else
664 container.back() += " sheet='"+popRecord.getCreatureSheet()->SheetId().toString()+"'";
667 FOREACHC(it, CCont<CBot>, bots())
669 std::vector<std::string> strings = it->getMultiLineInfoString();
670 FOREACHC(itString, std::vector<std::string>, strings)
671 container.push_back(" " + *itString);
673 pushFooter(container);
676 return container;
679 IAliasCont* CGrpFauna::getAliasCont(TAIType type)
681 switch(type)
683 case AITypePlaceFauna:
684 case AITypePlace:
685 return &_Places;
686 case AITypeGrpFaunaPop:
687 return &_Populations;
688 default:
689 return NULL;
693 CAliasTreeOwner* CGrpFauna::createChild(IAliasCont* cont, CAIAliasDescriptionNode* aliasTree)
695 if (!cont)
696 return NULL;
698 CAliasTreeOwner* child = NULL;
700 switch (aliasTree->getType())
702 // create the child and adds it to the corresponding position.
703 case AITypePlaceFauna:
704 child = new CAIPlaceXYRFauna(this, aliasTree);
705 break;
706 case AITypePlace:
708 std::string const& name = aliasTree->getName();
709 CAIPlaceXYRFauna *faunaPlace = new CAIPlaceXYRFauna(this, aliasTree);
710 child = faunaPlace;
711 uint placeIndex = faunaPlace->setupFromOldName(name);
712 nlassert(placeIndex!=std::numeric_limits<uint>::max());
714 if (placeIndex!=std::numeric_limits<uint>::max())
715 cont->addAliasChild(child, placeIndex);
717 return child;
719 break;
720 case AITypeGrpFaunaPop:
721 child = new CPopulation(this, aliasTree);
722 break;
725 if (child)
726 cont->addAliasChild(child);
727 return child;
730 void CGrpFauna::displayPlaces(CStringWriter& stringWriter) const
732 FOREACHC(it, CCont<CAIPlace>, _Places)
734 it->display(stringWriter);
739 CGrpFauna::~CGrpFauna()
741 if (isSpawned()) // to avoid bad CDbgPtr link interpretation
743 despawnGrp();
746 // unlink all child persistent state instance
747 while (!_PSIChilds.empty())
749 _PSIChilds.back()->setParentStateInstance(NULL);
751 _PSIChilds.clear();
754 void CGrpFauna::setEvent(uint eventId)
756 nlassert(eventId<10);
757 processStateEvent(getEventContainer().EventUserEvent[eventId]);
760 void CGrpFauna::serviceEvent (const CServiceEvent &info)
762 CGroup::serviceEvent(info);
764 if ((info.getServiceName() == "EGS") && (info.getEventType() == CServiceEvent::SERVICE_UP))
766 processStateEvent(getEventContainer().EventEGSUp);
771 NLMISC::CSmartPtr<CSpawnGroup> CGrpFauna::createSpawnGroup()
773 return new CSpawnGroupFauna(*this, getAStarFlag());
776 bool CGrpFauna::spawn()
778 if (!getSpawnCounter().remainToMax())
779 return false;
781 setStartState(getStartState()); // stateInstance.
782 return spawnPop(std::numeric_limits<uint>::max());
785 bool CGrpFauna::timeAllowSpawn(uint32 popVersion) const
787 if (popVersion==12345)
789 popVersion = _CurPopulation;
792 CPopulation* popPtr = _Populations[popVersion];
793 #ifdef NL_DEBUG
794 nlassert(popPtr);
795 #endif
796 if (!popPtr)
798 return false;
800 TSpawnType st = popPtr->getSpawnType();
802 bool const& isDay = CTimeInterface::isDay();
804 return (st==SpawnTypeAlways) || (isDay&&st==SpawnTypeDay) || (!isDay&&st==SpawnTypeNight);
807 bool CGrpFauna::spawnPop(uint popVersion)
809 if (places().isEmpty()) return false;
810 for (uint k = 0; k < places().size(); ++k)
812 if (!places()[k]->worldValidPos().isValid()) return false;
815 if ( !places()[SPAWN_PLACE]->worldValidPos().isValid()
816 || !places()[EAT_PLACE]->worldValidPos().isValid()
817 || !places()[REST_PLACE]->worldValidPos().isValid()) // coz time is not initialized yet ..
818 return false;*/
820 // check compatibility.
823 RYAI_MAP_CRUNCH::CCompatibleResult res;
824 areCompatiblesWithoutStartRestriction(places()[SPAWN_PLACE]->worldValidPos(), places()[EAT_PLACE]->worldValidPos(), getAStarFlag(), res);
825 if (!res.isValid())
826 return false;
827 areCompatiblesWithoutStartRestriction(places()[SPAWN_PLACE]->worldValidPos(), places()[REST_PLACE]->worldValidPos(), getAStarFlag(), res);
828 if (!res.isValid())
829 return false;
830 areCompatiblesWithoutStartRestriction(places()[EAT_PLACE]->worldValidPos(), places()[REST_PLACE]->worldValidPos(), getAStarFlag(), res);
831 if (!res.isValid())
832 return false;
835 // check each arc of the graph
836 for (uint k = 0; k < places().size(); ++k)
838 nlassert(_Places[k]);
839 checkArcs(*_Places[k]);
843 // check flags ..
844 for (uint32 i=0;i<places().size();i++)
846 if (!places()[i]->worldValidPos().isValid())
847 return false;
848 const RYAI_MAP_CRUNCH::TAStarFlag flags=places()[i]->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
849 if ((flags&getAStarFlag())!=0)
850 return false;
853 // check the validity of the input parameter
854 if (popVersion!=std::numeric_limits<uint>::max() && popVersion>=_Populations.size())
856 nlwarning("CGrpFauna::spawn(idx) FAILED for group %s because idx (%d) >= _Populations.size() (%d)",this->CGroup::getFullName().c_str(),popVersion,_Populations.size());
857 return false;
860 popVersion = std::numeric_limits<uint>::max();
862 // if we are in a cycle.
863 if (_CurrentCycle != std::numeric_limits<uint32>::max())
865 Cycle const& cycle = _Cycles[_CurrentCycle];
867 // this to avoid bug dues to bad data initialization.
870 ++_CurrentCycleIndex;
872 while ( _CurrentCycleIndex<(sint32)cycle._PopList.size()
873 && !_Populations[cycle._PopList[_CurrentCycleIndex]]);
875 if (_CurrentCycleIndex<(sint32)cycle._PopList.size())
877 popVersion=cycle._PopList[_CurrentCycleIndex];
879 if (!timeAllowSpawn(popVersion))
881 popVersion = std::numeric_limits<uint>::max();
885 if (popVersion == std::numeric_limits<uint>::max())
887 _CurrentCycle = std::numeric_limits<uint32>::max();
891 // if the population version has not been specified then select one at weighted random with day/night difference.
892 if (popVersion == std::numeric_limits<uint>::max())
894 uint32 totalWeight = 0;
896 // we can precalculate this, but it won't appears so much to be called.
897 FOREACH(it, CCont<CPopulation>, _Populations)
899 CPopulation const& pop = *(*it);
900 if (!timeAllowSpawn(pop.getChildIndex()))
901 continue;
902 totalWeight += pop.getWeight();
905 if (totalWeight==0)
906 return false;
909 sint32 rnd = CAIS::rand32(totalWeight);
910 FOREACH(it, CCont<CPopulation>, _Populations)
912 CPopulation const& pop = *(*it);
913 if (!timeAllowSpawn(pop.getChildIndex()))
914 continue;
916 rnd -= pop.getWeight();
917 if (rnd>0) // we found the population to spawn. :)
918 continue;
920 popVersion=pop.getChildIndex();
921 break;
925 #if !FINAL_VERSION
926 nlassert(popVersion != std::numeric_limits<uint>::max());
927 #endif
928 if (popVersion == std::numeric_limits<uint>::max())
929 return false;
931 // find if we are starting a new cycle ..
932 for (uint32 i=0;i<_Cycles.size();i++)
934 nlassert(_Cycles[i]._PopList.size()>0);
935 if (_Cycles[i]._PopList[0]!=popVersion)
936 continue;
938 _CurrentCycle = i;
939 _CurrentCycleIndex = 0;
943 if (popVersion >= _Populations.size())
945 nlwarning("Problem with pop size for group id %s, NAME = %s", this->CGroup::getFullName().c_str(), getName().c_str() );
946 return false;
949 // setup the pointer to the current population
950 _CurPopulation = popVersion;
952 // check that we have a defined spawn location
953 if (!_Places[SPAWN_PLACE])
955 nlwarning("CGrpFauna::spawn(idx) FAILED for group %s because _spawnPlace==NULL",this->CGroup::getFullName().c_str());
956 return false;
959 // if the group is already spawned despawn it
960 if (isSpawned())
962 despawnGrp();
965 nlassert(_CurPopulation != std::numeric_limits<uint32>::max());
967 //////////////////////////////////////////////////////////////////////////
968 // Init the group type.
969 setType ((*_Populations[_CurPopulation])[0].getCreatureSheet()->FaunaType()); // gets the first population record of the population to spawn.
972 uint32 botCount=0;
973 uint32 i;
974 CPopulation& curPop = *populations()[_CurPopulation];
975 for (i=0; i<curPop.size(); ++i)
977 botCount += curPop[i].getBotCount(getCountMultiplierFlag());
978 if ( curPop[i].getBotCount(getCountMultiplierFlag()) == 0
979 || curPop[i].getCreatureSheet()->FaunaType() == getType())
980 continue;
982 if (getGroupDesc()) // Dyn system.
984 nlwarning("****** WARNING: Different Fauna Type in Template Group %s", getGroupDesc()->getFullName().c_str());
986 else
988 nlwarning("****** WARNING: Different Fauna Type in group %s", this->CGroup::getFullName().c_str());
991 bots().setChildSize(botCount); // set the good size for bots vector.
993 for (i=0;i<botCount;i++)
994 _Bots.addChild(new CBotFauna(getType(), this), i);
997 return CGroup::spawn();
1000 void CGrpFauna::despawnGrp()
1002 CGroup::despawnGrp();
1003 _CurPopulation = std::numeric_limits<uint32>::max();
1006 // reads cycle from primitive (string representation).
1007 void CGrpFauna::setCyles(std::string const& cycles)
1009 uint32 strIndex = 0;
1010 uint32 curCycle = std::numeric_limits<uint32>::max();
1012 while (strIndex<cycles.size())
1014 char carac = cycles[++strIndex];
1016 if (carac>='A' && carac<='Z')
1017 carac += 'a'-'A';
1019 if (carac>='a' && carac<='z')
1021 if (curCycle == std::numeric_limits<uint32>::max())
1023 curCycle = (uint32)_Cycles.size();
1024 _Cycles.push_back(Cycle());
1026 Cycle& CycleRef = _Cycles[curCycle];
1027 CycleRef._PopList.push_back((uint16)(carac-'a'));
1029 else
1031 curCycle = std::numeric_limits<uint32>::max();
1036 void CGrpFauna::setPopulation(CPopulation* pop)
1038 CPopulation* sameAliasPop = NULL;
1039 uint32 index = std::numeric_limits<uint32>::max();
1041 if (pop)
1042 sameAliasPop = _Populations.getChildByAlias(pop->getAlias());
1044 if (pop && pop->size()==0) // no population record :(
1045 pop=NULL;
1047 if (sameAliasPop) // Alias already present ?
1049 index = sameAliasPop->getChildIndex();
1050 _Populations.addChild(pop, index); // automatic deletion with smart pointers
1052 else
1054 _Populations.addChild(pop); // else simply add it to the populations container
1057 // if it was the current population, respawn it. (to check with designers?)
1058 if (index==_CurPopulation)
1060 if (isSpawned()) // if spawned, despawn.
1061 getSpawnObj()->despawnGrp();
1065 //----------------------------------------------------------------------------
1066 // private utilities
1067 //----------------------------------------------------------------------------
1069 void CGrpFauna::allocateBots()
1071 uint maxPopulation = 0;
1073 // work out how much space we need
1074 CCont<CPopulation>::iterator it = populations().begin();
1075 CCont<CPopulation>::iterator itEnd = populations().end();
1077 while (it!=itEnd)
1079 CPopulation* pop = *(it);
1080 uint count=0;
1082 for (sint j=(sint)pop->size()-1;j>=0;j--)
1083 count+=(*pop)[j].getBotCount(getCountMultiplierFlag());
1085 if (count>maxPopulation)
1086 maxPopulation=count;
1087 ++it;
1090 _Bots.setChildSize(maxPopulation);
1091 for (uint32 i=0;i<maxPopulation;i++)
1092 _Bots.addChild(new CBotFauna(getType(),this),i);
1095 // Methods for setting up static data ----------------------------------------
1096 void CGrpFauna::setType(TFaunaType type)
1098 faction().removeProperties();
1099 if (type==AITYPES::FaunaTypePredator)
1100 faction().addProperty(AITYPES::CPropertyId("Predator"));
1101 else if (type==AITYPES::FaunaTypeHerbivore)
1102 faction().addProperty(AITYPES::CPropertyId("Herbivore"));
1103 else if (type==AITYPES::FaunaTypePlant)
1104 faction().addProperty(AITYPES::CPropertyId("Plant"));
1105 _Type = type;
1108 CMgrFauna& CGrpFauna::mgr() const
1110 return *static_cast<CMgrFauna*>(getOwner());
1113 CAIS::CCounter& CGrpFauna::getSpawnCounter()
1115 return CAIS::instance()._FaunaBotCounter;
1118 void CGrpFauna::lastBotDespawned()
1120 // send message
1121 processStateEvent(getEventContainer().EventLastBotDespawned);
1124 void CGrpFauna::firstBotSpawned()
1126 setFirstBotSpawned();
1129 sint CGrpFauna::getNextPlace(const CFaunaGenericPlace *startPlace, CAIPlaceXYRFauna::TFlag wantedFlag) const
1131 nlassert(wantedFlag < CAIPlaceXYRFauna::FLAG_COUNT);
1132 std::vector<uint> candidates;
1133 std::vector<uint> activeCandidates;
1134 if (!startPlace)
1136 for (uint k = 0; k < _Places.size(); ++k)
1138 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1139 if (place->getFlag(wantedFlag))
1141 if (place->getActive())
1143 activeCandidates.push_back(_Places[k]->getChildIndex());
1145 else
1147 candidates.push_back(_Places[k]->getChildIndex());
1152 else
1154 sint minIndex = INT_MAX;
1155 sint firstIndex = INT_MAX;
1156 if (startPlace->getReachNext())
1158 for (uint k = 0; k < _Places.size(); ++k)
1160 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1161 firstIndex = std::min(firstIndex, (sint) place->getIndex());
1162 if (place->getIndex() < minIndex && place->getIndex() > startPlace->getIndex())
1164 minIndex = place->getIndex();
1167 minIndex = std::max(minIndex, firstIndex);
1168 for (uint k = 0; k < _Places.size(); ++k)
1170 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1171 if ((sint) place->getIndex() == minIndex)
1173 if (place->getActive())
1175 activeCandidates.push_back(_Places[k]->getChildIndex());
1177 else
1179 candidates.push_back(_Places[k]->getChildIndex());
1184 // includes all places reachable from the arcs list
1185 for (uint k = 0; k < _Places.size(); ++k)
1187 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1188 if (place != startPlace && place->getFlag(wantedFlag))
1190 // if this place is reachable from current place arcs ...
1191 if (std::find(startPlace->getArcs().begin(), startPlace->getArcs().end(), place->getIndex()) != startPlace->getArcs().end())
1193 // ... then it is a candidate.
1194 if (place->getActive())
1196 activeCandidates.push_back(_Places[k]->getChildIndex());
1198 else
1200 candidates.push_back(_Places[k]->getChildIndex());
1206 // active vertices are taken in priority
1207 // nlwarning("%d active place, %d unactive places", (int) activeCandidates.size(), (int) candidates.size());
1208 if (!activeCandidates.empty())
1210 return (sint) activeCandidates[rand() % activeCandidates.size()];
1212 // if current place is valid then don't move
1213 if (startPlace && startPlace->getActive()) return CAIPlaceXYRFauna::INVALID_PLACE;
1214 // otherwise select a place in unactive places
1215 if (candidates.empty()) return CAIPlaceXYRFauna::INVALID_PLACE;
1216 return (sint) candidates[rand() % candidates.size()];
1219 bool CGrpFauna::checkArcs(const CAIPlace &startPlace) const
1221 const CFaunaGenericPlace *startPlaceGeneric = getFaunaGenericPlace(&startPlace);
1222 // TODO nico : this function has a lot of similarities with CGrpFauna::getNextPlace
1223 // adding a getArcs function would be nice
1224 sint minIndex = INT_MAX;
1225 sint firstIndex = INT_MAX;
1226 if (startPlaceGeneric->getReachNext())
1228 for (uint k = 0; k < _Places.size(); ++k)
1230 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1231 firstIndex = std::min(firstIndex, (sint) place->getIndex());
1232 if (place->getIndex() < minIndex && place->getIndex() > startPlaceGeneric->getIndex())
1234 minIndex = place->getIndex();
1237 minIndex = std::max(minIndex, firstIndex);
1238 for (uint k = 0; k < _Places.size(); ++k)
1240 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1241 if ((sint) place->getIndex() == minIndex)
1243 RYAI_MAP_CRUNCH::CCompatibleResult res;
1244 areCompatiblesWithoutStartRestriction(startPlace.worldValidPos(), _Places[k]->worldValidPos(), getAStarFlag(), res);
1245 if (!res.isValid()) return false;
1249 // includes all places reachable from the arcs list
1250 for (uint k = 0; k < _Places.size(); ++k)
1252 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1253 if (place != startPlaceGeneric)
1255 if (std::find(startPlaceGeneric->getArcs().begin(), startPlaceGeneric->getArcs().end(), place->getIndex()) != startPlaceGeneric->getArcs().end())
1257 // this place is in current arc list
1258 RYAI_MAP_CRUNCH::CCompatibleResult res;
1259 areCompatiblesWithoutStartRestriction(startPlace.worldValidPos(), _Places[k]->worldValidPos(), getAStarFlag(), res);
1260 if (!res.isValid()) return false;
1264 return true;
1267 void CSpawnGroupFauna::setPlace(int placeIndex)
1269 const CFaunaGenericPlace *place = getFaunaGenericPlace(getPersistent().places()[placeIndex]);
1270 //nlwarning("Going to place %s with index %d", getPersistent().places()[placeIndex]->getName().c_str(), place->getIndex());
1272 if ((int) getPersistent().places().size() <= placeIndex)
1274 nlwarning("Bad place index for fauna group %s", getPersistent().getName().c_str());
1277 // const CFaunaGenericPlace *faunaPlace = getFaunaGenericPlace(getPersistent().places()[placeIndex]);
1278 // nlwarning("Group %s : Chosing place %s (%d) with graph index %d", getPersistent().getName().c_str(), faunaPlace->getName().c_str(), placeIndex, (int) faunaPlace->getIndex());
1280 _TargetPlace = getPersistent().places()[placeIndex];
1281 #if !FINAL_VERSION
1282 const RYAI_MAP_CRUNCH::TAStarFlag flags=targetPlace()->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
1283 nlassert((flags&getPersistent().getAStarFlag())==0);
1284 #endif
1285 _PathCont.setDestination(targetPlace()->getVerticalPos(), targetPlace()->worldValidPos());
1286 _ArrivedInZone = false;
1289 #include "event_reaction_include.h"