diff options
author | megamage <none@none> | 2009-05-05 16:56:15 -0500 |
---|---|---|
committer | megamage <none@none> | 2009-05-05 16:56:15 -0500 |
commit | e69d2cbed95b6cddd009f68dd80fd4614342d1fc (patch) | |
tree | 8dd0777d15adf284f2b0146e791ac4cd868b36e3 | |
parent | dcb2b5aa019c409b1b9b1061aa4dd24c8ecacb5d (diff) |
[7776] Completed implementation of CMSG_SPELLCLICK Author: arrai
For vehicles, you have to add the correct SPELL_AURA_CONTROL_VEHICLE spells to
npc_spellclick_spells, otherwise you won't be able to use them
--HG--
branch : trunk
-rw-r--r-- | sql/mangos.sql | 2 | ||||
-rw-r--r-- | sql/updates/3148_mangos_7776_01_world_npc_spellclick_spells.sql | 8 | ||||
-rw-r--r-- | src/game/Chat.cpp | 1 | ||||
-rw-r--r-- | src/game/Chat.h | 1 | ||||
-rw-r--r-- | src/game/CreatureAI.h | 2 | ||||
-rw-r--r-- | src/game/Group.cpp | 8 | ||||
-rw-r--r-- | src/game/Level3.cpp | 9 | ||||
-rw-r--r-- | src/game/MiscHandler.cpp | 23 | ||||
-rw-r--r-- | src/game/MovementHandler.cpp | 1 | ||||
-rw-r--r-- | src/game/Object.cpp | 11 | ||||
-rw-r--r-- | src/game/ObjectMgr.cpp | 70 | ||||
-rw-r--r-- | src/game/ObjectMgr.h | 12 | ||||
-rw-r--r-- | src/game/Player.cpp | 43 | ||||
-rw-r--r-- | src/game/Player.h | 4 | ||||
-rw-r--r-- | src/game/SpellAuras.cpp | 32 | ||||
-rw-r--r-- | src/game/SpellEffects.cpp | 13 | ||||
-rw-r--r-- | src/game/SpellHandler.cpp | 27 | ||||
-rw-r--r-- | src/game/World.cpp | 3 |
18 files changed, 228 insertions, 42 deletions
diff --git a/sql/mangos.sql b/sql/mangos.sql index 1fca13b12cc..6cfb3a8a82f 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_7776_01_mangos_npc_spellclick_spells` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- diff --git a/sql/updates/3148_mangos_7776_01_world_npc_spellclick_spells.sql b/sql/updates/3148_mangos_7776_01_world_npc_spellclick_spells.sql new file mode 100644 index 00000000000..c8cd342a7f7 --- /dev/null +++ b/sql/updates/3148_mangos_7776_01_world_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/src/game/Chat.cpp b/src/game/Chat.cpp index c2293fb8848..1bc6eb304e4 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -455,6 +455,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 6675094e942..cc7de6731a6 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -374,6 +374,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/CreatureAI.h b/src/game/CreatureAI.h index f99d9fe33cb..e3a5a674e68 100644 --- a/src/game/CreatureAI.h +++ b/src/game/CreatureAI.h @@ -242,7 +242,7 @@ class TRINITY_DLL_SPEC CreatureAI : public UnitAI void OnCharmed(bool apply); - virtual void SpellClick(Player *player) {} + //virtual void SpellClick(Player *player) {} // Called at reaching home after evade virtual void JustReachedHome() {} diff --git a/src/game/Group.cpp b/src/game/Group.cpp index 601440c0466..02fc72f2022 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -210,7 +210,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) @@ -304,7 +304,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; @@ -323,7 +323,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; @@ -404,7 +404,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 a7024dd0775..474a79c5680 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -576,6 +576,7 @@ bool ChatHandler::HandleReloadAllNpcCommand(const char* /*args*/) HandleReloadNpcTrainerCommand("a"); HandleReloadNpcVendorCommand("a"); HandleReloadPointsOfInterestCommand("a"); + HandleReloadSpellClickSpellsCommand("a"); return true; } @@ -922,6 +923,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 e41e3211344..a75141e2937 100644 --- a/src/game/MiscHandler.cpp +++ b/src/game/MiscHandler.cpp @@ -1686,29 +1686,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; - - Creature *creature = NULL; - if(IS_VEHICLE_GUID(guid)) - { - Vehicle *vehicle = ObjectAccessor::GetVehicle(guid); - if(vehicle) - { - _player->EnterVehicle(vehicle); - creature = vehicle; - } - } - if(!creature) - creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); - if(creature && creature->IsAIEnabled) - creature->AI()->SpellClick(_player); -} - 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 2ef75c24be7..59d0d263227 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -26,6 +26,7 @@ #include "Corpse.h" #include "Player.h" #include "Vehicle.h" +#include "SpellAuras.h" #include "MapManager.h" #include "Transports.h" #include "BattleGround.h" diff --git a/src/game/Object.cpp b/src/game/Object.cpp index 56d52f5e49f..7adc7d306bb 100644 --- a/src/game/Object.cpp +++ b/src/game/Object.cpp @@ -526,9 +526,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 + UNIT_NPC_FLAG_OUTDOORPVP)); + { + // remove custom flag before sending + uint32 appendValue = m_uint32Values[ index ] & ~(UNIT_NPC_FLAG_GUARD + UNIT_NPC_FLAG_OUTDOORPVP); + + 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 a2af970e9f3..12fb26f0ad8 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -6207,6 +6207,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 e655d122e41..eff1f3812a1 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -99,6 +99,15 @@ extern ScriptMapMap sGameObjectScripts; extern ScriptMapMap sEventScripts; extern ScriptMapMap sWaypointScripts; +struct SpellClickInfo +{ + uint32 spellId; + uint32 questId; + uint8 castFlags; +}; + +typedef std::multimap<uint32, SpellClickInfo> SpellClickInfoMap; + struct AreaTrigger { uint32 access_id; @@ -569,6 +578,9 @@ class ObjectMgr void LoadReputationOnKill(); void LoadPointsOfInterest(); + SpellClickInfoMap mSpellClickInfoMap; + void LoadNPCSpellClickSpells(); + void LoadWeatherZoneChances(); void LoadGameTele(); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index c03fe059851..0f1cce9bab3 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -12562,7 +12562,7 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver ) CastSpell(this,itr->second->spellId,true); } - UpdateForQuestsGO(); + UpdateForQuestWorldObjects(); } void Player::CompleteQuest( uint32 quest_id ) @@ -13293,7 +13293,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 TrinIty, but used in scripting code @@ -13414,7 +13414,7 @@ void Player::ItemAddedQuestCheck( uint32 entry, uint32 count ) } } } - UpdateForQuestsGO(); + UpdateForQuestWorldObjects(); } void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count ) @@ -13455,7 +13455,7 @@ void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count ) } } } - UpdateForQuestsGO(); + UpdateForQuestWorldObjects(); } void Player::KilledMonster( uint32 entry, uint64 guid ) @@ -18890,7 +18890,7 @@ bool Player::HasQuestForGO(int32 GOId) const return false; } -void Player::UpdateForQuestsGO() +void Player::UpdateForQuestWorldObjects() { if(m_clientGUIDs.empty()) return; @@ -18905,6 +18905,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); @@ -20455,3 +20473,18 @@ void Player::ResummonPetTemporaryUnSummonedIfAny() m_temporaryUnsummonedPetNumber = 0; } + +bool Player::canSeeSpellClickOn(Creature const *c) const +{ + SpellClickInfoMap const& map = objmgr.mSpellClickInfoMap; + if(map.empty()) + return true; + + 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; +} + diff --git a/src/game/Player.h b/src/game/Player.h index 0f1c7b6689f..ecab25122bf 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1160,7 +1160,7 @@ class TRINITY_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 ); @@ -2028,6 +2028,8 @@ class TRINITY_DLL_SPEC Player : public Unit bool HasTitle(CharTitlesEntry const* title) { return HasTitle(title->bit_index); } void SetTitle(CharTitlesEntry const* title); + //bool isActiveObject() const { return true; } + bool canSeeSpellClickOn(Creature const* creature) const; protected: /*********************************************************/ diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 264a4c39aea..29783de6c70 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -47,6 +47,7 @@ #include "Util.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" +#include "Vehicle.h" #include "CellImpl.h" pAuraHandler AuraHandler[TOTAL_AURAS]= @@ -6726,19 +6727,40 @@ void AuraEffect::HandleArenaPreparation(bool apply, bool Real) m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION); } +/** + * Such auras are applied from a caster(=player) to a vehicle. + * This has been verified using spell #49256 + */ void AuraEffect::HandleAuraControlVehicle(bool apply, bool Real) { if(!Real) return; - if(m_target->GetTypeId() != TYPEID_PLAYER) + if(m_target->GetTypeId() != TYPEID_UNIT || !((Creature*)m_target)->isVehicle()) + return; + + Unit *caster = GetCaster(); + if(!caster) return; - if(Pet *pet = ((Player*)m_target)->GetPet()) - pet->Remove(PET_SAVE_AS_CURRENT); + Vehicle *vehicle = dynamic_cast<Vehicle*>(m_target); - //WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); - //((Player*)m_target)->GetSession()->SendPacket(&data); + if (apply) + { + if(caster->GetTypeId() == TYPEID_PLAYER) + if(Pet *pet = ((Player*)caster)->GetPet()) + pet->Remove(PET_SAVE_AS_CURRENT); + caster->EnterVehicle(vehicle); + } + else + { + SpellEntry const *spell = GetSpellProto(); + + // some SPELL_AURA_CONTROL_VEHICLE auras have a dummy effect on the player - remove them + caster->RemoveAurasDueToSpell(spell->Id); + + caster->ExitVehicle(); + } } void AuraEffect::HandleAuraConvertRune(bool apply, bool Real) diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index f08299631cd..3915d61d876 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -1227,6 +1227,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 5cd5da67118..c24a9e2bb92 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -483,3 +483,30 @@ void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ ) } } +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); + } + } + + if(unit->isVehicle()) + _player->EnterVehicle((Vehicle*)unit); +} diff --git a/src/game/World.cpp b/src/game/World.cpp index 9615a378faa..fa44e527da5 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -1293,6 +1293,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(); |