Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / group_profile.cpp
blob7ee57afa7b0e1c5f35bf99ed8c434f5fd595fc4d
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdpch.h"
21 #include "group_profile.h"
22 #include "ai.h"
23 #include "family_behavior.h"
25 using namespace std;
26 using namespace NLMISC;
27 using namespace AITYPES;
29 int CGrpProfileDynFollowPath::_InstanceCounter = 0;
31 /// Overload for IDynFollowPath interface
32 void CGrpProfileDynFollowPath::setPath(CNpcZone const* const start, CNpcZone const* const end, CPropertySet const& zoneFilter)
34 _StartZone = start;
35 _EndZone = end;
36 _ZoneFilter = zoneFilter;
37 _PathCursor = 0;
38 _Path.clear();
39 _hasChanged=true;
40 calcPath();
43 void CGrpProfileDynFollowPath::calcPath()
45 if ( _StartZone.isNull()
46 || _EndZone.isNull()
47 || !_hasChanged)
48 return;
50 _hasChanged=false;
51 if (!::pathFind(_StartZone, _EndZone, _ZoneFilter, _Path, false))
53 ::pathFind(_StartZone, _EndZone, CPropertySet(), _Path); // search without flags.
56 _PathCursor = 0;
58 if (_Path.empty())
60 // nlwarning("calcPath failed: for group '%s', can't find path between zones:", _Grp->getPersistent().getAliasFullName().c_str());
61 // nlwarning(" - from %s", _StartZone->getAliasTreeOwner().getAliasFullName().c_str());
62 // nlwarning(" - to %s", _EndZone->getAliasTreeOwner().getAliasFullName().c_str());
63 RYAI_MAP_CRUNCH::CWorldPosition startPos;
64 CWorldContainer::getWorldMap().setWorldPosition(vp_auto, startPos, _StartZone->midPos());
65 RYAI_MAP_CRUNCH::CWorldPosition endPos;
66 CWorldContainer::getWorldMap().setWorldPosition(vp_auto, endPos, _EndZone->midPos());
67 _FollowRoute.setAIProfile(new CGrpProfileGoToPoint(_Grp, startPos, endPos, true));
68 return;
70 nlassert(_PathCursor < _Path.size());
71 _CurrentRoad=_Path[_PathCursor];
72 _CurrentZone=_StartZone;
74 _FollowRoute.setAIProfile(new CGrpProfileFollowRoute(_Grp, _CurrentRoad->coords(), _CurrentRoad->verticalPos(), true));
75 CGrpProfileFollowRoute*const fr = static_cast<CGrpProfileFollowRoute*>(_FollowRoute.getAIProfile());
77 nlassert(_CurrentZone==_CurrentRoad->startZone() || _CurrentZone==_CurrentRoad->endZone());
78 fr->setDirection(_CurrentRoad->startZone()==_CurrentZone);
81 void CGrpProfileDynFollowPath::beginProfile()
83 calcPath();
84 CMoveProfile::beginProfile();
87 void CGrpProfileDynFollowPath::endProfile()
89 _Path.clear();
90 _StartZone = _EndZone = NULL;
91 if( _FollowRoute.getAIProfile() != NULL)
92 _FollowRoute.getAIProfile()->endProfile();
96 bool CGrpProfileDynFollowPath::destinationReach() const
98 return _CurrentZone==_EndZone;
101 // routine called every time the bot is updated (frequency depends on player proximity, etc)
102 void CGrpProfileDynFollowPath::updateProfile(uint ticksSinceLastUpdate)
104 H_AUTO(GrpDynFollowProfileUpdate)
105 CFollowPathContext fpcGrpDynFollowProfileUpdate("GrpDynFollowProfileUpdate");
107 if (destinationReach())
108 return;
110 if (_FollowRoute.getAIProfile() == NULL)
111 return;
113 CGroupNpc &persGrp=*safe_cast<CGroupNpc*>(&_Grp->getPersistent());
115 if (_FollowRoute.getAIProfileType() == AITYPES::MOVE_FOLLOW_ROUTE)
117 if (_CurrentZone.isNULL())
118 return;
120 // update the follow route until all bots reach the end
121 _FollowRoute.updateProfile(ticksSinceLastUpdate);
122 CGrpProfileFollowRoute *fr = safe_cast<CGrpProfileFollowRoute*>(_FollowRoute.getAIProfile());
124 if (fr->profileTerminated())
126 // next segment.
127 _CurrentZone=fr->getDirection()?_CurrentRoad->endZone():_CurrentRoad->startZone();
128 _PathCursor++;
129 if (_PathCursor < _Path.size())
131 _CurrentRoad=_Path[_PathCursor];
134 fr = new CGrpProfileFollowRoute(_Grp, _CurrentRoad->coords(), _CurrentRoad->verticalPos(), true);
136 _FollowRoute.setAIProfile(fr);
138 // pay attention to CGrpProfileFollowRouteSpawn init in static case .. :\ (to adapt ?)
139 #ifdef NL_DEBUG
140 nlassert(_CurrentZone==_CurrentRoad->startZone() || _CurrentZone==_CurrentRoad->endZone());
141 #endif
142 fr->setDirection(_CurrentRoad->startZone()==_CurrentZone); // CurrentZone);
144 else
146 _CurrentZone=_EndZone;
147 // Message Event for StateMachine.
148 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
149 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
155 else if (_FollowRoute.getAIProfileType() == AITYPES::MOVE_GOTO_POINT)
157 // update the follow route until all bots reach the end
158 _FollowRoute.updateProfile(ticksSinceLastUpdate);
159 CGrpProfileGoToPoint* fr = safe_cast<CGrpProfileGoToPoint*>(_FollowRoute.getAIProfile());
161 if (fr->profileTerminated())
163 _CurrentZone=_EndZone;
164 // Message Event for StateMachine.
165 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedFirst);
166 persGrp.processStateEvent(persGrp.mgr().EventDestinationReachedAll);
169 else
171 #ifdef NL_DEBUG
172 nlassert(false);
173 #endif
179 // routine used to build a debug string for detailed information on a bot's status (with respect to their aiProfile)
180 std::string CGrpProfileDynFollowPath::getOneLineInfoString() const
182 return NLMISC::toString("");
183 // // CAIStatePositional *grpState=(CAIStatePositional *)bot->spawnGrp().getCAIState();
184 // return NLMISC::toString("stand_on_start_point(%s)%s",
185 // bot->getPersistent().getStartPos().toString().c_str(),
186 // (bot->getPersistent().getStartPos() != bot->posxy())? " EN ROUTE": " ARRIVED"
187 // );
190 void CGrpProfileDynCamping::beginProfile()
192 // choose a random point in the camp to group around
193 CGroup &persGrp=_Grp->getPersistent();
194 if (!_CurrentZone.isNull())
196 RYAI_MAP_CRUNCH::CWorldPosition wp;
197 _CurrentZone->getPlaceRandomPos().getRandomPos(wp);
199 _CampPos = wp;
200 // try to place all the npc around the camp pos.
201 const uint32 count = persGrp.bots().size();
202 const float requiredSpace = 2.f;
203 const float radius = float(count*(requiredSpace/(2.f*NLMISC::Pi)));
205 for (uint i=0; i<count; ++i)
207 const CBot *const bot = persGrp.bots()[i];
208 if (!bot)
209 continue;
211 CSpawnBot *const spawnBot=bot->getSpawnObj();
212 if (!spawnBot)
213 continue;
215 CAIVector pos(_CampPos.toAIVector());
216 pos.setX(pos.x() + radius * cos(2.0f*NLMISC::Pi*(float(i)/count)));
217 pos.setY(pos.y() + radius * sin(2.0f*NLMISC::Pi*(float(i)/count)));
219 RYAI_MAP_CRUNCH::CWorldPosition newPos;
220 TVerticalPos vertPos=_CurrentZone->getPlaceRandomPos().getVerticalPos(); // persGrp.getCurrentNpcZone()->getVerticalPos();
222 if (!CWorldContainer::calcNearestWPosFromPosAnRadius(vertPos, newPos, pos, 6, 100, CWorldContainer::CPosValidatorDefault()))
223 newPos=_CampPos;
225 spawnBot->setAIProfile(new CBotProfileMoveTo(vertPos, newPos, spawnBot));
229 // set the timer for end of camping to 5 à 10 mn
230 _EndOfCamping.set(5*60*10 + CAIS::rand32(5*60*10));
233 void CGrpProfileDynCamping::endProfile()
235 const CCont<CBot > &bots=_Grp->getPersistent().bots();
237 for (uint i=0; i<bots.size(); ++i)
239 const CBot *const bot = /*dynamic_cast<CBotNpc*>(*/bots[i]/*)*/;
240 if (!bot)
241 continue;
243 CSpawnBot *const spawnBot = bot->getSpawnObj();
244 if (!spawnBot)
245 continue;
247 if (spawnBot->getAIProfileType() == BOT_MOVE_TO)
249 // set an idle activity
250 spawnBot->setAIProfile(new CBotProfileStandAtPos(spawnBot));
252 else
254 // bot already at destination, stand up the bot
255 spawnBot->setMode(MBEHAV::NORMAL);
262 void CGrpProfileDynCamping::updateProfile(uint ticksSinceLastUpdate)
264 H_AUTO(GrpCampingProfileUpdate)
265 CFollowPathContext fpcGrpCampingProfileUpdate("GrpCampingProfileUpdate");
267 const CCont<CBot > &bots=_Grp->getPersistent().bots();
269 // turn the bot to the group center then sit them
270 for (uint i=0; i<bots.size(); ++i)
272 const CBot *const bot = bots[i];
273 if (!bot)
274 continue;
276 CSpawnBot *const spawnBot = bot->getSpawnObj();
277 if (!spawnBot)
278 continue;
280 if (spawnBot->getAIProfileType()!=BOT_MOVE_TO)
281 continue;
283 CBotProfileMoveTo const* const bp = safe_cast<CBotProfileMoveTo*>(spawnBot->getAIProfile());
285 if (!bp->destinationReach())
286 continue;
288 // ok, turn the bot to face the group center
289 spawnBot->setTheta(CAngle(bot->getSpawnObj()->pos().angleTo(_CampPos)));
290 // set an idle activity
291 spawnBot->setAIProfile(new CBotProfileStandAtPos(spawnBot));
292 // sit down the bot
293 spawnBot->setMode(MBEHAV::SIT);
298 std::string CGrpProfileDynCamping::getOneLineInfoString() const
300 return string("dyn_camping");
304 ///////////////////////////////////////////////////////////////////////////
305 void CGrpProfileDynWaitInZone::beginProfile()
307 CGroupNpc &persGrp=_Grp->getPersistent();
309 // choose a random point in the camp to group around
310 breakable
312 if (_CurrentZone.isNull())
313 break;
314 if (_CurrentZone->getPlaceRandomPos().getRandomPosCount()==0)
316 nlwarning("No Random Pos in _CurrentZone %s", _CurrentZone->getAliasTreeOwner().getAliasFullName().c_str());
317 break;
320 RYAI_MAP_CRUNCH::CWorldPosition wp;
321 _CurrentZone->getPlaceRandomPos().getRandomPos(wp);
322 if (!wp.isValid())
323 break;
325 WaitPos = wp;
326 // try to place all the npc around the camp pos.
327 const uint32 count = persGrp.bots().size();
328 const float requiredSpace = 2.f;
329 const float radius = float(count*(requiredSpace/(2.f*NLMISC::Pi)));
331 for (uint i=0; i<count; ++i)
333 const CBot *const bot = persGrp.bots()[i];
334 if (!bot)
335 continue;
337 CSpawnBot *const spawnBot = bot->getSpawnObj();
338 if (!spawnBot)
339 continue;
341 CAIVector pos = WaitPos.toAIVector();
342 pos.setX(pos.x() + radius * cos(2.0f*NLMISC::Pi*(float(i)/count)));
343 pos.setY(pos.y() + radius * sin(2.0f*NLMISC::Pi*(float(i)/count)));
345 RYAI_MAP_CRUNCH::CWorldPosition newPos;
346 TVerticalPos vertPos=_CurrentZone->getPlaceRandomPos().getVerticalPos();
348 if (!CWorldContainer::calcNearestWPosFromPosAnRadius(vertPos, newPos, pos, 6, 100, CWorldContainer::CPosValidatorDefault()))
349 newPos = WaitPos;
351 spawnBot->setAIProfile(new CBotProfileMoveTo(vertPos, newPos, spawnBot));
355 StartOfWait = CTimeInterface::gameCycle();
358 void CGrpProfileDynWaitInZone::endProfile()
362 void CGrpProfileDynWaitInZone::updateProfile(uint ticksSinceLastUpdate)
364 H_AUTO(GrpDynWaitProfileUpdate)
365 CFollowPathContext fpcGrpDynWaitProfileUpdate("GrpDynWaitProfileUpdate");
367 const CCont<CBot > &bots=_Grp->getPersistent().bots();
369 // turn the bot to the group center
370 for (uint i=0; i<bots.size(); ++i)
372 const CBot *const bot = bots[i];
373 if (!bot)
374 continue;
376 CSpawnBot *const spawnBot = bot->getSpawnObj();
377 if (!spawnBot)
378 continue;
380 if (spawnBot->getAIProfileType() != BOT_MOVE_TO)
381 continue;
383 CBotProfileMoveTo const* const bp = safe_cast<CBotProfileMoveTo*>(spawnBot->getAIProfile());
384 if (!bp->destinationReach())
385 continue;
387 // ok, turn the bot to face the group center
388 spawnBot->setTheta(CAngle(spawnBot->pos().angleTo(WaitPos)));
389 // set an idle activity
390 spawnBot->setAIProfile(new CBotProfileStandAtPos(spawnBot));
395 std::string CGrpProfileDynWaitInZone::getOneLineInfoString() const
397 return string("dyn_wait_in_zone profile");
400 #include "event_reaction_include.h"