// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifndef CONTINENT_INLINE_H #define CONTINENT_INLINE_H #include "ai_bot.h" #include "ai_grp_fauna.h" #include "ai_grp_npc.h" #include "ai_bot_npc.h" #include "ais_actions.h" extern NLMISC::CVariable LogAcceptablePos; extern NLMISC::CVariable LogGroupCreationFailure; extern NLMISC::CVariable LogOutpostDebug; ////////////////////////////////////////////////////////////////////////////// // CPopulationRecord // ////////////////////////////////////////////////////////////////////////////// inline CPopulationRecord::CPopulationRecord(AISHEETS::ICreatureCPtr const& sheetData, uint count) : _SheetData(sheetData) , _Count(count) { } inline bool CPopulationRecord::operator==(CPopulationRecord const& other) const { return (_Count==other._Count && _SheetData==other._SheetData); } inline AISHEETS::ICreatureCPtr CPopulationRecord::getCreatureSheet() const { return _SheetData; } inline uint CPopulationRecord::getBotCount(bool useCreatureMultiplier) const { if (useCreatureMultiplier && _SheetData) return _Count * _SheetData->DynamicGroupCountMultiplier(); else return _Count; } inline uint32 CPopulationRecord::getEnergyValue(bool useCreatureMultiplier) const { if (_SheetData) { return getBotCount(useCreatureMultiplier)*_SheetData->EnergyValue(); } else { #ifdef NL_DEBUG nlassert(_SheetData); #endif return 0; } } ////////////////////////////////////////////////////////////////////////////// // CPopulation // ////////////////////////////////////////////////////////////////////////////// inline CPopulation::CPopulation(CPopulationOwner* owner, CAIAliasDescriptionNode* aliasDescription) : CAliasChild(owner, aliasDescription) , _Weight(0) , _SpawnType(AITYPES::SpawnTypeBadType) { } inline CPopulation::CPopulation(CPopulationOwner* owner, uint32 alias, std::string name) : CAliasChild(owner, alias, name) , _Weight(0) , _SpawnType(AITYPES::SpawnTypeBadType) { } inline void CPopulation::addPopRecord(CPopulationRecord popRecord) { _PopRecords.push_back(popRecord); } ////////////////////////////////////////////////////////////////////////////// // CAICircle // ////////////////////////////////////////////////////////////////////////////// /* // Template members must be inside class definitions for VC++6 template CAICircle::CAICircle(V const& center, R radius) : _Center(center) , _Radius(radius) { } // Template members must be inside class definitions for VC++6 template bool CAICircle::isInside(V const& pos) { return (pos - _Center).sqrnorm() <= (_Radius+1)*(_Radius+1); } */ ////////////////////////////////////////////////////////////////////////////// // CAabb // ////////////////////////////////////////////////////////////////////////////// inline CAabb::CAabb() : _VMax(INT_MIN/CAICoord::UNITS_PER_METER, INT_MIN/CAICoord::UNITS_PER_METER) , _VMin(INT_MAX/CAICoord::UNITS_PER_METER, INT_MAX/CAICoord::UNITS_PER_METER) { } /* template CAabb::CAabb(std::vector const& coords) : _VMax(INT_MIN/CAICoord::UNITS_PER_METER, INT_MIN/CAICoord::UNITS_PER_METER) , _VMin(INT_MAX/CAICoord::UNITS_PER_METER, INT_MAX/CAICoord::UNITS_PER_METER) { for (uint k=0; k void CAabb::includePoint(V const& point) { if (point.x() < _VMin.x()) _VMin.setX(point.x()); if (point.x() > _VMax.x()) _VMax.setX(point.x()); if (point.y() < _VMin.y()) _VMin.setY(point.y()); if (point.y() > _VMax.y()) _VMax.setY(point.y()); } */ inline void CAabb::includeAabb(CAabb const& box) { if (box._VMin.x() < _VMin.x()) _VMin.setX(box._VMin.x()); if (box._VMax.x() > _VMax.x()) _VMax.setX(box._VMax.x()); if (box._VMin.y() < _VMin.y()) _VMin.setY(box._VMin.y()); if (box._VMax.y() > _VMax.y()) _VMax.setY(box._VMax.y()); } inline void CAabb::init() { *this = CAabb(); } /* // Template members must be inside class definitions for VC++6 template bool CAabb::isInside(V const& v) { return v.x() >= _VMin.x() && v.y() >= _VMin.y() && v.x() <= _VMax.x() && v.y() <= _VMax.y(); } */ ////////////////////////////////////////////////////////////////////////////// // CFaunaZone // ////////////////////////////////////////////////////////////////////////////// inline bool CFaunaZone::haveActivity(AITYPES::CPropertyId const& activity) const { if (!_AdditionalActivities.empty()) return _AdditionalActivities.have(activity); return _InitialActivities.have(activity); } inline bool CFaunaZone::haveActivity(AITYPES::CPropertySet const& activities) const { if (!_AdditionalActivities.empty()) return _AdditionalActivities.containsPartOfStrict(activities); return _InitialActivities.containsPartOfStrict(activities); } inline float CFaunaZone::getFreeAreaScore() const { float radius = getRadius(); return (float)((radius*radius)/((float)getNbUse()+0.001)); } inline uint32 CFaunaZone::getNbUse() const { sint nbUse = getRefCount()-1; // -1 for the container #ifdef NL_DEBUG nlassert(nbUse>=0); #endif return nbUse; } ////////////////////////////////////////////////////////////////////////////// // CAIRefPlaceXYR // ////////////////////////////////////////////////////////////////////////////// inline CAIRefPlaceXYR::CAIRefPlaceXYR(CPlaceOwner* owner, CAIPlace const* zone) : CAIPlace(owner, NULL) { #ifdef NL_DEBUG nlassert(zone!=NULL); #endif _Zone = zone; } inline CAIRefPlaceXYR::operator CAIPlace const*() const { return _Zone; } ////////////////////////////////////////////////////////////////////////////// // CAIRefPlaceXYRFauna // ////////////////////////////////////////////////////////////////////////////// inline CAIRefPlaceXYRFauna::CAIRefPlaceXYRFauna(CPlaceOwner* owner, CAIPlace const* zone) : CAIRefPlaceXYR(owner, zone) { } ////////////////////////////////////////////////////////////////////////////// // CRebuildContinentAndOutPost // ////////////////////////////////////////////////////////////////////////////// inline CRebuildContinentAndOutPost::CRebuildContinentAndOutPost(CContinent* continent) : _Continent(continent) { } inline bool CRebuildContinentAndOutPost::absorb(CLazyProcess const& lazyProcess) const { CRebuildContinentAndOutPost const* other = dynamic_cast(&lazyProcess); if (!other) return false; return *other==*this; } inline bool CRebuildContinentAndOutPost::operator==(CRebuildContinentAndOutPost const& other) const { return other._Continent==_Continent; } ////////////////////////////////////////////////////////////////////////////// // CContinent // ////////////////////////////////////////////////////////////////////////////// inline CContinent::CContinent(CAIInstance* owner) : CChild(owner) { } ////////////////////////////////////////////////////////////////////////////// // CRoad // ////////////////////////////////////////////////////////////////////////////// inline void CRoad::calcLength() { double length = 0.0f; for (sint j=((sint)_Coords.size())-1; j>0; j--) length += (_Coords[j] - _Coords[j-1]).toAIVector().norm(); _Length = (float)length; calCost(); } inline void CRoad::setDifficulty(float const& difficulty) { _Difficulty = difficulty; calCost(); } inline void CRoad::calCost() { _CostCoef = _Length*_Difficulty; } inline float CRoad::getCost() const { return _CostCoef*getRefCount(); // takes account of use. } ////////////////////////////////////////////////////////////////////////////// // CRoadTrigger // ////////////////////////////////////////////////////////////////////////////// inline CRoadTrigger::CRoadTrigger(CRoad* owner, uint32 alias, std::string const& name) : CAliasChild(owner, alias, name) { } ////////////////////////////////////////////////////////////////////////////// // CCell // ////////////////////////////////////////////////////////////////////////////// inline size_t CCell::npcZoneCount() { return _NpcZonePlaces.size() + _NpcZoneShapes.size(); } inline CNpcZone* CCell::npcZone(size_t index) { if (index<_NpcZonePlaces.size()) return _NpcZonePlaces[(uint32)index]; else return _NpcZoneShapes[(uint32)index]; } inline void CCell::getNeighBourgCellList(std::vector& cells) const { cells.reserve(_NeighbourCells.size()+1); // build a list of candidate cell to look for the rest zone : ether the current zone or a beighbour std::set >::const_iterator first(_NeighbourCells.begin()), last(_NeighbourCells.end()); for (; first != last; ++first) cells.push_back(*first); } ////////////////////////////////////////////////////////////////////////////// // CGroupFamily // ////////////////////////////////////////////////////////////////////////////// inline CGroupFamily::~CGroupFamily() { _GroupDescs.clear(); } inline void CGroupFamily::setProfileParams(std::string const& str, NLMISC::CVirtualRefCount* objet) { #if !FINAL_VERSION nlassert(objet); #endif _Params.insert(std::make_pair(NLMISC::CStringMapper::map(str),objet)); } inline NLMISC::CVirtualRefCount const* CGroupFamily::getProfileParams(std::string const& str) const { TParamsList::const_iterator const it = _Params.find(NLMISC::CStringMapper::map(str)); if (it==_Params.end()) return NULL; return it->second; } inline void CGroupFamily::addProfileProperty(std::string const& propertyName, AITYPES::CPropertySet const& property) { _Properties[NLMISC::CStringMapper::map(propertyName)] = property; } inline AITYPES::CPropertySet const& CGroupFamily::getProfileProperty(std::string const& propertyName) const { static AITYPES::CPropertySet emptyActivities; TActivityList::const_iterator it = _Properties.find(NLMISC::CStringMapper::map(propertyName)); if (it==_Properties.end()) return emptyActivities; return it->second; } ////////////////////////////////////////////////////////////////////////////// // CGroupDesc // ////////////////////////////////////////////////////////////////////////////// template CGroupDesc::CGroupDesc(FamilyT* owner, uint32 alias, std::string const& name) : CAliasChild(owner, alias, name) , _BotCount(0) , _CountMultiplier(false) , _GroupEnergyValue((uint32)(0.01*AITYPES::ENERGY_SCALE)) , _SpawnType(AITYPES::SpawnTypeAlways) , _EnergyCoef(1.f) , _MultiLevel(false) , _Sheet(NULL) , _MultiLevelSheets(_MultiLevelSheetCount) , _LevelDelta(0) , _PlayerAttackable(true) , _BotAttackable(true) { for (size_t j=0; j<_MultiLevelSheetCount; ++j) _MultiLevelSheets[j] = NULL; for (uint32 i=0; i<4; ++i) { _SeasonFlags[i] = false; _WeightLevel[i] = 0; } } template bool CGroupDesc::isValidForDayOrNight(bool const& isDay) const { if (_SpawnType == AITYPES::SpawnTypeAlways) return true; return isDay?(_SpawnType == AITYPES::SpawnTypeDay):(_SpawnType == AITYPES::SpawnTypeNight); } template void CGroupDesc::setSheet(AISHEETS::ICreatureCPtr const& sheetPtr) { #ifdef NL_DEBUG nlassert(sheetPtr); #endif _Sheet = sheetPtr; } template bool CGroupDesc::setSheet(std::string const& sheetName) { if (!sheetName.empty()) { if (_MultiLevel) { for (size_t i=0; i<_MultiLevelSheetCount; ++i) { char letter = char(i/4) + 'b'; char number = (i%4) + '1'; std::string sheetNameLevel = sheetName+letter+number; // Compute sheet id NLMISC::CSheetId sheetId(sheetNameLevel+".creature"); // Find the sheet AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId); // If the sheet doesn't exist if (sheetId==NLMISC::CSheetId::Unknown || !sheet) { nlwarning("Sheet '%s' for group '%s'%s is unknown !", sheetNameLevel.c_str(), this->getFullName().c_str(), this->getAliasString().c_str()); return false; } _MultiLevelSheets[i] = sheet; } } else { // Compute sheet id NLMISC::CSheetId sheetId(sheetName+".creature"); // Find the sheet AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId); // If the sheet doesn't exist if (sheetId==NLMISC::CSheetId::Unknown || !sheet) { nlwarning("Sheet '%s' for group '%s'%s is unknown !", sheetName.c_str(), this->getFullName().c_str(), this->getAliasString().c_str()); return false; } _Sheet=sheet; } } return true; } template AISHEETS::ICreatureCPtr CGroupDesc::sheet(sint32 baseLevel) const { if (_MultiLevel && baseLevel!=-1) { sint32 level = baseLevel + getLevelDelta(); // Clamp to [0;_MultiLevelSheetCount] level = std::min(level, (sint32)(_MultiLevelSheetCount-1)); level = std::max(level, (sint32)0); return _MultiLevelSheets[level]; } else return _Sheet; } template uint32 CGroupDesc::getRealBotCount() const { if (_Sheet && getCountMultiplierFlag() && !_MultiLevel) return _BotCount * _Sheet->DynamicGroupCountMultiplier(); else return _BotCount; } template void CGroupDesc::setSeasonFlags(bool const seasonFlags[4]) { for (uint32 i=0;i<4;i++) _SeasonFlags[i] = seasonFlags[i]; } template void CGroupDesc::setWeightLevels(uint32 const weights[4]) { for (uint32 i=0;i<4;i++) _WeightLevel[i] = weights[i]; } template CGrpFauna* CGroupDesc::createFaunaGroup(CFamilyBehavior* familyBehavior) const { H_AUTO(createFaunaGroup) uint32 energyLevel = familyBehavior->effectiveLevel(); AISHEETS::ICreatureCPtr const ls = sheet(); const RYAI_MAP_CRUNCH::TAStarFlag AStarFlag=RYAI_MAP_CRUNCH::WaterAndNogo; if (!ls) { #if !FINAL_VERSION nlwarning("CRegion::createGroup Can't retreive creature info for sheet '%s' from group desc '%s'%s to spawn in region '%s'%s", (sheet()?sheet()->SheetId().toString().c_str():NLMISC::CSheetId::Unknown.toString().c_str()), this->getAliasFullName().c_str(), this->getAliasString().c_str(), this->getOwner()->getAliasFullName().c_str(), this->getOwner()->getAliasString().c_str()); // CaracSheet #endif return NULL; } AITYPES::CPropertySet food, rest; familyBehavior->getActivities (food, rest); /* nlinfo("Num food activities = %d", (int) food.size()); nlinfo("Num rest activities = %d", (int) rest.size()); */ const CFaunaZone *faunaZone=familyBehavior->getOwner()->lookupFaunaZone(rest, AStarFlag, familyBehavior->grpFamily()->getSubstitutionId()); if (!faunaZone) return NULL; const CFaunaZone *fzFood=NULL; const CFaunaZone *fzRest=NULL; // select a random spawn zone into the vector { std::vector cells; faunaZone->getOwner()->getNeighBourgCellList(cells); cells.push_back(faunaZone->getOwner()); if (!familyBehavior->getOwner()->findRestAndFoodFaunaZoneInCellList(fzRest, rest, fzFood, food, cells, AStarFlag)) { #if !FINAL_VERSION nlwarning("CRegion::createGroup can't find zone pair with properties food: <%s> rest: <%s>, for family %s, group '%s'%s", food.toString().c_str(), rest.toString().c_str(), familyBehavior->getName().c_str(), this->getAliasFullName().c_str(), this->getAliasString().c_str()); #endif return NULL; } if ( !fzFood || !fzRest) { #if !FINAL_VERSION nlwarning("CRegion::createGroup can't find zone pair with properties food: <%s> rest: <%s>, for family %s, group '%s'%s", food.toString().c_str(), rest.toString().c_str(), familyBehavior->getName().c_str(), this->getAliasFullName().c_str(), this->getAliasString().c_str()); #endif return NULL; } } if ( !fzRest && !rest.empty() ) { #if !FINAL_VERSION nlwarning("Fauna dynamic: CRegion::createGroup can't find fauna zone to rest '%s' in region '%s'%s", familyBehavior->getName().c_str(), this->getAliasFullName().c_str(), this->getAliasString().c_str()); #endif return NULL; } CMgrFauna *mf = familyBehavior->mgrFauna(); CGrpFauna *grp = new CGrpFauna(mf, NULL, AStarFlag); mf->groups().addAliasChild(grp); grp->initDynGrp (this, familyBehavior); /// Fill the fauna group data CAIRefPlaceXYRFauna *refPlace = new CAIRefPlaceXYRFauna(grp,fzRest); refPlace->setupFromOldName("spawn"); grp->places().addChild (refPlace, CGrpFauna::SPAWN_PLACE ); refPlace = new CAIRefPlaceXYRFauna(grp,fzFood); refPlace->setupFromOldName("food"); grp->places().addChild (refPlace, CGrpFauna::EAT_PLACE ); refPlace = new CAIRefPlaceXYRFauna(grp,fzRest); refPlace->setupFromOldName("rest"); grp->places().addChild (refPlace, CGrpFauna::REST_PLACE ); if ( !grp->places()[CGrpFauna::SPAWN_PLACE]->worldValidPos().isValid() || !grp->places()[CGrpFauna::EAT_PLACE]->worldValidPos().isValid() || !grp->places()[CGrpFauna::REST_PLACE]->worldValidPos().isValid() ) { if (LogGroupCreationFailure) { #if !FINAL_VERSION nlwarning("Invalid place to spawn dynamic group '%s'%s", this->getAliasFullName().c_str(), this->getAliasString().c_str()); if (!grp->places()[CGrpFauna::SPAWN_PLACE]->worldValidPos().isValid()) nlwarning(" Invalid spawn place %s", grp->places()[CGrpFauna::SPAWN_PLACE]->midPos().toString().c_str()); if (!grp->places()[CGrpFauna::EAT_PLACE]->worldValidPos().isValid()) nlwarning(" Invalid eat place %s", grp->places()[CGrpFauna::EAT_PLACE]->midPos().toString().c_str()); if (!grp->places()[CGrpFauna::REST_PLACE]->worldValidPos().isValid()) nlwarning(" Invalid rest place %s", grp->places()[CGrpFauna::REST_PLACE]->midPos().toString().c_str()); #endif } mf->groups().removeChildByIndex(grp->getChildIndex()); return NULL; } CPopulation *pop = new CPopulation(grp); { pop->setWeight(1); pop->setSpawnType(spawnType()); pop->addPopRecord(CPopulationRecord(ls, getRealBotCount())); for (size_t i=0; i<_PopulationRecords.size(); ++i) pop->addPopRecord(_PopulationRecords[i]); } grp->populations().addAliasChild(pop); grp->setAutoSpawn(false); grp->spawn(); if (!grp->getSpawnObj()) { // the spawning has failed, delete the useless object #if !FINAL_VERSION if (!grp->getSpawnCounter().remainToMax()) nldebug("Cannot spawn the dynamic group: maximum reached"); else nlwarning("Failed to spawn the dynamic group"); #endif mf->groups().removeChildByIndex(grp->getChildIndex()); return NULL; } return grp; } inline static CAIVector randomPos(double dispersionRadius) { if (dispersionRadius<=0.) { return CAIVector(0., 0.); } uint32 const maxLimit=((uint32)~0U)>>1; double rval = (double)CAIS::rand32(maxLimit)/(double)maxLimit; // [0-1[ double r = dispersionRadius*sqrt(rval); rval = (double)CAIS::rand32(maxLimit)/(double)maxLimit; // [0-1[ double t = 2.0*NLMISC::Pi*rval; double dx = cos(t)*r; double dy = sin(t)*r; return CAIVector(dx, dy); } template CGroupNpc* CGroupDesc::createNpcGroup(CMgrNpc* mgr, CAIVector const& pos, double dispersionRadius, sint32 baseLevel, bool spawnBots) const { H_AUTO(createNpcGroup) // Keep base level positive or -1 (single level) baseLevel = std::max(baseLevel, (sint32)-1); // Verify we have all the sheets // :TODO: Add verification for named bots sheets if (!sheet(baseLevel) && getBaseBotCount()>0) { 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()); return NULL; } FOREACHC (itBotDesc, typename CCont >, botDescs()) { if (!itBotDesc->sheet(baseLevel)) { 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()); return NULL; } } // Create a group CGroupNpc *grp = new CGroupNpc(mgr, NULL, /*AStarFlag*/RYAI_MAP_CRUNCH::Nothing); // Register it in the manager mgr->groups().addAliasChild(grp); // Set the group parameters grp->setAutoSpawn(false); grp->setName(this->getName()); grp->clearParameters(); for (uint i=0; iaddParameter(grpParameters()[i]); grp->setPlayerAttackable(_PlayerAttackable); grp->setBotAttackable(_BotAttackable); // Save whether we have named or unnamed bots if (getRealBotCount() == 0) grp->setBotsAreNamedFlag(); else grp->clrBotsAreNamedFlag(); { uint i=0; // build the specific bots data for (; i *const bd = botDescs()[i]; uint nbClone = 1; if (getCountMultiplierFlag()) { // the group use the multiplier from the creature sheet nbClone *= bd->sheet(baseLevel)->DynamicGroupCountMultiplier(); } // loop for the requested clones for (uint j=0; jbots().addChild(new CBotNpc(grp, 0, bd->getBotName()), i); // Doub: 0 instead of bd->getAlias() otherwise all bots will have the same non-zero alias CBotNpc *const bot = static_cast(grp->bots()[i]); bot->setSheet (bd->sheet(baseLevel)); bot->equipmentInit (); bot->initEnergy (groupEnergyCoef()); CAIVector rpos(pos); if (i!=0) { RYAI_MAP_CRUNCH::CWorldMap const& worldMap = CWorldContainer::getWorldMap(); RYAI_MAP_CRUNCH::CWorldPosition wp; uint32 maxTries = 100; do { rpos = pos; rpos += randomPos(dispersionRadius); --maxTries; } while (!worldMap.setWorldPosition(AITYPES::vp_auto, wp, rpos) && maxTries>0); if (maxTries<=0) rpos = pos; } bot->setStartPos (rpos.x().asDouble(),rpos.y().asDouble(), 0, AITYPES::vp_auto); } } // build un-named bot uint nbClone = getRealBotCount(); for (uint j=0; jbots().addChild(new CBotNpc(grp, 0, grp->getName()), i); // Doub: 0 instead of getAlias()+i otherwise aliases are wrong CBotNpc *const bot = NLMISC::safe_cast(grp->bots()[i]); bot->setSheet (sheet(baseLevel)); bot->equipmentInit (); bot->initEnergy (groupEnergyCoef()); CAIVector rpos(pos); if (i!=0) { RYAI_MAP_CRUNCH::CWorldMap const& worldMap = CWorldContainer::getWorldMap(); RYAI_MAP_CRUNCH::CWorldPosition wp; uint32 maxTries = 100; do { rpos = pos; rpos += randomPos(dispersionRadius); --maxTries; } while (!worldMap.setWorldPosition(AITYPES::vp_auto, wp, rpos) && maxTries>0); if (maxTries<=0) rpos = pos; } bot->setStartPos (rpos.x().asDouble(),rpos.y().asDouble(), 0, AITYPES::vp_auto); } } grp->spawn(); if (!grp->getSpawnObj()) { // the spawning has failed, delete the useless object nlwarning("Failed to spawn the dynamic group"); mgr->groups().removeChildByIndex(grp->getChildIndex()); return NULL; } if (spawnBots) grp->getSpawnObj()->spawnBots(); return grp; } template uint32 CGroupDesc::calcTotalEnergyValue () const { uint32 totalEnergyValue=0; { typename CCont >::const_iterator it=botDescs().begin(), itEnd=botDescs().end(); // add specified bots .. while (it!=itEnd) { totalEnergyValue+=it->energyValue(); ++it; } } // add botcount .. if (sheet()) totalEnergyValue += getRealBotCount()*sheet()->EnergyValue(); { std::vector::const_iterator it=_PopulationRecords.begin(), itEnd=_PopulationRecords.end(); while (it!=itEnd) { const CPopulationRecord &pr = *it; totalEnergyValue += pr.getEnergyValue(getCountMultiplierFlag()); ++it; } } return totalEnergyValue; } template IAliasCont *CGroupDesc::getAliasCont(AITYPES::TAIType type) { switch(type) { case AITYPES::AITypeSquadTemplateMember: case AITYPES::AITypeBotTemplate: case AITYPES::AITypeBotTemplateMultiLevel: return &_BotDescs; default: return NULL; } } template sint CGroupDesc::getNbUse() const { return getRefCount()-1; // less one because its also referenced by aliascont. } template CAliasTreeOwner *CGroupDesc::createChild(IAliasCont *cont, CAIAliasDescriptionNode *aliasTree) { if (!cont) return NULL; CAliasTreeOwner* child = NULL; switch(aliasTree->getType()) { // create the child and adds it to the corresponding position. case AITYPES::AITypeSquadTemplateMember: case AITYPES::AITypeBotTemplate: case AITYPES::AITypeBotTemplateMultiLevel: child = new CBotDesc(this, aliasTree->getAlias(), aliasTree->getName()); break; default: break; } if (child) cont->addAliasChild(child); return child; } template std::string CGroupDesc::getIndexString() const { return this->getOwner()->getIndexString()+NLMISC::toString(":%u", this->getChildIndex()); } ////////////////////////////////////////////////////////////////////////////// // ContextGroupDesc actions // ////////////////////////////////////////////////////////////////////////////// /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_SHEE,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; std::string lookSheet; if (!getArgs(args,name(), lookSheet)) return; if (!groupDesc->setSheet(lookSheet)) { groupDesc->getOwner()->groupDescs().removeChildByIndex(groupDesc->getChildIndex()); CWorkPtr::groupDesc(NULL); return; } } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_LVLD,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; sint32 levelDelta; if (!getArgs(args,name(), levelDelta)) return; groupDesc->setLevelDelta(levelDelta); } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_SEAS,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; bool seasons[4]; if (!getArgs(args,name(), seasons[0], seasons[1], seasons[2], seasons[3])) return; groupDesc->setSeasonFlags(seasons); } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_ACT,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; uint32 spawnType; if (!getArgs(args, name(), spawnType)) return; groupDesc->setSpawnType((AITYPES::TSpawnType)spawnType); } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_APRM,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; for (size_t i=0; iproperties().addProperty(AITYPES::CPropertyId::create(property)); } } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_NRG,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; uint32 weight[4]; if (!getArgs(args,name(), weight[0], weight[1], weight[2], weight[3])) return; groupDesc->setWeightLevels(weight); } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_EQUI,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; groupDesc->botEquipment().clear(); for (size_t i=0; ibotEquipment().push_back(equip); } } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_GPRM,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; for (size_t i=0; iproperties().addProperty(param); else // unreconized param, leace it for the group instance groupDesc->grpParameters().push_back(param); } } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,BOTTMPL,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; std::string lookSheet; bool multiLevel; // read the alias tree from the argument list CAIAliasDescriptionNode* aliasTree; if (!getArgs(args, name(), aliasTree, lookSheet, multiLevel)) return; // see whether the region is already loaded CBotDesc* botDesc = groupDesc->botDescs().getChildByAlias(aliasTree->getAlias()); if (!botDesc) return; botDesc->setMultiLevel(multiLevel); botDesc->setSheet(lookSheet); CWorkPtr::botDesc(botDesc); CContextStack::setContext(CAISActionEnums::ContextBotDesc); } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextBotDesc,BT_EQUI,FamilyT) { CBotDesc* botDesc = static_cast*>(CWorkPtr::botDesc()); if (!botDesc) return; for (size_t i=0; iequipement().push_back(equip); } } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextBotDesc,BT_LVLD,FamilyT) { CBotDesc* botDesc = static_cast*>(CWorkPtr::botDesc()); if (!botDesc) return; sint32 levelDelta; if (!getArgs(args,name(), levelDelta)) return; botDesc->setLevelDelta(levelDelta); } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_GNRJ,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; uint32 energyValue; if (!getArgs(args,name(), energyValue)) return; } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,POPVER,FamilyT) { // add a population version for a group // args: uint32 alias, string spawn_type, uint weight, (string sheet, uint32 count)+ if(!CWorkPtr::groupDesc()) return; const uint32 fixedArgsCount = 0; if (args.size()lookup(sheetId); if (!sheetPtr) { nlwarning("POPVER Add Record Invalid sheet: %s", sheet.c_str()); continue; } static_cast*>(CWorkPtr::groupDesc())->populationRecords().push_back(CPopulationRecord(sheetPtr, count)); } } /// :KLUDGE: This code is copied in ai_outpost_actions.h. Update both if you /// make a modification. // scales bot energy .. to match with group's one. DEFINE_ACTION_TEMPLATE1(ContextGroupDesc,GT_END,FamilyT) { CGroupDesc* groupDesc = static_cast*>(CWorkPtr::groupDesc()); if (!groupDesc) return; if (!groupDesc->isMultiLevel()) { uint32 totalEnergyValue = groupDesc->calcTotalEnergyValue(); if (totalEnergyValue) { double coef = (double)groupDesc->groupEnergyValue()/(double)totalEnergyValue; groupDesc->setGroupEnergyCoef((float)coef); } else { nlwarning("Retrieved total energy value of 0 for group: %s",groupDesc->getFullName().c_str()); } } } ////////////////////////////////////////////////////////////////////////////// // CBotDesc // ////////////////////////////////////////////////////////////////////////////// template CBotDesc::CBotDesc(CGroupDesc* owner, uint32 alias, std::string const& name) : CAliasChild >(owner, alias, name) , _MultiLevel(false) , _Sheet(NULL) , _MultiLevelSheets(_MultiLevelSheetCount) , _LevelDelta(0) , _UseSheetBotName(false) { for (size_t i=0; i<_MultiLevelSheetCount; ++i) _MultiLevelSheets[i] = NULL; } template std::string CBotDesc::getIndexString() const { return this->getOwner()->getIndexString() + NLMISC::toString(":%u", this->getChildIndex()); } template void CBotDesc::setSheet(std::string const& sheetName) { if (!sheetName.empty()) { if (_MultiLevel) { for (size_t i=0; i<_MultiLevelSheetCount; ++i) { char letter = char(i/4) + 'b'; char number = (i%4) + '1'; std::string sheetNameLevel = sheetName+letter+number; // Compute sheet id NLMISC::CSheetId sheetId(sheetNameLevel+".creature"); // Find the sheet AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId); // If the sheet doesn't exist if (sheetId==NLMISC::CSheetId::Unknown || !sheet) { nlwarning("Sheet '%s' for bot '%s' is unknown !", sheetNameLevel.c_str(), this->getAliasFullName().c_str()); } _MultiLevelSheets[i] = sheet; } } else { // Compute sheet id NLMISC::CSheetId sheetId(sheetName+".creature"); // Find the sheet AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId); // If the sheet doesn't exist if (sheetId==NLMISC::CSheetId::Unknown || !sheet) { nlwarning("Sheet '%s' for bot '%s' is unknown !", sheetName.c_str(), this->getAliasFullName().c_str()); } _Sheet = sheet; } } } template AISHEETS::ICreatureCPtr CBotDesc::sheet(sint32 baseLevel) const { if (_MultiLevel && baseLevel!=-1) { CGroupDesc* parent = this->getOwner(); sint32 level = baseLevel + getLevelDelta() + parent->getLevelDelta(); // Clamp to [0;_MultiLevelSheetCount] level = std::min(level, (sint32)(_MultiLevelSheetCount-1)); level = std::max(level, (sint32)0); return _MultiLevelSheets[level]; } else return _Sheet; } template uint32 CBotDesc::energyValue() const { if (!_Sheet && !_MultiLevel) nlwarning("Bot descriptor has no sheet and is not multilevel, correct above warnings!"); if (_Sheet) return _Sheet->EnergyValue() * _Sheet->DynamicGroupCountMultiplier(); return 0; } template std::string const& CBotDesc::getBotName() const { if (_UseSheetBotName && _Sheet && !_Sheet->BotName().empty()) return _Sheet->BotName(); else return this->getName(); } ////////////////////////////////////////////////////////////////////////////// // CCell // ////////////////////////////////////////////////////////////////////////////// inline void CCell::unrefZoneInRoads() { FOREACH(it, TAliasZonePlaceList, _NpcZonePlaces) it->unrefZoneInRoads (); FOREACH(it, TAliasZoneShapeList, _NpcZoneShapes) it->unrefZoneInRoads (); _NeighbourCells.clear (); } ////////////////////////////////////////////////////////////////////////////// // CNpcZone // ////////////////////////////////////////////////////////////////////////////// // :TODO: check if that inlining is necessary inline void CNpcZone::unrefZoneInRoads() { while (!_Roads.empty()) _Roads.back()->unlinkRoad(); } ////////////////////////////////////////////////////////////////////////////// // CCellZone // ////////////////////////////////////////////////////////////////////////////// inline void CCellZone::unrefZoneInRoads() { FOREACH(it, CCont, _Cells) it->unrefZoneInRoads(); } #endif