1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "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
)
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";
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
);
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
)
73 , _DespawnImmediately(false)
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
;
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();
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;
149 FOREACH(it
, CCont
<CBot
>, bots())
151 CBotFauna
*bot
=NLMISC::safe_cast
<CBotFauna
*>(*it
);
152 CSpawnBotFauna
*botFauna
=bot
->getSpawn();
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));
163 if (botFauna
->havePlayersAround())
175 _CenterPos
= CAIVector(grpPosx
/nbBots
,grpPosy
/nbBots
);
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();
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
;
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())
229 H_AUTO(GrpFaunaUpdateDealWithDead
);
232 if (nbBotToRespawn()>0)
234 if (nbSpawnedBot()>0) // (getPersistent().bots().size()/2))
236 if (getPersistent().timeAllowSpawn ())
243 // critical code (despawn 'this' object).
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
);
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
);
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()))
348 nlassert(getPersistent().populations()[getPersistent()._CurPopulation
]);
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())
363 // identify the bot to spawn
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())
375 if (j
<popRecord
.getBotCount(getPersistent().getCountMultiplierFlag()))
383 // by definition there must be a bot type
384 nlassert(i
<curPop
.size());
388 CBotFauna
*faunaPt
=NLMISC::safe_cast
<CBotFauna
*>(bots()[count
]);
390 faunaPt
->setSheet (/*const_cast<AISHEETS::ICreature *>(*/curPop
[i
].getCreatureSheet()/*)*/);
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
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());
418 // did we start a new cycle ?
419 if (cycle
>=(sizeof(CGrpFauna::cycles
)/sizeof(CGrpFauna::CCycleDef
)))
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
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
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
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
473 _CurrentCycle
= cycle
;
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();
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
)
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());
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(),
524 faunaPlace->getName().c_str(),
525 (int) faunaPlace->getIndex());
535 // nlwarning("Group %s : Timer = %d", getPersistent().getName().c_str(), (int) (100 * _Timer.timeRemaining() / _Timer.totalTime()));
536 if (_Timer
.test()) // si fin de timer.
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 //////////////////////////////////////////////////////////////////////////////
592 //////////////////////////////////////////////////////////////////////////////
595 uint32
CGrpFauna::refTimer(TTime 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)
611 CGrpFauna::CGrpFauna(CMgrFauna
* mgr
, CAIAliasDescriptionNode
* aliasTree
, RYAI_MAP_CRUNCH::TAStarFlag denyFlags
)
612 : CGroup(mgr
, denyFlags
, aliasTree
)
614 , CPersistentStateInstance(*mgr
->getStateMachine())
619 _CurPopulation
= std::numeric_limits
<uint32
>::max();
621 _CurrentCycle
= std::numeric_limits
<uint32
>::max();
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>";
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
);
679 IAliasCont
* CGrpFauna::getAliasCont(TAIType type
)
683 case AITypePlaceFauna
:
686 case AITypeGrpFaunaPop
:
687 return &_Populations
;
693 CAliasTreeOwner
* CGrpFauna::createChild(IAliasCont
* cont
, CAIAliasDescriptionNode
* aliasTree
)
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
);
708 std::string
const& name
= aliasTree
->getName();
709 CAIPlaceXYRFauna
*faunaPlace
= new CAIPlaceXYRFauna(this, aliasTree
);
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
);
720 case AITypeGrpFaunaPop
:
721 child
= new CPopulation(this, aliasTree
);
726 cont
->addAliasChild(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
746 // unlink all child persistent state instance
747 while (!_PSIChilds
.empty())
749 _PSIChilds
.back()->setParentStateInstance(NULL
);
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())
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
];
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 ..
820 // check compatibility.
823 RYAI_MAP_CRUNCH::CCompatibleResult res;
824 areCompatiblesWithoutStartRestriction(places()[SPAWN_PLACE]->worldValidPos(), places()[EAT_PLACE]->worldValidPos(), getAStarFlag(), res);
827 areCompatiblesWithoutStartRestriction(places()[SPAWN_PLACE]->worldValidPos(), places()[REST_PLACE]->worldValidPos(), getAStarFlag(), res);
830 areCompatiblesWithoutStartRestriction(places()[EAT_PLACE]->worldValidPos(), places()[REST_PLACE]->worldValidPos(), getAStarFlag(), res);
835 // check each arc of the graph
836 for (uint k
= 0; k
< places().size(); ++k
)
838 nlassert(_Places
[k
]);
839 checkArcs(*_Places
[k
]);
844 for (uint32 i
=0;i
<places().size();i
++)
846 if (!places()[i
]->worldValidPos().isValid())
848 const RYAI_MAP_CRUNCH::TAStarFlag flags
=places()[i
]->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
849 if ((flags
&getAStarFlag())!=0)
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());
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()))
902 totalWeight
+= pop
.getWeight();
909 sint32 rnd
= CAIS::rand32(totalWeight
);
910 FOREACH(it
, CCont
<CPopulation
>, _Populations
)
912 CPopulation
const& pop
= *(*it
);
913 if (!timeAllowSpawn(pop
.getChildIndex()))
916 rnd
-= pop
.getWeight();
917 if (rnd
>0) // we found the population to spawn. :)
920 popVersion
=pop
.getChildIndex();
926 nlassert(popVersion
!= std::numeric_limits
<uint
>::max());
928 if (popVersion
== std::numeric_limits
<uint
>::max())
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
)
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() );
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());
959 // if the group is already spawned despawn it
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.
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())
982 if (getGroupDesc()) // Dyn system.
984 nlwarning("****** WARNING: Different Fauna Type in Template Group %s", getGroupDesc()->getFullName().c_str());
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')
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'));
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();
1042 sameAliasPop
= _Populations
.getChildByAlias(pop
->getAlias());
1044 if (pop
&& pop
->size()==0) // no population record :(
1047 if (sameAliasPop
) // Alias already present ?
1049 index
= sameAliasPop
->getChildIndex();
1050 _Populations
.addChild(pop
, index
); // automatic deletion with smart pointers
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();
1079 CPopulation
* pop
= *(it
);
1082 for (sint j
=(sint
)pop
->size()-1;j
>=0;j
--)
1083 count
+=(*pop
)[j
].getBotCount(getCountMultiplierFlag());
1085 if (count
>maxPopulation
)
1086 maxPopulation
=count
;
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"));
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()
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
;
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());
1147 candidates
.push_back(_Places
[k
]->getChildIndex());
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());
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());
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;
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
];
1282 const RYAI_MAP_CRUNCH::TAStarFlag flags
=targetPlace()->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
1283 nlassert((flags
&getPersistent().getAStarFlag())==0);
1285 _PathCont
.setDestination(targetPlace()->getVerticalPos(), targetPlace()->worldValidPos());
1286 _ArrivedInZone
= false;
1289 #include "event_reaction_include.h"