diff options
37 files changed, 913 insertions, 645 deletions
diff --git a/sql/mangos.sql b/sql/mangos.sql index 207e227d457..68253952147 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -22,7 +22,7 @@ DROP TABLE IF EXISTS `db_version`; CREATE TABLE `db_version` ( `version` varchar(120) default NULL, - `required_7156_01_mangos_spell_proc_event` bit(1) default NULL + `required_7175_01_mangos_spell_proc_event` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -322,7 +322,7 @@ INSERT INTO `command` VALUES ('instance savedata',3,'Syntax: .instance savedata\r\n Save the InstanceData for the current player\'s map to the DB.'), ('itemmove',2,'Syntax: .itemmove #sourceslotid #destinationslotid\r\n\r\nMove an item from slots #sourceslotid to #destinationslotid in your inventory\r\n\r\nNot yet implemented'), ('kick',2,'Syntax: .kick [$charactername]\r\n\r\nKick the given character name from the world. If no character name is provided then the selected player (except for yourself) will be kicked.'), -('learn',3,'Syntax: .learn #parameter\r\n\r\nSelected character learn a spell of id #parameter.'), +('learn',3,'Syntax: .learn #spell [all]\r\n\r\nSelected character learn a spell of id #spell. If \'all\' provided then all ranks learned.'), ('learn all',3,'Syntax: .learn all\r\n\r\nLearn all big set different spell maybe useful for Administaror.'), ('learn all_crafts',2,'Syntax: .learn crafts\r\n\r\nLearn all professions and recipes.'), ('learn all_default',1,'Syntax: .learn all_default [$playername]\r\n\r\nLearn for selected/$playername player all default spells for his race/class and spells rewarded by completed quests.'), @@ -464,7 +464,7 @@ INSERT INTO `command` VALUES ('unban account',3,'Syntax is: unban account $Name\r\nUnban accounts for account name pattern.'), ('unban character',3,'Syntax is: unban character $Name\r\nUnban accounts for character name pattern.'), ('unban ip',3,'Syntax is: unban ip $Ip\r\nUnban accounts for IP pattern.'), -('unlearn',3,'Syntax: .unlearn #startspell #endspell\r\n\r\nUnlearn for selected player the range of spells between id #startspell and #endspell. If no #endspell is provided, just unlearn spell of id #startspell.'), +('unlearn',3,'Syntax: .unlearn #spell [all]\r\n\r\nUnlearn for selected player a spell #spell. If \'all\' provided then all ranks unlearned.'), ('unmute',1,'Syntax: .unmute $playerName\r\n\r\nRestore chat messaging for any character from account of character $playerName.'), ('waterwalk',2,'Syntax: .waterwalk on/off\r\n\r\nSet on/off waterwalk state for selected player.'), ('wchange',3,'Syntax: .wchange #weathertype #status\r\n\r\nSet current weather to #weathertype with an intensity of #status.\r\n\r\n#weathertype can be 1 for rain, 2 for snow, and 3 for sand. #status can be 0 for disabled, and 1 for enabled.'), @@ -17106,6 +17106,9 @@ INSERT INTO `spell_proc_event` VALUES (55680, 0x00000000, 6, 0x00000200, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (55689, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), (56218, 0x00000000, 5, 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), +(56333, 0x00000000, 9, 0x00000004, 0x00000000, 0x00000200, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), +(56336, 0x00000000, 9, 0x00000004, 0x00000000, 0x00000200, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), +(56337, 0x00000000, 9, 0x00000004, 0x00000000, 0x00000200, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (56342, 0x00000000, 9, 0x00004000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (56343, 0x00000000, 9, 0x00004000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (56344, 0x00000000, 9, 0x00004000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), diff --git a/sql/updates/7156_01_mangos_spell_proc_event.sql b/sql/updates/7156_01_mangos_spell_proc_event.sql new file mode 100644 index 00000000000..82e9d264932 --- /dev/null +++ b/sql/updates/7156_01_mangos_spell_proc_event.sql @@ -0,0 +1,5 @@ +ALTER TABLE db_version CHANGE COLUMN required_7150_01_mangos_playercreateinfo_spell required_7156_01_mangos_spell_proc_event bit; + +-- (44401) Missile Barrage () +DELETE FROM `spell_proc_event` WHERE `entry` IN (44401); +INSERT INTO `spell_proc_event` VALUES (44401, 0x00, 3, 0x00200000, 0x00000000, 0x00000000, 0x00000000, 0x00000FFF, 0.000000, 0.000000, 0); diff --git a/sql/updates/7168_01_mangos_command.sql b/sql/updates/7168_01_mangos_command.sql new file mode 100644 index 00000000000..67bbd5e3194 --- /dev/null +++ b/sql/updates/7168_01_mangos_command.sql @@ -0,0 +1,6 @@ +ALTER TABLE db_version CHANGE COLUMN required_7156_01_mangos_spell_proc_event required_7168_01_mangos_command bit; + +DELETE FROM `command` WHERE `name` IN ('learn','unlearn'); +INSERT INTO `command` VALUES +('learn',3,'Syntax: .learn #spell [all]\r\n\r\nSelected character learn a spell of id #spell. If \'all\' provided then all ranks learned.'), +('unlearn',3,'Syntax: .unlearn #spell [all]\r\n\r\nUnlearn for selected player a spell #spell. If \'all\' provided then all ranks unlearned.'); diff --git a/sql/updates/7175_01_mangos_spell_proc_event.sql b/sql/updates/7175_01_mangos_spell_proc_event.sql new file mode 100644 index 00000000000..57e02defea4 --- /dev/null +++ b/sql/updates/7175_01_mangos_spell_proc_event.sql @@ -0,0 +1,13 @@ +ALTER TABLE db_version CHANGE COLUMN required_7168_01_mangos_command required_7175_01_mangos_spell_proc_event bit; + +-- (56333) T.N.T. (Rank 1) +DELETE FROM `spell_proc_event` WHERE `entry` IN (56333); +INSERT INTO `spell_proc_event` VALUES (56333, 0x00, 9, 0x00000004, 0x00000000, 0x00000200, 0x00000000, 0x00000000, 0.000000, 0.000000, 0); + +-- (56336) T.N.T. (Rank 2) +DELETE FROM `spell_proc_event` WHERE `entry` IN (56336); +INSERT INTO `spell_proc_event` VALUES (56336, 0x00, 9, 0x00000004, 0x00000000, 0x00000200, 0x00000000, 0x00000000, 0.000000, 0.000000, 0); + +-- (56337) T.N.T. (Rank 3) +DELETE FROM `spell_proc_event` WHERE `entry` IN (56337); +INSERT INTO `spell_proc_event` VALUES (56337, 0x00, 9, 0x00000004, 0x00000000, 0x00000200, 0x00000000, 0x00000000, 0.000000, 0.000000, 0);
\ No newline at end of file diff --git a/src/bindings/scripts/include/sc_creature.cpp b/src/bindings/scripts/include/sc_creature.cpp index cbbb574d253..d18a65f66f5 100644 --- a/src/bindings/scripts/include/sc_creature.cpp +++ b/src/bindings/scripts/include/sc_creature.cpp @@ -615,7 +615,7 @@ void FillSpellSummary() //Spell targets AoE at enemy if ( TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA || TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || - TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER || + TempSpell->EffectImplicitTargetA[j] == TARGET_DEST_CASTER || TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED ) SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_ENEMY-1); @@ -624,7 +624,7 @@ void FillSpellSummary() TempSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_ENEMY_COORDINATES || TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA || TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || - TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER || + TempSpell->EffectImplicitTargetA[j] == TARGET_DEST_CASTER || TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED ) SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_ENEMY-1); @@ -637,7 +637,7 @@ void FillSpellSummary() //Spell targets aoe friends if ( TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER || TempSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY || - TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER) + TempSpell->EffectImplicitTargetA[j] == TARGET_DEST_CASTER) SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_FRIEND-1); //Spell targets any friend(or self) @@ -646,7 +646,7 @@ void FillSpellSummary() TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY || TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER || TempSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY || - TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER) + TempSpell->EffectImplicitTargetA[j] == TARGET_DEST_CASTER) SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_FRIEND-1); //Make sure that this spell includes a damage effect diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index ad8a4306109..36fdb216d17 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -325,6 +325,10 @@ void AchievementMgr::CheckAllAchievementCriteria() void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time) { sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time); + + if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER) + return; + AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type); for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i) { @@ -930,7 +934,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList() } sLog.outString(); - sLog.outErrorDb(">> Loaded %u achievement criteria.",m_AchievementCriteriasByType->size()); + sLog.outString(">> Loaded %u achievement criteria.",m_AchievementCriteriasByType->size()); } diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index 1065eecfa29..91a73bb2ae6 100644 --- a/src/game/BattleGroundMgr.cpp +++ b/src/game/BattleGroundMgr.cpp @@ -1244,17 +1244,18 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) { uint8 type = (bg->isArena() ? 1 : 0); - // last check on 2.4.1 + // last check on 3.0.3 data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize())); - *data << uint8(type); // seems to be type (battleground=0/arena=1) + *data << uint8(type); // type (battleground=0/arena=1) if(type) // arena { // it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H for(int i = 1; i >= 0; --i) { - *data << uint32(3000-bg->m_ArenaTeamRatingChanges[i]); // rating change: showed value - 3000 + *data << uint32(bg->m_ArenaTeamRatingChanges[i]); *data << uint32(3999); // huge thanks for TOM_RUS for this! + *data << uint32(0); // unknown - new in 3.0.3 sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]); } for(int i = 1; i >= 0; --i) @@ -1268,9 +1269,9 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) } } - if(bg->GetWinner() == 2) + if(bg->GetStatus() != STATUS_WAIT_LEAVE) { - *data << uint8(0); // bg in progress + *data << uint8(0); // bg not ended } else { @@ -1284,9 +1285,6 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) { *data << (uint64)itr->first; *data << (int32)itr->second->KillingBlows; - Player *plr = objmgr.GetPlayer(itr->first); - uint32 team = bg->GetPlayerTeam(itr->first); - if(!team && plr) team = plr->GetTeam(); if(type == 0) { *data << (int32)itr->second->HonorableKills; @@ -1295,18 +1293,12 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) } else { - // that part probably wrong - if(plr) - { - if(team == HORDE) - *data << uint8(0); - else if(team == ALLIANCE) - { - *data << uint8(1); - } - else - *data << uint8(0); - } + Player *plr = objmgr.GetPlayer(itr->first); + uint32 team = bg->GetPlayerTeam(itr->first); + if(!team && plr) + team = plr->GetTeam(); + if( ( bg->GetWinner()==0 && team == ALLIANCE ) || ( bg->GetWinner()==1 && team==HORDE ) ) + *data << uint8(1); else *data << uint8(0); } diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index 09eb5594f62..42fe369e1d4 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -36,6 +36,22 @@ #include "AccountMgr.h" #include "TicketMgr.h" +// Supported shift-links (client generated and server side) +// |color|Harea:area_id|h[name]|h|r +// |color|Hcreature:creature_guid|h[name]|h|r +// |color|Hcreature_entry:creature_id|h[name]|h|r +// |color|Hgameevent:id|h[name]|h|r +// |color|Hgameobject:go_guid|h[name]|h|r +// |color|Hgameobject_entry:go_id|h[name]|h|r +// |color|Hitem:item_id:perm_ench_id:0:0|h[name]|h|r +// |color|Hitemset:itemset_id|h[name]|h|r +// |color|Hquest:quest_id|h[name]|h|r +// |color|Hskill:skill_id|h[name]|h|r +// |color|Hspell:spell_id|h[name]|h|r - client, spellbook spell icon shift-click +// |color|Htalent:talent_id,rank|h[name]|h|r - client, talent icon shift-click +// |color|Htele:id|h[name]|h|r +// |color|Htrade:spell_id,cur_value,max_value,unk3int,unk3str|h[name]|h|r - client, spellbook profession icon shift-click + bool ChatHandler::load_command_table = true; ChatCommand * ChatHandler::getCommandTable() @@ -1317,9 +1333,18 @@ GameObject* ChatHandler::GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid return obj; } -static char const* const spellTalentKeys[] = { - "Hspell", - "Htalent", +enum SpellLinkType +{ + SPELL_LINK_SPELL = 0, + SPELL_LINK_TALENT = 1, + SPELL_LINK_TRADE = 2 +}; + +static char const* const spellKeys[] = +{ + "Hspell", // normal spell + "Htalent", // talent spell + "Htrade", // profession/skill spell 0 }; @@ -1327,31 +1352,41 @@ uint32 ChatHandler::extractSpellIdFromLink(char* text) { // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r // number or [name] Shift-click form |color|Htalent:talent_id,rank|h[name]|h|r + // number or [name] Shift-click form |color|Htrade:spell_id,skill_id,max_value,cur_value|h[name]|h|r int type = 0; - char* rankS = NULL; - char* idS = extractKeyFromLink(text,spellTalentKeys,&type,&rankS); + char* param1_str = NULL; + char* idS = extractKeyFromLink(text,spellKeys,&type,¶m1_str); if(!idS) return 0; uint32 id = (uint32)atol(idS); - // spell - if(type==0) - return id; + switch(type) + { + case SPELL_LINK_SPELL: + return id; + case SPELL_LINK_TALENT: + { + // talent + TalentEntry const* talentEntry = sTalentStore.LookupEntry(id); + if(!talentEntry) + return 0; - // talent - TalentEntry const* talentEntry = sTalentStore.LookupEntry(id); - if(!talentEntry) - return 0; + int32 rank = param1_str ? (uint32)atol(param1_str) : 0; + if(rank >= 5) + return 0; - int32 rank = rankS ? (uint32)atol(rankS) : 0; - if(rank >= 5) - return 0; + if(rank < 0) + rank = 0; - if(rank < 0) - rank = 0; + return talentEntry->RankID[rank]; + } + case SPELL_LINK_TRADE: + return id; + } - return talentEntry->RankID[rank]; + // unknown type? + return 0; } GameTele const* ChatHandler::extractGameTeleFromLink(char* text) diff --git a/src/game/Chat.h b/src/game/Chat.h index 73b561b164d..483f3c97863 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -499,8 +499,10 @@ class ChatHandler Player* getSelectedPlayer(); Creature* getSelectedCreature(); Unit* getSelectedUnit(); + char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL); char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL); + uint32 extractSpellIdFromLink(char* text); GameTele const* extractGameTeleFromLink(char* text); bool GetPlayerGroupAndGUIDByName(const char* cname, Player* &plr, Group* &group, uint64 &guid, bool offline = false); diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index a888ba6ec5b..99fe5f725bc 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -360,8 +360,8 @@ void GameObject::Update(uint32 /*p_time*/) { //Unit *caster = owner ? owner : ok; - //caster->CastSpell(ok, goInfo->trap.spellId, true); CastSpell(ok, goInfo->trap.spellId); + //caster->CastSpell(ok, goInfo->trap.spellId, true, 0, 0, GetGUID()); m_cooldownTime = time(NULL) + 4; // 4 seconds if(NeedDespawn) @@ -411,7 +411,7 @@ void GameObject::Update(uint32 /*p_time*/) for (; it != end; it++) { Unit* owner = Unit::GetUnit(*this, uint64(*it)); - if (owner) owner->CastSpell(owner, spellId, false); + if (owner) owner->CastSpell(owner, spellId, false, 0, 0, GetGUID()); } m_unique_users.clear(); @@ -826,7 +826,7 @@ void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target) // found correct GO // FIXME: when GO casting will be implemented trap must cast spell to target if(trapGO) - target->CastSpell(target,trapSpell,true); + target->CastSpell(target,trapSpell,true, 0, 0, GetGUID()); } GameObject* GameObject::LookupFishingHoleAround(float range) diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index 3105c630157..63b00300639 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -3742,7 +3742,7 @@ bool ChatHandler::HandleLearnAllCraftsCommand(const char* /*args*/) if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false)) continue; - m_session->GetPlayer()->learnSpell(skillLine->spellId); + m_session->GetPlayer()->learnSpell(skillLine->spellId,false); } } } @@ -3815,7 +3815,7 @@ bool ChatHandler::HandleLearnAllRecipesCommand(const char* args) continue; if( !target->HasSpell(spellInfo->Id) ) - m_session->GetPlayer()->learnSpell(skillLine->spellId); + m_session->GetPlayer()->learnSpell(skillLine->spellId,false); } uint16 maxLevel = target->GetPureMaxSkillValue(skillInfo->id); diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 5016d0bcd56..74f012b55d7 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -1292,7 +1292,7 @@ bool ChatHandler::HandleSetSkillCommand(const char* args) { // number or [name] Shift-click form |color|Hskill:skill_id|h[name]|h|r char* skill_p = extractKeyFromLink((char*)args,"Hskill"); - if(!skill_p) + if(!skill_p) return false; char *level_p = strtok (NULL, " "); @@ -1303,7 +1303,6 @@ bool ChatHandler::HandleSetSkillCommand(const char* args) char *max_p = strtok (NULL, " "); int32 skill = atoi(skill_p); - if (skill <= 0) { PSendSysMessage(LANG_INVALID_SKILL_ID, skill); @@ -1353,27 +1352,12 @@ bool ChatHandler::HandleUnLearnCommand(const char* args) return false; // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r - uint32 min_id = extractSpellIdFromLink((char*)args); - if(!min_id) + uint32 spell_id = extractSpellIdFromLink((char*)args); + if(!spell_id) return false; - // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r - char* tail = strtok(NULL,""); - - uint32 max_id = extractSpellIdFromLink(tail); - - if (!max_id) - { - // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r - max_id = min_id+1; - } - else - { - if (max_id < min_id) - std::swap(min_id,max_id); - - max_id=max_id+1; - } + char const* allStr = strtok(NULL," "); + bool allRanks = allStr ? (strncmp(allStr, "all", strlen(allStr)) == 0) : false; Player* target = getSelectedPlayer(); if(!target) @@ -1383,13 +1367,13 @@ bool ChatHandler::HandleUnLearnCommand(const char* args) return false; } - for(uint32 spell=min_id;spell<max_id;spell++) - { - if (target->HasSpell(spell)) - target->removeSpell(spell); - else - SendSysMessage(LANG_FORGET_SPELL); - } + if(allRanks) + spell_id = spellmgr.GetFirstSpellInChain (spell_id); + + if (target->HasSpell(spell_id)) + target->removeSpell(spell_id,false,!allRanks); + else + SendSysMessage(LANG_FORGET_SPELL); return true; } @@ -2054,7 +2038,7 @@ bool ChatHandler::HandleLearnAllCommand(const char* /*args*/) continue; } - m_session->GetPlayer()->learnSpell(spell); + m_session->GetPlayer()->learnSpell(spell,false); } SendSysMessage(LANG_COMMAND_LEARN_MANY_SPELLS); @@ -2094,7 +2078,7 @@ bool ChatHandler::HandleLearnAllGMCommand(const char* /*args*/) continue; } - m_session->GetPlayer()->learnSpell(spell); + m_session->GetPlayer()->learnSpell(spell,false); } SendSysMessage(LANG_LEARNING_GM_SKILLS); @@ -2142,27 +2126,13 @@ bool ChatHandler::HandleLearnAllMySpellsCommand(const char* /*args*/) if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false)) continue; - m_session->GetPlayer()->learnSpell(i); + m_session->GetPlayer()->learnSpell(i,false); } SendSysMessage(LANG_COMMAND_LEARN_CLASS_SPELLS); return true; } -static void learnAllHighRanks(Player* player, uint32 spellid) -{ - SpellChainNode const* node; - do - { - node = spellmgr.GetSpellChainNode(spellid); - player->learnSpell(spellid); - if (!node) - break; - spellid=node->next; - } - while (node->next); -} - bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/) { Player* player = m_session->GetPlayer(); @@ -2200,11 +2170,8 @@ bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/) if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false)) continue; - // learn highest rank of talent - player->learnSpell(spellid); - - // and learn all non-talent spell ranks (recursive by tree) - learnAllHighRanks(player,spellid); + // learn highest rank of talent and learn all non-talent spell ranks (recursive by tree) + player->learnSpellHighRank(spellid); } SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS); @@ -2215,7 +2182,7 @@ bool ChatHandler::HandleLearnAllLangCommand(const char* /*args*/) { // skipping UNIVERSAL language (0) for(int i = 1; i < LANGUAGES_COUNT; ++i) - m_session->GetPlayer()->learnSpell(lang_description[i].spell_id); + m_session->GetPlayer()->learnSpell(lang_description[i].spell_id,false); SendSysMessage(LANG_COMMAND_LEARN_ALL_LANG); return true; @@ -2271,25 +2238,31 @@ bool ChatHandler::HandleLearnCommand(const char* args) if(!spell || !sSpellStore.LookupEntry(spell)) return false; - if (targetPlayer->HasSpell(spell)) + char const* allStr = strtok(NULL," "); + bool allRanks = allStr ? (strncmp(allStr, "all", strlen(allStr)) == 0) : false; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); + if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer())) { - if(targetPlayer == m_session->GetPlayer()) - SendSysMessage(LANG_YOU_KNOWN_SPELL); - else - PSendSysMessage(LANG_TARGET_KNOWN_SPELL,targetPlayer->GetName()); + PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell); SetSentErrorMessage(true); return false; } - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); - if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer())) + if (!allRanks && targetPlayer->HasSpell(spell)) { - PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell); + if(targetPlayer == m_session->GetPlayer()) + SendSysMessage(LANG_YOU_KNOWN_SPELL); + else + PSendSysMessage(LANG_TARGET_KNOWN_SPELL,targetPlayer->GetName()); SetSentErrorMessage(true); return false; } - targetPlayer->learnSpell(spell); + if(allRanks) + targetPlayer->learnSpellHighRank(spell); + else + targetPlayer->learnSpell(spell,false); return true; } @@ -4703,11 +4676,28 @@ bool ChatHandler::HandleListAurasCommand (const char * /*args*/) for (Unit::AuraMap::const_iterator itr = uAuras.begin(); itr != uAuras.end(); ++itr) { bool talent = GetTalentSpellCost(itr->second->GetId()) > 0; - PSendSysMessage(LANG_COMMAND_TARGET_AURADETAIL, itr->second->GetId(), itr->second->GetEffIndex(), - itr->second->GetModifier()->m_auraname, itr->second->GetAuraDuration(), itr->second->GetAuraMaxDuration(), - itr->second->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()], - (itr->second->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""), - IS_PLAYER_GUID(itr->second->GetCasterGUID()) ? "player" : "creature",GUID_LOPART(itr->second->GetCasterGUID())); + + char const* name = itr->second->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()]; + + if (m_session) + { + std::ostringstream ss_name; + ss_name << "|cffffffff|Hspell:" << itr->second->GetId() << "|h[" << name << "]|h|r"; + + PSendSysMessage(LANG_COMMAND_TARGET_AURADETAIL, itr->second->GetId(), itr->second->GetEffIndex(), + itr->second->GetModifier()->m_auraname, itr->second->GetAuraDuration(), itr->second->GetAuraMaxDuration(), + ss_name.str().c_str(), + (itr->second->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""), + IS_PLAYER_GUID(itr->second->GetCasterGUID()) ? "player" : "creature",GUID_LOPART(itr->second->GetCasterGUID())); + } + else + { + PSendSysMessage(LANG_COMMAND_TARGET_AURADETAIL, itr->second->GetId(), itr->second->GetEffIndex(), + itr->second->GetModifier()->m_auraname, itr->second->GetAuraDuration(), itr->second->GetAuraMaxDuration(), + name, + (itr->second->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""), + IS_PLAYER_GUID(itr->second->GetCasterGUID()) ? "player" : "creature",GUID_LOPART(itr->second->GetCasterGUID())); + } } for (int i = 0; i < TOTAL_AURAS; i++) { @@ -4717,9 +4707,24 @@ bool ChatHandler::HandleListAurasCommand (const char * /*args*/) for (Unit::AuraList::const_iterator itr = uAuraList.begin(); itr != uAuraList.end(); ++itr) { bool talent = GetTalentSpellCost((*itr)->GetId()) > 0; - PSendSysMessage(LANG_COMMAND_TARGET_AURASIMPLE, (*itr)->GetId(), (*itr)->GetEffIndex(), - (*itr)->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()],((*itr)->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""), - IS_PLAYER_GUID((*itr)->GetCasterGUID()) ? "player" : "creature",GUID_LOPART((*itr)->GetCasterGUID())); + + char const* name = (*itr)->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()]; + + if (m_session) + { + std::ostringstream ss_name; + ss_name << "|cffffffff|Hspell:" << (*itr)->GetId() << "|h[" << name << "]|h|r"; + + PSendSysMessage(LANG_COMMAND_TARGET_AURASIMPLE, (*itr)->GetId(), (*itr)->GetEffIndex(), + ss_name.str().c_str(),((*itr)->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""), + IS_PLAYER_GUID((*itr)->GetCasterGUID()) ? "player" : "creature",GUID_LOPART((*itr)->GetCasterGUID())); + } + else + { + PSendSysMessage(LANG_COMMAND_TARGET_AURASIMPLE, (*itr)->GetId(), (*itr)->GetEffIndex(), + name,((*itr)->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""), + IS_PLAYER_GUID((*itr)->GetCasterGUID()) ? "player" : "creature",GUID_LOPART((*itr)->GetCasterGUID())); + } } } return true; diff --git a/src/game/NPCHandler.cpp b/src/game/NPCHandler.cpp index a99208e63bd..4f721c8a9de 100644 --- a/src/game/NPCHandler.cpp +++ b/src/game/NPCHandler.cpp @@ -243,24 +243,22 @@ void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data ) _player->ModifyMoney( -int32(nSpellCost) ); + WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12); // visual effect on trainer + data << uint64(guid) << uint32(0xB3); + SendPacket(&data); + + data.Initialize(SMSG_PLAY_SPELL_IMPACT, 12); // visual effect on player + data << uint64(_player->GetGUID()) << uint32(0x016A); + SendPacket(&data); + // learn explicitly or cast explicitly if(trainer_spell->IsCastable ()) //FIXME: prof. spell entry in trainer list not marked gray until list re-open. - unit->CastSpell(_player,trainer_spell->spell,true); + _player->CastSpell(_player,trainer_spell->spell,true); else - { - WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12); // visual effect on trainer - data << uint64(guid) << uint32(0xB3); - SendPacket(&data); - - data.Initialize(SMSG_PLAY_SPELL_IMPACT, 12); // visual effect on player - data << uint64(_player->GetGUID()) << uint32(0x016A); - SendPacket(&data); - - _player->learnSpell(spellId); - } + _player->learnSpell(spellId,false); - WorldPacket data(SMSG_TRAINER_BUY_SUCCEEDED, 12); + data.Initialize(SMSG_TRAINER_BUY_SUCCEEDED, 12); data << uint64(guid) << uint32(trainer_spell->spell); SendPacket(&data); } diff --git a/src/game/ObjectAccessor.cpp b/src/game/ObjectAccessor.cpp index 3c54244ae13..1891dc0396d 100644 --- a/src/game/ObjectAccessor.cpp +++ b/src/game/ObjectAccessor.cpp @@ -173,11 +173,16 @@ Corpse* ObjectAccessor::GetCorpse(WorldObject const &u, uint64 guid) { Corpse * ret = GetObjectInWorld(guid, (Corpse*)NULL); - if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL; + if(!ret) + return NULL; + if(ret->GetMapId() != u.GetMapId()) + ret = NULL; + if(ret->GetInstanceId() != u.GetInstanceId()) + return NULL; return ret; } -Object* ObjectAccessor::GetObjectByTypeMask(Player const &p, uint64 guid, uint32 typemask) +Object* ObjectAccessor::GetObjectByTypeMask(WorldObject const &p, uint64 guid, uint32 typemask) { Object *obj = NULL; @@ -205,9 +210,9 @@ Object* ObjectAccessor::GetObjectByTypeMask(Player const &p, uint64 guid, uint32 if(obj) return obj; } - if(typemask & TYPEMASK_ITEM) + if(typemask & TYPEMASK_ITEM && p.GetTypeId() == TYPEID_PLAYER) { - obj = p.GetItemByGuid( guid ); + obj = ((Player const &)p).GetItemByGuid( guid ); if(obj) return obj; } @@ -218,15 +223,25 @@ GameObject* ObjectAccessor::GetGameObject(WorldObject const &u, uint64 guid) { GameObject * ret = GetObjectInWorld(guid, (GameObject*)NULL); - if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL; + if(!ret) + return NULL; + if(ret->GetMapId() != u.GetMapId()) + ret = NULL; + if(ret->GetInstanceId() != u.GetInstanceId()) + return NULL; return ret; } DynamicObject* -ObjectAccessor::GetDynamicObject(Unit const &u, uint64 guid) +ObjectAccessor::GetDynamicObject(WorldObject const &u, uint64 guid) { DynamicObject * ret = GetObjectInWorld(guid, (DynamicObject*)NULL); - if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL; + if(!ret) + return NULL; + if(ret->GetMapId() != u.GetMapId()) + ret = NULL; + if(ret->GetInstanceId() != u.GetInstanceId()) + return NULL; return ret; } diff --git a/src/game/ObjectAccessor.h b/src/game/ObjectAccessor.h index 6eb9e6ee0f5..350bc62ebad 100644 --- a/src/game/ObjectAccessor.h +++ b/src/game/ObjectAccessor.h @@ -138,7 +138,7 @@ class TRINITY_DLL_DECL ObjectAccessor : public Trinity::Singleton<ObjectAccessor else return NULL; } - static Object* GetObjectByTypeMask(Player const &, uint64, uint32 typemask); + static Object* GetObjectByTypeMask(WorldObject const &, uint64, uint32 typemask); static Creature* GetNPCIfCanInteractWith(Player const &player, uint64 guid, uint32 npcflagmask); static Creature* GetCreature(WorldObject const &, uint64); static Creature* GetCreatureOrPetOrVehicle(WorldObject const &, uint64); @@ -146,7 +146,7 @@ class TRINITY_DLL_DECL ObjectAccessor : public Trinity::Singleton<ObjectAccessor static Pet* GetPet(Unit const &, uint64 guid) { return GetPet(guid); } static Player* GetPlayer(Unit const &, uint64 guid) { return FindPlayer(guid); } static GameObject* GetGameObject(WorldObject const &, uint64); - static DynamicObject* GetDynamicObject(Unit const &, uint64); + static DynamicObject* GetDynamicObject(WorldObject const &, uint64); static Corpse* GetCorpse(WorldObject const &u, uint64 guid); static Pet* GetPet(uint64 guid); static Vehicle* GetVehicle(uint64 guid); diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index ccd78c4be20..4a5c0bbdf0b 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -6486,11 +6486,18 @@ void ObjectMgr::LoadReservedPlayersNames() bar.step(); fields = result->Fetch(); std::string name= fields[0].GetCppString(); - if(normalizePlayerName(name)) + + std::wstring wstr; + if(!Utf8toWStr (name,wstr)) { - m_ReservedNames.insert(name); - ++count; + sLog.outError("Table `reserved_name` have invalid name: %s", name.c_str() ); + continue; } + + wstrToLower(wstr); + + m_ReservedNames.insert(wstr); + ++count; } while ( result->NextRow() ); delete result; @@ -6499,6 +6506,17 @@ void ObjectMgr::LoadReservedPlayersNames() sLog.outString( ">> Loaded %u reserved player names", count ); } +bool ObjectMgr::IsReservedName( const std::string& name ) const +{ + std::wstring wstr; + if(!Utf8toWStr (name,wstr)) + return false; + + wstrToLower(wstr); + + return m_ReservedNames.find(wstr) != m_ReservedNames.end(); +} + enum LanguageType { LT_BASIC_LATIN = 0x0000, diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index cbf8514fe25..4c8339d7f6f 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -736,10 +736,7 @@ class ObjectMgr // reserved names void LoadReservedPlayersNames(); - bool IsReservedName(const std::string& name) const - { - return m_ReservedNames.find(name) != m_ReservedNames.end(); - } + bool IsReservedName(const std::string& name) const; // name with valid structure and symbols static bool IsValidName( const std::string& name, bool create = false ); @@ -872,7 +869,7 @@ class ObjectMgr PetCreateSpellMap mPetCreateSpell; //character reserved names - typedef std::set<std::string> ReservedNamesMap; + typedef std::set<std::wstring> ReservedNamesMap; ReservedNamesMap m_ReservedNames; std::set<uint32> m_DisabledPlayerSpells; diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 6a39aeb6d3b..0da02b8fe14 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -41,25 +41,14 @@ char const* petTypeSuffix[MAX_PET_TYPE] = "'s Companion" // MINI_PET }; -Pet::Pet(PetType type) : Creature() +Pet::Pet(PetType type) : +Creature(), m_petType(type), m_removed(false), m_happinessTimer(7500), m_duration(0), m_bonusdamage(0), +m_resetTalentsCost(0), m_resetTalentsTime(0), m_usedTalentCount(0), m_auraUpdateMask(0), m_loading(false), +m_declinedname(NULL) { m_isPet = true; m_name = "Pet"; - m_petType = type; - - m_removed = false; m_regenTimer = 4000; - m_happinessTimer = 7500; - m_duration = 0; - m_bonusdamage = 0; - - m_resetTalentsCost = 0; - m_resetTalentsTime = 0; - m_usedTalentCount = 0; - - m_auraUpdateMask = 0; - - m_loading = false; // pets always have a charminfo, even if they are not actually charmed CharmInfo* charmInfo = InitCharmInfo(this); @@ -69,12 +58,6 @@ Pet::Pet(PetType type) : Creature() else if(type == GUARDIAN_PET) // always aggressive charmInfo->SetReactState(REACT_AGGRESSIVE); - m_spells.clear(); - m_Auras.clear(); - m_CreatureSpellCooldowns.clear(); - m_CreatureCategoryCooldowns.clear(); - m_autospells.clear(); - m_declinedname = NULL; m_isActive = true; } @@ -1117,7 +1100,7 @@ void Pet::_LoadSpells() { Field *fields = result->Fetch(); - addSpell(fields[0].GetUInt16(), fields[1].GetUInt16(), PETSPELL_UNCHANGED); + addSpell(fields[0].GetUInt32(), fields[1].GetUInt16(), PETSPELL_UNCHANGED); } while( result->NextRow() ); @@ -1270,7 +1253,7 @@ void Pet::_SaveAuras() } } -bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, PetSpellType type) +bool Pet::addSpell(uint32 spell_id, uint16 active, PetSpellState state, PetSpellType type) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id); if (!spellInfo) @@ -1387,7 +1370,7 @@ bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, PetSpell return true; } -bool Pet::learnSpell(uint16 spell_id) +bool Pet::learnSpell(uint32 spell_id) { // prevent duplicated entires in spell book if (!addSpell(spell_id)) @@ -1424,7 +1407,7 @@ void Pet::learnLevelupSpells() } } -bool Pet::unlearnSpell(uint16 spell_id) +bool Pet::unlearnSpell(uint32 spell_id) { if(removeSpell(spell_id)) { @@ -1442,7 +1425,7 @@ bool Pet::unlearnSpell(uint16 spell_id) return false; } -bool Pet::removeSpell(uint16 spell_id) +bool Pet::removeSpell(uint32 spell_id) { PetSpellMap::iterator itr = m_spells.find(spell_id); if (itr == m_spells.end()) @@ -1476,7 +1459,7 @@ bool Pet::removeSpell(uint16 spell_id) return true; } -bool Pet::_removeSpell(uint16 spell_id) +bool Pet::_removeSpell(uint32 spell_id) { PetSpellMap::iterator itr = m_spells.find(spell_id); if (itr != m_spells.end()) @@ -1493,7 +1476,7 @@ void Pet::InitPetCreateSpells() m_charmInfo->InitPetActionBar(); m_spells.clear(); - int32 petspellid; + uint32 petspellid; PetCreateSpellEntry const* CreateSpells = objmgr.GetPetCreateSpellEntry(GetEntry()); if(CreateSpells) { @@ -1513,7 +1496,7 @@ void Pet::InitPetCreateSpells() if(owner->GetTypeId() == TYPEID_PLAYER && !((Player*)owner)->HasSpell(learn_spellproto->Id)) { if(IsPassiveSpell(petspellid)) //learn passive skills when tamed, not sure if thats right - ((Player*)owner)->learnSpell(learn_spellproto->Id); + ((Player*)owner)->learnSpell(learn_spellproto->Id,false); else AddTeachSpell(learn_spellproto->EffectTriggerSpell[0], learn_spellproto->Id); } @@ -1547,7 +1530,7 @@ void Pet::CheckLearning(uint32 spellid) if(urand(0, 100) < 10) { - ((Player*)owner)->learnSpell(itr->second); + ((Player*)owner)->learnSpell(itr->second,false); m_teachspells.erase(itr); } } @@ -1693,7 +1676,7 @@ void Pet::ToggleAutocast(uint32 spellid, bool apply) // && tempSpell->EffectImplicitTargetA[0] != TARGET_CHAIN_DAMAGE) // return; - PetSpellMap::const_iterator itr = m_spells.find((uint16)spellid); + PetSpellMap::const_iterator itr = m_spells.find(spellid); int i; @@ -1747,7 +1730,7 @@ bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number) bool Pet::HasSpell(uint32 spell) const { - PetSpellMap::const_iterator itr = m_spells.find((uint16)spell); + PetSpellMap::const_iterator itr = m_spells.find(spell); return (itr != m_spells.end() && itr->second->state != PETSPELL_REMOVED ); } diff --git a/src/game/Pet.h b/src/game/Pet.h index 113d1e203fa..3d0573d8f75 100644 --- a/src/game/Pet.h +++ b/src/game/Pet.h @@ -105,7 +105,7 @@ enum PetNameInvalidReason PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 16 }; -typedef UNORDERED_MAP<uint16, PetSpell*> PetSpellMap; +typedef UNORDERED_MAP<uint32, PetSpell*> PetSpellMap; typedef std::map<uint32,uint32> TeachSpellMap; typedef std::vector<uint32> AutoSpellList; @@ -190,12 +190,12 @@ class Pet : public Creature void _LoadSpells(); void _SaveSpells(); - bool addSpell(uint16 spell_id,uint16 active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL); - bool learnSpell(uint16 spell_id); + bool addSpell(uint32 spell_id,uint16 active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL); + bool learnSpell(uint32 spell_id); void learnLevelupSpells(); - bool unlearnSpell(uint16 spell_id); - bool removeSpell(uint16 spell_id); - bool _removeSpell(uint16 spell_id); + bool unlearnSpell(uint32 spell_id); + bool removeSpell(uint32 spell_id); + bool _removeSpell(uint32 spell_id); PetSpellMap m_spells; TeachSpellMap m_teachspells; diff --git a/src/game/Player.cpp b/src/game/Player.cpp index fffe5ec5fb2..2d584da26f7 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -2649,7 +2649,7 @@ void Player::AddNewMailDeliverTime(time_t deliver_time) } } -bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled) +bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id); if (!spellInfo) @@ -2682,29 +2682,80 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED; + bool dependent_set = false; bool disabled_case = false; bool superceded_old = false; PlayerSpellMap::iterator itr = m_spells.find(spell_id); if (itr != m_spells.end()) { + uint32 next_active_spell_id = 0; + // fix activate state for non-stackable low rank (and find next spell for !active case) + if(!SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0) + { + if(uint32 next = spellmgr.GetNextSpellInChain(spell_id)) + { + if(HasSpell(next)) + { + // high rank already known so this must !active + active = false; + next_active_spell_id = next; + } + } + } + + // not do anything if already known in expected state + if(itr->second->state != PLAYERSPELL_REMOVED && itr->second->active == active && + itr->second->dependent == dependent && itr->second->disabled == disabled) + { + if(!IsInWorld() && !learning) // explicitly load from DB and then exist in it already and set correctly + itr->second->state = PLAYERSPELL_UNCHANGED; + + return false; + } + + // dependent spell known as not dependent, overwrite state + if (itr->second->state != PLAYERSPELL_REMOVED && !itr->second->dependent && dependent) + { + itr->second->dependent = dependent; + if (itr->second->state != PLAYERSPELL_NEW) + itr->second->state = PLAYERSPELL_CHANGED; + dependent_set = true; + } + // update active state for known spell if(itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled) { itr->second->active = active; - // !IsInWorld() && !learning == explicitly load from DB and then exist in it already and set correctly - if(!IsInWorld() && !learning) + if(!IsInWorld() && !learning && !dependent_set) // explicitly load from DB and then exist in it already and set correctly itr->second->state = PLAYERSPELL_UNCHANGED; else if(itr->second->state != PLAYERSPELL_NEW) itr->second->state = PLAYERSPELL_CHANGED; - if(!active) + if(active) { - WorldPacket data(SMSG_REMOVED_SPELL, 4); - data << uint16(spell_id); - GetSession()->SendPacket(&data); + if (IsPassiveSpell(spell_id) && IsNeedCastPassiveSpellAtLearn(spellInfo)) + CastSpell (this,spell_id,true); + } + else if(IsInWorld()) + { + if(next_active_spell_id) + { + // update spell ranks in spellbook and action bar + WorldPacket data(SMSG_SUPERCEDED_SPELL, (4)); + data << uint16(spell_id); + data << uint16(next_active_spell_id); + GetSession()->SendPacket( &data ); + } + else + { + WorldPacket data(SMSG_REMOVED_SPELL, 4); + data << uint16(spell_id); + GetSession()->SendPacket(&data); + } } + return active; // learn (show in spell book if active now) } @@ -2733,7 +2784,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled default: // known not saved yet spell (new or modified) { // can be in case spell loading but learned at some previous spell loading - if(!IsInWorld() && !learning) + if(!IsInWorld() && !learning && !dependent_set) itr->second->state = PLAYERSPELL_UNCHANGED; return false; @@ -2755,10 +2806,6 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled if(!rankSpellId || rankSpellId==spell_id) continue; - // skip unknown ranks - if(!HasSpell(rankSpellId)) - continue; - removeSpell(rankSpellId); } } @@ -2766,16 +2813,17 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled // non talent spell: learn low ranks (recursive call) else if(uint32 prev_spell = spellmgr.GetPrevSpellInChain(spell_id)) { - if(!IsInWorld()) // at spells loading, no output, but allow save - addSpell(prev_spell,active,true,disabled); + if(!IsInWorld() || disabled) // at spells loading, no output, but allow save + addSpell(prev_spell,active,true,true,disabled); else // at normal learning - learnSpell(prev_spell); + learnSpell(prev_spell,true); } PlayerSpell *newspell = new PlayerSpell; - newspell->active = active; - newspell->state = state; - newspell->disabled = disabled; + newspell->state = state; + newspell->active = active; + newspell->dependent = dependent; + newspell->disabled = disabled; // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible if(newspell->active && !newspell->disabled && !SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0) @@ -2802,7 +2850,8 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new) itr->second->active = false; - itr->second->state = PLAYERSPELL_CHANGED; + if(itr->second->state != PLAYERSPELL_NEW) + itr->second->state = PLAYERSPELL_CHANGED; superceded_old = true; // new spell replace old in action bars and spell book. } else if(spellmgr.IsHighRankOfSpell(itr->first,spell_id)) @@ -2844,26 +2893,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks else if (IsPassiveSpell(spell_id)) { - bool need_cast = false; - - switch(spell_id) - { - // some spells not have stance data expacted cast at form change or present - case 5420: need_cast = (m_form == FORM_TREE); break; - case 5419: need_cast = (m_form == FORM_TRAVEL); break; - case 7376: need_cast = (m_form == FORM_DEFENSIVESTANCE); break; - case 7381: need_cast = (m_form == FORM_BERSERKERSTANCE); break; - case 21156: need_cast = (m_form == FORM_BATTLESTANCE); break; - case 21178: need_cast = (m_form == FORM_BEAR || m_form == FORM_DIREBEAR); break; - case 33948: need_cast = (m_form == FORM_FLIGHT); break; - case 34764: need_cast = (m_form == FORM_FLIGHT); break; - case 40121: need_cast = (m_form == FORM_FLIGHT_EPIC); break; - case 40122: need_cast = (m_form == FORM_FLIGHT_EPIC); break; - // another spells have proper stance data - default: need_cast = !spellInfo->Stances || m_form != 0 && (spellInfo->Stances & (1<<(m_form-1))); break; - } - //Check CasterAuraStates - if (need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState)))) + if(IsNeedCastPassiveSpellAtLearn(spellInfo)) CastSpell(this, spell_id, true); } else if( IsSpellHaveEffect(spellInfo,SPELL_EFFECT_SKILL_STEP) ) @@ -2948,9 +2978,9 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled if(!itr->second.autoLearned) { if(!IsInWorld() || !itr->second.active) // at spells loading, no output, but allow save - addSpell(itr->second.spell,itr->second.active,true,false); + addSpell(itr->second.spell,itr->second.active,true,true,false); else // at normal learning - learnSpell(itr->second.spell); + learnSpell(itr->second.spell,true); } } @@ -2964,14 +2994,39 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool disabled return active && !disabled && !superceded_old; } -void Player::learnSpell(uint32 spell_id) +bool Player::IsNeedCastPassiveSpellAtLearn(SpellEntry const* spellInfo) const +{ + bool need_cast = false; + + switch(spellInfo->Id) + { + // some spells not have stance data expacted cast at form change or present + case 5420: need_cast = (m_form == FORM_TREE); break; + case 5419: need_cast = (m_form == FORM_TRAVEL); break; + case 7376: need_cast = (m_form == FORM_DEFENSIVESTANCE); break; + case 7381: need_cast = (m_form == FORM_BERSERKERSTANCE); break; + case 21156: need_cast = (m_form == FORM_BATTLESTANCE); break; + case 21178: need_cast = (m_form == FORM_BEAR || m_form == FORM_DIREBEAR); break; + case 33948: need_cast = (m_form == FORM_FLIGHT); break; + case 34764: need_cast = (m_form == FORM_FLIGHT); break; + case 40121: need_cast = (m_form == FORM_FLIGHT_EPIC); break; + case 40122: need_cast = (m_form == FORM_FLIGHT_EPIC); break; + // another spells have proper stance data + default: need_cast = !spellInfo->Stances || m_form != 0 && (spellInfo->Stances & (1<<(m_form-1))); break; + } + + //Check CasterAuraStates + return need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState))); +} + +void Player::learnSpell(uint32 spell_id, bool dependent) { PlayerSpellMap::iterator itr = m_spells.find(spell_id); bool disabled = (itr != m_spells.end()) ? itr->second->disabled : false; bool active = disabled ? itr->second->active : true; - bool learning = addSpell(spell_id,active,true,false); + bool learning = addSpell(spell_id,active,true,dependent,false); // learn all disabled higher ranks (recursive) if(disabled) @@ -2981,7 +3036,7 @@ void Player::learnSpell(uint32 spell_id) { PlayerSpellMap::iterator iter = m_spells.find(node->next); if (iter != m_spells.end() && iter->second->disabled ) - learnSpell(node->next); + learnSpell(node->next,false); } } @@ -2994,7 +3049,7 @@ void Player::learnSpell(uint32 spell_id) GetSession()->SendPacket(&data); } -void Player::removeSpell(uint32 spell_id, bool disabled) +void Player::removeSpell(uint32 spell_id, bool disabled, bool update_action_bar_for_low_rank) { PlayerSpellMap::iterator itr = m_spells.find(spell_id); if (itr == m_spells.end()) @@ -3016,10 +3071,8 @@ void Player::removeSpell(uint32 spell_id, bool disabled) for (uint32 i=reqMap.count(spell_id);i>0;i--,itr2++) removeSpell(itr2->second,disabled); - // removing - WorldPacket data(SMSG_REMOVED_SPELL, 4); - data << uint16(spell_id); - GetSession()->SendPacket(&data); + bool cur_active = itr->second->active; + bool cur_dependent = itr->second->dependent; if (disabled) { @@ -3132,7 +3185,57 @@ void Player::removeSpell(uint32 spell_id, bool disabled) for(SpellLearnSpellMap::const_iterator itr2 = spell_begin; itr2 != spell_end; ++itr2) removeSpell(itr2->second.spell, disabled); - // TODO: recast if need lesser ranks spell for passive with IsPassiveSpellStackableWithRanks + // activate lesser rank in spellbook/action bar, and cast it if need + bool prev_activate = false; + + 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) + 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) + { + // 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) + { + prev_itr->second->dependent = cur_dependent; + 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) + { + if(addSpell(prev_id,true,false,prev_itr->second->dependent,prev_itr->second->disabled)) + { + if(update_action_bar_for_low_rank) + { + // downgrade spell ranks in spellbook and action bar + WorldPacket data(SMSG_SUPERCEDED_SPELL, (4)); + data << uint16(spell_id); + data << uint16(prev_id); + GetSession()->SendPacket( &data ); + prev_activate = true; + } + } + } + } + } + } + + // remove from spell book if not replaced by lesser rank + if(!prev_activate) + { + WorldPacket data(SMSG_REMOVED_SPELL, 4); + data << uint16(spell_id); + GetSession()->SendPacket(&data); + } } void Player::RemoveArenaSpellCooldowns() @@ -3355,18 +3458,6 @@ bool Player::resetTalents(bool no_cost) return true; } -bool Player::_removeSpell(uint16 spell_id) -{ - PlayerSpellMap::iterator itr = m_spells.find(spell_id); - if (itr != m_spells.end()) - { - delete itr->second; - m_spells.erase(itr); - return true; - } - return false; -} - Mail* Player::GetMail(uint32 id) { for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr) @@ -3572,8 +3663,16 @@ void Player::DestroyForPlayer( Player *target ) const bool Player::HasSpell(uint32 spell) const { - PlayerSpellMap::const_iterator itr = m_spells.find((uint16)spell); - return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled); + PlayerSpellMap::const_iterator itr = m_spells.find(spell); + return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED && + !itr->second->disabled); +} + +bool Player::HasActiveSpell(uint32 spell) const +{ + PlayerSpellMap::const_iterator itr = m_spells.find(spell); + return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED && + itr->second->active && !itr->second->disabled); } TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell) const @@ -4828,7 +4927,7 @@ bool Player::UpdateCraftSkill(uint32 spellid) if(spellEntry && spellEntry->Mechanic==MECHANIC_DISCOVERY) { if(uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this)) - learnSpell(discoveredSpell); + learnSpell(discoveredSpell,false); } uint32 craft_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_CRAFTING); @@ -4884,6 +4983,11 @@ bool Player::UpdateFishingSkill() return UpdateSkillPro(SKILL_FISHING,chance*10,gathering_skill_gain); } +// levels sync. with spell requirement for skill levels to learn +// bonus abilities in sSkillLineAbilityStore +// Used only to avoid scan DBC at each skill grow +static uint32 bonusSkillLevels[] = {75,150,225,300,375,450}; + bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step) { sLog.outDebug("UpdateSkillPro(SkillId %d, Chance %3.1f%%)", SkillId, Chance/10.0); @@ -4918,6 +5022,14 @@ bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step) new_value = MaxValue; SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,MaxValue)); + for(uint32* bsl = &bonusSkillLevels[0]; *bsl; ++bsl) + { + if((SkillValue < *bsl && new_value >= *bsl)) + { + learnSkillRewardedSpells( SkillId, new_value); + break; + } + } GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL); sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% taken", Chance/10.0); return true; @@ -5094,6 +5206,7 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal) if(currVal) { SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal)); + learnSkillRewardedSpells(id, currVal); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL); } else //remove @@ -5103,27 +5216,11 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal) SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),0); SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0); - // remove spells that depend on this skill when removing the skill - for (PlayerSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next) - { - ++next; - if(itr->second->state == PLAYERSPELL_REMOVED) - continue; - - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(itr->first); - - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) - { - if (_spell_idx->second->skillId == id) - { - // this may remove more than one spell (dependents) - removeSpell(itr->first); - next = m_spells.begin(); - break; - } - } - } + // remove all spells that related to this skill + for (uint32 j=0; j<sSkillLineAbilityStore.GetNumRows(); ++j) + if(SkillLineAbilityEntry const *pAbility = sSkillLineAbilityStore.LookupEntry(j)) + if (pAbility->skillId==id) + removeSpell(spellmgr.GetFirstSpellInChain(pAbility->spellId)); } } else if(currVal) //add @@ -5161,7 +5258,7 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal) (*i)->ApplyModifier(true); // Learn all spells for skill - learnSkillRewardedSpells(id); + learnSkillRewardedSpells(id, currVal); return; } } @@ -9128,8 +9225,8 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV if(slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_START+GetMaxKeyringSize() && !(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; - // vanitypet case (disabled until proper implement) - if(slot >= VANITYPET_SLOT_START && slot < VANITYPET_SLOT_END && !(false /*pProto->BagFamily & BAG_FAMILY_MASK_VANITY_PETS*/)) + // vanitypet case (not use, vanity pets stored as spells) + if(slot >= VANITYPET_SLOT_START && slot < VANITYPET_SLOT_END) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; // currencytoken case (disabled until proper implement) @@ -9472,28 +9569,9 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } } - /* until proper implementation - else if(pProto->BagFamily & BAG_FAMILY_MASK_VANITY_PETS) - { - res = _CanStoreItem_InInventorySlots(VANITYPET_SLOT_START,VANITYPET_SLOT_END,dest,pProto,count,false,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) - { - if(no_space_count) - *no_space_count = count + no_similar_count; - return res; - } - if(count==0) - { - if(no_similar_count==0) - return EQUIP_ERR_OK; + // Vanity pet case skipped as not used - if(no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - */ /* until proper implementation else if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) { @@ -9685,28 +9763,9 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } } - /* until proper implementation - else if(false pProto->BagFamily & BAG_FAMILY_MASK_VANITY_PETS) - { - res = _CanStoreItem_InInventorySlots(VANITYPET_SLOT_START,VANITYPET_SLOT_END,dest,pProto,count,false,pItem,bag,slot); - if(res!=EQUIP_ERR_OK) - { - if(no_space_count) - *no_space_count = count + no_similar_count; - return res; - } - if(count==0) - { - if(no_similar_count==0) - return EQUIP_ERR_OK; + // Vanity pet case skipped as not used - if(no_space_count) - *no_space_count = count + no_similar_count; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - } - } - */ /* until proper implementation else if(false pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) { @@ -9821,14 +9880,12 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const int inv_slot_items[INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START]; int inv_bags[INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE]; int inv_keys[KEYRING_SLOT_END-KEYRING_SLOT_START]; - int inv_pets[VANITYPET_SLOT_END-VANITYPET_SLOT_START]; int inv_tokens[CURRENCYTOKEN_SLOT_END-CURRENCYTOKEN_SLOT_START]; int inv_quests[QUESTBAG_SLOT_END-QUESTBAG_SLOT_START]; memset(inv_slot_items,0,sizeof(int)*(INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START)); memset(inv_bags,0,sizeof(int)*(INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START)*MAX_BAG_SIZE); memset(inv_keys,0,sizeof(int)*(KEYRING_SLOT_END-KEYRING_SLOT_START)); - memset(inv_pets,0,sizeof(int)*(VANITYPET_SLOT_END-VANITYPET_SLOT_START)); memset(inv_tokens,0,sizeof(int)*(CURRENCYTOKEN_SLOT_END-CURRENCYTOKEN_SLOT_START)); memset(inv_quests,0,sizeof(int)*(QUESTBAG_SLOT_END-QUESTBAG_SLOT_START)); @@ -9852,15 +9909,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const } } - for(int i = VANITYPET_SLOT_START; i < VANITYPET_SLOT_END; i++) - { - pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - - if (pItem2 && !pItem2->IsInTrade()) - { - inv_pets[i-VANITYPET_SLOT_START] = pItem2->GetCount(); - } - } + // Vanity pet case skipped as not used for(int i = CURRENCYTOKEN_SLOT_START; i < CURRENCYTOKEN_SLOT_END; i++) { @@ -9941,17 +9990,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const } if (b_found) continue; - for(int t = VANITYPET_SLOT_START; t < VANITYPET_SLOT_END; t++) - { - pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t ); - if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_pets[t-VANITYPET_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) - { - inv_pets[t-VANITYPET_SLOT_START] += pItem->GetCount(); - b_found = true; - break; - } - } - if (b_found) continue; + // Vanity pet case skipped as not used for(int t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; t++) { @@ -10029,22 +10068,8 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const if (b_found) continue; - /* until proper implementation - if(pProto->BagFamily & BAG_FAMILY_MASK_VANITY_PETS) - { - for(uint32 t = VANITYPET_SLOT_START; t < VANITYPET_SLOT_END; ++t) - { - if( inv_pets[t-VANITYPET_SLOT_START] == 0 ) - { - inv_pets[t-VANITYPET_SLOT_START] = 1; - b_found = true; - break; - } - } - } + // Vanity pet case skipped as not used - if (b_found) continue; - */ /* until proper implementation if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) { @@ -14687,24 +14712,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) SetUInt32Value(PLAYER_TRACK_CREATURES, 0 ); SetUInt32Value(PLAYER_TRACK_RESOURCES, 0 ); - // reset skill modifiers and set correct unlearn flags - for (uint32 i = 0; i < PLAYER_MAX_SKILLS; i++) - { - SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0); - - // set correct unlearn bit - uint32 id = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF; - if(!id) continue; - - SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id); - if(!pSkill) continue; - - // enable unlearn button for primary professions only - if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION) - SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,1)); - else - SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0)); - } + _LoadSkills(); // make sure the unit is considered out of combat for proper loading ClearInCombat(); @@ -14745,7 +14753,6 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) // after spell and quest load InitTalentForLevel(); - learnSkillRewardedSpells(); learnDefaultSpells(); _LoadTutorials(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTUTORIALS)); @@ -14849,6 +14856,28 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) break; } + switch(sWorld.getConfig(CONFIG_GM_VISIBLE_STATE)) + { + default: + case 0: SetGMVisible(false); break; // invisible + case 1: break; // visible + case 2: // save state + if(extraflags & PLAYER_EXTRA_GM_INVISIBLE) + SetGMVisible(false); + break; + } + + /*switch(sWorld.getConfig(CONFIG_GM_ACCEPT_TICKETS)) + { + default: + case 0: break; // disable + case 1: SetAcceptTicket(true); break; // enable + case 2: // save state + if(extraflags & PLAYER_EXTRA_GM_ACCEPT_TICKETS) + SetAcceptTicket(true); + break; + }*/ + switch(sWorld.getConfig(CONFIG_GM_CHAT)) { default: @@ -15540,7 +15569,7 @@ void Player::_LoadSpells(QueryResult *result) { Field *fields = result->Fetch(); - addSpell(fields[0].GetUInt16(), fields[1].GetBool(), false, fields[2].GetBool()); + addSpell(fields[0].GetUInt16(), fields[1].GetBool(), false, false, fields[2].GetBool()); } while( result->NextRow() ); @@ -16318,18 +16347,26 @@ void Player::_SaveReputation() void Player::_SaveSpells() { - for (PlayerSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next) + for (PlayerSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end();) { - ++next; if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED) CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first); - if (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED) + + // add only changed/new not dependent spells + if (!itr->second->dependent && (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED)) CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,active,disabled) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second->active ? 1 : 0,itr->second->disabled ? 1 : 0); if (itr->second->state == PLAYERSPELL_REMOVED) - _removeSpell(itr->first); + { + delete itr->second; + m_spells.erase(itr++); + } else + { itr->second->state = PLAYERSPELL_UNCHANGED; + ++itr; + } + } } @@ -18678,9 +18715,9 @@ void Player::learnDefaultSpells() uint32 tspell = *itr; sLog.outDebug("PLAYER (Class: %u Race: %u): Adding initial spell, id = %u",uint32(getClass()),uint32(getRace()), tspell); if(!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add - addSpell(tspell,true,true,false); + addSpell(tspell,true,true,true,false); else // but send in normal spell in game learn case - learnSpell(tspell); + learnSpell(tspell,true); } } @@ -18772,7 +18809,7 @@ void Player::learnQuestRewardedSpells() } } -void Player::learnSkillRewardedSpells(uint32 skill_id ) +void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value ) { uint32 raceMask = getRaceMask(); uint32 classMask = getClassMask(); @@ -18790,25 +18827,18 @@ void Player::learnSkillRewardedSpells(uint32 skill_id ) if (sSpellStore.LookupEntry(pAbility->spellId)) { - // Ok need learn spell - learnSpell(pAbility->spellId); + // need unlearn spell + if (skill_value < pAbility->req_skill_value) + removeSpell(pAbility->spellId); + // need learn + else if (!IsInWorld()) + addSpell(pAbility->spellId,true,true,true,false); + else + learnSpell(pAbility->spellId,true); } } } -void Player::learnSkillRewardedSpells() -{ - for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++) - { - if(!GetUInt32Value(PLAYER_SKILL_INDEX(i))) - continue; - - uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF; - - learnSkillRewardedSpells(pskill); - } -} - void Player::SendAurasForTarget(Unit *target) { if(target->GetVisibleAuras()->empty()) // speedup things @@ -19486,6 +19516,13 @@ void Player::UpdateAreaDependentAuras( uint32 newArea ) if( !HasAura(51721,0) ) CastSpell(this,51721,true); break; + // Mist of the Kvaldir + case 4028: //Riplash Strand + case 4029: //Riplash Ruins + case 4106: //Garrosh's Landing + case 4031: //Pal'ea + CastSpell(this,54119,true); + break; } } @@ -20178,3 +20215,67 @@ bool Player::IsAllowUseFlyMountsHere() const } return true; } + +void Player::learnSpellHighRank(uint32 spellid) +{ + learnSpell(spellid,false); + + if(uint32 next = spellmgr.GetNextSpellInChain(spellid)) + learnSpellHighRank(next); +} + +void Player::_LoadSkills() +{ + // Note: skill data itself loaded from `data` field. This is only cleanup part of load + + // reset skill modifiers and set correct unlearn flags + for (uint32 i = 0; i < PLAYER_MAX_SKILLS; i++) + { + SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0); + + // set correct unlearn bit + uint32 id = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF; + if(!id) continue; + + SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id); + if(!pSkill) continue; + + // enable unlearn button for primary professions only + if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION) + SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,1)); + else + SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0)); + + uint32 vskill = SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))); + + learnSkillRewardedSpells(id, vskill); + } + + // special settings + if(getClass()==CLASS_DEATH_KNIGHT) + { + uint32 base_level = std::min(getLevel(),sWorld.getConfig (CONFIG_START_HEROIC_PLAYER_LEVEL)); + if(base_level < 1) + base_level = 1; + uint32 base_skill = (base_level-1)*5; // 270 at starting level 55 + if(base_skill < 1) + base_skill = 1; // skill mast be known and then > 0 in any case + + if(GetPureSkillValue (SKILL_FIRST_AID) < base_skill) + SetSkill(SKILL_FIRST_AID,base_skill, base_skill); + if(GetPureSkillValue (SKILL_AXES) < base_skill) + SetSkill(SKILL_AXES, base_skill,base_skill); + if(GetPureSkillValue (SKILL_DEFENSE) < base_skill) + SetSkill(SKILL_DEFENSE, base_skill,base_skill); + if(GetPureSkillValue (SKILL_POLEARMS) < base_skill) + SetSkill(SKILL_POLEARMS, base_skill,base_skill); + if(GetPureSkillValue (SKILL_SWORDS) < base_skill) + SetSkill(SKILL_SWORDS, base_skill,base_skill); + if(GetPureSkillValue (SKILL_2H_AXES) < base_skill) + SetSkill(SKILL_2H_AXES, base_skill,base_skill); + if(GetPureSkillValue (SKILL_2H_SWORDS) < base_skill) + SetSkill(SKILL_2H_SWORDS, base_skill,base_skill); + if(GetPureSkillValue (SKILL_UNARMED) < base_skill) + SetSkill(SKILL_UNARMED, base_skill,base_skill); + } +} diff --git a/src/game/Player.h b/src/game/Player.h index 19dd5a4e06d..c6b99cefe70 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -88,8 +88,9 @@ enum PlayerSpellState struct PlayerSpell { PlayerSpellState state : 8; - bool active : 1; - bool disabled : 1; + bool active : 1; // show in spellbook + bool dependent : 1; // learned as result another spell learn, skill grow, quest reward, etc + bool disabled : 1; // first rank has been learned in result talent learn but currently talent unlearned, save max learned ranks }; // Spell modifier (used for modify other spells) @@ -106,7 +107,7 @@ struct SpellModifier Spell const* lastAffected; }; -typedef UNORDERED_MAP<uint16, PlayerSpell*> PlayerSpellMap; +typedef UNORDERED_MAP<uint32, PlayerSpell*> PlayerSpellMap; typedef std::list<SpellModifier*> SpellModList; struct SpellCooldown @@ -731,8 +732,8 @@ enum KeyRingSlots enum VanityPetSlots { - VANITYPET_SLOT_START = 118, - VANITYPET_SLOT_END = 136 + VANITYPET_SLOT_START = 118, // not use, vanity pets stored as spells + VANITYPET_SLOT_END = 136 // not alloed any content in. }; enum CurrencyTokenSlots @@ -1342,6 +1343,7 @@ class TRINITY_DLL_SPEC Player : public Unit /*********************************************************/ bool LoadFromDB(uint32 guid, SqlQueryHolder *holder); + bool MinimalLoadFromDB(QueryResult *result, uint32 guid); static bool LoadValuesArrayFromDB(Tokens& data,uint64 guid); static uint32 GetUInt32ValueFromArray(Tokens const& data, uint16 index); @@ -1475,18 +1477,21 @@ class TRINITY_DLL_SPEC Player : public Unit void CharmSpellInitialize(); void PossessSpellInitialize(); bool HasSpell(uint32 spell) const; + bool HasActiveSpell(uint32 spell) const; // show in spellbook TrainerSpellState GetTrainerSpellState(TrainerSpell const* trainer_spell) const; bool IsSpellFitByClassAndRace( uint32 spell_id ) const; + bool IsNeedCastPassiveSpellAtLearn(SpellEntry const* spellInfo) const; void SendProficiency(uint8 pr1, uint32 pr2); void SendInitialSpells(); - bool addSpell(uint32 spell_id, bool active, bool learning, bool disabled); - void learnSpell(uint32 spell_id); - void removeSpell(uint32 spell_id, bool disabled = false); + bool addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled); + void learnSpell(uint32 spell_id, bool dependent); + void removeSpell(uint32 spell_id, bool disabled = false, bool update_action_bar_for_low_rank = false); void resetSpells(); void learnDefaultSpells(); void learnQuestRewardedSpells(); void learnQuestRewardedSpells(Quest const* quest); + void learnSpellHighRank(uint32 spellid); uint32 GetFreeTalentPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS1); } void SetFreeTalentPoints(uint32 points) { SetUInt32Value(PLAYER_CHARACTER_POINTS1,points); } @@ -1767,8 +1772,7 @@ class TRINITY_DLL_SPEC Player : public Unit int16 GetSkillPermBonusValue(uint32 skill) const; int16 GetSkillTempBonusValue(uint32 skill) const; bool HasSkill(uint32 skill) const; - void learnSkillRewardedSpells( uint32 id ); - void learnSkillRewardedSpells(); + void learnSkillRewardedSpells(uint32 id, uint32 value); void SetDontMove(bool dontMove); bool GetDontMove() const { return m_dontMove; } @@ -2270,6 +2274,7 @@ class TRINITY_DLL_SPEC Player : public Unit void _LoadDailyQuestStatus(QueryResult *result); void _LoadGroup(QueryResult *result); void _LoadReputation(QueryResult *result); + void _LoadSkills(); void _LoadSpells(QueryResult *result); void _LoadTutorials(QueryResult *result); void _LoadFriendList(QueryResult *result); @@ -2311,7 +2316,6 @@ class TRINITY_DLL_SPEC Player : public Unit time_t m_lastHonorUpdateTime; void outDebugValues() const; - bool _removeSpell(uint16 spell_id); uint64 m_lootGuid; uint32 m_race; diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index d5102857ca2..02c7fb6adc8 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -832,7 +832,6 @@ enum Targets TARGET_UNIT_PARTY_CASTER = 20, TARGET_SINGLE_FRIEND = 21, TARGET_UNIT_TARGET_ALLY = 21, - TARGET_ALL_AROUND_CASTER = 22, // used only in TargetA, target selection dependent from TargetB TARGET_DEST_CASTER = 22, TARGET_GAMEOBJECT = 23, //TARGET_OBJECT_OPEN diff --git a/src/game/SkillHandler.cpp b/src/game/SkillHandler.cpp index 9f3915c30cb..e909842dc86 100644 --- a/src/game/SkillHandler.cpp +++ b/src/game/SkillHandler.cpp @@ -129,7 +129,7 @@ void WorldSession::HandleLearnTalentOpcode( WorldPacket & recv_data ) return; // learn! (other talent ranks will unlearned at learning) - GetPlayer( )->learnSpell(spellid); + GetPlayer( )->learnSpell(spellid,false); sLog.outDetail("TalentID: %u Rank: %u Spell: %u\n", talent_id, requested_rank, spellid); // update free talent points diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index d0e1e3f9db3..f2f03249e22 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -119,6 +119,14 @@ void SpellCastTargets::setDestination(Unit *target, bool send) m_targetMask |= TARGET_FLAG_DEST_LOCATION; } +void SpellCastTargets::setSource(float x, float y, float z) +{ + m_srcX = x; + m_srcY = y; + m_srcZ = z; + m_targetMask |= TARGET_FLAG_SOURCE_LOCATION; +} + void SpellCastTargets::setGOTarget(GameObject *target) { m_GOTarget = target; @@ -354,6 +362,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi focusObject = NULL; m_cast_count = 0; m_glyphIndex = 0; + m_preCastSpell = 0; m_triggeredByAuraSpell = NULL; //Auto Shot & Shoot (wand) @@ -597,7 +606,6 @@ void Spell::FillTargetMap() } } - if(IsChanneledSpell(m_spellInfo) && !tmpUnitMap.empty()) m_needAliveTargetMask |= (1<<i); @@ -701,7 +709,7 @@ void Spell::prepareDataForTriggerSystem() } // Hunter traps spells (for Entrapment trigger) // Gives your Immolation Trap, Frost Trap, Explosive Trap, and Snake Trap .... - if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->SpellFamilyFlags & 0x0000200000000014LL) + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->SpellFamilyFlags & 0x000020000000001CLL) m_procAttacker |= PROC_FLAG_ON_TRAP_ACTIVATION; } @@ -877,7 +885,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) return; // Get original caster (if exist) and calculate damage/healing from him data - Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster; + Unit *caster = m_originalCaster ? m_originalCaster : m_caster; // Skip if m_originalCaster not avaiable if (!caster) @@ -970,25 +978,6 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) int32 damagePoint = damageInfo.damage * 33 / 100; m_caster->CastCustomSpell(m_caster, 32220, &damagePoint, NULL, NULL, true); } - // Bloodthirst - else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && m_spellInfo->SpellFamilyFlags & 0x40000000000LL) - { - uint32 BTAura = 0; - switch(m_spellInfo->Id) - { - case 23881: BTAura = 23885; break; - case 23892: BTAura = 23886; break; - case 23893: BTAura = 23887; break; - case 23894: BTAura = 23888; break; - case 25251: BTAura = 25252; break; - case 30335: BTAura = 30339; break; - default: - sLog.outError("Spell::EffectSchoolDMG: Spell %u not handled in BTAura",m_spellInfo->Id); - break; - } - if (BTAura) - m_caster->CastSpell(m_caster,BTAura,true); - } } // Passive spell hits/misses or active spells only misses (only triggers) else @@ -1105,6 +1094,10 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) unit->IncrDiminishing(m_diminishGroup); } + // Apply additional spell effects to target + if (m_preCastSpell) + m_caster->CastSpell(unit,m_preCastSpell, true, m_CastItem); + for(uint32 effectNumber=0;effectNumber<3;effectNumber++) { if (effectMask & (1<<effectNumber)) @@ -1604,7 +1597,6 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) m_targets.setDestination(((Player*)m_caster)->m_homebindX,((Player*)m_caster)->m_homebindY,((Player*)m_caster)->m_homebindZ, true, ((Player*)m_caster)->m_homebindMapId); break; - case TARGET_IN_FRONT_OF_CASTER: case TARGET_UNIT_CONE_ENEMY_UNKNOWN: if(m_customAttr & SPELL_ATTR_CU_CONE_BACK) @@ -2162,6 +2154,49 @@ void Spell::cast(bool skipCheck) if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) CalculateDamageDoneForAllTargets(); + switch(m_spellInfo->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + { + if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages + m_preCastSpell = 11196; // Recently Bandaged + else if(m_spellInfo->SpellIconID == 1662 && m_spellInfo->AttributesEx & 0x20) // Blood Fury (Racial) + m_preCastSpell = 23230; // Blood Fury - Healing Reduction + break; + } + case SPELLFAMILY_MAGE: + { + if (m_spellInfo->SpellFamilyFlags&0x0000008000000000LL) // Ice Block + m_preCastSpell = 41425; // Hypothermia + break; + } + case SPELLFAMILY_PRIEST: + { + if (m_spellInfo->Mechanic == MECHANIC_SHIELD && + m_spellInfo->SpellIconID == 566) // Power Word: Shield + m_preCastSpell = 6788; // Weakened Soul + if (m_spellInfo->Id == 47585) // Dispersion (transform) + m_preCastSpell = 60069; // Dispersion (mana regen) + break; + } + case SPELLFAMILY_PALADIN: + { + if (m_spellInfo->SpellFamilyFlags&0x0000000000400080LL) // Divine Shield, Divine Protection or Hand of Protection + m_preCastSpell = 25771; // Forbearance + break; + } + case SPELLFAMILY_SHAMAN: + { + if (m_spellInfo->Id == 2825) // Bloodlust + m_preCastSpell = 57724; // Sated + else if (m_spellInfo->Id == 32182) // Heroism + m_preCastSpell = 57723; // Exhaustion + break; + } + default: + break; + } + // traded items have trade slot instead of guid in m_itemTargetGUID // set to real guid to be sent later to the client m_targets.updateTradeSlotItem(); @@ -2644,68 +2679,6 @@ void Spell::finish(bool ok) m_caster->resetAttackTimer(RANGED_ATTACK); } - // Post effects apply on spell targets in some spells - if(!m_UniqueTargetInfo.empty()) - { - uint32 spellId = 0; - switch(m_spellInfo->SpellFamilyName) - { - case SPELLFAMILY_GENERIC: - { - if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages - spellId = 11196; // Recently Bandaged - else if(m_spellInfo->SpellIconID == 1662 && m_spellInfo->AttributesEx & 0x20) // Blood Fury (Racial) - spellId = 23230; // Blood Fury - Healing Reduction - break; - } - case SPELLFAMILY_MAGE: - { - if (m_spellInfo->SpellFamilyFlags&0x0000008000000000LL) // Ice Block - spellId = 41425; // Hypothermia - break; - } - case SPELLFAMILY_PRIEST: - { - if (m_spellInfo->Mechanic == MECHANIC_SHIELD && - m_spellInfo->SpellIconID == 566) // Power Word: Shield - spellId = 6788; // Weakened Soul - break; - } - case SPELLFAMILY_PALADIN: - { - if (m_spellInfo->SpellFamilyFlags&0x0000000000400080LL) // Divine Shield, Divine Protection or Hand of Protection - spellId = 25771; // Forbearance - break; - } - case SPELLFAMILY_SHAMAN: - { - if (m_spellInfo->Id == 2825) // Bloodlust - spellId = 57724; // Sated - else if (m_spellInfo->Id == 32182) // Heroism - spellId = 57723; // Exhaustion - break; - } - default: - break; - } - if (spellId) - { - for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) - { - Unit* unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); - if (unit) - { -// TODO: fix me use cast spell (now post spell can immune by this spell) -// m_caster->CastSpell(unit, spellId, true, m_CastItem); - SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(spellId); - if (!AdditionalSpellInfo) - continue; - Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, 0, &m_currentBasePoints[0], unit, m_caster, m_CastItem); - unit->AddAura(AdditionalAura); - } - } - } - } // call triggered spell only at successful cast (after clear combo points -> for add some if need) if(!m_TriggerSpells.empty()) TriggerSpell(); @@ -2852,6 +2825,7 @@ void Spell::SendSpellGo() } WorldPacket data(SMSG_SPELL_GO, 50); // guess size + if(m_CastItem) data.append(m_CastItem->GetPackGUID()); else @@ -2949,6 +2923,8 @@ void Spell::WriteAmmoToPacket( WorldPacket * data ) void Spell::WriteSpellGoTargets( WorldPacket * data ) { + // This function also fill data for channeled spells: + // m_needAliveTargetMask req for stop channelig if one target die uint32 hit = m_UniqueGOTargetInfo.size(); // Always hits on GO uint32 miss = 0; for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) @@ -2968,7 +2944,10 @@ void Spell::WriteSpellGoTargets( WorldPacket * data ) *data << (uint8)hit; for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) if ((*ihit).missCondition == SPELL_MISS_NONE) // Add only hits + { *data << uint64(ihit->targetGUID); + m_needAliveTargetMask |=ihit->effectMask; + } for(std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin();ighit != m_UniqueGOTargetInfo.end();++ighit) *data << uint64(ighit->targetGUID); // Always hits @@ -2984,6 +2963,9 @@ void Spell::WriteSpellGoTargets( WorldPacket * data ) *data << uint8(ihit->reflectResult); } } + // Reset m_needAliveTargetMask for non channeled spell + if(!IsChanneledSpell(m_spellInfo)) + m_needAliveTargetMask = 0; } void Spell::SendLogExecute() @@ -5189,18 +5171,22 @@ void Spell::Delayed() // only called in DealDamage() //if (m_spellState == SPELL_STATE_DELAYED) // return; // spell is active and can't be time-backed + if(isDelayableNoMore()) // Spells may only be delayed twice + return; + // spells not loosing casting time ( slam, dynamites, bombs.. ) //if(!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE)) // return; - //check resist chance - int32 resistChance = 100; //must be initialized to 100 for percent modifiers - ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this); - resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100; - if (roll_chance_i(resistChance)) + //check pushback reduce + int32 delaytime = 500; // spellcasting delay is normally 500ms + int32 delayReduce = 100; // must be initialized to 100 for percent modifiers + ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if(delayReduce >= 100) return; - int32 delaytime = GetNextDelayAtDamageMsTime(); + delaytime = delaytime * (100 - delayReduce) / 100; if(int32(m_timer) + delaytime > m_casttime) { @@ -5224,14 +5210,18 @@ void Spell::DelayedChannel() if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING) return; - //check resist chance - int32 resistChance = 100; //must be initialized to 100 for percent modifiers - ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this); - resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100; - if (roll_chance_i(resistChance)) + if(isDelayableNoMore()) // Spells may only be delayed twice + return; + + //check pushback reduce + int32 delaytime = GetSpellDuration(m_spellInfo) * 25 / 100; // channeling delay is normally 25% of its time per hit + int32 delayReduce = 100; // must be initialized to 100 for percent modifiers + ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,delayReduce, this); + delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if(delayReduce >= 100) return; - int32 delaytime = GetNextDelayAtDamageMsTime(); + delaytime = delaytime * (100 - delayReduce) / 100; if(int32(m_timer) < delaytime) { @@ -5404,7 +5394,13 @@ bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase ) // all ok by some way or another, skip normal check break; default: // normal case - if(target!=m_caster && !target->IsWithinLOSInMap(m_caster)) + // Get GO cast coordinates if original caster -> GO + WorldObject *caster = NULL; + if (m_originalCasterGUID) + caster = ObjectAccessor::GetGameObject(*m_caster, m_originalCasterGUID); + if (!caster) + caster = m_caster; + if(target!=m_caster && !target->IsWithinLOSInMap(caster)) return false; break; } diff --git a/src/game/Spell.h b/src/game/Spell.h index afef4262058..f87cb9fc139 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -161,6 +161,7 @@ class SpellCastTargets void setUnitTarget(Unit *target); void setDestination(float x, float y, float z, bool send = true, int32 mapId = -1); void setDestination(Unit *target, bool send = true); + void setSource(float x, float y, float z); uint64 getGOTargetGUID() const { return m_GOTargetGUID; } GameObject *getGOTarget() const { return m_GOTarget; } @@ -419,6 +420,7 @@ class Spell uint64 m_castItemGUID; uint8 m_cast_count; uint32 m_glyphIndex; + uint32 m_preCastSpell; SpellCastTargets m_targets; int32 GetCastTime() const { return m_casttime; } @@ -485,7 +487,14 @@ class Spell uint8 m_runesState; uint8 m_delayAtDamageCount; - int32 GetNextDelayAtDamageMsTime() { return m_delayAtDamageCount < 5 ? 1000 - (m_delayAtDamageCount++)* 200 : 200; } + bool isDelayableNoMore() + { + if(m_delayAtDamageCount >= 2) + return true; + + m_delayAtDamageCount++; + return false; + } // Delayed spells system uint64 m_delayStart; // time of spell delay start, filled by event handler, zero = just started diff --git a/src/game/SpellAuraDefines.h b/src/game/SpellAuraDefines.h index eb802d1c934..a958722b0f9 100644 --- a/src/game/SpellAuraDefines.h +++ b/src/game/SpellAuraDefines.h @@ -193,7 +193,7 @@ enum AuraType SPELL_AURA_ALLOW_TAME_PET_TYPE = 146, SPELL_AURA_ADD_CREATURE_IMMUNITY = 147, SPELL_AURA_RETAIN_COMBO_POINTS = 148, - SPELL_AURA_RESIST_PUSHBACK = 149, // Resist Pushback + SPELL_AURA_REDUCE_PUSHBACK = 149, // Reduce Pushback SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT = 150, SPELL_AURA_TRACK_STEALTHED = 151, // Track Stealthed SPELL_AURA_MOD_DETECTED_RANGE = 152, // Mod Detected Range @@ -304,7 +304,7 @@ enum AuraType SPELL_AURA_MOD_TARGET_RESIST_BY_SPELL_CLASS = 257, SPELL_AURA_258 = 258, SPELL_AURA_259 = 259, - SPELL_AURA_260 = 260, + SPELL_AURA_SCREEN_EFFECT = 260, SPELL_AURA_261 = 261, SPELL_AURA_262 = 262, SPELL_AURA_ALLOW_ONLY_ABILITY = 263, diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 661fac9befe..3cf59160187 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -203,7 +203,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleNoImmediateEffect, //146 SPELL_AURA_ALLOW_TAME_PET_TYPE &Aura::HandleNULL, //147 SPELL_AURA_ADD_CREATURE_IMMUNITY &Aura::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS - &Aura::HandleNoImmediateEffect, //149 SPELL_AURA_RESIST_PUSHBACK + &Aura::HandleNoImmediateEffect, //149 SPELL_AURA_REDUCE_PUSHBACK &Aura::HandleShieldBlockValue, //150 SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT &Aura::HandleAuraTrackStealthed, //151 SPELL_AURA_TRACK_STEALTHED &Aura::HandleNoImmediateEffect, //152 SPELL_AURA_MOD_DETECTED_RANGE implemented in Creature::GetAttackDistance @@ -314,7 +314,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleNULL, //257 SPELL_AURA_MOD_TARGET_RESIST_BY_SPELL_CLASS Use SpellClassMask for spell select &Aura::HandleNULL, //258 SPELL_AURA_MOD_SPELL_VISUAL &Aura::HandleNULL, //259 corrupt healing over time spell - &Aura::HandleNULL, //260 + &Aura::HandleNoImmediateEffect, //260 SPELL_AURA_SCREEN_EFFECT (miscvalue = id in ScreenEffect.dbc) not required any code &Aura::HandleNULL, //261 out of phase? &Aura::HandleNULL, //262 &Aura::HandleNULL, //263 SPELL_AURA_ALLOW_ONLY_ABILITY player can use only abilites set in SpellClassMask @@ -2141,6 +2141,28 @@ void Aura::HandleAuraDummy(bool apply, bool Real) { break; } + case SPELLFAMILY_PRIEST: + { + // Pain and Suffering + if( m_spellProto->SpellIconID == 2874 && m_target->GetTypeId()==TYPEID_PLAYER ) + { + if(apply) + { + // Reduce backfire damage (dot damage) from Shadow Word: Death + SpellModifier *mod = new SpellModifier; + mod->op = SPELLMOD_DOT; + mod->value = m_modifier.m_amount; + mod->type = SPELLMOD_PCT; + mod->spellId = GetId(); + mod->mask = 0x0000000200000000LL; + mod->mask2= 0LL; + m_spellmod = mod; + } + ((Player*)m_target)->AddSpellMod(m_spellmod, apply); + return; + } + break; + } case SPELLFAMILY_DRUID: { // Lifebloom @@ -5039,6 +5061,7 @@ void Aura::HandleShapeshiftBoosts(bool apply) break; case FORM_TREE: spellId = 5420; + spellId2 = 34123; break; case FORM_TRAVEL: spellId = 5419; @@ -6073,47 +6096,11 @@ void Aura::PeriodicDummyTick() { if ((*i)->GetId() == GetId()) { - // Get tick number - int32 tick = (m_maxduration - m_duration) / m_modifier.periodictime; - // Default case (not on arenas) - if (tick == 0) - { - (*i)->GetModifier()->m_amount = m_modifier.m_amount; - ((Player*)m_target)->UpdateManaRegen(); - // Disable continue - m_isPeriodic = false; - } - return; - //********************************************** - // Code commended since arena patch not added - // This feature uses only in arenas - //********************************************** - // Here need increase mana regen per tick (6 second rule) - // on 0 tick - 0 (handled in 2 second) - // on 1 tick - 166% (handled in 4 second) - // on 2 tick - 133% (handled in 6 second) - // Not need update after 3 tick - /* - if (tick > 3) - return; - // Apply bonus for 0 - 3 tick - switch (tick) - { - case 0: // 0% - (*i)->GetModifier()->m_amount = m_modifier.m_amount = 0; - break; - case 1: // 166% - (*i)->GetModifier()->m_amount = m_modifier.m_amount * 5 / 3; - break; - case 2: // 133% - (*i)->GetModifier()->m_amount = m_modifier.m_amount * 4 / 3; - break; - default: // 100% - normal regen - (*i)->GetModifier()->m_amount = m_modifier.m_amount; - break; - } + (*i)->GetModifier()->m_amount = m_modifier.m_amount; ((Player*)m_target)->UpdateManaRegen(); - return;*/ + // Disable continue + m_isPeriodic = false; + return; } } return; diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 78df7d77bd6..1cc4911a05a 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -1377,6 +1377,12 @@ void Spell::EffectDummy(uint32 i) m_caster->CastCustomSpell(m_caster, 12976, &healthModSpellBasePoints0, NULL, NULL, true, NULL); return; } + // Bloodthirst + case 23881: + { + m_caster->CastCustomSpell(unitTarget, 23885, &damage, NULL, NULL, true, NULL); + return; + } } break; case SPELLFAMILY_WARLOCK: @@ -1487,16 +1493,6 @@ void Spell::EffectDummy(uint32 i) } break; case SPELLFAMILY_DRUID: - switch(m_spellInfo->Id ) - { - case 5420: // Tree of Life passive - { - // Tree of Life area effect - int32 health_mod = int32(m_caster->GetStat(STAT_SPIRIT)/4); - m_caster->CastCustomSpell(m_caster,34123,&health_mod,NULL,NULL,true,NULL); - return; - } - } break; case SPELLFAMILY_ROGUE: switch(m_spellInfo->Id ) @@ -2228,7 +2224,7 @@ void Spell::EffectApplyAura(uint32 i) (unitTarget->GetTypeId()!=TYPEID_PLAYER || !((Player*)unitTarget)->GetSession()->PlayerLoading()) ) return; - Unit* caster = m_originalCasterGUID ? m_originalCaster : m_caster; + Unit* caster = m_originalCaster ? m_originalCaster : m_caster; if(!caster) return; @@ -3380,7 +3376,7 @@ void Spell::EffectLearnSpell(uint32 i) Player *player = (Player*)unitTarget; uint32 spellToLearn = ((m_spellInfo->Id==SPELL_ID_GENERIC_LEARN) || (m_spellInfo->Id==SPELL_ID_GENERIC_LEARN_PET)) ? damage : m_spellInfo->EffectTriggerSpell[i]; - player->learnSpell(spellToLearn); + player->learnSpell(spellToLearn,false); sLog.outDebug( "Spell: Player %u have learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() ); } @@ -5045,7 +5041,7 @@ void Spell::EffectScriptEffect(uint32 effIndex) // learn random explicit discovery recipe (if any) if(uint32 discoveredSpell = GetExplicitDiscoverySpell(m_spellInfo->Id, player)) - player->learnSpell(discoveredSpell); + player->learnSpell(discoveredSpell,false); return; } } @@ -5120,6 +5116,34 @@ void Spell::EffectScriptEffect(uint32 effIndex) } break; } + case SPELLFAMILY_PRIEST: + { + switch(m_spellInfo->Id) + { + // Pain and Suffering + case 47948: + { + if (!unitTarget) + return; + // Refresh Shadow Word: Pain on target + Unit::AuraList const &mPeriodic = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); + for(Unit::AuraList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i) + { + if( (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_PRIEST && + (*i)->GetSpellProto()->SpellFamilyFlags & 0x0000000000008000LL && + (*i)->GetCasterGUID()==m_caster->GetGUID() ) + { + (*i)->RefreshAura(); + return; + } + } + return; + } + default: + break; + } + break; + } case SPELLFAMILY_HUNTER: { switch(m_spellInfo->Id) diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index cd8468c4c7b..790f0a60136 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -264,8 +264,8 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) return; } - // not have spell or spell passive and not casted by client - if ( !_player->HasSpell (spellId) || IsPassiveSpell(spellId) ) + // not have spell in spellbook or spell passive and not casted by client + if ( !_player->HasActiveSpell (spellId) || IsPassiveSpell(spellId) ) { //cheater? kick? ban? return; diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 9092b494013..c640dc58af4 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -480,8 +480,6 @@ bool IsPositiveTarget(uint32 targetA, uint32 targetB) case TARGET_CURRENT_ENEMY_COORDINATES: case TARGET_UNIT_CHANNEL: return false; - case TARGET_ALL_AROUND_CASTER: - return (targetB == TARGET_ALL_PARTY || targetB == TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER); default: break; } @@ -1095,7 +1093,7 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const * spellP return false; // Always trigger for this - if (EventProcFlag & (PROC_FLAG_KILLED | PROC_FLAG_KILL)) + if (EventProcFlag & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_ON_TRAP_ACTIVATION)) return true; if (spellProcEvent) // Exist event data @@ -1223,28 +1221,38 @@ bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellI bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo) { + if(IsPassiveSpell(spellInfo->Id)) // ranked passive spell + return false; if(spellInfo->powerType != POWER_MANA && spellInfo->powerType != POWER_HEALTH) return false; if(IsProfessionOrRidingSpell(spellInfo->Id)) return false; + if(spellmgr.IsSkillBonusSpell(spellInfo->Id)) + return false; + // All stance spells. if any better way, change it. for (int i = 0; i < 3; i++) { - // Paladin aura Spell - if(spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN - && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AREA_AURA_RAID) - return false; - // Druid form Spell - if(spellInfo->SpellFamilyName == SPELLFAMILY_DRUID - && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA - && spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT) - return false; - // Rogue Stealth - if(spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE - && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA - && spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT) - return false; + switch(spellInfo->SpellFamilyName) + { + case SPELLFAMILY_PALADIN: + // Paladin aura Spell + if (spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AREA_AURA_RAID) + return false; + break; + case SPELLFAMILY_DRUID: + // Druid form Spell + if (spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA && + spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT) + return false; + break; + case SPELLFAMILY_ROGUE: + // Rogue Stealth + if (spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA && + spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT) + return false; + } } return true; } @@ -1380,6 +1388,24 @@ bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const return IsPrimaryProfessionSpell(spellId) && GetSpellRank(spellId)==1; } +bool SpellMgr::IsSkillBonusSpell(uint32 spellId) const +{ + SkillLineAbilityMap::const_iterator lower = GetBeginSkillLineAbilityMap(spellId); + SkillLineAbilityMap::const_iterator upper = GetEndSkillLineAbilityMap(spellId); + + for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_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) + return true; + } + + return false; +} + SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const { // ignore passive spells @@ -2558,6 +2584,8 @@ uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo,uint32 map_id,u case 51721: // Dominion Over Acherus case 54055: // Dominion Over Acherus return area_id == 4281 || area_id == 4342 ? 0 : SPELL_FAILED_INCORRECT_AREA; + case 54119: // Mist of the Kvaldir + return area_id == 4028 || area_id == 4029 || area_id == 4106 || area_id == 4031 ? 0 : SPELL_FAILED_INCORRECT_AREA; } return 0; diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index 9903220677f..77ee39e2249 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -397,6 +397,7 @@ bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId); uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id); static bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS]; + inline bool IsAreaOfEffectSpell(SpellEntry const *spellInfo) { if(IsAreaEffectTarget[spellInfo->EffectImplicitTargetA[0]] || IsAreaEffectTarget[spellInfo->EffectImplicitTargetB[0]]) @@ -873,6 +874,14 @@ class SpellMgr return 0; } + uint32 GetNextSpellInChain(uint32 spell_id) const + { + if(SpellChainNode const* node = GetSpellChainNode(spell_id)) + return node->next; + + return 0; + } + SpellsRequiringSpellMap const& GetSpellsRequiringSpell() const { return mSpellsReqSpell; } // Note: not use rank for compare to spell ranks: spell chains isn't linear order @@ -957,6 +966,9 @@ class SpellMgr static bool IsPrimaryProfessionSpell(uint32 spellId); bool IsPrimaryProfessionFirstRankSpell(uint32 spellId) const; + bool IsSkillBonusSpell(uint32 spellId) const; + + // Spell script targets SpellScriptTarget::const_iterator GetBeginSpellScriptTarget(uint32 spell_id) const { diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index b90bd733a6d..e635efa0457 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -4567,7 +4567,8 @@ bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown) { SpellEntry const *dummySpell = triggeredByAura->GetSpellProto (); - uint32 effIndex = triggeredByAura->GetEffIndex (); + uint32 effIndex = triggeredByAura->GetEffIndex(); + int32 triggerAmount = triggeredByAura->GetModifier()->m_amount; Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL; @@ -4591,7 +4592,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // return damage % to attacker but < 50% own total health - basepoints0 = triggeredByAura->GetModifier()->m_amount*int32(damage)/100; + basepoints0 = triggerAmount*int32(damage)/100; if(basepoints0 > GetMaxHealth()/2) basepoints0 = GetMaxHealth()/2; @@ -4913,7 +4914,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case 48504: { triggered_spell_id = 48503; - basepoints0 = triggeredByAura->GetModifier()->m_amount; + basepoints0 = triggerAmount; target = this; break; } @@ -4931,7 +4932,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case 31872: { // Roll chane - if (!roll_chance_i(triggeredByAura->GetModifier()->m_amount)) + if (!roll_chance_i(triggerAmount)) return false; // Remove any stun effect on target @@ -4962,7 +4963,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // mana reward - basepoints0 = (triggeredByAura->GetModifier()->m_amount * GetMaxPower(POWER_MANA) / 100); + basepoints0 = (triggerAmount * GetMaxPower(POWER_MANA) / 100); target = this; triggered_spell_id = 29442; break; @@ -4975,7 +4976,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu // mana cost save int32 cost = procSpell->manaCost + procSpell->ManaCostPercentage * GetCreateMana() / 100; - basepoints0 = cost * triggeredByAura->GetModifier()->m_amount/100; + basepoints0 = cost * triggerAmount/100; if( basepoints0 <=0 ) return false; @@ -4986,7 +4987,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu // Hot Streak if (dummySpell->SpellIconID == 2999) { - if (triggeredByAura->GetEffIndex()!=0) + if (effIndex!=0) return true; Aura *counter = GetAura(triggeredByAura->GetId(), 1); if (!counter) @@ -5000,7 +5001,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if (mod->m_amount < 100) // not enough return true; // Crititcal counted -> roll chance - if (roll_chance_i(triggeredByAura->GetModifier()->m_amount)) + if (roll_chance_i(triggerAmount)) CastSpell(this, 48108, true, castItem, triggeredByAura); } mod->m_amount = 25; @@ -5013,7 +5014,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; int32 cost = procSpell->manaCost + procSpell->ManaCostPercentage * GetCreateMana() / 100; - basepoints0 = cost * triggeredByAura->GetModifier()->m_amount/100; + basepoints0 = cost * triggerAmount/100; if( basepoints0 <=0 ) return false; triggered_spell_id = 44450; @@ -5109,7 +5110,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if (dummySpell->SpellIconID == 3214) { triggered_spell_id = 59653; - basepoints0 = GetShieldBlockValue() * triggeredByAura->GetModifier()->m_amount / 100; + basepoints0 = GetShieldBlockValue() * triggerAmount / 100; break; } break; @@ -5168,7 +5169,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if (!target) return false; triggered_spell_id = 54181; - basepoints0 = damage * triggeredByAura->GetModifier()->m_amount / 100; + basepoints0 = damage * triggerAmount / 100; break; } switch(dummySpell->Id) @@ -5187,7 +5188,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case 30296: { // health - basepoints0 = int32(damage*triggeredByAura->GetModifier()->m_amount/100); + basepoints0 = int32(damage*triggerAmount/100); target = this; triggered_spell_id = 30294; break; @@ -5206,7 +5207,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // heal amount - basepoints0 = damage * triggeredByAura->GetModifier()->m_amount/100; + basepoints0 = damage * triggerAmount/100; triggered_spell_id = 37382; break; } @@ -5232,14 +5233,14 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // energize amount - basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100; + basepoints0 = triggerAmount*damage/100; pVictim->CastCustomSpell(pVictim,34919,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); return true; // no hidden cooldown } // Divine Aegis if (dummySpell->SpellIconID == 2820) { - basepoints0 = damage * triggeredByAura->GetModifier()->m_amount/100; + basepoints0 = damage * triggerAmount/100; triggered_spell_id = 47753; break; } @@ -5256,8 +5257,9 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // heal amount - basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100; - pVictim->CastCustomSpell(pVictim,15290,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); + int32 team = triggerAmount*damage/500; + int32 self = triggerAmount*damage/100 - team; + pVictim->CastCustomSpell(pVictim,15290,&team,&self,NULL,true,castItem,triggeredByAura); return true; // no hidden cooldown } // Priest Tier 6 Trinket (Ashtongue Talisman of Acumen) @@ -5291,7 +5293,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // heal amount - basepoints0 = damage * triggeredByAura->GetModifier()->m_amount/100; + basepoints0 = damage * triggerAmount/100; target = this; triggered_spell_id = 39373; break; @@ -5379,7 +5381,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if (!procSpell) return false; // Only 0 aura can proc - if (triggeredByAura->GetEffIndex()!=0) + if (effIndex!=0) return true; // Wrath crit if (procSpell->SpellFamilyFlags & 0x0000000000000001LL) @@ -5403,7 +5405,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu else if (dummySpell->SpellIconID == 2860) { triggered_spell_id = 48504; - basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100; + basepoints0 = triggerAmount * damage / 100; break; } break; @@ -5455,7 +5457,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // energy cost save - basepoints0 = procSpell->manaCost * triggeredByAura->GetModifier()->m_amount/100; + basepoints0 = procSpell->manaCost * triggerAmount/100; if(basepoints0 <= 0) return false; @@ -5474,7 +5476,8 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // mana cost save - basepoints0 = procSpell->manaCost * 40/100; + int32 mana = procSpell->manaCost + procSpell->ManaCostPercentage * GetCreateMana() / 100; + basepoints0 = mana * 40/100; if(basepoints0 <= 0) return false; @@ -5493,9 +5496,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if ( dummySpell->SpellIconID == 3579 ) { // Proc only from periodic (from trap activation proc another aura of this spell) - if (!(procFlag & PROC_FLAG_ON_DO_PERIODIC)) - return false; - if (!roll_chance_i(triggeredByAura->GetModifier()->m_amount)) + if (!(procFlag & PROC_FLAG_ON_DO_PERIODIC) || !roll_chance_i(triggerAmount)) return false; triggered_spell_id = 56453; target = this; @@ -5516,7 +5517,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case SPELLFAMILY_PALADIN: { // Seal of Righteousness - melee proc dummy (addition ${$MWS*(0.022*$AP+0.044*$SPH)} damage) - if (dummySpell->SpellFamilyFlags&0x000000008000000LL && triggeredByAura->GetEffIndex()==0) + if (dummySpell->SpellFamilyFlags&0x000000008000000LL && effIndex==0) { triggered_spell_id = 25742; float ap = GetTotalAttackPowerValue(BASE_ATTACK); @@ -5536,7 +5537,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if (dummySpell->SpellIconID == 3025) { // 4 damage tick - basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/400; + basepoints0 = triggerAmount*damage/400; triggered_spell_id = 61840; break; } @@ -5544,7 +5545,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if (dummySpell->SpellIconID == 3030) { // 4 healing tick - basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/400; + basepoints0 = triggerAmount*damage/400; triggered_spell_id = 54203; break; } @@ -5620,7 +5621,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // heal amount - basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100; + basepoints0 = triggerAmount*damage/100; target = this; triggered_spell_id = 31786; break; @@ -5628,13 +5629,13 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu // Seal of Blood do damage trigger case 31892: { - if (triggeredByAura->GetEffIndex() == 0) // 0 effect - is proc on enemy + if (effIndex == 0) // 0 effect - is proc on enemy triggered_spell_id = 31893; - else if (triggeredByAura->GetEffIndex() == 1) // 1 effect - is proc on self + else if (effIndex == 1) // 1 effect - is proc on self { // add spell damage from prev effect (27%) damage += CalculateDamage(BASE_ATTACK, false) * 27 / 100; - basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100; + basepoints0 = triggerAmount * damage / 100; target = this; triggered_spell_id = 32221; } @@ -5645,13 +5646,13 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu // Seal of the Martyr do damage trigger case 53720: { - if (triggeredByAura->GetEffIndex() == 0) // 0 effect - is proc on enemy + if (effIndex == 0) // 0 effect - is proc on enemy triggered_spell_id = 53719; - else if (triggeredByAura->GetEffIndex() == 1) // 1 effect - is proc on self + else if (effIndex == 1) // 1 effect - is proc on self { // add spell damage from prev effect (27%) damage += CalculateDamage(BASE_ATTACK, false) * 27 / 100; - basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100; + basepoints0 = triggerAmount * damage / 100; target = this; triggered_spell_id = 53718; } @@ -5704,14 +5705,14 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case 54936: { triggered_spell_id = 54957; - basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100; + basepoints0 = triggerAmount*damage/100; break; } // Glyph of Holy Light case 54937: { triggered_spell_id = 54968; - basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100; + basepoints0 = triggerAmount*damage/100; break; } } @@ -5864,7 +5865,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu // Not proc from self heals if (this==pVictim) return false; - basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100; + basepoints0 = triggerAmount * damage / 100; target = this; triggered_spell_id = 55533; break; @@ -5876,7 +5877,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu target = GetOwner(); if(!target) return false; - basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100; + basepoints0 = triggerAmount * damage / 100; triggered_spell_id = 58879; break; } @@ -5886,14 +5887,14 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu { // TODO: frite dummy fot triggered spell triggered_spell_id = 52759; - basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100; + basepoints0 = triggerAmount * damage / 100; target = this; break; } // Earth Shield if(dummySpell->SpellFamilyFlags & 0x0000040000000000LL) { - basepoints0 = triggeredByAura->GetModifier()->m_amount; + basepoints0 = triggerAmount; target = this; triggered_spell_id = 379; break; @@ -6027,14 +6028,14 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu { if (GetTypeId() != TYPEID_PLAYER || !((Player*)this)->isHonorOrXPTarget(pVictim)) return false; - basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100; + basepoints0 = triggerAmount * damage / 100; triggered_spell_id = 53168; break; } // Butchery if (dummySpell->SpellIconID == 2664) { - basepoints0 = triggeredByAura->GetModifier()->m_amount; + basepoints0 = triggerAmount; triggered_spell_id = 50163; target = this; break; @@ -6043,7 +6044,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if (dummySpell->Id == 49028) { // 1 dummy aura for dismiss rune blade - if (triggeredByAura->GetEffIndex()!=2) + if (effIndex!=2) return false; // TODO: wite script for this "fights on its own, doing the same attacks" // NOTE: Trigger here on every attack and spell cast @@ -6059,7 +6060,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu // Vendetta if (dummySpell->SpellFamilyFlags & 0x0000000000010000LL) { - basepoints0 = triggeredByAura->GetModifier()->m_amount * GetMaxHealth() / 100; + basepoints0 = triggerAmount * GetMaxHealth() / 100; triggered_spell_id = 50181; target = this; break; @@ -6067,7 +6068,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu // Necrosis if (dummySpell->SpellIconID == 2709) { - basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100; + basepoints0 = triggerAmount * damage / 100; triggered_spell_id = 51460; break; } @@ -6089,7 +6090,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu { if (!roll_chance_f(GetUnitCriticalChance(BASE_ATTACK, pVictim))) return false; - basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100; + basepoints0 = triggerAmount * damage / 100; triggered_spell_id = 50526; break; } @@ -6747,11 +6748,16 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB // Need add combopoint AFTER finish movie (or they dropped in finish phase) break; } + // Bloodthirst (($m/100)% of max health) + case 23880: + { + basepoints0 = int32(GetMaxHealth() * triggerAmount / 10000); + break; + } // Shamanistic Rage triggered spell case 30824: { basepoints0 = int32(GetTotalAttackPowerValue(BASE_ATTACK) * triggerAmount / 100); - trigger_spell_id = 30824; break; } // Enlightenment (trigger only from mana cost spells) @@ -6806,6 +6812,14 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB return false; break; } + // Lock and Load + case 56453: + { + // Proc only from trap activation (from periodic proc another aura of this spell) + if (!(procFlags & PROC_FLAG_ON_TRAP_ACTIVATION) || !roll_chance_i(triggerAmount)) + return false; + break; + } } if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(trigger_spell_id)) @@ -10554,7 +10568,7 @@ bool InitTriggerAuraData() isTriggerAura[SPELL_AURA_MOD_DAMAGE_FROM_CASTER] = true; isNonTriggerAura[SPELL_AURA_MOD_POWER_REGEN]=true; - isNonTriggerAura[SPELL_AURA_RESIST_PUSHBACK]=true; + isNonTriggerAura[SPELL_AURA_REDUCE_PUSHBACK]=true; return true; } diff --git a/src/game/World.cpp b/src/game/World.cpp index 7dd1a54769f..71bc1058cee 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -777,7 +777,8 @@ void World::LoadConfigSettings(bool reload) } m_configs[CONFIG_GM_LOGIN_STATE] = sConfig.GetIntDefault("GM.LoginState", 2); -// m_configs[CONFIG_GM_ACCEPT_TICKETS] = sConfig.GetIntDefault("GM.AcceptTickets", 2); + m_configs[CONFIG_GM_VISIBLE_STATE] = sConfig.GetIntDefault("GM.Visible", 2); + //m_configs[CONFIG_GM_ACCEPT_TICKETS] = sConfig.GetIntDefault("GM.AcceptTickets", 2); m_configs[CONFIG_GM_CHAT] = sConfig.GetIntDefault("GM.Chat", 2); m_configs[CONFIG_GM_WISPERING_TO] = sConfig.GetIntDefault("GM.WhisperingTo", 2); m_configs[CONFIG_GM_IN_GM_LIST] = sConfig.GetBoolDefault("GM.InGMList", false); @@ -796,6 +797,7 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_START_GM_LEVEL] = MAX_LEVEL; } m_configs[CONFIG_GM_LOWER_SECURITY] = sConfig.GetBoolDefault("GM.LowerSecurity", false); + m_configs[CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS] = sConfig.GetBoolDefault("GM.AllowAchievementGain", true); m_configs[CONFIG_GROUP_VISIBILITY] = sConfig.GetIntDefault("Visibility.GroupMode",0); diff --git a/src/game/World.h b/src/game/World.h index c35529b21f8..fb0d82e5069 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -125,6 +125,8 @@ enum WorldConfigs CONFIG_MAX_PRIMARY_TRADE_SKILL, CONFIG_MIN_PETITION_SIGNS, CONFIG_GM_LOGIN_STATE, + CONFIG_GM_VISIBLE_STATE, + CONFIG_GM_ACCEPT_TICKETS, CONFIG_GM_CHAT, CONFIG_GM_WISPERING_TO, CONFIG_GM_IN_GM_LIST, @@ -132,6 +134,7 @@ enum WorldConfigs CONFIG_GM_LOG_TRADE, CONFIG_START_GM_LEVEL, CONFIG_GM_LOWER_SECURITY, + CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS, CONFIG_GROUP_VISIBILITY, CONFIG_MAIL_DELIVERY_DELAY, CONFIG_UPTIME_UPDATE, diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index 5d303e0c131..c0ec604a7aa 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -854,6 +854,12 @@ Channel.SilentlyGMJoin = 0 # 0 (disable) # 1 (enable) # +# GM.Visible +# GM visibility at login +# Default: 2 (last save state) +# 0 (invisible) +# 1 (visible) +# # GM.AcceptTickets # Is GM accepting tickets from player by default or not. # Default: 2 (last save state) @@ -896,17 +902,24 @@ Channel.SilentlyGMJoin = 0 # Default: 0 (disable) # 1 (enable) # +# GM.AllowAchievementGain +# If enabled it allows gaining achievements for GM characters +# Default: 0 (disable) +# 1 (enable) (default) +# ################################################################################################################### -GM.LoginState = 2 -GM.AcceptTickets = 2 -GM.Chat = 2 -GM.WhisperingTo = 2 -GM.InGMList = 0 -GM.InWhoList = 0 -GM.LogTrade = 1 -GM.StartLevel = 70 -GM.LowerSecurity = 0 +GM.LoginState = 2 +GM.Visible = 2 +GM.AcceptTickets = 2 +GM.Chat = 2 +GM.WhisperingTo = 2 +GM.InGMList = 0 +GM.InWhoList = 0 +GM.LogTrade = 1 +GM.StartLevel = 80 +GM.LowerSecurity = 0 +GM.AllowAchievementGain = 1 ################################################################################################################### # VISIBILITY AND RADIUSES diff --git a/src/shared/revision.h b/src/shared/revision.h index 028a5a28b40..fcee9b895b3 100644 --- a/src/shared/revision.h +++ b/src/shared/revision.h @@ -1,7 +1,7 @@ #ifndef __REVISION_H__ #define __REVISION_H__ - #define _REVISION "1066" - #define _HASH "1264da9c9234" + #define _REVISION "1083" + #define _HASH "b79b2c3d242f" #define _REVISION_DATE "*" #define _REVISION_TIME "*" #endif // __REVISION_H__ diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 7f75b49b7ca..aecd6e404c8 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 "7158" + #define REVISION_NR "7183" #endif // __REVISION_NR_H__ |