diff --git a/sql/mangos.sql b/sql/mangos.sql index 822ae85e8..eece58437 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -23,7 +23,7 @@ DROP TABLE IF EXISTS `db_version`; CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, - `required_7720_01_mangos_mangos_string` bit(1) default NULL + `required_7782_01_mangos_spell_proc_event` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- diff --git a/sql/updates/7776_01_mangos_npc_spellclick_spells.sql b/sql/updates/7776_01_mangos_npc_spellclick_spells.sql new file mode 100644 index 000000000..c8cd342a7 --- /dev/null +++ b/sql/updates/7776_01_mangos_npc_spellclick_spells.sql @@ -0,0 +1,8 @@ +ALTER TABLE db_version CHANGE COLUMN required_7720_01_mangos_mangos_string required_7776_01_mangos_npc_spellclick_spells bit; + +CREATE TABLE `npc_spellclick_spells` ( + `npc_entry` INT UNSIGNED NOT NULL COMMENT 'reference to creature_template', + `spell_id` INT UNSIGNED NOT NULL COMMENT 'spell which should be casted ', + `quest_id` INT UNSIGNED NOT NULL COMMENT 'reference to quest_template', + `cast_flags` TINYINT UNSIGNED NOT NULL COMMENT 'first bit defines caster: 1=player, 0=creature; second bit defines target, same mapping as caster bit' +) ENGINE = MYISAM DEFAULT CHARSET=utf8; diff --git a/sql/updates/7777_01_mangos_spell_proc_event.sql b/sql/updates/7777_01_mangos_spell_proc_event.sql new file mode 100644 index 000000000..e54db2854 --- /dev/null +++ b/sql/updates/7777_01_mangos_spell_proc_event.sql @@ -0,0 +1,6 @@ +ALTER TABLE db_version CHANGE COLUMN required_7776_01_mangos_npc_spellclick_spells required_7777_01_mangos_spell_proc_event bit; + +DELETE FROM spell_proc_event WHERE entry IN (30299,30301,30302); +INSERT INTO spell_proc_event VALUES (30299, 0x0000007E, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0); +INSERT INTO spell_proc_event VALUES (30301, 0x0000007E, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0); +INSERT INTO spell_proc_event VALUES (30302, 0x0000007E, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0); diff --git a/sql/updates/7782_01_mangos_spell_proc_event.sql b/sql/updates/7782_01_mangos_spell_proc_event.sql new file mode 100644 index 000000000..8efa5573b --- /dev/null +++ b/sql/updates/7782_01_mangos_spell_proc_event.sql @@ -0,0 +1,4 @@ +ALTER TABLE db_version CHANGE COLUMN required_7777_01_mangos_spell_proc_event required_7782_01_mangos_spell_proc_event bit; + +DELETE FROM spell_proc_event WHERE entry = 34074; +INSERT INTO spell_proc_event VALUES (34074, 0, 9, 522819, 8917121, 513, 0, 0, 0, 0, 0); \ No newline at end of file diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 302a634d1..1973ed5de 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -177,6 +177,9 @@ pkgdata_DATA = \ 7706_01_mangos_command.sql \ 7714_01_mangos_command.sql \ 7720_01_mangos_mangos_string.sql \ + 7776_01_mangos_npc_spellclick_spells.sql \ + 7777_01_mangos_spell_proc_event.sql \ + 7782_01_mangos_spell_proc_event.sql \ README ## Additional files to include when running 'make dist' @@ -334,4 +337,7 @@ EXTRA_DIST = \ 7706_01_mangos_command.sql \ 7714_01_mangos_command.sql \ 7720_01_mangos_mangos_string.sql \ + 7776_01_mangos_npc_spellclick_spells.sql \ + 7777_01_mangos_spell_proc_event.sql \ + 7782_01_mangos_spell_proc_event.sql \ README diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index 8d91103c0..12bca0ed5 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -563,6 +563,11 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // std. case: increment at 1 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: + case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: + case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: + case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: + case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; @@ -1126,6 +1131,21 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS)); break; + case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: + if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: + if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST); + break; // std case: not exist in DBC, not triggered in code as result case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: @@ -1148,26 +1168,18 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING: case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK: case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS: case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: - case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: - case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: - case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: case ACHIEVEMENT_CRITERIA_TYPE_TOTAL: break; // Not implemented yet :( } @@ -1234,14 +1246,15 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve } case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: return progress->counter >= achievementCriteria->reach_skill_level.skillLevel; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: - return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75); case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: return progress->counter >= 1; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount; + case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: + case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: + return progress->counter >= achievementCriteria->healing_done.count; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: return progress->counter >= achievementCriteria->complete_daily_quest.questCount; case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: @@ -1256,10 +1269,10 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve return progress->counter >= achievementCriteria->cast_spell.castCount; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: return progress->counter >= 1; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - return progress->counter >= achievementCriteria->loot_type.lootTypeCount; case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: return progress->counter >= achievementCriteria->own_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: + return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75); case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: return progress->counter >= achievementCriteria->use_item.itemCount; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: @@ -1277,11 +1290,12 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: return progress->counter >= achievementCriteria->roll_greed_on_loot.count; + case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: + return progress->counter >= achievementCriteria->hk_class.count; + case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: + return progress->counter >= achievementCriteria->hk_race.count; case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: return progress->counter >= achievementCriteria->do_emote.count; - case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: - case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: - return progress->counter >= achievementCriteria->healing_done.count; case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: return progress->counter >= achievementCriteria->equip_item.count; case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: @@ -1294,6 +1308,10 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: + return progress->counter >= achievementCriteria->win_duel.duelCount; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: + return progress->counter >= achievementCriteria->loot_type.lootTypeCount; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: return progress->counter >= achievementCriteria->learn_skill_line.spellCount; case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: @@ -1308,16 +1326,22 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: + case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: + case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR: + case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: + case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: + case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: return false; } return false; diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index bb4d08010..d48de4acb 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -420,6 +420,7 @@ ChatCommand * ChatHandler::getCommandTable() { "page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadPageTextsCommand, "", NULL }, { "pickpocketing_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesPickpocketingCommand,"",NULL}, { "points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadPointsOfInterestCommand, "",NULL}, + { "npc_spellclick_spells", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellClickSpellsCommand, "",NULL}, { "prospecting_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesProspectingCommand,"", NULL }, { "quest_mail_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesQuestMailCommand, "", NULL }, { "quest_end_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestEndScriptsCommand, "", NULL }, diff --git a/src/game/Chat.h b/src/game/Chat.h index 47d18974f..6e4a28261 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -345,6 +345,7 @@ class ChatHandler bool HandleReloadNpcVendorCommand(const char* args); bool HandleReloadPageTextsCommand(const char* args); bool HandleReloadPointsOfInterestCommand(const char* args); + bool HandleReloadSpellClickSpellsCommand(const char* args); bool HandleReloadQuestAreaTriggersCommand(const char* args); bool HandleReloadQuestEndScriptsCommand(const char* args); bool HandleReloadQuestStartScriptsCommand(const char* args); diff --git a/src/game/Group.cpp b/src/game/Group.cpp index 46863ce76..b2b495cb8 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -208,7 +208,7 @@ void Group::ConvertToRaid() // update quest related GO states (quest activity dependent from raid membership) for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr) if(Player* player = objmgr.GetPlayer(citr->guid)) - player->UpdateForQuestsGO(); + player->UpdateForQuestWorldObjects(); } bool Group::AddInvite(Player *player) @@ -302,7 +302,7 @@ bool Group::AddMember(const uint64 &guid, const char* name) // quest related GO state dependent from raid memebership if(isRaidGroup()) - player->UpdateForQuestsGO(); + player->UpdateForQuestWorldObjects(); } return true; @@ -319,7 +319,7 @@ uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method) { // quest related GO state dependent from raid membership if(isRaidGroup()) - player->UpdateForQuestsGO(); + player->UpdateForQuestWorldObjects(); WorldPacket data; @@ -400,7 +400,7 @@ void Group::Disband(bool hideDestroy) // quest related GO state dependent from raid membership if(isRaidGroup()) - player->UpdateForQuestsGO(); + player->UpdateForQuestWorldObjects(); if(!player->GetSession()) continue; diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 4942ea09a..04e1e08d2 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -102,6 +102,7 @@ bool ChatHandler::HandleReloadAllNpcCommand(const char* /*args*/) HandleReloadNpcTrainerCommand("a"); HandleReloadNpcVendorCommand("a"); HandleReloadPointsOfInterestCommand("a"); + HandleReloadSpellClickSpellsCommand("a"); return true; } @@ -429,6 +430,14 @@ bool ChatHandler::HandleReloadPointsOfInterestCommand(const char*) return true; } +bool ChatHandler::HandleReloadSpellClickSpellsCommand(const char*) +{ + sLog.outString( "Re-Loading `npc_spellclick_spells` Table!" ); + objmgr.LoadNPCSpellClickSpells(); + SendGlobalSysMessage("DB table `npc_spellclick_spells` reloaded."); + return true; +} + bool ChatHandler::HandleReloadReservedNameCommand(const char*) { sLog.outString( "Loading ReservedNames... (`reserved_name`)" ); diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp index 0a4c85f5d..95380e2e7 100644 --- a/src/game/MiscHandler.cpp +++ b/src/game/MiscHandler.cpp @@ -1564,21 +1564,6 @@ void WorldSession::HandleSetTaxiBenchmarkOpcode( WorldPacket & recv_data ) sLog.outDebug("Client used \"/timetest %d\" command", mode); } -void WorldSession::HandleSpellClick( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 8); - - uint64 guid; - recv_data >> guid; - - Vehicle *vehicle = ObjectAccessor::GetVehicle(guid); - - if(!vehicle) - return; - - _player->EnterVehicle(vehicle); -} - void WorldSession::HandleInspectAchievements( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data, 1); diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index 75e132b50..c93c1cccd 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -24,6 +24,7 @@ #include "Corpse.h" #include "Player.h" #include "Vehicle.h" +#include "SpellAuras.h" #include "MapManager.h" #include "Transports.h" #include "BattleGround.h" @@ -474,8 +475,8 @@ void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data) // using charm guid, because we don't have vehicle guid... if(Vehicle *vehicle = ObjectAccessor::GetVehicle(vehicleGUID)) { - _player->ExitVehicle(vehicle); - vehicle->Dismiss(); + // Aura::HandleAuraControlVehicle will call Player::ExitVehicle + vehicle->RemoveSpellsCausingAura(SPELL_AURA_CONTROL_VEHICLE); } } diff --git a/src/game/Object.cpp b/src/game/Object.cpp index 7b11731bd..3f9cb66d9 100644 --- a/src/game/Object.cpp +++ b/src/game/Object.cpp @@ -620,9 +620,16 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask { if( updateMask->GetBit( index ) ) { - // remove custom flag before send if( index == UNIT_NPC_FLAGS ) - *data << uint32(m_uint32Values[ index ] & ~UNIT_NPC_FLAG_GUARD); + { + // remove custom flag before sending + uint32 appendValue = m_uint32Values[ index ] & ~UNIT_NPC_FLAG_GUARD; + + if (GetTypeId() == TYPEID_UNIT && !target->canSeeSpellClickOn((Creature*)this)) + appendValue &= ~UNIT_NPC_FLAG_SPELLCLICK; + + *data << uint32(appendValue); + } // FIXME: Some values at server stored in float format but must be sent to client in uint32 format else if(index >= UNIT_FIELD_BASEATTACKTIME && index <= UNIT_FIELD_RANGEDATTACKTIME) { diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 137351b21..292acd88a 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -5927,6 +5927,76 @@ void ObjectMgr::LoadPointsOfInterest() sLog.outString(">> Loaded %u Points of Interest definitions", count); } +void ObjectMgr::LoadNPCSpellClickSpells() +{ + uint32 count = 0; + + mSpellClickInfoMap.clear(); + + QueryResult *result = WorldDatabase.Query("SELECT npc_entry, spell_id, quest_id, cast_flags FROM npc_spellclick_spells"); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 npc_entry = fields[0].GetUInt32(); + CreatureInfo const* cInfo = GetCreatureTemplate(npc_entry); + if (!cInfo) + { + sLog.outErrorDb("Table npc_spellclick_spells references unknown creature_template %u. Skipping entry.", npc_entry); + continue; + } + + uint32 spellid = fields[1].GetUInt32(); + SpellEntry const *spellinfo = sSpellStore.LookupEntry(spellid); + if (!spellinfo) + { + sLog.outErrorDb("Table npc_spellclick_spells references unknown spellid %u. Skipping entry.", spellid); + continue; + } + + uint32 quest = fields[2].GetUInt32(); + + // quest might be 0 to enable spellclick independent of any quest + if (quest) + { + if(mQuestTemplates.find(quest) == mQuestTemplates.end()) + { + sLog.outErrorDb("Table npc_spellclick_spells references unknown quest %u. Skipping entry.", spellid); + continue; + } + + } + + uint8 castFlags = fields[3].GetUInt8(); + SpellClickInfo info; + info.spellId = spellid; + info.questId = quest; + info.castFlags = castFlags; + mSpellClickInfoMap.insert(SpellClickInfoMap::value_type(npc_entry, info)); + ++count; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString(">> Loaded %u spellclick definitions", count); +} + void ObjectMgr::LoadWeatherZoneChances() { uint32 count = 0; diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index d2815c4b8..e28b5419e 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -95,6 +95,15 @@ extern ScriptMapMap sSpellScripts; extern ScriptMapMap sGameObjectScripts; extern ScriptMapMap sEventScripts; +struct SpellClickInfo +{ + uint32 spellId; + uint32 questId; + uint8 castFlags; +}; + +typedef std::multimap SpellClickInfoMap; + struct AreaTrigger { uint8 requiredLevel; @@ -533,6 +542,9 @@ class ObjectMgr void LoadReputationOnKill(); void LoadPointsOfInterest(); + SpellClickInfoMap mSpellClickInfoMap; + void LoadNPCSpellClickSpells(); + void LoadWeatherZoneChances(); void LoadGameTele(); diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 3e5253bdc..1fb45f80d 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -552,18 +552,26 @@ void Pet::Update(uint32 diff) } } - if(getPetType() != HUNTER_PET) - break; - - //regenerate Focus + //regenerate focus for hunter pets or energy for deathknight's ghoul if(m_regenTimer <= diff) { - RegenerateFocus(); + switch (getPowerType()) + { + case POWER_FOCUS: + case POWER_ENERGY: + Regenerate(getPowerType()); + break; + default: + break; + } m_regenTimer = 4000; } else m_regenTimer -= diff; + if(getPetType() != HUNTER_PET) + break; + if(m_happinessTimer <= diff) { LooseHappiness(); @@ -580,22 +588,41 @@ void Pet::Update(uint32 diff) Creature::Update(diff); } -void Pet::RegenerateFocus() +void Pet::Regenerate(Powers power) { - uint32 curValue = GetPower(POWER_FOCUS); - uint32 maxValue = GetMaxPower(POWER_FOCUS); + uint32 curValue = GetPower(power); + uint32 maxValue = GetMaxPower(power); if (curValue >= maxValue) return; - float addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS); + float addvalue = 0.0f; + switch (power) + { + case POWER_FOCUS: + { + // For hunter pets. + addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS); + break; + } + case POWER_ENERGY: + { + // For deathknight's ghoul. + addvalue = 20; + break; + } + default: + return; + } + + // Apply modifiers (if any). AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue == POWER_FOCUS) + if ((*i)->GetModifier()->m_miscvalue == power) addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f; - ModifyPower(POWER_FOCUS, (int32)addvalue); + ModifyPower(power, (int32)addvalue); } void Pet::LooseHappiness() diff --git a/src/game/Pet.h b/src/game/Pet.h index 255690e77..e3be2c4ed 100644 --- a/src/game/Pet.h +++ b/src/game/Pet.h @@ -152,7 +152,7 @@ class Pet : public Creature return m_autospells[pos]; } - void RegenerateFocus(); + void Regenerate(Powers power); void LooseHappiness(); HappinessState GetHappinessState(); void GivePetXP(uint32 xp); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 8d3fff91a..89be85dc0 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -5850,6 +5850,8 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor) // and those in a lifetime ApplyModUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 1, true); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL); + UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS, pVictim->getClass()); + UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_RACE, pVictim->getRace()); } else { @@ -6179,6 +6181,13 @@ void Player::DuelComplete(DuelCompleteType type) SendMessageToSet(&data,true); } + if (type == DUEL_WON) + { + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL, 1); + if (duel->opponent) + duel->opponent->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1); + } + // cool-down duel spell /*data.Initialize(SMSG_SPELL_COOLDOWN, 17); @@ -12240,7 +12249,7 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver ) CastSpell(this,itr->second->spellId,true); } - UpdateForQuestsGO(); + UpdateForQuestWorldObjects(); } void Player::CompleteQuest( uint32 quest_id ) @@ -12971,7 +12980,7 @@ void Player::SetQuestStatus( uint32 quest_id, QuestStatus status ) if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; } - UpdateForQuestsGO(); + UpdateForQuestWorldObjects(); } // not used in MaNGOS, but used in scripting code @@ -13092,7 +13101,7 @@ void Player::ItemAddedQuestCheck( uint32 entry, uint32 count ) } } } - UpdateForQuestsGO(); + UpdateForQuestWorldObjects(); } void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count ) @@ -13133,7 +13142,7 @@ void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count ) } } } - UpdateForQuestsGO(); + UpdateForQuestWorldObjects(); } void Player::KilledMonster( uint32 entry, uint64 guid ) @@ -16134,8 +16143,11 @@ bool Player::HasGuardianWithEntry(uint32 entry) // pet guid middle part is entry (and creature also) // and in guardian list must be guardians with same entry _always_ for(GuardianPetList::const_iterator itr = m_guardianPets.begin(); itr != m_guardianPets.end(); ++itr) - if(GUID_ENPART(*itr)==entry) - return true; + { + if(Pet* pet = ObjectAccessor::GetPet(*itr)) + if (pet->GetEntry() == entry) + return true; + } return false; } @@ -16673,11 +16685,31 @@ bool Player::ActivateTaxiPathTo(std::vector const& nodes, uint32 mount_i // starting node too far away (cheat?) TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(sourcenode); - if( !node || node->map_id != GetMapId() || - (node->x - GetPositionX())*(node->x - GetPositionX())+ - (node->y - GetPositionY())*(node->y - GetPositionY())+ - (node->z - GetPositionZ())*(node->z - GetPositionZ()) > - (2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE) ) + if (!node) + { + WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4); + data << uint32(ERR_TAXINOSUCHPATH); + GetSession()->SendPacket(&data); + return false; + } + + // check node starting pos data set case if provided + if (node->x != 0.0f || node->y != 0.0f || node->z != 0.0f) + { + if (node->map_id != GetMapId() || + (node->x - GetPositionX())*(node->x - GetPositionX())+ + (node->y - GetPositionY())*(node->y - GetPositionY())+ + (node->z - GetPositionZ())*(node->z - GetPositionZ()) > + (2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)) + { + WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4); + data << uint32(ERR_TAXITOOFARAWAY); + GetSession()->SendPacket(&data); + return false; + } + } + // node must have pos if not spell case (npc!=0) + else if(npc) { WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4); data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR); @@ -16743,10 +16775,8 @@ bool Player::ActivateTaxiPathTo(std::vector const& nodes, uint32 mount_i uint32 money = GetMoney(); - if(npc) - { + if (npc) totalcost = (uint32)ceil(totalcost*GetReputationPriceDiscount(npc)); - } if(money < totalcost) { @@ -16760,6 +16790,7 @@ bool Player::ActivateTaxiPathTo(std::vector const& nodes, uint32 mount_i //Checks and preparations done, DO FLIGHT ModifyMoney(-(int32)totalcost); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN, 1); // prevent stealth flight RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); @@ -18262,7 +18293,7 @@ bool Player::HasQuestForGO(int32 GOId) const return false; } -void Player::UpdateForQuestsGO() +void Player::UpdateForQuestWorldObjects() { if(m_clientGUIDs.empty()) return; @@ -18277,6 +18308,24 @@ void Player::UpdateForQuestsGO() if(obj) obj->BuildValuesUpdateBlockForPlayer(&udata,this); } + else if(IS_CREATURE_GUID(*itr) || IS_VEHICLE_GUID(*itr)) + { + Creature *obj = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr); + if(!obj) + continue; + // check if this unit requires quest specific flags + + SpellClickInfoMap const& map = objmgr.mSpellClickInfoMap; + for(SpellClickInfoMap::const_iterator itr = map.lower_bound(obj->GetEntry()); itr != map.upper_bound(obj->GetEntry()); ++itr) + { + if(itr->second.questId != 0) + { + obj->BuildCreateUpdateBlockForPlayer(&udata,this); + break; + } + } + + } } udata.BuildPacket(&packet); GetSession()->SendPacket(&packet); @@ -18308,6 +18357,8 @@ void Player::SummonIfPossible(bool agree) m_summon_expire = 0; + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS, 1); + TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z,GetOrientation()); } @@ -19141,8 +19192,8 @@ void Player::ExitVehicle(Vehicle *vehicle) data << uint32(0); GetSession()->SendPacket(&data); - // only for flyable vehicles? - CastSpell(this, 45472, true); // Parachute + // maybe called at dummy aura remove? + // CastSpell(this, 45472, true); // Parachute } bool Player::isTotalImmune() @@ -19787,6 +19838,17 @@ void Player::ResummonPetTemporaryUnSummonedIfAny() m_temporaryUnsummonedPetNumber = 0; } +bool Player::canSeeSpellClickOn(Creature const *c) const +{ + SpellClickInfoMap const& map = objmgr.mSpellClickInfoMap; + for(SpellClickInfoMap::const_iterator itr = map.lower_bound(c->GetEntry()); itr != map.upper_bound(c->GetEntry()); ++itr) + { + if(itr->second.questId == 0 || GetQuestStatus(itr->second.questId) == QUEST_STATUS_INCOMPLETE) + return true; + } + return false; +} + void Player::BuildPlayerTalentsInfoData(WorldPacket *data) { *data << uint32(GetFreeTalentPoints()); // unspentTalentPoints @@ -20072,6 +20134,7 @@ void Player::_SaveEquipmentSets() break; } } + return false; } void Player::DeleteEquipmentSet(uint64 setGuid) @@ -20084,4 +20147,3 @@ void Player::DeleteEquipmentSet(uint64 setGuid) break; } } -} diff --git a/src/game/Player.h b/src/game/Player.h index e21802479..d30c2be6e 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1208,7 +1208,7 @@ class MANGOS_DLL_SPEC Player : public Unit void ReputationChanged(FactionEntry const* factionEntry ); bool HasQuestForItem( uint32 itemid ) const; bool HasQuestForGO(int32 GOId) const; - void UpdateForQuestsGO(); + void UpdateForQuestWorldObjects(); bool CanShareQuest(uint32 quest_id) const; void SendQuestComplete( uint32 quest_id ); @@ -1289,6 +1289,7 @@ class MANGOS_DLL_SPEC Player : public Unit { SetUInt32Value (PLAYER_FIELD_COINAGE, value); MoneyChanged( value ); + UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED); } QuestStatusMap& getQuestStatusMap() { return mQuestStatus; }; @@ -2063,6 +2064,7 @@ class MANGOS_DLL_SPEC Player : public Unit void SetTitle(CharTitlesEntry const* title); bool isActiveObject() const { return true; } + bool canSeeSpellClickOn(Creature const* creature) const; protected: /*********************************************************/ diff --git a/src/game/QuestHandler.cpp b/src/game/QuestHandler.cpp index a49529f13..646f8658c 100644 --- a/src/game/QuestHandler.cpp +++ b/src/game/QuestHandler.cpp @@ -372,6 +372,8 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recv_data) } _player->SetQuestSlot(slot, 0); + + _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED, 1); } } @@ -616,7 +618,7 @@ void WorldSession::HandleQuestgiverStatusQueryMultipleOpcode(WorldPacket& /*recv uint8 questStatus = DIALOG_STATUS_NONE; uint8 defstatus = DIALOG_STATUS_NONE; - if(IS_CREATURE_GUID(*itr)) + if (IS_CREATURE_OR_PET_GUID(*itr)) { // need also pet quests case support Creature *questgiver = ObjectAccessor::GetCreatureOrPetOrVehicle(*GetPlayer(),*itr); diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index a35938dbc..c44dd6e64 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -43,6 +43,7 @@ #include "Util.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" +#include "Vehicle.h" #include "CellImpl.h" #define NULL_AURA_SLOT 0xFF @@ -3532,6 +3533,7 @@ void Aura::HandleAuraModStun(bool apply, bool Real) void Aura::HandleModStealth(bool apply, bool Real) { Unit* pTarget = m_target; + SpellEntry const* pSpellInfo = GetSpellProto(); if (apply) { @@ -3567,7 +3569,7 @@ void Aura::HandleModStealth(bool apply, bool Real) pTarget->CastCustomSpell(pTarget,31665,&bp,NULL,NULL,true); } // Overkill - else if ((*i)->GetId() == 58426 && GetSpellProto()->SpellFamilyFlags & 0x0000000000400000LL) + else if ((*i)->GetId() == 58426 && pSpellInfo->SpellFamilyFlags & 0x0000000000400000LL) { pTarget->RemoveAurasDueToSpell(58428); pTarget->CastSpell(pTarget, 58427, true); @@ -3607,7 +3609,7 @@ void Aura::HandleModStealth(bool apply, bool Real) if ((*i)->GetSpellProto()->SpellIconID == 2114) pTarget->CastSpell(pTarget,31666,true); // Overkill - else if ((*i)->GetId() == 58426 && GetSpellProto()->SpellFamilyFlags & 0x0000000000400000LL) + else if ((*i)->GetId() == 58426 && pSpellInfo->SpellFamilyFlags & 0x0000000000400000LL) pTarget->CastSpell(pTarget, 58428, true); } } @@ -3992,21 +3994,26 @@ void Aura::HandleModMechanicImmunity(bool apply, bool /*Real*/) if(GetId()==42292 || GetId()==59752) mechanic=IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; - if(apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) + // cache values in local vars for prevent access to possible deleted aura data + SpellEntry const* spellInfo = GetSpellProto(); + uint32 misc = m_modifier.m_miscvalue; + Unit* target = m_target; + + if(apply && spellInfo->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) { - Unit::AuraMap& Auras = m_target->GetAuras(); + Unit::AuraMap& Auras = target->GetAuras(); for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next) { next = iter; ++next; SpellEntry const *spell = iter->second->GetSpellProto(); if (!( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) && // spells unaffected by invulnerability - spell->Id != GetId()) + spell->Id != spellInfo->Id) { //check for mechanic mask if(GetSpellMechanicMask(spell, iter->second->GetEffIndex()) & mechanic) { - m_target->RemoveAurasDueToSpell(spell->Id); + target->RemoveAurasDueToSpell(spell->Id); if(Auras.empty()) break; else @@ -4016,19 +4023,19 @@ void Aura::HandleModMechanicImmunity(bool apply, bool /*Real*/) } } - m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,m_modifier.m_miscvalue,apply); + target->ApplySpellImmune(spellInfo->Id,IMMUNITY_MECHANIC,misc,apply); // Bestial Wrath - if ( GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->SpellIconID == 1680) + if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && spellInfo->SpellIconID == 1680) { // The Beast Within cast on owner if talent present - if ( Unit* owner = m_target->GetOwner() ) + if (Unit* owner = target->GetOwner()) { // Search talent - Unit::AuraList const& m_dummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i) + Unit::AuraList const& dummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator i = dummyAuras.begin(); i != dummyAuras.end(); ++i) { - if ( (*i)->GetSpellProto()->SpellIconID == 2229 ) + if ((*i)->GetSpellProto()->SpellIconID == 2229) { if (apply) owner->CastSpell(owner, 34471, true, 0, this); @@ -4041,21 +4048,21 @@ void Aura::HandleModMechanicImmunity(bool apply, bool /*Real*/) } // The Beast Within and Bestial Wrath - immunity - if(GetId() == 19574 || GetId() == 34471) + if (spellInfo->Id == 19574 || spellInfo->Id == 34471) { - if(apply) + if (apply) { - m_target->CastSpell(m_target,24395,true); - m_target->CastSpell(m_target,24396,true); - m_target->CastSpell(m_target,24397,true); - m_target->CastSpell(m_target,26592,true); + target->CastSpell(target,24395,true); + target->CastSpell(target,24396,true); + target->CastSpell(target,24397,true); + target->CastSpell(target,26592,true); } else { - m_target->RemoveAurasDueToSpell(24395); - m_target->RemoveAurasDueToSpell(24396); - m_target->RemoveAurasDueToSpell(24397); - m_target->RemoveAurasDueToSpell(26592); + target->RemoveAurasDueToSpell(24395); + target->RemoveAurasDueToSpell(24396); + target->RemoveAurasDueToSpell(24397); + target->RemoveAurasDueToSpell(26592); } } } @@ -6684,19 +6691,35 @@ void Aura::HandleArenaPreparation(bool apply, bool Real) m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION); } -void Aura::HandleAuraControlVehicle(bool /*apply*/, bool Real) +/** + * Such auras are applied from a caster(=player) to a vehicle. + * This has been verified using spell #49256 + */ +void Aura::HandleAuraControlVehicle(bool apply, bool Real) { if(!Real) return; - if(m_target->GetTypeId() != TYPEID_PLAYER) + Unit *player = GetCaster(); + Vehicle *vehicle = dynamic_cast(m_target); + if(!player || player->GetTypeId()!=TYPEID_PLAYER || !vehicle) return; - if(Pet *pet = m_target->GetPet()) - pet->Remove(PET_SAVE_AS_CURRENT); + if (apply) + { + if(Pet *pet = player->GetPet()) + pet->Remove(PET_SAVE_AS_CURRENT); + ((Player*)player)->EnterVehicle(vehicle); + } + else + { + SpellEntry const *spell = GetSpellProto(); - WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); - ((Player*)m_target)->GetSession()->SendPacket(&data); + // some SPELL_AURA_CONTROL_VEHICLE auras have a dummy effect on the player - remove them + player->RemoveAurasDueToSpell(spell->Id); + + ((Player*)player)->ExitVehicle(vehicle); + } } void Aura::HandleAuraConvertRune(bool apply, bool Real) diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index c4a04a5ef..f7b4d51cd 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -1111,6 +1111,19 @@ void Spell::EffectDummy(uint32 i) m_caster->CastSpell(m_caster, 30452, true, NULL); return; } + case 51592: // Pickup Primordial Hatchling + { + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) + return; + + Creature* creatureTarget = (Creature*)unitTarget; + + creatureTarget->setDeathState(JUST_DIED); + creatureTarget->RemoveCorpse(); + creatureTarget->SetHealth(0); // just for nice GM-mode view + return; + + } case 52308: { switch(i) diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 2601497cb..4a31f0dca 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -489,3 +489,29 @@ void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ ) _player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0); } } + +void WorldSession::HandleSpellClick( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + uint64 guid; + recv_data >> guid; + + Creature *unit = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); + + if(!unit) + return; + + SpellClickInfoMap const& map = objmgr.mSpellClickInfoMap; + for(SpellClickInfoMap::const_iterator itr = map.lower_bound(unit->GetEntry()); itr != map.upper_bound(unit->GetEntry()); ++itr) + { + if(itr->second.questId == 0 || _player->GetQuestStatus(itr->second.questId) == QUEST_STATUS_INCOMPLETE) + { + Unit *caster = (itr->second.castFlags & 0x1) ? (Unit*)_player : (Unit*)unit; + Unit *target = (itr->second.castFlags & 0x2) ? (Unit*)_player : (Unit*)unit; + + caster->CastSpell(target, itr->second.spellId, true); + } + } +} + diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 3413fba83..7c72d2ec2 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -1175,6 +1175,11 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons (spellInfo_2->Id == 52950 && spellInfo_1->Id == 52707) ) return false; + // Regular and Night Elf Ghost + if( (spellInfo_1->Id == 8326 && spellInfo_2->Id == 20584) || + (spellInfo_2->Id == 8326 && spellInfo_1->Id == 20584) ) + return false; + break; } case SPELLFAMILY_WARRIOR: diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 986f84690..e265c2f0b 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -3352,6 +3352,7 @@ bool Unit::AddAura(Aura *Aur) } SpellEntry const* aurSpellInfo = Aur->GetSpellProto(); + AuraType aurName = Aur->GetModifier()->m_auraname; spellEffectPair spair = spellEffectPair(Aur->GetId(), Aur->GetEffIndex()); AuraMap::iterator i = m_Auras.find( spair ); @@ -3379,7 +3380,7 @@ bool Unit::AddAura(Aura *Aur) } bool stop = false; - switch(aurSpellInfo->EffectApplyAuraName[Aur->GetEffIndex()]) + switch(aurName) { // DoT/HoT/etc case SPELL_AURA_PERIODIC_DAMAGE: // allow stack @@ -3455,13 +3456,13 @@ bool Unit::AddAura(Aura *Aur) // add aura, register in lists and arrays Aur->_AddAura(); m_Auras.insert(AuraMap::value_type(spellEffectPair(Aur->GetId(), Aur->GetEffIndex()), Aur)); - if (Aur->GetModifier()->m_auraname < TOTAL_AURAS) + if (aurName < TOTAL_AURAS) { - m_modAuras[Aur->GetModifier()->m_auraname].push_back(Aur); + m_modAuras[aurName].push_back(Aur); } Aur->ApplyModifier(true,true); - sLog.outDebug("Aura %u now is in use", Aur->GetModifier()->m_auraname); + sLog.outDebug("Aura %u now is in use", aurName); return true; } @@ -5323,6 +5324,16 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu } case SPELLFAMILY_HUNTER: { + // Aspect of the Viper + if ( dummySpell->SpellFamilyFlags & 0x4000000000000LL ) + { + uint32 maxmana = GetMaxPower(POWER_MANA); + basepoints0 = maxmana* GetAttackTime(RANGED_ATTACK)/1000.0f/100.0f; + + target = this; + triggered_spell_id = 34075; + break; + } // Thrill of the Hunt if ( dummySpell->SpellIconID == 2236 ) { @@ -6227,8 +6238,8 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell))) { case SPELL_SCHOOL_NORMAL: - case SPELL_SCHOOL_HOLY: return false; // ignore + case SPELL_SCHOOL_HOLY: trigger_spell_id = 54370; break; case SPELL_SCHOOL_FIRE: trigger_spell_id = 54371; break; case SPELL_SCHOOL_NATURE: trigger_spell_id = 54375; break; case SPELL_SCHOOL_FROST: trigger_spell_id = 54372; break; @@ -10614,6 +10625,7 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag break; } case SPELL_AURA_MANA_SHIELD: + case SPELL_AURA_OBS_MOD_MANA: case SPELL_AURA_DUMMY: { sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); diff --git a/src/game/World.cpp b/src/game/World.cpp index 156dd9015..e026ca271 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -1200,6 +1200,9 @@ void World::SetInitialWorldSettings() sLog.outString( ">>> Quests Relations loaded" ); sLog.outString(); + sLog.outString( "Loading UNIT_NPC_FLAG_SPELLCLICK Data..." ); + objmgr.LoadNPCSpellClickSpells(); + sLog.outString( "Loading SpellArea Data..." ); // must be after quest load spellmgr.LoadSpellAreas(); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 09af13931..c5e56799c 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "7768" + #define REVISION_NR "7785" #endif // __REVISION_NR_H__