Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / continent_inline.h
blobabde78fbfa2e41f51c61e34a4456e477b826e926
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #ifndef CONTINENT_INLINE_H
21 #define CONTINENT_INLINE_H
23 #include "ai_bot.h"
24 #include "ai_grp_fauna.h"
25 #include "ai_grp_npc.h"
26 #include "ai_bot_npc.h"
27 #include "ais_actions.h"
29 extern NLMISC::CVariable<bool> LogAcceptablePos;
30 extern NLMISC::CVariable<bool> LogGroupCreationFailure;
31 extern NLMISC::CVariable<bool> LogOutpostDebug;
33 //////////////////////////////////////////////////////////////////////////////
34 // CPopulationRecord //
35 //////////////////////////////////////////////////////////////////////////////
37 inline
38 CPopulationRecord::CPopulationRecord(AISHEETS::ICreatureCPtr const& sheetData, uint count)
39 : _SheetData(sheetData)
40 , _Count(count)
44 inline
45 bool CPopulationRecord::operator==(CPopulationRecord const& other) const
47 return (_Count==other._Count && _SheetData==other._SheetData);
50 inline
51 AISHEETS::ICreatureCPtr CPopulationRecord::getCreatureSheet() const
53 return _SheetData;
56 inline
57 uint CPopulationRecord::getBotCount(bool useCreatureMultiplier) const
59 if (useCreatureMultiplier && _SheetData)
60 return _Count * _SheetData->DynamicGroupCountMultiplier();
61 else
62 return _Count;
65 inline
66 uint32 CPopulationRecord::getEnergyValue(bool useCreatureMultiplier) const
68 if (_SheetData)
70 return getBotCount(useCreatureMultiplier)*_SheetData->EnergyValue();
72 else
74 #ifdef NL_DEBUG
75 nlassert(_SheetData);
76 #endif
77 return 0;
81 //////////////////////////////////////////////////////////////////////////////
82 // CPopulation //
83 //////////////////////////////////////////////////////////////////////////////
85 inline
86 CPopulation::CPopulation(CPopulationOwner* owner, CAIAliasDescriptionNode* aliasDescription)
87 : CAliasChild<CPopulationOwner>(owner, aliasDescription)
88 , _Weight(0)
89 , _SpawnType(AITYPES::SpawnTypeBadType)
93 inline
94 CPopulation::CPopulation(CPopulationOwner* owner, uint32 alias, std::string name)
95 : CAliasChild<CPopulationOwner>(owner, alias, name)
96 , _Weight(0)
97 , _SpawnType(AITYPES::SpawnTypeBadType)
101 inline
102 void CPopulation::addPopRecord(CPopulationRecord popRecord)
104 _PopRecords.push_back(popRecord);
107 //////////////////////////////////////////////////////////////////////////////
108 // CAICircle //
109 //////////////////////////////////////////////////////////////////////////////
111 // Template members must be inside class definitions for VC++6
112 template <class V, class R>
113 CAICircle::CAICircle(V const& center, R radius)
114 : _Center(center)
115 , _Radius(radius)
119 // Template members must be inside class definitions for VC++6
120 template <class V>
121 bool CAICircle::isInside(V const& pos)
123 return (pos - _Center).sqrnorm() <= (_Radius+1)*(_Radius+1);
126 //////////////////////////////////////////////////////////////////////////////
127 // CAabb //
128 //////////////////////////////////////////////////////////////////////////////
130 inline
131 CAabb::CAabb()
132 : _VMin(INT_MAX/CAICoord::UNITS_PER_METER, INT_MAX/CAICoord::UNITS_PER_METER)
133 , _VMax(INT_MIN/CAICoord::UNITS_PER_METER, INT_MIN/CAICoord::UNITS_PER_METER)
137 template <class V>
138 CAabb::CAabb(std::vector<V> const& coords)
139 : _VMax(INT_MIN/CAICoord::UNITS_PER_METER, INT_MIN/CAICoord::UNITS_PER_METER)
140 , _VMin(INT_MAX/CAICoord::UNITS_PER_METER, INT_MAX/CAICoord::UNITS_PER_METER)
142 for (uint k=0; k<coords.size(); ++k)
143 includePoint(coords[k]);
146 template <class V>
147 void CAabb::includePoint(V const& point)
149 if (point.x() < _VMin.x())
150 _VMin.setX(point.x());
151 if (point.x() > _VMax.x())
152 _VMax.setX(point.x());
153 if (point.y() < _VMin.y())
154 _VMin.setY(point.y());
155 if (point.y() > _VMax.y())
156 _VMax.setY(point.y());
159 inline
160 void CAabb::includeAabb(CAabb const& box)
162 if (box._VMin.x() < _VMin.x())
163 _VMin.setX(box._VMin.x());
164 if (box._VMax.x() > _VMax.x())
165 _VMax.setX(box._VMax.x());
166 if (box._VMin.y() < _VMin.y())
167 _VMin.setY(box._VMin.y());
168 if (box._VMax.y() > _VMax.y())
169 _VMax.setY(box._VMax.y());
172 inline
173 void CAabb::init()
175 *this = CAabb();
178 // Template members must be inside class definitions for VC++6
179 template<class V>
180 bool CAabb::isInside(V const& v)
182 return v.x() >= _VMin.x() && v.y() >= _VMin.y() && v.x() <= _VMax.x() && v.y() <= _VMax.y();
186 //////////////////////////////////////////////////////////////////////////////
187 // CFaunaZone //
188 //////////////////////////////////////////////////////////////////////////////
190 inline
191 bool CFaunaZone::haveActivity(AITYPES::CPropertyId const& activity) const
193 if (!_AdditionalActivities.empty())
194 return _AdditionalActivities.have(activity);
195 return _InitialActivities.have(activity);
198 inline
199 bool CFaunaZone::haveActivity(AITYPES::CPropertySet const& activities) const
201 if (!_AdditionalActivities.empty())
202 return _AdditionalActivities.containsPartOfStrict(activities);
203 return _InitialActivities.containsPartOfStrict(activities);
206 inline
207 float CFaunaZone::getFreeAreaScore() const
209 float radius = getRadius();
210 return (float)((radius*radius)/((float)getNbUse()+0.001));
213 inline
214 uint32 CFaunaZone::getNbUse() const
216 sint nbUse = getRefCount()-1; // -1 for the container
217 #ifdef NL_DEBUG
218 nlassert(nbUse>=0);
219 #endif
220 return nbUse;
223 //////////////////////////////////////////////////////////////////////////////
224 // CAIRefPlaceXYR //
225 //////////////////////////////////////////////////////////////////////////////
227 inline
228 CAIRefPlaceXYR::CAIRefPlaceXYR(CPlaceOwner* owner, CAIPlace const* zone)
229 : CAIPlace(owner, NULL)
231 #ifdef NL_DEBUG
232 nlassert(zone!=NULL);
233 #endif
234 _Zone = zone;
237 inline
238 CAIRefPlaceXYR::operator CAIPlace const*() const
240 return _Zone;
243 //////////////////////////////////////////////////////////////////////////////
244 // CAIRefPlaceXYRFauna //
245 //////////////////////////////////////////////////////////////////////////////
247 inline
248 CAIRefPlaceXYRFauna::CAIRefPlaceXYRFauna(CPlaceOwner* owner, CAIPlace const* zone)
249 : CAIRefPlaceXYR(owner, zone)
253 //////////////////////////////////////////////////////////////////////////////
254 // CRebuildContinentAndOutPost //
255 //////////////////////////////////////////////////////////////////////////////
257 inline
258 CRebuildContinentAndOutPost::CRebuildContinentAndOutPost(CContinent* continent)
259 : _Continent(continent)
263 inline
264 bool CRebuildContinentAndOutPost::absorb(CLazyProcess const& lazyProcess) const
266 CRebuildContinentAndOutPost const* other = dynamic_cast<CRebuildContinentAndOutPost const*>(&lazyProcess);
267 if (!other)
268 return false;
269 return *other==*this;
272 inline
273 bool CRebuildContinentAndOutPost::operator==(CRebuildContinentAndOutPost const& other) const
275 return other._Continent==_Continent;
278 //////////////////////////////////////////////////////////////////////////////
279 // CContinent //
280 //////////////////////////////////////////////////////////////////////////////
282 inline
283 CContinent::CContinent(CAIInstance* owner)
284 : CChild<CAIInstance>(owner)
288 //////////////////////////////////////////////////////////////////////////////
289 // CRoad //
290 //////////////////////////////////////////////////////////////////////////////
292 inline
293 void CRoad::calcLength()
295 double length = 0.0f;
296 for (sint j=((sint)_Coords.size())-1; j>0; j--)
297 length += (_Coords[j] - _Coords[j-1]).toAIVector().norm();
298 _Length = (float)length;
299 calCost();
302 inline
303 void CRoad::setDifficulty(float const& difficulty)
305 _Difficulty = difficulty;
306 calCost();
309 inline
310 void CRoad::calCost()
312 _CostCoef = _Length*_Difficulty;
315 inline
316 float CRoad::getCost() const
318 return _CostCoef*getRefCount(); // takes account of use.
321 //////////////////////////////////////////////////////////////////////////////
322 // CRoadTrigger //
323 //////////////////////////////////////////////////////////////////////////////
325 inline
326 CRoadTrigger::CRoadTrigger(CRoad* owner, uint32 alias, std::string const& name)
327 : CAliasChild<CRoad>(owner, alias, name)
331 //////////////////////////////////////////////////////////////////////////////
332 // CCell //
333 //////////////////////////////////////////////////////////////////////////////
335 inline
336 size_t CCell::npcZoneCount()
338 return _NpcZonePlaces.size() + _NpcZoneShapes.size();
341 inline
342 CNpcZone* CCell::npcZone(size_t index)
344 if (index<_NpcZonePlaces.size())
345 return _NpcZonePlaces[(uint32)index];
346 else
347 return _NpcZoneShapes[(uint32)index];
350 inline
351 void CCell::getNeighBourgCellList(std::vector<CCell*>& cells) const
353 cells.reserve(_NeighbourCells.size()+1);
354 // build a list of candidate cell to look for the rest zone : ether the current zone or a beighbour
355 std::set<NLMISC::CDbgPtr<CCell> >::const_iterator first(_NeighbourCells.begin()), last(_NeighbourCells.end());
356 for (; first != last; ++first)
357 cells.push_back(*first);
360 //////////////////////////////////////////////////////////////////////////////
361 // CGroupFamily //
362 //////////////////////////////////////////////////////////////////////////////
366 inline
367 CGroupFamily::~CGroupFamily()
369 _GroupDescs.clear();
372 inline
373 void CGroupFamily::setProfileParams(std::string const& str, NLMISC::CVirtualRefCount* objet)
375 #if !FINAL_VERSION
376 nlassert(objet);
377 #endif
378 _Params.insert(std::make_pair(NLMISC::CStringMapper::map(str),objet));
381 inline
382 NLMISC::CVirtualRefCount const* CGroupFamily::getProfileParams(std::string const& str) const
384 TParamsList::const_iterator const it = _Params.find(NLMISC::CStringMapper::map(str));
385 if (it==_Params.end())
386 return NULL;
387 return it->second;
390 inline
391 void CGroupFamily::addProfileProperty(std::string const& propertyName, AITYPES::CPropertySet const& property)
393 _Properties[NLMISC::CStringMapper::map(propertyName)] = property;
396 inline
397 AITYPES::CPropertySet const& CGroupFamily::getProfileProperty(std::string const& propertyName) const
399 static AITYPES::CPropertySet emptyActivities;
401 TActivityList::const_iterator it = _Properties.find(NLMISC::CStringMapper::map(propertyName));
402 if (it==_Properties.end())
403 return emptyActivities;
404 return it->second;
407 //////////////////////////////////////////////////////////////////////////////
408 // CGroupDesc //
409 //////////////////////////////////////////////////////////////////////////////
411 template <class FamilyT>
412 CGroupDesc<FamilyT>::CGroupDesc(FamilyT* owner, uint32 alias, std::string const& name)
413 : CAliasChild<FamilyT>(owner, alias, name)
414 , _BotCount(0)
415 , _CountMultiplier(false)
416 , _GroupEnergyValue((uint32)(0.01*AITYPES::ENERGY_SCALE))
417 , _SpawnType(AITYPES::SpawnTypeAlways)
418 , _EnergyCoef(1.f)
419 , _MultiLevel(false)
420 , _Sheet(NULL)
421 , _MultiLevelSheets(_MultiLevelSheetCount)
422 , _LevelDelta(0)
423 , _PlayerAttackable(true)
424 , _BotAttackable(true)
426 for (size_t j=0; j<_MultiLevelSheetCount; ++j)
427 _MultiLevelSheets[j] = NULL;
428 for (uint32 i=0; i<4; ++i)
430 _SeasonFlags[i] = false;
431 _WeightLevel[i] = 0;
435 template <class FamilyT>
436 bool CGroupDesc<FamilyT>::isValidForDayOrNight(bool const& isDay) const
438 if (_SpawnType == AITYPES::SpawnTypeAlways)
439 return true;
440 return isDay?(_SpawnType == AITYPES::SpawnTypeDay):(_SpawnType == AITYPES::SpawnTypeNight);
443 template <class FamilyT>
444 void CGroupDesc<FamilyT>::setSheet(AISHEETS::ICreatureCPtr const& sheetPtr)
446 #ifdef NL_DEBUG
447 nlassert(sheetPtr);
448 #endif
449 _Sheet = sheetPtr;
452 template <class FamilyT>
453 bool CGroupDesc<FamilyT>::setSheet(std::string const& sheetName)
455 if (!sheetName.empty())
457 if (_MultiLevel)
459 for (size_t i=0; i<_MultiLevelSheetCount; ++i)
461 char letter = char(i/4) + 'b';
462 char number = (i%4) + '1';
463 std::string sheetNameLevel = sheetName+letter+number;
464 // Compute sheet id
465 NLMISC::CSheetId sheetId(sheetNameLevel+".creature");
466 // Find the sheet
467 AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
468 // If the sheet doesn't exist
469 if (sheetId==NLMISC::CSheetId::Unknown || !sheet)
471 nlwarning("Sheet '%s' for group '%s'%s is unknown !",
472 sheetNameLevel.c_str(),
473 this->getFullName().c_str(),
474 this->getAliasString().c_str());
475 return false;
477 _MultiLevelSheets[i] = sheet;
480 else
482 // Compute sheet id
483 NLMISC::CSheetId sheetId(sheetName+".creature");
484 // Find the sheet
485 AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
486 // If the sheet doesn't exist
487 if (sheetId==NLMISC::CSheetId::Unknown || !sheet)
489 nlwarning("Sheet '%s' for group '%s'%s is unknown !",
490 sheetName.c_str(),
491 this->getFullName().c_str(),
492 this->getAliasString().c_str());
493 return false;
495 _Sheet=sheet;
498 return true;
501 template <class FamilyT>
502 AISHEETS::ICreatureCPtr CGroupDesc<FamilyT>::sheet(sint32 baseLevel) const
504 if (_MultiLevel && baseLevel!=-1)
506 sint32 level = baseLevel + getLevelDelta();
507 // Clamp to [0;_MultiLevelSheetCount]
508 level = std::min(level, (sint32)(_MultiLevelSheetCount-1));
509 level = std::max(level, (sint32)0);
510 return _MultiLevelSheets[level];
512 else
513 return _Sheet;
516 template <class FamilyT>
517 uint32 CGroupDesc<FamilyT>::getRealBotCount() const
519 if (_Sheet && getCountMultiplierFlag() && !_MultiLevel)
520 return _BotCount * _Sheet->DynamicGroupCountMultiplier();
521 else
522 return _BotCount;
525 template <class FamilyT>
526 void CGroupDesc<FamilyT>::setSeasonFlags(bool const seasonFlags[4])
528 for (uint32 i=0;i<4;i++)
529 _SeasonFlags[i] = seasonFlags[i];
532 template <class FamilyT>
533 void CGroupDesc<FamilyT>::setWeightLevels(uint32 const weights[4])
535 for (uint32 i=0;i<4;i++)
536 _WeightLevel[i] = weights[i];
539 template <class FamilyT>
540 CGrpFauna* CGroupDesc<FamilyT>::createFaunaGroup(CFamilyBehavior* familyBehavior) const
542 H_AUTO(createFaunaGroup)
544 uint32 energyLevel = familyBehavior->effectiveLevel();
545 AISHEETS::ICreatureCPtr const ls = sheet();
547 const RYAI_MAP_CRUNCH::TAStarFlag AStarFlag=RYAI_MAP_CRUNCH::WaterAndNogo;
549 if (!ls)
551 #if !FINAL_VERSION
552 nlwarning("CRegion::createGroup Can't retreive creature info for sheet '%s' from group desc '%s'%s to spawn in region '%s'%s",
553 (sheet()?sheet()->SheetId().toString().c_str():NLMISC::CSheetId::Unknown.toString().c_str()),
554 this->getAliasFullName().c_str(),
555 this->getAliasString().c_str(),
556 this->getOwner()->getAliasFullName().c_str(),
557 this->getOwner()->getAliasString().c_str()); // CaracSheet
558 #endif
559 return NULL;
562 AITYPES::CPropertySet food, rest;
564 familyBehavior->getActivities (food, rest);
567 nlinfo("Num food activities = %d", (int) food.size());
568 nlinfo("Num rest activities = %d", (int) rest.size());
571 const CFaunaZone *faunaZone=familyBehavior->getOwner()->lookupFaunaZone(rest, AStarFlag, familyBehavior->grpFamily()->getSubstitutionId());
572 if (!faunaZone)
573 return NULL;
575 const CFaunaZone *fzFood=NULL;
576 const CFaunaZone *fzRest=NULL;
578 // select a random spawn zone into the vector
580 std::vector<CCell*> cells;
581 faunaZone->getOwner()->getNeighBourgCellList(cells);
582 cells.push_back(faunaZone->getOwner());
584 if (!familyBehavior->getOwner()->findRestAndFoodFaunaZoneInCellList(fzRest, rest, fzFood, food, cells, AStarFlag))
586 #if !FINAL_VERSION
587 nlwarning("CRegion::createGroup can't find zone pair with properties food: <%s> rest: <%s>, for family %s, group '%s'%s",
588 food.toString().c_str(),
589 rest.toString().c_str(),
590 familyBehavior->getName().c_str(),
591 this->getAliasFullName().c_str(),
592 this->getAliasString().c_str());
593 #endif
594 return NULL;
596 if ( !fzFood
597 || !fzRest)
599 #if !FINAL_VERSION
600 nlwarning("CRegion::createGroup can't find zone pair with properties food: <%s> rest: <%s>, for family %s, group '%s'%s",
601 food.toString().c_str(),
602 rest.toString().c_str(),
603 familyBehavior->getName().c_str(),
604 this->getAliasFullName().c_str(),
605 this->getAliasString().c_str());
606 #endif
607 return NULL;
612 if ( !fzRest
613 && !rest.empty() )
615 #if !FINAL_VERSION
616 nlwarning("Fauna dynamic: CRegion::createGroup can't find fauna zone to rest '%s' in region '%s'%s",
617 familyBehavior->getName().c_str(),
618 this->getAliasFullName().c_str(),
619 this->getAliasString().c_str());
620 #endif
621 return NULL;
624 CMgrFauna *mf = familyBehavior->mgrFauna();
625 CGrpFauna *grp = new CGrpFauna(mf, NULL, AStarFlag);
626 mf->groups().addAliasChild(grp);
628 grp->initDynGrp (this, familyBehavior);
630 /// Fill the fauna group data
631 CAIRefPlaceXYRFauna *refPlace = new CAIRefPlaceXYRFauna(grp,fzRest);
632 refPlace->setupFromOldName("spawn");
633 grp->places().addChild (refPlace, CGrpFauna::SPAWN_PLACE );
634 refPlace = new CAIRefPlaceXYRFauna(grp,fzFood);
635 refPlace->setupFromOldName("food");
636 grp->places().addChild (refPlace, CGrpFauna::EAT_PLACE );
637 refPlace = new CAIRefPlaceXYRFauna(grp,fzRest);
638 refPlace->setupFromOldName("rest");
639 grp->places().addChild (refPlace, CGrpFauna::REST_PLACE );
641 if ( !grp->places()[CGrpFauna::SPAWN_PLACE]->worldValidPos().isValid()
642 || !grp->places()[CGrpFauna::EAT_PLACE]->worldValidPos().isValid()
643 || !grp->places()[CGrpFauna::REST_PLACE]->worldValidPos().isValid() )
645 if (LogGroupCreationFailure)
647 #if !FINAL_VERSION
648 nlwarning("Invalid place to spawn dynamic group '%s'%s",
649 this->getAliasFullName().c_str(),
650 this->getAliasString().c_str());
651 if (!grp->places()[CGrpFauna::SPAWN_PLACE]->worldValidPos().isValid())
652 nlwarning(" Invalid spawn place %s", grp->places()[CGrpFauna::SPAWN_PLACE]->midPos().toString().c_str());
653 if (!grp->places()[CGrpFauna::EAT_PLACE]->worldValidPos().isValid())
654 nlwarning(" Invalid eat place %s", grp->places()[CGrpFauna::EAT_PLACE]->midPos().toString().c_str());
655 if (!grp->places()[CGrpFauna::REST_PLACE]->worldValidPos().isValid())
656 nlwarning(" Invalid rest place %s", grp->places()[CGrpFauna::REST_PLACE]->midPos().toString().c_str());
657 #endif
659 mf->groups().removeChildByIndex(grp->getChildIndex());
660 return NULL;
663 CPopulation *pop = new CPopulation(grp);
665 pop->setWeight(1);
666 pop->setSpawnType(spawnType());
667 pop->addPopRecord(CPopulationRecord(ls, getRealBotCount()));
669 for (size_t i=0; i<_PopulationRecords.size(); ++i)
670 pop->addPopRecord(_PopulationRecords[i]);
673 grp->populations().addAliasChild(pop);
675 grp->setAutoSpawn(false);
676 grp->spawn();
678 if (!grp->getSpawnObj())
680 // the spawning has failed, delete the useless object
681 #if !FINAL_VERSION
682 if (!grp->getSpawnCounter().remainToMax())
683 nldebug("Cannot spawn the dynamic group: maximum reached");
684 else
685 nlwarning("Failed to spawn the dynamic group");
686 #endif
687 mf->groups().removeChildByIndex(grp->getChildIndex());
688 return NULL;
691 return grp;
694 inline
695 static CAIVector randomPos(double dispersionRadius)
697 if (dispersionRadius<=0.)
699 return CAIVector(0., 0.);
701 static const uint32 maxLimit = std::numeric_limits<uint32>::max() >> 1;
702 double rval = (double)CAIS::rand32(maxLimit)/(double)maxLimit; // [0-1[
703 double r = dispersionRadius*sqrt(rval);
704 rval = (double)CAIS::rand32(maxLimit)/(double)maxLimit; // [0-1[
705 double t = 2.0*NLMISC::Pi*rval;
706 double dx = cos(t)*r;
707 double dy = sin(t)*r;
708 return CAIVector(dx, dy);
711 template <class FamilyT>
712 CGroupNpc* CGroupDesc<FamilyT>::createNpcGroup(CMgrNpc* mgr, CAIVector const& pos, double dispersionRadius, sint32 baseLevel, bool spawnBots) const
714 H_AUTO(createNpcGroup)
715 // Keep base level positive or -1 (single level)
716 baseLevel = std::max(baseLevel, (sint32)-1);
717 // Verify we have all the sheets
718 // :TODO: Add verification for named bots sheets
719 if (!sheet(baseLevel) && getBaseBotCount()>0)
721 nlwarning("CGroupDesc::createNpcGroup can't retrieve sheet from group '%s'%s in region '%s'%s", this->getAliasFullName().c_str(), this->getAliasString().c_str(), this->getOwner()->getAliasFullName().c_str(), this->getOwner()->getAliasString().c_str());
722 return NULL;
724 FOREACHC (itBotDesc, typename CCont<CBotDesc<FamilyT> >, botDescs())
726 if (!itBotDesc->sheet(baseLevel))
728 nlwarning("CGroupDesc::createNpcGroup can't retrieve sheet from bot '%s'%s in group '%s'%s in region '%s'%s", itBotDesc->getAliasFullName().c_str(), itBotDesc->getAliasString().c_str(), this->getAliasFullName().c_str(), this->getAliasString().c_str(), this->getOwner()->getAliasFullName().c_str(), this->getOwner()->getAliasString().c_str());
729 return NULL;
732 // Create a group
733 CGroupNpc *grp = new CGroupNpc(mgr, NULL, /*AStarFlag*/RYAI_MAP_CRUNCH::Nothing);
734 // Register it in the manager
735 mgr->groups().addAliasChild(grp);
736 // Set the group parameters
737 grp->setAutoSpawn(false);
738 grp->setName(this->getName());
739 grp->clearParameters();
740 for (uint i=0; i<grpParameters().size(); ++i)
741 grp->addParameter(grpParameters()[i]);
742 grp->setPlayerAttackable(_PlayerAttackable);
743 grp->setBotAttackable(_BotAttackable);
745 // Save whether we have named or unnamed bots
746 if (getRealBotCount() == 0)
747 grp->setBotsAreNamedFlag();
748 else
749 grp->clrBotsAreNamedFlag();
753 uint i=0;
754 // build the specific bots data
755 for (; i<botDescs().size(); ++i)
757 const CBotDesc<FamilyT> *const bd = botDescs()[i];
759 uint nbClone = 1;
760 if (getCountMultiplierFlag())
762 // the group use the multiplier from the creature sheet
763 nbClone *= bd->sheet(baseLevel)->DynamicGroupCountMultiplier();
766 // loop for the requested clones
767 for (uint j=0; j<nbClone; ++j)
769 grp->bots().addChild(new CBotNpc(grp, 0, bd->getBotName()), i); // Doub: 0 instead of bd->getAlias() otherwise all bots will have the same non-zero alias
770 CBotNpc *const bot = static_cast<CBotNpc*>(grp->bots()[i]);
772 bot->setSheet (bd->sheet(baseLevel));
773 bot->equipmentInit ();
774 bot->initEnergy (groupEnergyCoef());
775 CAIVector rpos(pos);
776 if (i!=0)
778 RYAI_MAP_CRUNCH::CWorldMap const& worldMap = CWorldContainer::getWorldMap();
779 RYAI_MAP_CRUNCH::CWorldPosition wp;
780 uint32 maxTries = 100;
783 rpos = pos;
784 rpos += randomPos(dispersionRadius);
785 --maxTries;
787 while (!worldMap.setWorldPosition(AITYPES::vp_auto, wp, rpos) && maxTries>0);
788 if (maxTries<=0)
789 rpos = pos;
791 bot->setStartPos (rpos.x().asDouble(),rpos.y().asDouble(), 0, AITYPES::vp_auto);
795 // build un-named bot
796 uint nbClone = getRealBotCount();
797 for (uint j=0; j<nbClone; ++i,++j)
799 grp->bots().addChild(new CBotNpc(grp, 0, grp->getName()), i); // Doub: 0 instead of getAlias()+i otherwise aliases are wrong
801 CBotNpc *const bot = NLMISC::safe_cast<CBotNpc*>(grp->bots()[i]);
803 bot->setSheet (sheet(baseLevel));
804 bot->equipmentInit ();
805 bot->initEnergy (groupEnergyCoef());
806 CAIVector rpos(pos);
807 if (i!=0)
809 RYAI_MAP_CRUNCH::CWorldMap const& worldMap = CWorldContainer::getWorldMap();
810 RYAI_MAP_CRUNCH::CWorldPosition wp;
811 uint32 maxTries = 100;
814 rpos = pos;
815 rpos += randomPos(dispersionRadius);
816 --maxTries;
818 while (!worldMap.setWorldPosition(AITYPES::vp_auto, wp, rpos) && maxTries>0);
819 if (maxTries<=0)
820 rpos = pos;
822 bot->setStartPos (rpos.x().asDouble(),rpos.y().asDouble(), 0, AITYPES::vp_auto);
827 grp->spawn();
828 if (!grp->getSpawnObj())
830 // the spawning has failed, delete the useless object
831 nlwarning("Failed to spawn the dynamic group");
832 mgr->groups().removeChildByIndex(grp->getChildIndex());
833 return NULL;
835 if (spawnBots)
836 grp->getSpawnObj()->spawnBots();
837 return grp;
840 template <class FamilyT>
841 uint32 CGroupDesc<FamilyT>::calcTotalEnergyValue () const
843 uint32 totalEnergyValue=0;
845 typename CCont<CBotDesc<FamilyT> >::const_iterator it=botDescs().begin(), itEnd=botDescs().end();
846 // add specified bots ..
847 while (it!=itEnd)
849 totalEnergyValue+=it->energyValue();
850 ++it;
854 // add botcount ..
856 if (sheet())
857 totalEnergyValue += getRealBotCount()*sheet()->EnergyValue();
860 std::vector<CPopulationRecord>::const_iterator it=_PopulationRecords.begin(), itEnd=_PopulationRecords.end();
861 while (it!=itEnd)
863 const CPopulationRecord &pr = *it;
864 totalEnergyValue += pr.getEnergyValue(getCountMultiplierFlag());
865 ++it;
869 return totalEnergyValue;
872 template <class FamilyT>
873 IAliasCont *CGroupDesc<FamilyT>::getAliasCont(AITYPES::TAIType type)
875 switch(type)
877 case AITYPES::AITypeSquadTemplateMember:
878 case AITYPES::AITypeBotTemplate:
879 case AITYPES::AITypeBotTemplateMultiLevel:
880 return &_BotDescs;
881 default:
882 return NULL;
887 template <class FamilyT>
888 sint CGroupDesc<FamilyT>::getNbUse() const
890 return getRefCount()-1; // less one because its also referenced by aliascont.
893 template <class FamilyT>
894 CAliasTreeOwner *CGroupDesc<FamilyT>::createChild(IAliasCont *cont, CAIAliasDescriptionNode *aliasTree)
896 if (!cont)
897 return NULL;
899 CAliasTreeOwner* child = NULL;
901 switch(aliasTree->getType())
903 // create the child and adds it to the corresponding position.
904 case AITYPES::AITypeSquadTemplateMember:
905 case AITYPES::AITypeBotTemplate:
906 case AITYPES::AITypeBotTemplateMultiLevel:
907 child = new CBotDesc<FamilyT>(this, aliasTree->getAlias(), aliasTree->getName());
908 break;
909 default:
910 break;
913 if (child)
914 cont->addAliasChild(child);
915 return child;
918 template <class FamilyT>
919 std::string CGroupDesc<FamilyT>::getIndexString() const
921 return this->getOwner()->getIndexString()+NLMISC::toString(":%u", this->getChildIndex());
924 //////////////////////////////////////////////////////////////////////////////
925 // ContextGroupDesc actions //
926 //////////////////////////////////////////////////////////////////////////////
928 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
929 /// make a modification.
930 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_SHEE,FamilyT)
932 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
933 if (!groupDesc)
934 return;
936 std::string lookSheet;
938 if (!getArgs(args,name(), lookSheet))
939 return;
941 if (!groupDesc->setSheet(lookSheet))
943 groupDesc->getOwner()->groupDescs().removeChildByIndex(groupDesc->getChildIndex());
944 CWorkPtr::groupDesc(NULL);
945 return;
949 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
950 /// make a modification.
951 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_LVLD,FamilyT)
953 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
954 if (!groupDesc)
955 return;
957 sint32 levelDelta;
959 if (!getArgs(args,name(), levelDelta))
960 return;
962 groupDesc->setLevelDelta(levelDelta);
965 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
966 /// make a modification.
967 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_SEAS,FamilyT)
969 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
970 if (!groupDesc)
971 return;
973 bool seasons[4];
975 if (!getArgs(args,name(), seasons[0], seasons[1], seasons[2], seasons[3]))
976 return;
978 groupDesc->setSeasonFlags(seasons);
981 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
982 /// make a modification.
983 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_ACT,FamilyT)
985 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
986 if (!groupDesc)
987 return;
989 uint32 spawnType;
990 if (!getArgs(args, name(), spawnType))
991 return;
993 groupDesc->setSpawnType((AITYPES::TSpawnType)spawnType);
996 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
997 /// make a modification.
998 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_APRM,FamilyT)
1000 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
1001 if (!groupDesc)
1002 return;
1004 for (size_t i=0; i<args.size(); ++i)
1006 std::string property;
1007 args[i].get(property);
1008 groupDesc->properties().addProperty(AITYPES::CPropertyId::create(property));
1012 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
1013 /// make a modification.
1014 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_NRG,FamilyT)
1016 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
1017 if (!groupDesc)
1018 return;
1020 uint32 weight[4];
1022 if (!getArgs(args,name(), weight[0], weight[1], weight[2], weight[3]))
1023 return;
1025 groupDesc->setWeightLevels(weight);
1028 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
1029 /// make a modification.
1030 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_EQUI,FamilyT)
1032 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
1033 if (!groupDesc)
1034 return;
1036 groupDesc->botEquipment().clear();
1038 for (size_t i=0; i<args.size(); ++i)
1040 std::string equip;
1041 args[i].get(equip);
1042 groupDesc->botEquipment().push_back(equip);
1046 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
1047 /// make a modification.
1048 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_GPRM,FamilyT)
1050 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
1051 if (!groupDesc)
1052 return;
1054 for (size_t i=0; i<args.size(); ++i)
1056 std::string param;
1057 args[i].get(param);
1059 param = NLMISC::toLowerAscii(param);
1061 if ( param == "contact camp"
1062 || param == "contact outpost"
1063 || param == "contact city"
1064 || param == "boss" )
1065 groupDesc->properties().addProperty(param);
1066 else // unreconized param, leace it for the group instance
1067 groupDesc->grpParameters().push_back(param);
1071 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
1072 /// make a modification.
1073 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,BOTTMPL,FamilyT)
1075 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
1076 if (!groupDesc)
1077 return;
1079 std::string lookSheet;
1080 bool multiLevel;
1082 // read the alias tree from the argument list
1083 CAIAliasDescriptionNode* aliasTree;
1084 if (!getArgs(args, name(), aliasTree, lookSheet, multiLevel))
1085 return;
1087 // see whether the region is already loaded
1088 CBotDesc<FamilyT>* botDesc = groupDesc->botDescs().getChildByAlias(aliasTree->getAlias());
1089 if (!botDesc)
1090 return;
1092 botDesc->setMultiLevel(multiLevel);
1093 botDesc->setSheet(lookSheet);
1095 CWorkPtr::botDesc(botDesc);
1096 CContextStack::setContext(CAISActionEnums::ContextBotDesc);
1099 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
1100 /// make a modification.
1101 DEFINE_ACTION_TEMPLATE1(ContextBotDesc,BT_EQUI,FamilyT)
1103 CBotDesc<FamilyT>* botDesc = static_cast<CBotDesc<FamilyT>*>(CWorkPtr::botDesc());
1104 if (!botDesc)
1105 return;
1107 for (size_t i=0; i<args.size(); ++i)
1109 std::string equip;
1110 args[i].get(equip);
1111 botDesc->equipement().push_back(equip);
1115 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
1116 /// make a modification.
1117 DEFINE_ACTION_TEMPLATE1(ContextBotDesc,BT_LVLD,FamilyT)
1119 CBotDesc<FamilyT>* botDesc = static_cast<CBotDesc<FamilyT>*>(CWorkPtr::botDesc());
1120 if (!botDesc)
1121 return;
1123 sint32 levelDelta;
1125 if (!getArgs(args,name(), levelDelta))
1126 return;
1128 botDesc->setLevelDelta(levelDelta);
1132 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
1133 /// make a modification.
1134 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_GNRJ,FamilyT)
1136 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
1137 if (!groupDesc)
1138 return;
1140 uint32 energyValue;
1142 if (!getArgs(args,name(), energyValue))
1143 return;
1146 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
1147 /// make a modification.
1148 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,POPVER,FamilyT)
1150 // add a population version for a group
1151 // args: uint32 alias, string spawn_type, uint weight, (string sheet, uint32 count)+
1153 if(!CWorkPtr::groupDesc())
1154 return;
1156 const uint32 fixedArgsCount = 0;
1157 if (args.size()<fixedArgsCount+2 || ((args.size()-fixedArgsCount)&1)==1)
1159 nlwarning("POPVER action FAILED due to bad number of arguments (%d)", args.size());
1160 return;
1163 // get hold of the parameters and check their validity
1164 for (size_t i=fixedArgsCount; i+1<args.size(); i+=2)
1166 std::string sheet;
1167 uint32 count;
1169 if ( !args[i].get(sheet)
1170 || !args[i+1].get(count))
1172 nlwarning("POPVER Add Record FAILED due to bad arguments");
1173 continue;
1176 NLMISC::CSheetId sheetId(sheet);
1177 if (sheetId==NLMISC::CSheetId::Unknown)
1179 nlwarning("POPVER Add Record Invalid sheet: %s", sheet.c_str());
1180 continue;
1183 AISHEETS::ICreatureCPtr sheetPtr = AISHEETS::CSheets::getInstance()->lookup(sheetId);
1184 if (!sheetPtr)
1186 nlwarning("POPVER Add Record Invalid sheet: %s", sheet.c_str());
1187 continue;
1189 static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc())->populationRecords().push_back(CPopulationRecord(sheetPtr, count));
1194 /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you
1195 /// make a modification.
1196 // scales bot energy .. to match with group's one.
1197 DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_END,FamilyT)
1199 CGroupDesc<FamilyT>* groupDesc = static_cast<CGroupDesc<FamilyT>*>(CWorkPtr::groupDesc());
1200 if (!groupDesc)
1201 return;
1203 if (!groupDesc->isMultiLevel())
1205 uint32 totalEnergyValue = groupDesc->calcTotalEnergyValue();
1206 if (totalEnergyValue)
1208 double coef = (double)groupDesc->groupEnergyValue()/(double)totalEnergyValue;
1209 groupDesc->setGroupEnergyCoef((float)coef);
1211 else
1213 nlwarning("Retrieved total energy value of 0 for group: %s",groupDesc->getFullName().c_str());
1218 //////////////////////////////////////////////////////////////////////////////
1219 // CBotDesc //
1220 //////////////////////////////////////////////////////////////////////////////
1222 template <class FamilyT>
1223 CBotDesc<FamilyT>::CBotDesc(CGroupDesc<FamilyT>* owner, uint32 alias, std::string const& name)
1224 : CAliasChild<CGroupDesc<FamilyT> >(owner, alias, name)
1225 , _MultiLevel(false)
1226 , _Sheet(NULL)
1227 , _MultiLevelSheets(_MultiLevelSheetCount)
1228 , _LevelDelta(0)
1229 , _UseSheetBotName(false)
1231 for (size_t i=0; i<_MultiLevelSheetCount; ++i)
1232 _MultiLevelSheets[i] = NULL;
1235 template <class FamilyT>
1236 std::string CBotDesc<FamilyT>::getIndexString() const
1238 return this->getOwner()->getIndexString() + NLMISC::toString(":%u", this->getChildIndex());
1241 template <class FamilyT>
1242 void CBotDesc<FamilyT>::setSheet(std::string const& sheetName)
1244 if (!sheetName.empty())
1246 if (_MultiLevel)
1248 for (size_t i=0; i<_MultiLevelSheetCount; ++i)
1250 char letter = char(i/4) + 'b';
1251 char number = (i%4) + '1';
1252 std::string sheetNameLevel = sheetName+letter+number;
1253 // Compute sheet id
1254 NLMISC::CSheetId sheetId(sheetNameLevel+".creature");
1255 // Find the sheet
1256 AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
1257 // If the sheet doesn't exist
1258 if (sheetId==NLMISC::CSheetId::Unknown || !sheet)
1260 nlwarning("Sheet '%s' for bot '%s' is unknown !", sheetNameLevel.c_str(), this->getAliasFullName().c_str());
1262 _MultiLevelSheets[i] = sheet;
1265 else
1267 // Compute sheet id
1268 NLMISC::CSheetId sheetId(sheetName+".creature");
1269 // Find the sheet
1270 AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId);
1271 // If the sheet doesn't exist
1272 if (sheetId==NLMISC::CSheetId::Unknown || !sheet)
1274 nlwarning("Sheet '%s' for bot '%s' is unknown !", sheetName.c_str(), this->getAliasFullName().c_str());
1276 _Sheet = sheet;
1281 template <class FamilyT>
1282 AISHEETS::ICreatureCPtr CBotDesc<FamilyT>::sheet(sint32 baseLevel) const
1284 if (_MultiLevel && baseLevel!=-1)
1286 CGroupDesc<FamilyT>* parent = this->getOwner();
1287 sint32 level = baseLevel + getLevelDelta() + parent->getLevelDelta();
1288 // Clamp to [0;_MultiLevelSheetCount]
1289 level = std::min(level, (sint32)(_MultiLevelSheetCount-1));
1290 level = std::max(level, (sint32)0);
1291 return _MultiLevelSheets[level];
1293 else
1294 return _Sheet;
1297 template <class FamilyT>
1298 uint32 CBotDesc<FamilyT>::energyValue() const
1300 if (!_Sheet && !_MultiLevel)
1301 nlwarning("Bot descriptor has no sheet and is not multilevel, correct above warnings!");
1303 if (_Sheet)
1304 return _Sheet->EnergyValue() * _Sheet->DynamicGroupCountMultiplier();
1305 return 0;
1308 template <class FamilyT>
1309 std::string const& CBotDesc<FamilyT>::getBotName() const
1311 if (_UseSheetBotName && _Sheet && !_Sheet->BotName().empty())
1312 return _Sheet->BotName();
1313 else
1314 return this->getName();
1317 //////////////////////////////////////////////////////////////////////////////
1318 // CCell //
1319 //////////////////////////////////////////////////////////////////////////////
1321 inline
1322 void CCell::unrefZoneInRoads()
1324 FOREACH(it, TAliasZonePlaceList, _NpcZonePlaces)
1325 it->unrefZoneInRoads ();
1326 FOREACH(it, TAliasZoneShapeList, _NpcZoneShapes)
1327 it->unrefZoneInRoads ();
1328 _NeighbourCells.clear ();
1331 //////////////////////////////////////////////////////////////////////////////
1332 // CNpcZone //
1333 //////////////////////////////////////////////////////////////////////////////
1335 // :TODO: check if that inlining is necessary
1336 inline
1337 void CNpcZone::unrefZoneInRoads()
1339 while (!_Roads.empty())
1340 _Roads.back()->unlinkRoad();
1343 //////////////////////////////////////////////////////////////////////////////
1344 // CCellZone //
1345 //////////////////////////////////////////////////////////////////////////////
1347 inline
1348 void CCellZone::unrefZoneInRoads()
1350 FOREACH(it, CCont<CCell>, _Cells)
1351 it->unrefZoneInRoads();
1355 #endif