// 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 "profile.h"
#include "ai_bot_npc.h"
#include "ai_grp_npc.h"
#include "ai_mgr_npc.h"
#include "ai_grp_fauna.h"
#include "ai_bot_fauna.h"
#include "states.h"
#include "path_behaviors.h"
#include "ai_aggro.h"
#include "ai_player.h"
#include "ai_generic_fight.h"
#include "ai_profile_npc.h"
#include "group_profile.h"
extern bool simulateBug(int bugId);
#include "dyn_grp_inline.h"
using namespace std;
using namespace RYAI_MAP_CRUNCH;
using namespace NLMISC;
using namespace NLNET;
using namespace AITYPES;
// Global configuration variables
extern CVariable DefaultWanderMinTimer;
extern CVariable DefaultWanderMaxTimer;
extern CVariable FameForGuardAttack;
extern CVariable FameForGuardHelp;
// Stuff used for management of log messages
bool ai_profile_npc_VerboseLog = false;
void ai_profile_npc_LOG(std::string const& type, std::string const& profile, std::string const& step, std::string const& object)
{
static size_t maxType = 0;
static size_t maxProfile = 0;
static size_t maxStep = 0;
static size_t maxObject = 0;
if (ai_profile_npc_VerboseLog)
{
maxType = std::max(maxType, type.length());
maxProfile = std::max(maxProfile, profile.length());
maxStep = std::max(maxStep, step.length());
maxObject = std::max(maxObject, object.length());
std::string log = "profile";
log += " " + type + std::string(maxType - type.length(), ' ');
log += " " + profile + std::string(maxProfile - profile.length(), ' ');
log += " " + step + std::string(maxStep - step.length(), ' ');
log += " " + object + std::string(maxObject - object.length(), ' ');
nlinfo("%s", log.c_str());
}
}
#define PROFILE_LOG(type,profile,step,object) ai_profile_npc_LOG(type,profile,step,object)
#define LOG if (!ai_profile_npc_VerboseLog) {} else nlinfo
NLMISC_COMMAND(verboseAIProfiles,"Turn on or off or check the state of verbose ai profile info logging","")
{
if(args.size()>1)
return false;
if(args.size()==1)
StrToBool (ai_profile_npc_VerboseLog, args[0]);
nlinfo("VerboseLogging is %s",ai_profile_npc_VerboseLog?"ON":"OFF");
return true;
}
/****************************************************************************/
/* Local classes */
/****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
// CBotProfileFightNpc //
//////////////////////////////////////////////////////////////////////////////
class CBotProfileFightNpc
: public CBotProfileFight
{
public:
CBotProfileFightNpc(CProfileOwner* owner, CAIEntityPhysical* ennemy);
virtual ~CBotProfileFightNpc();
virtual std::string getOneLineInfoString() const { return "fight npc bot profile"; }
void noMoreTarget();
void eventBeginFight();
void eventTargetKilled();
};
//////////////////////////////////////////////////////////////////////////////
// CBotProfileHealNpc //
//////////////////////////////////////////////////////////////////////////////
class CBotProfileHealNpc
: public CBotProfileHeal
, public IAIEntityPhysicalHealer
{
public:
CBotProfileHealNpc(CAIEntityPhysical* target, CProfileOwner* owner);
virtual ~CBotProfileHealNpc();
virtual std::string getOneLineInfoString() const { return "heal npc bot profile"; }
void noMoreTarget();
virtual void healerAdded(CAIEntityPhysical* entity);
virtual void healerRemoved(CAIEntityPhysical* entity);
// void eventBeginFight();
// void eventTargetKilled();
private:
CAIEntityPhysical* _Target;
};
//////////////////////////////////////////////////////////////////////////////
// CBotProfileReturnAfterFightNpc //
//////////////////////////////////////////////////////////////////////////////
class CBotProfileReturnAfterFightNpc
: public CBotProfileReturnAfterFight
{
public:
CBotProfileReturnAfterFightNpc(CSpawnBotNpc* owner);
~CBotProfileReturnAfterFightNpc();
virtual void beginProfile();
virtual void endProfile();
virtual void updateProfile(uint ticksSinceLastUpdate);
virtual std::string getOneLineInfoString() const;
// virtual NLMISC::CSmartPtr const& getMovementMagnet() const { return _MovementMagnet; }
private:
CPathCont _PathCont;
NLMISC::CSmartPtr _MoveProfile;
};
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileTribu //
//////////////////////////////////////////////////////////////////////////////
class CGrpProfileTribu : public CGrpProfileNormal
{
public:
CGrpProfileTribu(CProfileOwner *owner);
virtual ~CGrpProfileTribu();
virtual void beginProfile();
virtual void endProfile();
virtual void updateProfile(uint ticksSinceLastUpdate);
virtual std::string getOneLineInfoString() const;
virtual TProfiles getAIProfileType() const { return ACTIVITY_TRIBU; }
private:
CAIVector _CenterPos;
};
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileStandOnVertices //
//////////////////////////////////////////////////////////////////////////////
class CGrpProfileStandAtStartPoint
: public CMoveProfile
{
public:
CGrpProfileStandAtStartPoint(CProfileOwner* owner);
virtual ~CGrpProfileStandAtStartPoint();
class CBotPositionner : public CRefCount
{
public:
CBotPositionner(RYAI_MAP_CRUNCH::TAStarFlag flags);
CBotPositionner(TVerticalPos verticalPos, CAIPos position, RYAI_MAP_CRUNCH::TAStarFlag flag);
virtual ~CBotPositionner();
void setBotAtDest(bool atDest = true);
bool isBotAtDest() const;
CPathCont _PathCont;
CAIPos _Position;
TVerticalPos _VerticalPos;
private:
bool _BotAtDest;
};
CPathCont* getPathCont(CBot const* bot);
virtual void beginProfile();
void resumeProfile();
virtual void endProfile();
virtual void updateProfile(uint ticksSinceLastUpdate);
void addBot(CBot* bot);
void removeBot(CBot* bot);
void setCurrentValidPos();
virtual TProfiles getAIProfileType() const { return MOVE_STAND_ON_VERTICES; }
virtual std::string getOneLineInfoString() const;
private:
typedef std::map > TNpcBotPositionnerMap;
TNpcBotPositionnerMap _NpcList;
bool _Finished;
};
/****************************************************************************/
/* Local profile factories */
/****************************************************************************/
//- Simple profile factories (based on generic factory) ----------------------
// CGrpProfileFightFactory
typedef CAIGenericProfileFactory CGrpProfileFightFactory;
// CGrpProfileTribuFactory
typedef CAIGenericProfileFactory CGrpProfileTribuFactory;
// CGrpProfileIdleFactory
typedef CAIGenericProfileFactory CGrpProfileIdleFactory;
// CGrpProfileStandAtStartPointFactory
typedef CAIGenericProfileFactory CGrpProfileStandAtStartPointFactory;
//- Singleton profiles (stateless ones) --------------------------------------
extern CGrpProfileFightFactory GrpProfileFightFactory;
/****************************************************************************/
/* Implementations */
/****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
// CSpawnGroupNpc //
//////////////////////////////////////////////////////////////////////////////
void CSpawnGroupNpc::botHaveDied(CBotNpc* bot)
{
// check bot profile type and update group profile bot list.
if (fightProfile().getAIProfile())
{
CSlaveProfile* const profile = NLMISC::type_cast(fightProfile().getAIProfile());
if (profile)
profile->removeBot(bot);
}
if (movingProfile().getAIProfile())
{
CSlaveProfile* const profile = NLMISC::type_cast(movingProfile().getAIProfile());
if (profile)
profile->removeBot(bot);
}
{
CSpawnBotNpc* const spawn = bot->getSpawn();
if (spawn)
spawn->setAIProfile(BotProfileStandAtPosFactory.createAIProfile(spawn));
}
}
void CSpawnGroupNpc::botHaveDespawn(CBotNpc* bot)
{
CSpawnGroupNpc::botHaveDied(bot);
}
void CSpawnGroupNpc::botHaveSpawn(CBotNpc* bot)
{
if (movingProfile().getAIProfile())
{
NLMISC::safe_cast(movingProfile().getAIProfile())->addBot(bot);
}
if (fightProfile().getAIProfile())
{
NLMISC::safe_cast(fightProfile().getAIProfile())->addBot(bot);
}
}
//////////////////////////////////////////////////////////////////////////////
// CBotProfileFightNpc //
//////////////////////////////////////////////////////////////////////////////
CBotProfileFightNpc::CBotProfileFightNpc(CProfileOwner* owner, CAIEntityPhysical* ennemy)
: CBotProfileFight(owner, ennemy)
{
PROFILE_LOG("bot", "fight_npc", "ctor", "");
}
CBotProfileFightNpc::~CBotProfileFightNpc()
{
PROFILE_LOG("bot", "fight_npc", "dtor", "");
}
void CBotProfileFightNpc::noMoreTarget()
{
_Bot->setAIProfile(BotProfileStandAtPosFactory.createAIProfile(_Bot.ptr()));
}
void CBotProfileFightNpc::eventBeginFight()
{
TempSpeaker = _Bot;
CSpawnBotNpc* spawnable = NLMISC::safe_cast(_Bot.ptr());
CGroupNpc* grpNpc = static_cast(spawnable->getPersistent().getOwner());
grpNpc->processStateEvent(grpNpc->getEventContainer().EventBotBeginFight);
TempSpeaker = NULL;
}
void CBotProfileFightNpc::eventTargetKilled()
{
TempSpeaker = _Bot;
CSpawnBotNpc* spawnable= NLMISC::safe_cast(_Bot.ptr());
CGroupNpc* grpNpc = static_cast(spawnable->getPersistent().getOwner());
grpNpc->processStateEvent(grpNpc->getEventContainer().EventBotTargetKilled);
TempSpeaker = NULL;
}
//////////////////////////////////////////////////////////////////////////////
// CBotProfileHealNpc //
//////////////////////////////////////////////////////////////////////////////
CBotProfileHealNpc::CBotProfileHealNpc(CAIEntityPhysical* target, CProfileOwner* owner)
: CBotProfileHeal(target->dataSetRow(), owner)
, _Target(target)
{
PROFILE_LOG("bot", "heal_npc", "ctor", "");
if (_Target)
_Target->addHealer(this);
}
CBotProfileHealNpc::~CBotProfileHealNpc()
{
PROFILE_LOG("bot", "heal_npc", "dtor", "");
if (_Target)
_Target->delHealer(this);
}
void CBotProfileHealNpc::healerAdded(CAIEntityPhysical* entity)
{
}
void CBotProfileHealNpc::healerRemoved(CAIEntityPhysical* entity)
{
if (_Target == entity)
_Target = NULL;
}
void CBotProfileHealNpc::noMoreTarget()
{
_Bot->setAIProfile(BotProfileStandAtPosFactory.createAIProfile(_Bot.ptr()));
}
//////////////////////////////////////////////////////////////////////////////
// CBotProfileReturnAfterFightNpc //
//////////////////////////////////////////////////////////////////////////////
CBotProfileReturnAfterFightNpc::CBotProfileReturnAfterFightNpc(CSpawnBotNpc* owner)
: CBotProfileReturnAfterFight(owner)
, _PathCont(owner->getPersistent().getOwner()->getAStarFlag())
{
PROFILE_LOG("bot_npc", "return_after_fight", "ctor", "");
// CSpawnBotNpc* bot = NLMISC::safe_cast(owner);
// RYAI_MAP_CRUNCH::TAStarFlag denyFlags = bot->getAStarFlag();
_PathCont.setDestination(owner->getReturnPos());
_MoveProfile = NLMISC::CSmartPtr(new CBotProfileFollowPos(&_PathCont, owner));
}
CBotProfileReturnAfterFightNpc::~CBotProfileReturnAfterFightNpc()
{
PROFILE_LOG("bot_npc", "return_after_fight", "dtor", "");
}
void CBotProfileReturnAfterFightNpc::beginProfile()
{
PROFILE_LOG("bot_npc", "return_after_fight", "begin", "");
CBotProfileReturnAfterFight::beginProfile();
_Bot->ignoreReturnAggro(true);
_MoveProfile->beginProfile();
}
void CBotProfileReturnAfterFightNpc::endProfile()
{
PROFILE_LOG("bot_npc", "return_after_fight", "end", "");
_MoveProfile->endProfile();
_Bot->ignoreReturnAggro(false);
CBotProfileReturnAfterFight::endProfile();
}
void CBotProfileReturnAfterFightNpc::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(CBotProfileReturnAfterFightFaunaUpdate);
CBotProfileReturnAfterFight::updateProfile(ticksSinceLastUpdate);
_MoveProfile->updateProfile(ticksSinceLastUpdate);
}
std::string CBotProfileReturnAfterFightNpc::getOneLineInfoString() const
{
std::string info = "return_after_fight npc bot profile";
info += " (";
info += _MoveProfile?_MoveProfile->getOneLineInfoString():std::string("");
info += ")";
return info;
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileFight //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileFight::CGrpProfileFight(CProfileOwner *owner)
: CFightProfile(owner)
, CFightOrganizer()
, _WasRunning(false)
{
PROFILE_LOG("group", "fight", "ctor", "");
}
CGrpProfileFight::~CGrpProfileFight()
{
PROFILE_LOG("group", "fight", "dtor", "");
for (CCont::iterator it=_Grp->bots().begin(), itEnd=_Grp->bots().end();it!=itEnd;++it)
{
CBot *bot=*it;
removeBot (bot);
}
}
void CGrpProfileFight::beginProfile()
{
PROFILE_LOG("group", "fight", "begin", "");
if (_Grp->getPersistent().getGrpDynBase() != /*(CDynGrpBase*)*/NULL
&& !_Grp->getPersistent().getGrpDynBase()->getFamilyBehavior().isNULL())
{
// this is a dynamic bots groups, activate assist between groups
_CheckAround.set(CHECK_AROUND_PERIOD);
}
_HaveEnnemy=true;
for (CCont::iterator it=_Grp->getPersistent().bots().begin(), itEnd=_Grp->getPersistent().bots().end();it!=itEnd;++it)
{
addBot (*it);
}
_WasRunning = _Grp->checkProfileParameter("running");
if (!_WasRunning)
_Grp->addProfileParameter("running", "", 0.f);
}
void CGrpProfileFight::endProfile()
{
PROFILE_LOG("group", "fight", "end", "");
if (!_WasRunning)
_Grp->removeProfileParameter("running");
}
void CGrpProfileFight::addBot(CBot* bot)
{
vector::iterator it = find(_NpcList.begin(), _NpcList.end(), bot);
if (it==_NpcList.end())
_NpcList.push_back(bot);
}
void CGrpProfileFight::removeBot(CBot* bot)
{
vector::iterator it = find(_NpcList.begin(), _NpcList.end(), bot);
if (it!=_NpcList.end())
_NpcList.erase(it);
}
void CGrpProfileFight::setFight(CSpawnBot* bot, CAIEntityPhysical* ennemy)
{
bot->setAIProfile(new CBotProfileFightNpc(bot, ennemy));
}
void CGrpProfileFight::setHeal(CSpawnBot* bot, CAIEntityPhysical* target)
{
bot->setAIProfile(new CBotProfileHealNpc(target, bot));
}
void CGrpProfileFight::setNoFight(CSpawnBot* bot)
{
if (!bot->getTarget().isNULL())
bot->setTarget(NULL);
if ( bot->getAIProfileType()==BOT_FLEE
|| bot->getAIProfileType()==BOT_FIGHT
|| bot->getAIProfileType()==BOT_HEAL
|| bot->getAIProfileType()==BOT_RETURN_AFTER_FIGHT )
{
bot->setAIProfile(new CBotProfileStandAtPos(bot));
}
}
void CGrpProfileFight::setFlee(CSpawnBot* bot, CAIVector& fleeVect)
{
bot->setMoveDecalage(fleeVect);
if (bot->getAIProfileType()!=BOT_FLEE)
{
bot->setAIProfile(new CBotProfileFlee(bot));
}
}
void CGrpProfileFight::setReturnAfterFight(CSpawnBot* bot)
{
if (bot->getAIProfileType()!=BOT_RETURN_AFTER_FIGHT)
{
bot->setAIProfile(new CBotProfileReturnAfterFightNpc(NLMISC::safe_cast(bot)));
}
}
bool CGrpProfileFight::stillHaveEnnemy() const
{
return _HaveEnnemy;
}
void CGrpProfileFight::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpFightProfileUpdate);
CFollowPathContext fpcGrpFightProfileUpdate("GrpFightProfileUpdate");
// check if some bots died or are despawned.
for(uint i = 0; i < _NpcList.size();)
{
CSpawnBot *spawnBot=_NpcList[i]->getSpawnObj();
if ( !spawnBot
|| !spawnBot->isAlive())
{
_NpcList.erase(_NpcList.begin()+i);
continue;
}
i++;
}
reorganize(_NpcList.begin(), _NpcList.end());
// check groups around
if (_CheckAround.test())
{
_CheckAround.set(CHECK_AROUND_PERIOD);
FOREACH(itBot, vector, _NpcList)
{
CBot* pBot = *itBot;
if (pBot)
{
CSpawnBot* bot = pBot->getSpawnObj();
if (bot)
bot->propagateAggro();
}
}
}
}
std::string CGrpProfileFight::getOneLineInfoString() const
{
return "fight profile";
}
std::vector& CGrpProfileFight::npcList()
{
return _NpcList;
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileNormal //
//////////////////////////////////////////////////////////////////////////////
void CGrpProfileNormal::beginProfile()
{
PROFILE_LOG("group", "normal", "begin", "");
_GroupFighting=false;
}
void CGrpProfileNormal::endProfile()
{
PROFILE_LOG("group", "normal", "end", "");
setGroupFighting(false);
}
void CGrpProfileNormal::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpNormalProfileUpdate);
CFollowPathContext fpcGrpNormalProfileUpdate("GrpNormalProfileUpdate");
if (_GroupFighting)
{
if (!_Grp->fightProfile().getAIProfile())
_Grp->fightProfile().setAIProfile(new CGrpProfileFight(_Grp));
_Grp->fightProfile().mayUpdateProfile(ticksSinceLastUpdate);
CFightProfile* profile = NLMISC::safe_cast(_Grp->fightProfile().getAIProfile());
if (!profile->stillHaveEnnemy ())
{
// :TODO: Verify if it's needed to erase bots aggro too/instead
// _Grp->clearAggroList(); // this erase all agro.
setGroupFighting(false);
_Grp->fightProfile().setAIProfile(NULL);
CSlaveProfile* moveProfile = NLMISC::type_cast(_Grp->movingProfile().getAIProfile());
if (moveProfile)
moveProfile->resumeProfile();
}
}
else
{
if (_Grp->haveAggroOrReturnPlace())
{
if(_Grp->isGroupAlive())
{
// set the fighting comportment.
if (!_Grp->fightProfile().getAIProfile())
// _Grp->fightProfile().setAIProfile(new CGrpProfileFight(_Grp));
_Grp->fightProfile().setAIProfile(_Grp.ptr(), &GrpProfileFightFactory, false);
setGroupFighting(true);
}
}
else
{
_Grp->movingProfile().mayUpdateProfile(ticksSinceLastUpdate);
}
}
}
std::string CGrpProfileNormal::getOneLineInfoString() const
{
std::string info = "normal profile";
info += " group_fighting=" + NLMISC::toString(_GroupFighting);
return info;
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileBandit //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileBandit::CGrpProfileBandit(CProfileOwner *owner)
: CGrpProfileNormal(owner)
{
PROFILE_LOG("group", "bandit", "ctor", "");
}
CGrpProfileBandit::~CGrpProfileBandit()
{
PROFILE_LOG("group", "bandit", "dtor", "");
}
void CGrpProfileBandit::beginProfile()
{
PROFILE_LOG("group", "bandit", "begin", "");
CGrpProfileNormal::beginProfile();
CGroupNpc &persGrp=_Grp->getPersistent();
if (persGrp.isRingGrp())
{
_AggroRange = persGrp.getAggroDist();
}
else
{
// look for aggro range parameter or set a default value
float aggroRangeFloat = 0.f;
if (!_Grp->getProfileParameter("aggro range", aggroRangeFloat))
_AggroRange =static_cast( CGrpProfileBanditFactory::getDefaultBanditAggroRange() );
else
_AggroRange = static_cast(aggroRangeFloat);
bool resendInfo = false;
if (!persGrp.getPlayerAttackable ())
{
persGrp.setPlayerAttackable (true);
resendInfo = true;
}
if (!persGrp.getBotAttackable ())
{
persGrp.setBotAttackable (true);
resendInfo = true;
}
if (resendInfo)
_Grp->sendInfoToEGS ();
}
}
void CGrpProfileBandit::endProfile()
{
PROFILE_LOG("group", "bandit", "end", "");
CGrpProfileNormal::endProfile();
}
void CGrpProfileBandit::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpBanditProfileUpdate);
CFollowPathContext fpcGrpBanditProfileUpdate("GrpBanditProfileUpdate");
CAIVision BanditVision;
breakable
{
CAIVector centerPos;
if (!_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
break;
_Grp->setCenterPos(centerPos);
uint32 playerRadius=uint(_AggroRange);
uint32 botRadius=uint(_AggroRange);
uint32 groupPlayerRadius=playerRadius*2;
uint32 groupBotRadius=botRadius*2;
uint32 minRadius=playerRadius>botRadius?botRadius:playerRadius;
CFightProfile* fightProfile=static_cast(_Grp->fightProfile().getAIProfile());
if (fightProfile)
{
CAIVision localBanditVision;
for (vector::iterator it=fightProfile->npcList().begin(), itEnd=fightProfile->npcList().end();it!=itEnd;++it)
{
CBot *bot=(*it);
CSpawnBot *spawnBot=bot->getSpawnObj();
if (!spawnBot)
continue;
double distToCenter=centerPos.quickDistTo(spawnBot->pos());
if (distToCenter>minRadius) // (minRadius*2) - minRadius
{
const CAIVector spawnBotPos(spawnBot->pos());
// bot vision update.
localBanditVision.updateBotsAndPlayers(spawnBot->getAIInstance(), spawnBotPos, playerRadius, botRadius);
// bandits don't like guards nor escorted people
{
const std::vector > &bots = localBanditVision.bots();
std::vector >::const_iterator first(bots.begin()), last(bots.end());
for (; first != last; ++first)
{
CAIEntityPhysical *ep = (*first)->getSpawnObj();
if ( ep
&& ep->getRyzomType()==RYZOMID::npc
&& ep->isAlive())
{
CSpawnBotNpc *botNpc = NLMISC::safe_cast(ep);
if ( botNpc->spawnGrp().activityProfile().getAIProfileType() == ACTIVITY_GUARD
|| botNpc->spawnGrp().activityProfile().getAIProfileType() == ACTIVITY_GUARD_ESCORTED
|| botNpc->spawnGrp().activityProfile().getAIProfileType() == ACTIVITY_ESCORTED )
{
spawnBot->setAggroMinimumFor(ep->dataSetRow(), 0.8f, false);
}
}
}
}
// bandits don't like players.
{
const std::vector > &players = localBanditVision.players();
std::vector >::const_iterator first(players.begin()), last(players.end());
for (; first != last; ++first)
{
CPersistentOfPhysical *player = (*first);
CAIEntityPhysical *ep = player->getSpawnObj();
if ( ep
&& ep->isAlive()
&& ep->currentHitPoints()>0.f)
{
const CRootCell *const rootCell=ep->wpos().getRootCell();
if ( rootCell
&& rootCell->getFlag()!=0 ) // Safe Zone ?
continue;
spawnBot->setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
}
}
}
}
}
}
// group vision update.
BanditVision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), centerPos, playerRadius, botRadius);
}
CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
// check if we are in war and if some bot are waiting for a bus.
if (_GroupFighting)
{
// check if some bots are not fighting.
for (CCont::iterator it=_Grp->getPersistent().bots().begin(), itEnd=_Grp->getPersistent().bots().end();it!=itEnd;++it)
{
CBot* bot=*it;
CSpawnBot *spawnBot=bot->getSpawnObj();
if ( spawnBot
&& spawnBot->isAlive()
&& spawnBot->getAIProfileType()==BOT_STAND_AT_POS)
{
// :KLUDGE: We verify here that we have a moving profile, to prevent some crashes
// :TODO: Remove that check and make sure a group always have a moving profile (even if none is defined in primitives)
if (_Grp->movingProfile().getAIProfile())
{
CMoveProfile *moveProf=NLMISC::safe_cast(_Grp->movingProfile().getAIProfile());
moveProf->resumeBot(bot);
}
}
}
}
// bandits don't like guards nor escorted people
{
const std::vector > &bots = BanditVision.bots();
std::vector >::const_iterator first(bots.begin()), last(bots.end());
for (; first != last; ++first)
{
CAIEntityPhysical *const ep = (*first)->getSpawnObj();
if ( !ep
|| ep->getRyzomType()!=RYZOMID::npc
|| !ep->isAlive())
continue;
const CSpawnBot *const bot = NLMISC::safe_cast(ep);
const TProfiles profileType=bot->spawnGrp().activityProfile().getAIProfileType();
if ( profileType != ACTIVITY_GUARD
&& profileType != ACTIVITY_GUARD_ESCORTED
&& profileType != ACTIVITY_ESCORTED )
continue;
_Grp->setAggroMinimumFor(ep->dataSetRow(), 0.8f, false);
}
}
// bandits don't like players.
{
const std::vector > &players = BanditVision.players();
std::vector >::const_iterator first(players.begin()), last(players.end());
for (; first != last; ++first)
{
CPersistentOfPhysical*const player = (*first);
CAIEntityPhysical*const ep = player->getSpawnObj();
if ( ep
&& ep->isAlive()
&& ep->currentHitPoints()>0.f) // not in safe zone.
{
const CRootCell *const rootCell=ep->wpos().getRootCell();
if ( rootCell
&& rootCell->getFlag()!=0 ) // Safe Zone ?
continue;
_Grp->setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
}
}
}
}
std::string CGrpProfileBandit::getOneLineInfoString() const
{
std::string info = "bandit profile";
info += " aggro_range=" + NLMISC::toString(_AggroRange);
return info;
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileGuard //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileGuard::CGrpProfileGuard(CProfileOwner *owner)
: CGrpProfileNormal(owner)
{
PROFILE_LOG("group", "guard", "ctor", "");
}
CGrpProfileGuard::~CGrpProfileGuard()
{
PROFILE_LOG("group", "guard", "dtor", "");
}
void CGrpProfileGuard::beginProfile()
{
PROFILE_LOG("group", "guard", "begin", "");
CGrpProfileNormal::beginProfile();
CGroupNpc &persGrp=_Grp->getPersistent();
if (persGrp.isRingGrp())
{
_AggroRange = persGrp.getAggroDist();
}
else
{
_AggroRange = 25;
}
}
void CGrpProfileGuard::endProfile()
{
PROFILE_LOG("group", "guard", "end", "");
CGrpProfileNormal::endProfile();
}
void CGrpProfileGuard::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpGuardProfileUpdate);
CFollowPathContext fpcGrpGuardProfileUpdate("GrpGuardProfileUpdate");
CAIVision GuardVision;
const uint32 aggroSize=uint32(_AggroRange);
TTicks startVisionTime = CTime::getPerformanceTime();
{
H_AUTO(GrpGuardProfileVision);
CAIVector centerPos;
if (_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
{
_Grp->setCenterPos(centerPos);
if (!_GroupFighting)
_CenterPos=_Grp->getCenterPos();
GuardVision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), _CenterPos, aggroSize, aggroSize);
}
}
TTicks endVisionTime = CTime::getPerformanceTime();
static uint32 s_maxBotsVisible = 0;
static double s_maxBotsVisionTime = 0.0;
uint32 numBotsVisible = (uint32)GuardVision.bots().size();
double deltaVisionTime = CTime::ticksToSecond(endVisionTime-startVisionTime);
bool bTellUs = false;
if( s_maxBotsVisible < numBotsVisible )
{
s_maxBotsVisible = numBotsVisible;
bTellUs = true;
}
if( s_maxBotsVisionTime < deltaVisionTime )
{
s_maxBotsVisionTime = deltaVisionTime;
bTellUs = true;
}
if( bTellUs )
{
nldebug( "==> max bots visible is now %u", s_maxBotsVisible );
nldebug( "vision time: %.2f", (float)(deltaVisionTime * 1000.0) );
}
uint32 factionIndex=CStaticFames::INVALID_FACTION_INDEX;
sint32 fameForGuardAttack = FameForGuardAttack;
{
H_AUTO(GrpGuardProfileFaction);
CAliasCont &bots = _Grp->getPersistent().bots();
if (!bots.size() != 0 && bots.begin() != bots.end())
{
CBot* bot = *(bots.begin());
if (bot != NULL)
{
factionIndex=bot->getSheet()->FactionIndex();
if (bot->getSheet()->FameForGuardAttack() != AISHEETS::ICreature::InvalidFameForGuardAttack)
fameForGuardAttack = bot->getSheet()->FameForGuardAttack();
/* if (factionIndex == CStaticFames::INVALID_FACTION_INDEX)
{
nlwarning("Bot sheet '%s' have invalid faction index (guard profile)", bot->getSheet()->SheetId().toString().c_str());
}
*/
}
}
}
string s;
float f = 0.f;
if (_Grp->getProfileParameter("faction", s) && !s.empty())
{
factionIndex = CStaticFames::getInstance().getFactionIndex(s);
}
if (_Grp->getProfileParameter("fame_for_guard_attack", f))
{
fameForGuardAttack = (sint32)f;
}
CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
// check if we are in war and if some bot are waiting for a bus.
if (_GroupFighting)
{
H_AUTO(GrpGuardProfileFighting);
const CAIVector centerPos(_Grp->getCenterPos());
// check if some bots are not fighting.
for (CCont::iterator it=_Grp->getPersistent().bots().begin(), itEnd=_Grp->getPersistent().bots().end();it!=itEnd;++it)
{
const CBot*const bot=*it;
CSpawnBot *const spawnBot=bot->getSpawnObj();
if ( spawnBot
&& spawnBot->isAlive())
{
switch(spawnBot->getAIProfileType())
{
case BOT_STAND_AT_POS:
{
CMoveProfile*const moveProf=NLMISC::type_cast(_Grp->movingProfile().getAIProfile());
if (moveProf)
moveProf->resumeBot(bot);
}
break;
case BOT_FIGHT:
{
// This system is now managed by CBotAggroOwner itself
/*
const CAIEntityPhysical*const target=spawnBot->getTarget();
if (target)
{
// if target is out of range, then forget the aggro.
if (centerPos.quickDistTo(target->pos())>50)
{
spawnBot->forgetAggroFor(target->dataSetRow());
_Grp->forgetAggroFor(target->dataSetRow());
}
}
*/
}
break;
default:
break;
}
}
}
}
// guards don't like bandits.
{
H_AUTO(GrpGuardProfileBandits);
const std::vector > &bots = GuardVision.bots();
std::vector >::const_iterator first(bots.begin()), last(bots.end());
for (; first != last; ++first)
{
const CAIEntityPhysical *const ep = (*first)->getSpawnObj();
if (ep->isAlive())
{
switch (ep->getRyzomType())
{
case RYZOMID::npc:
{
const CSpawnBotNpc *const botNpc = NLMISC::safe_cast(ep);
if (botNpc->spawnGrp().activityProfile().getAIProfileType() == ACTIVITY_BANDIT)
{
if (_CenterPos.quickDistTo(botNpc->pos())setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
}
}
}
break;
case RYZOMID::creature:
{
const CSpawnBotFauna *const botFauna = NLMISC::safe_cast(ep);
if (botFauna->getPersistent().faunaType()==FaunaTypePredator)
{
if (_CenterPos.quickDistTo(botFauna->pos())setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
}
}
}
break;
default:
break;
}
}
}
}
// guards don't like bots that attack players.
{
H_AUTO(GrpGuardProfileAttack);
const std::vector > &players = GuardVision.players();
LOG("%u players in group vision with aggrosize %d", players.size(), aggroSize);
const CAIVector ¢erPos=_Grp->getCenterPos();
std::vector >::const_iterator first(players.begin()), last(players.end());
for (; first != last; ++first)
{
const CPersistentOfPhysical *const player = (*first);
CAIEntityPhysical *const ep = player->getSpawnObj();
if ( !ep
|| !ep->isAlive()
|| ep->currentHitPoints()<=0.f
|| ep->wpos().toAIVector().quickDistTo(centerPos)>aggroSize)
continue;
// check Fame before choosing what to do ..
sint32 const fame = ep->getFameIndexed(factionIndex);
// if player is kos attack him (only if bot is attackable by player)
if ((_Grp->getPersistent().getPlayerAttackable() || (factionIndex!=CStaticFames::INVALID_FACTION_INDEX && _Grp->getPersistent().isFactionAttackable(CStaticFames::getInstance().getFactionName(factionIndex), fame))) && ((factionIndex!=CStaticFames::INVALID_FACTION_INDEX && famesetAggroMinimumFor(ep->dataSetRow(), 1.f, false);
continue;
}
// check if player is attacked and assist him).
CAIEntityPhysical const* phys = ep->firstTargeter();
while (phys)
{
switch(phys->getRyzomType())
{
case RYZOMID::player:
break;
case RYZOMID::npc:
{
const CSpawnBotNpc *const botNpc=dynamic_cast(phys);
if ( botNpc
&& botNpc->getPersistent().getGroup().getSpawnObj()->activityProfile().getAIProfileType() == ACTIVITY_GUARD)
{
break;
}
// guard don't attack npc of the same faction, rather, they attack the player !
if (factionIndex != CStaticFames::INVALID_FACTION_INDEX && botNpc->getPersistent().getSheet()->FactionIndex() == factionIndex)
{
// the guard attack the player !
_Grp->setAggroMinimumFor(ep->dataSetRow(), 1.f, false);
break;
}
}
default:
// guard defend only player with a not too bad fame
if (fame >= FameForGuardHelp && !OUTPOSTHELPERS::isAttackingFaction(factionIndex, ep))
_Grp->setAggroMinimumFor(phys->dataSetRow(), 1.f, false);
break;
}
phys = phys->nextTargeter();
}
}
}
}
std::string CGrpProfileGuard::getOneLineInfoString() const
{
return "guard profile";
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileTribu //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileTribu::CGrpProfileTribu(CProfileOwner *owner)
: CGrpProfileNormal(owner)
{
PROFILE_LOG("group", "tribe", "ctor", "");
}
CGrpProfileTribu::~CGrpProfileTribu()
{
PROFILE_LOG("group", "tribe", "dtor", "");
}
void CGrpProfileTribu::beginProfile()
{
PROFILE_LOG("group", "tribe", "begin", "");
CGrpProfileNormal::beginProfile();
}
void CGrpProfileTribu::endProfile()
{
PROFILE_LOG("group", "tribe", "end", "");
CGrpProfileNormal::endProfile();
}
void CGrpProfileTribu::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpTribuProfileUpdate);
CFollowPathContext fpcGrpTribuProfileUpdate("GrpTribuProfileUpdate");
CAIVision TribuVision;
const uint32 aggroSize=40;
breakable
{
CAIVector centerPos;
if (!_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
break;
_Grp->setCenterPos(centerPos);
if (!_GroupFighting)
_CenterPos=_Grp->getCenterPos();
TribuVision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), _CenterPos, aggroSize, aggroSize);
}
uint32 factionIndex=CStaticFames::INVALID_FACTION_INDEX;
{
CBotNpc* bot = static_cast(*_Grp->getPersistent().bots().begin());
factionIndex = bot->getSheet()->FactionIndex();
}
CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
// check if we are in war and if some bot are waiting for a bus.
if (_GroupFighting)
{
CAIVector centerPos(_Grp->getCenterPos());
// check if some bots are not fighting.
for (CCont::iterator it=_Grp->getPersistent().bots().begin(), itEnd=_Grp->getPersistent().bots().end();it!=itEnd;++it)
{
const CBot*const bot=*it;
CSpawnBot *const spawnBot=bot->getSpawnObj();
if ( !spawnBot
|| !spawnBot->isAlive())
continue;
switch(spawnBot->getAIProfileType())
{
case BOT_STAND_AT_POS:
{
CMoveProfile *moveProf=NLMISC::safe_cast(_Grp->movingProfile().getAIProfile());
moveProf->resumeBot(bot);
}
break;
case BOT_FIGHT:
{
// This system is managed by CBotAggroOwner now
/*
const CAIEntityPhysical *const target=spawnBot->getTarget();
if (!target)
break;
// if target is out of range, then forget the aggro.
if (centerPos.quickDistTo(target->pos())<=50)
break;
spawnBot->forgetAggroFor(target->dataSetRow());
_Grp->forgetAggroFor(target->dataSetRow());
*/
}
break;
default:
break;
}
}
}
// Tribus don't like players with bad fame.
{
const std::vector > &players = TribuVision.players();
LOG("%u players in group vision", players.size());
std::vector >::const_iterator first(players.begin()), last(players.end());
for (; first != last; ++first)
{
CPersistentOfPhysical *const player = (*first);
CAIEntityPhysical *const ep = player->getSpawnObj();
if ( !ep
|| !ep->isAlive()
|| ep->currentHitPoints()<=0.f)
continue;
const CRootCell *const rootCell=ep->wpos().getRootCell();
if ( rootCell
&& rootCell->getFlag()!=0 ) // Safe Zone ?
continue;
// check Fame before choosing what to do ..
sint32 const fame = ep->getFameIndexed(factionIndex);
// check if player is attacked.
if (fame<-10000 || OUTPOSTHELPERS::isAttackingFaction(factionIndex, ep))
{
_Grp->setAggroMinimumFor(ep->dataSetRow(), 1.f, false);
}
}
}
}
std::string CGrpProfileTribu::getOneLineInfoString() const
{
return "tribu profile";
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileFollowRoute //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileGoToPoint::CGrpProfileGoToPoint(CProfileOwner *owner, RYAI_MAP_CRUNCH::CWorldPosition const& startPos, RYAI_MAP_CRUNCH::CWorldPosition const& endPos, bool dontSendEvent)
: CMoveProfile(owner)
, _StartPos(startPos)
, _EndPos(endPos)
, _PathCont(NLMISC::safe_cast(owner)->getPersistent().getAStarFlag())
, _DontSendEvent(dontSendEvent)
{
PROFILE_LOG("group", "go_to_point", "ctor", "");
_GlobalOrient.setX(1);
_GlobalOrient.setY(0);
_FollowForward=true;
_ValidPosInit=false;
_StopNpc = false;
}
void CGrpProfileGoToPoint::setDirection(bool forward)
{
if ( _FollowForward==forward
&& _ValidPosInit )
return;
_ValidPosInit=true;
_FollowForward=forward;
}
void CGrpProfileGoToPoint::beginProfile()
{
PROFILE_LOG("group", "go_to_point", "begin", "");
_ProfileTerminated = false;
CMoveProfile::beginProfile();
setCurrentDestination(_EndPos); // *
}
void CGrpProfileGoToPoint::stateChangeProfile()
{
setCurrentDestination(_EndPos); // *
// set a stand at pos profile on every bots
FOREACH(it, CAliasCont, _Grp->bots())
{
CSpawnBot *sb = (*it)->getSpawnObj();
if (sb)
sb->setAIProfile(new CBotProfileStandAtPos(sb));
}
resumeProfile();
}
void CGrpProfileGoToPoint::endProfile()
{
PROFILE_LOG("group", "go_to_point", "end", "");
}
void CGrpProfileGoToPoint::resumeProfile()
{
PROFILE_LOG("group", "go_to_point", "resume", "");
FOREACH(it, TBotFollowerMap, _NpcList)
{
const CBot *const bot=it->first;
CSpawnBot *const sbot=bot->getSpawnObj();
if (!sbot)
continue;
switch (sbot->getAIProfileType())
{
case BOT_FOLLOW_POS:
break;
default: // push the correct comportment.
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
break;
}
}
setCurrentDestination(_EndPos);
}
void CGrpProfileGoToPoint::addBot(CBot* bot)
{
_NpcList[bot] = CBotFollower();
CSpawnBot *sbot=bot->getSpawnObj();
if (sbot)
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
_MustCalcRatios = true;
}
void CGrpProfileGoToPoint::removeBot(CBot* bot)
{
TBotFollowerMap::iterator it=_NpcList.find(bot);
if (it==_NpcList.end())
return;
CSpawnBotNpc *const spawnBot=NLMISC::safe_cast(bot)->getSpawn();
if (spawnBot)
spawnBot->setAIProfile(BotProfileStandAtPosFactory.createAIProfile(spawnBot));
_NpcList.erase (it);
_MustCalcRatios = true;
}
void CGrpProfileGoToPoint::setCurrentDestination(RYAI_MAP_CRUNCH::CWorldPosition const& dest)
{
_PathCont.setDestination(dest);
FOREACH(it, TBotFollowerMap, _NpcList)
it->second.setBotAtDest(false);
}
void CGrpProfileGoToPoint::calcRatios()
{
_MustCalcRatios = false;
// loop to compute max speeds
_MaxRunSpeed = FLT_MAX;
_MaxWalkSpeed = FLT_MAX;
FOREACH(it, TBotFollowerMap, _NpcList)
{
CBot *bot = it->first; //static_cast(_Grp->bots()[botFollower.getIndex()]);
CSpawnBot *sbot = bot->getSpawnObj();
if ( !sbot
|| !sbot->isAlive())
continue;
_MaxRunSpeed = std::min(sbot->runSpeed(), _MaxRunSpeed);
_MaxWalkSpeed = std::min(sbot->walkSpeed(), _MaxWalkSpeed);
}
if (_Shape!=SHAPE_RECTANGLE)
return;
const uint32 nbbots=(uint32)_NpcList.size();
_NbRange = (uint32) sqrt(_Ratio*nbbots);
if (_NbRange==0)
_NbRange=1;
_NbLines = nbbots/_NbRange;
_NbBotInNormalShape = _NbLines*_NbRange;
_Rest = nbbots-_NbBotInNormalShape;
_Cx=(double(_NbRange)-1.0)*0.5;
_Cy=(double(_NbLines)-1.0)*0.5;
_Cy=(_Cy*_NbBotInNormalShape+double(_NbLines)*_Rest)/double(nbbots);
}
void CGrpProfileGoToPoint::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(CGrpGoToPointProfileUpdate);
CFollowPathContext fpcCGrpGoToPointProfileUpdate("CGrpGoToPointProfileUpdate");
CMoveProfile::updateProfile(ticksSinceLastUpdate);
CSpawnGroupNpc *NpcGrp=NLMISC::safe_cast(_Grp.ptr());
CGroupNpc &persGrp=NpcGrp->getPersistent();
CAIVector groupPosition = NpcGrp->getCenterPos();
CAIVector perpGlobalOrient;
NpcGrp->calcCenterPos(groupPosition);
uint32 nbAtDest=0;
uint32 nbNewAtDest=0;
uint32 botIndex=0;
uint32 xIndex=0;
uint32 yIndex=0;
double dx=0;
double dy=0;
if (_Shape==SHAPE_RECTANGLE)
{
perpGlobalOrient.setX(-_GlobalOrient.y());
perpGlobalOrient.setY(_GlobalOrient.x());
}
//////////////////////////////////////////////////////////////////////////
// Calcs the correct gravity grid position (must be done only when bot are removed or add to the group.
if (_MustCalcRatios)
calcRatios ();
FOREACH(it, TBotFollowerMap, _NpcList)
{
CBotFollower &botFollower=it->second;
if (botFollower.isBotAtDest())
{
nbAtDest++;
continue;
}
CBot *bot=it->first;
CSpawnBot *sbot=bot->getSpawnObj();
if ( !sbot
|| !sbot->isAlive())
continue;
// verify if the bot has a correct profile and if he reached the destination position.
switch (sbot->getAIProfileType())
{
case BOT_FOLLOW_POS:
{
if (_ProfileTerminated)
{
// remove the profile
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
}
else
{
CBotProfileFollowPos* prof = NLMISC::safe_cast(sbot->getAIProfile());
// flag the sub profile to stop the npc
prof->setStop(_StopNpc);
if (!_StopNpc)
{
if (prof->_Status==CFollowPath::FOLLOW_ARRIVED)
{
botFollower.setBotAtDest();
nbNewAtDest++;
nbAtDest++;
}
else
{
// update speeds
prof->setMaxSpeeds(_MaxWalkSpeed, _MaxRunSpeed);
}
}
}
}
break;
default: // push the correct comportment.
{
if (!_ProfileTerminated)
{
sbot->setAIProfile(new CBotProfileFollowPos(&_PathCont, sbot));
CBotProfileFollowPos* prof = NLMISC::safe_cast(sbot->getAIProfile());
// update speeds
prof->setMaxSpeeds(_MaxWalkSpeed, _MaxRunSpeed);
}
}
break;
}
if (_Shape==SHAPE_RECTANGLE)
{
NLMISC::CVector2d dir=sbot->theta().asVector2d();
dx+=dir.x;
dy+=dir.y;
// 4 rows
CAIVector idealPos=groupPosition;
if (botIndex>=_NbBotInNormalShape)
{
idealPos += perpGlobalOrient * (_XSize*(_Cx-double(xIndex)-(_NbRange-_Rest)*0.5));
}
else
{
idealPos += perpGlobalOrient * (_XSize*(_Cx-double(xIndex)));
}
idealPos+=_GlobalOrient*(_YSize*(_Cy-(double)yIndex));
idealPos-=CAIVector(sbot->pos());
botIndex++;
xIndex++;
if (xIndex>=_NbRange)
{
xIndex=0;
yIndex++;
}
sbot->setMoveDecalage(idealPos);
}
}
if (_Shape==SHAPE_RECTANGLE)
{
_GlobalOrient.setX(dx/botIndex);
_GlobalOrient.setY(dy/botIndex);
}
// first to arrived ?
if (nbAtDest>0 && !_ProfileTerminated)
{
if (!_DontSendEvent)
{
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
}
_ProfileTerminated = true;
}
}
CGrpProfileGoToPoint::~CGrpProfileGoToPoint()
{
for (CCont::iterator it=_Grp->bots().begin(), itEnd=_Grp->bots().end();it!=itEnd;++it)
removeBot(*it);
}
CGrpProfileGoToPoint::CBotFollower::CBotFollower()
: _BotAtDest(false)
{
}
CGrpProfileGoToPoint::CBotFollower::~CBotFollower()
{
}
void CGrpProfileGoToPoint::CBotFollower::setBotAtDest(bool atDest)
{
_BotAtDest = atDest;
}
const bool& CGrpProfileGoToPoint::CBotFollower::isBotAtDest() const
{
return _BotAtDest;
}
bool CGrpProfileGoToPoint::getDirection()
{
return _FollowForward;
}
CPathCont* CGrpProfileGoToPoint::getPathCont(CBot const* bot)
{
return &_PathCont;
}
std::string CGrpProfileGoToPoint::getOneLineInfoString() const
{
std::string info = "go_to_point profile";
info += " stop_npc=" + NLMISC::toString(_StopNpc);
return info;
}
bool CGrpProfileGoToPoint::profileTerminated() const
{
return _ProfileTerminated;
}
void CGrpProfileGoToPoint::stopNpc(bool stop)
{
_StopNpc = stop;
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileGoToPoint //
//////////////////////////////////////////////////////////////////////////////
//RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileGoToPoint, "goto_point");
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileFollowRoute //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileFollowRoute::CGrpProfileFollowRoute(CProfileOwner *owner)
: CMoveProfile(owner)
, _PathCont(NLMISC::safe_cast(owner)->getPersistent().getAStarFlag())
, _DontSendEvent(false)
{
PROFILE_LOG("group", "follow_route", "ctor", "");
_GlobalOrient.setX(1);
_GlobalOrient.setY(0);
_FollowForward = true;
_StopNpc = false;
CGroupNpc &grp=*safe_cast(&_Grp->getPersistent());
if (grp.getState())
{
const CAIState *const state = grp.getCAIState();
if (state->isPositional())
{
const CAIStatePositional *const statePositionnal=static_cast(state);
_Geometry=&statePositionnal->shape().getGeometry();
_GeometryComeFromState = true;
_VerticalPos=statePositionnal->shape().getVerticalPos();
return;
}
}
_GeometryComeFromState = false;
_Geometry = NULL;
_VerticalPos = TVerticalPos();
_GeomIndex = 0;
#ifdef NL_DEBUG
nlassert(true==false); // Cannot use this constructor outside machine state context.
#endif
}
CGrpProfileFollowRoute::CGrpProfileFollowRoute(CProfileOwner *owner,const std::vector &geometry,const TVerticalPos &verticalPos, bool dontSendEvent)
: CMoveProfile(owner)
, _PathCont(NLMISC::safe_cast(owner)->getPersistent().getAStarFlag())
, _GeometryComeFromState(false)
, _Geometry(&geometry)
, _VerticalPos(verticalPos)
, _DontSendEvent(dontSendEvent)
{
PROFILE_LOG("group", "follow_route", "ctor2", "");
_GlobalOrient.setX(1);
_GlobalOrient.setY(0);
_FollowForward=true;
_ValidPosInit=false;
_GeomIndex=0;
_StopNpc = false;
}
void CGrpProfileFollowRoute::setDirection(bool forward)
{
if ( _FollowForward==forward
&& _ValidPosInit )
return;
_ValidPosInit=true;
_FollowForward=forward;
#ifdef NL_DEBUG
nlassert(_Geometry);
#endif
setCurrentValidPos(_VerticalPos);
}
void CGrpProfileFollowRoute::beginProfile()
{
PROFILE_LOG("group", "follow_route", "begin", "");
_ProfileTerminated = false;
CMoveProfile::beginProfile();
if (_GeometryComeFromState)
assignGeometryFromState();
}
void CGrpProfileFollowRoute::assignGeometryFromState()
{
_ProfileTerminated = false;
// default value initialization.
std::string shape;
_Shape = SHAPE_NOTHING;
_XSize = 1;
_YSize = 1;
_Ratio = 1;
if (_Grp->getProfileParameter("shape", shape)
&& shape=="rectangle")
{
_Shape = SHAPE_RECTANGLE;
}
_Grp->getProfileParameter("ratio", _Ratio);
_Grp->getProfileParameter("xsize", _XSize);
_Grp->getProfileParameter("ysize", _YSize);
_GeomIndex=0;
{
CGroup &persGrp=NLMISC::safe_cast(_Grp.ptr())->getPersistent();
// R2_PRIMITIVE_LAXITY
if (IsRingShard.get())
{
nlassertex(_Geometry, ("CGrpProfileFollowRoute : NULL geometry data for group '%s'", persGrp.getFullName().c_str()));
if (_Geometry->empty())
{
nlwarning("CGrpProfileFollowRoute : missing geometry data for group '%s'", persGrp.getFullName().c_str());
}
}
else
{
nlassertex(_Geometry && _Geometry->size()>0, ("CGrpProfileFollowRoute : missing geometry data for group '%s'", persGrp.getFullName().c_str()));
}
}
CSpawnGroupNpc *grp = static_cast(static_cast(_Grp));
CGroupNpc &pgrp = grp->getPersistent();
const CAIState *const state = pgrp.getActiveState();
if ( !state
|| !state->isPositional())
return;
const CAIStatePositional *const sp = static_cast(state);
if (!sp->shape().hasPoints() && (_GeometryComeFromState))
{
nlwarning("Error, no position in state '%s'%s",
sp->getAliasFullName().c_str(),
sp->getAliasString().c_str());
}
else
{
_Geometry = &(sp->shape().getGeometry());
_GeometryComeFromState = true;
_MustCalcRatios = true;
}
setCurrentValidPos (_VerticalPos); // static_cast(_Grp.ptr())->getPersistent().getGeometryVerticalPos(), geometry);
}
void CGrpProfileFollowRoute::stateChangeProfile()
{
assignGeometryFromState();
// set a stand at pos profile on every bots
FOREACH(it, CAliasCont, _Grp->bots())
{
CSpawnBot *sb = (*it)->getSpawnObj();
if (sb)
sb->setAIProfile(new CBotProfileStandAtPos(sb));
}
// Reset stop flag to false
stopNpc(false);
resumeProfile();
}
void CGrpProfileFollowRoute::endProfile()
{
PROFILE_LOG("group", "follow_route", "end", "");
}
void CGrpProfileFollowRoute::resumeProfile()
{
PROFILE_LOG("group", "follow_route", "resume", "");
FOREACH(it, TBotFollowerMap, _NpcList)
{
const CBot *const bot=it->first;
CSpawnBot *const sbot=bot->getSpawnObj();
if (!sbot)
continue;
switch (sbot->getAIProfileType())
{
case BOT_FOLLOW_POS:
break;
default: // push the correct comportment.
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
break;
}
}
// setCurrentValidPos (NLMISC::safe_cast(NLMISC::safe_cast(_Grp)->getPersistent().getCAIState()));
#ifdef NL_DEBUG
nlassert(_Geometry);
#endif
setCurrentValidPos (_VerticalPos);
}
void CGrpProfileFollowRoute::addBot (CBot *bot)
{
_NpcList[bot]=CBotFollower();
CSpawnBot *sbot=bot->getSpawnObj();
if (sbot)
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
_MustCalcRatios = true;
}
void CGrpProfileFollowRoute::removeBot (CBot *bot)
{
TBotFollowerMap::iterator it=_NpcList.find(bot);
if (it==_NpcList.end())
return;
CSpawnBotNpc *const spawnBot=NLMISC::safe_cast(bot)->getSpawn();
if (spawnBot)
spawnBot->setAIProfile(BotProfileStandAtPosFactory.createAIProfile(spawnBot));
_NpcList.erase (it);
_MustCalcRatios = true;
}
void CGrpProfileFollowRoute::setCurrentValidPos (TVerticalPos verticalPos)
{
if (_Geometry->size()>0)
{
#if !FINAL_VERSION
nlassert(_Geometry!=NULL);
#endif
size_t index = getDirection()?_GeomIndex:(_Geometry->size()-_GeomIndex-1);
#if !FINAL_VERSION
nlassert(index < _Geometry->size());
#endif
_PathCont.setDestination (verticalPos, (*_Geometry)[index]);
}
FOREACH(it, TBotFollowerMap, _NpcList)
it->second.setBotAtDest(false);
}
void CGrpProfileFollowRoute::calcRatios ()
{
_MustCalcRatios = false;
// loop to compute max speeds
_MaxRunSpeed = FLT_MAX;
_MaxWalkSpeed = FLT_MAX;
FOREACH(it, TBotFollowerMap, _NpcList)
{
CBot *bot = it->first; //static_cast(_Grp->bots()[botFollower.getIndex()]);
CSpawnBot *sbot = bot->getSpawnObj();
if ( !sbot
|| !sbot->isAlive())
continue;
_MaxRunSpeed = std::min(sbot->runSpeed(), _MaxRunSpeed);
_MaxWalkSpeed = std::min(sbot->walkSpeed(), _MaxWalkSpeed);
}
if (_Shape!=SHAPE_RECTANGLE)
return;
const uint32 nbbots=(uint32)_NpcList.size();
_NbRange = (uint32) sqrt(_Ratio*nbbots);
if (_NbRange==0)
_NbRange=1;
_NbLines = nbbots/_NbRange;
_NbBotInNormalShape = _NbLines*_NbRange;
_Rest = nbbots-_NbBotInNormalShape;
_Cx=(double(_NbRange)-1.0)*0.5;
_Cy=(double(_NbLines)-1.0)*0.5;
_Cy=(_Cy*_NbBotInNormalShape+double(_NbLines)*_Rest)/double(nbbots);
}
void CGrpProfileFollowRoute::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpFollowRouteProfileUpdate);
CFollowPathContext fpcGrpFollowRouteProfileUpdate("GrpFollowRouteProfileUpdate");
CMoveProfile::updateProfile(ticksSinceLastUpdate);
CSpawnGroupNpc *NpcGrp=NLMISC::safe_cast(_Grp.ptr());
CGroupNpc &persGrp=NpcGrp->getPersistent();
CAIVector groupPosition = NpcGrp->getCenterPos();
CAIVector perpGlobalOrient;
NpcGrp->calcCenterPos(groupPosition);
uint32 nbAtDest=0;
uint32 nbNewAtDest=0;
uint32 botIndex=0;
uint32 xIndex=0;
uint32 yIndex=0;
double dx=0;
double dy=0;
// R2_PRIMITIVE_LAXITY
if (IsRingShard.get())
{
if (!_ProfileTerminated && _Geometry->empty())
_ProfileTerminated = true;
}
if (_Shape==SHAPE_RECTANGLE)
{
perpGlobalOrient.setX(-_GlobalOrient.y());
perpGlobalOrient.setY(_GlobalOrient.x());
}
//////////////////////////////////////////////////////////////////////////
// Calcs the correct gravity grid position (must be done only when bot are removed or add to the group.
if (_MustCalcRatios)
calcRatios ();
FOREACH(it, TBotFollowerMap, _NpcList)
{
CBotFollower &botFollower=it->second;
if (botFollower.isBotAtDest())
{
nbAtDest++;
continue;
}
CBot *bot=it->first;
CSpawnBot *sbot=bot->getSpawnObj();
if ( !sbot
|| !sbot->isAlive())
continue;
// verify if the bot has a correct profile and if he reached the destination position.
switch (sbot->getAIProfileType())
{
case BOT_FOLLOW_POS:
{
if (_ProfileTerminated)
{
// remove the profile
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
}
else
{
CBotProfileFollowPos* prof = NLMISC::safe_cast(sbot->getAIProfile());
// flag the sub profile to stop the npc
prof->setStop(_StopNpc);
if (!_StopNpc)
{
if (prof->_Status==CFollowPath::FOLLOW_ARRIVED)
{
botFollower.setBotAtDest();
nbNewAtDest++;
if (simulateBug(2))
{
/* Following statement was missing */
}
else
{
nbAtDest++;
}
}
else
{
// update speeds
prof->setMaxSpeeds(_MaxWalkSpeed, _MaxRunSpeed);
}
}
}
}
break;
default: // push the correct comportment.
{
if (!_ProfileTerminated)
{
sbot->setAIProfile(new CBotProfileFollowPos(&_PathCont, sbot));
CBotProfileFollowPos* prof = NLMISC::safe_cast(sbot->getAIProfile());
// update speeds
prof->setMaxSpeeds(_MaxWalkSpeed, _MaxRunSpeed);
}
}
break;
}
if (_Shape==SHAPE_RECTANGLE)
{
NLMISC::CVector2d dir=sbot->theta().asVector2d();
dx+=dir.x;
dy+=dir.y;
// 4 rows
CAIVector idealPos=groupPosition;
if (botIndex>=_NbBotInNormalShape)
{
idealPos += perpGlobalOrient * (_XSize*(_Cx-double(xIndex)-(_NbRange-_Rest)*0.5));
}
else
{
idealPos += perpGlobalOrient * (_XSize*(_Cx-double(xIndex)));
}
idealPos+=_GlobalOrient*(_YSize*(_Cy-(double)yIndex));
idealPos-=CAIVector(sbot->pos());
botIndex++;
xIndex++;
if (xIndex>=_NbRange)
{
xIndex=0;
yIndex++;
}
sbot->setMoveDecalage(idealPos);
}
}
if (_Shape==SHAPE_RECTANGLE)
{
_GlobalOrient.setX(dx/botIndex);
_GlobalOrient.setY(dy/botIndex);
}
// first to arrived ?
if (nbAtDest>0 && !_ProfileTerminated)
{
// oh la la (la, let 's go dancing)..
_GeomIndex++;
#ifdef NL_DEBUG
nlassert(_Geometry);
#endif
if (_GeomIndex>=_Geometry->size()) // we reach the end.
{
_GeomIndex=0;
if (!_DontSendEvent)
{
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
}
_ProfileTerminated = true;
}
else
{
setCurrentValidPos(_VerticalPos);
}
}
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileStandOnVertices //
//////////////////////////////////////////////////////////////////////////////
CPathCont* CGrpProfileStandOnVertices::getPathCont(CBot const* bot)
{
TNpcBotPositionnerMap::const_iterator it=_NpcList.find(bot);
if (it==_NpcList.end())
return NULL;
return &it->second->_PathCont;
}
void CGrpProfileStandOnVertices::beginProfile()
{
PROFILE_LOG("group", "stand_on_vertices", "begin", "");
CMoveProfile::beginProfile();
CSpawnGroupNpc *NpcGrp=NLMISC::safe_cast(_Grp.ptr());
CAIStatePositional *grpState=NLMISC::safe_cast(NpcGrp->getPersistent().getCAIState());
if ( !grpState
|| grpState->shape().numPoints() == 0
|| !grpState->isPositional())
{
if (grpState)
nlwarning("CGrpProfileStandOnVertices::beginProfile : grpState without valid points %s for group %s", grpState->getAliasFullName().c_str(), NpcGrp->getPersistent().getFullName().c_str());
else
nlwarning("CGrpProfileStandOnVertices::beginProfile : invalid no grpState for group %s", NpcGrp->getPersistent().getFullName().c_str());
}
setCurrentValidPos (grpState);
_Finished=false;
}
void CGrpProfileStandOnVertices::setCurrentValidPos(CAIStatePositional *grpState)
{
for (TNpcBotPositionnerMap::iterator it=_NpcList.begin(), itEnd=_NpcList.end();it!=itEnd;++it)
{
CBotPositionner *botPos=(*it).second;
botPos->setBotAtDest(false);
botPos->_PathCont.setDestination(grpState->shape().getVerticalPos(), *grpState->shape().point(botPos->_GeomIndex));
}
}
void CGrpProfileStandOnVertices::resumeProfile()
{
PROFILE_LOG("group", "stand_on_vertices", "resume", "");
for (TNpcBotPositionnerMap::iterator it=_NpcList.begin(), itEnd=_NpcList.end();it!=itEnd;++it)
{
const CBot*const bot=(*it).first;
CSpawnBot *sbot=bot->getSpawnObj();
if (!sbot)
continue;
switch (sbot->getAIProfileType())
{
case BOT_FOLLOW_POS:
break;
default: // push the correct behaviour
{
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
}
break;
}
}
CSpawnGroupNpc *NpcGrp=NLMISC::safe_cast(_Grp.ptr());
CAIStatePositional *grpState=NLMISC::safe_cast(NpcGrp->getPersistent().getCAIState());
nlassert( grpState->shape().numPoints()>0
&& grpState
&& grpState->isPositional());
setCurrentValidPos (grpState);
_Finished=false;
}
void CGrpProfileStandOnVertices::addBot (CBot *bot)
{
CGroupNpc& grp=NLMISC::safe_cast(bot)->grp();
#ifdef NL_DEBUG
nlassert(grp.getSpawnObj());
#endif
CAIStatePositional *grpState=NLMISC::safe_cast(grp.getCAIState());
CSpawnBot *const sbot=bot->getSpawnObj();
if ( grpState->shape().numPoints() < 1 )
{
nlwarning("CGrpProfileStandOnVertices : group state '%s'%s: no vertice !", grpState->getAliasFullName().c_str(), grpState->getAliasString().c_str());
return;
}
if (sbot)
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
_NpcList[bot]=new CBotPositionner (bot->getChildIndex()%grpState->shape().numPoints(), bot->getGroup().getAStarFlag());
}
void CGrpProfileStandOnVertices::removeBot (CBot *bot)
{
TNpcBotPositionnerMap::iterator it=_NpcList.find(bot);
if (it!=_NpcList.end())
{
CSpawnBotNpc *spawnBot=NLMISC::safe_cast(bot)->getSpawn();
if (spawnBot)
spawnBot->setAIProfile(new CBotProfileStandAtPos(spawnBot));
_NpcList.erase (it);
}
}
void CGrpProfileStandOnVertices::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpStandProfileUpdate);
CFollowPathContext fpcGrpStandProfileUpdate("GrpStandProfileUpdate");
CMoveProfile::updateProfile(ticksSinceLastUpdate);
CGroupNpc &persGrp=NLMISC::safe_cast(_Grp.ptr())->getPersistent();
CAIStatePositional *grpState=static_cast(persGrp.getCAIState());
uint32 nbAtDest=0;
uint32 nbNewAtDest=0;
for (TNpcBotPositionnerMap::iterator it=_NpcList.begin(), itEnd=_NpcList.end();it!=itEnd;++it)
{
CBotPositionner *botPos=(*it).second;
if (!botPos->isBotAtDest())
{
const CBot*const bot=(*it).first;
CSpawnBot *sbot=bot->getSpawnObj();
if ( sbot
&& sbot->isAlive())
{
switch (sbot->getAIProfileType())
{
case BOT_FOLLOW_POS:
{
CBotProfileFollowPos* prof = NLMISC::safe_cast(sbot->getAIProfile());
if (prof->_Status==CFollowPath::FOLLOW_ARRIVED)
{
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
botPos->setBotAtDest();
nbNewAtDest++;
if (simulateBug(2))
{
/* Following statement was missing */
}
else
{
nbAtDest++;
}
}
}
break;
default: // push the correct comportment.
{
sbot->setAIProfile(new CBotProfileFollowPos(&botPos->_PathCont, sbot));
}
break;
}
}
}
else
{
nbAtDest++;
}
}
// first to arrived ?
if ( nbNewAtDest==nbAtDest
&& nbAtDest>0 )
{
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
}
// all arrived ?
if ( nbAtDest==_NpcList.size()
&& !_Finished )
{
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
_Finished=true;
}
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileFollowPlayer //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileFollowPlayer::CGrpProfileFollowPlayer(CProfileOwner* owner, TDataSetRow const& playerRow, uint32 dispersionRadius)
: CMoveProfile(owner)
, _PlayerRow(playerRow)
, _DispersionRadius(dispersionRadius)
, _PathPos(CAngle(0))
, _PathCont(NLMISC::safe_cast(owner)->getAStarFlag())
{
PROFILE_LOG("group", "follow player", "ctor", "");
_Status = CFollowPath::FOLLOWING;
}
bool CGrpProfileFollowPlayer::destinationReach() const
{
return _Status == CFollowPath::FOLLOW_ARRIVED
|| _Status==CFollowPath::FOLLOW_NO_PATH;
}
void CGrpProfileFollowPlayer::beginProfile()
{
_Status = CFollowPath::FOLLOWING;
}
// TODO: this doesn't work very well at all...
void CGrpProfileFollowPlayer::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(CGrpProfileFollowPlayerUpdate);
CFollowPathContext fpcGrpFollowPlayerUpdate("CGrpProfileFollowPlayerUpdate");
// check all bot to see if there need to move
CSpawnGroupNpc* grp = static_cast(static_cast(_Grp));
CGroupNpc &pgrp = grp->getPersistent();
CBotPlayer* plrPtr = dynamic_cast(CAIS::instance().getEntityPhysical(_PlayerRow));
if ( ! plrPtr) {
nlwarning("CGrpProfileFollowPlayer: No valid player position to follow");
return;
}
_PathCont.setDestination(plrPtr->wpos());
_PathPos._Angle = plrPtr->theta();
for (uint i = 0; i < pgrp.bots().size(); ++i)
{
CBotNpc* bot = static_cast(pgrp.bots()[i]);
if (!bot)
continue;
// check current bot state
CSpawnBotNpc *sbot = bot->getSpawn();
if (!sbot)
continue;
// Need to wait for a correct position before moving?
CAIVector const& dest = _PathCont.getDestination();
if (dest.x()==0 || dest.y()==0)
return;
static const std::string runParameter("running");
float dist;
if (sbot->getPersistent().getOwner()->getSpawnObj()->checkProfileParameter(runParameter))
dist = sbot->runSpeed()*ticksSinceLastUpdate;
else
dist = sbot->walkSpeed()*ticksSinceLastUpdate;
// Move
CFollowPath::TFollowStatus const status = CFollowPath::getInstance()->followPath(
sbot,
_PathPos,
_PathCont,
dist,
0.f,
0.5f);
if (status==CFollowPath::FOLLOW_NO_PATH)
{
nlwarning("Problem with following player");
}
}
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileIdle //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileIdle::CGrpProfileIdle(CProfileOwner* owner)
: CMoveProfile(owner)
{
PROFILE_LOG("group", "idle", "ctor", "");
}
CGrpProfileIdle::~CGrpProfileIdle()
{
PROFILE_LOG("group", "idle", "dtor", "");
FOREACH(it, CCont, _Grp->bots())
{
CBot* bot = *it;
removeBot(bot);
}
}
CGrpProfileIdle::CBotPositionner::CBotPositionner()
{
}
CGrpProfileIdle::CBotPositionner::~CBotPositionner()
{
}
void CGrpProfileIdle::beginProfile()
{
PROFILE_LOG("group", "idle", "begin", "");
CMoveProfile::beginProfile();
}
CPathCont* CGrpProfileIdle::getPathCont(CBot const* bot)
{
return NULL;
}
void CGrpProfileIdle::resumeProfile()
{
PROFILE_LOG("group", "idle", "resume", "");
typedef std::map TCont;
FOREACH(it, TCont, _NpcList)
{
CBot* bot = (*it).first;
CSpawnBot* sbot = bot->getSpawnObj();
if (sbot)
{
switch (sbot->getAIProfileType())
{
case BOT_STAND_AT_POS:
break;
default: // push the correct comportment.
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
break;
}
}
}
}
void CGrpProfileIdle::addBot(CBot* bot)
{
#ifdef NL_DEBUG
CGroupNpc& grp=NLMISC::safe_cast(bot)->grp();
nlassert(grp.getSpawnObj());
#endif
_NpcList[bot]=CBotPositionner ();
CSpawnBot *sbot=bot->getSpawnObj();
if (sbot)
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
}
void CGrpProfileIdle::removeBot(CBot* bot)
{
std::map::iterator it=_NpcList.find(bot);
if (it!=_NpcList.end())
{
_NpcList.erase (it);
}
}
void CGrpProfileIdle::endProfile()
{
PROFILE_LOG("group", "idle", "end", "");
}
void CGrpProfileIdle::updateProfile(uint ticksSinceLastUpdate)
{
CMoveProfile::updateProfile(ticksSinceLastUpdate);
}
std::string CGrpProfileIdle::getOneLineInfoString() const
{
return "idle profile";
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileWander //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileWander::CGrpProfileWander(CProfileOwner* owner, CNpcZone const* npcZone)
: CMoveProfile(owner)
, _Social(false)
, _NpcZone(npcZone)
{
PROFILE_LOG("group", "wander", "ctor", "");
_BotStandProfileType = BOT_STAND_AT_POS;
_BotStandProfileFactory = &BotProfileStandAtPosFactory;
_RandomPos=&npcZone->getPlaceRandomPos();
}
CGrpProfileWander::CGrpProfileWander(CProfileOwner* owner)
: CMoveProfile(owner)
, _Social(false)
{
PROFILE_LOG("group", "wander", "ctor2", "");
affectZoneFromStateMachine();
#if !FINAL_VERSION
// R2_PRIMITIVE_LAXITY
if (!IsRingShard.get())
{
nlassert(!_RandomPos.isNULL());
}
#endif
// default to stand apt pos profile
_BotStandProfileType = BOT_STAND_AT_POS;
_BotStandProfileFactory = &BotProfileStandAtPosFactory;
}
CGrpProfileWander::~CGrpProfileWander()
{
PROFILE_LOG("group", "wander", "dtor", "");
}
void CGrpProfileWander::stateChangeProfile()
{
affectZoneFromStateMachine();
resetDestinationReachedData();
}
void CGrpProfileWander::affectZoneFromStateMachine()
{
CSpawnGroupNpc* grp = static_cast(static_cast(_Grp));
CGroupNpc& pgrp = grp->getPersistent();
CAIState const* const state = pgrp.getActiveState();
if ( !state
|| !state->isPositional())
return;
CAIStatePositional const* const sp = static_cast(state);
if (!sp->shape().hasPoints())
{
if (sp->getAliasNode())
nlwarning("Error, no position in state %s", sp->getAliasNode()->fullName().c_str());
else
nlwarning("Error, no position in state %s", sp->getName().c_str());
}
if (sp->shape().hasPatat())
_RandomPos=&(sp->shape());
}
void CGrpProfileWander::resetDestinationReachedData()
{
_DestinationReachedFirst = false;
_DestinationReachedAll = false;
std::fill(_NpcDestinationReached.begin(), _NpcDestinationReached.end(), false);
}
void CGrpProfileWander::setBotStandProfile(TProfiles botStandProfileType, IAIProfileFactory *botStandProfileFactory)
{
_BotStandProfileType = botStandProfileType;
_BotStandProfileFactory = botStandProfileFactory;
}
void CGrpProfileWander::beginProfile()
{
PROFILE_LOG("group", "wander", "begin", "");
CMoveProfile::beginProfile();
CSpawnGroupNpc *grp = static_cast(static_cast(_Grp));
if (grp->checkProfileParameter("forage"))
{
_BotStandProfileType = BOT_FORAGE;
_BotStandProfileFactory = &BotProfileForageFactory;
}
else if (grp->checkProfileParameter("social"))
{
_Social = true;
}
_NpcDestinationReached.resize( grp->getPersistent().bots().size());
resetDestinationReachedData();
}
void CGrpProfileWander::addBot(CBot* bot)
{
CSpawnBot *const spawnBot=bot->getSpawnObj();
if (!spawnBot)
return;
if (_BotStandProfileType == BOT_FORAGE)
{
// special case, only set the forage activity and let the bot live there life
spawnBot->setAIProfile(_BotStandProfileFactory->createAIProfile(spawnBot));
return;
}
CBotProfileStandAtPos* const profile = new CBotProfileStandAtPos(spawnBot);
#ifdef NL_DEBUG
nlassert(profile!=NULL);
#endif
spawnBot->setAIProfile(profile);
}
void CGrpProfileWander::removeBot(CBot* bot)
{
}
CPathCont* CGrpProfileWander::getPathCont(CBot const* bot)
{
return NULL;
}
void CGrpProfileWander::endProfile()
{
PROFILE_LOG("group", "wander", "end", "");
}
void CGrpProfileWander::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpWanderProfileUpdate);
CFollowPathContext fpcGrpWanderProfileUpdate("GrpWanderProfileUpdate");
if (_BotStandProfileType == BOT_FORAGE)
{
// special case, only set the forage activity and let the bot live there life
return;
}
// check all bot to see if there need to move
CSpawnGroupNpc* grp = static_cast(static_cast(_Grp));
CGroupNpc &pgrp = grp->getPersistent();
bool aNpcHasReachDestination = false;
for (uint i=0; i(pgrp.bots()[i]);
if (!bot)
continue;
// check current bot state
CSpawnBotNpc *sbot = bot->getSpawn();
if (!sbot)
continue;
IAIProfile *profile = sbot->getAIProfile();
if (!profile)
{
// init a profile on the bot
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
continue;
}
if (profile->getAIProfileType() == BOT_MOVE_TO)
{
// check if we are arrived
CBotProfileMoveTo* mt = static_cast(profile);
if (mt->destinationReach())
{
if (!_DestinationReachedAll)
{
uint32 npcSize = (uint32)pgrp.bots().size();
uint32 reachedSize = (uint32)_NpcDestinationReached.size();
if (reachedSize!= npcSize)
{
_NpcDestinationReached.resize(npcSize);
// invalid the vector a new bot has arrived
if (npcSize>reachedSize){ std::fill(_NpcDestinationReached.begin(), _NpcDestinationReached.end(), false); }
}
if ( !_NpcDestinationReached[i])
{
_NpcDestinationReached[i] = true;
aNpcHasReachDestination = true;
}
}
// look arround for interesting target
CAIVision vision;
vision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), CAIVector(sbot->pos()), 10, 10);
CPersistentOfPhysical *target = NULL;
if (!vision.players().empty())
{
// there are some player near, look at one if it is not behin us
uint index = CAIS::rand16((uint32)vision.players().size());
CAngle angle(CAngle::pi());
while (index < vision.players().size() && !target)
{
CPersistentOfPhysical *pop = vision.players()[index++];
angle = sbot->pos().angleTo(pop->getSpawnObj()->pos());
if (angle < CAngle::pi()/2 && angle > CAngle::pi()/-2)
{
target = pop;
break;
}
}
}
if (!target && !vision.bots().empty())
{
// there are some bots near, look at one if it is not behin us
uint index = CAIS::rand16((uint32)vision.bots().size());
CAngle angle(CAngle::pi());
while (index < vision.bots().size() && !target)
{
CPersistentOfPhysical *pop = vision.bots()[index++];
angle = sbot->pos().angleTo(pop->getSpawnObj()->pos());
if (angle < CAngle::pi()/2 && angle > CAngle::pi()/-2)
{
target = pop;
break;
}
}
}
// set the visual target
if (target && target->isSpawned())
sbot->setVisualTarget(target->getSpawnObj());
else
sbot->setVisualTarget(NULL);
// now, set the idle activity with a random timer
sbot->setAIProfile(_BotStandProfileFactory->createAIProfile(sbot));
CBotProfileWanderBase *wbs = safe_cast(sbot->getAIProfile());
if (wbs)
{
float waitMin;
float waitMax;
static string waitMinStr("wait min");
static string waitMaxStr("wait max");
if (grp->getProfileParameter(waitMinStr, waitMin))
{
if (!grp->getProfileParameter(waitMaxStr, waitMax))
waitMax = waitMin;
else
waitMax = waitMax > waitMin ? waitMax : waitMin;
}
else
{
waitMin = float(DefaultWanderMinTimer);
waitMax = float(DefaultWanderMaxTimer);
}
wbs->setTimer(uint32(waitMin+CAIS::rand32(uint32(waitMax-waitMin))));
}
}
continue;
}
if (profile->getAIProfileType()==_BotStandProfileType)
{
const CBotProfileWanderBase*const wbs = static_cast(sbot->getAIProfile());
#ifdef NL_DEBUG
nlassert(wbs);
#endif
if (!wbs->testTimer())
continue;
}
#ifdef NL_DEBUG
nlassert(_RandomPos != NULL && _RandomPos->getRandomPosCount() != 0);
#endif
// R2_PRIMITIVE_LAXITY
// for a Ring shard stop here and only do a warning to avoid asserting later
// primitives generated by players may be incorrect and should not crash the service
if (IsRingShard.get())
{
if ( _RandomPos == NULL
|| _RandomPos->getRandomPosCount() == 0)
{
string stateName = "NULL state!";
CAIState const* const state = pgrp.getActiveState();
if (state != NULL)
stateName = state->getAliasFullName();
nlwarning("No valid wander position for state '%s'", stateName.c_str());
return;
}
}
// time out, move to another point in the geometry
RYAI_MAP_CRUNCH::CWorldPosition wp;
if (_Social && CAIS::rand32(3) == 0)
{
// this time, we try to reach an npc in the neighbour
//TODO : implemente this behavior
}
else
{
// standard random move
_RandomPos->getRandomPos(wp);
}
CBotProfileMoveTo* mts = new CBotProfileMoveTo(_RandomPos->getVerticalPos(), wp, sbot);
sbot->setAIProfile(mts);
}
if (aNpcHasReachDestination && !_DestinationReachedAll)
{
if (!_DestinationReachedFirst)
{
_DestinationReachedFirst = true;
pgrp.processStateEvent(pgrp.mgr().EventDestinationReachedFirst);
}
uint32 first=0, last=(uint32)_NpcDestinationReached.size();
for ( ; first != last && _NpcDestinationReached[first]; ++first) {}
if (first == last)
{
_DestinationReachedAll = true;
pgrp.processStateEvent(pgrp.mgr().EventDestinationReachedAll);
}
}
}
std::string CGrpProfileWander::getOneLineInfoString() const
{
return "wander group profile";
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileWanderNoPrim //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileWanderNoPrim::CGrpProfileWanderNoPrim(CProfileOwner* owner, NLMISC::CSmartPtr const& npcZone)
: CMoveProfile(owner)
, _Social(false)
, _NpcZone(npcZone)
{
PROFILE_LOG("group", "wander", "ctor", "");
_BotStandProfileType = BOT_STAND_AT_POS;
_BotStandProfileFactory = &BotProfileStandAtPosFactory;
// _RandomPos = &npcZone->getPlaceRandomPos();
}
/*
CGrpProfileWanderNoPrim::CGrpProfileWanderNoPrim(CProfileOwner* owner)
: CMoveProfile(owner)
, _Social(false)
{
PROFILE_LOG("group", "wander", "ctor2", "");
affectZoneFromStateMachine();
#if !FINAL_VERSION
nlassert(!_RandomPos.isNULL());
#endif
// default to stand apt pos profile
_BotStandProfileType = BOT_STAND_AT_POS;
_BotStandProfileFactory = &BotProfileStandAtPosFactory;
}
*/
void CGrpProfileWanderNoPrim::stateChangeProfile()
{
// affectZoneFromStateMachine();
// resetDestinationReachedData();
}
/*
void CGrpProfileWanderNoPrim::affectZoneFromStateMachine()
{
CSpawnGroupNpc* grp = static_cast(static_cast(_Grp));
CGroupNpc& pgrp = grp->getPersistent();
CAIState const* const state = pgrp.getActiveState();
if ( !state
|| !state->isPositional())
return;
CAIStatePositional const* const sp = static_cast(state);
if (!sp->shape().hasPoints())
{
if (sp->getAliasNode())
nlwarning("Error, no position in state %s", sp->getAliasNode()->fullName().c_str());
else
nlwarning("Error, no position in state %s", sp->getName().c_str());
}
if (sp->shape().hasPatat())
_RandomPos=&(sp->shape());
}
*/
CGrpProfileWanderNoPrim::~CGrpProfileWanderNoPrim()
{
PROFILE_LOG("group", "wander", "dtor", "");
}
void CGrpProfileWanderNoPrim::setBotStandProfile(TProfiles botStandProfileType, IAIProfileFactory *botStandProfileFactory)
{
_BotStandProfileType = botStandProfileType;
_BotStandProfileFactory = botStandProfileFactory;
}
void CGrpProfileWanderNoPrim::beginProfile()
{
PROFILE_LOG("group", "wander", "begin", "");
CMoveProfile::beginProfile();
CSpawnGroupNpc *grp = static_cast(static_cast(_Grp));
if (grp->checkProfileParameter("forage"))
{
_BotStandProfileType = BOT_FORAGE;
_BotStandProfileFactory = &BotProfileForageFactory;
}
else if (grp->checkProfileParameter("social"))
{
_Social = true;
}
}
void CGrpProfileWanderNoPrim::addBot(CBot* bot)
{
CSpawnBot *const spawnBot=bot->getSpawnObj();
if (!spawnBot)
return;
if (_BotStandProfileType == BOT_FORAGE)
{
// special case, only set the forage activity and let the bot live there life
spawnBot->setAIProfile(_BotStandProfileFactory->createAIProfile(spawnBot));
return;
}
CBotProfileStandAtPos* const profile = new CBotProfileStandAtPos(spawnBot);
#ifdef NL_DEBUG
nlassert(profile!=NULL);
#endif
spawnBot->setAIProfile(profile);
}
void CGrpProfileWanderNoPrim::removeBot(CBot* bot)
{
}
CPathCont* CGrpProfileWanderNoPrim::getPathCont(CBot const* bot)
{
return NULL;
}
void CGrpProfileWanderNoPrim::endProfile()
{
PROFILE_LOG("group", "wander", "end", "");
}
void CGrpProfileWanderNoPrim::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpWanderProfileUpdate);
CFollowPathContext fpcGrpWanderProfileUpdate("GrpWanderProfileUpdate");
if (_BotStandProfileType == BOT_FORAGE)
{
// special case, only set the forage activity and let the bot live there life
return;
}
// check all bot to see if there need to move
CSpawnGroupNpc* grp = static_cast(static_cast(_Grp));
CGroupNpc &pgrp = grp->getPersistent();
for (uint i=0; i(pgrp.bots()[i]);
if (!bot)
continue;
// check current bot state
CSpawnBotNpc *sbot = bot->getSpawn();
if (!sbot)
continue;
IAIProfile *profile = sbot->getAIProfile();
if (!profile)
{
// init a profile on the bot
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
continue;
}
if (profile->getAIProfileType() == BOT_MOVE_TO)
{
// check if we are arrived
CBotProfileMoveTo* mt = static_cast(profile);
if (mt->destinationReach())
{
// look arround for interesting target
CAIVision vision;
vision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), CAIVector(sbot->pos()), 10, 10);
CPersistentOfPhysical *target = NULL;
if (!vision.players().empty())
{
// there are some player near, look at one if it is not behin us
uint index = CAIS::rand16((uint32)vision.players().size());
CAngle angle(CAngle::pi());
while (index < vision.players().size() && !target)
{
CPersistentOfPhysical *pop = vision.players()[index++];
angle = sbot->pos().angleTo(pop->getSpawnObj()->pos());
if (angle < CAngle::pi()/2 && angle > CAngle::pi()/-2)
{
target = pop;
break;
}
}
}
if (!target && !vision.bots().empty())
{
// there are some bots near, look at one if it is not behin us
uint index = CAIS::rand16((uint32)vision.bots().size());
CAngle angle(CAngle::pi());
while (index < vision.bots().size() && !target)
{
CPersistentOfPhysical *pop = vision.bots()[index++];
angle = sbot->pos().angleTo(pop->getSpawnObj()->pos());
if (angle < CAngle::pi()/2 && angle > CAngle::pi()/-2)
{
target = pop;
break;
}
}
}
// set the visual target
if (target && target->isSpawned())
sbot->setVisualTarget(target->getSpawnObj());
else
sbot->setVisualTarget(NULL);
// now, set the idle activity with a random timer
sbot->setAIProfile(_BotStandProfileFactory->createAIProfile(sbot));
CBotProfileWanderBase *wbs = safe_cast(sbot->getAIProfile());
if (wbs)
{
float waitMin;
float waitMax;
static string waitMinStr("wait min");
static string waitMaxStr("wait max");
if (grp->getProfileParameter(waitMinStr, waitMin))
{
if (!grp->getProfileParameter(waitMaxStr, waitMax))
waitMax = waitMin;
else
waitMax = waitMax > waitMin ? waitMax : waitMin;
}
else
{
waitMin = float(DefaultWanderMinTimer);
waitMax = float(DefaultWanderMaxTimer);
}
wbs->setTimer(uint32(waitMin+CAIS::rand32(uint32(waitMax-waitMin))));
}
}
continue;
}
if (profile->getAIProfileType()==_BotStandProfileType)
{
const CBotProfileWanderBase*const wbs = static_cast(sbot->getAIProfile());
#ifdef NL_DEBUG
nlassert(wbs);
#endif
if (!wbs->testTimer())
continue;
}
#ifdef NL_DEBUG
nlassert(_NpcZone->getRandomPosCount());
#endif
// time out, move to another point in the geometry
RYAI_MAP_CRUNCH::CWorldPosition wp;
if (_Social && CAIS::rand32(3) == 0)
{
// this time, we try to reach an npc in the neighbour
//TODO : implemente this behavior
}
else
{
// standard random move
_NpcZone->getRandomPos(wp);
}
CBotProfileMoveTo* mts = new CBotProfileMoveTo(_NpcZone->getVerticalPos(), wp, sbot);
sbot->setAIProfile(mts);
}
}
std::string CGrpProfileWanderNoPrim::getOneLineInfoString() const
{
return "wander group profile (without primitive)";
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileStandOnVertices //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileStandAtStartPoint::CGrpProfileStandAtStartPoint(CProfileOwner* owner)
: CMoveProfile(owner)
{
PROFILE_LOG("group", "stand_at_start_point", "ctor", "");
}
CGrpProfileStandAtStartPoint::~CGrpProfileStandAtStartPoint()
{
PROFILE_LOG("group", "stand_at_start_point", "dtor", "");
for (CCont::iterator it=_Grp->bots().begin(), itEnd=_Grp->bots().end();it!=itEnd;++it)
{
CBot *bot=*it;
removeBot (bot);
}
}
CGrpProfileStandAtStartPoint::CBotPositionner::CBotPositionner(RYAI_MAP_CRUNCH::TAStarFlag flags)
: _PathCont(flags)
{
}
CGrpProfileStandAtStartPoint::CBotPositionner::CBotPositionner(TVerticalPos verticalPos, CAIPos position, RYAI_MAP_CRUNCH::TAStarFlag flag)
: _PathCont(flag)
, _Position(position)
, _VerticalPos(verticalPos)
, _BotAtDest(false)
{
_PathCont.setDestination(verticalPos, position);
}
CGrpProfileStandAtStartPoint::CBotPositionner::~CBotPositionner()
{
}
inline
void CGrpProfileStandAtStartPoint::CBotPositionner::setBotAtDest(bool atDest)
{
_BotAtDest = atDest;
}
inline
bool CGrpProfileStandAtStartPoint::CBotPositionner::isBotAtDest() const
{
return _BotAtDest;
}
CPathCont* CGrpProfileStandAtStartPoint::getPathCont(CBot const* bot)
{
TNpcBotPositionnerMap::const_iterator it = _NpcList.find(bot);
if (it==_NpcList.end())
return NULL;
return &it->second->_PathCont;
}
void CGrpProfileStandAtStartPoint::beginProfile()
{
PROFILE_LOG("group", "stand_at_start_point", "begin", "");
CMoveProfile::beginProfile();
setCurrentValidPos();
_Finished = false;
}
void CGrpProfileStandAtStartPoint::resumeProfile()
{
PROFILE_LOG("group", "stand_at_start_point", "resume", "");
for (TNpcBotPositionnerMap::iterator it=_NpcList.begin(), itEnd=_NpcList.end();it!=itEnd;++it)
{
const CBot*const bot=(*it).first;
CSpawnBot *sbot=bot->getSpawnObj();
if (!sbot)
continue;
switch (sbot->getAIProfileType())
{
case BOT_FOLLOW_POS:
break;
default: // push the correct comportment.
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
break;
}
}
setCurrentValidPos();
_Finished = false;
}
void CGrpProfileStandAtStartPoint::addBot(CBot* bot)
{
CBotNpc* botNpc = NLMISC::safe_cast(bot);
CGroupNpc& grp = botNpc->grp();
#ifdef NL_DEBUG
nlassert(grp.getSpawnObj());
#endif
CAIStatePositional *grpState=NLMISC::safe_cast(grp.getCAIState());
_NpcList[bot]=new CBotPositionner (botNpc->getStartVerticalPos(), botNpc->getStartPos(), botNpc->getGroup().getAStarFlag());
CSpawnBot *const sbot=bot->getSpawnObj();
if (sbot)
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
}
void CGrpProfileStandAtStartPoint::removeBot(CBot* bot)
{
TNpcBotPositionnerMap::iterator it=_NpcList.find(bot);
if (it==_NpcList.end())
return;
CSpawnBotNpc* const spawnBot = NLMISC::safe_cast(bot)->getSpawn();
if (spawnBot)
spawnBot->setAIProfile(new CBotProfileStandAtPos(spawnBot));
_NpcList.erase (it);
}
void CGrpProfileStandAtStartPoint::setCurrentValidPos()
{
FOREACH(it, TNpcBotPositionnerMap, _NpcList)
{
CBotPositionner *botPos=(*it).second;
botPos->setBotAtDest(false);
botPos->_PathCont.setDestination(botPos->_VerticalPos, botPos->_Position);
}
}
void CGrpProfileStandAtStartPoint::endProfile()
{
PROFILE_LOG("group", "stand_at_start_point", "end", "");
}
void CGrpProfileStandAtStartPoint::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpStandStartPointProfileUpdate);
CFollowPathContext fpcGrpStandStartPointProfileUpdate("GrpStandStartPointProfileUpdate");
CMoveProfile::updateProfile(ticksSinceLastUpdate);
CGroupNpc &persGrp=NLMISC::safe_cast(_Grp.ptr())->getPersistent();
CAIStatePositional *grpState=static_cast(persGrp.getCAIState());
uint32 nbAtDest=0;
uint32 nbNewAtDest=0;
for (TNpcBotPositionnerMap::iterator it=_NpcList.begin(), itEnd=_NpcList.end();it!=itEnd;++it)
{
CBotPositionner*const botPos=(*it).second;
if (botPos->isBotAtDest())
{
nbAtDest++;
continue;
}
const CBot*const bot=(*it).first;
CSpawnBot *sbot=bot->getSpawnObj();
if ( !sbot
|| !sbot->isAlive())
continue;
switch (sbot->getAIProfileType())
{
case BOT_FOLLOW_POS:
{
CBotProfileFollowPos* prof = NLMISC::safe_cast(sbot->getAIProfile());
if (prof->_Status==CFollowPath::FOLLOW_ARRIVED)
{
sbot->setTheta(botPos->_Position.theta());
sbot->setAIProfile(new CBotProfileStandAtPos(sbot));
botPos->setBotAtDest();
nbNewAtDest++;
if (simulateBug(2))
{
/* Following statement was missing */
}
else
{
nbAtDest++;
}
}
}
break;
default: // push the correct comportment.
{
sbot->setAIProfile(new CBotProfileFollowPos(&botPos->_PathCont, sbot));
}
break;
}
}
// first to arrived ?
if ( nbNewAtDest==nbAtDest
&& nbAtDest>0 )
{
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
}
// all arrived ?
if ( nbAtDest==_NpcList.size()
&& !_Finished )
{
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
_Finished=true;
}
}
std::string CGrpProfileStandAtStartPoint::getOneLineInfoString() const
{
std::string info = "stand_at_start_point group profile";
info += " finished=" + NLMISC::toString(_Finished);
return info;
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileEscorted //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileEscorted::CGrpProfileEscorted(CProfileOwner *owner)
: CGrpProfileNormal(owner)
{
PROFILE_LOG("group", "escorted", "ctor", "");
}
CGrpProfileEscorted::~CGrpProfileEscorted()
{
PROFILE_LOG("group", "escorted", "dtor", "");
}
void CGrpProfileEscorted::beginProfile()
{
PROFILE_LOG("group", "escorted", "begin", "");
CGrpProfileNormal::beginProfile();
_EscortTeamInRange = false;
}
void CGrpProfileEscorted::endProfile()
{
PROFILE_LOG("group", "escorted", "end", "");
if (_Grp->movingProfile().getAIProfileType() == AITYPES::MOVE_FOLLOW_ROUTE)
{
CGrpProfileFollowRoute* prof = NLMISC::safe_cast(_Grp->movingProfile().getAIProfile());
if (prof)
prof->stopNpc(false);
}
}
void CGrpProfileEscorted::stateChangeProfile()
{
CGrpProfileNormal::beginProfile();
}
void CGrpProfileEscorted::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpEscortedProfileUpdate);
CFollowPathContext fpcGrpEscortedProfileUpdate("GrpEscortedProfileUpdate");
const uint32 ESCORT_RANGE_DELTA = 3;
CGroupNpc &GrpRef = _Grp->getPersistent();
const uint16 teamId = GrpRef.getEscortTeamId();
if (teamId == CTEAM::InvalidTeamId)
{
// no escort team assigned, can't move!
_EscortTeamInRange = false;
return;
}
// we need to have some member of the escort team in range to allow movement
// look arround for an escort member
CAIVector centerPos;
bool escortAway = true;
bool escortBack = false;
float escortRange=GrpRef.getEscortRange();
double distAway = escortRange+ESCORT_RANGE_DELTA;
double distBack = escortRange-ESCORT_RANGE_DELTA;
// square the dist to speedup test
distAway *= distAway;
distBack *= distBack;
if (_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
{
CAIVision vision;
// group vision update.
vision.updateBotsAndPlayers(_Grp->getPersistent().getAIInstance(), centerPos, uint(escortRange+ESCORT_RANGE_DELTA), 0);
if (vision.players().empty())
{
// no player, no need to check, the escort is away !
escortAway = true;
}
else
{
// loop on each player until we know if escort is away or back
vector >::const_iterator first(vision.players().begin()), last(vision.players().end());
for (;first != last && (escortAway || !escortBack); ++first)
{
CBotPlayer *player = safe_cast((*first).ptr()); // (CPersistentOfPhysical*)
if (player->isAlive())
{
if (CMirrors::getTeamId(player->getSpawnObj()->dataSetRow()) == teamId)
{
// on of our escort girl ;)
// check the distance
double sqrDist = (centerPos - player->getSpawnObj()->pos()).sqrnorm();
if (sqrDist < distBack)
{
escortBack = true;
// the escort is back
}
if (sqrDist < distAway)
{
escortAway = false;
// the escort is not away
}
}
}
}
}
}
if (_EscortTeamInRange)
{
if (escortAway)
{
// switch to 'wait for escort' mode and send event 'escort away'
_EscortTeamInRange = false;
GrpRef.processStateEvent(GrpRef.mgr().getStateMachine()->EventEscortAway);
}
}
else
{
if (escortBack)
{
// switch to 'escort in range' mode and send event 'escort back'
_EscortTeamInRange = true;
GrpRef.processStateEvent(GrpRef.mgr().getStateMachine()->EventEscortBack);
}
}
if (_GroupFighting)
{
if (!_Grp->fightProfile().getAIProfile())
_Grp->fightProfile().setAIProfile(_Grp.ptr(), &GrpProfileFightFactory, false);
_Grp->fightProfile().mayUpdateProfile(ticksSinceLastUpdate);
CFightProfile* profile = NLMISC::safe_cast(_Grp->fightProfile().getAIProfile());
if (!profile->stillHaveEnnemy())
{
// :TODO: Verify if it's needed to erase bots aggro too/instead
// _Grp->clearAggroList(); // this erase all aggro.
setGroupFighting (false);
_Grp->fightProfile().setAIProfile(NULL);
(NLMISC::safe_cast(_Grp->movingProfile().getAIProfile()))->resumeProfile ();
}
}
else
{
if (!_Grp->checkProfileParameter("defenceless"))
{
if (_Grp->haveAggroOrReturnPlace())
{
if(_Grp->isGroupAlive())
{
// set the fighting comportment.
if (!_Grp->fightProfile().getAIProfile())
_Grp->fightProfile().setAIProfile(_Grp.ptr(), &GrpProfileFightFactory, false);
setGroupFighting(true);
}
}
}
}
if (_Grp->movingProfile().getAIProfileType() == AITYPES::MOVE_FOLLOW_ROUTE && !_GroupFighting)
{
CGrpProfileFollowRoute* prof = NLMISC::safe_cast(_Grp->movingProfile().getAIProfile());
if (prof)
{
prof->stopNpc(!_EscortTeamInRange);
_Grp->movingProfile().mayUpdateProfile(ticksSinceLastUpdate);
}
}
}
std::string CGrpProfileEscorted::getOneLineInfoString() const
{
std::string info = "escorted profile";
info += " escort_team_in_range=" + NLMISC::toString(_EscortTeamInRange);
uint16 teamId = _Grp->getPersistent().getEscortTeamId();
info += " team_id=" + (teamId==CTEAM::InvalidTeamId)?"InvalidTeamId":NLMISC::toString(teamId);
return info;
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileGuardEscorted //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileGuardEscorted::CGrpProfileGuardEscorted(CProfileOwner *owner)
: CGrpProfileNormal(owner)
{
PROFILE_LOG("group", "guard_escorted", "ctor", "");
_GuardProfile = new CGrpProfileGuard(owner);
_EscortedProfile = new CGrpProfileEscorted(owner);
}
CGrpProfileGuardEscorted::~CGrpProfileGuardEscorted()
{
PROFILE_LOG("group", "guard_escorted", "dtor", "");
}
void CGrpProfileGuardEscorted::beginProfile()
{
PROFILE_LOG("group", "guard_escorted", "begin", "");
CGrpProfileNormal::beginProfile();
_GuardProfile->beginProfile();
_EscortedProfile->beginProfile();
}
void CGrpProfileGuardEscorted::endProfile()
{
PROFILE_LOG("group", "guard_escorted", "end", "");
_EscortedProfile->endProfile();
_GuardProfile->endProfile();
}
void CGrpProfileGuardEscorted::stateChangeProfile()
{
CGrpProfileNormal::beginProfile();
_EscortedProfile->stateChangeProfile();
_GuardProfile->stateChangeProfile();
}
void CGrpProfileGuardEscorted::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpEscortedGuardProfileUpdate);
CFollowPathContext fpcGrpEscortedGuardProfileUpdate("GrpEscortedGuardProfileUpdate");
_GuardProfile->updateProfile(ticksSinceLastUpdate);
if (!_EscortedProfile->isGroupFighting())
_EscortedProfile->updateProfile(ticksSinceLastUpdate);
}
std::string CGrpProfileGuardEscorted::getOneLineInfoString() const
{
std::string info = "guard_escorted profile";
info += " (";
info += _GuardProfile?_GuardProfile->getOneLineInfoString():std::string("");
info += ") (";
info += _EscortedProfile?_EscortedProfile->getOneLineInfoString():std::string("");
info += ")";
return info;
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileSquad //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileSquad::CGrpProfileSquad(CProfileOwner* const owner)
: CGrpProfileFaction(owner)
{
PROFILE_LOG("group", "squad", "ctor", "");
}
CGrpProfileSquad::~CGrpProfileSquad()
{
PROFILE_LOG("group", "squad", "dtor", "");
}
void CGrpProfileSquad::beginProfile()
{
PROFILE_LOG("group", "squad", "begin", "");
CGrpProfileFaction::beginProfile();
CGroupNpc& thisGrpNpc = _Grp->getPersistent();
// Set aggro parameters
thisGrpNpc._AggroRange = 25;
thisGrpNpc._UpdateNbTicks = 10;
}
string CGrpProfileSquad::getOneLineInfoString() const
{
return "squad profile";
}
void CGrpProfileSquad::aggroEntity(CAIEntityPhysical const* entity)
{
// COutpost* outpost = getDefendedOutpost();
// if (!outpost || outpost->getShape()->atPlace(CAIVector(entity->pos())))
_Grp->setAggroMinimumFor(entity->dataSetRow(), 0.5f, false);
}
NLMISC::CSmartPtr CGrpProfileSquad::buildFirstHitPlace(TDataSetRow const& aggroBot)
{
COutpost* outpost = getDefendedOutpost();
if (outpost)
return &*(outpost->getShape());
else
return NULL;
}
/*
void CGrpProfileSquad::aggroEntity(CAIEntityPhysical const* entity)
{
COutpost* outpost = getDefendedOutpost();
if (outpost)
{
// Check that the (player) entity is in the outpost zone (according to the rules of the EGS with timers)
CMirrorPropValueRO entityInOutpostAlias(TheDataset, entity->dataSetRow(), DSPropertyIN_OUTPOST_ZONE_ALIAS);
if (entityInOutpostAlias != outpost->getAlias())
return;
}
_Grp->setBotAggroMinimum(entity->dataSetRow(), -0.5f);
}
*/
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileFaction //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileFaction::CGrpProfileFaction(CProfileOwner* const owner)
: CGrpProfileNormal(owner)
{
PROFILE_LOG("group", "faction", "ctor", "");
bNoAssist = false;
}
CGrpProfileFaction::~CGrpProfileFaction()
{
PROFILE_LOG("group", "faction", "dtor", "");
}
void CGrpProfileFaction::beginProfile()
{
PROFILE_LOG("group", "faction", "begin", "");
}
void CGrpProfileFaction::endProfile()
{
PROFILE_LOG("group", "faction", "end", "");
}
AITYPES::CPropertySet CGrpProfileFaction::_FameFactions;
void CGrpProfileFaction::initFameFactions()
{
static bool inited = false;
if (!inited)
{
inited = true;
std::map factionTranslator;
std::vector const& factionNames = CStaticFames::getInstance().getFactionNames();
std::vector::const_iterator it, end=factionNames.end();
for (it=factionNames.begin(); it!=end; ++it)
{
std::string fameFaction = CStringMapper::unmap(*it);
std::string scriptFaction = fameFactionToScriptFaction(fameFaction);
factionTranslator.insert(std::make_pair(scriptFaction, *it));
_FameFactions.addProperty(scriptFaction);
}
_FameFactions.addProperty(CPropertyId("Player"));
#ifdef NL_DEBUG_FACTION_NAME_CONVERTER
nldebug("Testing script faction name converters");
std::string names[][2] = {
{ "matis", "FamousMatis" },
{ "fyros", "FamousFyros" },
{ "tryker", "FamousTryker" },
{ "zorai", "FamousZorai" },
{ "kami", "FamousKami" },
{ "black_kami", "FamousBlackKami" },
{ "karavan", "FamousKaravan" },
{ "white_karavan", "FamousWhiteKaravan" },
{ "tribe_ancient_dryads", "FamousTribeAncientDryads" },
{ "tribe_antikamis", "FamousTribeAntikamis" },
{ "tribe_barkers", "FamousTribeBarkers" },
{ "tribe_beachcombers", "FamousTribeBeachcombers" },
{ "tribe_black_circle", "FamousTribeBlackCircle" },
{ "tribe_cholorogoos", "FamousTribeCholorogoos" },
{ "tribe_cockroaches", "FamousTribeCockroaches" },
{ "tribe_company_of_the_eternal_tree", "FamousTribeCompanyOfTheEternalTree" },
{ "tribe_corsair", "FamousTribeCorsair" },
{ "tribe_cute", "FamousTribeCute" },
{ "tribe_darkening_sap", "FamousTribeDarkeningSap" },
{ "tribe_dune_riders", "FamousTribeDuneRiders" },
{ "tribe_ecowarriors", "FamousTribeEcowarriors" },
{ "tribe_firebrands", "FamousTribeFirebrands" },
{ "tribe_first_deserter", "FamousTribeFirstDeserter" },
{ "tribe_frahar", "FamousTribeFrahar" },
{ "tribe_frahar_hunters", "FamousTribeFraharHunters" },
{ "tribe_gibbay", "FamousTribeGibbay" },
{ "tribe_goo_heads", "FamousTribeGooHeads" },
{ "tribe_green_seed", "FamousTribeGreenSeed" },
{ "tribe_hamazans_of_the_dead_seed", "FamousTribeHamazansOfTheDeadSeed" },
{ "tribe_icon_workshipers", "FamousTribeIconWorkshipers" },
{ "tribe_keepers", "FamousTribeKeepers" },
{ "tribe_kitin_gatheres", "FamousTribeKitinGatheres" },
{ "tribe_lagoon_brothers", "FamousTribeLagoonBrothers" },
{ "tribe_lawless", "FamousTribeLawless" },
{ "tribe_leviers", "FamousTribeLeviers" },
{ "tribe_master_of_the_goo", "FamousTribeMasterOfTheGoo" },
{ "tribe_matisian_border_guards", "FamousTribeMatisianBorderGuards" },
{ "tribe_night_turners", "FamousTribeNightTurners" },
{ "tribe_oasis_diggers", "FamousTribeOasisDiggers" },
{ "tribe_pyromancers", "FamousTribePyromancers" },
{ "tribe_recoverers", "FamousTribeRecoverers" },
{ "tribe_renegades", "FamousTribeRenegades" },
{ "tribe_restorers", "FamousTribeRestorers" },
{ "tribe_root_tappers", "FamousTribeRootTappers" },
{ "tribe_sacred_sap", "FamousTribeSacredSap" },
{ "tribe_sap_gleaners", "FamousTribeSapGleaners" },
{ "tribe_sap_slaves", "FamousTribeSapSlaves" },
{ "tribe_scorchers", "FamousTribeScorchers" },
{ "tribe_shadow_runners", "FamousTribeShadowRunners" },
{ "tribe_siblings_of_the_weeds", "FamousTribeSiblingsOfTheWeeds" },
{ "tribe_silt_sculptors", "FamousTribeSiltSculptors" },
{ "tribe_slavers", "FamousTribeSlavers" },
{ "tribe_smuglers", "FamousTribeSmuglers" },
{ "tribe_the_arid_matis", "FamousTribeTheAridMatis" },
{ "tribe_the_kuilde", "FamousTribeTheKuilde" },
{ "tribe_the_slash_and_burn", "FamousTribeTheSlashAndBurn" },
{ "tribe_tutors", "FamousTribeTutors" },
{ "tribe_water_breakers", "FamousTribeWaterBreakers" },
{ "tribe_woven_bridles", "FamousTribeWovenBridles" }
};
size_t size = sizeof(names)/sizeof(names[0]);
for (size_t i=0; i %s", names[i][0].c_str(), fameFactionToScriptFaction(names[i][0]).c_str());
nlassert( names[i][1] == fameFactionToScriptFaction(names[i][0]) );
nldebug("%s -> %s", names[i][1].c_str(), scriptFactionToFameFaction(names[i][1]).c_str());
nlassert( names[i][0] == scriptFactionToFameFaction(names[i][1]) );
}
#endif
}
}
bool CGrpProfileFaction::entityHavePartOfFactions(CAIEntityPhysical const* entity, AITYPES::CPropertySetWithExtraList const& factions)
{
H_AUTO(CGrpProfileFaction_entityHavePartOfFactions);
switch (entity->getRyzomType())
{
case RYZOMID::player:
{
// Test Player
if (factions.have(AITYPES::CPropertyId("Player")))
{
// nldebug("Entity has faction Player");
return true;
}
// Test if the entity is involved in an outpost war
if (entity->outpostAlias()!=0)
{
if (factions.have(NLMISC::toString("outpost:%s:%s", LigoConfig.aliasToString(entity->outpostAlias()).c_str(), entity->outpostSide()?"attacker":"defender")))
return true;
}
// Test entity fame value
std::set factionsSet = factions.properties();
std::set::const_iterator it, end = factionsSet.end();
for (it=factionsSet.begin(); it!=end; ++it)
{
string factionInfos = CStringMapper::unmap(*it);
string fameFaction = scriptFactionToFameFaction(factionInfos);
// sint32 fame = CFameInterface::getInstance().getFameOrCivilisationFame(entity->getEntityId(), CStringMapper::map(fameFaction));
sint32 const fame = entity->getFame(fameFaction);
sint32 const value = scriptFactionToFameFactionValue(factionInfos);
bool gt = scriptFactionToFameFactionGreaterThan(factionInfos);
if ((fame != NO_FAME && gt && fame > value) ||
(fame != NO_FAME && !gt && fame < value))
{
// nldebug("Entity has faction %s", CStringMapper::unmap(*it).c_str());
return true;
}
}
return false;
}
case RYZOMID::npc:
{
CSpawnBotNpc const* const sbnEntity = NLMISC::safe_cast(entity);
CGroupNpc *const gnEntityGroup = NLMISC::safe_cast(&sbnEntity->getPersistent().getGroup());
return gnEntityGroup->faction().containsPartOfStrict(factions);
}
case RYZOMID::creature:
{
CSpawnBotFauna const* const sbnEntity = NLMISC::safe_cast(entity);
CGrpFauna* const gnEntityGroup = NLMISC::safe_cast(&sbnEntity->getPersistent().getGroup());
return gnEntityGroup->faction().containsPartOfStrict(factions);
}
default:
{
return false;
}
}
}
std::string CGrpProfileFaction::scriptFactionToFameFaction(std::string name)
{
if (name.find("Famous")!=0)
return name;
std::string ret = "";
for (size_t i=6; i='A' && name[i]<='Z')
ret += name[i]-'A'+'a';
else if (name[i]>='A' && name[i]<='Z')
{
ret += "_";
ret += name[i]-'A'+'a';
}
else if (name[i] == '>' || name[i] == '<')
{
return ret;
}
else
{
ret += name[i];
}
}
return ret;
}
bool CGrpProfileFaction::scriptFactionToFameFactionGreaterThan(string name)
{
if (name.find("<") != string::npos)
return false;
return true;
}
sint32 CGrpProfileFaction::scriptFactionToFameFactionValue(string name)
{
size_t start = name.find(">");
if (start == string::npos)
{
start = name.find("<");
if (start == string::npos)
return 0;
}
sint32 value;
NLMISC::fromString(name.substr(start+1), value);
return value*6000;
}
std::string CGrpProfileFaction::fameFactionToScriptFaction(std::string name)
{
std::string ret = "Famous";
for (size_t i=0; i='a' && name[i]<='z')
ret += name[i]-'a'+'A';
else if (name[i]=='_' && name[i+1]>='a' && name[i+1]<='z')
ret += name[++i]-'a'+'A';
else
ret += name[i];
}
return ret;
}
void CGrpProfileFaction::aggroEntity(CAIEntityPhysical const* entity)
{
_Grp->setAggroMinimumFor(entity->dataSetRow(), 0.5f, false);
}
void CGrpProfileFaction::checkTargetsAround()
{
if (!_checkTargetTimer.test())
return;
CGroupNpc& thisGrpNpc = _Grp->getPersistent();
_checkTargetTimer.set(thisGrpNpc._UpdateNbTicks+CAIS::rand16(2)); // every _UpdateNbTicks+1 seconds.
initFameFactions();
CPropertySetWithExtraList const& thisFaction = thisGrpNpc.faction();
CPropertySetWithExtraList const& thisFriendFactions = thisGrpNpc.friendFaction();
CPropertySetWithExtraList const& thisEnnemyFactions = thisGrpNpc.ennemyFaction();
// We don't assist or attack players if our friends/ennemies are not in factions
bool const assistPlayers = (thisFriendFactions.containsPartOfStrictFilter("Famous*") || thisFriendFactions.have(AITYPES::CPropertyId("Player")));
bool const assistBots = !thisFriendFactions.empty() && !bNoAssist;
bool const attackPlayers = (!thisEnnemyFactions.extraSetEmpty()) || thisEnnemyFactions.containsPartOfStrictFilter("Famous*") || thisEnnemyFactions.have(AITYPES::CPropertyId("Player")) || thisEnnemyFactions.containsPartOfStrictFilter("outpost:*");
bool const attackBots = !thisEnnemyFactions.empty();
CAIVision Vision;
breakable
{
CAIVector centerPos;
if (!_Grp->calcCenterPos(centerPos)) // true if there's some bots in the group.
break;
const uint32 playerRadius=(uint32)(assistPlayers||attackPlayers?thisGrpNpc._AggroRange:0);
const uint32 botRadius=(uint32)(assistBots||attackBots?thisGrpNpc._AggroRange:0);
Vision.updateBotsAndPlayers(thisGrpNpc.getAIInstance(), centerPos, playerRadius, botRadius);
}
// Assist players
if (assistPlayers)
{
// For each player
FOREACHC (itAssisted, std::vector >, Vision.players())
{
// Get assisted entity
CPersistentOfPhysical const* const popAssisted = (*itAssisted);
CAIEntityPhysical* const epAssisted = popAssisted->getSpawnObj();
// If entity is not alive skip it
if (!epAssisted || !epAssisted->isAlive() || epAssisted->currentHitPoints()<=0.f)
continue;
// If entity is not a friend skip it
if (!entityHavePartOfFactions(epAssisted, thisFriendFactions))
continue;
// For each targeter of the assisted entity
CAIEntityPhysical const* epAttacker = epAssisted->firstTargeter();
while (epAttacker)
{
// If attacker is not in our faction attack him
if (!entityHavePartOfFactions(epAttacker, thisFaction))
aggroEntity(epAttacker);
epAttacker = epAttacker->nextTargeter();
}
}
}
// Assist bots
if (assistBots)
{
FOREACHC (itAssisted, std::vector >, Vision.bots())
{
// Get assisted entity
const CPersistentOfPhysical *const popAssisted = (*itAssisted);
CAIEntityPhysical *const epAssisted = popAssisted->getSpawnObj();
// If entity is not alive skip it
if (!epAssisted || !epAssisted->isAlive() || epAssisted->currentHitPoints()<=0.f)
continue;
// If entity is not a npc skip it
if (epAssisted->getRyzomType()!=RYZOMID::npc)
continue;
// If entity is not a friend skip it
if (!entityHavePartOfFactions(epAssisted, thisFriendFactions))
continue;
// For each targeter of the assisted entity
CAIEntityPhysical const* epAttacker = epAssisted->firstTargeter();
while (epAttacker)
{
// If attacker is not in our faction attack him
if (!entityHavePartOfFactions(epAttacker, thisFaction))
{
const CSpawnBot * spwBot = dynamic_cast(epAssisted);
if(spwBot)
{
if (spwBot->haveAggroWithEntity(epAttacker->dataSetRow()))
aggroEntity(epAttacker);
}
}
epAttacker = epAttacker->nextTargeter();
}
}
}
// Attack players
if (attackPlayers)
{
FOREACHC (itAttacked, TPersistentList, Vision.players())
{
// Get attacked entity
CPersistentOfPhysical const* const popAttacked = (*itAttacked);
CAIEntityPhysical const* const epAttacked = popAttacked->getSpawnObj();
// If entity is not alive skip it
if (!epAttacked || !epAttacked->isAlive() || epAttacked->currentHitPoints()<=0.f)
continue;
// If entity is not an ennemy skip it
if (!entityHavePartOfFactions(epAttacked, thisEnnemyFactions))
continue;
// If entity is a friend skip it
if (entityHavePartOfFactions(epAttacked, thisFriendFactions))
continue;
// If entity is in our faction skip it
if (entityHavePartOfFactions(epAttacked, thisFaction))
continue;
// If player is in safe zone skip it
CRootCell const* const rootCell = epAttacked->wpos().getRootCell();
if (rootCell && rootCell->getFlag()!=0) // Safe Zone ?
continue;
// Attack the rest
aggroEntity(epAttacked);
}
}
// Attack bots
if (attackBots)
{
FOREACHC (itAttacked, TPersistentList, Vision.bots())
{
// Get attacked entity
CPersistentOfPhysical const* const popAttacked = (*itAttacked);
CAIEntityPhysical const* const epAttacked = popAttacked->getSpawnObj();
// If entity is not alive skip it
if (!epAttacked || !epAttacked->isAlive() || epAttacked->currentHitPoints()<=0.f)
continue;
// If entity is not an ennemy skip it
if (!entityHavePartOfFactions(epAttacked, thisEnnemyFactions))
continue;
// If entity is a friend skip it
if (entityHavePartOfFactions(epAttacked, thisFriendFactions))
continue;
// If entity is in our faction skip it
if (entityHavePartOfFactions(epAttacked, thisFaction))
continue;
// Attack the rest
aggroEntity(epAttacked);
}
}
}
void CGrpProfileFaction::updateProfile(uint ticksSinceLastUpdate)
{
checkTargetsAround ();
CGrpProfileNormal::updateProfile(ticksSinceLastUpdate);
}
string CGrpProfileFaction::getOneLineInfoString() const
{
return "faction profile";
}
//////////////////////////////////////////////////////////////////////////////
// Profile factories implementation //
//////////////////////////////////////////////////////////////////////////////
//- Singleton profiles (stateless ones) --------------------------------------
CBotProfileStandAtPosFactory BotProfileStandAtPosFactory;
CBotProfileForageFactory BotProfileForageFactory;
CGrpProfileFightFactory GrpProfileFightFactory;
//- Factory registering ------------------------------------------------------
// CGrpProfileGuardFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileGuardFactory, "guard");
// CGrpProfileTribuFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileTribuFactory, "tribu");
// CGrpProfileIdleFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileIdleFactory, "idle");
// CGrpProfileStandAtStartPointFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileStandAtStartPointFactory, "stand_on_start_point");
// CGrpProfileEscortedFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileEscortedFactory, "escorted");
// CGrpProfileGuardEscorted Factory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileGuardEscortedFactory, "guard_escorted");
// CGrpProfileSquadFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileSquadFactory, "squad");
// CGrpProfileFactionFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileFactionFactory, "faction");
// CGrpProfileStandOnVerticesFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileStandOnVerticesFactory, "stand_on_vertices");
// CGrpProfileWanderFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileWanderFactory, "wander");
// CGrpProfileNormalFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileNormalFactory, "normal");
// CGrpProfileFollowRouteFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileFollowRouteFactory, "follow_route");
// CGrpProfileNoChangeFactory
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileNoChangeFactory, "no_change");
// CGrpProfileBanditFactory
NLMISC::CSmartPtr CGrpProfileBanditFactory::createAIProfile(CProfileOwner* owner)
{
LOG("bandit group profile factory create");
static bool firstCall = true;
if (firstCall)
{
CConfigFile::CVar *v = IService::getInstance()->ConfigFile.getVarPtr("DefaultBanditAggroRange");
if (v)
_DefaultAggroRange = v->asFloat();
else
_DefaultAggroRange = 15.0f;
}
return new CGrpProfileBandit(owner);
}
float CGrpProfileBanditFactory::getDefaultBanditAggroRange()
{
return _DefaultAggroRange;
}
float CGrpProfileBanditFactory::_DefaultAggroRange;
RYAI_REGISTER_PROFILE_FACTORY(CGrpProfileBanditFactory, "bandit");
// Global profile factory stuff. This should be put in some profile.cpp since it's common with fauna profiles.
IAIProfileFactory* lookupAIGrpProfile(const char *name)
{
IAIProfileFactory *ret = CAiFactoryIndirect::instance().getFactory(std::string(name));
if (!ret)
nlwarning("Can't find activity '%s', returning NULL", name);
return ret;
}
RYAI_IMPLEMENT_FACTORY_INDIRECT(IAIProfileFactory, std::string);
//***************************************************************************/
/* Below that line is magical ununderstandable stuff */
//***************************************************************************/
float getDistBetWeen(CAIEntityPhysical& creat1, CAIEntityPhysical& creat2)
{
// coz player position is not updated really 'goodly' as it can be in a invalid ai map position.
float angTo = (creat1.pos().angleTo(creat2.pos())).asRadians();
return creat1.getCollisionDist(angTo) + creat2.getCollisionDist(-angTo);
}
//////////////////////////////////////////////////////////////////////////////
// CBotProfileMoveTo //
//////////////////////////////////////////////////////////////////////////////
CBotProfileMoveTo::CBotProfileMoveTo(AITYPES::TVerticalPos verticalPos, RYAI_MAP_CRUNCH::CWorldPosition const& dest, CProfileOwner* owner)
: CAIBaseProfile()
, _VerticalPos(verticalPos)
, _Dest(dest)
, _PathCont(NLMISC::safe_cast(owner)->getAStarFlag())
, _PathPos(NLMISC::safe_cast(owner)->theta())
, _Bot(NLMISC::safe_cast(owner))
{
PROFILE_LOG("bot", "move_to", "ctor", "");
#ifdef NL_DEBUG_PTR
_Bot.setData(this);
#endif
#if !FINAL_VERSION
nlassert(dest.x()!=0 || dest.y()!=0);
#endif
}
void CBotProfileMoveTo::beginProfile()
{
PROFILE_LOG("bot", "move_to", "begin", "");
_LastPos = CAIVector(_Bot->pos());
_PathCont.setDestination(_VerticalPos, _Dest);
_Status = CFollowPath::FOLLOWING;
}
void CBotProfileMoveTo::endProfile()
{
PROFILE_LOG("bot", "move_to", "end", "");
}
void CBotProfileMoveTo::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(BotMoveToProfileUpdate);
CFollowPathContext fpcBotMoveToProfileUpdate("BotMoveToProfileUpdate");
if (!_Bot->canMove())
return;
if (_Status != CFollowPath::FOLLOW_ARRIVED)
{
static const std::string runParameter("running");
float dist;
if (_Bot->getPersistent().getOwner()->getSpawnObj()->checkProfileParameter(runParameter))
dist =_Bot->runSpeed()*ticksSinceLastUpdate;
else
dist =_Bot->walkSpeed()*ticksSinceLastUpdate;
_Status = CFollowPath::getInstance()->followPath(
_Bot,
_PathPos,
_PathCont,
dist,
0.f,
.5f);
if (_Status==CFollowPath::FOLLOW_NO_PATH)
{
// get a base pointer to allow virtual call to work
nlwarning("Follow No Path : %s", _Bot->getPersistent().getOneLineInfoString().c_str());
}
}
}
bool CBotProfileMoveTo::destinationReach() const
{
return _Status == CFollowPath::FOLLOW_ARRIVED
|| _Status==CFollowPath::FOLLOW_NO_PATH;
}
std::string CBotProfileMoveTo::getOneLineInfoString() const
{
return "move_to bot profile";
}
//////////////////////////////////////////////////////////////////////////////
// CBotProfileFollowPos //
//////////////////////////////////////////////////////////////////////////////
CBotProfileFollowPos::CBotProfileFollowPos(CBotProfileFollowPos const& other)
: CAIBaseProfile()
, _PathPos(const_cast(other)._PathPos._Angle) // Just to debug...
, _Bot(const_cast(other)._Bot)
, _PathCont(const_cast(other)._PathCont)
, _MaxWalkSpeed(FLT_MAX)
, _MaxRunSpeed(FLT_MAX)
, _Stop(false)
{
PROFILE_LOG("bot", "follow_pos", "ctor", "");
#ifdef NL_DEBUG_PTR
_Bot.setData(this);
#endif
}
CBotProfileFollowPos::CBotProfileFollowPos(CPathCont* pathCont, CProfileOwner* owner)
: CAIBaseProfile()
, _PathPos(NLMISC::safe_cast(owner)->theta())
, _Bot(NLMISC::safe_cast(owner))
, _PathCont(pathCont)
, _MaxWalkSpeed(FLT_MAX)
, _MaxRunSpeed(FLT_MAX)
, _Stop(false)
{
PROFILE_LOG("bot", "follow_pos", "ctor", "");
#ifdef NL_DEBUG
nlassert(pathCont);
#endif
}
void CBotProfileFollowPos::beginProfile()
{
PROFILE_LOG("bot", "follow_pos", "begin", "");
_Status = CFollowPath::FOLLOWING;
}
void CBotProfileFollowPos::endProfile()
{
PROFILE_LOG("bot", "follow_pos", "end", "");
}
void CBotProfileFollowPos::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(BotFollowPosProfileUpdate);
CFollowPathContext fpcBotFollowPosProfileUpdate("BotFollowPosProfileUpdate");
if (!_Bot->canMove() || _Stop)
return;
static const std::string runParameter("running");
float dist;
float speed;
if (_Bot->getPersistent().getOwner()->getSpawnObj()->checkProfileParameter(runParameter))
speed = std::min(_Bot->runSpeed(), _MaxRunSpeed);
else
speed = std::min(_Bot->walkSpeed(), _MaxWalkSpeed);
dist = speed*ticksSinceLastUpdate;
CPathCont &pathContRef=*_PathCont;
if (_Status!=CFollowPath::FOLLOW_NO_PATH)
{
_Status = CFollowPath::getInstance()->followPath(
_Bot,
_PathPos,
pathContRef,
dist,
0,
.5f);
}
if (_Status==CFollowPath::FOLLOW_NO_PATH)
{
// R2_PRIMITIVE_LAXITY
if (!IsRingShard.get())
{
nlwarning("Follow No Path for '%s'%s",
_Bot->getPersistent().getAliasFullName().c_str(),
_Bot->getPersistent().getAliasString().c_str());
}
}
}
std::string CBotProfileFollowPos::getOneLineInfoString() const
{
std::string info = "follow_pos bot profile";
info += " stop=" + NLMISC::toString(_Stop);
info += " max_walk_speed=" + NLMISC::toString(_MaxWalkSpeed);
info += " max_run_speed=" + NLMISC::toString(_MaxRunSpeed);
return info;
}
void CBotProfileFollowPos::setMaxSpeeds(float walkSpeed, float runSpeed)
{
_MaxWalkSpeed = walkSpeed;
_MaxRunSpeed = runSpeed;
}
void CBotProfileFollowPos::setStop(bool stop)
{
_Stop = stop;
}
//////////////////////////////////////////////////////////////////////////////
// CBotProfileWanderBase //
//////////////////////////////////////////////////////////////////////////////
CBotProfileWanderBase::CBotProfileWanderBase()
: CAIBaseProfile()
{
PROFILE_LOG("bot", "wander_base", "ctor", "");
}
void CBotProfileWanderBase::setTimer(uint32 ticks)
{
_Timer.set(ticks);
}
bool CBotProfileWanderBase::testTimer() const
{
return _Timer.test();
}
//////////////////////////////////////////////////////////////////////////////
// CBotProfileStandAtPos //
//////////////////////////////////////////////////////////////////////////////
CBotProfileStandAtPos::CBotProfileStandAtPos(CProfileOwner* owner)
: CBotProfileWanderBase()
, _Bot(NLMISC::safe_cast(owner))
{
PROFILE_LOG("bot", "stand_at_pos", "ctor", "");
#ifdef NL_DEBUG_PTR
_Bot.setData(this);
#endif
}
void CBotProfileStandAtPos::beginProfile()
{
PROFILE_LOG("bot", "stand_at_pos", "begin", "");
}
void CBotProfileStandAtPos::endProfile()
{
PROFILE_LOG("bot", "stand_at_pos", "end", "");
}
void CBotProfileStandAtPos::updateProfile(uint ticksSinceLastUpdate)
{
}
std::string CBotProfileStandAtPos::getOneLineInfoString() const
{
return "stand_at_pos bot profile";
}
//////////////////////////////////////////////////////////////////////////////
// CBotProfileForage //
//////////////////////////////////////////////////////////////////////////////
CBotProfileForage::CBotProfileForage(CProfileOwner* owner)
: CBotProfileWanderBase()
, _Bot(NLMISC::safe_cast(owner))
, _TemporarySheetUsed(false)
{
PROFILE_LOG("bot", "forage", "ctor", "");
#ifdef NL_DEBUG_PTR
_Bot.setData(this);
#endif
_OldSheet = 0;
}
CBotProfileForage::~CBotProfileForage()
{
PROFILE_LOG("bot", "forage", "dtor", "");
#ifdef NL_DEBUG
nlassert(_TemporarySheetUsed==false);
#endif
if (_TemporarySheetUsed)
{
setOldSheet(); // if this leads to a virtual pure call somewhere,
// try to ask profileOwner to flush its profile in Bot dtor,
// which is a proper way to delete objects.
}
}
void CBotProfileForage::beginProfile()
{
PROFILE_LOG("bot", "forage", "begin", "");
// begin first forage in 3-10 second
_ForageTimer.set(30+CAIS::rand32(70));
static const NLMISC::CSheetId forageTool("itforage.sitem");
/*
AISHEETS::ICreature *cs = new AISHEETS::CCreature(*_Bot->getPersistent().getSheet());
_OldSheet = _Bot->getPersistent().getSheet();
cs->LeftItem = NLMISC::CSheetId();
cs->RightItem = forageTool;
_Bot->getPersistent().setSheet(cs);
_Bot->getPersistent().sendVPA();
_TemporarySheetUsed = true;
*/
nlerror("This profile has been broken (above block commented), it shouldn't be used");
// begin propecting
_Bot->setBehaviour(MBEHAV::PROSPECTING);
}
void CBotProfileForage::endProfile()
{
PROFILE_LOG("bot", "forage", "end", "");
// stop foraging if needed
if (_Bot->getBehaviour() == MBEHAV::EXTRACTING)
_Bot->setBehaviour(MBEHAV::EXTRACTING_END);
else if (_Bot->getBehaviour() == MBEHAV::PROSPECTING)
_Bot->setBehaviour(MBEHAV::PROSPECTING_END);
/*
setOldSheet();
*/
nlerror("This profile has been broken (above block commented), it shouldn't be used");
}
void CBotProfileForage::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(BotForageProfileUpdate);
CFollowPathContext fpcBotForageProfileUpdate("BotForageProfileUpdate");
if (_ForageTimer.test())
{
// somethink to do :)
if (_Bot->getBehaviour() != MBEHAV::EXTRACTING)
{
// do a little turn
CAngle a = _Bot->theta();
CAngle newAngle(CAIS::randPlusMinus(CAngle::PI/4));
_Bot->setTheta(a+newAngle);
// begin foraging
_Bot->setBehaviour(MBEHAV::EXTRACTING);
// forage for 10 to 15 sec
_ForageTimer.set(100+CAIS::rand32(50));
}
else if (_Bot->getBehaviour() == MBEHAV::EXTRACTING)
{
// end foraging
_Bot->setBehaviour(MBEHAV::PROSPECTING);
// wait 20-30s before next forage
_ForageTimer.set(250+CAIS::rand32(50));
}
}
}
void CBotProfileForage::setOldSheet()
{
AISHEETS::ICreatureCPtr cs = _Bot->getPersistent().getSheet();
_Bot->getPersistent().setSheet(_OldSheet);
_Bot->getPersistent().sendVPA();
_TemporarySheetUsed = false;
// delete const_cast(cs);
nlerror("This profile has been broken (above line commented), it shouldn't be used");
}
std::string CBotProfileForage::getOneLineInfoString() const
{
return "forage bot profile";
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileNormal //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileNormal::CGrpProfileNormal(CProfileOwner* owner)
: _Grp(NLMISC::safe_cast(owner))
//, _GroupFighting(false) // :KLUDGE: Replaced by a bug simulation
{
PROFILE_LOG("group", "normal", "ctor", "");
// This should be part of init list, but can't do conditionnal init
if (simulateBug(7))
{
/* Following statement was missing in initialization list. */
}
else
{
_GroupFighting = false;
}
}
bool CGrpProfileNormal::isGroupFighting() const
{
return _GroupFighting;
}
void CGrpProfileNormal::setGroupFighting(bool groupFighting)
{
if (_GroupFighting!=groupFighting)
{
_GroupFighting=groupFighting;
CGroupNpc &grp=_Grp->getPersistent();
if (groupFighting)
grp.processStateEvent(grp.getEventContainer().EventGroupBeginFight);
else
grp.processStateEvent(grp.getEventContainer().EventGroupEndFight);
}
}
//////////////////////////////////////////////////////////////////////////////
// CSlaveProfile //
//////////////////////////////////////////////////////////////////////////////
CSlaveProfile::CSlaveProfile(CProfileOwner* owner)
: _Grp(NLMISC::safe_cast(owner))
{
PROFILE_LOG("base", "slave", "ctor", "");
}
void CSlaveProfile::beginProfile()
{
PROFILE_LOG("base", "slave", "begin", "");
FOREACH(itBot, CCont, _Grp->bots())
{
CBot* bot = *itBot;
CSpawnBot* spawnBot = bot->getSpawnObj();
if (!spawnBot || !spawnBot->isAlive())
continue;
addBot(bot);
}
}
void CSlaveProfile::updateProfile(uint ticksSinceLastUpdate)
{
}
//////////////////////////////////////////////////////////////////////////////
// CMoveProfile //
//////////////////////////////////////////////////////////////////////////////
CMoveProfile::CMoveProfile(CProfileOwner* owner)
: CSlaveProfile(owner)
, _MaxRunSpeed(FLT_MAX)
, _MaxWalkSpeed(FLT_MAX)
{
PROFILE_LOG("base", "move", "ctor", "");
}
void CMoveProfile::resumeBot(CBot const* bot)
{
CSpawnBot* spawnBot = bot->getSpawnObj();
if (!spawnBot)
return;
CPathCont* pathCont = getPathCont(bot);
if (!pathCont)
return;
spawnBot->setAIProfile(new CBotProfileFollowPos(pathCont, spawnBot));
}
//////////////////////////////////////////////////////////////////////////////
// CFightProfile //
//////////////////////////////////////////////////////////////////////////////
CFightProfile::CFightProfile(CProfileOwner* owner)
: CSlaveProfile(owner)
{
PROFILE_LOG("base", "fight", "ctor", "");
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileFollowRoute //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileFollowRoute::~CGrpProfileFollowRoute()
{
PROFILE_LOG("group", "follow_route", "dtor", "");
FOREACH(itBot, CCont, _Grp->bots())
removeBot(*itBot);
}
CGrpProfileFollowRoute::CBotFollower::CBotFollower()
: _BotAtDest(false)
{
}
CGrpProfileFollowRoute::CBotFollower::~CBotFollower()
{
}
void CGrpProfileFollowRoute::CBotFollower::setBotAtDest(bool atDest)
{
_BotAtDest = atDest;
}
const bool& CGrpProfileFollowRoute::CBotFollower::isBotAtDest() const
{
return _BotAtDest;
}
bool CGrpProfileFollowRoute::getDirection()
{
return _FollowForward;
}
CPathCont* CGrpProfileFollowRoute::getPathCont(CBot const* bot)
{
return &_PathCont;
}
std::string CGrpProfileFollowRoute::getOneLineInfoString() const
{
std::string info = "follow_route group profile";
info += " stop_npc=" + NLMISC::toString(_StopNpc);
return info;
}
bool CGrpProfileFollowRoute::profileTerminated() const
{
return _ProfileTerminated;
}
void CGrpProfileFollowRoute::stopNpc(bool stop)
{
_StopNpc = stop;
}
//////////////////////////////////////////////////////////////////////////////
// CGrpProfileStandOnVertices //
//////////////////////////////////////////////////////////////////////////////
CGrpProfileStandOnVertices::CBotPositionner::CBotPositionner(RYAI_MAP_CRUNCH::TAStarFlag flags)
: _PathCont(flags)
{
}
CGrpProfileStandOnVertices::CBotPositionner::CBotPositionner(uint32 geomIndex, RYAI_MAP_CRUNCH::TAStarFlag flags)
: _PathCont(flags)
, _GeomIndex(geomIndex)
, _BotAtDest(false)
{
}
void CGrpProfileStandOnVertices::CBotPositionner::setBotAtDest(bool atDest)
{
_BotAtDest=atDest;
}
bool CGrpProfileStandOnVertices::CBotPositionner::isBotAtDest() const
{
return _BotAtDest;
}
CGrpProfileStandOnVertices::CGrpProfileStandOnVertices(CProfileOwner* owner)
: CMoveProfile(owner)
, _DenyFlags(NLMISC::safe_cast(owner)->getPersistent().getAStarFlag())
{
PROFILE_LOG("group", "stand_on_vertices", "ctor", "");
}
CGrpProfileStandOnVertices::~CGrpProfileStandOnVertices()
{
PROFILE_LOG("group", "stand_on_vertices", "dtor", "");
for (CCont::iterator it=_Grp->bots().begin(), itEnd=_Grp->bots().end();it!=itEnd;++it)
{
removeBot(*it);
}
}
void CGrpProfileStandOnVertices::endProfile()
{
PROFILE_LOG("group", "stand_on_vertices", "end", "");
}
std::string CGrpProfileStandOnVertices::getOneLineInfoString() const
{
std::string info = "stand_on_vertices group profile";
info += " finished=" + NLMISC::toString(_Finished);
return info;
}
//- Complex profile factories ------------------------------------------------
// CBotProfileMoveToFactory (with specialization)
typedef CAIGenericProfileFactory CBotProfileMoveToFactory;
template <>
NLMISC::CSmartPtr CAIGenericProfileFactory::createAIProfile(CProfileOwner* owner)
{
nlerror("This profile factory (CBotProfileMoveToFactory) can't be used");
return NULL;
}
// CBotProfileFollowPosFactory (with specialization)
typedef CAIGenericProfileFactory CBotProfileFollowPosFactory;
template <>
NLMISC::CSmartPtr CAIGenericProfileFactory::createAIProfile(CProfileOwner* owner)
{
nlerror("This profile factory (CBotProfileFollowPosFactory) can't be used");
return NULL;
}
#include "event_reaction_include.h"