aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/game/Chat.cpp2
-rw-r--r--src/game/Chat.h2
-rw-r--r--src/game/Creature.cpp105
-rw-r--r--src/game/Creature.h4
-rw-r--r--src/game/Language.h3
-rw-r--r--src/game/Level2.cpp33
-rw-r--r--src/game/Level3.cpp11
-rw-r--r--src/game/ObjectMgr.cpp91
-rw-r--r--src/game/ObjectMgr.h11
-rw-r--r--src/game/World.cpp3
10 files changed, 230 insertions, 35 deletions
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp
index 27e54bd7bde..9671a3f89ec 100644
--- a/src/game/Chat.cpp
+++ b/src/game/Chat.cpp
@@ -252,6 +252,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "event_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventScriptsCommand, "", NULL },
{ "command", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCommandCommand, "", NULL },
{ "creature_involvedrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCreatureQuestInvRelationsCommand,"",NULL },
+ { "creature_linked_respawn", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCreatureLinkedRespawnCommand, "", NULL },
{ "creature_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesCreatureCommand, "", NULL },
{ "creature_questrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCreatureQuestRelationsCommand, "", NULL },
//{ "db_script_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadDbScriptStringCommand, "", NULL },
@@ -443,6 +444,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "yell", SEC_MODERATOR, false, &ChatHandler::HandleNpcYellCommand, "", NULL },
{ "addtemp", SEC_GAMEMASTER, false, &ChatHandler::HandleTempAddSpwCommand, "", NULL },
{ "addformation", SEC_MODERATOR, false, &ChatHandler::HandleNpcAddFormationCommand, "", NULL },
+ { "setlink", SEC_MODERATOR, false, &ChatHandler::HandleNpcSetLinkCommand, "", NULL },
//{ TODO: fix or remove this commands
{ "name", SEC_GAMEMASTER, false, &ChatHandler::HandleNameCommand, "", NULL },
diff --git a/src/game/Chat.h b/src/game/Chat.h
index 8a1d11b4886..64f923dad95 100644
--- a/src/game/Chat.h
+++ b/src/game/Chat.h
@@ -205,6 +205,7 @@ class ChatHandler
bool HandleNpcWhisperCommand(const char* args);
bool HandleNpcYellCommand(const char* args);
bool HandleNpcAddFormationCommand(const char* args);
+ bool HandleNpcSetLinkCommand(const char* args);
bool HandleReloadCommand(const char* args);
bool HandleReloadAllCommand(const char* args);
@@ -227,6 +228,7 @@ class ChatHandler
bool HandleReloadCommandCommand(const char* args);
bool HandleReloadCreatureQuestRelationsCommand(const char* args);
bool HandleReloadCreatureQuestInvRelationsCommand(const char* args);
+ bool HandleReloadCreatureLinkedRespawnCommand(const char* args);
bool HandleReloadDbScriptStringCommand(const char* args);
bool HandleReloadGameGraveyardZoneCommand(const char* args);
bool HandleReloadGameObjectScriptsCommand(const char* args);
diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp
index 0681dcddd04..f8643543038 100644
--- a/src/game/Creature.cpp
+++ b/src/game/Creature.cpp
@@ -413,32 +413,21 @@ void Creature::Update(uint32 diff)
{
if( m_respawnTime <= time(NULL) )
{
- DEBUG_LOG("Respawning...");
- m_respawnTime = 0;
- lootForPickPocketed = false;
- lootForBody = false;
-
- if(m_originalEntry != GetEntry())
- UpdateEntry(m_originalEntry);
-
- CreatureInfo const *cinfo = GetCreatureInfo();
- SelectLevel(cinfo);
-
- if (m_isDeadByDefault)
+ if(!GetLinkedCreatureRespawnTime()) // Can respawn
+ Respawn();
+ else // the master is dead
{
- setDeathState(JUST_DIED);
- SetHealth(0);
- i_motionMaster.Clear();
- clearUnitState(UNIT_STAT_ALL_STATE);
- LoadCreaturesAddon(true);
+ if(uint32 targetGuid = objmgr.GetLinkedRespawnGuid(m_DBTableGuid))
+ {
+ if(targetGuid == m_DBTableGuid) // if linking self, never respawn (check delayed to next day)
+ SetRespawnTime(DAY);
+ else
+ m_respawnTime = GetLinkedCreatureRespawnTime()+urand(5,MINUTE); // else copy time from master and add a little
+ SaveRespawnTime(); // also save to DB immediately
+ }
+ else
+ Respawn();
}
- else
- setDeathState( JUST_ALIVED );
-
- //Call AI respawn virtual function
- AI()->JustRespawned();
-
- GetMap()->Add(this);
}
break;
}
@@ -1424,7 +1413,7 @@ bool Creature::LoadFromDB(uint32 guid, Map *map)
m_deathState = m_isDeadByDefault ? DEAD : ALIVE;
m_respawnTime = objmgr.GetCreatureRespawnTime(m_DBTableGuid,GetInstanceId());
- if(m_respawnTime > time(NULL)) // not ready to respawn
+ if(m_respawnTime) // respawn on Update
{
m_deathState = DEAD;
if(canFly())
@@ -1434,11 +1423,6 @@ bool Creature::LoadFromDB(uint32 guid, Map *map)
Relocate(data->posX,data->posY,tz);
}
}
- else if(m_respawnTime) // respawn time set but expired
- {
- m_respawnTime = 0;
- objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
- }
uint32 curhealth = data->curhealth;
if(curhealth)
@@ -1709,7 +1693,33 @@ void Creature::Respawn()
{
if (m_DBTableGuid)
objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
- m_respawnTime = time(NULL); // respawn at next tick
+
+ DEBUG_LOG("Respawning...");
+ m_respawnTime = 0;
+ lootForPickPocketed = false;
+ lootForBody = false;
+
+ if(m_originalEntry != GetEntry())
+ UpdateEntry(m_originalEntry);
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ SelectLevel(cinfo);
+
+ if (m_isDeadByDefault)
+ {
+ setDeathState(JUST_DIED);
+ SetHealth(0);
+ i_motionMaster.Clear();
+ clearUnitState(UNIT_STAT_ALL_STATE);
+ LoadCreaturesAddon(true);
+ }
+ else
+ setDeathState( JUST_ALIVED );
+
+ //Call AI respawn virtual function
+ AI()->JustRespawned();
+
+ GetMap()->Add(this);
}
}
@@ -2323,3 +2333,36 @@ const char* Creature::GetNameForLocaleIdx(int32 loc_idx) const
return GetName();
}
+const CreatureData* Creature::GetLinkedRespawnCreatureData() const
+{
+ if(!m_DBTableGuid) // only hard-spawned creatures from DB can have a linked master
+ return NULL;
+
+ if(uint32 targetGuid = objmgr.GetLinkedRespawnGuid(m_DBTableGuid))
+ return objmgr.GetCreatureData(targetGuid);
+
+ return NULL;
+}
+
+// returns master's remaining respawn time if any
+time_t Creature::GetLinkedCreatureRespawnTime() const
+{
+ if(!m_DBTableGuid) // only hard-spawned creatures from DB can have a linked master
+ return 0;
+
+ if(uint32 targetGuid = objmgr.GetLinkedRespawnGuid(m_DBTableGuid))
+ {
+ Map* targetMap = NULL;
+ if(const CreatureData* data = objmgr.GetCreatureData(targetGuid))
+ {
+ if(data->mapid == GetMapId()) // look up on the same map
+ targetMap = GetMap();
+ else // it shouldn't be instanceable map here
+ targetMap = MapManager::Instance().FindMap(data->mapid);
+ }
+ if(targetMap)
+ return objmgr.GetCreatureRespawnTime(targetGuid,targetMap->GetInstanceId());
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/src/game/Creature.h b/src/game/Creature.h
index 0930fe00701..078edb7d865 100644
--- a/src/game/Creature.h
+++ b/src/game/Creature.h
@@ -594,6 +594,10 @@ class TRINITY_DLL_SPEC Creature : public Unit
float GetRespawnRadius() const { return m_respawnradius; }
void SetRespawnRadius(float dist) { m_respawnradius = dist; }
+ // Linked Creature Respawning System
+ time_t GetLinkedCreatureRespawnTime() const;
+ const CreatureData* GetLinkedRespawnCreatureData() const;
+
uint32 m_groupLootTimer; // (msecs)timer used for group loot
uint64 lootingGroupLeaderGUID; // used to find group which is looting corpse
diff --git a/src/game/Language.h b/src/game/Language.h
index a1b1b3bf1b7..03c485a4b02 100644
--- a/src/game/Language.h
+++ b/src/game/Language.h
@@ -679,8 +679,9 @@ enum TrinityStrings
LANG_BG_AV_H_NEAR_LOSE = 747,
LANG_BG_AV_H_CAPTAIN_DEAD = 748,
LANG_BG_AV_A_CAPTAIN_DEAD = 749,
+ LANG_NPCINFO_LINKGUID = 750,
- // Room for BG/ARENA 750-769 not used
+ // Room for BG/ARENA 751-769 not used
LANG_ARENA_TESTING = 785,
diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp
index 26a701fc507..f79b8d9f04c 100644
--- a/src/game/Level2.cpp
+++ b/src/game/Level2.cpp
@@ -4054,3 +4054,36 @@ bool ChatHandler::HandleNpcAddFormationCommand(const char* args)
return true;
}
+bool ChatHandler::HandleNpcSetLinkCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 linkguid = (uint32) atoi((char*)args);
+
+ Creature* pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!pCreature->GetDBTableGUIDLow())
+ {
+ PSendSysMessage("Selected creature isn't in `creature` table", pCreature->GetGUIDLow());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!objmgr.SetCreatureLinkedRespawn(pCreature->GetDBTableGUIDLow(), linkguid))
+ {
+ PSendSysMessage("Selected creature can't link with guid '%u'", linkguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage("LinkGUID '%u' added to creature with DBTableGUID: '%u'", linkguid, pCreature->GetDBTableGUIDLow());
+ return true;
+} \ No newline at end of file
diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp
index 73d1fa1e5c7..128971f0153 100644
--- a/src/game/Level3.cpp
+++ b/src/game/Level3.cpp
@@ -695,6 +695,14 @@ bool ChatHandler::HandleReloadCreatureQuestRelationsCommand(const char*)
return true;
}
+bool ChatHandler::HandleReloadCreatureLinkedRespawnCommand(const char* args)
+{
+ sLog.outString( "Loading Linked Respawns... (`creature_linked_respawn`)" );
+ objmgr.LoadCreatureLinkedRespawn();
+ SendGlobalGMSysMessage("DB table `creature_linked_respawn` (creature linked respawns) reloaded.");
+ return true;
+}
+
bool ChatHandler::HandleReloadCreatureQuestInvRelationsCommand(const char*)
{
sLog.outString( "Loading Quests Relations... (`creature_involvedrelation`)" );
@@ -4244,6 +4252,9 @@ bool ChatHandler::HandleNpcInfoCommand(const char* /*args*/)
PSendSysMessage(LANG_NPCINFO_LOOT, cInfo->lootid,cInfo->pickpocketLootId,cInfo->SkinLootId);
PSendSysMessage(LANG_NPCINFO_DUNGEON_ID, target->GetInstanceId());
PSendSysMessage(LANG_NPCINFO_POSITION,float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ()));
+ if(const CreatureData* const linked = target->GetLinkedRespawnCreatureData())
+ if(CreatureInfo const *master = GetCreatureInfo(linked->id))
+ PSendSysMessage(LANG_NPCINFO_LINKGUID, objmgr.GetLinkedRespawnGuid(target->GetDBTableGUIDLow()), linked->id, master->Name);
if ((npcflags & UNIT_NPC_FLAG_VENDOR) )
{
diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp
index 9d4e3020cfc..408d46ceb62 100644
--- a/src/game/ObjectMgr.cpp
+++ b/src/game/ObjectMgr.cpp
@@ -829,6 +829,94 @@ void ObjectMgr::LoadCreatureModelInfo()
}
}
+bool ObjectMgr::CheckCreatureLinkedRespawn(uint32 guid, uint32 linkedGuid) const
+{
+ const CreatureData* const slave = GetCreatureData(guid);
+ const CreatureData* const master = GetCreatureData(linkedGuid);
+
+ if(!slave || !master) // they must have a corresponding entry in db
+ {
+ sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' which doesn't exist",guid,linkedGuid);
+ return false;
+ }
+
+ const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
+
+ if(master->mapid != slave->mapid // link only to same map
+ && (!map || map->Instanceable())) // or to unistanced world
+ {
+ sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' on an unpermitted map",guid,linkedGuid);
+ return false;
+ }
+
+ if(!(master->spawnMask & slave->spawnMask) // they must have a possibility to meet (normal/heroic difficulty)
+ && (!map || map->Instanceable()))
+ {
+ sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask",guid,linkedGuid);
+ return false;
+ }
+
+ return true;
+}
+
+void ObjectMgr::LoadCreatureLinkedRespawn()
+{
+ mCreatureLinkedRespawnMap.clear();
+ QueryResult *result = WorldDatabase.Query("SELECT guid, linkedGuid FROM creature_linked_respawn ORDER BY guid ASC");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outErrorDb(">> Loaded 0 linked respawns. DB table `creature_linked_respawn` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 guid = fields[0].GetUInt32();
+ uint32 linkedGuid = fields[1].GetUInt32();
+
+ if(CheckCreatureLinkedRespawn(guid,linkedGuid))
+ mCreatureLinkedRespawnMap[guid] = linkedGuid;
+
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u linked respawns", mCreatureLinkedRespawnMap.size() );
+}
+
+bool ObjectMgr::SetCreatureLinkedRespawn(uint32 guid, uint32 linkedGuid)
+{
+ if(!guid)
+ return false;
+
+ if(!linkedGuid) // we're removing the linking
+ {
+ mCreatureLinkedRespawnMap.erase(guid);
+ WorldDatabase.DirectPExecute("DELETE FROM `creature_linked_respawn` WHERE `guid` = '%u'",guid);
+ return true;
+ }
+
+ if(CheckCreatureLinkedRespawn(guid,linkedGuid)) // we add/change linking
+ {
+ mCreatureLinkedRespawnMap[guid] = linkedGuid;
+ WorldDatabase.DirectPExecute("REPLACE INTO `creature_linked_respawn`(`guid`,`linkedGuid`) VALUES ('%u','%u')",guid,linkedGuid);
+ return true;
+ }
+ return false;
+}
+
void ObjectMgr::LoadCreatures()
{
uint32 count = 0;
@@ -1089,9 +1177,6 @@ void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data
void ObjectMgr::LoadCreatureRespawnTimes()
{
- // remove outdated data
- WorldDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
-
uint32 count = 0;
QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM creature_respawn");
diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h
index 954bb6dbe61..8de6c3333b3 100644
--- a/src/game/ObjectMgr.h
+++ b/src/game/ObjectMgr.h
@@ -134,6 +134,7 @@ struct TrinityStringLocale
std::vector<std::string> Content; // 0 -> default, i -> i-1 locale index
};
+typedef std::map<uint32,uint32> CreatureLinkedRespawnMap;
typedef UNORDERED_MAP<uint32,CreatureData> CreatureDataMap;
typedef UNORDERED_MAP<uint32,GameObjectData> GameObjectDataMap;
typedef UNORDERED_MAP<uint32,CreatureLocale> CreatureLocaleMap;
@@ -513,6 +514,9 @@ class ObjectMgr
void LoadCreatureLocales();
void LoadCreatureTemplates();
void LoadCreatures();
+ void LoadCreatureLinkedRespawn();
+ bool CheckCreatureLinkedRespawn(uint32 guid, uint32 linkedGuid) const;
+ bool SetCreatureLinkedRespawn(uint32 guid, uint32 linkedGuid);
void LoadCreatureRespawnTimes();
void LoadCreatureAddons();
void LoadCreatureModelInfo();
@@ -618,6 +622,12 @@ class ObjectMgr
}
CreatureData& NewOrExistCreatureData(uint32 guid) { return mCreatureDataMap[guid]; }
void DeleteCreatureData(uint32 guid);
+ uint32 GetLinkedRespawnGuid(uint32 guid) const
+ {
+ CreatureLinkedRespawnMap::const_iterator itr = mCreatureLinkedRespawnMap.find(guid);
+ if(itr == mCreatureLinkedRespawnMap.end()) return 0;
+ return itr->second;
+ }
CreatureLocale const* GetCreatureLocale(uint32 entry) const
{
CreatureLocaleMap::const_iterator itr = mCreatureLocaleMap.find(entry);
@@ -874,6 +884,7 @@ class ObjectMgr
MapObjectGuids mMapObjectGuids;
CreatureDataMap mCreatureDataMap;
+ CreatureLinkedRespawnMap mCreatureLinkedRespawnMap;
CreatureLocaleMap mCreatureLocaleMap;
GameObjectDataMap mGameObjectDataMap;
GameObjectLocaleMap mGameObjectLocaleMap;
diff --git a/src/game/World.cpp b/src/game/World.cpp
index d138b15ac75..85a156c7206 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -1178,6 +1178,9 @@ void World::SetInitialWorldSettings()
sLog.outString( "Loading Creature Data..." );
objmgr.LoadCreatures();
+ sLog.outString( "Loading Creature Linked Respawn..." );
+ objmgr.LoadCreatureLinkedRespawn(); // must be after LoadCreatures()
+
sLog.outString( "Loading Creature Addon Data..." );
objmgr.LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures()