aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/2706_world_SD2_scripts.sql6
-rw-r--r--sql/updates/TC1_1362_world_creature_linked_respawn.sql10
-rw-r--r--sql/updates/TC1_world_1371_script.sql1
-rw-r--r--src/bindings/scripts/include/sc_creature.h2
-rw-r--r--src/bindings/scripts/scripts/zone/black_temple/boss_reliquary_of_souls.cpp10
-rw-r--r--src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_anetheron.cpp23
-rw-r--r--src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_azgalor.cpp22
-rw-r--r--src/game/Chat.cpp2
-rw-r--r--src/game/Chat.h2
-rw-r--r--src/game/Creature.cpp116
-rw-r--r--src/game/Creature.h4
-rw-r--r--src/game/CreatureAI.cpp24
-rw-r--r--src/game/CreatureAI.h3
-rw-r--r--src/game/Language.h4
-rw-r--r--src/game/Level1.cpp1
-rw-r--r--src/game/Level2.cpp37
-rw-r--r--src/game/Level3.cpp11
-rw-r--r--src/game/Map.h2
-rw-r--r--src/game/ObjectMgr.cpp91
-rw-r--r--src/game/ObjectMgr.h11
-rw-r--r--src/game/Spell.cpp9
-rw-r--r--src/game/SpellAuras.cpp11
-rw-r--r--src/game/SpellAuras.h2
-rw-r--r--src/game/World.cpp3
24 files changed, 329 insertions, 78 deletions
diff --git a/sql/updates/2706_world_SD2_scripts.sql b/sql/updates/2706_world_SD2_scripts.sql
index cd9c4ea3b19..71f97997461 100644
--- a/sql/updates/2706_world_SD2_scripts.sql
+++ b/sql/updates/2706_world_SD2_scripts.sql
@@ -1,4 +1,4 @@
-UPDATE creature_template SET ScriptName='npc_kingdom_of_dalaran_quests' WHERE entry IN (29169,23729,26673,27158,29158,29161,26471,29155,29159,29160,29162);
-DELETE FROM spell_target_position WHERE id=53360;
-INSERT INTO spell_target_position VALUES
+UPDATE creature_template SET ScriptName='npc_kingdom_of_dalaran_quests' WHERE entry IN (29169,23729,26673,27158,29158,29161,26471,29155,29159,29160,29162);
+DELETE FROM spell_target_position WHERE id=53360;
+INSERT INTO spell_target_position VALUES
(53360, 571, 5807.829, 587.960, 660.939, 1.663); \ No newline at end of file
diff --git a/sql/updates/TC1_1362_world_creature_linked_respawn.sql b/sql/updates/TC1_1362_world_creature_linked_respawn.sql
new file mode 100644
index 00000000000..a6d786871d6
--- /dev/null
+++ b/sql/updates/TC1_1362_world_creature_linked_respawn.sql
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS `creature_linked_respawn`;
+CREATE TABLE `creature_linked_respawn` (
+ `guid` int(10) unsigned NOT NULL COMMENT 'dependent creature',
+ `linkedGuid` int(10) unsigned NOT NULL COMMENT 'master creature',
+ PRIMARY KEY (`guid`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Creature Respawn Link System';
+
+DELETE FROM `trinity_string` WHERE `entry` = '750';
+INSERT INTO `trinity_string` (`entry`, `content_default`, `content_loc1`, `content_loc2`, `content_loc3`, `content_loc4`, `content_loc5`, `content_loc6`, `content_loc7`, `content_loc8`) VALUES
+('750', 'linkGUID: %u, Entry: %u (%s)', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
diff --git a/sql/updates/TC1_world_1371_script.sql b/sql/updates/TC1_world_1371_script.sql
new file mode 100644
index 00000000000..39dab9882d0
--- /dev/null
+++ b/sql/updates/TC1_world_1371_script.sql
@@ -0,0 +1 @@
+UPDATE `creature_template` SET `ScriptName` = 'mob_unkor_the_ruthless' WHERE `entry` = 18262;
diff --git a/src/bindings/scripts/include/sc_creature.h b/src/bindings/scripts/include/sc_creature.h
index 61a82bb6dd5..03194e9a3a9 100644
--- a/src/bindings/scripts/include/sc_creature.h
+++ b/src/bindings/scripts/include/sc_creature.h
@@ -108,7 +108,7 @@ struct TRINITY_DLL_DECL ScriptedAI : public CreatureAI
//*************
//Called at creature reset either by death or evade
- virtual void Reset() = 0;
+ void Reset() {}
//Called at creature aggro either by MoveInLOS or Attack Start
virtual void Aggro(Unit*) = 0;
diff --git a/src/bindings/scripts/scripts/zone/black_temple/boss_reliquary_of_souls.cpp b/src/bindings/scripts/scripts/zone/black_temple/boss_reliquary_of_souls.cpp
index df58c705c17..353a16fe0e7 100644
--- a/src/bindings/scripts/scripts/zone/black_temple/boss_reliquary_of_souls.cpp
+++ b/src/bindings/scripts/scripts/zone/black_temple/boss_reliquary_of_souls.cpp
@@ -79,6 +79,7 @@ EndScriptData */
#define ENSLAVED_SOUL_PASSIVE 41535
#define SPELL_SOUL_RELEASE 41542
+#define SPELL_SUBMERGE 37550 //dropout 'head'
#define CREATURE_ENSLAVED_SOUL 23469
#define NUMBER_ENSLAVED_SOUL 8
@@ -153,6 +154,7 @@ struct TRINITY_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
+ m_creature->RemoveAurasDueToSpell(SPELL_SUBMERGE);
}
void Aggro(Unit* who)
@@ -231,13 +233,14 @@ struct TRINITY_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI
break;
case 1:
Timer = 2800;
- m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_SUBMERGE); // Release the cube
+ //m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_SUBMERGE); // Release the cube
+ DoCast(m_creature,SPELL_SUBMERGE);
break;
case 2:
Timer = 5000;
if(Creature* Summon = DoSpawnCreature(23417+Phase, 0, 0, 0, 0, TEMPSUMMON_DEAD_DESPAWN, 0))
{
- m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_SUBMERGED); // Ribs: open
+ //m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_SUBMERGED); // Ribs: open
Summon->AI()->AttackStart(SelectUnit(SELECT_TARGET_TOPAGGRO, 0));
EssenceGUID = Summon->GetGUID();
}else EnterEvadeMode();
@@ -262,7 +265,8 @@ struct TRINITY_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI
case 4:
Timer = 1500;
if(Essence->IsWithinDistInMap(m_creature, 10))
- Essence->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_SUBMERGE); //rotate and disappear
+ m_creature->RemoveAurasDueToSpell(SPELL_SUBMERGE);
+ //Essence->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_SUBMERGE); //rotate and disappear
else
return;
break;
diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_anetheron.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_anetheron.cpp
index c5f7b402df6..cae3a17ff02 100644
--- a/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_anetheron.cpp
+++ b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_anetheron.cpp
@@ -225,15 +225,21 @@ struct TRINITY_DLL_DECL mob_towering_infernalAI : public ScriptedAI
{
mob_towering_infernalAI(Creature *c) : ScriptedAI(c)
{
- Reset();
+ pInstance = ((ScriptedInstance*)c->GetInstanceData());
+ if(pInstance)
+ AnetheronGUID = pInstance->GetData64(DATA_ANETHERON);
}
uint32 ImmolationTimer;
+ uint32 CheckTimer;
+ uint64 AnetheronGUID;
+ ScriptedInstance* pInstance;
void Reset()
{
DoCast(m_creature, SPELL_INFERNO_EFFECT);
ImmolationTimer = 5000;
+ CheckTimer = 5000;
}
void Aggro(Unit *who)
@@ -262,6 +268,21 @@ struct TRINITY_DLL_DECL mob_towering_infernalAI : public ScriptedAI
void UpdateAI(const uint32 diff)
{
+ if(CheckTimer < diff)
+ {
+ if(AnetheronGUID)
+ {
+ Creature* boss = Unit::GetCreature((*m_creature),AnetheronGUID);
+ if(!boss || (boss && boss->isDead()))
+ {
+ m_creature->setDeathState(JUST_DIED);
+ m_creature->RemoveCorpse();
+ return;
+ }
+ }
+ CheckTimer = 5000;
+ }else CheckTimer -= diff;
+
//Return since we have no target
if (!UpdateVictim())
return;
diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_azgalor.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_azgalor.cpp
index fac53fbda02..9bdee9887e5 100644
--- a/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_azgalor.cpp
+++ b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_azgalor.cpp
@@ -188,17 +188,22 @@ struct TRINITY_DLL_DECL mob_lesser_doomguardAI : public hyjal_trashAI
mob_lesser_doomguardAI(Creature *c) : hyjal_trashAI(c)
{
pInstance = ((ScriptedInstance*)c->GetInstanceData());
- Reset();
+ if(pInstance)
+ AzgalorGUID = pInstance->GetData64(DATA_AZGALOR);
}
uint32 CrippleTimer;
uint32 WarstompTimer;
+ uint32 CheckTimer;
+ uint64 AzgalorGUID;
+ ScriptedInstance* pInstance;
void Reset()
{
CrippleTimer = 50000;
WarstompTimer = 10000;
DoCast(m_creature, SPELL_THRASH);
+ CheckTimer = 5000;
}
void Aggro(Unit *who)
@@ -231,6 +236,21 @@ struct TRINITY_DLL_DECL mob_lesser_doomguardAI : public hyjal_trashAI
void UpdateAI(const uint32 diff)
{
+ if(CheckTimer < diff)
+ {
+ if(AzgalorGUID)
+ {
+ Creature* boss = Unit::GetCreature((*m_creature),AzgalorGUID);
+ if(!boss || (boss && boss->isDead()))
+ {
+ m_creature->setDeathState(JUST_DIED);
+ m_creature->RemoveCorpse();
+ return;
+ }
+ }
+ CheckTimer = 5000;
+ }else CheckTimer -= diff;
+
//Return since we have no target
if (!UpdateVictim() )
return;
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp
index 402fdd048c7..1f66482ac9f 100644
--- a/src/game/Chat.cpp
+++ b/src/game/Chat.cpp
@@ -408,6 +408,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 },
@@ -518,6 +519,7 @@ ChatCommand * ChatHandler::getCommandTable()
{
{ "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL },
{ "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCommand, "", NULL },
+ { "setlink", SEC_MODERATOR, false, &ChatHandler::HandleNpcSetLinkCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
diff --git a/src/game/Chat.h b/src/game/Chat.h
index d516d44c6a1..b40dd49a2a9 100644
--- a/src/game/Chat.h
+++ b/src/game/Chat.h
@@ -279,6 +279,7 @@ class ChatHandler
bool HandleNpcWhisperCommand(const char* args);
bool HandleNpcYellCommand(const char* args);
bool HandleNpcAddFormationCommand(const char* args);
+ bool HandleNpcSetLinkCommand(const char* args);
//TODO: NpcCommands that needs to be fixed :
bool HandleNpcAddWeaponCommand(const char* args);
@@ -312,6 +313,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 71631e4bdbc..9650c537751 100644
--- a/src/game/Creature.cpp
+++ b/src/game/Creature.cpp
@@ -404,36 +404,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 = (time(NULL)>GetLinkedCreatureRespawnTime()? time(NULL):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();
-
- uint16 poolid = poolhandler.IsPartOfAPool(GetGUIDLow(), GetTypeId());
- if (poolid)
- poolhandler.UpdatePool(poolid, GetGUIDLow(), GetTypeId());
- else
- GetMap()->Add(this);
}
break;
}
@@ -618,7 +603,7 @@ bool Creature::AIM_Initialize(CreatureAI* ai)
i_AI = ai ? ai : FactorySelector::selectAI(this);
if(oldAI) delete oldAI;
IsAIEnabled = true;
- i_AI->Reset();
+ i_AI->InitializeAI();
return true;
}
@@ -1444,7 +1429,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())
@@ -1454,11 +1439,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)
@@ -1728,7 +1708,37 @@ 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();
+
+ uint16 poolid = poolhandler.IsPartOfAPool(GetGUIDLow(), GetTypeId());
+ if (poolid)
+ poolhandler.UpdatePool(poolid, GetGUIDLow(), GetTypeId());
+ else
+ GetMap()->Add(this);
}
}
@@ -2349,3 +2359,37 @@ 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 0f12401bba8..07bed9af13c 100644
--- a/src/game/Creature.h
+++ b/src/game/Creature.h
@@ -645,6 +645,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/CreatureAI.cpp b/src/game/CreatureAI.cpp
index fdd145737ab..59df2e866f2 100644
--- a/src/game/CreatureAI.cpp
+++ b/src/game/CreatureAI.cpp
@@ -23,6 +23,7 @@
#include "Player.h"
#include "Pet.h"
#include "SpellAuras.h"
+#include "World.h"
void UnitAI::AttackStart(Unit *victim)
{
@@ -104,22 +105,15 @@ void CreatureAI::DoZoneInCombat(Unit* pUnit)
void CreatureAI::MoveInLineOfSight(Unit *who)
{
- if(!me->getVictim() && me->canStartAttack(who))
- AttackStart(who);
+ if(me->getVictim())
+ return;
- if (who->isInCombat() && who->getVictim() && me->GetCreatureType()==CREATURE_TYPE_HUMANOID && me->IsFriendlyTo(who))
- {
- if (me->GetDistanceZ(who) <= 2 && me->IsWithinLOSInMap(who))
- {
- float attackRadius = (me->GetAttackDistance(who) *0.5);
- if (me->IsWithinDistInMap(who, attackRadius))
- {
- Unit* target = NULL;
- target = who->getVictim();
- AttackStart(target);
- }
- }
- }
+ if(me->canStartAttack(who))
+ AttackStart(who);
+ else if(who->getVictim() && me->IsFriendlyTo(who)
+ && me->IsWithinDistInMap(who, sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS))
+ && me->canAttack(who->getVictim()))
+ AttackStart(who->getVictim());
}
bool CreatureAI::UpdateVictim()
diff --git a/src/game/CreatureAI.h b/src/game/CreatureAI.h
index 6d7026a9766..c968f07532a 100644
--- a/src/game/CreatureAI.h
+++ b/src/game/CreatureAI.h
@@ -79,7 +79,8 @@ class TRINITY_DLL_SPEC UnitAI
virtual void AttackStart(Unit *);
virtual void UpdateAI(const uint32 diff) = 0;
- // Initialize
+ virtual void InitializeAI() { Reset(); }
+
virtual void Reset() {};
// Called when unit is charmed
diff --git a/src/game/Language.h b/src/game/Language.h
index 28c0a81c879..9c85571c17e 100644
--- a/src/game/Language.h
+++ b/src/game/Language.h
@@ -694,7 +694,9 @@ enum TrinityStrings
// LANG_BG_EY_START_TWO_MINUTES = 755, - defined above
// Room for batleground/arena strings 756-799 not used
- // Room for BG/ARENA 750-769 not used
+ // Room for BG/ARENA 751-769 not used
+
+ LANG_NPCINFO_LINKGUID = 750,
LANG_ARENA_TESTING = 785,
LANG_AUTO_ANN = 786,
diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp
index 452020e667b..51829e504f0 100644
--- a/src/game/Level1.cpp
+++ b/src/game/Level1.cpp
@@ -282,6 +282,7 @@ std::string ChatHandler::PGetParseString(int32 entry, ...)
va_end(ap);
return (std::string)str;
}
+
bool ChatHandler::HandleGMTicketListCommand(const char* args)
{
SendSysMessage(LANG_COMMAND_TICKETSHOWLIST);
diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp
index 116fea3ac46..9aa1fec4778 100644
--- a/src/game/Level2.cpp
+++ b/src/game/Level2.cpp
@@ -42,6 +42,7 @@
#include <fstream>
#include <map>
#include "GlobalEvents.h"
+#include "TicketMgr.h"
#include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand
#include "CreatureGroups.h"
@@ -160,7 +161,7 @@ bool ChatHandler::HandleUnmuteCommand(const char* args)
bool ChatHandler::HandleGoTicketCommand(const char * args)
{
- if(!*args)
+ if(!*args)
return false;
char *cstrticket_id = strtok((char*)args, " ");
@@ -4640,3 +4641,37 @@ 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;
+}
diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp
index 8122166ef31..844bf9b5f68 100644
--- a/src/game/Level3.cpp
+++ b/src/game/Level3.cpp
@@ -714,6 +714,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`)" );
@@ -4300,6 +4308,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/Map.h b/src/game/Map.h
index 3d533da1675..fe29f448d27 100644
--- a/src/game/Map.h
+++ b/src/game/Map.h
@@ -479,6 +479,8 @@ class TRINITY_DLL_SPEC Map : public GridRefManager<NGridType>, public Trinity::O
typedef std::set<WorldObject*> ActiveNonPlayers;
ActiveNonPlayers m_activeNonPlayers;
ActiveNonPlayers::iterator m_activeNonPlayersIter;
+
+
private:
typedef GridReadGuard ReadGuard;
typedef GridWriteGuard WriteGuard;
diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp
index 7096fa03dfa..b9b7c0a489d 100644
--- a/src/game/ObjectMgr.cpp
+++ b/src/game/ObjectMgr.cpp
@@ -940,6 +940,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;
@@ -1246,9 +1334,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 56e1cbc2366..3c52eaa2546 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;
@@ -528,6 +529,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();
@@ -635,6 +639,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);
@@ -897,6 +907,7 @@ class ObjectMgr
MapObjectGuids mMapObjectGuids;
CreatureDataMap mCreatureDataMap;
+ CreatureLinkedRespawnMap mCreatureLinkedRespawnMap;
CreatureLocaleMap mCreatureLocaleMap;
GameObjectDataMap mGameObjectDataMap;
GameObjectLocaleMap mGameObjectLocaleMap;
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp
index d9aa5ffc2a1..ec07bde8f72 100644
--- a/src/game/Spell.cpp
+++ b/src/game/Spell.cpp
@@ -2046,14 +2046,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
if(m_spellInfo->Id == 5246) //Intimidating Shout
TagUnitMap.remove(m_targets.getUnitTarget());
- // remove random units from the map
- std::list<Unit*>::iterator itr;
- while(TagUnitMap.size() > unMaxTargets)
- {
- itr = TagUnitMap.begin();
- advance(itr, urand(0, TagUnitMap.size() - 1));
- TagUnitMap.erase(itr);
- }
+ Trinity::RandomResizeList(TagUnitMap, unMaxTargets);
/*if(m_spellInfo->Id==57669) //Replenishment (special target selection)
{
diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp
index b9f440188d2..9a682ed22b0 100644
--- a/src/game/SpellAuras.cpp
+++ b/src/game/SpellAuras.cpp
@@ -454,7 +454,7 @@ Aura::~Aura()
AuraEffect::AuraEffect(Aura * parentAura, uint8 effIndex, int32 * currentBasePoints , Unit * caster, Item* castItem) :
m_parentAura(parentAura), m_spellmod(NULL), m_periodicTimer(0), m_isPeriodic(false), m_isAreaAura(false), m_isPersistent(false),
-m_target(parentAura->GetTarget())
+m_target(parentAura->GetTarget()), m_tickNumber(0)
{
m_spellProto = parentAura->GetSpellProto();
m_effIndex = effIndex;
@@ -696,6 +696,8 @@ void AuraEffect::Update(uint32 diff)
m_periodicTimer -= diff;
if(m_periodicTimer <= 0) // tick also at m_periodicTimer==0 to prevent lost last tick in case max m_duration == (max m_periodicTimer)*N
{
+ ++m_tickNumber;
+
// update before applying (aura can be removed in TriggerSpell or PeriodicTick calls)
m_periodicTimer += m_amplitude;
@@ -5667,7 +5669,7 @@ void AuraEffect::PeriodicTick()
break;
}
}
- m_amount += 100;
+ m_amount = 100 * m_tickNumber;
}break;
default:
break;
@@ -5699,11 +5701,12 @@ void AuraEffect::PeriodicTick()
// Curse of Agony damage-per-tick calculation
if (GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags[0] & 0x400) && GetSpellProto()->SpellIconID==544)
{
+ uint32 totalTick = GetParentAura()->GetAuraMaxDuration() / m_amplitude;
// 1..4 ticks, 1/2 from normal tick damage
- if (GetParentAura()->GetAuraDuration()>=((GetParentAura()->GetAuraMaxDuration()-m_amplitude)*2/3))
+ if(m_tickNumber <= totalTick / 3)
pdamage = pdamage/2;
// 9..12 ticks, 3/2 from normal tick damage
- else if(GetParentAura()->GetAuraDuration()<((GetParentAura()->GetAuraMaxDuration()-m_amplitude)/3))
+ else if(m_tickNumber > totalTick * 2 / 3)
pdamage += (pdamage+1)/2; // +1 prevent 0.5 damage possible lost at 1..4 ticks
// 5..8 ticks have normal tick damage
}
diff --git a/src/game/SpellAuras.h b/src/game/SpellAuras.h
index bd8e9d00c3b..698df1f9a30 100644
--- a/src/game/SpellAuras.h
+++ b/src/game/SpellAuras.h
@@ -339,6 +339,7 @@ class TRINITY_DLL_SPEC AuraEffect
int32 GetAuraAmplitude(){return m_amplitude;}
virtual void Update(uint32 diff);
+ uint32 GetTickNumber() const { return m_tickNumber; }
bool IsAreaAura() const { return m_isAreaAura; }
bool IsPeriodic() const { return m_isPeriodic; }
bool IsPersistent() const { return m_isPersistent; }
@@ -367,6 +368,7 @@ class TRINITY_DLL_SPEC AuraEffect
Unit * const m_target;
SpellEntry const *m_spellProto;
+ uint32 m_tickNumber;
uint8 m_effIndex;
AuraType m_auraName;
diff --git a/src/game/World.cpp b/src/game/World.cpp
index d9a22a4a4a2..fe96d160a10 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -1254,6 +1254,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..." );
sLog.outString();
objmgr.LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures()