diff options
-rw-r--r-- | sql/mangos.sql | 13 | ||||
-rw-r--r-- | sql/updates/4043_world_npc_spellclick_spells.sql | 28 | ||||
-rw-r--r-- | sql/world_spell_full.sql | 77 | ||||
-rw-r--r-- | src/game/ObjectMgr.cpp | 62 | ||||
-rw-r--r-- | src/game/ObjectMgr.h | 17 | ||||
-rw-r--r-- | src/game/Player.cpp | 25 | ||||
-rw-r--r-- | src/game/SpellHandler.cpp | 6 | ||||
-rw-r--r-- | src/game/Unit.h | 2 |
8 files changed, 142 insertions, 88 deletions
diff --git a/sql/mangos.sql b/sql/mangos.sql index af73760ac0e..636af9faca1 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_7988_09_mangos_spell_proc_event` bit(1) default NULL + `required_8016_01_mangos_npc_spellclick_spells` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -3190,11 +3190,12 @@ UNLOCK TABLES; DROP TABLE IF EXISTS `npc_spellclick_spells`; 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', - `quest_status` INT(11) UNSIGNED NOT NULL DEFAULT 3 COMMENT 'Quest status: 3 incompleted, 1 completed/rewarded', - `cast_flags` TINYINT UNSIGNED NOT NULL COMMENT 'first bit defines caster: 1=player, 0=creature; second bit defines target, same mapping as caster bit' + `npc_entry` int unsigned NOT NULL COMMENT 'reference to creature_template', + `spell_id` int unsigned NOT NULL COMMENT 'spell which should be casted ', + `quest_start` mediumint(8) unsigned NOT NULL COMMENT 'reference to quest_template', + `quest_start_active` tinyint(1) unsigned NOT NULL default '0', + `quest_end` mediumint(8) unsigned NOT NULL default '0', + `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/4043_world_npc_spellclick_spells.sql b/sql/updates/4043_world_npc_spellclick_spells.sql new file mode 100644 index 00000000000..2e1895a25fd --- /dev/null +++ b/sql/updates/4043_world_npc_spellclick_spells.sql @@ -0,0 +1,28 @@ +ALTER TABLE npc_spellclick_spells + DROP COLUMN quest_status, + CHANGE COLUMN quest_id quest_start mediumint(8) unsigned NOT NULL COMMENT 'reference to quest_template', + ADD COLUMN quest_start_active tinyint(1) unsigned NOT NULL default '0' AFTER quest_start, + ADD COLUMN quest_end mediumint(8) unsigned NOT NULL default '0' AFTER quest_start_active; + +/* compatibility with old data */ +UPDATE npc_spellclick_spells + SET quest_end = quest_start, quest_start_active = 1 + WHERE quest_start <> 0; + +DELETE FROM `npc_spellclick_spells` WHERE `spell_id` IN ( +54568, 54575, 52263, 52280, 52447); +INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `quest_start`, `quest_start_active`, `quest_end`, `cast_flags`) VALUES +(29488, 54568, 12670, 1, 0, 3), -- Taxi to Death's Breath +(29501, 54575, 12670, 1, 0, 3), +(28605, 52263, 12680, 1, 12680, 1), -- Stolen Horse +(28606, 52263, 12680, 1, 12680, 1), +(28607, 52263, 12680, 1, 12680, 1), +(28782, 52280, 12687, 1, 12687, 1), -- Unbound Charger +(28833, 52447, 12701, 1, 12701, 1), -- Scarlet Cannon Master +(28887, 52447, 12701, 1, 12701, 1); + +DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN (29912); +INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `quest_start`, `quest_start_active`, `quest_end`, `cast_flags`) VALUES +(29912, 55479, 0, 0, 0, 3); # Obedience Crystal - Force Obedience + +REPLACE into `spell_target_position` values (51852, 609, 2361.21, -5660.45, 503.828, 4.49);
\ No newline at end of file diff --git a/sql/world_spell_full.sql b/sql/world_spell_full.sql index 08c23a2b726..42c2257ec28 100644 --- a/sql/world_spell_full.sql +++ b/sql/world_spell_full.sql @@ -164,22 +164,6 @@ UPDATE creature_template SET speed = 1.0 WHERE entry = 23095; # molten_flame -- CLICK -- -------- -DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN ('29501', '29488'); -INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `quest_id`, `quest_status`, `cast_flags`) VALUES -('29488', '54568', '12670', '1', '3'), -('29501', '54575', '12670', '1', '3'); - -DELETE FROM npc_spellclick_spells WHERE `npc_entry` IN (28605,28606,28607); -INSERT INTO npc_spellclick_spells (`npc_entry`, `spell_id`, `quest_id`, `quest_status`, `cast_flags`) VALUES -(28605, 52263, 12680, 3, 1), -(28606, 52263, 12680, 3, 1), -(28607, 52263, 12680, 3, 1); - --- Spellclick spell to mount deathcharger -DELETE FROM npc_spellclick_spells WHERE `npc_entry`=28782; -INSERT INTO npc_spellclick_spells (`npc_entry`, `spell_id`, `quest_id`, `quest_status`, `cast_flags`) VALUES -(28782, 52280, 12687, 3, 1); - -- -------- -- TARGET -- -------- @@ -1510,25 +1494,24 @@ UPDATE creature_template SET VehicleId = 312 WHERE entry IN (31857,31858,31861,3 update creature_template set spell5=51890 where entry = 28511; -- Eye of Acherus flight DELETE FROM `spell_script_target` WHERE entry IN -(51859, 48743); +(51859, 48743, 52124, 52479, 52576, 53110); INSERT INTO `spell_script_target` (`entry`, `type`, `targetEntry`) VALUES (51859, 1, 28525), -- siphon of archerus (51859, 1, 28542), (51859, 1, 28543), (51859, 1, 28544), +(52124, 1, 28655), -- Sky Darkener Assault +(52479, 1, 28819), -- gift of harvester +(52479, 1, 28822), +(52576, 1, 28834), -- Electro-magnetic Pulse +(52576, 1, 28886), +(53110, 1, 28940), -- Devour Humanoid (48743, 1, 26125); -- Death pact -update creature_template set minlevel=50,maxlevel=52,minhealth=2215,maxhealth=2317,faction_A=2084,faction_H=2084,mindmg=50,maxdmg=50 where entry=28528; -- ghoul - --- taxi -DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN ('29501', '29488'); -INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `quest_id`, `quest_status`, `cast_flags`) VALUES -('29488', '54568', '12670', '1', '3'), -('29501', '54575', '12670', '1', '3'); +-- Eye of Acherus +REPLACE into `spell_target_position` values (51852, 609, 2361.21, -5660.45, 503.828, 4.49); -DELETE FROM `spell_script_target` WHERE entry IN (52124); -INSERT INTO `spell_script_target` (`entry`, `type`, `targetEntry`) VALUES -(52124, 1, 28655); +update creature_template set minlevel=50,maxlevel=52,minhealth=2215,maxhealth=2317,faction_A=2084,faction_H=2084,mindmg=50,maxdmg=50 where entry=28528; -- ghoul UPDATE `creature_template` SET spell1=52372,spell2=52373,spell3=52374,spell4=52375 WHERE `entry`=28406; -- death charger @@ -1542,14 +1525,6 @@ UPDATE creature_template SET `VehicleId`=200 WHERE `entry` IN (28605,28606,28607 -- Vehicle and summon spell(summon npc 28788) for Acherus Deathcharger UPDATE creature_template SET `spell1`=52362, `VehicleId`=200 WHERE `entry`=28782; --- gift of harvester -DELETE FROM `spell_script_target` WHERE entry IN -(52479); -INSERT INTO `spell_script_target` (`entry`, `type`, `targetEntry`) VALUES -(52479,1,28819), -(52479,1,28822); - - replace into creature_questrelation (id,quest) VALUES (28377,12701); replace into creature_involvedrelation (id,quest) VALUES (28377,12701); replace into creature_involvedrelation (id,quest) VALUES (28914,12723); @@ -1560,31 +1535,25 @@ replace into creature_involvedrelation (id,quest) VALUES (28912,12725); replace into creature_questrelation (id,quest) VALUES (28912,12727); replace into creature_involvedrelation (id,quest) VALUES (28913,12727); --- ship cannon -DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN (28833,28887); -INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `quest_id`, `quest_status`, `cast_flags`) VALUES -(28833, 52447, 12701, 3, 3), -(28887, 52447, 12701, 3, 3); +DELETE FROM `npc_spellclick_spells` WHERE `spell_id` IN ( +54568, 54575, 52263, 52280, 52447); +INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `quest_start`, `quest_start_active`, `quest_end`, `cast_flags`) VALUES +(29488, 54568, 12670, 1, 0, 3), -- Taxi to Death's Breath +(29501, 54575, 12670, 1, 0, 3), +(28605, 52263, 12680, 1, 12680, 1), -- Stolen Horse +(28606, 52263, 12680, 1, 12680, 1), +(28607, 52263, 12680, 1, 12680, 1), +(28782, 52280, 12687, 1, 12687, 1), -- Unbound Charger +(28833, 52447, 12701, 1, 12701, 1), -- Scarlet Cannon Master +(28887, 52447, 12701, 1, 12701, 1); UPDATE creature_template SET spell1=52435,spell2=52576,spell5=52588,VehicleId=68,speed=0 WHERE entry IN (28833,28887); UPDATE creature_template SET spell1=52211 WHERE entry=28864; -DELETE FROM `spell_script_target` WHERE entry IN -(52576); -INSERT INTO `spell_script_target` (`entry`, `type`, `targetEntry`) VALUES -(52576,1,28834), -(52576,1,28886); - -- frostbrood vanquisher update creature_template set maxhealth = 133525, minhealth = 133525, maxmana = 51360, minmana = 51360, spell1 = 53114, spell2 = 53112, spell3=53110, VehicleId = 156 where entry = 28670; -DELETE FROM `spell_script_target` WHERE entry IN -(53110); -INSERT INTO `spell_script_target` (`entry`, `type`, `targetEntry`) VALUES -(53110,1,28940); - - -- -------- -- NAXXARAMAS @@ -1634,8 +1603,8 @@ flags_extra = VALUES(flags_extra), scriptname = VALUES(scriptname); DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN (29912); -INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `quest_id`, `cast_flags`) VALUES -(29912, 55479, 0, 1); # Obedience Crystal - Force Obedience +INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `quest_start`, `quest_start_active`, `quest_end`, `cast_flags`) VALUES +(29912, 55479, 0, 0, 0, 3); # Obedience Crystal - Force Obedience DELETE FROM `spell_script_target` WHERE `entry` IN (28732,54097,55479, diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 20ace65d79b..867da4401d3 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -110,6 +110,25 @@ LanguageDesc const* GetLanguageDescByID(uint32 lang) return NULL; } +bool SpellClickInfo::IsFitToRequirements(Player const* player) const +{ + if(questStart) + { + // not in expected required quest state + if(!player || (!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)) + return false; + } + + if(questEnd) + { + // not in expected forbidden quest state + if(!player || player->GetQuestRewardStatus(questEnd)) + return false; + } + + return true; +} + ObjectMgr::ObjectMgr() { m_hiCharGuid = 1; @@ -699,6 +718,12 @@ void ObjectMgr::LoadCreatureTemplates() if(cInfo->rangeattacktime == 0) const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME; + if(cInfo->npcflag & UNIT_NPC_FLAG_SPELLCLICK) + { + sLog.outErrorDb("Creature (Entry: %u) has dynamic flag UNIT_NPC_FLAG_SPELLCLICK (%u) set, it expect to be set by code base at `npc_spellclick_spells` content.",cInfo->Entry,UNIT_NPC_FLAG_SPELLCLICK); + const_cast<CreatureInfo*>(cInfo)->npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK; + } + if((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE) sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u",cInfo->Entry,cInfo->trainer_type); @@ -6567,8 +6592,8 @@ void ObjectMgr::LoadNPCSpellClickSpells() uint32 count = 0; mSpellClickInfoMap.clear(); - - QueryResult *result = WorldDatabase.Query("SELECT npc_entry, spell_id, quest_id, quest_status, cast_flags FROM npc_spellclick_spells"); + // 0 1 2 3 4 5 + QueryResult *result = WorldDatabase.Query("SELECT npc_entry, spell_id, quest_start, quest_start_active, quest_end, cast_flags FROM npc_spellclick_spells"); if(!result) { @@ -6607,28 +6632,45 @@ void ObjectMgr::LoadNPCSpellClickSpells() continue; } - uint32 quest = fields[2].GetUInt32(); + uint32 quest_start = fields[2].GetUInt32(); // quest might be 0 to enable spellclick independent of any quest - if (quest) + if (quest_start) { - if(mQuestTemplates.find(quest) == mQuestTemplates.end()) + if(mQuestTemplates.find(quest_start) == mQuestTemplates.end()) { - sLog.outErrorDb("Table npc_spellclick_spells references unknown quest %u. Skipping entry.", spellid); + sLog.outErrorDb("Table npc_spellclick_spells references unknown start quest %u. Skipping entry.", quest_start); continue; } } - uint32 queststatus = fields[3].GetUInt32(); + bool quest_start_active = fields[3].GetBool(); + + uint32 quest_end = fields[4].GetUInt32(); + // quest might be 0 to enable spellclick active infinity after start quest + if (quest_end) + { + if(mQuestTemplates.find(quest_end) == mQuestTemplates.end()) + { + sLog.outErrorDb("Table npc_spellclick_spells references unknown end quest %u. Skipping entry.", quest_end); + continue; + } + + } - uint8 castFlags = fields[4].GetUInt8(); + uint8 castFlags = fields[5].GetUInt8(); SpellClickInfo info; info.spellId = spellid; - info.questId = quest; - info.questStatus = queststatus; + info.questStart = quest_start; + info.questStartCanActive = quest_start_active; + info.questEnd = quest_end; info.castFlags = castFlags; mSpellClickInfoMap.insert(SpellClickInfoMap::value_type(npc_entry, info)); + + // mark creature template as spell clickable + const_cast<CreatureInfo*>(cInfo)->npcflag |= UNIT_NPC_FLAG_SPELLCLICK; + ++count; } while (result->NextRow()); diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index a508c573ef6..0c032bc8ec3 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -102,12 +102,17 @@ extern ScriptMapMap sWaypointScripts; struct SpellClickInfo { uint32 spellId; - uint32 questId; - uint32 questStatus; + uint32 questStart; // quest start (quest must be active or rewarded for spell apply) + uint32 questEnd; // quest end (quest don't must be rewarded for spell apply) + bool questStartCanActive; // if true then quest start can be active (not only rewarded) uint8 castFlags; + + // helpers + bool IsFitToRequirements(Player const* player) const; }; typedef std::multimap<uint32, SpellClickInfo> SpellClickInfoMap; +typedef std::pair<SpellClickInfoMap::const_iterator,SpellClickInfoMap::const_iterator> SpellClickInfoMapBounds; struct AreaTrigger { @@ -585,7 +590,6 @@ class ObjectMgr void LoadReputationOnKill(); void LoadPointsOfInterest(); - SpellClickInfoMap mSpellClickInfoMap; void LoadNPCSpellClickSpells(); void LoadWeatherZoneChances(); @@ -828,6 +832,11 @@ class ObjectMgr int GetOrNewIndexForLocale(LocaleConstant loc); + SpellClickInfoMapBounds GetSpellClickInfoMapBounds(uint32 creature_id) const + { + return SpellClickInfoMapBounds(mSpellClickInfoMap.lower_bound(creature_id),mSpellClickInfoMap.upper_bound(creature_id)); + } + ItemRequiredTargetMapBounds GetItemRequiredTargetMapBounds(uint32 uiItemEntry) const { return ItemRequiredTargetMapBounds(m_ItemRequiredTarget.lower_bound(uiItemEntry),m_ItemRequiredTarget.upper_bound(uiItemEntry)); @@ -921,6 +930,8 @@ class ObjectMgr ScriptNameMap m_scriptNames; + SpellClickInfoMap mSpellClickInfoMap; + ItemRequiredTargetMap m_ItemRequiredTarget; typedef std::vector<LocaleConstant> LocalForIndex; diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 19e5c7e02d2..af7057518fb 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -19186,18 +19186,20 @@ void Player::UpdateForQuestWorldObjects() Creature *obj = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr); if(!obj) continue; + // check if this unit requires quest specific flags + if(!obj->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_SPELLCLICK)) + continue; - SpellClickInfoMap const& map = objmgr.mSpellClickInfoMap; - for(SpellClickInfoMap::const_iterator itr = map.lower_bound(obj->GetEntry()); itr != map.upper_bound(obj->GetEntry()); ++itr) + SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(obj->GetEntry()); + for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr) { - if(itr->second.questId != 0) + if(itr->second.questStart || itr->second.questEnd) { obj->BuildCreateUpdateBlockForPlayer(&udata,this); break; } } - } } udata.BuildPacket(&packet); @@ -20754,16 +20756,17 @@ void Player::ResummonPetTemporaryUnSummonedIfAny() bool Player::canSeeSpellClickOn(Creature const *c) const { - SpellClickInfoMap::const_iterator lower = objmgr.mSpellClickInfoMap.lower_bound(c->GetEntry()); - SpellClickInfoMap::const_iterator upper = objmgr.mSpellClickInfoMap.upper_bound(c->GetEntry()); - if(lower == upper) + if(!c->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_SPELLCLICK)) + return false; + + SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(c->GetEntry()); + if(clickPair.first == clickPair.second) return true; - for(SpellClickInfoMap::const_iterator itr = lower; itr != upper; ++itr) - { - if(itr->second.questId == 0 || GetQuestStatus(itr->second.questId) == itr->second.questStatus) + for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr) + if(itr->second.IsFitToRequirements(this)) return true; - } + return false; } diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 1a9bc13eab8..74313eaa38b 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -522,10 +522,10 @@ void WorldSession::HandleSpellClick( WorldPacket & recv_data ) 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) + SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(unit->GetEntry()); + for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr) { - if(itr->second.questId == 0 || _player->GetQuestStatus(itr->second.questId) == itr->second.questStatus) + if(itr->second.IsFitToRequirements(_player)) { Unit *caster = (itr->second.castFlags & 0x1) ? (Unit*)_player : (Unit*)unit; Unit *target = (itr->second.castFlags & 0x2) ? (Unit*)_player : (Unit*)unit; diff --git a/src/game/Unit.h b/src/game/Unit.h index 00478a74acd..ce1c0265268 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -614,7 +614,7 @@ enum NPCFlags UNIT_NPC_FLAG_AUCTIONEER = 0x00200000, // 100% UNIT_NPC_FLAG_STABLEMASTER = 0x00400000, // 100% UNIT_NPC_FLAG_GUILD_BANKER = 0x00800000, // cause client to send 997 opcode - UNIT_NPC_FLAG_SPELLCLICK = 0x01000000, // cause client to send 1015 opcode (spell click) + UNIT_NPC_FLAG_SPELLCLICK = 0x01000000, // cause client to send 1015 opcode (spell click), dynamic, set at loading and don't must be set in DB UNIT_NPC_FLAG_GUARD = 0x10000000, // custom flag for guards UNIT_NPC_FLAG_OUTDOORPVP = 0x20000000, // custom flag for outdoor pvp creatures }; |