Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / ai_grp_fauna.cpp
blob1a54c8c4c84e48d4f7f0c00e7e2fe4df98f9e3ee
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::despawnBots(bool immediately)
556 setDespawnImmediately(immediately);
557 setMustDespawnBots();
560 CAIPos const& CSpawnGroupFauna::magnetPos() const
562 return targetPlace()->midPos();
565 float CSpawnGroupFauna::magnetRadiusNear() const
567 return targetPlace()->getRadius()*(7.0f/8.0f);
570 float CSpawnGroupFauna::magnetRadiusFar() const
572 return targetPlace()->getRadius()*(9.0f/8.0f);
575 CGrpFauna& CSpawnGroupFauna::getPersistent()
577 return static_cast<CGrpFauna&>(CSpawnGroup::getPersistent());
580 uint32 CSpawnGroupFauna::getCurrentCycleTime()
582 return getPersistent().timer(CGrpFauna::cycles[_CurrentCycle]._Time);
585 //////////////////////////////////////////////////////////////////////////////
586 // CGrpFauna //
587 //////////////////////////////////////////////////////////////////////////////
590 uint32 CGrpFauna::refTimer(TTime time)
592 switch(time)
594 case EAT_TIME: return 250;
595 case REST_TIME: return 250;
596 case SPAWN_TIME: return 30;
597 case CORPSE_TIME: return 120;
598 case RESPAWN_TIME: return 45; // "Backward" compatibility: 45 seconds after the corpse is despawned (after corpse time, or when looted)
599 default:
600 nlassert(0);
601 break;
603 return 0;
606 CGrpFauna::CGrpFauna(CMgrFauna* mgr, CAIAliasDescriptionNode* aliasTree, RYAI_MAP_CRUNCH::TAStarFlag denyFlags)
607 : CGroup(mgr, denyFlags, aliasTree)
608 , CDynGrpBase()
609 , CPersistentStateInstance(*mgr->getStateMachine())
612 // state
614 _CurPopulation = std::numeric_limits<uint32>::max();
616 _CurrentCycle = std::numeric_limits<uint32>::max();
618 // default values.
619 setTimer(EAT_TIME, refTimer(EAT_TIME));
620 setTimer(REST_TIME, refTimer(REST_TIME));
621 setTimer(SPAWN_TIME, refTimer(SPAWN_TIME));
622 setTimer(CORPSE_TIME, refTimer(CORPSE_TIME));
623 setTimer(RESPAWN_TIME, refTimer(RESPAWN_TIME));
626 void CGrpFauna::stateChange(CAIState const* oldState, CAIState const* newState)
630 std::string CGrpFauna::getOneLineInfoString() const
632 return std::string("Fauna group '") + getName() + "'";
635 std::vector<std::string> CGrpFauna::getMultiLineInfoString() const
637 std::vector<std::string> container;
640 pushTitle(container, "CGrpFauna");
641 pushEntry(container, "id=" + CGroup::getIndexString());
642 container.back() += " alias=" + getAliasString();
643 container.back() += " name=" + getName();
644 pushEntry(container, "fullname=" + CGroup::getFullName());
645 FOREACHC(it, CCont<CPopulation>, _Populations)
647 CPopulation const* pop = *it;
648 uint32 index = pop->getChildIndex();
649 pushEntry(container, "- population["+toString(index)+"]: "+((_CurPopulation==index)? "* ACTIVE *": ""));
651 for (uint j=0; j<pop->size(); ++j)
653 CPopulationRecord& popRecord = (*pop)[j];
654 pushEntry(container, "bots:");
655 container.back() += " count="+toString(popRecord.getBotCount(getCountMultiplierFlag()));
656 if (popRecord.getCreatureSheet()==NULL)
657 container.back() += " <no sheet>";
658 else
659 container.back() += " sheet='"+popRecord.getCreatureSheet()->SheetId().toString()+"'";
662 FOREACHC(it, CCont<CBot>, bots())
664 std::vector<std::string> strings = it->getMultiLineInfoString();
665 FOREACHC(itString, std::vector<std::string>, strings)
666 container.push_back(" " + *itString);
668 pushFooter(container);
671 return container;
674 IAliasCont* CGrpFauna::getAliasCont(TAIType type)
676 switch(type)
678 case AITypePlaceFauna:
679 case AITypePlace:
680 return &_Places;
681 case AITypeGrpFaunaPop:
682 return &_Populations;
683 default:
684 return NULL;
688 CAliasTreeOwner* CGrpFauna::createChild(IAliasCont* cont, CAIAliasDescriptionNode* aliasTree)
690 if (!cont)
691 return NULL;
693 CAliasTreeOwner* child = NULL;
695 switch (aliasTree->getType())
697 // create the child and adds it to the corresponding position.
698 case AITypePlaceFauna:
699 child = new CAIPlaceXYRFauna(this, aliasTree);
700 break;
701 case AITypePlace:
703 std::string const& name = aliasTree->getName();
704 CAIPlaceXYRFauna *faunaPlace = new CAIPlaceXYRFauna(this, aliasTree);
705 child = faunaPlace;
706 uint placeIndex = faunaPlace->setupFromOldName(name);
707 nlassert(placeIndex!=std::numeric_limits<uint>::max());
709 if (placeIndex!=std::numeric_limits<uint>::max())
710 cont->addAliasChild(child, placeIndex);
712 return child;
714 break;
715 case AITypeGrpFaunaPop:
716 child = new CPopulation(this, aliasTree);
717 break;
720 if (child)
721 cont->addAliasChild(child);
722 return child;
725 void CGrpFauna::displayPlaces(CStringWriter& stringWriter) const
727 FOREACHC(it, CCont<CAIPlace>, _Places)
729 it->display(stringWriter);
734 CGrpFauna::~CGrpFauna()
736 if (isSpawned()) // to avoid bad CDbgPtr link interpretation
738 despawnGrp();
741 // unlink all child persistent state instance
742 while (!_PSIChilds.empty())
744 _PSIChilds.back()->setParentStateInstance(NULL);
746 _PSIChilds.clear();
749 void CGrpFauna::setEvent(uint eventId)
751 nlassert(eventId<10);
752 processStateEvent(getEventContainer().EventUserEvent[eventId]);
755 void CGrpFauna::serviceEvent (const CServiceEvent &info)
757 CGroup::serviceEvent(info);
759 if ((info.getServiceName() == "EGS") && (info.getEventType() == CServiceEvent::SERVICE_UP))
761 processStateEvent(getEventContainer().EventEGSUp);
766 NLMISC::CSmartPtr<CSpawnGroup> CGrpFauna::createSpawnGroup()
768 return new CSpawnGroupFauna(*this, getAStarFlag());
771 bool CGrpFauna::spawn()
773 if (!getSpawnCounter().remainToMax())
774 return false;
776 setStartState(getStartState()); // stateInstance.
777 return spawnPop(std::numeric_limits<uint>::max());
780 bool CGrpFauna::timeAllowSpawn(uint32 popVersion) const
782 if (popVersion==12345)
784 popVersion = _CurPopulation;
787 CPopulation* popPtr = _Populations[popVersion];
788 #ifdef NL_DEBUG
789 nlassert(popPtr);
790 #endif
791 if (!popPtr)
793 return false;
795 TSpawnType st = popPtr->getSpawnType();
797 bool const& isDay = CTimeInterface::isDay();
799 return (st==SpawnTypeAlways) || (isDay&&st==SpawnTypeDay) || (!isDay&&st==SpawnTypeNight);
802 bool CGrpFauna::spawnPop(uint popVersion)
804 if (places().isEmpty()) return false;
805 for (uint k = 0; k < places().size(); ++k)
807 if (!places()[k]->worldValidPos().isValid()) return false;
810 if ( !places()[SPAWN_PLACE]->worldValidPos().isValid()
811 || !places()[EAT_PLACE]->worldValidPos().isValid()
812 || !places()[REST_PLACE]->worldValidPos().isValid()) // coz time is not initialized yet ..
813 return false;*/
815 // check compatibility.
818 RYAI_MAP_CRUNCH::CCompatibleResult res;
819 areCompatiblesWithoutStartRestriction(places()[SPAWN_PLACE]->worldValidPos(), places()[EAT_PLACE]->worldValidPos(), getAStarFlag(), res);
820 if (!res.isValid())
821 return false;
822 areCompatiblesWithoutStartRestriction(places()[SPAWN_PLACE]->worldValidPos(), places()[REST_PLACE]->worldValidPos(), getAStarFlag(), res);
823 if (!res.isValid())
824 return false;
825 areCompatiblesWithoutStartRestriction(places()[EAT_PLACE]->worldValidPos(), places()[REST_PLACE]->worldValidPos(), getAStarFlag(), res);
826 if (!res.isValid())
827 return false;
830 // check each arc of the graph
831 for (uint k = 0; k < places().size(); ++k)
833 nlassert(_Places[k]);
834 checkArcs(*_Places[k]);
838 // check flags ..
839 for (uint32 i=0;i<places().size();i++)
841 if (!places()[i]->worldValidPos().isValid())
842 return false;
843 const RYAI_MAP_CRUNCH::TAStarFlag flags=places()[i]->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
844 if ((flags&getAStarFlag())!=0)
845 return false;
848 // check the validity of the input parameter
849 if (popVersion!=std::numeric_limits<uint>::max() && popVersion>=_Populations.size())
851 nlwarning("CGrpFauna::spawn(idx) FAILED for group %s because idx (%d) >= _Populations.size() (%d)",this->CGroup::getFullName().c_str(),popVersion,_Populations.size());
852 return false;
855 popVersion = std::numeric_limits<uint>::max();
857 // if we are in a cycle.
858 if (_CurrentCycle != std::numeric_limits<uint32>::max())
860 Cycle const& cycle = _Cycles[_CurrentCycle];
862 // this to avoid bug dues to bad data initialization.
865 ++_CurrentCycleIndex;
867 while ( _CurrentCycleIndex<(sint32)cycle._PopList.size()
868 && !_Populations[cycle._PopList[_CurrentCycleIndex]]);
870 if (_CurrentCycleIndex<(sint32)cycle._PopList.size())
872 popVersion=cycle._PopList[_CurrentCycleIndex];
874 if (!timeAllowSpawn(popVersion))
876 popVersion = std::numeric_limits<uint>::max();
880 if (popVersion == std::numeric_limits<uint>::max())
882 _CurrentCycle = std::numeric_limits<uint32>::max();
886 // if the population version has not been specified then select one at weighted random with day/night difference.
887 if (popVersion == std::numeric_limits<uint>::max())
889 uint32 totalWeight = 0;
891 // we can precalculate this, but it won't appears so much to be called.
892 FOREACH(it, CCont<CPopulation>, _Populations)
894 CPopulation const& pop = *(*it);
895 if (!timeAllowSpawn(pop.getChildIndex()))
896 continue;
897 totalWeight += pop.getWeight();
900 if (totalWeight==0)
901 return false;
904 sint32 rnd = CAIS::rand32(totalWeight);
905 FOREACH(it, CCont<CPopulation>, _Populations)
907 CPopulation const& pop = *(*it);
908 if (!timeAllowSpawn(pop.getChildIndex()))
909 continue;
911 rnd -= pop.getWeight();
912 if (rnd>0) // we found the population to spawn. :)
913 continue;
915 popVersion=pop.getChildIndex();
916 break;
920 #if !FINAL_VERSION
921 nlassert(popVersion != std::numeric_limits<uint>::max());
922 #endif
923 if (popVersion == std::numeric_limits<uint>::max())
924 return false;
926 // find if we are starting a new cycle ..
927 for (uint32 i=0;i<_Cycles.size();i++)
929 nlassert(_Cycles[i]._PopList.size()>0);
930 if (_Cycles[i]._PopList[0]!=popVersion)
931 continue;
933 _CurrentCycle = i;
934 _CurrentCycleIndex = 0;
938 if (popVersion >= _Populations.size())
940 nlwarning("Problem with pop size for group id %s, NAME = %s", this->CGroup::getFullName().c_str(), getName().c_str() );
941 return false;
944 // setup the pointer to the current population
945 _CurPopulation = popVersion;
947 // check that we have a defined spawn location
948 if (!_Places[SPAWN_PLACE])
950 nlwarning("CGrpFauna::spawn(idx) FAILED for group %s because _spawnPlace==NULL",this->CGroup::getFullName().c_str());
951 return false;
954 // if the group is already spawned despawn it
955 if (isSpawned())
957 despawnGrp();
960 nlassert(_CurPopulation != std::numeric_limits<uint32>::max());
962 //////////////////////////////////////////////////////////////////////////
963 // Init the group type.
964 setType ((*_Populations[_CurPopulation])[0].getCreatureSheet()->FaunaType()); // gets the first population record of the population to spawn.
967 uint32 botCount=0;
968 uint32 i;
969 CPopulation& curPop = *populations()[_CurPopulation];
970 for (i=0; i<curPop.size(); ++i)
972 botCount += curPop[i].getBotCount(getCountMultiplierFlag());
973 if ( curPop[i].getBotCount(getCountMultiplierFlag()) == 0
974 || curPop[i].getCreatureSheet()->FaunaType() == getType())
975 continue;
977 if (getGroupDesc()) // Dyn system.
979 nlwarning("****** WARNING: Different Fauna Type in Template Group %s", getGroupDesc()->getFullName().c_str());
981 else
983 nlwarning("****** WARNING: Different Fauna Type in group %s", this->CGroup::getFullName().c_str());
986 bots().setChildSize(botCount); // set the good size for bots vector.
988 for (i=0;i<botCount;i++)
989 _Bots.addChild(new CBotFauna(getType(), this), i);
992 return CGroup::spawn();
995 void CGrpFauna::despawnGrp()
997 CGroup::despawnGrp();
998 _CurPopulation = std::numeric_limits<uint32>::max();
1001 // reads cycle from primitive (string representation).
1002 void CGrpFauna::setCyles(std::string const& cycles)
1004 uint32 strIndex = 0;
1005 uint32 curCycle = std::numeric_limits<uint32>::max();
1007 while (strIndex<cycles.size())
1009 char carac = cycles[++strIndex];
1011 if (carac>='A' && carac<='Z')
1012 carac += 'a'-'A';
1014 if (carac>='a' && carac<='z')
1016 if (curCycle == std::numeric_limits<uint32>::max())
1018 curCycle = (uint32)_Cycles.size();
1019 _Cycles.push_back(Cycle());
1021 Cycle& CycleRef = _Cycles[curCycle];
1022 CycleRef._PopList.push_back((uint16)(carac-'a'));
1024 else
1026 curCycle = std::numeric_limits<uint32>::max();
1031 void CGrpFauna::setPopulation(CPopulation* pop)
1033 CPopulation* sameAliasPop = NULL;
1034 uint32 index = std::numeric_limits<uint32>::max();
1036 if (pop)
1037 sameAliasPop = _Populations.getChildByAlias(pop->getAlias());
1039 if (pop && pop->size()==0) // no population record :(
1040 pop=NULL;
1042 if (sameAliasPop) // Alias already present ?
1044 index = sameAliasPop->getChildIndex();
1045 _Populations.addChild(pop, index); // automatic deletion with smart pointers
1047 else
1049 _Populations.addChild(pop); // else simply add it to the populations container
1052 // if it was the current population, respawn it. (to check with designers?)
1053 if (index==_CurPopulation)
1055 if (isSpawned()) // if spawned, despawn.
1056 getSpawnObj()->despawnGrp();
1060 //----------------------------------------------------------------------------
1061 // private utilities
1062 //----------------------------------------------------------------------------
1064 void CGrpFauna::allocateBots()
1066 uint maxPopulation = 0;
1068 // work out how much space we need
1069 CCont<CPopulation>::iterator it = populations().begin();
1070 CCont<CPopulation>::iterator itEnd = populations().end();
1072 while (it!=itEnd)
1074 CPopulation* pop = *(it);
1075 uint count=0;
1077 for (sint j=(sint)pop->size()-1;j>=0;j--)
1078 count+=(*pop)[j].getBotCount(getCountMultiplierFlag());
1080 if (count>maxPopulation)
1081 maxPopulation=count;
1082 ++it;
1085 _Bots.setChildSize(maxPopulation);
1086 for (uint32 i=0;i<maxPopulation;i++)
1087 _Bots.addChild(new CBotFauna(getType(),this),i);
1090 // Methods for setting up static data ----------------------------------------
1091 void CGrpFauna::setType(TFaunaType type)
1093 faction().removeProperties();
1094 if (type==AITYPES::FaunaTypePredator)
1095 faction().addProperty(AITYPES::CPropertyId("Predator"));
1096 else if (type==AITYPES::FaunaTypeHerbivore)
1097 faction().addProperty(AITYPES::CPropertyId("Herbivore"));
1098 else if (type==AITYPES::FaunaTypePlant)
1099 faction().addProperty(AITYPES::CPropertyId("Plant"));
1100 _Type = type;
1103 CMgrFauna& CGrpFauna::mgr() const
1105 return *static_cast<CMgrFauna*>(getOwner());
1108 CAIS::CCounter& CGrpFauna::getSpawnCounter()
1110 return CAIS::instance()._FaunaBotCounter;
1113 void CGrpFauna::lastBotDespawned()
1115 // send message
1116 processStateEvent(getEventContainer().EventLastBotDespawned);
1119 void CGrpFauna::firstBotSpawned()
1121 setFirstBotSpawned();
1124 sint CGrpFauna::getNextPlace(const CFaunaGenericPlace *startPlace, CAIPlaceXYRFauna::TFlag wantedFlag) const
1126 nlassert(wantedFlag < CAIPlaceXYRFauna::FLAG_COUNT);
1127 std::vector<uint> candidates;
1128 std::vector<uint> activeCandidates;
1129 if (!startPlace)
1131 for (uint k = 0; k < _Places.size(); ++k)
1133 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1134 if (place->getFlag(wantedFlag))
1136 if (place->getActive())
1138 activeCandidates.push_back(_Places[k]->getChildIndex());
1140 else
1142 candidates.push_back(_Places[k]->getChildIndex());
1147 else
1149 sint minIndex = INT_MAX;
1150 sint firstIndex = INT_MAX;
1151 if (startPlace->getReachNext())
1153 for (uint k = 0; k < _Places.size(); ++k)
1155 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1156 firstIndex = std::min(firstIndex, (sint) place->getIndex());
1157 if (place->getIndex() < minIndex && place->getIndex() > startPlace->getIndex())
1159 minIndex = place->getIndex();
1162 minIndex = std::max(minIndex, firstIndex);
1163 for (uint k = 0; k < _Places.size(); ++k)
1165 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1166 if ((sint) place->getIndex() == minIndex)
1168 if (place->getActive())
1170 activeCandidates.push_back(_Places[k]->getChildIndex());
1172 else
1174 candidates.push_back(_Places[k]->getChildIndex());
1179 // includes all places reachable from the arcs list
1180 for (uint k = 0; k < _Places.size(); ++k)
1182 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1183 if (place != startPlace && place->getFlag(wantedFlag))
1185 // if this place is reachable from current place arcs ...
1186 if (std::find(startPlace->getArcs().begin(), startPlace->getArcs().end(), place->getIndex()) != startPlace->getArcs().end())
1188 // ... then it is a candidate.
1189 if (place->getActive())
1191 activeCandidates.push_back(_Places[k]->getChildIndex());
1193 else
1195 candidates.push_back(_Places[k]->getChildIndex());
1201 // active vertices are taken in priority
1202 // nlwarning("%d active place, %d unactive places", (int) activeCandidates.size(), (int) candidates.size());
1203 if (!activeCandidates.empty())
1205 return (sint) activeCandidates[rand() % activeCandidates.size()];
1207 // if current place is valid then don't move
1208 if (startPlace && startPlace->getActive()) return CAIPlaceXYRFauna::INVALID_PLACE;
1209 // otherwise select a place in unactive places
1210 if (candidates.empty()) return CAIPlaceXYRFauna::INVALID_PLACE;
1211 return (sint) candidates[rand() % candidates.size()];
1214 bool CGrpFauna::checkArcs(const CAIPlace &startPlace) const
1216 const CFaunaGenericPlace *startPlaceGeneric = getFaunaGenericPlace(&startPlace);
1217 // TODO nico : this function has a lot of similarities with CGrpFauna::getNextPlace
1218 // adding a getArcs function would be nice
1219 sint minIndex = INT_MAX;
1220 sint firstIndex = INT_MAX;
1221 if (startPlaceGeneric->getReachNext())
1223 for (uint k = 0; k < _Places.size(); ++k)
1225 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1226 firstIndex = std::min(firstIndex, (sint) place->getIndex());
1227 if (place->getIndex() < minIndex && place->getIndex() > startPlaceGeneric->getIndex())
1229 minIndex = place->getIndex();
1232 minIndex = std::max(minIndex, firstIndex);
1233 for (uint k = 0; k < _Places.size(); ++k)
1235 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1236 if ((sint) place->getIndex() == minIndex)
1238 RYAI_MAP_CRUNCH::CCompatibleResult res;
1239 areCompatiblesWithoutStartRestriction(startPlace.worldValidPos(), _Places[k]->worldValidPos(), getAStarFlag(), res);
1240 if (!res.isValid()) return false;
1244 // includes all places reachable from the arcs list
1245 for (uint k = 0; k < _Places.size(); ++k)
1247 const CFaunaGenericPlace *place = getFaunaGenericPlace(_Places[k]);
1248 if (place != startPlaceGeneric)
1250 if (std::find(startPlaceGeneric->getArcs().begin(), startPlaceGeneric->getArcs().end(), place->getIndex()) != startPlaceGeneric->getArcs().end())
1252 // this place is in current arc list
1253 RYAI_MAP_CRUNCH::CCompatibleResult res;
1254 areCompatiblesWithoutStartRestriction(startPlace.worldValidPos(), _Places[k]->worldValidPos(), getAStarFlag(), res);
1255 if (!res.isValid()) return false;
1259 return true;
1262 void CSpawnGroupFauna::setPlace(int placeIndex)
1264 const CFaunaGenericPlace *place = getFaunaGenericPlace(getPersistent().places()[placeIndex]);
1265 //nlwarning("Going to place %s with index %d", getPersistent().places()[placeIndex]->getName().c_str(), place->getIndex());
1267 if ((int) getPersistent().places().size() <= placeIndex)
1269 nlwarning("Bad place index for fauna group %s", getPersistent().getName().c_str());
1272 // const CFaunaGenericPlace *faunaPlace = getFaunaGenericPlace(getPersistent().places()[placeIndex]);
1273 // nlwarning("Group %s : Chosing place %s (%d) with graph index %d", getPersistent().getName().c_str(), faunaPlace->getName().c_str(), placeIndex, (int) faunaPlace->getIndex());
1275 _TargetPlace = getPersistent().places()[placeIndex];
1276 #if !FINAL_VERSION
1277 const RYAI_MAP_CRUNCH::TAStarFlag flags=targetPlace()->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
1278 nlassert((flags&getPersistent().getAStarFlag())==0);
1279 #endif
1280 _PathCont.setDestination(targetPlace()->getVerticalPos(), targetPlace()->worldValidPos());
1281 _ArrivedInZone = false;
1284 #include "event_reaction_include.h"