// 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 . #include "stdpch.h" #include "ai_profile_fauna.h" #include "ai_bot_fauna.h" #include "ai_grp_fauna.h" #include "ai_mgr_fauna.h" //- Compact logging facilities ----------------------------------------------- CFaunaProfileFloodLogger CWanderFaunaProfile::_FloodLogger(600); CFaunaProfileFloodLogger CGrazeFaunaProfile::_FloodLogger(600); CFaunaProfileFloodLogger CRestFaunaProfile::_FloodLogger(600); //- Profile factories singletons --------------------------------------------- CPlanteIdleFaunaProfileFactory PlanteIdleFaunaProfileFactory; CWanderFaunaProfileFactory WanderFaunaProfileFactory; CGrazeFaunaProfileFactory GrazeFaunaProfileFactory; CRestFaunaProfileFactory RestFaunaProfileFactory; CFightFaunaProfileFactory FightFaunaProfileFactory; CCorpseFaunaProfileFactory CorpseFaunaProfileFactory; CEatCorpseFaunaProfileFactory EatCorpseFaunaProfileFactory; CCuriosityFaunaProfileFactory CuriosityFaunaProfileFactory; ////////////////////////////////////////////////////////////////////////////// // CBotProfileFightFauna // ////////////////////////////////////////////////////////////////////////////// class CBotProfileFightFauna : public CBotProfileFight { public: CBotProfileFightFauna(CProfileOwner* owner, CAIEntityPhysical* ennemy); virtual std::string getOneLineInfoString() const { return NLMISC::toString("fight fauna bot profile"); } void noMoreTarget(); void eventBeginFight(); void eventTargetKilled(); }; ////////////////////////////////////////////////////////////////////////////// // CBotProfileHealFauna // ////////////////////////////////////////////////////////////////////////////// class CBotProfileHealFauna : public CBotProfileHeal { public: CBotProfileHealFauna(TDataSetRow const& row, CProfileOwner* owner); virtual std::string getOneLineInfoString() const { return NLMISC::toString("heal fauna bot profile"); } void noMoreTarget(); // void eventBeginFight(); // void eventTargetKilled(); }; ////////////////////////////////////////////////////////////////////////////// // CBotProfileReturnAfterFightFauna // ////////////////////////////////////////////////////////////////////////////// class CBotProfileReturnAfterFightFauna : public CBotProfileReturnAfterFight , public IMouvementMagnetOwner { public: CBotProfileReturnAfterFightFauna(CProfileOwner* owner); ~CBotProfileReturnAfterFightFauna(); virtual void beginProfile(); virtual void endProfile(); virtual void updateProfile(uint ticksSinceLastUpdate); virtual std::string getOneLineInfoString() const { return NLMISC::toString("return_after_fight fauna bot profile"); } virtual NLMISC::CSmartPtr const& getMovementMagnet() const { return _MovementMagnet; } private: NLMISC::CSmartPtr _MovementMagnet; }; ////////////////////////////////////////////////////////////////////////////// // CBotProfileFightFauna // ////////////////////////////////////////////////////////////////////////////// CBotProfileFightFauna::CBotProfileFightFauna(CProfileOwner* owner, CAIEntityPhysical* ennemy) : CBotProfileFight(owner, ennemy) { } void CBotProfileFightFauna::noMoreTarget() { static_cast(_Bot.ptr())->setDefaultComportment(); } void CBotProfileFightFauna::eventBeginFight() { CSpawnBotFauna* spawnable= NLMISC::safe_cast(_Bot.ptr()); CGrpFauna &grpFauna = static_cast(*spawnable->getPersistent().getOwner()); grpFauna.processStateEvent(grpFauna.getEventContainer().EventBotBeginFight); } void CBotProfileFightFauna::eventTargetKilled() { CSpawnBotFauna* spawnable= NLMISC::safe_cast(_Bot.ptr()); CGrpFauna *grpFauna = static_cast(spawnable->getPersistent().getOwner()); grpFauna->processStateEvent(grpFauna->getEventContainer().EventBotTargetKilled); } ////////////////////////////////////////////////////////////////////////////// // CBotProfileHealFauna // ////////////////////////////////////////////////////////////////////////////// CBotProfileHealFauna::CBotProfileHealFauna(TDataSetRow const& row, CProfileOwner* owner) : CBotProfileHeal(row, owner) { } void CBotProfileHealFauna::noMoreTarget() { static_cast(_Bot.ptr())->setDefaultComportment(); } ////////////////////////////////////////////////////////////////////////////// // CBotProfileReturnAfterFightFauna // ////////////////////////////////////////////////////////////////////////////// CBotProfileReturnAfterFightFauna::CBotProfileReturnAfterFightFauna(CProfileOwner* owner) : CBotProfileReturnAfterFight(owner) { CSpawnBotFauna* bot = NLMISC::safe_cast(owner); RYAI_MAP_CRUNCH::TAStarFlag denyFlags = bot->getAStarFlag(); _MovementMagnet = NLMISC::CSmartPtr(new CReturnMovementMagnet(bot->getReturnPos(), *bot, bot->getAStarFlag())); } CBotProfileReturnAfterFightFauna::~CBotProfileReturnAfterFightFauna() { } void CBotProfileReturnAfterFightFauna::beginProfile() { // PROFILE_LOG("bot_fauna", "return_after_fight", "begin", ""); CBotProfileReturnAfterFight::beginProfile(); _Bot->ignoreReturnAggro(true); } void CBotProfileReturnAfterFightFauna::endProfile() { // PROFILE_LOG("bot_fauna", "return_after_fight", "end", ""); _Bot->ignoreReturnAggro(false); CBotProfileReturnAfterFight::endProfile(); } void CBotProfileReturnAfterFightFauna::updateProfile(uint ticksSinceLastUpdate) { H_AUTO(CBotProfileReturnAfterFightFaunaUpdate); CBotProfileReturnAfterFight::updateProfile(ticksSinceLastUpdate); _MovementMagnet->update(0, ticksSinceLastUpdate); } ////////////////////////////////////////////////////////////////////////////// // CSpawnBotFauna // ////////////////////////////////////////////////////////////////////////////// void CSpawnBotFauna::doFight(CAIEntityPhysical* ennemy) { #ifdef NL_DEBUG nlassert(ennemy); #endif setAIProfile(new CBotProfileFightFauna(this, ennemy)); } void CSpawnBotFauna::setDefaultComportment() { if (getPersistent().getRyzomType()==RYZOMID::flora) setAIProfile(this, &PlanteIdleFaunaProfileFactory, false); else setAIProfile(this, &WanderFaunaProfileFactory, false); } void CSpawnGroupFauna::setFight(CSpawnBot* bot, CAIEntityPhysical* ennemy) { bot->setAIProfile(new CBotProfileFightFauna(bot, ennemy)); } void CSpawnGroupFauna::setHeal(CSpawnBot* bot, CAIEntityPhysical* target) { bot->setAIProfile(new CBotProfileHealFauna(target->dataSetRow(), bot)); } void CSpawnGroupFauna::setNoFight(CSpawnBot* bot) { if (!bot->getTarget().isNULL()) bot->setTarget(NULL); if ( bot->getAIProfileType()==AITYPES::BOT_FLEE || bot->getAIProfileType()==AITYPES::BOT_FIGHT || bot->getAIProfileType()==AITYPES::BOT_HEAL || bot->getAIProfileType()==AITYPES::BOT_RETURN_AFTER_FIGHT ) { static_cast(bot)->setDefaultComportment(); } } void CSpawnGroupFauna::setFlee(CSpawnBot* bot, CAIVector& fleeVect) { bot->setMoveDecalage(fleeVect); if (bot->getAIProfileType()!=AITYPES::BOT_FLEE) { bot->setAIProfile(new CBotProfileFlee(bot)); } } void CSpawnGroupFauna::setReturnAfterFight(CSpawnBot* bot) { if (bot->getAIProfileType()!=AITYPES::BOT_RETURN_AFTER_FIGHT) { bot->setAIProfile(new CBotProfileReturnAfterFightFauna(bot)); } } ////////////////////////////////////////////////////////////////////////////// // CPlanteIdleFaunaProfile // ////////////////////////////////////////////////////////////////////////////// CPlanteIdleFaunaProfile::CPlanteIdleFaunaProfile(CProfileOwner* owner) : CAIBaseProfile() , _Bot(NLMISC::safe_cast(owner)) { } void CPlanteIdleFaunaProfile::beginProfile() { _Bot->setMode(MBEHAV::NORMAL); } void CPlanteIdleFaunaProfile::endProfile() { } void CPlanteIdleFaunaProfile::updateProfile(uint ticksSinceLastUpdate) { } std::string CPlanteIdleFaunaProfile::getOneLineInfoString() const { return NLMISC::toString("plante_idle fauna profile"); } ////////////////////////////////////////////////////////////////////////////// // CAIFaunaActivityBaseSpawnProfile // ////////////////////////////////////////////////////////////////////////////// CAIFaunaActivityBaseSpawnProfile::CAIFaunaActivityBaseSpawnProfile(CProfileOwner* owner) : CAIBaseProfile() , _PathPos(NLMISC::safe_cast(owner)->theta()) , _OutOfMagnet(false) { } NLMISC::CSmartPtr const& CAIFaunaActivityBaseSpawnProfile::getMovementMagnet() const { return _MovementMagnet; } ////////////////////////////////////////////////////////////////////////////// // CWanderFaunaProfile // ////////////////////////////////////////////////////////////////////////////// CWanderFaunaProfile::CWanderFaunaProfile(CProfileOwner* owner) : CAIFaunaActivityBaseSpawnProfile(owner) , _Bot(NLMISC::safe_cast(owner)) { _DenyFlags = _Bot->getAStarFlag(); _MovementMagnet = new CMovementMagnet(*_Bot, _DenyFlags); } void CWanderFaunaProfile::beginProfile() { _Bot->setMode(MBEHAV::NORMAL); _OutOfMagnet=false; } void CWanderFaunaProfile::endProfile() { } void CWanderFaunaProfile::updateProfile(uint ticksSinceLastUpdate) { H_AUTO(WanderFaunaProfileUpdate) CFollowPathContext fpcWanderFaunaProfileUpdate("WanderFaunaProfileUpdate"); // calculate distance from bot position to magnet point (used in all the different processes) _magnetDistSq=_Bot->pos().distSqTo(_Bot->spawnGrp().magnetPos()); double grpMagnetRadiusFar=_Bot->spawnGrp().magnetRadiusFar(); if (_magnetDistSq>(grpMagnetRadiusFar*grpMagnetRadiusFar)) { _Bot->setMode( MBEHAV::NORMAL ); if (!_OutOfMagnet) { _PathPos._Angle=_Bot->theta(); _OutOfMagnet=true; _MovementMagnet=NULL; } if (_Bot->canMove()) { const float dist=_Bot->walkSpeed()*ticksSinceLastUpdate; CFollowPath::TFollowStatus const status = CFollowPath::getInstance()->followPath( _Bot, _PathPos, _Bot->spawnGrp().getPathCont(), dist, dist*0.71f, 0.5f); if (status==CFollowPath::FOLLOW_NO_PATH) { CFollowPath::TFollowReason lastReason = CFollowPath::getInstance()->lastReason(); RYAI_MAP_CRUNCH::CWorldMap::TFindInsideAStarPathReason lastFIASPReason = CFollowPath::getInstance()->lastFIASPReason(); #ifdef COMPACT_POS_WARNINGS int logTick = CTimeInterface::gameCycle(); std::string error = _Bot->spawnGrp().getPathCont().getDestPos().toString() + " " + CFollowPath::toString(lastReason, lastFIASPReason); ++_FloodLogger.logPositions[error]; if ((logTick-_FloodLogger.logLastTick)>_FloodLogger.logPeriod) // Log errors every minute // :TODO: Put that in a config thing { if (!_FloodLogger.logPositions.empty()) { nlwarning("bots cannot reach their groups while wandering, positions are:"); FOREACH(it, CFaunaProfileFloodLogger::TLogPositions, _FloodLogger.logPositions) { nlwarning(" - %s: %d times", it->first.c_str(), it->second); } _FloodLogger.logPositions.clear(); } _FloodLogger.logLastTick = logTick; } #else nlwarning("Problem with a bot that cannot reach its group from %s to %s %s", _Bot->pos().toString().c_str(), _Bot->spawnGrp().getPathCont().getDestination().toString().c_str(), CFollowPath::toString(lastReason, lastFIASPReason).c_str()); #endif } } return; } _OutOfMagnet=false; if (_MovementMagnet.isNull()) _MovementMagnet=new CMovementMagnet(*_Bot,_DenyFlags); _MovementMagnet->update(30, ticksSinceLastUpdate); } std::string CWanderFaunaProfile::getOneLineInfoString() const { return NLMISC::toString("wander fauna profile"); } ////////////////////////////////////////////////////////////////////////////// // CGrazeFaunaProfile // ////////////////////////////////////////////////////////////////////////////// CGrazeFaunaProfile::CGrazeFaunaProfile(CProfileOwner* owner) : CAIFaunaActivityBaseSpawnProfile(owner) , _ArrivedInZone(false) , _Bot(NLMISC::safe_cast(owner)) { _DenyFlags=_Bot->getAStarFlag(); } void CGrazeFaunaProfile::beginProfile() { _ArrivedInZone=false; _OutOfMagnet=false; _Bot->setCycleState(CFaunaActivity::CycleStateHungry); _CycleTimerBaseTime=_Bot->spawnGrp().getCurrentCycleTime()>>2; // 4 steps. (sorry, its hard coded) _CycleTimer.set(_CycleTimerBaseTime+CAIS::rand16(_CycleTimerBaseTime/4)); } void CGrazeFaunaProfile::endProfile() { } void CGrazeFaunaProfile::updateProfile(uint ticksSinceLastUpdate) { H_AUTO(GrazeFaunaProfileUpdate) CFollowPathContext fpcGrazeFaunaProfileUpdate("GrazeFaunaProfileUpdate"); // calculate distance from bot position to magnet point (used in all the different processes) _magnetDistSq=_Bot->pos().distSqTo(_Bot->spawnGrp().magnetPos()); if (!_ArrivedInZone) { float grpMagnetRadiusFar=_Bot->spawnGrp().magnetRadiusFar(); if (_magnetDistSq>(grpMagnetRadiusFar*grpMagnetRadiusFar)) { _Bot->setMode( MBEHAV::NORMAL ); if (!_OutOfMagnet) { _OutOfMagnet=true; _PathPos._Angle=_Bot->theta(); } if (_Bot->canMove()) { float dist = _Bot->walkSpeed()*ticksSinceLastUpdate; CFollowPath::TFollowStatus status = CFollowPath::getInstance()->followPath( _Bot, _PathPos, _Bot->spawnGrp().getPathCont(), dist, dist*.71f, .5f); if (status==CFollowPath::FOLLOW_NO_PATH) { CFollowPath::TFollowReason lastReason = CFollowPath::getInstance()->lastReason(); RYAI_MAP_CRUNCH::CWorldMap::TFindInsideAStarPathReason lastFIASPReason = CFollowPath::getInstance()->lastFIASPReason(); #ifdef COMPACT_POS_WARNINGS int logTick = CTimeInterface::gameCycle(); std::string error = _Bot->spawnGrp().getPathCont().getDestPos().toString() + " " + CFollowPath::toString(lastReason, lastFIASPReason); ++_FloodLogger.logPositions[error]; if ((logTick-_FloodLogger.logLastTick)>_FloodLogger.logPeriod) // Log errors every minute // :TODO: Put that in a config thing { if (!_FloodLogger.logPositions.empty()) { nlwarning("bots cannot reach their groups while grazing, positions are:"); FOREACH(it, CFaunaProfileFloodLogger::TLogPositions, _FloodLogger.logPositions) { nlwarning(" - %s: %d times", it->first.c_str(), it->second); } _FloodLogger.logPositions.clear(); } _FloodLogger.logLastTick = logTick; } #else nlwarning("Follow No Path from %s to %s %s", _Bot->pos().toString().c_str(), _Bot->spawnGrp().getPathCont().getDestPos().toString().c_str(), CFollowPath::toString(lastReason, lastFIASPReason).c_str()); // _Bot->spawnGrp().addBotToDespawnAndRespawnTime(&_Bot->getPersistent(),1,3000); #endif } return; } } else { _Bot->VisualTargetTimer().set(0); _ArrivedInZone=true; _MovementMagnet=new CMovementMagnet(*_Bot,_DenyFlags); } } if (_MovementMagnet.isNull()) _MovementMagnet=new CMovementMagnet(*_Bot,_DenyFlags); if (_OutOfMagnet) { _MovementMagnet->setBotAngle(); _OutOfMagnet=false; } // increase cycle state when needed. if (_CycleTimer.test()) { _Bot->setCycleState((CFaunaActivity::TCycleState)(_Bot->cycleState()+1)); if (_Bot->cycleState()>CFaunaActivity::CycleStateDigesting) _Bot->setCycleState(CFaunaActivity::CycleStateDigesting); _CycleTimer.set(_CycleTimerBaseTime+CAIS::rand16(_CycleTimerBaseTime/4)); } switch (_Bot->cycleState()) { case CFaunaActivity::CycleStateHungry: _MovementMagnet->update(FAUNA_BEHAVIOR_GLOBAL_SCALE*30, ticksSinceLastUpdate); break; case CFaunaActivity::CycleStateVeryHungry: _MovementMagnet->update(FAUNA_BEHAVIOR_GLOBAL_SCALE*60, ticksSinceLastUpdate); break; case CFaunaActivity::CycleStateStarving: _MovementMagnet->update(FAUNA_BEHAVIOR_GLOBAL_SCALE*120, ticksSinceLastUpdate); break; case CFaunaActivity::CycleStateDigesting: _MovementMagnet->update(FAUNA_BEHAVIOR_GLOBAL_SCALE*30, ticksSinceLastUpdate); break; } // && _state==0 ) // means wait in movementmagnet. const float grpMagnetRadiusNear=_Bot->spawnGrp().magnetRadiusNear(); if ( _magnetDistSq<=(grpMagnetRadiusNear*grpMagnetRadiusNear) && _MovementMagnet->getMovementType()==CMovementMagnet::Movement_Anim) { if (_Bot->getPersistent().grp().getType()==AITYPES::FaunaTypePredator) { _Bot->setMode(MBEHAV::ALERT); } else { _Bot->setMode(MBEHAV::EAT); } } else { if ( _Bot->cycleState()==CFaunaActivity::CycleStateHungry || _Bot->cycleState()==CFaunaActivity::CycleStateDigesting) { _Bot->setMode(MBEHAV::NORMAL); } else { _Bot->setMode(MBEHAV::HUNGRY); } } } std::string CGrazeFaunaProfile::getOneLineInfoString() const { return NLMISC::toString("graze fauna profile"); } ////////////////////////////////////////////////////////////////////////////// // CRestFaunaProfile // ////////////////////////////////////////////////////////////////////////////// CRestFaunaProfile::CRestFaunaProfile(CProfileOwner* owner) : CAIFaunaActivityBaseSpawnProfile(owner) , _ArrivedInZone(false) , _Bot(NLMISC::safe_cast(owner)) { _DenyFlags=_Bot->getAStarFlag(); } void CRestFaunaProfile::beginProfile() { _ArrivedInZone=false; _Bot->setCycleState(CFaunaActivity::CycleStateTired); _CycleTimerBaseTime=_Bot->spawnGrp().getCurrentCycleTime()>>2; // 4 steps. (sorry, its hard coded) _CycleTimer.set(_CycleTimerBaseTime+CAIS::rand16(_CycleTimerBaseTime/4)); _OutOfMagnet=false; } void CRestFaunaProfile::endProfile() { _Bot->setHungry (); } void CRestFaunaProfile::updateProfile(uint ticksSinceLastUpdate) { H_AUTO(RestFaunaProfileUpdate) CFollowPathContext fpcRestFaunaProfileUpdate("RestFaunaProfileUpdate"); // calculate distance from bot position to magnet point (used in all the different processes) _magnetDistSq=_Bot->pos().distSqTo(_Bot->spawnGrp().magnetPos()); if (!_ArrivedInZone) { float grpMagnetRadiusFar=_Bot->spawnGrp().magnetRadiusFar(); if (_magnetDistSq>(grpMagnetRadiusFar*grpMagnetRadiusFar)) { if (!_OutOfMagnet) { _PathPos._Angle=_Bot->theta(); _OutOfMagnet=true; } _Bot->setMode( MBEHAV::NORMAL ); if (_Bot->canMove()) { const float dist=_Bot->walkSpeed()*ticksSinceLastUpdate; CFollowPath::TFollowStatus const status = CFollowPath::getInstance()->followPath( _Bot, _PathPos, _Bot->spawnGrp().getPathCont(), dist, dist*.71f, .5f); if (status==CFollowPath::FOLLOW_NO_PATH) { CFollowPath::TFollowReason lastReason = CFollowPath::getInstance()->lastReason(); RYAI_MAP_CRUNCH::CWorldMap::TFindInsideAStarPathReason lastFIASPReason = CFollowPath::getInstance()->lastFIASPReason(); #ifdef COMPACT_POS_WARNINGS int logTick = CTimeInterface::gameCycle(); std::string error = _Bot->spawnGrp().getPathCont().getDestPos().toString() + " " + CFollowPath::toString(lastReason, lastFIASPReason); ++_FloodLogger.logPositions[error]; if ((logTick-_FloodLogger.logLastTick)>_FloodLogger.logPeriod) // Log errors every minute // :TODO: Put that in a config thing { if (!_FloodLogger.logPositions.empty()) { nlwarning("bots cannot reach their groups, positions are:"); FOREACH(it, CFaunaProfileFloodLogger::TLogPositions, _FloodLogger.logPositions) { nlwarning(" - %s: %d times", it->first.c_str(), it->second); } _FloodLogger.logPositions.clear(); } _FloodLogger.logLastTick = logTick; } #else nlwarning("bot cannot reach its group from %s to %s %s", _Bot->pos().toString().c_str(), _Bot->spawnGrp().getPathCont().getDestPos().toString().c_str(), CFollowPath::toString(lastReason, lastFIASPReason).c_str()); #endif } } return; } else { _Bot->VisualTargetTimer().set(0); _ArrivedInZone=true; _MovementMagnet=new CMovementMagnet(*_Bot, _DenyFlags); } } if (_MovementMagnet.isNull()) _MovementMagnet=new CMovementMagnet(*_Bot, _DenyFlags); if (_OutOfMagnet) { _MovementMagnet->setBotAngle(); _OutOfMagnet=false; } // increase cycle state when needed. if (_CycleTimer.test()) { _Bot->setCycleState((CFaunaActivity::TCycleState)(_Bot->cycleState()+1)); if (_Bot->cycleState()>CFaunaActivity::CycleStateShaking) _Bot->setCycleState(CFaunaActivity::CycleStateShaking); _CycleTimer.set(_CycleTimerBaseTime+CAIS::rand16(_CycleTimerBaseTime/4)); } switch(_Bot->cycleState()) { case CFaunaActivity::CycleStateTired: _MovementMagnet->update(FAUNA_BEHAVIOR_GLOBAL_SCALE*80, ticksSinceLastUpdate); break; case CFaunaActivity::CycleStateVeryTired: _MovementMagnet->update(FAUNA_BEHAVIOR_GLOBAL_SCALE*120, ticksSinceLastUpdate); break; case CFaunaActivity::CycleStateExhausted: _MovementMagnet->update(FAUNA_BEHAVIOR_GLOBAL_SCALE*200, ticksSinceLastUpdate); break; case CFaunaActivity::CycleStateShaking: _MovementMagnet->update(FAUNA_BEHAVIOR_GLOBAL_SCALE*80, ticksSinceLastUpdate); break; } const float grpMagnetRadiusNear=_Bot->spawnGrp().magnetRadiusNear(); if ( _magnetDistSq<=(grpMagnetRadiusNear*grpMagnetRadiusNear) && _MovementMagnet->getMovementType()==CMovementMagnet::Movement_Anim) { _Bot->setMode(MBEHAV::REST); } else { _Bot->setMode(MBEHAV::NORMAL); } } std::string CRestFaunaProfile::getOneLineInfoString() const { return NLMISC::toString("rest fauna profile"); } ////////////////////////////////////////////////////////////////////////////// // CEatCorpseFaunaProfile // ////////////////////////////////////////////////////////////////////////////// CEatCorpseFaunaProfile::CEatCorpseFaunaProfile(CProfileOwner* owner, TDataSetRow const& corpse, RYAI_MAP_CRUNCH::TAStarFlag flag) : CAIBaseProfile() , _Bot(NLMISC::safe_cast(owner)) , _PathPos(NLMISC::safe_cast(owner)->theta()) , _eated(corpse) , _atGoodDist(false) , _PathCont(flag) { CAIEntityPhysical const* const entity = CAIS::instance().getEntityPhysical(_eated); if (entity) _PathCont.setDestination(AITYPES::vp_auto, CAIVector(entity->pos())); } void CEatCorpseFaunaProfile::beginProfile() { #ifdef NL_DEBUG nlassert(_Bot->canMove ()); #endif } void CEatCorpseFaunaProfile::endProfile() { CAIEntityPhysical *const entity=CAIS::instance().getEntityPhysical(_eated); if (entity) { CSpawnBotFauna const* const botFauna = NLMISC::safe_cast(entity); IAIProfile* const profile = botFauna->getAIProfile(); if (_Bot) { float pieceOffood=_Bot->radius()*(1.f/3.f); if (pieceOffood>=entity->food()) { pieceOffood=entity->food(); entity->food()=0; } else { entity->food()-=pieceOffood; } _Bot->hungry()-=pieceOffood; if (_Bot->food()<0) _Bot->food()=0; } if ( profile && profile->getAIProfileType()==AITYPES::ACTIVITY_CORPSE) { CCorpseFaunaProfile *const corpseProfile=NLMISC::safe_cast(profile); corpseProfile->setEater(false); corpseProfile->setEated(_Bot->food()==0); } } } void CEatCorpseFaunaProfile::updateProfile(uint ticksSinceLastUpdate) { H_AUTO(EatCorpseFaunaProfileUpdate) CFollowPathContext fpcEatCorpseFaunaProfileUpdate("EatCorpseFaunaProfileUpdate"); CAIEntityPhysical *const entity=CAIS::instance().getEntityPhysical(_eated); if (entity) { _Bot->setTarget(entity); if ( !_atGoodDist && _Bot->canMove()) { float dist=(float) entity->pos().quickDistTo(_Bot->pos()); if (dist>0.5f) // 0.5f far { if (!_Bot->canMove()) { return; } float moveDist=_Bot->runSpeed()*ticksSinceLastUpdate; if (moveDist>dist*0.25f) // do go too far .. { moveDist=dist*0.25f; } CFollowPath::TFollowStatus const status = CFollowPath::getInstance()->followPath( _Bot, _PathPos, _PathCont, moveDist, moveDist*.5f, .3f); if (status==CFollowPath::FOLLOW_NO_PATH) { _Bot->setDefaultComportment(); // .. gives the control to its group. return; } if (status!=CFollowPath::FOLLOW_ARRIVED) { return; } } _atGoodDist=true; _Bot->setTheta(_Bot->pos().angleTo(entity->pos())); _Bot->setMode(MBEHAV::EAT); _eatTimer.set(100); // ten seconds to eat a corpse (vorace!). const CSpawnBotFauna *const botFauna=NLMISC::safe_cast(entity); IAIProfile* const profile = botFauna->getAIProfile(); if ( profile && profile->getAIProfileType()==AITYPES::ACTIVITY_CORPSE) { CCorpseFaunaProfile *corpseProfile=NLMISC::safe_cast(profile); corpseProfile->setEater(true); } return; } else { if (!_eatTimer.test()) // did he finished to eat the corpse ? return; } } _Bot->setDefaultComportment(); // .. gives the control to its group. } std::string CEatCorpseFaunaProfile::getOneLineInfoString() const { return NLMISC::toString("eat_corpse fauna profile"); } ////////////////////////////////////////////////////////////////////////////// // CCuriosityFaunaProfile // ////////////////////////////////////////////////////////////////////////////// CCuriosityFaunaProfile::CCuriosityFaunaProfile(CProfileOwner* owner, TDataSetRow const& player, RYAI_MAP_CRUNCH::TAStarFlag flag) : CAIBaseProfile() , _Bot(NLMISC::safe_cast(owner)) , _PathPos(NLMISC::safe_cast(owner)->theta()) , _player(player) , _atGoodDist(false) , _Flag(flag) , _PathCont(flag) { #ifdef NL_DEBUG nlassert(owner); #endif } void CCuriosityFaunaProfile::beginProfile() { #ifdef NL_DEBUG nlassert(_Bot->canMove ()); #endif _curiosityTimer.set(100+CAIS::rand32(100)); // i give you 10 to 20 sec of my time _addCuriosityTime=30+CAIS::rand32(30); // 3 to 6 seconds to say hello. _Bot->setMode(MBEHAV::NORMAL); _TooFar=true; } void CCuriosityFaunaProfile::endProfile() { _Bot->timeBeforeNextCuriosity().set((CAIS::rand32(120)+120)*10*3); // consider every 2 to 4 minutes; _Bot->setVisualTarget(NULL); } void CCuriosityFaunaProfile::updateProfile(uint ticksSinceLastUpdate) { H_AUTO(CuriosityFaunaProfileUpdate) CFollowPathContext fpcCuriosityFaunaProfileUpdate("CuriosityFaunaProfileUpdate"); if (_curiosityTimer.test()) { _Bot->setDefaultComportment(); // .. gives the control to its group. return; } CAIEntityPhysical *const entity=CAIS::instance().getEntityPhysical(_player); if (entity) { if ( !entity->wpos().isValid() || (entity->wpos().getFlags()&_Flag)!=0 // we don't want to follow player there .. || !entity->isAlive()) { _Bot->setDefaultComportment(); // .. gives the control to its group. return; } _Bot->setVisualTarget(entity); float dist=(float) entity->pos().quickDistTo(_Bot->pos()); if (dist>(1.f+getDistBetWeen (*_Bot, *entity)) ) // 1.f too far { _TooFar=true; if (!_Bot->canMove()) { return; } _PathCont.setDestination(AITYPES::vp_auto, CAIVector(entity->pos())); float speedCoef=1.f/(dist*0.1f+1.f); float moveDist=(speedCoef*_Bot->walkSpeed()+(1.f-speedCoef)*_Bot->runSpeed())*ticksSinceLastUpdate; if (moveDist>dist*0.5f) // do go too far .. { moveDist=dist*0.5f; } CFollowPath::TFollowStatus const status = CFollowPath::getInstance()->followPath( _Bot, _PathPos, _PathCont, moveDist, moveDist*.5f, .3f); if (status==CFollowPath::FOLLOW_NO_PATH) { _Bot->setDefaultComportment(); } return; } if (_TooFar) // just arrived ? { _Bot->setTheta(_Bot->pos().angleTo(entity->pos())); _Bot->setMode(MBEHAV::ALERT); _curiosityTimer.set((uint32)(_curiosityTimer.timeRemaining()*0.5f+_addCuriosityTime)); // ten seconds to say hello (cute!). _TooFar=false; } } } std::string CCuriosityFaunaProfile::getOneLineInfoString() const { return NLMISC::toString("curiosity fauna profile"); } ////////////////////////////////////////////////////////////////////////////// // CCorpseFaunaProfile // ////////////////////////////////////////////////////////////////////////////// CCorpseFaunaProfile::CCorpseFaunaProfile(CProfileOwner* owner) : CAIBaseProfile() , _Bot(NLMISC::safe_cast(owner)) , _HaveEater(false) , _Eated(false) { } inline void CCorpseFaunaProfile::beginProfile() { _Bot->setTarget(NULL); _Bot->setMode(MBEHAV::DEATH); } #include "event_reaction_include.h"