mirror of
https://port.numenaute.org/aleajactaest/khanat-code-old.git
synced 2025-01-05 23:03:59 +00:00
948 lines
29 KiB
C++
948 lines
29 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
#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<CMovementMagnet> const& getMovementMagnet() const { return _MovementMagnet; }
|
|
|
|
private:
|
|
NLMISC::CSmartPtr<CMovementMagnet> _MovementMagnet;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CBotProfileFightFauna //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
CBotProfileFightFauna::CBotProfileFightFauna(CProfileOwner* owner, CAIEntityPhysical* ennemy)
|
|
: CBotProfileFight(owner, ennemy)
|
|
{
|
|
}
|
|
|
|
void CBotProfileFightFauna::noMoreTarget()
|
|
{
|
|
static_cast<CSpawnBotFauna*>(_Bot.ptr())->setDefaultComportment();
|
|
}
|
|
|
|
void CBotProfileFightFauna::eventBeginFight()
|
|
{
|
|
CSpawnBotFauna* spawnable= NLMISC::safe_cast<CSpawnBotFauna*>(_Bot.ptr());
|
|
CGrpFauna &grpFauna = static_cast<CGrpFauna&>(*spawnable->getPersistent().getOwner());
|
|
grpFauna.processStateEvent(grpFauna.getEventContainer().EventBotBeginFight);
|
|
}
|
|
|
|
void CBotProfileFightFauna::eventTargetKilled()
|
|
{
|
|
CSpawnBotFauna* spawnable= NLMISC::safe_cast<CSpawnBotFauna*>(_Bot.ptr());
|
|
CGrpFauna *grpFauna = static_cast<CGrpFauna*>(spawnable->getPersistent().getOwner());
|
|
grpFauna->processStateEvent(grpFauna->getEventContainer().EventBotTargetKilled);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CBotProfileHealFauna //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
CBotProfileHealFauna::CBotProfileHealFauna(TDataSetRow const& row, CProfileOwner* owner)
|
|
: CBotProfileHeal(row, owner)
|
|
{
|
|
}
|
|
|
|
void CBotProfileHealFauna::noMoreTarget()
|
|
{
|
|
static_cast<CSpawnBotFauna*>(_Bot.ptr())->setDefaultComportment();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CBotProfileReturnAfterFightFauna //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
CBotProfileReturnAfterFightFauna::CBotProfileReturnAfterFightFauna(CProfileOwner* owner)
|
|
: CBotProfileReturnAfterFight(owner)
|
|
{
|
|
CSpawnBotFauna* bot = NLMISC::safe_cast<CSpawnBotFauna*>(owner);
|
|
RYAI_MAP_CRUNCH::TAStarFlag denyFlags = bot->getAStarFlag();
|
|
_MovementMagnet = NLMISC::CSmartPtr<CMovementMagnet>(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<CSpawnBotFauna*>(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<CSpawnBotFauna*>(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<CSpawnBotFauna*>(owner)->theta())
|
|
, _OutOfMagnet(false)
|
|
{
|
|
}
|
|
|
|
NLMISC::CSmartPtr<CMovementMagnet> const& CAIFaunaActivityBaseSpawnProfile::getMovementMagnet() const
|
|
{
|
|
return _MovementMagnet;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CWanderFaunaProfile //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
CWanderFaunaProfile::CWanderFaunaProfile(CProfileOwner* owner)
|
|
: CAIFaunaActivityBaseSpawnProfile(owner)
|
|
, _Bot(NLMISC::safe_cast<CSpawnBotFauna*>(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<CSpawnBotFauna*>(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<CSpawnBotFauna*>(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<CSpawnBotFauna*>(owner))
|
|
, _PathPos(NLMISC::safe_cast<CSpawnBotFauna*>(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<CSpawnBotFauna*>(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<CCorpseFaunaProfile*>(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<CSpawnBotFauna*>(entity);
|
|
IAIProfile* const profile = botFauna->getAIProfile();
|
|
if ( profile
|
|
&& profile->getAIProfileType()==AITYPES::ACTIVITY_CORPSE)
|
|
{
|
|
CCorpseFaunaProfile *corpseProfile=NLMISC::safe_cast<CCorpseFaunaProfile*>(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<CSpawnBotFauna*>(owner))
|
|
, _PathPos(NLMISC::safe_cast<CSpawnBotFauna*>(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<CSpawnBotFauna*>(owner))
|
|
, _HaveEater(false)
|
|
, _Eated(false)
|
|
{
|
|
}
|
|
|
|
inline
|
|
void CCorpseFaunaProfile::beginProfile()
|
|
{
|
|
_Bot->setTarget(NULL);
|
|
_Bot->setMode(MBEHAV::DEATH);
|
|
}
|
|
|
|
#include "event_reaction_include.h"
|