[11968] Add support for range dependend slave/master

Add new column `search_radius` to `creature_linking_template`.
If this column is used (value > 0), master and slave are linked together if and only if they have their respawn coordinates within this range (point-to-point distance calculation)
Note that linked spawning is slower this way and should only be used if required

Signed-off-by: Schmoozerd <schmoozerd@scriptdev2.com>
This commit is contained in:
Schmoozerd 2012-04-19 21:21:42 +02:00
parent 516283e28c
commit a5a081c2ec
6 changed files with 94 additions and 33 deletions

View file

@ -24,7 +24,7 @@ CREATE TABLE `db_version` (
`version` varchar(120) default NULL, `version` varchar(120) default NULL,
`creature_ai_version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL,
`cache_id` int(10) default '0', `cache_id` int(10) default '0',
`required_11964_01_mangos_conditions` bit(1) default NULL `required_11968_01_mangos_creature_linking_template` bit(1) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes';
-- --
@ -976,6 +976,7 @@ CREATE TABLE `creature_linking_template` (
`map` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Id of map of the mobs', `map` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Id of map of the mobs',
`master_entry` mediumint(8) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'master to trigger events', `master_entry` mediumint(8) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'master to trigger events',
`flag` mediumint(8) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'flag - describing what should happen when', `flag` mediumint(8) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'flag - describing what should happen when',
`search_range` mediumint(8) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'search_range - describing in which range (spawn-coords) master and slave are linked together',
PRIMARY KEY (`entry`,`map`) PRIMARY KEY (`entry`,`map`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Creature Linking System'; ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Creature Linking System';

View file

@ -0,0 +1,3 @@
ALTER TABLE db_version CHANGE COLUMN required_11964_01_mangos_conditions required_11968_01_mangos_creature_linking_template bit;
ALTER TABLE creature_linking_template ADD COLUMN search_range MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER flag;

View file

@ -61,7 +61,7 @@ void CreatureLinkingMgr::LoadFromDB()
m_creatureLinkingMap.clear(); m_creatureLinkingMap.clear();
m_eventTriggers.clear(); // master m_eventTriggers.clear(); // master
QueryResult* result = WorldDatabase.Query("SELECT entry, map, master_entry, flag FROM creature_linking_template"); QueryResult* result = WorldDatabase.Query("SELECT entry, map, master_entry, flag, search_range FROM creature_linking_template");
uint32 count = 0; uint32 count = 0;
@ -89,13 +89,14 @@ void CreatureLinkingMgr::LoadFromDB()
tmp.mapId = fields[1].GetUInt32(); tmp.mapId = fields[1].GetUInt32();
tmp.masterId = fields[2].GetUInt32(); tmp.masterId = fields[2].GetUInt32();
tmp.linkingFlag = fields[3].GetUInt16(); tmp.linkingFlag = fields[3].GetUInt16();
tmp.searchRange = fields[4].GetUInt16();
tmp.masterDBGuid = 0; // Will be initialized for unique mobs later (only for spawning dependend) tmp.masterDBGuid = 0; // Will be initialized for unique mobs later (only for spawning dependend)
if (!IsLinkingEntryValid(entry, &tmp)) if (!IsLinkingEntryValid(entry, &tmp))
continue; continue;
// Store db-guid for master of whom pTmp is spawn dependend // Store db-guid for master of whom pTmp is spawn dependend (only non-local bosses)
if (tmp.linkingFlag & (FLAG_CANT_SPAWN_IF_BOSS_DEAD | FLAG_CANT_SPAWN_IF_BOSS_ALIVE)) if (tmp.searchRange == 0 && tmp.linkingFlag & (FLAG_CANT_SPAWN_IF_BOSS_DEAD | FLAG_CANT_SPAWN_IF_BOSS_ALIVE))
{ {
if (QueryResult* guid_result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE id=%u AND map=%u LIMIT 1", tmp.masterId, tmp.mapId)) if (QueryResult* guid_result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE id=%u AND map=%u LIMIT 1", tmp.masterId, tmp.mapId))
{ {
@ -152,7 +153,7 @@ bool CreatureLinkingMgr::IsLinkingEntryValid(uint32 slaveEntry, CreatureLinkingI
} }
// Check for uniqueness of mob whom is followed, on whom spawning is dependend // Check for uniqueness of mob whom is followed, on whom spawning is dependend
if (pTmp->linkingFlag & (FLAG_FOLLOW | FLAG_CANT_SPAWN_IF_BOSS_DEAD | FLAG_CANT_SPAWN_IF_BOSS_ALIVE)) if (pTmp->searchRange == 0 && pTmp->linkingFlag & (FLAG_FOLLOW | FLAG_CANT_SPAWN_IF_BOSS_DEAD | FLAG_CANT_SPAWN_IF_BOSS_ALIVE))
{ {
// Painfully slow, needs better idea // Painfully slow, needs better idea
QueryResult *result = WorldDatabase.PQuery("SELECT COUNT(guid) FROM creature WHERE id=%u AND map=%u", pTmp->masterId, pTmp->mapId); QueryResult *result = WorldDatabase.PQuery("SELECT COUNT(guid) FROM creature WHERE id=%u AND map=%u", pTmp->masterId, pTmp->mapId);
@ -204,7 +205,7 @@ bool CreatureLinkingMgr::IsSpawnedByLinkedMob(Creature* pCreature)
{ {
CreatureLinkingInfo const* pInfo = CreatureLinkingMgr::GetLinkedTriggerInformation(pCreature); CreatureLinkingInfo const* pInfo = CreatureLinkingMgr::GetLinkedTriggerInformation(pCreature);
return pInfo && pInfo->linkingFlag & (FLAG_CANT_SPAWN_IF_BOSS_DEAD | FLAG_CANT_SPAWN_IF_BOSS_ALIVE) && pInfo->masterDBGuid; return pInfo && pInfo->linkingFlag & (FLAG_CANT_SPAWN_IF_BOSS_DEAD | FLAG_CANT_SPAWN_IF_BOSS_ALIVE) && (pInfo->masterDBGuid || pInfo->searchRange);
} }
// This gives the information of a linked NPC (describes action when its ActionTrigger triggers) // This gives the information of a linked NPC (describes action when its ActionTrigger triggers)
@ -232,7 +233,7 @@ void CreatureLinkingHolder::AddSlaveToHolder(Creature* pCreature)
HolderMapBounds bounds = m_holderMap.equal_range(pInfo->masterId); HolderMapBounds bounds = m_holderMap.equal_range(pInfo->masterId);
for (HolderMap::iterator itr = bounds.first; itr != bounds.second; ++itr) for (HolderMap::iterator itr = bounds.first; itr != bounds.second; ++itr)
{ {
if (itr->second.linkingFlag == pInfo->linkingFlag) if (itr->second.linkingFlag == pInfo->linkingFlag && itr->second.searchRange == pInfo->searchRange)
{ {
itr->second.linkedGuids.push_back(pCreature->GetObjectGuid()); itr->second.linkedGuids.push_back(pCreature->GetObjectGuid());
pCreature = NULL; // Store that is was handled pCreature = NULL; // Store that is was handled
@ -243,9 +244,10 @@ void CreatureLinkingHolder::AddSlaveToHolder(Creature* pCreature)
// If this is a new flag, insert new entry // If this is a new flag, insert new entry
if (pCreature) if (pCreature)
{ {
FlagAndGuids tmp; InfoAndGuids tmp;
tmp.linkedGuids.push_back(pCreature->GetObjectGuid()); tmp.linkedGuids.push_back(pCreature->GetObjectGuid());
tmp.linkingFlag = pInfo->linkingFlag; tmp.linkingFlag = pInfo->linkingFlag;
tmp.searchRange = pInfo->searchRange;
m_holderMap.insert(HolderMap::value_type(pInfo->masterId, tmp)); m_holderMap.insert(HolderMap::value_type(pInfo->masterId, tmp));
} }
} }
@ -260,7 +262,13 @@ void CreatureLinkingHolder::AddMasterToHolder(Creature* pCreature)
if (!sCreatureLinkingMgr.IsLinkedMaster(pCreature)) if (!sCreatureLinkingMgr.IsLinkedMaster(pCreature))
return; return;
m_masterGuid[pCreature->GetEntry()] = pCreature->GetObjectGuid(); // Check, if already stored
BossGuidMapBounds bounds = m_masterGuid.equal_range(pCreature->GetEntry());
for (BossGuidMap::iterator itr = bounds.first; itr != bounds.second; ++itr)
if (itr->second == pCreature->GetObjectGuid())
return; // Already added
m_masterGuid.emplace(BossGuidMap::value_type(pCreature->GetEntry(), pCreature->GetObjectGuid()));
} }
// Function to process actions for linked NPCs // Function to process actions for linked NPCs
@ -292,17 +300,18 @@ void CreatureLinkingHolder::DoCreatureLinkingEvent(CreatureLinkingEvent eventTyp
HolderMapBounds bounds = m_holderMap.equal_range(pSource->GetEntry()); HolderMapBounds bounds = m_holderMap.equal_range(pSource->GetEntry());
// Get all holders for this boss // Get all holders for this boss
for (HolderMap::iterator itr = bounds.first; itr != bounds.second; ++itr) for (HolderMap::iterator itr = bounds.first; itr != bounds.second; ++itr)
ProcessSlaveGuidList(eventType, pSource, itr->second.linkingFlag & eventFlagFilter, itr->second.linkedGuids, pEnemy); ProcessSlaveGuidList(eventType, pSource, itr->second.linkingFlag & eventFlagFilter, itr->second.searchRange, itr->second.linkedGuids, pEnemy);
// Process Master // Process Master
if (CreatureLinkingInfo const* pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(pSource)) if (CreatureLinkingInfo const* pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(pSource))
{ {
if (pInfo->linkingFlag & reverseEventFlagFilter) if (pInfo->linkingFlag & reverseEventFlagFilter)
{ {
BossGuidMap::const_iterator find = m_masterGuid.find(pInfo->masterId); BossGuidMapBounds finds = m_masterGuid.equal_range(pInfo->masterId);
if (find != m_masterGuid.end()) for (BossGuidMap::iterator itr = finds.first; itr != finds.second; ++itr)
{ {
if (Creature* pMaster = pSource->GetMap()->GetCreature(find->second)) Creature* pMaster = pSource->GetMap()->GetCreature(itr->second);
if (pMaster && IsSlaveInRangeOfBoss(pSource, pMaster, pInfo->searchRange))
{ {
switch (eventType) switch (eventType)
{ {
@ -330,7 +339,7 @@ void CreatureLinkingHolder::DoCreatureLinkingEvent(CreatureLinkingEvent eventTyp
} }
// Helper function, to process a slave list // Helper function, to process a slave list
void CreatureLinkingHolder::ProcessSlaveGuidList(CreatureLinkingEvent eventType, Creature* pSource, uint32 flag, GuidList& slaveGuidList, Unit* pEnemy) void CreatureLinkingHolder::ProcessSlaveGuidList(CreatureLinkingEvent eventType, Creature* pSource, uint32 flag, uint16 searchRange, GuidList& slaveGuidList, Unit* pEnemy)
{ {
if (!flag) if (!flag)
return; return;
@ -352,6 +361,7 @@ void CreatureLinkingHolder::ProcessSlaveGuidList(CreatureLinkingEvent eventType,
continue; continue;
// Handle single slave // Handle single slave
if (IsSlaveInRangeOfBoss(pSlave, pSource, searchRange))
ProcessSlave(eventType, pSource, flag, pSlave, pEnemy); ProcessSlave(eventType, pSource, flag, pSlave, pEnemy);
} }
} }
@ -431,19 +441,61 @@ void CreatureLinkingHolder::SetFollowing(Creature* pWho, Creature* pWhom)
pWho->GetMotionMaster()->MoveFollow(pWhom, dist, angle); pWho->GetMotionMaster()->MoveFollow(pWhom, dist, angle);
} }
// Function to check if a slave belongs to a boss by range-issue
bool CreatureLinkingHolder::IsSlaveInRangeOfBoss(Creature* pSlave, Creature* pBoss, uint16 searchRange)
{
if (!searchRange)
return true;
// Do some calculations
float sX, sY, sZ, mX, mY, mZ;
pSlave->GetRespawnCoord(sX, sY, sZ);
pBoss->GetRespawnCoord(mX, mY, mZ);
float dx, dy;
dx = sX - mX;
dy = sY - mY;
return dx*dx + dy*dy < searchRange*searchRange;
}
// Function to check if a passive spawning condition is met // Function to check if a passive spawning condition is met
bool CreatureLinkingHolder::CanSpawn(Creature* pCreature) bool CreatureLinkingHolder::CanSpawn(Creature* pCreature)
{ {
CreatureLinkingInfo const* pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(pCreature); CreatureLinkingInfo const* pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(pCreature);
if (!pInfo || !pInfo->masterDBGuid) if (!pInfo)
return true; 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) if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_DEAD)
return pCreature->GetMap()->GetPersistentState()->GetCreatureRespawnTime(pInfo->masterDBGuid) == 0; return pCreature->GetMap()->GetPersistentState()->GetCreatureRespawnTime(pInfo->masterDBGuid) == 0;
else if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_ALIVE) else if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_ALIVE)
return pCreature->GetMap()->GetPersistentState()->GetCreatureRespawnTime(pInfo->masterDBGuid) > 0; return pCreature->GetMap()->GetPersistentState()->GetCreatureRespawnTime(pInfo->masterDBGuid) > 0;
else
return true; 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
} }
// This function lets a slave refollow his master // This function lets a slave refollow his master
@ -453,11 +505,11 @@ bool CreatureLinkingHolder::TryFollowMaster(Creature* pCreature)
if (!pInfo || !(pInfo->linkingFlag & FLAG_FOLLOW)) if (!pInfo || !(pInfo->linkingFlag & FLAG_FOLLOW))
return false; return false;
BossGuidMap::const_iterator find = m_masterGuid.find(pInfo->masterId); BossGuidMapBounds finds = m_masterGuid.equal_range(pInfo->masterId);
if (find != m_masterGuid.end()) for (BossGuidMap::iterator itr = finds.first; itr != finds.second; ++itr)
{ {
Creature* pMaster = pCreature->GetMap()->GetCreature(find->second); Creature* pMaster = pCreature->GetMap()->GetCreature(itr->second);
if (pMaster && pMaster->isAlive()) if (pMaster && pMaster->isAlive() && IsSlaveInRangeOfBoss(pCreature, pMaster, pInfo->searchRange))
{ {
SetFollowing(pCreature, pMaster); SetFollowing(pCreature, pMaster);
return true; return true;

View file

@ -84,8 +84,9 @@ struct CreatureLinkingInfo
{ {
uint32 mapId; uint32 mapId;
uint32 masterId; uint32 masterId;
uint16 linkingFlag;
uint32 masterDBGuid; uint32 masterDBGuid;
uint16 linkingFlag;
uint16 searchRange;
}; };
/** /**
@ -118,7 +119,7 @@ class CreatureLinkingMgr
typedef std::multimap<uint32 /*slaveEntry*/, CreatureLinkingInfo> CreatureLinkingMap; typedef std::multimap<uint32 /*slaveEntry*/, CreatureLinkingInfo> CreatureLinkingMap;
typedef std::pair<CreatureLinkingMap::const_iterator, CreatureLinkingMap::const_iterator> CreatureLinkingMapBounds; typedef std::pair<CreatureLinkingMap::const_iterator, CreatureLinkingMap::const_iterator> CreatureLinkingMapBounds;
// Storage of Data: npc_entry_slave, (map, npc_entry_master, flag, master_db_guid[If Unique]) // Storage of Data: npc_entry_slave, (map, npc_entry_master, flag, master_db_guid[If Unique], search_range)
CreatureLinkingMap m_creatureLinkingMap; CreatureLinkingMap m_creatureLinkingMap;
// Lookup Storage for fast access: // Lookup Storage for fast access:
@ -158,22 +159,26 @@ class CreatureLinkingHolder
private: private:
typedef std::list<ObjectGuid> GuidList; typedef std::list<ObjectGuid> GuidList;
// Structure associated to a master // Structure associated to a master
struct FlagAndGuids struct InfoAndGuids
{ {
uint16 linkingFlag; uint16 linkingFlag;
uint16 searchRange;
GuidList linkedGuids; GuidList linkedGuids;
}; };
typedef std::multimap<uint32 /*masterEntry*/, FlagAndGuids> HolderMap; typedef std::multimap<uint32 /*masterEntry*/, InfoAndGuids> HolderMap;
typedef std::pair<HolderMap::iterator, HolderMap::iterator> HolderMapBounds; typedef std::pair<HolderMap::iterator, HolderMap::iterator> HolderMapBounds;
typedef UNORDERED_MAP<uint32 /*Entry*/, ObjectGuid> BossGuidMap; typedef std::multimap<uint32 /*Entry*/, ObjectGuid> BossGuidMap;
typedef std::pair<BossGuidMap::iterator, BossGuidMap::iterator> BossGuidMapBounds;
// Helper function, to process a slave list // Helper function, to process a slave list
void ProcessSlaveGuidList(CreatureLinkingEvent eventType, Creature* pSource, uint32 flag, GuidList& slaveGuidList, Unit* pEnemy); void ProcessSlaveGuidList(CreatureLinkingEvent eventType, Creature* pSource, uint32 flag, uint16 searchRange, GuidList& slaveGuidList, Unit* pEnemy);
// Helper function, to process a single slave // Helper function, to process a single slave
void ProcessSlave(CreatureLinkingEvent eventType, Creature* pSource, uint32 flag, Creature* pSlave, Unit* pEnemy); void ProcessSlave(CreatureLinkingEvent eventType, Creature* pSource, uint32 flag, Creature* pSlave, Unit* pEnemy);
// Helper function to set following // Helper function to set following
void SetFollowing(Creature* pWho, Creature* pWhom); void SetFollowing(Creature* pWho, Creature* pWhom);
// Helper function to return if a slave is in range of a boss
bool IsSlaveInRangeOfBoss(Creature* pSlave, Creature* pBoss, uint16 searchRange);
// Storage of Data (boss, flag) GuidList for action triggering // Storage of Data (boss, flag) GuidList for action triggering
HolderMap m_holderMap; HolderMap m_holderMap;

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__ #ifndef __REVISION_NR_H__
#define __REVISION_NR_H__ #define __REVISION_NR_H__
#define REVISION_NR "11967" #define REVISION_NR "11968"
#endif // __REVISION_NR_H__ #endif // __REVISION_NR_H__

View file

@ -1,6 +1,6 @@
#ifndef __REVISION_SQL_H__ #ifndef __REVISION_SQL_H__
#define __REVISION_SQL_H__ #define __REVISION_SQL_H__
#define REVISION_DB_CHARACTERS "required_11785_02_characters_instance" #define REVISION_DB_CHARACTERS "required_11785_02_characters_instance"
#define REVISION_DB_MANGOS "required_11964_01_mangos_conditions" #define REVISION_DB_MANGOS "required_11968_01_mangos_creature_linking_template"
#define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version" #define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version"
#endif // __REVISION_SQL_H__ #endif // __REVISION_SQL_H__