1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
21 #include "visual_properties_interface.h"
22 #include "ai_profile_npc.h"
24 using namespace MULTI_LINE_FORMATER
;
26 //////////////////////////////////////////////////////////////////////
27 // Construction/Destruction
28 //////////////////////////////////////////////////////////////////////
30 //--------------------------------------------------------------------------
31 // METHODS for debugging stuff
32 //--------------------------------------------------------------------------
34 bool GrpHistoryRecordLog
= true;
35 CAIVector lastTriedPos
;
37 CGroup::CGroup (CManager
*owner
, RYAI_MAP_CRUNCH::TAStarFlag denyFlags
, CAIAliasDescriptionNode
*aliasTree
) :
38 CAliasChild
<CManager
>(owner
,aliasTree
),
39 CPersistent
<CSpawnGroup
>(),
40 _EscortTeamId(CTEAM::InvalidTeamId
),
43 _DenyFlags(denyFlags
),
48 owner
->getAIInstance()->addGroupInfo(this);
51 CGroup::CGroup (CManager
*owner
, RYAI_MAP_CRUNCH::TAStarFlag denyFlags
, uint32 alias
, std::string
const& name
) :
52 CAliasChild
<CManager
>(owner
, alias
, name
),
53 CPersistent
<CSpawnGroup
>(),
54 _EscortTeamId(CTEAM::InvalidTeamId
),
57 _DenyFlags(denyFlags
),
62 owner
->getAIInstance()->addGroupInfo(this);
67 getAIInstance()->removeGroupInfo(this, this);
68 getOwner()->removeFromSpawnList (this);
76 CBot
* CGroup::getLeader()
78 FOREACH(itBot
, CCont
<CBot
>, bots())
80 CSpawnBot
* const bot
= itBot
->getSpawnObj();
81 if (bot
&& bot
->isAlive())
84 // no bots alive, no leader !
88 CBot
* CGroup::getSquadLeader(bool checkAliveStatus
)
90 CCont
<CBot
>::iterator itBot
= bots().begin();
91 if (itBot
!=bots().end())
93 CSpawnBot
* const bot
= itBot
->getSpawnObj();
94 if (bot
&& (!checkAliveStatus
|| bot
->isAlive()))
97 // first bot not alive, no squad leader !
101 void CGroup::serviceEvent (const CServiceEvent
&info
)
103 CCont
<CBot
>::iterator it
=bots().begin(), itEnd
=bots().end();
106 it
->serviceEvent (info
);
112 void CGroup::despawnBots ()
114 for (CCont
<CBot
>::iterator it
=_Bots
.begin(), itEnd
=_Bots
.end(); it
!=itEnd
;++it
)
122 std::string
CGroup::getIndexString() const
124 return getOwner()->getIndexString()+NLMISC::toString(":g%u", getChildIndex());
127 std::string
CGroup::getOneLineInfoString() const
129 return std::string("Group '") + getName() + "'";
132 std::vector
<std::string
> CGroup::getMultiLineInfoString() const
134 std::vector
<std::string
> container
;
136 pushTitle(container
, "CGroup");
137 pushEntry(container
, "id=" + getIndexString());
138 container
.back() += " alias=" + getAliasString();
139 container
.back() += " name=" + getName();
140 pushEntry(container
, "fullname=" + getFullName());
141 pushEntry(container
, "autoSpawn=" + NLMISC::toString(_AutoSpawn
));
142 pushEntry(container
, "aggroRange=" + NLMISC::toString(_AggroRange
));
143 container
.back() += " updateNbTicks=" + NLMISC::toString(_UpdateNbTicks
);
146 std::vector
<std::string
> strings
= getSpawnObj()->getMultiLineInfoString();
147 FOREACHC(it
, std::vector
<std::string
>, strings
)
148 pushEntry(container
, *it
);
151 pushEntry(container
, "<not spawned>");
152 pushFooter(container
);
157 std::string
CGroup::getFullName() const
159 return std::string(getOwner()->getFullName()+":"+getName());
162 void CGroup::lastBotDespawned()
167 void CGroup::firstBotSpawned()
172 //////////////////////////////////////////////////////////////////////////////
174 //////////////////////////////////////////////////////////////////////////////
176 CSpawnGroup::~CSpawnGroup()
178 FOREACH(it
, CCont
<CBot
>,bots())
180 if ((*it
)->isSpawned())
184 getPersistent().despawnBots();
187 _MovingProfile
= CProfilePtr();
188 _FightProfile
= CProfilePtr();
189 _ActivityProfile
= CProfilePtr();
190 _PunctualHoldActivityProfile
= CProfilePtr();
191 _PunctualHoldMovingProfile
= CProfilePtr();
194 bool CSpawnGroup::calcCenterPos(CAIVector
& grp_pos
, bool allowDeadBot
)
196 if (bots().size()<=0)
202 FOREACH(it
, CCont
<CBot
>, bots())
204 CSpawnBot
const* const spawnBot
= it
->getSpawnObj();
205 if (!spawnBot
|| (!allowDeadBot
&& !spawnBot
->isAlive()))
208 x
+= spawnBot
->pos().x().asDouble();
209 y
+= spawnBot
->pos().y().asDouble();
215 grp_pos
.setX(x
/count
);
216 grp_pos
.setY(y
/count
);
220 void CSpawnGroup::spawnBotOfGroup()
222 CCont
<CBot
>::iterator it
= bots().begin();
223 CCont
<CBot
>::iterator itEnd
= bots().end();
227 if (!bot
->isSpawned())
229 bool ok
= bot
->spawn();
230 // code removed by Sadge because it didn't fix the problem it was added for
233 // // the spawn succeeded
234 // // make sure the bot isn't in the despawn list
235 // for (uint32 i= _BotsToDespawn.size(); i--;)
237 // if (_BotsToDespawn[i].getBotIndex()== bot->getChildIndex())
239 // nldebug("Removing bot from the despawn list because they just respawned: %s",bot->getFullName().c_str());
240 // _BotsToDespawn[i]= _BotsToDespawn.back();
241 // _BotsToDespawn.pop_back();
250 if (!bot
->getFullName().empty())
251 name
= bot
->getFullName();
254 if (!bot
->getOwner()->getFullName().empty())
255 name
= std::string("from group ") + bot
->getOwner()->getFullName();
257 name
= std::string("Unknown");
260 if (bot
->getSheet()->SheetId()==NLMISC::CSheetId::Unknown
)
261 nlwarning("***> Spawn failed position(%s), UNKNOWN SHEET! Bot %s ", lastTriedPos
.toString().c_str(), name
.c_str());
263 nlwarning("***> Spawn failed position(%s), sheetId(%s) Bot %s ", lastTriedPos
.toString().c_str(), bot
->getSheet()->SheetId().toString().c_str(), name
.c_str());
270 void CSpawnGroup::addBotToDespawnAndRespawnTime(CBot
* bot
, uint32 despawnTime
, uint32 respawnTime
)
272 nlassert(bot
->isSpawned());
273 nlassert(bot
->getOwner()==&getPersistent());
275 uint32
const botIndex
= bot
->getChildIndex();
277 FOREACH(it
, std::vector
<CBotToSpawn
>, _BotsToDespawn
)
279 if (it
->getBotIndex()==botIndex
)
281 *it
= CBotToSpawn(botIndex
, despawnTime
, respawnTime
);
285 _BotsToDespawn
.push_back(CBotToSpawn(botIndex
, despawnTime
, respawnTime
));
288 void CSpawnGroup::checkDespawn()
290 if (_BotsToDespawn
.empty())
293 //FOREACH_NOINC(it, std::vector<CBotToSpawn>, _BotsToDespawn)
294 for(uint32 i
= 0; i
< _BotsToDespawn
.size();)
296 CBotToSpawn
& botToDespawn
= _BotsToDespawn
[i
];
297 if (botToDespawn
.waitingDespawnTimeOver())
299 if (botToDespawn
.getBotIndex()>=getPersistent().bots().size())
301 STOP("Array overflow in despawn code!");
303 else if (getPersistent().bots()[botToDespawn
.getBotIndex()]==NULL
)
305 STOP("Trying to despawn a bot who doesn't exist!!");
309 getPersistent().bots()[botToDespawn
.getBotIndex()]->despawnBot();
310 if (getPersistent().isAutoSpawn())
311 _BotsToRespawn
.push_back(botToDespawn
);
314 // pop this entry out of the bots to despawn vector
315 // ace: we don't use iterator because when pop_back(), all iterators are invalidated
316 _BotsToDespawn
[i
] = _BotsToDespawn
.back();
317 _BotsToDespawn
.pop_back();
323 if (_NbSpawnedBot
==0 && _BotsToRespawn
.size()==0)
325 // Warn the parent manager that this group is now dead.
326 getPersistent().getOwner()->getOwner()->groupDead(&getPersistent());
327 if (getPersistent()._AutoDestroy
)
328 getPersistent().getOwner()->groups().removeChildByIndex(getPersistent().getChildIndex());
333 void CSpawnGroup::incSpawnedBot(CBot
& spawnBot
)
336 uint32 botIndex
= spawnBot
.getChildIndex();
337 for (uint32 i
=(uint32
)_BotsToRespawn
.size(); i
--; )
339 if (_BotsToRespawn
[i
].getBotIndex()==botIndex
)
341 nldebug("Removing bot from _BotsToRespawn because they just respawned: %s",spawnBot
.getFullName().c_str());
342 // nlwarning("WARNING!!! Old assert \"_BotsToRespawn[i].getBotIndex()!=botIndex\" would have failed");
343 _BotsToRespawn
[i
]=_BotsToRespawn
.back();
344 _BotsToRespawn
.pop_back();
347 for (uint32 i
=(uint32
)_BotsToDespawn
.size(); i
--; )
349 if (_BotsToDespawn
[i
].getBotIndex()==botIndex
)
351 nldebug("Removing bot from _BotsToDespawn because they just respawned: %s",spawnBot
.getFullName().c_str());
352 // nlwarning("WARNING!!! Old assert \"_BotsToDespawn[i].getBotIndex()!=botIndex\" would have failed");
353 _BotsToDespawn
[i
]=_BotsToDespawn
.back();
354 _BotsToDespawn
.pop_back();
358 if (_NbSpawnedBot
==0)
360 getPersistent().firstBotSpawned();
365 void CSpawnGroup::decSpawnedBot()
368 if (_NbSpawnedBot
==0)
370 getPersistent().lastBotDespawned();
374 void CSpawnGroup::checkRespawn()
376 // respawn if there is not too much dead .. (no more than one at each tick).
377 if (_BotsToRespawn
.size()<=0)
381 std::string url
= getUrl();
382 std::string actionName
= getActionName();
383 CCreatureSetUrlMsg msg
;
385 //FOREACH_NOINC(it, std::vector<CBotToSpawn>, _BotsToRespawn)
386 for(uint32 i
= 0; i
< _BotsToRespawn
.size();)
388 CBotToSpawn
const& botToSpawn
= _BotsToRespawn
[i
];
389 if (botToSpawn
.waitingRespawnTimeOver())
391 CBot
* botPt
= getPersistent().bots()[botToSpawn
.getBotIndex()];
393 CBotToSpawn
const botToSpawn
= _BotsToRespawn
.back();
396 _BotsToRespawn
[i
] = _BotsToRespawn
.back();
397 _BotsToRespawn
.pop_back();
399 if (botPt
->isSpawned())
401 nlwarning("CSpawnGroup::checkRespawn : trying to respawn a spawned bot");
403 if (botPt
->isSpawned() || botPt
->reSpawn(false))
405 CSpawnBot
* pbot
= botPt
->getSpawnObj();
408 msg
.Entities
.push_back(pbot
->dataSetRow());
410 continue; // directly test the same it (the next in fact).
414 _BotsToRespawn
.insert(_BotsToRespawn
.begin(), botToSpawn
); // push_front so the end doesn't change.
422 msg
.ActionName
= actionName
;
429 CBot
* CSpawnGroup::findLeader()
431 FOREACH(itBot
, CCont
<CBot
>, bots())
434 if (bot
->isSpawned())
436 if (bot
->getSpawnObj()->isAlive())
443 std::vector
<std::string
> CSpawnGroup::getMultiLineInfoString() const
445 std::vector
<std::string
> container
;
447 pushTitle(container
, "CSpawnGroup");
448 pushEntry(container
, "move profile: " + _MovingProfile
.getOneLineInfoString());
449 pushEntry(container
, "activity profile: " + _ActivityProfile
.getOneLineInfoString());
450 pushEntry(container
, "fight profile: " + _FightProfile
.getOneLineInfoString());
451 pushFooter(container
);
456 NLMISC::CSmartPtr
<CAIPlace
const> CSpawnGroup::buildFirstHitPlace(TDataSetRow
const& aggroBot
) const
458 if (_ActivityProfile
.getAIProfileType()==AITYPES::ACTIVITY_SQUAD
)
459 return static_cast<CGrpProfileSquad
*>(_ActivityProfile
.getAIProfile())->buildFirstHitPlace(aggroBot
);
463 void CSpawnGroup::addAggroFor(TDataSetRow
const& bot
, float aggro
, bool forceReturnAggro
, NLMISC::CSmartPtr
<CAIPlace
const> place
)
465 CGroup
& grp
= getPersistent();
466 FOREACH(itBot
, CCont
<CBot
>, grp
.bots())
471 CSpawnBot
* spBot
= pBot
->getSpawnObj();
474 spBot
->addAggroFor(bot
, aggro
, forceReturnAggro
, place
, false);
479 void CSpawnGroup::setAggroMinimumFor(TDataSetRow
const& bot
, float aggro
, bool forceReturnAggro
, NLMISC::CSmartPtr
<CAIPlace
const> place
)
481 CGroup
& grp
= getPersistent();
482 FOREACH(itBot
, CCont
<CBot
>, grp
.bots())
487 CSpawnBot
* spBot
= pBot
->getSpawnObj();
490 spBot
->setAggroMinimumFor(bot
, aggro
, forceReturnAggro
, place
, false);
496 bool CSpawnGroup::haveAggro() const
498 CGroup
const& group
= getPersistent();
499 FOREACHC(itBot
, CCont
<CBot
>, group
.bots())
501 CBot
const* pBot
= *itBot
;
504 CSpawnBot
const* spBot
= pBot
->getSpawnObj();
505 if (spBot
&& spBot
->haveAggro())
512 bool CSpawnGroup::haveAggroOrReturnPlace() const
514 CGroup
const& group
= getPersistent();
515 FOREACHC(itBot
, CCont
<CBot
>, group
.bots())
517 CBot
const* pBot
= *itBot
;
520 CSpawnBot
const* spBot
= pBot
->getSpawnObj();
521 if (spBot
&& spBot
->haveAggroOrReturnPlace())