// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
#include "group_profile.h"
#include "ai.h"
#include "family_behavior.h"
using namespace std;
using namespace NLMISC;
using namespace AITYPES;
int CGrpProfileDynFollowPath::_InstanceCounter = 0;
/// Overload for IDynFollowPath interface
void CGrpProfileDynFollowPath::setPath(CNpcZone const* const start, CNpcZone const* const end, CPropertySet const& zoneFilter)
{
_StartZone = start;
_EndZone = end;
_ZoneFilter = zoneFilter;
_PathCursor = 0;
_Path.clear();
_hasChanged=true;
calcPath();
}
void CGrpProfileDynFollowPath::calcPath()
{
if ( _StartZone.isNull()
|| _EndZone.isNull()
|| !_hasChanged)
return;
_hasChanged=false;
if (!::pathFind(_StartZone, _EndZone, _ZoneFilter, _Path, false))
{
::pathFind(_StartZone, _EndZone, CPropertySet(), _Path); // search without flags.
}
_PathCursor = 0;
if (_Path.empty())
{
// nlwarning("calcPath failed: for group '%s', can't find path between zones:", _Grp->getPersistent().getAliasFullName().c_str());
// nlwarning(" - from %s", _StartZone->getAliasTreeOwner().getAliasFullName().c_str());
// nlwarning(" - to %s", _EndZone->getAliasTreeOwner().getAliasFullName().c_str());
RYAI_MAP_CRUNCH::CWorldPosition startPos;
CWorldContainer::getWorldMap().setWorldPosition(vp_auto, startPos, _StartZone->midPos());
RYAI_MAP_CRUNCH::CWorldPosition endPos;
CWorldContainer::getWorldMap().setWorldPosition(vp_auto, endPos, _EndZone->midPos());
_FollowRoute.setAIProfile(new CGrpProfileGoToPoint(_Grp, startPos, endPos, true));
return;
}
nlassert(_PathCursor < _Path.size());
_CurrentRoad=_Path[_PathCursor];
_CurrentZone=_StartZone;
_FollowRoute.setAIProfile(new CGrpProfileFollowRoute(_Grp, _CurrentRoad->coords(), _CurrentRoad->verticalPos(), true));
CGrpProfileFollowRoute*const fr = static_cast(_FollowRoute.getAIProfile());
nlassert(_CurrentZone==_CurrentRoad->startZone() || _CurrentZone==_CurrentRoad->endZone());
fr->setDirection(_CurrentRoad->startZone()==_CurrentZone);
}
void CGrpProfileDynFollowPath::beginProfile()
{
calcPath();
CMoveProfile::beginProfile();
}
void CGrpProfileDynFollowPath::endProfile()
{
_Path.clear();
_StartZone = _EndZone = NULL;
if( _FollowRoute.getAIProfile() != NULL)
_FollowRoute.getAIProfile()->endProfile();
}
bool CGrpProfileDynFollowPath::destinationReach() const
{
return _CurrentZone==_EndZone;
}
// routine called every time the bot is updated (frequency depends on player proximity, etc)
void CGrpProfileDynFollowPath::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpDynFollowProfileUpdate)
CFollowPathContext fpcGrpDynFollowProfileUpdate("GrpDynFollowProfileUpdate");
if (destinationReach())
return;
if (_FollowRoute.getAIProfile() == NULL)
return;
CGroupNpc &persGrp=*safe_cast(&_Grp->getPersistent());
if (_FollowRoute.getAIProfileType() == AITYPES::MOVE_FOLLOW_ROUTE)
{
if (_CurrentZone.isNULL())
return;
// update the follow route until all bots reach the end
_FollowRoute.updateProfile(ticksSinceLastUpdate);
CGrpProfileFollowRoute *fr = safe_cast(_FollowRoute.getAIProfile());
if (fr->profileTerminated())
{
// next segment.
_CurrentZone=fr->getDirection()?_CurrentRoad->endZone():_CurrentRoad->startZone();
_PathCursor++;
if (_PathCursor < _Path.size())
{
_CurrentRoad=_Path[_PathCursor];
fr = new CGrpProfileFollowRoute(_Grp, _CurrentRoad->coords(), _CurrentRoad->verticalPos(), true);
_FollowRoute.setAIProfile(fr);
// pay attention to CGrpProfileFollowRouteSpawn init in static case .. :\ (to adapt ?)
#ifdef NL_DEBUG
nlassert(_CurrentZone==_CurrentRoad->startZone() || _CurrentZone==_CurrentRoad->endZone());
#endif
fr->setDirection(_CurrentRoad->startZone()==_CurrentZone); // CurrentZone);
}
else
{
_CurrentZone=_EndZone;
// Message Event for StateMachine.
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
}
}
}
else if (_FollowRoute.getAIProfileType() == AITYPES::MOVE_GOTO_POINT)
{
// update the follow route until all bots reach the end
_FollowRoute.updateProfile(ticksSinceLastUpdate);
CGrpProfileGoToPoint* fr = safe_cast(_FollowRoute.getAIProfile());
if (fr->profileTerminated())
{
_CurrentZone=_EndZone;
// Message Event for StateMachine.
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
}
}
else
{
#ifdef NL_DEBUG
nlassert(false);
#endif
}
}
// routine used to build a debug string for detailed information on a bot's status (with respect to their aiProfile)
std::string CGrpProfileDynFollowPath::getOneLineInfoString() const
{
return NLMISC::toString("");
// // CAIStatePositional *grpState=(CAIStatePositional *)bot->spawnGrp().getCAIState();
// return NLMISC::toString("stand_on_start_point(%s)%s",
// bot->getPersistent().getStartPos().toString().c_str(),
// (bot->getPersistent().getStartPos() != bot->posxy())? " EN ROUTE": " ARRIVED"
// );
}
void CGrpProfileDynCamping::beginProfile()
{
// choose a random point in the camp to group around
CGroup &persGrp=_Grp->getPersistent();
if (!_CurrentZone.isNull())
{
RYAI_MAP_CRUNCH::CWorldPosition wp;
_CurrentZone->getPlaceRandomPos().getRandomPos(wp);
_CampPos = wp;
// try to place all the npc around the camp pos.
const uint32 count = persGrp.bots().size();
const float requiredSpace = 2.f;
const float radius = float(count*(requiredSpace/(2.f*NLMISC::Pi)));
for (uint i=0; igetSpawnObj();
if (!spawnBot)
continue;
CAIVector pos(_CampPos.toAIVector());
pos.setX(pos.x() + radius * cos(2.0f*NLMISC::Pi*(float(i)/count)));
pos.setY(pos.y() + radius * sin(2.0f*NLMISC::Pi*(float(i)/count)));
RYAI_MAP_CRUNCH::CWorldPosition newPos;
TVerticalPos vertPos=_CurrentZone->getPlaceRandomPos().getVerticalPos(); // persGrp.getCurrentNpcZone()->getVerticalPos();
if (!CWorldContainer::calcNearestWPosFromPosAnRadius(vertPos, newPos, pos, 6, 100, CWorldContainer::CPosValidatorDefault()))
newPos=_CampPos;
spawnBot->setAIProfile(new CBotProfileMoveTo(vertPos, newPos, spawnBot));
}
}
// set the timer for end of camping to 5 à 10 mn
_EndOfCamping.set(5*60*10 + CAIS::rand32(5*60*10));
}
void CGrpProfileDynCamping::endProfile()
{
const CCont &bots=_Grp->getPersistent().bots();
for (uint i=0; i(*/bots[i]/*)*/;
if (!bot)
continue;
CSpawnBot *const spawnBot = bot->getSpawnObj();
if (!spawnBot)
continue;
if (spawnBot->getAIProfileType() == BOT_MOVE_TO)
{
// set an idle activity
spawnBot->setAIProfile(new CBotProfileStandAtPos(spawnBot));
}
else
{
// bot already at destination, stand up the bot
spawnBot->setMode(MBEHAV::NORMAL);
}
}
}
void CGrpProfileDynCamping::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpCampingProfileUpdate)
CFollowPathContext fpcGrpCampingProfileUpdate("GrpCampingProfileUpdate");
const CCont &bots=_Grp->getPersistent().bots();
// turn the bot to the group center then sit them
for (uint i=0; igetSpawnObj();
if (!spawnBot)
continue;
if (spawnBot->getAIProfileType()!=BOT_MOVE_TO)
continue;
CBotProfileMoveTo const* const bp = safe_cast(spawnBot->getAIProfile());
if (!bp->destinationReach())
continue;
// ok, turn the bot to face the group center
spawnBot->setTheta(CAngle(bot->getSpawnObj()->pos().angleTo(_CampPos)));
// set an idle activity
spawnBot->setAIProfile(new CBotProfileStandAtPos(spawnBot));
// sit down the bot
spawnBot->setMode(MBEHAV::SIT);
}
}
std::string CGrpProfileDynCamping::getOneLineInfoString() const
{
return string("dyn_camping");
}
///////////////////////////////////////////////////////////////////////////
void CGrpProfileDynWaitInZone::beginProfile()
{
CGroupNpc &persGrp=_Grp->getPersistent();
// choose a random point in the camp to group around
breakable
{
if (_CurrentZone.isNull())
break;
if (_CurrentZone->getPlaceRandomPos().getRandomPosCount()==0)
{
nlwarning("No Random Pos in _CurrentZone %s", _CurrentZone->getAliasTreeOwner().getAliasFullName().c_str());
break;
}
RYAI_MAP_CRUNCH::CWorldPosition wp;
_CurrentZone->getPlaceRandomPos().getRandomPos(wp);
if (!wp.isValid())
break;
WaitPos = wp;
// try to place all the npc around the camp pos.
const uint32 count = persGrp.bots().size();
const float requiredSpace = 2.f;
const float radius = float(count*(requiredSpace/(2.f*NLMISC::Pi)));
for (uint i=0; igetSpawnObj();
if (!spawnBot)
continue;
CAIVector pos = WaitPos.toAIVector();
pos.setX(pos.x() + radius * cos(2.0f*NLMISC::Pi*(float(i)/count)));
pos.setY(pos.y() + radius * sin(2.0f*NLMISC::Pi*(float(i)/count)));
RYAI_MAP_CRUNCH::CWorldPosition newPos;
TVerticalPos vertPos=_CurrentZone->getPlaceRandomPos().getVerticalPos();
if (!CWorldContainer::calcNearestWPosFromPosAnRadius(vertPos, newPos, pos, 6, 100, CWorldContainer::CPosValidatorDefault()))
newPos = WaitPos;
spawnBot->setAIProfile(new CBotProfileMoveTo(vertPos, newPos, spawnBot));
}
}
StartOfWait = CTimeInterface::gameCycle();
}
void CGrpProfileDynWaitInZone::endProfile()
{
}
void CGrpProfileDynWaitInZone::updateProfile(uint ticksSinceLastUpdate)
{
H_AUTO(GrpDynWaitProfileUpdate)
CFollowPathContext fpcGrpDynWaitProfileUpdate("GrpDynWaitProfileUpdate");
const CCont &bots=_Grp->getPersistent().bots();
// turn the bot to the group center
for (uint i=0; igetSpawnObj();
if (!spawnBot)
continue;
if (spawnBot->getAIProfileType() != BOT_MOVE_TO)
continue;
CBotProfileMoveTo const* const bp = safe_cast(spawnBot->getAIProfile());
if (!bp->destinationReach())
continue;
// ok, turn the bot to face the group center
spawnBot->setTheta(CAngle(spawnBot->pos().angleTo(WaitPos)));
// set an idle activity
spawnBot->setAIProfile(new CBotProfileStandAtPos(spawnBot));
}
}
std::string CGrpProfileDynWaitInZone::getOneLineInfoString() const
{
return string("dyn_wait_in_zone profile");
}
#include "event_reaction_include.h"