diff --git a/Makefile.am b/Makefile.am index b63cd3472..767eda776 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,19 +20,8 @@ SUBDIRS = dep doc sql src ## Additional files to include when running 'make dist' -# Win32 project workspace for Visual Studio .NET 2003 -EXTRA_DIST = \ - win/mangosdVC71.sln \ - win/VC71/framework.vcproj \ - win/VC71/game.vcproj \ - win/VC71/mangosd.vcproj \ - win/VC71/realmd.vcproj \ - win/VC71/shared.vcproj \ - win/VC71/zlib.vcproj \ - win/VC71/g3dlite.vcproj - # Win32 project workspace for Visual Studio .NET 2005 -EXTRA_DIST += \ +EXTRA_DIST = \ win/mangosdVC80.sln \ win/VC80/framework.vcproj \ win/VC80/game.vcproj \ @@ -53,3 +42,14 @@ EXTRA_DIST += \ win/VC90/zlib.vcproj \ win/VC90/g3dlite.vcproj +# Win32 project workspace for Visual Studio .NET 2010 +EXTRA_DIST += \ + win/mangosdVC100.sln \ + win/VC100/framework.vcxproj \ + win/VC100/game.vcxproj \ + win/VC100/mangosd.vcxproj \ + win/VC100/realmd.vcxproj \ + win/VC100/shared.vcxproj \ + win/VC100/zlib.vcxproj \ + win/VC100/g3dlite.vcxproj + diff --git a/sql/mangos.sql b/sql/mangos.sql index f566859cb..c63f3b6cf 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -23,7 +23,7 @@ DROP TABLE IF EXISTS `db_version`; CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, - `required_8115_01_mangos_playercreateinfo_action` bit(1) default NULL + `required_8140_01_mangos_spell_proc_event` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -17123,9 +17123,9 @@ INSERT INTO `spell_proc_event` VALUES (31836, 0x00000000, 10, 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (31871, 0x00000000, 10, 0x00000010, 0x00000000, 0x00000000, 0x00004000, 0x00000000, 0.000000, 0.000000, 0), (31872, 0x00000000, 10, 0x00000010, 0x00000000, 0x00000000, 0x00004000, 0x00000000, 0.000000, 0.000000, 0), -(31876, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), -(31877, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), -(31878, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), +(31876, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000008, 0x00004110, 0x00000000, 0.000000, 0.000000, 0), +(31877, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000008, 0x00004110, 0x00000000, 0.000000, 0.000000, 0), +(31878, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000008, 0x00004110, 0x00000000, 0.000000, 0.000000, 0), (31904, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000040, 0.000000, 0.000000, 0), (32385, 0x00000000, 5, 0x00000402, 0x00000011, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (32387, 0x00000000, 5, 0x00000402, 0x00000011, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), diff --git a/sql/updates/8140_01_mangos_spell_proc_event.sql b/sql/updates/8140_01_mangos_spell_proc_event.sql new file mode 100644 index 000000000..84a6ef378 --- /dev/null +++ b/sql/updates/8140_01_mangos_spell_proc_event.sql @@ -0,0 +1,7 @@ +ALTER TABLE db_version CHANGE COLUMN required_8115_01_mangos_playercreateinfo_action required_8140_01_mangos_spell_proc_event bit; + +DELETE FROM spell_proc_event WHERE entry IN (31876, 31877, 31878); +INSERT INTO spell_proc_event VALUES +(31876, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000008, 0x00004110, 0x00000000, 0.000000, 0.000000, 0), +(31877, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000008, 0x00004110, 0x00000000, 0.000000, 0.000000, 0), +(31878, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000008, 0x00004110, 0x00000000, 0.000000, 0.000000, 0); \ No newline at end of file diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index ef83aa02b..c9002001c 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -242,6 +242,7 @@ pkgdata_DATA = \ 8104_01_characters.sql \ 8112_01_mangos_spell_proc_event.sql \ 8115_01_mangos_playercreateinfo_action.sql \ + 8140_01_mangos_spell_proc_event.sql \ README ## Additional files to include when running 'make dist' @@ -464,4 +465,5 @@ EXTRA_DIST = \ 8104_01_characters.sql \ 8112_01_mangos_spell_proc_event.sql \ 8115_01_mangos_playercreateinfo_action.sql \ + 8140_01_mangos_spell_proc_event.sql \ README diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index a0799d0bc..f8bd12d02 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -244,30 +244,31 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) } // prevent character creating with invalid name - if(!normalizePlayerName(name)) + if (!normalizePlayerName(name)) { - data << (uint8)CHAR_NAME_INVALID_CHARACTER; + data << (uint8)CHAR_NAME_NO_NAME; SendPacket( &data ); sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId()); return; } // check name limitations - if(!ObjectMgr::IsValidName(name,true)) + uint8 res = ObjectMgr::CheckPlayerName(name,true); + if (res != CHAR_NAME_SUCCESS) { - data << (uint8)CHAR_NAME_INVALID_CHARACTER; + data << uint8(res); SendPacket( &data ); return; } - if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(name)) + if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(name)) { data << (uint8)CHAR_NAME_RESERVED; SendPacket( &data ); return; } - if(objmgr.GetPlayerGUIDByName(name)) + if (objmgr.GetPlayerGUIDByName(name)) { data << (uint8)CHAR_CREATE_NAME_IN_USE; SendPacket( &data ); @@ -275,7 +276,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) } QueryResult *resultacct = loginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId()); - if ( resultacct ) + if (resultacct) { Field *fields=resultacct->Fetch(); uint32 acctcharcount = fields[0].GetUInt32(); @@ -967,7 +968,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) recv_data >> newname; // prevent character rename to invalid name - if(!normalizePlayerName(newname)) + if (!normalizePlayerName(newname)) { WorldPacket data(SMSG_CHAR_RENAME, 1); data << uint8(CHAR_NAME_NO_NAME); @@ -975,16 +976,17 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) return; } - if(!ObjectMgr::IsValidName(newname, true)) + uint8 res = ObjectMgr::CheckPlayerName(newname,true); + if (res != CHAR_NAME_SUCCESS) { WorldPacket data(SMSG_CHAR_RENAME, 1); - data << uint8(CHAR_NAME_INVALID_CHARACTER); + data << uint8(res); SendPacket( &data ); return; } // check name limitations - if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) + if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) { WorldPacket data(SMSG_CHAR_RENAME, 1); data << uint8(CHAR_NAME_RESERVED); @@ -1240,7 +1242,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) } // prevent character rename to invalid name - if(!normalizePlayerName(newname)) + if (!normalizePlayerName(newname)) { WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); data << uint8(CHAR_NAME_NO_NAME); @@ -1248,16 +1250,17 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) return; } - if(!ObjectMgr::IsValidName(newname,true)) + uint8 res = ObjectMgr::CheckPlayerName(newname,true); + if (res != CHAR_NAME_SUCCESS) { WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_NAME_INVALID_CHARACTER); + data << uint8(res); SendPacket( &data ); return; } // check name limitations - if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) + if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) { WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); data << uint8(CHAR_NAME_RESERVED); @@ -1266,9 +1269,9 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) } // character with this name already exist - if(uint64 newguid = objmgr.GetPlayerGUIDByName(newname)) + if (uint64 newguid = objmgr.GetPlayerGUIDByName(newname)) { - if(newguid != guid) + if (newguid != guid) { WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); data << uint8(CHAR_CREATE_NAME_IN_USE); diff --git a/src/game/CreatureEventAI.cpp b/src/game/CreatureEventAI.cpp index 7b7e6966e..ca0384740 100644 --- a/src/game/CreatureEventAI.cpp +++ b/src/game/CreatureEventAI.cpp @@ -112,13 +112,6 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction if (pHolder.Event.event_inverse_phase_mask & (1 << Phase)) return false; - //Store random here so that all random actions match up - uint32 rnd = rand(); - - //Return if chance for event is not met - if (pHolder.Event.event_chance <= rnd % 100) - return false; - CreatureEventAI_Event const& event = pHolder.Event; //Check event conditions based on the event type, also reset events @@ -330,6 +323,13 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction if (!(pHolder.Event.event_flags & EFLAG_REPEATABLE)) pHolder.Enabled = false; + //Store random here so that all random actions match up + uint32 rnd = rand(); + + //Return if chance for event is not met + if (pHolder.Event.event_chance <= rnd % 100) + return false; + //Process actions for (uint32 j = 0; j < MAX_ACTIONS; j++) ProcessAction(pHolder.Event.action[j], rnd, pHolder.Event.event_id, pActionInvoker); diff --git a/src/game/CreatureEventAIMgr.cpp b/src/game/CreatureEventAIMgr.cpp index 79f059961..b90f944ce 100644 --- a/src/game/CreatureEventAIMgr.cpp +++ b/src/game/CreatureEventAIMgr.cpp @@ -473,9 +473,8 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() action.morph.modelId = 0; } } - - break; } + break; case ACTION_T_SOUND: if (!sSoundEntriesStore.LookupEntry(action.sound.soundId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i, j+1, action.sound.soundId); diff --git a/src/game/GridDefines.h b/src/game/GridDefines.h index d5ddf3aaf..6565e0e0f 100644 --- a/src/game/GridDefines.h +++ b/src/game/GridDefines.h @@ -65,7 +65,7 @@ typedef GridRefManager GameObjectMapType; typedef GridRefManager PlayerMapType; typedef Grid GridType; -typedef NGrid<8, Player, AllWorldObjectTypes, AllGridObjectTypes> NGridType; +typedef NGrid NGridType; typedef TypeMapContainer GridTypeMapContainer; typedef TypeMapContainer WorldTypeMapContainer; diff --git a/src/game/ItemHandler.cpp b/src/game/ItemHandler.cpp index ac030bd67..b9b919174 100644 --- a/src/game/ItemHandler.cpp +++ b/src/game/ItemHandler.cpp @@ -673,7 +673,31 @@ void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data ) recv_data >> vendorguid >> item >> slot >> bagguid >> bagslot >> count; - GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bagguid,bagslot); + uint8 bag = NULL_BAG; // init for case invalid bagGUID + + // find bag slot by bag guid + if (bagguid == _player->GetGUID()) + bag = INVENTORY_SLOT_BAG_0; + else + { + for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;++i) + { + if (Bag *pBag = (Bag*)_player->GetItemByPos(INVENTORY_SLOT_BAG_0,i)) + { + if (bagguid == pBag->GetGUID()) + { + bag = i; + break; + } + } + } + } + + // bag not found, cheating? + if (bag == NULL_BAG) + return; + + GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bag,bagslot); } void WorldSession::HandleBuyItemOpcode( WorldPacket & recv_data ) diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 7ff4ecf3c..98cbe805f 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -4504,9 +4504,6 @@ bool ChatHandler::HandleResetHonorCommand (const char * args) static bool HandleResetStatsOrLevelHelper(Player* player) { - PlayerInfo const *info = objmgr.GetPlayerInfo(player->getRace(), player->getClass()); - if(!info) return false; - ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(player->getClass()); if(!cEntry) { @@ -4529,21 +4526,7 @@ static bool HandleResetStatsOrLevelHelper(Player* player) // reset only if player not in some form; if(player->m_form==FORM_NONE) - { - switch(player->getGender()) - { - case GENDER_FEMALE: - player->SetDisplayId(info->displayId_f); - player->SetNativeDisplayId(info->displayId_f); - break; - case GENDER_MALE: - player->SetDisplayId(info->displayId_m); - player->SetNativeDisplayId(info->displayId_m); - break; - default: - break; - } - } + player->InitDisplayIds(); player->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP ); player->SetByteValue(UNIT_FIELD_BYTES_2, 3, player->m_form); @@ -5623,18 +5606,18 @@ bool ChatHandler::HandlePDumpLoadCommand(const char *args) char* name_str = strtok(NULL, " "); std::string name; - if(name_str) + if (name_str) { name = name_str; // normalize the name if specified and check if it exists - if(!normalizePlayerName(name)) + if (!normalizePlayerName(name)) { PSendSysMessage(LANG_INVALID_CHARACTER_NAME); SetSentErrorMessage(true); return false; } - if(!ObjectMgr::IsValidName(name,true)) + if (ObjectMgr::CheckPlayerName(name,true) != CHAR_NAME_SUCCESS) { PSendSysMessage(LANG_INVALID_CHARACTER_NAME); SetSentErrorMessage(true); @@ -5646,17 +5629,17 @@ bool ChatHandler::HandlePDumpLoadCommand(const char *args) uint32 guid = 0; - if(guid_str) + if (guid_str) { guid = atoi(guid_str); - if(!guid) + if (!guid) { PSendSysMessage(LANG_INVALID_CHARACTER_GUID); SetSentErrorMessage(true); return false; } - if(objmgr.GetPlayerAccountIdByGUID(guid)) + if (objmgr.GetPlayerAccountIdByGUID(guid)) { PSendSysMessage(LANG_CHARACTER_GUID_IN_USE,guid); SetSentErrorMessage(true); @@ -6562,8 +6545,7 @@ bool ChatHandler::HandleModifyGenderCommand(const char *args) player->SetByteValue(PLAYER_BYTES_3, 0, gender); // Change display ID - player->SetDisplayId(gender ? info->displayId_f : info->displayId_m); - player->SetNativeDisplayId(gender ? info->displayId_f : info->displayId_m); + player->InitDisplayIds(); char const* gender_full = gender ? "female" : "male"; diff --git a/src/game/LootHandler.cpp b/src/game/LootHandler.cpp index cfba29ad3..06c84aeff 100644 --- a/src/game/LootHandler.cpp +++ b/src/game/LootHandler.cpp @@ -247,6 +247,10 @@ void WorldSession::HandleLootOpcode( WorldPacket & recv_data ) uint64 guid; recv_data >> guid; + // Check possible cheat + if(!_player->isAlive()) + return; + GetPlayer()->SendLoot(guid, LOOT_CORPSE); } diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp index 2453f193f..72c42825e 100644 --- a/src/game/Mail.cpp +++ b/src/game/Mail.cpp @@ -762,38 +762,40 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ ) { data << (uint32) 0; // float data << (uint32) 0; // count + uint32 count = 0; + time_t now = time(NULL); for(PlayerMails::iterator itr = _player->GetmailBegin(); itr != _player->GetmailEnd(); ++itr) { Mail *m = (*itr); - // not checked yet, already must be delivered - if((m->checked & MAIL_CHECK_MASK_READ)==0 && (m->deliver_time <= time(NULL))) + // must be not checked yet + if(m->checked & MAIL_CHECK_MASK_READ) + continue; + + // and already delivered + if(now < m->deliver_time) + continue; + + data << (uint64) m->sender; // sender guid + + switch(m->messageType) { - ++count; - - if(count > 2) - { - count = 2; + case MAIL_AUCTION: + data << (uint32) 2; + data << (uint32) 2; + data << (uint32) m->stationery; + break; + default: + data << (uint32) 0; + data << (uint32) 0; + data << (uint32) m->stationery; break; - } - - data << (uint64) m->sender; // sender guid - - switch(m->messageType) - { - case MAIL_AUCTION: - data << (uint32) 2; - data << (uint32) 2; - data << (uint32) m->stationery; - break; - default: - data << (uint32) 0; - data << (uint32) 0; - data << (uint32) m->stationery; - break; - } - data << (uint32) 0xC6000000; // float unk, time or something } + data << (uint32) 0xC6000000; // float unk, time or something + + ++count; + if(count == 2) // do not display more than 2 mails + break; } data.put(4, count); } diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 8999c1fb8..3c85796c3 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -6645,18 +6645,24 @@ bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bo return false; } -bool ObjectMgr::IsValidName( const std::string& name, bool create ) +uint8 ObjectMgr::CheckPlayerName( const std::string& name, bool create ) { std::wstring wname; if(!Utf8toWStr(name,wname)) - return false; + return CHAR_NAME_INVALID_CHARACTER; - if(wname.size() < 1 || wname.size() > MAX_PLAYER_NAME) - return false; + if(wname.size() > MAX_PLAYER_NAME) + return CHAR_NAME_TOO_LONG; + + uint32 minName = sWorld.getConfig(CONFIG_MIN_PLAYER_NAME); + if(wname.size() < minName) + return CHAR_NAME_TOO_SHORT; uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PLAYER_NAMES); - - return isValidString(wname,strictMask,false,create); + if(!isValidString(wname,strictMask,false,create)) + return CHAR_NAME_MIXED_LANGUAGES; + + return CHAR_NAME_SUCCESS; } bool ObjectMgr::IsValidCharterName( const std::string& name ) @@ -6665,7 +6671,11 @@ bool ObjectMgr::IsValidCharterName( const std::string& name ) if(!Utf8toWStr(name,wname)) return false; - if(wname.size() < 1) + if(wname.size() > MAX_CHARTER_NAME) + return false; + + uint32 minName = sWorld.getConfig(CONFIG_MIN_CHARTER_NAME); + if(wname.size() < minName) return false; uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_CHARTER_NAMES); @@ -6673,18 +6683,24 @@ bool ObjectMgr::IsValidCharterName( const std::string& name ) return isValidString(wname,strictMask,true); } -bool ObjectMgr::IsValidPetName( const std::string& name ) +PetNameInvalidReason ObjectMgr::CheckPetName( const std::string& name ) { std::wstring wname; if(!Utf8toWStr(name,wname)) - return false; + return PET_NAME_INVALID; - if(wname.size() < 1) - return false; + if(wname.size() > MAX_PET_NAME) + return PET_NAME_TOO_LONG; + + uint32 minName = sWorld.getConfig(CONFIG_MIN_PET_NAME); + if(wname.size() < minName) + return PET_NAME_TOO_SHORT; uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PET_NAMES); + if(!isValidString(wname,strictMask,false)) + return PET_NAME_MIXED_LANGUAGES; - return isValidString(wname,strictMask,false); + return PET_NAME_SUCCESS; } int ObjectMgr::GetIndexForLocale( LocaleConstant loc ) diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index c869a191f..7aeac8405 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -279,8 +279,10 @@ enum SkillRangeType SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial); -#define MAX_PLAYER_NAME 12 // max allowed by client name length +#define MAX_PLAYER_NAME 12 // max allowed by client name length #define MAX_INTERNAL_PLAYER_NAME 15 // max server internal player name length ( > MAX_PLAYER_NAME for support declined names ) +#define MAX_PET_NAME 12 // max allowed by client name length +#define MAX_CHARTER_NAME 24 // max allowed by client name length bool normalizePlayerName(std::string& name); @@ -693,9 +695,9 @@ class ObjectMgr bool IsReservedName(const std::string& name) const; // name with valid structure and symbols - static bool IsValidName( const std::string& name, bool create = false ); + static uint8 CheckPlayerName( const std::string& name, bool create = false ); + static PetNameInvalidReason CheckPetName( const std::string& name ); static bool IsValidCharterName( const std::string& name ); - static bool IsValidPetName( const std::string& name ); static bool CheckDeclinedNames(std::wstring mainpart, DeclinedName const& names); diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 2f9cbfdba..32964c818 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -1459,13 +1459,16 @@ bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab) { if(removeSpell(spell_id,learn_prev,clear_ab)) { - if(GetOwner()->GetTypeId() == TYPEID_PLAYER) + if(!m_loading) { - if(!m_loading) + if (Unit* owner = GetOwner()) { - WorldPacket data(SMSG_PET_REMOVED_SPELL, 4); - data << uint32(spell_id); - ((Player*)GetOwner())->GetSession()->SendPacket(&data); + if(owner->GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_PET_REMOVED_SPELL, 4); + data << uint32(spell_id); + ((Player*)owner)->GetSession()->SendPacket(&data); + } } } return true; diff --git a/src/game/Pet.h b/src/game/Pet.h index bbe878d1d..303d3ba50 100644 --- a/src/game/Pet.h +++ b/src/game/Pet.h @@ -91,6 +91,9 @@ enum PetTalk enum PetNameInvalidReason { + // custom, not send + PET_NAME_SUCCESS = 0, + PET_NAME_INVALID = 1, PET_NAME_NO_NAME = 2, PET_NAME_TOO_SHORT = 3, diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp index cd0e1db02..9c4223e34 100644 --- a/src/game/PetAI.cpp +++ b/src/game/PetAI.cpp @@ -335,4 +335,4 @@ void PetAI::AttackedBy(Unit *attacker) if(!m_creature->getVictim() && m_creature->GetCharmInfo() && !m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) && (!m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY) || m_creature->canReachWithAttack(attacker))) AttackStart(attacker); -} +} \ No newline at end of file diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index a9f464b40..b665a0584 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -401,9 +401,10 @@ void WorldSession::HandlePetRename( WorldPacket & recv_data ) pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() ) return; - if(!ObjectMgr::IsValidPetName(name)) + PetNameInvalidReason res = ObjectMgr::CheckPetName(name); + if(res != PET_NAME_SUCCESS) { - SendPetNameInvalid(PET_NAME_INVALID, name, NULL); + SendPetNameInvalid(res, name, NULL); return; } diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 72c5db56d..22f1b8f3c 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -556,27 +556,12 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE); SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f); - switch(gender) - { - case GENDER_FEMALE: - SetDisplayId(info->displayId_f ); - SetNativeDisplayId(info->displayId_f ); - break; - case GENDER_MALE: - SetDisplayId(info->displayId_m ); - SetNativeDisplayId(info->displayId_m ); - break; - default: - sLog.outError("Invalid gender %u for player",gender); - return false; - break; - } - setFactionForRace(race); uint32 RaceClassGender = ( race ) | ( class_ << 8 ) | ( gender << 16 ); SetUInt32Value(UNIT_FIELD_BYTES_0, ( RaceClassGender | ( powertype << 24 ) ) ); + InitDisplayIds(); SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP ); SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE ); SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER); @@ -1412,7 +1397,7 @@ bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) PlayerInfo const *info = objmgr.GetPlayerInfo(pRace, pClass); if(!info) { - sLog.outError("Player %u have incorrect race/class pair. Don't build enum.", guid); + sLog.outError("Player %u has incorrect race/class pair. Don't build enum.", guid); return false; } @@ -3354,6 +3339,22 @@ void Player::RemoveSpellCooldown( uint32 spell_id, bool update /* = false */ ) SendClearCooldown(spell_id, this); } +void Player::RemoveSpellCategoryCooldown(uint32 cat, bool update /* = false */) +{ + SpellCategoryStore::const_iterator ct = sSpellCategoryStore.find(cat); + if (ct == sSpellCategoryStore.end()) + return; + + const SpellCategorySet& ct_set = ct->second; + for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end();) + { + if (ct_set.find(i->first) != ct_set.end()) + RemoveSpellCooldown((i++)->first, update); + else + ++i; + } +} + void Player::RemoveArenaSpellCooldowns() { // remove cooldowns on spells that has < 15 min CD @@ -3406,7 +3407,7 @@ void Player::_LoadSpellCooldowns(QueryResult *result) if(!sSpellStore.LookupEntry(spell_id)) { - sLog.outError("Player %u have unknown spell %u in `character_spell_cooldown`, skipping.",GetGUIDLow(),spell_id); + sLog.outError("Player %u has unknown spell %u in `character_spell_cooldown`, skipping.",GetGUIDLow(),spell_id); continue; } @@ -12524,7 +12525,7 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i ) { - if ( pQuest->ReqItemId[i] ) + if (pQuest->ReqItemId[i]) DestroyItemCount( pQuest->ReqItemId[i], pQuest->ReqItemCount[i], true); } @@ -12532,12 +12533,12 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver // SetTimedQuest( 0 ); m_timedquests.erase(pQuest->GetQuestId()); - if ( pQuest->GetRewChoiceItemsCount() > 0 ) + if (pQuest->GetRewChoiceItemsCount() > 0) { - if( pQuest->RewChoiceItemId[reward] ) + if (pQuest->RewChoiceItemId[reward]) { ItemPosCountVec dest; - if( CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] ) == EQUIP_ERR_OK ) + if (CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] ) == EQUIP_ERR_OK) { Item* item = StoreNewItem( dest, pQuest->RewChoiceItemId[reward], true); SendNewItem(item, pQuest->RewChoiceItemCount[reward], true, false); @@ -12545,14 +12546,14 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver } } - if ( pQuest->GetRewItemsCount() > 0 ) + if (pQuest->GetRewItemsCount() > 0) { for (uint32 i=0; i < pQuest->GetRewItemsCount(); ++i) { - if( pQuest->RewItemId[i] ) + if (pQuest->RewItemId[i]) { ItemPosCountVec dest; - if( CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] ) == EQUIP_ERR_OK ) + if (CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] ) == EQUIP_ERR_OK) { Item* item = StoreNewItem( dest, pQuest->RewItemId[i], true); SendNewItem(item, pQuest->RewItemCount[i], true, false); @@ -12563,13 +12564,8 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver RewardReputation( pQuest ); - if( pQuest->GetRewSpellCast() > 0 ) - CastSpell( this, pQuest->GetRewSpellCast(), true); - else if( pQuest->GetRewSpell() > 0) - CastSpell( this, pQuest->GetRewSpell(), true); - uint16 log_slot = FindQuestSlot( quest_id ); - if( log_slot < MAX_QUEST_LOG_SIZE) + if (log_slot < MAX_QUEST_LOG_SIZE) SetQuestSlot(log_slot,0); QuestStatusData& q_status = mQuestStatus[quest_id]; @@ -12577,7 +12573,7 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver // Not give XP in case already completed once repeatable quest uint32 XP = q_status.m_rewarded ? 0 : uint32(pQuest->XPValue( this )*sWorld.getRate(RATE_XP_QUEST)); - if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) ) + if (getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) GiveXP( XP , NULL ); else { @@ -12587,33 +12583,33 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver } // Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative - if(pQuest->GetRewOrReqMoney()) + if (pQuest->GetRewOrReqMoney()) { ModifyMoney( pQuest->GetRewOrReqMoney() ); - if(pQuest->GetRewOrReqMoney() > 0) + if (pQuest->GetRewOrReqMoney() > 0) GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney()); } // honor reward - if(pQuest->GetRewHonorableKills()) + if (pQuest->GetRewHonorableKills()) RewardHonor(NULL, 0, MaNGOS::Honor::hk_honor_at_level(getLevel(), pQuest->GetRewHonorableKills())); // title reward - if(pQuest->GetCharTitleId()) + if (pQuest->GetCharTitleId()) { - if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId())) + if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId())) SetTitle(titleEntry); } - if(pQuest->GetBonusTalents()) + if (pQuest->GetBonusTalents()) { m_questRewardTalentCount+=pQuest->GetBonusTalents(); InitTalentForLevel(); } // Send reward mail - if(pQuest->GetRewMailTemplateId()) + if (pQuest->GetRewMailTemplateId()) { MailMessageType mailType; uint32 senderGuidOrEntry; @@ -12651,9 +12647,9 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver uint32 max_slot = questMailLoot.GetMaxSlotInLootFor(this); for(uint32 i = 0; mi.size() < MAX_MAIL_ITEMS && i < max_slot; ++i) { - if(LootItem* lootitem = questMailLoot.LootItemInSlot(i,this)) + if (LootItem* lootitem = questMailLoot.LootItemInSlot(i,this)) { - if(Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,this)) + if (Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,this)) { item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item); @@ -12664,23 +12660,30 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver WorldSession::SendMailTo(this, mailType, MAIL_STATIONERY_NORMAL, senderGuidOrEntry, GetGUIDLow(), "", 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE,pQuest->GetRewMailDelaySecs(),pQuest->GetRewMailTemplateId()); } - if(pQuest->IsDaily()) + if (pQuest->IsDaily()) { SetDailyQuestStatus(quest_id); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, 1); } - if ( !pQuest->IsRepeatable() ) + if (!pQuest->IsRepeatable()) SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE); else SetQuestStatus(quest_id, QUEST_STATUS_NONE); q_status.m_rewarded = true; + if (q_status.uState != QUEST_NEW) + q_status.uState = QUEST_CHANGED; - if(announce) + if (announce) SendQuestReward( pQuest, XP, questGiver ); - if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; + // cast spells after mark quest complete (some spells have quest completed state reqyurements in spell_area data) + if (pQuest->GetRewSpellCast() > 0) + CastSpell( this, pQuest->GetRewSpellCast(), true); + else if ( pQuest->GetRewSpell() > 0) + CastSpell( this, pQuest->GetRewSpell(), true); + if (pQuest->GetZoneOrSort() > 0) GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE, pQuest->GetZoneOrSort()); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT); @@ -14028,7 +14031,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) m_name = fields[3].GetCppString(); // check name limitations - if(!ObjectMgr::IsValidName(m_name) || (GetSession()->GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(m_name))) + if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS || + GetSession()->GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(m_name)) { delete result; CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid ='%u'", uint32(AT_LOGIN_RENAME),guid); @@ -14060,6 +14064,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) SetUInt32Value(PLAYER_BYTES_3, (GetUInt32Value(PLAYER_BYTES_3) & ~1) | fields[6].GetUInt8()); SetUInt32Value(PLAYER_FLAGS, fields[12].GetUInt32()); + InitDisplayIds(); + // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory) for(uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) { @@ -14921,7 +14927,7 @@ void Player::_LoadMailedItems(Mail *mail) if(!proto) { - sLog.outError( "Player %u have unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), item_guid_low, item_template,mail->messageID); + sLog.outError( "Player %u has unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), item_guid_low, item_template,mail->messageID); CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low); CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid_low); continue; @@ -15514,7 +15520,6 @@ void Player::SaveToDB() SetByteValue(UNIT_FIELD_BYTES_1, 0, UNIT_STAND_STATE_STAND); SetByteValue(UNIT_FIELD_BYTES_2, 3, 0); // shapeshift RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); - SetDisplayId(GetNativeDisplayId()); bool inworld = IsInWorld(); @@ -15659,7 +15664,6 @@ void Player::SaveToDB() CharacterDatabase.CommitTransaction(); // restore state (before aura apply, if aura remove flag then aura must set it ack by self) - SetDisplayId(tmp_displayid); SetUInt32Value(UNIT_FIELD_BYTES_1, tmp_bytes); SetUInt32Value(UNIT_FIELD_BYTES_2, tmp_bytes2); SetUInt32Value(UNIT_FIELD_FLAGS, tmp_flags); @@ -16103,28 +16107,17 @@ void Player::SetFloatValueInDB(uint16 index, float value, uint64 guid) void Player::Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair) { - // 0 1 2 3 4 - QueryResult* result = CharacterDatabase.PQuery("SELECT data, race, class, playerBytes, playerBytes2 FROM characters WHERE guid = '%u'", GUID_LOPART(guid)); + // 0 + QueryResult* result = CharacterDatabase.PQuery("SELECT playerBytes2 FROM characters WHERE guid = '%u'", GUID_LOPART(guid)); if(!result) return; Field* fields = result->Fetch(); - Tokens tokens = StrSplit(fields[0].GetString(), " "); - - PlayerInfo const* info = objmgr.GetPlayerInfo(fields[1].GetUInt8(), fields[2].GetUInt8()); - if(!info) - return; - - // TODO: do not access data field here - SetUInt32ValueInArray(tokens, UNIT_FIELD_DISPLAYID, gender ? info->displayId_f : info->displayId_m); - SetUInt32ValueInArray(tokens, UNIT_FIELD_NATIVEDISPLAYID, gender ? info->displayId_f : info->displayId_m); - - uint32 player_bytes2 = fields[4].GetUInt32(); + uint32 player_bytes2 = fields[0].GetUInt32(); player_bytes2 &= ~0xFF; player_bytes2 |= facialHair; - SaveValuesArrayInDB(tokens, guid); CharacterDatabase.PExecute("UPDATE characters SET gender = '%u', playerBytes = '%u', playerBytes2 = '%u' WHERE guid = '%u'", gender, skin | (face << 8) | (hairStyle << 16) | (hairColor << 24), player_bytes2, GUID_LOPART(guid)); delete result; @@ -16379,9 +16372,7 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) if(pet->isControlled()) { - WorldPacket data(SMSG_PET_SPELLS, 8); - data << uint64(0); - GetSession()->SendPacket(&data); + RemovePetActionBar(); if(GetGroup()) SetGroupUpdateFlag(GROUP_UPDATE_PET); @@ -16643,6 +16634,13 @@ void Player::CharmSpellInitialize() GetSession()->SendPacket(&data); } +void Player::RemovePetActionBar() +{ + WorldPacket data(SMSG_PET_SPELLS, 8); + data << uint64(0); + SendDirectMessage(&data); +} + bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell) { if (!mod || !spellInfo) @@ -17156,17 +17154,43 @@ void Player::InitDataForForm(bool reapplyMods) UpdateAttackPowerAndDamage(true); } +void Player::InitDisplayIds() +{ + PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass()); + if(!info) + { + sLog.outError("Player %u has incorrect race/class pair. Can't init display ids.", GetGUIDLow()); + return; + } + + uint8 gender = getGender(); + switch(gender) + { + case GENDER_FEMALE: + SetDisplayId(info->displayId_f ); + SetNativeDisplayId(info->displayId_f ); + break; + case GENDER_MALE: + SetDisplayId(info->displayId_m ); + SetNativeDisplayId(info->displayId_m ); + break; + default: + sLog.outError("Invalid gender %u for player",gender); + return; + } +} + // Return true is the bought item has a max count to force refresh of window by caller -bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint64 bagguid, uint8 slot) +bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint8 bag, uint8 slot) { // cheating attempt - if(count < 1) count = 1; + if (count < 1) count = 1; - if(!isAlive()) + if (!isAlive()) return false; ItemPrototype const *pProto = objmgr.GetItemPrototype( item ); - if( !pProto ) + if (!pProto) { SendBuyError( BUY_ERR_CANT_FIND_ITEM, NULL, item, 0); return false; @@ -17188,7 +17212,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint } size_t vendor_slot = vItems->FindItemSlot(item); - if(vendor_slot >= vItems->GetItemCount()) + if (vendor_slot >= vItems->GetItemCount()) { SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0); return false; @@ -17197,39 +17221,39 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint VendorItem const* crItem = vItems->m_items[vendor_slot]; // check current item amount if it limited - if( crItem->maxcount != 0 ) + if (crItem->maxcount != 0) { - if(pCreature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count ) + if (pCreature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count ) { SendBuyError( BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0); return false; } } - if( uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank) + if (uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank) { SendBuyError( BUY_ERR_REPUTATION_REQUIRE, pCreature, item, 0); return false; } - if(crItem->ExtendedCost) + if (crItem->ExtendedCost) { ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost); - if(!iece) + if (!iece) { sLog.outError("Item %u have wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost); return false; } // honor points price - if(GetHonorPoints() < (iece->reqhonorpoints * count)) + if (GetHonorPoints() < (iece->reqhonorpoints * count)) { SendEquipError(EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS, NULL, NULL); return false; } // arena points price - if(GetArenaPoints() < (iece->reqarenapoints * count)) + if (GetArenaPoints() < (iece->reqarenapoints * count)) { SendEquipError(EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS, NULL, NULL); return false; @@ -17259,62 +17283,38 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint // reputation discount price = uint32(floor(price * GetReputationPriceDiscount(pCreature))); - if( GetMoney() < price ) + if (GetMoney() < price) { SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, item, 0); return false; } - uint8 bag = 0; // init for case invalid bagGUID - - if (bagguid != NULL_BAG && slot != NULL_SLOT) - { - if( bagguid == GetGUID() ) - { - bag = INVENTORY_SLOT_BAG_0; - } - else - { - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;++i) - { - if( Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0,i) ) - { - if( bagguid == pBag->GetGUID() ) - { - bag = i; - break; - } - } - } - } - } - - if( IsInventoryPos( bag, slot ) || (bagguid == NULL_BAG && slot == NULL_SLOT) ) + if ((bag == NULL_BAG && slot == NULL_SLOT) || IsInventoryPos(bag, slot)) { ItemPosCountVec dest; uint8 msg = CanStoreNewItem( bag, slot, dest, item, pProto->BuyCount * count ); - if( msg != EQUIP_ERR_OK ) + if (msg != EQUIP_ERR_OK) { SendEquipError( msg, NULL, NULL ); return false; } ModifyMoney( -(int32)price ); - if(crItem->ExtendedCost) // case for new honor system + if (crItem->ExtendedCost) // case for new honor system { ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost); - if(iece->reqhonorpoints) + if (iece->reqhonorpoints) ModifyHonorPoints( - int32(iece->reqhonorpoints * count)); - if(iece->reqarenapoints) + if (iece->reqarenapoints) ModifyArenaPoints( - int32(iece->reqarenapoints * count)); for (uint8 i = 0; i < 5; ++i) { - if(iece->reqitem[i]) + if (iece->reqitem[i]) DestroyItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count), true); } } - if(Item *it = StoreNewItem( dest, item, true )) + if (Item *it = StoreNewItem( dest, item, true )) { uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count); @@ -17328,9 +17328,9 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint SendNewItem(it, pProto->BuyCount*count, true, false, false); } } - else if( IsEquipmentPos( bag, slot ) ) + else if (IsEquipmentPos(bag, slot)) { - if(pProto->BuyCount * count != 1) + if (pProto->BuyCount * count != 1) { SendEquipError( EQUIP_ERR_ITEM_CANT_BE_EQUIPPED, NULL, NULL ); return false; @@ -17338,19 +17338,19 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint uint16 dest; uint8 msg = CanEquipNewItem( slot, dest, item, false ); - if( msg != EQUIP_ERR_OK ) + if (msg != EQUIP_ERR_OK) { SendEquipError( msg, NULL, NULL ); return false; } ModifyMoney( -(int32)price ); - if(crItem->ExtendedCost) // case for new honor system + if (crItem->ExtendedCost) // case for new honor system { ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost); - if(iece->reqhonorpoints) + if (iece->reqhonorpoints) ModifyHonorPoints( - int32(iece->reqhonorpoints)); - if(iece->reqarenapoints) + if (iece->reqarenapoints) ModifyArenaPoints( - int32(iece->reqarenapoints)); for (uint8 i = 0; i < 5; ++i) { @@ -17359,7 +17359,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint } } - if(Item *it = EquipNewItem( dest, item, true )) + if (Item *it = EquipNewItem( dest, item, true )) { uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count); @@ -17381,7 +17381,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint return false; } - return crItem->maxcount!=0; + return crItem->maxcount != 0; } uint32 Player::GetMaxPersonalArenaRatingRequirement() @@ -19394,7 +19394,7 @@ void Player::EnterVehicle(Vehicle *vehicle) vehicle->SetCharmerGUID(GetGUID()); vehicle->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - vehicle->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); + vehicle->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); vehicle->setFaction(getFaction()); SetCharm(vehicle); // charm @@ -19446,7 +19446,7 @@ void Player::ExitVehicle(Vehicle *vehicle) { vehicle->SetCharmerGUID(0); vehicle->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - vehicle->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); + vehicle->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); vehicle->setFaction((GetTeam() == ALLIANCE) ? vehicle->GetCreatureInfo()->faction_A : vehicle->GetCreatureInfo()->faction_H); SetCharm(NULL); @@ -19468,9 +19468,7 @@ void Player::ExitVehicle(Vehicle *vehicle) data << uint32(0); // fall time GetSession()->SendPacket(&data); - data.Initialize(SMSG_PET_SPELLS, 8); - data << uint64(0); - GetSession()->SendPacket(&data); + RemovePetActionBar(); // maybe called at dummy aura remove? // CastSpell(this, 45472, true); // Parachute diff --git a/src/game/Player.h b/src/game/Player.h index 80cdf36d9..4c29a8fd1 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1191,7 +1191,7 @@ class MANGOS_DLL_SPEC Player : public Unit return mainItem && mainItem->GetProto()->InventoryType == INVTYPE_2HWEAPON && !CanTitanGrip(); } void SendNewItem( Item *item, uint32 count, bool received, bool created, bool broadcast = false ); - bool BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint64 bagguid, uint8 slot); + bool BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint8 bag, uint8 slot); float GetReputationPriceDiscount( Creature const* pCreature ) const; Player* GetTrader() const { return pTrader; } @@ -1447,6 +1447,8 @@ class MANGOS_DLL_SPEC Player : public Unit void PetSpellInitialize(); void CharmSpellInitialize(); void PossessSpellInitialize(); + void RemovePetActionBar(); + bool HasSpell(uint32 spell) const; bool HasActiveSpell(uint32 spell) const; // show in spellbook TrainerSpellState GetTrainerSpellState(TrainerSpell const* trainer_spell) const; @@ -1497,6 +1499,8 @@ class MANGOS_DLL_SPEC Player : public Unit PlayerSpellMap const& GetSpellMap() const { return m_spells; } PlayerSpellMap & GetSpellMap() { return m_spells; } + SpellCooldowns const& GetSpellCooldownMap() const { return m_spellCooldowns; } + void AddSpellMod(SpellModifier* mod, bool apply); bool IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell = NULL); template T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell const* spell = NULL); @@ -1520,6 +1524,7 @@ class MANGOS_DLL_SPEC Player : public Unit void SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId = 0, Spell* spell = NULL); void ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs ); void RemoveSpellCooldown(uint32 spell_id, bool update = false); + void RemoveSpellCategoryCooldown(uint32 cat, bool update = false); void SendClearCooldown( uint32 spell_id, Unit* target ); void RemoveArenaSpellCooldowns(); @@ -1783,6 +1788,8 @@ class MANGOS_DLL_SPEC Player : public Unit static uint32 getFactionForRace(uint8 race); void setFactionForRace(uint8 race); + void InitDisplayIds(); + bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const; bool RewardPlayerAndGroupAtKill(Unit* pVictim); void RewardPlayerAndGroupAtEvent(uint32 creature_id,WorldObject* pRewardSource); diff --git a/src/game/PlayerDump.cpp b/src/game/PlayerDump.cpp index 42a512689..5319ac8d7 100644 --- a/src/game/PlayerDump.cpp +++ b/src/game/PlayerDump.cpp @@ -393,7 +393,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s { QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", account); uint8 charcount = 0; - if ( result ) + if (result) { Field *fields=result->Fetch(); charcount = fields[0].GetUInt8(); @@ -405,7 +405,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s } FILE *fin = fopen(file.c_str(), "r"); - if(!fin) + if (!fin) return DUMP_FILE_OPEN_ERROR; QueryResult * result = NULL; @@ -413,7 +413,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s // make sure the same guid doesn't already exist and is safe to use bool incHighest = true; - if(guid != 0 && guid < objmgr.m_hiCharGuid) + if (guid != 0 && guid < objmgr.m_hiCharGuid) { result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE guid = '%d'", guid); if (result) @@ -427,10 +427,10 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s guid = objmgr.m_hiCharGuid; // normalize the name if specified and check if it exists - if(!normalizePlayerName(name)) + if (!normalizePlayerName(name)) name = ""; - if(ObjectMgr::IsValidName(name,true)) + if (ObjectMgr::CheckPlayerName(name,true) == CHAR_NAME_SUCCESS) { CharacterDatabase.escape_string(name); // for safe, we use name only for sql quearies anyway result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE name = '%s'", name.c_str()); @@ -440,7 +440,8 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s delete result; } } - else name = ""; + else + name = ""; // name encoded or empty diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 44fe64ac2..4cd2aaf68 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -395,6 +395,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi m_castPositionX = m_castPositionY = m_castPositionZ = 0; m_TriggerSpells.clear(); + m_preCastSpells.clear(); m_IsTriggeredSpell = triggered; //m_AreaAura = false; m_CastItem = NULL; @@ -405,7 +406,6 @@ 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) @@ -550,7 +550,7 @@ void Spell::FillTargetMap() if(m_targets.getUnitTarget()) tmpUnitMap.push_back(m_targets.getUnitTarget()); else - tmpUnitMap.push_back(m_caster); + tmpUnitMap.push_back(m_caster); break; case TARGET_AREAEFFECT_INSTANT: // All 17/7 pairs used for dest teleportation, A processed in effect code SetTargetMap(i, m_spellInfo->EffectImplicitTargetB[i], tmpUnitMap); @@ -1146,15 +1146,17 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) { - if(!unit || !effectMask) + if (!unit || !effectMask) return; + Unit* realCaster = m_originalCaster ? m_originalCaster : m_caster; + // Recheck immune (only for delayed spells) - if( m_spellInfo->speed && ( + if (m_spellInfo->speed && ( unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo)) || unit->IsImmunedToSpell(m_spellInfo))) { - m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE); + realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE); return; } @@ -1164,67 +1166,65 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, m_spellInfo->Id); } - if(m_caster->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, unit); - } + if (realCaster->GetTypeId() == TYPEID_PLAYER) + ((Player*)realCaster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, unit); - if( m_caster != unit ) + if (realCaster != unit) { // Recheck UNIT_FLAG_NON_ATTACKABLE for delayed spells if (m_spellInfo->speed > 0.0f && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE) && unit->GetCharmerOrOwnerGUID() != m_caster->GetGUID()) { - m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); + realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); return; } - if( !m_caster->IsFriendlyTo(unit) ) + if (!realCaster->IsFriendlyTo(unit)) { // for delayed spells ignore not visible explicit target - if(m_spellInfo->speed > 0.0f && unit == m_targets.getUnitTarget() && !unit->isVisibleForOrDetect(m_caster,false)) + if (m_spellInfo->speed > 0.0f && unit == m_targets.getUnitTarget() && + !unit->isVisibleForOrDetect(m_caster,false)) { - m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); + realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); return; } unit->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); - if( !(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO) ) + if (!(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO)) { - if(!unit->IsStandState() && !unit->hasUnitState(UNIT_STAT_STUNNED)) + if (!unit->IsStandState() && !unit->hasUnitState(UNIT_STAT_STUNNED)) unit->SetStandState(UNIT_STAND_STATE_STAND); - if(!unit->isInCombat() && unit->GetTypeId() != TYPEID_PLAYER && ((Creature*)unit)->AI()) - ((Creature*)unit)->AI()->AttackedBy(m_caster); + if (!unit->isInCombat() && unit->GetTypeId() != TYPEID_PLAYER && ((Creature*)unit)->AI()) + ((Creature*)unit)->AI()->AttackedBy(realCaster); - unit->AddThreat(m_caster, 0.0f); - unit->SetInCombatWith(m_caster); - m_caster->SetInCombatWith(unit); + unit->AddThreat(realCaster, 0.0f); + unit->SetInCombatWith(realCaster); + realCaster->SetInCombatWith(unit); - if(Player *attackedPlayer = unit->GetCharmerOrOwnerPlayerOrPlayerItself()) - { - m_caster->SetContestedPvP(attackedPlayer); - } + if (Player *attackedPlayer = unit->GetCharmerOrOwnerPlayerOrPlayerItself()) + realCaster->SetContestedPvP(attackedPlayer); } } else { // for delayed spells ignore negative spells (after duel end) for friendly targets - if(m_spellInfo->speed > 0.0f && !IsPositiveSpell(m_spellInfo->Id)) + if (m_spellInfo->speed > 0.0f && !IsPositiveSpell(m_spellInfo->Id)) { - m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); + realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); return; } // assisting case, healing and resurrection - if(unit->hasUnitState(UNIT_STAT_ATTACK_PLAYER)) - m_caster->SetContestedPvP(); - if( unit->isInCombat() && !(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO) ) + if (unit->hasUnitState(UNIT_STAT_ATTACK_PLAYER)) + realCaster->SetContestedPvP(); + + if (unit->isInCombat() && !(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO)) { - m_caster->SetInCombatState(unit->GetCombatTimer() > 0); - unit->getHostilRefManager().threatAssist(m_caster, 0.0f); + realCaster->SetInCombatState(unit->GetCombatTimer() > 0); + unit->getHostilRefManager().threatAssist(realCaster, 0.0f); } } } @@ -1233,12 +1233,17 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) m_diminishGroup = GetDiminishingReturnsGroupForSpell(m_spellInfo,m_triggeredByAuraSpell); m_diminishLevel = unit->GetDiminishing(m_diminishGroup); // Increase Diminishing on unit, current informations for actually casts will use values above - if((GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_PLAYER && unit->GetTypeId() == TYPEID_PLAYER) || GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_ALL) + if ((GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_PLAYER && unit->GetTypeId() == TYPEID_PLAYER) || + GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_ALL) unit->IncrDiminishing(m_diminishGroup); // Apply additional spell effects to target - if (m_preCastSpell) - m_caster->CastSpell(unit, m_preCastSpell, true, m_CastItem); + while (!m_preCastSpells.empty()) + { + uint32 spellId = *m_preCastSpells.begin(); + m_caster->CastSpell(unit, spellId, true, m_CastItem); + m_preCastSpells.erase(m_preCastSpells.begin()); + } for(uint32 effectNumber = 0; effectNumber < 3; ++effectNumber) { @@ -1250,7 +1255,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) // Get multiplier float multiplier = m_spellInfo->DmgMultiplier[effectNumber]; // Apply multiplier mods - if(Player* modOwner = m_originalCaster->GetSpellModOwner()) + if(Player* modOwner = realCaster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier, this); m_damageMultipliers[effectNumber] *= multiplier; } @@ -2287,17 +2292,17 @@ void Spell::cast(bool skipCheck) case SPELLFAMILY_GENERIC: { if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages - m_preCastSpell = 11196; // Recently Bandaged + AddPrecastSpell(11196); // Recently Bandaged else if(m_spellInfo->SpellIconID == 1662 && m_spellInfo->AttributesEx & 0x20) // Blood Fury (Racial) - m_preCastSpell = 23230; // Blood Fury - Healing Reduction + AddPrecastSpell(23230); // Blood Fury - Healing Reduction break; } case SPELLFAMILY_MAGE: { // Ice Block if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000008000000000)) - m_preCastSpell = 41425; // Hypothermia + AddPrecastSpell(41425); // Hypothermia break; } case SPELLFAMILY_PRIEST: @@ -2305,27 +2310,32 @@ void Spell::cast(bool skipCheck) // Power Word: Shield if (m_spellInfo->Mechanic == MECHANIC_SHIELD && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000001))) - m_preCastSpell = 6788; // Weakened Soul + AddPrecastSpell(6788); // Weakened Soul // Dispersion (transform) if (m_spellInfo->Id == 47585) - m_preCastSpell = 60069; // Dispersion (mana regen) + AddPrecastSpell(60069); // Dispersion (mana regen) break; } case SPELLFAMILY_PALADIN: { // Divine Shield, Divine Protection or Hand of Protection if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000400080)) - m_preCastSpell = 25771; // Forbearance + { + AddPrecastSpell(25771); // Forbearance + AddPrecastSpell(61987); // Avenging Wrath Marker + } + else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x200000000000)) + AddPrecastSpell(61987); // Avenging Wrath Marker break; } case SPELLFAMILY_SHAMAN: { // Bloodlust if (m_spellInfo->Id == 2825) - m_preCastSpell = 57724; // Sated + AddPrecastSpell(57724); // Sated // Heroism else if (m_spellInfo->Id == 32182) - m_preCastSpell = 57723; // Exhaustion + AddPrecastSpell(57723); // Exhaustion break; } default: @@ -3659,8 +3669,17 @@ SpellCastResult Spell::CheckCast(bool strict) // Caster aura req check if need if(m_spellInfo->casterAuraSpell && !m_caster->HasAura(m_spellInfo->casterAuraSpell)) return SPELL_FAILED_CASTER_AURASTATE; - if(m_spellInfo->excludeCasterAuraSpell && m_caster->HasAura(m_spellInfo->excludeCasterAuraSpell)) - return SPELL_FAILED_CASTER_AURASTATE; + if(m_spellInfo->excludeCasterAuraSpell) + { + // Special cases of non existing auras handling + if(m_spellInfo->excludeCasterAuraSpell == 61988) + { + if(m_caster->HasAura(61987)) + return SPELL_FAILED_CASTER_AURASTATE; + } + else if(m_caster->HasAura(m_spellInfo->excludeCasterAuraSpell)) + return SPELL_FAILED_CASTER_AURASTATE; + } // cancel autorepeat spells if cast start when moving // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) @@ -4093,6 +4112,14 @@ SpellCastResult Spell::CheckCast(bool strict) break; } + case SPELL_EFFECT_APPLY_GLYPH: + { + uint32 glyphId = m_spellInfo->EffectMiscValue[i]; + if(GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyphId)) + if(m_caster->HasAura(gp->SpellId)) + return SPELL_FAILED_UNIQUE_GLYPH; + break; + } case SPELL_EFFECT_FEED_PET: { if (m_caster->GetTypeId() != TYPEID_PLAYER) @@ -4356,8 +4383,13 @@ SpellCastResult Spell::CheckCast(bool strict) break; } case SPELL_AURA_MOD_POSSESS: - case SPELL_AURA_MOD_CHARM: { + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_UNKNOWN; + + if(m_targets.getUnitTarget() == m_caster) + return SPELL_FAILED_BAD_TARGETS; + if(m_caster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; @@ -4378,6 +4410,51 @@ SpellCastResult Spell::CheckCast(bool strict) break; } + case SPELL_AURA_MOD_CHARM: + { + if(m_targets.getUnitTarget() == m_caster) + return SPELL_FAILED_BAD_TARGETS; + + if(m_caster->GetPetGUID()) + return SPELL_FAILED_ALREADY_HAVE_SUMMON; + + if(m_caster->GetCharmGUID()) + return SPELL_FAILED_ALREADY_HAVE_CHARM; + + if(m_caster->GetCharmerGUID()) + return SPELL_FAILED_CHARMED; + + if(!m_targets.getUnitTarget()) + return SPELL_FAILED_BAD_IMPLICIT_TARGETS; + + if(m_targets.getUnitTarget()->GetCharmerGUID()) + return SPELL_FAILED_CHARMED; + + if(int32(m_targets.getUnitTarget()->getLevel()) > CalculateDamage(i,m_targets.getUnitTarget())) + return SPELL_FAILED_HIGHLEVEL; + + break; + } + case SPELL_AURA_MOD_POSSESS_PET: + { + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_UNKNOWN; + + if(m_caster->GetCharmGUID()) + return SPELL_FAILED_ALREADY_HAVE_CHARM; + + if(m_caster->GetCharmerGUID()) + return SPELL_FAILED_CHARMED; + + Pet* pet = m_caster->GetPet(); + if(!pet) + return SPELL_FAILED_NO_PET; + + if(pet->GetCharmerGUID()) + return SPELL_FAILED_CHARMED; + + break; + } case SPELL_AURA_MOUNTED: { if (m_caster->IsInWater()) diff --git a/src/game/Spell.h b/src/game/Spell.h index 01c25060d..d3b587e95 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -399,7 +399,6 @@ class Spell Item* m_CastItem; uint8 m_cast_count; uint32 m_glyphIndex; - uint32 m_preCastSpell; SpellCastTargets m_targets; int32 GetCastTime() const { return m_casttime; } @@ -440,6 +439,7 @@ class Spell bool CheckTargetCreatureType(Unit* target) const; void AddTriggeredSpell(SpellEntry const* spell) { m_TriggerSpells.push_back(spell); } + void AddPrecastSpell(uint32 spellId) { m_preCastSpells.push_back(spellId); } void CleanupTargetList(); protected: @@ -559,7 +559,9 @@ class Spell //List For Triggered Spells typedef std::list TriggerSpells; + typedef std::list SpellPrecasts; TriggerSpells m_TriggerSpells; + SpellPrecasts m_preCastSpells; uint32 m_spellState; uint32 m_timer; diff --git a/src/game/SpellAuraDefines.h b/src/game/SpellAuraDefines.h index 02519e375..9254468ca 100644 --- a/src/game/SpellAuraDefines.h +++ b/src/game/SpellAuraDefines.h @@ -328,7 +328,7 @@ enum AuraType SPELL_AURA_MOD_HEALING_RECEIVED = 283, // Possibly only for some spell family class spells SPELL_AURA_284, SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR = 285, - SPELL_AURA_286, + SPELL_AURA_ABILITY_PERIODIC_CRIT = 286, SPELL_AURA_DEFLECT_SPELLS, SPELL_AURA_288, SPELL_AURA_289, diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 67ece8a60..df1cd5497 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -336,7 +336,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleNoImmediateEffect, //283 SPELL_AURA_MOD_HEALING_RECEIVED implemented in Unit::SpellHealingBonus &Aura::HandleUnused, //284 not used by any spells (3.08a) &Aura::HandleAuraModAttackPowerOfArmor, //285 SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR implemented in Player::UpdateAttackPowerAndDamage - &Aura::HandleUnused, //286 not used by any spells (3.08a) + &Aura::HandleNoImmediateEffect, //286 SPELL_AURA_ABILITY_PERIODIC_CRIT implemented in Aura::IsCritFromAbilityAura called from Aura::PeriodicTick &Aura::HandleNoImmediateEffect, //287 SPELL_AURA_DEFLECT_SPELLS implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult &Aura::HandleUnused, //288 not used by any spells (3.09) except 1 test spell. &Aura::HandleUnused, //289 unused @@ -1178,23 +1178,32 @@ void Aura::SendAuraUpdate(bool remove) void Aura::SetStackAmount(uint8 stackAmount) { - if (stackAmount != m_stackAmount) + if (stackAmount == m_stackAmount) + // Nothing changed + return; + + Unit *target = GetTarget(); + Unit *caster = GetCaster(); + if (!target || !caster) + return; + + bool refresh = stackAmount > m_stackAmount; + m_stackAmount = stackAmount; + int32 amount = m_stackAmount * caster->CalculateSpellDamage(m_spellProto, m_effIndex, m_currentBasePoints, target); + // Reapply if amount change + if (amount!=m_modifier.m_amount) { - Unit *target = GetTarget(); - Unit *caster = GetCaster(); - if (!target || !caster) - return; - m_stackAmount = stackAmount; - int32 amount = m_stackAmount * caster->CalculateSpellDamage(m_spellProto, m_effIndex, m_currentBasePoints, target); - // Reapply if amount change - if (amount!=m_modifier.m_amount) - { - ApplyModifier(false, true); - m_modifier.m_amount = amount; - ApplyModifier(true, true); - } + ApplyModifier(false, true); + m_modifier.m_amount = amount; + ApplyModifier(true, true); } - RefreshAura(); + + if (refresh) + // Stack increased refresh duration + RefreshAura(); + else + // Stack decreased only send update + SendAuraUpdate(false); } bool Aura::modStackAmount(int32 num) @@ -3065,33 +3074,33 @@ void Aura::HandleModPossess(bool apply, bool Real) if(!Real) return; - if(m_target->getLevel() > m_modifier.m_amount) - return; - // not possess yourself if(GetCasterGUID() == m_target->GetGUID()) return; Unit* caster = GetCaster(); - if(!caster) + if(!caster || caster->GetTypeId() != TYPEID_PLAYER) return; + Player* p_caster = (Player*)caster; + + if( apply ) { - m_target->SetCharmerGUID(GetCasterGUID()); - m_target->setFaction(caster->getFaction()); + m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - caster->SetCharm(m_target); + m_target->SetCharmerGUID(p_caster->GetGUID()); + m_target->setFaction(p_caster->getFaction()); - if(caster->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)caster)->SetFarSightGUID(m_target->GetGUID()); - ((Player*)caster)->SetClientControl(m_target, 1); - ((Player*)caster)->SetMover(m_target); - } + p_caster->SetCharm(m_target); + + p_caster->SetFarSightGUID(m_target->GetGUID()); + p_caster->SetClientControl(m_target, 1); + p_caster->SetMover(m_target); m_target->CombatStop(); m_target->DeleteThreatList(); + if(m_target->GetTypeId() == TYPEID_UNIT) { m_target->StopMoving(); @@ -3106,13 +3115,14 @@ void Aura::HandleModPossess(bool apply, bool Real) if(CharmInfo *charmInfo = m_target->InitCharmInfo(m_target)) charmInfo->InitPossessCreateSpells(); - if(caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)caster)->PossessSpellInitialize(); + p_caster->PossessSpellInitialize(); } else { + m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); + m_target->SetCharmerGUID(0); - caster->InterruptSpell(CURRENT_CHANNELED_SPELL); // the spell is not automatically canceled when interrupted, do it now + p_caster->InterruptSpell(CURRENT_CHANNELED_SPELL); // the spell is not automatically canceled when interrupted, do it now if(m_target->GetTypeId() == TYPEID_PLAYER) { @@ -3125,18 +3135,13 @@ void Aura::HandleModPossess(bool apply, bool Real) m_target->setFaction(cinfo->faction_A); } - caster->SetCharm(NULL); + p_caster->SetCharm(NULL); - if(caster->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)caster)->SetFarSightGUID(0); - ((Player*)caster)->SetClientControl(m_target, 0); - ((Player*)caster)->SetMover(NULL); + p_caster->SetFarSightGUID(0); + p_caster->SetClientControl(m_target, 0); + p_caster->SetMover(NULL); - WorldPacket data(SMSG_PET_SPELLS, 8); - data << uint64(0); - ((Player*)caster)->GetSession()->SendPacket(&data); - } + p_caster->RemovePetActionBar(); if(m_target->GetTypeId() == TYPEID_UNIT) { @@ -3161,14 +3166,16 @@ void Aura::HandleModPossessPet(bool apply, bool Real) if(!pet || pet != m_target) return; - if(apply) - pet->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); - else - pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); + Player* p_caster = (Player*)caster; - ((Player*)caster)->SetFarSightGUID(apply ? pet->GetGUID() : 0); - ((Player*)caster)->SetCharm(apply ? pet : NULL); - ((Player*)caster)->SetClientControl(pet, apply ? 1 : 0); + if(apply) + pet->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); + else + pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); + + p_caster->SetFarSightGUID(apply ? pet->GetGUID() : 0); + p_caster->SetCharm(apply ? pet : NULL); + p_caster->SetClientControl(pet, apply ? 1 : 0); ((Player*)caster)->SetMover(apply ? pet : NULL); if(apply) @@ -3208,100 +3215,92 @@ void Aura::HandleModCharm(bool apply, bool Real) if(!caster) return; - if(int32(m_target->getLevel()) <= m_modifier.m_amount) + if( apply ) { - if( apply ) + m_target->SetCharmerGUID(GetCasterGUID()); + m_target->setFaction(caster->getFaction()); + m_target->CastStop(m_target == caster ? GetId() : 0); + caster->SetCharm(m_target); + + m_target->CombatStop(); + m_target->DeleteThreatList(); + + if(m_target->GetTypeId() == TYPEID_UNIT) { - m_target->SetCharmerGUID(GetCasterGUID()); - m_target->setFaction(caster->getFaction()); - m_target->CastStop(m_target == caster ? GetId() : 0); - caster->SetCharm(m_target); + ((Creature*)m_target)->AIM_Initialize(); + CharmInfo *charmInfo = m_target->InitCharmInfo(m_target); + charmInfo->InitCharmCreateSpells(); + charmInfo->SetReactState( REACT_DEFENSIVE ); - m_target->CombatStop(); - m_target->DeleteThreatList(); - - if(m_target->GetTypeId() == TYPEID_UNIT) - { - ((Creature*)m_target)->AIM_Initialize(); - CharmInfo *charmInfo = m_target->InitCharmInfo(m_target); - charmInfo->InitCharmCreateSpells(); - charmInfo->SetReactState( REACT_DEFENSIVE ); - - if(caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK) - { - CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo(); - if(cinfo && cinfo->type == CREATURE_TYPE_DEMON) - { - //does not appear to have relevance. Why code added initially? See note below at !apply - //to prevent client crash - //m_target->SetFlag(UNIT_FIELD_BYTES_0, 2048); - //just to enable stat window - charmInfo->SetPetNumber(objmgr.GeneratePetNumber(), true); - //if charmed two demons the same session, the 2nd gets the 1st one's name - m_target->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); - } - } - } - - if(caster->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)caster)->CharmSpellInitialize(); - } - } - else - { - m_target->SetCharmerGUID(0); - - if(m_target->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_target)->setFactionForRace(m_target->getRace()); - else + if(caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK) { CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo(); - - // restore faction - if(((Creature*)m_target)->isPet()) + if(cinfo && cinfo->type == CREATURE_TYPE_DEMON) { - if(Unit* owner = m_target->GetOwner()) - m_target->setFaction(owner->getFaction()); - else if(cinfo) - m_target->setFaction(cinfo->faction_A); + //does not appear to have relevance. Why code added initially? See note below at !apply + //to prevent client crash + //m_target->SetFlag(UNIT_FIELD_BYTES_0, 2048); + //just to enable stat window + charmInfo->SetPetNumber(objmgr.GeneratePetNumber(), true); + //if charmed two demons the same session, the 2nd gets the 1st one's name + m_target->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); } - else if(cinfo) // normal creature + } + } + + if(caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)caster)->CharmSpellInitialize(); + } + else + { + m_target->SetCharmerGUID(0); + + if(m_target->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_target)->setFactionForRace(m_target->getRace()); + else + { + CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo(); + + // restore faction + if(((Creature*)m_target)->isPet()) + { + if(Unit* owner = m_target->GetOwner()) + m_target->setFaction(owner->getFaction()); + else if(cinfo) m_target->setFaction(cinfo->faction_A); - - // restore UNIT_FIELD_BYTES_0 - if(cinfo && caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK && cinfo->type == CREATURE_TYPE_DEMON) - { - //does not appear to have relevance. Why code added initially? Class, gender, powertype should be same. - //db field removed and replaced with better way to set class, restore using this if problems - /*CreatureDataAddon const *cainfo = ((Creature*)m_target)->GetCreatureAddon(); - if(cainfo && cainfo->bytes0 != 0) - m_target->SetUInt32Value(UNIT_FIELD_BYTES_0, cainfo->bytes0); - else - m_target->RemoveFlag(UNIT_FIELD_BYTES_0, 2048);*/ - - if(m_target->GetCharmInfo()) - m_target->GetCharmInfo()->SetPetNumber(0, true); - else - sLog.outError("Aura::HandleModCharm: target (GUID: %u TypeId: %u) has a charm aura but no charm info!", m_target->GetGUIDLow(), m_target->GetTypeId()); - } } + else if(cinfo) // normal creature + m_target->setFaction(cinfo->faction_A); - caster->SetCharm(NULL); - - if(caster->GetTypeId() == TYPEID_PLAYER) + // restore UNIT_FIELD_BYTES_0 + if(cinfo && caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK && cinfo->type == CREATURE_TYPE_DEMON) { - WorldPacket data(SMSG_PET_SPELLS, 8); - data << uint64(0); - ((Player*)caster)->GetSession()->SendPacket(&data); - } - if(m_target->GetTypeId() == TYPEID_UNIT) - { - ((Creature*)m_target)->AIM_Initialize(); - if (((Creature*)m_target)->AI()) - ((Creature*)m_target)->AI()->AttackedBy(caster); + //does not appear to have relevance. Why code added initially? Class, gender, powertype should be same. + //db field removed and replaced with better way to set class, restore using this if problems + /*CreatureDataAddon const *cainfo = ((Creature*)m_target)->GetCreatureAddon(); + if(cainfo && cainfo->bytes0 != 0) + m_target->SetUInt32Value(UNIT_FIELD_BYTES_0, cainfo->bytes0); + else + m_target->RemoveFlag(UNIT_FIELD_BYTES_0, 2048);*/ + + if(m_target->GetCharmInfo()) + m_target->GetCharmInfo()->SetPetNumber(0, true); + else + sLog.outError("Aura::HandleModCharm: target (GUID: %u TypeId: %u) has a charm aura but no charm info!", m_target->GetGUIDLow(), m_target->GetTypeId()); } } + + caster->SetCharm(NULL); + + if(caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)caster)->RemovePetActionBar(); + + if(m_target->GetTypeId() == TYPEID_UNIT) + { + ((Creature*)m_target)->AIM_Initialize(); + if (((Creature*)m_target)->AI()) + ((Creature*)m_target)->AI()->AttackedBy(caster); + } } } @@ -5887,6 +5886,9 @@ void Aura::PeriodicTick() else pdamage = uint32(m_target->GetMaxHealth()*amount/100); + // This method can modify pdamage + bool isCrit = IsCritFromAbilityAura(pCaster, pdamage); + // As of 2.2 resilience reduces damage from DoT ticks as much as the chance to not be critically hit // Reduce dot damage from resilience for players if (m_target->GetTypeId() == TYPEID_PLAYER) @@ -5899,7 +5901,7 @@ void Aura::PeriodicTick() pCaster->DealDamageMods(m_target, pdamage, &absorb); - SpellPeriodicAuraLogInfo pInfo(this, pdamage, 0, absorb, resist, 0.0f); + SpellPeriodicAuraLogInfo pInfo(this, pdamage, 0, absorb, resist, 0.0f, isCrit); m_target->SendPeriodicAuraLog(&pInfo); Unit* target = m_target; // aura can be deleted in DealDamage @@ -6021,10 +6023,13 @@ void Aura::PeriodicTick() pdamage = pCaster->SpellHealingBonus(m_target, GetSpellProto(), pdamage, DOT, GetStackAmount()); + // This method can modify pdamage + bool isCrit = IsCritFromAbilityAura(pCaster, pdamage); + sLog.outDetail("PeriodicTick: %u (TypeId: %u) heal of %u (TypeId: %u) for %u health inflicted by %u", GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId()); - SpellPeriodicAuraLogInfo pInfo(this, pdamage, 0, 0, 0, 0.0f); + SpellPeriodicAuraLogInfo pInfo(this, pdamage, 0, 0, 0, 0.0f, isCrit); m_target->SendPeriodicAuraLog(&pInfo); int32 gain = m_target->ModifyHealth(pdamage); @@ -6912,3 +6917,19 @@ void Aura::HandleAuraSafeFall( bool Apply, bool Real ) if(Apply && Real && GetId() == 32474 && m_target->GetTypeId() == TYPEID_PLAYER) ((Player*)m_target)->ActivateTaxiPathTo(506, GetId()); } + +bool Aura::IsCritFromAbilityAura(Unit* caster, uint32& damage) +{ + Unit::AuraList const& auras = caster->GetAurasByType(SPELL_AURA_ABILITY_PERIODIC_CRIT); + for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + { + if (!(*itr)->isAffectedOnSpell(m_spellProto)) + continue; + if (!caster->isSpellCrit(m_target, m_spellProto, GetSpellSchoolMask(m_spellProto))) + break; + + damage = caster->SpellCriticalDamageBonus(m_spellProto, damage, m_target); + return true; + } + return false; +} diff --git a/src/game/SpellAuras.h b/src/game/SpellAuras.h index 1588992d3..f0767e299 100644 --- a/src/game/SpellAuras.h +++ b/src/game/SpellAuras.h @@ -325,6 +325,8 @@ class MANGOS_DLL_SPEC Aura protected: Aura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); + bool IsCritFromAbilityAura(Unit* caster, uint32& damage); + Modifier m_modifier; SpellModifier *m_spellmod; diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 651cf8b1a..5c1703814 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -1187,21 +1187,19 @@ void Spell::EffectDummy(uint32 i) return; // immediately finishes the cooldown on Frost spells - const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + const SpellCooldowns& cm = ((Player *)m_caster)->GetSpellCooldownMap(); + for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) { - if (itr->second->state == PLAYERSPELL_REMOVED) - continue; - - uint32 classspell = itr->first; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); if( spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) && spellInfo->Id != 11958 && GetSpellRecoveryTime(spellInfo) > 0 ) { - ((Player*)m_caster)->RemoveSpellCooldown(classspell, true); + ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first, true); } + else + ++itr; } return; } @@ -1436,14 +1434,15 @@ void Spell::EffectDummy(uint32 i) return; //immediately finishes the cooldown on certain Rogue abilities - const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + const SpellCooldowns& cm = ((Player *)m_caster)->GetSpellCooldownMap(); + for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) { - uint32 classspell = itr->first; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & UI64LIT(0x0000024000000860))) - ((Player*)m_caster)->RemoveSpellCooldown(classspell,true); + ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true); + else + ++itr; } return; } @@ -1487,14 +1486,15 @@ void Spell::EffectDummy(uint32 i) return; //immediately finishes the cooldown for hunter abilities - const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + const SpellCooldowns& cm = ((Player*)m_caster)->GetSpellCooldownMap(); + for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) { - uint32 classspell = itr->first; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && spellInfo->Id != 23989 && GetSpellRecoveryTime(spellInfo) > 0 ) - ((Player*)m_caster)->RemoveSpellCooldown(classspell,true); + ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true); + else + ++itr; } return; } @@ -2317,7 +2317,7 @@ void Spell::EffectUnlearnSpecialization( uint32 i ) _player->removeSpell(spellToUnlearn); - sLog.outDebug( "Spell: Player %u have unlearned spell %u from NpcGUID: %u", _player->GetGUIDLow(), spellToUnlearn, m_caster->GetGUIDLow() ); + sLog.outDebug( "Spell: Player %u has unlearned spell %u from NpcGUID: %u", _player->GetGUIDLow(), spellToUnlearn, m_caster->GetGUIDLow() ); } void Spell::EffectPowerDrain(uint32 i) @@ -3320,7 +3320,7 @@ void Spell::EffectLearnSpell(uint32 i) 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,false); - sLog.outDebug( "Spell: Player %u have learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() ); + sLog.outDebug( "Spell: Player %u has learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() ); } void Spell::EffectDispel(uint32 i) @@ -4327,24 +4327,30 @@ void Spell::EffectWeaponDmg(uint32 i) spell_bonus += m_caster->CalculateDamage (OFF_ATTACK, normalized); } // Devastate bonus and sunder armor refresh - else if(m_spellInfo->SpellVisual[0] == 671 && m_spellInfo->SpellIconID == 1508) + else if(m_spellInfo->SpellVisual[0] == 12295 && m_spellInfo->SpellIconID == 1508) { uint32 stack = 0; // Need refresh all Sunder Armor auras from this caster Unit::AuraMap& suAuras = unitTarget->GetAuras(); + SpellEntry const *spellInfo; for(Unit::AuraMap::iterator itr = suAuras.begin(); itr != suAuras.end(); ++itr) { - SpellEntry const *spellInfo = (*itr).second->GetSpellProto(); + spellInfo = (*itr).second->GetSpellProto(); if( spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && (spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000004000)) && (*itr).second->GetCasterGUID() == m_caster->GetGUID()) { (*itr).second->RefreshAura(); stack = (*itr).second->GetStackAmount(); + break; } } if (stack) spell_bonus += stack * CalculateDamage(2, unitTarget); + if (!stack || stack < spellInfo->StackAmount) + // Devastate causing Sunder Armor Effect + // and no need to cast over max stack amount + m_caster->CastSpell(unitTarget, 58567, true); } break; } diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 04499d490..373f76489 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -27,6 +27,7 @@ #include "Spell.h" #include "ScriptCalls.h" #include "Totem.h" +#include "SpellAuras.h" void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) { @@ -377,10 +378,6 @@ void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket) { CHECK_PACKET_SIZE(recvPacket,4); - // ignore for remote control state - if(_player->m_mover != _player) - return; - uint32 spellId; recvPacket >> spellId; @@ -388,10 +385,34 @@ void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket) if (!spellInfo) return; - // not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL - if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL)) + if (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL) return; + if(!IsPositiveSpell(spellId)) + { + // ignore for remote control state + if (_player->m_mover != _player) + { + // except own aura spells + bool allow = false; + for(int k = 0; k < 3; ++k) + { + if (spellInfo->EffectApplyAuraName[k] == SPELL_AURA_MOD_POSSESS || + spellInfo->EffectApplyAuraName[k] == SPELL_AURA_MOD_POSSESS_PET) + { + allow = true; + break; + } + } + + // this also include case when aura not found + if(!allow) + return; + } + else + return; + } + // channeled spell case (it currently casted then) if (IsChanneledSpell(spellInfo)) { diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 6f68c336d..1dd4544d6 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -334,7 +334,7 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) case 11196: // Recently Bandaged return false; // some spells have unclear target modes for selection, so just make effect positive - case 27184: + case 27184: case 27190: case 27191: case 27201: @@ -1126,7 +1126,7 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons if(!spellInfo_1 || !spellInfo_2) return false; - if(spellInfo_1->Id == spellId_2) + if(spellId_1 == spellId_2) return false; //I think we don't check this correctly because i need a exception for spell: @@ -1193,6 +1193,11 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons break; } + case SPELLFAMILY_MAGE: + // Arcane Intellect and Insight + if( spellInfo_2->SpellIconID == 125 && spellInfo_1->Id == 18820 ) + return false; + break; case SPELLFAMILY_WARRIOR: { // Scroll of Protection and Defensive Stance (multi-family check) @@ -1274,6 +1279,10 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons if( spellInfo_1->Id == 11129 && spellInfo_2->SpellIconID == 33 && spellInfo_2->SpellVisual[0] == 321 ) return false; + // Arcane Intellect and Insight + if( spellInfo_1->SpellIconID == 125 && spellInfo_2->Id == 18820 ) + return false; + break; case SPELLFAMILY_WARLOCK: if( spellInfo_2->SpellFamilyName == SPELLFAMILY_WARLOCK ) @@ -2610,7 +2619,7 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell // Try search in next group groupEntry = sAreaGroupStore.LookupEntry(groupEntry->nextGroup); } - + if(!found) return SPELL_FAILED_INCORRECT_AREA; } diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 9c29a7d89..dd106c5fc 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -4278,13 +4278,13 @@ void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo *pInfo) data << uint32(GetSpellSchoolMask(aura->GetSpellProto())); data << uint32(pInfo->absorb); // absorb data << uint32(pInfo->resist); // resist - data << uint8(0); // new 3.1.2 + data << uint8(pInfo->critical ? 1 : 0); // new 3.1.2 critical flag break; case SPELL_AURA_PERIODIC_HEAL: case SPELL_AURA_OBS_MOD_HEALTH: data << uint32(pInfo->damage); // damage data << uint32(pInfo->overDamage); // overheal? - data << uint8(0); // new 3.1.2 + data << uint8(pInfo->critical ? 1 : 0); // new 3.1.2 critical flag break; case SPELL_AURA_OBS_MOD_MANA: case SPELL_AURA_PERIODIC_ENERGIZE: @@ -5521,6 +5521,17 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu } return true; } + // Judgements of the Wise + case 31876: + case 31877: + case 31878: + target = this; + basepoints0 = GetCreatePowers(POWER_MANA) * 25 / 100; + triggered_spell_id = 31930; + + // Replenishment + CastSpell(this, 57669, true, NULL, triggeredByAura); + break; // Holy Power (Redemption Armor set) case 28789: { @@ -6767,6 +6778,14 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB return false; break; } + // Sword and Board + case 50227: + { + // Remove cooldown on Shield Slam + if (GetTypeId() == TYPEID_PLAYER) + ((Player*)this)->RemoveSpellCategoryCooldown(1209, true); + break; + } // Brain Freeze case 57761: { @@ -9788,7 +9807,8 @@ int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_inde if(spellProto->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION && spellProto->spellLevel && spellProto->Effect[effect_index] != SPELL_EFFECT_WEAPON_PERCENT_DAMAGE && - spellProto->Effect[effect_index] != SPELL_EFFECT_KNOCK_BACK) + spellProto->Effect[effect_index] != SPELL_EFFECT_KNOCK_BACK && + (spellProto->Effect[effect_index] != SPELL_EFFECT_APPLY_AURA || spellProto->EffectApplyAuraName[effect_index] != SPELL_AURA_MOD_DECREASE_SPEED)) value = int32(value*0.25f*exp(getLevel()*(70-spellProto->spellLevel)/1000.0f)); return value; diff --git a/src/game/Unit.h b/src/game/Unit.h index fe35362b1..8762723a7 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -514,7 +514,7 @@ enum UnitFlags UNIT_FLAG_DISARMED = 0x00200000, // 3.0.3, disable melee spells casting..., "Required melee weapon" added to melee spells tooltip. UNIT_FLAG_CONFUSED = 0x00400000, UNIT_FLAG_FLEEING = 0x00800000, - UNIT_FLAG_UNK_24 = 0x01000000, // used in spell Eyes of the Beast for pet... + UNIT_FLAG_PLAYER_CONTROLLED= 0x01000000, // used in spell Eyes of the Beast for pet... let attack by controlled creature UNIT_FLAG_NOT_SELECTABLE = 0x02000000, UNIT_FLAG_SKINNABLE = 0x04000000, UNIT_FLAG_MOUNT = 0x08000000, @@ -686,8 +686,8 @@ struct SpellNonMeleeDamage{ struct SpellPeriodicAuraLogInfo { - SpellPeriodicAuraLogInfo(Aura *_aura, uint32 _damage, uint32 _overDamage, uint32 _absorb, uint32 _resist, float _multiplier) - : aura(_aura), damage(_damage), overDamage(_overDamage), absorb(_absorb), resist(_resist), multiplier(_multiplier) {} + SpellPeriodicAuraLogInfo(Aura *_aura, uint32 _damage, uint32 _overDamage, uint32 _absorb, uint32 _resist, float _multiplier, bool _critical = false) + : aura(_aura), damage(_damage), overDamage(_overDamage), absorb(_absorb), resist(_resist), multiplier(_multiplier), critical(_critical) {} Aura *aura; uint32 damage; @@ -695,6 +695,7 @@ struct SpellPeriodicAuraLogInfo uint32 resist; uint32 overDamage; // overkill/overheal float multiplier; + bool critical; }; uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missCondition); diff --git a/src/game/World.cpp b/src/game/World.cpp index 87b06e7b8..bf20ccf08 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -624,6 +624,27 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfig.GetIntDefault ("StrictCharterNames", 0); m_configs[CONFIG_STRICT_PET_NAMES] = sConfig.GetIntDefault ("StrictPetNames", 0); + m_configs[CONFIG_MIN_PLAYER_NAME] = sConfig.GetIntDefault ("MinPlayerName", 2); + if(m_configs[CONFIG_MIN_PLAYER_NAME] < 1 || m_configs[CONFIG_MIN_PLAYER_NAME] > MAX_PLAYER_NAME) + { + sLog.outError("MinPlayerName (%i) must be in range 1..%u. Set to 2.",m_configs[CONFIG_MIN_PLAYER_NAME],MAX_PLAYER_NAME); + m_configs[CONFIG_MIN_PLAYER_NAME] = 2; + } + + m_configs[CONFIG_MIN_CHARTER_NAME] = sConfig.GetIntDefault ("MinCharterName", 2); + if(m_configs[CONFIG_MIN_CHARTER_NAME] < 1 || m_configs[CONFIG_MIN_CHARTER_NAME] > MAX_CHARTER_NAME) + { + sLog.outError("MinCharterName (%i) must be in range 1..%u. Set to 2.",m_configs[CONFIG_MIN_CHARTER_NAME],MAX_CHARTER_NAME); + m_configs[CONFIG_MIN_CHARTER_NAME] = 2; + } + + m_configs[CONFIG_MIN_PET_NAME] = sConfig.GetIntDefault ("MinPetName", 2); + if(m_configs[CONFIG_MIN_PET_NAME] < 1 || m_configs[CONFIG_MIN_PET_NAME] > MAX_PET_NAME) + { + sLog.outError("MinPetName (%i) must be in range 1..%u. Set to 2.",m_configs[CONFIG_MIN_PET_NAME],MAX_PET_NAME); + m_configs[CONFIG_MIN_PET_NAME] = 2; + } + m_configs[CONFIG_CHARACTERS_CREATING_DISABLED] = sConfig.GetIntDefault ("CharactersCreatingDisabled", 0); m_configs[CONFIG_CHARACTERS_PER_REALM] = sConfig.GetIntDefault("CharactersPerRealm", 10); diff --git a/src/game/World.h b/src/game/World.h index 5fdfb00ad..8c2f2df14 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -107,6 +107,9 @@ enum WorldConfigs CONFIG_STRICT_PLAYER_NAMES, CONFIG_STRICT_CHARTER_NAMES, CONFIG_STRICT_PET_NAMES, + CONFIG_MIN_PLAYER_NAME, + CONFIG_MIN_CHARTER_NAME, + CONFIG_MIN_PET_NAME, CONFIG_CHARACTERS_CREATING_DISABLED, CONFIG_CHARACTERS_PER_ACCOUNT, CONFIG_CHARACTERS_PER_REALM, diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index 69643c7cf..de5dbe22d 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -396,6 +396,18 @@ LogColors = "" # (included in client by default, with active official localization or custom localization fonts in clientdir/Fonts). # 3 basic latin characters + server timezone specific # +# MinPlayerName +# Minimal name length (1..12) +# Default: 2 +# +# MinCharterName +# Minimal name length (1..24) +# Default: 2 +# +# MinPetName +# Minimal name length (1..12) +# Default: 2 +# # CharactersCreatingDisabled # Disable characters creating for specific team or any (non-player accounts not affected) # Default: 0 - enabled @@ -579,6 +591,9 @@ DeclinedNames = 0 StrictPlayerNames = 0 StrictCharterNames = 0 StrictPetNames = 0 +MinPlayerName = 2 +MinCharterName = 2 +MinPetName = 2 CharactersCreatingDisabled = 0 CharactersPerAccount = 50 CharactersPerRealm = 10 diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 4312804a9..ff2b133ff 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 "8118" + #define REVISION_NR "8147" #endif // __REVISION_NR_H__