// 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 "ai_grp.h" #include "visual_properties_interface.h" #include "ai_profile_npc.h" using namespace MULTI_LINE_FORMATER; ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// //-------------------------------------------------------------------------- // METHODS for debugging stuff //-------------------------------------------------------------------------- bool GrpHistoryRecordLog = true; CAIVector lastTriedPos; CGroup::CGroup (CManager *owner, RYAI_MAP_CRUNCH::TAStarFlag denyFlags, CAIAliasDescriptionNode *aliasTree) : CAliasChild(owner,aliasTree), CPersistent(), _EscortTeamId(CTEAM::InvalidTeamId), _EscortRange(30), _AutoSpawn(true), _DenyFlags(denyFlags), _AutoDestroy(false), _AggroRange(0), _UpdateNbTicks(30) { owner->getAIInstance()->addGroupInfo(this); } CGroup::CGroup (CManager *owner, RYAI_MAP_CRUNCH::TAStarFlag denyFlags, uint32 alias, std::string const& name) : CAliasChild(owner, alias, name), CPersistent(), _EscortTeamId(CTEAM::InvalidTeamId), _EscortRange(30), _AutoSpawn(true), _DenyFlags(denyFlags), _AutoDestroy(false), _AggroRange(0), _UpdateNbTicks(30) { owner->getAIInstance()->addGroupInfo(this); } CGroup::~CGroup () { getAIInstance()->removeGroupInfo(this, this); getOwner()->removeFromSpawnList (this); if (isSpawned()) { despawnGrp (); } } CBot* CGroup::getLeader() { FOREACH(itBot, CCont, bots()) { CSpawnBot* const bot = itBot->getSpawnObj(); if (bot && bot->isAlive()) return *itBot; } // no bots alive, no leader ! return NULL; } CBot* CGroup::getSquadLeader(bool checkAliveStatus) { CCont::iterator itBot = bots().begin(); if (itBot!=bots().end()) { CSpawnBot* const bot = itBot->getSpawnObj(); if (bot && (!checkAliveStatus || bot->isAlive())) return *itBot; } // first bot not alive, no squad leader ! return NULL; } void CGroup::serviceEvent (const CServiceEvent &info) { CCont::iterator it=bots().begin(), itEnd=bots().end(); while (it!=itEnd) { it->serviceEvent (info); ++it; } } void CGroup::despawnBots () { for (CCont::iterator it=_Bots.begin(), itEnd=_Bots.end(); it!=itEnd;++it) { if (it->isSpawned()) it->despawnBot(); } } std::string CGroup::getIndexString() const { return getOwner()->getIndexString()+NLMISC::toString(":g%u", getChildIndex()); } std::string CGroup::getOneLineInfoString() const { return std::string("Group '") + getName() + "'"; } std::vector CGroup::getMultiLineInfoString() const { std::vector container; pushTitle(container, "CGroup"); pushEntry(container, "id=" + getIndexString()); container.back() += " alias=" + getAliasString(); container.back() += " name=" + getName(); pushEntry(container, "fullname=" + getFullName()); pushEntry(container, "autoSpawn=" + NLMISC::toString(_AutoSpawn)); pushEntry(container, "aggroRange=" + NLMISC::toString(_AggroRange)); container.back() += " updateNbTicks=" + NLMISC::toString(_UpdateNbTicks); if (isSpawned()) { std::vector strings = getSpawnObj()->getMultiLineInfoString(); FOREACHC(it, std::vector, strings) pushEntry(container, *it); } else pushEntry(container, ""); pushFooter(container); return container; } std::string CGroup::getFullName() const { return std::string(getOwner()->getFullName()+":"+getName()); } void CGroup::lastBotDespawned() { // send message } void CGroup::firstBotSpawned() { // send message } ////////////////////////////////////////////////////////////////////////////// // CSpawnGroup // ////////////////////////////////////////////////////////////////////////////// CSpawnGroup::~CSpawnGroup() { FOREACH(it, CCont,bots()) { if ((*it)->isSpawned()) (*it)->despawnBot(); } getPersistent().despawnBots(); // clear profiles _MovingProfile = CProfilePtr(); _FightProfile = CProfilePtr(); _ActivityProfile = CProfilePtr(); _PunctualHoldActivityProfile = CProfilePtr(); _PunctualHoldMovingProfile = CProfilePtr(); } bool CSpawnGroup::calcCenterPos(CAIVector& grp_pos, bool allowDeadBot) { if (bots().size()<=0) return false; double x(0), y(0); uint count(0); FOREACH(it, CCont, bots()) { CSpawnBot const* const spawnBot = it->getSpawnObj(); if (!spawnBot || (!allowDeadBot && !spawnBot->isAlive())) continue; x += spawnBot->pos().x().asDouble(); y += spawnBot->pos().y().asDouble(); ++count; } if (count==0) return false; grp_pos.setX(x/count); grp_pos.setY(y/count); return true; } void CSpawnGroup::spawnBotOfGroup() { CCont::iterator it = bots().begin(); CCont::iterator itEnd = bots().end(); while (it!=itEnd) { CBot* bot = *it; if (!bot->isSpawned()) { bool ok= bot->spawn(); // code removed by Sadge because it didn't fix the problem it was added for // if (ok) // { // // the spawn succeeded // // make sure the bot isn't in the despawn list // for (uint32 i= _BotsToDespawn.size(); i--;) // { // if (_BotsToDespawn[i].getBotIndex()== bot->getChildIndex()) // { // nldebug("Removing bot from the despawn list because they just respawned: %s",bot->getFullName().c_str()); // _BotsToDespawn[i]= _BotsToDespawn.back(); // _BotsToDespawn.pop_back(); // } // } // } if (!ok) { // the spawn failed std::string name; if (!bot->getFullName().empty()) name = bot->getFullName(); else { if (!bot->getOwner()->getFullName().empty()) name = std::string("from group ") + bot->getOwner()->getFullName(); else name = std::string("Unknown"); } if (bot->getSheet()->SheetId()==NLMISC::CSheetId::Unknown) nlwarning("***> Spawn failed position(%s), UNKNOWN SHEET! Bot %s ", lastTriedPos.toString().c_str(), name.c_str()); else nlwarning("***> Spawn failed position(%s), sheetId(%s) Bot %s ", lastTriedPos.toString().c_str(), bot->getSheet()->SheetId().toString().c_str(), name.c_str()); } } ++it; } } void CSpawnGroup::addBotToDespawnAndRespawnTime(CBot* bot, uint32 despawnTime, uint32 respawnTime) { nlassert(bot->isSpawned()); nlassert(bot->getOwner()==&getPersistent()); uint32 const botIndex = bot->getChildIndex(); FOREACH(it, std::vector, _BotsToDespawn) { if (it->getBotIndex()==botIndex) { *it = CBotToSpawn(botIndex, despawnTime, respawnTime); return; } } _BotsToDespawn.push_back(CBotToSpawn(botIndex, despawnTime, respawnTime)); } void CSpawnGroup::checkDespawn() { if (_BotsToDespawn.empty()) return; //FOREACH_NOINC(it, std::vector, _BotsToDespawn) for(uint32 i = 0; i < _BotsToDespawn.size();) { CBotToSpawn& botToDespawn = _BotsToDespawn[i]; if (botToDespawn.waitingDespawnTimeOver()) { if (botToDespawn.getBotIndex()>=getPersistent().bots().size()) { STOP("Array overflow in despawn code!"); } else if (getPersistent().bots()[botToDespawn.getBotIndex()]==NULL) { STOP("Trying to despawn a bot who doesn't exist!!"); } else { getPersistent().bots()[botToDespawn.getBotIndex()]->despawnBot(); if (getPersistent().isAutoSpawn()) _BotsToRespawn.push_back(botToDespawn); } // pop this entry out of the bots to despawn vector // ace: we don't use iterator because when pop_back(), all iterators are invalidated _BotsToDespawn[i] = _BotsToDespawn.back(); _BotsToDespawn.pop_back(); continue; } i++; } if (_NbSpawnedBot==0 && _BotsToRespawn.size()==0) { // Warn the parent manager that this group is now dead. getPersistent().getOwner()->getOwner()->groupDead(&getPersistent()); if (getPersistent()._AutoDestroy) getPersistent().getOwner()->groups().removeChildByIndex(getPersistent().getChildIndex()); } } void CSpawnGroup::incSpawnedBot(CBot& spawnBot) { #if !FINAL_VERSION uint32 botIndex = spawnBot.getChildIndex(); for (uint32 i=(uint32)_BotsToRespawn.size(); i--; ) { if (_BotsToRespawn[i].getBotIndex()==botIndex) { nldebug("Removing bot from _BotsToRespawn because they just respawned: %s",spawnBot.getFullName().c_str()); // nlwarning("WARNING!!! Old assert \"_BotsToRespawn[i].getBotIndex()!=botIndex\" would have failed"); _BotsToRespawn[i]=_BotsToRespawn.back(); _BotsToRespawn.pop_back(); } } for (uint32 i=(uint32)_BotsToDespawn.size(); i--; ) { if (_BotsToDespawn[i].getBotIndex()==botIndex) { nldebug("Removing bot from _BotsToDespawn because they just respawned: %s",spawnBot.getFullName().c_str()); // nlwarning("WARNING!!! Old assert \"_BotsToDespawn[i].getBotIndex()!=botIndex\" would have failed"); _BotsToDespawn[i]=_BotsToDespawn.back(); _BotsToDespawn.pop_back(); } } #endif if (_NbSpawnedBot==0) { getPersistent().firstBotSpawned(); } _NbSpawnedBot++; } void CSpawnGroup::decSpawnedBot() { --_NbSpawnedBot; if (_NbSpawnedBot==0) { getPersistent().lastBotDespawned(); } } void CSpawnGroup::checkRespawn() { // respawn if there is not too much dead .. (no more than one at each tick). if (_BotsToRespawn.size()<=0) return; //FOREACH_NOINC(it, std::vector, _BotsToRespawn) for(uint32 i = 0; i < _BotsToRespawn.size();) { CBotToSpawn const& botToSpawn = _BotsToRespawn[i]; if (botToSpawn.waitingRespawnTimeOver()) { CBot* botPt = getPersistent().bots()[botToSpawn.getBotIndex()]; CBotToSpawn const botToSpawn = _BotsToRespawn.back(); // remove the entry _BotsToRespawn[i] = _BotsToRespawn.back(); _BotsToRespawn.pop_back(); if (botPt->isSpawned()) { nlwarning("CSpawnGroup::checkRespawn : trying to respawn a spawned bot"); } if (botPt->isSpawned() || botPt->reSpawn(false)) { continue; // directly test the same it (the next in fact). } else { _BotsToRespawn.insert(_BotsToRespawn.begin(), botToSpawn); // push_front so the end doesn't change. } } ++i; } } CBot* CSpawnGroup::findLeader() { FOREACH(itBot, CCont, bots()) { CBot* bot = *itBot; if (bot->isSpawned()) { if (bot->getSpawnObj()->isAlive()) return bot; } } return NULL; } std::vector CSpawnGroup::getMultiLineInfoString() const { std::vector container; pushTitle(container, "CSpawnGroup"); pushEntry(container, "move profile: " + _MovingProfile.getOneLineInfoString()); pushEntry(container, "activity profile: " + _ActivityProfile.getOneLineInfoString()); pushEntry(container, "fight profile: " + _FightProfile.getOneLineInfoString()); pushFooter(container); return container; } NLMISC::CSmartPtr CSpawnGroup::buildFirstHitPlace(TDataSetRow const& aggroBot) const { if (_ActivityProfile.getAIProfileType()==AITYPES::ACTIVITY_SQUAD) return static_cast(_ActivityProfile.getAIProfile())->buildFirstHitPlace(aggroBot); return NULL; } void CSpawnGroup::addAggroFor(TDataSetRow const& bot, float aggro, bool forceReturnAggro, NLMISC::CSmartPtr place) { CGroup& grp = getPersistent(); FOREACH(itBot, CCont, grp.bots()) { CBot* pBot = *itBot; if (pBot) { CSpawnBot* spBot = pBot->getSpawnObj(); if (spBot) { spBot->addAggroFor(bot, aggro, forceReturnAggro, place, false); } } } } void CSpawnGroup::setAggroMinimumFor(TDataSetRow const& bot, float aggro, bool forceReturnAggro, NLMISC::CSmartPtr place) { CGroup& grp = getPersistent(); FOREACH(itBot, CCont, grp.bots()) { CBot* pBot = *itBot; if (pBot) { CSpawnBot* spBot = pBot->getSpawnObj(); if (spBot) { spBot->setAggroMinimumFor(bot, aggro, forceReturnAggro, place, false); } } } } bool CSpawnGroup::haveAggro() const { CGroup const& group = getPersistent(); FOREACHC(itBot, CCont, group.bots()) { CBot const* pBot = *itBot; if (pBot) { CSpawnBot const* spBot = pBot->getSpawnObj(); if (spBot && spBot->haveAggro()) return true; } } return false; } bool CSpawnGroup::haveAggroOrReturnPlace() const { CGroup const& group = getPersistent(); FOREACHC(itBot, CCont, group.bots()) { CBot const* pBot = *itBot; if (pBot) { CSpawnBot const* spBot = pBot->getSpawnObj(); if (spBot && spBot->haveAggroOrReturnPlace()) return true; } } return false; }