diff options
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() |