4598 lines
132 KiB
C++
4598 lines
132 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
||
// Copyright (C) 2010 Winch Gate Property Limited
|
||
//
|
||
// This program is free software: you can redistribute it and/or modify
|
||
// it under the terms of the GNU Affero General Public License as
|
||
// published by the Free Software Foundation, either version 3 of the
|
||
// License, or (at your option) any later version.
|
||
//
|
||
// This program is distributed in the hope that it will be useful,
|
||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
// GNU Affero General Public License for more details.
|
||
//
|
||
// You should have received a copy of the GNU Affero General Public License
|
||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
#include "stdpch.h"
|
||
#include "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<int> DefaultWanderMinTimer;
|
||
extern CVariable<int> DefaultWanderMaxTimer;
|
||
extern CVariable<sint32> FameForGuardAttack;
|
||
extern CVariable<sint32> 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<CMovementMagnet> const& getMovementMagnet() const { return _MovementMagnet; }
|
||
|
||
private:
|
||
CPathCont _PathCont;
|
||
NLMISC::CSmartPtr<CBotProfileFollowPos> _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<CBot const*, CSmartPtr<CBotPositionner> > TNpcBotPositionnerMap;
|
||
|
||
TNpcBotPositionnerMap _NpcList;
|
||
bool _Finished;
|
||
};
|
||
|
||
/****************************************************************************/
|
||
/* Local profile factories */
|
||
/****************************************************************************/
|
||
|
||
//- Simple profile factories (based on generic factory) ----------------------
|
||
|
||
// CGrpProfileFightFactory
|
||
typedef CAIGenericProfileFactory<CGrpProfileFight> CGrpProfileFightFactory;
|
||
|
||
// CGrpProfileTribuFactory
|
||
typedef CAIGenericProfileFactory<CGrpProfileTribu> CGrpProfileTribuFactory;
|
||
|
||
// CGrpProfileIdleFactory
|
||
typedef CAIGenericProfileFactory<CGrpProfileIdle> CGrpProfileIdleFactory;
|
||
|
||
// CGrpProfileStandAtStartPointFactory
|
||
typedef CAIGenericProfileFactory<CGrpProfileStandAtStartPoint> 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<CSlaveProfile*>(fightProfile().getAIProfile());
|
||
if (profile)
|
||
profile->removeBot(bot);
|
||
}
|
||
if (movingProfile().getAIProfile())
|
||
{
|
||
CSlaveProfile* const profile = NLMISC::type_cast<CSlaveProfile*>(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<CSlaveProfile*>(movingProfile().getAIProfile())->addBot(bot);
|
||
}
|
||
if (fightProfile().getAIProfile())
|
||
{
|
||
NLMISC::safe_cast<CSlaveProfile*>(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<CSpawnBotNpc*>(_Bot.ptr());
|
||
CGroupNpc* grpNpc = static_cast<CGroupNpc*>(spawnable->getPersistent().getOwner());
|
||
grpNpc->processStateEvent(grpNpc->getEventContainer().EventBotBeginFight);
|
||
TempSpeaker = NULL;
|
||
}
|
||
void CBotProfileFightNpc::eventTargetKilled()
|
||
{
|
||
TempSpeaker = _Bot;
|
||
CSpawnBotNpc* spawnable= NLMISC::safe_cast<CSpawnBotNpc*>(_Bot.ptr());
|
||
CGroupNpc* grpNpc = static_cast<CGroupNpc*>(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<CSpawnBotNpc*>(owner);
|
||
// RYAI_MAP_CRUNCH::TAStarFlag denyFlags = bot->getAStarFlag();
|
||
_PathCont.setDestination(owner->getReturnPos());
|
||
_MoveProfile = NLMISC::CSmartPtr<CBotProfileFollowPos>(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("<no move profile>");
|
||
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<CBot >::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<CBot >::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<CBot*>::iterator it = find(_NpcList.begin(), _NpcList.end(), bot);
|
||
if (it==_NpcList.end())
|
||
_NpcList.push_back(bot);
|
||
}
|
||
|
||
void CGrpProfileFight::removeBot(CBot* bot)
|
||
{
|
||
vector<CBot*>::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<CSpawnBotNpc*>(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<CBot*>, _NpcList)
|
||
{
|
||
CBot* pBot = *itBot;
|
||
if (pBot)
|
||
{
|
||
CSpawnBot* bot = pBot->getSpawnObj();
|
||
if (bot)
|
||
bot->propagateAggro();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
std::string CGrpProfileFight::getOneLineInfoString() const
|
||
{
|
||
return "fight profile";
|
||
}
|
||
|
||
std::vector<CBot*>& 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<CFightProfile*>(_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<CSlaveProfile*>(_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;
|
||
if (!_Grp->getProfileParameter("aggro range", aggroRangeFloat))
|
||
_AggroRange =static_cast<uint32>( CGrpProfileBanditFactory::getDefaultBanditAggroRange() );
|
||
else
|
||
_AggroRange = static_cast<uint32>(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<CPersistentOfPhysical> 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<CFightProfile*>(_Grp->fightProfile().getAIProfile());
|
||
|
||
if (fightProfile)
|
||
{
|
||
CAIVision<CPersistentOfPhysical> localBanditVision;
|
||
|
||
for (vector<CBot*>::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<NLMISC::CDbgPtr<CPersistentOfPhysical> > &bots = localBanditVision.bots();
|
||
std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::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<CSpawnBotNpc *>(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<NLMISC::CDbgPtr<CPersistentOfPhysical> > &players = localBanditVision.players();
|
||
std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::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<CBot >::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<CMoveProfile*>(_Grp->movingProfile().getAIProfile());
|
||
moveProf->resumeBot(bot);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// bandits don't like guards nor escorted people
|
||
{
|
||
const std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> > &bots = BanditVision.bots();
|
||
std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::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<const CSpawnBot *>(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<NLMISC::CDbgPtr<CPersistentOfPhysical> > &players = BanditVision.players();
|
||
|
||
std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::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<CPersistentOfPhysical> 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<CBot> &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;
|
||
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<CBot >::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<CMoveProfile*>(_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<NLMISC::CDbgPtr<CPersistentOfPhysical> > &bots = GuardVision.bots();
|
||
std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::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<const CSpawnBotNpc*>(ep);
|
||
|
||
if (botNpc->spawnGrp().activityProfile().getAIProfileType() == ACTIVITY_BANDIT)
|
||
{
|
||
if (_CenterPos.quickDistTo(botNpc->pos())<aggroSize)
|
||
{
|
||
_Grp->setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case RYZOMID::creature:
|
||
{
|
||
const CSpawnBotFauna *const botFauna = NLMISC::safe_cast<const CSpawnBotFauna*>(ep);
|
||
|
||
if (botFauna->getPersistent().faunaType()==FaunaTypePredator)
|
||
{
|
||
if (_CenterPos.quickDistTo(botFauna->pos())<aggroSize)
|
||
{
|
||
_Grp->setAggroMinimumFor(ep->dataSetRow(), 0.5f, false);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// guards don't like bots that attack players.
|
||
{
|
||
H_AUTO(GrpGuardProfileAttack);
|
||
const std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> > &players = GuardVision.players();
|
||
LOG("%u players in group vision with aggrosize %d", players.size(), aggroSize);
|
||
|
||
const CAIVector ¢erPos=_Grp->getCenterPos();
|
||
|
||
std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::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 && fame<fameForGuardAttack) || OUTPOSTHELPERS::isAttackingFaction(factionIndex, ep)))
|
||
{
|
||
// the guard attack the player !
|
||
_Grp->setAggroMinimumFor(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<const CSpawnBotNpc*>(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<CPersistentOfPhysical> 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<CBotNpc*>(*_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<CBot >::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<CMoveProfile*>(_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<NLMISC::CDbgPtr<CPersistentOfPhysical> > &players = TribuVision.players();
|
||
|
||
LOG("%u players in group vision", players.size());
|
||
|
||
std::vector<NLMISC::CDbgPtr<CPersistentOfPhysical> >::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<CSpawnGroup*>(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<CBot>, _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<CBotNpc*>(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<CBotNpc*>(_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<CSpawnGroupNpc*>(_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<CBotProfileFollowPos*>(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<CBotProfileFollowPos*>(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 rang<6E>es.
|
||
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<CBot >::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<CSpawnGroup*>(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<CGroupNpc*>(&_Grp->getPersistent());
|
||
if (grp.getState())
|
||
{
|
||
const CAIState *const state = grp.getCAIState();
|
||
if (state->isPositional())
|
||
{
|
||
const CAIStatePositional *const statePositionnal=static_cast<const CAIStatePositional*>(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<CShape::TPosition> &geometry,const TVerticalPos &verticalPos, bool dontSendEvent)
|
||
: CMoveProfile(owner)
|
||
, _Geometry(&geometry)
|
||
, _GeometryComeFromState(false)
|
||
, _VerticalPos(verticalPos)
|
||
, _PathCont(NLMISC::safe_cast<CSpawnGroup*>(owner)->getPersistent().getAStarFlag())
|
||
, _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<CSpawnGroup*>(_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<CSpawnGroupNpc*>(static_cast<CSpawnGroup *>(_Grp));
|
||
CGroupNpc &pgrp = grp->getPersistent();
|
||
const CAIState *const state = pgrp.getActiveState();
|
||
|
||
if ( !state
|
||
|| !state->isPositional())
|
||
return;
|
||
|
||
const CAIStatePositional *const sp = static_cast<const CAIStatePositional *const>(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<CSpawnGroupNpc*>(_Grp.ptr())->getPersistent().getGeometryVerticalPos(), geometry);
|
||
}
|
||
|
||
void CGrpProfileFollowRoute::stateChangeProfile()
|
||
{
|
||
assignGeometryFromState();
|
||
|
||
// set a stand at pos profile on every bots
|
||
FOREACH(it, CAliasCont<CBot>, _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<CAIStatePositional *>(NLMISC::safe_cast<CSpawnGroupNpc*>(_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<CBotNpc*>(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<CBotNpc*>(_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<CSpawnGroupNpc*>(_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<CBotProfileFollowPos*>(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<CBotProfileFollowPos*>(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 rang<6E>es.
|
||
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<CSpawnGroupNpc*>(_Grp.ptr());
|
||
|
||
CAIStatePositional *grpState=NLMISC::safe_cast<CAIStatePositional *>(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<CSpawnGroupNpc*>(_Grp.ptr());
|
||
CAIStatePositional *grpState=NLMISC::safe_cast<CAIStatePositional *>(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<CBotNpc*>(bot)->grp();
|
||
#ifdef NL_DEBUG
|
||
nlassert(grp.getSpawnObj());
|
||
#endif
|
||
|
||
CAIStatePositional *grpState=NLMISC::safe_cast<CAIStatePositional *>(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<CBotNpc*>(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<CSpawnGroupNpc*>(_Grp.ptr())->getPersistent();
|
||
CAIStatePositional *grpState=static_cast<CAIStatePositional*>(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<CBotProfileFollowPos*>(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;
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
// CGrpProfileIdle //
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
CGrpProfileIdle::CGrpProfileIdle(CProfileOwner* owner)
|
||
: CMoveProfile(owner)
|
||
{
|
||
PROFILE_LOG("group", "idle", "ctor", "");
|
||
}
|
||
|
||
CGrpProfileIdle::~CGrpProfileIdle()
|
||
{
|
||
PROFILE_LOG("group", "idle", "dtor", "");
|
||
FOREACH(it, CCont<CBot>, _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<CBot*,CBotPositionner> 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<CBotNpc*>(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<CBot*, CBotPositionner>::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)
|
||
, _NpcZone(npcZone)
|
||
, _Social(false)
|
||
{
|
||
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<CSpawnGroupNpc*>(static_cast<CSpawnGroup *>(_Grp));
|
||
CGroupNpc& pgrp = grp->getPersistent();
|
||
CAIState const* const state = pgrp.getActiveState();
|
||
|
||
if ( !state
|
||
|| !state->isPositional())
|
||
return;
|
||
|
||
CAIStatePositional const* const sp = static_cast<CAIStatePositional const* const>(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<CSpawnGroupNpc*>(static_cast<CSpawnGroup *>(_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<CSpawnGroupNpc*>(static_cast<CSpawnGroup*>(_Grp));
|
||
CGroupNpc &pgrp = grp->getPersistent();
|
||
bool aNpcHasReachDestination = false;
|
||
for (uint i=0; i<pgrp.bots().size(); ++i)
|
||
{
|
||
CBotNpc* bot = static_cast<CBotNpc*>(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<CBotProfileMoveTo*>(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<CPersistentOfPhysical> 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<CBotProfileWanderBase*>(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<CBotProfileStandAtPos *>(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<CNpcZonePlaceNoPrim> const& npcZone)
|
||
: CMoveProfile(owner)
|
||
, _NpcZone(npcZone)
|
||
, _Social(false)
|
||
{
|
||
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<CSpawnGroupNpc*>(static_cast<CSpawnGroup *>(_Grp));
|
||
CGroupNpc& pgrp = grp->getPersistent();
|
||
CAIState const* const state = pgrp.getActiveState();
|
||
|
||
if ( !state
|
||
|| !state->isPositional())
|
||
return;
|
||
|
||
CAIStatePositional const* const sp = static_cast<CAIStatePositional const* const>(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<CSpawnGroupNpc*>(static_cast<CSpawnGroup *>(_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<CSpawnGroupNpc*>(static_cast<CSpawnGroup*>(_Grp));
|
||
CGroupNpc &pgrp = grp->getPersistent();
|
||
|
||
for (uint i=0; i<pgrp.bots().size(); ++i)
|
||
{
|
||
CBotNpc* bot = static_cast<CBotNpc*>(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<CBotProfileMoveTo*>(profile);
|
||
if (mt->destinationReach())
|
||
{
|
||
// look arround for interesting target
|
||
CAIVision<CPersistentOfPhysical> 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<CBotProfileWanderBase*>(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<CBotProfileStandAtPos *>(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<CBot >::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)
|
||
: _BotAtDest(false)
|
||
, _Position(position)
|
||
, _VerticalPos(verticalPos)
|
||
, _PathCont(flag)
|
||
{
|
||
_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<CBotNpc*>(bot);
|
||
|
||
CGroupNpc& grp = botNpc->grp();
|
||
#ifdef NL_DEBUG
|
||
nlassert(grp.getSpawnObj());
|
||
#endif
|
||
CAIStatePositional *grpState=NLMISC::safe_cast<CAIStatePositional *>(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<CBotNpc*>(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<CSpawnGroupNpc*>(_Grp.ptr())->getPersistent();
|
||
CAIStatePositional *grpState=static_cast<CAIStatePositional*>(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<CBotProfileFollowPos*>(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<CGrpProfileFollowRoute*>(_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<CPersistentOfPhysical> 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<NLMISC::CDbgPtr<CPersistentOfPhysical> >::const_iterator first(vision.players().begin()), last(vision.players().end());
|
||
for (;first != last && (escortAway || !escortBack); ++first)
|
||
{
|
||
CBotPlayer *player = safe_cast<CBotPlayer*>((*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<CFightProfile*>(_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<CMoveProfile*>(_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<CGrpProfileFollowRoute*>(_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("<no guard profile>");
|
||
info += ") (";
|
||
info += _EscortedProfile?_EscortedProfile->getOneLineInfoString():std::string("<no escorted profile>");
|
||
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<CAIPlace const> 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<TYPE_IN_OUTPOST_ZONE_ALIAS> 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<std::string, NLMISC::TStringId> factionTranslator;
|
||
std::vector<NLMISC::TStringId> const& factionNames = CStaticFames::getInstance().getFactionNames();
|
||
std::vector<NLMISC::TStringId>::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<size; ++i)
|
||
{
|
||
nldebug("%s -> %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<TAllianceId> 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<TStringId> factionsSet = factions.properties();
|
||
std::set<TStringId>::const_iterator it, end = factionsSet.end();
|
||
for (it=factionsSet.begin(); it!=end; ++it)
|
||
{
|
||
std::string fameFaction = scriptFactionToFameFaction(CStringMapper::unmap(*it));
|
||
// sint32 fame = CFameInterface::getInstance().getFameOrCivilisationFame(entity->getEntityId(), CStringMapper::map(fameFaction));
|
||
sint32 const fame = entity->getFame(fameFaction);
|
||
if (fame!=NO_FAME && fame>0)
|
||
{
|
||
// nldebug("Entity has faction %s", CStringMapper::unmap(*it).c_str());
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
case RYZOMID::npc:
|
||
{
|
||
CSpawnBotNpc const* const sbnEntity = NLMISC::safe_cast<CSpawnBotNpc const*>(entity);
|
||
CGroupNpc *const gnEntityGroup = NLMISC::safe_cast<CGroupNpc*>(&sbnEntity->getPersistent().getGroup());
|
||
return gnEntityGroup->faction().containsPartOfStrict(factions);
|
||
}
|
||
case RYZOMID::creature:
|
||
{
|
||
CSpawnBotFauna const* const sbnEntity = NLMISC::safe_cast<CSpawnBotFauna const*>(entity);
|
||
CGrpFauna* const gnEntityGroup = NLMISC::safe_cast<CGrpFauna*>(&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<name.length(); ++i)
|
||
{
|
||
if (i==6 && name[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
|
||
ret += name[i];
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
std::string CGrpProfileFaction::fameFactionToScriptFaction(std::string name)
|
||
{
|
||
std::string ret = "Famous";
|
||
for (size_t i=0; i<name.length(); ++i)
|
||
{
|
||
if (i==0 && name[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<TAllianceId> const& thisFaction = thisGrpNpc.faction();
|
||
CPropertySetWithExtraList<TAllianceId> const& thisFriendFactions = thisGrpNpc.friendFaction();
|
||
CPropertySetWithExtraList<TAllianceId> const& thisEnnemyFactions = thisGrpNpc.ennemyFaction();
|
||
|
||
// We don't assist or attack players if our friends/ennemies are not in factions
|
||
bool const assistPlayers = thisFriendFactions.containsPartOfStrict(_FameFactions);
|
||
bool const assistBots = !thisFriendFactions.empty() && !bNoAssist;
|
||
bool const attackPlayers = (!thisEnnemyFactions.extraSetEmpty()) || thisEnnemyFactions.containsPartOfStrict(_FameFactions) || thisEnnemyFactions.containsPartOfStrictFilter("outpost:*");
|
||
bool const attackBots = !thisEnnemyFactions.empty();
|
||
|
||
CAIVision<CPersistentOfPhysical> 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<NLMISC::CDbgPtr<CPersistentOfPhysical> >, 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<NLMISC::CDbgPtr<CPersistentOfPhysical> >, 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<const CSpawnBot *>(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<IAIProfile> 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<IAIProfileFactory, std::string>::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)
|
||
, _Bot(NLMISC::safe_cast<CSpawnBotNpc*>(owner))
|
||
, _PathPos(NLMISC::safe_cast<CSpawnBotNpc*>(owner)->theta())
|
||
, _PathCont(NLMISC::safe_cast<CSpawnBotNpc*>(owner)->getAStarFlag())
|
||
{
|
||
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()
|
||
, _PathCont(const_cast<CBotProfileFollowPos&>(other)._PathCont)
|
||
, _Bot(const_cast<CBotProfileFollowPos&>(other)._Bot)
|
||
, _MaxWalkSpeed(FLT_MAX)
|
||
, _MaxRunSpeed(FLT_MAX)
|
||
, _PathPos(const_cast<CBotProfileFollowPos&>(other)._PathPos._Angle) // Just to debug...
|
||
, _Stop(false)
|
||
{
|
||
PROFILE_LOG("bot", "follow_pos", "ctor", "");
|
||
#ifdef NL_DEBUG_PTR
|
||
_Bot.setData(this);
|
||
#endif
|
||
}
|
||
|
||
CBotProfileFollowPos::CBotProfileFollowPos(CPathCont* pathCont, CProfileOwner* owner)
|
||
: CAIBaseProfile()
|
||
, _PathCont(pathCont)
|
||
, _Bot(NLMISC::safe_cast<CSpawnBotNpc*>(owner))
|
||
, _MaxWalkSpeed(FLT_MAX)
|
||
, _MaxRunSpeed(FLT_MAX)
|
||
, _PathPos(NLMISC::safe_cast<CSpawnBotNpc*>(owner)->theta())
|
||
, _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<CSpawnBotNpc*>(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<CSpawnBotNpc*>(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<AISHEETS::ICreature*>(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<CSpawnGroupNpc*>(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<CSpawnGroupNpc*>(owner))
|
||
{
|
||
PROFILE_LOG("base", "slave", "ctor", "");
|
||
}
|
||
|
||
void CSlaveProfile::beginProfile()
|
||
{
|
||
PROFILE_LOG("base", "slave", "begin", "");
|
||
FOREACH(itBot, CCont<CBot>, _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<CBot>, _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)
|
||
: _BotAtDest(false)
|
||
, _GeomIndex(geomIndex)
|
||
, _PathCont(flags)
|
||
{
|
||
}
|
||
|
||
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<CSpawnGroup*>(owner)->getPersistent().getAStarFlag())
|
||
{
|
||
PROFILE_LOG("group", "stand_on_vertices", "ctor", "");
|
||
}
|
||
|
||
CGrpProfileStandOnVertices::~CGrpProfileStandOnVertices()
|
||
{
|
||
PROFILE_LOG("group", "stand_on_vertices", "dtor", "");
|
||
for (CCont<CBot >::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<CBotProfileMoveTo> CBotProfileMoveToFactory;
|
||
template <>
|
||
NLMISC::CSmartPtr<IAIProfile> CAIGenericProfileFactory<CBotProfileMoveTo>::createAIProfile(CProfileOwner* owner)
|
||
{
|
||
nlerror("This profile factory (CBotProfileMoveToFactory) can't be used");
|
||
return NULL;
|
||
}
|
||
|
||
// CBotProfileFollowPosFactory (with specialization)
|
||
typedef CAIGenericProfileFactory<CBotProfileFollowPos> CBotProfileFollowPosFactory;
|
||
template <>
|
||
NLMISC::CSmartPtr<IAIProfile> CAIGenericProfileFactory<CBotProfileFollowPos>::createAIProfile(CProfileOwner* owner)
|
||
{
|
||
nlerror("This profile factory (CBotProfileFollowPosFactory) can't be used");
|
||
return NULL;
|
||
}
|
||
|
||
#include "event_reaction_include.h"
|