1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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
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 //////////////////////////////////////////////////////////////////////////////
38 CPopulationRecord::CPopulationRecord(AISHEETS::ICreatureCPtr
const& sheetData
, uint count
)
39 : _SheetData(sheetData
)
45 bool CPopulationRecord::operator==(CPopulationRecord
const& other
) const
47 return (_Count
==other
._Count
&& _SheetData
==other
._SheetData
);
51 AISHEETS::ICreatureCPtr
CPopulationRecord::getCreatureSheet() const
57 uint
CPopulationRecord::getBotCount(bool useCreatureMultiplier
) const
59 if (useCreatureMultiplier
&& _SheetData
)
60 return _Count
* _SheetData
->DynamicGroupCountMultiplier();
66 uint32
CPopulationRecord::getEnergyValue(bool useCreatureMultiplier
) const
70 return getBotCount(useCreatureMultiplier
)*_SheetData
->EnergyValue();
81 //////////////////////////////////////////////////////////////////////////////
83 //////////////////////////////////////////////////////////////////////////////
86 CPopulation::CPopulation(CPopulationOwner
* owner
, CAIAliasDescriptionNode
* aliasDescription
)
87 : CAliasChild
<CPopulationOwner
>(owner
, aliasDescription
)
89 , _SpawnType(AITYPES::SpawnTypeBadType
)
94 CPopulation::CPopulation(CPopulationOwner
* owner
, uint32 alias
, std::string name
)
95 : CAliasChild
<CPopulationOwner
>(owner
, alias
, name
)
97 , _SpawnType(AITYPES::SpawnTypeBadType
)
102 void CPopulation::addPopRecord(CPopulationRecord popRecord
)
104 _PopRecords
.push_back(popRecord
);
107 //////////////////////////////////////////////////////////////////////////////
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)
119 // Template members must be inside class definitions for VC++6
121 bool CAICircle::isInside(V const& pos)
123 return (pos - _Center).sqrnorm() <= (_Radius+1)*(_Radius+1);
126 //////////////////////////////////////////////////////////////////////////////
128 //////////////////////////////////////////////////////////////////////////////
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
)
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]);
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());
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());
178 // Template members must be inside class definitions for VC++6
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 //////////////////////////////////////////////////////////////////////////////
188 //////////////////////////////////////////////////////////////////////////////
191 bool CFaunaZone::haveActivity(AITYPES::CPropertyId
const& activity
) const
193 if (!_AdditionalActivities
.empty())
194 return _AdditionalActivities
.have(activity
);
195 return _InitialActivities
.have(activity
);
199 bool CFaunaZone::haveActivity(AITYPES::CPropertySet
const& activities
) const
201 if (!_AdditionalActivities
.empty())
202 return _AdditionalActivities
.containsPartOfStrict(activities
);
203 return _InitialActivities
.containsPartOfStrict(activities
);
207 float CFaunaZone::getFreeAreaScore() const
209 float radius
= getRadius();
210 return (float)((radius
*radius
)/((float)getNbUse()+0.001));
214 uint32
CFaunaZone::getNbUse() const
216 sint nbUse
= getRefCount()-1; // -1 for the container
223 //////////////////////////////////////////////////////////////////////////////
225 //////////////////////////////////////////////////////////////////////////////
228 CAIRefPlaceXYR::CAIRefPlaceXYR(CPlaceOwner
* owner
, CAIPlace
const* zone
)
229 : CAIPlace(owner
, NULL
)
232 nlassert(zone
!=NULL
);
238 CAIRefPlaceXYR::operator CAIPlace
const*() const
243 //////////////////////////////////////////////////////////////////////////////
244 // CAIRefPlaceXYRFauna //
245 //////////////////////////////////////////////////////////////////////////////
248 CAIRefPlaceXYRFauna::CAIRefPlaceXYRFauna(CPlaceOwner
* owner
, CAIPlace
const* zone
)
249 : CAIRefPlaceXYR(owner
, zone
)
253 //////////////////////////////////////////////////////////////////////////////
254 // CRebuildContinentAndOutPost //
255 //////////////////////////////////////////////////////////////////////////////
258 CRebuildContinentAndOutPost::CRebuildContinentAndOutPost(CContinent
* continent
)
259 : _Continent(continent
)
264 bool CRebuildContinentAndOutPost::absorb(CLazyProcess
const& lazyProcess
) const
266 CRebuildContinentAndOutPost
const* other
= dynamic_cast<CRebuildContinentAndOutPost
const*>(&lazyProcess
);
269 return *other
==*this;
273 bool CRebuildContinentAndOutPost::operator==(CRebuildContinentAndOutPost
const& other
) const
275 return other
._Continent
==_Continent
;
278 //////////////////////////////////////////////////////////////////////////////
280 //////////////////////////////////////////////////////////////////////////////
283 CContinent::CContinent(CAIInstance
* owner
)
284 : CChild
<CAIInstance
>(owner
)
288 //////////////////////////////////////////////////////////////////////////////
290 //////////////////////////////////////////////////////////////////////////////
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
;
303 void CRoad::setDifficulty(float const& difficulty
)
305 _Difficulty
= difficulty
;
310 void CRoad::calCost()
312 _CostCoef
= _Length
*_Difficulty
;
316 float CRoad::getCost() const
318 return _CostCoef
*getRefCount(); // takes account of use.
321 //////////////////////////////////////////////////////////////////////////////
323 //////////////////////////////////////////////////////////////////////////////
326 CRoadTrigger::CRoadTrigger(CRoad
* owner
, uint32 alias
, std::string
const& name
)
327 : CAliasChild
<CRoad
>(owner
, alias
, name
)
331 //////////////////////////////////////////////////////////////////////////////
333 //////////////////////////////////////////////////////////////////////////////
336 size_t CCell::npcZoneCount()
338 return _NpcZonePlaces
.size() + _NpcZoneShapes
.size();
342 CNpcZone
* CCell::npcZone(size_t index
)
344 if (index
<_NpcZonePlaces
.size())
345 return _NpcZonePlaces
[(uint32
)index
];
347 return _NpcZoneShapes
[(uint32
)index
];
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 //////////////////////////////////////////////////////////////////////////////
362 //////////////////////////////////////////////////////////////////////////////
367 CGroupFamily::~CGroupFamily()
373 void CGroupFamily::setProfileParams(std::string
const& str
, NLMISC::CVirtualRefCount
* objet
)
378 _Params
.insert(std::make_pair(NLMISC::CStringMapper::map(str
),objet
));
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())
391 void CGroupFamily::addProfileProperty(std::string
const& propertyName
, AITYPES::CPropertySet
const& property
)
393 _Properties
[NLMISC::CStringMapper::map(propertyName
)] = property
;
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
;
407 //////////////////////////////////////////////////////////////////////////////
409 //////////////////////////////////////////////////////////////////////////////
411 template <class FamilyT
>
412 CGroupDesc
<FamilyT
>::CGroupDesc(FamilyT
* owner
, uint32 alias
, std::string
const& name
)
413 : CAliasChild
<FamilyT
>(owner
, alias
, name
)
415 , _CountMultiplier(false)
416 , _GroupEnergyValue((uint32
)(0.01*AITYPES::ENERGY_SCALE
))
417 , _SpawnType(AITYPES::SpawnTypeAlways
)
421 , _MultiLevelSheets(_MultiLevelSheetCount
)
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;
435 template <class FamilyT
>
436 bool CGroupDesc
<FamilyT
>::isValidForDayOrNight(bool const& isDay
) const
438 if (_SpawnType
== AITYPES::SpawnTypeAlways
)
440 return isDay
?(_SpawnType
== AITYPES::SpawnTypeDay
):(_SpawnType
== AITYPES::SpawnTypeNight
);
443 template <class FamilyT
>
444 void CGroupDesc
<FamilyT
>::setSheet(AISHEETS::ICreatureCPtr
const& sheetPtr
)
452 template <class FamilyT
>
453 bool CGroupDesc
<FamilyT
>::setSheet(std::string
const& sheetName
)
455 if (!sheetName
.empty())
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
;
465 NLMISC::CSheetId
sheetId(sheetNameLevel
+".creature");
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());
477 _MultiLevelSheets
[i
] = sheet
;
483 NLMISC::CSheetId
sheetId(sheetName
+".creature");
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 !",
491 this->getFullName().c_str(),
492 this->getAliasString().c_str());
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
];
516 template <class FamilyT
>
517 uint32 CGroupDesc
<FamilyT
>::getRealBotCount() const
519 if (_Sheet
&& getCountMultiplierFlag() && !_MultiLevel
)
520 return _BotCount
* _Sheet
->DynamicGroupCountMultiplier();
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
;
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
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());
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
))
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());
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());
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());
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
)
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());
659 mf
->groups().removeChildByIndex(grp
->getChildIndex());
663 CPopulation
*pop
= new CPopulation(grp
);
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);
678 if (!grp
->getSpawnObj())
680 // the spawning has failed, delete the useless object
682 if (!grp
->getSpawnCounter().remainToMax())
683 nldebug("Cannot spawn the dynamic group: maximum reached");
685 nlwarning("Failed to spawn the dynamic group");
687 mf
->groups().removeChildByIndex(grp
->getChildIndex());
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());
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());
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();
749 grp
->clrBotsAreNamedFlag();
754 // build the specific bots data
755 for (; i
<botDescs().size(); ++i
)
757 const CBotDesc
<FamilyT
> *const bd
= botDescs()[i
];
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());
778 RYAI_MAP_CRUNCH::CWorldMap
const& worldMap
= CWorldContainer::getWorldMap();
779 RYAI_MAP_CRUNCH::CWorldPosition wp
;
780 uint32 maxTries
= 100;
784 rpos
+= randomPos(dispersionRadius
);
787 while (!worldMap
.setWorldPosition(AITYPES::vp_auto
, wp
, rpos
) && maxTries
>0);
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());
809 RYAI_MAP_CRUNCH::CWorldMap
const& worldMap
= CWorldContainer::getWorldMap();
810 RYAI_MAP_CRUNCH::CWorldPosition wp
;
811 uint32 maxTries
= 100;
815 rpos
+= randomPos(dispersionRadius
);
818 while (!worldMap
.setWorldPosition(AITYPES::vp_auto
, wp
, rpos
) && maxTries
>0);
822 bot
->setStartPos (rpos
.x().asDouble(),rpos
.y().asDouble(), 0, AITYPES::vp_auto
);
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());
836 grp
->getSpawnObj()->spawnBots();
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 ..
849 totalEnergyValue
+=it
->energyValue();
857 totalEnergyValue
+= getRealBotCount()*sheet()->EnergyValue();
860 std::vector
<CPopulationRecord
>::const_iterator it
=_PopulationRecords
.begin(), itEnd
=_PopulationRecords
.end();
863 const CPopulationRecord
&pr
= *it
;
864 totalEnergyValue
+= pr
.getEnergyValue(getCountMultiplierFlag());
869 return totalEnergyValue
;
872 template <class FamilyT
>
873 IAliasCont
*CGroupDesc
<FamilyT
>::getAliasCont(AITYPES::TAIType type
)
877 case AITYPES::AITypeSquadTemplateMember
:
878 case AITYPES::AITypeBotTemplate
:
879 case AITYPES::AITypeBotTemplateMultiLevel
:
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
)
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());
914 cont
->addAliasChild(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());
936 std::string lookSheet
;
938 if (!getArgs(args
,name(), lookSheet
))
941 if (!groupDesc
->setSheet(lookSheet
))
943 groupDesc
->getOwner()->groupDescs().removeChildByIndex(groupDesc
->getChildIndex());
944 CWorkPtr::groupDesc(NULL
);
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());
959 if (!getArgs(args
,name(), levelDelta
))
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());
975 if (!getArgs(args
,name(), seasons
[0], seasons
[1], seasons
[2], seasons
[3]))
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());
990 if (!getArgs(args
, name(), spawnType
))
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());
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());
1022 if (!getArgs(args
,name(), weight
[0], weight
[1], weight
[2], weight
[3]))
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());
1036 groupDesc
->botEquipment().clear();
1038 for (size_t i
=0; i
<args
.size(); ++i
)
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());
1054 for (size_t i
=0; i
<args
.size(); ++i
)
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());
1079 std::string lookSheet
;
1082 // read the alias tree from the argument list
1083 CAIAliasDescriptionNode
* aliasTree
;
1084 if (!getArgs(args
, name(), aliasTree
, lookSheet
, multiLevel
))
1087 // see whether the region is already loaded
1088 CBotDesc
<FamilyT
>* botDesc
= groupDesc
->botDescs().getChildByAlias(aliasTree
->getAlias());
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());
1107 for (size_t i
=0; i
<args
.size(); ++i
)
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());
1125 if (!getArgs(args
,name(), levelDelta
))
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());
1142 if (!getArgs(args
,name(), energyValue
))
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())
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());
1163 // get hold of the parameters and check their validity
1164 for (size_t i
=fixedArgsCount
; i
+1<args
.size(); i
+=2)
1169 if ( !args
[i
].get(sheet
)
1170 || !args
[i
+1].get(count
))
1172 nlwarning("POPVER Add Record FAILED due to bad arguments");
1176 NLMISC::CSheetId
sheetId(sheet
);
1177 if (sheetId
==NLMISC::CSheetId::Unknown
)
1179 nlwarning("POPVER Add Record Invalid sheet: %s", sheet
.c_str());
1183 AISHEETS::ICreatureCPtr sheetPtr
= AISHEETS::CSheets::getInstance()->lookup(sheetId
);
1186 nlwarning("POPVER Add Record Invalid sheet: %s", sheet
.c_str());
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());
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
);
1213 nlwarning("Retrieved total energy value of 0 for group: %s",groupDesc
->getFullName().c_str());
1218 //////////////////////////////////////////////////////////////////////////////
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)
1227 , _MultiLevelSheets(_MultiLevelSheetCount
)
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())
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
;
1254 NLMISC::CSheetId
sheetId(sheetNameLevel
+".creature");
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
;
1268 NLMISC::CSheetId
sheetId(sheetName
+".creature");
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());
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
];
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!");
1304 return _Sheet
->EnergyValue() * _Sheet
->DynamicGroupCountMultiplier();
1308 template <class FamilyT
>
1309 std::string
const& CBotDesc
<FamilyT
>::getBotName() const
1311 if (_UseSheetBotName
&& _Sheet
&& !_Sheet
->BotName().empty())
1312 return _Sheet
->BotName();
1314 return this->getName();
1317 //////////////////////////////////////////////////////////////////////////////
1319 //////////////////////////////////////////////////////////////////////////////
1322 void CCell::unrefZoneInRoads()
1324 FOREACH(it
, TAliasZonePlaceList
, _NpcZonePlaces
)
1325 it
->unrefZoneInRoads ();
1326 FOREACH(it
, TAliasZoneShapeList
, _NpcZoneShapes
)
1327 it
->unrefZoneInRoads ();
1328 _NeighbourCells
.clear ();
1331 //////////////////////////////////////////////////////////////////////////////
1333 //////////////////////////////////////////////////////////////////////////////
1335 // :TODO: check if that inlining is necessary
1337 void CNpcZone::unrefZoneInRoads()
1339 while (!_Roads
.empty())
1340 _Roads
.back()->unlinkRoad();
1343 //////////////////////////////////////////////////////////////////////////////
1345 //////////////////////////////////////////////////////////////////////////////
1348 void CCellZone::unrefZoneInRoads()
1350 FOREACH(it
, CCont
<CCell
>, _Cells
)
1351 it
->unrefZoneInRoads();