// Helper function, to process a slave list void CreatureLinkingHolder::ProcessSlaveGuidList(CreatureLinkingEvent eventType, Creature* pSource, uint32 flag, uint16 searchRange, GuidList& slaveGuidList, Unit* pEnemy) { if (!flag) return; for (GuidList::iterator slave_itr = slaveGuidList.begin(); slave_itr != slaveGuidList.end();) { Creature* pSlave = pSource->GetMap()->GetCreature(*slave_itr); if (!pSlave) { // Remove old guid first slaveGuidList.erase(slave_itr++); continue; } ++slave_itr; // Ignore Pets if (pSlave->IsPet()) continue; // Handle single slave if (IsSlaveInRangeOfBoss(pSlave, pSource, searchRange)) ProcessSlave(eventType, pSource, flag, pSlave, pEnemy); } }
// This function lets a slave refollow his master bool CreatureLinkingHolder::TryFollowMaster(Creature* pCreature) { CreatureLinkingInfo const* pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(pCreature); if (!pInfo || !(pInfo->linkingFlag & FLAG_FOLLOW)) return false; Creature* pMaster = NULL; if (pInfo->mapId != INVALID_MAP_ID) // entry case { BossGuidMapBounds finds = m_masterGuid.equal_range(pInfo->masterId); for (BossGuidMap::iterator itr = finds.first; itr != finds.second; ++itr) { pMaster = pCreature->GetMap()->GetCreature(itr->second); if (pMaster && IsSlaveInRangeOfBoss(pCreature, pMaster, pInfo->searchRange)) break; } } else // guid case { CreatureData const* masterData = sObjectMgr.GetCreatureData(pInfo->masterDBGuid); CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(masterData->id); pMaster = pCreature->GetMap()->GetCreature(ObjectGuid(cInfo->GetHighGuid(), cInfo->Entry, pInfo->masterDBGuid)); } if (pMaster && pMaster->isAlive()) { SetFollowing(pCreature, pMaster); return true; } return false; }
/** Worker function to check if a spawning condition is met * * This function is used directly from above function, and for recursive use * in case of recursive use it is used only on _map with information of lowGuid. * * @param lowGuid (only relevant in case of recursive uses) -- db-guid of the npc that is checked * @param _map Map on which things are checked * @param pInfo (only shipped in case of initial use) -- used as marker of first use, also in first use filled directly * @param sx, sy (spawn position of the checked npc with initial use) */ bool CreatureLinkingHolder::CanSpawn(uint32 lowGuid, Map* _map, CreatureLinkingInfo const* pInfo, float sx, float sy) const { if (!pInfo) // Prepare data for recursive use { CreatureData const* data = sObjectMgr.GetCreatureData(lowGuid); if (!data) return true; pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(data->id, lowGuid, data->mapid); if (!pInfo) return true; // Has lowGuid npc actually spawning linked? if (!sCreatureLinkingMgr.IsSpawnedByLinkedMob(pInfo)) return true; sx = data->posX; // Fill position data sy = data->posY; } if (pInfo->searchRange == 0) // Map wide case { if (!pInfo->masterDBGuid) return false; // This should never happen if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_DEAD) return IsRespawnReady(pInfo->masterDBGuid, _map); else if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_ALIVE) return !IsRespawnReady(pInfo->masterDBGuid, _map); else return true; } // Search for nearby master BossGuidMapBounds finds = m_masterGuid.equal_range(pInfo->masterId); for (BossGuidMap::const_iterator itr = finds.first; itr != finds.second; ++itr) { Creature* pMaster = _map->GetCreature(itr->second); if (pMaster && IsSlaveInRangeOfBoss(pMaster, sx, sy, pInfo->searchRange)) { if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_DEAD) return pMaster->IsAlive(); else if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_ALIVE) return !pMaster->IsAlive(); else return true; } } return true; // local boss does not exist - spawn }
// This function lets a slave refollow his master bool CreatureLinkingHolder::TryFollowMaster(Creature* pCreature) { CreatureLinkingInfo const* pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(pCreature); if (!pInfo || !(pInfo->linkingFlag & FLAG_FOLLOW)) return false; BossGuidMapBounds finds = m_masterGuid.equal_range(pInfo->masterId); for (BossGuidMap::iterator itr = finds.first; itr != finds.second; ++itr) { Creature* pMaster = pCreature->GetMap()->GetCreature(itr->second); if (pMaster && pMaster->isAlive() && IsSlaveInRangeOfBoss(pCreature, pMaster, pInfo->searchRange)) { SetFollowing(pCreature, pMaster); return true; } } return false; }
// Function to check if a passive spawning condition is met bool CreatureLinkingHolder::CanSpawn(Creature* pCreature) { CreatureLinkingInfo const* pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(pCreature); if (!pInfo) return true; if (pInfo->searchRange == 0) // Map wide case { if (!pInfo->masterDBGuid) return false; // This should never happen if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_DEAD) return pCreature->GetMap()->GetPersistentState()->GetCreatureRespawnTime(pInfo->masterDBGuid) == 0; else if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_ALIVE) return pCreature->GetMap()->GetPersistentState()->GetCreatureRespawnTime(pInfo->masterDBGuid) > 0; else return true; } // Search for nearby master BossGuidMapBounds finds = m_masterGuid.equal_range(pInfo->masterId); for (BossGuidMap::iterator itr = finds.first; itr != finds.second; ++itr) { Creature* pMaster = pCreature->GetMap()->GetCreature(itr->second); if (pMaster && IsSlaveInRangeOfBoss(pCreature, pMaster, pInfo->searchRange)) { if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_DEAD) return pMaster->isAlive(); else if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_ALIVE) return !pMaster->isAlive(); else return true; } } return true; // local boss does not exist - spawn }
// Function to process actions for linked NPCs void CreatureLinkingHolder::DoCreatureLinkingEvent(CreatureLinkingEvent eventType, Creature* pSource, Unit* pEnemy /* = NULL*/) { // This check will be needed in reload case if (!sCreatureLinkingMgr.IsLinkedEventTrigger(pSource)) return; // Ignore atypic behaviour if (pSource->IsControlledByPlayer()) return; if (eventType == LINKING_EVENT_AGGRO && !pEnemy) return; uint32 eventFlagFilter = 0; uint32 reverseEventFlagFilter = 0; switch (eventType) { case LINKING_EVENT_AGGRO: eventFlagFilter = EVENT_MASK_ON_AGGRO; reverseEventFlagFilter = FLAG_TO_AGGRO_ON_AGGRO; break; case LINKING_EVENT_EVADE: eventFlagFilter = EVENT_MASK_ON_EVADE; reverseEventFlagFilter = FLAG_TO_RESPAWN_ON_EVADE; break; case LINKING_EVENT_DIE: eventFlagFilter = EVENT_MASK_ON_DIE; reverseEventFlagFilter = 0; break; case LINKING_EVENT_RESPAWN: eventFlagFilter = EVENT_MASK_ON_RESPAWN; reverseEventFlagFilter = FLAG_FOLLOW; break; } // Process Slaves (by entry) HolderMapBounds bounds = m_holderMap.equal_range(pSource->GetEntry()); for (HolderMap::iterator itr = bounds.first; itr != bounds.second; ++itr) ProcessSlaveGuidList(eventType, pSource, itr->second.linkingFlag & eventFlagFilter, itr->second.searchRange, itr->second.linkedGuids, pEnemy); // Process Slaves (by guid) bounds = m_holderGuidMap.equal_range(pSource->GetGUIDLow()); for (HolderMap::iterator itr = bounds.first; itr != bounds.second; ++itr) ProcessSlaveGuidList(eventType, pSource, itr->second.linkingFlag & eventFlagFilter, itr->second.searchRange, itr->second.linkedGuids, pEnemy); // Process Master if (CreatureLinkingInfo const* pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(pSource)) { if (pInfo->linkingFlag & reverseEventFlagFilter) { Creature* pMaster = NULL; if (pInfo->mapId != INVALID_MAP_ID) // entry case { BossGuidMapBounds finds = m_masterGuid.equal_range(pInfo->masterId); for (BossGuidMap::iterator itr = finds.first; itr != finds.second; ++itr) { pMaster = pSource->GetMap()->GetCreature(itr->second); if (pMaster && IsSlaveInRangeOfBoss(pSource, pMaster, pInfo->searchRange)) break; } } else // guid case { CreatureData const* masterData = sObjectMgr.GetCreatureData(pInfo->masterDBGuid); CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(masterData->id); pMaster = pSource->GetMap()->GetCreature(ObjectGuid(cInfo->GetHighGuid(), cInfo->Entry, pInfo->masterDBGuid)); } if (pMaster) { switch (eventType) { case LINKING_EVENT_AGGRO: if (pMaster->IsControlledByPlayer()) return; if (pMaster->isInCombat()) pMaster->SetInCombatWith(pEnemy); else pMaster->AI()->AttackStart(pEnemy); break; case LINKING_EVENT_EVADE: if (!pMaster->isAlive()) pMaster->Respawn(); break; case LINKING_EVENT_RESPAWN: if (pMaster->isAlive()) SetFollowing(pSource, pMaster); } } } } }
// Function to check if a slave belongs to a boss by range-issue bool CreatureLinkingHolder::IsSlaveInRangeOfBoss(Creature const* pSlave, Creature const* pBoss, uint16 searchRange) const { float sX, sY, sZ; pSlave->GetRespawnCoord(sX, sY, sZ); return IsSlaveInRangeOfBoss(pBoss, sX, sY, searchRange); }