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::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 //////////////////////////////////////////////////////////////////////////////
587 //////////////////////////////////////////////////////////////////////////////
590 uint32
CGrpFauna::refTimer(TTime 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)
606 CGrpFauna::CGrpFauna(CMgrFauna
* mgr
, CAIAliasDescriptionNode
* aliasTree
, RYAI_MAP_CRUNCH::TAStarFlag denyFlags
)
607 : CGroup(mgr
, denyFlags
, aliasTree
)
609 , CPersistentStateInstance(*mgr
->getStateMachine())
614 _CurPopulation
= std::numeric_limits
<uint32
>::max();
616 _CurrentCycle
= std::numeric_limits
<uint32
>::max();
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>";
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
);
674 IAliasCont
* CGrpFauna::getAliasCont(TAIType type
)
678 case AITypePlaceFauna
:
681 case AITypeGrpFaunaPop
:
682 return &_Populations
;
688 CAliasTreeOwner
* CGrpFauna::createChild(IAliasCont
* cont
, CAIAliasDescriptionNode
* aliasTree
)
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
);
703 std::string
const& name
= aliasTree
->getName();
704 CAIPlaceXYRFauna
*faunaPlace
= new CAIPlaceXYRFauna(this, aliasTree
);
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
);
715 case AITypeGrpFaunaPop
:
716 child
= new CPopulation(this, aliasTree
);
721 cont
->addAliasChild(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
741 // unlink all child persistent state instance
742 while (!_PSIChilds
.empty())
744 _PSIChilds
.back()->setParentStateInstance(NULL
);
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())
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
];
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 ..
815 // check compatibility.
818 RYAI_MAP_CRUNCH::CCompatibleResult res;
819 areCompatiblesWithoutStartRestriction(places()[SPAWN_PLACE]->worldValidPos(), places()[EAT_PLACE]->worldValidPos(), getAStarFlag(), res);
822 areCompatiblesWithoutStartRestriction(places()[SPAWN_PLACE]->worldValidPos(), places()[REST_PLACE]->worldValidPos(), getAStarFlag(), res);
825 areCompatiblesWithoutStartRestriction(places()[EAT_PLACE]->worldValidPos(), places()[REST_PLACE]->worldValidPos(), getAStarFlag(), res);
830 // check each arc of the graph
831 for (uint k
= 0; k
< places().size(); ++k
)
833 nlassert(_Places
[k
]);
834 checkArcs(*_Places
[k
]);
839 for (uint32 i
=0;i
<places().size();i
++)
841 if (!places()[i
]->worldValidPos().isValid())
843 const RYAI_MAP_CRUNCH::TAStarFlag flags
=places()[i
]->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
844 if ((flags
&getAStarFlag())!=0)
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());
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()))
897 totalWeight
+= pop
.getWeight();
904 sint32 rnd
= CAIS::rand32(totalWeight
);
905 FOREACH(it
, CCont
<CPopulation
>, _Populations
)
907 CPopulation
const& pop
= *(*it
);
908 if (!timeAllowSpawn(pop
.getChildIndex()))
911 rnd
-= pop
.getWeight();
912 if (rnd
>0) // we found the population to spawn. :)
915 popVersion
=pop
.getChildIndex();
921 nlassert(popVersion
!= std::numeric_limits
<uint
>::max());
923 if (popVersion
== std::numeric_limits
<uint
>::max())
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
)
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() );
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());
954 // if the group is already spawned despawn it
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.
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())
977 if (getGroupDesc()) // Dyn system.
979 nlwarning("****** WARNING: Different Fauna Type in Template Group %s", getGroupDesc()->getFullName().c_str());
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')
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'));
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();
1037 sameAliasPop
= _Populations
.getChildByAlias(pop
->getAlias());
1039 if (pop
&& pop
->size()==0) // no population record :(
1042 if (sameAliasPop
) // Alias already present ?
1044 index
= sameAliasPop
->getChildIndex();
1045 _Populations
.addChild(pop
, index
); // automatic deletion with smart pointers
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();
1074 CPopulation
* pop
= *(it
);
1077 for (sint j
=(sint
)pop
->size()-1;j
>=0;j
--)
1078 count
+=(*pop
)[j
].getBotCount(getCountMultiplierFlag());
1080 if (count
>maxPopulation
)
1081 maxPopulation
=count
;
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"));
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()
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
;
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());
1142 candidates
.push_back(_Places
[k
]->getChildIndex());
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());
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());
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;
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
];
1277 const RYAI_MAP_CRUNCH::TAStarFlag flags
=targetPlace()->worldValidPos().getTopologyRef().getCstTopologyNode().getFlags();
1278 nlassert((flags
&getPersistent().getAStarFlag())==0);
1280 _PathCont
.setDestination(targetPlace()->getVerticalPos(), targetPlace()->worldValidPos());
1281 _ArrivedInZone
= false;
1284 #include "event_reaction_include.h"