diff --git a/Makefile.am b/Makefile.am index 767eda776..2b292050f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,3 +53,5 @@ EXTRA_DIST += \ win/VC100/zlib.vcxproj \ win/VC100/g3dlite.vcxproj +ACLOCAL_AMFLAGS = -I m4 + diff --git a/configure.ac b/configure.ac index c9f635b78..20534009f 100644 --- a/configure.ac +++ b/configure.ac @@ -48,7 +48,7 @@ if test "$srcdir" = "." && test "$enable_maintainer_mode" != "yes"; then ../configure make - This will create a build space in the directory `objdir' and + This will create a build space in the directory 'objdir' and start a build in that directory. If however you realy want to disable this error, diff --git a/m4/pkg.m4 b/m4/pkg.m4 new file mode 100644 index 000000000..a0b9cd45d --- /dev/null +++ b/m4/pkg.m4 @@ -0,0 +1,155 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# +# Copyright © 2004 Scott James Remnant . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# +# Similar to PKG_CHECK_MODULES, make sure that the first instance of +# this or PKG_CHECK_MODULES is called, or make sure to call +# PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_ifval([$2], [$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + ifelse([$4], , [AC_MSG_ERROR(dnl +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT +])], + [AC_MSG_RESULT([no]) + $4]) +elif test $pkg_failed = untried; then + ifelse([$4], , [AC_MSG_FAILURE(dnl +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])], + [$4]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + ifelse([$3], , :, [$3]) +fi[]dnl +])# PKG_CHECK_MODULES diff --git a/sql/mangos_spell_check.sql b/sql/mangos_spell_check.sql index 1585270f7..e284285f0 100644 --- a/sql/mangos_spell_check.sql +++ b/sql/mangos_spell_check.sql @@ -38,6 +38,7 @@ CREATE TABLE `spell_check` ( INSERT INTO spell_check (spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMaskB,SpellIcon,SpellVisual,SpellCategory,EffectType,EffectAura,EffectIdx,Name,Code) VALUES /* sorted by spell ids */ /*id fm familyMaskA fmMaskB icon vis cat eff aur ef name code */ +(781, 9, -1, -1, -1, -1, -1, 3, -1,-1,'Disengage', 'Spell::EffectDummy'), (1454, 5,0x0000000000040000,0x00000000, -1, -1, -1, 3, -1,-1,'Life Tap', 'Spell::EffectDummy'), (1455, 5,0x0000000000040000,0x00000000, -1, -1, -1, 3, -1,-1,'Life Tap', 'Spell::EffectDummy'), (1456, 5,0x0000000000040000,0x00000000, -1, -1, -1, 3, -1,-1,'Life Tap', 'Spell::EffectDummy'), @@ -315,7 +316,10 @@ INSERT INTO spell_check (spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMas (55004, 0, -1, -1, -1, -1, -1, 3, -1,-1,'Nitro Boosts', 'Spell::EffectDummy'), (55441,11, -1, -1, -1, -1, -1, -1, 4,-1,'Glyph of Mana Tide', 'Spell::EffectDummy'), (56235,-1, -1, -1, -1, -1, -1, -1, -1,-1,'Glyph of Conflagrate', 'Spell::EffectSchoolDMG'), +(56446,-1, -1, -1, -1, -1, -1, -1, -1,-1,'Disengage', 'Spell::EffectDummy'), (57627,-1, -1, -1, -1, -1, -1, -1, -1,-1,'Charge', 'Spell::EffectSchoolDMG'), +(57635, 9, -1, -1, -1, -1, -1, 3, -1,-1,'Disengage', 'Spell::EffectDummy'), +(57636,-1, -1, -1, -1, -1, -1, -1, -1,-1,'Disengage', 'Spell::EffectDummy'), (57946, 5,0x0000000000040000,0x00000000, -1, -1, -1, 3, -1,-1,'Life Tap', 'Spell::EffectDummy'), (58367,-1, -1, -1, -1, -1, -1, -1, 4,-1,'Glyph of Execution', 'Spell::EffectDummy'), (58418, 0, -1, -1, -1, -1, -1, 3, -1,-1,'Portal to Orgrimmar', 'Spell::EffectDummy'), @@ -324,9 +328,13 @@ INSERT INTO spell_check (spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMas (59645,-1, -1, -1, -1, -1, -1, -1, -1,-1,'Underbelly Elixir', 'Spell::EffectDummy'), (59831,-1, -1, -1, -1, -1, -1, -1, -1,-1,'Underbelly Elixir', 'Spell::EffectDummy'), (59843,-1, -1, -1, -1, -1, -1, -1, -1,-1,'Underbelly Elixir', 'Spell::EffectDummy'), +(60932, 0, -1, -1, -1, -1, -1, 3, -1,-1,'Disengage', 'Spell::EffectDummy'), +(60934,-1, -1, -1, -1, -1, -1, -1, -1,-1,'Disengage', 'Spell::EffectDummy'), (61290, 5,0x0001000000000000,0x00000000, -1, -1, -1, 2, -1,-1,'Shadowflame', 'Spell::EffectSchoolDMG'), (61291,-1, -1, -1, -1, -1, -1, -1, -1,-1,'Shadowflame', 'Spell::EffectSchoolDMG'), (61491, 0, -1, -1, -1, -1, -1, 2, -1,-1,'Intercept', 'Spell::EffectSchoolDMG'), +(61507, 9, -1, -1, -1, -1, -1, 3, -1,-1,'Disengage', 'Spell::EffectDummy'), +(61508,-1, -1, -1, -1, -1, -1, -1, -1,-1,'Disengage', 'Spell::EffectDummy'), (63375,-1, -1, -1, -1, -1, -1, 30, -1,-1,'Improved Stormstrike', 'Spell::EffectEnergize'), /* sorted by spell names */ @@ -344,6 +352,7 @@ INSERT INTO spell_check (spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMas ( 0,15,0x0000000000002000,0x00000000, -1, -1, -1, 3, -1,-1,'Death Coil', 'Spell::EffectDummy'), ( 0,15,0x0000000000000010,0x00000000, -1, -1, -1, 3, -1,-1,'Death Strike', 'Spell::EffectDummy'), ( 0, 5, -1, -1, -1, -1, 12, 38, -1,-1,'Devour Magic', 'Spell::EffectDispel'), +( 0, 9,0x0000400000000000,0x00000000, -1, -1, -1, 3, -1,-1,'Disengage', 'Spell::EffectDummy'), ( 0, 8,0x0000000800000000,0x00000000, -1, -1, -1, 2, -1,-1,'Envenom', 'Spell::EffectSchoolDMG'), ( 0, 8,0x0000000000020000,0x00000000, -1, -1, -1, 2, -1,-1,'Eviscerate', 'Spell::EffectSchoolDMG'), (0, 4,0x0000000020000000,0x00000000, -1, -1, -1, 3, -1,-1,'Execute', 'Spell::EffectDummy'), diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index c44df2751..5d3beea8c 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -1208,18 +1208,18 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui } case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: // miscvalue1 = item_id - if(!miscvalue1) + if (!miscvalue1) continue; - if(miscvalue1 != achievementCriteria->equip_item.itemID) + if (miscvalue1 != achievementCriteria->equip_item.itemID) continue; SetCriteriaProgress(achievementCriteria, 1); break; case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: // miscvalue1 = go entry - if(!miscvalue1) + if (!miscvalue1) continue; - if(miscvalue1 != achievementCriteria->use_gameobject.goEntry) + if (miscvalue1 != achievementCriteria->use_gameobject.goEntry) continue; SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); @@ -1234,7 +1234,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui break; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: { - if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine) + if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine) continue; uint32 spellCount = 0; @@ -1242,9 +1242,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui spellIter != GetPlayer()->GetSpellMap().end(); ++spellIter) { - for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first); - skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first); - ++skillIter) + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellIter->first); + for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) { if(skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine) spellCount++; @@ -1255,17 +1254,17 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui } case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if(!miscvalue1) + if (!miscvalue1) continue; - if(achievementCriteria->win_duel.duelCount) + if (achievementCriteria->win_duel.duelCount) { // those requirements couldn't be found in the dbc AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if(!data) + if (!data) continue; - if(!data->Meets(GetPlayer(),unit)) + if (!data->Meets(GetPlayer(),unit)) continue; } @@ -1301,7 +1300,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui } case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: { - if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine) + if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine) continue; uint32 spellCount = 0; @@ -1309,13 +1308,10 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui spellIter != GetPlayer()->GetSpellMap().end(); ++spellIter) { - for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first); - skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first); - ++skillIter) - { - if(skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine) + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellIter->first); + for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) + if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine) spellCount++; - } } SetCriteriaProgress(achievementCriteria, spellCount); break; diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp index 9c70da28e..d40078c5a 100644 --- a/src/game/BattleGround.cpp +++ b/src/game/BattleGround.cpp @@ -1099,6 +1099,10 @@ void BattleGround::StartBattleGround() void BattleGround::AddPlayer(Player *plr) { + // remove afk from player + if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK)) + plr->ToggleAFK(); + // score struct must be created in inherited class uint64 guid = plr->GetGUID(); diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index c674e4ad8..fbc4495f6 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -31,6 +31,7 @@ #include "GridNotifiersImpl.h" #include "CellImpl.h" #include "AccountMgr.h" +#include "SpellMgr.h" // Supported shift-links (client generated and server side) // |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r @@ -980,6 +981,540 @@ int ChatHandler::ParseCommands(const char* text) return 1; } +bool ChatHandler::isValidChatMessage(const char* message) +{ +/* + +valid examples: +|cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r +|cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r +|cffffd000|Htrade:4037:1:150:1:6AAAAAAAAAAAAAAAAAAAAAAOAADAAAAAAAAAAAAAAAAIAAAAAAAAA|h[Engineering]|h|r +|cff4e96f7|Htalent:2232:-1|h[Taste for Blood]|h|r +|cff71d5ff|Hspell:21563|h[Command]|h|r +|cffffd000|Henchant:3919|h[Engineering: Rough Dynamite]|h|r +|cffffff00|Hachievement:546:0000000000000001:0:0:0:-1:0:0:0:0|h[Safe Deposit]|h|r +|cff66bbff|Hglyph:21:762|h[Glyph of Bladestorm]|h|r + +| will be escaped to || +*/ + + if(strlen(message) > 255) + return false; + + const char validSequence[6] = "cHhhr"; + const char* validSequenceIterator = validSequence; + + // more simple checks + if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) < 3) + { + const std::string validCommands = "cHhr|"; + + while(*message) + { + // find next pipe command + message = strchr(message, '|'); + + if(!message) + return true; + + ++message; + char commandChar = *message; + if(validCommands.find(commandChar) == std::string::npos) + return false; + + ++message; + // validate sequence + if(sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) == 2) + { + if(commandChar == *validSequenceIterator) + { + if (validSequenceIterator == validSequence+4) + validSequenceIterator = validSequence; + else + ++validSequenceIterator; + } + else + return false; + } + } + return true; + } + + std::istringstream reader(message); + char buffer[256]; + + uint32 color; + + ItemPrototype const* linkedItem; + Quest const* linkedQuest; + SpellEntry const *linkedSpell; + AchievementEntry const* linkedAchievement; + + while(!reader.eof()) + { + if (validSequence == validSequenceIterator) + { + linkedItem = NULL; + linkedQuest = NULL; + linkedSpell = NULL; + linkedAchievement = NULL; + + reader.ignore(255, '|'); + } + else if(reader.get() != '|') + { +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage sequence aborted unexpectedly"); +#endif + return false; + } + + // pipe has always to be followed by at least one char + if ( reader.peek() == '\0') + { +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage pipe followed by \\0"); +#endif + return false; + } + + // no further pipe commands + if (reader.eof()) + break; + + char commandChar; + reader >> commandChar; + + // | in normal messages is escaped by || + if (commandChar != '|') + { + if(commandChar == *validSequenceIterator) + { + if (validSequenceIterator == validSequence+4) + validSequenceIterator = validSequence; + else + ++validSequenceIterator; + } + else + { +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage invalid sequence, expected %c but got %c", *validSequenceIterator, commandChar); +#endif + return false; + } + } + else if(validSequence != validSequenceIterator) + { + // no escaped pipes in sequences +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage got escaped pipe in sequence"); +#endif + return false; + } + + switch (commandChar) + { + case 'c': + color = 0; + // validate color, expect 8 hex chars + for(int i=0; i<8; i++) + { + char c; + reader >> c; + if(!c) + { +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage got \\0 while reading color in |c command"); +#endif + return false; + } + + color <<= 4; + // check for hex char + if(c >= '0' && c <='9') + { + color |= c-'0'; + continue; + } + if(c >= 'a' && c <='f') + { + color |= 10+c-'a'; + continue; + } +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage got non hex char '%c' while reading color", c); +#endif + return false; + } + break; + case 'H': + // read chars up to colon = link type + reader.getline(buffer, 256, ':'); + + if (strcmp(buffer, "item") == 0) + { + // read item entry + reader.getline(buffer, 256, ':'); + + linkedItem= objmgr.GetItemPrototype(atoi(buffer)); + if(!linkedItem) + { +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage got invalid itemID %u in |item command", atoi(buffer)); +#endif + return false; + } + + if (color != ItemQualityColors[linkedItem->Quality]) + { +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage linked item has color %u, but user claims %u", ItemQualityColors[linkedItem->Quality], + color); +#endif + return false; + } + + char c = reader.peek(); + + // ignore enchants etc. + while(c >='0' && c <='9' || c==':') + { + reader.ignore(1); + c = reader.peek(); + } + } + else if(strcmp(buffer, "quest") == 0) + { + // no color check for questlinks, each client will adapt it anyway + uint32 questid= 0; + // read questid + char c = reader.peek(); + while(c >='0' && c<='9') + { + reader.ignore(1); + questid *= 10; + questid += c-'0'; + c = reader.peek(); + } + + linkedQuest = objmgr.GetQuestTemplate(questid); + + if(!linkedQuest) + { +#ifdef MANOGS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage Questtemplate %u not found", questid); +#endif + return false; + } + c = reader.peek(); + // level + while(c !='|' && c!='\0') + { + reader.ignore(1); + c = reader.peek(); + } + } + else if(strcmp(buffer, "trade") == 0) + { + if(color != CHAT_LINK_COLOR_TRADE) + return false; + + // read spell entry + reader.getline(buffer, 256, ':'); + linkedSpell = sSpellStore.LookupEntry(atoi(buffer)); + if(!linkedSpell) + return false; + + char c = reader.peek(); + // base64 encoded stuff + while(c !='|' && c!='\0') + { + reader.ignore(1); + c = reader.peek(); + } + } + else if(strcmp(buffer, "talent") == 0) + { + // talent links are always supposed to be blue + if(color != CHAT_LINK_COLOR_TALENT) + return false; + + // read talent entry + reader.getline(buffer, 256, ':'); + TalentEntry const *talentInfo = sTalentStore.LookupEntry(atoi(buffer)); + if(!talentInfo) + return false; + + linkedSpell = sSpellStore.LookupEntry(talentInfo->RankID[0]); + if(!linkedSpell) + return false; + + char c = reader.peek(); + // skillpoints? whatever, drop it + while(c !='|' && c!='\0') + { + reader.ignore(1); + c = reader.peek(); + } + } + else if(strcmp(buffer, "spell") == 0) + { + if(color != CHAT_LINK_COLOR_SPELL) + return false; + + uint32 spellid = 0; + // read spell entry + char c = reader.peek(); + while(c >='0' && c<='9') + { + reader.ignore(1); + spellid *= 10; + spellid += c-'0'; + c = reader.peek(); + } + linkedSpell = sSpellStore.LookupEntry(spellid); + if(!linkedSpell) + return false; + } + else if(strcmp(buffer, "enchant") == 0) + { + if(color != CHAT_LINK_COLOR_ENCHANT) + return false; + + uint32 spellid = 0; + // read spell entry + char c = reader.peek(); + while(c >='0' && c<='9') + { + reader.ignore(1); + spellid *= 10; + spellid += c-'0'; + c = reader.peek(); + } + linkedSpell = sSpellStore.LookupEntry(spellid); + if(!linkedSpell) + return false; + } + else if(strcmp(buffer, "achievement") == 0) + { + if(color != CHAT_LINK_COLOR_ACHIEVEMENT) + return false; + reader.getline(buffer, 256, ':'); + uint32 achievementId = atoi(buffer); + linkedAchievement = sAchievementStore.LookupEntry(achievementId); + + if(!linkedAchievement) + return false; + + char c = reader.peek(); + // skip progress + while(c !='|' && c!='\0') + { + reader.ignore(1); + c = reader.peek(); + } + } + else if(strcmp(buffer, "glyph") == 0) + { + if(color != CHAT_LINK_COLOR_GLYPH) + return false; + + // first id is slot, drop it + reader.getline(buffer, 256, ':'); + uint32 glyphId = 0; + char c = reader.peek(); + while(c>='0' && c <='9') + { + glyphId *= 10; + glyphId += c-'0'; + reader.ignore(1); + c = reader.peek(); + } + GlyphPropertiesEntry const* glyph = sGlyphPropertiesStore.LookupEntry(glyphId); + if (!glyph) + return false; + + linkedSpell = sSpellStore.LookupEntry(glyph->SpellId); + + if (!linkedSpell) + return false; + } + else + { +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage user sent unsupported link type '%s'", buffer); +#endif + return false; + } + break; + case 'h': + // if h is next element in sequence, this one must contain the linked text :) + if (*validSequenceIterator == 'h') + { + // links start with '[' + if (reader.get() != '[') + { +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage link caption doesn't start with '['"); +#endif + return false; + } + reader.getline(buffer, 256, ']'); + + // verify the link name + if (linkedSpell) + { + // spells with that flag have a prefix of "$PROFESSION: " + if (linkedSpell->Attributes & SPELL_ATTR_TRADESPELL) + { + // lookup skillid + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(linkedSpell->Id); + if (bounds.first == bounds.second) + { + return false; + } + + SkillLineAbilityEntry const *skillInfo = bounds.first->second; + + if (!skillInfo) + { + return false; + } + + SkillLineEntry const *skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId); + if (!skillLine) + { + return false; + } + + for(uint8 i=0; iname[i]); + if (skillLineNameLength > 0 && strncmp(skillLine->name[i], buffer, skillLineNameLength) == 0) + { + // found the prefix, remove it to perform spellname validation below + // -2 = strlen(": ") + uint32 spellNameLength = strlen(buffer)-skillLineNameLength-2; + memmove(buffer, buffer+skillLineNameLength+2, spellNameLength+1); + } + } + } + bool foundName = false; + for(uint8 i=0; iSpellName[i] && strcmp(linkedSpell->SpellName[i], buffer) == 0) + { + foundName = true; + break; + } + } + if (!foundName) + return false; + } + else if (linkedQuest) + { + if (linkedQuest->GetTitle() != buffer) + { + QuestLocale const *ql = objmgr.GetQuestLocale(linkedQuest->GetQuestId()); + + if (!ql) + { +#ifdef MANOGS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage default questname didn't match and there is no locale"); +#endif + return false; + } + + bool foundName = false; + for(uint8 i=0; iTitle.size(); i++) + { + if (ql->Title[i] == buffer) + { + foundName = true; + break; + } + } + if (!foundName) + { +#ifdef MANOGS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage no quest locale title matched") +#endif + return false; + } + } + } + else if(linkedItem) + { + if (strcmp(linkedItem->Name1, buffer) != 0) + { + ItemLocale const *il = objmgr.GetItemLocale(linkedItem->ItemId); + + if (!il) + { +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage linked item name doesn't is wrong and there is no localization"); +#endif + return false; + } + + bool foundName = false; + for(uint8 i=0; iName.size(); ++i) + { + if (il->Name[i] == buffer) + { + foundName = true; + break; + } + } + if (!foundName) + { +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage linked item name wasn't found in any localization"); +#endif + return false; + } + } + } + else if (linkedAchievement) + { + bool foundName = false; + for(uint8 i=0; iname[i], strcmp(linkedAchievement->name[i], buffer) == 0) + { + foundName = true; + break; + } + } + if (!foundName) + return false; + } + // that place should never be reached - if nothing linked has been set in |H + // it will return false before + else + return false; + } + break; + case 'r': + case '|': + // no further payload + break; + default: +#ifdef MANGOS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage got invalid command |%c", commandChar); +#endif + return false; + } + } + + // check if every opened sequence was also closed properly +#ifdef MANGOS_DEBUG + if(validSequence != validSequenceIterator) + sLog.outBasic("ChatHandler::isValidChatMessage EOF in active sequence"); +#endif + return validSequence == validSequenceIterator; +} + bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd) { std::string list; @@ -1666,4 +2201,4 @@ LocaleConstant CliHandler::GetSessionDbcLocale() const int CliHandler::GetSessionDbLocaleIndex() const { return objmgr.GetDBCLocaleIndex(); -} +} \ No newline at end of file diff --git a/src/game/Chat.h b/src/game/Chat.h index ac03a6f07..2bc558fcf 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -69,6 +69,8 @@ class ChatHandler void PSendSysMessage( int32 entry, ... ); int ParseCommands(const char* text); + + bool isValidChatMessage(const char* msg); protected: explicit ChatHandler() : m_session(NULL) {} // for CLI subclass diff --git a/src/game/ChatHandler.cpp b/src/game/ChatHandler.cpp index a6c4f8f8d..f6bd8db92 100644 --- a/src/game/ChatHandler.cpp +++ b/src/game/ChatHandler.cpp @@ -37,6 +37,28 @@ #include "GridNotifiersImpl.h" #include "CellImpl.h" +bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang) +{ + if (lang != LANG_ADDON) + { + // strip invisible characters for non-addon messages + if(sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) + stripLineInvisibleChars(msg); + + if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && GetSecurity() < SEC_MODERATOR + && !ChatHandler(this).isValidChatMessage(msg.c_str())) + { + sLog.outError("Player %s (GUID: %u) sent a chatmessage with an invalid link: %s", GetPlayer()->GetName(), + GetPlayer()->GetGUIDLow(), msg.c_str()); + if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK)) + KickPlayer(); + return false; + } + } + + return true; +} + void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) { uint32 type; @@ -150,9 +172,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) break; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; @@ -171,9 +192,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) recv_data >> to; recv_data >> msg; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; @@ -224,9 +244,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) break; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; @@ -253,9 +272,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) break; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; @@ -280,9 +298,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) break; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; @@ -306,9 +323,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) break; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; @@ -334,9 +350,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) break; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; @@ -355,9 +370,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) std::string msg=""; recv_data >> msg; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; @@ -377,9 +391,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) std::string msg=""; recv_data >> msg; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; @@ -399,9 +412,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) std::string msg=""; recv_data >> msg; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; @@ -423,9 +435,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) recv_data >> msg; - // strip invisible characters for non-addon messages - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; if(msg.empty()) break; diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h index 0e9a32dc4..9e503fded 100644 --- a/src/game/DBCStructure.h +++ b/src/game/DBCStructure.h @@ -41,7 +41,7 @@ struct AchievementEntry uint32 factionFlag; // 1 -1=all, 0=horde, 1=alliance uint32 mapID; // 2 -1=none //uint32 parentAchievement; // 3 its Achievement parent (can`t start while parent uncomplete, use its Criteria if don`t have own, use its progress on begin) - //char *name[16]; // 4-19 + char *name[16]; // 4-19 //uint32 name_flags; // 20 //char *description[16]; // 21-36 //uint32 desc_flags; // 37 diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h index 4ede36f76..20b60c388 100644 --- a/src/game/DBCfmt.h +++ b/src/game/DBCfmt.h @@ -19,7 +19,7 @@ #ifndef MANGOS_DBCSFRM_H #define MANGOS_DBCSFRM_H -const char Achievementfmt[]="niixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii"; +const char Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii"; const char AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiixix"; const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxxx"; const char AreaGroupEntryfmt[]="niiiiiii"; diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp index 0f4798471..70c386605 100644 --- a/src/game/LootMgr.cpp +++ b/src/game/LootMgr.cpp @@ -1322,7 +1322,7 @@ void LoadLootTemplates_Spell() { // not report about not trainable spells (optionally supported by DB) // ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example - if (!(spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) || (spellInfo->Attributes & SPELL_ATTR_UNK5)) + if (!(spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) || (spellInfo->Attributes & SPELL_ATTR_TRADESPELL)) { LootTemplates_Spell.ReportNotExistedId(spell_id); } diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index 0f48fe60b..cddb39594 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -511,43 +511,41 @@ void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvdata*/) GetPlayer()->SendMessageToSet(&data, false); } -void WorldSession::HandleMoveKnockBackAck( WorldPacket & /*recv_data*/ ) +void WorldSession::HandleMoveKnockBackAck( WorldPacket & recv_data ) { sLog.outDebug("CMSG_MOVE_KNOCK_BACK_ACK"); - // Currently not used but maybe use later for recheck final player position - // (must be at call same as into "recv_data >> x >> y >> z >> orientation;" - /* - uint32 flags, time; - float x, y, z, orientation; - uint64 guid; - uint32 sequence; - uint32 ukn1; - float xdirection,ydirection,hspeed,vspeed; + recv_data.read_skip(); // guid + recv_data.read_skip(); // unk - recv_data >> guid; - recv_data >> sequence; - recv_data >> flags >> time; - recv_data >> x >> y >> z >> orientation; - recv_data >> ukn1; //unknown - recv_data >> vspeed >> xdirection >> ydirection >> hspeed; - - // skip not personal message; - if(GetPlayer()->GetGUID()!=guid) - return; - - // check code - */ + MovementInfo movementInfo; + ReadMovementInfo(recv_data, &movementInfo); } -void WorldSession::HandleMoveHoverAck( WorldPacket& /*recv_data*/ ) +void WorldSession::HandleMoveHoverAck( WorldPacket& recv_data ) { sLog.outDebug("CMSG_MOVE_HOVER_ACK"); + + recv_data.read_skip(); // guid + recv_data.read_skip(); // unk + + MovementInfo movementInfo; + ReadMovementInfo(recv_data, &movementInfo); + + recv_data.read_skip(); // unk2 } -void WorldSession::HandleMoveWaterWalkAck(WorldPacket& /*recv_data*/) +void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recv_data) { sLog.outDebug("CMSG_MOVE_WATER_WALK_ACK"); + + recv_data.read_skip(); // guid + recv_data.read_skip(); // unk + + MovementInfo movementInfo; + ReadMovementInfo(recv_data, &movementInfo); + + recv_data.read_skip(); // unk2 } void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data) diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index fdcb99832..03069a440 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -2034,10 +2034,8 @@ void ObjectMgr::LoadItemRequiredTarget() if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE || pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) { - SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(pSpellInfo->Id); - SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(pSpellInfo->Id); - - if (lower != upper) + SpellScriptTargetBounds bounds = spellmgr.GetSpellScriptTargetBounds(pSpellInfo->Id); + if (bounds.first != bounds.second) break; for (int j = 0; j < 3; ++j) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 2281a44c8..660c2b1af 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -3001,7 +3001,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned) // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive - if( talentCost > 0 && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_LEARN_SPELL) ) + if (talentCost > 0 && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_LEARN_SPELL)) { // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show) CastSpell(this, spell_id, true); @@ -3009,10 +3009,10 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks else if (IsPassiveSpell(spell_id)) { - if(IsNeedCastPassiveSpellAtLearn(spellInfo)) + if (IsNeedCastPassiveSpellAtLearn(spellInfo)) CastSpell(this, spell_id, true); } - else if( IsSpellHaveEffect(spellInfo,SPELL_EFFECT_SKILL_STEP) ) + else if (IsSpellHaveEffect(spellInfo,SPELL_EFFECT_SKILL_STEP)) { CastSpell(this, spell_id, true); return false; @@ -3022,7 +3022,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen m_usedTalentCount += talentCost; // update free primary prof.points (if any, can be none in case GM .learn prof. learning) - if(uint32 freeProfs = GetFreePrimaryProfessionPoints()) + if (uint32 freeProfs = GetFreePrimaryProfessionPoints()) { if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell_id)) SetFreePrimaryProfessions(freeProfs-1); @@ -3033,20 +3033,19 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen SpellLearnSkillNode const* spellLearnSkill = spellmgr.GetSpellLearnSkill(spell_id); - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id); + SkillLineAbilityMapBounds skill_bounds = spellmgr.GetSkillLineAbilityMapBounds(spell_id); - if(spellLearnSkill) + if (spellLearnSkill) { uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill); uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill); - if(skill_value < spellLearnSkill->value) + if (skill_value < spellLearnSkill->value) skill_value = spellLearnSkill->value; uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue; - if(skill_max_value < new_skill_max_value) + if (skill_max_value < new_skill_max_value) skill_max_value = new_skill_max_value; SetSkill(spellLearnSkill->skill,skill_value,skill_max_value); @@ -3054,16 +3053,16 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen else { // not ranked skills - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) + for(SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx) { SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId); - if(!pSkill) + if (!pSkill) continue; - if(HasSkill(pSkill->id)) + if (HasSkill(pSkill->id)) continue; - if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || + if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL (pSkill->id==SKILL_LOCKPICKING || pSkill->id==SKILL_RUNEFORGING) && _spell_idx->second->max_value==0 ) { @@ -3086,24 +3085,23 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen } // learn dependent spells - SpellLearnSpellMap::const_iterator spell_begin = spellmgr.GetBeginSpellLearnSpell(spell_id); - SpellLearnSpellMap::const_iterator spell_end = spellmgr.GetEndSpellLearnSpell(spell_id); + SpellLearnSpellMapBounds spell_bounds = spellmgr.GetSpellLearnSpellMapBounds(spell_id); - for(SpellLearnSpellMap::const_iterator itr2 = spell_begin; itr2 != spell_end; ++itr2) + for(SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2) { - if(!itr2->second.autoLearned) + if (!itr2->second.autoLearned) { - if(!IsInWorld() || !itr2->second.active) // at spells loading, no output, but allow save + if (!IsInWorld() || !itr2->second.active) // at spells loading, no output, but allow save addSpell(itr2->second.spell,itr2->second.active,true,true,false); else // at normal learning learnSpell(itr2->second.spell,true); } } - if(!GetSession()->PlayerLoading()) + if (!GetSession()->PlayerLoading()) { // not ranked skills - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) + for(SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first; _spell_idx != skill_bounds.second; ++_spell_idx) { GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE,_spell_idx->second->skillId); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS,_spell_idx->second->skillId); @@ -3238,19 +3236,19 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank) prevSkill = spellmgr.GetSpellLearnSkill(spellmgr.GetFirstSpellInChain(prev_spell)); } - if(!prevSkill) // not found prev skill setting, remove skill + if (!prevSkill) // not found prev skill setting, remove skill SetSkill(spellLearnSkill->skill,0,0); else // set to prev. skill setting values { uint32 skill_value = GetPureSkillValue(prevSkill->skill); uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill); - if(skill_value > prevSkill->value) + if (skill_value > prevSkill->value) skill_value = prevSkill->value; uint32 new_skill_max_value = prevSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : prevSkill->maxvalue; - if(skill_max_value > new_skill_max_value) + if (skill_max_value > new_skill_max_value) skill_max_value = new_skill_max_value; SetSkill(prevSkill->skill,skill_value,skill_max_value); @@ -3261,22 +3259,22 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank) else { // not ranked skills - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id); + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spell_id); - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) + for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) { SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId); - if(!pSkill) + if (!pSkill) continue; - if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || + if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL && + pSkill->categoryId != SKILL_CATEGORY_CLASS ||// not unlearn class skills (spellbook/talent pages) // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL (pSkill->id==SKILL_LOCKPICKING || pSkill->id==SKILL_RUNEFORGING) && _spell_idx->second->max_value==0 ) { // not reset skills for professions and racial abilities - if( (pSkill->categoryId==SKILL_CATEGORY_SECONDARY || pSkill->categoryId==SKILL_CATEGORY_PROFESSION) && - (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask!=0) ) + if ((pSkill->categoryId==SKILL_CATEGORY_SECONDARY || pSkill->categoryId==SKILL_CATEGORY_PROFESSION) && + (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask!=0)) continue; SetSkill(pSkill->id, 0, 0 ); @@ -3285,43 +3283,42 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank) } // remove dependent spells - SpellLearnSpellMap::const_iterator spell_begin = spellmgr.GetBeginSpellLearnSpell(spell_id); - SpellLearnSpellMap::const_iterator spell_end = spellmgr.GetEndSpellLearnSpell(spell_id); + SpellLearnSpellMapBounds spell_bounds = spellmgr.GetSpellLearnSpellMapBounds(spell_id); - for(SpellLearnSpellMap::const_iterator itr2 = spell_begin; itr2 != spell_end; ++itr2) + for(SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2) removeSpell(itr2->second.spell, disabled); // activate lesser rank in spellbook/action bar, and cast it if need bool prev_activate = false; - if(uint32 prev_id = spellmgr.GetPrevSpellInChain (spell_id)) + if (uint32 prev_id = spellmgr.GetPrevSpellInChain (spell_id)) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id); // if talent then lesser rank also talent and need learn - if(talentCosts) + if (talentCosts) { if(learn_low_rank) learnSpell (prev_id,false); } // if ranked non-stackable spell: need activate lesser rank and update dendence state - else if(cur_active && !SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0) + else if (cur_active && !SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0) { // need manually update dependence state (learn spell ignore like attempts) PlayerSpellMap::iterator prev_itr = m_spells.find(prev_id); if (prev_itr != m_spells.end()) { - if(prev_itr->second->dependent != cur_dependent) + if (prev_itr->second->dependent != cur_dependent) { prev_itr->second->dependent = cur_dependent; - if(prev_itr->second->state != PLAYERSPELL_NEW) + if (prev_itr->second->state != PLAYERSPELL_NEW) prev_itr->second->state = PLAYERSPELL_CHANGED; } // now re-learn if need re-activate - if(cur_active && !prev_itr->second->active && learn_low_rank) + if (cur_active && !prev_itr->second->active && learn_low_rank) { - if(addSpell(prev_id,true,false,prev_itr->second->dependent,prev_itr->second->disabled)) + if (addSpell(prev_id,true,false,prev_itr->second->dependent,prev_itr->second->disabled)) { // downgrade spell ranks in spellbook and action bar WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4); @@ -5045,20 +5042,19 @@ bool Player::UpdateCraftSkill(uint32 spellid) { sLog.outDebug("UpdateCraftSkill spellid %d", spellid); - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid); + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellid); - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) + for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) { - if(_spell_idx->second->skillId) + if (_spell_idx->second->skillId) { uint32 SkillValue = GetPureSkillValue(_spell_idx->second->skillId); // Alchemy Discoveries here SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellid); - if(spellEntry && spellEntry->Mechanic==MECHANIC_DISCOVERY) + if (spellEntry && spellEntry->Mechanic==MECHANIC_DISCOVERY) { - if(uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this)) + if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this)) learnSpell(discoveredSpell,false); } @@ -18596,19 +18592,18 @@ bool Player::IsSpellFitByClassAndRace( uint32 spell_id ) const uint32 racemask = getRaceMask(); uint32 classmask = getClassMask(); - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id); - if(lower==upper) + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spell_id); + if (bounds.first==bounds.second) return true; - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) + for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) { // skip wrong race skills - if( _spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0) + if (_spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0) continue; // skip wrong class skills - if( _spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0) + if (_spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0) continue; return true; diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index d00305557..096aa775d 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -207,6 +207,18 @@ enum ItemQualities #define MAX_ITEM_QUALITY 8 +const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = { + 0xff9d9d9d, //GREY + 0xffffffff, //WHITE + 0xff1eff00, //GREEN + 0xff0070dd, //BLUE + 0xffa335ee, //PURPLE + 0xffff8000, //ORANGE + 0xffe6cc80, //LIGHT YELLOW + 0xffe6cc80 //LIGHT YELLOW +}; + + // *********************************** // Spell Attributes definitions // *********************************** @@ -216,7 +228,7 @@ enum ItemQualities #define SPELL_ATTR_ON_NEXT_SWING_1 0x00000004 // 2 on next swing #define SPELL_ATTR_UNK3 0x00000008 // 3 not set in 3.0.3 #define SPELL_ATTR_UNK4 0x00000010 // 4 -#define SPELL_ATTR_UNK5 0x00000020 // 5 trade spells? +#define SPELL_ATTR_TRADESPELL 0x00000020 // 5 trade spells, will be added by client to a sublist of profession spell #define SPELL_ATTR_PASSIVE 0x00000040 // 6 Passive spell #define SPELL_ATTR_UNK7 0x00000080 // 7 visible? #define SPELL_ATTR_UNK8 0x00000100 // 8 @@ -658,7 +670,7 @@ enum SpellEffects SPELL_EFFECT_CALL_PET = 135, SPELL_EFFECT_HEAL_PCT = 136, SPELL_EFFECT_ENERGIZE_PCT = 137, - SPELL_EFFECT_138 = 138, + SPELL_EFFECT_LEAP_BACK = 138, SPELL_EFFECT_CLEAR_QUEST = 139, SPELL_EFFECT_FORCE_CAST = 140, SPELL_EFFECT_141 = 141, @@ -2309,6 +2321,16 @@ enum ChatMsg #define MAX_CHAT_MSG_TYPE 0x32 +enum ChatLinkColors +{ + CHAT_LINK_COLOR_TRADE = 0xffffd000, // orange + CHAT_LINK_COLOR_TALENT = 0xff4e96f7, // blue + CHAT_LINK_COLOR_SPELL = 0xff71d5ff, // bright blue + CHAT_LINK_COLOR_ENCHANT = 0xffffd000, // orange + CHAT_LINK_COLOR_ACHIEVEMENT = 0xffffff00, + CHAT_LINK_COLOR_GLYPH = 0xff66bbff +}; + // Values from ItemPetFood (power of (value-1) used for compare with CreatureFamilyEntry.petDietMask enum PetDiet { diff --git a/src/game/SkillDiscovery.cpp b/src/game/SkillDiscovery.cpp index 6f04a8571..91c915ff6 100644 --- a/src/game/SkillDiscovery.cpp +++ b/src/game/SkillDiscovery.cpp @@ -114,21 +114,18 @@ void LoadSkillDiscoveryTable() SkillDiscoveryStore[reqSkillOrSpell].push_back( SkillDiscoveryEntry(spellId, reqSkillValue, chance) ); } - else if( reqSkillOrSpell == 0 ) // skill case + else if (reqSkillOrSpell == 0) // skill case { - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellId); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellId); + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellId); - if(lower==upper) + if (bounds.first==bounds.second) { sLog.outErrorDb("Spell (ID: %u) not listed in `SkillLineAbility.dbc` but listed with `reqSpell`=0 in `skill_discovery_template` table",spellId); continue; } - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) - { + for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back( SkillDiscoveryEntry(spellId, reqSkillValue, chance) ); - } } else { @@ -143,21 +140,21 @@ void LoadSkillDiscoveryTable() sLog.outString(); sLog.outString( ">> Loaded %u skill discovery definitions", count ); - if(!ssNonDiscoverableEntries.str().empty()) + if (!ssNonDiscoverableEntries.str().empty()) sLog.outErrorDb("Some items can't be successfully discovered: have in chance field value < 0.000001 in `skill_discovery_template` DB table . List:\n%s",ssNonDiscoverableEntries.str().c_str()); // report about empty data for explicit discovery spells for(uint32 spell_id = 1; spell_id < sSpellStore.GetNumRows(); ++spell_id) { SpellEntry const* spellEntry = sSpellStore.LookupEntry(spell_id); - if(!spellEntry) + if (!spellEntry) continue; // skip not explicit discovery spells if (!IsExplicitDiscoverySpell(spellEntry)) continue; - if(SkillDiscoveryStore.find(spell_id)==SkillDiscoveryStore.end()) + if (SkillDiscoveryStore.find(spell_id)==SkillDiscoveryStore.end()) sLog.outErrorDb("Spell (ID: %u) is 100%% chance random discovery ability but not have data in `skill_discovery_template` table",spell_id); } } @@ -170,9 +167,8 @@ uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player) if (tab == SkillDiscoveryStore.end()) return 0; - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellId); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellId); - uint32 skillvalue = lower != upper ? player->GetSkillValue(lower->second->skillId) : 0; + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellId); + uint32 skillvalue = bounds.first != bounds.second ? player->GetSkillValue(bounds.first->second->skillId) : 0; float full_chance = 0; for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 24b155e62..2d894fbca 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -3966,15 +3966,15 @@ SpellCastResult Spell::CheckCast(bool strict) } // Spell casted only on battleground - if((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND) && m_caster->GetTypeId() == TYPEID_PLAYER) + if ((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND) && m_caster->GetTypeId() == TYPEID_PLAYER) if(!((Player*)m_caster)->InBattleGround()) return SPELL_FAILED_ONLY_BATTLEGROUNDS; // do not allow spells to be cast in arenas // - with greater than 15 min CD without SPELL_ATTR_EX4_USABLE_IN_ARENA flag // - with SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA flag - if( (m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) || - GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * IN_MILISECONDS && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) ) + if ((m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) || + GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * IN_MILISECONDS && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA)) if(MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId())) if(mapEntry->IsBattleArena()) return SPELL_FAILED_NOT_IN_ARENA; @@ -3985,21 +3985,21 @@ SpellCastResult Spell::CheckCast(bool strict) SpellCastResult locRes= spellmgr.GetSpellAllowedInLocationError(m_spellInfo,m_caster->GetMapId(),zone,area, m_caster->GetTypeId() == TYPEID_PLAYER ? ((Player*)m_caster) : NULL); - if(locRes != SPELL_CAST_OK) + if (locRes != SPELL_CAST_OK) return locRes; // not let players cast spells at mount (and let do it to creatures) - if( m_caster->IsMounted() && m_caster->GetTypeId()==TYPEID_PLAYER && !m_IsTriggeredSpell && - !IsPassiveSpell(m_spellInfo->Id) && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_MOUNTED) ) + if (m_caster->IsMounted() && m_caster->GetTypeId()==TYPEID_PLAYER && !m_IsTriggeredSpell && + !IsPassiveSpell(m_spellInfo->Id) && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_MOUNTED)) { - if(m_caster->isInFlight()) + if (m_caster->isInFlight()) return SPELL_FAILED_NOT_FLYING; else return SPELL_FAILED_NOT_MOUNTED; } // always (except passive spells) check items (focus object can be required for any type casts) - if(!IsPassiveSpell(m_spellInfo->Id)) + if (!IsPassiveSpell(m_spellInfo->Id)) { SpellCastResult castResult = CheckItems(); if(castResult != SPELL_CAST_OK) @@ -4007,7 +4007,7 @@ SpellCastResult Spell::CheckCast(bool strict) } //ImpliciteTargetA-B = 38, If fact there is 0 Spell with ImpliciteTargetB=38 - if(m_UniqueTargetInfo.empty()) // skip second CheckCast apply (for delayed spells for example) + if (m_UniqueTargetInfo.empty()) // skip second CheckCast apply (for delayed spells for example) { for(uint8 j = 0; j < 3; ++j) { @@ -4016,9 +4016,8 @@ SpellCastResult Spell::CheckCast(bool strict) m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES || m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES ) { - SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(m_spellInfo->Id); - SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id); - if(lower==upper) + SpellScriptTargetBounds bounds = spellmgr.GetSpellScriptTargetBounds(m_spellInfo->Id); + if(bounds.first==bounds.second) sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = TARGET_SCRIPT or TARGET_SCRIPT_COORDINATES, but does not have record in `spell_script_target`",m_spellInfo->Id); SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); @@ -4027,7 +4026,7 @@ SpellCastResult Spell::CheckCast(bool strict) Creature* creatureScriptTarget = NULL; GameObject* goScriptTarget = NULL; - for(SpellScriptTarget::const_iterator i_spellST = lower; i_spellST != upper; ++i_spellST) + for(SpellScriptTarget::const_iterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) { switch(i_spellST->second.type) { @@ -4035,7 +4034,7 @@ SpellCastResult Spell::CheckCast(bool strict) { GameObject* p_GameObject = NULL; - if(i_spellST->second.targetEntry) + if (i_spellST->second.targetEntry) { CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY())); Cell cell(p); @@ -4048,7 +4047,7 @@ SpellCastResult Spell::CheckCast(bool strict) CellLock cell_lock(cell, p); cell_lock->Visit(cell_lock, object_checker, *m_caster->GetMap()); - if(p_GameObject) + if (p_GameObject) { // remember found target and range, next attempt will find more near target with another entry creatureScriptTarget = NULL; @@ -4056,10 +4055,10 @@ SpellCastResult Spell::CheckCast(bool strict) range = go_check.GetLastRange(); } } - else if( focusObject ) // Focus Object + else if (focusObject) // Focus Object { float frange = m_caster->GetDistance(focusObject); - if(range >= frange) + if (range >= frange) { creatureScriptTarget = NULL; goScriptTarget = focusObject; @@ -5952,4 +5951,4 @@ void Spell::FillRaidOrPartyHealthPriorityTargets( UnitList &TagUnitMap, Unit* me TagUnitMap.push_back(healthQueue.top().getUnit()); healthQueue.pop(); } -} +} \ No newline at end of file diff --git a/src/game/Spell.h b/src/game/Spell.h index 8e8dcf412..d3092c895 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -280,7 +280,8 @@ class Spell void EffectResurrect(uint32 i); void EffectParry(uint32 i); void EffectBlock(uint32 i); - void EffectMomentMove(uint32 i); + void EffectLeapForward(uint32 i); + void EffectLeapBack(uint32 i); void EffectTransmitted(uint32 i); void EffectDisEnchant(uint32 i); void EffectInebriate(uint32 i); diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 983afe46f..9e53018ea 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -1317,13 +1317,18 @@ void Aura::HandleAddModifier(bool apply, bool Real) ((Player*)m_target)->AddSpellMod(m_spellmod, apply); - // reapply some passive spells after add/remove related spellmods - if(m_spellProto->SpellFamilyName==SPELLFAMILY_WARRIOR && (spellFamilyMask & UI64LIT(0x0000100000000000))) - { - m_target->RemoveAurasDueToSpell(45471); + // reaplly talents to own passive persistent auras + std::set affectedPassives; - if(apply) - m_target->CastSpell(m_target, 45471, true); + for(Unit::AuraMap::const_iterator itr = m_target->GetAuras().begin(); itr != m_target->GetAuras().end(); ++itr) + if (itr->second->IsPassive() && itr->second->IsPermanent() && + itr->second->GetCasterGUID() == m_target->GetGUID() && isAffectedOnSpell(itr->second->GetSpellProto())) + affectedPassives.insert(itr->second->GetId()); + + for(std::set::const_iterator set_itr = affectedPassives.begin(); set_itr != affectedPassives.end(); ++set_itr) + { + m_target->RemoveAurasDueToSpell(*set_itr); + m_target->CastSpell(m_target, *set_itr, true); } } void Aura::HandleAddTargetTrigger(bool apply, bool /*Real*/) @@ -5993,93 +5998,102 @@ void Aura::HandleSchoolAbsorb(bool apply, bool Real) if(!caster) return; - // prevent double apply bonuses - if(apply && (m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading())) + if (apply) { - float DoneActualBenefit = 0.0f; - switch(m_spellProto->SpellFamilyName) + // prevent double apply bonuses + if (m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading()) { - case SPELLFAMILY_PRIEST: - // Power Word: Shield - if (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000000000001)) - //+80.68% from +spell bonus - DoneActualBenefit = caster->SpellBaseHealingBonus(GetSpellSchoolMask(m_spellProto)) * 0.8068f; - break; - case SPELLFAMILY_MAGE: - // Frost Ward, Fire Ward - if (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000000000108)) - //+10% from +spell bonus - DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.1f; - // Ice Barrier - else if (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000100000000)) - //+80.67% from +spell bonus - DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.8067f; - break; - case SPELLFAMILY_WARLOCK: - // Shadow Ward - if (m_spellProto->SpellFamilyFlags2 & 0x00000040) - //+30% from +spell bonus - DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.30f; - break; - default: - break; - } - - DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto()); - - m_modifier.m_amount += (int32)DoneActualBenefit; - } - - // Ice Barrier (remove effect from Shattered Barrier) - if(!apply && m_spellProto->SpellIconID == 32 && m_spellProto->SpellFamilyName == SPELLFAMILY_MAGE) - { - if (!((m_removeMode == AURA_REMOVE_BY_DEFAULT && !m_modifier.m_amount) || m_removeMode == AURA_REMOVE_BY_DISPEL)) - return; - - if (m_target->HasAura(44745,0)) // Shattered Barrier, rank 1 - { - if(roll_chance_i(50)) - m_target->CastSpell(m_target, 55080, true, NULL, this); - } - else if (m_target->HasAura(54787,0)) // Shattered Barrier, rank 2 - { - m_target->CastSpell(m_target, 55080, true, NULL, this); - } - } - - if (!apply && caster && - // Power Word: Shield - m_spellProto->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellProto->Mechanic == MECHANIC_SHIELD && - (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000000000001)) && - // completely absorbed or dispelled - ((m_removeMode == AURA_REMOVE_BY_DEFAULT && !m_modifier.m_amount) || m_removeMode == AURA_REMOVE_BY_DISPEL)) - { - Unit::AuraList const& vDummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator itr = vDummyAuras.begin(); itr != vDummyAuras.end(); itr++) - { - SpellEntry const* vSpell = (*itr)->GetSpellProto(); - - // Rapture (main spell) - if(vSpell->SpellFamilyName == SPELLFAMILY_PRIEST && vSpell->SpellIconID == 2894 && vSpell->Effect[1]) + float DoneActualBenefit = 0.0f; + switch(m_spellProto->SpellFamilyName) { - switch((*itr)->GetEffIndex()) - { - case 0: - { - // energize caster - int32 manapct1000 = 5 * ((*itr)->GetModifier()->m_amount + spellmgr.GetSpellRank(vSpell->Id)); - int32 basepoints0 = caster->GetMaxPower(POWER_MANA) * manapct1000 / 1000; - caster->CastCustomSpell(caster, 47755, &basepoints0, NULL, NULL, true); - break; - } - case 1: - { - // energize target - if (!roll_chance_i((*itr)->GetModifier()->m_amount) || caster->HasAura(63853)) - break; + case SPELLFAMILY_PRIEST: + // Power Word: Shield + if (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000000000001)) + //+80.68% from +spell bonus + DoneActualBenefit = caster->SpellBaseHealingBonus(GetSpellSchoolMask(m_spellProto)) * 0.8068f; + break; + case SPELLFAMILY_MAGE: + // Frost Ward, Fire Ward + if (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000000000108)) + //+10% from +spell bonus + DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.1f; + // Ice Barrier + else if (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000100000000)) + //+80.67% from +spell bonus + DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.8067f; + break; + case SPELLFAMILY_WARLOCK: + // Shadow Ward + if (m_spellProto->SpellFamilyFlags2 & 0x00000040) + //+30% from +spell bonus + DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.30f; + break; + case SPELLFAMILY_DRUID: + // Savage Defense (amount store original percent of attack power applied) + if (m_spellProto->SpellIconID == 50) // only spell with this aura fit + m_modifier.m_amount = int32(m_modifier.m_amount * m_target->GetTotalAttackPowerValue(BASE_ATTACK) / 100); + break; + default: + break; + } - switch(m_target->getPowerType()) + DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto()); + + m_modifier.m_amount += (int32)DoneActualBenefit; + } + } + else + { + // Ice Barrier (remove effect from Shattered Barrier) + if (m_spellProto->SpellIconID == 32 && m_spellProto->SpellFamilyName == SPELLFAMILY_MAGE) + { + if (!((m_removeMode == AURA_REMOVE_BY_DEFAULT && !m_modifier.m_amount) || m_removeMode == AURA_REMOVE_BY_DISPEL)) + return; + + if (m_target->HasAura(44745,0)) // Shattered Barrier, rank 1 + { + if(roll_chance_i(50)) + m_target->CastSpell(m_target, 55080, true, NULL, this); + } + else if (m_target->HasAura(54787,0)) // Shattered Barrier, rank 2 + { + m_target->CastSpell(m_target, 55080, true, NULL, this); + } + } + + if (caster && + // Power Word: Shield + m_spellProto->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellProto->Mechanic == MECHANIC_SHIELD && + (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000000000001)) && + // completely absorbed or dispelled + ((m_removeMode == AURA_REMOVE_BY_DEFAULT && !m_modifier.m_amount) || m_removeMode == AURA_REMOVE_BY_DISPEL)) + { + Unit::AuraList const& vDummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator itr = vDummyAuras.begin(); itr != vDummyAuras.end(); itr++) + { + SpellEntry const* vSpell = (*itr)->GetSpellProto(); + + // Rapture (main spell) + if(vSpell->SpellFamilyName == SPELLFAMILY_PRIEST && vSpell->SpellIconID == 2894 && vSpell->Effect[1]) + { + switch((*itr)->GetEffIndex()) + { + case 0: { + // energize caster + int32 manapct1000 = 5 * ((*itr)->GetModifier()->m_amount + spellmgr.GetSpellRank(vSpell->Id)); + int32 basepoints0 = caster->GetMaxPower(POWER_MANA) * manapct1000 / 1000; + caster->CastCustomSpell(caster, 47755, &basepoints0, NULL, NULL, true); + break; + } + case 1: + { + // energize target + if (!roll_chance_i((*itr)->GetModifier()->m_amount) || caster->HasAura(63853)) + break; + + switch(m_target->getPowerType()) + { case POWER_RUNIC_POWER: m_target->CastSpell(m_target, 63652, true, NULL, NULL, m_caster_guid); break; @@ -6087,25 +6101,26 @@ void Aura::HandleSchoolAbsorb(bool apply, bool Real) m_target->CastSpell(m_target, 63653, true, NULL, NULL, m_caster_guid); break; case POWER_MANA: - { - int32 basepoints0 = m_target->GetMaxPower(POWER_MANA) * 2 / 100; - m_target->CastCustomSpell(m_target, 63654, &basepoints0, NULL, NULL, true); - break; - } + { + int32 basepoints0 = m_target->GetMaxPower(POWER_MANA) * 2 / 100; + m_target->CastCustomSpell(m_target, 63654, &basepoints0, NULL, NULL, true); + break; + } case POWER_ENERGY: m_target->CastSpell(m_target, 63655, true, NULL, NULL, m_caster_guid); break; default: break; - } + } - //cooldwon aura - caster->CastSpell(caster, 63853, true); - break; + //cooldwon aura + caster->CastSpell(caster, 63853, true); + break; + } + default: + sLog.outError("Changes in R-dummy spell???: effect 3"); + break; } - default: - sLog.outError("Changes in R-dummy spell???: effect 3"); - break; } } } diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index f4ecd31eb..886eceb24 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -86,7 +86,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectUnused, // 26 SPELL_EFFECT_DEFENSE one spell: Defense &Spell::EffectPersistentAA, // 27 SPELL_EFFECT_PERSISTENT_AREA_AURA &Spell::EffectSummonType, // 28 SPELL_EFFECT_SUMMON - &Spell::EffectMomentMove, // 29 SPELL_EFFECT_LEAP + &Spell::EffectLeapForward, // 29 SPELL_EFFECT_LEAP &Spell::EffectEnergize, // 30 SPELL_EFFECT_ENERGIZE &Spell::EffectWeaponDmg, // 31 SPELL_EFFECT_WEAPON_PERCENT_DAMAGE &Spell::EffectTriggerMissileSpell, // 32 SPELL_EFFECT_TRIGGER_MISSILE @@ -195,7 +195,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectNULL, //135 SPELL_EFFECT_CALL_PET &Spell::EffectHealPct, //136 SPELL_EFFECT_HEAL_PCT &Spell::EffectEnergisePct, //137 SPELL_EFFECT_ENERGIZE_PCT - &Spell::EffectNULL, //138 SPELL_EFFECT_138 Leap + &Spell::EffectLeapBack, //138 SPELL_EFFECT_LEAP_BACK Leap back &Spell::EffectUnused, //139 SPELL_EFFECT_CLEAR_QUEST (misc - is quest ID) &Spell::EffectForceCast, //140 SPELL_EFFECT_FORCE_CAST &Spell::EffectNULL, //141 SPELL_EFFECT_141 damage and reduce speed? @@ -1234,6 +1234,11 @@ void Spell::EffectDummy(uint32 i) m_caster->CastSpell(m_caster,spell_id,true,NULL); return; } + case 60932: // Disengage (one from creature versions) + if (!unitTarget) + return; + m_caster->CastSpell(unitTarget,60934,true,NULL); + return; } //All IconID Check in there @@ -1568,6 +1573,28 @@ void Spell::EffectDummy(uint32 i) return; } + // Disengage + if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000400000000000)) + { + Unit* target = unitTarget; + uint32 spellid; + switch(m_spellInfo->Id) + { + case 781: // player case + target = m_caster; + spellid = 56446; + break; + case 57635: spellid = 57636; break; // one from creature cases + case 61507: spellid = 61508; break; // one from creature cases + default: + sLog.outError("Spell %u not handled propertly in EffectDummy(Disengage)",m_spellInfo->Id); + return; + } + if (!target || !target->isAlive()) + return; + m_caster->CastSpell(target,spellid,true,NULL); + } + switch(m_spellInfo->Id) { case 23989: // Readiness talent @@ -5985,7 +6012,7 @@ void Spell::EffectBlock(uint32 /*i*/) ((Player*)unitTarget)->SetCanBlock(true); } -void Spell::EffectMomentMove(uint32 i) +void Spell::EffectLeapForward(uint32 i) { if(unitTarget->isInFlight()) return; @@ -6013,6 +6040,14 @@ void Spell::EffectMomentMove(uint32 i) } } +void Spell::EffectLeapBack(uint32 i) +{ + if(unitTarget->isInFlight()) + return; + + m_caster->KnockBackFrom(unitTarget,float(m_spellInfo->EffectMiscValue[i])/10,float(damage)/10); +} + void Spell::EffectReputation(uint32 i) { if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) @@ -6242,25 +6277,10 @@ void Spell::EffectSummonCritter(uint32 i) void Spell::EffectKnockBack(uint32 i) { - if(!unitTarget || !m_caster) + if(!unitTarget) return; - // Effect only works on players - if(unitTarget->GetTypeId()!=TYPEID_PLAYER) - return; - - float vsin = sin(m_caster->GetAngle(unitTarget)); - float vcos = cos(m_caster->GetAngle(unitTarget)); - - WorldPacket data(SMSG_MOVE_KNOCK_BACK, 8+4+4+4+4+4); - data.append(unitTarget->GetPackGUID()); - data << uint32(0); // Sequence - data << float(vcos); // x direction - data << float(vsin); // y direction - data << float(m_spellInfo->EffectMiscValue[i])/10; // Horizontal speed - data << float(damage/-10); // Z Movement speed (vertical) - - ((Player*)unitTarget)->GetSession()->SendPacket(&data); + unitTarget->KnockBackFrom(m_caster,float(m_spellInfo->EffectMiscValue[i])/10,float(damage)/10); } void Spell::EffectSendTaxi(uint32 i) @@ -6273,26 +6293,10 @@ void Spell::EffectSendTaxi(uint32 i) void Spell::EffectPlayerPull(uint32 i) { - if(!unitTarget || !m_caster) + if(!unitTarget) return; - // Effect only works on players - if(unitTarget->GetTypeId()!=TYPEID_PLAYER) - return; - - float vsin = sin(unitTarget->GetAngle(m_caster)); - float vcos = cos(unitTarget->GetAngle(m_caster)); - - WorldPacket data(SMSG_MOVE_KNOCK_BACK, 8+4+4+4+4+4); - data.append(unitTarget->GetPackGUID()); - data << uint32(0); // Sequence - data << float(vcos); // x direction - data << float(vsin); // y direction - // Horizontal speed - data << float(damage ? damage : unitTarget->GetDistance2d(m_caster)); - data << float(m_spellInfo->EffectMiscValue[i])/-10; // Z Movement speed - - ((Player*)unitTarget)->GetSession()->SendPacket(&data); + unitTarget->KnockBackFrom(m_caster,float(damage ? damage : unitTarget->GetDistance2d(m_caster)),float(m_spellInfo->EffectMiscValue[i])/10); } void Spell::EffectDispelMechanic(uint32 i) diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 85e5389c0..20ef326b3 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -75,7 +75,7 @@ uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell) if(Player* modOwner = spell->GetCaster()->GetSpellModOwner()) modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell); - if( !(spellInfo->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_UNK5)) ) + if( !(spellInfo->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_TRADESPELL)) ) castTime = int32(castTime * spell->GetCaster()->GetFloatValue(UNIT_MOD_CAST_SPEED)); else { @@ -1693,16 +1693,15 @@ bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const bool SpellMgr::IsSkillBonusSpell(uint32 spellId) const { - SkillLineAbilityMap::const_iterator lower = GetBeginSkillLineAbilityMap(spellId); - SkillLineAbilityMap::const_iterator upper = GetEndSkillLineAbilityMap(spellId); + SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId); - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) + for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) { SkillLineAbilityEntry const *pAbility = _spell_idx->second; if (!pAbility || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL) continue; - if(pAbility->req_skill_value > 0) + if (pAbility->req_skill_value > 0) return true; } @@ -1964,7 +1963,7 @@ void SpellMgr::LoadSpellLearnSpells() // 0 1 2 QueryResult *result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell"); - if(!result) + if (!result) { barGoLink bar( 1 ); bar.step(); @@ -1990,19 +1989,19 @@ void SpellMgr::LoadSpellLearnSpells() node.active = fields[2].GetBool(); node.autoLearned= false; - if(!sSpellStore.LookupEntry(spell_id)) + if (!sSpellStore.LookupEntry(spell_id)) { sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",spell_id); continue; } - if(!sSpellStore.LookupEntry(node.spell)) + if (!sSpellStore.LookupEntry(node.spell)) { sLog.outErrorDb("Spell %u listed in `spell_learn_spell` learning not existed spell %u",spell_id,node.spell); continue; } - if(GetTalentSpellCost(node.spell)) + if (GetTalentSpellCost(node.spell)) { sLog.outErrorDb("Spell %u listed in `spell_learn_spell` attempt learning talent spell %u, skipped",spell_id,node.spell); continue; @@ -2021,7 +2020,7 @@ void SpellMgr::LoadSpellLearnSpells() { SpellEntry const* entry = sSpellStore.LookupEntry(spell); - if(!entry) + if (!entry) continue; for(int i = 0; i < 3; ++i) @@ -2033,7 +2032,7 @@ void SpellMgr::LoadSpellLearnSpells() dbc_node.active = true; // all dbc based learned spells is active (show in spell book or hide by client itself) // ignore learning not existed spells (broken/outdated/or generic learnig spell 483 - if(!sSpellStore.LookupEntry(dbc_node.spell)) + if (!sSpellStore.LookupEntry(dbc_node.spell)) continue; // talent or passive spells or skill-step spells auto-casted and not need dependent learning, @@ -2041,13 +2040,12 @@ void SpellMgr::LoadSpellLearnSpells() // other required explicit dependent learning dbc_node.autoLearned = entry->EffectImplicitTargetA[i]==TARGET_PET || GetTalentSpellCost(spell) > 0 || IsPassiveSpell(spell) || IsSpellHaveEffect(entry,SPELL_EFFECT_SKILL_STEP); - SpellLearnSpellMap::const_iterator db_node_begin = GetBeginSpellLearnSpell(spell); - SpellLearnSpellMap::const_iterator db_node_end = GetEndSpellLearnSpell(spell); + SpellLearnSpellMapBounds db_node_bounds = GetSpellLearnSpellMapBounds(spell); bool found = false; - for(SpellLearnSpellMap::const_iterator itr = db_node_begin; itr != db_node_end; ++itr) + for(SpellLearnSpellMap::const_iterator itr = db_node_bounds.first; itr != db_node_bounds.second; ++itr) { - if(itr->second.spell == dbc_node.spell) + if (itr->second.spell == dbc_node.spell) { sLog.outErrorDb("Spell %u auto-learn spell %u in spell.dbc then the record in `spell_learn_spell` is redundant, please fix DB.", spell,dbc_node.spell); @@ -2056,7 +2054,7 @@ void SpellMgr::LoadSpellLearnSpells() } } - if(!found) // add new spell-spell pair if not found + if (!found) // add new spell-spell pair if not found { mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell,dbc_node)); ++dbc_count; diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index a43e51437..96f93e035 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -545,6 +545,7 @@ struct SpellTargetEntry }; typedef std::multimap SpellScriptTarget; +typedef std::pair SpellScriptTargetBounds; // coordinates for spells (accessed using SpellMgr functions) struct SpellTargetPosition @@ -666,8 +667,10 @@ struct SpellLearnSpellNode }; typedef std::multimap SpellLearnSpellMap; +typedef std::pair SpellLearnSpellMapBounds; typedef std::multimap SkillLineAbilityMap; +typedef std::pair SkillLineAbilityMapBounds; typedef std::multimap PetLevelupSpellSet; typedef std::map PetLevelupSpellMap; @@ -883,22 +886,16 @@ class SpellMgr return mSpellLearnSpells.find(spell_id) != mSpellLearnSpells.end(); } - SpellLearnSpellMap::const_iterator GetBeginSpellLearnSpell(uint32 spell_id) const + SpellLearnSpellMapBounds GetSpellLearnSpellMapBounds(uint32 spell_id) const { - return mSpellLearnSpells.lower_bound(spell_id); - } - - SpellLearnSpellMap::const_iterator GetEndSpellLearnSpell(uint32 spell_id) const - { - return mSpellLearnSpells.upper_bound(spell_id); + return SpellLearnSpellMapBounds(mSpellLearnSpells.lower_bound(spell_id),mSpellLearnSpells.upper_bound(spell_id)); } bool IsSpellLearnToSpell(uint32 spell_id1,uint32 spell_id2) const { - SpellLearnSpellMap::const_iterator b = GetBeginSpellLearnSpell(spell_id1); - SpellLearnSpellMap::const_iterator e = GetEndSpellLearnSpell(spell_id1); - for(SpellLearnSpellMap::const_iterator i = b; i != e; ++i) - if(i->second.spell==spell_id2) + SpellLearnSpellMapBounds bounds = GetSpellLearnSpellMapBounds(spell_id1); + for(SpellLearnSpellMap::const_iterator i = bounds.first; i != bounds.second; ++i) + if (i->second.spell==spell_id2) return true; return false; } @@ -912,27 +909,17 @@ class SpellMgr // Spell script targets - SpellScriptTarget::const_iterator GetBeginSpellScriptTarget(uint32 spell_id) const + SpellScriptTargetBounds GetSpellScriptTargetBounds(uint32 spell_id) const { - return mSpellScriptTarget.lower_bound(spell_id); - } - - SpellScriptTarget::const_iterator GetEndSpellScriptTarget(uint32 spell_id) const - { - return mSpellScriptTarget.upper_bound(spell_id); + return SpellScriptTargetBounds(mSpellScriptTarget.lower_bound(spell_id),mSpellScriptTarget.upper_bound(spell_id)); } // Spell correctess for client using static bool IsSpellValid(SpellEntry const * spellInfo, Player* pl = NULL, bool msg = true); - SkillLineAbilityMap::const_iterator GetBeginSkillLineAbilityMap(uint32 spell_id) const + SkillLineAbilityMapBounds GetSkillLineAbilityMapBounds(uint32 spell_id) const { - return mSkillLineAbilityMap.lower_bound(spell_id); - } - - SkillLineAbilityMap::const_iterator GetEndSkillLineAbilityMap(uint32 spell_id) const - { - return mSkillLineAbilityMap.upper_bound(spell_id); + return SkillLineAbilityMapBounds(mSkillLineAbilityMap.lower_bound(spell_id),mSkillLineAbilityMap.upper_bound(spell_id)); } PetAura const* GetPetAura(uint32 spell_id, uint8 eff) diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 802c949f1..de59318a4 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -44,6 +44,7 @@ #include "CellImpl.h" #include "Path.h" #include "Traveller.h" +#include "VMapFactory.h" #include @@ -3436,17 +3437,17 @@ bool Unit::AddAura(Aura *Aur) switch(aurName) { // DoT/HoT/etc - case SPELL_AURA_PERIODIC_DAMAGE: // allow stack + case SPELL_AURA_PERIODIC_DAMAGE: // allow stack case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: case SPELL_AURA_PERIODIC_LEECH: case SPELL_AURA_PERIODIC_HEAL: case SPELL_AURA_OBS_MOD_HEALTH: case SPELL_AURA_PERIODIC_MANA_LEECH: - case SPELL_AURA_PERIODIC_ENERGIZE: case SPELL_AURA_OBS_MOD_MANA: case SPELL_AURA_POWER_BURN_MANA: break; - default: // not allow + case SPELL_AURA_PERIODIC_ENERGIZE: // all or self or clear non-stackable + default: // not allow // can be only single (this check done at _each_ aura add RemoveAura(i2,AURA_REMOVE_BY_STACK); stop = true; @@ -12051,3 +12052,47 @@ void Unit::SetPvP( bool state ) if(Creature *totem = GetMap()->GetCreature(m_TotemSlot[i])) totem->SetPvP(state); } + +void Unit::KnockBackFrom(Unit* target, float horizintalSpeed, float verticalSpeed) +{ + float angle = this == target ? GetOrientation() + M_PI : target->GetAngle(this); + float vsin = sin(angle); + float vcos = cos(angle); + + // Effect propertly implemented only for players + if(GetTypeId()==TYPEID_PLAYER) + { + WorldPacket data(SMSG_MOVE_KNOCK_BACK, 8+4+4+4+4+4); + data.append(GetPackGUID()); + data << uint32(0); // Sequence + data << float(vcos); // x direction + data << float(vsin); // y direction + data << float(horizintalSpeed); // Horizontal speed + data << float(-verticalSpeed); // Z Movement speed (vertical) + ((Player*)this)->GetSession()->SendPacket(&data); + } + else + { + float dis = horizintalSpeed; + + float ox, oy, oz; + GetPosition(ox, oy, oz); + + float fx = ox + dis * vcos; + float fy = oy + dis * vsin; + float fz = oz; + + float fx2, fy2, fz2; // getObjectHitPos overwrite last args in any result case + if(VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(GetMapId(), ox,oy,oz+0.5, fx,fy,oz+0.5,fx2,fy2,fz2, -0.5)) + { + fx = fx2; + fy = fy2; + fz = fz2; + UpdateGroundPositionZ(fx, fy, fz); + } + + //FIXME: this mostly hack, must exist some packet for proper creature move at client side + // with CreatureRelocation at server side + NearTeleportTo(fx, fy, fz, GetOrientation(), this == target); + } +} diff --git a/src/game/Unit.h b/src/game/Unit.h index 30875044d..385224077 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -1450,6 +1450,8 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void SetHover(bool on); bool isHover() const { return HasAuraType(SPELL_AURA_HOVER); } + void KnockBackFrom(Unit* target, float horizintalSpeed, float verticalSpeed); + void _RemoveAllAuraMods(); void _ApplyAllAuraMods(); diff --git a/src/game/World.cpp b/src/game/World.cpp index c4f7183e5..36452d110 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -922,6 +922,8 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_TALENTS_INSPECTING] = sConfig.GetBoolDefault("TalentsInspecting", true); m_configs[CONFIG_CHAT_FAKE_MESSAGE_PREVENTING] = sConfig.GetBoolDefault("ChatFakeMessagePreventing", false); + m_configs[CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY] = sConfig.GetIntDefault("ChatStrictLinkChecking.Severity", 0); + m_configs[CONFIG_CHAT_STRICT_LINK_CHECKING_KICK] = sConfig.GetIntDefault("ChatStrictLinkChecking.Kick", 0); m_configs[CONFIG_CORPSE_DECAY_NORMAL] = sConfig.GetIntDefault("Corpse.Decay.NORMAL", 60); m_configs[CONFIG_CORPSE_DECAY_RARE] = sConfig.GetIntDefault("Corpse.Decay.RARE", 300); diff --git a/src/game/World.h b/src/game/World.h index ad708f9af..f8d5b908a 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -178,6 +178,8 @@ enum WorldConfigs CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL, CONFIG_TALENTS_INSPECTING, CONFIG_CHAT_FAKE_MESSAGE_PREVENTING, + CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY, + CONFIG_CHAT_STRICT_LINK_CHECKING_KICK, CONFIG_CORPSE_DECAY_NORMAL, CONFIG_CORPSE_DECAY_RARE, CONFIG_CORPSE_DECAY_ELITE, diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index 0e96edc06..df4765695 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -563,6 +563,7 @@ class MANGOS_DLL_SPEC WorldSession void HandlePushQuestToParty(WorldPacket& recvPacket); void HandleQuestPushResult(WorldPacket& recvPacket); + bool processChatmessageFurtherAfterSecurityChecks(std::string&, uint32); void HandleMessagechatOpcode(WorldPacket& recvPacket); void HandleTextEmoteOpcode(WorldPacket& recvPacket); void HandleChatIgnoredOpcode(WorldPacket& recvPacket); diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index 0246f3393..a0dcb2195 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -826,6 +826,19 @@ ListenRange.Yell = 300 # Default: 0 (disible fake messages preventing) # 1 (enabled fake messages preventing) # +# ChatStrictLinkChecking.Severity +# Check chat messages for ingame links to spells, items, quests, achievements etc. +# Default: 0 (disable link checking) +# 1 (check if only valid pipe commands are used. This prevents posting pictures for example) +# 2 (verifiy that pipe commands are used in a correct order) +# 3 (check if color, entry and name don't contradict each other. For correct work, please assure +# that you have extracted locale DBCs of every language specific client playing on this server.) +# +# ChatStrictLinkChecking.Kick +# Defines, what should be done if a message is considered to contain invalid pipe commands. +# Default: 0 (silently ignore message) +# 1 (kick players who sent invalid formed messages) +# # ChatFlood.MessageCount # Chat anti-flood protection, haste message count to activate protection # Default: 10 @@ -852,6 +865,8 @@ ListenRange.Yell = 300 ################################################################################################################### ChatFakeMessagePreventing = 0 +ChatStrictLinkChecking.Severity = 0 +ChatStrictLinkChecking.Kick = 0 ChatFlood.MessageCount = 10 ChatFlood.MessageDelay = 1 ChatFlood.MuteTime = 10 diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index f34136be4..3428b512e 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 "8423" + #define REVISION_NR "8432" #endif // __REVISION_NR_H__